From b42f7b13bb4f0ec10bec82f18bdb1ca8dc6988fa Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Thu, 12 Mar 2026 13:34:19 +0100 Subject: [PATCH] feat(07-01): add quick-add HTTP handlers and route registration - Add ListQuickAddItems, CreateQuickAddItem, UpdateQuickAddItem, DeleteQuickAddItem handler methods - Register GET/POST /api/quick-add and PUT/DELETE /api/quick-add/{itemId} in authenticated route group - Validate name non-empty on create and update; return 404 on update when item not found --- backend/internal/api/handlers.go | 82 ++++++++++++++++++++++++++++++++ backend/internal/api/router.go | 7 +++ 2 files changed, 89 insertions(+) diff --git a/backend/internal/api/handlers.go b/backend/internal/api/handlers.go index d716906..1247486 100644 --- a/backend/internal/api/handlers.go +++ b/backend/internal/api/handlers.go @@ -636,6 +636,88 @@ func (h *Handlers) GenerateBudget(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusCreated, detail) } +// Quick Add Item Handlers + +func (h *Handlers) ListQuickAddItems(w http.ResponseWriter, r *http.Request) { + userID := auth.UserIDFromContext(r.Context()) + items, err := h.queries.ListQuickAddItems(r.Context(), userID) + if err != nil { + writeError(w, http.StatusInternalServerError, "failed to list quick add items") + return + } + writeJSON(w, http.StatusOK, items) +} + +func (h *Handlers) CreateQuickAddItem(w http.ResponseWriter, r *http.Request) { + userID := auth.UserIDFromContext(r.Context()) + var req struct { + Name string `json:"name"` + Icon string `json:"icon"` + } + if err := decodeJSON(r, &req); err != nil { + writeError(w, http.StatusBadRequest, "invalid request body") + return + } + + if req.Name == "" { + writeError(w, http.StatusBadRequest, "name is required") + return + } + + item, err := h.queries.CreateQuickAddItem(r.Context(), userID, req.Name, req.Icon) + if err != nil { + writeError(w, http.StatusInternalServerError, "failed to create quick add item") + return + } + writeJSON(w, http.StatusCreated, item) +} + +func (h *Handlers) UpdateQuickAddItem(w http.ResponseWriter, r *http.Request) { + userID := auth.UserIDFromContext(r.Context()) + itemID, err := parseUUID(chi.URLParam(r, "itemId")) + if err != nil { + writeError(w, http.StatusBadRequest, "invalid item id") + return + } + + var req struct { + Name string `json:"name"` + Icon string `json:"icon"` + SortOrder int `json:"sort_order"` + } + if err := decodeJSON(r, &req); err != nil { + writeError(w, http.StatusBadRequest, "invalid request body") + return + } + + if req.Name == "" { + writeError(w, http.StatusBadRequest, "name is required") + return + } + + item, err := h.queries.UpdateQuickAddItem(r.Context(), itemID, userID, req.Name, req.Icon, req.SortOrder) + if err != nil { + writeError(w, http.StatusNotFound, "quick add item not found") + return + } + writeJSON(w, http.StatusOK, item) +} + +func (h *Handlers) DeleteQuickAddItem(w http.ResponseWriter, r *http.Request) { + userID := auth.UserIDFromContext(r.Context()) + itemID, err := parseUUID(chi.URLParam(r, "itemId")) + if err != nil { + writeError(w, http.StatusBadRequest, "invalid item id") + return + } + + if err := h.queries.DeleteQuickAddItem(r.Context(), itemID, userID); err != nil { + writeError(w, http.StatusInternalServerError, "failed to delete quick add item") + return + } + w.WriteHeader(http.StatusNoContent) +} + // Settings Handlers func (h *Handlers) GetSettings(w http.ResponseWriter, r *http.Request) { diff --git a/backend/internal/api/router.go b/backend/internal/api/router.go index dfd4ffb..d354538 100644 --- a/backend/internal/api/router.go +++ b/backend/internal/api/router.go @@ -70,6 +70,13 @@ func NewRouter(queries *db.Queries, sessionSecret string, frontendFS fs.FS) http r.Delete("/items/{itemId}", h.DeleteTemplateItem) }) + 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) + }) + r.Get("/api/settings", h.GetSettings) r.Put("/api/settings", h.UpdateSettings) })