--- phase: 06-template-frontend-and-workflow-replacement plan: 01 type: execute wave: 1 depends_on: [] files_modified: - frontend/src/lib/api.ts - frontend/src/hooks/useTemplate.ts - frontend/src/pages/TemplatePage.tsx - frontend/src/components/AppLayout.tsx - frontend/src/App.tsx - frontend/src/i18n/en.json - frontend/src/i18n/de.json autonomous: true requirements: [TMPL-05] must_haves: truths: - "User can navigate to a Template page from the sidebar" - "Template page shows current template items grouped by tier (fixed items with amounts, variable items without)" - "User can add a new item to the template by selecting a category and tier" - "User can remove an item from the template" - "User can reorder template items via move-up/move-down buttons" - "One-off tier is not available when adding template items" artifacts: - path: "frontend/src/lib/api.ts" provides: "Template API functions (get, create item, update item, delete item, reorder, generate)" contains: "template" - path: "frontend/src/hooks/useTemplate.ts" provides: "useTemplate hook with CRUD operations" exports: ["useTemplate"] - path: "frontend/src/pages/TemplatePage.tsx" provides: "Template management page component" exports: ["TemplatePage"] - path: "frontend/src/components/AppLayout.tsx" provides: "Sidebar nav item for Template" contains: "template" - path: "frontend/src/App.tsx" provides: "Route for /template" contains: "/template" key_links: - from: "frontend/src/pages/TemplatePage.tsx" to: "/api/template" via: "useTemplate hook" pattern: "useTemplate" - from: "frontend/src/components/AppLayout.tsx" to: "/template" via: "Link component in nav" pattern: "to.*template" - from: "frontend/src/App.tsx" to: "TemplatePage" via: "Route element" pattern: "Route.*template" --- Create the template management page where users can add, remove, and reorder fixed and variable budget items in their monthly template. Purpose: TMPL-05 requires a dedicated template page for managing the items that auto-populate new monthly budgets. This plan builds the full frontend: API client functions, data hook, page component, routing, and navigation. Output: Working template management page accessible from sidebar, with full CRUD for template items. @/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/05-template-data-model-and-api/05-01-SUMMARY.md @.planning/phases/05-template-data-model-and-api/05-02-SUMMARY.md From backend/internal/models/models.go: ```go type ItemTier string const ( ItemTierFixed ItemTier = "fixed" ItemTierVariable ItemTier = "variable" ItemTierOneOff ItemTier = "one_off" ) type TemplateItem struct { ID uuid.UUID `json:"id"` TemplateID uuid.UUID `json:"template_id"` CategoryID uuid.UUID `json:"category_id"` CategoryName string `json:"category_name,omitempty"` CategoryType CategoryType `json:"category_type,omitempty"` CategoryIcon string `json:"category_icon,omitempty"` ItemTier ItemTier `json:"item_tier"` BudgetedAmount *decimal.Decimal `json:"budgeted_amount"` SortOrder int `json:"sort_order"` } type TemplateDetail struct { Template // id, user_id, name, created_at, updated_at Items []TemplateItem `json:"items"` } ``` API endpoints (from Phase 5 Plan 02): - GET /api/template -> TemplateDetail (empty items array when no template) - PUT /api/template -> update name - POST /api/template/items -> add item (auto-creates template if needed) - PUT /api/template/items/{itemId} -> update item - DELETE /api/template/items/{itemId} -> remove item (204) - PUT /api/template/items/reorder -> batch update sort_order (204) - POST /api/budgets/generate -> { month: "2026-04", currency: "EUR" } -> BudgetDetail or 409 From frontend/src/lib/api.ts: ```typescript export type CategoryType = 'bill' | 'variable_expense' | 'debt' | 'saving' | 'investment' | 'income' export interface Category { id: string; name: string; type: CategoryType; icon: string; sort_order: number } export interface BudgetItem { id: string; budget_id: string; category_id: string; category_name: string; category_type: CategoryType; budgeted_amount: number; actual_amount: number; notes: string } export const categories = { list: () => request('/categories') } ``` From frontend/src/components/AppLayout.tsx: ```typescript const navItems = [ { path: '/', label: t('nav.dashboard'), icon: LayoutDashboard }, { path: '/categories', label: t('nav.categories'), icon: Tags }, { path: '/settings', label: t('nav.settings'), icon: Settings }, ] ``` Task 1: API client extensions and useTemplate hook frontend/src/lib/api.ts, frontend/src/hooks/useTemplate.ts 1. In `frontend/src/lib/api.ts`: - Add `item_tier` field to existing `BudgetItem` interface: `item_tier: 'fixed' | 'variable' | 'one_off'` - Add `ItemTier` type alias: `export type ItemTier = 'fixed' | 'variable' | 'one_off'` - Add `TemplateItem` interface: `{ id: string, template_id: string, category_id: string, category_name: string, category_type: CategoryType, category_icon: string, item_tier: ItemTier, budgeted_amount: number | null, sort_order: number }` - Add `TemplateDetail` interface: `{ id: string | null, name: string, items: TemplateItem[] }` - Add `template` API object: ``` get: () => request('/template') updateName: (name: string) => request('/template', { method: 'PUT', body: JSON.stringify({ name }) }) addItem: (data: { category_id: string, item_tier: ItemTier, budgeted_amount?: number }) => request('/template/items', { method: 'POST', body: JSON.stringify(data) }) updateItem: (itemId: string, data: { item_tier?: ItemTier, budgeted_amount?: number }) => request(`/template/items/${itemId}`, { method: 'PUT', body: JSON.stringify(data) }) deleteItem: (itemId: string) => request(`/template/items/${itemId}`, { method: 'DELETE' }) reorder: (items: { id: string, sort_order: number }[]) => request('/template/items/reorder', { method: 'PUT', body: JSON.stringify({ items }) }) ``` - Add `generate` function to `budgets` object: `generate: (data: { month: string, currency: string }) => request('/budgets/generate', { method: 'POST', body: JSON.stringify(data) })` 2. Create `frontend/src/hooks/useTemplate.ts`: - Import `template as templateApi, categories as categoriesApi` from api.ts - State: `templateDetail` (TemplateDetail | null), `categories` (Category[]), `loading` (boolean) - `fetchTemplate`: calls templateApi.get(), sets state - `fetchCategories`: calls categoriesApi.list(), sets state - `addItem(data)`: calls templateApi.addItem(data), then refetches template - `removeItem(itemId)`: calls templateApi.deleteItem(itemId), then refetches template - `moveItem(itemId, direction: 'up' | 'down')`: compute new sort_order values for the swapped pair, call templateApi.reorder with full item list's updated sort_orders, then refetch - `updateItem(itemId, data)`: calls templateApi.updateItem(itemId, data), then refetches - useEffect on mount: fetch both template and categories - Return: `{ template: templateDetail, categories, loading, addItem, removeItem, moveItem, updateItem, refetch: fetchTemplate }` cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && npx tsc --noEmit 2>&1 | head -30 api.ts has template and generate API functions, BudgetItem includes item_tier, useTemplate hook compiles without errors Task 2: TemplatePage component, routing, navigation, and i18n frontend/src/pages/TemplatePage.tsx, frontend/src/components/AppLayout.tsx, frontend/src/App.tsx, frontend/src/i18n/en.json, frontend/src/i18n/de.json 1. Create `frontend/src/pages/TemplatePage.tsx`: - Import useTemplate hook, useTranslation, shadcn components (Card, CardHeader, CardTitle, CardContent, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Badge), EmptyState, lucide icons (FileText, Plus, Trash2, ArrowUp, ArrowDown, GripVertical) - Use `useTemplate()` to get template, categories, and CRUD functions - Layout: Card with pastel gradient header (use `headerGradient` pattern from palette.ts -- pick a suitable palette key or use inline violet/indigo gradient similar to BudgetSetup) - **Add item form** at top of card content: - Row with: Category select (filtered to exclude categories already in template), Item tier select (only "fixed" and "variable" -- NOT "one_off"), budgeted_amount Input (only shown when tier is "fixed"), Add button (Plus icon) - On add: call `addItem({ category_id, item_tier, budgeted_amount })`, clear form - Disable Add button when category not selected or (tier is fixed and amount is empty) - **Template items table** below the add form: - Columns: Reorder (up/down arrow buttons), Category (name + icon), Tier (Badge showing "Fixed" or "Variable"), Amount (show formatted amount for fixed, dash for variable), Actions (Trash2 delete button) - Items displayed in sort_order - Move up disabled on first item, move down disabled on last item - Delete button calls `removeItem(itemId)` - **Empty state** when template has no items: use EmptyState component with FileText icon, heading like "No template items", subtext explaining to add fixed and variable items - Show loading skeleton while data loads (use Skeleton component with pastel tint) - Use `useTranslation()` for all visible text via `t('template.*')` keys 2. Update `frontend/src/components/AppLayout.tsx`: - Import `FileText` icon from lucide-react - Add template nav item to `navItems` array after categories: `{ path: '/template', label: t('nav.template'), icon: FileText }` 3. Update `frontend/src/App.tsx`: - Import `TemplatePage` from pages - Add route: `} />` 4. Update `frontend/src/i18n/en.json`: - Add `"nav.template": "Template"` (inside nav object) - Add `"template"` object with keys: ``` "title": "Monthly Template", "addItem": "Add Item", "category": "Category", "tier": "Tier", "fixed": "Fixed", "variable": "Variable", "oneOff": "One-off", "amount": "Amount", "actions": "Actions", "noItems": "No template items yet", "noItemsHint": "Add fixed and variable items to define your monthly budget template.", "selectCategory": "Select category", "selectTier": "Select tier" ``` 5. Update `frontend/src/i18n/de.json`: - Add `"nav.template": "Vorlage"` (inside nav object) - Add `"template"` object with German translations: ``` "title": "Monatliche Vorlage", "addItem": "Eintrag hinzufuegen", "category": "Kategorie", "tier": "Typ", "fixed": "Fest", "variable": "Variabel", "oneOff": "Einmalig", "amount": "Betrag", "actions": "Aktionen", "noItems": "Noch keine Vorlageneintraege", "noItemsHint": "Fuegen Sie feste und variable Eintraege hinzu, um Ihre monatliche Budgetvorlage zu definieren.", "selectCategory": "Kategorie auswaehlen", "selectTier": "Typ auswaehlen" ``` cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && npx tsc --noEmit 2>&1 | head -30 && bun run build 2>&1 | tail -5 Template page renders with add/remove/reorder functionality, accessible via sidebar nav item at /template, all text uses i18n keys in both EN and DE - TypeScript compiles without errors: `cd frontend && npx tsc --noEmit` - Production build succeeds: `cd frontend && bun run build` - Template page is reachable at /template route - Sidebar shows Template nav item between Categories and Settings - Template API functions exist in api.ts and have correct method/path - TemplatePage shows add form with category select, tier select (fixed/variable only), and conditional amount input - Existing template items display in a table with tier badges and delete buttons - Reorder arrows move items up/down and persist via API - Empty state shown when no template items exist - Sidebar navigation includes Template link - All UI text is i18n-translated (EN + DE) After completion, create `.planning/phases/06-template-frontend-and-workflow-replacement/06-01-SUMMARY.md`