docs(07-quick-add-library): create phase plan
This commit is contained in:
217
.planning/phases/07-quick-add-library/07-01-PLAN.md
Normal file
217
.planning/phases/07-quick-add-library/07-01-PLAN.md
Normal file
@@ -0,0 +1,217 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user