feat(frontend): T-221 auto-select first project + switch refetch tests (closes #89)#115
Conversation
… refetch tests (closes #89) - App.tsx: after `setProjects(projects)`, destructure first element and call `selectProject(firstProject.id)` only when both `firstProject !== undefined` and `useProjectStore.getState().selectedId === undefined`. Encodes literal spec ("if no current id, select first") and keeps the existing cold-boot `loaded` guard semantics so an in-flight `upsert` race still wins. - App.tsx: rename local slice action `select` → `selectProject` to avoid shadowing in the `App()` body; add to `useEffect` deps (stable Zustand ref). - App.test.tsx: +3 tests. Auto-select picks first of multiple, no auto-select on empty list, switching project asserts exact ordered tasks_list payload sequence `[{ project_id: PROJECT_ID }, { project_id: SECOND_PROJECT_ID }]`. - CHANGELOG.md: [Unreleased] Added entry. Validation: pnpm exec vitest run 307/307 (+3 vs 0.0.2), oxlint clean, oxfmt clean, tsc -b clean.
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)
📝 WalkthroughWalkthroughThe PR completes T-221 by adding auto-selection of the first project during app initialization and ensuring task lists refetch when switching projects, with comprehensive test coverage. ChangesProject Selection and Task Refetch
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
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 |
Summary
Closes #89 (T-221, Sprint 2 §21, depends on T-215/216/218/219/220).
The Tasks-route wiring of
<ProjectTabBar />+<KanbanBoard />and theinvokeProjectsList/invokeTasksListhydration effects already landed in T-130 (PR #64) and T-216 (PR #110). This PR closes the two remaining acceptance gaps from #89:useProjectStore.setProjectsonly reconciles invalid ids viareconcileSelectedId; it does NOT auto-pick the first project on cold boot.ProjectTabBaronly auto-selects when the user creates a new project, not on hydration.invokeTasksListwith the newproject_id.Changes
src/App.tsxsetProjects(projects), destructureconst [firstProject] = projectsand callselectProject(firstProject.id)only when bothfirstProject !== undefinedanduseProjectStore.getState().selectedId === undefined.selectedId === undefinedguard encodes the literal spec wording and is defense-in-depth: today it never matters because the outerloadedguard (introduced in T-130 to win the user-upsert race) ensures the auto-select code path only runs on cold boot when no selection could exist yet. If we later persistselectedIdacross sessions, this guard prevents the restored id from being silently overwritten.select→selectProjectto avoid shadowing in theApp()body and make the intent self-documenting at the call site. Zustand action refs are stable across renders, so adding it to theuseEffectdependency array causes zero extra re-runs.src/App.test.tsxThree new tests in the existing
describeblocks (no new helpers required):auto-selects the first project (not the last) when projects_list returns multiple— seeds two distinct projects to prove the selector picksprojects[0]. A single-project assertion would green-bar a "select last" regression.does not auto-select when projects_list resolves with an empty list— regression guard that theprojects[0]access stays safe on an empty array andselectedIdremainsundefined.refetches tasks_list with the new project id when switching projects— installs two projects, waits for the auto-select-driven firsttasks_listforPROJECT_ID, thenselect(SECOND_PROJECT_ID)and asserts the exact ordered payload sequence viatasksCalls.map(([, payload]) => payload).toEqual([{ project_id: PROJECT_ID }, { project_id: SECOND_PROJECT_ID }]). The looserlength >= 2form was flagged by adversarial review — it would have allowedPROJECT_IDto fire twice and still passed.CHANGELOG.md[Unreleased] ### Addedentry.Acceptance criteria (#89)
App — AC5: no console errors(lines 287-310). Re-validated against new auto-select path.refetches tasks_list with the new project id when switching projectstest (exact ordered payload assertion).App.test.tsx— +3 tests, 19 total in the file, 307 total in the suite.Why
Adversarial review
Two parallel reviewers (typescript + clean-code) ran on the initial draft. Their consensus findings were applied before commit:
selectedIdselectedId === undefinedguard alongside thefirstProject !== undefinedcheck.length >= 2and inclusion —PROJECT_IDtwice would passtoEqual([{ project_id: PROJECT_ID }, { project_id: SECOND_PROJECT_ID }])exact-sequence assertion.FIRST_PROJECT_INDEX = 0overdone vs codebase's existingEMPTY_LENGTH = 0idiomconst [firstProject] = projectsdestructure pattern — no positional sentinel constant needed.Test plan
pnpm exec vitest run— 307/307 pass (+3 vs0.0.2).pnpm exec oxlint .— 0 warnings 0 errors.pnpm exec oxfmt --check .— clean.pnpm exec tsc -b— no TS errors.pnpm exec knip— only pre-existingknip.jsonconfig hints (not introduced by this PR).pnpm tauri dev, confirm app boots straight into Kanban with first project pre-selected, click second tab → board switches without console errors. (deferred to merge reviewer)Summary by CodeRabbit