Skip to content

CreatorTrack Calendar app

URL: https://mkdocs.justinsforge.com/memory/general/reference_creatortrack_calendar/

Overhauled 2026-07-07 (branch feat/calendar-overhaul, merged to main, LIVE on prod). Node type calendar; prod node 5671 in workspace 67 (justin), URL app.creatortrack.ai/n/5671. Server bridge lib/google-calendar.ts (never import client-side); data layer lib/calendar.ts; API app/api/apps/calendar/route.ts; UI under components/apps/calendar/.

Google-style sidebar

CalendarSidebar.tsx renders inside AppSidebar's panel slot (the calendar rail item is panel: true; time stays rail-only). Has: + Create, mini-month picker (drives anchor; snaps to anchor on external nav via the render-time prev-prop pattern, NOT a setState-effect), "Synced (all calendars)" master toggle, My/Other calendars checklist with per-calendar colour swatches, and Add-Google-account + Settings footer. Default view is week (matches Google). WeekView draws the red current-time line client-only (SSR renders none → no hydration mismatch).

Per-sub-calendar visibility (CANONICAL)

Visibility is per sub-calendar, not per account. The visibility key is <sourceKey>::<calendarId> where sourceKey ∈ personal|business|google:<connId>; split on the FIRST :: (Google calendarIds contain @ # . but never ::). Helper calVisKey().

Two per-user prefs under core.users.prefs.calendar_app: - workspace_calendars.<wsId> = string[] of enabled calVisKeys → the DISPLAY filter (lib/calendar.ts prunes events by it) AND the source of truth for the checklist. - workspace_sources.<wsId> = string[] of enabled source keys → DERIVED from the calendars (a source syncs when ≥1 of its calendars is on). Drives which accounts syncCalendarRange pulls. setWorkspaceCalendarPref writes both. Default when unset: legacy workspace_sources if present, else workspace kind (personal = all on, brand/shared = all off, so a brand workspace never surfaces personal calendars unasked).

app.calendar_source_calendars (migration 20260707T164600, per-user RLS like api_connections) caches each connection's calendarList (summary, bg_color, access_role, is_primary) so the checklist renders all calendars with stable colours + My(owner/writer/primary)/Other(reader) grouping. Populated by syncCalendarList / refreshAllCalendarLists.

Second Google account

Sidebar "Add Google account" → /api/google/connect?groups=calendar&select_account=1&redirect=.... select_account=1 sets prompt=select_account consent (buildAuthorizeUrl, keeps consent so a refresh token is still issued) so a SEPARATE account attaches instead of reusing the signed-in one. Connections are per-user (multi-account); each becomes its own google:<id> source. Only works on prod (https://app.creatortrack.ai/api/google/connect/callback is the registered redirect; a dev-host http/IP redirect gets Google 400 invalid_request). GCP project + OAuth details: [[creatortrack-google-oauth-project]].

Embeddable component

CalendarWeekSegment.tsx = read-only "This week" agenda (next 7 days grouped by day), registered as app_segment calendar/calendar/week (appSegmentDefs.ts + AppSegmentBlock.tsx map) beside the existing today segment. Reuses the cached GET plane; never syncs/mutates from an embed. (The interactive WeekView was NOT extracted into a shared grid — deferred; the embed is a purpose-built read-only view.)

Rate-limit hardening (2026-07-07, after Google rateLimitExceeded)

  • refreshAllCalendarLists only re-fetches a source's calendarList when its cached rows are stale (>6h) or missing, instead of per-account on every open.
  • set_calendars only runs a range sync when a calendar was NEWLY enabled; disable/master-off is served from cache (verified 57ms vs 966ms).

Plan: memory/plans/creatortrack-calendar-overhaul-2026-07-07.md. Related: [[creatortrack-google-oauth-project]], sync-state CHECK twin-table trap (see LESSONS.md 2026-07-07).