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 existingusers.prefs.calendar_app.workspace_sources.<wsId>but the array widens to holdgoogle:<id>#<calendar_id>calendar-level keys (account-level key still accepted for back-compat). Idempotent DDL + RLS viacore.apply_workspace_rls. - Verification:
WORKSPACE_MIGRATIONS_DIR=db/migrations PGDATABASE=<dev-clone> python3 /home/justinwieb/forge/scripts/forge_workspace_migrate.py --status | tailshows 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 GooglecalendarList.list, upserting rows intoapp.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, thenpython3 -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/calendarSourcesForto return per-sub-calendar entries{ key: 'google:<id>#<calId>', name, color, group: 'mine'|'other', enabled }. ExtendsyncCalendarRangeto iterate each ENABLED sub-calendar (events.list per calendarId, its owncalendar_sync_staterow keyed bysource_account='google:<id>'+calendar_id). Widen theset_sourcesop 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
CalendarSidebarreplacing 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 intoCalendarClientcal-rootgrid. - 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
toggleSourceflow, extended to calendar-level keys; add master toggle op. Bottom⚙ Settingsbutton 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=1param to the connect route that addsprompt=select_account consentto 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, runsyncCalendarListfor 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/skillsscreenshot:bash <screenshot skill>ORmcp playwrightcapture of/n/<id>?view=weekcompared 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
initialViewtoweekwhen noviewparam; 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
CalendarGridtaking{ days, events, onEventClick, interactive }. Week/Day views become thin wrappers.interactive=falsedisables 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 existingCalendarTodaySegmentwiring. - 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-overhaulto 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.shthen loadhttps://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-calendarworkspace_sourceskey 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