diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 14:09:50 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 14:09:50 -1000 |
| commit | 2fee76ea41f37e3a068273c05a98b892ab29228c (patch) | |
| tree | d063a5785a5d51d806b9cbb72298a7d0768be494 | |
| parent | 0fda0e9e4b0c6a73be513987264329e4515170f1 (diff) | |
Add Trello card creation and completion UI (Phase 3 Step 3)
Implement interactive Trello card management with HTMX:
Frontend:
- Create trello-board.html partial with add card form
- Add collapsible form with list selector and card title input
- Add completion checkbox on each card
- Update trello-boards.html to use new partial
- Use HTMX for seamless partial updates (hx-post, hx-swap)
Backend:
- Add HandleCreateCard: creates card and re-renders board
- Add HandleCompleteCard: marks card as closed
- Register /cards and /cards/complete POST routes
Features:
- Add cards to any list via dropdown
- Mark cards complete with checkbox (removes from view)
- Real-time board updates without full page reload
- Glassmorphism styling for form
All tests pass. Full Trello write operations now available in UI.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
| -rw-r--r-- | SESSION_STATE.md | 33 | ||||
| -rw-r--r-- | cmd/dashboard/main.go | 4 | ||||
| -rw-r--r-- | internal/handlers/handlers.go | 90 | ||||
| -rw-r--r-- | issues/phase3_step3_trello_ui.md | 36 | ||||
| -rw-r--r-- | web/templates/partials/trello-board.html | 82 | ||||
| -rw-r--r-- | web/templates/partials/trello-boards.html | 29 |
6 files changed, 231 insertions, 43 deletions
diff --git a/SESSION_STATE.md b/SESSION_STATE.md index ce4619e..7fd2eaa 100644 --- a/SESSION_STATE.md +++ b/SESSION_STATE.md @@ -1,23 +1,26 @@ # Session State -## Current Phase -Phase 3: Interactivity & Write Operations +**Current Phase:** Phase 3: Interactivity & Write Operations +**Current Step:** Step 3: Trello UI & Handlers -## Active Task -Step 2: Trello Lists Support (Backend) +## Active Issues +* `issues/phase3_step3_trello_ui.md` (In Progress) -## Recent Completed Tasks -* [x] Phase 2.5: Glassmorphism UI (CSS/HTML) -* [x] Phase 3 Step 1: Trello Write Ops (Create/Update Card) +## Completed Issues +* `issues/bug_002_tab_state.md` (Resolved) +* `issues/bug_001_template_rendering.md` (Resolved) +* `issues/phase3_step1_trello_write.md` (Resolved) +* `issues/phase3_step2_trello_lists.md` (Resolved) + +## Recent Changes +* **Trello API:** Implemented `CreateCard` and `UpdateCard`. +* **Trello Lists:** Added `Lists` support to Board model and API. +* **UI:** Glassmorphism overhaul (Phase 2.5) completed. ## Next Steps -1. **Implement Trello Lists Support** (Current) - * Update models and API client to fetch and expose lists. -2. **Trello UI Integration** - * Add "Add Card" button and modal. - * Add "Done" checkbox. -3. **Todoist Write Ops** - * Implement Create/Complete Task. +1. **Implement Trello UI:** Create partials and handlers for adding/completing cards. +2. **Todoist Write Ops:** Implement `CreateTask` and `CompleteTask`. +3. **Unified Quick Add:** Implement global command bar. ## Context -We are adding write capabilities. We just implemented `CreateCard` and `UpdateCard` in the Trello client. Now we need to expose the Lists so the UI can present a dropdown for "Add Card". +We are turning the read-only dashboard into an interactive tool. We just finished the backend plumbing for Trello (Write Ops + List fetching). Now we are connecting it to the frontend. diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go index 3f46e8d..a307484 100644 --- a/cmd/dashboard/main.go +++ b/cmd/dashboard/main.go @@ -77,6 +77,10 @@ func main() { r.Get("/tabs/meals", tabsHandler.HandleMeals) r.Post("/tabs/refresh", h.HandleRefreshTab) + // Trello card operations + r.Post("/cards", h.HandleCreateCard) + r.Post("/cards/complete", h.HandleCompleteCard) + // Serve static files fileServer := http.FileServer(http.Dir("web/static")) r.Handle("/static/*", http.StripPrefix("/static/", fileServer)) diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index ed100fc..8762035 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -438,3 +438,93 @@ func (h *Handler) fetchBoards(ctx context.Context, forceRefresh bool) ([]models. return boards, nil } + +// HandleCreateCard creates a new Trello card +func (h *Handler) HandleCreateCard(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + // Parse form data + if err := r.ParseForm(); err != nil { + http.Error(w, "Failed to parse form", http.StatusBadRequest) + log.Printf("Error parsing form: %v", err) + return + } + + boardID := r.FormValue("board_id") + listID := r.FormValue("list_id") + name := r.FormValue("name") + + if boardID == "" || listID == "" || name == "" { + http.Error(w, "Missing required fields", http.StatusBadRequest) + return + } + + // Create the card + _, err := h.trelloClient.CreateCard(ctx, listID, name, "", nil) + if err != nil { + http.Error(w, "Failed to create card", http.StatusInternalServerError) + log.Printf("Error creating card: %v", err) + return + } + + // Force refresh to get updated data + data, err := h.aggregateData(ctx, true) + if err != nil { + http.Error(w, "Failed to refresh data", http.StatusInternalServerError) + log.Printf("Error refreshing data: %v", err) + return + } + + // Find the specific board + var targetBoard *models.Board + for i := range data.Boards { + if data.Boards[i].ID == boardID { + targetBoard = &data.Boards[i] + break + } + } + + if targetBoard == nil { + http.Error(w, "Board not found", http.StatusNotFound) + return + } + + // Render the updated board + if err := h.templates.ExecuteTemplate(w, "trello-board", targetBoard); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Error rendering board template: %v", err) + } +} + +// HandleCompleteCard marks a Trello card as complete +func (h *Handler) HandleCompleteCard(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + // Parse form data + if err := r.ParseForm(); err != nil { + http.Error(w, "Failed to parse form", http.StatusBadRequest) + log.Printf("Error parsing form: %v", err) + return + } + + cardID := r.FormValue("card_id") + + if cardID == "" { + http.Error(w, "Missing card_id", http.StatusBadRequest) + return + } + + // Mark card as closed (complete) + updates := map[string]interface{}{ + "closed": true, + } + + if err := h.trelloClient.UpdateCard(ctx, cardID, updates); err != nil { + http.Error(w, "Failed to complete card", http.StatusInternalServerError) + log.Printf("Error completing card: %v", err) + return + } + + // Return empty response (card will be removed from DOM) + w.WriteHeader(http.StatusOK) +} diff --git a/issues/phase3_step3_trello_ui.md b/issues/phase3_step3_trello_ui.md new file mode 100644 index 0000000..92dd8a5 --- /dev/null +++ b/issues/phase3_step3_trello_ui.md @@ -0,0 +1,36 @@ +# Phase 3 Step 3: Trello UI & Handlers + +**Status:** Open +**Priority:** High +**Created:** 2024-05-22 + +## Description +Implement the UI and backend handlers to enable creating and completing Trello cards directly from the dashboard. + +## Requirements + +### 1. UI Updates +* **Refactor:** Extract individual board rendering into a new partial `web/templates/partials/trello-board.html`. +* **Add Card:** Add a "Quick Add" form to each board (using `<details>` for simplicity) that allows selecting a List and entering a Name. +* **Complete Card:** Add a checkbox to each card that marks it as complete (archives/closes it). + +### 2. Backend Handlers +* `HandleCreateCard`: + * POST `/cards` + * Params: `board_id`, `list_id`, `name` + * Action: Call `CreateCard` API. + * Response: Re-render the specific board partial with updated data. +* `HandleCompleteCard`: + * POST `/cards/complete` + * Params: `card_id` + * Action: Call `UpdateCard` API (set `closed=true`). + * Response: Empty string (removes the card from UI via HTMX). + +### 3. Routing +* Register the new routes in `cmd/dashboard/main.go`. + +## Implementation Plan +1. Create `web/templates/partials/trello-board.html`. +2. Update `web/templates/partials/trello-boards.html`. +3. Implement handlers in `internal/handlers/handlers.go`. +4. Register routes in `cmd/dashboard/main.go`. diff --git a/web/templates/partials/trello-board.html b/web/templates/partials/trello-board.html new file mode 100644 index 0000000..0b176cd --- /dev/null +++ b/web/templates/partials/trello-board.html @@ -0,0 +1,82 @@ +{{define "trello-board"}} +<div class="board-card"> + <!-- Board Header --> + <div class="flex items-center justify-between mb-4"> + <h3 class="font-bold text-lg text-gray-900">{{.Name}}</h3> + </div> + + <!-- Add Card Form (Collapsible) --> + {{if .Lists}} + <details class="mb-4"> + <summary class="cursor-pointer text-sm text-indigo-600 hover:text-indigo-800 font-medium transition-colors"> + + Add Card + </summary> + <form hx-post="/cards" + hx-target="closest .board-card" + hx-swap="outerHTML" + class="mt-3 space-y-2 bg-white/40 p-3 rounded-lg"> + <input type="hidden" name="board_id" value="{{.ID}}"> + + <select name="list_id" + required + class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"> + <option value="">Select list...</option> + {{range .Lists}} + <option value="{{.ID}}">{{.Name}}</option> + {{end}} + </select> + + <input type="text" + name="name" + placeholder="Card title" + required + class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"> + + <button type="submit" + class="w-full bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"> + Add Card + </button> + </form> + </details> + {{end}} + + <!-- Cards List --> + <div class="space-y-3 max-h-96 overflow-y-auto scrollbar-thin"> + {{range .Cards}} + <div class="trello-card-item"> + <div class="flex items-start gap-2"> + <!-- Complete Checkbox --> + <input type="checkbox" + hx-post="/cards/complete" + hx-vals='{"card_id": "{{.ID}}"}' + hx-target="closest .trello-card-item" + hx-swap="outerHTML" + class="mt-1 h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer"> + + <div class="flex-1"> + <p class="font-medium text-sm text-gray-900 mb-2">{{.Name}}</p> + <div class="flex flex-wrap gap-2 items-center"> + {{if .ListName}} + <span class="badge bg-gray-100 text-gray-700"> + {{.ListName}} + </span> + {{end}} + {{if .DueDate}} + <span class="badge bg-red-100 text-red-800"> + Due: {{.DueDate.Format "Jan 2"}} + </span> + {{end}} + {{if .URL}} + <a href="{{.URL}}" target="_blank" + class="text-trello hover:text-trello/80 text-xs font-medium ml-auto transition-colors"> + View → + </a> + {{end}} + </div> + </div> + </div> + </div> + {{end}} + </div> +</div> +{{end}} diff --git a/web/templates/partials/trello-boards.html b/web/templates/partials/trello-boards.html index bd460cf..3d42517 100644 --- a/web/templates/partials/trello-boards.html +++ b/web/templates/partials/trello-boards.html @@ -11,34 +11,7 @@ <div class="card-grid mb-6"> {{range .Boards}} {{if .Cards}} - <div class="board-card"> - <h3 class="font-bold text-lg text-gray-900 mb-4">{{.Name}}</h3> - <div class="space-y-3 max-h-96 overflow-y-auto scrollbar-thin"> - {{range .Cards}} - <div class="trello-card-item"> - <p class="font-medium text-sm text-gray-900 mb-2">{{.Name}}</p> - <div class="flex flex-wrap gap-2 items-center"> - {{if .ListName}} - <span class="badge bg-gray-100 text-gray-700"> - {{.ListName}} - </span> - {{end}} - {{if .DueDate}} - <span class="badge bg-red-100 text-red-800"> - Due: {{.DueDate.Format "Jan 2"}} - </span> - {{end}} - {{if .URL}} - <a href="{{.URL}}" target="_blank" - class="text-trello hover:text-trello/80 text-xs font-medium ml-auto transition-colors"> - View → - </a> - {{end}} - </div> - </div> - {{end}} - </div> - </div> + {{template "trello-board" .}} {{end}} {{end}} </div> |
