From e97a1bc259d3aa91956ec73a522421cdb621ae57 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Thu, 22 Jan 2026 15:28:06 -1000 Subject: Add Google Calendar integration - Add GoogleCalendarClient for fetching upcoming events - Add GoogleCalendarAPI interface and CalendarEvent model - Add config for GOOGLE_CREDENTIALS_FILE and GOOGLE_CALENDAR_ID - Display events in Planning tab with date/time formatting - Update handlers and tests to support optional calendar client Config env vars: - GOOGLE_CREDENTIALS_FILE: Path to service account JSON - GOOGLE_CALENDAR_ID: Calendar ID (defaults to "primary") Co-Authored-By: Claude Opus 4.5 --- internal/handlers/tabs.go | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'internal/handlers/tabs.go') diff --git a/internal/handlers/tabs.go b/internal/handlers/tabs.go index 2f22c44..b651dac 100644 --- a/internal/handlers/tabs.go +++ b/internal/handlers/tabs.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "task-dashboard/internal/api" "task-dashboard/internal/models" "task-dashboard/internal/store" ) @@ -46,12 +47,13 @@ func atomUrgencyTier(a models.Atom) int { // TabsHandler handles tab-specific rendering with Atom model type TabsHandler struct { - store *store.Store - templates *template.Template + store *store.Store + googleCalendarClient api.GoogleCalendarAPI + templates *template.Template } // NewTabsHandler creates a new TabsHandler instance -func NewTabsHandler(store *store.Store, templateDir string) *TabsHandler { +func NewTabsHandler(store *store.Store, googleCalendarClient api.GoogleCalendarAPI, templateDir string) *TabsHandler { // Parse templates including partials tmpl, err := template.ParseGlob(filepath.Join(templateDir, "*.html")) if err != nil { @@ -65,8 +67,9 @@ func NewTabsHandler(store *store.Store, templateDir string) *TabsHandler { } return &TabsHandler{ - store: store, - templates: tmpl, + store: store, + googleCalendarClient: googleCalendarClient, + templates: tmpl, } } @@ -178,12 +181,25 @@ func (h *TabsHandler) HandlePlanning(w http.ResponseWriter, r *http.Request) { return } + // Fetch Google Calendar events + var events []models.CalendarEvent + if h.googleCalendarClient != nil { + var err error + events, err = h.googleCalendarClient.GetUpcomingEvents(r.Context(), 10) + if err != nil { + log.Printf("Error fetching calendar events: %v", err) + // Don't fail the whole request, just show empty events + } + } + data := struct { Boards []models.Board Projects []models.Project + Events []models.CalendarEvent }{ Boards: boards, Projects: []models.Project{}, // Empty for now + Events: events, } if err := h.templates.ExecuteTemplate(w, "planning-tab", data); err != nil { -- cgit v1.2.3