diff options
| -rw-r--r-- | cmd/dashboard/main.go | 4 | ||||
| -rw-r--r-- | internal/handlers/handlers.go | 46 | ||||
| -rw-r--r-- | internal/store/sqlite.go | 32 | ||||
| -rw-r--r-- | issues/bug_004_trello_cards_missing.md | 31 | ||||
| -rw-r--r-- | migrations/005_add_bugs.sql | 6 | ||||
| -rw-r--r-- | web/templates/index.html | 42 |
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> |
