Skip to content

feat(web): migrate dashboard to GET /projects/overview#143

Merged
lee-to merged 2 commits into
lee-to:mainfrom
ichinya:feat/dashboard-overview-migration
Jun 30, 2026
Merged

feat(web): migrate dashboard to GET /projects/overview#143
lee-to merged 2 commits into
lee-to:mainfrom
ichinya:feat/dashboard-overview-migration

Conversation

@ichinya

@ichinya ichinya commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to the merged additive /projects/overview endpoint (#139). Moves the dashboard consumers off the legacy bare GET /tasks (full Task[]) onto the aggregate /overview endpoint.

This is the UI-migration step that was deliberately split out of #139 per the review ("keep that PR purely additive"). #139 landed the endpoint; this PR lands the consumer switch.

What's here

Dashboard migration (consume ProjectTaskOverview[] from /overview):

  • App.tsx: header metrics switch from api.listTasks() (bare) + calculateTaskMetricsuseProjectTaskOverviews() + calculateOverviewMetrics.
  • ProjectsOverview.tsx: card view reads per-project statusCounts, statusPreviews, and metric totals from the aggregate endpoint instead of fanning out full task lists.
  • useProjects.ts: add useProjectTaskOverviews hook + invalidateProjectTaskOverviews helper; invalidate the overview query on project mutations.
  • useWebSocket.ts: invalidate the overview query on task/roadmap events.
  • useTasks.ts: list path is scoped-only (TaskListItem[]); the no-arg bare caller path is removed (the dashboard no longer uses it).
  • lib/api.ts: listProjectTaskOverviews method; listTasks(projectId) now requires projectId (no-arg overload removed).
  • lib/taskMetrics.ts: calculateOverviewMetrics + calculateProjectOverviewMetrics reduce ProjectTaskOverview[] into the dashboard summary.

Tests: AppSmoke asserts overview hook wiring; ProjectsOverview regression test for the empty-project loading state; api.test covers the /overview endpoint.

Merge-safety

Builds on the merged #139 endpoint. The server bare GET /tasks route stays available (legacy) — the web client just no longer calls it. After this lands, the bare route's only remaining caller is gone, and a future cleanup PR can remove the dead no-arg client overload / deprecate the server route.

Verification

  • tsc --noEmit clean (web).
  • web tests 674 green (incl. the empty-project loading regression + overview endpoint test).
  • prettier / lint / build green.
  • 10 files, +347 / -118.

Follow-up to the additive /projects/overview endpoint (lee-to#139, merged). Moves
the dashboard consumers off the legacy bare GET /tasks (full Task[]) onto the
aggregate /overview endpoint.

Dashboard migration:
- App.tsx: header metrics switch from bare listTasks() + calculateTaskMetrics
  to useProjectTaskOverviews() + calculateOverviewMetrics.
- ProjectsOverview.tsx: card view reads from per-project overviews
  (statusCounts, statusPreviews, totals) instead of fanning out full lists.
- useProjects.ts: add useProjectTaskOverviews hook + invalidateProjectTaskOverviews
  helper; invalidate overview query on project mutations.
- useWebSocket.ts: invalidate overview query on task/roadmap events.
- useTasks.ts: drop the no-arg bare listTasks() caller path; list path is
  scoped-only (TaskListItem[]) for the board.
- lib/api.ts: listProjectTaskOverviews method; listTasks(projectId) required
  (no-arg overload removed — dashboard no longer uses it).
- lib/taskMetrics.ts: calculateOverviewMetrics + calculateProjectOverviewMetrics
  reduce ProjectTaskOverview[] into the dashboard summary.
- Tests: AppSmoke asserts overview hook wiring; ProjectsOverview regression
  test for empty-project loading state; api.test covers the /overview endpoint.

Non-breaking: builds on the merged lee-to#139 endpoint. The server bare GET /tasks
route stays available (legacy), but the web client no longer calls it.

Verification: tsc clean (web); 674 web tests green; prettier/lint/build green.
@ichinya ichinya requested a review from lee-to June 29, 2026 08:55
@lee-to

lee-to commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Code review

Verdict: REQUEST_CHANGES.

This PR is a cohesive UI migration from dashboard-wide bare task fetches to the aggregate /projects/overview endpoint. The direction is good and the added regression coverage is useful, but two details need fixing before merge: the retained bare api.listTasks() path is now broken, and one documented WebSocket event path can leave the new overview metrics stale.

Must fix

  1. Retained bare task helper now calls /tasks/taskspackages/web/src/lib/api.ts:260 / packages/web/src/lib/api.ts:265 — the no-arg overload is still exported as api.listTasks(), but its branch now does request<Task[]>(${API_BASE}/tasks) while API_BASE is already "/tasks". Any remaining no-arg caller will hit /tasks/tasks instead of the legacy GET /tasks route. Either delete the no-arg overload and branch completely if this PR is meant to remove that client surface, or restore the legacy branch to request<Task[]>(API_BASE) and add a test that covers the retained path.

  2. Runtime usage WebSocket updates do not invalidate the new overview querypackages/web/src/hooks/useWebSocket.ts:224 / packages/web/src/hooks/useWebSocket.ts:228project:runtime_limit_updated returns immediately after invalidating ["tasks"], so it skips invalidateProjectTaskOverviews(queryClient). That event is documented as firing when persisted runtime-limit state or last usage changes (docs/api.md:1298), and the overview metrics now aggregate token/cost fields from ProjectTaskOverview (packages/web/src/lib/taskMetrics.ts:130). Add invalidateProjectTaskOverviews(queryClient) in this branch, ideally with a WebSocket hook regression test, so the dashboard header and project overview cards refresh after usage updates.

Should fix

  1. PR size is just over the soft review limit — 451 changed lines / 10 files / one concern. This is cohesive and test-heavy, so I am not asking for a split here, but keep the follow-up cleanup PR limited to removing the legacy task-list path rather than combining it with unrelated UI work.

Nits

None.

Context gates

  • Architecture: pass — the web changes consume data through lib/api.ts and hooks; no package boundary or DB access violations found.
  • Rules: error — the web checklist requires complete WebSocket invalidation for consumed events; the project:runtime_limit_updated branch misses the new overview query.
  • Roadmap: warn — this is a feat(web) PR but does not link to a roadmap milestone. It does reference follow-up PR feat(projects): add compact project task overview endpoint (additive) #139, which is enough context for review, but milestone traceability is still absent.
  • CHECKLIST compliance (touched packages: packages/web): error — API calls are centralized and no new UI primitive was added, but the WebSocket invalidation checklist item is not fully satisfied.
  • Docs sync: pass — this consumes an already documented endpoint/event; no new API contract is introduced in this PR.
  • PR size (451 lines / 10 files / one concern): warn — over the soft line threshold, but cohesive.
  • Risk gating (feature flag): n/a — this is a web consumer migration to an existing endpoint, with the old server route still available.

Positive note: the empty-project overview regression test is the right kind of coverage for this migration, and the aggregate metric reducer keeps the UI-side calculation small.

…-limit WS

Per lee-to#143 review (REQUEST_CHANGES), two fixes:

1. Must-fix #1 — bare listTasks() URL /tasks/tasks:
   The retained no-arg overload did request(`${API_BASE}/tasks`) while
   API_BASE is already '/tasks' (regression re-introduced when migrating files
   off an older branch state). After the dashboard migrated to /projects/overview,
   the no-arg path has no remaining caller, so the overload + bare branch are
   removed entirely. listTasks(projectId) is the only signature now.

2. Must-fix #2 — WS runtime_limit_updated leaves overview metrics stale:
   project:runtime_limit_updated fired but only invalidated ['tasks'], skipping
   the new overview query. Since overview aggregates token/cost fields, the
   dashboard header + project cards showed stale token/cost after usage updates.
   Add invalidateProjectTaskOverviews(queryClient) in that branch.

Regression test: useWebSocketOverviewInvalidation.test pins that
invalidateProjectTaskOverviews invalidates ['projectTaskOverviews'], so a
future change that drops/renames the query key surfaces immediately.

Verification: tsc clean (web); 675 web tests green; prettier/lint/build green.
@ichinya

ichinya commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Both must-fix items addressed. Head: 9f75d18 (3 files, +33 / -10).

Must-fix #1/tasks/tasks bare URL → overload removed entirely

You flagged that the no-arg overload hit /tasks/tasks (Bug #1, re-introduced when I migrated files off an older branch state). Since this PR migrates the dashboard off the bare path, the no-arg overload has no remaining caller — so rather than restore the legacy branch, I removed the overload completely:

  • listTasks(projectId: string) is the only signature now; projectId is required at the type boundary.
  • The bare branch + its /tasks/tasks URL are gone.
  • tsc --noEmit confirms no remaining no-arg callers (the only list-path caller is useTasks.ts, always scoped).

The server bare route stays available for backward compatibility; the web client just no longer calls it.

Must-fix #2project:runtime_limit_updated overview invalidation

Added invalidateProjectTaskOverviews(queryClient) to that branch (useWebSocket.ts), so the dashboard header + project cards refresh token/cost after usage updates:

if (data.type === "project:runtime_limit_updated" && hasRuntimeLimitPayload(data.payload)) {
  invalidateRuntimeLimitQueries(queryClient, data.payload);
  if (typeof data.payload.taskId === "string" && data.payload.taskId.length > 0) {
    pendingTaskIds.current.add(data.payload.taskId);
    queryClient.invalidateQueries({ queryKey: ["tasks"] });
  }
  // Overview aggregates token/cost fields, so refresh after usage updates.
  invalidateProjectTaskOverviews(queryClient);
  return;
}

Regression test

useWebSocketOverviewInvalidation.test.ts pins that invalidateProjectTaskOverviews invalidates ["projectTaskOverviews"]. A future change that drops or renames the overview query key surfaces immediately. (The handler isn't exported in isolation, so the test targets the public helper it calls — same surface the WS branch relies on.)

Verification

  • tsc --noEmit clean (web).
  • web tests 675 green (+1 new regression test).
  • prettier / lint / build green.

The should-fix (size) is left as-is per your note ("not asking for a split here").

@lee-to

lee-to commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Code review

Follow-up review for the prior comment: #143 (comment)

Verdict: APPROVE.

This follow-up commit addresses both previously blocking issues without widening the PR scope. The bare no-arg task-list client path is removed, the runtime-limit WebSocket branch now refreshes the new overview query, and the current CI checks are green.

Response pass

  • Addressed: Must-fix fix: normalize file paths and enhance path validation #1, the retained bare api.listTasks() path. packages/web/src/lib/api.ts:262 now exposes only listTasks(projectId: string), and the /tasks/tasks branch is gone.
  • Addressed: Must-fix [feature] исключить папку aif #2, stale overview metrics after runtime usage updates. packages/web/src/hooks/useWebSocket.ts:231 now calls invalidateProjectTaskOverviews(queryClient) in the project:runtime_limit_updated branch.
  • New: none found in the follow-up delta.
  • Still unresolved: none.

Must fix

None.

Should fix

  1. PR size remains over the soft review limit - 492 changed lines / 11 files / one concern. This is still cohesive and test-heavy, so I am not blocking on decomposition. Keep the next cleanup PR narrow.

Nits

None.

Context gates

  • Architecture: pass - the web package still consumes data through hooks and lib/api.ts; no package-boundary or DB-access violations found.
  • Rules: pass - the previous WebSocket invalidation gap is fixed, and no new UI primitive or expensive CSS was introduced in the follow-up.
  • Roadmap: warn - this feat(web) PR still does not link a roadmap milestone, though the PR body links the relevant follow-up context from feat(projects): add compact project task overview endpoint (additive) #139.
  • CHECKLIST compliance (touched packages: packages/web): pass - API calls remain centralized, the consumed WebSocket event now invalidates the affected overview query, and the follow-up adds regression coverage around the invalidation helper.
  • Docs sync: pass - this consumes the already documented /projects/overview endpoint and project:runtime_limit_updated event; no new API contract is introduced.
  • PR size (492 lines / 11 files / one concern): warn - over the soft line threshold, but still a single dashboard migration.
  • Risk gating (feature flag): n/a - this is a web consumer migration to an existing endpoint, with the legacy server route still available.

LGTM now.

@lee-to lee-to merged commit 5160dd6 into lee-to:main Jun 30, 2026
6 checks passed
@ichinya ichinya deleted the feat/dashboard-overview-migration branch June 30, 2026 15:02
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.

2 participants