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