diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-22 15:28:06 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-22 15:31:50 -1000 |
| commit | e97a1bc259d3aa91956ec73a522421cdb621ae57 (patch) | |
| tree | a9f424ef97673c0dd7da8cee6044413e6417b626 /internal/handlers | |
| parent | db5deb2330448c75ba6b719e6a397832362b222a (diff) | |
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 <noreply@anthropic.com>
Diffstat (limited to 'internal/handlers')
| -rw-r--r-- | internal/handlers/handlers.go | 44 | ||||
| -rw-r--r-- | internal/handlers/heuristic_test.go | 2 | ||||
| -rw-r--r-- | internal/handlers/tab_state_test.go | 2 | ||||
| -rw-r--r-- | internal/handlers/tabs.go | 26 |
4 files changed, 54 insertions, 20 deletions
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 19415c7..1cb978d 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -22,16 +22,17 @@ import ( // Handler holds dependencies for HTTP handlers type Handler struct { - store *store.Store - todoistClient api.TodoistAPI - trelloClient api.TrelloAPI - planToEatClient api.PlanToEatAPI - config *config.Config - templates *template.Template + store *store.Store + todoistClient api.TodoistAPI + trelloClient api.TrelloAPI + planToEatClient api.PlanToEatAPI + googleCalendarClient api.GoogleCalendarAPI + config *config.Config + templates *template.Template } // New creates a new Handler instance -func New(s *store.Store, todoist api.TodoistAPI, trello api.TrelloAPI, planToEat api.PlanToEatAPI, cfg *config.Config) *Handler { +func New(s *store.Store, todoist api.TodoistAPI, trello api.TrelloAPI, planToEat api.PlanToEatAPI, googleCalendar api.GoogleCalendarAPI, cfg *config.Config) *Handler { // Parse templates including partials tmpl, err := template.ParseGlob(filepath.Join(cfg.TemplateDir, "*.html")) if err != nil { @@ -45,12 +46,13 @@ func New(s *store.Store, todoist api.TodoistAPI, trello api.TrelloAPI, planToEat } return &Handler{ - store: s, - todoistClient: todoist, - trelloClient: trello, - planToEatClient: planToEat, - config: cfg, - templates: tmpl, + store: s, + todoistClient: todoist, + trelloClient: trello, + planToEatClient: planToEat, + googleCalendarClient: googleCalendar, + config: cfg, + templates: tmpl, } } @@ -279,6 +281,22 @@ func (h *Handler) aggregateData(ctx context.Context, forceRefresh bool) (*models }() } + // Fetch Google Calendar events (if configured) + if h.googleCalendarClient != nil { + wg.Add(1) + go func() { + defer wg.Done() + events, err := h.googleCalendarClient.GetUpcomingEvents(ctx, 10) + mu.Lock() + defer mu.Unlock() + if err != nil { + data.Errors = append(data.Errors, "Google Calendar: "+err.Error()) + } else { + data.Events = events + } + }() + } + wg.Wait() // Filter Trello cards into tasks based on heuristic diff --git a/internal/handlers/heuristic_test.go b/internal/handlers/heuristic_test.go index dc8620a..2b70218 100644 --- a/internal/handlers/heuristic_test.go +++ b/internal/handlers/heuristic_test.go @@ -63,7 +63,7 @@ func TestHandleTasks_Heuristic(t *testing.T) { } // Create Handler - h := NewTabsHandler(db, "../../web/templates") + h := NewTabsHandler(db, nil, "../../web/templates") // Skip if templates are not loaded if h.templates == nil { diff --git a/internal/handlers/tab_state_test.go b/internal/handlers/tab_state_test.go index a4f6d23..d7bb8dd 100644 --- a/internal/handlers/tab_state_test.go +++ b/internal/handlers/tab_state_test.go @@ -30,7 +30,7 @@ func TestHandleDashboard_TabState(t *testing.T) { } // Create handler - h := New(db, todoistClient, trelloClient, nil, cfg) + h := New(db, todoistClient, trelloClient, nil, nil, cfg) // Skip if templates are not loaded (test environment issue) if h.templates == nil { 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 { |
