/** * Calendar demo — the SECONDARY SURFACES (the sheets): task details, the * patient record / notes / appointments / tasks panels, the appointment * editor (create, edit and details in ONE form) with its identity and * extras blocks, the patient care plan, the Final State's Backlog and * Increment 1's platform waiting list with their shared entry form, * Increment 1's online-bookings panel, the find-free-slots finder and * the front-desk print sheet. * * The editor's form is a DENSITY-TIERED grid: a container query on the * panel's own inline size pairs its short fields from 30rem and moves the * label beside the control from 44rem, so it fits the rail without * scrolling. No target shrinks — only the column count and the label's * position change. * * Every panel composes kit primitives over the host-supplied read model * (`useCalendarData()`, see `calendar.data.ts`) and the pure model in * `calendar.model.ts`; none knows WHERE it is hosted — the open / close * seam is `calendar.host.ts`, which the Final State demo maps onto its * full Control Room rail and the shipped `CalendarWorkspace` onto a * minimal five-tool one. Nothing here imports from the stories module, * the demos or the shared story shell. */ import { type ReactNode } from 'react'; import { AppointmentHistoryTimeline } from '../../components/appointment-details-panel/appointment-details-panel.history'; import { IndicatorChips } from '../../components/appointment-details-panel/appointment-details-panel.identity'; import type { CalendarEditorCapabilities } from './calendar.model'; import type { CalendarEditorSeries } from './calendar-editor-hydration'; import { type CalendarResource } from '../../components/calendar'; import { type NotesPanelNote, type NotesPanelProps, type NotesPanelSubmission, type AiConsentGate } from '../../components/notes-panel'; import { type ColorAssociation, type CalendarCarePlan, type CalendarAnamnesisAnswer, type CalendarOperatorGroup, type CalendarPatient, type CalendarWaitingEntry, type CalendarHistoryEntry, type CalendarSignChannel, type PatientRef } from './calendar.types'; import { type CalendarData } from './calendar.data'; import { type CalendarRow, type DisplaySettings, type EditorPrefill, type EditorDraft, type DraftSlot, type SlotPatch, type BacklogFilter, type BacklogItem, type WaitingEntryDraft, type CalendarFreeSlot, type PrintDaySection, type FindSlotsSeed, type AvailabilityContext } from './calendar.model'; import { type BacklogGap, type EditorDraftExtras, type DeleteOptions, type WorkspaceDisplaySettings } from './calendar-workspace.model'; import { type BookingCompleteIntent } from './calendar-bookings-panel'; /** The operators a NEW booking may take — the practice's ACTIVE cast, as * on the platform. A disabled operator reaches a list only through * `platform.operatorOptions` (the appointment that already holds her); * lookups by id stay inclusive so her own rows still name her. */ export declare function activeOperators(data: CalendarData): CalendarResource[]; /** Why the WhatsApp affordance cannot act, as an i18n key. Decided in ONE * place so the waiting list's two rows — the backlog card and the grid * cell — cannot drift apart again. * * Three states, never two (see `patientChannelState`). `unknown` folds * "no record at all" together with "a record that omits the number", so * a caller reading it as "no record" told a patient the row had just * rendered by name that their record had failed to load. Only * `ref.unresolved` — the lookup we really did fail — may say that; a * record we DID load and that simply carries no number says so itself, * and never claims the practice holds nothing (that is state `none`, * which keeps the platform's own SMS reason). */ export declare function whatsAppUnavailableKey(ref: PatientRef): string; export declare function TaskDetailsPanel({ row, locale, }: { row: CalendarRow; locale: string; }): import("react/jsx-runtime").JSX.Element; export declare function PatientRecordPanel({ patientId, locale, }: { patientId: string; locale: string; }): import("react/jsx-runtime").JSX.Element | null; /** * Where a note WRITE goes. The workspace builds one of these from its * `CalendarJourney` and hands it down; the panel renders `notes` and * reports intent, holding no note state and composing no note text of * its own. Nothing the kit invented — a draft paragraph, a transcript, * an author's name — can reach a patient's record through it. */ export interface PatientNotesSeam { /** The patient's notes as the HOST holds them, newest first. */ notes: NotesPanelNote[]; /** Commit what was typed. The host stamps the id, date and author. */ onAddNote: (submission: NotesPanelSubmission) => void | Promise; /** Replace a note's body: HTML for an HTML note, else plain text. */ onEditNote: (noteId: string, content: string) => void | Promise; /** Called on the click that confirms it. Resolve once the delete has * landed; a rejection restores the row and the panel shows the message. * A clinical note is taken as kept, marked deleted, unless the note * says `keptOnDelete: false`: its row waits in place. */ onDeleteNote: (noteId: string) => void | Promise; /** `next` is the value the note moves to. A rejection rolls the pin back * and the panel shows the message: do not also toast it. */ onTogglePin: (noteId: string, next: boolean) => void | Promise; /** As `onTogglePin`, for the flag. Omitted → no flag control. */ onToggleFlag?: (noteId: string, next: boolean) => void | Promise; /** Hears every refused save, delete, pin or flag. See `NotesPanel`'s * `onWriteError`: report only the ones with `shown: false`. */ onWriteError?: NotesPanelProps['onWriteError']; /** The practice's own drafting endpoint. Omitted → no AI affordance. */ onRequestAiDraft?: NotesPanelProps['onRequestAiDraft']; /** The host's live transcription. Omitted → no microphone. */ onDictate?: NotesPanelProps['onDictate']; /** * The patient's recorded AI-consent state, gating Help-me-write and both * mics. Omitted → today's ungated behaviour (matches `NotesPanel`). */ aiConsent?: AiConsentGate; /** Template variables for the composer's "Insert variable" menu. See * `RichTextEditor`. */ variables?: NotesPanelProps['variables']; /** Decide whether a `[[TOKEN]]` is valid. See `RichTextEditor`. */ validateVariable?: NotesPanelProps['validateVariable']; /** The invalid `[[TOKEN]]`s in the draft. See `RichTextEditor`. */ onVariableValidityChange?: NotesPanelProps['onVariableValidityChange']; /** Bring your own Help-me-write surface (e.g. practice templates). See * `RichTextEditor`. */ renderHelpMeWrite?: NotesPanelProps['renderHelpMeWrite']; /** Practice templates in the composer's AI pill. See `RichTextEditor`. */ aiTemplates?: NotesPanelProps['aiTemplates']; /** After a pill action rewrote the draft. See `RichTextEditor`. */ onAiApplied?: NotesPanelProps['onAiApplied']; /** Upload an image yourself and resolve its URL. See `RichTextEditor`. */ onUploadImage?: NotesPanelProps['onUploadImage']; /** An AI call ran out of credits. See `RichTextEditor`. */ onAiCreditsExhausted?: NotesPanelProps['onAiCreditsExhausted']; /** Show the editor extensions affordance. See `RichTextEditor`. */ showExtensions?: NotesPanelProps['showExtensions']; /** Shows the AI-credits/bundles hover card on every AI trigger. See * `RichTextEditor`. */ aiCreditsNotice?: NotesPanelProps['aiCreditsNotice']; /** Editor extensions offered under the toolbar's toy brick. See * `RichTextEditor`. */ extensions?: NotesPanelProps['extensions']; extensionContext?: NotesPanelProps['extensionContext']; onExtensionRun?: NotesPanelProps['onExtensionRun']; /** Rendered just above the composer — e.g. the templates pattern's * `undoNotice`. See `NotesPanel`. */ composerBefore?: NotesPanelProps['composerBefore']; /** Rendered just after the composer — e.g. the templates pattern's * `manager`. See `NotesPanel`. */ composerAfter?: NotesPanelProps['composerAfter']; /** The host's read. Absent while `onLoad` is present reads as loading. */ status?: 'loading' | 'ready' | 'error'; total?: number | null; /** True once `notes` is the full list rather than a preview. */ complete?: boolean; canAddClinicalNote?: boolean; /** Whether this reader may write notes. Beats the panel's `editable`. */ writable?: boolean; onLoad?: (scope: 'preview' | 'all') => void; onRetry?: () => void; } export declare function PatientNotesPanel(props: Parameters[0]): import("react/jsx-runtime").JSX.Element; declare function PatientNotesPanelContent({ patientId, seam, editable, currentNoteId, currentNoteKey, }: { patientId: string; /** * Where every write goes. WITHOUT one the panel is a read-only * timeline read from the host's read model: there is nowhere to * persist to, so no write is offered and none can be lost. */ seam?: PatientNotesSeam; /** * Whether this reader may write when `seam.writable` is absent: note * writes follow the notes, not `practice_appointment_edit`. False (or * no `seam`) leaves the composer, the pin, the in-place edit and the * delete unmounted. */ editable?: boolean; /** Address one note, exactly as `NotesPanel` does. */ currentNoteId?: string; currentNoteKey?: string | number; }): import("react/jsx-runtime").JSX.Element; /** * The latest few notes for the appointment details panel (N-C3, N-C5), in * the notes tool's order and read-only. See more or a click on a note * hands over to the host's Notes tool on the same seam. */ export declare function PatientNotesPreview({ patientId, seam, onOpenNotes, limit, }: { patientId: string; seam: PatientNotesSeam; /** Open the notes tool, addressed to one note when an id is given. */ onOpenNotes?: (noteId?: string) => void; limit?: number; }): import("react/jsx-runtime").JSX.Element; export declare function PatientAppointmentsTimeline({ patientId, locale, }: { patientId: string; locale?: string; }): import("react/jsx-runtime").JSX.Element; export { AppointmentHistoryTimeline }; export declare function PatientTasksPanel({ patientId }: { patientId: string; }): import("react/jsx-runtime").JSX.Element; export declare function DurationCombobox({ value, onChange, }: { value: number | null; onChange: (next: number | null) => void; }): import("react/jsx-runtime").JSX.Element; /** Channels the editor's send-reminder group may offer. */ export type ReminderChannel = 'whatsapp' | 'sms' | 'email'; /** The Final State's full set — the default when a host says nothing. */ export declare const REMINDER_CHANNELS: readonly ReminderChannel[]; /** The platform facts the Increment 1 editor adds — every field optional, * so a host that passes nothing gets the Final State form unchanged. */ export interface EditorPlatformOptions { /** Practice branches (platform "Branca"); the select shows for > 1. */ branches?: readonly { id: string; name: string; }[]; /** Care id → device names; drives the read-only devices line. */ devicesByCare?: Readonly>; /** Required documents still unsigned for a patient (sign-mode radios). */ unsignedRequiredDocs?: (patientId: string) => number; /** The agenda, for the busy chair / busy operator warnings. */ rows?: readonly CalendarRow[]; /** Enter in a text field submits (the platform's modal never did). */ submitOnEnter?: boolean; /** The edited appointment belongs to a series — offer the save scope. */ recurringScope?: boolean; /** Price-list recurrence parent — ask about the care-plan lines on delete. */ carePlanCleanup?: boolean; /** Operator options for the select — defaults to the shared active * cast. A host whose appointment is held by an operator the practice * has since DISABLED passes the wider list, so the name still shows * (a new appointment keeps the active cast, as on the platform). */ operatorOptions?: readonly { id: string; title: string; }[]; /** Reminder channels in the action footer — defaults to all three. * The platform has NO "send reminder now" action on the calendar; its * only outbound bridge is the popover's WhatsApp Snippets island, so * Increment 1 narrows this to `['whatsapp']` (audit A2). */ reminderChannels?: readonly ReminderChannel[]; /** Offer the Final State's extra recurrence rules — the "Ends: after N * / on a date" choice and the skip-closed-days switch. The platform's * modal only ever sets a repetition count, so Increment 1 turns this * off and keeps frequency + count alone (audit A3). */ recurrenceExtras?: boolean; /** Offer the Custom rule (HTP-4261) inline: every N days, weeks, months * or years, the weekdays of a weekly rule, the day or nth weekday of a * monthly one, and a summary line. Its draft carries `interval` and * `byDay` (platform `recurrenceInterval` / `recurrenceByDay`), so a * host that cannot save them leaves this off. Defaults to * `recurrenceExtras`. */ recurrenceCustom?: boolean; /** Show the skip-closed-days switch. Defaults to `recurrenceExtras`. * The platform has no closed-day skip, so a platform host that turns * `recurrenceExtras` on for the end date turns this off. */ recurrenceSkipClosedDays?: boolean; /** Warn — never block — when the chosen operator is away that day, as * a third sibling of the busy warnings. Off: an absence keeps the * date-side closure banner and holds the save (audit C9). */ operatorAbsenceWarning?: boolean; /** Show the reminder checkboxes only once a patient is chosen, and * disable each channel the patient cannot take, with the platform's * "(disabled)" suffix on the label (audit C10). */ reminderCapability?: boolean; /** Show "First visit" only once a patient is chosen (audit C11). */ firstVisitRequiresPatient?: boolean; /** Give each price-list row its catalogue phases, and lock a row the * practice has already invoiced (audit C12). */ priceListPhases?: boolean; /** Refuse a description longer than this and say so (platform: 255 * characters, silently blocking; audit C14). */ descriptionMaxLength?: number; /** Where "save and go back" goes — the deep link's origin. Only ever * reached from a prefill with `simpleSave: false` (audit C13). */ onSaveAndReturn?: () => void; } export declare function AppointmentEditorPanel({ prefill, onSave, onDelete, onDuplicate, panelTitle, headerIcon, identity, after, closeOnSave, returnsOnCancel, onDraftChange, onPatientContextChange, slotPatch, colorAssociation, locale, scheduleByEntry, history, noteUnrecordedEdits, onMove, onCopy, onWhatsApp, readOnly, capabilities: hostCapabilities, series, platform, className, }: { prefill?: EditorPrefill; capabilities?: CalendarEditorCapabilities; /** A host-read occurrence: its scope is asked before the form. */ series?: CalendarEditorSeries; /** The editor closes only after acceptance. A rejection leaves the * draft intact and displays its safe, user-facing Error message. */ onSave: (draft: EditorDraft, extras?: EditorDraftExtras) => void | Promise; /** Live composition feed — the grid mirrors the slot being filled as a * draft placeholder while this panel is open. Emits `null` on unmount * so the placeholder clears with the panel. */ onDraftChange?: (draft: DraftSlot | null) => void; /** Reports the current patient to an optional workspace Notes rail tool. */ onPatientContextChange?: (patientId?: string) => void; /** Slot edits made on the grid (dragging or resizing the draft mirror). * Applied by `tick` and only to the slot fields, so everything else * the user has typed survives. */ slotPatch?: SlotPatch | null; /** Edit mode only — removes the event; confirmed via an alert dialog * that also asks the recurring scope and the care-plan cleanup. */ onDelete?: (eventId: string, options?: DeleteOptions) => void; /** Edit mode only — re-seeds the CREATE editor from the current form * state (everything but the identity). Unset hides the Duplicate icon * altogether, leaving Copy… placement as the one copy path. */ onDuplicate?: (prefill: EditorPrefill) => void; /** Panel-chrome heading override — "Appointment details" reuses this * SAME form so read and edit are one surface, not two paradigms. */ panelTitle?: string; headerIcon?: ReactNode; /** Identity block (avatar · name · status chip) above the form. */ identity?: ReactNode; /** Read-only extras (billing, patient contact) below the form. */ after?: ReactNode; closeOnSave?: boolean; /** Cancel returns somewhere (the details panel), so an unchanged edit * may cancel too. */ returnsOnCancel?: boolean; /** The cog's colour association: the default colour on create and the * re-colour when the chair/operator select changes. */ colorAssociation: ColorAssociation; /** BCP 47 locale for the checklist's scheduled-date chips and prices. */ locale?: string; /** Plan line id → its booked appointments (`scheduleByEntryId`). */ scheduleByEntry?: Record>; /** Details only — the ledger behind the History sub-view (newest first). */ history?: CalendarHistoryEntry[]; /** In History, say "What changed was not recorded." under an edit that * carries no detail. Off by default. */ noteUnrecordedEdits?: boolean; /** Edit only — arm the grid's move mode (the platform's "Sposta"). */ onMove?: () => void; /** Edit only — arm the grid's copy mode; a linked appointment offers * the platform's two copies (appointment only / with treatments). */ onCopy?: (withTreatments: boolean) => void; /** Bridge the WhatsApp reminder to the legacy Snippets island. */ onWhatsApp?: () => void; /** No edit permission: every write action goes, the form locks and a * note says why. */ readOnly?: boolean; /** Platform parity extras — see `EditorPlatformOptions`. */ platform?: EditorPlatformOptions; /** Root class — a dialog host bounds the panel's height through it. */ className?: string; }): import("react/jsx-runtime").JSX.Element; export { IndicatorChips }; export declare function AppointmentIdentity({ row, patientRef, showAge, onOpenPatient, anamnesisAnswers, }: { row: CalendarRow; /** Who this appointment is for: the id it carries and the record, when * the host could resolve it. */ patientRef: PatientRef; /** Append the platform popover's "(NN anni)" to the name (audit C15). */ showAge?: boolean; /** Set = the name becomes the platform's patient-folder link. */ onOpenPatient?: (patient: CalendarPatient) => void; /** The highlighted anamnesis answers themselves — the platform's * details dialog lists them, the popover only counts (audit C16). */ anamnesisAnswers?: readonly CalendarAnamnesisAnswer[]; }): import("react/jsx-runtime").JSX.Element; export declare function SendReminderGroup({ onWhatsApp, channels: allowed, }: { /** When set, the WhatsApp segment bridges to the legacy Snippets * island instead of the demo-fake send. */ onWhatsApp?: () => void; /** Which channels to offer — defaults to all three. Increment 1 keeps * the WhatsApp bridge alone, the platform's only outbound path. */ channels?: readonly ReminderChannel[]; }): import("react/jsx-runtime").JSX.Element | null; /** The platform popover facts the Final State never modelled — every * field optional, so a host that passes nothing sees the extras as before. */ export interface ExtrasPlatformOptions { /** Deposit paid online when booking (Stripe). */ paidOnlineAmount?: number; /** The patient's outstanding balance. */ toCollectAmount?: number; /** The user's map-link setting (the display cog). */ mapsLink?: boolean; /** The phone typed on the booking site, when it differs from the record. */ bookingPhone?: string; } export declare function AppointmentExtras({ row, patient, locale, signRequestedVia, onRequestSignature, onRequestSignatureOnDevice, readOnly, platform, }: { row: CalendarRow; patient?: CalendarPatient; locale: string; /** Channel the signature request went out on (journey state wins). */ signRequestedVia?: CalendarSignChannel; onRequestSignature?: (eventId: string, channel: CalendarSignChannel) => void; /** "On this device…" — bridges to the legacy e-signature flow. */ onRequestSignatureOnDevice?: () => void; /** No edit permission: the request menu and the setup links go. */ readOnly?: boolean; /** Platform parity extras — see `ExtrasPlatformOptions`. */ platform?: ExtrasPlatformOptions; }): import("react/jsx-runtime").JSX.Element; export declare function PatientCarePlanPanel({ plans, linkedPlanId, linkedEntryIds, schedule, locale, onReveal, }: { plans: CalendarCarePlan[]; /** The selected appointment's plan — listed first, entries expanded. */ linkedPlanId?: string; linkedEntryIds: string[]; schedule: Record>; locale: string; /** Navigate the grid to the appointment and select it. */ onReveal: (eventId: string) => void; }): import("react/jsx-runtime").JSX.Element; export declare function BacklogPanel({ items, locale, seed, signRequests, onProcess, onFocusEvent, onConfirm, onRequestSignature, onScheduleWaiting, onRemoveWaiting, onFindSlotsForWaiting, onUpsertWaiting, quickView, onOpenEntryForm, onPlaceWaiting, }: { items: BacklogItem[]; locale: string; /** A fresh tick preselects `type` — how every caller opens the queue * on one segment without reaching into panel state. */ seed: { tick: number; type: BacklogFilter; }; /** Event id → the channel a signature request went out on this session. */ signRequests: Record; /** Dock the editor prefilled (Process booking, Assign operator). */ onProcess: (prefill: EditorPrefill) => void; /** Ring the appointment on the grid so it is clear which one is meant. */ onFocusEvent: (eventId: string) => void; /** Unconfirmed → confirmed, in place. */ onConfirm: (eventId: string) => void; onRequestSignature: (eventId: string, channel: CalendarSignChannel) => void; onScheduleWaiting: (entry: CalendarWaitingEntry) => void; onRemoveWaiting: (entry: CalendarWaitingEntry) => void; /** Hand the entry to the find-free-slots tool, prefilled. */ onFindSlotsForWaiting: (entry: CalendarWaitingEntry) => void; /** The entry form's Save — add (no id) or update (id) in the queue. */ onUpsertWaiting: (draft: WaitingEntryDraft) => void; /** Increment 1: the platform's waiting-list quick views above the queue. */ quickView?: boolean; /** Set = Add entry and the row's edit open the HOST's entry form (a * dialog) instead of the in-panel sub-view. */ onOpenEntryForm?: (entry?: CalendarWaitingEntry) => void; /** Set = Schedule / Reschedule hands the entry to the host's placement * mode (create / move banner) instead of opening the editor at once. */ onPlaceWaiting?: (entry: CalendarWaitingEntry) => void; }): import("react/jsx-runtime").JSX.Element; export declare function BookingsPanel({ bookings, locale, privacy, status, lastSeenIso, createdAtById, currentBookingId, currentBookingKey, onRetry, onShowInCalendar, onComplete, resourceKind, filters, onFiltersChange, }: { bookings: readonly CalendarRow[]; locale?: string; /** Masks the patient names, in step with the grid. */ privacy?: boolean; status?: 'loading' | 'ready' | 'error'; lastSeenIso?: string | null; createdAtById?: Readonly>; /** The booking the host is addressing: scrolled to, focused, and ringed * in the accent until the ring settles. Unknown ids are ignored. */ currentBookingId?: string; /** Bump on every address, so the SAME booking addressed twice scrolls, * focuses and rings again. An address is a request, not a value. */ currentBookingKey?: string | number; onRetry?: () => void; /** Steers the grid to the booking and rings its card. */ onShowInCalendar: (eventId: string) => void; /** Opens the editor to complete it (platform "Completa appuntamento"). * `intent` says which assign segment was pressed; the pencil sends none. */ onComplete?: (row: CalendarRow, intent?: BookingCompleteIntent) => void; /** The grid's column axis — chair-columns hide a chairless row, so the * panel says so once when that is the current view. */ resourceKind?: WorkspaceDisplaySettings['resourceKind']; /** The three gap switches, behind the header's settings cog. `bookings` * already arrives filtered by the host (`CalendarWorkspace` applies * `filterBacklogViews` before this panel ever sees the rows); these two * only drive the controls themselves and are optional so a host * bypassing the workspace still gets an unfiltered list rather than a * crash. Omit `filters` and no cog is rendered. */ filters?: Readonly>; onFiltersChange?: (filters: Readonly>) => void; }): import("react/jsx-runtime").JSX.Element; /** * The platform's waiting-list AddModal as a rail sub-view — same field * order (patient, operator, colour, description) and its one rule: * a patient OR a description, checked on submit with an inline error. */ export declare function WaitingListEntryForm({ entry, onSave, onCancel, chrome, readOnly, }: { /** Set = edit this entry; unset = add a new one. */ entry?: CalendarWaitingEntry; onSave: (draft: WaitingEntryDraft) => void; onCancel: () => void; /** No edit permission: the fields lock, Save is withheld and `submit` * refuses — the same three layers the appointment editor uses, so no * path can close this form as if a queue entry had been written. */ readOnly?: boolean; /** `'dialog'` drops the rail's back arrow — the dialog's own Close and * the form's Cancel already dismiss it. */ chrome?: 'panel' | 'dialog'; }): import("react/jsx-runtime").JSX.Element; /** * The same entry form as a `PanelSubview`: it slides over the queue it was * opened from, pins Cancel and Save, and hands focus back to the opener. */ export declare function WaitingListEntrySubview({ open, entry, onSave, onCancel, onRemove, readOnly, }: { open: boolean; /** Set = edit this entry; unset = add a new one. */ entry?: CalendarWaitingEntry; onSave: (draft: WaitingEntryDraft) => void; onCancel: () => void; /** Edit mode only: the panel's EXISTING remove seam (the card's ⋮ menu * `onRemove`) — reused here rather than a second one. Unset or no * `entry` (add mode) and the footer's leading slot is not rendered. */ onRemove?: (entry: CalendarWaitingEntry) => void; readOnly?: boolean; }): import("react/jsx-runtime").JSX.Element; /** * The call sheet itself. A portal to `document.body`: the shell's AppFrame * is an `h-dvh overflow-hidden` clamp that prints one screenful, so the * sheet lives outside it — hidden on screen, the only thing on paper. */ export declare function PrintSheet({ sections, rangeTitle, axisKind, visibleChairIds, visibleOperatorIds, privacy, locale, }: { sections: PrintDaySection[]; rangeTitle: string; axisKind: DisplaySettings['resourceKind']; visibleChairIds: string[]; visibleOperatorIds: string[]; privacy: boolean; locale: string; }): import("react").ReactPortal | null; /** The platform depth the dialog host asks for (decision 6). Every field * is optional, so the rail keeps exactly the behaviour it had. */ export interface FindSlotsPlatformOptions { /** Results as clickable rows and NO grid projection — the dialog host. */ listOnly?: boolean; /** Care id → medical devices it needs (`FIND_FREE_SLOTS_BY_CARE`). */ devicesByCare?: Readonly>; /** Warn past the platform's look-ahead ceiling; the search still runs. */ maxRangeDays?: number; /** "Show more slots" pushes the To date out by this many days. */ pagerStepDays?: number; /** Advisory: the practice has at least one business-hours row. */ practiceHasBusinessHours?: boolean; /** Advisory: at least one operator has business-hours rows. */ operatorsHaveBusinessHours?: boolean; /** The advisory's scheduler link. */ onOpenScheduler?: () => void; /** Operator and chair scope as grouped checklists (the platform's * `OperatorDropdown` / `ChairDropdown`) instead of one Select plus * "Any". Nothing ticked means every eligible resource, exactly as * the platform's empty selection does server-side. */ multiSelectScope?: boolean; /** Group sections for that operator checklist (platform * `GetGroupedOperatorService`); absent = one flat list. */ operatorGroups?: readonly CalendarOperatorGroup[]; } export declare function FindFreeSlotsPanel({ rows, locale, seed, onBookSlot, onShowInCalendar, onSearchRange, platform, canBook, }: { rows: CalendarRow[]; locale: string; /** Prefill context (the tool keys its panel by seed.tick). */ seed?: FindSlotsSeed | null; onBookSlot: (prefill: EditorPrefill) => void; /** False for a viewer without the appointment-edit right: the slots * still list and project, but no row offers Book. */ canBook?: boolean; /** Platform depth the rail never needed — absent = rail behaviour. */ platform?: FindSlotsPlatformOptions; /** Project the found free ranges onto the grid (null = clear) — the * Calendar dims booked cards and glows the free ranges. The context * carries the seeded patient/care into grid-click bookings. */ onShowInCalendar: (slots: CalendarFreeSlot[] | null, fromDate?: Date, context?: AvailabilityContext) => void; /** Retained for host compatibility; projection supports every grid axis. */ gridAxis?: 'none' | 'chair' | 'operator'; /** * The window a search is about to cover, raised BEFORE it runs. * * The projection's busy set is `rows` and nothing else, so a host whose rows * cover only the visible grid would have this report booked time as free * over a range it was never told about. A host that loads on this call gets * a corrected search for nothing: new rows re-render the panel and the * search runs again. */ onSearchRange?: (range: { from: Date; to: Date; }) => void; }): import("react/jsx-runtime").JSX.Element; //# sourceMappingURL=calendar.sheets.d.ts.map