import { formatTime, choiceTime } from './dateTime'; import * as React from 'react'; import { __ } from '@wordpress/i18n'; import type { BookingApi } from './api'; import type { Slot, BookingResource } from './types'; /** Rendering state only. Every selectable end and duration comes from the backend. */ export function useBookingTimes(api: BookingApi, route: string, query: Record, initialStart = '', initialEnd = '', initialSlot?: Slot, preserveExistingTerm = false) { const initialResource = React.useRef(query.resource_id).current; const [start, setStart] = React.useState(initialStart), [end, setEnd] = React.useState(initialEnd || initialSlot?.end_times[0]?.ends_at || ''); const [revision, refresh] = React.useReducer(n => n + 1, 0); const key = query.resource_id && query.date ? JSON.stringify(query) : ''; const [state, setState] = React.useState({ key: '', slots: initialSlot ? [initialSlot] : [] as Slot[], loading: !!key, error: '', restricted: [] as (Slot & { booking_restriction_message?: string; booking_contact_tel?: string; booking_contact_phone?: string; })[], reason: '' }); React.useEffect(() => { let alive = true; if (!key) { setState({ key, slots: [], loading: false, error: '', restricted: [], reason: '' }); return; } setState(old => ({ ...old, key, loading: true, error: '' })); api.request<{ intervals: Slot[]; restricted_intervals?: typeof state.restricted; booking_restriction_message?: string; }>(route, 'GET', undefined, JSON.parse(key)).then(value => { if (alive) { setState({ key, slots: value.intervals, loading: false, error: '', restricted: value.restricted_intervals ?? [], reason: value.booking_restriction_message ?? '' }); } }).catch(e => { if (alive) setState({ key, slots: [], loading: false, error: e.message, restricted: [], reason: '' }); }); return () => { alive = false; }; }, [api, route, key, start, revision]); const selected = state.slots.find(slot => slot.starts_at === start), ends = selected?.end_times ?? []; // Historical terms remain visible for notes-only edits. Changed terms still use backend ends. const originalTerm = preserveExistingTerm && !!initialEnd && end === initialEnd && start === initialStart && query.resource_id === initialResource && query.date === initialStart.slice(0, 10); const selectedEnd = ends.find(choice => choice.ends_at === end) ?? (originalTerm ? undefined : ends[0]); React.useEffect(() => { if (key && state.key === key && !state.loading && !originalTerm) { setEnd(old => ends.some(choice => choice.ends_at === old) ? old : ends[0]?.ends_at ?? ''); } }, [key, state.key, state.loading, selected, originalTerm]); const valid = !!key && state.key === key && !state.loading && !!selected && !!selectedEnd; const unavailable = !!start && !!key && state.key === key && !state.loading && !selected; function chooseStart(value: string) { setStart(value); setEnd(''); } return { start, end: selectedEnd?.ends_at ?? end, setEnd, chooseStart, clear: () => { setStart(''); setEnd(''); }, slots: state.slots, ends, duration: selectedEnd?.duration_minutes ?? 0, valid, unavailable, loading: state.loading || state.key !== key, error: state.error, restrictionContact: state.restricted.find(slot => slot.starts_at === start) ?? state.restricted[0], restriction: state.restricted.find(slot => slot.starts_at === start)?.booking_restriction_message || state.reason || state.restricted[0]?.booking_restriction_message || '', refresh }; } export type BookingTimesState = ReturnType; export function BookingTerm({ resources, date, onDate, resource, onResource, resourceName, times, disabled = false, focusEnd = false, customTime = false }: { resources: BookingResource[]; date: string; onDate: (v: string) => void; resource: string; onResource: (v: string) => void; resourceName?: string; times: BookingTimesState; disabled?: boolean; focusEnd?: boolean; customTime?: boolean; }) { return

{__('Date and time', 'abece-booking')}

{!customTime && <> }
{times.loading &&

{__('Checking available times…', 'abece-booking')}

}{times.error &&

{times.error}

}{times.restriction &&

{times.restriction}{times.restrictionContact?.booking_contact_tel && <> {times.restrictionContact.booking_contact_phone}}

}{!customTime && times.unavailable && !times.restriction &&

{__('A booking can no longer be made at this time.', 'abece-booking')}

}{!customTime && !times.loading && !times.start && resource && date && !times.slots.length &&

{__('No available times for this date.', 'abece-booking')}

}
; } export function clock(iso: string): string { return formatTime(iso); }