diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 08:56:26 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 08:56:26 -1000 |
| commit | 2292dff2d8d6f4b43dad8dffd3d559f7c1e5bb35 (patch) | |
| tree | 3d4749226897b410292ea1858327d76464fecc1a /internal/handlers/tabs.go | |
| parent | 043f48c12eb4dfc410e8724b430166000d7cb905 (diff) | |
Implement 4-Tab Architecture with unified Atom model
Diffstat (limited to 'internal/handlers/tabs.go')
| -rw-r--r-- | internal/handlers/tabs.go | 178 |
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) + } +} |
