import { useCallback, useRef, useState } from 'react'; import type { EventInput } from '@fullcalendar/core'; import { useAppointmentServiceContext } from '../services/AppointmentService'; import type { MyVisitInvitation } from '../types/visitInvitation'; import { visitTitle } from '../utils/visitInvitationWords'; /** * The caller's accepted team video visits in the visible range, for their own calendar. They come from the self-scoped * invitation route (`view=calendar`), so a visit hosted by another organization appears even though this organization's * appointment read cannot see it. Only the latest range's answer is kept, so a slow read for an old range is dropped. */ export const useAcceptedTeamVisits = () => { const { fetchMyVisitCalendar } = useAppointmentServiceContext(); const [visits, setVisits] = useState([]); const [failed, setFailed] = useState(false); const latest = useRef(0); const load = useCallback(async (start: Date, end: Date) => { const ticket = ++latest.current; try { const rows = await fetchMyVisitCalendar(start.toISOString(), end.toISOString()); if (ticket !== latest.current) return; setVisits(rows); setFailed(false); } catch (error) { if (ticket !== latest.current) return; const status = (error as { status?: number })?.status; // A caller the route does not serve has no team visits; anything else is a failure worth saying. setVisits([]); setFailed(status !== 401 && status !== 403 && status !== 404); } }, [fetchMyVisitCalendar]); return { visits, failed, load }; }; // A visit that will not take place, or never did: it would sit beside a live one at the same time and read the same. const NOT_A_VISIT = new Set(['cancelled', 'entered-in-error']); /** Calendar events for accepted team visits, leaving out any this organization's own appointments already show. */ export const teamVisitEvents = (visits: MyVisitInvitation[], shownAppointmentIds: Set): EventInput[] => visits .filter((visit) => visit.start && !NOT_A_VISIT.has(visit.appointmentStatus ?? '') && !shownAppointmentIds.has(visit.appointmentId)) .map((visit) => ({ id: `team-visit-${visit.appointmentId}`, title: `Team video ยท ${visitTitle(visit)}`, start: visit.start, end: visit.end, extendedProps: { type: 'team-visit', appointmentId: visit.appointmentId }, }));