@arkipay/booking-widget 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,521 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as _ea_api_contracts from '@ea/api-contracts';
3
+ import { PublicTenantConfigResponse, PublicServiceDto, PublicProviderDto, TimeSlotDto, CreatePublicAppointmentBody, AppointmentDto, BookingTimeDisplayDto } from '@ea/api-contracts';
4
+ import * as react from 'react';
5
+ import { CSSProperties, ReactNode } from 'react';
6
+ import { Temporal } from '@js-temporal/polyfill';
7
+
8
+ interface BookingClientOptions {
9
+ readonly publishableKey: string;
10
+ readonly apiUrl?: string | undefined;
11
+ readonly origin?: string | undefined;
12
+ }
13
+ declare class BookingApiError extends Error {
14
+ readonly status: number;
15
+ readonly code: string;
16
+ constructor(status: number, code: string, message: string);
17
+ }
18
+ declare class BookingClient {
19
+ private readonly baseUrl;
20
+ private readonly publishableKey;
21
+ private readonly origin;
22
+ constructor(opts: BookingClientOptions);
23
+ getTenantConfig(): Promise<PublicTenantConfigResponse>;
24
+ listServices(): Promise<PublicServiceDto[]>;
25
+ listProviders(params?: {
26
+ serviceId?: string | undefined;
27
+ }): Promise<PublicProviderDto[]>;
28
+ getAvailability(params: {
29
+ providerId: string;
30
+ serviceId: string;
31
+ date: string;
32
+ }): Promise<TimeSlotDto[]>;
33
+ getUnavailableDates(params: {
34
+ providerId: string;
35
+ serviceId: string;
36
+ month: string;
37
+ }): Promise<string[]>;
38
+ createAppointment(body: CreatePublicAppointmentBody): Promise<AppointmentDto>;
39
+ private headers;
40
+ private getJson;
41
+ private postJson;
42
+ private parseJson;
43
+ }
44
+
45
+ /**
46
+ * Factory for the public booking API client.
47
+ * Uses the local fetch implementation until `@ea/booking-sdk` exports `BookingClient`.
48
+ */
49
+ declare function createBookingClient(opts: BookingClientOptions): BookingClient;
50
+
51
+ declare const enCatalog: {
52
+ readonly steps: {
53
+ readonly service: "Choose a service";
54
+ readonly provider: "Choose a provider";
55
+ readonly date: "Choose a date";
56
+ readonly time: "Choose a time";
57
+ readonly customer: "Your details";
58
+ readonly legal: "Legal information";
59
+ readonly confirm: "Confirm your booking";
60
+ readonly booked: "Booking confirmed";
61
+ };
62
+ readonly actions: {
63
+ readonly back: "Back";
64
+ readonly continue: "Continue";
65
+ readonly confirm: "Confirm booking";
66
+ readonly submitting: "Booking…";
67
+ };
68
+ readonly service: {
69
+ readonly empty: "No services are available right now.";
70
+ readonly durationMinutes: "{minutes} min";
71
+ readonly price: "{amount}";
72
+ };
73
+ readonly provider: {
74
+ readonly empty: "No providers are available for this service.";
75
+ readonly any: "Any available provider";
76
+ };
77
+ readonly date: {
78
+ readonly unavailable: "Unavailable";
79
+ readonly minHint: "Earliest booking is tomorrow.";
80
+ };
81
+ readonly time: {
82
+ readonly empty: "No time slots are available on this date.";
83
+ readonly timezone: "All times in {timezone}";
84
+ readonly timezoneHint: "Times shown in {timezone}";
85
+ readonly mismatchBanner: "This business uses {businessTimezone}. You are in {customerTimezone}.";
86
+ readonly showMyTime: "Show in my time";
87
+ readonly showBusinessTime: "Show business time";
88
+ readonly yourTimeLabel: "Your time ({timezone})";
89
+ readonly businessTimeLabel: "Business time ({timezone})";
90
+ };
91
+ readonly customer: {
92
+ readonly required: "Required";
93
+ readonly invalidEmail: "Enter a valid email address.";
94
+ };
95
+ readonly legal: {
96
+ readonly cookie: "Cookie notice";
97
+ readonly terms: "Terms & conditions";
98
+ readonly privacy: "Privacy policy";
99
+ readonly acceptCookie: "I accept the cookie notice";
100
+ readonly acceptTerms: "I accept the terms & conditions";
101
+ readonly acceptPrivacy: "I accept the privacy policy";
102
+ readonly readMore: "Read";
103
+ };
104
+ readonly confirm: {
105
+ readonly summary: "Review your appointment";
106
+ readonly service: "Service";
107
+ readonly provider: "Provider";
108
+ readonly when: "When";
109
+ readonly customer: "Customer";
110
+ readonly error: "We could not complete your booking. Please try again.";
111
+ readonly conflict: "That time slot is no longer available. Please choose another time.";
112
+ };
113
+ readonly booked: {
114
+ readonly reference: "Reference";
115
+ readonly message: "Your appointment has been booked.";
116
+ };
117
+ readonly disabled: {
118
+ readonly title: "Booking unavailable";
119
+ };
120
+ readonly fields: {
121
+ readonly first_name: "First name";
122
+ readonly last_name: "Last name";
123
+ readonly email: "Email";
124
+ readonly phone: "Phone";
125
+ readonly address: "Address";
126
+ readonly city: "City";
127
+ readonly state: "State / province";
128
+ readonly zip: "Postal code";
129
+ readonly notes: "Notes";
130
+ readonly custom_1: "Additional information";
131
+ readonly custom_2: "Additional information (2)";
132
+ readonly custom_3: "Additional information (3)";
133
+ readonly custom_4: "Additional information (4)";
134
+ readonly custom_5: "Additional information (5)";
135
+ };
136
+ };
137
+
138
+ type Catalog = typeof enCatalog;
139
+ type NestedKeyOf<T, Prefix extends string = ""> = T extends string ? Prefix extends "" ? never : Prefix : {
140
+ [K in keyof T & string]: NestedKeyOf<T[K], Prefix extends "" ? K : `${Prefix}.${K}`>;
141
+ }[keyof T & string];
142
+ type TranslationKey = NestedKeyOf<Catalog>;
143
+ type Params = Record<string, string | number>;
144
+ declare function createTranslator(locale: string | undefined, tenantDefaultLanguage: string | undefined): (key: TranslationKey, params?: Params) => string;
145
+
146
+ declare const ANY_PROVIDER_ID: "any";
147
+ type AnyProviderId = typeof ANY_PROVIDER_ID;
148
+
149
+ type CustomerFieldValues = {
150
+ readonly firstName: string;
151
+ readonly lastName: string;
152
+ readonly email: string;
153
+ readonly phone: string;
154
+ readonly address: string;
155
+ readonly city: string;
156
+ readonly state: string;
157
+ readonly zip: string;
158
+ readonly notes: string;
159
+ readonly customField1: string;
160
+ readonly customField2: string;
161
+ readonly customField3: string;
162
+ readonly customField4: string;
163
+ readonly customField5: string;
164
+ readonly timezone: string;
165
+ };
166
+
167
+ type BookingTimeDisplay = BookingTimeDisplayDto;
168
+ type TimeDisplayMode = `business` | `customer`;
169
+ declare function getCustomerTimezone(): string;
170
+ declare function resolveBusinessTimezone(params: {
171
+ readonly providerId: ProviderSelection;
172
+ readonly providers: readonly PublicProviderDto[];
173
+ readonly tenantDefaultTimezone: string;
174
+ readonly anyProviderId?: AnyProviderId | undefined;
175
+ }): string;
176
+ declare function allowsTimeDisplayToggle(bookingTimeDisplay: BookingTimeDisplay | undefined): boolean;
177
+ declare function resolveDisplayTimezone(params: {
178
+ readonly businessTimezone: string;
179
+ readonly customerTimezone: string;
180
+ readonly bookingTimeDisplay?: BookingTimeDisplay | undefined;
181
+ readonly timeDisplayMode?: TimeDisplayMode | undefined;
182
+ }): string;
183
+ declare function shouldShowSecondaryTimezone(params: {
184
+ readonly zonesDiffer: boolean;
185
+ readonly bookingTimeDisplay?: BookingTimeDisplay | undefined;
186
+ }): boolean;
187
+
188
+ type ProviderSelection = string | typeof ANY_PROVIDER_ID;
189
+ type BookingConsents = {
190
+ readonly cookie?: boolean | undefined;
191
+ readonly terms?: boolean | undefined;
192
+ readonly privacy?: boolean | undefined;
193
+ };
194
+ type BookingStepState = {
195
+ readonly step: `service`;
196
+ } | {
197
+ readonly step: `provider`;
198
+ readonly serviceId: string;
199
+ } | {
200
+ readonly step: `date`;
201
+ readonly serviceId: string;
202
+ readonly providerId: ProviderSelection;
203
+ } | {
204
+ readonly step: `time`;
205
+ readonly serviceId: string;
206
+ readonly providerId: ProviderSelection;
207
+ readonly date: string;
208
+ } | {
209
+ readonly step: `customer`;
210
+ readonly serviceId: string;
211
+ readonly providerId: ProviderSelection;
212
+ readonly date: string;
213
+ readonly slot: TimeSlotDto;
214
+ } | {
215
+ readonly step: `legal`;
216
+ readonly serviceId: string;
217
+ readonly providerId: ProviderSelection;
218
+ readonly date: string;
219
+ readonly slot: TimeSlotDto;
220
+ readonly customer: CustomerFieldValues;
221
+ } | {
222
+ readonly step: `confirm`;
223
+ readonly serviceId: string;
224
+ readonly providerId: ProviderSelection;
225
+ readonly date: string;
226
+ readonly slot: TimeSlotDto;
227
+ readonly customer: CustomerFieldValues;
228
+ readonly consents: BookingConsents;
229
+ } | {
230
+ readonly step: `booked`;
231
+ readonly appointment: AppointmentDto;
232
+ };
233
+ type BookingState = {
234
+ readonly timeDisplayMode: TimeDisplayMode;
235
+ } & BookingStepState;
236
+ type BookingAction = {
237
+ readonly type: `select_service`;
238
+ readonly serviceId: string;
239
+ } | {
240
+ readonly type: `select_provider`;
241
+ readonly providerId: ProviderSelection;
242
+ } | {
243
+ readonly type: `select_date`;
244
+ readonly date: string;
245
+ } | {
246
+ readonly type: `select_slot`;
247
+ readonly slot: TimeSlotDto;
248
+ } | {
249
+ readonly type: `set_customer`;
250
+ readonly customer: CustomerFieldValues;
251
+ } | {
252
+ readonly type: `set_consents`;
253
+ readonly consents: BookingConsents;
254
+ } | {
255
+ readonly type: `set_time_display_mode`;
256
+ readonly mode: TimeDisplayMode;
257
+ } | {
258
+ readonly type: `booked`;
259
+ readonly appointment: AppointmentDto;
260
+ } | {
261
+ readonly type: `back`;
262
+ } | {
263
+ readonly type: `prefill`;
264
+ readonly serviceId?: string | undefined;
265
+ readonly providerId?: string | undefined;
266
+ };
267
+ declare function createInitialBookingState(prefill?: {
268
+ readonly serviceId?: string | undefined;
269
+ readonly providerId?: string | undefined;
270
+ }): BookingState;
271
+ declare function bookingReducer(state: BookingState, action: BookingAction, tenantConfig?: PublicTenantConfigResponse | null): BookingState;
272
+ declare function useBooking(): {
273
+ state: BookingState;
274
+ dispatch: react.Dispatch<BookingAction>;
275
+ tenantConfig: {
276
+ business: {
277
+ displayName: string;
278
+ contactEmail: string | null;
279
+ link: string | null;
280
+ logoUrl: string | null;
281
+ brandColor: string;
282
+ theme: string;
283
+ };
284
+ localization: {
285
+ currency: string;
286
+ defaultLanguage: string;
287
+ defaultTimezone: string;
288
+ dateFormat: "DMY" | "MDY" | "YMD";
289
+ timeFormat: "h12" | "h24";
290
+ firstWeekday: "sunday" | "monday" | "saturday";
291
+ };
292
+ booking: {
293
+ disableBooking: boolean;
294
+ disableBookingMessage: string | null;
295
+ displayAnyProvider: boolean;
296
+ futureBookingLimitDays: number;
297
+ bookAdvanceTimeoutMinutes: number;
298
+ requireCaptcha: boolean;
299
+ bookingTimeDisplay: "customer" | "business" | "customer_only";
300
+ };
301
+ formFields: {
302
+ required: boolean;
303
+ sortOrder: number;
304
+ fieldKey: string;
305
+ isCustom: boolean;
306
+ display: boolean;
307
+ label: string | null;
308
+ }[];
309
+ legal: {
310
+ displayCookieNotice: boolean;
311
+ cookieNoticeContent: string | null;
312
+ displayTerms: boolean;
313
+ termsContent: string | null;
314
+ displayPrivacy: boolean;
315
+ privacyContent: string | null;
316
+ };
317
+ statuses: {
318
+ color: string | null;
319
+ slug: string;
320
+ label: string;
321
+ isDefault: boolean;
322
+ }[];
323
+ } | null;
324
+ onBooked: ((appointment: _ea_api_contracts.AppointmentDto) => void) | undefined;
325
+ timeDisplayMode: TimeDisplayMode;
326
+ };
327
+
328
+ /**
329
+ * Visual overrides for the widget. Every field is optional — anything left unset
330
+ * falls through to the host's CSS design-system token, then tenant config (brand
331
+ * only), then a built-in default. See `styles.css` for the full resolution order.
332
+ * Each field maps directly to a `--ea-*` custom property.
333
+ */
334
+ type BookingTheme = {
335
+ /** Primary / call-to-action colour → `--ea-brand`. */
336
+ readonly brandColor?: string | undefined;
337
+ /** Text colour on primary buttons → `--ea-on-brand`. */
338
+ readonly onBrandColor?: string | undefined;
339
+ /** Body text colour → `--ea-text`. */
340
+ readonly textColor?: string | undefined;
341
+ /** Secondary / hint text colour → `--ea-muted`. */
342
+ readonly mutedColor?: string | undefined;
343
+ /** Card / input border colour → `--ea-border`. */
344
+ readonly borderColor?: string | undefined;
345
+ /** Card / input / surface background → `--ea-surface`. */
346
+ readonly surfaceColor?: string | undefined;
347
+ /** Error / validation colour → `--ea-error`. */
348
+ readonly errorColor?: string | undefined;
349
+ /** Corner radius for cards, inputs, buttons → `--ea-radius`. */
350
+ readonly borderRadius?: string | undefined;
351
+ /** Font stack → `--ea-font`. */
352
+ readonly fontFamily?: string | undefined;
353
+ /** Max width of the widget container → `--ea-max-width`. */
354
+ readonly maxWidth?: string | undefined;
355
+ /** Light/dark scheme. `"system"` follows `prefers-color-scheme`. */
356
+ readonly colorScheme?: "light" | "dark" | "system" | undefined;
357
+ };
358
+
359
+ type BookingProviderProps = {
360
+ readonly publishableKey: string;
361
+ readonly apiUrl?: string | undefined;
362
+ readonly locale?: string | undefined;
363
+ readonly theme?: BookingTheme | undefined;
364
+ readonly onBooked?: ((appointment: AppointmentDto) => void) | undefined;
365
+ readonly initialServiceId?: string | undefined;
366
+ readonly initialProviderId?: string | undefined;
367
+ readonly origin?: string | undefined;
368
+ /** Extra class(es) on the widget root, for host layout/positioning. */
369
+ readonly className?: string | undefined;
370
+ /** Inline styles on the widget root (merged after theme vars, so they win). */
371
+ readonly style?: CSSProperties | undefined;
372
+ /**
373
+ * Vercel BotID client-side protection on the booking POST. Defaults to `true`.
374
+ * Set `false` on hosts not running the Vercel BotID proxy — the API's
375
+ * server-side rate limiter is still the backstop.
376
+ */
377
+ readonly botProtection?: boolean | undefined;
378
+ readonly children: ReactNode;
379
+ };
380
+ declare function BookingProvider({ publishableKey, apiUrl, locale, theme, onBooked, initialServiceId, initialProviderId, origin, className, style, botProtection, children, }: BookingProviderProps): react_jsx_runtime.JSX.Element;
381
+
382
+ declare function BookingWidget(): react_jsx_runtime.JSX.Element;
383
+
384
+ declare function ServicePicker(): react_jsx_runtime.JSX.Element | null;
385
+
386
+ declare function ProviderPicker(): react_jsx_runtime.JSX.Element | null;
387
+
388
+ declare function DatePicker(): react_jsx_runtime.JSX.Element | null;
389
+
390
+ declare function TimeSlotPicker(): react_jsx_runtime.JSX.Element | null;
391
+
392
+ declare function CustomerForm(): react_jsx_runtime.JSX.Element | null;
393
+
394
+ declare function LegalGate(): react_jsx_runtime.JSX.Element | null;
395
+
396
+ declare function ConfirmStep(): react_jsx_runtime.JSX.Element | null;
397
+
398
+ declare function BookedSummary(): react_jsx_runtime.JSX.Element | null;
399
+
400
+ declare function DisabledBooking(): react_jsx_runtime.JSX.Element | null;
401
+
402
+ declare function useTenantConfig(): {
403
+ tenantConfig: {
404
+ business: {
405
+ displayName: string;
406
+ contactEmail: string | null;
407
+ link: string | null;
408
+ logoUrl: string | null;
409
+ brandColor: string;
410
+ theme: string;
411
+ };
412
+ localization: {
413
+ currency: string;
414
+ defaultLanguage: string;
415
+ defaultTimezone: string;
416
+ dateFormat: "DMY" | "MDY" | "YMD";
417
+ timeFormat: "h12" | "h24";
418
+ firstWeekday: "sunday" | "monday" | "saturday";
419
+ };
420
+ booking: {
421
+ disableBooking: boolean;
422
+ disableBookingMessage: string | null;
423
+ displayAnyProvider: boolean;
424
+ futureBookingLimitDays: number;
425
+ bookAdvanceTimeoutMinutes: number;
426
+ requireCaptcha: boolean;
427
+ bookingTimeDisplay: "customer" | "business" | "customer_only";
428
+ };
429
+ formFields: {
430
+ required: boolean;
431
+ sortOrder: number;
432
+ fieldKey: string;
433
+ isCustom: boolean;
434
+ display: boolean;
435
+ label: string | null;
436
+ }[];
437
+ legal: {
438
+ displayCookieNotice: boolean;
439
+ cookieNoticeContent: string | null;
440
+ displayTerms: boolean;
441
+ termsContent: string | null;
442
+ displayPrivacy: boolean;
443
+ privacyContent: string | null;
444
+ };
445
+ statuses: {
446
+ color: string | null;
447
+ slug: string;
448
+ label: string;
449
+ isDefault: boolean;
450
+ }[];
451
+ } | null;
452
+ loading: boolean;
453
+ error: string | null;
454
+ };
455
+
456
+ declare function useServices(): {
457
+ services: {
458
+ description: string | null;
459
+ name: string;
460
+ id: string;
461
+ durationMinutes: number;
462
+ categoryId: string | null;
463
+ priceMinor: number | null;
464
+ providerIds: string[];
465
+ }[];
466
+ loading: boolean;
467
+ error: string | null;
468
+ reload: () => Promise<void>;
469
+ };
470
+
471
+ declare function useProviders(serviceId: string | undefined): {
472
+ providers: {
473
+ id: string;
474
+ timezone: string;
475
+ services: {
476
+ name: string;
477
+ id: string;
478
+ color: string | null;
479
+ durationMinutes: number;
480
+ }[];
481
+ displayName: string;
482
+ }[];
483
+ loading: boolean;
484
+ error: string | null;
485
+ reload: () => Promise<void>;
486
+ };
487
+
488
+ declare function useAvailability(params: {
489
+ providerId: ProviderSelection | undefined;
490
+ serviceId: string | undefined;
491
+ date: string | undefined;
492
+ providerIdsForAny?: readonly string[] | undefined;
493
+ }): {
494
+ slots: {
495
+ startAt: string;
496
+ endAt: string;
497
+ remainingCapacity: number;
498
+ }[];
499
+ loading: boolean;
500
+ error: string | null;
501
+ reload: () => Promise<void>;
502
+ };
503
+
504
+ declare function useUnavailableDates(params: {
505
+ providerId: ProviderSelection | undefined;
506
+ serviceId: string | undefined;
507
+ month: string | undefined;
508
+ providerIdsForAny?: readonly string[] | undefined;
509
+ }): {
510
+ unavailableDates: string[];
511
+ loading: boolean;
512
+ error: string | null;
513
+ reload: () => Promise<void>;
514
+ };
515
+
516
+ declare function minBookableDate(timeZone: string): Temporal.PlainDate;
517
+ declare function maxBookableDate(futureBookingLimitDays: number, timeZone: string): Temporal.PlainDate;
518
+ declare function formatCalendarDate(date: Temporal.PlainDate): string;
519
+ declare function formatSlotDateTimeDual(iso: string, locale: string, primaryTz: string, secondaryTz: string | null): string;
520
+
521
+ export { ANY_PROVIDER_ID, BookedSummary, type BookingAction, BookingApiError, BookingClient, type BookingClientOptions, type BookingConsents, BookingProvider, type BookingProviderProps, type BookingState, type BookingTheme, type BookingTimeDisplay, BookingWidget, ConfirmStep, CustomerForm, DatePicker, DisabledBooking as DisableBookingMessage, DisabledBooking, LegalGate, ProviderPicker, type ProviderSelection, ServicePicker, type TimeDisplayMode, TimeSlotPicker, type TranslationKey, allowsTimeDisplayToggle, bookingReducer, createBookingClient, createInitialBookingState, createTranslator, enCatalog, formatCalendarDate, formatSlotDateTimeDual, getCustomerTimezone, maxBookableDate, minBookableDate, resolveBusinessTimezone, resolveDisplayTimezone, shouldShowSecondaryTimezone, useAvailability, useBooking, useProviders, useServices, useTenantConfig, useUnavailableDates };