--- phase: 07-quick-add-library plan: 02 type: execute wave: 2 depends_on: ["07-01"] files_modified: - frontend/src/lib/api.ts - frontend/src/hooks/useQuickAdd.ts - frontend/src/pages/QuickAddPage.tsx - frontend/src/components/QuickAddPicker.tsx - frontend/src/components/AppLayout.tsx - frontend/src/App.tsx - frontend/src/i18n/en.json - frontend/src/i18n/de.json autonomous: false requirements: [QADD-01, QADD-02, QADD-03] must_haves: truths: - "User can view, add, edit, and remove saved categories on the quick-add library page" - "User can browse their quick-add library when adding a one-off item to a budget" - "Selecting a quick-add item creates a one-off budget item with that name and icon" - "Quick-add library page is accessible from sidebar navigation" artifacts: - path: "frontend/src/lib/api.ts" provides: "QuickAddItem type and quickAdd API namespace" contains: "quickAdd" - path: "frontend/src/hooks/useQuickAdd.ts" provides: "useQuickAdd hook with CRUD operations" exports: ["useQuickAdd"] - path: "frontend/src/pages/QuickAddPage.tsx" provides: "Management page for quick-add library" min_lines: 50 - path: "frontend/src/components/QuickAddPicker.tsx" provides: "Picker component for selecting quick-add items when adding one-off budget items" min_lines: 30 - path: "frontend/src/components/AppLayout.tsx" provides: "Sidebar nav item for quick-add library" contains: "quick-add" - path: "frontend/src/App.tsx" provides: "Route for /quick-add" contains: "QuickAddPage" key_links: - from: "frontend/src/hooks/useQuickAdd.ts" to: "frontend/src/lib/api.ts" via: "quickAdd namespace import" pattern: "import.*quickAdd.*api" - from: "frontend/src/pages/QuickAddPage.tsx" to: "frontend/src/hooks/useQuickAdd.ts" via: "useQuickAdd hook call" pattern: "useQuickAdd\\(\\)" - from: "frontend/src/components/QuickAddPicker.tsx" to: "frontend/src/lib/api.ts" via: "quickAdd.list and budgetItems.create" pattern: "quickAdd|budgetItems" --- Build the frontend for the quick-add library: a management page for CRUD operations, a picker component for the budget item add flow, and all routing/navigation wiring. Purpose: Users need a page to manage their saved one-off categories and a way to insert them as budget items with one click when viewing a budget. Output: QuickAddPage, QuickAddPicker component, useQuickAdd hook, API client additions, routing and i18n @/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/07-quick-add-library/07-01-SUMMARY.md @frontend/src/lib/api.ts @frontend/src/hooks/useTemplate.ts @frontend/src/pages/TemplatePage.tsx @frontend/src/pages/DashboardPage.tsx @frontend/src/components/AppLayout.tsx @frontend/src/App.tsx @frontend/src/i18n/en.json @frontend/src/i18n/de.json QuickAddItem JSON shape (GET /api/quick-add returns array of these): ```json { "id": "uuid", "user_id": "uuid", "name": "string", "icon": "string", "sort_order": 0, "created_at": "timestamp", "updated_at": "timestamp" } ``` Endpoints: - GET /api/quick-add — list all quick-add items - POST /api/quick-add — create {name, icon} - PUT /api/quick-add/{itemId} — update {name, icon, sort_order} - DELETE /api/quick-add/{itemId} — remove From frontend/src/lib/api.ts: ```typescript export const budgetItems = { create: (budgetId: string, data: Partial) => request(`/budgets/${budgetId}/items`, { method: 'POST', body: JSON.stringify(data) }), } ``` From frontend/src/hooks/useTemplate.ts (hook pattern to follow): ```typescript export function useTemplate() { const [template, setTemplate] = useState(null) const [categories, setCategories] = useState([]) const [loading, setLoading] = useState(true) // ... CRUD functions that call API and refresh state return { template, categories, loading, addItem, removeItem, moveItem } } ``` From frontend/src/components/AppLayout.tsx (nav items pattern): ```typescript const navItems = [ { path: '/', label: t('nav.dashboard'), icon: LayoutDashboard }, { path: '/categories', label: t('nav.categories'), icon: Tags }, { path: '/template', label: t('nav.template'), icon: FileText }, { path: '/settings', label: t('nav.settings'), icon: Settings }, ] ``` Task 1: API client, hook, management page, routing, and i18n frontend/src/lib/api.ts, frontend/src/hooks/useQuickAdd.ts, frontend/src/pages/QuickAddPage.tsx, frontend/src/components/AppLayout.tsx, frontend/src/App.tsx, frontend/src/i18n/en.json, frontend/src/i18n/de.json 1. **API client** (`frontend/src/lib/api.ts`): Add QuickAddItem interface after the TemplateDetail interface: ```typescript export interface QuickAddItem { id: string user_id: string name: string icon: string sort_order: number created_at: string updated_at: string } ``` Add quickAdd namespace after the template namespace: ```typescript export const quickAdd = { list: () => request('/quick-add'), create: (data: { name: string; icon: string }) => request('/quick-add', { method: 'POST', body: JSON.stringify(data) }), update: (id: string, data: { name: string; icon: string; sort_order: number }) => request(`/quick-add/${id}`, { method: 'PUT', body: JSON.stringify(data) }), delete: (id: string) => request(`/quick-add/${id}`, { method: 'DELETE' }), } ``` 2. **Hook** (`frontend/src/hooks/useQuickAdd.ts`): Create `useQuickAdd()` hook following the useTemplate pattern: - State: `items: QuickAddItem[]`, `loading: boolean` - On mount: fetch via `quickAdd.list()`, set items (default to empty array) - `addItem(name, icon)`: calls `quickAdd.create()`, refreshes list - `updateItem(id, name, icon, sortOrder)`: calls `quickAdd.update()`, refreshes list - `removeItem(id)`: calls `quickAdd.delete()`, refreshes list - Return: `{ items, loading, addItem, updateItem, removeItem }` 3. **Management page** (`frontend/src/pages/QuickAddPage.tsx`): Follow the TemplatePage pattern (pastel gradient header, table layout): - Header with gradient: `bg-gradient-to-r from-amber-50 to-orange-50` (warm tone for one-offs, distinct from template's violet) - Title from i18n: `t('quickAdd.title')` - Add form row at top: text input for name, text input for icon (emoji or short string), Add button - Table with columns: Name, Icon, Actions (Edit pencil button, Delete trash button) - Edit mode: clicking edit turns row into inline inputs, Save/Cancel buttons - Delete: immediate delete (no confirmation needed for library items — they are presets, not budget data) - Empty state: use EmptyState component with Zap icon, heading "No saved items", subtext "Save your frequently-used one-off categories here for quick access." 4. **Sidebar nav** (`frontend/src/components/AppLayout.tsx`): Add nav item after template: `{ path: '/quick-add', label: t('nav.quickAdd'), icon: Zap }` Import `Zap` from `lucide-react`. 5. **Route** (`frontend/src/App.tsx`): Add route: `} />` Import QuickAddPage. 6. **i18n** — add keys to both en.json and de.json: English (en.json): ```json "nav": { ... "quickAdd": "Quick Add" }, "quickAdd": { "title": "Quick-Add Library", "name": "Name", "icon": "Icon", "addItem": "Add Item", "noItems": "No saved items", "noItemsHint": "Save your frequently-used one-off categories here for quick access.", "editItem": "Edit", "deleteItem": "Remove", "save": "Save", "cancel": "Cancel" } ``` German (de.json): translate equivalently: ```json "nav": { ... "quickAdd": "Schnellzugriff" }, "quickAdd": { "title": "Schnellzugriff-Bibliothek", "name": "Name", "icon": "Symbol", "addItem": "Hinzufuegen", "noItems": "Keine gespeicherten Eintraege", "noItemsHint": "Speichere hier haeufig genutzte Einmal-Kategorien fuer schnellen Zugriff.", "editItem": "Bearbeiten", "deleteItem": "Entfernen", "save": "Speichern", "cancel": "Abbrechen" } ``` cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun run build QuickAddPage renders with add form, table, and empty state. Sidebar shows "Quick Add" nav item. Route /quick-add works. All i18n keys present in both languages. Build succeeds. Task 2: Quick-add picker in dashboard for one-off budget items frontend/src/components/QuickAddPicker.tsx, frontend/src/pages/DashboardPage.tsx 1. **QuickAddPicker component** (`frontend/src/components/QuickAddPicker.tsx`): A dropdown/popover that lets users add a one-off item to the current budget from their quick-add library. Props: ```typescript interface Props { budgetId: string onItemAdded: () => void // callback to refresh budget after adding } ``` Implementation: - On mount, fetch quick-add items via `quickAdd.list()` (direct API call, not hook — this is a lightweight picker, not a full CRUD page) - Render a Popover (from shadcn/ui) with trigger button: icon `Zap` + text "Quick Add" (from i18n `quickAdd.addOneOff`) - Inside popover: list of quick-add items, each as a clickable row showing icon + name - On click: create a budget item via `budgetItems.create(budgetId, { category_id: null, item_tier: 'one_off', notes: item.name })` — Since quick-add items are independent presets (not linked to categories), the picker creates a one-off budget item. The backend CreateBudgetItem handler must accept this. **However**, looking at the existing BudgetItem model, category_id is required (UUID NOT NULL in DB). So instead: - The picker should first check if a category with the same name exists for the user. If not, create one via `categories.create({ name: item.name, type: 'variable_expense', icon: item.icon })`, then create the budget item with that category_id and `item_tier: 'one_off'`. - Alternatively (simpler): show the quick-add library items, and on click, find or create a matching category, then call `budgetItems.create(budgetId, { category_id, item_tier: 'one_off' })`. - After creation: call `onItemAdded()` to refresh, close popover - If quick-add library is empty: show a small message "No saved items" with a link to /quick-add - Add loading spinner on the clicked item while creating Add i18n keys: - en.json: `"quickAdd": { ... "addOneOff": "Quick Add", "emptyPicker": "No saved items", "goToLibrary": "Manage library" }` - de.json: equivalent translations 2. **Wire into DashboardPage** (`frontend/src/pages/DashboardPage.tsx`): - Import QuickAddPicker - Import `categories as categoriesApi` from api.ts - Add QuickAddPicker next to the budget selector (after the "Create Budget" button), only visible when a budget is selected: ```tsx {current && ( selectBudget(current.id)} /> )} ``` - The `onItemAdded` callback re-fetches the current budget to show the new item cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/frontend && bun run build QuickAddPicker renders in dashboard toolbar. Clicking a quick-add item creates a one-off budget item (finding or creating the category first). Popover closes after add. Empty library shows link to management page. Build succeeds. Task 3: Verify complete quick-add library feature Human verifies the complete quick-add library feature: 1. Management page at /quick-add with add/edit/remove for saved one-off categories 2. Quick-add picker button in dashboard toolbar that creates one-off budget items from saved library 3. Sidebar navigation includes Quick Add link Steps to verify: 1. Start the app: `docker compose up --build` 2. Navigate to /quick-add from sidebar — verify empty state shows 3. Add 2-3 items (e.g., "Pharmacy" with pill emoji, "Haircut" with scissors emoji) 4. Verify items appear in the table with edit/delete actions 5. Edit one item name — verify it updates 6. Delete one item — verify it disappears 7. Go to Dashboard, select a budget 8. Click "Quick Add" button in toolbar — verify popover shows your saved items 9. Click one item — verify a new one-off budget item appears in the budget 10. Verify the new item shows with item_tier badge "one-off" All quick-add library features work end-to-end: management page CRUD, picker creates one-off budget items, sidebar nav accessible - `bun run build` succeeds with no TypeScript errors - QuickAddPage renders management UI with CRUD operations - QuickAddPicker creates one-off budget items from library - Sidebar shows Quick Add navigation item - Route /quick-add loads the management page - All i18n keys present in en.json and de.json - User can add, edit, and remove items from the quick-add library page (QADD-03) - User can save a one-off category with icon to their library (QADD-01) - User can browse and select from library when adding one-off items to a budget (QADD-02) - Selected quick-add item creates a one-off budget item in the current budget After completion, create `.planning/phases/07-quick-add-library/07-02-SUMMARY.md`