--- phase: 07-quick-add-library plan: 01 type: execute wave: 1 depends_on: [] files_modified: - backend/migrations/003_quick_add_library.sql - backend/internal/models/models.go - backend/internal/db/queries.go - backend/internal/api/handlers.go - backend/internal/api/router.go autonomous: true requirements: [QADD-01, QADD-03] must_haves: truths: - "The API accepts and returns quick-add items with name, icon, and sort_order in all CRUD responses" - "API returns a list of quick-add items for the authenticated user" - "API can create, update, and delete quick-add items" - "Quick-add items are user-scoped — one user cannot see another's items" artifacts: - path: "backend/migrations/003_quick_add_library.sql" provides: "quick_add_items table DDL" contains: "CREATE TABLE quick_add_items" - path: "backend/internal/models/models.go" provides: "QuickAddItem Go struct" contains: "QuickAddItem" - path: "backend/internal/db/queries.go" provides: "CRUD query functions for quick-add items" exports: ["ListQuickAddItems", "CreateQuickAddItem", "UpdateQuickAddItem", "DeleteQuickAddItem"] - path: "backend/internal/api/handlers.go" provides: "HTTP handlers for quick-add CRUD" contains: "ListQuickAddItems" - path: "backend/internal/api/router.go" provides: "Route registrations under /api/quick-add" contains: "/api/quick-add" key_links: - from: "backend/internal/api/router.go" to: "backend/internal/api/handlers.go" via: "handler method references" pattern: "h\\..*QuickAdd" - from: "backend/internal/api/handlers.go" to: "backend/internal/db/queries.go" via: "query function calls" pattern: "q\\..*QuickAdd" --- Create the backend data model and REST API for the quick-add library feature. Purpose: Users need a persistent library of saved one-off expense categories (name + icon) that they can reuse across months. This plan creates the database table, Go model, query functions, and HTTP endpoints. Output: Migration file, QuickAddItem model, CRUD queries, REST handlers at /api/quick-add @/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 @backend/internal/models/models.go @backend/internal/db/queries.go @backend/internal/api/handlers.go @backend/internal/api/router.go @backend/migrations/002_templates.sql From backend/internal/models/models.go: ```go // Category struct pattern — QuickAddItem should follow same shape type Category struct { ID uuid.UUID `json:"id"` UserID uuid.UUID `json:"user_id"` Name string `json:"name"` Type CategoryType `json:"type"` Icon string `json:"icon"` SortOrder int `json:"sort_order"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } ``` From backend/internal/api/router.go: ```go // Route group pattern — quick-add follows same structure r.Route("/api/template", func(r chi.Router) { r.Get("/", h.GetTemplate) r.Put("/", h.UpdateTemplateName) r.Post("/items", h.CreateTemplateItem) // ... }) ``` From backend/internal/api/handlers.go: ```go // Handler struct — all handlers are methods on this type Handlers struct { queries *db.Queries sessionSecret string } // Helper functions available: writeJSON, writeError, auth.UserIDFromContext ``` Task 1: Migration, model, and query functions backend/migrations/003_quick_add_library.sql, backend/internal/models/models.go, backend/internal/db/queries.go 1. Create migration `backend/migrations/003_quick_add_library.sql`: ```sql CREATE TABLE quick_add_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(100) NOT NULL, icon VARCHAR(50) NOT NULL DEFAULT '', sort_order INT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_quick_add_items_user ON quick_add_items(user_id); ``` The table stores saved one-off category presets (name + icon). No FK to categories — these are independent presets the user can pick from when adding a one-off budget item. 2. Add `QuickAddItem` struct to `backend/internal/models/models.go`: ```go type QuickAddItem struct { ID uuid.UUID `json:"id"` UserID uuid.UUID `json:"user_id"` Name string `json:"name"` Icon string `json:"icon"` SortOrder int `json:"sort_order"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } ``` 3. Add four query functions to `backend/internal/db/queries.go` (append after template section): - `ListQuickAddItems(ctx, userID) ([]QuickAddItem, error)` — SELECT ordered by sort_order, returns empty slice (not nil) when none exist - `CreateQuickAddItem(ctx, userID, name, icon string) (*QuickAddItem, error)` — INSERT with sort_order = (SELECT COALESCE(MAX(sort_order),0)+1), RETURNING all columns - `UpdateQuickAddItem(ctx, id, userID, name, icon string, sortOrder int) (*QuickAddItem, error)` — UPDATE with WHERE id=$1 AND user_id=$2, return error if no rows affected - `DeleteQuickAddItem(ctx, id, userID) error` — DELETE with WHERE id=$1 AND user_id=$2 Follow existing query patterns: use context parameter, userID scoping in WHERE clause, fmt.Errorf wrapping, pgx row scanning. cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/backend && go vet ./... QuickAddItem struct compiles, all four query functions compile, migration file exists with CREATE TABLE statement Task 2: HTTP handlers and route registration backend/internal/api/handlers.go, backend/internal/api/router.go 1. Add four handler methods to `backend/internal/api/handlers.go`: - `ListQuickAddItems(w, r)` — GET, extracts userID from context, calls queries.ListQuickAddItems, returns JSON array (200) - `CreateQuickAddItem(w, r)` — POST, accepts JSON `{name: string, icon: string}`, validates name is non-empty (400 if missing), calls queries.CreateQuickAddItem, returns created item (201) - `UpdateQuickAddItem(w, r)` — PUT, extracts itemId from chi URL param, accepts JSON `{name: string, icon: string, sort_order: int}`, validates name non-empty, calls queries.UpdateQuickAddItem, returns updated item (200). Return 404 if no rows affected. - `DeleteQuickAddItem(w, r)` — DELETE, extracts itemId from chi URL param, calls queries.DeleteQuickAddItem, returns 204 Follow existing handler patterns: - Use `auth.UserIDFromContext(r.Context())` for userID - Use `chi.URLParam(r, "itemId")` for path params - Use `writeJSON(w, status, data)` and `writeError(w, status, message)` - Parse UUID with `uuid.Parse()`, return 400 on invalid 2. Register routes in `backend/internal/api/router.go` inside the authenticated group (after the template route block): ```go r.Route("/api/quick-add", func(r chi.Router) { r.Get("/", h.ListQuickAddItems) r.Post("/", h.CreateQuickAddItem) r.Put("/{itemId}", h.UpdateQuickAddItem) r.Delete("/{itemId}", h.DeleteQuickAddItem) }) ``` cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/backend && go vet ./... && go build ./cmd/server All four handlers compile, routes registered under /api/quick-add, go build succeeds with no errors - `go vet ./...` passes with no issues - `go build ./cmd/server` produces binary without errors - Migration file 003 exists and has valid SQL - QuickAddItem struct has json tags matching API contract - All handlers use userID scoping (no cross-user data leak) - Backend compiles and builds successfully - Migration creates quick_add_items table with correct schema - Four REST endpoints exist: GET/POST /api/quick-add, PUT/DELETE /api/quick-add/{itemId} - All endpoints require authentication (inside authenticated route group) - All queries scope by user_id After completion, create `.planning/phases/07-quick-add-library/07-01-SUMMARY.md`