Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
8.1 KiB
phase: 02-calendar-display plan: "03" subsystem: pwa-foundation tags: [token-layer, schedule-x, temporal, color-utils, calendar-config, hydrate-events, zustand, fetch-client] dependency_graph: requires: ["02-01"] provides: [css-token-layer, sx-color-overrides, colorUtils, calendarConfig, hydrateEvents, calendarStore, windowed-fetchEvents, temporal-polyfill-global] affects: ["02-04", "02-05"] tech_stack: added: - "@schedule-x/calendar@4.6.0" - "@schedule-x/react@4.1.0" - "@schedule-x/theme-default@4.6.0" - "@schedule-x/event-modal@4.6.0" - "@schedule-x/events-service@4.6.0" - "temporal-polyfill@0.3.2" - "lucide-react@1.17.0" patterns: - CSS custom property token layer (clean theme, D-01/D-02) - Schedule-X --sx-color-* override via cascade (imported after theme-default in main.tsx) - Temporal polyfill-first import order in main.tsx - Alpha-blend-over-white for event chip container colors - WEEK_START_DAY=0 (JS) to SX_FIRST_DAY_OF_WEEK=7 (Temporal) translation - hydrateEvents calendarId routing: isShared ? 'shared' : String(ownerUserId) - Zustand UI store with localStorage view persistence per breakpoint group - Windowed fetchEvents(start,end) with credentials:include key_files: created: - apps/pwa/src/styles/tokens.css - apps/pwa/src/styles/tokens.ts - apps/pwa/src/styles/index.css - apps/pwa/src/lib/colorUtils.ts - apps/pwa/src/lib/colorUtils.test.ts - apps/pwa/src/lib/calendarConfig.ts - apps/pwa/src/lib/hydrateEvents.ts - apps/pwa/src/store/calendarStore.ts modified: - apps/pwa/package.json - apps/pwa/src/main.tsx - apps/pwa/src/api/client.ts - apps/pwa/src/lib/hydrateEvents.test.ts - apps/pwa/src/lib/calendarConfig.test.ts - apps/pwa/src/components/EventProof.tsx - pnpm-lock.yaml decisions:
- "calendarId routing uses isShared/ownerUserId (never String(calendarId)) to match buildCalendarConfig keys"
- "SX_FIRST_DAY_OF_WEEK = WEEK_START_DAY === 0 ? 7 : WEEK_START_DAY encoded as exported constant"
- "fetchEventsLegacy() preserves EventProof compilation until Plan 05 removes the component"
- "calendarStore initializes calendarRange to today's month +-7-day buffer for initial TanStack Query fetch"
- "tokens.css imported via index.css (not directly in main.tsx) to preserve correct cascade order" metrics: duration: "6m 56s" completed: "2026-06-05" tasks_completed: 3 files_created: 8 files_modified: 7
Phase 02 Plan 03: PWA Foundation — Token Layer, Color Utils, Calendar Config, Hydration, Store Summary
CSS custom-property token layer with Schedule-X overrides, hex-blend color utilities, firstDayOfWeek 0 to 7 translation, Temporal-based event hydration with all-day PlainDate guard and ownership-routed calendarId, Zustand UI store with localStorage view persistence, and windowed fetchEvents.
What Was Built
Task 1: Schedule-X Stack + Token Layer + main.tsx Import Order
Installed packages in apps/pwa:
@schedule-x/calendar@4.6.0,@schedule-x/react@4.1.0,@schedule-x/theme-default@4.6.0@schedule-x/event-modal@4.6.0,@schedule-x/events-service@4.6.0temporal-polyfill@0.3.2,lucide-react@1.17.0
apps/pwa/src/styles/tokens.css — CSS custom properties declaring:
- Surface/border/text palette:
--color-surface,--color-surface-dim,--color-surface-raised,--color-border,--color-border-subtle,--color-text-primary/secondary/muted,--color-focus-ring,--color-overlay - Calendar colors:
--color-member-0..5+--color-shared-family: #F25C7A+--color-destructive - Spacing scale:
--space-1..12(multiples of 4px) - Typography:
--font-family-base,--text-body/label/heading/display-size/weight/line-height - Breakpoints:
--bp-phone: 0px,--bp-tablet: 768px,--bp-desktop: 1280px - Schedule-X overrides: all
--sx-color-*vars mapped to project tokens;--sx-font-family @keyframes shimmerfor SkeletonCalendar
apps/pwa/src/styles/tokens.ts — TypeScript mirror of all token values for inline-style props; as const typed.
apps/pwa/src/styles/index.css — imports tokens.css + minimal global reset.
apps/pwa/src/main.tsx — updated with load-bearing import order:
import 'temporal-polyfill/global'(must be first)import '@schedule-x/theme-default/dist/index.css'(SX layout CSS)import './styles/index.css'(token overrides win cascade)
Task 2: colorUtils + calendarConfig — RED Stubs Turned GREEN
apps/pwa/src/lib/colorUtils.ts exports:
hexToContainer(hex)— alpha blends at 15% opacity over whitehexToOnContainer(hex)— darkens 40% (channel multiply by 0.6)deriveScheduleXColors(main)returning{ main, container, onContainer }
apps/pwa/src/lib/calendarConfig.ts exports:
WEEK_START_DAY = 0(JS Sunday convention)SX_FIRST_DAY_OF_WEEK = WEEK_START_DAY === 0 ? 7 : WEEK_START_DAYbuildCalendarConfig(members)returning{ firstDayOfWeek: 7, calendars }with'shared'(rose) + per-member entries keyed byString(userId)
calendarConfig.test.ts (Plan 01 RED stubs) — all 4 assertions now GREEN.
Task 3: hydrateEvents + calendarStore + windowed fetchEvents — RED Stubs Turned GREEN
apps/pwa/src/lib/hydrateEvents.ts:
allDay:trueusesTemporal.PlainDate.from(occ.start)(guards all-day date shift)allDay:falseusesTemporal.ZonedDateTime.from(occ.start/end)calendarId = occ.isShared ? 'shared' : String(occ.ownerUserId)— neverString(occ.calendarId)_familySync: { uid, color, isShared }carried for popover rendering
hydrateEvents.test.ts (Plan 01 RED stubs) — all 4 assertions now GREEN.
apps/pwa/src/store/calendarStore.ts Zustand store:
selectedView— from localStorage per breakpoint group; D-05 defaultscalendarRange— month ± 7-day buffer for initial TanStack Query keyopenEventId,selectedDate— UI-only, not persisted
apps/pwa/src/api/client.ts:
- Added
CalendarOccurrence,OccurrencesResponse,fetchEvents(start, end) - Kept deprecated
CalendarEvent,EventsResponse,fetchEventsLegacy()for EventProof.tsx (removed Plan 05)
Verification Results
Test Files 3 passed (3)
Tests 18 passed (18)
tsc --noEmit: clean (0 errors)
All Wave 1 RED stubs are GREEN:
calendarConfig.test.ts— 4/4 passhydrateEvents.test.ts— 4/4 passcolorUtils.test.ts— 10/10 pass
Deviations from Plan
Auto-fixed Issues
1. [Rule 2 - Missing Critical Functionality] Added temporal-polyfill/global to hydrateEvents.test.ts
- Found during: Task 3 test run
- Issue: Plan 01 RED stub lacked
import 'temporal-polyfill/global'; jsdom has no native Temporal - Fix: Added as first import in
hydrateEvents.test.ts - Files modified:
apps/pwa/src/lib/hydrateEvents.test.ts - Commit:
f377d7c
2. [Rule 2 - Missing Critical Functionality] Added fetchEventsLegacy() to preserve EventProof
- Found during: Task 3 — updating client.ts
- Issue: EventProof.tsx called no-arg
fetchEvents()and usedCalendarEventfields not onCalendarOccurrence - Fix: Added
fetchEventsLegacy()(deprecated) + updated EventProof to use it; plan says it is replaced in Plan 05 - Files modified:
apps/pwa/src/api/client.ts,apps/pwa/src/components/EventProof.tsx - Commit:
f377d7c
Known Stubs
None — all artifacts are fully wired. Plan 04 mounts Schedule-X and consumes these modules.
Threat Flags
No new threat surface beyond the plan's threat model. All packages pre-approved in RESEARCH.md with no postinstall scripts.
Self-Check: PASSED
Files created:
- apps/pwa/src/styles/tokens.css
- apps/pwa/src/styles/tokens.ts
- apps/pwa/src/styles/index.css
- apps/pwa/src/lib/colorUtils.ts
- apps/pwa/src/lib/colorUtils.test.ts
- apps/pwa/src/lib/calendarConfig.ts
- apps/pwa/src/lib/hydrateEvents.ts
- apps/pwa/src/store/calendarStore.ts
Commits: