diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-22 21:00:38 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-22 21:00:38 -1000 |
| commit | 539122f3c80fe1f27348f0ddfc7fd428a58384b8 (patch) | |
| tree | 069eadd594ac20df355f52fc13d435dcd26104f6 | |
| parent | bedcd8a185e4197d087b2166afa79ff4dce25f7c (diff) | |
Add shopping quick-add feature
- Add Shopping tab to action modal (Ctrl+K)
- Add /partials/shopping-lists endpoint to fetch lists from Shopping board
- Store dropdown populated dynamically from Trello
- Items added via existing unified-add endpoint
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| -rw-r--r-- | cmd/dashboard/main.go | 1 | ||||
| -rw-r--r-- | internal/handlers/handlers.go | 36 | ||||
| -rw-r--r-- | web/templates/index.html | 52 |
3 files changed, 88 insertions, 1 deletions
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go index 7645048..da49165 100644 --- a/cmd/dashboard/main.go +++ b/cmd/dashboard/main.go @@ -148,6 +148,7 @@ func main() { // Unified Quick Add (for Tasks tab) r.Post("/unified-add", h.HandleUnifiedAdd) r.Get("/partials/lists", h.HandleGetListsOptions) + r.Get("/partials/shopping-lists", h.HandleGetShoppingLists) // Task detail/edit r.Get("/tasks/detail", h.HandleGetTaskDetail) diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 84a00fd..c44e771 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -1004,6 +1004,42 @@ func (h *Handler) HandleGetTaskDetail(w http.ResponseWriter, r *http.Request) { w.Write([]byte(html)) } +// HandleGetShoppingLists returns the lists from the Shopping board for quick-add +func (h *Handler) HandleGetShoppingLists(w http.ResponseWriter, r *http.Request) { + boards, err := h.store.GetBoards() + if err != nil { + http.Error(w, "Failed to get boards", http.StatusInternalServerError) + return + } + + // Find the Shopping board + var shoppingBoardID string + for _, b := range boards { + if strings.EqualFold(b.Name, "Shopping") { + shoppingBoardID = b.ID + break + } + } + + if shoppingBoardID == "" { + http.Error(w, "Shopping board not found", http.StatusNotFound) + return + } + + // Get lists for the shopping board + lists, err := h.trelloClient.GetLists(r.Context(), shoppingBoardID) + if err != nil { + http.Error(w, "Failed to get lists", http.StatusInternalServerError) + log.Printf("Error fetching shopping lists: %v", err) + return + } + + w.Header().Set("Content-Type", "text/html") + for _, list := range lists { + fmt.Fprintf(w, `<option value="%s">%s</option>`, list.ID, list.Name) + } +} + // HandleUpdateTask updates a task description func (h *Handler) HandleUpdateTask(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/web/templates/index.html b/web/templates/index.html index f525a09..32c0857 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -87,7 +87,11 @@ <div class="flex gap-2"> <button onclick="switchActionTab('add')" id="tab-add" class="px-3 py-1 rounded-lg text-sm font-medium bg-white/20 text-white"> - ✓ Quick Add + ✓ Task + </button> + <button onclick="switchActionTab('shopping')" id="tab-shopping" + class="px-3 py-1 rounded-lg text-sm font-medium text-white/60 hover:bg-white/10"> + 🛒 Shopping </button> <button onclick="switchActionTab('bug')" id="tab-bug" class="px-3 py-1 rounded-lg text-sm font-medium text-white/60 hover:bg-white/10"> @@ -125,6 +129,30 @@ </form> </div> + <!-- Shopping Tab --> + <div id="panel-shopping" class="p-4 hidden"> + <form hx-post="/unified-add" + hx-swap="none" + hx-on::after-request="if(event.detail.successful) { this.reset(); closeActionModal(); htmx.trigger(document.body, 'refresh-tasks'); }"> + <input type="hidden" name="source" value="trello"> + <input type="text" + name="title" + placeholder="Item name..." + class="w-full bg-black/40 border border-white/20 rounded-lg px-3 py-2 text-sm text-white placeholder-white/50 mb-3" + required> + <select name="list_id" + id="shopping-list-select" + class="w-full bg-black/40 border border-white/20 rounded-lg px-3 py-2 text-sm text-white mb-3" + required> + <option value="">Select store...</option> + </select> + <button type="submit" + class="w-full bg-green-900/50 hover:bg-green-900/70 text-green-300 px-4 py-2 rounded-lg text-sm font-medium"> + Add to Shopping List + </button> + </form> + </div> + <!-- Bug Report Tab --> <div id="panel-bug" class="p-4 hidden"> <form hx-post="/bugs" @@ -171,13 +199,35 @@ } function switchActionTab(tab) { document.getElementById('panel-add').classList.toggle('hidden', tab !== 'add'); + document.getElementById('panel-shopping').classList.toggle('hidden', tab !== 'shopping'); document.getElementById('panel-bug').classList.toggle('hidden', tab !== 'bug'); + // Task tab styling document.getElementById('tab-add').classList.toggle('bg-white/20', tab === 'add'); document.getElementById('tab-add').classList.toggle('text-white', tab === 'add'); document.getElementById('tab-add').classList.toggle('text-white/60', tab !== 'add'); + // Shopping tab styling + document.getElementById('tab-shopping').classList.toggle('bg-green-900/50', tab === 'shopping'); + document.getElementById('tab-shopping').classList.toggle('text-green-300', tab === 'shopping'); + document.getElementById('tab-shopping').classList.toggle('text-white/60', tab !== 'shopping'); + // Bug tab styling document.getElementById('tab-bug').classList.toggle('bg-red-900/50', tab === 'bug'); document.getElementById('tab-bug').classList.toggle('text-red-300', tab === 'bug'); document.getElementById('tab-bug').classList.toggle('text-white/60', tab !== 'bug'); + // Load shopping lists when switching to shopping tab + if (tab === 'shopping') { + loadShoppingLists(); + } + } + function loadShoppingLists() { + var select = document.getElementById('shopping-list-select'); + if (select.options.length <= 1) { + fetch('/partials/shopping-lists') + .then(function(r) { return r.text(); }) + .then(function(html) { + select.innerHTML = '<option value="">Select store...</option>' + html; + }) + .catch(function(e) { console.error('Failed to load shopping lists:', e); }); + } } // Keyboard shortcut: Ctrl+K or Cmd+K document.addEventListener('keydown', function(e) { |
