summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SESSION_STATE.md33
-rw-r--r--cmd/dashboard/main.go4
-rw-r--r--internal/handlers/handlers.go90
-rw-r--r--issues/phase3_step3_trello_ui.md36
-rw-r--r--web/templates/partials/trello-board.html82
-rw-r--r--web/templates/partials/trello-boards.html29
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>