summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-13 08:56:26 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-13 08:56:26 -1000
commit2292dff2d8d6f4b43dad8dffd3d559f7c1e5bb35 (patch)
tree3d4749226897b410292ea1858327d76464fecc1a /internal
parent043f48c12eb4dfc410e8724b430166000d7cb905 (diff)
Implement 4-Tab Architecture with unified Atom model
Diffstat (limited to 'internal')
-rw-r--r--internal/handlers/tabs.go178
1 files changed, 178 insertions, 0 deletions
diff --git a/internal/handlers/tabs.go b/internal/handlers/tabs.go
new file mode 100644
index 0000000..fa60a10
--- /dev/null
+++ b/internal/handlers/tabs.go
@@ -0,0 +1,178 @@
+package handlers
+
+import (
+ "html/template"
+ "log"
+ "net/http"
+ "sort"
+ "time"
+
+ "task-dashboard/internal/models"
+ "task-dashboard/internal/store"
+)
+
+// TabsHandler handles tab-specific rendering with Atom model
+type TabsHandler struct {
+ store *store.Store
+ templates *template.Template
+}
+
+// NewTabsHandler creates a new TabsHandler instance
+func NewTabsHandler(store *store.Store) *TabsHandler {
+ // Parse templates including partials
+ tmpl, err := template.ParseGlob("web/templates/*.html")
+ if err != nil {
+ log.Printf("Warning: failed to parse templates: %v", err)
+ }
+
+ // Also parse partials
+ tmpl, err = tmpl.ParseGlob("web/templates/partials/*.html")
+ if err != nil {
+ log.Printf("Warning: failed to parse partial templates: %v", err)
+ }
+
+ return &TabsHandler{
+ store: store,
+ templates: tmpl,
+ }
+}
+
+// HandleTasks renders the unified Tasks tab (Todoist + Trello cards with due dates)
+func (h *TabsHandler) HandleTasks(w http.ResponseWriter, r *http.Request) {
+ // Fetch Todoist tasks
+ tasks, err := h.store.GetTasks()
+ if err != nil {
+ http.Error(w, "Failed to fetch tasks", http.StatusInternalServerError)
+ log.Printf("Error fetching tasks: %v", err)
+ return
+ }
+
+ // Fetch Trello boards
+ boards, err := h.store.GetBoards()
+ if err != nil {
+ http.Error(w, "Failed to fetch boards", http.StatusInternalServerError)
+ log.Printf("Error fetching boards: %v", err)
+ return
+ }
+
+ // Convert to Atoms
+ atoms := make([]models.Atom, 0)
+
+ // Convert Todoist tasks
+ for _, task := range tasks {
+ if !task.Completed {
+ atoms = append(atoms, models.TaskToAtom(task))
+ }
+ }
+
+ // Convert Trello cards with due dates
+ for _, board := range boards {
+ for _, card := range board.Cards {
+ if card.DueDate != nil {
+ atoms = append(atoms, models.CardToAtom(card))
+ }
+ }
+ }
+
+ // Sort atoms: by DueDate (earliest first), then by Priority (descending)
+ sort.SliceStable(atoms, func(i, j int) bool {
+ // Handle nil due dates (push to end)
+ if atoms[i].DueDate == nil && atoms[j].DueDate != nil {
+ return false
+ }
+ if atoms[i].DueDate != nil && atoms[j].DueDate == nil {
+ return true
+ }
+
+ // Both have due dates, sort by date
+ if atoms[i].DueDate != nil && atoms[j].DueDate != nil {
+ if !atoms[i].DueDate.Equal(*atoms[j].DueDate) {
+ return atoms[i].DueDate.Before(*atoms[j].DueDate)
+ }
+ }
+
+ // Same due date (or both nil), sort by priority (descending)
+ return atoms[i].Priority > atoms[j].Priority
+ })
+
+ // Render template
+ data := struct {
+ Atoms []models.Atom
+ }{
+ Atoms: atoms,
+ }
+
+ if err := h.templates.ExecuteTemplate(w, "tasks-tab", data); err != nil {
+ http.Error(w, "Failed to render template", http.StatusInternalServerError)
+ log.Printf("Error rendering tasks tab: %v", err)
+ }
+}
+
+// HandlePlanning renders the Planning tab (Trello boards)
+func (h *TabsHandler) HandlePlanning(w http.ResponseWriter, r *http.Request) {
+ // Fetch Trello boards
+ boards, err := h.store.GetBoards()
+ if err != nil {
+ http.Error(w, "Failed to fetch boards", http.StatusInternalServerError)
+ log.Printf("Error fetching boards: %v", err)
+ return
+ }
+
+ data := struct {
+ Boards []models.Board
+ }{
+ Boards: boards,
+ }
+
+ if err := h.templates.ExecuteTemplate(w, "planning-tab", data); err != nil {
+ http.Error(w, "Failed to render template", http.StatusInternalServerError)
+ log.Printf("Error rendering planning tab: %v", err)
+ }
+}
+
+// HandleNotes renders the Notes tab (Obsidian notes)
+func (h *TabsHandler) HandleNotes(w http.ResponseWriter, r *http.Request) {
+ // Fetch Obsidian notes
+ notes, err := h.store.GetNotes(20)
+ if err != nil {
+ http.Error(w, "Failed to fetch notes", http.StatusInternalServerError)
+ log.Printf("Error fetching notes: %v", err)
+ return
+ }
+
+ data := struct {
+ Notes []models.Note
+ }{
+ Notes: notes,
+ }
+
+ if err := h.templates.ExecuteTemplate(w, "notes-tab", data); err != nil {
+ http.Error(w, "Failed to render template", http.StatusInternalServerError)
+ log.Printf("Error rendering notes tab: %v", err)
+ }
+}
+
+// HandleMeals renders the Meals tab (PlanToEat)
+func (h *TabsHandler) HandleMeals(w http.ResponseWriter, r *http.Request) {
+ // Fetch meals for next 7 days
+ startDate := time.Now()
+ endDate := startDate.AddDate(0, 0, 7)
+
+ meals, err := h.store.GetMeals(startDate, endDate)
+ if err != nil {
+ http.Error(w, "Failed to fetch meals", http.StatusInternalServerError)
+ log.Printf("Error fetching meals: %v", err)
+ return
+ }
+
+ data := struct {
+ Meals []models.Meal
+ }{
+ Meals: meals,
+ }
+
+ if err := h.templates.ExecuteTemplate(w, "meals-tab", data); err != nil {
+ http.Error(w, "Failed to render template", http.StatusInternalServerError)
+ log.Printf("Error rendering meals tab: %v", err)
+ }
+}