summaryrefslogtreecommitdiff
path: root/internal/handlers/handlers.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-12 14:28:50 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-12 14:28:50 -1000
commit06c7485a7d05de86f9898e388161e8d932d5f3e6 (patch)
tree376083a75278c9758f53c0062742062dedb75633 /internal/handlers/handlers.go
parent9ef5b7f37883f846f105da9dc5d2ba1415e594e3 (diff)
Modernize frontend with tabs, HTMX, and Tailwind build pipeline
Complete UI overhaul implementing modern design patterns with HTMX for dynamic updates, proper Tailwind build pipeline, and improved UX. Build Pipeline: - Add npm + PostCSS + Tailwind CSS configuration - Custom design system with brand colors - Compiled CSS: 27KB (vs 3MB CDN), 99% reduction - Makefile for unified build commands - Inter font for improved typography Tab Interface: - Separate Tasks tab from Notes tab using HTMX - Partial page updates without full refreshes - Tab state management with proper refresh handling - New endpoints: /tabs/tasks, /tabs/notes, /tabs/refresh Template Architecture: - Modular partials system (7 reusable components) - Cleaner separation of concerns Empty Board Management: - Active boards in main 3-column grid - Empty boards in collapsible section - Reduces visual clutter Visual Design Enhancements: - Inter font, brand color accents - Improved typography hierarchy and spacing - Enhanced card styling with hover effects Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/handlers/handlers.go')
-rw-r--r--internal/handlers/handlers.go67
1 files changed, 66 insertions, 1 deletions
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index 6872ba7..f31fc56 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -28,12 +28,18 @@ type Handler struct {
// New creates a new Handler instance
func New(store *store.Store, todoist api.TodoistAPI, trello api.TrelloAPI, obsidian api.ObsidianAPI, planToEat api.PlanToEatAPI, cfg *config.Config) *Handler {
- // Parse templates
+ // 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 &Handler{
store: store,
todoistClient: todoist,
@@ -136,6 +142,65 @@ func (h *Handler) HandleGetBoards(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(boards)
}
+// HandleTasksTab renders the tasks tab content (Trello + Todoist + PlanToEat)
+func (h *Handler) HandleTasksTab(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ data, err := h.aggregateData(ctx, false)
+ if err != nil {
+ http.Error(w, "Failed to load tasks", http.StatusInternalServerError)
+ log.Printf("Error loading tasks tab: %v", err)
+ return
+ }
+
+ 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)
+ }
+}
+
+// HandleNotesTab renders the notes tab content (Obsidian)
+func (h *Handler) HandleNotesTab(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ data, err := h.aggregateData(ctx, false)
+ if err != nil {
+ http.Error(w, "Failed to load notes", http.StatusInternalServerError)
+ log.Printf("Error loading notes tab: %v", err)
+ return
+ }
+
+ 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)
+ }
+}
+
+// HandleRefreshTab refreshes and re-renders the specified tab
+func (h *Handler) HandleRefreshTab(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ tab := r.URL.Query().Get("tab") // "tasks" or "notes"
+
+ // Force refresh
+ data, err := h.aggregateData(ctx, true)
+ if err != nil {
+ http.Error(w, "Failed to refresh", http.StatusInternalServerError)
+ log.Printf("Error refreshing tab: %v", err)
+ return
+ }
+
+ // Determine template to render
+ templateName := "tasks-tab"
+ if tab == "notes" {
+ templateName = "notes-tab"
+ }
+
+ if err := h.templates.ExecuteTemplate(w, templateName, data); err != nil {
+ http.Error(w, "Failed to render template", http.StatusInternalServerError)
+ log.Printf("Error rendering refreshed tab: %v", err)
+ }
+}
+
// aggregateData fetches and caches data from all sources
func (h *Handler) aggregateData(ctx context.Context, forceRefresh bool) (*models.DashboardData, error) {
data := &models.DashboardData{