'use client'; import { useEffect, useRef } from 'react'; import { DialogCloseButton } from '@/components/ui/shared/DialogCloseButton'; import { APPOINTMENT_PURPOSE_LABEL, purposeStyle, } from '@/components/ui/appointments/appointmentPurposeStyles'; import type { AppointmentRecord } from '@/types/appointment'; type AppointmentOverlapPopoverProps = { appointments: AppointmentRecord[]; anchorRect: DOMRect; onSelect: (appointment: AppointmentRecord) => void; onClose: () => void; }; function formatTimeRange(apt: AppointmentRecord): string { const start = new Date(apt.startAt); const end = new Date(apt.endAt); const opts: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit' }; return `${start.toLocaleTimeString(undefined, opts)} – ${end.toLocaleTimeString(undefined, opts)}`; } export function AppointmentOverlapPopover({ appointments, anchorRect, onSelect, onClose, }: AppointmentOverlapPopoverProps) { const panelRef = useRef(null); useEffect(() => { function onPointerDown(event: MouseEvent) { if (!panelRef.current?.contains(event.target as Node)) { onClose(); } } function onKeyDown(event: KeyboardEvent) { if (event.key === 'Escape') { onClose(); } } document.addEventListener('mousedown', onPointerDown); document.addEventListener('keydown', onKeyDown); return () => { document.removeEventListener('mousedown', onPointerDown); document.removeEventListener('keydown', onKeyDown); }; }, [onClose]); const sorted = [...appointments].sort( (a, b) => new Date(a.startAt).getTime() - new Date(b.startAt).getTime(), ); const viewportPadding = 12; const panelWidth = Math.min(320, window.innerWidth - viewportPadding * 2); let top = anchorRect.bottom + 8; let left = anchorRect.left + anchorRect.width / 2 - panelWidth / 2; left = Math.max(viewportPadding, Math.min(left, window.innerWidth - panelWidth - viewportPadding)); const estimatedHeight = 56 + sorted.length * 52; if (top + estimatedHeight > window.innerHeight - viewportPadding) { top = Math.max(viewportPadding, anchorRect.top - estimatedHeight - 8); } return (

Overlapping appointments ({sorted.length})

    {sorted.map((apt) => { const purpose = apt.purpose as keyof typeof APPOINTMENT_PURPOSE_LABEL; return (
  • ); })}
); }