fix(03): revise plans based on checker feedback

This commit is contained in:
2026-03-11 22:27:07 +01:00
parent 0d40043615
commit 72fb62635d
6 changed files with 202 additions and 34 deletions

View File

@@ -54,13 +54,14 @@ Plans:
**Depends on**: Phase 2
**Requirements**: IXTN-01, IXTN-02, IXTN-03, IXTN-05, STATE-01, STATE-02, STATE-03
**Success Criteria** (what must be TRUE):
1. Submitting a login, register, budget create, or budget edit form shows a spinner on the button — the user knows the request is in flight
1. Submitting a login, register, budget create, or budget edit form shows a spinner on the button — the user knows the request is in flight (Note: Budget Edit form does not exist yet; spinner covers 3 existing forms — Login, Register, Budget Create)
2. Hovering over an inline-editable row reveals a pencil icon, making the edit affordance discoverable before clicking
3. After saving an inline edit, the row briefly flashes a confirmation color, confirming the save completed
4. Attempting to delete a category triggers a confirmation dialog before the deletion executes
5. A user with no budgets sees a designed empty state with a clear CTA on the dashboard; a user with no categories sees the same on the categories page; loading skeletons use pastel-tinted backgrounds matching their section
**Plans:** 3 plans
**Plans:** 4 plans
Plans:
- [ ] 03-00-PLAN.md — Wave 0 test stub files (BudgetSetup, CategoriesPage, DashboardPage, BillsTracker)
- [ ] 03-01-PLAN.md — InlineEditCell pencil icon + save callbacks, form submit spinners
- [ ] 03-02-PLAN.md — Delete confirmation dialog, empty states for Dashboard and Categories
- [ ] 03-03-PLAN.md — Row flash wiring in trackers, pastel-tinted loading skeletons
@@ -71,18 +72,18 @@ Plans:
**Requirements**: IXTN-04, FIX-01
**Success Criteria** (what must be TRUE):
1. All chart fills (donut slices, bar segments) use the semantic category colors from `lib/palette.ts` — "Bills" is the same color in the donut chart as it is in the FinancialOverview table
2. Chart tooltips display values formatted with the budget's currency (e.g., "1,234.56" not "1234.56")
2. Chart tooltips display values formatted with the budget's currency (e.g., "1,234.56" not "1234.56")
3. The `formatCurrency` function uses the user's locale preference from their settings instead of the hardcoded `de-DE` — an English-locale user sees their numbers formatted correctly
**Plans**: TBD
## Progress
**Execution Order:**
Phases execute in numeric order: 1 2 3 4
Phases execute in numeric order: 1 -> 2 -> 3 -> 4
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Design Token Foundation | 2/2 | Complete | 2026-03-11 |
| 2. Layout and Brand Identity | 0/2 | In progress | - |
| 3. Interaction Quality and Completeness | 0/3 | Not started | - |
| 3. Interaction Quality and Completeness | 0/4 | Not started | - |
| 4. Chart Polish and Bug Fixes | 0/TBD | Not started | - |

View File

@@ -0,0 +1,161 @@
---
phase: 03-interaction-quality-and-completeness
plan: 00
type: execute
wave: 0
depends_on: []
files_modified:
- frontend/src/components/BudgetSetup.test.tsx
- frontend/src/pages/CategoriesPage.test.tsx
- frontend/src/pages/DashboardPage.test.tsx
- frontend/src/components/BillsTracker.test.tsx
autonomous: true
requirements: [IXTN-01, IXTN-05, STATE-01, STATE-02, STATE-03]
must_haves:
truths:
- "All 4 test stub files exist and can be loaded by vitest"
- "Each stub contains at least one pending/skipped test describing the target behavior"
artifacts:
- path: "frontend/src/components/BudgetSetup.test.tsx"
provides: "Test stub for budget form spinner (IXTN-01)"
contains: "BudgetSetup"
- path: "frontend/src/pages/CategoriesPage.test.tsx"
provides: "Test stubs for delete confirmation (IXTN-05) and empty state (STATE-02)"
contains: "CategoriesPage"
- path: "frontend/src/pages/DashboardPage.test.tsx"
provides: "Test stub for dashboard empty state (STATE-01)"
contains: "DashboardPage"
- path: "frontend/src/components/BillsTracker.test.tsx"
provides: "Test stub for tinted skeleton (STATE-03)"
contains: "BillsTracker"
key_links: []
---
<objective>
Create Wave 0 test stub files for the 4 components that lack test coverage. Each stub imports the component, renders it with minimal props, and contains skipped (it.skip) test cases describing the behaviors that Plans 01-03 will implement.
Purpose: Satisfy Nyquist compliance — every task in Plans 01-03 must have a runnable test file in its verify command. Wave 0 ensures those files exist before execution begins.
Output: 4 new test files with pending test stubs.
</objective>
<execution_context>
@/home/jean-luc-makiola/.claude/get-shit-done/workflows/execute-plan.md
@/home/jean-luc-makiola/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/03-interaction-quality-and-completeness/03-CONTEXT.md
@.planning/phases/03-interaction-quality-and-completeness/03-VALIDATION.md
<interfaces>
From frontend/src/components/BudgetSetup.tsx:
```typescript
interface Props {
existingBudgets: Budget[]
onCreated: () => void
onCancel: () => void
}
export function BudgetSetup({ existingBudgets, onCreated, onCancel }: Props)
```
From frontend/src/pages/CategoriesPage.tsx:
```typescript
// Default export, no props — uses hooks internally (useTranslation, etc.)
export default function CategoriesPage()
```
From frontend/src/pages/DashboardPage.tsx:
```typescript
// Default export, no props — uses hooks internally
export default function DashboardPage()
```
From frontend/src/components/BillsTracker.tsx:
```typescript
interface Props {
budget: BudgetDetail
onUpdate: (itemId: string, data: { actual_amount?: number; budgeted_amount?: number }) => Promise<void>
}
export function BillsTracker({ budget, onUpdate }: Props)
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Create 4 test stub files with pending test cases</name>
<files>frontend/src/components/BudgetSetup.test.tsx, frontend/src/pages/CategoriesPage.test.tsx, frontend/src/pages/DashboardPage.test.tsx, frontend/src/components/BillsTracker.test.tsx</files>
<action>
Create each test file with the following structure: import vitest globals (describe, it, expect), import @testing-library/react (render, screen), import the component, wrap tests in a describe block, and add `it.skip` stubs for the behaviors that will be implemented in Plans 01-03.
**BudgetSetup.test.tsx:**
- Import `BudgetSetup` from `@/components/BudgetSetup`
- Mock `@/lib/api` (budgets.create) and `react-i18next` (useTranslation returns t = key passthrough)
- `it.skip('shows spinner in create button when saving')` — IXTN-01
- `it.skip('disables create button when saving')` — IXTN-01
- Add one basic `it('renders without crashing')` test that renders BudgetSetup with minimal props: `existingBudgets: [], onCreated: vi.fn(), onCancel: vi.fn()` — this validates the stub file works.
**CategoriesPage.test.tsx:**
- Import `CategoriesPage` from `@/pages/CategoriesPage`
- Mock `@/lib/api` (categories.list returns [], categories.delete) and `react-i18next`
- `it.skip('opens confirmation dialog when delete button clicked')` — IXTN-05
- `it.skip('executes delete on confirm and shows spinner')` — IXTN-05
- `it.skip('shows error inline when delete fails')` — IXTN-05
- `it.skip('shows empty state when no categories exist')` — STATE-02
- Add one basic `it('renders without crashing')` that renders CategoriesPage inside a MemoryRouter (it uses routing).
**DashboardPage.test.tsx:**
- Import `DashboardPage` from `@/pages/DashboardPage`
- Mock `@/lib/api` (budgets.list returns []) and `react-i18next`
- `it.skip('shows empty state with CTA when no budgets')` — STATE-01
- `it.skip('shows loading skeleton while fetching')` — STATE-03
- Add one basic `it('renders without crashing')` that renders DashboardPage inside a MemoryRouter.
**BillsTracker.test.tsx:**
- Import `BillsTracker` from `@/components/BillsTracker`
- Mock `react-i18next`
- `it.skip('shows tinted skeleton when no bill items')` — STATE-03
- `it.skip('flashes row green on successful inline save')` — IXTN-03
- `it.skip('flashes row red on failed inline save')` — IXTN-03
- Add one basic `it('renders without crashing')` that renders BillsTracker with a minimal budget fixture (empty items array) and `onUpdate: vi.fn()`.
For components needing MemoryRouter (page-level components with routing), wrap in `<MemoryRouter>` from `react-router-dom`.
Pattern for all mocks:
```typescript
vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: (key: string) => key, i18n: { language: 'en' } }),
}))
```
Each file MUST have at least one non-skipped test that passes to confirm the stub is valid.
</action>
<verify>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run src/components/BudgetSetup.test.tsx src/pages/CategoriesPage.test.tsx src/pages/DashboardPage.test.tsx src/components/BillsTracker.test.tsx</automated>
</verify>
<done>All 4 test stub files exist, each has at least one passing basic test and multiple it.skip stubs describing target behaviors. Vitest runs all 4 files without errors.</done>
</task>
</tasks>
<verification>
- `cd frontend && bun vitest run` — full test suite passes (new stubs + existing tests)
- All 4 files importable by vitest
- Each file has at least one non-skipped passing test
</verification>
<success_criteria>
- BudgetSetup.test.tsx, CategoriesPage.test.tsx, DashboardPage.test.tsx, BillsTracker.test.tsx all exist
- Each file has skip-marked stubs for the behaviors Plans 01-03 will implement
- Each file has at least one passing smoke test
- Full test suite remains green
</success_criteria>
<output>
After completion, create `.planning/phases/03-interaction-quality-and-completeness/03-00-SUMMARY.md`
</output>

View File

@@ -3,7 +3,7 @@ phase: 03-interaction-quality-and-completeness
plan: 01
type: execute
wave: 1
depends_on: []
depends_on: ["03-00"]
files_modified:
- frontend/src/components/InlineEditCell.tsx
- frontend/src/components/InlineEditCell.test.tsx
@@ -49,10 +49,12 @@ must_haves:
---
<objective>
Add pencil icon hover affordance and save/error callbacks to InlineEditCell, plus loading spinners to all four form submit buttons.
Add pencil icon hover affordance and save/error callbacks to InlineEditCell, plus loading spinners to three form submit buttons (Login, Register, Budget Create).
Purpose: Make inline editing discoverable (pencil icon on hover) and prepare the callback interface for row-level flash feedback in downstream plans. Make form submissions feel responsive with spinner indicators.
Output: Enhanced InlineEditCell with pencil + callbacks, spinner-enabled Login/Register/BudgetSetup forms.
Note: CONTEXT.md specifies spinners on "all four forms: Login, Register, Budget Create, Budget Edit." No Budget Edit form component exists in the codebase — only BudgetSetup (create-only). Budget Edit spinner is deferred until that form is built. This plan covers the 3 existing forms.
</objective>
<execution_context>
@@ -151,10 +153,12 @@ export function formatCurrency(value: number, currency: string): string
- Add `className="min-w-[120px]"` to Button.
- Replace button text with: `{saving ? <Spinner /> : t('budget.create')}`
Note: CONTEXT.md mentions "Budget Edit" as a fourth form, but no Budget Edit component exists in the codebase (BudgetSetup is create-only). Spinner for Budget Edit is deferred until that form is created.
Do NOT modify any other logic in these files — only the Button content and className.
</action>
<verify>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run src/pages/LoginPage.test.tsx src/pages/RegisterPage.test.tsx && bun run build</automated>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run src/pages/LoginPage.test.tsx src/pages/RegisterPage.test.tsx src/components/BudgetSetup.test.tsx && bun run build</automated>
</verify>
<done>All three form submit buttons show Spinner component when loading/saving state is true, buttons are disabled during loading, min-width prevents layout shift. Build passes with zero errors.</done>
</task>
@@ -171,6 +175,7 @@ export function formatCurrency(value: number, currency: string): string
- Pencil icon renders in InlineEditCell display mode (opacity-0, visible on hover via CSS)
- onSaveSuccess fires after successful save; onSaveError fires and reverts value on failure
- Login, Register, BudgetSetup buttons show Spinner when loading, disabled to prevent double-submit
- Budget Edit spinner is explicitly deferred (no form component exists yet)
- All existing tests continue to pass; new tests cover the added behaviors
</success_criteria>

View File

@@ -3,7 +3,7 @@ phase: 03-interaction-quality-and-completeness
plan: 02
type: execute
wave: 1
depends_on: []
depends_on: ["03-00"]
files_modified:
- frontend/src/pages/CategoriesPage.tsx
- frontend/src/pages/DashboardPage.tsx
@@ -105,6 +105,9 @@ export { Spinner }
<name>Task 1: Create shared EmptyState component and wire into Dashboard and Categories pages</name>
<files>frontend/src/components/EmptyState.tsx, frontend/src/pages/DashboardPage.tsx, frontend/src/pages/CategoriesPage.tsx</files>
<action>
**Step 0 — Check shadcn registry first (per project skill rules):**
Run `bunx --bun shadcn@latest search -q empty` in the frontend directory. If shadcn provides an EmptyState or similar component, use it instead of creating a custom one. If nothing relevant is found (expected), proceed with custom component below.
**Create `frontend/src/components/EmptyState.tsx`:**
```typescript
interface EmptyStateProps {
@@ -128,7 +131,7 @@ export { Spinner }
- Guard the grouped cards render with `{grouped.length > 0 && grouped.map(...)}` so both empty state and cards don't show simultaneously.
</action>
<verify>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun run build</automated>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run src/pages/DashboardPage.test.tsx src/pages/CategoriesPage.test.tsx && bun run build</automated>
</verify>
<done>EmptyState component exists and is used in DashboardPage (no-budgets case) and CategoriesPage (no-categories case). CategoriesPage has loading state to prevent empty-state flash. Build passes.</done>
</task>
@@ -190,7 +193,7 @@ export { Spinner }
**CRITICAL:** The ON DELETE RESTRICT constraint means deleting a category with budget items returns 500. The catch block handles this — the error message displays inline in the dialog. The dialog does NOT auto-close on error, letting the user read the message and dismiss manually.
</action>
<verify>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun run build</automated>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run src/pages/CategoriesPage.test.tsx && bun run build</automated>
</verify>
<done>Delete button opens confirmation dialog. Confirm executes delete with spinner. Error from ON DELETE RESTRICT shows inline. Cancel closes dialog. Build passes with zero errors.</done>
</task>

View File

@@ -3,7 +3,7 @@ phase: 03-interaction-quality-and-completeness
plan: 03
type: execute
wave: 2
depends_on: ["03-01"]
depends_on: ["03-01", "03-00"]
files_modified:
- frontend/src/components/BillsTracker.tsx
- frontend/src/components/VariableExpenses.tsx
@@ -171,7 +171,7 @@ From frontend/src/components/ui/skeleton.tsx:
**Do NOT modify** the totals row or the CardHeader — only add flash state and wire callbacks.
</action>
<verify>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun run build</automated>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run src/components/BillsTracker.test.tsx && bun run build</automated>
</verify>
<done>All three tracker components have flash state, triggerFlash helper, inline style on data rows, and onSaveSuccess/onSaveError wired to InlineEditCell. Build passes.</done>
</task>
@@ -234,7 +234,7 @@ From frontend/src/components/ui/skeleton.tsx:
**Note:** These skeletons show when a budget exists but has no items of that type — they serve as visual placeholders indicating the section exists. This is distinct from the DashboardPage loading skeleton (which shows before any data loads).
</action>
<verify>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run && bun run build</automated>
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run src/components/BillsTracker.test.tsx src/pages/DashboardPage.test.tsx && bun run build</automated>
</verify>
<done>Dashboard loading skeleton uses palette-tinted backgrounds per section. Each tracker shows tinted skeletons when no items of its type exist. All tests pass, build succeeds.</done>
</task>

View File

@@ -2,7 +2,7 @@
phase: 3
slug: interaction-quality-and-completeness
status: draft
nyquist_compliant: false
nyquist_compliant: true
wave_0_complete: false
created: 2026-03-11
---
@@ -38,24 +38,22 @@ created: 2026-03-11
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|-----------|-------------------|-------------|--------|
| 03-00-01 | 00 | 0 | IXTN-01 | unit stub | `bun vitest run src/components/BudgetSetup.test.tsx` | ❌ W0 | pending |
| 03-00-02 | 00 | 0 | IXTN-05, STATE-02 | unit stub | `bun vitest run src/pages/CategoriesPage.test.tsx` | ❌ W0 | ⬜ pending |
| 03-00-03 | 00 | 0 | STATE-01 | unit stub | `bun vitest run src/pages/DashboardPage.test.tsx` | ❌ W0 | pending |
| 03-00-04 | 00 | 0 | STATE-03 | unit stub | `bun vitest run src/components/BillsTracker.test.tsx` | W0 | pending |
| 03-01-01 | 01 | 1 | IXTN-02, IXTN-03 | unit | `bun vitest run src/components/InlineEditCell.test.tsx` | ✅ extend | ⬜ pending |
| 03-01-02 | 01 | 1 | IXTN-01 | unit | `bun vitest run src/pages/LoginPage.test.tsx src/pages/RegisterPage.test.tsx` | ✅ extend | ⬜ pending |
| 03-01-03 | 01 | 1 | IXTN-01 | unit | `bun vitest run src/components/BudgetSetup.test.tsx` | W0 | pending |
| 03-02-01 | 02 | 1 | IXTN-05 | unit | `bun vitest run src/pages/CategoriesPage.test.tsx` | ❌ W0 | ⬜ pending |
| 03-02-02 | 02 | 1 | STATE-01 | unit | `bun vitest run src/pages/DashboardPage.test.tsx` | ❌ W0 | ⬜ pending |
| 03-02-03 | 02 | 1 | STATE-02 | unit | `bun vitest run src/pages/CategoriesPage.test.tsx` | ❌ W0 | ⬜ pending |
| 03-02-04 | 02 | 1 | STATE-03 | unit | `bun vitest run src/components/BillsTracker.test.tsx` | ❌ W0 | ⬜ pending |
| 03-00-01 | 00 | 0 | IXTN-01, IXTN-05, STATE-01, STATE-02, STATE-03 | unit stub | `bun vitest run src/components/BudgetSetup.test.tsx src/pages/CategoriesPage.test.tsx src/pages/DashboardPage.test.tsx src/components/BillsTracker.test.tsx` | Created by 03-00 | pending |
| 03-01-01 | 01 | 1 | IXTN-02, IXTN-03 | unit | `bun vitest run src/components/InlineEditCell.test.tsx` | extend | pending |
| 03-01-02 | 01 | 1 | IXTN-01 | unit | `bun vitest run src/pages/LoginPage.test.tsx src/pages/RegisterPage.test.tsx src/components/BudgetSetup.test.tsx` | extend + W0 | pending |
| 03-02-01 | 02 | 1 | STATE-01, STATE-02 | unit | `bun vitest run src/pages/DashboardPage.test.tsx src/pages/CategoriesPage.test.tsx` | W0 | pending |
| 03-02-02 | 02 | 1 | IXTN-05 | unit | `bun vitest run src/pages/CategoriesPage.test.tsx` | W0 | pending |
| 03-03-01 | 03 | 2 | IXTN-03 | unit | `bun vitest run src/components/BillsTracker.test.tsx` | W0 | pending |
| 03-03-02 | 03 | 2 | STATE-03 | unit | `bun vitest run src/components/BillsTracker.test.tsx src/pages/DashboardPage.test.tsx` | W0 | pending |
*Status: pending · ✅ green · ❌ red · ⚠️ flaky*
*Status: pending / green / red / flaky*
---
## Wave 0 Requirements
Plan 03-00-PLAN.md creates all 4 stub files:
- [ ] `frontend/src/components/BudgetSetup.test.tsx` — stubs for IXTN-01 (budget form spinner)
- [ ] `frontend/src/pages/CategoriesPage.test.tsx` — stubs for IXTN-05 (delete confirmation), STATE-02 (empty state)
- [ ] `frontend/src/pages/DashboardPage.test.tsx` — stubs for STATE-01 (dashboard empty state)
@@ -82,11 +80,11 @@ created: 2026-03-11
## Validation Sign-Off
- [ ] All tasks have `<automated>` verify or Wave 0 dependencies
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
- [ ] Wave 0 covers all MISSING references
- [ ] No watch-mode flags
- [ ] Feedback latency < 20s
- [ ] `nyquist_compliant: true` set in frontmatter
- [x] All tasks have `<automated>` verify or Wave 0 dependencies
- [x] Sampling continuity: no 3 consecutive tasks without automated verify
- [x] Wave 0 covers all MISSING references (03-00-PLAN.md)
- [x] No watch-mode flags
- [x] Feedback latency < 20s
- [ ] `wave_0_complete: true` set after 03-00 executes
**Approval:** pending
**Approval:** pending execution