@patientos/website-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ /** The four theme values an island will honour. Anything absent falls back to kit defaults. */
2
+ interface PatientSurfaceTheme {
3
+ /** Brand colour — the island's primary. */
4
+ primary?: string;
5
+ /** Secondary emphasis colour. */
6
+ accent?: string;
7
+ /** Corner radius, a plain CSS length. */
8
+ radius?: string;
9
+ /** Font family stack. */
10
+ fontFamily?: string;
11
+ }
12
+ /**
13
+ * The clinic-supplied half of an island's theme, validated.
14
+ *
15
+ * Reads `primary` (falling back to the `brandColor` key the brand row uses), `accent`,
16
+ * `radius` and `fontFamily` off a loose token bag. Every value is checked; failures are
17
+ * dropped silently — a broken token should degrade to the kit default, never fail a build
18
+ * or a render. `null`/absent tokens yield `{}`.
19
+ */
20
+ declare function patientSurfaceTheme(themeTokens: Record<string, unknown> | null): PatientSurfaceTheme;
21
+
22
+ export { type PatientSurfaceTheme, patientSurfaceTheme };
@@ -0,0 +1,7 @@
1
+ import {
2
+ patientSurfaceTheme
3
+ } from "./chunk-JTZ6GYA7.js";
4
+ import "./chunk-MLKGABMK.js";
5
+ export {
6
+ patientSurfaceTheme
7
+ };
@@ -0,0 +1,113 @@
1
+ import * as React from 'react';
2
+
3
+ /** Which portal surface this panel shows. */
4
+ type PortalSurface = 'appointments' | 'documents' | 'orders' | 'profile';
5
+ /** JSON-safe island configuration. Crosses the build→runtime boundary — see `./islands`. */
6
+ type PortalPanelProps = {
7
+ /** The surface to render. Required — a panel is always ONE surface. */
8
+ surface: PortalSurface;
9
+ /**
10
+ * Where a signed-out visitor is sent to sign in. Omitted ⇒ the app's reserved
11
+ * `/portal` path on this same origin.
12
+ */
13
+ portalUrl?: string;
14
+ };
15
+ /** Marker-only props: the island config plus presentation the page controls. */
16
+ type PortalPanelMarkerProps = PortalPanelProps & {
17
+ className?: string;
18
+ };
19
+ /**
20
+ * BUILD-TIME marker. Emits the mount point plus a static no-JS fallback.
21
+ *
22
+ * The fallback deliberately shows NO patient data (there is none at build time — the
23
+ * page is ONE static artifact served to every visitor) and only a sign-in link. It is
24
+ * fully VISIBLE, not a hidden skeleton: this HTML is what a crawler indexes, what a
25
+ * screen reader reads before the island mounts, and what a patient with JS blocked
26
+ * gets permanently. A `visibility:hidden` / `opacity:0` placeholder would make the
27
+ * no-JS experience a blank box.
28
+ */
29
+ declare function PortalPanel({ surface, portalUrl, className, }: PortalPanelMarkerProps): React.ReactElement;
30
+
31
+ /** A service the launcher can offer — what the build snapshot carries, nothing more. */
32
+ type LauncherService = {
33
+ /** The clinic-stable `service.key`; what the funnel starts from. */
34
+ key: string;
35
+ label: string;
36
+ };
37
+
38
+ /** The build-time wiring an island is handed (the snapshot's `publicApi` block). */
39
+ interface PublicApiConfig {
40
+ publishableKey: string;
41
+ /** '' ⇒ same-origin (the one-origin case). */
42
+ apiBase: string;
43
+ turnstileSiteKey?: string;
44
+ portalUrl?: string;
45
+ portalOrigin?: string;
46
+ }
47
+
48
+ /**
49
+ * Which account surface this island renders.
50
+ *
51
+ * A SUPERSET of `<PortalPanel/>`'s four: the teaser can only summarise things that
52
+ * already exist, so it has no `book` and no `addresses` — there is nothing to tease. The
53
+ * four shared names stay identical so a clinic page can pair a teaser and a full surface
54
+ * without a second vocabulary (PAT-783), and the account island adds the two DOING
55
+ * surfaces on top (PAT-789).
56
+ */
57
+ type PortalAccountSurface = PortalSurface | 'book' | 'addresses';
58
+ /** JSON-safe island configuration. Crosses the build→runtime boundary — see `./islands`. */
59
+ type PortalAccountProps = {
60
+ /** The surface to render. Required — one island is always ONE surface. */
61
+ surface: PortalAccountSurface;
62
+ /**
63
+ * The portal's base path on this origin, used for the two things this island still
64
+ * does NOT inline (joining a telehealth consult, the signed-out/unreachable fallback).
65
+ * Omitted ⇒ the app's reserved `/portal`.
66
+ */
67
+ portalUrl?: string;
68
+ /**
69
+ * The SITE page carrying `<PortalAccount surface="book"/>`.
70
+ *
71
+ * Booking is inline now (PAT-789), so "Book an appointment" must land on the clinic's
72
+ * OWN page rather than bouncing to `/portal/book` — the bounce is the seam one-origin
73
+ * exists to remove. Omitted ⇒ `/account/book`, where the scaffolded account pages live.
74
+ */
75
+ bookHref?: string;
76
+ /**
77
+ * The SITE page carrying `<PortalAccount surface="appointments"/>` — where a completed
78
+ * booking sends the patient. Omitted ⇒ `bookHref` minus its last segment, which is the
79
+ * right answer for the scaffolded `/account/book` → `/account` pairing.
80
+ *
81
+ * NO CONSUMER since PAT-899: the account area stopped completing bookings when `book`
82
+ * became a launcher, and the funnel owns where a finished request lands. Kept so pages
83
+ * already passing it keep building; drop it once no site does.
84
+ */
85
+ appointmentsHref?: string;
86
+ /**
87
+ * The SITE page carrying `<CertificateFunnel/>` — where the `book` surface sends a
88
+ * patient who picks a service (PAT-899). Omitted ⇒ `/certificates`.
89
+ */
90
+ funnelHref?: string;
91
+ /** The clinic's services, for the `book` launcher. */
92
+ services?: LauncherService[];
93
+ /** How the launcher reaches the public API to price those services. */
94
+ publicApi?: PublicApiConfig;
95
+ /** Shown when the clinic has published no services at all. */
96
+ clinicPhone?: string;
97
+ };
98
+ /** Marker-only props: the island config plus presentation the page controls. */
99
+ type PortalAccountMarkerProps = PortalAccountProps & {
100
+ className?: string;
101
+ };
102
+ /**
103
+ * BUILD-TIME marker. Emits the mount point plus a static no-JS fallback.
104
+ *
105
+ * The fallback is REAL, VISIBLE content — a sign-in card with a link into the portal —
106
+ * not a hidden skeleton. This HTML is what a crawler indexes, what a screen reader reads
107
+ * before the island mounts, and what a patient with JavaScript blocked gets PERMANENTLY.
108
+ * It shows no patient data because there is none at build time: the page is ONE static
109
+ * artifact served to every visitor.
110
+ */
111
+ declare function PortalAccount({ surface, portalUrl, bookHref, appointmentsHref, funnelHref, className, }: PortalAccountMarkerProps): React.ReactElement;
112
+
113
+ export { PortalAccount as P, type PortalAccountMarkerProps as a, type PortalAccountProps as b, type PortalAccountSurface as c, PortalPanel as d, type PortalPanelMarkerProps as e, type PortalPanelProps as f, type PortalSurface as g };
@@ -0,0 +1,169 @@
1
+ import * as React from 'react';
2
+ import { b as PortalAccountProps } from './portal-account-EhOtLV5u.js';
3
+
4
+ /** The injectable fetch. Present so every behaviour here is provable without a DOM. */
5
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
6
+ /**
7
+ * The outcome of one portal call. A VALUE, never a throw.
8
+ *
9
+ * `status` is the HTTP status when the server answered and refused, and `null` when the
10
+ * transport failed or the body was not JSON — the caller needs that distinction because
11
+ * a 404 means "the clinic turned this off" (a quiet note) while a transport failure means
12
+ * "we cannot answer here" (link out).
13
+ */
14
+ type PortalResult<T> = {
15
+ ok: true;
16
+ data: T;
17
+ }
18
+ /**
19
+ * `body` is the parsed error payload when there was one — typed `unknown`, NOT `T`:
20
+ * a refusal is not the success shape, and a caller must narrow it before trusting a
21
+ * field (see `hasPortalIdentity`). Keeping it off `T` also keeps a failed result
22
+ * assignable across surface types, which the callers that pass one through rely on.
23
+ */
24
+ | {
25
+ ok: false;
26
+ status: number | null;
27
+ error: string | null;
28
+ body?: unknown;
29
+ };
30
+
31
+ type PortalAddress = {
32
+ id: string;
33
+ line1?: string | null;
34
+ line2?: string | null;
35
+ suburb?: string | null;
36
+ state?: string | null;
37
+ postcode?: string | null;
38
+ /** Exactly one row is the primary. The SERVER owns that invariant, not this island. */
39
+ isPrimary?: boolean;
40
+ label?: string | null;
41
+ };
42
+ type PortalAddressesBody = {
43
+ items: PortalAddress[];
44
+ };
45
+
46
+ type PortalAppointment = {
47
+ id: string;
48
+ title?: string | null;
49
+ status?: string | null;
50
+ start?: string | null;
51
+ end?: string | null;
52
+ timezone?: string | null;
53
+ locationName?: string | null;
54
+ practitionerName?: string | null;
55
+ telehealth?: boolean;
56
+ canCancel?: boolean;
57
+ canReschedule?: boolean;
58
+ /**
59
+ * The server's own answer to "can this patient join the video room right now?" — the
60
+ * join window is open AND the clinic has telehealth configured. When true the button
61
+ * shows regardless of `joinOpensAt` (PAT-792).
62
+ */
63
+ canJoin?: boolean;
64
+ /**
65
+ * When the join window OPENS, ISO. Present on a telehealth row whose window is still
66
+ * ahead, so the page can flip to the button on the LOCAL clock rather than making the
67
+ * patient reload at the exact moment they least want to. The mint re-checks anyway.
68
+ */
69
+ joinOpensAt?: string | null;
70
+ };
71
+ type PortalAppointmentsBody = {
72
+ upcoming: PortalAppointment[];
73
+ past: PortalAppointment[];
74
+ };
75
+ type PortalDocument = {
76
+ id: string;
77
+ title?: string | null;
78
+ status?: string | null;
79
+ date?: string | null;
80
+ };
81
+ type PortalDocumentsBody = {
82
+ items: PortalDocument[];
83
+ };
84
+ type PortalOrder = {
85
+ id: string;
86
+ title?: string | null;
87
+ status?: string | null;
88
+ paymentStatus?: string | null;
89
+ total?: string | null;
90
+ currency?: string | null;
91
+ placedAt?: string | null;
92
+ };
93
+ type PortalOrdersBody = {
94
+ items: PortalOrder[];
95
+ };
96
+ type PortalProfile = {
97
+ givenName?: string | null;
98
+ familyName?: string | null;
99
+ email?: string | null;
100
+ phone?: string | null;
101
+ dateOfBirth?: string | null;
102
+ };
103
+ /** The body of whichever surface this island is showing. */
104
+ type PortalSurfaceBody = PortalAppointmentsBody | PortalDocumentsBody | PortalOrdersBody | PortalProfile | PortalAddressesBody;
105
+ /**
106
+ * Everything the island can be showing. A discriminated union derived once by
107
+ * `loadPortalAccount`, so the render is a pure function of state — and so the whole
108
+ * boot (including every failure mode) is testable without a DOM.
109
+ */
110
+ type PortalAccountState =
111
+ /**
112
+ * Booting. `hinted` means a previous page in this TAB was signed in (PAT-797) — the
113
+ * account tabs are four separate documents, so without it every tab click flashes
114
+ * "Checking your sign-in…" for an answer that has not changed. Hinted, we show the
115
+ * shape of the surface instead and let the response fill it in. ADVISORY ONLY: it
116
+ * never gates what the patient is shown, it only picks the placeholder.
117
+ */
118
+ {
119
+ kind: 'loading';
120
+ hinted?: boolean;
121
+ }
122
+ /** No patient session on this host — show the inline magic-link ramp. */
123
+ | {
124
+ kind: 'signed-out';
125
+ }
126
+ /** A link was requested; we never say whether the address matched an account. */
127
+ | {
128
+ kind: 'link-sent';
129
+ email: string;
130
+ } | {
131
+ kind: 'signed-in';
132
+ givenName: string | null;
133
+ data: PortalSurfaceBody;
134
+ }
135
+ /** Signed in, but the clinic has this module off. A quiet note, NOT an error. */
136
+ | {
137
+ kind: 'not-available';
138
+ givenName: string | null;
139
+ }
140
+ /** We cannot answer here. Degrades to a plain link out. */
141
+ | {
142
+ kind: 'link-out';
143
+ };
144
+ /** How a fetched URL is opened. Injected so the open is assertable without a DOM. */
145
+ type OpenUrl = (url: string) => void;
146
+ type PortalAccountClientProps = PortalAccountProps & {
147
+ /** Test seam: the initial state, so the view is renderable without effects. */
148
+ initialState?: PortalAccountState;
149
+ /** Test seam: the fetch every call goes through. Defaults to the global one. */
150
+ fetchImpl?: FetchLike;
151
+ /** Test seam: how a minted document/receipt URL is opened. */
152
+ openUrl?: OpenUrl;
153
+ };
154
+ /**
155
+ * The island entry point, which forks on ONE surface.
156
+ *
157
+ * `book` is a LAUNCHER now (PAT-899), and a launcher is not an account view: it shows the
158
+ * clinic's public service list, identically to everyone. Routing it around the session
159
+ * machinery is not an optimisation — it is the behaviour. Sending it through the shared
160
+ * path would make it fetch a surface it does not read, and would put a sign-in ramp in
161
+ * front of a patient who is ready to book, when the funnel it links to is itself an
162
+ * anonymous flow that upgrades on session.
163
+ *
164
+ * A wrapper rather than an early return inside the view, so the branch cannot be mistaken
165
+ * for a conditional hook.
166
+ */
167
+ declare function PortalAccountClient(props: PortalAccountClientProps): React.ReactElement;
168
+
169
+ export { type FetchLike as F, PortalAccountClient as P, type PortalAppointment as a, type PortalAppointmentsBody as b, type PortalResult as c };
@@ -0,0 +1,232 @@
1
+ import * as React from 'react';
2
+ import { a as PortalAppointment, F as FetchLike, b as PortalAppointmentsBody, c as PortalResult } from './portal-account.client-CTget1-3.js';
3
+ import './portal-account-EhOtLV5u.js';
4
+
5
+ /** One bookable instant, collapsing the practitioners free at the same time. */
6
+ interface TimeSlot {
7
+ start: string;
8
+ practitionerId: string;
9
+ /** How many practitioners are free — drives the "1 left" nudge. */
10
+ count: number;
11
+ }
12
+
13
+ type PortalBookingType = {
14
+ id: string;
15
+ key?: string | null;
16
+ label?: string | null;
17
+ durationMinutes?: number | null;
18
+ description?: string | null;
19
+ modality?: string | null;
20
+ };
21
+ type PortalBookingTypesBody = {
22
+ types: PortalBookingType[];
23
+ };
24
+ type PortalAvailabilitySlot = {
25
+ start: string;
26
+ end?: string | null;
27
+ practitionerId?: string | null;
28
+ practitionerName?: string | null;
29
+ locationName?: string | null;
30
+ timezone?: string | null;
31
+ };
32
+ type PortalAvailabilityBody = {
33
+ slots: PortalAvailabilitySlot[];
34
+ timezone: string;
35
+ };
36
+ /** The advisory reservation. `expiresAt` is shown to the patient, never enforced here. */
37
+ type PortalHold = {
38
+ holdId: string;
39
+ expiresAt?: string | null;
40
+ };
41
+ /**
42
+ * What the server hands back to drive a reschedule.
43
+ *
44
+ * Read LOOSELY on purpose: it may carry the slots itself (one round trip) or only the
45
+ * appointment type to fetch availability for (two). Both are supported, so the server
46
+ * half can settle on either without breaking a published site — which is a real risk
47
+ * here, because a clinic's site is a static artifact built at a moment in time.
48
+ */
49
+ type PortalRescheduleContext = {
50
+ appointment?: (PortalAppointment & {
51
+ appointmentType?: {
52
+ id?: string | null;
53
+ label?: string | null;
54
+ } | null;
55
+ }) | null;
56
+ appointmentTypeId?: string | null;
57
+ typeLabel?: string | null;
58
+ slots?: PortalAvailabilitySlot[];
59
+ timezone?: string | null;
60
+ };
61
+ /**
62
+ * The appointment type to fetch availability for. The server (PAT-789 canonical
63
+ * contract) nests it as `appointment.appointmentType.id`; the flat `appointmentTypeId`
64
+ * stays supported so an older artifact keeps working if the shape ever flattens.
65
+ */
66
+ declare function rescheduleContextTypeId(ctx: PortalRescheduleContext): string | null;
67
+ declare const PORTAL_BOOKING_TYPES_URL = "/portal/api/booking/types";
68
+ /** The reschedule context for one appointment. */
69
+ declare function portalRescheduleContextUrl(id: string): string;
70
+ declare const SLOT_TAKEN_COPY = "That time just filled up. Please choose another.";
71
+ declare const BOOK_FAILED_COPY = "We couldn't book that time just now. Please try again.";
72
+ declare const AVAILABILITY_FAILED_COPY = "We could not load available times.";
73
+ declare const RESCHEDULE_CUTOFF_COPY = "It's too close to your appointment to change it online. Please call the clinic and we'll sort it out.";
74
+ declare const RESCHEDULE_FAILED_COPY = "We couldn't move that appointment just now. Please try again.";
75
+ /** Normalise a `{types:[…]}` body — a missing or wrong-typed list is an EMPTY list. */
76
+ declare function readPortalBookingTypes(body: unknown): PortalBookingTypesBody;
77
+ /**
78
+ * Normalise an availability body.
79
+ *
80
+ * The timezone is the CLINIC's and travels with the answer: a patient booking a Perth
81
+ * clinic from Sydney must see Perth wall-clock times or they turn up two hours out. It is
82
+ * read from the body, then from the first slot, then falls back — never from the browser.
83
+ */
84
+ declare function readPortalAvailability(body: unknown): PortalAvailabilityBody;
85
+ /**
86
+ * Collapse raw slots into the board's `TimeSlot`s.
87
+ *
88
+ * Several practitioners free at the same minute is ONE choice for a patient with a count
89
+ * behind it (which drives the "1 left" nudge) — the same collapsing the anonymous funnel
90
+ * does, so the two grids read identically.
91
+ */
92
+ declare function portalTimeSlots(slots: readonly PortalAvailabilitySlot[]): TimeSlot[];
93
+ /** The first raw slot at an instant — where the practitioner/location labels come from. */
94
+ declare function portalSlotAt(slots: readonly PortalAvailabilitySlot[], start?: string | null): PortalAvailabilitySlot | null;
95
+ /**
96
+ * The availability window: from today, out a fortnight.
97
+ *
98
+ * `YYYY-MM-DD`, NOT an ISO instant. The edge validates `from`/`to` through the canonical
99
+ * `availabilityQuery` schema, whose dates are strictly date-only — an instant is a 400
100
+ * `invalid_query`, so the surface could never load a single time (PAT-796). The anonymous
101
+ * widget has always sent date-only here; this is the same window, spelled the same way.
102
+ *
103
+ * Losing the time component costs nothing: the availability core drops slots starting
104
+ * before `now + leadTimeMinutes`, so a same-day window never offers a time that has
105
+ * already passed. The day keys are UTC, matching the public path exactly.
106
+ *
107
+ * Pure + exported so the range is assertable without freezing the clock in a component
108
+ * test. Fourteen days matches the public booking window the anonymous funnel uses.
109
+ */
110
+ declare function portalAvailabilityWindow(now?: number, days?: number): {
111
+ from: string;
112
+ to: string;
113
+ };
114
+ declare function portalAvailabilityUrl(typeId: string, window?: {
115
+ from: string;
116
+ to: string;
117
+ }): string;
118
+ declare function loadPortalBookingTypes(fetchImpl?: FetchLike): Promise<PortalResult<PortalBookingTypesBody>>;
119
+ declare function loadPortalAvailability(typeId: string, fetchImpl?: FetchLike, window?: {
120
+ from: string;
121
+ to: string;
122
+ }): Promise<PortalResult<PortalAvailabilityBody>>;
123
+ /**
124
+ * Take an advisory hold on a slot.
125
+ *
126
+ * Returns `null` rather than an error on failure: a hold that could not be taken is a
127
+ * missing courtesy, not a reason to stop a patient booking. The caller carries on.
128
+ */
129
+ declare function createPortalHold(input: {
130
+ typeId: string;
131
+ start: string;
132
+ practitionerId?: string | null;
133
+ }, fetchImpl?: FetchLike): Promise<PortalHold | null>;
134
+ /** Give a hold back when the patient steps away from the confirm screen. Best-effort. */
135
+ declare function releasePortalHold(holdId: string, fetchImpl?: FetchLike): Promise<{
136
+ ok: boolean;
137
+ }>;
138
+ type PortalBookInput = {
139
+ typeId: string;
140
+ start: string;
141
+ practitionerId?: string | null;
142
+ holdId?: string | null;
143
+ };
144
+ /** The outcome a patient is shown. `slotTaken` is the one failure with a next step. */
145
+ type PortalBookOutcome = {
146
+ ok: true;
147
+ appointmentId: string | null;
148
+ } | {
149
+ ok: false;
150
+ slotTaken: boolean;
151
+ error: string;
152
+ };
153
+ /**
154
+ * Book. The AUTHORITATIVE call — the hold above only reduced the odds of losing the race.
155
+ *
156
+ * A 409 means someone else got there first, which is not the patient's mistake: it is
157
+ * reported as "choose another" and the caller refetches, never as an error they must
158
+ * interpret.
159
+ */
160
+ declare function bookPortalAppointment(input: PortalBookInput, fetchImpl?: FetchLike): Promise<PortalBookOutcome>;
161
+ /** What a reschedule context read can turn into. `cutoff` is a real, expected answer. */
162
+ type PortalRescheduleContextOutcome = {
163
+ ok: true;
164
+ context: PortalRescheduleContext;
165
+ } | {
166
+ ok: false;
167
+ cutoff: boolean;
168
+ error: string;
169
+ };
170
+ /**
171
+ * Read the reschedule context.
172
+ *
173
+ * A 409 is the CUTOFF: the appointment is too close to move online. That is clinic policy
174
+ * working, not a failure — it gets its own copy telling the patient to call, because
175
+ * "something went wrong, try again" would have them retrying forever.
176
+ */
177
+ declare function loadPortalRescheduleContext(id: string, fetchImpl?: FetchLike): Promise<PortalRescheduleContextOutcome>;
178
+ type PortalRescheduleOutcome = {
179
+ ok: true;
180
+ data: PortalAppointmentsBody;
181
+ } | {
182
+ ok: false;
183
+ cutoff: boolean;
184
+ slotTaken: boolean;
185
+ error: string;
186
+ };
187
+ /**
188
+ * Move the appointment, then RE-READ the list.
189
+ *
190
+ * Server-confirmed, never optimistic — the same rule as cancelling. A move the server
191
+ * refused that the UI had already redrawn would send a patient to the clinic on the wrong
192
+ * day, which is the worst outcome this island can produce.
193
+ */
194
+ declare function reschedulePortalAppointmentAndReload(id: string, input: {
195
+ start: string;
196
+ practitionerId?: string | null;
197
+ }, fetchImpl?: FetchLike): Promise<PortalRescheduleOutcome>;
198
+ /** One line describing a chosen time: the day + time, then who and where. */
199
+ declare function PortalSlotSummary({ start, slot, tz, typeLabel, }: {
200
+ start: string;
201
+ slot: PortalAvailabilitySlot | null;
202
+ tz: string;
203
+ typeLabel?: string | null;
204
+ }): React.ReactElement;
205
+ interface PortalRescheduleInlineProps {
206
+ appointment: PortalAppointment;
207
+ fetchImpl?: FetchLike;
208
+ /** The re-read list, after a successful move. */
209
+ onRescheduled: (data: PortalAppointmentsBody) => void;
210
+ onClose: () => void;
211
+ /** Test seam: the interaction state a static render should start from. */
212
+ initialUi?: {
213
+ context?: PortalRescheduleContext | null;
214
+ slots?: PortalAvailabilitySlot[] | null;
215
+ tz?: string;
216
+ selected?: TimeSlot | null;
217
+ confirming?: boolean;
218
+ error?: string | null;
219
+ /** A blocking answer with no picker behind it — the cutoff. */
220
+ blocked?: string | null;
221
+ };
222
+ }
223
+ /**
224
+ * Rescheduling, expanded IN PLACE under the appointment it moves.
225
+ *
226
+ * In place rather than a route or a dialog: the patient is standing on the clinic's own
227
+ * page, a modal over a layout we do not control is a shape we cannot make safe, and the
228
+ * row above is the context that makes "move it to when?" a sensible question.
229
+ */
230
+ declare function PortalRescheduleInline({ appointment, fetchImpl, onRescheduled, onClose, initialUi, }: PortalRescheduleInlineProps): React.ReactElement;
231
+
232
+ export { AVAILABILITY_FAILED_COPY, BOOK_FAILED_COPY, PORTAL_BOOKING_TYPES_URL, type PortalAvailabilityBody, type PortalAvailabilitySlot, type PortalBookInput, type PortalBookOutcome, type PortalBookingType, type PortalBookingTypesBody, type PortalHold, type PortalRescheduleContext, type PortalRescheduleContextOutcome, PortalRescheduleInline, type PortalRescheduleInlineProps, type PortalRescheduleOutcome, PortalSlotSummary, RESCHEDULE_CUTOFF_COPY, RESCHEDULE_FAILED_COPY, SLOT_TAKEN_COPY, bookPortalAppointment, createPortalHold, loadPortalAvailability, loadPortalBookingTypes, loadPortalRescheduleContext, portalAvailabilityUrl, portalAvailabilityWindow, portalRescheduleContextUrl, portalSlotAt, portalTimeSlots, readPortalAvailability, readPortalBookingTypes, releasePortalHold, rescheduleContextTypeId, reschedulePortalAppointmentAndReload };
@@ -0,0 +1,51 @@
1
+ import {
2
+ AVAILABILITY_FAILED_COPY,
3
+ BOOK_FAILED_COPY,
4
+ PORTAL_BOOKING_TYPES_URL,
5
+ PortalRescheduleInline,
6
+ PortalSlotSummary,
7
+ RESCHEDULE_CUTOFF_COPY,
8
+ RESCHEDULE_FAILED_COPY,
9
+ SLOT_TAKEN_COPY,
10
+ bookPortalAppointment,
11
+ createPortalHold,
12
+ loadPortalAvailability,
13
+ loadPortalBookingTypes,
14
+ loadPortalRescheduleContext,
15
+ portalAvailabilityUrl,
16
+ portalAvailabilityWindow,
17
+ portalRescheduleContextUrl,
18
+ portalSlotAt,
19
+ portalTimeSlots,
20
+ readPortalAvailability,
21
+ readPortalBookingTypes,
22
+ releasePortalHold,
23
+ rescheduleContextTypeId,
24
+ reschedulePortalAppointmentAndReload
25
+ } from "./chunk-G3U6EXJM.js";
26
+ import "./chunk-MLKGABMK.js";
27
+ export {
28
+ AVAILABILITY_FAILED_COPY,
29
+ BOOK_FAILED_COPY,
30
+ PORTAL_BOOKING_TYPES_URL,
31
+ PortalRescheduleInline,
32
+ PortalSlotSummary,
33
+ RESCHEDULE_CUTOFF_COPY,
34
+ RESCHEDULE_FAILED_COPY,
35
+ SLOT_TAKEN_COPY,
36
+ bookPortalAppointment,
37
+ createPortalHold,
38
+ loadPortalAvailability,
39
+ loadPortalBookingTypes,
40
+ loadPortalRescheduleContext,
41
+ portalAvailabilityUrl,
42
+ portalAvailabilityWindow,
43
+ portalRescheduleContextUrl,
44
+ portalSlotAt,
45
+ portalTimeSlots,
46
+ readPortalAvailability,
47
+ readPortalBookingTypes,
48
+ releasePortalHold,
49
+ rescheduleContextTypeId,
50
+ reschedulePortalAppointmentAndReload
51
+ };
@@ -0,0 +1,11 @@
1
+ /** Kit-authored chunk served beside a published islands site's `main.js`. */
2
+ declare const CONSULT_CHUNK_PATH = "assets/portal-consult.js";
3
+ /** Third-party script origins permitted on published islands sites. */
4
+ declare const TURNSTILE_SCRIPT_ORIGIN = "https://challenges.cloudflare.com";
5
+ declare const BPOINT_SCRIPT_ORIGIN = "https://www.bpoint.com.au";
6
+ declare const THIRD_PARTY_SCRIPT_ORIGINS: readonly ["https://challenges.cloudflare.com", "https://www.bpoint.com.au"];
7
+ /** Provider entry scripts loaded lazily by kit-owned islands. */
8
+ declare const TURNSTILE_SCRIPT_URL = "https://challenges.cloudflare.com/turnstile/v0/api.js";
9
+ declare const BPOINT_SCRIPT_URL = "https://www.bpoint.com.au/rest/clientscripts/api.js";
10
+
11
+ export { BPOINT_SCRIPT_ORIGIN, BPOINT_SCRIPT_URL, CONSULT_CHUNK_PATH, THIRD_PARTY_SCRIPT_ORIGINS, TURNSTILE_SCRIPT_ORIGIN, TURNSTILE_SCRIPT_URL };
@@ -0,0 +1,17 @@
1
+ import {
2
+ BPOINT_SCRIPT_ORIGIN,
3
+ BPOINT_SCRIPT_URL,
4
+ CONSULT_CHUNK_PATH,
5
+ THIRD_PARTY_SCRIPT_ORIGINS,
6
+ TURNSTILE_SCRIPT_ORIGIN,
7
+ TURNSTILE_SCRIPT_URL
8
+ } from "./chunk-ZOF22TJA.js";
9
+ import "./chunk-MLKGABMK.js";
10
+ export {
11
+ BPOINT_SCRIPT_ORIGIN,
12
+ BPOINT_SCRIPT_URL,
13
+ CONSULT_CHUNK_PATH,
14
+ THIRD_PARTY_SCRIPT_ORIGINS,
15
+ TURNSTILE_SCRIPT_ORIGIN,
16
+ TURNSTILE_SCRIPT_URL
17
+ };