summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/dashboard/main.go4
-rw-r--r--internal/handlers/handlers.go46
-rw-r--r--internal/store/sqlite.go32
-rw-r--r--issues/bug_004_trello_cards_missing.md31
-rw-r--r--migrations/005_add_bugs.sql6
-rw-r--r--web/templates/index.html42
6 files changed, 161 insertions, 0 deletions
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go
index de3fb7c..7cacd06 100644
--- a/cmd/dashboard/main.go
+++ b/cmd/dashboard/main.go
@@ -137,6 +137,10 @@ func main() {
// Unified Quick Add (for Tasks tab)
r.Post("/unified-add", h.HandleUnifiedAdd)
r.Get("/partials/lists", h.HandleGetListsOptions)
+
+ // Bug reporting
+ r.Get("/bugs", h.HandleGetBugs)
+ r.Post("/bugs", h.HandleReportBug)
})
// Start server
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index d52e786..8d809ae 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -829,3 +829,49 @@ func (h *Handler) HandleGetListsOptions(w http.ResponseWriter, r *http.Request)
fmt.Fprintf(w, `<option value="%s">%s</option>`, list.ID, list.Name)
}
}
+
+// HandleGetBugs returns the list of reported bugs
+func (h *Handler) HandleGetBugs(w http.ResponseWriter, r *http.Request) {
+ bugs, err := h.store.GetBugs()
+ if err != nil {
+ http.Error(w, "Failed to fetch bugs", http.StatusInternalServerError)
+ log.Printf("Error fetching bugs: %v", err)
+ return
+ }
+
+ w.Header().Set("Content-Type", "text/html")
+ if len(bugs) == 0 {
+ fmt.Fprint(w, `<p class="text-gray-500 text-sm">No bugs reported yet.</p>`)
+ return
+ }
+
+ for _, bug := range bugs {
+ fmt.Fprintf(w, `<div class="text-sm border-b border-gray-100 py-2">
+ <p class="text-gray-900">%s</p>
+ <p class="text-gray-400 text-xs">%s</p>
+ </div>`, template.HTMLEscapeString(bug.Description), bug.CreatedAt.Format("Jan 2, 3:04 PM"))
+ }
+}
+
+// HandleReportBug saves a new bug report
+func (h *Handler) HandleReportBug(w http.ResponseWriter, r *http.Request) {
+ if err := r.ParseForm(); err != nil {
+ http.Error(w, "Invalid form data", http.StatusBadRequest)
+ return
+ }
+
+ description := strings.TrimSpace(r.FormValue("description"))
+ if description == "" {
+ http.Error(w, "Description is required", http.StatusBadRequest)
+ return
+ }
+
+ if err := h.store.SaveBug(description); err != nil {
+ http.Error(w, "Failed to save bug", http.StatusInternalServerError)
+ log.Printf("Error saving bug: %v", err)
+ return
+ }
+
+ // Return updated bug list
+ h.HandleGetBugs(w, r)
+}
diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go
index 7961f35..8640b9b 100644
--- a/internal/store/sqlite.go
+++ b/internal/store/sqlite.go
@@ -541,3 +541,35 @@ func (s *Store) ClearSyncToken(service string) error {
_, err := s.db.Exec(`DELETE FROM sync_tokens WHERE service = ?`, service)
return err
}
+
+// Bug represents a user-reported bug
+type Bug struct {
+ ID int64
+ Description string
+ CreatedAt time.Time
+}
+
+// SaveBug saves a new bug report
+func (s *Store) SaveBug(description string) error {
+ _, err := s.db.Exec(`INSERT INTO bugs (description) VALUES (?)`, description)
+ return err
+}
+
+// GetBugs retrieves all bugs, newest first
+func (s *Store) GetBugs() ([]Bug, error) {
+ rows, err := s.db.Query(`SELECT id, description, created_at FROM bugs ORDER BY created_at DESC`)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ var bugs []Bug
+ for rows.Next() {
+ var b Bug
+ if err := rows.Scan(&b.ID, &b.Description, &b.CreatedAt); err != nil {
+ return nil, err
+ }
+ bugs = append(bugs, b)
+ }
+ return bugs, rows.Err()
+}
diff --git a/issues/bug_004_trello_cards_missing.md b/issues/bug_004_trello_cards_missing.md
new file mode 100644
index 0000000..c882424
--- /dev/null
+++ b/issues/bug_004_trello_cards_missing.md
@@ -0,0 +1,31 @@
+# Bug: Trello cards not showing on all boards
+
+## Status
+Open
+
+## Symptoms
+- Only one board shows cards
+- Other boards with cards appear empty
+- Refresh works (no 403 error) but doesn't fix the issue
+- No errors in server logs
+
+## Investigation Notes
+- CSRF fix applied - refresh now works
+- API client code looks correct (fetches all boards, then cards per board)
+- Cache logic appears correct (SaveBoards clears and replaces all data)
+- Need to investigate:
+ - Is the Trello API returning all cards?
+ - Is there a filter issue (`visible` vs `all`)?
+ - Board permissions issue on Trello side?
+ - Race condition in concurrent card fetching?
+
+## Reproduction
+1. Login to dashboard
+2. Go to Planning tab
+3. Observe: only one board has cards, others are empty
+4. Compare with actual Trello - should have cards on multiple boards
+
+## Next Steps
+- Add logging to trace Trello API responses
+- Check if specific boards are returning empty card arrays
+- Verify Trello API credentials have access to all boards
diff --git a/migrations/005_add_bugs.sql b/migrations/005_add_bugs.sql
new file mode 100644
index 0000000..999b1f2
--- /dev/null
+++ b/migrations/005_add_bugs.sql
@@ -0,0 +1,6 @@
+-- Bug tracking table
+CREATE TABLE IF NOT EXISTS bugs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ description TEXT NOT NULL,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);
diff --git a/web/templates/index.html b/web/templates/index.html
index 617ccfe..2cd4c59 100644
--- a/web/templates/index.html
+++ b/web/templates/index.html
@@ -84,6 +84,48 @@
</div>
</div>
+ <!-- Bug Report Button -->
+ <button onclick="document.getElementById('bug-modal').classList.remove('hidden')"
+ class="fixed bottom-4 right-4 bg-red-500 hover:bg-red-600 text-white p-3 rounded-full shadow-lg no-print"
+ title="Report a bug">
+ 🐛
+ </button>
+
+ <!-- Bug Report Modal -->
+ <div id="bug-modal" class="hidden fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50">
+ <div class="bg-white rounded-lg shadow-xl max-w-md w-full max-h-[80vh] overflow-hidden">
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
+ <h2 class="font-semibold text-gray-900">Report Bug</h2>
+ <button onclick="document.getElementById('bug-modal').classList.add('hidden')"
+ class="text-gray-400 hover:text-gray-600">✕</button>
+ </div>
+ <div class="p-4">
+ <form hx-post="/bugs"
+ hx-target="#bug-list"
+ hx-swap="innerHTML"
+ hx-on::after-request="if(event.detail.successful) this.reset()">
+ <textarea name="description"
+ placeholder="Describe the bug..."
+ class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm mb-3 h-24"
+ required></textarea>
+ <button type="submit"
+ class="w-full bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium">
+ Submit Bug Report
+ </button>
+ </form>
+ <div class="mt-4 border-t border-gray-200 pt-4">
+ <h3 class="text-sm font-medium text-gray-700 mb-2">Recent Reports</h3>
+ <div id="bug-list"
+ class="max-h-48 overflow-y-auto"
+ hx-get="/bugs"
+ hx-trigger="load">
+ <p class="text-gray-400 text-sm">Loading...</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
<script src="/static/js/htmx.min.js"></script>
<script src="/static/js/app.js"></script>
</body>