--- phase: 04-full-app-design-consistency plan: 02 type: execute wave: 2 depends_on: [04-01] files_modified: - src/pages/CategoriesPage.tsx - src/pages/TemplatePage.tsx - src/pages/QuickAddPage.tsx - src/pages/SettingsPage.tsx - src/i18n/en.json - src/i18n/de.json autonomous: true requirements: [UI-CATEGORIES-01, UI-TEMPLATE-01, UI-QUICKADD-01, UI-SETTINGS-01, UI-DESIGN-01] must_haves: truths: - "Categories page uses PageShell for header with title and Add Category button" - "Categories page shows category group headers with left-border accent styling" - "Categories page shows skeleton loading state instead of blank screen" - "Template page uses PageShell with inline-editable name and Add Item button" - "Template page shows category group headers with left-border accent styling" - "QuickAdd page uses PageShell for header" - "QuickAdd page shows skeleton loading state instead of blank screen" - "Settings page uses PageShell with no duplicate heading" - "Settings page shows skeleton loading state instead of blank screen" - "German locale shows all text translated on all four pages" artifacts: - path: "src/pages/CategoriesPage.tsx" provides: "PageShell adoption, skeleton, group header upgrade" contains: "PageShell" - path: "src/pages/TemplatePage.tsx" provides: "PageShell adoption, skeleton, group header upgrade" contains: "PageShell" - path: "src/pages/QuickAddPage.tsx" provides: "PageShell adoption, skeleton" contains: "PageShell" - path: "src/pages/SettingsPage.tsx" provides: "PageShell adoption, skeleton, no double heading" contains: "PageShell" key_links: - from: "src/pages/CategoriesPage.tsx" to: "src/components/shared/PageShell.tsx" via: "import and render" pattern: 'import.*PageShell.*from.*shared/PageShell' - from: "src/pages/SettingsPage.tsx" to: "src/components/shared/PageShell.tsx" via: "import and render — replacing redundant h1" pattern: 'import.*PageShell.*from.*shared/PageShell' --- Apply PageShell, skeleton loading states, and group header upgrades to the four CRUD/settings pages (Categories, Template, QuickAdd, Settings). Purpose: These four authenticated pages currently use inline `

` + action div headers, return `null` while loading, and use small-dot group headers. This plan upgrades them to match the dashboard's design language -- consistent headers via PageShell, skeleton loading placeholders, and left-border accent group headers. Output: Four updated page components with consistent design system application, plus new i18n keys for page descriptions. @/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md @/home/jlmak/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/04-full-app-design-consistency/04-CONTEXT.md @.planning/phases/04-full-app-design-consistency/04-RESEARCH.md From src/components/shared/PageShell.tsx: ```tsx interface PageShellProps { title: string description?: string action?: React.ReactNode children: React.ReactNode } export function PageShell({ title, description, action, children }: PageShellProps) ``` From src/components/ui/skeleton.tsx: ```tsx // Skeleton primitive -- use for building page-specific loading states import { Skeleton } from "@/components/ui/skeleton" // Usage: ``` From src/lib/palette.ts: ```tsx export const categoryColors: Record // Maps category type to CSS variable string like "var(--color-income)" ``` Group header upgrade pattern (from RESEARCH.md): ```tsx // Replace plain dot headers with left-border accent
{t(`categories.types.${type}`)}
``` Current pattern in all CRUD pages to replace: ```tsx

{t(`categories.types.${type}`)}

``` Task 1: Upgrade CategoriesPage and TemplatePage with PageShell, skeletons, and group headers src/pages/CategoriesPage.tsx, src/pages/TemplatePage.tsx, src/i18n/en.json, src/i18n/de.json **CategoriesPage.tsx changes:** 1. **Import PageShell:** Add `import { PageShell } from "@/components/shared/PageShell"` and `import { Skeleton } from "@/components/ui/skeleton"`. 2. **Replace header:** Remove the `
` block containing the `

` and ` } > {/* existing content (empty state check + grouped sections) */} ``` 3. **Skeleton loading:** Replace `if (loading) return null` with: ```tsx if (loading) return (
{[1, 2, 3].map((i) => (
{[1, 2].map((j) => (
))}
))}
) ``` 4. **Group header upgrade:** Replace the plain dot group header pattern in the `grouped.map` with the left-border accent pattern: ```tsx
{t(`categories.types.${type}`)}
``` Remove the old `
` block with the `size-3 rounded-full` dot and `

`. **TemplatePage.tsx changes:** 1. **Import PageShell and Skeleton:** Same imports as CategoriesPage. 2. **Replace header:** The TemplatePage header has an inline-editable `TemplateName` component. Wrap with PageShell, putting TemplateName as the title area. Since PageShell accepts a `title` string but TemplateName is a component, use PageShell differently here: Instead of wrapping with PageShell using `title` prop, replace the header div with PageShell but pass the template name as a plain string title when NOT editing. Actually, the TemplateName component handles its own editing state inline. The cleanest approach: keep the TemplateName component but wrap the page content differently. Replace the entire page structure: ```tsx
{/* Header */}
...
``` With: ```tsx

} > ... ``` **Note:** The TemplateName inline-edit functionality is a nice feature that will be lost if we just use a plain title string. To preserve it while using PageShell: remove the `title` prop from PageShell and instead render TemplateName inside the PageShell children, ABOVE the content. Actually, the simplest correct approach is to NOT use PageShell's title prop for TemplatePage -- instead, pass a custom `action` that includes the Add button, and render TemplateName as the first child inside PageShell with the title styling matching PageShell's own h1 style. But this defeats the purpose. Best approach: Use PageShell for the layout but pass the TemplateName component as a React node for the title slot. Since PageShell only accepts `title: string`, we need to slightly modify the approach. Just use PageShell's wrapper layout manually: Replace the header with: ```tsx
{/* rest of content */}
``` This mirrors PageShell's exact DOM structure (flex flex-col gap-6 > flex items-start justify-between gap-4) without importing PageShell, since TemplateName is a custom component that cannot be a plain string. This keeps visual consistency. Additionally, update TemplateName's `

` to use `className="text-2xl font-semibold tracking-tight"` (add `tracking-tight` to match PageShell's h1 styling). 3. **Skeleton loading:** Replace `if (loading) return null` with a skeleton that mirrors the template page layout: ```tsx if (loading) return (
{[1, 2].map((i) => (
{[1, 2, 3].map((j) => (
))}
))}
) ``` 4. **Group header upgrade:** Same left-border accent pattern as CategoriesPage. Replace the dot+h2 pattern in grouped.map with: ```tsx
{t(`categories.types.${type}`)}
``` **i18n: No new keys needed for this task.** Categories and Template pages already have all required i18n keys. The page descriptions are optional (Claude's discretion) -- skip them for these two pages since the page purpose is self-evident from the content. cd /home/jlmak/Projects/jlmak/SimpleFinanceDash && bun run build CategoriesPage and TemplatePage both show: consistent header layout matching PageShell spacing (flex-col gap-6), left-border accent group headers replacing dot headers, skeleton loading states replacing `return null`. No inline h1 header pattern remains. Build passes. Task 2: Upgrade QuickAddPage and SettingsPage with PageShell and skeletons src/pages/QuickAddPage.tsx, src/pages/SettingsPage.tsx **QuickAddPage.tsx changes:** 1. **Import PageShell and Skeleton:** Add `import { PageShell } from "@/components/shared/PageShell"` and `import { Skeleton } from "@/components/ui/skeleton"`. 2. **Replace header:** Remove the `
` header block. Wrap the entire return in: ```tsx {t("quickAdd.add")} } > {/* empty state + table + dialog */} ``` Remove the wrapping `
` root since PageShell provides the outer container. 3. **Skeleton loading:** Replace `if (loading) return null` with: ```tsx if (loading) return (
{[1, 2, 3, 4, 5].map((i) => (
))}
) ``` **SettingsPage.tsx changes:** 1. **Import PageShell and Skeleton:** Add `import { PageShell } from "@/components/shared/PageShell"` and `import { Skeleton } from "@/components/ui/skeleton"`. 2. **Remove duplicate heading:** Delete the `

{t("settings.title")}

` on line 67. This creates a double heading since the Card below also has a CardTitle with "Settings". 3. **Wrap with PageShell:** Replace the `
` root with: ```tsx
{/* Remove CardHeader with CardTitle since PageShell provides the title. Keep CardContent as-is. */} {/* existing form fields unchanged */}
``` Remove the CardHeader and CardTitle entirely -- PageShell provides the page-level title, and the Card should just contain the form. Add `pt-6` to CardContent's className since without CardHeader the content needs top padding. 4. **Skeleton loading:** Replace `if (loading) return null` with: ```tsx if (loading) return (
{[1, 2, 3].map((i) => (
))}
) ``` 5. **Clean up unused imports:** After removing CardHeader and CardTitle usage, update the import to: `import { Card, CardContent } from "@/components/ui/card"`. Remove `CardHeader` and `CardTitle` from the import. **No i18n changes needed for this task.** QuickAdd and Settings pages already have all required translation keys. cd /home/jlmak/Projects/jlmak/SimpleFinanceDash && bun run build QuickAddPage uses PageShell with title and action button, shows skeleton on load. SettingsPage uses PageShell with no double "Settings" heading, Card contains only the form, shows skeleton on load. No `return null` loading patterns remain in either file. Build passes. - `bun run build` compiles without TypeScript errors - `grep -c "return null" src/pages/CategoriesPage.tsx src/pages/TemplatePage.tsx src/pages/QuickAddPage.tsx src/pages/SettingsPage.tsx` returns 0 for all files - `grep -c "size-3 rounded-full" src/pages/CategoriesPage.tsx src/pages/TemplatePage.tsx` returns 0 for both (old dot headers removed) - `grep -c "border-l-4" src/pages/CategoriesPage.tsx src/pages/TemplatePage.tsx` returns at least 1 for each (new accent headers applied) - `grep -c "PageShell" src/pages/CategoriesPage.tsx src/pages/QuickAddPage.tsx src/pages/SettingsPage.tsx` returns at least 1 for each - All four pages (Categories, Template, QuickAdd, Settings) show consistent PageShell-style headers - All four pages show skeleton loading states instead of blank screens - Categories and Template pages show left-border accent group headers - Settings page has exactly ONE "Settings" heading (via PageShell), not two - `bun run build` passes - No `return null` loading patterns remain in any of the four files After completion, create `.planning/phases/04-full-app-design-consistency/04-02-SUMMARY.md`