@idkwebsites/components 0.1.23 → 0.1.24
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.
- package/dist/index.cjs +1033 -435
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +1078 -438
- package/dist/index.js.map +1 -1
- package/dist/styles.css +7 -8
- package/dist/styles.css.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -118,7 +118,9 @@ function normalizeBaseUrl(apiUrl) {
|
|
|
118
118
|
async function apiRequest(config, path, options = {}) {
|
|
119
119
|
const url = `${normalizeBaseUrl(config.apiUrl)}${path.startsWith("/") ? path : `/${path}`}`;
|
|
120
120
|
const headers = new Headers(options.headers);
|
|
121
|
-
|
|
121
|
+
if (config.apiKey) {
|
|
122
|
+
headers.set("X-API-Key", config.apiKey);
|
|
123
|
+
}
|
|
122
124
|
if (!headers.has("Content-Type") && options.body) {
|
|
123
125
|
headers.set("Content-Type", "application/json");
|
|
124
126
|
}
|
|
@@ -285,6 +287,24 @@ function useCancelBooking() {
|
|
|
285
287
|
})
|
|
286
288
|
});
|
|
287
289
|
}
|
|
290
|
+
function usePrepareBookingPayment() {
|
|
291
|
+
const config = usePlatformConfig();
|
|
292
|
+
return (0, import_react_query6.useMutation)({
|
|
293
|
+
mutationFn: (input) => apiRequest(config, "/bookings/payment/prepare", {
|
|
294
|
+
method: "POST",
|
|
295
|
+
body: JSON.stringify(input)
|
|
296
|
+
})
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
function useFinalizeBookingPayment() {
|
|
300
|
+
const config = usePlatformConfig();
|
|
301
|
+
return (0, import_react_query6.useMutation)({
|
|
302
|
+
mutationFn: (input) => apiRequest(config, "/bookings/payment/finalize", {
|
|
303
|
+
method: "POST",
|
|
304
|
+
body: JSON.stringify(input)
|
|
305
|
+
})
|
|
306
|
+
});
|
|
307
|
+
}
|
|
288
308
|
function useBookingLookup(uid, enabled = true) {
|
|
289
309
|
const config = usePlatformConfig();
|
|
290
310
|
return (0, import_react_query6.useQuery)({
|
|
@@ -308,6 +328,7 @@ var addDays = (dateStr, days) => {
|
|
|
308
328
|
parsed.setDate(parsed.getDate() + days);
|
|
309
329
|
return parsed.toISOString().slice(0, 10);
|
|
310
330
|
};
|
|
331
|
+
var getDefaultLocationType = (service) => service?.locationOptions?.find((option) => option.isDefault)?.type || service?.locationOptions?.[0]?.type || "";
|
|
311
332
|
function useBookingWidget(options = {}) {
|
|
312
333
|
const {
|
|
313
334
|
services: servicesProp,
|
|
@@ -341,6 +362,8 @@ function useBookingWidget(options = {}) {
|
|
|
341
362
|
const { data: servicesData, isLoading: servicesLoading } = useServices(resolvedServicesQuery);
|
|
342
363
|
const { data: teamData } = useTeam(teamQuery);
|
|
343
364
|
const createBooking = useCreateBooking();
|
|
365
|
+
const prepareBookingPayment = usePrepareBookingPayment();
|
|
366
|
+
const finalizeBookingPayment = useFinalizeBookingPayment();
|
|
344
367
|
const [step, setStep] = (0, import_react6.useState)("service");
|
|
345
368
|
const [selectedService, setSelectedService] = (0, import_react6.useState)(null);
|
|
346
369
|
const [selectedStaff, setSelectedStaff] = (0, import_react6.useState)(null);
|
|
@@ -360,7 +383,14 @@ function useBookingWidget(options = {}) {
|
|
|
360
383
|
return new Date(now.getFullYear(), now.getMonth(), 1);
|
|
361
384
|
});
|
|
362
385
|
const [bookingError, setBookingError] = (0, import_react6.useState)(null);
|
|
363
|
-
const [
|
|
386
|
+
const [paymentSession, setPaymentSession] = (0, import_react6.useState)(null);
|
|
387
|
+
const [details, setDetails] = (0, import_react6.useState)({
|
|
388
|
+
name: "",
|
|
389
|
+
email: "",
|
|
390
|
+
phone: "",
|
|
391
|
+
notes: "",
|
|
392
|
+
locationType: ""
|
|
393
|
+
});
|
|
364
394
|
const servicesList = (0, import_react6.useMemo)(() => {
|
|
365
395
|
const list = servicesProp ? [...servicesProp] : servicesData?.services ? [...servicesData.services] : [];
|
|
366
396
|
return serviceFilter ? list.filter(serviceFilter) : list;
|
|
@@ -368,15 +398,24 @@ function useBookingWidget(options = {}) {
|
|
|
368
398
|
const categories = (0, import_react6.useMemo)(() => {
|
|
369
399
|
const set = /* @__PURE__ */ new Set();
|
|
370
400
|
servicesList.forEach((s) => {
|
|
371
|
-
|
|
401
|
+
const cat = s.category;
|
|
402
|
+
if (!cat) return;
|
|
403
|
+
const label = typeof cat === "string" ? cat : typeof cat === "object" && cat !== null && "name" in cat ? String(cat.name) : "";
|
|
404
|
+
if (label) set.add(label);
|
|
372
405
|
});
|
|
373
406
|
return Array.from(set).sort();
|
|
374
407
|
}, [servicesList]);
|
|
375
408
|
const filteredServices = (0, import_react6.useMemo)(() => {
|
|
376
409
|
const search = serviceSearch.trim().toLowerCase();
|
|
410
|
+
const resolveCategoryLabel2 = (cat) => {
|
|
411
|
+
if (typeof cat === "string") return cat;
|
|
412
|
+
if (cat && typeof cat === "object" && "name" in cat) return String(cat.name);
|
|
413
|
+
return "";
|
|
414
|
+
};
|
|
377
415
|
return servicesList.filter((service) => {
|
|
378
|
-
const
|
|
379
|
-
const
|
|
416
|
+
const catLabel = resolveCategoryLabel2(service.category);
|
|
417
|
+
const matchesSearch = !search || service.name.toLowerCase().includes(search) || (service.description || "").toLowerCase().includes(search) || catLabel.toLowerCase().includes(search);
|
|
418
|
+
const matchesCategory = categoryFilter === "all" ? true : catLabel === categoryFilter;
|
|
380
419
|
return matchesSearch && matchesCategory;
|
|
381
420
|
});
|
|
382
421
|
}, [servicesList, serviceSearch, categoryFilter]);
|
|
@@ -407,6 +446,9 @@ function useBookingWidget(options = {}) {
|
|
|
407
446
|
const hasMoreStaff = staffOptions.length > pagedStaff.length;
|
|
408
447
|
const requiresStaff = showStaffSelection || Boolean(selectedService?.requiresStaffSelection) || selectedService?.schedulingType === "customer-choice";
|
|
409
448
|
const canSkipStaff = !selectedService?.requiresStaffSelection;
|
|
449
|
+
const paymentRequired = Boolean(
|
|
450
|
+
selectedService?.effectivePaymentSettings && selectedService.effectivePaymentSettings.mode !== "none" && selectedService.effectivePaymentSettings.amountDueNow > 0
|
|
451
|
+
);
|
|
410
452
|
const totalSteps = requiresStaff ? 4 : 3;
|
|
411
453
|
const stepNumber = (() => {
|
|
412
454
|
if (step === "service") return 1;
|
|
@@ -595,6 +637,7 @@ function useBookingWidget(options = {}) {
|
|
|
595
637
|
setSelectedDate(null);
|
|
596
638
|
setSelectedTime(null);
|
|
597
639
|
setSelectedEndTime(null);
|
|
640
|
+
setPaymentSession(null);
|
|
598
641
|
setBookingError(null);
|
|
599
642
|
}, []);
|
|
600
643
|
(0, import_react6.useEffect)(() => {
|
|
@@ -604,6 +647,22 @@ function useBookingWidget(options = {}) {
|
|
|
604
647
|
if (matches) return;
|
|
605
648
|
setSelectedDate(availability.data.dates[0].date);
|
|
606
649
|
}, [step, availability.data, selectedDate]);
|
|
650
|
+
(0, import_react6.useEffect)(() => {
|
|
651
|
+
const defaultLocationType = getDefaultLocationType(selectedService);
|
|
652
|
+
setDetails((prev) => {
|
|
653
|
+
const stillValid = selectedService?.locationOptions?.some(
|
|
654
|
+
(option) => option.type === prev.locationType
|
|
655
|
+
);
|
|
656
|
+
if (!selectedService) {
|
|
657
|
+
return prev.locationType ? { ...prev, locationType: "" } : prev;
|
|
658
|
+
}
|
|
659
|
+
if (stillValid) return prev;
|
|
660
|
+
return {
|
|
661
|
+
...prev,
|
|
662
|
+
locationType: defaultLocationType
|
|
663
|
+
};
|
|
664
|
+
});
|
|
665
|
+
}, [selectedService]);
|
|
607
666
|
(0, import_react6.useEffect)(() => {
|
|
608
667
|
if (!selectedService) return;
|
|
609
668
|
if (datePickerMode === "calendar") {
|
|
@@ -639,6 +698,7 @@ function useBookingWidget(options = {}) {
|
|
|
639
698
|
setSelectedDate(null);
|
|
640
699
|
setSelectedTime(null);
|
|
641
700
|
setSelectedEndTime(null);
|
|
701
|
+
setPaymentSession(null);
|
|
642
702
|
setBookingError(null);
|
|
643
703
|
if (autoAdvanceOnSelect) {
|
|
644
704
|
setStep("time");
|
|
@@ -651,12 +711,14 @@ function useBookingWidget(options = {}) {
|
|
|
651
711
|
setSelectedDate(date);
|
|
652
712
|
setSelectedTime(null);
|
|
653
713
|
setSelectedEndTime(null);
|
|
714
|
+
setPaymentSession(null);
|
|
654
715
|
setBookingError(null);
|
|
655
716
|
}, []);
|
|
656
717
|
const selectTime = (0, import_react6.useCallback)(
|
|
657
718
|
(time, endTime) => {
|
|
658
719
|
setSelectedTime(time);
|
|
659
720
|
setSelectedEndTime(endTime);
|
|
721
|
+
setPaymentSession(null);
|
|
660
722
|
setBookingError(null);
|
|
661
723
|
if (autoAdvanceOnSelect) {
|
|
662
724
|
setStep("details");
|
|
@@ -687,6 +749,9 @@ function useBookingWidget(options = {}) {
|
|
|
687
749
|
else if (step === "time") setStep(requiresStaff ? "staff" : "service");
|
|
688
750
|
else if (step === "details") setStep("time");
|
|
689
751
|
else if (step === "done") setStep("details");
|
|
752
|
+
if (step === "details") {
|
|
753
|
+
setPaymentSession(null);
|
|
754
|
+
}
|
|
690
755
|
setBookingError(null);
|
|
691
756
|
scrollToTop();
|
|
692
757
|
}, [step, requiresStaff, scrollToTop]);
|
|
@@ -698,9 +763,34 @@ function useBookingWidget(options = {}) {
|
|
|
698
763
|
setStep("service");
|
|
699
764
|
setSelectedService(null);
|
|
700
765
|
resetSelections();
|
|
701
|
-
|
|
766
|
+
setPaymentSession(null);
|
|
767
|
+
setDetails({ name: "", email: "", phone: "", notes: "", locationType: "" });
|
|
702
768
|
scrollToTop();
|
|
703
769
|
}, [resetSelections, scrollToTop]);
|
|
770
|
+
const finalizePayment = (0, import_react6.useCallback)(
|
|
771
|
+
async (paymentIntentId, bookingId) => {
|
|
772
|
+
if (!paymentSession?.bookingId || !paymentSession.paymentIntentId) {
|
|
773
|
+
setBookingError("Your payment session expired. Please start checkout again.");
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
try {
|
|
777
|
+
setBookingError(null);
|
|
778
|
+
const result = await finalizeBookingPayment.mutateAsync({
|
|
779
|
+
bookingId: bookingId || paymentSession.bookingId,
|
|
780
|
+
paymentIntentId: paymentIntentId || paymentSession.paymentIntentId
|
|
781
|
+
});
|
|
782
|
+
setPaymentSession(null);
|
|
783
|
+
setStep("done");
|
|
784
|
+
onSuccess?.(result?.booking);
|
|
785
|
+
scrollToTop();
|
|
786
|
+
} catch (error) {
|
|
787
|
+
setBookingError(
|
|
788
|
+
error instanceof Error ? error.message : "Payment completed, but we could not confirm the booking yet."
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
},
|
|
792
|
+
[finalizeBookingPayment, onSuccess, paymentSession, scrollToTop]
|
|
793
|
+
);
|
|
704
794
|
const submitBooking = (0, import_react6.useCallback)(
|
|
705
795
|
async (event) => {
|
|
706
796
|
event?.preventDefault();
|
|
@@ -711,8 +801,27 @@ function useBookingWidget(options = {}) {
|
|
|
711
801
|
setBookingError("Please select a valid time slot.");
|
|
712
802
|
return;
|
|
713
803
|
}
|
|
804
|
+
if (selectedService.locationOptions?.length && !details.locationType) {
|
|
805
|
+
setBookingError("Please choose a booking location.");
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
714
808
|
try {
|
|
715
809
|
setBookingError(null);
|
|
810
|
+
if (paymentRequired) {
|
|
811
|
+
const session = await prepareBookingPayment.mutateAsync({
|
|
812
|
+
serviceId: selectedService.id,
|
|
813
|
+
staffId: selectedStaff?.id,
|
|
814
|
+
startTime: start.toISOString(),
|
|
815
|
+
endTime: end.toISOString(),
|
|
816
|
+
customerName: details.name,
|
|
817
|
+
customerEmail: details.email,
|
|
818
|
+
customerPhone: details.phone || void 0,
|
|
819
|
+
customerNotes: details.notes || void 0,
|
|
820
|
+
locationType: details.locationType || void 0
|
|
821
|
+
});
|
|
822
|
+
setPaymentSession(session);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
716
825
|
const result = await createBooking.mutateAsync({
|
|
717
826
|
serviceId: selectedService.id,
|
|
718
827
|
staffId: selectedStaff?.id,
|
|
@@ -721,7 +830,8 @@ function useBookingWidget(options = {}) {
|
|
|
721
830
|
customerName: details.name,
|
|
722
831
|
customerEmail: details.email,
|
|
723
832
|
customerPhone: details.phone || void 0,
|
|
724
|
-
customerNotes: details.notes || void 0
|
|
833
|
+
customerNotes: details.notes || void 0,
|
|
834
|
+
locationType: details.locationType || void 0
|
|
725
835
|
});
|
|
726
836
|
setStep("done");
|
|
727
837
|
onSuccess?.(result?.booking);
|
|
@@ -732,7 +842,20 @@ function useBookingWidget(options = {}) {
|
|
|
732
842
|
);
|
|
733
843
|
}
|
|
734
844
|
},
|
|
735
|
-
[
|
|
845
|
+
[
|
|
846
|
+
selectedService,
|
|
847
|
+
selectedDate,
|
|
848
|
+
selectedTime,
|
|
849
|
+
selectedEndTime,
|
|
850
|
+
selectedStaff,
|
|
851
|
+
details,
|
|
852
|
+
createBooking,
|
|
853
|
+
onSuccess,
|
|
854
|
+
parseDateTime,
|
|
855
|
+
paymentRequired,
|
|
856
|
+
prepareBookingPayment,
|
|
857
|
+
scrollToTop
|
|
858
|
+
]
|
|
736
859
|
);
|
|
737
860
|
const handlePrevDates = (0, import_react6.useCallback)(() => {
|
|
738
861
|
if (availabilityStartDate <= todayString) return;
|
|
@@ -812,8 +935,10 @@ function useBookingWidget(options = {}) {
|
|
|
812
935
|
monthOptions,
|
|
813
936
|
isServicesLoading: servicesProp ? false : servicesLoading,
|
|
814
937
|
isAvailabilityLoading: availability.isLoading,
|
|
815
|
-
isSubmitting: createBooking.isPending,
|
|
938
|
+
isSubmitting: createBooking.isPending || prepareBookingPayment.isPending || finalizeBookingPayment.isPending,
|
|
816
939
|
bookingError,
|
|
940
|
+
paymentSession,
|
|
941
|
+
paymentRequired,
|
|
817
942
|
requiresStaff,
|
|
818
943
|
canSkipStaff,
|
|
819
944
|
serviceSearch,
|
|
@@ -839,6 +964,7 @@ function useBookingWidget(options = {}) {
|
|
|
839
964
|
continueFromStaff,
|
|
840
965
|
continueFromTime,
|
|
841
966
|
submitBooking,
|
|
967
|
+
finalizePayment,
|
|
842
968
|
goBack,
|
|
843
969
|
reset,
|
|
844
970
|
scrollToTop,
|
|
@@ -885,7 +1011,7 @@ function ServiceCard({
|
|
|
885
1011
|
service.duration,
|
|
886
1012
|
" min"
|
|
887
1013
|
] }),
|
|
888
|
-
service.category ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: service.category }) : null
|
|
1014
|
+
service.category ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: typeof service.category === "string" ? service.category : service.category.name }) : null
|
|
889
1015
|
] })
|
|
890
1016
|
]
|
|
891
1017
|
}
|
|
@@ -1209,8 +1335,250 @@ function ContactForm({
|
|
|
1209
1335
|
}
|
|
1210
1336
|
|
|
1211
1337
|
// src/components/BookingWidget.tsx
|
|
1338
|
+
var import_react9 = require("react");
|
|
1339
|
+
|
|
1340
|
+
// src/components/StripePaymentSection.tsx
|
|
1212
1341
|
var import_react8 = require("react");
|
|
1213
1342
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1343
|
+
var STRIPE_JS_SRC = "https://js.stripe.com/v3/";
|
|
1344
|
+
var stripeScriptPromise = null;
|
|
1345
|
+
var stripeInstances = /* @__PURE__ */ new Map();
|
|
1346
|
+
async function loadStripeClient(publishableKey) {
|
|
1347
|
+
if (typeof window === "undefined") {
|
|
1348
|
+
throw new Error("Stripe.js can only load in the browser.");
|
|
1349
|
+
}
|
|
1350
|
+
if (!stripeScriptPromise) {
|
|
1351
|
+
stripeScriptPromise = new Promise((resolve, reject) => {
|
|
1352
|
+
const existing = document.querySelector(`script[src="${STRIPE_JS_SRC}"]`);
|
|
1353
|
+
if (existing) {
|
|
1354
|
+
existing.addEventListener("load", () => resolve(), { once: true });
|
|
1355
|
+
existing.addEventListener("error", () => reject(new Error("Failed to load Stripe.js.")), {
|
|
1356
|
+
once: true
|
|
1357
|
+
});
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
const script = document.createElement("script");
|
|
1361
|
+
script.src = STRIPE_JS_SRC;
|
|
1362
|
+
script.async = true;
|
|
1363
|
+
script.onload = () => resolve();
|
|
1364
|
+
script.onerror = () => reject(new Error("Failed to load Stripe.js."));
|
|
1365
|
+
document.head.appendChild(script);
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
if (!stripeInstances.has(publishableKey)) {
|
|
1369
|
+
stripeInstances.set(
|
|
1370
|
+
publishableKey,
|
|
1371
|
+
(async () => {
|
|
1372
|
+
await stripeScriptPromise;
|
|
1373
|
+
const stripe = window.Stripe?.(publishableKey);
|
|
1374
|
+
if (!stripe) {
|
|
1375
|
+
throw new Error("Unable to initialize Stripe.");
|
|
1376
|
+
}
|
|
1377
|
+
return stripe;
|
|
1378
|
+
})()
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
return stripeInstances.get(publishableKey);
|
|
1382
|
+
}
|
|
1383
|
+
function formatCurrency(amount, currency) {
|
|
1384
|
+
return new Intl.NumberFormat("en-US", {
|
|
1385
|
+
style: "currency",
|
|
1386
|
+
currency: currency.toUpperCase()
|
|
1387
|
+
}).format(amount / 100);
|
|
1388
|
+
}
|
|
1389
|
+
function StripePaymentSection({
|
|
1390
|
+
bookingId,
|
|
1391
|
+
clientSecret,
|
|
1392
|
+
publishableKey,
|
|
1393
|
+
paymentIntentId,
|
|
1394
|
+
customerName,
|
|
1395
|
+
customerEmail,
|
|
1396
|
+
customerPhone,
|
|
1397
|
+
amountDueNow,
|
|
1398
|
+
fullAmount,
|
|
1399
|
+
currency,
|
|
1400
|
+
paymentTerms,
|
|
1401
|
+
isSubmitting = false,
|
|
1402
|
+
onPaymentAuthorized
|
|
1403
|
+
}) {
|
|
1404
|
+
const mountRef = (0, import_react8.useRef)(null);
|
|
1405
|
+
const stripeRef = (0, import_react8.useRef)(null);
|
|
1406
|
+
const elementsRef = (0, import_react8.useRef)(null);
|
|
1407
|
+
const paymentElementRef = (0, import_react8.useRef)(null);
|
|
1408
|
+
const [isReady, setIsReady] = (0, import_react8.useState)(false);
|
|
1409
|
+
const [isConfirming, setIsConfirming] = (0, import_react8.useState)(false);
|
|
1410
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
1411
|
+
const [statusMessage, setStatusMessage] = (0, import_react8.useState)(null);
|
|
1412
|
+
const dueNowLabel = (0, import_react8.useMemo)(
|
|
1413
|
+
() => formatCurrency(amountDueNow, currency),
|
|
1414
|
+
[amountDueNow, currency]
|
|
1415
|
+
);
|
|
1416
|
+
const fullAmountLabel = (0, import_react8.useMemo)(
|
|
1417
|
+
() => formatCurrency(fullAmount, currency),
|
|
1418
|
+
[currency, fullAmount]
|
|
1419
|
+
);
|
|
1420
|
+
(0, import_react8.useEffect)(() => {
|
|
1421
|
+
let cancelled = false;
|
|
1422
|
+
const mountElement = async () => {
|
|
1423
|
+
if (!mountRef.current) return;
|
|
1424
|
+
setIsReady(false);
|
|
1425
|
+
setError(null);
|
|
1426
|
+
setStatusMessage(null);
|
|
1427
|
+
try {
|
|
1428
|
+
const stripe = await loadStripeClient(publishableKey);
|
|
1429
|
+
if (cancelled || !mountRef.current) return;
|
|
1430
|
+
stripeRef.current = stripe;
|
|
1431
|
+
const elements = stripe.elements({
|
|
1432
|
+
clientSecret,
|
|
1433
|
+
appearance: {
|
|
1434
|
+
theme: "stripe",
|
|
1435
|
+
labels: "above",
|
|
1436
|
+
variables: {
|
|
1437
|
+
colorPrimary: "#111827",
|
|
1438
|
+
colorText: "#111827",
|
|
1439
|
+
borderRadius: "12px"
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
elementsRef.current = elements;
|
|
1444
|
+
const paymentElement = elements.create("payment", {
|
|
1445
|
+
layout: "tabs",
|
|
1446
|
+
wallets: {
|
|
1447
|
+
applePay: "auto",
|
|
1448
|
+
googlePay: "auto"
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
paymentElementRef.current = paymentElement;
|
|
1452
|
+
paymentElement.mount(mountRef.current);
|
|
1453
|
+
setIsReady(true);
|
|
1454
|
+
} catch (mountError) {
|
|
1455
|
+
setError(
|
|
1456
|
+
mountError instanceof Error ? mountError.message : "Unable to load secure payment fields."
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
void mountElement();
|
|
1461
|
+
return () => {
|
|
1462
|
+
cancelled = true;
|
|
1463
|
+
try {
|
|
1464
|
+
paymentElementRef.current?.destroy?.();
|
|
1465
|
+
paymentElementRef.current?.unmount();
|
|
1466
|
+
} catch {
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
1469
|
+
}, [clientSecret, publishableKey]);
|
|
1470
|
+
const handleConfirm = async () => {
|
|
1471
|
+
if (!stripeRef.current || !elementsRef.current) {
|
|
1472
|
+
setError("Secure payment fields are still loading. Please try again.");
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
setIsConfirming(true);
|
|
1476
|
+
setError(null);
|
|
1477
|
+
setStatusMessage(null);
|
|
1478
|
+
try {
|
|
1479
|
+
const result = await stripeRef.current.confirmPayment({
|
|
1480
|
+
elements: elementsRef.current,
|
|
1481
|
+
redirect: "if_required",
|
|
1482
|
+
confirmParams: {
|
|
1483
|
+
return_url: window.location.href,
|
|
1484
|
+
payment_method_data: {
|
|
1485
|
+
billing_details: {
|
|
1486
|
+
name: customerName,
|
|
1487
|
+
email: customerEmail,
|
|
1488
|
+
phone: customerPhone || void 0
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
});
|
|
1493
|
+
if (result.error?.message) {
|
|
1494
|
+
setError(result.error.message);
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
if (result.paymentIntent?.status === "succeeded") {
|
|
1498
|
+
await onPaymentAuthorized(result.paymentIntent.id, bookingId);
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
setStatusMessage("Your payment is processing. We will finalize the booking as soon as Stripe confirms it.");
|
|
1502
|
+
} catch (confirmError) {
|
|
1503
|
+
setError(
|
|
1504
|
+
confirmError instanceof Error ? confirmError.message : "Unable to confirm payment. Please try again."
|
|
1505
|
+
);
|
|
1506
|
+
} finally {
|
|
1507
|
+
setIsConfirming(false);
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1511
|
+
"div",
|
|
1512
|
+
{
|
|
1513
|
+
style: {
|
|
1514
|
+
marginTop: 16,
|
|
1515
|
+
padding: 16,
|
|
1516
|
+
borderRadius: 16,
|
|
1517
|
+
border: "1px solid rgba(15, 23, 42, 0.12)",
|
|
1518
|
+
background: "#fff"
|
|
1519
|
+
},
|
|
1520
|
+
children: [
|
|
1521
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", gap: 12, flexWrap: "wrap" }, children: [
|
|
1522
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
1523
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-booking__summary-label", children: "Payment due now" }),
|
|
1524
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: { margin: "4px 0 0", fontWeight: 700 }, children: dueNowLabel })
|
|
1525
|
+
] }),
|
|
1526
|
+
fullAmount > amountDueNow ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-booking__summary-label", children: "Full service price" }),
|
|
1528
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: { margin: "4px 0 0", fontWeight: 600 }, children: fullAmountLabel })
|
|
1529
|
+
] }) : null
|
|
1530
|
+
] }),
|
|
1531
|
+
paymentTerms ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1532
|
+
"div",
|
|
1533
|
+
{
|
|
1534
|
+
style: {
|
|
1535
|
+
marginTop: 12,
|
|
1536
|
+
padding: 12,
|
|
1537
|
+
borderRadius: 12,
|
|
1538
|
+
background: "rgba(15, 23, 42, 0.04)",
|
|
1539
|
+
whiteSpace: "pre-line",
|
|
1540
|
+
fontSize: 14,
|
|
1541
|
+
lineHeight: 1.5
|
|
1542
|
+
},
|
|
1543
|
+
children: paymentTerms
|
|
1544
|
+
}
|
|
1545
|
+
) : null,
|
|
1546
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1547
|
+
"div",
|
|
1548
|
+
{
|
|
1549
|
+
style: {
|
|
1550
|
+
marginTop: 12,
|
|
1551
|
+
padding: 12,
|
|
1552
|
+
borderRadius: 12,
|
|
1553
|
+
border: "1px solid rgba(15, 23, 42, 0.12)",
|
|
1554
|
+
background: "#fff"
|
|
1555
|
+
},
|
|
1556
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: mountRef })
|
|
1557
|
+
}
|
|
1558
|
+
),
|
|
1559
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-booking__error", style: { marginTop: 12 }, children: error }) : null,
|
|
1560
|
+
statusMessage ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-state", style: { marginTop: 12 }, children: statusMessage }) : null,
|
|
1561
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between", gap: 12, flexWrap: "wrap" }, children: [
|
|
1562
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-booking__summary-label", children: "Powered by Stripe. Wallets and local payment methods appear automatically when available." }),
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1564
|
+
"button",
|
|
1565
|
+
{
|
|
1566
|
+
type: "button",
|
|
1567
|
+
className: "idk-button",
|
|
1568
|
+
disabled: !isReady || isSubmitting || isConfirming,
|
|
1569
|
+
onClick: handleConfirm,
|
|
1570
|
+
children: isSubmitting || isConfirming ? "Processing..." : `Pay ${dueNowLabel} and confirm`
|
|
1571
|
+
}
|
|
1572
|
+
)
|
|
1573
|
+
] }),
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "hidden", value: paymentIntentId, readOnly: true })
|
|
1575
|
+
]
|
|
1576
|
+
}
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// src/components/BookingWidget.tsx
|
|
1581
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1214
1582
|
var parseDateOnly2 = (value) => {
|
|
1215
1583
|
if (!value) return null;
|
|
1216
1584
|
const normalized = value.includes("T") ? value.split("T")[0] : value;
|
|
@@ -1239,7 +1607,7 @@ function BookingWidget({
|
|
|
1239
1607
|
disabledWeekdays = [],
|
|
1240
1608
|
...hookProps
|
|
1241
1609
|
}) {
|
|
1242
|
-
const widgetRef = (0,
|
|
1610
|
+
const widgetRef = (0, import_react9.useRef)(null);
|
|
1243
1611
|
const bw = useBookingWidget({
|
|
1244
1612
|
...hookProps,
|
|
1245
1613
|
enableServiceSearch,
|
|
@@ -1258,28 +1626,31 @@ function BookingWidget({
|
|
|
1258
1626
|
const disabledWeekdaysSet = new Set(disabledWeekdays);
|
|
1259
1627
|
const shouldPaginate = showServicePagination ?? bw.hasMoreServices;
|
|
1260
1628
|
const shouldPaginateStaff = showStaffPagination ?? bw.hasMoreStaff;
|
|
1629
|
+
const selectedLocationOption = bw.selectedService?.locationOptions?.find(
|
|
1630
|
+
(option) => option.type === bw.details.locationType
|
|
1631
|
+
);
|
|
1261
1632
|
if (bw.isServicesLoading) {
|
|
1262
|
-
return /* @__PURE__ */ (0,
|
|
1633
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "Loading booking options..." });
|
|
1263
1634
|
}
|
|
1264
1635
|
if (!bw.servicesList.length) {
|
|
1265
|
-
return /* @__PURE__ */ (0,
|
|
1636
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "No services available." });
|
|
1266
1637
|
}
|
|
1267
|
-
return /* @__PURE__ */ (0,
|
|
1268
|
-
/* @__PURE__ */ (0,
|
|
1269
|
-
/* @__PURE__ */ (0,
|
|
1270
|
-
/* @__PURE__ */ (0,
|
|
1271
|
-
subtitle ? /* @__PURE__ */ (0,
|
|
1638
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref: widgetRef, className: ["idk-booking", className].filter(Boolean).join(" "), children: [
|
|
1639
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__header", children: [
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__title-wrap", children: [
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "idk-booking__title", children: title }),
|
|
1642
|
+
subtitle ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "idk-booking__subtitle", children: subtitle }) : null
|
|
1272
1643
|
] }),
|
|
1273
|
-
bw.step !== "service" && bw.step !== "done" ? /* @__PURE__ */ (0,
|
|
1644
|
+
bw.step !== "service" && bw.step !== "done" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "idk-link", onClick: bw.goBack, children: "Back" }) : null
|
|
1274
1645
|
] }),
|
|
1275
|
-
bw.step !== "done" ? /* @__PURE__ */ (0,
|
|
1276
|
-
/* @__PURE__ */ (0,
|
|
1646
|
+
bw.step !== "done" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__progress", children: [
|
|
1647
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
|
|
1277
1648
|
"Step ",
|
|
1278
1649
|
bw.stepNumber,
|
|
1279
1650
|
" of ",
|
|
1280
1651
|
bw.totalSteps
|
|
1281
1652
|
] }),
|
|
1282
|
-
/* @__PURE__ */ (0,
|
|
1653
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-booking__bar", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1283
1654
|
"div",
|
|
1284
1655
|
{
|
|
1285
1656
|
className: "idk-booking__bar-fill",
|
|
@@ -1287,10 +1658,10 @@ function BookingWidget({
|
|
|
1287
1658
|
}
|
|
1288
1659
|
) })
|
|
1289
1660
|
] }) : null,
|
|
1290
|
-
bw.step === "service" && /* @__PURE__ */ (0,
|
|
1291
|
-
/* @__PURE__ */ (0,
|
|
1292
|
-
enableServiceSearch || enableCategoryFilter ? /* @__PURE__ */ (0,
|
|
1293
|
-
enableServiceSearch ? /* @__PURE__ */ (0,
|
|
1661
|
+
bw.step === "service" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__step", children: [
|
|
1662
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { children: "Select a service" }),
|
|
1663
|
+
enableServiceSearch || enableCategoryFilter ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__filters", children: [
|
|
1664
|
+
enableServiceSearch ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1294
1665
|
"input",
|
|
1295
1666
|
{
|
|
1296
1667
|
type: "search",
|
|
@@ -1299,19 +1670,19 @@ function BookingWidget({
|
|
|
1299
1670
|
placeholder: serviceSearchPlaceholder
|
|
1300
1671
|
}
|
|
1301
1672
|
) : null,
|
|
1302
|
-
enableCategoryFilter && bw.categories.length > 0 ? /* @__PURE__ */ (0,
|
|
1673
|
+
enableCategoryFilter && bw.categories.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1303
1674
|
"select",
|
|
1304
1675
|
{
|
|
1305
1676
|
value: bw.categoryFilter,
|
|
1306
1677
|
onChange: (event) => bw.setCategoryFilter(event.target.value),
|
|
1307
1678
|
children: [
|
|
1308
|
-
/* @__PURE__ */ (0,
|
|
1309
|
-
bw.categories.map((category) => /* @__PURE__ */ (0,
|
|
1679
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "all", children: categoryLabel }),
|
|
1680
|
+
bw.categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: category, children: category }, category))
|
|
1310
1681
|
]
|
|
1311
1682
|
}
|
|
1312
1683
|
) : null
|
|
1313
1684
|
] }) : null,
|
|
1314
|
-
bw.filteredServices.length === 0 ? /* @__PURE__ */ (0,
|
|
1685
|
+
bw.filteredServices.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "No services match your filters." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1315
1686
|
"div",
|
|
1316
1687
|
{
|
|
1317
1688
|
className: [
|
|
@@ -1324,27 +1695,27 @@ function BookingWidget({
|
|
|
1324
1695
|
},
|
|
1325
1696
|
children: bw.pagedServices.map((service) => {
|
|
1326
1697
|
const isSelected = bw.selectedService?.id === service.id;
|
|
1327
|
-
return /* @__PURE__ */ (0,
|
|
1698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1328
1699
|
"button",
|
|
1329
1700
|
{
|
|
1330
1701
|
type: "button",
|
|
1331
1702
|
className: ["idk-card", "idk-card--clickable", isSelected ? "is-active" : ""].join(" "),
|
|
1332
1703
|
onClick: () => bw.selectService(service),
|
|
1333
1704
|
children: [
|
|
1334
|
-
/* @__PURE__ */ (0,
|
|
1335
|
-
/* @__PURE__ */ (0,
|
|
1336
|
-
/* @__PURE__ */ (0,
|
|
1337
|
-
isSelected ? /* @__PURE__ */ (0,
|
|
1338
|
-
typeof service.price === "number" && service.price > 0 ? /* @__PURE__ */ (0,
|
|
1705
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-card__header", children: [
|
|
1706
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-card__title", children: service.name }),
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "idk-card__aside", children: [
|
|
1708
|
+
isSelected ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-card__check", children: "\u2713" }) : null,
|
|
1709
|
+
typeof service.price === "number" && service.price > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "idk-card__price", children: [
|
|
1339
1710
|
"$",
|
|
1340
1711
|
(service.price / 100).toFixed(2)
|
|
1341
1712
|
] }) : null
|
|
1342
1713
|
] })
|
|
1343
1714
|
] }),
|
|
1344
|
-
service.description ? /* @__PURE__ */ (0,
|
|
1345
|
-
/* @__PURE__ */ (0,
|
|
1346
|
-
/* @__PURE__ */ (0,
|
|
1347
|
-
service.category ? /* @__PURE__ */ (0,
|
|
1715
|
+
service.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-card__description", children: service.description }) : null,
|
|
1716
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-card__meta", children: [
|
|
1717
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: bw.formatDuration(service.duration) }),
|
|
1718
|
+
service.category ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-pill", children: typeof service.category === "string" ? service.category : service.category.name }) : null
|
|
1348
1719
|
] })
|
|
1349
1720
|
]
|
|
1350
1721
|
},
|
|
@@ -1353,8 +1724,8 @@ function BookingWidget({
|
|
|
1353
1724
|
})
|
|
1354
1725
|
}
|
|
1355
1726
|
),
|
|
1356
|
-
/* @__PURE__ */ (0,
|
|
1357
|
-
shouldPaginate ? /* @__PURE__ */ (0,
|
|
1727
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__actions", children: [
|
|
1728
|
+
shouldPaginate ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1358
1729
|
"button",
|
|
1359
1730
|
{
|
|
1360
1731
|
type: "button",
|
|
@@ -1364,12 +1735,12 @@ function BookingWidget({
|
|
|
1364
1735
|
children: bw.hasMoreServices ? "Show more services" : "All services shown"
|
|
1365
1736
|
}
|
|
1366
1737
|
) : null,
|
|
1367
|
-
bw.selectedService && !hookProps.autoAdvanceOnSelect ? /* @__PURE__ */ (0,
|
|
1738
|
+
bw.selectedService && !hookProps.autoAdvanceOnSelect ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "idk-button", onClick: bw.continueFromService, children: "Continue" }) : null
|
|
1368
1739
|
] })
|
|
1369
1740
|
] }),
|
|
1370
|
-
bw.step === "staff" && /* @__PURE__ */ (0,
|
|
1371
|
-
/* @__PURE__ */ (0,
|
|
1372
|
-
bw.staffOptions.length === 0 ? /* @__PURE__ */ (0,
|
|
1741
|
+
bw.step === "staff" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__step", children: [
|
|
1742
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { children: "Select a team member" }),
|
|
1743
|
+
bw.staffOptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "No team members available." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1373
1744
|
"div",
|
|
1374
1745
|
{
|
|
1375
1746
|
className: [
|
|
@@ -1378,34 +1749,34 @@ function BookingWidget({
|
|
|
1378
1749
|
].join(" "),
|
|
1379
1750
|
style: staffLayout === "grid" ? { gridTemplateColumns: `repeat(${Math.max(1, staffColumns)}, minmax(0, 1fr))` } : void 0,
|
|
1380
1751
|
children: [
|
|
1381
|
-
(hookProps.showAnyStaffOption ?? true) && bw.canSkipStaff ? /* @__PURE__ */ (0,
|
|
1752
|
+
(hookProps.showAnyStaffOption ?? true) && bw.canSkipStaff ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1382
1753
|
"button",
|
|
1383
1754
|
{
|
|
1384
1755
|
type: "button",
|
|
1385
1756
|
className: ["idk-card", "idk-card--clickable", !bw.selectedStaff ? "is-active" : ""].join(" "),
|
|
1386
1757
|
onClick: () => bw.selectStaff(null),
|
|
1387
1758
|
children: [
|
|
1388
|
-
/* @__PURE__ */ (0,
|
|
1389
|
-
/* @__PURE__ */ (0,
|
|
1390
|
-
/* @__PURE__ */ (0,
|
|
1759
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-card__header", children: [
|
|
1760
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-card__title", children: "Any Available" }),
|
|
1761
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-card__badge", children: "Recommended" })
|
|
1391
1762
|
] }),
|
|
1392
|
-
/* @__PURE__ */ (0,
|
|
1763
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "idk-card__description", children: "We'll match you with the first available stylist." })
|
|
1393
1764
|
]
|
|
1394
1765
|
},
|
|
1395
1766
|
"any-staff"
|
|
1396
1767
|
) : null,
|
|
1397
|
-
bw.pagedStaff.map((staff) => /* @__PURE__ */ (0,
|
|
1768
|
+
bw.pagedStaff.map((staff) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1398
1769
|
"button",
|
|
1399
1770
|
{
|
|
1400
1771
|
type: "button",
|
|
1401
1772
|
className: ["idk-card", "idk-card--clickable", bw.selectedStaff?.id === staff.id ? "is-active" : ""].join(" "),
|
|
1402
1773
|
onClick: () => bw.selectStaff(staff),
|
|
1403
|
-
children: /* @__PURE__ */ (0,
|
|
1404
|
-
/* @__PURE__ */ (0,
|
|
1405
|
-
/* @__PURE__ */ (0,
|
|
1406
|
-
/* @__PURE__ */ (0,
|
|
1407
|
-
/* @__PURE__ */ (0,
|
|
1408
|
-
staff.bio ? /* @__PURE__ */ (0,
|
|
1774
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-team__card", children: [
|
|
1775
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-team__avatar", children: staff.photo?.url ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: staff.photo.url, alt: staff.name }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: staff.name.split(" ").map((part) => part[0]).slice(0, 2).join("").toUpperCase() }) }),
|
|
1776
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
1777
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-card__title", children: staff.name }),
|
|
1778
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-card__meta", children: staff.role || "Staff" }),
|
|
1779
|
+
staff.bio ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "idk-card__description", children: renderLinkedText(staff.bio) }) : null
|
|
1409
1780
|
] })
|
|
1410
1781
|
] })
|
|
1411
1782
|
},
|
|
@@ -1414,8 +1785,8 @@ function BookingWidget({
|
|
|
1414
1785
|
]
|
|
1415
1786
|
}
|
|
1416
1787
|
),
|
|
1417
|
-
/* @__PURE__ */ (0,
|
|
1418
|
-
shouldPaginateStaff ? /* @__PURE__ */ (0,
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__actions", children: [
|
|
1789
|
+
shouldPaginateStaff ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1419
1790
|
"button",
|
|
1420
1791
|
{
|
|
1421
1792
|
type: "button",
|
|
@@ -1425,7 +1796,7 @@ function BookingWidget({
|
|
|
1425
1796
|
children: bw.hasMoreStaff ? "Show more staff" : bw.staffPage > 1 ? "Show fewer staff" : "All staff shown"
|
|
1426
1797
|
}
|
|
1427
1798
|
) : null,
|
|
1428
|
-
bw.canSkipStaff ? /* @__PURE__ */ (0,
|
|
1799
|
+
bw.canSkipStaff ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1429
1800
|
"button",
|
|
1430
1801
|
{
|
|
1431
1802
|
type: "button",
|
|
@@ -1437,7 +1808,7 @@ function BookingWidget({
|
|
|
1437
1808
|
children: "Continue without selecting"
|
|
1438
1809
|
}
|
|
1439
1810
|
) : null,
|
|
1440
|
-
!hookProps.autoAdvanceOnSelect ? /* @__PURE__ */ (0,
|
|
1811
|
+
!hookProps.autoAdvanceOnSelect ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1441
1812
|
"button",
|
|
1442
1813
|
{
|
|
1443
1814
|
type: "button",
|
|
@@ -1449,10 +1820,10 @@ function BookingWidget({
|
|
|
1449
1820
|
) : null
|
|
1450
1821
|
] })
|
|
1451
1822
|
] }),
|
|
1452
|
-
bw.step === "time" && /* @__PURE__ */ (0,
|
|
1453
|
-
/* @__PURE__ */ (0,
|
|
1454
|
-
bw.isAvailabilityLoading ? /* @__PURE__ */ (0,
|
|
1455
|
-
/* @__PURE__ */ (0,
|
|
1823
|
+
bw.step === "time" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__step", children: [
|
|
1824
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { children: "Select a time" }),
|
|
1825
|
+
bw.isAvailabilityLoading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "Loading availability..." }) : bw.availabilityDates.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: ["idk-availability", timeLayout === "split" ? "idk-availability--split" : ""].join(" "), children: [
|
|
1826
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-availability__dates", children: (hookProps.datePickerMode ?? "list") === "calendar" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1456
1827
|
"div",
|
|
1457
1828
|
{
|
|
1458
1829
|
className: [
|
|
@@ -1460,8 +1831,8 @@ function BookingWidget({
|
|
|
1460
1831
|
calendarVariant === "compact" ? "idk-calendar--compact" : ""
|
|
1461
1832
|
].filter(Boolean).join(" "),
|
|
1462
1833
|
children: [
|
|
1463
|
-
/* @__PURE__ */ (0,
|
|
1464
|
-
/* @__PURE__ */ (0,
|
|
1834
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-calendar__header", children: calendarVariant === "compact" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-calendar__header idk-calendar__header--compact", children: [
|
|
1835
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1465
1836
|
"button",
|
|
1466
1837
|
{
|
|
1467
1838
|
type: "button",
|
|
@@ -1472,16 +1843,16 @@ function BookingWidget({
|
|
|
1472
1843
|
children: "<"
|
|
1473
1844
|
}
|
|
1474
1845
|
),
|
|
1475
|
-
/* @__PURE__ */ (0,
|
|
1846
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1476
1847
|
"select",
|
|
1477
1848
|
{
|
|
1478
1849
|
className: "idk-calendar__month-select",
|
|
1479
1850
|
value: `${bw.calendarMonth.getFullYear()}-${String(bw.calendarMonth.getMonth() + 1).padStart(2, "0")}`,
|
|
1480
1851
|
onChange: (event) => bw.handleMonthSelect(event.target.value),
|
|
1481
|
-
children: bw.monthOptions.map((option) => /* @__PURE__ */ (0,
|
|
1852
|
+
children: bw.monthOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: option.value, children: option.label }, option.value))
|
|
1482
1853
|
}
|
|
1483
1854
|
),
|
|
1484
|
-
/* @__PURE__ */ (0,
|
|
1855
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1485
1856
|
"button",
|
|
1486
1857
|
{
|
|
1487
1858
|
type: "button",
|
|
@@ -1492,8 +1863,8 @@ function BookingWidget({
|
|
|
1492
1863
|
children: ">"
|
|
1493
1864
|
}
|
|
1494
1865
|
)
|
|
1495
|
-
] }) : /* @__PURE__ */ (0,
|
|
1496
|
-
/* @__PURE__ */ (0,
|
|
1866
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1867
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1497
1868
|
"button",
|
|
1498
1869
|
{
|
|
1499
1870
|
type: "button",
|
|
@@ -1503,8 +1874,8 @@ function BookingWidget({
|
|
|
1503
1874
|
children: "Prev"
|
|
1504
1875
|
}
|
|
1505
1876
|
),
|
|
1506
|
-
/* @__PURE__ */ (0,
|
|
1507
|
-
/* @__PURE__ */ (0,
|
|
1877
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-calendar__title", children: new Intl.DateTimeFormat("en-US", { month: "long", year: "numeric" }).format(bw.calendarMonth) }),
|
|
1878
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1508
1879
|
"button",
|
|
1509
1880
|
{
|
|
1510
1881
|
type: "button",
|
|
@@ -1515,8 +1886,8 @@ function BookingWidget({
|
|
|
1515
1886
|
}
|
|
1516
1887
|
)
|
|
1517
1888
|
] }) }),
|
|
1518
|
-
/* @__PURE__ */ (0,
|
|
1519
|
-
/* @__PURE__ */ (0,
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-calendar__weekdays", children: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((label) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: label }, label)) }),
|
|
1890
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-calendar__grid", children: bw.calendarDays.map((day) => {
|
|
1520
1891
|
const entry = bw.availabilityByDate.get(day.date);
|
|
1521
1892
|
const hasAvailable = entry?.slots?.some((slot) => slot.available) ?? false;
|
|
1522
1893
|
const dateValue = parseDateOnly2(day.date);
|
|
@@ -1527,7 +1898,7 @@ function BookingWidget({
|
|
|
1527
1898
|
const isBlocked = Boolean(entry) && !hasAvailable;
|
|
1528
1899
|
const isDisabled = !entry || isPast || isBeyondMax || isBlocked || isDisabledWeekday;
|
|
1529
1900
|
const isActive = bw.selectedDate === day.date;
|
|
1530
|
-
return /* @__PURE__ */ (0,
|
|
1901
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1531
1902
|
"button",
|
|
1532
1903
|
{
|
|
1533
1904
|
type: "button",
|
|
@@ -1542,22 +1913,22 @@ function BookingWidget({
|
|
|
1542
1913
|
disabled: isDisabled,
|
|
1543
1914
|
onClick: () => bw.selectDate(day.date),
|
|
1544
1915
|
children: [
|
|
1545
|
-
/* @__PURE__ */ (0,
|
|
1546
|
-
showFullyBookedLabel && isBlocked && day.isCurrentMonth ? /* @__PURE__ */ (0,
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: day.date.split("-")[2] }),
|
|
1917
|
+
showFullyBookedLabel && isBlocked && day.isCurrentMonth ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-calendar__label", children: calendarVariant === "compact" ? "\u2022" : "Fully booked" }) : null
|
|
1547
1918
|
]
|
|
1548
1919
|
},
|
|
1549
1920
|
day.date
|
|
1550
1921
|
);
|
|
1551
1922
|
}) }),
|
|
1552
|
-
showFullyBookedLabel && calendarVariant === "compact" && bw.hasBlockedInCalendarView ? /* @__PURE__ */ (0,
|
|
1553
|
-
/* @__PURE__ */ (0,
|
|
1923
|
+
showFullyBookedLabel && calendarVariant === "compact" && bw.hasBlockedInCalendarView ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-calendar__legend", children: [
|
|
1924
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-calendar__legend-dot", children: "\u2022" }),
|
|
1554
1925
|
" Fully booked"
|
|
1555
1926
|
] }) : null
|
|
1556
1927
|
]
|
|
1557
1928
|
}
|
|
1558
|
-
) : /* @__PURE__ */ (0,
|
|
1559
|
-
enableDatePaging ? /* @__PURE__ */ (0,
|
|
1560
|
-
/* @__PURE__ */ (0,
|
|
1929
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1930
|
+
enableDatePaging ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-availability__nav", children: [
|
|
1931
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1561
1932
|
"button",
|
|
1562
1933
|
{
|
|
1563
1934
|
type: "button",
|
|
@@ -1567,7 +1938,7 @@ function BookingWidget({
|
|
|
1567
1938
|
children: "Previous"
|
|
1568
1939
|
}
|
|
1569
1940
|
),
|
|
1570
|
-
/* @__PURE__ */ (0,
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1571
1942
|
"button",
|
|
1572
1943
|
{
|
|
1573
1944
|
type: "button",
|
|
@@ -1577,25 +1948,25 @@ function BookingWidget({
|
|
|
1577
1948
|
}
|
|
1578
1949
|
)
|
|
1579
1950
|
] }) : null,
|
|
1580
|
-
bw.availabilityDates.map((entry) => /* @__PURE__ */ (0,
|
|
1951
|
+
bw.availabilityDates.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1581
1952
|
"button",
|
|
1582
1953
|
{
|
|
1583
1954
|
type: "button",
|
|
1584
1955
|
className: entry.date === bw.selectedDate ? "is-active" : void 0,
|
|
1585
1956
|
onClick: () => bw.selectDate(entry.date),
|
|
1586
|
-
children: /* @__PURE__ */ (0,
|
|
1957
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-date-chip__day", children: bw.formatDateLabel(entry.date) })
|
|
1587
1958
|
},
|
|
1588
1959
|
entry.date
|
|
1589
1960
|
))
|
|
1590
1961
|
] }) }),
|
|
1591
|
-
/* @__PURE__ */ (0,
|
|
1592
|
-
/* @__PURE__ */ (0,
|
|
1593
|
-
/* @__PURE__ */ (0,
|
|
1594
|
-
/* @__PURE__ */ (0,
|
|
1962
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-availability__panel", children: [
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-availability__panel-header", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
1964
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-availability__panel-title", children: bw.selectedDate ? bw.formatDateHeading(bw.selectedDate) : bw.availabilityDates[0]?.date ? bw.formatDateHeading(bw.availabilityDates[0].date) : "Select a date" }),
|
|
1965
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-availability__panel-subtitle", children: bw.timeZoneLabel })
|
|
1595
1966
|
] }) }),
|
|
1596
|
-
/* @__PURE__ */ (0,
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-availability__slots", children: (() => {
|
|
1597
1968
|
const activeEntry = bw.selectedDate ? bw.availabilityDates.find((entry) => entry.date === bw.selectedDate) : bw.availabilityDates[0];
|
|
1598
|
-
if (!activeEntry) return /* @__PURE__ */ (0,
|
|
1969
|
+
if (!activeEntry) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "Select a date to see times." });
|
|
1599
1970
|
const availableSlots = (activeEntry.slots || []).filter((slot) => slot.available).sort((a, b) => {
|
|
1600
1971
|
const aTime = new Date(a.time).getTime() || Number.POSITIVE_INFINITY;
|
|
1601
1972
|
const bTime = new Date(b.time).getTime() || Number.POSITIVE_INFINITY;
|
|
@@ -1610,9 +1981,9 @@ function BookingWidget({
|
|
|
1610
1981
|
(slot, index) => index === 0 || slot.time !== availableSlots[index - 1].time || slot.endTime !== availableSlots[index - 1].endTime
|
|
1611
1982
|
);
|
|
1612
1983
|
if (slots.length === 0) {
|
|
1613
|
-
return /* @__PURE__ */ (0,
|
|
1984
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "No available times for this date." });
|
|
1614
1985
|
}
|
|
1615
|
-
return slots.map((slot, index) => /* @__PURE__ */ (0,
|
|
1986
|
+
return slots.map((slot, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1616
1987
|
"button",
|
|
1617
1988
|
{
|
|
1618
1989
|
type: "button",
|
|
@@ -1628,8 +1999,8 @@ function BookingWidget({
|
|
|
1628
1999
|
));
|
|
1629
2000
|
})() })
|
|
1630
2001
|
] })
|
|
1631
|
-
] }) : /* @__PURE__ */ (0,
|
|
1632
|
-
!hookProps.autoAdvanceOnSelect ? /* @__PURE__ */ (0,
|
|
2002
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "No availability found." }),
|
|
2003
|
+
!hookProps.autoAdvanceOnSelect ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-booking__actions", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1633
2004
|
"button",
|
|
1634
2005
|
{
|
|
1635
2006
|
type: "button",
|
|
@@ -1640,96 +2011,132 @@ function BookingWidget({
|
|
|
1640
2011
|
}
|
|
1641
2012
|
) }) : null
|
|
1642
2013
|
] }),
|
|
1643
|
-
bw.step === "details" && /* @__PURE__ */ (0,
|
|
1644
|
-
/* @__PURE__ */ (0,
|
|
1645
|
-
bw.bookingError ? /* @__PURE__ */ (0,
|
|
1646
|
-
bw.selectedService && bw.selectedTime ? /* @__PURE__ */ (0,
|
|
1647
|
-
/* @__PURE__ */ (0,
|
|
1648
|
-
/* @__PURE__ */ (0,
|
|
1649
|
-
/* @__PURE__ */ (0,
|
|
2014
|
+
bw.step === "details" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("form", { className: "idk-form", onSubmit: bw.submitBooking, children: [
|
|
2015
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { children: "Your details" }),
|
|
2016
|
+
bw.bookingError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-booking__error", children: bw.bookingError }) : null,
|
|
2017
|
+
bw.selectedService && bw.selectedTime ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__summary", children: [
|
|
2018
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2019
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-booking__summary-label", children: "Service" }),
|
|
2020
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { children: bw.selectedService.name })
|
|
1650
2021
|
] }),
|
|
1651
|
-
bw.selectedStaff ? /* @__PURE__ */ (0,
|
|
1652
|
-
/* @__PURE__ */ (0,
|
|
1653
|
-
/* @__PURE__ */ (0,
|
|
2022
|
+
bw.selectedStaff ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2023
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-booking__summary-label", children: "Staff" }),
|
|
2024
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { children: bw.selectedStaff.name })
|
|
1654
2025
|
] }) : null,
|
|
1655
|
-
/* @__PURE__ */ (0,
|
|
1656
|
-
/* @__PURE__ */ (0,
|
|
1657
|
-
/* @__PURE__ */ (0,
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2027
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-booking__summary-label", children: "When" }),
|
|
2028
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { children: [
|
|
1658
2029
|
bw.formatDateHeading(bw.selectedDate || ""),
|
|
1659
2030
|
" \xB7 ",
|
|
1660
2031
|
bw.formatTimeLabel(bw.selectedTime)
|
|
1661
2032
|
] })
|
|
1662
2033
|
] }),
|
|
1663
|
-
bw.selectedService.price ? /* @__PURE__ */ (0,
|
|
1664
|
-
/* @__PURE__ */ (0,
|
|
1665
|
-
/* @__PURE__ */ (0,
|
|
2034
|
+
bw.selectedService.price ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2035
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "idk-booking__summary-label", children: "Price" }),
|
|
2036
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { children: [
|
|
1666
2037
|
"$",
|
|
1667
2038
|
(bw.selectedService.price / 100).toFixed(2)
|
|
1668
2039
|
] })
|
|
1669
2040
|
] }) : null
|
|
1670
2041
|
] }) : null,
|
|
1671
|
-
/* @__PURE__ */ (0,
|
|
1672
|
-
/* @__PURE__ */ (0,
|
|
1673
|
-
/* @__PURE__ */ (0,
|
|
2042
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "idk-form__field", children: [
|
|
2043
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Name" }),
|
|
2044
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1674
2045
|
"input",
|
|
1675
2046
|
{
|
|
1676
2047
|
value: bw.details.name,
|
|
1677
2048
|
onChange: (event) => bw.setDetails((prev) => ({ ...prev, name: event.target.value })),
|
|
2049
|
+
disabled: Boolean(bw.paymentSession),
|
|
1678
2050
|
required: true
|
|
1679
2051
|
}
|
|
1680
2052
|
)
|
|
1681
2053
|
] }),
|
|
1682
|
-
/* @__PURE__ */ (0,
|
|
1683
|
-
/* @__PURE__ */ (0,
|
|
1684
|
-
/* @__PURE__ */ (0,
|
|
2054
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "idk-form__field", children: [
|
|
2055
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Email" }),
|
|
2056
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1685
2057
|
"input",
|
|
1686
2058
|
{
|
|
1687
2059
|
type: "email",
|
|
1688
2060
|
value: bw.details.email,
|
|
1689
2061
|
onChange: (event) => bw.setDetails((prev) => ({ ...prev, email: event.target.value })),
|
|
2062
|
+
disabled: Boolean(bw.paymentSession),
|
|
1690
2063
|
required: true
|
|
1691
2064
|
}
|
|
1692
2065
|
)
|
|
1693
2066
|
] }),
|
|
1694
|
-
/* @__PURE__ */ (0,
|
|
1695
|
-
/* @__PURE__ */ (0,
|
|
1696
|
-
/* @__PURE__ */ (0,
|
|
2067
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "idk-form__field", children: [
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Phone" }),
|
|
2069
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1697
2070
|
"input",
|
|
1698
2071
|
{
|
|
1699
2072
|
value: bw.details.phone,
|
|
1700
|
-
onChange: (event) => bw.setDetails((prev) => ({ ...prev, phone: event.target.value }))
|
|
2073
|
+
onChange: (event) => bw.setDetails((prev) => ({ ...prev, phone: event.target.value })),
|
|
2074
|
+
disabled: Boolean(bw.paymentSession)
|
|
1701
2075
|
}
|
|
1702
2076
|
)
|
|
1703
2077
|
] }),
|
|
1704
|
-
/* @__PURE__ */ (0,
|
|
1705
|
-
/* @__PURE__ */ (0,
|
|
1706
|
-
/* @__PURE__ */ (0,
|
|
2078
|
+
bw.selectedService?.locationOptions?.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "idk-form__field", children: [
|
|
2079
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Location" }),
|
|
2080
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2081
|
+
"select",
|
|
2082
|
+
{
|
|
2083
|
+
value: bw.details.locationType,
|
|
2084
|
+
onChange: (event) => bw.setDetails((prev) => ({ ...prev, locationType: event.target.value })),
|
|
2085
|
+
disabled: Boolean(bw.paymentSession),
|
|
2086
|
+
required: true,
|
|
2087
|
+
children: bw.selectedService.locationOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: option.type, children: option.label }, option.type))
|
|
2088
|
+
}
|
|
2089
|
+
),
|
|
2090
|
+
selectedLocationOption?.details ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("small", { className: "idk-form__hint", children: selectedLocationOption.details }) : null
|
|
2091
|
+
] }) : null,
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "idk-form__field", children: [
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Notes" }),
|
|
2094
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1707
2095
|
"textarea",
|
|
1708
2096
|
{
|
|
1709
2097
|
rows: 4,
|
|
1710
2098
|
value: bw.details.notes,
|
|
1711
|
-
onChange: (event) => bw.setDetails((prev) => ({ ...prev, notes: event.target.value }))
|
|
2099
|
+
onChange: (event) => bw.setDetails((prev) => ({ ...prev, notes: event.target.value })),
|
|
2100
|
+
disabled: Boolean(bw.paymentSession)
|
|
1712
2101
|
}
|
|
1713
2102
|
)
|
|
1714
2103
|
] }),
|
|
1715
|
-
/* @__PURE__ */ (0,
|
|
2104
|
+
bw.paymentSession && bw.selectedService?.effectivePaymentSettings ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2105
|
+
StripePaymentSection,
|
|
2106
|
+
{
|
|
2107
|
+
bookingId: bw.paymentSession.bookingId,
|
|
2108
|
+
clientSecret: bw.paymentSession.clientSecret,
|
|
2109
|
+
publishableKey: bw.paymentSession.publishableKey,
|
|
2110
|
+
paymentIntentId: bw.paymentSession.paymentIntentId,
|
|
2111
|
+
customerName: bw.details.name,
|
|
2112
|
+
customerEmail: bw.details.email,
|
|
2113
|
+
customerPhone: bw.details.phone || void 0,
|
|
2114
|
+
amountDueNow: bw.selectedService.effectivePaymentSettings.amountDueNow,
|
|
2115
|
+
fullAmount: bw.selectedService.effectivePaymentSettings.fullAmount,
|
|
2116
|
+
currency: bw.selectedService.effectivePaymentSettings.currency,
|
|
2117
|
+
paymentTerms: bw.selectedService.effectivePaymentSettings.paymentTerms,
|
|
2118
|
+
isSubmitting: bw.isSubmitting,
|
|
2119
|
+
onPaymentAuthorized: bw.finalizePayment
|
|
2120
|
+
}
|
|
2121
|
+
) : null,
|
|
2122
|
+
!bw.paymentSession ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "submit", className: "idk-button", disabled: bw.isSubmitting, children: bw.isSubmitting ? bw.paymentRequired ? "Preparing payment..." : "Booking..." : bw.paymentRequired ? "Continue to payment" : "Confirm booking" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "Your details are locked while payment is in progress." })
|
|
1716
2123
|
] }),
|
|
1717
|
-
bw.step === "done" && /* @__PURE__ */ (0,
|
|
1718
|
-
/* @__PURE__ */ (0,
|
|
1719
|
-
/* @__PURE__ */ (0,
|
|
1720
|
-
/* @__PURE__ */ (0,
|
|
2124
|
+
bw.step === "done" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "idk-booking__done", children: [
|
|
2125
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-booking__done-title", children: "Booking confirmed!" }),
|
|
2126
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "idk-booking__done-text", children: "We'll send a confirmation email shortly with your appointment details." }),
|
|
2127
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "idk-link", onClick: bw.reset, children: "Book another appointment" })
|
|
1721
2128
|
] })
|
|
1722
2129
|
] });
|
|
1723
2130
|
}
|
|
1724
2131
|
|
|
1725
2132
|
// src/components/BookingWidgetPanel.tsx
|
|
1726
|
-
var
|
|
2133
|
+
var import_react10 = require("react");
|
|
1727
2134
|
var import_react_dom = require("react-dom");
|
|
1728
2135
|
var import_lucide_react = require("lucide-react");
|
|
1729
2136
|
var import_framer_motion = require("framer-motion");
|
|
1730
2137
|
var import_smooth_scrollbar = __toESM(require("smooth-scrollbar"), 1);
|
|
1731
2138
|
var import_overscroll = __toESM(require("smooth-scrollbar/plugins/overscroll"), 1);
|
|
1732
|
-
var
|
|
2139
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1733
2140
|
import_smooth_scrollbar.default.use(import_overscroll.default);
|
|
1734
2141
|
var DEFAULT_STEP_TITLES = [
|
|
1735
2142
|
"Schedule your visit",
|
|
@@ -1738,29 +2145,211 @@ var DEFAULT_STEP_TITLES = [
|
|
|
1738
2145
|
"Your details"
|
|
1739
2146
|
];
|
|
1740
2147
|
var DEFAULT_CATEGORY_ICONS = {
|
|
2148
|
+
// Hair & barber
|
|
1741
2149
|
hair: import_lucide_react.Scissors,
|
|
1742
2150
|
cut: import_lucide_react.Scissors,
|
|
1743
2151
|
barber: import_lucide_react.Scissors,
|
|
1744
2152
|
styling: import_lucide_react.Scissors,
|
|
1745
|
-
|
|
1746
|
-
|
|
2153
|
+
trim: import_lucide_react.Scissors,
|
|
2154
|
+
shave: import_lucide_react.Scissors,
|
|
2155
|
+
beard: import_lucide_react.Scissors,
|
|
2156
|
+
// Beauty & aesthetics
|
|
1747
2157
|
beauty: import_lucide_react.Sparkles,
|
|
2158
|
+
lash: import_lucide_react.Sparkles,
|
|
2159
|
+
brow: import_lucide_react.Sparkles,
|
|
2160
|
+
makeup: import_lucide_react.Sparkles,
|
|
2161
|
+
aestheti: import_lucide_react.Sparkles,
|
|
2162
|
+
glow: import_lucide_react.Sparkles,
|
|
2163
|
+
// Skin & facials
|
|
2164
|
+
skin: import_lucide_react.Droplets,
|
|
2165
|
+
facial: import_lucide_react.Droplets,
|
|
2166
|
+
complexion: import_lucide_react.Droplets,
|
|
2167
|
+
peel: import_lucide_react.Droplets,
|
|
2168
|
+
acne: import_lucide_react.Droplets,
|
|
2169
|
+
// Massage & spa
|
|
1748
2170
|
massage: import_lucide_react.Heart,
|
|
1749
2171
|
wellness: import_lucide_react.Heart,
|
|
1750
2172
|
spa: import_lucide_react.Heart,
|
|
2173
|
+
relax: import_lucide_react.Heart,
|
|
2174
|
+
swedish: import_lucide_react.Heart,
|
|
2175
|
+
deep: import_lucide_react.Heart,
|
|
2176
|
+
stone: import_lucide_react.Heart,
|
|
2177
|
+
// Nails
|
|
1751
2178
|
nail: import_lucide_react.Palette,
|
|
1752
|
-
color: import_lucide_react.Palette,
|
|
1753
2179
|
manicure: import_lucide_react.Palette,
|
|
1754
|
-
pedicure: import_lucide_react.Palette
|
|
2180
|
+
pedicure: import_lucide_react.Palette,
|
|
2181
|
+
gel: import_lucide_react.Palette,
|
|
2182
|
+
acrylic: import_lucide_react.Palette,
|
|
2183
|
+
// Fitness & training
|
|
2184
|
+
fitness: import_lucide_react.Dumbbell,
|
|
2185
|
+
training: import_lucide_react.Dumbbell,
|
|
2186
|
+
strength: import_lucide_react.Dumbbell,
|
|
2187
|
+
bootcamp: import_lucide_react.Dumbbell,
|
|
2188
|
+
gym: import_lucide_react.Dumbbell,
|
|
2189
|
+
weight: import_lucide_react.Dumbbell,
|
|
2190
|
+
yoga: import_lucide_react.Leaf,
|
|
2191
|
+
pilates: import_lucide_react.Leaf,
|
|
2192
|
+
stretch: import_lucide_react.Leaf,
|
|
2193
|
+
meditation: import_lucide_react.Leaf,
|
|
2194
|
+
mindful: import_lucide_react.Leaf,
|
|
2195
|
+
cardio: import_lucide_react.Bike,
|
|
2196
|
+
cycling: import_lucide_react.Bike,
|
|
2197
|
+
spin: import_lucide_react.Bike,
|
|
2198
|
+
dance: import_lucide_react.Footprints,
|
|
2199
|
+
ballet: import_lucide_react.Footprints,
|
|
2200
|
+
jazz: import_lucide_react.Footprints,
|
|
2201
|
+
hiit: import_lucide_react.Flame,
|
|
2202
|
+
crossfit: import_lucide_react.Flame,
|
|
2203
|
+
circuit: import_lucide_react.Flame,
|
|
2204
|
+
// Medical & therapy
|
|
2205
|
+
consult: import_lucide_react.Stethoscope,
|
|
2206
|
+
doctor: import_lucide_react.Stethoscope,
|
|
2207
|
+
exam: import_lucide_react.Stethoscope,
|
|
2208
|
+
checkup: import_lucide_react.Stethoscope,
|
|
2209
|
+
assessment: import_lucide_react.Stethoscope,
|
|
2210
|
+
inject: import_lucide_react.Syringe,
|
|
2211
|
+
botox: import_lucide_react.Syringe,
|
|
2212
|
+
filler: import_lucide_react.Syringe,
|
|
2213
|
+
iv: import_lucide_react.Syringe,
|
|
2214
|
+
therapy: import_lucide_react.Brain,
|
|
2215
|
+
counseling: import_lucide_react.Brain,
|
|
2216
|
+
coaching: import_lucide_react.Brain,
|
|
2217
|
+
mental: import_lucide_react.Brain,
|
|
2218
|
+
psycho: import_lucide_react.Brain,
|
|
2219
|
+
rehab: import_lucide_react.Activity,
|
|
2220
|
+
chiro: import_lucide_react.Activity,
|
|
2221
|
+
adjust: import_lucide_react.Activity,
|
|
2222
|
+
physio: import_lucide_react.Activity,
|
|
2223
|
+
recovery: import_lucide_react.Activity,
|
|
2224
|
+
sport: import_lucide_react.Activity,
|
|
2225
|
+
acupuncture: import_lucide_react.Zap,
|
|
2226
|
+
cupping: import_lucide_react.Zap,
|
|
2227
|
+
herbal: import_lucide_react.Pill,
|
|
2228
|
+
// Education & tutoring
|
|
2229
|
+
tutor: import_lucide_react.GraduationCap,
|
|
2230
|
+
lesson: import_lucide_react.GraduationCap,
|
|
2231
|
+
class: import_lucide_react.GraduationCap,
|
|
2232
|
+
course: import_lucide_react.GraduationCap,
|
|
2233
|
+
academic: import_lucide_react.GraduationCap,
|
|
2234
|
+
music: import_lucide_react.Music,
|
|
2235
|
+
guitar: import_lucide_react.Music,
|
|
2236
|
+
piano: import_lucide_react.Music,
|
|
2237
|
+
vocal: import_lucide_react.Music,
|
|
2238
|
+
drum: import_lucide_react.Music,
|
|
2239
|
+
singing: import_lucide_react.Music,
|
|
2240
|
+
prep: import_lucide_react.BookOpen,
|
|
2241
|
+
study: import_lucide_react.BookOpen,
|
|
2242
|
+
reading: import_lucide_react.BookOpen,
|
|
2243
|
+
// Pets
|
|
2244
|
+
dog: import_lucide_react.Dog,
|
|
2245
|
+
puppy: import_lucide_react.Dog,
|
|
2246
|
+
cat: import_lucide_react.Cat,
|
|
2247
|
+
kitten: import_lucide_react.Cat,
|
|
2248
|
+
pet: import_lucide_react.PawPrint,
|
|
2249
|
+
groom: import_lucide_react.PawPrint,
|
|
2250
|
+
brush: import_lucide_react.PawPrint,
|
|
2251
|
+
// Cleaning
|
|
2252
|
+
clean: import_lucide_react.SprayCan,
|
|
2253
|
+
saniti: import_lucide_react.SprayCan,
|
|
2254
|
+
disinfect: import_lucide_react.SprayCan,
|
|
2255
|
+
residential: import_lucide_react.Home,
|
|
2256
|
+
house: import_lucide_react.Home,
|
|
2257
|
+
apartment: import_lucide_react.Home,
|
|
2258
|
+
move: import_lucide_react.Home,
|
|
2259
|
+
laundry: import_lucide_react.Shirt,
|
|
2260
|
+
press: import_lucide_react.Shirt,
|
|
2261
|
+
// Auto & repair
|
|
2262
|
+
auto: import_lucide_react.Car,
|
|
2263
|
+
vehicle: import_lucide_react.Car,
|
|
2264
|
+
car: import_lucide_react.Car,
|
|
2265
|
+
detailing: import_lucide_react.Car,
|
|
2266
|
+
repair: import_lucide_react.Wrench,
|
|
2267
|
+
mechanic: import_lucide_react.Wrench,
|
|
2268
|
+
maintenance: import_lucide_react.Wrench,
|
|
2269
|
+
plumb: import_lucide_react.Wrench,
|
|
2270
|
+
electric: import_lucide_react.Wrench,
|
|
2271
|
+
handyman: import_lucide_react.Hammer,
|
|
2272
|
+
install: import_lucide_react.Hammer,
|
|
2273
|
+
assembl: import_lucide_react.Hammer,
|
|
2274
|
+
// Creative & photo
|
|
2275
|
+
photo: import_lucide_react.Camera,
|
|
2276
|
+
portrait: import_lucide_react.Camera,
|
|
2277
|
+
headshot: import_lucide_react.Camera,
|
|
2278
|
+
video: import_lucide_react.Video,
|
|
2279
|
+
film: import_lucide_react.Video,
|
|
2280
|
+
paint: import_lucide_react.Paintbrush,
|
|
2281
|
+
art: import_lucide_react.Paintbrush,
|
|
2282
|
+
drawing: import_lucide_react.Paintbrush,
|
|
2283
|
+
design: import_lucide_react.Paintbrush,
|
|
2284
|
+
tattoo: import_lucide_react.Pen,
|
|
2285
|
+
piercing: import_lucide_react.Pen,
|
|
2286
|
+
// Food & hospitality
|
|
2287
|
+
cook: import_lucide_react.ChefHat,
|
|
2288
|
+
chef: import_lucide_react.ChefHat,
|
|
2289
|
+
culinary: import_lucide_react.ChefHat,
|
|
2290
|
+
baking: import_lucide_react.ChefHat,
|
|
2291
|
+
coffee: import_lucide_react.Coffee,
|
|
2292
|
+
barista: import_lucide_react.Coffee,
|
|
2293
|
+
tea: import_lucide_react.Coffee,
|
|
2294
|
+
wine: import_lucide_react.Wine,
|
|
2295
|
+
tasting: import_lucide_react.Wine,
|
|
2296
|
+
sommelier: import_lucide_react.Wine,
|
|
2297
|
+
catering: import_lucide_react.UtensilsCrossed,
|
|
2298
|
+
meal: import_lucide_react.UtensilsCrossed,
|
|
2299
|
+
nutrition: import_lucide_react.UtensilsCrossed,
|
|
2300
|
+
diet: import_lucide_react.UtensilsCrossed,
|
|
2301
|
+
// Kids & family
|
|
2302
|
+
baby: import_lucide_react.Baby,
|
|
2303
|
+
infant: import_lucide_react.Baby,
|
|
2304
|
+
newborn: import_lucide_react.Baby,
|
|
2305
|
+
prenatal: import_lucide_react.Baby,
|
|
2306
|
+
postnatal: import_lucide_react.Baby,
|
|
2307
|
+
kids: import_lucide_react.Users,
|
|
2308
|
+
children: import_lucide_react.Users,
|
|
2309
|
+
family: import_lucide_react.Users,
|
|
2310
|
+
group: import_lucide_react.Users,
|
|
2311
|
+
// Wellness misc
|
|
2312
|
+
laser: import_lucide_react.Eye,
|
|
2313
|
+
contour: import_lucide_react.Sun,
|
|
2314
|
+
tan: import_lucide_react.Sun,
|
|
2315
|
+
wax: import_lucide_react.Wind,
|
|
2316
|
+
thread: import_lucide_react.Wind,
|
|
2317
|
+
sugaring: import_lucide_react.Wind,
|
|
2318
|
+
remov: import_lucide_react.Wind,
|
|
2319
|
+
aroma: import_lucide_react.Flower2,
|
|
2320
|
+
holistic: import_lucide_react.Flower2,
|
|
2321
|
+
essential: import_lucide_react.Flower2,
|
|
2322
|
+
outdoor: import_lucide_react.TreePine,
|
|
2323
|
+
hike: import_lucide_react.TreePine,
|
|
2324
|
+
adventure: import_lucide_react.TreePine,
|
|
2325
|
+
// General
|
|
2326
|
+
session: import_lucide_react.MessageCircle,
|
|
2327
|
+
workshop: import_lucide_react.MessageCircle,
|
|
2328
|
+
seminar: import_lucide_react.MessageCircle,
|
|
2329
|
+
package: import_lucide_react.Shield,
|
|
2330
|
+
membership: import_lucide_react.Shield,
|
|
2331
|
+
subscription: import_lucide_react.Shield,
|
|
2332
|
+
plan: import_lucide_react.Shield,
|
|
2333
|
+
care: import_lucide_react.HandHeart,
|
|
2334
|
+
support: import_lucide_react.HandHeart,
|
|
2335
|
+
assist: import_lucide_react.HandHeart,
|
|
2336
|
+
service: import_lucide_react.HandHeart
|
|
1755
2337
|
};
|
|
1756
2338
|
function getCategoryIcon(name, overrides) {
|
|
2339
|
+
const str = typeof name === "string" ? name : name && typeof name === "object" && "name" in name ? String(name.name) : "";
|
|
2340
|
+
if (!str) return import_lucide_react.CircleDot;
|
|
1757
2341
|
const map = { ...DEFAULT_CATEGORY_ICONS, ...overrides };
|
|
1758
|
-
const lower =
|
|
2342
|
+
const lower = str.toLowerCase();
|
|
1759
2343
|
for (const [kw, Icon] of Object.entries(map)) {
|
|
1760
2344
|
if (lower.includes(kw)) return Icon;
|
|
1761
2345
|
}
|
|
1762
2346
|
return import_lucide_react.CircleDot;
|
|
1763
2347
|
}
|
|
2348
|
+
function resolveCategoryLabel(cat) {
|
|
2349
|
+
if (typeof cat === "string") return cat;
|
|
2350
|
+
if (cat && typeof cat === "object" && "name" in cat) return String(cat.name);
|
|
2351
|
+
return "";
|
|
2352
|
+
}
|
|
1764
2353
|
function getInitials(name) {
|
|
1765
2354
|
return name.split(" ").map((w) => w[0]).join("").slice(0, 2).toUpperCase();
|
|
1766
2355
|
}
|
|
@@ -1768,13 +2357,13 @@ function truncate(str, max) {
|
|
|
1768
2357
|
return str.length > max ? str.slice(0, max) + "\u2026" : str;
|
|
1769
2358
|
}
|
|
1770
2359
|
function DefaultMenuIcon() {
|
|
1771
|
-
return /* @__PURE__ */ (0,
|
|
2360
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.Menu, { size: 20 });
|
|
1772
2361
|
}
|
|
1773
2362
|
function DefaultCloseIcon() {
|
|
1774
|
-
return /* @__PURE__ */ (0,
|
|
2363
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.X, { size: 20 });
|
|
1775
2364
|
}
|
|
1776
2365
|
function DefaultLink({ href, className, onClick, children }) {
|
|
1777
|
-
return /* @__PURE__ */ (0,
|
|
2366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("a", { href, className, onClick, children });
|
|
1778
2367
|
}
|
|
1779
2368
|
function BookingWidgetPanel({
|
|
1780
2369
|
mobileHeader,
|
|
@@ -1785,20 +2374,18 @@ function BookingWidgetPanel({
|
|
|
1785
2374
|
stepTitles,
|
|
1786
2375
|
linkComponent: LinkComp = DefaultLink
|
|
1787
2376
|
} = {}) {
|
|
1788
|
-
const widgetRef = (0,
|
|
1789
|
-
const formRef = (0,
|
|
1790
|
-
const
|
|
1791
|
-
const
|
|
1792
|
-
const
|
|
1793
|
-
const
|
|
1794
|
-
const
|
|
1795
|
-
const
|
|
1796
|
-
const
|
|
1797
|
-
const [
|
|
1798
|
-
const [
|
|
1799
|
-
const [
|
|
1800
|
-
const [navOpen, setNavOpen] = (0, import_react9.useState)(false);
|
|
1801
|
-
const [localStaff, setLocalStaff] = (0, import_react9.useState)(null);
|
|
2377
|
+
const widgetRef = (0, import_react10.useRef)(null);
|
|
2378
|
+
const formRef = (0, import_react10.useRef)(null);
|
|
2379
|
+
const rightScrollRef = (0, import_react10.useRef)(null);
|
|
2380
|
+
const staffTriggerRef = (0, import_react10.useRef)(null);
|
|
2381
|
+
const mobileBodyRef = (0, import_react10.useRef)(null);
|
|
2382
|
+
const mobileScrollbarRef = (0, import_react10.useRef)(null);
|
|
2383
|
+
const portalRef = (0, import_react10.useRef)(null);
|
|
2384
|
+
const [mobileStep, setMobileStep] = (0, import_react10.useState)(1);
|
|
2385
|
+
const [monthOpen, setMonthOpen] = (0, import_react10.useState)(false);
|
|
2386
|
+
const [staffOpen, setStaffOpen] = (0, import_react10.useState)(false);
|
|
2387
|
+
const [navOpen, setNavOpen] = (0, import_react10.useState)(false);
|
|
2388
|
+
const [localStaff, setLocalStaff] = (0, import_react10.useState)(null);
|
|
1802
2389
|
const { data, isLoading: servicesLoading, error: servicesError } = useServices();
|
|
1803
2390
|
const { data: teamData } = useTeam();
|
|
1804
2391
|
const allStaff = teamData?.team || [];
|
|
@@ -1817,7 +2404,7 @@ function BookingWidgetPanel({
|
|
|
1817
2404
|
servicePageSize: 50
|
|
1818
2405
|
});
|
|
1819
2406
|
const todayStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1820
|
-
(0,
|
|
2407
|
+
(0, import_react10.useEffect)(() => {
|
|
1821
2408
|
if (bw.isAvailabilityLoading || !bw.selectedService || !bw.availabilityByDate) return;
|
|
1822
2409
|
if (bw.selectedDate) return;
|
|
1823
2410
|
for (const day of bw.calendarDays) {
|
|
@@ -1829,19 +2416,19 @@ function BookingWidgetPanel({
|
|
|
1829
2416
|
}
|
|
1830
2417
|
}
|
|
1831
2418
|
}, [bw.isAvailabilityLoading, bw.selectedService?.id, bw.availabilityByDate, bw.selectedDate]);
|
|
1832
|
-
const
|
|
1833
|
-
(0,
|
|
2419
|
+
const hasCategories = bw.categories.length >= 1;
|
|
2420
|
+
(0, import_react10.useEffect)(() => {
|
|
1834
2421
|
if (bw.categories.length >= 1 && bw.categoryFilter === "all") {
|
|
1835
2422
|
bw.setCategoryFilter(bw.categories[0]);
|
|
1836
2423
|
}
|
|
1837
2424
|
}, [bw.categories.length]);
|
|
1838
|
-
(0,
|
|
2425
|
+
(0, import_react10.useEffect)(() => {
|
|
1839
2426
|
if (localStaff && !bw.selectedStaff) {
|
|
1840
2427
|
bw.selectStaff(localStaff);
|
|
1841
2428
|
}
|
|
1842
2429
|
}, [bw.selectedStaff, localStaff]);
|
|
1843
2430
|
const isReady = !servicesLoading && !bw.isServicesLoading && !!data?.services?.length && bw.step !== "done";
|
|
1844
|
-
(0,
|
|
2431
|
+
(0, import_react10.useEffect)(() => {
|
|
1845
2432
|
if (!isReady) return;
|
|
1846
2433
|
const mq = window.matchMedia("(min-width: 1025px)");
|
|
1847
2434
|
const scrollbars = [];
|
|
@@ -1856,9 +2443,6 @@ function BookingWidgetPanel({
|
|
|
1856
2443
|
const init = () => {
|
|
1857
2444
|
scrollbars.forEach((sb) => sb.destroy());
|
|
1858
2445
|
scrollbars.length = 0;
|
|
1859
|
-
if (mq.matches && catScrollRef.current) {
|
|
1860
|
-
scrollbars.push(import_smooth_scrollbar.default.init(catScrollRef.current, opts));
|
|
1861
|
-
}
|
|
1862
2446
|
if (mq.matches && rightScrollRef.current) {
|
|
1863
2447
|
scrollbars.push(import_smooth_scrollbar.default.init(rightScrollRef.current, opts));
|
|
1864
2448
|
}
|
|
@@ -1870,25 +2454,7 @@ function BookingWidgetPanel({
|
|
|
1870
2454
|
scrollbars.forEach((sb) => sb.destroy());
|
|
1871
2455
|
};
|
|
1872
2456
|
}, [isReady, mobileStep]);
|
|
1873
|
-
|
|
1874
|
-
if (svcScrollbarRef.current) {
|
|
1875
|
-
try {
|
|
1876
|
-
svcScrollbarRef.current.destroy();
|
|
1877
|
-
} catch {
|
|
1878
|
-
}
|
|
1879
|
-
svcScrollbarRef.current = null;
|
|
1880
|
-
}
|
|
1881
|
-
if (!el || typeof window !== "undefined" && window.matchMedia("(max-width: 1024px)").matches) return;
|
|
1882
|
-
svcScrollbarRef.current = import_smooth_scrollbar.default.init(el, {
|
|
1883
|
-
damping: 0.08,
|
|
1884
|
-
continuousScrolling: false,
|
|
1885
|
-
alwaysShowTracks: false,
|
|
1886
|
-
plugins: {
|
|
1887
|
-
overscroll: { effect: "bounce", damping: 0.15, maxOverscroll: 80 }
|
|
1888
|
-
}
|
|
1889
|
-
});
|
|
1890
|
-
};
|
|
1891
|
-
(0, import_react9.useEffect)(() => {
|
|
2457
|
+
(0, import_react10.useEffect)(() => {
|
|
1892
2458
|
if (!isReady) return;
|
|
1893
2459
|
const mq = window.matchMedia("(max-width: 1024px)");
|
|
1894
2460
|
const init = () => {
|
|
@@ -1923,7 +2489,7 @@ function BookingWidgetPanel({
|
|
|
1923
2489
|
}
|
|
1924
2490
|
};
|
|
1925
2491
|
}, [isReady]);
|
|
1926
|
-
(0,
|
|
2492
|
+
(0, import_react10.useEffect)(() => {
|
|
1927
2493
|
if (mobileScrollbarRef.current) {
|
|
1928
2494
|
mobileScrollbarRef.current.scrollTo(0, 0, 0);
|
|
1929
2495
|
mobileScrollbarRef.current.update();
|
|
@@ -1941,6 +2507,9 @@ function BookingWidgetPanel({
|
|
|
1941
2507
|
const canAdvanceStep2 = !!bw.selectedService;
|
|
1942
2508
|
const canAdvanceStep3 = !!bw.selectedTime;
|
|
1943
2509
|
const canSubmit = !!bw.selectedService && !!bw.selectedTime && !bw.isSubmitting;
|
|
2510
|
+
const selectedLocationOption = bw.selectedService?.locationOptions?.find(
|
|
2511
|
+
(option) => option.type === bw.details.locationType
|
|
2512
|
+
);
|
|
1944
2513
|
const selectedStaff = localStaff;
|
|
1945
2514
|
const isServiceForStaff = (svc) => {
|
|
1946
2515
|
if (!selectedStaff) return true;
|
|
@@ -1949,47 +2518,47 @@ function BookingWidgetPanel({
|
|
|
1949
2518
|
};
|
|
1950
2519
|
const staffFilteredServices = bw.pagedServices.filter(isServiceForStaff);
|
|
1951
2520
|
if (servicesLoading || bw.isServicesLoading) {
|
|
1952
|
-
return /* @__PURE__ */ (0,
|
|
1953
|
-
/* @__PURE__ */ (0,
|
|
1954
|
-
/* @__PURE__ */ (0,
|
|
1955
|
-
/* @__PURE__ */ (0,
|
|
1956
|
-
/* @__PURE__ */ (0,
|
|
1957
|
-
/* @__PURE__ */ (0,
|
|
1958
|
-
/* @__PURE__ */ (0,
|
|
1959
|
-
/* @__PURE__ */ (0,
|
|
2521
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw", ref: widgetRef, children: [
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-header", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 220, height: 28 } }) }),
|
|
2523
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-body bw-skel-desktop", children: [
|
|
2524
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-col bw-col--left", children: [
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 100, height: 12, marginBottom: 10 } }),
|
|
2526
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 52 } }),
|
|
2527
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 100, height: 12, marginTop: 20, marginBottom: 10 } }),
|
|
2528
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel-list", children: [...Array(3)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 56 } }, i)) })
|
|
1960
2529
|
] }),
|
|
1961
|
-
/* @__PURE__ */ (0,
|
|
1962
|
-
/* @__PURE__ */ (0,
|
|
1963
|
-
[...Array(3)].map((_, i) => /* @__PURE__ */ (0,
|
|
1964
|
-
/* @__PURE__ */ (0,
|
|
1965
|
-
/* @__PURE__ */ (0,
|
|
2530
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-col bw-col--center", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--calendar", style: { height: "100%" } }) }),
|
|
2531
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-col bw-col--right", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-skel-list", children: [
|
|
2532
|
+
[...Array(3)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--input" }, i)),
|
|
2533
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--input", style: { height: 80 } }),
|
|
2534
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 48, marginTop: 16 } })
|
|
1966
2535
|
] }) })
|
|
1967
2536
|
] }),
|
|
1968
|
-
/* @__PURE__ */ (0,
|
|
1969
|
-
/* @__PURE__ */ (0,
|
|
1970
|
-
/* @__PURE__ */ (0,
|
|
1971
|
-
/* @__PURE__ */ (0,
|
|
1972
|
-
[...Array(4)].map((_, i) => /* @__PURE__ */ (0,
|
|
1973
|
-
/* @__PURE__ */ (0,
|
|
2537
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-skel-mobile", children: [
|
|
2538
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 100, height: 12, marginBottom: 10 } }),
|
|
2539
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 52 } }),
|
|
2540
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 100, height: 12, marginTop: 24, marginBottom: 10 } }),
|
|
2541
|
+
[...Array(4)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 52 } }, i)),
|
|
2542
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 48, marginTop: "auto" } })
|
|
1974
2543
|
] })
|
|
1975
2544
|
] });
|
|
1976
2545
|
}
|
|
1977
2546
|
if (servicesError) {
|
|
1978
2547
|
const message = servicesError instanceof Error ? servicesError.message : "Unable to load services.";
|
|
1979
|
-
return /* @__PURE__ */ (0,
|
|
2548
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw", ref: widgetRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-empty", children: [
|
|
1980
2549
|
"Booking is unavailable right now. ",
|
|
1981
2550
|
message
|
|
1982
2551
|
] }) });
|
|
1983
2552
|
}
|
|
1984
2553
|
if (!data?.services?.length) {
|
|
1985
|
-
return /* @__PURE__ */ (0,
|
|
2554
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw", ref: widgetRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-empty", children: "No services are configured yet." }) });
|
|
1986
2555
|
}
|
|
1987
2556
|
if (bw.step === "done") {
|
|
1988
|
-
return /* @__PURE__ */ (0,
|
|
1989
|
-
/* @__PURE__ */ (0,
|
|
1990
|
-
/* @__PURE__ */ (0,
|
|
1991
|
-
/* @__PURE__ */ (0,
|
|
1992
|
-
/* @__PURE__ */ (0,
|
|
2557
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw", ref: widgetRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-done", children: [
|
|
2558
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-done-icon", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.Check, { size: 28, strokeWidth: 2.5 }) }),
|
|
2559
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "bw-done-title", children: "You're All Set!" }),
|
|
2560
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-done-text", children: "We've received your booking request. You'll get a confirmation email shortly with all the details." }),
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "bw-btn-primary", onClick: bw.reset, children: "Book Another Visit" })
|
|
1993
2562
|
] }) });
|
|
1994
2563
|
}
|
|
1995
2564
|
const renderServiceRow = (svc, variant = "inline") => {
|
|
@@ -1999,22 +2568,22 @@ function BookingWidgetPanel({
|
|
|
1999
2568
|
if (variant === "full") {
|
|
2000
2569
|
const duration = svc.duration != null ? bw.formatDuration(svc.duration) : null;
|
|
2001
2570
|
const hasImg = !!imgUrl;
|
|
2002
|
-
return /* @__PURE__ */ (0,
|
|
2571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2003
2572
|
"button",
|
|
2004
2573
|
{
|
|
2005
2574
|
className: `bw-svc-row bw-svc-row--full ${hasImg ? "bw-svc-row--has-img" : ""} ${sel ? "is-active" : ""}`,
|
|
2006
2575
|
onClick: () => bw.selectService(svc),
|
|
2007
2576
|
children: [
|
|
2008
|
-
/* @__PURE__ */ (0,
|
|
2009
|
-
/* @__PURE__ */ (0,
|
|
2010
|
-
/* @__PURE__ */ (0,
|
|
2011
|
-
duration && /* @__PURE__ */ (0,
|
|
2012
|
-
desc && /* @__PURE__ */ (0,
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `bw-check bw-check--lg ${sel ? "is-checked" : ""}`, children: sel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.Check, { size: 12 }) }),
|
|
2578
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "bw-svc-info", children: [
|
|
2579
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-name", children: svc.name }),
|
|
2580
|
+
duration && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-meta", children: duration }),
|
|
2581
|
+
desc && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-desc", children: desc })
|
|
2013
2582
|
] }),
|
|
2014
|
-
hasImg ? /* @__PURE__ */ (0,
|
|
2015
|
-
/* @__PURE__ */ (0,
|
|
2016
|
-
svc.price != null && /* @__PURE__ */ (0,
|
|
2017
|
-
] }) : svc.price != null && /* @__PURE__ */ (0,
|
|
2583
|
+
hasImg ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "bw-svc-img", children: [
|
|
2584
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { src: imgUrl, alt: svc.name }),
|
|
2585
|
+
svc.price != null && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-badge", children: formatPrice(Number(svc.price)) })
|
|
2586
|
+
] }) : svc.price != null && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-price", children: formatPrice(Number(svc.price)) })
|
|
2018
2587
|
]
|
|
2019
2588
|
},
|
|
2020
2589
|
svc.id
|
|
@@ -2024,39 +2593,39 @@ function BookingWidgetPanel({
|
|
|
2024
2593
|
svc.duration != null ? bw.formatDuration(svc.duration) : null,
|
|
2025
2594
|
svc.price != null ? formatPrice(Number(svc.price)) : null
|
|
2026
2595
|
].filter(Boolean).join(" \xB7 ");
|
|
2027
|
-
return /* @__PURE__ */ (0,
|
|
2596
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2028
2597
|
"button",
|
|
2029
2598
|
{
|
|
2030
2599
|
className: `bw-svc-row ${sel ? "is-active" : ""}`,
|
|
2031
2600
|
onClick: () => bw.selectService(svc),
|
|
2032
2601
|
children: [
|
|
2033
|
-
/* @__PURE__ */ (0,
|
|
2034
|
-
/* @__PURE__ */ (0,
|
|
2035
|
-
/* @__PURE__ */ (0,
|
|
2036
|
-
meta && /* @__PURE__ */ (0,
|
|
2037
|
-
desc && /* @__PURE__ */ (0,
|
|
2602
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `bw-check ${sel ? "is-checked" : ""}`, children: sel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.Check, { size: 11 }) }),
|
|
2603
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "bw-svc-info", children: [
|
|
2604
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-name", children: svc.name }),
|
|
2605
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-meta", children: meta }),
|
|
2606
|
+
desc && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-desc", children: desc })
|
|
2038
2607
|
] }),
|
|
2039
|
-
imgUrl && /* @__PURE__ */ (0,
|
|
2608
|
+
imgUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-svc-thumb", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { src: imgUrl, alt: svc.name }) })
|
|
2040
2609
|
]
|
|
2041
2610
|
},
|
|
2042
2611
|
svc.id
|
|
2043
2612
|
);
|
|
2044
2613
|
};
|
|
2045
|
-
return /* @__PURE__ */ (0,
|
|
2046
|
-
mobileHeader ? /* @__PURE__ */ (0,
|
|
2047
|
-
/* @__PURE__ */ (0,
|
|
2048
|
-
/* @__PURE__ */ (0,
|
|
2049
|
-
navLinks.length > 0 && /* @__PURE__ */ (0,
|
|
2614
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw", ref: widgetRef, children: [
|
|
2615
|
+
mobileHeader ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-mobile-header", children: mobileHeader }) : null,
|
|
2616
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-topbar", children: [
|
|
2617
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-dots", children: Array.from({ length: 4 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `bw-dot ${mobileStep > i ? "is-filled" : ""}` }, i)) }),
|
|
2618
|
+
navLinks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2050
2619
|
"button",
|
|
2051
2620
|
{
|
|
2052
2621
|
className: "bw-topbar-menu",
|
|
2053
2622
|
onClick: () => setNavOpen((o) => !o),
|
|
2054
2623
|
"aria-label": navOpen ? "Close menu" : "Open menu",
|
|
2055
|
-
children: navOpen ? closeIcon || /* @__PURE__ */ (0,
|
|
2624
|
+
children: navOpen ? closeIcon || /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DefaultCloseIcon, {}) : menuIcon || /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DefaultMenuIcon, {})
|
|
2056
2625
|
}
|
|
2057
2626
|
)
|
|
2058
2627
|
] }),
|
|
2059
|
-
navLinks.length > 0 && /* @__PURE__ */ (0,
|
|
2628
|
+
navLinks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_framer_motion.AnimatePresence, { children: navOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2060
2629
|
import_framer_motion.motion.div,
|
|
2061
2630
|
{
|
|
2062
2631
|
className: "bw-nav-overlay",
|
|
@@ -2064,7 +2633,7 @@ function BookingWidgetPanel({
|
|
|
2064
2633
|
animate: { opacity: 1 },
|
|
2065
2634
|
exit: { opacity: 0 },
|
|
2066
2635
|
transition: { duration: 0.2 },
|
|
2067
|
-
children: /* @__PURE__ */ (0,
|
|
2636
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("nav", { className: "bw-nav", children: navLinks.map((link) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2068
2637
|
LinkComp,
|
|
2069
2638
|
{
|
|
2070
2639
|
href: link.href,
|
|
@@ -2076,23 +2645,23 @@ function BookingWidgetPanel({
|
|
|
2076
2645
|
)) })
|
|
2077
2646
|
}
|
|
2078
2647
|
) }),
|
|
2079
|
-
/* @__PURE__ */ (0,
|
|
2080
|
-
/* @__PURE__ */ (0,
|
|
2081
|
-
/* @__PURE__ */ (0,
|
|
2648
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-header", children: [
|
|
2649
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "bw-title bw-title--desktop", children: "Schedule your visit" }),
|
|
2650
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "bw-title bw-title--mobile", children: (stepTitles || DEFAULT_STEP_TITLES)[mobileStep - 1] })
|
|
2082
2651
|
] }),
|
|
2083
|
-
/* @__PURE__ */ (0,
|
|
2084
|
-
/* @__PURE__ */ (0,
|
|
2085
|
-
/* @__PURE__ */ (0,
|
|
2086
|
-
/* @__PURE__ */ (0,
|
|
2087
|
-
/* @__PURE__ */ (0,
|
|
2088
|
-
/* @__PURE__ */ (0,
|
|
2652
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-body", "data-mobile-step": mobileStep, ref: mobileBodyRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-body-inner", children: [
|
|
2653
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-col bw-col--left", children: [
|
|
2654
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-step-1", children: [
|
|
2655
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "bw-label", children: "Select Specialist" }),
|
|
2656
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-staff-dropdown", children: [
|
|
2657
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2089
2658
|
"button",
|
|
2090
2659
|
{
|
|
2091
2660
|
ref: staffTriggerRef,
|
|
2092
2661
|
className: `bw-staff-trigger${selectedStaff ? " has-value" : ""}`,
|
|
2093
2662
|
onClick: () => setStaffOpen((o) => !o),
|
|
2094
2663
|
children: [
|
|
2095
|
-
/* @__PURE__ */ (0,
|
|
2664
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_framer_motion.AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2096
2665
|
import_framer_motion.motion.span,
|
|
2097
2666
|
{
|
|
2098
2667
|
className: "bw-staff-trigger-content",
|
|
@@ -2100,28 +2669,28 @@ function BookingWidgetPanel({
|
|
|
2100
2669
|
animate: { opacity: 1 },
|
|
2101
2670
|
exit: { opacity: 0 },
|
|
2102
2671
|
transition: { duration: 0.15 },
|
|
2103
|
-
children: selectedStaff ? /* @__PURE__ */ (0,
|
|
2104
|
-
/* @__PURE__ */ (0,
|
|
2105
|
-
/* @__PURE__ */ (0,
|
|
2106
|
-
/* @__PURE__ */ (0,
|
|
2107
|
-
selectedStaff.bio && /* @__PURE__ */ (0,
|
|
2672
|
+
children: selectedStaff ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2673
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-avatar", children: selectedStaff.photo?.url ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { src: selectedStaff.photo.url, alt: selectedStaff.name }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-initials", children: getInitials(selectedStaff.name) }) }),
|
|
2674
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "bw-staff-trigger-info", children: [
|
|
2675
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-trigger-name", children: selectedStaff.name }),
|
|
2676
|
+
selectedStaff.bio && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-trigger-desc", children: truncate(selectedStaff.bio, 40) })
|
|
2108
2677
|
] })
|
|
2109
|
-
] }) : /* @__PURE__ */ (0,
|
|
2110
|
-
/* @__PURE__ */ (0,
|
|
2111
|
-
/* @__PURE__ */ (0,
|
|
2678
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2679
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-avatar bw-staff-avatar--placeholder", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.User, { size: 14 }) }),
|
|
2680
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-trigger-info", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-trigger-name", children: "Any available specialist" }) })
|
|
2112
2681
|
] })
|
|
2113
2682
|
},
|
|
2114
2683
|
selectedStaff?.id ?? "any"
|
|
2115
2684
|
) }),
|
|
2116
|
-
/* @__PURE__ */ (0,
|
|
2685
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ChevronDown, { size: 16, className: "bw-staff-trigger-chevron" })
|
|
2117
2686
|
]
|
|
2118
2687
|
}
|
|
2119
2688
|
),
|
|
2120
2689
|
portalRef.current && (0, import_react_dom.createPortal)(
|
|
2121
|
-
/* @__PURE__ */ (0,
|
|
2690
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_framer_motion.AnimatePresence, { children: staffOpen && (() => {
|
|
2122
2691
|
const rect = staffTriggerRef.current?.getBoundingClientRect();
|
|
2123
|
-
return /* @__PURE__ */ (0,
|
|
2124
|
-
/* @__PURE__ */ (0,
|
|
2692
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2693
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2125
2694
|
import_framer_motion.motion.div,
|
|
2126
2695
|
{
|
|
2127
2696
|
className: "bw-staff-overlay",
|
|
@@ -2132,7 +2701,7 @@ function BookingWidgetPanel({
|
|
|
2132
2701
|
transition: { duration: 0.15 }
|
|
2133
2702
|
}
|
|
2134
2703
|
),
|
|
2135
|
-
/* @__PURE__ */ (0,
|
|
2704
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2136
2705
|
import_framer_motion.motion.div,
|
|
2137
2706
|
{
|
|
2138
2707
|
className: "bw-staff-list",
|
|
@@ -2146,7 +2715,7 @@ function BookingWidgetPanel({
|
|
|
2146
2715
|
width: rect.width
|
|
2147
2716
|
} : void 0,
|
|
2148
2717
|
children: [
|
|
2149
|
-
/* @__PURE__ */ (0,
|
|
2718
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2150
2719
|
"button",
|
|
2151
2720
|
{
|
|
2152
2721
|
className: `bw-staff-option${!selectedStaff ? " is-active" : ""}`,
|
|
@@ -2157,10 +2726,10 @@ function BookingWidgetPanel({
|
|
|
2157
2726
|
setStaffOpen(false);
|
|
2158
2727
|
},
|
|
2159
2728
|
children: [
|
|
2160
|
-
/* @__PURE__ */ (0,
|
|
2161
|
-
/* @__PURE__ */ (0,
|
|
2162
|
-
/* @__PURE__ */ (0,
|
|
2163
|
-
/* @__PURE__ */ (0,
|
|
2729
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-avatar bw-staff-avatar--placeholder", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.User, { size: 14 }) }),
|
|
2730
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "bw-staff-option-info", children: [
|
|
2731
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-option-name", children: "Any Available" }),
|
|
2732
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-option-desc", children: "First available specialist" })
|
|
2164
2733
|
] })
|
|
2165
2734
|
]
|
|
2166
2735
|
}
|
|
@@ -2168,7 +2737,7 @@ function BookingWidgetPanel({
|
|
|
2168
2737
|
allStaff.map((member) => {
|
|
2169
2738
|
const active = selectedStaff?.id === member.id;
|
|
2170
2739
|
const photoUrl = member.photo?.url;
|
|
2171
|
-
return /* @__PURE__ */ (0,
|
|
2740
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2172
2741
|
"button",
|
|
2173
2742
|
{
|
|
2174
2743
|
className: `bw-staff-option${active ? " is-active" : ""}`,
|
|
@@ -2179,10 +2748,10 @@ function BookingWidgetPanel({
|
|
|
2179
2748
|
setStaffOpen(false);
|
|
2180
2749
|
},
|
|
2181
2750
|
children: [
|
|
2182
|
-
/* @__PURE__ */ (0,
|
|
2183
|
-
/* @__PURE__ */ (0,
|
|
2184
|
-
/* @__PURE__ */ (0,
|
|
2185
|
-
member.bio && /* @__PURE__ */ (0,
|
|
2751
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-avatar", children: photoUrl ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { src: photoUrl, alt: member.name }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-initials", children: getInitials(member.name) }) }),
|
|
2752
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "bw-staff-option-info", children: [
|
|
2753
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-option-name", children: member.name }),
|
|
2754
|
+
member.bio && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-staff-option-desc", children: truncate(member.bio, 60) })
|
|
2186
2755
|
] })
|
|
2187
2756
|
]
|
|
2188
2757
|
},
|
|
@@ -2197,93 +2766,75 @@ function BookingWidgetPanel({
|
|
|
2197
2766
|
portalRef.current
|
|
2198
2767
|
)
|
|
2199
2768
|
] }),
|
|
2200
|
-
|
|
2201
|
-
/* @__PURE__ */ (0,
|
|
2202
|
-
/* @__PURE__ */ (0,
|
|
2769
|
+
hasCategories && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2770
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "bw-label", children: "Select Category" }),
|
|
2771
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-cat-scroll", children: bw.categories.map((cat, catIdx) => {
|
|
2203
2772
|
const active = bw.categoryFilter === cat;
|
|
2204
2773
|
const Icon = getCategoryIcon(cat, categoryIcons);
|
|
2205
|
-
const catServices = (data?.services || []).filter((s) => s.category === cat).filter(isServiceForStaff);
|
|
2206
|
-
return /* @__PURE__ */ (0,
|
|
2207
|
-
/* @__PURE__ */ (0,
|
|
2774
|
+
const catServices = (data?.services || []).filter((s) => resolveCategoryLabel(s.category) === cat).filter(isServiceForStaff);
|
|
2775
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
2776
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2208
2777
|
"button",
|
|
2209
2778
|
{
|
|
2210
2779
|
className: `bw-cat-card${active ? " is-active" : ""}`,
|
|
2211
2780
|
onClick: () => bw.setCategoryFilter(active ? "all" : cat),
|
|
2212
2781
|
children: [
|
|
2213
|
-
/* @__PURE__ */ (0,
|
|
2214
|
-
/* @__PURE__ */ (0,
|
|
2215
|
-
/* @__PURE__ */ (0,
|
|
2782
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Icon, { size: 20, className: "bw-cat-icon" }),
|
|
2783
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-cat-name", children: cat }),
|
|
2784
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2216
2785
|
import_framer_motion.motion.span,
|
|
2217
2786
|
{
|
|
2218
2787
|
className: "bw-cat-chevron",
|
|
2219
2788
|
animate: { rotate: active ? 90 : 0 },
|
|
2220
2789
|
transition: { duration: 0.2, ease: "easeOut" },
|
|
2221
|
-
children: /* @__PURE__ */ (0,
|
|
2790
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ChevronRight, { size: 16 })
|
|
2222
2791
|
}
|
|
2223
2792
|
),
|
|
2224
|
-
active && /* @__PURE__ */ (0,
|
|
2793
|
+
active && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-cat-check", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.Check, { size: 12 }) })
|
|
2225
2794
|
]
|
|
2226
2795
|
}
|
|
2227
2796
|
),
|
|
2228
|
-
/* @__PURE__ */ (0,
|
|
2797
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2229
2798
|
"div",
|
|
2230
2799
|
{
|
|
2231
2800
|
className: `bw-svc-scroll ${active ? "is-open" : ""}`,
|
|
2232
|
-
|
|
2233
|
-
if (!el) return;
|
|
2234
|
-
if (active) {
|
|
2235
|
-
const wrap = el.querySelector(".bw-svc-scroll-wrap");
|
|
2236
|
-
if (wrap) requestAnimationFrame(() => initSvcScrollbar(wrap));
|
|
2237
|
-
} else {
|
|
2238
|
-
if (svcScrollbarRef.current) {
|
|
2239
|
-
svcScrollbarRef.current.scrollTo(0, 0, 0);
|
|
2240
|
-
try {
|
|
2241
|
-
svcScrollbarRef.current.destroy();
|
|
2242
|
-
} catch {
|
|
2243
|
-
}
|
|
2244
|
-
svcScrollbarRef.current = null;
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
},
|
|
2248
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-svc-scroll-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-svc-scroll-inner", children: catServices.length ? catServices.map((svc) => renderServiceRow(svc)) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-no-services", children: "No services available" }) }) })
|
|
2801
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-svc-scroll-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-svc-scroll-inner", children: catServices.length ? catServices.map((svc) => renderServiceRow(svc)) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-no-services", children: "No services available" }) }) })
|
|
2249
2802
|
}
|
|
2250
2803
|
)
|
|
2251
2804
|
] }, cat);
|
|
2252
2805
|
}) })
|
|
2253
2806
|
] }),
|
|
2254
|
-
!
|
|
2255
|
-
if (el) requestAnimationFrame(() => initSvcScrollbar(el));
|
|
2256
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-svc-scroll-inner", children: staffFilteredServices.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2807
|
+
!hasCategories && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-svc-scroll is-open", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-svc-scroll-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-svc-scroll-inner", children: staffFilteredServices.length ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2257
2808
|
staffFilteredServices.map((svc) => renderServiceRow(svc)),
|
|
2258
|
-
bw.hasMoreServices && /* @__PURE__ */ (0,
|
|
2259
|
-
] }) : /* @__PURE__ */ (0,
|
|
2809
|
+
bw.hasMoreServices && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "bw-load-more", onClick: bw.loadMoreServices, children: "Show more services" })
|
|
2810
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-no-services", children: "No services available" }) }) }) })
|
|
2260
2811
|
] }),
|
|
2261
|
-
/* @__PURE__ */ (0,
|
|
2262
|
-
bw.categoryFilter !== "all" && /* @__PURE__ */ (0,
|
|
2263
|
-
staffFilteredServices.length ? /* @__PURE__ */ (0,
|
|
2812
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-step-2", children: [
|
|
2813
|
+
bw.categoryFilter !== "all" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-step-2-heading", children: bw.categoryFilter }),
|
|
2814
|
+
staffFilteredServices.length ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2264
2815
|
staffFilteredServices.map((svc) => renderServiceRow(svc, "full")),
|
|
2265
|
-
bw.hasMoreServices && /* @__PURE__ */ (0,
|
|
2266
|
-
] }) : /* @__PURE__ */ (0,
|
|
2816
|
+
bw.hasMoreServices && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "bw-load-more", onClick: bw.loadMoreServices, children: "Show more services" })
|
|
2817
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-no-services", children: "No services available" })
|
|
2267
2818
|
] })
|
|
2268
2819
|
] }),
|
|
2269
|
-
/* @__PURE__ */ (0,
|
|
2270
|
-
/* @__PURE__ */ (0,
|
|
2271
|
-
/* @__PURE__ */ (0,
|
|
2272
|
-
/* @__PURE__ */ (0,
|
|
2820
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-col bw-col--center", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-cal-card", children: [
|
|
2821
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-cal-header", children: [
|
|
2822
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-month-dropdown", children: [
|
|
2823
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2273
2824
|
"button",
|
|
2274
2825
|
{
|
|
2275
2826
|
className: "bw-month-btn",
|
|
2276
2827
|
onClick: () => setMonthOpen((o) => !o),
|
|
2277
2828
|
children: [
|
|
2278
|
-
/* @__PURE__ */ (0,
|
|
2829
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: bw.monthOptions.find(
|
|
2279
2830
|
(o) => o.value === `${bw.calendarMonth.getFullYear()}-${String(bw.calendarMonth.getMonth() + 1).padStart(2, "0")}`
|
|
2280
2831
|
)?.label ?? "Select month" }),
|
|
2281
|
-
/* @__PURE__ */ (0,
|
|
2832
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ChevronDown, { size: 14 })
|
|
2282
2833
|
]
|
|
2283
2834
|
}
|
|
2284
2835
|
),
|
|
2285
|
-
/* @__PURE__ */ (0,
|
|
2286
|
-
/* @__PURE__ */ (0,
|
|
2836
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_framer_motion.AnimatePresence, { children: monthOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2837
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2287
2838
|
import_framer_motion.motion.div,
|
|
2288
2839
|
{
|
|
2289
2840
|
className: "bw-month-overlay",
|
|
@@ -2294,7 +2845,7 @@ function BookingWidgetPanel({
|
|
|
2294
2845
|
transition: { duration: 0.15 }
|
|
2295
2846
|
}
|
|
2296
2847
|
),
|
|
2297
|
-
/* @__PURE__ */ (0,
|
|
2848
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2298
2849
|
import_framer_motion.motion.div,
|
|
2299
2850
|
{
|
|
2300
2851
|
className: "bw-month-list",
|
|
@@ -2304,7 +2855,7 @@ function BookingWidgetPanel({
|
|
|
2304
2855
|
transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
|
|
2305
2856
|
children: bw.monthOptions.map((opt) => {
|
|
2306
2857
|
const cur = `${bw.calendarMonth.getFullYear()}-${String(bw.calendarMonth.getMonth() + 1).padStart(2, "0")}`;
|
|
2307
|
-
return /* @__PURE__ */ (0,
|
|
2858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2308
2859
|
"button",
|
|
2309
2860
|
{
|
|
2310
2861
|
className: `bw-month-option${opt.value === cur ? " is-active" : ""}`,
|
|
@@ -2321,21 +2872,21 @@ function BookingWidgetPanel({
|
|
|
2321
2872
|
)
|
|
2322
2873
|
] }) })
|
|
2323
2874
|
] }),
|
|
2324
|
-
/* @__PURE__ */ (0,
|
|
2325
|
-
/* @__PURE__ */ (0,
|
|
2326
|
-
/* @__PURE__ */ (0,
|
|
2875
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-cal-navs", children: [
|
|
2876
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "bw-cal-nav", onClick: bw.handlePrevMonth, "aria-label": "Previous month", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ChevronLeft, { size: 18 }) }),
|
|
2877
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2327
2878
|
"button",
|
|
2328
2879
|
{
|
|
2329
2880
|
className: "bw-cal-nav",
|
|
2330
2881
|
onClick: bw.handleNextMonth,
|
|
2331
2882
|
disabled: bw.maxAdvanceMonthStart ? bw.calendarMonth >= bw.maxAdvanceMonthStart : false,
|
|
2332
2883
|
"aria-label": "Next month",
|
|
2333
|
-
children: /* @__PURE__ */ (0,
|
|
2884
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ChevronRight, { size: 18 })
|
|
2334
2885
|
}
|
|
2335
2886
|
)
|
|
2336
2887
|
] })
|
|
2337
2888
|
] }),
|
|
2338
|
-
/* @__PURE__ */ (0,
|
|
2889
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_framer_motion.AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2339
2890
|
import_framer_motion.motion.div,
|
|
2340
2891
|
{
|
|
2341
2892
|
initial: { opacity: 0 },
|
|
@@ -2343,8 +2894,8 @@ function BookingWidgetPanel({
|
|
|
2343
2894
|
exit: { opacity: 0 },
|
|
2344
2895
|
transition: { duration: 0.15 },
|
|
2345
2896
|
children: [
|
|
2346
|
-
/* @__PURE__ */ (0,
|
|
2347
|
-
/* @__PURE__ */ (0,
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-cal-weekdays", children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((d) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: d }, d)) }),
|
|
2898
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-cal-grid", children: bw.calendarDays.map((day, i) => {
|
|
2348
2899
|
const dateStr = day.date;
|
|
2349
2900
|
const dateObj = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
|
|
2350
2901
|
const avail = bw.availabilityByDate?.get(dateStr);
|
|
@@ -2354,7 +2905,7 @@ function BookingWidgetPanel({
|
|
|
2354
2905
|
const isPast = dateStr < todayStr;
|
|
2355
2906
|
const isBeyondMax = bw.maxAdvanceDate ? dateStr > bw.maxAdvanceDate.toISOString().split("T")[0] : false;
|
|
2356
2907
|
const isDisabled = !day.isCurrentMonth || isPast || isBeyondMax;
|
|
2357
|
-
return /* @__PURE__ */ (0,
|
|
2908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2358
2909
|
"button",
|
|
2359
2910
|
{
|
|
2360
2911
|
className: [
|
|
@@ -2380,17 +2931,17 @@ function BookingWidgetPanel({
|
|
|
2380
2931
|
},
|
|
2381
2932
|
`${bw.calendarMonth.getFullYear()}-${bw.calendarMonth.getMonth()}`
|
|
2382
2933
|
) }),
|
|
2383
|
-
!bw.selectedService && /* @__PURE__ */ (0,
|
|
2384
|
-
bw.selectedService && bw.isAvailabilityLoading && /* @__PURE__ */ (0,
|
|
2934
|
+
!bw.selectedService && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-cal-prompt", children: "Please select a service to see availability." }),
|
|
2935
|
+
bw.selectedService && bw.isAvailabilityLoading && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel-slots", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel-slots-grid", children: [...Array(8)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-skel bw-skel--slot" }, i)) }) }),
|
|
2385
2936
|
bw.selectedService && bw.selectedDate && !bw.isAvailabilityLoading && (() => {
|
|
2386
2937
|
const dateAvail = bw.availabilityByDate?.get(bw.selectedDate);
|
|
2387
2938
|
const slots = dateAvail?.slots?.filter((s) => s.available) || [];
|
|
2388
2939
|
if (!slots.length) {
|
|
2389
|
-
return /* @__PURE__ */ (0,
|
|
2940
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-no-slots", children: "No available times for this date." });
|
|
2390
2941
|
}
|
|
2391
|
-
return /* @__PURE__ */ (0,
|
|
2392
|
-
/* @__PURE__ */ (0,
|
|
2393
|
-
/* @__PURE__ */ (0,
|
|
2942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2943
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-time-divider" }),
|
|
2944
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-time-slots", children: slots.map((slot, idx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2394
2945
|
"button",
|
|
2395
2946
|
{
|
|
2396
2947
|
className: `bw-slot ${bw.selectedTime === slot.time ? "is-active" : ""}`,
|
|
@@ -2401,19 +2952,19 @@ function BookingWidgetPanel({
|
|
|
2401
2952
|
)) })
|
|
2402
2953
|
] });
|
|
2403
2954
|
})(),
|
|
2404
|
-
bw.timeZoneLabel && /* @__PURE__ */ (0,
|
|
2405
|
-
/* @__PURE__ */ (0,
|
|
2406
|
-
/* @__PURE__ */ (0,
|
|
2955
|
+
bw.timeZoneLabel && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-timezone", children: [
|
|
2956
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.Globe, { size: 14 }),
|
|
2957
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: bw.timeZoneLabel })
|
|
2407
2958
|
] })
|
|
2408
2959
|
] }) }),
|
|
2409
|
-
/* @__PURE__ */ (0,
|
|
2410
|
-
/* @__PURE__ */ (0,
|
|
2411
|
-
/* @__PURE__ */ (0,
|
|
2412
|
-
/* @__PURE__ */ (0,
|
|
2960
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-col bw-col--right", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-right-scroll", ref: rightScrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-right-inner", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("form", { className: "bw-form", ref: formRef, onSubmit: handleFormSubmit, children: [
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-form-fields", children: [
|
|
2962
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-field", children: [
|
|
2963
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { htmlFor: "bw-name", children: [
|
|
2413
2964
|
"Full name ",
|
|
2414
|
-
/* @__PURE__ */ (0,
|
|
2965
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-required", children: "*" })
|
|
2415
2966
|
] }),
|
|
2416
|
-
/* @__PURE__ */ (0,
|
|
2967
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2417
2968
|
"input",
|
|
2418
2969
|
{
|
|
2419
2970
|
id: "bw-name",
|
|
@@ -2421,16 +2972,17 @@ function BookingWidgetPanel({
|
|
|
2421
2972
|
required: true,
|
|
2422
2973
|
placeholder: "John",
|
|
2423
2974
|
value: bw.details.name,
|
|
2424
|
-
onChange: (e) => bw.setDetails((p) => ({ ...p, name: e.target.value }))
|
|
2975
|
+
onChange: (e) => bw.setDetails((p) => ({ ...p, name: e.target.value })),
|
|
2976
|
+
disabled: Boolean(bw.paymentSession)
|
|
2425
2977
|
}
|
|
2426
2978
|
)
|
|
2427
2979
|
] }),
|
|
2428
|
-
/* @__PURE__ */ (0,
|
|
2429
|
-
/* @__PURE__ */ (0,
|
|
2980
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-field", children: [
|
|
2981
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { htmlFor: "bw-email", children: [
|
|
2430
2982
|
"Email ",
|
|
2431
|
-
/* @__PURE__ */ (0,
|
|
2983
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-required", children: "*" })
|
|
2432
2984
|
] }),
|
|
2433
|
-
/* @__PURE__ */ (0,
|
|
2985
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2434
2986
|
"input",
|
|
2435
2987
|
{
|
|
2436
2988
|
id: "bw-email",
|
|
@@ -2438,27 +2990,50 @@ function BookingWidgetPanel({
|
|
|
2438
2990
|
required: true,
|
|
2439
2991
|
placeholder: "jane@example.com",
|
|
2440
2992
|
value: bw.details.email,
|
|
2441
|
-
onChange: (e) => bw.setDetails((p) => ({ ...p, email: e.target.value }))
|
|
2993
|
+
onChange: (e) => bw.setDetails((p) => ({ ...p, email: e.target.value })),
|
|
2994
|
+
disabled: Boolean(bw.paymentSession)
|
|
2442
2995
|
}
|
|
2443
2996
|
)
|
|
2444
2997
|
] }),
|
|
2445
|
-
/* @__PURE__ */ (0,
|
|
2446
|
-
/* @__PURE__ */ (0,
|
|
2447
|
-
/* @__PURE__ */ (0,
|
|
2998
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-field", children: [
|
|
2999
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { htmlFor: "bw-phone", children: "Phone" }),
|
|
3000
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2448
3001
|
"input",
|
|
2449
3002
|
{
|
|
2450
3003
|
id: "bw-phone",
|
|
2451
3004
|
type: "tel",
|
|
2452
3005
|
placeholder: "+1 (555) 000-0000",
|
|
2453
3006
|
value: bw.details.phone,
|
|
2454
|
-
onChange: (e) => bw.setDetails((p) => ({ ...p, phone: e.target.value }))
|
|
3007
|
+
onChange: (e) => bw.setDetails((p) => ({ ...p, phone: e.target.value })),
|
|
3008
|
+
disabled: Boolean(bw.paymentSession)
|
|
2455
3009
|
}
|
|
2456
3010
|
)
|
|
2457
|
-
] })
|
|
3011
|
+
] }),
|
|
3012
|
+
bw.selectedService?.locationOptions?.length ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-field", children: [
|
|
3013
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { htmlFor: "bw-location", children: [
|
|
3014
|
+
"Location ",
|
|
3015
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-required", children: "*" })
|
|
3016
|
+
] }),
|
|
3017
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3018
|
+
"select",
|
|
3019
|
+
{
|
|
3020
|
+
id: "bw-location",
|
|
3021
|
+
value: bw.details.locationType,
|
|
3022
|
+
onChange: (e) => bw.setDetails((p) => ({
|
|
3023
|
+
...p,
|
|
3024
|
+
locationType: e.target.value
|
|
3025
|
+
})),
|
|
3026
|
+
disabled: Boolean(bw.paymentSession),
|
|
3027
|
+
required: true,
|
|
3028
|
+
children: bw.selectedService.locationOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: option.type, children: option.label }, option.type))
|
|
3029
|
+
}
|
|
3030
|
+
),
|
|
3031
|
+
selectedLocationOption?.details ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-char-count", style: { justifyContent: "flex-start" }, children: selectedLocationOption.details }) : null
|
|
3032
|
+
] }) : null
|
|
2458
3033
|
] }),
|
|
2459
|
-
/* @__PURE__ */ (0,
|
|
2460
|
-
/* @__PURE__ */ (0,
|
|
2461
|
-
/* @__PURE__ */ (0,
|
|
3034
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-field bw-field--notes", children: [
|
|
3035
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { htmlFor: "bw-notes", children: "Additional notes" }),
|
|
3036
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2462
3037
|
"textarea",
|
|
2463
3038
|
{
|
|
2464
3039
|
id: "bw-notes",
|
|
@@ -2467,119 +3042,142 @@ function BookingWidgetPanel({
|
|
|
2467
3042
|
maxLength: 500,
|
|
2468
3043
|
style: { resize: "none" },
|
|
2469
3044
|
value: bw.details.notes,
|
|
2470
|
-
onChange: (e) => bw.setDetails((p) => ({ ...p, notes: e.target.value }))
|
|
3045
|
+
onChange: (e) => bw.setDetails((p) => ({ ...p, notes: e.target.value })),
|
|
3046
|
+
disabled: Boolean(bw.paymentSession)
|
|
2471
3047
|
}
|
|
2472
3048
|
),
|
|
2473
|
-
/* @__PURE__ */ (0,
|
|
3049
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "bw-char-count", children: [
|
|
2474
3050
|
bw.details.notes?.length || 0,
|
|
2475
3051
|
"/500"
|
|
2476
3052
|
] })
|
|
2477
3053
|
] }),
|
|
2478
|
-
bw.selectedService && /* @__PURE__ */ (0,
|
|
2479
|
-
/* @__PURE__ */ (0,
|
|
2480
|
-
/* @__PURE__ */ (0,
|
|
2481
|
-
/* @__PURE__ */ (0,
|
|
2482
|
-
/* @__PURE__ */ (0,
|
|
2483
|
-
/* @__PURE__ */ (0,
|
|
3054
|
+
bw.selectedService && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary", children: [
|
|
3055
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-title", children: "Booking Summary" }),
|
|
3056
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-rows", children: [
|
|
3057
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3058
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Service" }),
|
|
3059
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: bw.selectedService.name })
|
|
2484
3060
|
] }),
|
|
2485
|
-
/* @__PURE__ */ (0,
|
|
2486
|
-
/* @__PURE__ */ (0,
|
|
2487
|
-
/* @__PURE__ */ (0,
|
|
3061
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3062
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Specialist" }),
|
|
3063
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: selectedStaff?.name || "Any Available" })
|
|
2488
3064
|
] }),
|
|
2489
|
-
bw.selectedDate && /* @__PURE__ */ (0,
|
|
2490
|
-
/* @__PURE__ */ (0,
|
|
2491
|
-
/* @__PURE__ */ (0,
|
|
3065
|
+
bw.selectedDate && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3066
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Date" }),
|
|
3067
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: bw.formatDateLabel(bw.selectedDate) })
|
|
2492
3068
|
] }),
|
|
2493
|
-
bw.selectedTime && /* @__PURE__ */ (0,
|
|
2494
|
-
/* @__PURE__ */ (0,
|
|
2495
|
-
/* @__PURE__ */ (0,
|
|
3069
|
+
bw.selectedTime && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3070
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Time" }),
|
|
3071
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: bw.formatTimeLabel(bw.selectedTime) })
|
|
2496
3072
|
] }),
|
|
2497
|
-
bw.selectedService.duration != null && /* @__PURE__ */ (0,
|
|
2498
|
-
/* @__PURE__ */ (0,
|
|
2499
|
-
/* @__PURE__ */ (0,
|
|
3073
|
+
bw.selectedService.duration != null && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3074
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Duration" }),
|
|
3075
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: bw.formatDuration(bw.selectedService.duration) })
|
|
2500
3076
|
] }),
|
|
2501
|
-
bw.
|
|
2502
|
-
/* @__PURE__ */ (0,
|
|
2503
|
-
/* @__PURE__ */ (0,
|
|
3077
|
+
bw.details.locationType && bw.selectedService.locationOptions?.length ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3078
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Location" }),
|
|
3079
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: selectedLocationOption?.label || bw.details.locationType })
|
|
3080
|
+
] }) : null,
|
|
3081
|
+
bw.selectedService.price != null && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row bw-summary-total", children: [
|
|
3082
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Estimated total" }),
|
|
3083
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: formatPrice(Number(bw.selectedService.price)) })
|
|
2504
3084
|
] })
|
|
2505
3085
|
] })
|
|
2506
3086
|
] }),
|
|
2507
|
-
bw.bookingError && /* @__PURE__ */ (0,
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
3087
|
+
bw.bookingError && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-error", children: bw.bookingError }),
|
|
3088
|
+
bw.paymentSession && bw.selectedService?.effectivePaymentSettings ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3089
|
+
StripePaymentSection,
|
|
3090
|
+
{
|
|
3091
|
+
bookingId: bw.paymentSession.bookingId,
|
|
3092
|
+
clientSecret: bw.paymentSession.clientSecret,
|
|
3093
|
+
publishableKey: bw.paymentSession.publishableKey,
|
|
3094
|
+
paymentIntentId: bw.paymentSession.paymentIntentId,
|
|
3095
|
+
customerName: bw.details.name,
|
|
3096
|
+
customerEmail: bw.details.email,
|
|
3097
|
+
customerPhone: bw.details.phone || void 0,
|
|
3098
|
+
amountDueNow: bw.selectedService.effectivePaymentSettings.amountDueNow,
|
|
3099
|
+
fullAmount: bw.selectedService.effectivePaymentSettings.fullAmount,
|
|
3100
|
+
currency: bw.selectedService.effectivePaymentSettings.currency,
|
|
3101
|
+
paymentTerms: bw.selectedService.effectivePaymentSettings.paymentTerms,
|
|
3102
|
+
isSubmitting: bw.isSubmitting,
|
|
3103
|
+
onPaymentAuthorized: bw.finalizePayment
|
|
3104
|
+
}
|
|
3105
|
+
) : null,
|
|
3106
|
+
!canSubmit && !bw.isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-confirm-hint", children: !bw.selectedService ? "Please select a service to confirm booking." : !bw.selectedTime ? "Please select a time to confirm booking." : "" }),
|
|
3107
|
+
!bw.paymentSession ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { type: "submit", className: "bw-confirm-btn", disabled: !canSubmit, children: [
|
|
3108
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: bw.isSubmitting ? bw.paymentRequired ? "Preparing payment..." : "Booking..." : bw.paymentRequired ? "Continue to payment" : "Confirm Booking" }),
|
|
3109
|
+
!bw.isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ArrowRight, { size: 16 })
|
|
3110
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "bw-confirm-hint", children: "Your details are locked while payment is in progress." })
|
|
2513
3111
|
] }) }) }) })
|
|
2514
3112
|
] }) }),
|
|
2515
|
-
/* @__PURE__ */ (0,
|
|
2516
|
-
mobileStep === 4 && bw.selectedService && /* @__PURE__ */ (0,
|
|
2517
|
-
/* @__PURE__ */ (0,
|
|
2518
|
-
/* @__PURE__ */ (0,
|
|
2519
|
-
/* @__PURE__ */ (0,
|
|
2520
|
-
/* @__PURE__ */ (0,
|
|
2521
|
-
/* @__PURE__ */ (0,
|
|
3113
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-footer", children: [
|
|
3114
|
+
mobileStep === 4 && bw.selectedService && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-footer-summary", children: [
|
|
3115
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-title", children: "Booking Summary" }),
|
|
3116
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-rows", children: [
|
|
3117
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3118
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Service" }),
|
|
3119
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: bw.selectedService.name })
|
|
2522
3120
|
] }),
|
|
2523
|
-
/* @__PURE__ */ (0,
|
|
2524
|
-
/* @__PURE__ */ (0,
|
|
2525
|
-
/* @__PURE__ */ (0,
|
|
3121
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3122
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Specialist" }),
|
|
3123
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: selectedStaff?.name || "Any Available" })
|
|
2526
3124
|
] }),
|
|
2527
|
-
bw.selectedDate && /* @__PURE__ */ (0,
|
|
2528
|
-
/* @__PURE__ */ (0,
|
|
2529
|
-
/* @__PURE__ */ (0,
|
|
3125
|
+
bw.selectedDate && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3126
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Date" }),
|
|
3127
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: bw.formatDateLabel(bw.selectedDate) })
|
|
2530
3128
|
] }),
|
|
2531
|
-
bw.selectedTime && /* @__PURE__ */ (0,
|
|
2532
|
-
/* @__PURE__ */ (0,
|
|
2533
|
-
/* @__PURE__ */ (0,
|
|
3129
|
+
bw.selectedTime && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row", children: [
|
|
3130
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Time" }),
|
|
3131
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: bw.formatTimeLabel(bw.selectedTime) })
|
|
2534
3132
|
] }),
|
|
2535
|
-
bw.selectedService.price != null && /* @__PURE__ */ (0,
|
|
2536
|
-
/* @__PURE__ */ (0,
|
|
2537
|
-
/* @__PURE__ */ (0,
|
|
3133
|
+
bw.selectedService.price != null && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-summary-row bw-summary-total", children: [
|
|
3134
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Estimated total" }),
|
|
3135
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "bw-summary-val", children: formatPrice(Number(bw.selectedService.price)) })
|
|
2538
3136
|
] })
|
|
2539
3137
|
] })
|
|
2540
3138
|
] }),
|
|
2541
|
-
/* @__PURE__ */ (0,
|
|
2542
|
-
mobileStep > 1 && /* @__PURE__ */ (0,
|
|
3139
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bw-footer-btns", children: [
|
|
3140
|
+
mobileStep > 1 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2543
3141
|
"button",
|
|
2544
3142
|
{
|
|
2545
3143
|
className: "bw-footer-back",
|
|
2546
3144
|
onClick: () => setMobileStep((s) => s - 1),
|
|
2547
3145
|
"aria-label": "Go back",
|
|
2548
|
-
children: /* @__PURE__ */ (0,
|
|
3146
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ArrowLeft, { size: 20 })
|
|
2549
3147
|
}
|
|
2550
3148
|
),
|
|
2551
|
-
mobileStep < 4 ? /* @__PURE__ */ (0,
|
|
3149
|
+
mobileStep < 4 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2552
3150
|
"button",
|
|
2553
3151
|
{
|
|
2554
3152
|
className: "bw-footer-next",
|
|
2555
3153
|
disabled: mobileStep === 1 && !canAdvanceStep1 || mobileStep === 2 && !canAdvanceStep2 || mobileStep === 3 && !canAdvanceStep3,
|
|
2556
3154
|
onClick: () => setMobileStep((s) => s + 1),
|
|
2557
3155
|
children: [
|
|
2558
|
-
/* @__PURE__ */ (0,
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
3156
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Next" }),
|
|
3157
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ArrowRight, { size: 16 })
|
|
2560
3158
|
]
|
|
2561
3159
|
}
|
|
2562
|
-
) : /* @__PURE__ */ (0,
|
|
3160
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: !bw.paymentSession ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2563
3161
|
"button",
|
|
2564
3162
|
{
|
|
2565
3163
|
className: "bw-footer-next",
|
|
2566
3164
|
disabled: !canSubmit,
|
|
2567
3165
|
onClick: () => formRef.current?.requestSubmit(),
|
|
2568
3166
|
children: [
|
|
2569
|
-
/* @__PURE__ */ (0,
|
|
2570
|
-
!bw.isSubmitting && /* @__PURE__ */ (0,
|
|
3167
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: bw.isSubmitting ? bw.paymentRequired ? "Preparing payment..." : "Booking..." : bw.paymentRequired ? "Continue to payment" : "Confirm Booking" }),
|
|
3168
|
+
!bw.isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.ArrowRight, { size: 16 })
|
|
2571
3169
|
]
|
|
2572
3170
|
}
|
|
2573
|
-
)
|
|
3171
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bw-confirm-hint", children: "Use the secure payment form above to finish booking." }) })
|
|
2574
3172
|
] })
|
|
2575
3173
|
] }),
|
|
2576
|
-
/* @__PURE__ */ (0,
|
|
3174
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: portalRef, className: "bw-portal" })
|
|
2577
3175
|
] });
|
|
2578
3176
|
}
|
|
2579
3177
|
|
|
2580
3178
|
// src/components/AvailabilityPicker.tsx
|
|
2581
|
-
var
|
|
2582
|
-
var
|
|
3179
|
+
var import_react11 = require("react");
|
|
3180
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2583
3181
|
function AvailabilityPicker({
|
|
2584
3182
|
serviceId,
|
|
2585
3183
|
staffId,
|
|
@@ -2588,8 +3186,8 @@ function AvailabilityPicker({
|
|
|
2588
3186
|
className,
|
|
2589
3187
|
onSelect
|
|
2590
3188
|
}) {
|
|
2591
|
-
const [selectedDate, setSelectedDate] = (0,
|
|
2592
|
-
const [selectedTime, setSelectedTime] = (0,
|
|
3189
|
+
const [selectedDate, setSelectedDate] = (0, import_react11.useState)(date || null);
|
|
3190
|
+
const [selectedTime, setSelectedTime] = (0, import_react11.useState)(null);
|
|
2593
3191
|
const { data, isLoading } = useAvailability({
|
|
2594
3192
|
serviceId,
|
|
2595
3193
|
staffId,
|
|
@@ -2597,15 +3195,15 @@ function AvailabilityPicker({
|
|
|
2597
3195
|
days
|
|
2598
3196
|
});
|
|
2599
3197
|
if (isLoading) {
|
|
2600
|
-
return /* @__PURE__ */ (0,
|
|
3198
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "idk-state", children: "Loading availability..." });
|
|
2601
3199
|
}
|
|
2602
3200
|
if (!data?.dates?.length) {
|
|
2603
|
-
return /* @__PURE__ */ (0,
|
|
3201
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "idk-state", children: "No availability found." });
|
|
2604
3202
|
}
|
|
2605
3203
|
const activeDate = selectedDate || data.dates[0]?.date;
|
|
2606
3204
|
const activeSlots = data.dates.find((entry) => entry.date === activeDate)?.slots || [];
|
|
2607
|
-
return /* @__PURE__ */ (0,
|
|
2608
|
-
/* @__PURE__ */ (0,
|
|
3205
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: ["idk-availability", className].filter(Boolean).join(" "), children: [
|
|
3206
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "idk-availability__dates", children: data.dates.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2609
3207
|
"button",
|
|
2610
3208
|
{
|
|
2611
3209
|
type: "button",
|
|
@@ -2618,7 +3216,7 @@ function AvailabilityPicker({
|
|
|
2618
3216
|
},
|
|
2619
3217
|
entry.date
|
|
2620
3218
|
)) }),
|
|
2621
|
-
/* @__PURE__ */ (0,
|
|
3219
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "idk-availability__slots", children: activeSlots.filter((slot) => slot.available).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "idk-state", children: "No slots available." }) : activeSlots.filter((slot) => slot.available).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2622
3220
|
"button",
|
|
2623
3221
|
{
|
|
2624
3222
|
type: "button",
|
|
@@ -2641,22 +3239,22 @@ function AvailabilityPicker({
|
|
|
2641
3239
|
}
|
|
2642
3240
|
|
|
2643
3241
|
// src/components/ServicePicker.tsx
|
|
2644
|
-
var
|
|
3242
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2645
3243
|
function ServicePicker({
|
|
2646
3244
|
services,
|
|
2647
3245
|
selectedId,
|
|
2648
3246
|
onSelect,
|
|
2649
3247
|
className
|
|
2650
3248
|
}) {
|
|
2651
|
-
return /* @__PURE__ */ (0,
|
|
3249
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: services.map((service) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2652
3250
|
"button",
|
|
2653
3251
|
{
|
|
2654
3252
|
type: "button",
|
|
2655
3253
|
className: selectedId === service.id ? "is-active" : void 0,
|
|
2656
3254
|
onClick: () => onSelect?.(service),
|
|
2657
3255
|
children: [
|
|
2658
|
-
/* @__PURE__ */ (0,
|
|
2659
|
-
/* @__PURE__ */ (0,
|
|
3256
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: service.name }),
|
|
3257
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("small", { children: [
|
|
2660
3258
|
service.duration,
|
|
2661
3259
|
" min"
|
|
2662
3260
|
] })
|
|
@@ -2667,22 +3265,22 @@ function ServicePicker({
|
|
|
2667
3265
|
}
|
|
2668
3266
|
|
|
2669
3267
|
// src/components/StaffPicker.tsx
|
|
2670
|
-
var
|
|
3268
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2671
3269
|
function StaffPicker({
|
|
2672
3270
|
staff,
|
|
2673
3271
|
selectedId,
|
|
2674
3272
|
onSelect,
|
|
2675
3273
|
className
|
|
2676
3274
|
}) {
|
|
2677
|
-
return /* @__PURE__ */ (0,
|
|
3275
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: staff.map((member) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2678
3276
|
"button",
|
|
2679
3277
|
{
|
|
2680
3278
|
type: "button",
|
|
2681
3279
|
className: selectedId === member.id ? "is-active" : void 0,
|
|
2682
3280
|
onClick: () => onSelect?.(member),
|
|
2683
3281
|
children: [
|
|
2684
|
-
/* @__PURE__ */ (0,
|
|
2685
|
-
/* @__PURE__ */ (0,
|
|
3282
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: member.name }),
|
|
3283
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("small", { children: member.role || "Staff" })
|
|
2686
3284
|
]
|
|
2687
3285
|
},
|
|
2688
3286
|
member.id
|
|
@@ -2690,14 +3288,14 @@ function StaffPicker({
|
|
|
2690
3288
|
}
|
|
2691
3289
|
|
|
2692
3290
|
// src/components/DatePicker.tsx
|
|
2693
|
-
var
|
|
3291
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2694
3292
|
function DatePicker({
|
|
2695
3293
|
dates,
|
|
2696
3294
|
selectedDate,
|
|
2697
3295
|
onSelect,
|
|
2698
3296
|
className
|
|
2699
3297
|
}) {
|
|
2700
|
-
return /* @__PURE__ */ (0,
|
|
3298
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: dates.map((date) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2701
3299
|
"button",
|
|
2702
3300
|
{
|
|
2703
3301
|
type: "button",
|
|
@@ -2710,14 +3308,14 @@ function DatePicker({
|
|
|
2710
3308
|
}
|
|
2711
3309
|
|
|
2712
3310
|
// src/components/TimePicker.tsx
|
|
2713
|
-
var
|
|
3311
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2714
3312
|
function TimePicker({
|
|
2715
3313
|
slots,
|
|
2716
3314
|
selectedTime,
|
|
2717
3315
|
onSelect,
|
|
2718
3316
|
className
|
|
2719
3317
|
}) {
|
|
2720
|
-
return /* @__PURE__ */ (0,
|
|
3318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2721
3319
|
"button",
|
|
2722
3320
|
{
|
|
2723
3321
|
type: "button",
|