--- phase: 03-interaction-quality-and-completeness plan: 03 type: execute wave: 2 depends_on: ["03-01"] files_modified: - frontend/src/components/BillsTracker.tsx - frontend/src/components/VariableExpenses.tsx - frontend/src/components/DebtTracker.tsx - frontend/src/pages/DashboardPage.tsx autonomous: true requirements: [IXTN-03, STATE-03] must_haves: truths: - "After saving an inline edit in BillsTracker, the entire row briefly flashes green" - "After a failed inline edit save, the row briefly flashes red" - "Same flash behavior works in VariableExpenses and DebtTracker" - "Dashboard loading skeleton uses pastel-tinted backgrounds matching section colors" - "BillsTracker, VariableExpenses, DebtTracker show tinted skeletons when budget has no items for that section" artifacts: - path: "frontend/src/components/BillsTracker.tsx" provides: "Row flash state + tinted skeleton loading state" contains: "flashRowId" - path: "frontend/src/components/VariableExpenses.tsx" provides: "Row flash state + tinted skeleton loading state" contains: "flashRowId" - path: "frontend/src/components/DebtTracker.tsx" provides: "Row flash state + tinted skeleton loading state" contains: "flashRowId" - path: "frontend/src/pages/DashboardPage.tsx" provides: "Tinted dashboard loading skeletons using palette light shades" contains: "palette" key_links: - from: "frontend/src/components/BillsTracker.tsx" to: "frontend/src/components/InlineEditCell.tsx" via: "onSaveSuccess/onSaveError callbacks" pattern: "onSaveSuccess.*flashRow" - from: "frontend/src/pages/DashboardPage.tsx" to: "frontend/src/lib/palette.ts" via: "palette import for skeleton tinting" pattern: "palette\\..*\\.light" --- Wire row-level flash feedback into all three tracker components and add pastel-tinted loading skeletons to the dashboard. Purpose: Complete the inline edit feedback loop — users see green/red row flashes confirming save success/failure. Tinted skeletons make the loading state feel intentional and branded rather than generic. Output: BillsTracker, VariableExpenses, DebtTracker with flash + skeleton states; DashboardPage with tinted skeletons. @/home/jean-luc-makiola/.claude/get-shit-done/workflows/execute-plan.md @/home/jean-luc-makiola/.claude/get-shit-done/templates/summary.md @.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-RESEARCH.md @.planning/phases/03-interaction-quality-and-completeness/03-01-SUMMARY.md From frontend/src/components/InlineEditCell.tsx (post Plan 01): ```typescript interface InlineEditCellProps { value: number currency: string onSave: (value: number) => Promise onSaveSuccess?: () => void onSaveError?: () => void className?: string } ``` From frontend/src/lib/palette.ts: ```typescript export const palette = { bill: { base: '...', light: 'oklch(0.96 0.03 250)', header: '...' }, variable_expense: { base: '...', light: 'oklch(0.97 0.04 85)', header: '...' }, debt: { base: '...', light: 'oklch(0.96 0.04 15)', header: '...' }, saving: { base: '...', light: 'oklch(0.95 0.04 280)', header: '...' }, investment: { base: '...', light: 'oklch(0.96 0.03 320)', header: '...' }, // ... } ``` From frontend/src/components/BillsTracker.tsx: ```typescript interface Props { budget: BudgetDetail onUpdate: (itemId: string, data: { actual_amount?: number; budgeted_amount?: number }) => Promise } // Uses: containing ``` From frontend/src/components/ui/skeleton.tsx: ```typescript // Accepts className and style props. Default bg is bg-muted. // Override with style={{ backgroundColor: '...' }} to tint. ``` Task 1: Wire row flash feedback into BillsTracker, VariableExpenses, and DebtTracker frontend/src/components/BillsTracker.tsx, frontend/src/components/VariableExpenses.tsx, frontend/src/components/DebtTracker.tsx Apply the same pattern to all three tracker components. Import `useState` (already imported in most) and `cn` from `@/lib/utils`. Add flash state and helper to each component: ```typescript const [flashRowId, setFlashRowId] = useState(null) const [errorRowId, setErrorRowId] = useState(null) const triggerFlash = (id: string, type: 'success' | 'error') => { if (type === 'success') { setFlashRowId(id) setTimeout(() => setFlashRowId(null), 600) } else { setErrorRowId(id) setTimeout(() => setErrorRowId(null), 600) } } ``` On each data `` (not the totals row), add inline style for the flash: ```tsx ``` Use `color-mix()` inline style (not Tailwind `bg-success/20`) per research recommendation — avoids potential Tailwind class generation issues. Pass callbacks to each ``: ```tsx onUpdate(item.id, { actual_amount: actual })} onSaveSuccess={() => triggerFlash(item.id, 'success')} onSaveError={() => triggerFlash(item.id, 'error')} className={amountColorClass({ type: 'bill', actual: item.actual_amount, budgeted: item.budgeted_amount })} /> ``` Adjust the `type` argument in `amountColorClass` per component: - BillsTracker: `type: 'bill'` - VariableExpenses: `type: 'variable_expense'` - DebtTracker: `type: 'debt'` (These should already be correct from Phase 1 — just ensure the new onSaveSuccess/onSaveError props are added.) **Do NOT modify** the totals row or the CardHeader — only add flash state and wire callbacks. cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun run build All three tracker components have flash state, triggerFlash helper, inline style on data rows, and onSaveSuccess/onSaveError wired to InlineEditCell. Build passes. Task 2: Add pastel-tinted loading skeletons to DashboardPage and tracker sections frontend/src/pages/DashboardPage.tsx, frontend/src/components/BillsTracker.tsx, frontend/src/components/VariableExpenses.tsx, frontend/src/components/DebtTracker.tsx **DashboardPage.tsx** — tint existing loading skeleton block (lines 39-47): - Import `palette` from `@/lib/palette` - Replace the existing generic Skeleton elements with tinted versions: ```tsx if (loading && list.length === 0) { return (
) } ``` Use `style` prop to override `bg-muted` without editing `ui/skeleton.tsx`. **BillsTracker.tsx, VariableExpenses.tsx, DebtTracker.tsx** — add skeleton for empty sections: - Import `Skeleton` from `@/components/ui/skeleton` and ensure `palette` is imported (already imported for `headerGradient`) - After the filter (e.g., `const bills = budget.items.filter(...)`) add an early return if no items exist: ```tsx if (bills.length === 0) { return ( {t('dashboard.billsTracker')} {[1, 2, 3].map((i) => ( ))} ) } ``` Use the matching palette key per component: - BillsTracker: `palette.bill.light` - VariableExpenses: `palette.variable_expense.light` - DebtTracker: `palette.debt.light` **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).
cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun vitest run && bun run build 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.
- `cd frontend && bun vitest run` — full test suite passes - `cd frontend && bun run build` — production build succeeds - Row flash uses `color-mix(in oklch, var(--success/destructive) 20%, transparent)` inline style - Dashboard skeleton uses palette.*.light inline styles - Tracker skeletons use matching palette key for their section - Inline edit save success produces visible green row flash (~600ms duration) - Inline edit save failure produces visible red row flash + value revert - Dashboard loading state shows pastel-tinted skeletons (not grey) - Empty tracker sections show tinted skeleton placeholders matching their card header color - No flash or skeleton interferes with existing functionality After completion, create `.planning/phases/03-interaction-quality-and-completeness/03-03-SUMMARY.md`