feat(frontend): T-215 ProjectTabBar — Radix Tabs + create-project popover (closes #83)#109
Conversation
…popover) (closes #83) ProjectTabBar lists projects from useProjectStore via Radix Tabs and exposes a trailing + button opening a Radix Popover with name + path inputs that calls invokeProjectsCreate then upserts + auto-selects into the store. - src/features/projects/components/ProjectTabBar.tsx: component with co-located useProjectCreateForm hook (double-submit guard via useRef, error path keeps popover open with role="alert" + aria-describedby on inputs) and FormField subcomponent. - src/shared/components/ui/Popover.tsx: new Radix Popover wrapper (Portal + Content with --color-bg-elevated / --color-border-default tokens). - src/shared/i18n/{i18n.ts,locales/{en,fr}.json}: new projects namespace with tabs.empty.{title,body}, tabs.create_label, create.{title,name,path,submit, cancel,error} keys (FR + EN). - src/features/projects/components/ProjectTabBar.test.tsx: 18 vitest cases — empty state, tab rendering + selection, popover open/submit/close, trimmed IPC payload, upsert + auto-select on success, error path, aria-describedby link on rejection, ArrowRight keyboard nav, FR i18n smoke test. App.tsx wiring of selectedId into the kanban data fetch and AppEvent subscriptions are sibling Sprint 2 tasks per ARCHI §21.
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds ProjectTabBar (Radix Tabs + popover create form), a shared Popover UI wrapper, EN/FR i18n keys under a new "projects" namespace, changelog entries, and a comprehensive Vitest suite covering create-flow, race-guards, accessibility, and localization. ChangesProjectTabBar Component Feature
Sequence DiagramsequenceDiagram
participant User
participant ProjectTabBar
participant ProjectStore
participant IPC as invokeProjectsCreate
User->>ProjectTabBar: click + button
ProjectTabBar->>ProjectTabBar: open popover
User->>ProjectTabBar: fill name & path, click submit
ProjectTabBar->>IPC: invoke with trimmed name/path
IPC->>ProjectTabBar: return created project
ProjectTabBar->>ProjectStore: upsert project
ProjectTabBar->>ProjectStore: select project
ProjectTabBar->>ProjectTabBar: close popover, reset form
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/features/projects/components/ProjectTabBar.tsx`:
- Around line 209-245: The submit result handlers can race with
closing/resetting the popover and overwrite fresh state; add a submission
version or token ref (e.g., submissionVersionRef) that you increment inside
onOpenChange when closing/resetting and capture in onSubmit before calling
tryCreateProject, then after the await only apply
setState/handleSuccess/handleFailure (and clear submittingRef) if the captured
token matches the current submissionVersionRef (or if the popover is still
open); update onOpenChange to increment/reset the token when setOpen(false) is
called and guard calls to handleSuccess/handleFailure in onSubmit using that
token so late async results are ignored.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 90adac5f-4119-4594-b7b2-573f13281b04
📒 Files selected for processing (7)
CHANGELOG.mdsrc/features/projects/components/ProjectTabBar.test.tsxsrc/features/projects/components/ProjectTabBar.tsxsrc/shared/components/ui/Popover.tsxsrc/shared/i18n/i18n.tssrc/shared/i18n/locales/en.jsonsrc/shared/i18n/locales/fr.json
There was a problem hiding this comment.
1 issue found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/features/projects/components/ProjectTabBar.tsx">
<violation number="1" location="src/features/projects/components/ProjectTabBar.tsx:212">
P2: Closing the popover during an in-flight create resets UI state but leaves the submit guard locked, so users can see an enabled submit button that no-ops until the previous request resolves.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…submit race Closing the popover while invokeProjectsCreate is in flight previously left submittingRef locked AND let the late result overwrite fresh state (stale error shown on next open). Add submitTokenRef bumped both on close and on submit-start; finalize() drops the result entirely when the token no longer matches and resets submittingRef only on the still-current path. onOpenChange(false) now bumps the token and clears submittingRef synchronously so a subsequent submit is never blocked by an abandoned in-flight request. Tests: - discards the IPC result when the popover closes before it resolves - frees the submit guard so a subsequent submit can proceed after close-during-flight Addresses CodeRabbit + cubic-dev-ai review findings on PR #109.
Summary
ProjectTabBarReact component listing projects fromuseProjectStorevia RadixTabswith a trailing+button opening a RadixPopoverthat creates a project throughinvokeProjectsCreateandupserts+ auto-selects it into the store.Popoverwrapper atsrc/shared/components/ui/Popover.tsx(Radix Popover + Portal,--color-bg-elevated/--color-border-defaulttokens).projectsi18n namespace (FR + EN) —tabs.empty.{title,body},tabs.create_label,create.{title,name,path,submit,cancel,error}.aria-describedbylink on rejection, ArrowRight keyboard nav, FR i18n smoke test.Why
+, fill inputs, dispatch IPC mock, verifyupsertcalled; keyboard nav via Radix defaults; empty-state message + CTA.Changes
src/features/projects/components/ProjectTabBar.tsx(new) — component with co-locateduseProjectCreateFormhook (double-submit guard viauseRef,try-style tagged-result helper for the IPC call, error path keeps popover open withrole="alert",aria-describedbylinking inputs to the alert,aria-busyon form during submit) andFormFieldsubcomponent.src/features/projects/components/ProjectTabBar.test.tsx(new) — 18 vitest + RTL cases mocking theprojectsIPC wrapper at the module boundary, resetting the Zustand store and i18n singleton inbeforeEach, asserting trimmed payload (" Gamma "→"Gamma"), upsert + auto-select, popover lifecycle, error recovery (submit re-enabled,aria-describedbylink), FR i18n smoke.src/shared/components/ui/Popover.tsx(new) — Radix Popover wrapper following theTabs.tsx/Dialog.tsxforwardRef+cn+displayNamepattern.src/shared/i18n/i18n.ts— registerprojectsnamespace.src/shared/i18n/locales/{en,fr}.json— newprojectskeys (FR + EN in lock-step).CHANGELOG.md— T-215 entry under[Unreleased] / Added.Test plan
pnpm exec tsc -bclean.pnpm exec oxlint .0 warnings / 0 errors.pnpm exec oxfmt --checkclean on tracked files.pnpm exec vitest run166/166 pass (was 142 + 6 baseline drift = 148 before; +18 ProjectTabBar cases).App.tsxwiring into the kanban data fetch is a sibling Sprint 2 task (see "Known follow-ups" in CHANGELOG entry); component is reviewed in isolation here.Known follow-ups (intentional scope cuts)
App.tsxstill uses the hardcodedDEFAULT_PROJECT_ID = 1— the wiring that swapsselectedIdfromuseProjectStoreintoinvokeTasksListand theAppEvent::Project{Created,Updated,Deleted}subscriptions for cross-window cache invalidation belong to a sibling task per ARCHI §21.@tauri-apps/plugin-dialog) is a UX nicety tracked outside Sprint 2.AppEvent::ProjectCreatedreactive subscription yet — only the optimisticupsertafter the IPC resolves.Summary by CodeRabbit
New Features
Tests