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.
264 lines
22 KiB
Markdown
264 lines
22 KiB
Markdown
---
|
||
phase: 02-calendar-display
|
||
plan: 03
|
||
type: execute
|
||
wave: 2
|
||
depends_on: ['02-01']
|
||
files_modified:
|
||
- 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/calendarConfig.test.ts
|
||
- apps/pwa/src/lib/hydrateEvents.ts
|
||
- apps/pwa/src/lib/hydrateEvents.test.ts
|
||
- apps/pwa/src/store/calendarStore.ts
|
||
- apps/pwa/src/api/client.ts
|
||
- apps/pwa/src/main.tsx
|
||
- apps/pwa/package.json
|
||
autonomous: true
|
||
requirements: [CAL-02, CAL-03, CAL-07]
|
||
user_setup: []
|
||
|
||
must_haves:
|
||
truths:
|
||
- 'A CSS custom-property token layer (the clean theme) defines all colors, spacing, typography, breakpoints from UI-SPEC; no hard-coded hex/px will be needed by components — tuned to stay legible/informational at tablet distance, not ultra-minimal (D-03)'
|
||
- 'Schedule-X --sx-color-* vars are mapped to project tokens so no Schedule-X default colors bleed through'
|
||
- 'colorUtils derives Schedule-X lightColors (main/container/onContainer) from a member hex'
|
||
- "calendarConfig translates WEEK_START_DAY=0 (Sunday/JS) to Schedule-X firstDayOfWeek=7 (Temporal Sunday) and builds the per-calendar config keyed by String(userId) + 'shared'"
|
||
- 'hydrateEvents converts all-day occurrences to Temporal.PlainDate and timed occurrences to Temporal.ZonedDateTime'
|
||
- "hydrateEvents routes each event's Schedule-X calendarId to 'shared' (isShared) or String(ownerUserId), matching the userId-keyed calendars config — never String(calendarId)"
|
||
- 'calendarStore (Zustand) holds selectedView (persisted per breakpoint group), selectedDate, openEventId, calendarRange — no server data'
|
||
- 'fetchEvents(start,end) calls the windowed /api/events with credentials:include and returns OccurrencesResponse'
|
||
artifacts:
|
||
- path: 'apps/pwa/src/styles/tokens.css'
|
||
provides: 'clean-theme CSS custom properties + Schedule-X var overrides'
|
||
contains: '--color-shared-family'
|
||
- path: 'apps/pwa/src/lib/calendarConfig.ts'
|
||
provides: 'WEEK_START_DAY, firstDayOfWeek translation, buildCalendarConfig()'
|
||
exports: ['WEEK_START_DAY', 'buildCalendarConfig']
|
||
- path: 'apps/pwa/src/lib/hydrateEvents.ts'
|
||
provides: 'hydrateEvents() ISO→Temporal with all-day PlainDate guard + isShared/ownerUserId calendarId routing'
|
||
exports: ['hydrateEvents']
|
||
- path: 'apps/pwa/src/store/calendarStore.ts'
|
||
provides: 'Zustand UI-state store with localStorage view persistence'
|
||
exports: ['useCalendarStore']
|
||
key_links:
|
||
- from: 'apps/pwa/src/main.tsx'
|
||
to: 'temporal-polyfill/global'
|
||
via: 'import before any Schedule-X mount'
|
||
pattern: 'temporal-polyfill/global'
|
||
- from: 'apps/pwa/src/lib/calendarConfig.ts'
|
||
to: 'apps/pwa/src/lib/colorUtils.ts'
|
||
via: 'deriveScheduleXColors() for lightColors'
|
||
pattern: 'deriveScheduleXColors'
|
||
- from: 'apps/pwa/src/lib/hydrateEvents.ts'
|
||
to: 'apps/pwa/src/lib/calendarConfig.ts'
|
||
via: "calendarId = isShared ? 'shared' : String(ownerUserId) matches buildCalendarConfig keys"
|
||
pattern: 'ownerUserId'
|
||
---
|
||
|
||
<objective>
|
||
Build the frontend foundation the calendar render slice depends on: the CSS custom-property token
|
||
layer (D-01/D-02 — the load-bearing deliverable), the color-derivation utility, the Schedule-X
|
||
calendar config with the firstDayOfWeek translation, the ISO→Temporal hydration util with the
|
||
all-day PlainDate guard, the Zustand UI-state store, the windowed fetchEvents client, and the
|
||
Temporal-polyfill + theme-CSS imports in main.tsx.
|
||
|
||
Purpose: These are pure PWA library/style/store files with zero overlap with the backend plan, so
|
||
this runs in parallel with Plan 02. Plan 04 mounts Schedule-X and wires all of this into a
|
||
rendering calendar.
|
||
Output: tokens.css/ts/index.css, colorUtils, calendarConfig, hydrateEvents, calendarStore,
|
||
windowed fetchEvents, updated main.tsx, Schedule-X deps installed.
|
||
</objective>
|
||
|
||
<execution_context>
|
||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||
</execution_context>
|
||
|
||
<context>
|
||
@.planning/PROJECT.md
|
||
@.planning/ROADMAP.md
|
||
@.planning/STATE.md
|
||
@.planning/phases/02-calendar-display/02-UI-SPEC.md
|
||
@.planning/phases/02-calendar-display/02-RESEARCH.md
|
||
@.planning/phases/02-calendar-display/02-PATTERNS.md
|
||
</context>
|
||
|
||
<tasks>
|
||
|
||
<task type="auto">
|
||
<name>Task 1: Install Schedule-X deps + token layer (tokens.css/ts/index.css) + main.tsx imports</name>
|
||
<files>apps/pwa/package.json, apps/pwa/src/styles/tokens.css, apps/pwa/src/styles/tokens.ts, apps/pwa/src/styles/index.css, apps/pwa/src/main.tsx</files>
|
||
<read_first>
|
||
- apps/pwa/src/main.tsx (current import order + QueryClientProvider setup)
|
||
- .planning/phases/02-calendar-display/02-UI-SPEC.md §"Token Layer", §"Color Tokens", §"Spacing Scale", §"Typography", §"Breakpoints", §"Schedule-X CSS Override Strategy"
|
||
- .planning/phases/02-calendar-display/02-RESEARCH.md §"Installation (PWA only)" (exact package versions) + §"Schedule-X CSS Token Override Pattern"
|
||
- .planning/phases/02-calendar-display/02-PATTERNS.md §"apps/pwa/src/main.tsx" (Temporal-polyfill-first import order)
|
||
</read_first>
|
||
<action>
|
||
Install the Schedule-X stack in apps/pwa at the pinned versions from RESEARCH: `@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` (via pnpm add in apps/pwa). These are the Approved packages from RESEARCH §Package Legitimacy.
|
||
|
||
Create `apps/pwa/src/styles/tokens.css` declaring on `:root` every token from UI-SPEC §Color Tokens (--color-surface, --color-surface-dim, --color-surface-raised, --color-border, --color-border-subtle, --color-text-primary/secondary/muted, --color-focus-ring, --color-overlay, --color-member-0..5 with the exact UI-SPEC hexes, --color-shared-family:#F25C7A, --color-destructive:#DC2626), §Spacing Scale (--space-1..12), §Typography (--font-family-base, --text-body/label/heading/display sizes+weights+line-heights), and §Breakpoints (--bp-phone/tablet/desktop). Then add the Schedule-X override block mapping --sx-color-* vars to these tokens per UI-SPEC §Schedule-X CSS Override Strategy (--sx-color-primary→--color-member-0, surface/on-surface/outline/neutral, --sx-font-family→--font-family-base). Include the `@keyframes shimmer` from PATTERNS for the skeleton.
|
||
|
||
Create `apps/pwa/src/styles/tokens.ts` exporting a typed object mirroring the same token values (so components can use them in inline-style props). Keep names aligned with the CSS var names.
|
||
|
||
Create `apps/pwa/src/styles/index.css` importing tokens.css, plus a minimal global reset (box-sizing border-box, body font-family var, margin 0) — no third-party reset library.
|
||
|
||
Update `apps/pwa/src/main.tsx`: as the FIRST three imports (before React), add `import 'temporal-polyfill/global'`, `import '@schedule-x/theme-default/dist/index.css'`, `import './styles/index.css'` (in that order — Temporal must register before any Schedule-X usage, and token overrides must come after the Schedule-X default CSS so they win). Leave the QueryClientProvider tree intact.
|
||
|
||
</action>
|
||
<verify>
|
||
<automated>cd apps/pwa && grep -q "temporal-polyfill/global" src/main.tsx && grep -q "@schedule-x/theme-default/dist/index.css" src/main.tsx && grep -q "./styles/index.css" src/main.tsx && echo MAIN_IMPORTS_OK</automated>
|
||
<automated>cd apps/pwa && grep -q -- "--color-shared-family: #F25C7A" src/styles/tokens.css && grep -q -- "--sx-color-" src/styles/tokens.css && echo TOKENS_OK</automated>
|
||
<automated>cd apps/pwa && node -e "require('@schedule-x/calendar');require('temporal-polyfill');require('lucide-react');console.log('DEPS_OK')"</automated>
|
||
</verify>
|
||
<acceptance_criteria>
|
||
- apps/pwa/package.json dependencies include all six @schedule-x/* + temporal-polyfill + lucide-react at the RESEARCH-pinned versions
|
||
- tokens.css declares --color-shared-family:#F25C7A, all --color-member-0..5, the spacing/typography/breakpoint tokens, and a --sx-color-* override block
|
||
- main.tsx imports temporal-polyfill/global FIRST, then theme-default CSS, then styles/index.css
|
||
- tokens.ts exports a token object mirroring the CSS var values
|
||
</acceptance_criteria>
|
||
<done>Schedule-X stack installed; clean-theme token layer + Schedule-X var overrides present; main.tsx imports Temporal polyfill + theme + tokens in correct order.</done>
|
||
</task>
|
||
|
||
<task type="auto" tdd="true">
|
||
<name>Task 2: colorUtils + calendarConfig (firstDayOfWeek translation) — turn RED stubs green</name>
|
||
<files>apps/pwa/src/lib/colorUtils.ts, apps/pwa/src/lib/colorUtils.test.ts, apps/pwa/src/lib/calendarConfig.ts, apps/pwa/src/lib/calendarConfig.test.ts</files>
|
||
<read_first>
|
||
- apps/pwa/src/lib/calendarConfig.test.ts (RED stub from Plan 01 — its 0→7 assertion is the contract)
|
||
- apps/pwa/src/App.tsx (ColorSwatch inline-style pattern, lines 18–34, the colorUtils analog)
|
||
- .planning/phases/02-calendar-display/02-UI-SPEC.md §"Color derivation rule for event chips" (container=15% over white; onContainer=darken 40%) + §"Calendar config constant"
|
||
- .planning/phases/02-calendar-display/02-RESEARCH.md §"Pattern 3: Schedule-X Calendar Configuration" + §"Pitfall 1" (firstDayOfWeek 0→7) + §"Pitfall 6" (limit to confirmed Schedule-X API surface)
|
||
- .planning/phases/02-calendar-display/02-PATTERNS.md §"apps/pwa/src/lib/colorUtils.ts" + §"apps/pwa/src/lib/calendarConfig.ts"
|
||
</read_first>
|
||
<behavior>
|
||
- colorUtils: deriveScheduleXColors('#4A90D9') returns { main:'#4A90D9', container: <15% over white>, onContainer: <darkened 40%> }
|
||
- calendarConfig: WEEK_START_DAY === 0 translates to Schedule-X firstDayOfWeek 7
|
||
- calendarConfig: buildCalendarConfig([{id:'1',name:'Lucas',color:'#4A90D9'}]) yields calendars['1'] with lightColors and a 'shared' entry colored from #F25C7A
|
||
</behavior>
|
||
<action>
|
||
Create `apps/pwa/src/lib/colorUtils.ts` exporting `hexToContainer(hex)` (main at 15% opacity blended over #FFFFFF → returns a hex/rgb string), `hexToOnContainer(hex)` (main darkened 40%), and `deriveScheduleXColors(main)` returning `{ main, container, onContainer }`. Implement the math inline (no third-party color lib per RESEARCH Don't-Hand-Roll note — it's simple enough). Write colorUtils.test.ts asserting the derivations for a known hex.
|
||
|
||
Create `apps/pwa/src/lib/calendarConfig.ts` exporting `export const WEEK_START_DAY = 0` with the inline comment that Schedule-X uses 7=Sunday, a translation `const sxFirstDay = WEEK_START_DAY === 0 ? 7 : WEEK_START_DAY` exposed as an exported `SX_FIRST_DAY_OF_WEEK`, the view factory list (`createViewDay`, `createViewWeek`, `createViewMonthGrid`, `createViewMonthAgenda` from @schedule-x/calendar), and `buildCalendarConfig(members: MemberCalendarConfig[])` returning `{ calendars }` keyed by String(userId) plus a reserved `'shared'` entry using deriveScheduleXColors('#F25C7A'). Per-member entries use deriveScheduleXColors(member.color). The `String(userId)` + `'shared'` key scheme here is the routing contract hydrateEvents (Task 3) must match — keep them aligned. Limit usage to the confirmed Schedule-X API surface (Pitfall 6). Turn the Plan 01 RED calendarConfig.test.ts green (it asserts the 0→7 translation).
|
||
|
||
</action>
|
||
<verify>
|
||
<automated>cd apps/pwa && pnpm test -- src/lib/colorUtils.test.ts src/lib/calendarConfig.test.ts</automated>
|
||
<automated>cd apps/pwa && grep -q "WEEK_START_DAY === 0 ? 7" src/lib/calendarConfig.ts && echo WEEKSTART_TRANSLATED</automated>
|
||
</verify>
|
||
<acceptance_criteria>
|
||
- colorUtils.ts exports deriveScheduleXColors, hexToContainer, hexToOnContainer; colorUtils.test.ts green
|
||
- calendarConfig.ts exports WEEK_START_DAY (=0), the 0→7 firstDayOfWeek translation, and buildCalendarConfig
|
||
- buildCalendarConfig output keys per-member by String(userId) and includes a 'shared' entry from #F25C7A
|
||
- calendarConfig.test.ts (Plan 01 RED stub) passes the 0→7 assertion
|
||
</acceptance_criteria>
|
||
<done>colorUtils + calendarConfig built; firstDayOfWeek 0→7 translation encoded; both test files green.</done>
|
||
</task>
|
||
|
||
<task type="auto" tdd="true">
|
||
<name>Task 3: hydrateEvents (Temporal, all-day guard, ownership-routed calendarId) + Zustand store + windowed fetchEvents</name>
|
||
<files>apps/pwa/src/lib/hydrateEvents.ts, apps/pwa/src/lib/hydrateEvents.test.ts, apps/pwa/src/store/calendarStore.ts, apps/pwa/src/api/client.ts</files>
|
||
<read_first>
|
||
- apps/pwa/src/lib/hydrateEvents.test.ts (RED stub from Plan 01 — PlainDate-vs-ZonedDateTime AND the calendarId='shared'/String(ownerUserId) routing contract)
|
||
- apps/pwa/src/api/client.ts (existing fetchMe pattern + the OLD unwindowed fetchEvents/EventsResponse to replace)
|
||
- apps/pwa/src/lib/calendarConfig.ts (Task 2 — buildCalendarConfig keys: String(userId) + 'shared'; hydrateEvents must produce calendarId values that match these keys)
|
||
- apps/api/src/broker/expand.ts CalendarOccurrence shape (if Plan 02 merged first: fields incl. calendarId, ownerUserId, isShared) OR .planning/phases/02-calendar-display/02-RESEARCH.md §"Pattern 1" interface (the server JSON contract)
|
||
- .planning/phases/02-calendar-display/02-RESEARCH.md §"Pattern 2" (hydrateEvents PlainDate/ZonedDateTime) + §"Pitfall 2/4"
|
||
- .planning/phases/02-calendar-display/02-PATTERNS.md §"apps/pwa/src/store/calendarStore.ts" (Zustand state shape) + §"apps/pwa/src/api/client.ts"
|
||
- .planning/phases/02-calendar-display/02-UI-SPEC.md §"State Management Contract" + §"View default logic (D-05)"
|
||
</read_first>
|
||
<behavior>
|
||
- hydrateEvents: occurrence with allDay:true and start '2026-06-15' → start is Temporal.PlainDate (NOT ZonedDateTime — guards Pitfall 2)
|
||
- hydrateEvents: timed occurrence → start/end Temporal.ZonedDateTime from the offset-aware ISO string
|
||
- hydrateEvents: a shared occurrence (isShared:true) → Schedule-X calendarId === 'shared'
|
||
- hydrateEvents: a personal occurrence (isShared:false, ownerUserId:7) → Schedule-X calendarId === '7' (String(ownerUserId)), NOT String(occ.calendarId)
|
||
- hydrateEvents: passes uid/color/isShared through on a _familySync field
|
||
- calendarStore: setSelectedView persists to localStorage keyed by breakpoint group ('phone' | 'tablet-desktop')
|
||
</behavior>
|
||
<action>
|
||
Create `apps/pwa/src/lib/hydrateEvents.ts` exporting `ScheduleXEvent` interface and `hydrateEvents(occurrences: CalendarOccurrence[]): ScheduleXEvent[]`. Branch on `occ.allDay`: if true use `Temporal.PlainDate.from(occ.start)` for start/end (never construct a ZonedDateTime from midnight UTC — Pitfall 2); else `Temporal.ZonedDateTime.from(occ.start/end)`.
|
||
|
||
CRITICAL — calendarId routing: set the Schedule-X `calendarId` to `occ.isShared ? 'shared' : String(occ.ownerUserId)`. Do NOT use `String(occ.calendarId)` — `occ.calendarId` is the DB calendar-row id, but `buildCalendarConfig` keys the calendars config by `String(userId)` plus `'shared'`. A member who owns multiple calendars (e.g. the broker account exposes "Calendar" and "USA Holidays") would otherwise produce a calendarId that matches no config key, and Schedule-X would render those events with no color. Routing by `'shared' | String(ownerUserId)` is the contract that aligns with buildCalendarConfig's keys.
|
||
|
||
Carry uid/color/isShared on `_familySync`. Temporal is global via the main.tsx polyfill import; in tests import `'temporal-polyfill/global'` at the top of hydrateEvents.test.ts. Turn the Plan 01 RED hydrateEvents.test.ts green — including its shared→'shared' and personal→String(ownerUserId) assertions.
|
||
|
||
Create `apps/pwa/src/store/calendarStore.ts` exporting `useCalendarStore` (Zustand `create`) with state: `selectedView:string`, `selectedDate:string`, `openEventId:string|null`, `calendarRange:{start:string;end:string}` and setters. selectedView is initialized from localStorage keyed by breakpoint group (`window.matchMedia('(max-width:767px)').matches ? 'phone' : 'tablet-desktop'`), defaulting to 'month-agenda' on phone / 'month-grid' on tablet-desktop (D-05); setSelectedView writes back to localStorage under `calendarView.{group}`. calendarRange defaults to the current month ± 1 week (do NOT depend on Schedule-X onRangeUpdate for the first fetch — A4/Open Q2). Server events NEVER enter this store. Add `zustand` to apps/pwa deps if not already present.
|
||
|
||
In `apps/pwa/src/api/client.ts`, REPLACE the old unwindowed `fetchEvents()` and its `CalendarEvent`/`EventsResponse` types with: `CalendarOccurrence` interface (mirror the server contract — include calendarId, ownerUserId, isShared so hydrateEvents can route), `OccurrencesResponse { occurrences: CalendarOccurrence[] }`, and `fetchEvents(start:string, end:string): Promise<OccurrencesResponse>` calling `/api/events?start=${start}&end=${end}` with `credentials:'include'` and the same `if(!res.ok) throw` pattern as fetchMe. Note: EventProof.tsx referenced the old fetchEvents — leave EventProof for Plan 05 to remove; if the type change breaks its build, update EventProof minimally to compile (it is replaced in Plan 05).
|
||
|
||
</action>
|
||
<verify>
|
||
<automated>cd apps/pwa && pnpm test -- src/lib/hydrateEvents.test.ts</automated>
|
||
<automated>cd apps/pwa && grep -q "Temporal.PlainDate.from" src/lib/hydrateEvents.ts && grep -q "ownerUserId" src/lib/hydrateEvents.ts && grep -q "credentials: 'include'" src/api/client.ts && grep -q "calendarRange" src/store/calendarStore.ts && echo SLICE_LIB_OK</automated>
|
||
<automated>cd apps/pwa && pnpm exec tsc --noEmit</automated>
|
||
</verify>
|
||
<acceptance_criteria>
|
||
- hydrateEvents.test.ts (Plan 01 RED stub) passes: all-day→PlainDate, timed→ZonedDateTime, shared→'shared', personal→String(ownerUserId)
|
||
- hydrateEvents.ts uses Temporal.PlainDate.from for all-day and never ZonedDateTime for all-day
|
||
- hydrateEvents.ts sets calendarId to `occ.isShared ? 'shared' : String(occ.ownerUserId)` (NOT String(occ.calendarId))
|
||
- calendarStore exports useCalendarStore with selectedView/selectedDate/openEventId/calendarRange and persists selectedView to localStorage per breakpoint group
|
||
- client.ts fetchEvents takes (start,end), hits /api/events?start=&end= with credentials:'include', returns OccurrencesResponse; CalendarOccurrence includes ownerUserId + isShared
|
||
- tsc --noEmit clean in apps/pwa
|
||
</acceptance_criteria>
|
||
<done>hydrateEvents (all-day PlainDate guard + ownership-routed calendarId), Zustand UI store, and windowed fetchEvents all built; hydrateEvents.test.ts green; PWA typechecks.</done>
|
||
</task>
|
||
|
||
</tasks>
|
||
|
||
<threat_model>
|
||
|
||
## Trust Boundaries
|
||
|
||
| Boundary | Description |
|
||
| --------------------------- | ----------------------------------------------- |
|
||
| localStorage → store init | persisted view string read at startup |
|
||
| server JSON → hydrateEvents | occurrence strings parsed into Temporal objects |
|
||
|
||
## STRIDE Threat Register
|
||
|
||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||
| --------- | --------- | ----------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| T-02c-01 | Tampering | localStorage selectedView | accept | UI-only state; an invalid stored view falls back to the D-05 default; no security impact (single-device, two-person app) |
|
||
| T-02c-02 | Tampering | hydrateEvents string parsing | accept | Temporal.from throws on malformed input surfaced as a React Query error, not a security boundary; data already passed server zod validation |
|
||
| T-02c-SC | Tampering | @schedule-x/\*, temporal-polyfill, lucide-react, zustand installs | mitigate | All Approved in RESEARCH §Package Legitimacy (mainstream, no postinstall); pinned versions; no [ASSUMED]/[SUS] |
|
||
|
||
</threat_model>
|
||
|
||
<verification>
|
||
- `pnpm --filter @familysync/pwa test` green (colorUtils, calendarConfig, hydrateEvents)
|
||
- `tsc --noEmit` clean in apps/pwa
|
||
- main.tsx import order correct (Temporal first)
|
||
</verification>
|
||
|
||
<success_criteria>
|
||
|
||
- Clean-theme token layer + Schedule-X overrides present (D-01/D-02)
|
||
- firstDayOfWeek 0→7 translation encoded; per-calendar config built from member colors
|
||
- All-day Temporal PlainDate guard in place
|
||
- hydrateEvents calendarId routes by isShared/ownerUserId to match buildCalendarConfig keys
|
||
- Zustand UI store + windowed fetchEvents ready for Plan 04
|
||
</success_criteria>
|
||
|
||
<artifacts_produced>
|
||
|
||
## Artifacts this phase produces (Plan 03)
|
||
|
||
- CSS custom properties: --color-_, --space-_, --text-_, --font-family-base, --bp-_, --sx-color-\* overrides (tokens.css)
|
||
- token object export (tokens.ts); styles/index.css global reset
|
||
- `hexToContainer`, `hexToOnContainer`, `deriveScheduleXColors` (colorUtils.ts)
|
||
- `WEEK_START_DAY`, `SX_FIRST_DAY_OF_WEEK`, `buildCalendarConfig`, `MemberCalendarConfig` (calendarConfig.ts)
|
||
- `hydrateEvents`, `ScheduleXEvent` (hydrateEvents.ts) — calendarId routed by isShared/ownerUserId
|
||
- `useCalendarStore` Zustand store + CalendarStore state shape (calendarStore.ts)
|
||
- `fetchEvents(start,end)`, `CalendarOccurrence`, `OccurrencesResponse` (client.ts — replaces old unwindowed versions)
|
||
- @schedule-x/\* + temporal-polyfill + lucide-react + zustand dependencies
|
||
- main.tsx Temporal-polyfill-first import block
|
||
</artifacts_produced>
|
||
|
||
<output>
|
||
Create `.planning/phases/02-calendar-display/02-03-SUMMARY.md` when done
|
||
</output>
|