summaryrefslogtreecommitdiff
path: root/internal/handlers/handlers.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/handlers/handlers.go')
-rw-r--r--internal/handlers/handlers.go65
1 files changed, 63 insertions, 2 deletions
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index c384c48..f0f2a19 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -1258,7 +1258,8 @@ func (h *Handler) HandleShoppingQuickAdd(w http.ResponseWriter, r *http.Request)
return
}
if storeName == "" {
- storeName = "Quick Add"
+ JSONError(w, http.StatusBadRequest, "Store is required", nil)
+ return
}
if err := h.store.SaveUserShoppingItem(name, storeName); err != nil {
@@ -1380,10 +1381,12 @@ func (h *Handler) HandleShoppingMode(w http.ResponseWriter, r *http.Request) {
StoreName string
Items []models.UnifiedShoppingItem
StoreNames []string
+ CSRFToken string
}{
StoreName: storeName,
Items: items,
StoreNames: storeNames,
+ CSRFToken: auth.GetCSRFTokenFromContext(ctx),
}
HTMLResponse(w, h.templates, "shopping-mode.html", data)
@@ -1450,6 +1453,64 @@ func (h *Handler) HandleShoppingModeToggle(w http.ResponseWriter, r *http.Reques
}{storeName, items})
}
+// HandleShoppingModeComplete removes an item from the shopping list
+func (h *Handler) HandleShoppingModeComplete(w http.ResponseWriter, r *http.Request) {
+ storeName := chi.URLParam(r, "store")
+ if err := r.ParseForm(); err != nil {
+ JSONError(w, http.StatusBadRequest, "Failed to parse form", err)
+ return
+ }
+
+ id := r.FormValue("id")
+ source := r.FormValue("source")
+
+ // Complete (remove) the item
+ switch source {
+ case "user":
+ var userID int64
+ if _, err := fmt.Sscanf(id, "user-%d", &userID); err != nil {
+ JSONError(w, http.StatusBadRequest, "Invalid user item ID", err)
+ return
+ }
+ if err := h.store.DeleteUserShoppingItem(userID); err != nil {
+ JSONError(w, http.StatusInternalServerError, "Failed to delete item", err)
+ return
+ }
+ case "trello", "plantoeat":
+ // Mark as checked (will be filtered out of view)
+ if err := h.store.SetShoppingItemChecked(source, id, true); err != nil {
+ JSONError(w, http.StatusInternalServerError, "Failed to complete item", err)
+ return
+ }
+ }
+
+ // URL decode the store name
+ storeName, _ = url.QueryUnescape(storeName)
+
+ // Return updated item list partial
+ ctx := r.Context()
+ allStores := h.aggregateShoppingLists(ctx)
+
+ var items []models.UnifiedShoppingItem
+ for _, store := range allStores {
+ if strings.EqualFold(store.Name, storeName) {
+ for _, cat := range store.Categories {
+ items = append(items, cat.Items...)
+ }
+ }
+ }
+
+ // Sort alphabetically (checked items already filtered in template)
+ sort.Slice(items, func(i, j int) bool {
+ return items[i].Name < items[j].Name
+ })
+
+ HTMLResponse(w, h.templates, "shopping-mode-items", struct {
+ StoreName string
+ Items []models.UnifiedShoppingItem
+ }{storeName, items})
+}
+
// aggregateShoppingLists combines Trello, PlanToEat, and user shopping items by store
func (h *Handler) aggregateShoppingLists(ctx context.Context) []models.ShoppingStore {
storeMap := make(map[string]map[string][]models.UnifiedShoppingItem)
@@ -1535,7 +1596,7 @@ func (h *Handler) aggregateShoppingLists(ctx context.Context) []models.ShoppingS
for _, item := range userItems {
storeName := item.Store
if storeName == "" {
- storeName = "Quick Add"
+ continue // Skip items without a store
}
if storeMap[storeName] == nil {