refactor: account settings page audit — tokens, a11y, hierarchy #123

Merged
chihlasm merged 4 commits from refactor/dashboard-design-critique into main 2026-04-01 01:05:25 +00:00
2 changed files with 11 additions and 1 deletions
Showing only changes of commit 5c11c1db33 - Show all commits

View File

@@ -373,7 +373,7 @@ gh run view <id> --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusi
**105. `npm run build` fails with `EACCES: permission denied` on `dist/` in code-server:** This is a filesystem permission issue in the Docker environment, not a TypeScript error — the TS compilation completes successfully. Use `npx tsc -b` to verify TypeScript cleanly without needing to write to `dist/`.
---
**106. Guard async "select item → load data → apply state" flows with a ref:** When a component lets the user switch between items (chat sessions, flows, scripts) and loads data asynchronously on each switch, the load for item A can complete *after* the user has already switched to item B — overwriting B's state with A's stale data. Fix pattern: keep a `currentSelectionRef = useRef(initialId)` and update it synchronously whenever the selection changes (in every creation/switch path). After every `await`, bail out if `currentSelectionRef.current !== thisItemId`. See `AssistantChatPage.tsx` `selectChat` for the reference implementation (`currentChatRef`).
## RBAC & Permissions

View File

@@ -81,6 +81,9 @@ export default function AssistantChatPage() {
const fileInputRef = useRef<HTMLInputElement>(null)
const dragCounterRef = useRef(0)
const prefillHandledRef = useRef(false)
// Tracks the most recently requested active chat ID so in-flight selectChat
// calls that complete after the user switches chats don't clobber new state.
const currentChatRef = useRef<string | null>(activeChatId)
// Persist active chat ID to sessionStorage
useEffect(() => {
@@ -214,6 +217,7 @@ export default function AssistantChatPage() {
}
const selectChat = useCallback(async (chatId: string) => {
currentChatRef.current = chatId
setActiveChatId(chatId)
// Clear TaskLane when switching chats — will restore from backend if available
setShowTaskLane(false)
@@ -221,6 +225,10 @@ export default function AssistantChatPage() {
setActiveActions([])
try {
const detail = await aiSessionsApi.getSession(chatId)
// Guard: if the user switched to a different chat while this API call was
// in flight (e.g. clicked "New Chat"), discard stale results so we don't
// clobber the new session's task lane state.
if (currentChatRef.current !== chatId) return
setMessages(
(detail.conversation_messages || []).map(m => ({
role: m.role as 'user' | 'assistant',
@@ -264,6 +272,7 @@ export default function AssistantChatPage() {
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
}
currentChatRef.current = session.session_id
setChats(prev => [chatItem, ...prev])
setActiveChatId(session.session_id)
setMessages([])
@@ -430,6 +439,7 @@ export default function AssistantChatPage() {
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
}
currentChatRef.current = session.session_id
setChats(prev => [chatItem, ...prev])
setActiveChatId(session.session_id)
setMessages([{ role: 'user', content: resumePrompt }])