/** * Calendar pattern — the READ MODEL the host supplies, stable for a page * load: the practice's resources, catalogue, agenda extras and lookups. * It mirrors what the demo once read straight from its fixture module — * one property per constant, one method per fixture-backed helper — so * the demos hand in their fixtures and the platform island hands in what * `/internalApi/...` returned, and the model, panels and surface compute * from either without knowing which. * * Provided once with ``; components * read it with `useCalendarData()`, pure model functions take it as an * explicit `data` parameter — never a module-level global. * * Every REQUIRED member is one the shipped runtime actually reads. A * member is optional for one of two reasons, and its own doc comment * says which: either only the story-only Final State demo * (`src/patterns/demos/calendar/`) reads it, so a platform host * mounting `CalendarWorkspace` can leave it out; or its ABSENCE is a * state the pattern must tell apart from emptiness (`priceLists`), in * which case `undefined` means "we were never told" and `[]` means * "there is none" — see `catalogueStateOf` in `calendar.types.ts`. */ import { type ReactNode } from 'react'; import type { CalendarResource } from '../../components/calendar'; import type { PaletteSwatch } from '../../components/color-picker'; import type { AppointmentItem } from '../../components/appointment-card'; import type { NotesPanelNote } from '../../components/notes-panel'; import type { TasksPanelItem } from '../../components/tasks-panel'; import type { CalendarAppointmentMeta, CalendarBaseEvent, CalendarBookingContact, CalendarBranch, CalendarBusinessHours, CalendarDayHours, CalendarCare, CalendarCareEntry, CalendarCareLink, CalendarCarePlan, CalendarClosure, CalendarHistoryEntry, CalendarHoliday, CalendarOperatorGroup, CalendarPatient, CalendarPracticeFeatures, CalendarPriceList, CalendarTaskDetail, CalendarWaitingEntry, FreeSlotQuery } from './calendar.types'; import type { CalendarFreeSlot } from './calendar.model'; /** What the host has loaded for one local `Y-m-d`. */ export type DayLoadState = 'loading' | 'ready' | 'error'; export interface CalendarData { chairs: readonly CalendarResource[]; operators: readonly CalendarResource[]; /** Operator groups (platform `OperatorGroup`) for the grouped filter. */ operatorGroups: readonly CalendarOperatorGroup[]; /** Operators the practice has disabled (platform `isActive === false`). */ inactiveOperatorIds: readonly string[]; /** Operator id → local ISO dates the operator is away. */ operatorAbsences: Record; /** Operator id → absence display title (a sibling map, optional per operator). */ operatorAbsenceTitles: Record; /** * Appointment colours the host accepts. When omitted, the workspace uses * its documented Google-style fallback palette for demos and legacy hosts. * A platform host should provide its configured DoctorColor values so a * colour selected in the editor always maps back to a writable colour id. */ appointmentPalette?: readonly PaletteSwatch[]; holidays: readonly CalendarHoliday[]; /** Practice-closed local ISO dates. */ closedDates: readonly string[]; /** JS `getDay()` numbers the practice does not work. Optional: the * shipped workspace derives its hidden days from `businessHours` and * the weekend toggle; only the Final State demo reads this. */ nonWorkingDays?: readonly number[]; businessHours: CalendarBusinessHours; /** * Per-DATE closed periods — the one member that can say "shut 13:00–14:00". * `businessHours` holds one range per weekday, so a split shift arrives as * its envelope and the lunch closure inside it would read as bookable. * Keyed by local `Y-m-d`; each window is minutes from local midnight, * `1440` the following midnight, disjoint and ascending. The finder never * offers a slot that overlaps one, and the grid washes it on every column * of that date. A date without a key is one the host said nothing about: * it adds no closure and takes nothing away from `businessHours`. * Optional — the demos and hosts without per-date hours leave it out. */ closedIntervals?: Readonly>>; /** * Per-DATE load state, keyed by local `Y-m-d` — for a host that fills * the grid one range at a time. The grid washes every cell of a day * that is not `'ready'` and the finder refuses to answer from one, * because "no row here" and "no row here YET" are different answers. * ABSENCE and presence differ: omit it and every rendered day is taken * as loaded (the shape every host had before this existed); provide it * and a date it does not name is NOT ready, so a host that knows its * own days must name them all. Optional. */ dayLoadState?: Readonly>; /** The clock every relative label and ledger stamp is pinned to. */ nowIso: string; /** The date the calendar opens on. */ initialDateIso: string; /** * The practice's billing currency as an ISO 4217 code (`'GBP'`, * `'CHF'`, …), read wherever the pattern prints a price (patient * ledger, appointment billing lines, the price-list editor). Optional * and additive: every host that omits it renders exactly as before — * every read site falls back to `'EUR'`, the kit's original hardcoded * assumption, so a euro-zone host needs no change. */ currency?: string; cares: readonly CalendarCare[]; /** The practice's price lists. THREE states, never two — see * `catalogueStateOf`. Omit it (`undefined`) when the host has no * care/price-list catalogue to hand over, as the platform island * does; `[]` says the practice genuinely has none. Either way the * "From price list" relation is withheld rather than rendered over * nothing, and neither is ever papered over with an invented list. */ priceLists?: readonly CalendarPriceList[]; /** Optional: no shipped code reads the flat list — the panels go * through `carePlanById` / `openCarePlansForPatient`. */ carePlans?: readonly CalendarCarePlan[]; patients: readonly CalendarPatient[]; /** Companies a task may be "about" (platform `doctorCompany`). Omit it * and the task editor's "Riguarda" choice offers only Nobody / Patient * — a free-text company name is not an id and the platform would * reject or mis-link it, so the Company segment is withheld rather * than rendered over nothing to type an id into. */ companies?: ReadonlyArray<{ id: string; name: string; }>; /** The task types this practice offers, system and practice-defined in * ONE list. Omit it (or `[]`) and the Type field is withheld, as with * `companies`. `id` is an opaque token, unique here and never `''`; * `custom` only groups the select; `label` is the host's own string, * already translated. See calendar.mdx § Tasks. */ taskTypes?: ReadonlyArray<{ id: string; label: string; custom?: boolean; }>; /** Practice USER accounts a task can be assigned to (platform * `practiceUser`) — NOT `operators`, which is the clinician axis. Same * withheld-not-disabled contract; `'none'` is reserved for Nobody. */ taskAssignees?: ReadonlyArray<{ id: string; label: string; }>; /** Optional host typeahead. Omit for client filtering of `patients`. * The picker debounces nonblank queries, aborts superseded requests and * keeps the host's result order, including matches by phone or number. * Before resolving, make returned records available through `patientById` * so other patient-dependent fields can resolve the chosen id. Reject * failures; an empty array means a successful search with no matches. */ searchPatients?(query: string, options: { signal: AbortSignal; }): Promise; /** * Optional host free-slot search. Omit it and the finder keeps its * in-memory search over the loaded rows; provide it and the panel asks * the practice's own finder instead. * * Called on Search and after every busy-set change — at most one call * in flight, and one per background refresh tick while the panel is * open. Abort a superseded call through `options.signal`; reject on * failure (the panel says so and offers a retry); `[]` is a successful * search with nothing free. Slots arrive floored at `nowIso` on * whatever step the host's calendar interval implies — the kit names * none. `medicalDeviceIds` are the host's own strings, round-tripped * untouched, and `operatorIds` / `chairIds` are never empty: the panel * publishes the empty answer itself rather than ask about nothing. */ searchFreeSlots?(query: FreeSlotQuery, options: { signal: AbortSignal; }): Promise; /** The host's LOAD STAMP — a change re-runs the last server search (the * platform island hands in `loadedAt.getTime()`). Omit it and the busy * set alone triggers the re-search. */ revision?: number; /** The appointments and tasks as the host loaded them, no resource axis. */ baseEvents: readonly CalendarBaseEvent[]; /** Event id → appointment extras (patient, operator, chair, care, billing). */ metaById: Record; /** Event id → the note the patient left on the online booking form. */ bookingNotes: Record; /** Event id → what the person typed on the booking website. */ bookingContacts: Record; /** Task-event id → its chair / operator axis (tasks carry no meta). * Optional: the shipped workspace drops tasks at the feed boundary; * only the Final State demo, which shows them, reads this. */ taskAxis?: Record; /** Event id → platform-shaped task detail. */ taskDetails: Record; /** The waiting list as loaded — the journey seeds its state from it. */ waitingList: readonly CalendarWaitingEntry[]; /** Event id → the modification ledger as loaded. Optional: the * workspace reads the ledger off `CalendarJourney.history`, which the * host seeds however it likes. */ appointmentHistory?: Record; practiceFeatures: CalendarPracticeFeatures; branches: readonly CalendarBranch[]; /** Care id → the medical devices it needs. */ careDevices: Readonly>; /** Patient id → required documents still unsigned. */ unsignedRequiredDocs: Readonly>; /** Patients with an advanced e-signature (AES) setup. */ patientsWithAes: ReadonlySet; /** Appointment id → the deposit paid online when booking. */ paidOnline: Readonly>; /** Appointment id → the phone typed on the booking site. */ bookingPhones: Readonly>; patientById(id: string): CalendarPatient | undefined; patientNotes(patientId: string): NotesPanelNote[]; patientAppointments(patientId: string): AppointmentItem[]; patientTasks(patientId: string): TasksPanelItem[]; careById(id: string): CalendarCare | undefined; carePlanById(id: string): CalendarCarePlan | undefined; /** Optional: no shipped code calls it — the editor links through * `openCarePlansForPatient`, the details panel through * `carePlanById`. */ carePlansForPatient?(patientId: string): CalendarCarePlan[]; /** Plans the editor may link to: not discarded or closed, with undone lines. */ openCarePlansForPatient(patientId: string): CalendarCarePlan[]; /** The care entries a link resolves to — plan lines in plan order, or * the price-list picks. Unknown ids are dropped. */ careEntriesFromLink(link: CalendarCareLink): CalendarCareEntry[]; /** Closed dates plus holidays, unless the practice stays open on them. */ practiceClosedOn(dateIso: string): boolean; /** Why a date (or, given an operator, that column) cannot take a * booking — closed > holiday > absence; null when it can. */ closureOn(dateIso: string, operatorId?: string): CalendarClosure | null; /** Local dates in [from, to] with practice hours that are neither * closed nor a holiday. */ openDatesBetween(from: Date, to: Date): string[]; /** Waiting-since instant for an entry (platform `createdAt`). */ waitingSinceIso(entry: CalendarWaitingEntry): string; } /** Wrap whatever renders the calendar's panels and surface — one provider * per subtree is fine (a rail renders its panels outside the page tree). */ export declare function CalendarDataProvider({ value, children, }: { value: CalendarData; children?: ReactNode; }): import("react").FunctionComponentElement>; export declare function useCalendarData(): CalendarData; //# sourceMappingURL=calendar.data.d.ts.map