336 lines
17 KiB
Markdown
336 lines
17 KiB
Markdown
---
|
|
phase: 01-design-token-foundation
|
|
plan: 02
|
|
type: execute
|
|
wave: 2
|
|
depends_on: ["01-01"]
|
|
files_modified:
|
|
- frontend/src/components/BillsTracker.tsx
|
|
- frontend/src/components/VariableExpenses.tsx
|
|
- frontend/src/components/DebtTracker.tsx
|
|
- frontend/src/components/AvailableBalance.tsx
|
|
- frontend/src/components/ExpenseBreakdown.tsx
|
|
- frontend/src/components/FinancialOverview.tsx
|
|
- frontend/src/components/InlineEditCell.tsx
|
|
- frontend/src/components/InlineEditCell.test.tsx
|
|
autonomous: false
|
|
requirements:
|
|
- DSGN-03
|
|
- DSGN-04
|
|
- DSGN-05
|
|
- FIX-02
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Card headers on BillsTracker, VariableExpenses, DebtTracker, AvailableBalance, ExpenseBreakdown use palette-driven gradients — no hardcoded Tailwind color classes remain"
|
|
- "FinancialOverview header uses a multi-pastel gradient from overviewHeaderGradient()"
|
|
- "FinancialOverview and AvailableBalance have hero typography (text-2xl titles, p-6 padding) while other cards have regular typography (text-base titles, px-4 py-3 padding)"
|
|
- "AvailableBalance center amount is text-3xl font-bold with green/red color coding"
|
|
- "Actual amount columns show green for positive income, amber for over-budget expenses, red for negative available — budget column stays neutral"
|
|
- "InlineEditCell.tsx is a shared component replacing three duplicate InlineEditRow functions"
|
|
- "FinancialOverview rows are tinted with their category's light shade"
|
|
artifacts:
|
|
- path: "frontend/src/components/InlineEditCell.tsx"
|
|
provides: "Shared inline edit cell component"
|
|
exports: ["InlineEditCell"]
|
|
min_lines: 25
|
|
- path: "frontend/src/components/InlineEditCell.test.tsx"
|
|
provides: "Unit tests for InlineEditCell"
|
|
min_lines: 30
|
|
- path: "frontend/src/components/BillsTracker.tsx"
|
|
provides: "Bills section with palette gradient header and InlineEditCell"
|
|
contains: "headerGradient"
|
|
- path: "frontend/src/components/FinancialOverview.tsx"
|
|
provides: "Hero overview with multi-gradient header and category-tinted rows"
|
|
contains: "overviewHeaderGradient"
|
|
key_links:
|
|
- from: "frontend/src/components/BillsTracker.tsx"
|
|
to: "frontend/src/lib/palette.ts"
|
|
via: "import headerGradient and amountColorClass"
|
|
pattern: "import.*palette"
|
|
- from: "frontend/src/components/InlineEditCell.tsx"
|
|
to: "frontend/src/components/BillsTracker.tsx"
|
|
via: "BillsTracker imports and uses InlineEditCell instead of local InlineEditRow"
|
|
pattern: "InlineEditCell"
|
|
- from: "frontend/src/components/AvailableBalance.tsx"
|
|
to: "frontend/src/lib/palette.ts"
|
|
via: "Chart Cell fill uses palette[type].base instead of PASTEL_COLORS array"
|
|
pattern: "palette.*base"
|
|
---
|
|
|
|
<objective>
|
|
Wire the palette.ts module into all dashboard components — replace hardcoded gradients, apply hero typography, add amount coloring, and extract InlineEditCell.
|
|
|
|
Purpose: This transforms the visual appearance of the dashboard from generic neutrals to a cohesive pastel-themed experience. Every component now reads from the token system established in Plan 01.
|
|
Output: All 6 dashboard components updated, InlineEditCell extracted and tested.
|
|
</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/01-design-token-foundation/01-CONTEXT.md
|
|
@.planning/phases/01-design-token-foundation/01-RESEARCH.md
|
|
@.planning/phases/01-design-token-foundation/01-01-SUMMARY.md
|
|
|
|
@frontend/src/lib/palette.ts
|
|
@frontend/src/index.css
|
|
@frontend/src/components/BillsTracker.tsx
|
|
@frontend/src/components/VariableExpenses.tsx
|
|
@frontend/src/components/DebtTracker.tsx
|
|
@frontend/src/components/AvailableBalance.tsx
|
|
@frontend/src/components/ExpenseBreakdown.tsx
|
|
@frontend/src/components/FinancialOverview.tsx
|
|
|
|
<interfaces>
|
|
<!-- Key types and contracts from Plan 01 that this plan uses directly. -->
|
|
|
|
From src/lib/palette.ts (created in Plan 01):
|
|
```typescript
|
|
export type CategoryType = 'income' | 'bill' | 'variable_expense' | 'debt' | 'saving' | 'investment' | 'carryover'
|
|
|
|
export interface CategoryShades {
|
|
light: string // oklch — row backgrounds, tinted surfaces
|
|
medium: string // oklch — header gradient to-color, badges
|
|
base: string // oklch — chart fills, text accents
|
|
}
|
|
|
|
export const palette: Record<CategoryType, CategoryShades>
|
|
|
|
export function headerGradient(type: CategoryType): React.CSSProperties
|
|
export function overviewHeaderGradient(): React.CSSProperties
|
|
export function amountColorClass(opts: {
|
|
type: CategoryType
|
|
actual: number
|
|
budgeted: number
|
|
isIncome?: boolean
|
|
isAvailable?: boolean
|
|
}): string
|
|
```
|
|
|
|
From src/lib/utils.ts:
|
|
```typescript
|
|
export function cn(...inputs: ClassValue[]): string
|
|
```
|
|
|
|
From src/lib/format.ts:
|
|
```typescript
|
|
export function formatCurrency(value: number, currency: string): string
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Extract InlineEditCell and wire palette into all components</name>
|
|
<files>
|
|
frontend/src/components/InlineEditCell.tsx,
|
|
frontend/src/components/InlineEditCell.test.tsx,
|
|
frontend/src/components/BillsTracker.tsx,
|
|
frontend/src/components/VariableExpenses.tsx,
|
|
frontend/src/components/DebtTracker.tsx,
|
|
frontend/src/components/AvailableBalance.tsx,
|
|
frontend/src/components/ExpenseBreakdown.tsx,
|
|
frontend/src/components/FinancialOverview.tsx
|
|
</files>
|
|
<behavior>
|
|
- InlineEditCell renders formatted currency in display mode
|
|
- InlineEditCell shows an Input on click (enters edit mode)
|
|
- InlineEditCell calls onSave with parsed number on blur/Enter
|
|
- InlineEditCell does NOT call onSave if value unchanged or NaN
|
|
</behavior>
|
|
<action>
|
|
**Part A — Write InlineEditCell tests (RED):**
|
|
|
|
Create `frontend/src/components/InlineEditCell.test.tsx` with tests:
|
|
- "renders formatted currency value in display mode"
|
|
- "enters edit mode on click"
|
|
- "calls onSave with parsed number on blur"
|
|
- "does not call onSave when value unchanged"
|
|
- "calls onSave on Enter key"
|
|
|
|
Use `@testing-library/react` render + `@testing-library/user-event`. Mock `formatCurrency` from `@/lib/format` if needed, or just test the DOM output.
|
|
|
|
Run tests — they must FAIL.
|
|
|
|
**Part B — Create InlineEditCell (GREEN):**
|
|
|
|
Create `frontend/src/components/InlineEditCell.tsx` following the pattern from RESEARCH.md Pattern 5. Props interface:
|
|
|
|
```typescript
|
|
interface InlineEditCellProps {
|
|
value: number
|
|
currency: string
|
|
onSave: (value: number) => Promise<void>
|
|
className?: string
|
|
}
|
|
```
|
|
|
|
The component renders a `<TableCell>` that:
|
|
- In display mode: shows a `<span>` with `formatCurrency(value, currency)`, `cursor-pointer rounded px-2 py-1 hover:bg-muted`
|
|
- On click: switches to edit mode with `<Input type="number" step="0.01">`, autofocused
|
|
- On blur or Enter: parses the input, calls `onSave` if value changed and is a valid number, then returns to display mode
|
|
|
|
Run tests — they must PASS.
|
|
|
|
**Part C — Wire palette into all 6 dashboard components:**
|
|
|
|
For each component, apply these changes:
|
|
|
|
**BillsTracker.tsx:**
|
|
1. Remove the private `InlineEditRow` function (lines ~59-110)
|
|
2. Add imports: `import { headerGradient, amountColorClass } from '@/lib/palette'` and `import { InlineEditCell } from '@/components/InlineEditCell'`
|
|
3. Replace `<CardHeader className="bg-gradient-to-r from-blue-50 to-indigo-50">` with `<CardHeader style={headerGradient('bill')}>`
|
|
4. Replace the `<InlineEditRow>` usage with `<InlineEditCell>` inside the existing `<TableRow>`. The caller keeps the label and budget cells; InlineEditCell replaces only the actual amount cell.
|
|
5. Add `amountColorClass({ type: 'bill', actual, budgeted })` as `className` on the InlineEditCell for amount coloring. Budget column stays neutral (no color class).
|
|
|
|
**VariableExpenses.tsx:**
|
|
1. Remove the private `InlineEditRow` function (lines ~86-142)
|
|
2. Add same imports as BillsTracker
|
|
3. Replace `<CardHeader className="bg-gradient-to-r from-amber-50 to-yellow-50">` with `<CardHeader style={headerGradient('variable_expense')}>`
|
|
4. Replace `<InlineEditRow>` with `<InlineEditCell>` for the actual cell. Keep the "remaining" cell in VariableExpenses — it's NOT part of InlineEditCell.
|
|
5. Add amount coloring to the InlineEditCell className.
|
|
|
|
**DebtTracker.tsx:**
|
|
1. Remove the private `InlineEditRow` function (lines ~61-112)
|
|
2. Add same imports
|
|
3. Replace `<CardHeader className="bg-gradient-to-r from-orange-50 to-red-50">` with `<CardHeader style={headerGradient('debt')}>`
|
|
4. Replace `<InlineEditRow>` with `<InlineEditCell>` for the actual cell
|
|
5. Add amount coloring
|
|
|
|
**AvailableBalance.tsx:**
|
|
1. Remove the `PASTEL_COLORS` array constant
|
|
2. Add imports: `import { palette, headerGradient, type CategoryType } from '@/lib/palette'`
|
|
3. Replace `<CardHeader className="bg-gradient-to-r from-sky-50 to-cyan-50">` with `<CardHeader style={headerGradient('saving')} className="px-6 py-5">` (hero padding per locked decision)
|
|
4. Update `<CardTitle>` to `className="text-2xl font-semibold"` (hero typography)
|
|
5. Replace `<Cell fill={PASTEL_COLORS[index % ...]}` with `<Cell fill={palette[entry.categoryType as CategoryType]?.base ?? palette.carryover.base}>`
|
|
6. Style the center donut amount: `text-3xl font-bold tabular-nums` with `cn()` applying `text-success` when available >= 0, `text-destructive` when negative
|
|
7. Add small `text-xs text-muted-foreground` label "Available" (or translated equivalent) below the center amount
|
|
|
|
**ExpenseBreakdown.tsx:**
|
|
1. Remove the `PASTEL_COLORS` array constant
|
|
2. Add imports: `import { palette, type CategoryType } from '@/lib/palette'`
|
|
3. Replace `<CardHeader className="bg-gradient-to-r from-pink-50 to-rose-50">` with `<CardHeader style={headerGradient('debt')}>`
|
|
4. Replace `<Cell fill={PASTEL_COLORS[index % ...]}` with `<Cell fill={palette[entry.categoryType as CategoryType]?.base ?? palette.carryover.base}>`
|
|
|
|
**FinancialOverview.tsx:**
|
|
1. Add imports: `import { palette, overviewHeaderGradient, amountColorClass, type CategoryType } from '@/lib/palette'`
|
|
2. Replace `<CardHeader className="bg-gradient-to-r from-sky-50 to-indigo-50">` with `<CardHeader style={overviewHeaderGradient()} className="px-6 py-5">` (hero padding)
|
|
3. Update `<CardTitle>` to `className="text-2xl font-semibold"` (hero typography)
|
|
4. Tint each summary row with its category's light shade: add `style={{ backgroundColor: palette[categoryType].light }}` to each `<TableRow>` that represents a category
|
|
5. Apply `amountColorClass()` to actual amount cells. For the income row, pass `isIncome: true`. For the remaining/available summary row, pass `isAvailable: true`. Budget column stays neutral.
|
|
|
|
**CRITICAL anti-patterns to avoid:**
|
|
- Do NOT add `style` to `<Card>` — only to `<CardHeader>` for gradients
|
|
- Do NOT color the budget column — only actual gets colored
|
|
- Do NOT use raw Tailwind color classes like `text-green-600` — use `text-success` from the semantic token
|
|
- Do NOT edit any files in `src/components/ui/`
|
|
- Do NOT use Tailwind v3 bracket syntax `bg-[--var]` — use v4 parenthesis syntax `bg-(--var)` if needed
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run --reporter=verbose</automated>
|
|
</verify>
|
|
<done>
|
|
- InlineEditCell.test.tsx passes all tests
|
|
- palette.test.ts still passes (no regressions)
|
|
- No `InlineEditRow` private function exists in BillsTracker, VariableExpenses, or DebtTracker
|
|
- No `PASTEL_COLORS` array exists in AvailableBalance or ExpenseBreakdown
|
|
- No `bg-gradient-to-r from-blue-50` or similar hardcoded gradient classes exist in any dashboard component
|
|
- All 6 dashboard components import from `@/lib/palette`
|
|
- FinancialOverview and AvailableBalance use hero sizing (text-2xl, p-6/px-6 py-5)
|
|
- Amount coloring uses amountColorClass() — only on actual column
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Build verification — ensure app compiles and no hardcoded colors remain</name>
|
|
<files>frontend/src/components/BillsTracker.tsx, frontend/src/components/VariableExpenses.tsx, frontend/src/components/DebtTracker.tsx, frontend/src/components/AvailableBalance.tsx, frontend/src/components/ExpenseBreakdown.tsx, frontend/src/components/FinancialOverview.tsx</files>
|
|
<action>
|
|
1. Run the Vite build to confirm no TypeScript errors:
|
|
`cd frontend && bun run build`
|
|
|
|
2. Run the full test suite:
|
|
`cd frontend && bun vitest run`
|
|
|
|
3. Verify no hardcoded gradient color classes remain in dashboard components:
|
|
`grep -rn "from-blue-50\|from-amber-50\|from-orange-50\|from-sky-50\|from-pink-50\|from-sky-50 to-indigo-50\|PASTEL_COLORS" frontend/src/components/`
|
|
This should return zero results.
|
|
|
|
4. Verify no raw Tailwind color utilities for amount coloring:
|
|
`grep -rn "text-green-\|text-red-\|text-amber-" frontend/src/components/`
|
|
This should return zero results (all amount colors use text-success, text-warning, text-destructive).
|
|
|
|
5. Verify InlineEditRow is fully removed:
|
|
`grep -rn "InlineEditRow" frontend/src/components/`
|
|
This should return zero results.
|
|
|
|
If any issues are found, fix them before proceeding.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun run build 2>&1 | tail -5 && bun vitest run 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<done>Vite production build succeeds with zero errors. All tests pass. No hardcoded gradient classes, PASTEL_COLORS arrays, InlineEditRow functions, or raw Tailwind color utilities exist in dashboard components.</done>
|
|
</task>
|
|
|
|
<task type="checkpoint:human-verify" gate="blocking">
|
|
<name>Task 3: Visual verification of pastel design token system</name>
|
|
<action>
|
|
Human verifies the complete pastel design token system and visual dashboard overhaul.
|
|
|
|
What was built:
|
|
- All shadcn components display in pastel tones (lavender-tinted backgrounds, borders, and surfaces)
|
|
- Card headers have category-specific pastel gradients (blue for bills, amber for variable expenses, rose for debt, etc.)
|
|
- FinancialOverview and AvailableBalance are visually dominant hero elements with larger text and more padding
|
|
- AvailableBalance donut center shows the amount in green (positive) or red (negative)
|
|
- Amount coloring: green for positive income, amber for over-budget expenses, red for negative available
|
|
- FinancialOverview rows are tinted with their category's pastel shade
|
|
- Charts use category colors from palette.ts (same colors as table headers)
|
|
- InlineEditCell is extracted as a shared component (click to edit actual amounts)
|
|
|
|
How to verify:
|
|
1. Start the app: `cd frontend && bun run dev` (ensure backend is running or use `docker compose up db` for database)
|
|
2. Open http://localhost:5173 in browser
|
|
3. Check these specific items:
|
|
a. Background tint: The page background should have a very subtle lavender tint; cards should be pure white floating on it
|
|
b. Card headers: Each section (Bills, Variable Expenses, Debt) should have a distinct pastel gradient header color — blue, amber, rose respectively
|
|
c. Hero elements: FinancialOverview and AvailableBalance should look visually larger/more prominent than other cards
|
|
d. Donut center: The available amount in the donut chart should be large text, colored green if positive or red if negative
|
|
e. Amount coloring: In any tracker, if an actual amount exceeds budget, it should show amber. Income actual amounts should be green. Remaining/available should be green (positive) or red (negative)
|
|
f. Row tinting: FinancialOverview summary rows should each have a subtle category-colored background
|
|
g. Inline editing: Click any actual amount in Bills/Variable Expenses/Debt — it should enter edit mode with an input field
|
|
h. Chart colors: Donut/bar chart segments should use the same colors as their corresponding card headers
|
|
</action>
|
|
<verify>Human visual inspection of dashboard at http://localhost:5173</verify>
|
|
<done>User confirms pastel theme, hero hierarchy, amount coloring, row tinting, inline editing, and chart colors all look correct.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
1. `cd frontend && bun run build` — zero TypeScript errors
|
|
2. `cd frontend && bun vitest run` — all tests pass
|
|
3. `grep -rn "PASTEL_COLORS\|InlineEditRow\|from-blue-50\|from-amber-50\|from-orange-50\|from-sky-50\|from-pink-50" frontend/src/components/` — zero results
|
|
4. `grep -rn "text-green-\|text-red-\|text-amber-" frontend/src/components/` — zero results
|
|
5. Visual inspection confirms pastel theme, hero hierarchy, amount coloring, and inline editing
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- All 6 dashboard components use palette.ts for gradients (no hardcoded Tailwind color classes)
|
|
- FinancialOverview and AvailableBalance have hero typography and padding
|
|
- Amount coloring follows locked rules: green income, amber over-budget, red negative
|
|
- InlineEditCell is the single shared component for inline editing (3 duplicates removed)
|
|
- Charts use palette.ts base colors matching their card header categories
|
|
- Vite build succeeds, all tests pass
|
|
- User approves visual result at checkpoint
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/01-design-token-foundation/01-02-SUMMARY.md`
|
|
</output>
|