Skip to content

feat(frontend): T-221 auto-select first project + switch refetch tests (closes #89)#115

Merged
mpiton merged 1 commit into
mainfrom
feat/i-89-t-221-app-tasks-route
May 14, 2026
Merged

feat(frontend): T-221 auto-select first project + switch refetch tests (closes #89)#115
mpiton merged 1 commit into
mainfrom
feat/i-89-t-221-app-tasks-route

Conversation

@mpiton
Copy link
Copy Markdown
Owner

@mpiton mpiton commented May 13, 2026

Summary

Closes #89 (T-221, Sprint 2 §21, depends on T-215/216/218/219/220).

The Tasks-route wiring of <ProjectTabBar /> + <KanbanBoard /> and the invokeProjectsList / invokeTasksList hydration effects already landed in T-130 (PR #64) and T-216 (PR #110). This PR closes the two remaining acceptance gaps from #89:

  1. "If no current id, select first" was not enforced — useProjectStore.setProjects only reconciles invalid ids via reconcileSelectedId; it does NOT auto-pick the first project on cold boot. ProjectTabBar only auto-selects when the user creates a new project, not on hydration.
  2. No Vitest coverage verified that switching projects in the tab bar actually re-fires invokeTasksList with the new project_id.

Changes

src/App.tsx

  • After setProjects(projects), destructure const [firstProject] = projects and call selectProject(firstProject.id) only when both firstProject !== undefined and useProjectStore.getState().selectedId === undefined.
    • The selectedId === undefined guard encodes the literal spec wording and is defense-in-depth: today it never matters because the outer loaded guard (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 persist selectedId across sessions, this guard prevents the restored id from being silently overwritten.
  • Rename the slice's exposed action from selectselectProject to avoid shadowing in the App() body and make the intent self-documenting at the call site. Zustand action refs are stable across renders, so adding it to the useEffect dependency array causes zero extra re-runs.

src/App.test.tsx

Three new tests in the existing describe blocks (no new helpers required):

  1. auto-selects the first project (not the last) when projects_list returns multiple — seeds two distinct projects to prove the selector picks projects[0]. A single-project assertion would green-bar a "select last" regression.
  2. does not auto-select when projects_list resolves with an empty list — regression guard that the projects[0] access stays safe on an empty array and selectedId remains undefined.
  3. refetches tasks_list with the new project id when switching projects — installs two projects, waits for the auto-select-driven first tasks_list for PROJECT_ID, then select(SECOND_PROJECT_ID) and asserts the exact ordered payload sequence via tasksCalls.map(([, payload]) => payload).toEqual([{ project_id: PROJECT_ID }, { project_id: SECOND_PROJECT_ID }]). The looser length >= 2 form was flagged by adversarial review — it would have allowed PROJECT_ID to fire twice and still passed.

CHANGELOG.md

[Unreleased] ### Added entry.

Acceptance criteria (#89)

  • App boots without console errors — already covered by App — AC5: no console errors (lines 287-310). Re-validated against new auto-select path.
  • Switching tabs refetches tasks — covered by the new refetches tasks_list with the new project id when switching projects test (exact ordered payload assertion).
  • Vitest update for App.test.tsx — +3 tests, 19 total in the file, 307 total in the suite.

Why

"Replace the current placeholder Tasks route in src/App.tsx" (issue body, line 4) — the actual placeholder was replaced by T-130 long before this PR. Issue #89 became a "verify and close the remaining ACs" task once the foundational wiring landed early. This PR makes the auto-select behavior explicit in code, locks it down with regression tests, and keeps the issue tracker honest by closing #89 only after every AC has a corresponding green test.

Adversarial review

Two parallel reviewers (typescript + clean-code) ran on the initial draft. Their consensus findings were applied before commit:

Finding Severity Action
Spec gap: code unconditionally auto-selected on cold boot, didn't check selectedId MAJOR Added the selectedId === undefined guard alongside the firstProject !== undefined check.
Auto-select test passed with a single project — couldn't distinguish "first" from "only" MAJOR Test now seeds two distinct projects.
Switch-refetch test asserted only length >= 2 and inclusion — PROJECT_ID twice would pass MAJOR Replaced with toEqual([{ project_id: PROJECT_ID }, { project_id: SECOND_PROJECT_ID }]) exact-sequence assertion.
FIRST_PROJECT_INDEX = 0 overdone vs codebase's existing EMPTY_LENGTH = 0 idiom NIT Replaced with const [firstProject] = projects destructure pattern — no positional sentinel constant needed.

Test plan

  • pnpm exec vitest run — 307/307 pass (+3 vs 0.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-existing knip.json config hints (not introduced by this PR).
  • Manual: 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

  • Bug Fixes
    • Automatic selection of the first project when none is initially selected
    • Task list now correctly refetches when switching between different projects
    • Fixed data consistency issues that could occur during project transitions
    • Added safeguards to prevent accidental data overwrites

Review Change Stack

… 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-code-review
Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 18dd5c6c-24e6-4923-9b83-04fbbcea1186

📥 Commits

Reviewing files that changed from the base of the PR and between b7e10f3 and ca81a94.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • src/App.test.tsx
  • src/App.tsx

📝 Walkthrough

Walkthrough

The 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.

Changes

Project Selection and Task Refetch

Layer / File(s) Summary
Store contract and auto-selection logic
src/App.tsx
selectProject action is added to the app store slice. The App() component destructures this action and calls selectProject(firstProject.id) after projects load, but only when no project is currently selected. The effect dependency array is updated to include the new action.
Test coverage and changelog
src/App.test.tsx, CHANGELOG.md
Tests validate that auto-selection chooses the first project (not the last) when multiple projects exist, handles empty project lists without selection, and verifies task list refetch on both initial project selection and on project switching. CHANGELOG documents completion of T-221 wiring.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • mpiton/forgent#64: Earlier PR that established the initial tasks_list hydration behavior for the Tasks route; this PR extends it with auto-selection and project-switch refetch logic.
  • mpiton/forgent#105: PR that updated the IPC contract to require { project_id } in tasks_list payloads, which this PR now leverages when refetching tasks on project switches.

Suggested labels

size:M

Poem

🐰 A project picks itself with gentle grace,
The first one takes its rightful place,
When switching comes, the tasks reload,
All tested on the dev's new road!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main changes: auto-selecting the first project on app load and adding test coverage for project switching refetch behavior, directly addressing T-221.
Linked Issues check ✅ Passed All coding requirements from issue #89 are met: App auto-selects first project on mount when no project is selected, tasks refetch on project switch, and Vitest tests were added with full coverage.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #89: App.tsx wiring for auto-selection and project switching, test coverage additions, and changelog documentation—no unrelated alterations present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/i-89-t-221-app-tasks-route

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented May 13, 2026

Merging this PR will not alter performance

✅ 7 untouched benchmarks


Comparing feat/i-89-t-221-app-tasks-route (ca81a94) with main (b7e10f3)

Open in CodSpeed

@mpiton mpiton merged commit ded844b into main May 14, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T-221: Wire App.tsx Tasks route → ProjectTabBar + KanbanBoard with reload on project switch

1 participant