Plan 06-05 added hasRrule as a required field on the PWA CalendarOccurrence
type (mirroring 06-03's server-side field), but pre-existing fixtures in
EventDetailPopover.test.tsx did not set it — breaking tsc --noEmit / vite build
(vitest passed because esbuild strips types). Both fixtures are non-recurring
single events, so hasRrule: false is correct. Post-merge integration fix.
Backend:
- expand.ts: add ownerName: string | null to CalendarOccurrence
interface and expandOccurrences() signature; thread it onto every
emitted occurrence.
- events.ts: SELECT users.displayName as ownerName in the join; pass
it to expandOccurrences().
Frontend:
- client.ts: add ownerName: string | null to CalendarOccurrence.
- EventDetailPopover.tsx: render isShared ? 'Family' :
(ownerName ?? calendarName) in the footer instead of calendarName.
Tests:
- expand.test.ts: pass ownerName to all expandOccurrences() calls;
assert ownerName is carried onto occurrences in the DST test.
- events.test.ts: add ownerName to mock rows; assert ownerName present
on occurrences; add ownerName assertion to timed-recurring test.
- EventDetailPopover.test.tsx: add ownerName to fixtures; split
"calendar name in footer" into three targeted tests covering
personal-with-owner, shared→Family, and null-owner fallback.
- BUG 2: onRangeUpdate sets exclusive end = range.end + 1 day so day view
sends a 1-day window (start < end, no 400) and week/month include the last day
- BUG 3: formatDateTime strips IANA bracket '[Zone]' before new Date() to prevent
'Invalid Date, Invalid Date – Invalid Date' in event popover; regression test added
- BUG 4: remove createEventModalPlugin + customComponents.eventModal — keep only
the Zustand-driven standalone EventDetailPopover to prevent double-open fight
- renders title/location/description/calendar-name as plain text
- Escape key and backdrop click close the popover via setOpenEventId(null)
- close button aria-label=Close assertion
- XSS guard: HTML-looking title and description render as escaped text not DOM elements
- renders null when openEventId is null