@asksable/site-connector 0.3.2 → 0.4.1

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.
@@ -1,23 +1,25 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useMemo, useRef, useState } from 'react';
3
- import { createPortal } from 'react-dom';
2
+ import { Elements, PaymentElement, useElements, useStripe, } from '@stripe/react-stripe-js';
4
3
  import { loadStripe } from '@stripe/stripe-js';
5
- import { Elements, PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js';
6
4
  import { Skeleton } from 'boneyard-js/react';
5
+ import { useEffect, useMemo, useRef, useState } from 'react';
6
+ import { createPortal } from 'react-dom';
7
7
  // Side-effect: registers all named bones with the boneyard runtime.
8
8
  // Regenerate by running `npx boneyard-js build` against a host site
9
9
  // that mounts <BookingWidgetPanel /> in its loaded state.
10
10
  import './bones/registry.js';
11
11
  import { BookingWidgetPlaceholder } from './booking-widget-placeholder.js';
12
- import { useSableSiteClient, useSableSiteConfig, useTranslation } from './provider.js';
13
- import { DEFAULT_LOCALE, localeToIntl, pickLocaleField } from './translations.js';
12
+ import { useSableSiteAnalytics, useSableSiteClient, useSableSiteConfig, useTranslation, } from './provider.js';
13
+ import { DEFAULT_LOCALE, localeToIntl, pickLocaleField, } from './translations.js';
14
14
  const stripePromises = new Map();
15
15
  function getStripePromise(publishableKey, connectAccountId) {
16
16
  const key = `${publishableKey}:${connectAccountId}`;
17
17
  const cached = stripePromises.get(key);
18
18
  if (cached)
19
19
  return cached;
20
- const promise = loadStripe(publishableKey, { stripeAccount: connectAccountId });
20
+ const promise = loadStripe(publishableKey, {
21
+ stripeAccount: connectAccountId,
22
+ });
21
23
  stripePromises.set(key, promise);
22
24
  return promise;
23
25
  }
@@ -52,6 +54,7 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
52
54
  const client = useSableSiteClient();
53
55
  const { siteSlug } = useSableSiteConfig();
54
56
  const { t, locale } = useTranslation();
57
+ const analytics = useSableSiteAnalytics();
55
58
  const intlLocale = localeToIntl(locale);
56
59
  const [setup, setSetup] = useState(null);
57
60
  const [isSetupLoading, setIsSetupLoading] = useState(true);
@@ -104,7 +107,11 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
104
107
  // Dev-only: force viewState to 'details' when previewing a payment
105
108
  // variant, since the payment panel renders inside the details form.
106
109
  useEffect(() => {
107
- if (__devForceState === 'payment-full' || __devForceState === 'payment-deposit') {
110
+ analytics.capture('sable_booking_widget_viewed', { mode });
111
+ }, [analytics, mode]);
112
+ useEffect(() => {
113
+ if (__devForceState === 'payment-full' ||
114
+ __devForceState === 'payment-deposit') {
108
115
  setViewState('details');
109
116
  }
110
117
  }, [__devForceState]);
@@ -177,7 +184,9 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
177
184
  })
178
185
  .catch((nextError) => {
179
186
  if (!cancelled) {
180
- setError(nextError instanceof Error ? nextError.message : 'Unable to load booking setup.');
187
+ setError(nextError instanceof Error
188
+ ? nextError.message
189
+ : 'Unable to load booking setup.');
181
190
  }
182
191
  })
183
192
  .finally(() => {
@@ -189,9 +198,12 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
189
198
  cancelled = true;
190
199
  };
191
200
  }, [client, siteSlug]);
192
- const selectedService = useMemo(() => setup?.services.find((service) => service._id === selectedServiceId) ?? null, [selectedServiceId, setup]);
201
+ const selectedService = useMemo(() => setup?.services.find((service) => service._id === selectedServiceId) ??
202
+ null, [selectedServiceId, setup]);
193
203
  const selectedServiceRequiresSlot = selectedService?.publicBookingMode !== 'async';
194
- const selectedServicePath = selectedServiceRequiresSlot ? 'scheduled' : 'async';
204
+ const selectedServicePath = selectedServiceRequiresSlot
205
+ ? 'scheduled'
206
+ : 'async';
195
207
  const selectedServiceHasIntakeForm = hasIntakeFormSections(selectedService);
196
208
  const isIntakeComplete = useMemo(() => !selectedService?.intakeForm ||
197
209
  areRequiredIntakeFieldsComplete(selectedService.intakeForm, intakeResponses, selectedServicePath), [intakeResponses, selectedService?.intakeForm, selectedServicePath]);
@@ -217,7 +229,7 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
217
229
  const uncategorizedKey = '__uncategorized__';
218
230
  for (const service of visibleServices) {
219
231
  const category = service.categoryId != null
220
- ? categoriesById.get(service.categoryId) ?? null
232
+ ? (categoriesById.get(service.categoryId) ?? null)
221
233
  : null;
222
234
  const key = category?._id ?? uncategorizedKey;
223
235
  const existing = groups.get(key);
@@ -286,7 +298,8 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
286
298
  // wiped on the first render before the staff list arrives.
287
299
  if (availableStaff.length === 0)
288
300
  return;
289
- if (selectedStaffId && !availableStaff.some((staffMember) => staffMember._id === selectedStaffId)) {
301
+ if (selectedStaffId &&
302
+ !availableStaff.some((staffMember) => staffMember._id === selectedStaffId)) {
290
303
  setSelectedStaffId(null);
291
304
  }
292
305
  }, [availableStaff, selectedStaffId]);
@@ -351,7 +364,9 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
351
364
  if (!cancelled) {
352
365
  setAvailabilityByDate(new Map());
353
366
  setSelectedDate(null);
354
- setError(nextError instanceof Error ? nextError.message : t('calendarLoadError'));
367
+ setError(nextError instanceof Error
368
+ ? nextError.message
369
+ : t('calendarLoadError'));
355
370
  }
356
371
  })
357
372
  .finally(() => {
@@ -362,7 +377,15 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
362
377
  return () => {
363
378
  cancelled = true;
364
379
  };
365
- }, [calendarMonth, client, selectedDate, selectedService?.publicBookingMode, selectedServiceId, selectedStaffId, siteSlug]);
380
+ }, [
381
+ calendarMonth,
382
+ client,
383
+ selectedDate,
384
+ selectedService?.publicBookingMode,
385
+ selectedServiceId,
386
+ selectedStaffId,
387
+ siteSlug,
388
+ ]);
366
389
  useEffect(() => {
367
390
  if (!selectedServiceId || selectedService?.publicBookingMode === 'async') {
368
391
  return;
@@ -375,7 +398,14 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
375
398
  calendarMonth: nextMonth,
376
399
  cacheRef: availabilityCacheRef,
377
400
  });
378
- }, [calendarMonth, client, selectedService?.publicBookingMode, selectedServiceId, selectedStaffId, siteSlug]);
401
+ }, [
402
+ calendarMonth,
403
+ client,
404
+ selectedService?.publicBookingMode,
405
+ selectedServiceId,
406
+ selectedStaffId,
407
+ siteSlug,
408
+ ]);
379
409
  useEffect(() => {
380
410
  setSelectedSlot(null);
381
411
  setPendingSlotKey(null);
@@ -394,7 +424,12 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
394
424
  else {
395
425
  setViewState('slots');
396
426
  }
397
- }, [selectedDate, selectedService?.publicBookingMode, selectedServiceId, selectedStaffId]);
427
+ }, [
428
+ selectedDate,
429
+ selectedService?.publicBookingMode,
430
+ selectedServiceId,
431
+ selectedStaffId,
432
+ ]);
398
433
  useEffect(() => {
399
434
  setIntakeResponses({});
400
435
  setQuote(null);
@@ -424,7 +459,9 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
424
459
  })
425
460
  .catch((nextError) => {
426
461
  if (!cancelled) {
427
- setError(nextError instanceof Error ? nextError.message : 'Unable to calculate price.');
462
+ setError(nextError instanceof Error
463
+ ? nextError.message
464
+ : 'Unable to calculate price.');
428
465
  }
429
466
  })
430
467
  .finally(() => {
@@ -436,7 +473,14 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
436
473
  cancelled = true;
437
474
  window.clearTimeout(handle);
438
475
  };
439
- }, [client, intakeResponses, isIntakeComplete, selectedService?.pricingMode, selectedServiceId, siteSlug]);
476
+ }, [
477
+ client,
478
+ intakeResponses,
479
+ isIntakeComplete,
480
+ selectedService?.pricingMode,
481
+ selectedServiceId,
482
+ siteSlug,
483
+ ]);
440
484
  useEffect(() => {
441
485
  return () => {
442
486
  if (!holdId) {
@@ -506,7 +550,6 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
506
550
  }, refreshAt);
507
551
  return () => window.clearTimeout(handle);
508
552
  // handleHoldExpired is stable enough — it only reads setters
509
- // eslint-disable-next-line react-hooks/exhaustive-deps
510
553
  }, [client, holdExpiresAt, holdId, sessionToken, siteSlug]);
511
554
  // Fires the moment the displayed countdown hits zero. The refresh
512
555
  // effect above usually preempts this by extending the hold ~30s
@@ -519,7 +562,6 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
519
562
  if (holdNow >= holdExpiresAt) {
520
563
  handleHoldExpired();
521
564
  }
522
- // eslint-disable-next-line react-hooks/exhaustive-deps
523
565
  }, [holdExpiresAt, holdNow]);
524
566
  function handleHoldExpired() {
525
567
  setHoldId(null);
@@ -576,9 +618,15 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
576
618
  }
577
619
  return set;
578
620
  }, [availabilityByDate, selectedDate]);
579
- const selectedDateSlots = selectedDate ? filteredAvailabilityByDate.get(selectedDate) ?? [] : [];
580
- const selectedHeldStaff = availableStaff.find((staffMember) => staffMember._id === heldStaffId) ?? selectedStaff ?? null;
581
- const holdSecondsRemaining = holdExpiresAt !== null ? Math.max(0, Math.floor((holdExpiresAt - holdNow) / 1000)) : null;
621
+ const selectedDateSlots = selectedDate
622
+ ? (filteredAvailabilityByDate.get(selectedDate) ?? [])
623
+ : [];
624
+ const selectedHeldStaff = availableStaff.find((staffMember) => staffMember._id === heldStaffId) ??
625
+ selectedStaff ??
626
+ null;
627
+ const holdSecondsRemaining = holdExpiresAt !== null
628
+ ? Math.max(0, Math.floor((holdExpiresAt - holdNow) / 1000))
629
+ : null;
582
630
  const monthOptions = useMemo(() => getMonthOptions(calendarMonth, 4, intlLocale), [calendarMonth, intlLocale]);
583
631
  const calendarDays = useMemo(() => buildCalendarDays(calendarMonth), [calendarMonth]);
584
632
  // Mobile 4-step gates. canAdvanceStepN = "the user can move PAST
@@ -623,11 +671,15 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
623
671
  const slotKey = `${slot.time}-${slot.endTime}`;
624
672
  const isPending = pendingSlotKey === slotKey;
625
673
  const isActive = isPending ||
626
- (selectedSlot?.time === slot.time && selectedSlot?.endTime === slot.endTime);
627
- return (_jsx("button", { type: "button", className: `bw-slot${isActive ? ' is-active' : ''}${isPending ? ' is-pending' : ''}`, style: { '--bw-slot-i': index }, onClick: () => void handleSlotSelect(slot), disabled: isSubmitting, children: isPending ? t('slotSecuring') : formatTimeLabel(slot.time, intlLocale) }, slotKey));
674
+ (selectedSlot?.time === slot.time &&
675
+ selectedSlot?.endTime === slot.endTime);
676
+ return (_jsx("button", { type: "button", className: `bw-slot${isActive ? ' is-active' : ''}${isPending ? ' is-pending' : ''}`, style: { '--bw-slot-i': index }, onClick: () => void handleSlotSelect(slot), disabled: isSubmitting, children: isPending
677
+ ? t('slotSecuring')
678
+ : formatTimeLabel(slot.time, intlLocale) }, slotKey));
628
679
  }) })) : (_jsx("p", { className: "bw-no-slots", children: t('slotsEmptyForDate') }))) : null }, slotsAreaKey));
629
680
  const canSubmit = Boolean(selectedService &&
630
- (!selectedServiceRequiresSlot || (selectedDate && selectedSlot && holdId)) &&
681
+ (!selectedServiceRequiresSlot ||
682
+ (selectedDate && selectedSlot && holdId)) &&
631
683
  isIntakeComplete &&
632
684
  (selectedService.pricingMode !== 'calculated' || quote) &&
633
685
  customerName.trim() &&
@@ -668,7 +720,7 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
668
720
  return null;
669
721
  const visibleBlockers = submitBlockers.slice(0, 5);
670
722
  const hiddenCount = submitBlockers.length - visibleBlockers.length;
671
- return (_jsxs("div", { className: "bw-submit-help", role: "status", "aria-live": "polite", children: [_jsx("span", { className: "bw-submit-help-title", children: isQuoteLoading ? t('helpCalculating') : t('helpComplete') }), !isQuoteLoading && visibleBlockers.length > 0 ? (_jsxs("ul", { children: [visibleBlockers.map((blocker) => (_jsx("li", { children: blocker }, blocker))), hiddenCount > 0 ? _jsx("li", { children: t('helpMoreFields', { count: hiddenCount }) }) : null] })) : null] }));
723
+ return (_jsxs("div", { className: "bw-submit-help", role: "status", "aria-live": "polite", children: [_jsx("span", { className: "bw-submit-help-title", children: isQuoteLoading ? t('helpCalculating') : t('helpComplete') }), !isQuoteLoading && visibleBlockers.length > 0 ? (_jsxs("ul", { children: [visibleBlockers.map((blocker) => (_jsx("li", { children: blocker }, blocker))), hiddenCount > 0 ? (_jsx("li", { children: t('helpMoreFields', { count: hiddenCount }) })) : null] })) : null] }));
672
724
  }
673
725
  function renderContactFields(idSuffix) {
674
726
  const nameId = `bw-name${idSuffix}`;
@@ -689,6 +741,12 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
689
741
  return (_jsxs(_Fragment, { children: [contactFields, intakeFields] }));
690
742
  }
691
743
  function handleServiceSelect(service) {
744
+ analytics.capture('sable_booking_service_selected', {
745
+ service_id: service._id,
746
+ service_name: pickLocaleField(service, 'name', locale) ?? service.name,
747
+ booking_mode: service.publicBookingMode ?? 'scheduled',
748
+ requires_payment: service.requiresPayment === true,
749
+ });
692
750
  if (holdId) {
693
751
  void client
694
752
  .releasePublicBookingHold({ siteSlug, holdId, sessionToken })
@@ -810,7 +868,8 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
810
868
  async function handleConfirmBooking() {
811
869
  if (!selectedServiceId ||
812
870
  !selectedService ||
813
- (selectedServiceRequiresSlot && (!selectedDate || !selectedSlot || !holdId))) {
871
+ (selectedServiceRequiresSlot &&
872
+ (!selectedDate || !selectedSlot || !holdId))) {
814
873
  return;
815
874
  }
816
875
  setIsSubmitting(true);
@@ -832,6 +891,11 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
832
891
  try {
833
892
  await onRescheduleSubmit({ newStartTime, newEndTime });
834
893
  setSuccess(t('successRescheduleConfirmed'));
894
+ analytics.capture('sable_booking_rescheduled', {
895
+ service_id: selectedServiceId,
896
+ service_name: pickLocaleField(selectedService, 'name', locale) ??
897
+ selectedService.name,
898
+ });
835
899
  setMobileStep(4);
836
900
  }
837
901
  catch (nextError) {
@@ -849,11 +913,16 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
849
913
  siteSlug,
850
914
  serviceId: selectedServiceId,
851
915
  staffMemberId: selectedServiceRequiresSlot
852
- ? heldStaffId ?? selectedStaffId ?? selectedSlot?.availableStaffIds[0]
916
+ ? (heldStaffId ??
917
+ selectedStaffId ??
918
+ selectedSlot?.availableStaffIds[0])
853
919
  : undefined,
854
920
  startTime: newStartTime,
855
921
  endTime: newEndTime,
856
- timezone: selectedHeldStaff?.timezone ?? availableStaff[0]?.timezone ?? setup?.workspaceTimezone ?? 'UTC',
922
+ timezone: selectedHeldStaff?.timezone ??
923
+ availableStaff[0]?.timezone ??
924
+ setup?.workspaceTimezone ??
925
+ 'UTC',
857
926
  deliveryMethod: selectedService.deliveryMethods[0] ?? 'in-person',
858
927
  customerName: customerName.trim(),
859
928
  customerEmail: customerEmail.trim(),
@@ -862,15 +931,34 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
862
931
  intakeResponses,
863
932
  quotedTotalCents: quote?.totalCents,
864
933
  bookingHoldId: selectedServiceRequiresSlot ? holdId : undefined,
865
- bookingSessionToken: selectedServiceRequiresSlot ? sessionToken : undefined,
934
+ bookingSessionToken: selectedServiceRequiresSlot
935
+ ? sessionToken
936
+ : undefined,
866
937
  });
867
938
  if (created.payment) {
939
+ analytics.capture('sable_booking_payment_started', {
940
+ appointment_id: created.appointmentId,
941
+ service_id: selectedServiceId,
942
+ service_name: pickLocaleField(selectedService, 'name', locale) ??
943
+ selectedService.name,
944
+ amount_cents: created.payment.amountCents,
945
+ currency: created.payment.currency,
946
+ });
868
947
  setPayment(created.payment);
869
948
  setPaymentAppointmentId(created.appointmentId);
870
949
  setError(null);
871
950
  return;
872
951
  }
873
952
  setSuccess(t('successBookingConfirmed'));
953
+ analytics.capture('sable_booking_created', {
954
+ appointment_id: created.appointmentId,
955
+ service_id: selectedServiceId,
956
+ service_name: pickLocaleField(selectedService, 'name', locale) ??
957
+ selectedService.name,
958
+ booking_mode: selectedService.publicBookingMode ?? 'scheduled',
959
+ requires_payment: selectedService.requiresPayment === true,
960
+ quoted_total_cents: quote?.totalCents ?? null,
961
+ });
874
962
  setSelectedSlot(null);
875
963
  setPendingSlotKey(null);
876
964
  setHoldId(null);
@@ -887,7 +975,9 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
887
975
  setMobileStep(4);
888
976
  }
889
977
  catch (nextError) {
890
- setError(nextError instanceof Error ? nextError.message : t('errorConfirmFailed'));
978
+ setError(nextError instanceof Error
979
+ ? nextError.message
980
+ : t('errorConfirmFailed'));
891
981
  }
892
982
  finally {
893
983
  setIsSubmitting(false);
@@ -909,22 +999,30 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
909
999
  }, children: t('cancelledCta') })] }) }));
910
1000
  }
911
1001
  if (success || forcedState === 'success') {
912
- return (_jsx("section", { className: "bw", children: _jsxs("div", { className: "bw-done", children: [_jsx("div", { className: "bw-done-icon", children: _jsx(CheckIcon, {}) }), _jsx("h3", { className: "bw-done-title", children: isReschedule ? t('successTitleReschedule') : t('successTitleCreate') }), _jsx("p", { className: "bw-done-text", children: isReschedule ? t('successBodyReschedule') : t('successBodyCreate') }), isReschedule ? null : (_jsx("button", { type: "button", className: "bw-btn-primary", onClick: () => {
1002
+ return (_jsx("section", { className: "bw", children: _jsxs("div", { className: "bw-done", children: [_jsx("div", { className: "bw-done-icon", children: _jsx(CheckIcon, {}) }), _jsx("h3", { className: "bw-done-title", children: isReschedule
1003
+ ? t('successTitleReschedule')
1004
+ : t('successTitleCreate') }), _jsx("p", { className: "bw-done-text", children: isReschedule ? t('successBodyReschedule') : t('successBodyCreate') }), isReschedule ? null : (_jsx("button", { type: "button", className: "bw-btn-primary", onClick: () => {
913
1005
  window.location.assign(successRedirectHref);
914
1006
  }, children: t('successCtaBackHome') }))] }) }));
915
1007
  }
916
- return (_jsx(Skeleton, { name: "booking-widget", loading: false, children: _jsxs("section", { className: "bw", "data-view-state": viewState, ref: widgetRootRef, children: [mobileHeader ? _jsx("div", { className: "bw-mobile-header", children: mobileHeader }) : null, _jsxs("div", { className: "bw-header", children: [_jsxs("div", { className: "bw-header-row", children: [_jsx("h2", { className: "bw-title bw-title--desktop", children: title ||
1008
+ return (_jsx(Skeleton, { name: "booking-widget", loading: false, children: _jsxs("section", { className: "bw", "data-view-state": viewState, ref: widgetRootRef, children: [mobileHeader ? (_jsx("div", { className: "bw-mobile-header", children: mobileHeader })) : null, _jsxs("div", { className: "bw-header", children: [_jsxs("div", { className: "bw-header-row", children: [_jsx("h2", { className: "bw-title bw-title--desktop", children: title ||
917
1009
  (isReschedule
918
1010
  ? rescheduleContext?.customerName
919
- ? t('rescheduleTitleNamed', { name: rescheduleContext.customerName })
1011
+ ? t('rescheduleTitleNamed', {
1012
+ name: rescheduleContext.customerName,
1013
+ })
920
1014
  : t('rescheduleTitle')
921
1015
  : t('pageTitle')) }), _jsx("h2", { className: "bw-title bw-title--mobile", children: isReschedule
922
1016
  ? rescheduleContext?.customerName
923
- ? t('rescheduleTitleNamed', { name: rescheduleContext.customerName })
1017
+ ? t('rescheduleTitleNamed', {
1018
+ name: rescheduleContext.customerName,
1019
+ })
924
1020
  : t('rescheduleTitle')
925
1021
  : mobileStepTitle(mobileStep, t) }), mobileStep > 1 ? (_jsx("div", { className: "bw-progress", "aria-hidden": "true", children: (selectedServiceRequiresSlot
926
1022
  ? MOBILE_PROGRESS_STEPS_SCHEDULED
927
- : MOBILE_PROGRESS_STEPS_ASYNC).map((step) => (_jsx("span", { className: `bw-dot${mobileStep >= step ? ' is-filled' : ''}` }, step))) })) : null] }), description ? _jsx("p", { className: "bw-description", children: description }) : null] }), _jsxs("div", { className: "bw-content", children: [_jsx("div", { className: "bw-body", "data-mobile-step": mobileStep, children: _jsxs("div", { className: "bw-body-inner", children: [_jsxs("div", { className: "bw-col bw-col--left", children: [_jsxs("div", { className: "bw-step-1", children: [_jsx("div", { className: "bw-service-picker", children: selectedService && !isEditingService ? (_jsxs("div", { className: "bw-svc-selected", children: [_jsxs("div", { className: "bw-svc-selected-header", children: [_jsx("label", { className: "bw-label", children: t('selectedService') }), _jsx("button", { type: "button", className: "bw-svc-change", onClick: () => setIsEditingService(true), children: t('btnChange') })] }), _jsxs("div", { className: "bw-svc-row bw-svc-row--readonly", children: [selectedService.image ? (_jsxs("span", { className: "bw-svc-image is-checked", children: [_jsx("img", { src: selectedService.image.url, alt: selectedService.image.alt ?? pickLocaleField(selectedService, 'name', locale) ?? selectedService.name, loading: "lazy" }), _jsx("span", { className: "bw-svc-image-badge", children: _jsx(CheckIcon, {}) })] })) : (_jsx("span", { className: "bw-check is-checked", children: _jsx(CheckIcon, {}) })), _jsxs("span", { className: "bw-svc-info", children: [_jsx("span", { className: "bw-svc-name", children: pickLocaleField(selectedService, 'name', locale) ?? selectedService.name }), pickLocaleField(selectedService, 'description', locale) ?? selectedService.description ? (_jsx("span", { className: "bw-svc-desc", children: pickLocaleField(selectedService, 'description', locale) ?? selectedService.description })) : null, _jsxs("span", { className: "bw-svc-meta-row", children: [_jsx("span", { className: "bw-svc-meta", children: formatDuration(selectedService.durationMinutes) }), _jsx("span", { className: "bw-svc-meta-dot", "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "bw-svc-price", children: formatServicePrice(selectedService, undefined, intlLocale) })] })] })] })] })) : (_jsxs(_Fragment, { children: [_jsx("label", { className: "bw-label", children: t('selectService') }), _jsx("div", { className: "bw-svc-scroll is-open", children: _jsx("div", { className: "bw-svc-scroll-wrap", children: _jsx("div", { className: "bw-svc-scroll-inner", children: serviceGroups.map((group) => (_jsxs("div", { className: "bw-svc-group", children: [group.categoryName ? (_jsx("div", { className: "bw-svc-category", children: group.categoryName })) : null, group.services.map((service) => {
1023
+ : MOBILE_PROGRESS_STEPS_ASYNC).map((step) => (_jsx("span", { className: `bw-dot${mobileStep >= step ? ' is-filled' : ''}` }, step))) })) : null] }), description ? _jsx("p", { className: "bw-description", children: description }) : null] }), _jsxs("div", { className: "bw-content", children: [_jsx("div", { className: "bw-body", "data-mobile-step": mobileStep, children: _jsxs("div", { className: "bw-body-inner", children: [_jsxs("div", { className: "bw-col bw-col--left", children: [_jsxs("div", { className: "bw-step-1", children: [_jsx("div", { className: "bw-service-picker", children: selectedService && !isEditingService ? (_jsxs("div", { className: "bw-svc-selected", children: [_jsxs("div", { className: "bw-svc-selected-header", children: [_jsx("label", { className: "bw-label", children: t('selectedService') }), _jsx("button", { type: "button", className: "bw-svc-change", onClick: () => setIsEditingService(true), children: t('btnChange') })] }), _jsxs("div", { className: "bw-svc-row bw-svc-row--readonly", children: [selectedService.image ? (_jsxs("span", { className: "bw-svc-image is-checked", children: [_jsx("img", { src: selectedService.image.url, alt: selectedService.image.alt ??
1024
+ pickLocaleField(selectedService, 'name', locale) ??
1025
+ selectedService.name, loading: "lazy" }), _jsx("span", { className: "bw-svc-image-badge", children: _jsx(CheckIcon, {}) })] })) : (_jsx("span", { className: "bw-check is-checked", children: _jsx(CheckIcon, {}) })), _jsxs("span", { className: "bw-svc-info", children: [_jsx("span", { className: "bw-svc-name", children: pickLocaleField(selectedService, 'name', locale) ?? selectedService.name }), (pickLocaleField(selectedService, 'description', locale) ?? selectedService.description) ? (_jsx("span", { className: "bw-svc-desc", children: pickLocaleField(selectedService, 'description', locale) ?? selectedService.description })) : null, _jsxs("span", { className: "bw-svc-meta-row", children: [_jsx("span", { className: "bw-svc-meta", children: formatDuration(selectedService.durationMinutes) }), _jsx("span", { className: "bw-svc-meta-dot", "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "bw-svc-price", children: formatServicePrice(selectedService, undefined, intlLocale) })] })] })] })] })) : (_jsxs(_Fragment, { children: [_jsx("label", { className: "bw-label", children: t('selectService') }), _jsx("div", { className: "bw-svc-scroll is-open", children: _jsx("div", { className: "bw-svc-scroll-wrap", children: _jsx("div", { className: "bw-svc-scroll-inner", children: serviceGroups.map((group) => (_jsxs("div", { className: "bw-svc-group", children: [group.categoryName ? (_jsx("div", { className: "bw-svc-category", children: group.categoryName })) : null, group.services.map((service) => {
928
1026
  const isActive = selectedServiceId === service._id;
929
1027
  return (_jsxs("button", { type: "button", className: "bw-svc-row", onMouseEnter: () => {
930
1028
  void prefetchAvailability({
@@ -944,8 +1042,12 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
944
1042
  });
945
1043
  }, onClick: () => {
946
1044
  handleServiceSelect(service);
947
- }, children: [service.image ? (_jsxs("span", { className: `bw-svc-image${isActive ? ' is-checked' : ''}`, children: [_jsx("img", { src: service.image.url, alt: service.image.alt ?? pickLocaleField(service, 'name', locale) ?? service.name, loading: "lazy" }), isActive ? (_jsx("span", { className: "bw-svc-image-badge", children: _jsx(CheckIcon, {}) })) : null] })) : (_jsx("span", { className: `bw-check${isActive ? ' is-checked' : ''}`, children: isActive ? _jsx(CheckIcon, {}) : null })), _jsxs("span", { className: "bw-svc-info", children: [_jsx("span", { className: "bw-svc-name", children: pickLocaleField(service, 'name', locale) ?? service.name }), pickLocaleField(service, 'description', locale) ?? service.description ? (_jsx("span", { className: "bw-svc-desc", children: pickLocaleField(service, 'description', locale) ?? service.description })) : null, _jsxs("span", { className: "bw-svc-meta-row", children: [_jsx("span", { className: "bw-svc-meta", children: formatDuration(service.durationMinutes) }), _jsx("span", { className: "bw-svc-meta-dot", "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "bw-svc-price", children: formatServicePrice(service, undefined, intlLocale) })] })] })] }, service._id));
948
- })] }, group.key))) }) }) })] })) }), selectedService && selectedServiceRequiresSlot && !isEditingService ? (_jsxs("div", { className: "bw-provider-section", children: [_jsx("div", { className: "bw-section-divider" }), _jsx("label", { className: "bw-label", children: t('selectProvider') }), providerRow] })) : null] }), _jsxs("div", { className: "bw-step-2", children: [_jsx("p", { className: "bw-step-2-heading", children: t('chooseYourService') }), _jsx("div", { className: "bw-step-2-divider" }), serviceGroups.map((group) => (_jsxs("div", { className: "bw-svc-group", children: [group.categoryName ? (_jsx("div", { className: "bw-svc-category", children: group.categoryName })) : null, group.services.map((service) => {
1045
+ }, children: [service.image ? (_jsxs("span", { className: `bw-svc-image${isActive ? ' is-checked' : ''}`, children: [_jsx("img", { src: service.image.url, alt: service.image.alt ??
1046
+ pickLocaleField(service, 'name', locale) ??
1047
+ service.name, loading: "lazy" }), isActive ? (_jsx("span", { className: "bw-svc-image-badge", children: _jsx(CheckIcon, {}) })) : null] })) : (_jsx("span", { className: `bw-check${isActive ? ' is-checked' : ''}`, children: isActive ? _jsx(CheckIcon, {}) : null })), _jsxs("span", { className: "bw-svc-info", children: [_jsx("span", { className: "bw-svc-name", children: pickLocaleField(service, 'name', locale) ?? service.name }), (pickLocaleField(service, 'description', locale) ?? service.description) ? (_jsx("span", { className: "bw-svc-desc", children: pickLocaleField(service, 'description', locale) ?? service.description })) : null, _jsxs("span", { className: "bw-svc-meta-row", children: [_jsx("span", { className: "bw-svc-meta", children: formatDuration(service.durationMinutes) }), _jsx("span", { className: "bw-svc-meta-dot", "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "bw-svc-price", children: formatServicePrice(service, undefined, intlLocale) })] })] })] }, service._id));
1048
+ })] }, group.key))) }) }) })] })) }), selectedService &&
1049
+ selectedServiceRequiresSlot &&
1050
+ !isEditingService ? (_jsxs("div", { className: "bw-provider-section", children: [_jsx("div", { className: "bw-section-divider" }), _jsx("label", { className: "bw-label", children: t('selectProvider') }), providerRow] })) : null] }), _jsxs("div", { className: "bw-step-2", children: [_jsx("p", { className: "bw-step-2-heading", children: t('chooseYourService') }), _jsx("div", { className: "bw-step-2-divider" }), serviceGroups.map((group) => (_jsxs("div", { className: "bw-svc-group", children: [group.categoryName ? (_jsx("div", { className: "bw-svc-category", children: group.categoryName })) : null, group.services.map((service) => {
949
1051
  const isActive = selectedServiceId === service._id;
950
1052
  return (_jsxs("div", { children: [_jsxs("button", { type: "button", className: "bw-svc-row bw-svc-row--full", onMouseEnter: () => {
951
1053
  void prefetchAvailability({
@@ -965,12 +1067,16 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
965
1067
  });
966
1068
  }, onClick: () => {
967
1069
  handleServiceSelect(service);
968
- }, children: [service.image ? (_jsxs("span", { className: `bw-svc-image bw-svc-image--lg${isActive ? ' is-checked' : ''}`, children: [_jsx("img", { src: service.image.url, alt: service.image.alt ?? pickLocaleField(service, 'name', locale) ?? service.name, loading: "lazy" }), isActive ? (_jsx("span", { className: "bw-svc-image-badge", children: _jsx(CheckIcon, {}) })) : null] })) : (_jsx("span", { className: `bw-check bw-check--lg${isActive ? ' is-checked' : ''}`, children: isActive ? _jsx(CheckIcon, {}) : null })), _jsxs("span", { className: "bw-svc-info", children: [_jsx("span", { className: "bw-svc-name", children: pickLocaleField(service, 'name', locale) ?? service.name }), pickLocaleField(service, 'description', locale) ?? service.description ? (_jsx("span", { className: "bw-svc-desc", children: pickLocaleField(service, 'description', locale) ?? service.description })) : null, _jsxs("span", { className: "bw-svc-meta-row", children: [_jsx("span", { className: "bw-svc-meta", children: formatDuration(service.durationMinutes) }), _jsx("span", { className: "bw-svc-meta-dot", "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "bw-svc-price", children: formatServicePrice(service, undefined, intlLocale) })] })] })] }), _jsx("div", { className: "bw-row-divider" })] }, service._id));
1070
+ }, children: [service.image ? (_jsxs("span", { className: `bw-svc-image bw-svc-image--lg${isActive ? ' is-checked' : ''}`, children: [_jsx("img", { src: service.image.url, alt: service.image.alt ??
1071
+ pickLocaleField(service, 'name', locale) ??
1072
+ service.name, loading: "lazy" }), isActive ? (_jsx("span", { className: "bw-svc-image-badge", children: _jsx(CheckIcon, {}) })) : null] })) : (_jsx("span", { className: `bw-check bw-check--lg${isActive ? ' is-checked' : ''}`, children: isActive ? _jsx(CheckIcon, {}) : null })), _jsxs("span", { className: "bw-svc-info", children: [_jsx("span", { className: "bw-svc-name", children: pickLocaleField(service, 'name', locale) ??
1073
+ service.name }), (pickLocaleField(service, 'description', locale) ?? service.description) ? (_jsx("span", { className: "bw-svc-desc", children: pickLocaleField(service, 'description', locale) ?? service.description })) : null, _jsxs("span", { className: "bw-svc-meta-row", children: [_jsx("span", { className: "bw-svc-meta", children: formatDuration(service.durationMinutes) }), _jsx("span", { className: "bw-svc-meta-dot", "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "bw-svc-price", children: formatServicePrice(service, undefined, intlLocale) })] })] })] }), _jsx("div", { className: "bw-row-divider" })] }, service._id));
969
1074
  })] }, group.key)))] })] }), _jsxs("div", { className: "bw-col bw-col--center", children: [_jsxs("div", { className: "bw-cal-card", ref: calCardRef, children: [_jsxs("div", { className: "bw-cal-header", children: [_jsxs("div", { className: "bw-month-dropdown", children: [_jsxs("button", { type: "button", className: "bw-month-btn", onClick: () => setMonthOpen((current) => !current), children: [_jsx("span", { children: formatMonthLabel(calendarMonth, intlLocale) }), _jsx(ChevronDownIcon, {})] }), monthOpen ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "bw-month-overlay", "aria-label": t('closeMonthPicker'), onClick: () => setMonthOpen(false) }), _jsx("div", { className: "bw-month-list", children: monthOptions.map((option) => (_jsx("button", { type: "button", className: `bw-month-option${option.value === optionCurrentValue(calendarMonth) ? ' is-active' : ''}`, onClick: () => {
970
1075
  setCalendarMonth(option.date);
971
1076
  setMonthOpen(false);
972
1077
  }, children: option.label }, option.value))) })] })) : null] }), _jsxs("div", { className: "bw-cal-navs", children: [_jsx("button", { type: "button", className: "bw-cal-nav", onMouseEnter: () => {
973
- if (!sameMonth(calendarMonth, startOfMonth(new Date())) && selectedServiceId) {
1078
+ if (!sameMonth(calendarMonth, startOfMonth(new Date())) &&
1079
+ selectedServiceId) {
974
1080
  void prefetchAvailability({
975
1081
  client,
976
1082
  siteSlug,
@@ -980,7 +1086,8 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
980
1086
  });
981
1087
  }
982
1088
  }, onClick: () => setCalendarMonth((current) => addMonths(current, -1)), disabled: sameMonth(calendarMonth, startOfMonth(new Date())), "aria-label": t('prevMonth'), children: _jsx(ChevronLeftIcon, {}) }), _jsx("button", { type: "button", className: "bw-cal-nav", onMouseEnter: () => {
983
- if (!sameMonth(calendarMonth, addMonths(startOfMonth(new Date()), 3)) && selectedServiceId) {
1089
+ if (!sameMonth(calendarMonth, addMonths(startOfMonth(new Date()), 3)) &&
1090
+ selectedServiceId) {
984
1091
  void prefetchAvailability({
985
1092
  client,
986
1093
  siteSlug,
@@ -1010,22 +1117,53 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
1010
1117
  isSelected ? 'is-selected' : '',
1011
1118
  isAvailable && !isPast ? 'is-available' : '',
1012
1119
  isPast ? 'is-disabled' : '',
1013
- !isAvailable && isCurrentMonth && !isPast ? 'is-blocked' : '',
1014
- dateKey === formatDateKey(startOfDay(new Date())) ? 'is-today' : '',
1120
+ !isAvailable && isCurrentMonth && !isPast
1121
+ ? 'is-blocked'
1122
+ : '',
1123
+ dateKey === formatDateKey(startOfDay(new Date()))
1124
+ ? 'is-today'
1125
+ : '',
1015
1126
  ]
1016
1127
  .filter(Boolean)
1017
1128
  .join(' ');
1018
1129
  return (_jsx("button", { type: "button", className: className, disabled: !isCurrentMonth || isPast || !isAvailable, onClick: () => {
1019
1130
  setSelectedDate(dateKey);
1020
1131
  setSelectedSlot(null);
1021
- setMobileStep((current) => (current < 2 ? 2 : current));
1132
+ setMobileStep((current) => current < 2 ? 2 : current);
1022
1133
  }, children: day.getDate() }, dateKey));
1023
- }) }), !selectedService ? (_jsx("p", { className: "bw-cal-prompt", children: t('calendarSelectServicePrompt') })) : null, _jsxs("div", { className: "bw-timezone", children: [_jsx(GlobeIcon, {}), _jsx("span", { children: selectedHeldStaff?.timezone ?? selectedStaff?.timezone ?? setup?.workspaceTimezone ?? setup?.staff?.[0]?.timezone ?? 'UTC' })] })] }), selectedService && !isEditingService ? (_jsxs("div", { className: "bw-mobile-card bw-provider-section--mobile", children: [_jsx("label", { className: "bw-label", children: t('selectProvider') }), providerRow] })) : null, selectedService && selectedDate ? (_jsxs("div", { className: "bw-mobile-card bw-slots-mobile", children: [_jsx("label", { className: "bw-label", children: t('selectTimePrompt') }), slotsArea] })) : null] }), _jsx("div", { className: "bw-col bw-col--review", "aria-hidden": mobileStep !== 4, children: selectedService ? (_jsxs("div", { className: "bw-summary bw-summary--review", children: [_jsx("span", { className: "bw-summary-title", children: isReschedule ? t('summaryTitleReschedule') : t('summaryTitleCreate') }), holdSecondsRemaining !== null ? (_jsx("div", { className: "bw-summary-rows", children: _jsxs("div", { className: "bw-summary-row bw-summary-row--hold", "aria-live": "polite", children: [_jsx("span", { children: t('summaryReservationHold') }), _jsx("span", { className: "bw-summary-val bw-summary-val--hold", children: formatHoldCountdown(holdSecondsRemaining) })] }) })) : null, _jsxs("div", { className: "bw-summary-group", children: [_jsx("span", { className: "bw-summary-subhead", children: t('reviewBookingDetails') }), _jsxs("div", { className: "bw-summary-rows", children: [isReschedule && rescheduleContext ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryFormerTime') }), _jsx("span", { className: "bw-summary-val", style: { ...summaryValStyle, textDecoration: 'line-through', opacity: 0.55 }, children: formatFormerSlot(rescheduleContext.formerStartTime, rescheduleContext.formerEndTime, intlLocale) })] })) : null, serviceChanged && formerService ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryFormerService') }), _jsx("span", { className: "bw-summary-val", style: { ...summaryValStyle, textDecoration: 'line-through', opacity: 0.55 }, children: formerService.name })] })) : null, staffChanged ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryFormerStaff') }), _jsx("span", { className: "bw-summary-val", style: { ...summaryValStyle, textDecoration: 'line-through', opacity: 0.55 }, children: formerStaffName ?? t('providerAny') })] })) : null, _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryService') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: pickLocaleField(selectedService, 'name', locale) ?? selectedService.name })] }), _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryProvider') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: selectedHeldStaff?.name ?? selectedStaff?.name ?? t('providerAny') })] }), selectedDate ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryDate') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatReadableDate(selectedDate, intlLocale) })] })) : null, selectedSlot ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryTime') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatTimeLabel(selectedSlot.time, intlLocale) })] })) : null, _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryDuration') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatDuration(selectedService.durationMinutes) })] })] })] }), _jsxs("div", { className: "bw-summary-group", children: [_jsx("span", { className: "bw-summary-subhead", children: t('reviewYourDetails') }), _jsxs("div", { className: "bw-summary-rows", children: [_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('contactFullName') }), _jsx("span", { className: "bw-summary-val", children: customerName.trim() || t('reviewNotProvided') })] }), _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('contactEmail') }), _jsx("span", { className: "bw-summary-val", children: customerEmail.trim() || t('reviewNotProvided') })] }), _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('contactPhone') }), _jsx("span", { className: "bw-summary-val", children: customerPhone.trim() || t('reviewNotProvided') })] })] })] }), (() => {
1134
+ }) }), !selectedService ? (_jsx("p", { className: "bw-cal-prompt", children: t('calendarSelectServicePrompt') })) : null, _jsxs("div", { className: "bw-timezone", children: [_jsx(GlobeIcon, {}), _jsx("span", { children: selectedHeldStaff?.timezone ??
1135
+ selectedStaff?.timezone ??
1136
+ setup?.workspaceTimezone ??
1137
+ setup?.staff?.[0]?.timezone ??
1138
+ 'UTC' })] })] }), selectedService && !isEditingService ? (_jsxs("div", { className: "bw-mobile-card bw-provider-section--mobile", children: [_jsx("label", { className: "bw-label", children: t('selectProvider') }), providerRow] })) : null, selectedService && selectedDate ? (_jsxs("div", { className: "bw-mobile-card bw-slots-mobile", children: [_jsx("label", { className: "bw-label", children: t('selectTimePrompt') }), slotsArea] })) : null] }), _jsx("div", { className: "bw-col bw-col--review", "aria-hidden": mobileStep !== 4, children: selectedService ? (_jsxs("div", { className: "bw-summary bw-summary--review", children: [_jsx("span", { className: "bw-summary-title", children: isReschedule
1139
+ ? t('summaryTitleReschedule')
1140
+ : t('summaryTitleCreate') }), holdSecondsRemaining !== null ? (_jsx("div", { className: "bw-summary-rows", children: _jsxs("div", { className: "bw-summary-row bw-summary-row--hold", "aria-live": "polite", children: [_jsx("span", { children: t('summaryReservationHold') }), _jsx("span", { className: "bw-summary-val bw-summary-val--hold", children: formatHoldCountdown(holdSecondsRemaining) })] }) })) : null, _jsxs("div", { className: "bw-summary-group", children: [_jsx("span", { className: "bw-summary-subhead", children: t('reviewBookingDetails') }), _jsxs("div", { className: "bw-summary-rows", children: [isReschedule && rescheduleContext ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryFormerTime') }), _jsx("span", { className: "bw-summary-val", style: {
1141
+ ...summaryValStyle,
1142
+ textDecoration: 'line-through',
1143
+ opacity: 0.55,
1144
+ }, children: formatFormerSlot(rescheduleContext.formerStartTime, rescheduleContext.formerEndTime, intlLocale) })] })) : null, serviceChanged && formerService ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryFormerService') }), _jsx("span", { className: "bw-summary-val", style: {
1145
+ ...summaryValStyle,
1146
+ textDecoration: 'line-through',
1147
+ opacity: 0.55,
1148
+ }, children: formerService.name })] })) : null, staffChanged ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryFormerStaff') }), _jsx("span", { className: "bw-summary-val", style: {
1149
+ ...summaryValStyle,
1150
+ textDecoration: 'line-through',
1151
+ opacity: 0.55,
1152
+ }, children: formerStaffName ?? t('providerAny') })] })) : null, _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryService') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: pickLocaleField(selectedService, 'name', locale) ??
1153
+ selectedService.name })] }), _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryProvider') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: selectedHeldStaff?.name ??
1154
+ selectedStaff?.name ??
1155
+ t('providerAny') })] }), selectedDate ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryDate') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatReadableDate(selectedDate, intlLocale) })] })) : null, selectedSlot ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryTime') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatTimeLabel(selectedSlot.time, intlLocale) })] })) : null, _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryDuration') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatDuration(selectedService.durationMinutes) })] })] })] }), _jsxs("div", { className: "bw-summary-group", children: [_jsx("span", { className: "bw-summary-subhead", children: t('reviewYourDetails') }), _jsxs("div", { className: "bw-summary-rows", children: [_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('contactFullName') }), _jsx("span", { className: "bw-summary-val", children: customerName.trim() || t('reviewNotProvided') })] }), _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('contactEmail') }), _jsx("span", { className: "bw-summary-val", children: customerEmail.trim() || t('reviewNotProvided') })] }), _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('contactPhone') }), _jsx("span", { className: "bw-summary-val", children: customerPhone.trim() || t('reviewNotProvided') })] })] })] }), (() => {
1024
1156
  const intakeRows = buildIntakeReviewRows(selectedService.intakeForm, intakeResponses, selectedServicePath, locale, t);
1025
1157
  if (intakeRows.length === 0)
1026
1158
  return null;
1027
1159
  return (_jsxs("div", { className: "bw-summary-group", children: [_jsx("span", { className: "bw-summary-subhead", children: t('reviewAdditionalInfo') }), _jsx("div", { className: "bw-summary-rows", children: intakeRows.map((row) => (_jsxs("div", { className: `bw-summary-row${row.multiline ? ' bw-summary-row--stack' : ''}`, children: [_jsx("span", { children: row.label }), _jsx("span", { className: "bw-summary-val", children: row.value })] }, row.key))) })] }));
1028
- })(), customerNotes.trim() ? (_jsxs("div", { className: "bw-summary-group", children: [_jsx("span", { className: "bw-summary-subhead", children: isReschedule ? t('notesLabelReschedule') : t('reviewNotesSection') }), _jsx("p", { className: "bw-summary-notes", children: customerNotes.trim() })] })) : null, _jsx("div", { className: "bw-summary-rows bw-summary-rows--total", children: _jsxs("div", { className: "bw-summary-row bw-summary-total", children: [_jsx("span", { children: isQuoteLoading ? t('summaryCalculating') : t('summaryEstimatedTotal') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatServicePrice(selectedService, quote, intlLocale) })] }) })] })) : null }), _jsx("div", { className: "bw-col bw-col--right", children: _jsx("div", { className: "bw-right-scroll", children: _jsx("div", { className: "bw-right-inner", children: _jsxs("div", { className: "bw-right-stage", children: [_jsxs("div", { className: `bw-pane bw-pane--slots${viewState === 'slots' ? ' is-active' : ' is-hidden'}`, "aria-hidden": viewState !== 'slots', children: [_jsxs("div", { className: "bw-slots-heading bw-slots-heading--sticky", children: [_jsx("span", { children: t('slotsHeading') }), selectedDateSlots.length > 0 ? (_jsx("span", { className: "bw-slots-count", children: selectedDateSlots.length })) : null] }), _jsx("div", { className: "bw-slots-desktop", children: slotsArea })] }), _jsxs("div", { className: `bw-pane bw-pane--details${viewState === 'details' ? ' is-active' : ' is-hidden'}`, "aria-hidden": viewState !== 'details', children: [selectedServiceRequiresSlot ? (_jsxs("button", { type: "button", className: "bw-back-btn", onClick: () => void handleBackToSlots(), children: [_jsx(ArrowLeftIcon, {}), _jsx("span", { children: t('backToDifferentTime') })] })) : (_jsxs("button", { type: "button", className: "bw-back-btn", onClick: handleBackToServicePicker, children: [_jsx(ArrowLeftIcon, {}), _jsx("span", { children: t('backToDifferentService') })] })), _jsxs("div", { className: "bw-form", children: [serviceWarning ? _jsx(ServiceWarningCallout, { warning: serviceWarning }) : null, renderIntakeAndContact('bw-intake', ''), renderNotesField('bw-notes', t('notesLabel'), t('notesPlaceholder')), selectedService ? (_jsxs("div", { className: "bw-summary", children: [_jsx("span", { className: "bw-summary-title", children: isReschedule ? t('summaryTitleReschedule') : t('summaryTitleCreate') }), _jsxs("div", { className: "bw-summary-rows", children: [holdSecondsRemaining !== null ? (_jsxs("div", { className: "bw-summary-row bw-summary-row--hold", "aria-live": "polite", children: [_jsx("span", { children: t('summaryReservationHold') }), _jsx("span", { className: "bw-summary-val bw-summary-val--hold", children: formatHoldCountdown(holdSecondsRemaining) })] })) : null, isReschedule && rescheduleContext ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryFormerTime') }), _jsx("span", { className: "bw-summary-val", style: {
1160
+ })(), customerNotes.trim() ? (_jsxs("div", { className: "bw-summary-group", children: [_jsx("span", { className: "bw-summary-subhead", children: isReschedule
1161
+ ? t('notesLabelReschedule')
1162
+ : t('reviewNotesSection') }), _jsx("p", { className: "bw-summary-notes", children: customerNotes.trim() })] })) : null, _jsx("div", { className: "bw-summary-rows bw-summary-rows--total", children: _jsxs("div", { className: "bw-summary-row bw-summary-total", children: [_jsx("span", { children: isQuoteLoading
1163
+ ? t('summaryCalculating')
1164
+ : t('summaryEstimatedTotal') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatServicePrice(selectedService, quote, intlLocale) })] }) })] })) : null }), _jsx("div", { className: "bw-col bw-col--right", children: _jsx("div", { className: "bw-right-scroll", children: _jsx("div", { className: "bw-right-inner", children: _jsxs("div", { className: "bw-right-stage", children: [_jsxs("div", { className: `bw-pane bw-pane--slots${viewState === 'slots' ? ' is-active' : ' is-hidden'}`, "aria-hidden": viewState !== 'slots', children: [_jsxs("div", { className: "bw-slots-heading bw-slots-heading--sticky", children: [_jsx("span", { children: t('slotsHeading') }), selectedDateSlots.length > 0 ? (_jsx("span", { className: "bw-slots-count", children: selectedDateSlots.length })) : null] }), _jsx("div", { className: "bw-slots-desktop", children: slotsArea })] }), _jsxs("div", { className: `bw-pane bw-pane--details${viewState === 'details' ? ' is-active' : ' is-hidden'}`, "aria-hidden": viewState !== 'details', children: [selectedServiceRequiresSlot ? (_jsxs("button", { type: "button", className: "bw-back-btn", onClick: () => void handleBackToSlots(), children: [_jsx(ArrowLeftIcon, {}), _jsx("span", { children: t('backToDifferentTime') })] })) : (_jsxs("button", { type: "button", className: "bw-back-btn", onClick: handleBackToServicePicker, children: [_jsx(ArrowLeftIcon, {}), _jsx("span", { children: t('backToDifferentService') })] })), _jsxs("div", { className: "bw-form", children: [serviceWarning ? (_jsx(ServiceWarningCallout, { warning: serviceWarning })) : null, renderIntakeAndContact('bw-intake', ''), renderNotesField('bw-notes', t('notesLabel'), t('notesPlaceholder')), selectedService ? (_jsxs("div", { className: "bw-summary", children: [_jsx("span", { className: "bw-summary-title", children: isReschedule
1165
+ ? t('summaryTitleReschedule')
1166
+ : t('summaryTitleCreate') }), _jsxs("div", { className: "bw-summary-rows", children: [holdSecondsRemaining !== null ? (_jsxs("div", { className: "bw-summary-row bw-summary-row--hold", "aria-live": "polite", children: [_jsx("span", { children: t('summaryReservationHold') }), _jsx("span", { className: "bw-summary-val bw-summary-val--hold", children: formatHoldCountdown(holdSecondsRemaining) })] })) : null, isReschedule && rescheduleContext ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryFormerTime') }), _jsx("span", { className: "bw-summary-val", style: {
1029
1167
  ...summaryValStyle,
1030
1168
  textDecoration: 'line-through',
1031
1169
  opacity: 0.55,
@@ -1037,7 +1175,11 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
1037
1175
  ...summaryValStyle,
1038
1176
  textDecoration: 'line-through',
1039
1177
  opacity: 0.55,
1040
- }, children: formerStaffName ?? t('providerAny') })] })) : null, _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryService') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: pickLocaleField(selectedService, 'name', locale) ?? selectedService.name })] }), _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryProvider') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: selectedHeldStaff?.name ?? selectedStaff?.name ?? t('providerAny') })] }), selectedDate ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryDate') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatReadableDate(selectedDate, intlLocale) })] })) : null, selectedSlot ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryTime') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatTimeLabel(selectedSlot.time, intlLocale) })] })) : null, _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryDuration') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatDuration(selectedService.durationMinutes) })] }), _jsxs("div", { className: "bw-summary-row bw-summary-total", children: [_jsx("span", { children: isQuoteLoading ? t('summaryCalculating') : t('summaryEstimatedTotal') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatServicePrice(selectedService, quote, intlLocale) })] })] })] })) : null, error ? _jsx("div", { className: "bw-error", children: error }) : null, renderSubmitHelp(), payment ? (_jsx(StripePaymentBlock, { payment: payment, appointmentId: paymentAppointmentId, showInlineButton: false, actionTarget: mobilePaymentActionTarget, onSuccess: () => {
1178
+ }, children: formerStaffName ?? t('providerAny') })] })) : null, _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryService') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: pickLocaleField(selectedService, 'name', locale) ?? selectedService.name })] }), _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryProvider') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: selectedHeldStaff?.name ??
1179
+ selectedStaff?.name ??
1180
+ t('providerAny') })] }), selectedDate ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryDate') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatReadableDate(selectedDate, intlLocale) })] })) : null, selectedSlot ? (_jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryTime') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatTimeLabel(selectedSlot.time, intlLocale) })] })) : null, _jsxs("div", { className: "bw-summary-row", children: [_jsx("span", { children: t('summaryDuration') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatDuration(selectedService.durationMinutes) })] }), _jsxs("div", { className: "bw-summary-row bw-summary-total", children: [_jsx("span", { children: isQuoteLoading
1181
+ ? t('summaryCalculating')
1182
+ : t('summaryEstimatedTotal') }), _jsx("span", { className: "bw-summary-val", style: summaryValStyle, children: formatServicePrice(selectedService, quote, intlLocale) })] })] })] })) : null, error ? (_jsx("div", { className: "bw-error", children: error })) : null, renderSubmitHelp(), payment ? (_jsx(StripePaymentBlock, { payment: payment, appointmentId: paymentAppointmentId, showInlineButton: false, actionTarget: mobilePaymentActionTarget, onSuccess: () => {
1041
1183
  setSuccess(t('successPaymentReceived'));
1042
1184
  setPayment(null);
1043
1185
  }, onError: setError })) : (_jsxs("button", { type: "button", className: `bw-confirm-btn${!canSubmit || isQuoteLoading ? ' is-disabled' : ''}`, "aria-disabled": !canSubmit || isQuoteLoading, onClick: handleConfirmAttempt, children: [_jsx("span", { children: isSubmitting
@@ -1048,7 +1190,23 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
1048
1190
  ? t('btnConfirmReschedule')
1049
1191
  : selectedServiceRequiresPayment
1050
1192
  ? t('btnContinueToPayment')
1051
- : t('btnConfirmBooking') }), !isSubmitting ? _jsx(ArrowRightIcon, {}) : null] }))] })] })] }) }) }) })] }) }), _jsxs("div", { className: "bw-details-view", children: [_jsx("div", { className: "bw-details-summary", children: selectedService ? (_jsxs(_Fragment, { children: [selectedServiceRequiresSlot ? (_jsxs("button", { type: "button", className: "bw-back-btn bw-back-btn--details", onClick: () => void handleBackToSlots(), children: [_jsx(ArrowLeftIcon, {}), _jsx("span", { children: t('backToDifferentTime') })] })) : (_jsxs("button", { type: "button", className: "bw-back-btn bw-back-btn--details", onClick: handleBackToServicePicker, children: [_jsx(ArrowLeftIcon, {}), _jsx("span", { children: t('backToDifferentService') })] })), _jsx("h3", { className: "bw-details-service", children: pickLocaleField(selectedService, 'name', locale) ?? selectedService.name }), pickLocaleField(selectedService, 'description', locale) ?? selectedService.description ? (_jsx("p", { className: "bw-details-desc", children: pickLocaleField(selectedService, 'description', locale) ?? selectedService.description })) : null, _jsxs("div", { className: "bw-details-meta", children: [isReschedule && rescheduleContext ? (_jsxs("div", { className: "bw-details-meta-row bw-details-meta-row--strike", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(CalendarIcon, {}) }), _jsxs("div", { className: "bw-details-meta-text", children: [_jsx("span", { className: "bw-details-meta-label", children: t('summaryFormerTime') }), _jsx("span", { className: "bw-details-meta-value", children: formatFormerSlot(rescheduleContext.formerStartTime, rescheduleContext.formerEndTime, intlLocale) })] })] })) : null, selectedDate && selectedSlot ? (_jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(CalendarIcon, {}) }), _jsxs("div", { className: "bw-details-meta-text", children: [_jsx("span", { className: "bw-details-meta-label", children: isReschedule ? t('summaryNewTime') : t('summaryWhen') }), _jsxs("span", { className: "bw-details-meta-value", children: [formatReadableDate(selectedDate, intlLocale), _jsx("br", {}), formatTimeLabel(selectedSlot.time, intlLocale), " \u2013 ", formatTimeLabel(selectedSlot.endTime, intlLocale)] })] })] })) : null, _jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(ClockIcon, {}) }), _jsx("span", { className: "bw-details-meta-value", children: formatDuration(selectedService.durationMinutes) })] }), _jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(UserIcon, {}) }), _jsx("span", { className: "bw-details-meta-value", children: selectedHeldStaff?.name ?? selectedStaff?.name ?? t('providerAny') })] }), _jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(GlobeIcon, {}) }), _jsx("span", { className: "bw-details-meta-value", children: selectedHeldStaff?.timezone ?? selectedStaff?.timezone ?? setup?.workspaceTimezone ?? setup?.staff?.[0]?.timezone ?? 'UTC' })] }), _jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", "aria-hidden": "true", children: "$" }), _jsxs("div", { className: "bw-details-meta-text", children: [_jsx("span", { className: "bw-details-meta-label", children: isQuoteLoading ? t('summaryCalculating') : t('summaryEstimatedTotal') }), _jsx("span", { className: "bw-details-meta-value", children: formatServicePrice(selectedService, quote, intlLocale) })] })] }), holdSecondsRemaining !== null ? (_jsxs("div", { className: "bw-details-meta-row bw-details-meta-row--hold", "aria-live": "polite", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(ClockIcon, {}) }), _jsxs("div", { className: "bw-details-meta-text", children: [_jsx("span", { className: "bw-details-meta-label", children: t('summaryReservationHold') }), _jsx("span", { className: "bw-details-meta-value bw-details-hold", children: formatHoldCountdown(holdSecondsRemaining) })] })] })) : null] })] })) : null }), _jsx("div", { className: "bw-details-form", children: _jsxs("div", { className: "bw-form bw-form--details", children: [serviceWarning ? _jsx(ServiceWarningCallout, { warning: serviceWarning }) : null, renderIntakeAndContact('bw-intake-d', '-d'), renderNotesField('bw-notes-d', isReschedule ? t('notesLabelReschedule') : t('notesLabel'), isReschedule ? t('notesPlaceholderReschedule') : t('notesPlaceholder')), error ? _jsx("div", { className: "bw-error", children: error }) : null, renderSubmitHelp(), !payment && (forcedState === 'payment-full' || forcedState === 'payment-deposit') ? (_jsx(PaymentPanel, { service: selectedService, mode: forcedState === 'payment-full' ? 'full' : 'deposit' })) : null, payment ? (_jsx(StripePaymentBlock, { payment: payment, appointmentId: paymentAppointmentId, onSuccess: () => {
1193
+ : t('btnConfirmBooking') }), !isSubmitting ? _jsx(ArrowRightIcon, {}) : null] }))] })] })] }) }) }) })] }) }), _jsxs("div", { className: "bw-details-view", children: [_jsx("div", { className: "bw-details-summary", children: selectedService ? (_jsxs(_Fragment, { children: [selectedServiceRequiresSlot ? (_jsxs("button", { type: "button", className: "bw-back-btn bw-back-btn--details", onClick: () => void handleBackToSlots(), children: [_jsx(ArrowLeftIcon, {}), _jsx("span", { children: t('backToDifferentTime') })] })) : (_jsxs("button", { type: "button", className: "bw-back-btn bw-back-btn--details", onClick: handleBackToServicePicker, children: [_jsx(ArrowLeftIcon, {}), _jsx("span", { children: t('backToDifferentService') })] })), _jsx("h3", { className: "bw-details-service", children: pickLocaleField(selectedService, 'name', locale) ??
1194
+ selectedService.name }), (pickLocaleField(selectedService, 'description', locale) ??
1195
+ selectedService.description) ? (_jsx("p", { className: "bw-details-desc", children: pickLocaleField(selectedService, 'description', locale) ?? selectedService.description })) : null, _jsxs("div", { className: "bw-details-meta", children: [isReschedule && rescheduleContext ? (_jsxs("div", { className: "bw-details-meta-row bw-details-meta-row--strike", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(CalendarIcon, {}) }), _jsxs("div", { className: "bw-details-meta-text", children: [_jsx("span", { className: "bw-details-meta-label", children: t('summaryFormerTime') }), _jsx("span", { className: "bw-details-meta-value", children: formatFormerSlot(rescheduleContext.formerStartTime, rescheduleContext.formerEndTime, intlLocale) })] })] })) : null, selectedDate && selectedSlot ? (_jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(CalendarIcon, {}) }), _jsxs("div", { className: "bw-details-meta-text", children: [_jsx("span", { className: "bw-details-meta-label", children: isReschedule
1196
+ ? t('summaryNewTime')
1197
+ : t('summaryWhen') }), _jsxs("span", { className: "bw-details-meta-value", children: [formatReadableDate(selectedDate, intlLocale), _jsx("br", {}), formatTimeLabel(selectedSlot.time, intlLocale), " \u2013", ' ', formatTimeLabel(selectedSlot.endTime, intlLocale)] })] })] })) : null, _jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(ClockIcon, {}) }), _jsx("span", { className: "bw-details-meta-value", children: formatDuration(selectedService.durationMinutes) })] }), _jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(UserIcon, {}) }), _jsx("span", { className: "bw-details-meta-value", children: selectedHeldStaff?.name ??
1198
+ selectedStaff?.name ??
1199
+ t('providerAny') })] }), _jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(GlobeIcon, {}) }), _jsx("span", { className: "bw-details-meta-value", children: selectedHeldStaff?.timezone ??
1200
+ selectedStaff?.timezone ??
1201
+ setup?.workspaceTimezone ??
1202
+ setup?.staff?.[0]?.timezone ??
1203
+ 'UTC' })] }), _jsxs("div", { className: "bw-details-meta-row", children: [_jsx("span", { className: "bw-details-meta-icon", "aria-hidden": "true", children: "$" }), _jsxs("div", { className: "bw-details-meta-text", children: [_jsx("span", { className: "bw-details-meta-label", children: isQuoteLoading
1204
+ ? t('summaryCalculating')
1205
+ : t('summaryEstimatedTotal') }), _jsx("span", { className: "bw-details-meta-value", children: formatServicePrice(selectedService, quote, intlLocale) })] })] }), holdSecondsRemaining !== null ? (_jsxs("div", { className: "bw-details-meta-row bw-details-meta-row--hold", "aria-live": "polite", children: [_jsx("span", { className: "bw-details-meta-icon", children: _jsx(ClockIcon, {}) }), _jsxs("div", { className: "bw-details-meta-text", children: [_jsx("span", { className: "bw-details-meta-label", children: t('summaryReservationHold') }), _jsx("span", { className: "bw-details-meta-value bw-details-hold", children: formatHoldCountdown(holdSecondsRemaining) })] })] })) : null] })] })) : null }), _jsx("div", { className: "bw-details-form", children: _jsxs("div", { className: "bw-form bw-form--details", children: [serviceWarning ? (_jsx(ServiceWarningCallout, { warning: serviceWarning })) : null, renderIntakeAndContact('bw-intake-d', '-d'), renderNotesField('bw-notes-d', isReschedule ? t('notesLabelReschedule') : t('notesLabel'), isReschedule
1206
+ ? t('notesPlaceholderReschedule')
1207
+ : t('notesPlaceholder')), error ? _jsx("div", { className: "bw-error", children: error }) : null, renderSubmitHelp(), !payment &&
1208
+ (forcedState === 'payment-full' ||
1209
+ forcedState === 'payment-deposit') ? (_jsx(PaymentPanel, { service: selectedService, mode: forcedState === 'payment-full' ? 'full' : 'deposit' })) : null, payment ? (_jsx(StripePaymentBlock, { payment: payment, appointmentId: paymentAppointmentId, onSuccess: () => {
1052
1210
  setSuccess(t('successPaymentReceived'));
1053
1211
  setPayment(null);
1054
1212
  }, onError: setError })) : (_jsxs("button", { type: "button", className: `bw-confirm-btn${!canSubmit || isQuoteLoading ? ' is-disabled' : ''}`, "aria-disabled": !canSubmit || isQuoteLoading, onClick: handleConfirmAttempt, children: [_jsx("span", { children: isSubmitting
@@ -1056,7 +1214,9 @@ export function BookingWidgetPanel({ title, description, mobileHeader, mode = 'c
1056
1214
  ? t('btnRescheduling')
1057
1215
  : t('btnBooking')
1058
1216
  : forcedState === 'payment-full'
1059
- ? t('btnPayAndConfirm', { price: formatPrice(selectedService, intlLocale) })
1217
+ ? t('btnPayAndConfirm', {
1218
+ price: formatPrice(selectedService, intlLocale),
1219
+ })
1060
1220
  : forcedState === 'payment-deposit'
1061
1221
  ? t('btnPayDepositAndConfirm')
1062
1222
  : isReschedule
@@ -1156,7 +1316,8 @@ function areRequiredIntakeFieldsComplete(form, responses, mode) {
1156
1316
  }
1157
1317
  function getSubmitBlockers({ selectedService, selectedServiceRequiresSlot, selectedDate, selectedSlot, holdId, isIntakeComplete, intakeResponses, selectedServicePath, quote, customerName, customerEmail, t, locale = DEFAULT_LOCALE, }) {
1158
1318
  const blockers = [];
1159
- if (selectedServiceRequiresSlot && (!selectedDate || !selectedSlot || !holdId)) {
1319
+ if (selectedServiceRequiresSlot &&
1320
+ (!selectedDate || !selectedSlot || !holdId)) {
1160
1321
  blockers.push(t('blockerDateTime'));
1161
1322
  }
1162
1323
  if (!customerName.trim())
@@ -1284,7 +1445,9 @@ function buildIntakeReviewRows(form, responses, servicePath, locale, t) {
1284
1445
  continue;
1285
1446
  for (const field of section.fields) {
1286
1447
  if (field.type === 'repeatable-group') {
1287
- const items = Array.isArray(responses[field.id]) ? responses[field.id] : [];
1448
+ const items = Array.isArray(responses[field.id])
1449
+ ? responses[field.id]
1450
+ : [];
1288
1451
  items.forEach((item, index) => {
1289
1452
  const record = item && typeof item === 'object' && !Array.isArray(item)
1290
1453
  ? item
@@ -1489,7 +1652,9 @@ function IntakeField({ field, value, onChange, idPrefix, }) {
1489
1652
  if (field.type === 'repeatable-group') {
1490
1653
  const items = Array.isArray(value) && value.length > 0 ? value : [{}];
1491
1654
  return (_jsxs("div", { className: "bw-field bw-field--wide bw-repeatable", children: [_jsx("label", { children: label }), items.map((item, index) => {
1492
- const record = item && typeof item === 'object' && !Array.isArray(item) ? item : {};
1655
+ const record = item && typeof item === 'object' && !Array.isArray(item)
1656
+ ? item
1657
+ : {};
1493
1658
  return (_jsxs("div", { className: "bw-repeatable-item", children: [_jsxs("div", { className: "bw-repeatable-head", children: [_jsx("span", { children: t('repeatItemLabel', { index: index + 1 }) }), items.length > 1 ? (_jsx("button", { type: "button", className: "bw-link-btn", onClick: () => onChange(items.filter((_, itemIndex) => itemIndex !== index)), children: t('repeatRemove') })) : null] }), _jsx("div", { className: "bw-form-fields", children: (field.fields ?? []).map((child) => (_jsx(IntakeField, { field: child, value: record[child.id], idPrefix: `${id}-${index}`, onChange: (nextValue) => {
1494
1659
  const nextItems = [...items];
1495
1660
  nextItems[index] = { ...record, [child.id]: nextValue };
@@ -1513,7 +1678,11 @@ function IntakeField({ field, value, onChange, idPrefix, }) {
1513
1678
  if (field.type === 'textarea') {
1514
1679
  return (_jsxs("div", { className: "bw-field bw-field--wide", children: [_jsxs("label", { htmlFor: id, children: [label, field.required ? _jsx("span", { className: "bw-required", children: " *" }) : null] }), _jsx("textarea", { id: id, rows: 3, required: field.required, value: stringValue, placeholder: field.placeholder, onChange: (event) => onChange(event.target.value) }), field.helpText ? _jsx("p", { className: "bw-help", children: field.helpText }) : null] }));
1515
1680
  }
1516
- return (_jsxs("div", { className: "bw-field", children: [_jsxs("label", { htmlFor: id, children: [label, field.required ? _jsx("span", { className: "bw-required", children: " *" }) : null] }), _jsx("input", { id: id, type: field.type === 'email' ? 'email' : field.type === 'number' ? 'number' : 'text', min: field.min, max: field.max, required: field.required, value: stringValue, placeholder: field.placeholder, onChange: (event) => {
1681
+ return (_jsxs("div", { className: "bw-field", children: [_jsxs("label", { htmlFor: id, children: [label, field.required ? _jsx("span", { className: "bw-required", children: " *" }) : null] }), _jsx("input", { id: id, type: field.type === 'email'
1682
+ ? 'email'
1683
+ : field.type === 'number'
1684
+ ? 'number'
1685
+ : 'text', min: field.min, max: field.max, required: field.required, value: stringValue, placeholder: field.placeholder, onChange: (event) => {
1517
1686
  onChange(field.type === 'number'
1518
1687
  ? event.target.value === ''
1519
1688
  ? ''
@@ -1744,7 +1913,8 @@ function buildCalendarDays(month) {
1744
1913
  });
1745
1914
  }
1746
1915
  function sameMonth(left, right) {
1747
- return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth();
1916
+ return (left.getFullYear() === right.getFullYear() &&
1917
+ left.getMonth() === right.getMonth());
1748
1918
  }
1749
1919
  function getMonthOptions(currentMonth, total, locale = 'en-US') {
1750
1920
  const start = startOfMonth(new Date());
@@ -1763,7 +1933,8 @@ function optionCurrentValue(date) {
1763
1933
  }
1764
1934
  function isDateInMonth(dateKey, month) {
1765
1935
  const date = new Date(`${dateKey}T00:00:00`);
1766
- return date.getMonth() === month.getMonth() && date.getFullYear() === month.getFullYear();
1936
+ return (date.getMonth() === month.getMonth() &&
1937
+ date.getFullYear() === month.getFullYear());
1767
1938
  }
1768
1939
  function findFirstAvailableDate(availabilityByDate, month) {
1769
1940
  const dates = [...availabilityByDate.entries()]