Skip to content

Plan: CreatorTrack Calendar overhaul (Google-style sidebar, per-sub-calendar visibility, second Google account, visual replica, embeddable component)

URL: https://mkdocs.justinsforge.com/memory/plans/creatortrack-calendar-overhaul-2026-07-07/

Date: 2026-07-07 Approved spec: Overhaul the CreatorTrack Calendar app to mirror Google Calendar. Replace the narrow icon rail with a full left sidebar (+Create, mini-month picker, My/Other calendars checkbox list, bottom Settings). Extend visibility from per-account to per-sub-calendar (each Google sub-calendar Birthdays/Holidays/Tasks/shared gets its own colored checkbox), with a "Synced (all on)" master toggle, persisted per-workspace. Add an "+ Add account" action forcing Google's account chooser so a separate business Google account can be attached. Refine the week/day grid to visually match Google (time gutter, hour lines, current-time line, colored stacked event blocks); default view becomes Week. Extract the grid into a reusable component and add a read-only mini-calendar app_segment block for embedding elsewhere. Branch: feat/calendar-overhaul in /home/justinwieb/creatortrack. Dev server :3060 (creatortrack-dev). Migrations applied to dev clone first, then prod on deploy.

Out of scope: - Two-way write to non-primary Google sub-calendars (still write to primary/local only). - Booking pages, "Search for people", invitations. - Recurring-event editing UI beyond current behavior. - Drag-create inside the read-only embed. - Month/Agenda visual overhaul (Week/Day is the replica target; Month stays as-is this round).

Task list

Task 1: Branch + baseline dev server

  • Files: (none; git) branch feat/calendar-overhaul
  • What: Create the feature branch off main; confirm creatortrack-dev builds the current calendar so we have a working baseline.
  • Verification: cd /home/justinwieb/creatortrack && git checkout -b feat/calendar-overhaul && curl -fsS http://127.0.0.1:3060/ -o /dev/null && echo OK
  • Commit: (no commit; branch only)

Task 2: Migration - per-source discovered calendar list + per-calendar visibility

  • Files: db/migrations/<stamp>_calendar_per_subcalendar_visibility.sql
  • What: New table app.calendar_source_calendars (user_id, source_account, calendar_id, summary, bg_color text, is_primary bool, updated_at; unique (user_id, source_account, calendar_id)) caching each connected account's calendarList (name+color). Visibility stays in the existing users.prefs.calendar_app.workspace_sources.<wsId> but the array widens to hold google:<id>#<calendar_id> calendar-level keys (account-level key still accepted for back-compat). Idempotent DDL + RLS via core.apply_workspace_rls.
  • Verification: WORKSPACE_MIGRATIONS_DIR=db/migrations PGDATABASE=<dev-clone> python3 /home/justinwieb/forge/scripts/forge_workspace_migrate.py --status | tail shows it pending, then apply and re-query constraints.
  • Commit: feat(calendar): migration for per-sub-calendar visibility + cached calendar list

Task 3: Backend - discover + store each account's calendar list

  • Files: lib/google-calendar.ts
  • What: Add syncCalendarList(me, source) calling Google calendarList.list, upserting rows into app.calendar_source_calendars (summary, backgroundColor, primary). Called on connect callback and on calendar open. Fail-soft: on error, keep prior rows and fall back to primary-only.
  • Verification: cd /home/justinwieb/creatortrack && node -e "require('ts-node/register'); ..." OR dev: open calendar, then python3 -c "...select summary,bg_color from app.calendar_source_calendars" shows Birthdays/Holidays/Tasks rows for the connected account.
  • Commit: feat(calendar): discover and cache Google sub-calendar list per connection

Task 4: Backend - per-calendar sources API + sync loop

  • Files: lib/google-calendar.ts, app/api/apps/calendar/route.ts
  • What: Extend workspaceSourcesFor/calendarSourcesFor to return per-sub-calendar entries { key: 'google:<id>#<calId>', name, color, group: 'mine'|'other', enabled }. Extend syncCalendarRange to iterate each ENABLED sub-calendar (events.list per calendarId, its own calendar_sync_state row keyed by source_account='google:<id>' + calendar_id). Widen the set_sources op to accept calendar-level keys.
  • Verification: curl -fsS -b <devcookie> 'http://127.0.0.1:3060/api/apps/calendar?...' | jq '.sources' returns per-calendar entries with colors + enabled flags.
  • Commit: feat(calendar): per-sub-calendar sync + workspace visibility keys

Task 5: Sidebar shell - layout + mini-month + Create

  • Files: components/apps/calendar/CalendarSidebar.tsx (new), components/apps/calendar/CalendarClient.tsx, components/apps/calendar/calendar.css
  • What: New CalendarSidebar replacing the icon rail: + Create (opens EventEditorModal), mini-month picker (reuses calendarMath; clicking a day sets anchor), view switch (Day/Week/Month/Agenda/Time), placeholder for calendar list (Task 6). Wire into CalendarClient cal-root grid.
  • Verification: Dev: load /n/<calendarNodeId>, sidebar renders; clicking a mini-month day moves the main grid anchor; Create opens the editor.
  • Commit: feat(calendar): Google-style left sidebar shell (mini-month + create + view switch)

Task 6: Sidebar - My/Other calendars checklist + colors + master toggle

  • Files: components/apps/calendar/CalendarSidebar.tsx, components/apps/calendar/CalendarClient.tsx, components/apps/calendar/calendar.css
  • What: Render sources grouped into "My calendars" (primary/owned + local) and "Other calendars" (holidays/shared), each a checkbox + color swatch + name. A top "Synced (all on)" master toggle flips every calendar. Reuse existing optimistic toggleSource flow, extended to calendar-level keys; add master toggle op. Bottom ⚙ Settings button opens CalendarSettingsModal.
  • Verification: Dev: toggling "Holidays" hides holiday events without touching other calendars; master toggle flips all; reload persists (pref saved).
  • Commit: feat(calendar): sidebar calendar checklist with colors + all-on master toggle

Task 7: Second Google account - "+ Add account" with account chooser

  • Files: app/api/google/connect/route.ts, lib/google-connections.ts, components/apps/calendar/CalendarSidebar.tsx
  • What: Add select_account=1 param to the connect route that adds prompt=select_account consent to the authorize URL so Google shows the account picker instead of silently reusing the signed-in account. Sidebar "+ Add account" link hits /api/google/connect?groups=calendar&select_account=1&redirect=/n/<id>. On callback, run syncCalendarList for the new connection.
  • Verification: Dev: click "+ Add account", Google shows the account chooser; after granting a different account, its calendars appear under a second account group in the checklist.
  • Commit: feat(calendar): add second Google account via forced account chooser

Task 8: Week/Day visual replica - grid, gutter, current-time line, event blocks

  • Files: components/apps/calendar/WeekView.tsx, components/apps/calendar/DayView.tsx, components/apps/calendar/calendar.css
  • What: Refine to match the Google screenshot: left time gutter (labeled hours), full hour gridlines, GMT offset label, red current-time line with dot on today's column, event blocks colored by their calendar's color, correct overlap/stacking, rounded blocks with title + time. Keep existing drag/resize/click wiring.
  • Verification: /home/justinwieb/forge/skills screenshot: bash <screenshot skill> OR mcp playwright capture of /n/<id>?view=week compared side-by-side with the reference; current-time line sits at the right offset for now.
  • Commit: feat(calendar): Google-style week/day grid (gutter, hour lines, now-line, colored blocks)

Task 9: Default view = Week + sidebar polish

  • Files: components/apps/calendar/CalendarApp.tsx, components/apps/calendar/calendarMath.ts, components/apps/calendar/calendar.css
  • What: Default initialView to week when no view param; verify week-start pref still honored; responsive collapse of sidebar under narrow widths.
  • Verification: Dev: open calendar with no query params -> lands on Week; narrow the viewport -> sidebar collapses without breaking the grid.
  • Commit: feat(calendar): default to week view + responsive sidebar

Task 10: Extract reusable CalendarGrid component

  • Files: components/apps/calendar/CalendarGrid.tsx (new), components/apps/calendar/WeekView.tsx, components/apps/calendar/DayView.tsx
  • What: Pull the pure grid (days, events, gutter, now-line, block layout) into a presentational CalendarGrid taking { days, events, onEventClick, interactive }. Week/Day views become thin wrappers. interactive=false disables drag/resize/create for embed use.
  • Verification: Dev: full calendar still works (Week/Day render via CalendarGrid, drag/resize intact when interactive).
  • Commit: refactor(calendar): extract presentational CalendarGrid (interactive flag)

Task 11: Read-only mini-calendar app_segment block

  • Files: components/workspace/blocks/segments/CalendarWeekSegment.tsx (new), components/workspace/blocks/appSegmentDefs.ts, components/workspace/blocks/AppSegmentPicker.tsx, components/workspace/BlockRenderer.tsx
  • What: New app_segment "Calendar - This week" rendering a compact read-only week/agenda via CalendarGrid interactive={false} (or an agenda list), events click-through deep-link to /n/<calendarNodeId>. Register in appSegmentDefs + picker so it can be inserted into any page, mirroring the existing CalendarTodaySegment wiring.
  • Verification: Dev: on a project page, insert the "Calendar - This week" segment; it shows this week's events read-only; clicking an event opens the Calendar app.
  • Commit: feat(calendar): embeddable read-only week segment for app_segment blocks

Task 12: Deploy to prod + verify live sync

  • Files: (deploy) scripts/forge_creatortrack_deploy.sh
  • What: Merge feat/calendar-overhaul to main (single merge, per WDA rule), apply migrations to prod DB, deploy prod, verify a real Google sync end-to-end.
  • Verification: bash /home/justinwieb/forge/scripts/forge_creatortrack_deploy.sh then load https://app.creatortrack.ai/n/<calendarNodeId> -> Week view, sidebar checklist with real sub-calendars, toggle Holidays persists, no console/sync errors.
  • Commit: (merge commit) feat(calendar): ship Google-style calendar overhaul

Task 13: Register and document

  • Files: MEMORY.md (Claude project index), memory/general/reference_creatortrack_calendar.md
  • What: Update the memory index + write/refresh the calendar topic file (sidebar model, per-sub-calendar visibility keys google:<id>#<calId>, second-account chooser, CalendarGrid embed, app_segment week block). Note the per-calendar workspace_sources key format so future sessions don't regress it.
  • Verification: grep -q "google:<id>#" /home/justinwieb/forge/memory/general/reference_creatortrack_calendar.md && echo OK
  • Commit: docs(calendar): topic file + memory index for calendar overhaul