14 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 07-quick-add-library | 02 | execute | 2 |
|
|
false |
|
|
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
<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>
@.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):
{
"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:
export const budgetItems = {
create: (budgetId: string, data: Partial<BudgetItem>) =>
request<BudgetItem>(`/budgets/${budgetId}/items`, { method: 'POST', body: JSON.stringify(data) }),
}
From frontend/src/hooks/useTemplate.ts (hook pattern to follow):
export function useTemplate() {
const [template, setTemplate] = useState<TemplateDetail | null>(null)
const [categories, setCategories] = useState<Category[]>([])
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):
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 },
]
-
Hook (
frontend/src/hooks/useQuickAdd.ts): CreateuseQuickAdd()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): callsquickAdd.create(), refreshes listupdateItem(id, name, icon, sortOrder): callsquickAdd.update(), refreshes listremoveItem(id): callsquickAdd.delete(), refreshes list- Return:
{ items, loading, addItem, updateItem, removeItem }
- State:
-
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."
- Header with gradient:
-
Sidebar nav (
frontend/src/components/AppLayout.tsx): Add nav item after template:{ path: '/quick-add', label: t('nav.quickAdd'), icon: Zap }ImportZapfromlucide-react. -
Route (
frontend/src/App.tsx): Add route:<Route path="/quick-add" element={<QuickAddPage />} />Import QuickAddPage. -
i18n — add keys to both en.json and de.json: English (en.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:
"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" }
Props:
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 i18nquickAdd.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 anditem_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' }).
- The picker should first check if a category with the same name exists for the user. If not, create one via
- 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
- Wire into DashboardPage (
frontend/src/pages/DashboardPage.tsx):- Import QuickAddPicker
- Import
categories as categoriesApifrom api.ts - Add QuickAddPicker next to the budget selector (after the "Create Budget" button), only visible when a budget is selected:
{current && ( <QuickAddPicker budgetId={current.id} onItemAdded={() => selectBudget(current.id)} /> )} - The
onItemAddedcallback 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.
<success_criteria>
- 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 </success_criteria>