fix(02): day-view 400, invalid date display, duplicate popover (BUG 2-4)
- 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
This commit is contained in:
@@ -6,11 +6,13 @@
|
||||
* → hydrateEvents (ISO → Temporal) → eventsService.set() → Schedule-X render
|
||||
*
|
||||
* Critical constraints:
|
||||
* - Plugins (eventsService, eventModal) created once via useState stable initialiser
|
||||
* - Plugins (eventsService) created once via useState stable initialiser
|
||||
* - onRangeUpdate fires on navigation; initial fetch uses Zustand default range (A4/Q2)
|
||||
* - DateRange.start/end are Temporal.ZonedDateTime → converted to 'YYYY-MM-DD' for Zustand
|
||||
* - calendarId routing: 'shared' | String(ownerUserId) — produced by hydrateEvents, consumed
|
||||
* by buildCalendarConfig; never the DB calendar-row id
|
||||
* - Event popover: driven exclusively by Zustand openEventId via onEventClick → standalone
|
||||
* EventDetailPopover; createEventModalPlugin and customComponents.eventModal are NOT used
|
||||
* - Threat T-02d-01: all event fields are plain-text JSX children — no raw HTML injection
|
||||
*
|
||||
* Layout:
|
||||
@@ -35,7 +37,6 @@ import {
|
||||
type CalendarType,
|
||||
} from '@schedule-x/calendar'
|
||||
import { createEventsServicePlugin } from '@schedule-x/events-service'
|
||||
import { createEventModalPlugin } from '@schedule-x/event-modal'
|
||||
|
||||
import { fetchMe, fetchEvents } from '../api/client.js'
|
||||
import { hydrateEvents } from '../lib/hydrateEvents.js'
|
||||
@@ -88,7 +89,6 @@ export function CalendarShell() {
|
||||
|
||||
// Create plugins once (stable across renders)
|
||||
const eventsService = useState(() => createEventsServicePlugin())[0]
|
||||
const eventModal = useState(() => createEventModalPlugin())[0]
|
||||
|
||||
// Build members list from /api/me for AppNav + ColorLegend
|
||||
const members = useMemo(() => {
|
||||
@@ -124,11 +124,13 @@ export function CalendarShell() {
|
||||
calendars: calendarsConfig,
|
||||
callbacks: {
|
||||
onRangeUpdate(range) {
|
||||
// range.start / range.end are Temporal.ZonedDateTime
|
||||
// Convert to ISO date strings ('YYYY-MM-DD') for the Zustand range
|
||||
// range.start / range.end are Temporal.ZonedDateTime.
|
||||
// Set end to the day AFTER range.end (exclusive window end) so that:
|
||||
// - Day view: start === day N, end === day N+1 → 1-day window (avoids 400 on 0-day span)
|
||||
// - Week/Month: end includes the last visible day instead of dropping it
|
||||
setCalendarRange({
|
||||
start: range.start.toPlainDate().toString(),
|
||||
end: range.end.toPlainDate().toString(),
|
||||
end: range.end.toPlainDate().add({ days: 1 }).toString(),
|
||||
})
|
||||
},
|
||||
onEventClick(event) {
|
||||
@@ -138,7 +140,7 @@ export function CalendarShell() {
|
||||
},
|
||||
},
|
||||
},
|
||||
[eventsService, eventModal],
|
||||
[eventsService],
|
||||
)
|
||||
|
||||
// Sync TanStack Query result into Schedule-X eventsService (Pitfall 4 guard)
|
||||
@@ -263,10 +265,7 @@ export function CalendarShell() {
|
||||
<EmptyState />
|
||||
) : (
|
||||
// Normal: Schedule-X calendar (primary focal point)
|
||||
<ScheduleXCalendar
|
||||
calendarApp={calendar}
|
||||
customComponents={{ eventModal: EventDetailPopover }}
|
||||
/>
|
||||
<ScheduleXCalendar calendarApp={calendar} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user