218 lines
8.6 KiB
Markdown
218 lines
8.6 KiB
Markdown
---
|
|
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:
|
|
- "A quick_add_items table exists with user_id, name, icon, and sort_order columns"
|
|
- "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"
|
|
---
|
|
|
|
<objective>
|
|
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
|
|
</objective>
|
|
|
|
<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>
|
|
|
|
<context>
|
|
@.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
|
|
|
|
<interfaces>
|
|
<!-- Existing patterns the executor must follow -->
|
|
|
|
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
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Migration, model, and query functions</name>
|
|
<files>backend/migrations/003_quick_add_library.sql, backend/internal/models/models.go, backend/internal/db/queries.go</files>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/backend && go vet ./...</automated>
|
|
</verify>
|
|
<done>QuickAddItem struct compiles, all four query functions compile, migration file exists with CREATE TABLE statement</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: HTTP handlers and route registration</name>
|
|
<files>backend/internal/api/handlers.go, backend/internal/api/router.go</files>
|
|
<action>
|
|
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)
|
|
})
|
|
```
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/jean-luc-makiola/Development/projects/SimpleFinanceDash/backend && go vet ./... && go build ./cmd/server</automated>
|
|
</verify>
|
|
<done>All four handlers compile, routes registered under /api/quick-add, go build succeeds with no errors</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- `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)
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- 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
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/07-quick-add-library/07-01-SUMMARY.md`
|
|
</output>
|