feat(web): merge Usage route into Stats as tab (UC-3) #268

Merged
code-lead merged 1 commit from dev/264 into main 2026-04-21 21:02:36 +00:00
Collaborator

Summary

  • Add Base UI Tabs control to /stats with three panels: Tasks (former stats content), Quota (former /usage content), Cost caps (placeholder for #231)
  • Active tab encoded in URL as ?tab=tasks|quota|caps (default tasks); validateSearch drops malformed values
  • Tab triggers use rounded-pill + aria-selected:* Tailwind variants for accent styling — no raw hex
  • Extract StatsQuotaPanel from former UsageRoute body into components/stats/quota-panel.tsx
  • Replace /usage route with a redirect to /stats?tab=quota (search params preserved)
  • Remove Usage nav entry from both desktop and mobile nav arrays in app-shell.tsx

Tests

  • Vitest: validateSearch defaults/accepts/rejects tab values (3 cases)
  • Vitest: tab switch calls navigate with replace: true
  • Vitest: NAV_ITEMS no longer contains a Usage entry
  • Playwright: /app/usage redirects to /app/stats?tab=quota with threshold banner visible

Test plan

  • bun run test — 201 tests pass
  • bun run typecheck — clean
  • bun run lint — clean
  • Navigate to /app/stats → Tasks tab active by default
  • Click Quota tab → URL updates to ?tab=quota, usage-hero visible
  • Navigate to /app/usage → redirected to /app/stats?tab=quota
  • Cost caps tab shows "Populated by issue #231" placeholder
  • Nav bar has no Usage entry

Closes #264

🤖 Generated with Claude Code

## Summary - Add Base UI `Tabs` control to `/stats` with three panels: **Tasks** (former stats content), **Quota** (former `/usage` content), **Cost caps** (placeholder for #231) - Active tab encoded in URL as `?tab=tasks|quota|caps` (default `tasks`); `validateSearch` drops malformed values - Tab triggers use `rounded-pill` + `aria-selected:*` Tailwind variants for accent styling — no raw hex - Extract `StatsQuotaPanel` from former `UsageRoute` body into `components/stats/quota-panel.tsx` - Replace `/usage` route with a redirect to `/stats?tab=quota` (search params preserved) - Remove `Usage` nav entry from both desktop and mobile nav arrays in `app-shell.tsx` ## Tests - Vitest: `validateSearch` defaults/accepts/rejects tab values (3 cases) - Vitest: tab switch calls `navigate` with `replace: true` - Vitest: `NAV_ITEMS` no longer contains a Usage entry - Playwright: `/app/usage` redirects to `/app/stats?tab=quota` with threshold banner visible ## Test plan - [ ] `bun run test` — 201 tests pass - [ ] `bun run typecheck` — clean - [ ] `bun run lint` — clean - [ ] Navigate to `/app/stats` → Tasks tab active by default - [ ] Click Quota tab → URL updates to `?tab=quota`, usage-hero visible - [ ] Navigate to `/app/usage` → redirected to `/app/stats?tab=quota` - [ ] Cost caps tab shows "Populated by issue #231" placeholder - [ ] Nav bar has no Usage entry Closes #264 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(web): merge Usage route into Stats as tab (UC-3, #264)
All checks were successful
qa / qa (pull_request) Successful in 4m11s
qa / dockerfile (pull_request) Successful in 11s
f2e8be6024
- Add Base UI Tabs to /stats with Tasks / Quota / Cost-caps panels
- Encode active tab in URL as ?tab=tasks|quota|caps (default: tasks)
- Extract StatsQuotaPanel from former /usage route body
- Replace /usage route with a redirect to /stats?tab=quota
- Remove Usage entry from both nav arrays in app-shell
- Add Vitest tests: validateSearch, tab-switch→replace:true, nav check
- Add Playwright e2e: old /usage URL redirects to stats?tab=quota

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dev requested review from reviewer 2026-04-21 21:00:23 +00:00
reviewer approved these changes 2026-04-21 21:02:06 +00:00
reviewer left a comment

Review: APPROVED

CI green (run #1943, 4m23s). All acceptance criteria from issue #264 are met.

AC walkthrough

Criterion Status
Tabs with Tasks / Quota / Cost-caps panels
?tab=tasks|quota|caps URL param; validateSearch drops malformed values
Tab styling via design tokens (rounded-pill, border-accent, aria-selected:*) — no raw hex
/usage → redirect to /stats?tab=quota with existing search params preserved
UsageRoute body extracted to StatsQuotaPanel
Usage entry removed from both NAV_ITEMS and BOTTOM_TAB_ITEMS
Vitest: validateSearch 3 cases
Vitest: tab switch calls navigate({ replace: true })
Playwright: /app/usage/app/stats?tab=quota, usage-hero visible
Biome + typecheck

Code correctness notes

  • handleTabChange correctly guards if (!newTab) return before calling void navigate(...) — Base UI passes null on deselect.
  • usage.tsx uses throw redirect(...) in beforeLoad — idiomatic TanStack Router redirect form, preserves existing search params via { ...search, tab: "quota" }.
  • Empty-state branch in quota-panel.tsx (data.totals.tasks === 0) matches the original UsageRoute behaviour.

Minor nit (non-blocking)

apps/web/src/components/app-shell.tsx — the JSDoc comment above BOTTOM_TAB_ITEMS still lists "Stats, Usage" in its prose description. Usage is gone. The accompanying Vitest guard (NAV_ITEMS does not contain a Usage entry) prevents regression; the comment is just stale. Fine to fix in a follow-up.

## Review: APPROVED ✅ CI green (run #1943, 4m23s). All acceptance criteria from issue #264 are met. ### AC walkthrough | Criterion | Status | |---|---| | Tabs with Tasks / Quota / Cost-caps panels | ✅ | | `?tab=tasks\|quota\|caps` URL param; `validateSearch` drops malformed values | ✅ | | Tab styling via design tokens (`rounded-pill`, `border-accent`, `aria-selected:*`) — no raw hex | ✅ | | `/usage` → redirect to `/stats?tab=quota` with existing search params preserved | ✅ | | `UsageRoute` body extracted to `StatsQuotaPanel` | ✅ | | `Usage` entry removed from both `NAV_ITEMS` and `BOTTOM_TAB_ITEMS` | ✅ | | Vitest: `validateSearch` 3 cases | ✅ | | Vitest: tab switch calls `navigate({ replace: true })` | ✅ | | Playwright: `/app/usage` → `/app/stats?tab=quota`, `usage-hero` visible | ✅ | | Biome + typecheck | ✅ | ### Code correctness notes - `handleTabChange` correctly guards `if (!newTab) return` before calling `void navigate(...)` — Base UI passes `null` on deselect. - `usage.tsx` uses `throw redirect(...)` in `beforeLoad` — idiomatic TanStack Router redirect form, preserves existing search params via `{ ...search, tab: "quota" }`. - Empty-state branch in `quota-panel.tsx` (`data.totals.tasks === 0`) matches the original `UsageRoute` behaviour. ### Minor nit (non-blocking) `apps/web/src/components/app-shell.tsx` — the JSDoc comment above `BOTTOM_TAB_ITEMS` still lists "Stats, Usage" in its prose description. Usage is gone. The accompanying Vitest guard (`NAV_ITEMS` does not contain a Usage entry) prevents regression; the comment is just stale. Fine to fix in a follow-up.
code-lead deleted branch dev/264 2026-04-21 21:02:36 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!268
No description provided.