@bigz-app/booking-widget 1.4.5 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/booking-widget.js +625 -74
- package/dist/booking-widget.js.map +1 -1
- package/dist/components/UniversalBookingWidget.d.ts +18 -0
- package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
- package/dist/components/booking/MolliePaymentForm.d.ts +11 -1
- package/dist/components/booking/MolliePaymentForm.d.ts.map +1 -1
- package/dist/components/booking/StripePaymentForm.d.ts +11 -1
- package/dist/components/booking/StripePaymentForm.d.ts.map +1 -1
- package/dist/components/events/EventTypeSelection.d.ts +15 -0
- package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
- package/dist/components/events/FreeformSelection.d.ts +49 -0
- package/dist/components/events/FreeformSelection.d.ts.map +1 -0
- package/dist/components/events/index.d.ts +1 -0
- package/dist/components/events/index.d.ts.map +1 -1
- package/dist/components/voucher/VoucherIntegration.d.ts +3 -2
- package/dist/components/voucher/VoucherIntegration.d.ts.map +1 -1
- package/dist/components/voucher/VoucherPurchaseForm.d.ts +8 -2
- package/dist/components/voucher/VoucherPurchaseForm.d.ts.map +1 -1
- package/dist/components/voucher/index.d.ts +1 -1
- package/dist/components/voucher/index.d.ts.map +1 -1
- package/dist/components/voucher/useVoucherConfig.d.ts +6 -0
- package/dist/components/voucher/useVoucherConfig.d.ts.map +1 -1
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/sv.d.ts.map +1 -1
- package/dist/index.cjs +625 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +625 -74
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/booking-widget.js
CHANGED
|
@@ -207,6 +207,10 @@
|
|
|
207
207
|
"booking.stepUpsells": "Extras",
|
|
208
208
|
"booking.stepDetails": "Angaben",
|
|
209
209
|
"booking.stepPayment": "Zahlung",
|
|
210
|
+
"booking.steps": "{{count}} Schritt(e)",
|
|
211
|
+
"booking.freeformUnitsAvailable": "{{count}} Einheiten verfuegbar",
|
|
212
|
+
"booking.freeformNotAvailable": "Das gewaehlte Zeitfenster ist nicht verfuegbar",
|
|
213
|
+
"booking.freeformMissingConfig": "Die Freeform-Konfiguration fuer diesen Event-Typ fehlt.",
|
|
210
214
|
// Booking summary
|
|
211
215
|
"summary.title": "Buchungszusammenfassung",
|
|
212
216
|
"summary.subtotal": "Zwischensumme:",
|
|
@@ -262,6 +266,13 @@
|
|
|
262
266
|
"voucher.monetaryTypeDesc": "Beliebigen Betrag wählen",
|
|
263
267
|
"voucher.eventType": "Erlebnisgutschein",
|
|
264
268
|
"voucher.eventTypeDesc": "Für ein bestimmtes Erlebnis",
|
|
269
|
+
"voucher.categoryType": "Kategoriegutschein",
|
|
270
|
+
"voucher.categoryTypeDesc": "Für ein beliebiges Erlebnis einer Kategorie",
|
|
271
|
+
"voucher.categoryVoucher": "Kategoriegutschein",
|
|
272
|
+
"voucher.selectCategory": "Kategorie wählen",
|
|
273
|
+
"voucher.categoryLabel": "Kategorie",
|
|
274
|
+
"voucher.selectCategoryPlaceholder": "Kategorie auswählen...",
|
|
275
|
+
"voucher.categoryAmount": "Gutscheinwert",
|
|
265
276
|
"voucher.selectAmount": "Betrag wählen",
|
|
266
277
|
"voucher.customAmount": "Oder eigenen Betrag eingeben",
|
|
267
278
|
"voucher.amountRange": "Betrag muss zwischen {{min}} und {{max}} liegen",
|
|
@@ -515,6 +526,10 @@
|
|
|
515
526
|
"booking.stepUpsells": "Extras",
|
|
516
527
|
"booking.stepDetails": "Details",
|
|
517
528
|
"booking.stepPayment": "Payment",
|
|
529
|
+
"booking.steps": "{{count}} step(s)",
|
|
530
|
+
"booking.freeformUnitsAvailable": "{{count}} units available",
|
|
531
|
+
"booking.freeformNotAvailable": "Selected window is not available",
|
|
532
|
+
"booking.freeformMissingConfig": "Missing freeform configuration for this event type.",
|
|
518
533
|
// Booking summary
|
|
519
534
|
"summary.title": "Booking Summary",
|
|
520
535
|
"summary.subtotal": "Subtotal:",
|
|
@@ -570,6 +585,13 @@
|
|
|
570
585
|
"voucher.monetaryTypeDesc": "Choose any amount",
|
|
571
586
|
"voucher.eventType": "Event Voucher",
|
|
572
587
|
"voucher.eventTypeDesc": "For a specific experience",
|
|
588
|
+
"voucher.categoryType": "Category Voucher",
|
|
589
|
+
"voucher.categoryTypeDesc": "For any experience in a category",
|
|
590
|
+
"voucher.categoryVoucher": "Category Voucher",
|
|
591
|
+
"voucher.selectCategory": "Select Category",
|
|
592
|
+
"voucher.categoryLabel": "Category",
|
|
593
|
+
"voucher.selectCategoryPlaceholder": "Choose a category...",
|
|
594
|
+
"voucher.categoryAmount": "Voucher Value",
|
|
573
595
|
"voucher.selectAmount": "Select Amount",
|
|
574
596
|
"voucher.customAmount": "Or enter custom amount",
|
|
575
597
|
"voucher.amountRange": "Amount must be between {{min}} and {{max}}",
|
|
@@ -860,6 +882,13 @@
|
|
|
860
882
|
"voucher.monetaryTypeDesc": "Elige cualquier cantidad",
|
|
861
883
|
"voucher.eventType": "Vale de experiencia",
|
|
862
884
|
"voucher.eventTypeDesc": "Para una experiencia específica",
|
|
885
|
+
"voucher.categoryType": "Vale de categoría",
|
|
886
|
+
"voucher.categoryTypeDesc": "Para cualquier experiencia de una categoría",
|
|
887
|
+
"voucher.categoryVoucher": "Vale de categoría",
|
|
888
|
+
"voucher.selectCategory": "Seleccionar categoría",
|
|
889
|
+
"voucher.categoryLabel": "Categoría",
|
|
890
|
+
"voucher.selectCategoryPlaceholder": "Elige una categoría...",
|
|
891
|
+
"voucher.categoryAmount": "Valor del vale",
|
|
863
892
|
"voucher.selectAmount": "Selecciona el importe",
|
|
864
893
|
"voucher.customAmount": "O introduce un importe personalizado",
|
|
865
894
|
"voucher.amountRange": "El importe debe estar entre {{min}} y {{max}}",
|
|
@@ -1150,6 +1179,13 @@
|
|
|
1150
1179
|
"voucher.monetaryTypeDesc": "Escolha qualquer quantia",
|
|
1151
1180
|
"voucher.eventType": "Vale de experiência",
|
|
1152
1181
|
"voucher.eventTypeDesc": "Para uma experiência específica",
|
|
1182
|
+
"voucher.categoryType": "Vale de categoria",
|
|
1183
|
+
"voucher.categoryTypeDesc": "Para qualquer experiência de uma categoria",
|
|
1184
|
+
"voucher.categoryVoucher": "Vale de categoria",
|
|
1185
|
+
"voucher.selectCategory": "Selecionar categoria",
|
|
1186
|
+
"voucher.categoryLabel": "Categoria",
|
|
1187
|
+
"voucher.selectCategoryPlaceholder": "Escolha uma categoria...",
|
|
1188
|
+
"voucher.categoryAmount": "Valor do vale",
|
|
1153
1189
|
"voucher.selectAmount": "Selecione o valor",
|
|
1154
1190
|
"voucher.customAmount": "Ou introduza um valor personalizado",
|
|
1155
1191
|
"voucher.amountRange": "O valor deve estar entre {{min}} e {{max}}",
|
|
@@ -1440,6 +1476,13 @@
|
|
|
1440
1476
|
"voucher.monetaryTypeDesc": "Välj valfritt belopp",
|
|
1441
1477
|
"voucher.eventType": "Upplevelsebevis",
|
|
1442
1478
|
"voucher.eventTypeDesc": "För en specifik upplevelse",
|
|
1479
|
+
"voucher.categoryType": "Kategoripresent",
|
|
1480
|
+
"voucher.categoryTypeDesc": "För alla upplevelser i en kategori",
|
|
1481
|
+
"voucher.categoryVoucher": "Kategoripresent",
|
|
1482
|
+
"voucher.selectCategory": "Välj kategori",
|
|
1483
|
+
"voucher.categoryLabel": "Kategori",
|
|
1484
|
+
"voucher.selectCategoryPlaceholder": "Välj en kategori...",
|
|
1485
|
+
"voucher.categoryAmount": "Presentkortsvärde",
|
|
1443
1486
|
"voucher.selectAmount": "Välj belopp",
|
|
1444
1487
|
"voucher.customAmount": "Eller ange eget belopp",
|
|
1445
1488
|
"voucher.amountRange": "Beloppet måste vara mellan {{min}} och {{max}}",
|
|
@@ -6478,7 +6521,7 @@
|
|
|
6478
6521
|
document.head.appendChild(script);
|
|
6479
6522
|
});
|
|
6480
6523
|
}
|
|
6481
|
-
function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess: _onSuccess, onError, upsellSelections = [], mollieProfileId, mollieTestmode, }) {
|
|
6524
|
+
function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess: _onSuccess, onError, upsellSelections = [], mollieProfileId, mollieTestmode, freeformSelection, }) {
|
|
6482
6525
|
const t = useTranslations();
|
|
6483
6526
|
const { locale } = useLocale();
|
|
6484
6527
|
const [isLoading, setIsLoading] = d$1(false);
|
|
@@ -6492,7 +6535,9 @@
|
|
|
6492
6535
|
const cardFormRef = A$2(null);
|
|
6493
6536
|
const cardContainerRef = A$2(null);
|
|
6494
6537
|
const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
|
|
6495
|
-
const isFullyCoveredByGiftCards =
|
|
6538
|
+
const isFullyCoveredByGiftCards = freeformSelection
|
|
6539
|
+
? false
|
|
6540
|
+
: isGiftCardFullyCovered(giftCards, eventDetails?.price || 0, participantCount, discountCode?.discountAmount || 0);
|
|
6496
6541
|
const isCreditCard = selectedMethod === "creditcard";
|
|
6497
6542
|
y$1(() => {
|
|
6498
6543
|
if (isFullyCoveredByGiftCards)
|
|
@@ -6602,6 +6647,9 @@
|
|
|
6602
6647
|
return (u$2(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: _onSuccess, onError: onError, upsellSelections: upsellSelections }));
|
|
6603
6648
|
}
|
|
6604
6649
|
const validateBeforePayment = () => {
|
|
6650
|
+
if (freeformSelection) {
|
|
6651
|
+
return null;
|
|
6652
|
+
}
|
|
6605
6653
|
const participantCount = formData.participants.filter((p) => p.name?.trim()).length;
|
|
6606
6654
|
const availableSpots = eventDetails.availableSpots || 0;
|
|
6607
6655
|
if (participantCount > availableSpots) {
|
|
@@ -6613,21 +6661,41 @@
|
|
|
6613
6661
|
return null;
|
|
6614
6662
|
};
|
|
6615
6663
|
const buildRequestData = (cardToken) => ({
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6664
|
+
...(freeformSelection
|
|
6665
|
+
? {
|
|
6666
|
+
eventTypeId: freeformSelection.eventTypeId,
|
|
6667
|
+
organizationId: config.organizationId,
|
|
6668
|
+
start: freeformSelection.start,
|
|
6669
|
+
end: freeformSelection.end,
|
|
6670
|
+
requestedUnits: freeformSelection.requestedUnits,
|
|
6671
|
+
amount: Math.round(totalAmount),
|
|
6672
|
+
currency: "EUR",
|
|
6673
|
+
customerName: freeformSelection.customerName.trim(),
|
|
6674
|
+
customerEmail: freeformSelection.customerEmail.trim(),
|
|
6675
|
+
customerPhone: freeformSelection.customerPhone?.trim(),
|
|
6676
|
+
comment: freeformSelection.comment?.trim(),
|
|
6677
|
+
locale,
|
|
6678
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
6679
|
+
...(cardToken && { cardToken }),
|
|
6680
|
+
...(selectedMethod && !cardToken && { method: selectedMethod }),
|
|
6681
|
+
}
|
|
6682
|
+
: {
|
|
6683
|
+
eventInstanceId: config.eventInstanceId || eventDetails.id,
|
|
6684
|
+
organizationId: config.organizationId,
|
|
6685
|
+
amount: Math.round(totalAmount),
|
|
6686
|
+
currency: "EUR",
|
|
6687
|
+
participants: formData.participants.filter((p) => p.name?.trim()),
|
|
6688
|
+
discountCode: discountCode?.code,
|
|
6689
|
+
giftCardCodes: giftCards?.map((gc) => gc.code) || [],
|
|
6690
|
+
customerName: formData.customerName?.trim(),
|
|
6691
|
+
customerEmail: formData.customerEmail?.trim(),
|
|
6692
|
+
customerPhone: formData.customerPhone?.trim(),
|
|
6693
|
+
comment: formData.comment?.trim(),
|
|
6694
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
6695
|
+
...(cardToken && { cardToken }),
|
|
6696
|
+
...(selectedMethod && !cardToken && { method: selectedMethod }),
|
|
6697
|
+
...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
|
|
6698
|
+
}),
|
|
6631
6699
|
});
|
|
6632
6700
|
const handleCardPayment = async () => {
|
|
6633
6701
|
if (!mollieRef.current)
|
|
@@ -6645,7 +6713,9 @@
|
|
|
6645
6713
|
setPaymentError(tokenError?.message || t("error.createPayment"));
|
|
6646
6714
|
return;
|
|
6647
6715
|
}
|
|
6648
|
-
const response = await fetch(getApiUrl(config.apiBaseUrl,
|
|
6716
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, freeformSelection
|
|
6717
|
+
? "/booking/create-freeform-mollie-payment"
|
|
6718
|
+
: "/booking/create-mollie-payment"), {
|
|
6649
6719
|
method: "POST",
|
|
6650
6720
|
headers: createApiHeaders(config, locale),
|
|
6651
6721
|
body: JSON.stringify(createRequestBody(config, buildRequestData(token))),
|
|
@@ -6681,7 +6751,9 @@
|
|
|
6681
6751
|
setPaymentError(validationError);
|
|
6682
6752
|
return;
|
|
6683
6753
|
}
|
|
6684
|
-
const response = await fetch(getApiUrl(config.apiBaseUrl,
|
|
6754
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, freeformSelection
|
|
6755
|
+
? "/booking/create-freeform-mollie-payment"
|
|
6756
|
+
: "/booking/create-mollie-payment"), {
|
|
6685
6757
|
method: "POST",
|
|
6686
6758
|
headers: createApiHeaders(config, locale),
|
|
6687
6759
|
body: JSON.stringify(createRequestBody(config, buildRequestData())),
|
|
@@ -6709,7 +6781,7 @@
|
|
|
6709
6781
|
? t("button.processingPayment")
|
|
6710
6782
|
: selectedMethodData
|
|
6711
6783
|
? t("payment.payWithMethod", { method: selectedMethodData.description })
|
|
6712
|
-
: totalAmount < eventDetails.price * formData.participants.filter((p) => p.name?.trim()).length
|
|
6784
|
+
: !freeformSelection && totalAmount < eventDetails.price * formData.participants.filter((p) => p.name?.trim()).length
|
|
6713
6785
|
? t("button.depositAndBook")
|
|
6714
6786
|
: t("button.bookNow");
|
|
6715
6787
|
const isPayDisabled = isLoading || !selectedMethod || (isCreditCard && !isMollieReady);
|
|
@@ -7016,12 +7088,18 @@
|
|
|
7016
7088
|
};
|
|
7017
7089
|
|
|
7018
7090
|
// Inner component that uses the Stripe hooks
|
|
7019
|
-
function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onError, }) {
|
|
7091
|
+
function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onError, freeformSelection, }) {
|
|
7020
7092
|
const t = useTranslations();
|
|
7021
|
-
const
|
|
7022
|
-
const
|
|
7023
|
-
|
|
7093
|
+
const isFreeform = Boolean(freeformSelection);
|
|
7094
|
+
const participantCount = isFreeform
|
|
7095
|
+
? 1
|
|
7096
|
+
: formData.participants.filter((p) => p.name.trim()).length;
|
|
7097
|
+
const fullAmount = isFreeform ? totalAmount : eventDetails.price * participantCount;
|
|
7098
|
+
const submitLabel = !isFreeform && totalAmount < fullAmount ? t("button.depositAndBook") : t("button.bookNow");
|
|
7024
7099
|
return (u$2(StripeElementsPaymentForm, { onValidate: () => {
|
|
7100
|
+
if (isFreeform) {
|
|
7101
|
+
return null;
|
|
7102
|
+
}
|
|
7025
7103
|
const availableSpots = eventDetails.availableSpots || 0;
|
|
7026
7104
|
if (participantCount > availableSpots) {
|
|
7027
7105
|
return t("payment.tooManyParticipants", { count: participantCount, available: availableSpots });
|
|
@@ -7043,7 +7121,7 @@
|
|
|
7043
7121
|
},
|
|
7044
7122
|
}, submitContent: u$2(k$3, { children: submitLabel }), loadingContent: u$2(k$3, { children: [spinner("var(--bw-surface-color)"), " ", t("button.processingPayment")] }) }));
|
|
7045
7123
|
}
|
|
7046
|
-
function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripeAppearance, upsellSelections = [], }) {
|
|
7124
|
+
function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripeAppearance, upsellSelections = [], freeformSelection, }) {
|
|
7047
7125
|
const t = useTranslations();
|
|
7048
7126
|
const { locale } = useLocale();
|
|
7049
7127
|
const [clientSecret, setClientSecret] = d$1(null);
|
|
@@ -7063,7 +7141,9 @@
|
|
|
7063
7141
|
return loadStripe(systemConfig.stripePublishableKey, stripeOptions);
|
|
7064
7142
|
}, [systemConfig?.stripePublishableKey, systemConfig?.connectedAccountId, locale]);
|
|
7065
7143
|
const storageKey = typeof window !== "undefined"
|
|
7066
|
-
?
|
|
7144
|
+
? freeformSelection
|
|
7145
|
+
? `bw_pi_freeform_${config?.organizationId}_${freeformSelection.eventTypeId}_${freeformSelection.start}_${freeformSelection.end}_${freeformSelection.requestedUnits}`
|
|
7146
|
+
: `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
|
|
7067
7147
|
: "";
|
|
7068
7148
|
const PAYMENT_INTENT_TTL = 24 * 60 * 60 * 1000;
|
|
7069
7149
|
function loadPersistedPaymentIntent() {
|
|
@@ -7118,48 +7198,82 @@
|
|
|
7118
7198
|
}, [paymentIntentId]);
|
|
7119
7199
|
y$1(() => {
|
|
7120
7200
|
const createStripePayment = async () => {
|
|
7121
|
-
if (!systemConfig || !eventDetails
|
|
7201
|
+
if (!systemConfig || !eventDetails) {
|
|
7202
|
+
return;
|
|
7203
|
+
}
|
|
7204
|
+
if (freeformSelection) {
|
|
7205
|
+
if (!freeformSelection.customerEmail?.trim() ||
|
|
7206
|
+
!freeformSelection.customerName?.trim() ||
|
|
7207
|
+
!freeformSelection.start ||
|
|
7208
|
+
!freeformSelection.end) {
|
|
7209
|
+
return;
|
|
7210
|
+
}
|
|
7211
|
+
}
|
|
7212
|
+
else if (!formData.participants?.length) {
|
|
7122
7213
|
return;
|
|
7123
7214
|
}
|
|
7124
7215
|
const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
|
|
7125
|
-
if (
|
|
7216
|
+
if (!freeformSelection &&
|
|
7217
|
+
(participantCount === 0 || !formData.customerEmail?.trim() || !formData.customerName?.trim())) {
|
|
7126
7218
|
return;
|
|
7127
7219
|
}
|
|
7128
7220
|
setIsCreatingPaymentIntent(true);
|
|
7129
7221
|
setPaymentError(null);
|
|
7130
7222
|
try {
|
|
7131
|
-
const
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7223
|
+
const endpoint = freeformSelection
|
|
7224
|
+
? "/booking/create-freeform-stripe-payment"
|
|
7225
|
+
: "/booking/create-stripe-payment";
|
|
7226
|
+
const requestData = freeformSelection
|
|
7227
|
+
? {
|
|
7228
|
+
eventTypeId: freeformSelection.eventTypeId,
|
|
7229
|
+
organizationId: config.organizationId,
|
|
7230
|
+
start: freeformSelection.start,
|
|
7231
|
+
end: freeformSelection.end,
|
|
7232
|
+
requestedUnits: freeformSelection.requestedUnits,
|
|
7233
|
+
amount: Math.round(totalAmount),
|
|
7234
|
+
currency: "eur",
|
|
7235
|
+
customerName: freeformSelection.customerName.trim(),
|
|
7236
|
+
customerEmail: freeformSelection.customerEmail.trim(),
|
|
7237
|
+
customerPhone: freeformSelection.customerPhone?.trim(),
|
|
7238
|
+
comment: freeformSelection.comment?.trim(),
|
|
7239
|
+
locale,
|
|
7240
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
7241
|
+
...(paymentIntentId && { paymentIntentId }),
|
|
7242
|
+
}
|
|
7243
|
+
: {
|
|
7244
|
+
eventInstanceId: config.eventInstanceId || eventDetails.id,
|
|
7245
|
+
organizationId: config.organizationId,
|
|
7246
|
+
amount: Math.round(totalAmount),
|
|
7247
|
+
currency: "eur",
|
|
7248
|
+
participants: formData.participants.filter((p) => p.name?.trim()),
|
|
7249
|
+
discountCode: discountCode?.code,
|
|
7250
|
+
giftCardCodes: giftCards?.map((gc) => gc.code) || [],
|
|
7251
|
+
customerName: formData.customerName?.trim(),
|
|
7252
|
+
customerEmail: formData.customerEmail?.trim(),
|
|
7253
|
+
customerPhone: formData.customerPhone?.trim(),
|
|
7254
|
+
comment: formData.comment?.trim(),
|
|
7255
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
7256
|
+
...(paymentIntentId && { paymentIntentId }),
|
|
7257
|
+
...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
|
|
7258
|
+
};
|
|
7150
7259
|
if (!requestData.organizationId) {
|
|
7151
7260
|
throw new Error("Organization ID is required");
|
|
7152
7261
|
}
|
|
7153
|
-
if (
|
|
7262
|
+
if (requestData.amount === undefined || requestData.amount < 0) {
|
|
7154
7263
|
throw new Error("Valid amount is required");
|
|
7155
7264
|
}
|
|
7156
|
-
if (!requestData
|
|
7265
|
+
if (!freeformSelection && "eventInstanceId" in requestData && !requestData.eventInstanceId) {
|
|
7266
|
+
throw new Error("Event instance ID is required");
|
|
7267
|
+
}
|
|
7268
|
+
if (!freeformSelection &&
|
|
7269
|
+
"participants" in requestData &&
|
|
7270
|
+
(!requestData.participants || requestData.participants.length === 0)) {
|
|
7157
7271
|
throw new Error("At least one participant is required");
|
|
7158
7272
|
}
|
|
7159
7273
|
if (!requestData.customerEmail) {
|
|
7160
7274
|
throw new Error("Customer email is required");
|
|
7161
7275
|
}
|
|
7162
|
-
const response = await fetch(getApiUrl(config.apiBaseUrl,
|
|
7276
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, endpoint), {
|
|
7163
7277
|
method: "POST",
|
|
7164
7278
|
headers: createApiHeaders(config, locale),
|
|
7165
7279
|
body: JSON.stringify(createRequestBody(config, requestData)),
|
|
@@ -7204,9 +7318,13 @@
|
|
|
7204
7318
|
giftCards,
|
|
7205
7319
|
config,
|
|
7206
7320
|
upsellSelections,
|
|
7321
|
+
freeformSelection,
|
|
7322
|
+
locale,
|
|
7207
7323
|
]);
|
|
7208
7324
|
const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
|
|
7209
|
-
const isFullyCoveredByGiftCards =
|
|
7325
|
+
const isFullyCoveredByGiftCards = freeformSelection
|
|
7326
|
+
? false
|
|
7327
|
+
: isGiftCardFullyCovered(giftCards, eventDetails?.price || 0, participantCount, discountCode?.discountAmount || 0);
|
|
7210
7328
|
if (isFullyCoveredByGiftCards && totalAmount <= 0) {
|
|
7211
7329
|
return (u$2(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: onSuccess, onError: onError, upsellSelections: upsellSelections }));
|
|
7212
7330
|
}
|
|
@@ -7245,7 +7363,7 @@
|
|
|
7245
7363
|
setPaymentIntentId(null);
|
|
7246
7364
|
setClientSecret(null);
|
|
7247
7365
|
onSuccess(result);
|
|
7248
|
-
}, onError: onError }) }));
|
|
7366
|
+
}, onError: onError, ...(freeformSelection ? { freeformSelection } : {}) }) }));
|
|
7249
7367
|
}
|
|
7250
7368
|
|
|
7251
7369
|
const HOLD_DURATION_SECONDS = 15 * 60;
|
|
@@ -13505,20 +13623,22 @@
|
|
|
13505
13623
|
const labelStyles = formStyles.label;
|
|
13506
13624
|
const inputStyles = formStyles.input;
|
|
13507
13625
|
const errorTextStyles = formStyles.error;
|
|
13508
|
-
function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClose, onSuccess, onError, systemConfig, preselectedEventTypeId, isLoadingEventTypes = false, }) {
|
|
13626
|
+
function VoucherPurchaseForm({ config, voucherConfig, eventTypes, categories, isOpen, onClose, onSuccess, onError, systemConfig, preselectedEventTypeId, isLoadingEventTypes = false, }) {
|
|
13509
13627
|
const t = useTranslations();
|
|
13510
13628
|
const { locale } = useLocale();
|
|
13511
13629
|
const paymentProvider = systemConfig?.paymentProvider ?? "stripe";
|
|
13512
13630
|
// Form state
|
|
13513
|
-
// Show voucher type selection if both monetary and event vouchers are allowed
|
|
13514
|
-
// The event type selection will handle the case when no event types are available
|
|
13515
13631
|
const canUseMonetaryVoucherType = voucherConfig.allowMonetaryVouchers !== false;
|
|
13516
13632
|
const canUseEventVoucherType = voucherConfig.allowEventVouchers;
|
|
13517
|
-
const
|
|
13633
|
+
const canUseCategoryVoucherType = canUseEventVoucherType && categories.length > 0;
|
|
13634
|
+
const availableTypeCount = [canUseMonetaryVoucherType, canUseEventVoucherType, canUseCategoryVoucherType].filter(Boolean).length;
|
|
13635
|
+
const showVoucherTypeSelection = availableTypeCount > 1 && !isLoadingEventTypes;
|
|
13518
13636
|
const [voucherType, setVoucherType] = d$1(canUseMonetaryVoucherType ? "monetary" : "event");
|
|
13519
13637
|
const [monetaryAmount, setMonetaryAmount] = d$1(voucherConfig.monetaryPresets[2] || 10000);
|
|
13520
13638
|
const [selectedEventTypeId, setSelectedEventTypeId] = d$1(null);
|
|
13521
13639
|
const [eventQuantity, setEventQuantity] = d$1(1);
|
|
13640
|
+
const [selectedCategoryId, setSelectedCategoryId] = d$1(null);
|
|
13641
|
+
const [categoryAmount, setCategoryAmount] = d$1(10000);
|
|
13522
13642
|
const [recipientName, setRecipientName] = d$1("");
|
|
13523
13643
|
const [message, setMessage] = d$1("");
|
|
13524
13644
|
const [customerName, setCustomerName] = d$1("");
|
|
@@ -13578,6 +13698,9 @@
|
|
|
13578
13698
|
if (voucherType === "monetary") {
|
|
13579
13699
|
return monetaryAmount;
|
|
13580
13700
|
}
|
|
13701
|
+
else if (voucherType === "category") {
|
|
13702
|
+
return categoryAmount;
|
|
13703
|
+
}
|
|
13581
13704
|
else {
|
|
13582
13705
|
const eventType = eventTypes.find((et) => et.id === selectedEventTypeId);
|
|
13583
13706
|
if (eventType) {
|
|
@@ -13585,7 +13708,7 @@
|
|
|
13585
13708
|
}
|
|
13586
13709
|
return 0;
|
|
13587
13710
|
}
|
|
13588
|
-
}, [voucherType, monetaryAmount, selectedEventTypeId, eventQuantity, eventTypes]);
|
|
13711
|
+
}, [voucherType, monetaryAmount, selectedEventTypeId, eventQuantity, eventTypes, categoryAmount]);
|
|
13589
13712
|
const selectedEventType = T$2(() => {
|
|
13590
13713
|
return eventTypes.find((et) => et.id === selectedEventTypeId);
|
|
13591
13714
|
}, [eventTypes, selectedEventTypeId]);
|
|
@@ -13594,6 +13717,8 @@
|
|
|
13594
13717
|
voucherType,
|
|
13595
13718
|
selectedEventTypeId,
|
|
13596
13719
|
eventQuantity,
|
|
13720
|
+
selectedCategoryId,
|
|
13721
|
+
categoryAmount,
|
|
13597
13722
|
recipientName: recipientName.trim(),
|
|
13598
13723
|
message: message.trim(),
|
|
13599
13724
|
customerName: customerName.trim(),
|
|
@@ -13603,6 +13728,8 @@
|
|
|
13603
13728
|
voucherType,
|
|
13604
13729
|
selectedEventTypeId,
|
|
13605
13730
|
eventQuantity,
|
|
13731
|
+
selectedCategoryId,
|
|
13732
|
+
categoryAmount,
|
|
13606
13733
|
recipientName,
|
|
13607
13734
|
message,
|
|
13608
13735
|
customerName,
|
|
@@ -13621,24 +13748,37 @@
|
|
|
13621
13748
|
return false;
|
|
13622
13749
|
if (voucherType === "event" && !selectedEventTypeId)
|
|
13623
13750
|
return false;
|
|
13751
|
+
if (voucherType === "category" && !selectedCategoryId)
|
|
13752
|
+
return false;
|
|
13624
13753
|
return true;
|
|
13625
|
-
}, [customerName, customerEmail, acceptTerms, totalAmount,
|
|
13754
|
+
}, [customerName, customerEmail, acceptTerms, totalAmount, voucherType, selectedEventTypeId, selectedCategoryId]);
|
|
13626
13755
|
const defaultMonetaryAmount = T$2(() => voucherConfig.monetaryPresets[2] || voucherConfig.monetaryPresets[0] || 1000, [voucherConfig.monetaryPresets]);
|
|
13627
13756
|
const handleVoucherTypeChange = q$2((nextType) => {
|
|
13628
13757
|
setVoucherType(nextType);
|
|
13629
13758
|
setPaymentError(null);
|
|
13630
13759
|
if (nextType === "event") {
|
|
13631
|
-
// Keep voucher type mutually exclusive: entering event flow resets amount selection state.
|
|
13632
13760
|
setMonetaryAmount(defaultMonetaryAmount);
|
|
13761
|
+
setSelectedCategoryId(null);
|
|
13762
|
+
setCategoryAmount(10000);
|
|
13633
13763
|
if (eventTypes.length === 1) {
|
|
13634
13764
|
setSelectedEventTypeId(eventTypes[0]?.id ?? null);
|
|
13635
13765
|
}
|
|
13636
13766
|
return;
|
|
13637
13767
|
}
|
|
13638
|
-
|
|
13768
|
+
if (nextType === "category") {
|
|
13769
|
+
setMonetaryAmount(defaultMonetaryAmount);
|
|
13770
|
+
setSelectedEventTypeId(null);
|
|
13771
|
+
setEventQuantity(1);
|
|
13772
|
+
if (categories.length === 1) {
|
|
13773
|
+
setSelectedCategoryId(categories[0]?.id ?? null);
|
|
13774
|
+
}
|
|
13775
|
+
return;
|
|
13776
|
+
}
|
|
13639
13777
|
setSelectedEventTypeId(null);
|
|
13640
13778
|
setEventQuantity(1);
|
|
13641
|
-
|
|
13779
|
+
setSelectedCategoryId(null);
|
|
13780
|
+
setCategoryAmount(10000);
|
|
13781
|
+
}, [defaultMonetaryAmount, eventTypes, categories]);
|
|
13642
13782
|
y$1(() => {
|
|
13643
13783
|
if (!canUseMonetaryVoucherType && voucherType === "monetary") {
|
|
13644
13784
|
setVoucherType("event");
|
|
@@ -13679,9 +13819,12 @@
|
|
|
13679
13819
|
organizationId: config.organizationId,
|
|
13680
13820
|
amount: totalAmount,
|
|
13681
13821
|
currency: "eur",
|
|
13682
|
-
voucherType,
|
|
13683
|
-
monetaryValue: voucherType === "monetary" ? totalAmount
|
|
13822
|
+
voucherType: voucherType === "category" ? "monetary" : voucherType,
|
|
13823
|
+
monetaryValue: voucherType === "monetary" ? totalAmount
|
|
13824
|
+
: voucherType === "category" ? categoryAmount
|
|
13825
|
+
: undefined,
|
|
13684
13826
|
eventTypeId: voucherType === "event" ? selectedEventTypeId : undefined,
|
|
13827
|
+
eventCategoryId: voucherType === "category" ? selectedCategoryId : undefined,
|
|
13685
13828
|
eventAmount: voucherType === "event" ? eventQuantity : undefined,
|
|
13686
13829
|
recipientName: recipientName.trim() || undefined,
|
|
13687
13830
|
message: message.trim() || undefined,
|
|
@@ -13872,7 +14015,30 @@
|
|
|
13872
14015
|
fontSize: "13px",
|
|
13873
14016
|
color: "var(--bw-text-muted)",
|
|
13874
14017
|
marginTop: "4px",
|
|
13875
|
-
}, children: t("voucher.eventTypeDesc") })] }))
|
|
14018
|
+
}, children: t("voucher.eventTypeDesc") })] })), canUseCategoryVoucherType && (u$2("button", { type: "button", onClick: () => handleVoucherTypeChange("category"), style: {
|
|
14019
|
+
flex: 1,
|
|
14020
|
+
padding: "16px",
|
|
14021
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14022
|
+
border: voucherType === "category"
|
|
14023
|
+
? "2px solid var(--bw-highlight-color)"
|
|
14024
|
+
: "1px solid var(--bw-border-color)",
|
|
14025
|
+
backgroundColor: voucherType === "category"
|
|
14026
|
+
? "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)"
|
|
14027
|
+
: "var(--bw-surface-color)",
|
|
14028
|
+
cursor: "pointer",
|
|
14029
|
+
fontFamily: "var(--bw-font-family)",
|
|
14030
|
+
transition: "all 0.2s ease",
|
|
14031
|
+
textAlign: "center",
|
|
14032
|
+
}, children: [u$2("div", { style: {
|
|
14033
|
+
fontWeight: 600,
|
|
14034
|
+
color: voucherType === "category"
|
|
14035
|
+
? "var(--bw-highlight-color)"
|
|
14036
|
+
: "var(--bw-text-color)",
|
|
14037
|
+
}, children: t("voucher.categoryType") }), u$2("div", { style: {
|
|
14038
|
+
fontSize: "13px",
|
|
14039
|
+
color: "var(--bw-text-muted)",
|
|
14040
|
+
marginTop: "4px",
|
|
14041
|
+
}, children: t("voucher.categoryTypeDesc") })] }))] })] })), voucherType === "monetary" && (u$2("div", { style: cardStyles, children: [u$2("h2", { style: sectionHeaderStyles, children: t("voucher.selectAmount") }), u$2("div", { style: {
|
|
13876
14042
|
display: "grid",
|
|
13877
14043
|
gridTemplateColumns: "repeat(3, 1fr)",
|
|
13878
14044
|
gap: "8px",
|
|
@@ -13927,7 +14093,50 @@
|
|
|
13927
14093
|
fontSize: "18px",
|
|
13928
14094
|
color: "var(--bw-highlight-color)",
|
|
13929
14095
|
fontFamily: "var(--bw-font-family)",
|
|
13930
|
-
}, children: formatCurrency(totalAmount) })] }) }))] })), u$2("div", { style: cardStyles, children: [u$2("h2", { style: sectionHeaderStyles, children: t("voucher.
|
|
14096
|
+
}, children: formatCurrency(totalAmount) })] }) }))] })), voucherType === "category" && (u$2("div", { style: cardStyles, children: [u$2("h2", { style: sectionHeaderStyles, children: t("voucher.selectCategory") }), u$2("div", { style: { marginTop: "12px" }, children: u$2("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [u$2("div", { children: [u$2("label", { htmlFor: "category-select", style: labelStyles, children: t("voucher.categoryLabel") }), u$2("select", { id: "category-select", value: selectedCategoryId || "", onChange: (e) => setSelectedCategoryId(e.target.value ? Number(e.target.value) : null), disabled: categories.length === 1, style: {
|
|
14097
|
+
...inputStyles,
|
|
14098
|
+
cursor: categories.length === 1 ? "not-allowed" : "pointer",
|
|
14099
|
+
}, children: [categories.length > 1 && (u$2("option", { value: "", children: t("voucher.selectCategoryPlaceholder") })), categories.map((cat) => (u$2("option", { value: cat.id, children: cat.name }, cat.id)))] })] }), selectedCategoryId && (u$2("div", { children: [u$2("label", { htmlFor: "category-amount", style: labelStyles, children: t("voucher.categoryAmount") }), u$2("div", { style: {
|
|
14100
|
+
display: "grid",
|
|
14101
|
+
gridTemplateColumns: "repeat(3, 1fr)",
|
|
14102
|
+
gap: "8px",
|
|
14103
|
+
marginTop: "4px",
|
|
14104
|
+
}, children: voucherConfig.monetaryPresets.map((preset) => (u$2("button", { type: "button", onClick: () => setCategoryAmount(preset), style: {
|
|
14105
|
+
padding: "10px",
|
|
14106
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14107
|
+
border: categoryAmount === preset
|
|
14108
|
+
? "2px solid var(--bw-highlight-color)"
|
|
14109
|
+
: "1px solid var(--bw-border-color)",
|
|
14110
|
+
backgroundColor: categoryAmount === preset
|
|
14111
|
+
? "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)"
|
|
14112
|
+
: "var(--bw-surface-color)",
|
|
14113
|
+
cursor: "pointer",
|
|
14114
|
+
fontFamily: "var(--bw-font-family)",
|
|
14115
|
+
fontWeight: 600,
|
|
14116
|
+
fontSize: "14px",
|
|
14117
|
+
color: categoryAmount === preset
|
|
14118
|
+
? "var(--bw-highlight-color)"
|
|
14119
|
+
: "var(--bw-text-color)",
|
|
14120
|
+
transition: "all 0.2s ease",
|
|
14121
|
+
}, children: formatCurrency(preset) }, preset))) })] }))] }) }), selectedCategoryId && categoryAmount > 0 && (u$2("div", { style: {
|
|
14122
|
+
marginTop: "16px",
|
|
14123
|
+
padding: "12px",
|
|
14124
|
+
backgroundColor: "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)",
|
|
14125
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14126
|
+
border: "1px solid var(--bw-highlight-color)",
|
|
14127
|
+
}, children: u$2("div", { style: {
|
|
14128
|
+
display: "flex",
|
|
14129
|
+
justifyContent: "space-between",
|
|
14130
|
+
alignItems: "center",
|
|
14131
|
+
}, children: [u$2("span", { style: {
|
|
14132
|
+
color: "var(--bw-text-color)",
|
|
14133
|
+
fontFamily: "var(--bw-font-family)",
|
|
14134
|
+
}, children: categories.find((c) => c.id === selectedCategoryId)?.name }), u$2("span", { style: {
|
|
14135
|
+
fontWeight: 700,
|
|
14136
|
+
fontSize: "18px",
|
|
14137
|
+
color: "var(--bw-highlight-color)",
|
|
14138
|
+
fontFamily: "var(--bw-font-family)",
|
|
14139
|
+
}, children: formatCurrency(categoryAmount) })] }) }))] })), u$2("div", { style: cardStyles, children: [u$2("h2", { style: sectionHeaderStyles, children: t("voucher.recipientSection") }), u$2("p", { style: {
|
|
13931
14140
|
fontSize: "13px",
|
|
13932
14141
|
color: "var(--bw-text-muted)",
|
|
13933
14142
|
fontFamily: "var(--bw-font-family)",
|
|
@@ -13960,7 +14169,9 @@
|
|
|
13960
14169
|
fontFamily: "var(--bw-font-family)",
|
|
13961
14170
|
}, children: voucherType === "monetary"
|
|
13962
14171
|
? t("voucher.monetaryVoucher")
|
|
13963
|
-
:
|
|
14172
|
+
: voucherType === "category"
|
|
14173
|
+
? categories.find((c) => c.id === selectedCategoryId)?.name || t("voucher.categoryVoucher")
|
|
14174
|
+
: `${eventQuantity}x ${selectedEventType?.name || t("voucher.eventVoucher")}` }), recipientName && (u$2("div", { style: {
|
|
13964
14175
|
fontSize: "13px",
|
|
13965
14176
|
color: "var(--bw-text-muted)",
|
|
13966
14177
|
fontFamily: "var(--bw-font-family)",
|
|
@@ -14236,7 +14447,7 @@
|
|
|
14236
14447
|
}, children: t("voucher.buyAsGift") })] }));
|
|
14237
14448
|
}
|
|
14238
14449
|
|
|
14239
|
-
function VoucherIntegration({ config, voucherConfig, eventTypes, systemConfig, isFormOpen, isLoadingConfig, preselectedEventTypeId, voucherPurchaseResult, isSuccess, showStandaloneCard, onCardClick, onFormClose, onSuccess, onError, onSuccessModalClose, }) {
|
|
14450
|
+
function VoucherIntegration({ config, voucherConfig, eventTypes, categories, systemConfig, isFormOpen, isLoadingConfig, preselectedEventTypeId, voucherPurchaseResult, isSuccess, showStandaloneCard, onCardClick, onFormClose, onSuccess, onError, onSuccessModalClose, }) {
|
|
14240
14451
|
if (!voucherConfig?.enabled) {
|
|
14241
14452
|
return null;
|
|
14242
14453
|
}
|
|
@@ -14247,7 +14458,7 @@
|
|
|
14247
14458
|
justifyContent: "center",
|
|
14248
14459
|
}, children: u$2(VoucherPurchaseCard, { config: voucherConfig, minEventPrice: eventTypes.length > 0
|
|
14249
14460
|
? Math.min(...eventTypes.map((et) => et.maxPrice))
|
|
14250
|
-
: undefined, fallbackImages: eventTypes.flatMap((eventType) => eventType.images || []), onClick: onCardClick, standalone: true }) }) })), u$2(VoucherPurchaseForm, { config: config, voucherConfig: voucherConfig, eventTypes: eventTypes, isOpen: isFormOpen, onClose: onFormClose, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, preselectedEventTypeId: preselectedEventTypeId, isLoadingEventTypes: isLoadingConfig }), isSuccess && voucherPurchaseResult && (u$2(VoucherSuccessModal, { isOpen: true, onClose: onSuccessModalClose, result: voucherPurchaseResult }))] }));
|
|
14461
|
+
: undefined, fallbackImages: eventTypes.flatMap((eventType) => eventType.images || []), onClick: onCardClick, standalone: true }) }) })), u$2(VoucherPurchaseForm, { config: config, voucherConfig: voucherConfig, eventTypes: eventTypes, categories: categories, isOpen: isFormOpen, onClose: onFormClose, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, preselectedEventTypeId: preselectedEventTypeId, isLoadingEventTypes: isLoadingConfig }), isSuccess && voucherPurchaseResult && (u$2(VoucherSuccessModal, { isOpen: true, onClose: onSuccessModalClose, result: voucherPurchaseResult }))] }));
|
|
14251
14462
|
}
|
|
14252
14463
|
|
|
14253
14464
|
// Helper function to preprocess markdown for underline support
|
|
@@ -15515,6 +15726,312 @@
|
|
|
15515
15726
|
}) })] }));
|
|
15516
15727
|
}
|
|
15517
15728
|
|
|
15729
|
+
function toMinutesForUnit(unit) {
|
|
15730
|
+
switch (unit.toLowerCase()) {
|
|
15731
|
+
case "minute":
|
|
15732
|
+
case "minutes":
|
|
15733
|
+
return 1;
|
|
15734
|
+
case "hour":
|
|
15735
|
+
case "hours":
|
|
15736
|
+
return 60;
|
|
15737
|
+
case "day":
|
|
15738
|
+
case "days":
|
|
15739
|
+
return 24 * 60;
|
|
15740
|
+
default:
|
|
15741
|
+
return 1;
|
|
15742
|
+
}
|
|
15743
|
+
}
|
|
15744
|
+
// --- Timezone helpers ------------------------------------------------------
|
|
15745
|
+
// The booking is evaluated server-side against the organization's local
|
|
15746
|
+
// operating hours. The customer's browser may be in a different timezone, so we
|
|
15747
|
+
// interpret the datetime-local field as a wall-clock time in the ORG timezone
|
|
15748
|
+
// (not the browser's) and convert to a UTC instant for the API.
|
|
15749
|
+
function getTzParts(date, timeZone) {
|
|
15750
|
+
const dtf = new Intl.DateTimeFormat("en-CA", {
|
|
15751
|
+
timeZone,
|
|
15752
|
+
hour12: false,
|
|
15753
|
+
year: "numeric",
|
|
15754
|
+
month: "2-digit",
|
|
15755
|
+
day: "2-digit",
|
|
15756
|
+
hour: "2-digit",
|
|
15757
|
+
minute: "2-digit",
|
|
15758
|
+
second: "2-digit",
|
|
15759
|
+
});
|
|
15760
|
+
const parts = {};
|
|
15761
|
+
for (const part of dtf.formatToParts(date)) {
|
|
15762
|
+
if (part.type !== "literal")
|
|
15763
|
+
parts[part.type] = part.value;
|
|
15764
|
+
}
|
|
15765
|
+
return parts;
|
|
15766
|
+
}
|
|
15767
|
+
function tzOffsetMs(utcMs, timeZone) {
|
|
15768
|
+
const p = getTzParts(new Date(utcMs), timeZone);
|
|
15769
|
+
const hour = p.hour === "24" ? "00" : p.hour;
|
|
15770
|
+
const asLocalUtc = Date.UTC(Number(p.year), Number(p.month) - 1, Number(p.day), Number(hour), Number(p.minute), Number(p.second));
|
|
15771
|
+
return asLocalUtc - utcMs;
|
|
15772
|
+
}
|
|
15773
|
+
/** Convert an org-tz wall-clock string ("YYYY-MM-DDTHH:mm") to a UTC instant. */
|
|
15774
|
+
function zonedWallTimeToUtc(wallTime, timeZone) {
|
|
15775
|
+
const [datePart, timePart] = wallTime.split("T");
|
|
15776
|
+
if (!datePart || !timePart)
|
|
15777
|
+
return new Date(NaN);
|
|
15778
|
+
const [y, mo, d] = datePart.split("-").map(Number);
|
|
15779
|
+
const [h, mi] = timePart.split(":").map(Number);
|
|
15780
|
+
const naiveUtc = Date.UTC(y, (mo ?? 1) - 1, d, h ?? 0, mi ?? 0);
|
|
15781
|
+
// Single-pass offset correction (good enough outside the ~1h DST fold).
|
|
15782
|
+
const offset = tzOffsetMs(naiveUtc, timeZone);
|
|
15783
|
+
return new Date(naiveUtc - offset);
|
|
15784
|
+
}
|
|
15785
|
+
/** Render a UTC instant as an org-tz datetime-local input value. */
|
|
15786
|
+
function utcToZonedInputValue(date, timeZone) {
|
|
15787
|
+
const p = getTzParts(date, timeZone);
|
|
15788
|
+
const hour = p.hour === "24" ? "00" : p.hour;
|
|
15789
|
+
return `${p.year}-${p.month}-${p.day}T${hour}:${p.minute}`;
|
|
15790
|
+
}
|
|
15791
|
+
/** Format a UTC instant for display in the org timezone. */
|
|
15792
|
+
function formatInTimeZone(date, timeZone, locale) {
|
|
15793
|
+
return new Intl.DateTimeFormat(locale, {
|
|
15794
|
+
timeZone,
|
|
15795
|
+
dateStyle: "medium",
|
|
15796
|
+
timeStyle: "short",
|
|
15797
|
+
}).format(date);
|
|
15798
|
+
}
|
|
15799
|
+
function calculateTieredPrice(durationUnits, config) {
|
|
15800
|
+
const tiers = [...(config.tiers ?? [])]
|
|
15801
|
+
.filter((tier) => tier.unitCount > 0 && tier.price >= 0)
|
|
15802
|
+
.sort((a, b) => b.unitCount - a.unitCount);
|
|
15803
|
+
if (tiers.length === 0) {
|
|
15804
|
+
return 0;
|
|
15805
|
+
}
|
|
15806
|
+
let total = 0;
|
|
15807
|
+
let remaining = durationUnits;
|
|
15808
|
+
for (const tier of tiers) {
|
|
15809
|
+
const count = Math.floor(remaining / tier.unitCount);
|
|
15810
|
+
if (count <= 0)
|
|
15811
|
+
continue;
|
|
15812
|
+
total += count * tier.price;
|
|
15813
|
+
remaining -= count * tier.unitCount;
|
|
15814
|
+
}
|
|
15815
|
+
if (remaining > 0) {
|
|
15816
|
+
if (typeof config.perUnitPrice === "number") {
|
|
15817
|
+
total += remaining * config.perUnitPrice;
|
|
15818
|
+
}
|
|
15819
|
+
else {
|
|
15820
|
+
total += tiers[tiers.length - 1]?.price ?? 0;
|
|
15821
|
+
}
|
|
15822
|
+
}
|
|
15823
|
+
return total;
|
|
15824
|
+
}
|
|
15825
|
+
function FreeformSelection({ config, eventType, systemConfig, isOpen, onClose, onSuccess, onError, }) {
|
|
15826
|
+
const t = useTranslations();
|
|
15827
|
+
const { locale } = useLocale();
|
|
15828
|
+
const freeformConfig = eventType.freeformConfig;
|
|
15829
|
+
const timeZone = eventType.organizationTimezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
15830
|
+
const isNamedResource = eventType.resourceKind === "named";
|
|
15831
|
+
const maxUnits = isNamedResource ? 1 : Math.max(1, eventType.resourceQuantity ?? 1);
|
|
15832
|
+
const now = T$2(() => {
|
|
15833
|
+
const next = new Date();
|
|
15834
|
+
next.setMinutes(next.getMinutes() + 30, 0, 0);
|
|
15835
|
+
return next;
|
|
15836
|
+
}, []);
|
|
15837
|
+
const [startInput, setStartInput] = d$1(utcToZonedInputValue(now, timeZone));
|
|
15838
|
+
const [duration, setDuration] = d$1(Math.max(1, freeformConfig?.minDuration ?? 1));
|
|
15839
|
+
const [requestedUnits, setRequestedUnits] = d$1(1);
|
|
15840
|
+
const [customerName, setCustomerName] = d$1("");
|
|
15841
|
+
const [customerEmail, setCustomerEmail] = d$1("");
|
|
15842
|
+
const [customerPhone, setCustomerPhone] = d$1("");
|
|
15843
|
+
const [comment, setComment] = d$1("");
|
|
15844
|
+
const [acceptTerms, setAcceptTerms] = d$1(false);
|
|
15845
|
+
const [checkoutStep, setCheckoutStep] = d$1("details");
|
|
15846
|
+
const [availability, setAvailability] = d$1(null);
|
|
15847
|
+
const [availabilityError, setAvailabilityError] = d$1(null);
|
|
15848
|
+
const [isCheckingAvailability, setIsCheckingAvailability] = d$1(false);
|
|
15849
|
+
y$1(() => {
|
|
15850
|
+
if (!isOpen)
|
|
15851
|
+
return;
|
|
15852
|
+
setCheckoutStep("details");
|
|
15853
|
+
}, [isOpen]);
|
|
15854
|
+
y$1(() => {
|
|
15855
|
+
if (!freeformConfig)
|
|
15856
|
+
return;
|
|
15857
|
+
setDuration(Math.max(1, freeformConfig.minDuration));
|
|
15858
|
+
setRequestedUnits(1);
|
|
15859
|
+
setAvailability(null);
|
|
15860
|
+
setAvailabilityError(null);
|
|
15861
|
+
}, [freeformConfig?.resourceId]);
|
|
15862
|
+
const unitMinutes = toMinutesForUnit(freeformConfig?.unit ?? "hour");
|
|
15863
|
+
const startDate = T$2(() => zonedWallTimeToUtc(startInput, timeZone), [startInput, timeZone]);
|
|
15864
|
+
const endDate = T$2(() => new Date(startDate.getTime() + duration * unitMinutes * 60000), [duration, startDate, unitMinutes]);
|
|
15865
|
+
y$1(() => {
|
|
15866
|
+
if (!isOpen || !freeformConfig)
|
|
15867
|
+
return;
|
|
15868
|
+
if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime()))
|
|
15869
|
+
return;
|
|
15870
|
+
const timer = setTimeout(async () => {
|
|
15871
|
+
setIsCheckingAvailability(true);
|
|
15872
|
+
setAvailabilityError(null);
|
|
15873
|
+
try {
|
|
15874
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/freeform-availability"), {
|
|
15875
|
+
method: "POST",
|
|
15876
|
+
headers: createApiHeaders(config, locale),
|
|
15877
|
+
body: JSON.stringify(createRequestBody(config, {
|
|
15878
|
+
eventTypeId: eventType.id,
|
|
15879
|
+
requestedUnits,
|
|
15880
|
+
start: startDate.toISOString(),
|
|
15881
|
+
end: endDate.toISOString(),
|
|
15882
|
+
})),
|
|
15883
|
+
});
|
|
15884
|
+
const data = await response.json();
|
|
15885
|
+
if (!response.ok) {
|
|
15886
|
+
setAvailability(null);
|
|
15887
|
+
setAvailabilityError(data.error || t("error.loadBookingData"));
|
|
15888
|
+
return;
|
|
15889
|
+
}
|
|
15890
|
+
setAvailability({
|
|
15891
|
+
available: Boolean(data.available),
|
|
15892
|
+
freeUnits: Number(data.freeUnits ?? 0),
|
|
15893
|
+
capacityUnits: Number(data.capacityUnits ?? 0),
|
|
15894
|
+
usedUnits: Number(data.usedUnits ?? 0),
|
|
15895
|
+
reservationUsedUnits: Number(data.reservationUsedUnits ?? 0),
|
|
15896
|
+
blockUsedUnits: Number(data.blockUsedUnits ?? 0),
|
|
15897
|
+
durationUnits: Number(data.durationUnits ?? 0),
|
|
15898
|
+
roundedDurationMinutes: Number(data.roundedDurationMinutes ?? 0),
|
|
15899
|
+
});
|
|
15900
|
+
}
|
|
15901
|
+
catch (_error) {
|
|
15902
|
+
setAvailability(null);
|
|
15903
|
+
setAvailabilityError(t("error.loadBookingData"));
|
|
15904
|
+
}
|
|
15905
|
+
finally {
|
|
15906
|
+
setIsCheckingAvailability(false);
|
|
15907
|
+
}
|
|
15908
|
+
}, 350);
|
|
15909
|
+
return () => clearTimeout(timer);
|
|
15910
|
+
}, [
|
|
15911
|
+
config,
|
|
15912
|
+
endDate,
|
|
15913
|
+
eventType.id,
|
|
15914
|
+
freeformConfig,
|
|
15915
|
+
isOpen,
|
|
15916
|
+
locale,
|
|
15917
|
+
requestedUnits,
|
|
15918
|
+
startDate,
|
|
15919
|
+
t,
|
|
15920
|
+
]);
|
|
15921
|
+
const billedDurationUnits = availability?.durationUnits ?? Math.max(1, Math.ceil(duration / Math.max(1, freeformConfig?.stepDuration ?? 1)));
|
|
15922
|
+
const basePrice = T$2(() => {
|
|
15923
|
+
if (!freeformConfig)
|
|
15924
|
+
return 0;
|
|
15925
|
+
if (freeformConfig.pricingModel === "perUnit") {
|
|
15926
|
+
return (freeformConfig.perUnitPrice ?? 0) * billedDurationUnits;
|
|
15927
|
+
}
|
|
15928
|
+
return calculateTieredPrice(billedDurationUnits, freeformConfig);
|
|
15929
|
+
}, [billedDurationUnits, freeformConfig]);
|
|
15930
|
+
const totalPrice = Math.max(0, basePrice * requestedUnits);
|
|
15931
|
+
const canContinueToPayment = !!freeformConfig &&
|
|
15932
|
+
customerName.trim().length >= 2 &&
|
|
15933
|
+
customerEmail.trim().length > 3 &&
|
|
15934
|
+
acceptTerms &&
|
|
15935
|
+
!!availability?.available &&
|
|
15936
|
+
!isCheckingAvailability;
|
|
15937
|
+
const freeformSelection = T$2(() => ({
|
|
15938
|
+
eventTypeId: eventType.id,
|
|
15939
|
+
start: startDate.toISOString(),
|
|
15940
|
+
end: endDate.toISOString(),
|
|
15941
|
+
requestedUnits,
|
|
15942
|
+
customerName: customerName.trim(),
|
|
15943
|
+
customerEmail: customerEmail.trim(),
|
|
15944
|
+
customerPhone: customerPhone.trim(),
|
|
15945
|
+
comment: comment.trim(),
|
|
15946
|
+
}), [
|
|
15947
|
+
comment,
|
|
15948
|
+
customerEmail,
|
|
15949
|
+
customerName,
|
|
15950
|
+
customerPhone,
|
|
15951
|
+
endDate,
|
|
15952
|
+
eventType.id,
|
|
15953
|
+
requestedUnits,
|
|
15954
|
+
startDate,
|
|
15955
|
+
]);
|
|
15956
|
+
const paymentFormData = T$2(() => ({
|
|
15957
|
+
customerName: customerName.trim(),
|
|
15958
|
+
customerEmail: customerEmail.trim(),
|
|
15959
|
+
customerPhone: customerPhone.trim(),
|
|
15960
|
+
participants: [{ name: customerName.trim() || customerEmail.trim(), level: undefined }],
|
|
15961
|
+
comment: comment.trim(),
|
|
15962
|
+
}), [comment, customerEmail, customerName, customerPhone]);
|
|
15963
|
+
const eventDetailsForPayment = T$2(() => ({
|
|
15964
|
+
id: eventType.id,
|
|
15965
|
+
name: eventType.name,
|
|
15966
|
+
startTime: startDate.toISOString(),
|
|
15967
|
+
endTime: endDate.toISOString(),
|
|
15968
|
+
price: basePrice,
|
|
15969
|
+
maxParticipants: availability?.capacityUnits ?? requestedUnits,
|
|
15970
|
+
participantCount: 0,
|
|
15971
|
+
availableSpots: availability?.freeUnits ?? requestedUnits,
|
|
15972
|
+
durationDays: 1,
|
|
15973
|
+
durationPerDay: availability?.roundedDurationMinutes ?? duration * unitMinutes,
|
|
15974
|
+
images: eventType.images ?? [],
|
|
15975
|
+
category: eventType.category,
|
|
15976
|
+
organization: {
|
|
15977
|
+
id: config.organizationId,
|
|
15978
|
+
name: "",
|
|
15979
|
+
},
|
|
15980
|
+
bookingOpen: availability?.available ?? false,
|
|
15981
|
+
}), [
|
|
15982
|
+
availability?.available,
|
|
15983
|
+
availability?.capacityUnits,
|
|
15984
|
+
availability?.freeUnits,
|
|
15985
|
+
availability?.roundedDurationMinutes,
|
|
15986
|
+
basePrice,
|
|
15987
|
+
config.organizationId,
|
|
15988
|
+
duration,
|
|
15989
|
+
endDate,
|
|
15990
|
+
eventType.category,
|
|
15991
|
+
eventType.id,
|
|
15992
|
+
eventType.images,
|
|
15993
|
+
eventType.name,
|
|
15994
|
+
requestedUnits,
|
|
15995
|
+
startDate,
|
|
15996
|
+
unitMinutes,
|
|
15997
|
+
]);
|
|
15998
|
+
const footer = (u$2("div", { style: { width: "100%", display: "flex", gap: "12px" }, children: [u$2("button", { type: "button", onClick: () => {
|
|
15999
|
+
if (checkoutStep === "payment") {
|
|
16000
|
+
setCheckoutStep("details");
|
|
16001
|
+
}
|
|
16002
|
+
else {
|
|
16003
|
+
onClose();
|
|
16004
|
+
}
|
|
16005
|
+
}, style: {
|
|
16006
|
+
flex: 1,
|
|
16007
|
+
padding: "12px 16px",
|
|
16008
|
+
border: "1px solid var(--bw-border-color)",
|
|
16009
|
+
borderRadius: "var(--bw-border-radius)",
|
|
16010
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
16011
|
+
color: "var(--bw-text-color)",
|
|
16012
|
+
fontFamily: "var(--bw-font-family)",
|
|
16013
|
+
fontWeight: 600,
|
|
16014
|
+
cursor: "pointer",
|
|
16015
|
+
}, children: checkoutStep === "payment" ? t("button.backToDetails") : t("common.back") }), checkoutStep === "details" && (u$2("button", { type: "button", onClick: () => setCheckoutStep("payment"), disabled: !canContinueToPayment, style: {
|
|
16016
|
+
flex: 1,
|
|
16017
|
+
padding: "12px 16px",
|
|
16018
|
+
border: "none",
|
|
16019
|
+
borderRadius: "var(--bw-border-radius)",
|
|
16020
|
+
backgroundColor: "var(--bw-highlight-color)",
|
|
16021
|
+
color: "var(--bw-button-text-color, #ffffff)",
|
|
16022
|
+
fontFamily: "var(--bw-font-family)",
|
|
16023
|
+
fontWeight: 700,
|
|
16024
|
+
cursor: canContinueToPayment ? "pointer" : "not-allowed",
|
|
16025
|
+
opacity: canContinueToPayment ? 1 : 0.6,
|
|
16026
|
+
}, children: t("button.continueToPayment") }))] }));
|
|
16027
|
+
if (!freeformConfig) {
|
|
16028
|
+
return (u$2(Sidebar, { isOpen: isOpen, onClose: onClose, title: eventType.name, children: u$2("div", { style: { padding: "16px", color: "var(--bw-error-color)", fontFamily: "var(--bw-font-family)" }, children: t("booking.freeformMissingConfig") }) }));
|
|
16029
|
+
}
|
|
16030
|
+
return (u$2(Sidebar, { isOpen: isOpen, onClose: onClose, title: eventType.name, footer: footer, children: u$2("div", { style: { padding: "16px", display: "flex", flexDirection: "column", gap: "16px" }, children: checkoutStep === "details" ? (u$2(k$3, { children: [u$2("div", { style: { border: "1px solid var(--bw-border-color)", borderRadius: "var(--bw-border-radius)", padding: "14px", backgroundColor: "var(--bw-surface-color)" }, children: [u$2("h3", { style: { margin: "0 0 12px 0", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-color)" }, children: t("booking.eventDetails") }), u$2("div", { style: { display: "grid", gap: "10px" }, children: [u$2("label", { style: { display: "grid", gap: "6px", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-muted)" }, children: [t("booking.date"), u$2("input", { type: "datetime-local", value: startInput, onChange: (event) => setStartInput(event.target.value), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } })] }), u$2("label", { style: { display: "grid", gap: "6px", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-muted)" }, children: [t("booking.duration"), u$2("input", { type: "number", min: freeformConfig.minDuration, max: freeformConfig.maxDuration, step: freeformConfig.stepDuration, value: duration, onChange: (event) => setDuration(Number(event.target.value)), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } })] }), !isNamedResource && (u$2("label", { style: { display: "grid", gap: "6px", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-muted)" }, children: [t("voucher.quantity"), u$2("input", { type: "number", min: 1, max: maxUnits, value: requestedUnits, onChange: (event) => setRequestedUnits(Math.min(maxUnits, Math.max(1, Number(event.target.value)))), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } })] }))] }), u$2("div", { style: { marginTop: "12px", fontSize: "13px", color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: [formatInTimeZone(startDate, timeZone, locale), " - ", formatInTimeZone(endDate, timeZone, locale), eventType.organizationTimezone ? ` (${timeZone})` : ""] }), isCheckingAvailability && (u$2("div", { style: { marginTop: "8px", fontSize: "13px", color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t("common.loading") })), availabilityError && (u$2("div", { style: { marginTop: "8px", fontSize: "13px", color: "var(--bw-error-color)", fontFamily: "var(--bw-font-family)" }, children: availabilityError })), !availabilityError && availability && (u$2("div", { style: { marginTop: "8px", fontSize: "13px", color: availability.available ? "var(--bw-success-color)" : "var(--bw-error-color)", fontFamily: "var(--bw-font-family)" }, children: availability.available
|
|
16031
|
+
? t("booking.freeformUnitsAvailable", { count: availability.freeUnits })
|
|
16032
|
+
: t("booking.freeformNotAvailable") }))] }), u$2("div", { style: { border: "1px solid var(--bw-border-color)", borderRadius: "var(--bw-border-radius)", padding: "14px", backgroundColor: "var(--bw-surface-color)" }, children: [u$2("h3", { style: { margin: "0 0 12px 0", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-color)" }, children: t("booking.contactInfo") }), u$2("div", { style: { display: "grid", gap: "10px" }, children: [u$2("input", { type: "text", placeholder: t("booking.namePlaceholder"), value: customerName, onChange: (event) => setCustomerName(event.target.value), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } }), u$2("input", { type: "email", placeholder: t("booking.emailPlaceholder"), value: customerEmail, onChange: (event) => setCustomerEmail(event.target.value), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } }), u$2("input", { type: "tel", placeholder: t("booking.phonePlaceholder"), value: customerPhone, onChange: (event) => setCustomerPhone(event.target.value), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } }), u$2("textarea", { placeholder: t("booking.commentPlaceholder"), value: comment, onChange: (event) => setComment(event.target.value), rows: 3, style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)", resize: "vertical" } }), u$2("label", { style: { display: "flex", alignItems: "center", gap: "8px", fontFamily: "var(--bw-font-family)", fontSize: "13px", color: "var(--bw-text-muted)" }, children: [u$2("input", { type: "checkbox", checked: acceptTerms, onChange: (event) => setAcceptTerms(event.target.checked) }), t("booking.acceptTerms"), " ", t("booking.terms")] })] })] }), u$2("div", { style: { border: "1px solid var(--bw-border-color)", borderRadius: "var(--bw-border-radius)", padding: "14px", backgroundColor: "var(--bw-surface-color)" }, children: [u$2("h3", { style: { margin: "0 0 12px 0", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-color)" }, children: t("summary.title") }), u$2("div", { style: { display: "flex", justifyContent: "space-between", color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "14px" }, children: [u$2("span", { children: t("booking.duration") }), u$2("span", { children: t("booking.steps", { count: billedDurationUnits }) })] }), u$2("div", { style: { display: "flex", justifyContent: "space-between", color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "14px", marginTop: "6px" }, children: [u$2("span", { children: t("booking.price") }), u$2("span", { children: [formatCurrency(basePrice), " x ", requestedUnits] })] }), u$2("div", { style: { display: "flex", justifyContent: "space-between", color: "var(--bw-text-color)", fontFamily: "var(--bw-font-family)", fontSize: "20px", fontWeight: 700, marginTop: "10px" }, children: [u$2("span", { children: t("summary.totalAmount") }), u$2("span", { children: formatCurrency(totalPrice) })] })] })] })) : (u$2(k$3, { children: [u$2(HoldCountdown, {}), u$2("div", { style: { border: "1px solid var(--bw-border-color)", borderRadius: "var(--bw-border-radius)", padding: "14px", backgroundColor: "var(--bw-surface-color)" }, children: [u$2("h3", { style: { margin: "0 0 12px 0", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-color)" }, children: t("summary.payment") }), !systemConfig?.paymentProvider && (u$2("p", { style: { margin: 0, color: "var(--bw-error-color)", fontFamily: "var(--bw-font-family)" }, children: t("booking.paymentUnavailable") })), systemConfig?.paymentProvider === "mollie" && (u$2(MolliePaymentForm, { config: config, eventDetails: eventDetailsForPayment, formData: paymentFormData, totalAmount: totalPrice, discountCode: null, giftCards: [], onSuccess: onSuccess, onError: onError, mollieProfileId: systemConfig?.mollieProfileId, mollieTestmode: systemConfig?.mollieTestmode, freeformSelection: freeformSelection })), systemConfig?.paymentProvider === "stripe" && (u$2(StripePaymentForm, { config: config, eventDetails: eventDetailsForPayment, formData: paymentFormData, totalAmount: totalPrice, discountCode: null, giftCards: [], onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, freeformSelection: freeformSelection }))] })] })) }) }));
|
|
16033
|
+
}
|
|
16034
|
+
|
|
15518
16035
|
const getThemeConfig = (theme = "generic") => {
|
|
15519
16036
|
switch (theme) {
|
|
15520
16037
|
case "christmas":
|
|
@@ -16155,6 +16672,7 @@
|
|
|
16155
16672
|
const [shouldRenderInstanceSelection, setShouldRenderInstanceSelection] = d$1(false);
|
|
16156
16673
|
const [shouldRenderUpsells, setShouldRenderUpsells] = d$1(false);
|
|
16157
16674
|
const [shouldRenderBookingForm, setShouldRenderBookingForm] = d$1(false);
|
|
16675
|
+
const [shouldRenderFreeformSelection, setShouldRenderFreeformSelection] = d$1(false);
|
|
16158
16676
|
// Google Ads config (received from API, set once from the first API response)
|
|
16159
16677
|
const [googleAdsConfig, setGoogleAdsConfig] = d$1(null);
|
|
16160
16678
|
const extractGoogleAdsConfig = (data) => {
|
|
@@ -16168,6 +16686,7 @@
|
|
|
16168
16686
|
// Voucher purchase state
|
|
16169
16687
|
const [voucherConfig, setVoucherConfig] = d$1(null);
|
|
16170
16688
|
const [voucherEventTypes, setVoucherEventTypes] = d$1([]);
|
|
16689
|
+
const [voucherCategories, setVoucherCategories] = d$1([]);
|
|
16171
16690
|
const [isLoadingVoucherConfig, setIsLoadingVoucherConfig] = d$1(false);
|
|
16172
16691
|
const [isVoucherFormOpen, setIsVoucherFormOpen] = d$1(false);
|
|
16173
16692
|
const [voucherPurchaseResult, setVoucherPurchaseResult] = d$1(null);
|
|
@@ -16247,6 +16766,7 @@
|
|
|
16247
16766
|
setVoucherConfig(mergedConfig);
|
|
16248
16767
|
extractGoogleAdsConfig(data);
|
|
16249
16768
|
setVoucherEventTypes(data.eventTypes || []);
|
|
16769
|
+
setVoucherCategories(data.categories || []);
|
|
16250
16770
|
// Set system config for payment processing
|
|
16251
16771
|
if (data.paymentProvider) {
|
|
16252
16772
|
setSystemConfig({
|
|
@@ -16275,7 +16795,7 @@
|
|
|
16275
16795
|
if (!analyticsInitRef.current && config.organizationId) {
|
|
16276
16796
|
analyticsInitRef.current = true;
|
|
16277
16797
|
initAnalytics(config.apiBaseUrl, config.organizationId, {
|
|
16278
|
-
partnerContractId: config.partnerContractId,
|
|
16798
|
+
...(config.partnerContractId ? { partnerContractId: config.partnerContractId } : {}),
|
|
16279
16799
|
});
|
|
16280
16800
|
trackEvent("widget_loaded", {
|
|
16281
16801
|
viewMode,
|
|
@@ -16754,6 +17274,18 @@
|
|
|
16754
17274
|
const handleEventTypeSelect = async (eventType) => {
|
|
16755
17275
|
trackEvent("event_type_selected", { eventTypeId: eventType.id, eventTypeName: eventType.name });
|
|
16756
17276
|
setSelectedEventType(eventType);
|
|
17277
|
+
if (eventType.bookingMode === "freeform") {
|
|
17278
|
+
setCurrentStep("freeform");
|
|
17279
|
+
setShouldRenderFreeformSelection(true);
|
|
17280
|
+
setIsLoadingEventInstances(true);
|
|
17281
|
+
try {
|
|
17282
|
+
await loadEventInstances(eventType.id);
|
|
17283
|
+
}
|
|
17284
|
+
finally {
|
|
17285
|
+
setIsLoadingEventInstances(false);
|
|
17286
|
+
}
|
|
17287
|
+
return;
|
|
17288
|
+
}
|
|
16757
17289
|
setCurrentStep("eventInstances");
|
|
16758
17290
|
setShouldRenderInstanceSelection(true);
|
|
16759
17291
|
setIsLoadingEventInstances(true);
|
|
@@ -16844,6 +17376,7 @@
|
|
|
16844
17376
|
setSuccessPaymentId(result.paymentIntent.id);
|
|
16845
17377
|
setSidebarOpen(false);
|
|
16846
17378
|
setShouldRenderBookingForm(false);
|
|
17379
|
+
setShouldRenderFreeformSelection(false);
|
|
16847
17380
|
setBookingPersistedState(null);
|
|
16848
17381
|
config.onSuccess?.(result);
|
|
16849
17382
|
};
|
|
@@ -17159,6 +17692,7 @@
|
|
|
17159
17692
|
setShouldRenderInstanceSelection(false);
|
|
17160
17693
|
setShouldRenderUpsells(false);
|
|
17161
17694
|
setShouldRenderBookingForm(false);
|
|
17695
|
+
setShouldRenderFreeformSelection(false);
|
|
17162
17696
|
setSelectedUpsells([]);
|
|
17163
17697
|
setUpsells([]);
|
|
17164
17698
|
setBookingPersistedState(null);
|
|
@@ -17180,6 +17714,7 @@
|
|
|
17180
17714
|
setShouldRenderInstanceSelection(false);
|
|
17181
17715
|
setShouldRenderUpsells(false);
|
|
17182
17716
|
setShouldRenderBookingForm(false);
|
|
17717
|
+
setShouldRenderFreeformSelection(false);
|
|
17183
17718
|
setSelectedUpsells([]);
|
|
17184
17719
|
setUpsells([]);
|
|
17185
17720
|
setBookingPersistedState(null);
|
|
@@ -17199,6 +17734,7 @@
|
|
|
17199
17734
|
setSuccessPaymentId(null);
|
|
17200
17735
|
setShouldRenderInstanceSelection(false);
|
|
17201
17736
|
setShouldRenderBookingForm(false);
|
|
17737
|
+
setShouldRenderFreeformSelection(false);
|
|
17202
17738
|
const url = new URL(window.location.href);
|
|
17203
17739
|
url.searchParams.delete("payment_intent");
|
|
17204
17740
|
url.searchParams.delete("payment_intent_client_secret");
|
|
@@ -17242,13 +17778,23 @@
|
|
|
17242
17778
|
setCurrentStep("booking");
|
|
17243
17779
|
setShouldRenderBookingForm(true);
|
|
17244
17780
|
}
|
|
17781
|
+
else if (selectedEventType?.bookingMode === "freeform") {
|
|
17782
|
+
setCurrentStep("freeform");
|
|
17783
|
+
setShouldRenderFreeformSelection(true);
|
|
17784
|
+
}
|
|
17245
17785
|
else {
|
|
17246
17786
|
setCurrentStep("eventInstances");
|
|
17247
17787
|
setSidebarOpen(true);
|
|
17248
17788
|
setShouldRenderInstanceSelection(true);
|
|
17249
17789
|
}
|
|
17250
17790
|
}, children: [isButtonBusy && u$2(Spinner, { size: 20, borderColor: "var(--bw-button-text-color, #ffffff)" }), config.buttonText ||
|
|
17251
|
-
(isDirectInstanceMode
|
|
17791
|
+
(isDirectInstanceMode || selectedEventType?.bookingMode === "freeform"
|
|
17792
|
+
? t("button.bookNow")
|
|
17793
|
+
: t("button.viewDates"))] }), shouldRenderInstanceSelection && (u$2(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), shouldRenderUpsells && (u$2(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (u$2(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), shouldRenderFreeformSelection && selectedEventType?.bookingMode === "freeform" && (u$2(FreeformSelection, { config: config, eventType: selectedEventType, isOpen: currentStep === "freeform", onClose: () => {
|
|
17794
|
+
setCurrentStep("eventTypes");
|
|
17795
|
+
setSidebarOpen(false);
|
|
17796
|
+
setShouldRenderFreeformSelection(false);
|
|
17797
|
+
}, onSuccess: handleBookingSuccess, onError: handleBookingError, systemConfig: systemConfig })), u$2(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
17252
17798
|
setIsSuccess(false);
|
|
17253
17799
|
setCurrentStep("eventTypes");
|
|
17254
17800
|
setSidebarOpen(false);
|
|
@@ -17256,6 +17802,7 @@
|
|
|
17256
17802
|
setShouldRenderInstanceSelection(false);
|
|
17257
17803
|
setShouldRenderUpsells(false);
|
|
17258
17804
|
setShouldRenderBookingForm(false);
|
|
17805
|
+
setShouldRenderFreeformSelection(false);
|
|
17259
17806
|
setSelectedUpsells([]);
|
|
17260
17807
|
setUpsells([]);
|
|
17261
17808
|
const url = new URL(window.location.href);
|
|
@@ -17268,7 +17815,7 @@
|
|
|
17268
17815
|
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
17269
17816
|
}
|
|
17270
17817
|
// Cards mode (default) - show event type selection with optional voucher card
|
|
17271
|
-
const cardsView = (u$2(k$3, { children: [hasEventSelection && (u$2(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, onInstancePreview: (instanceId, eventTypeId) => void handleUpcomingEventSelect(instanceId, eventTypeId), isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: false, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: null, voucherPurchaseResult: null, isSuccess: false, showStandaloneCard: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled), onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => { } })), isStandaloneVoucherMode && isLoading && !voucherConfig && (u$2("div", { style: { padding: "24px", textAlign: "center" }, children: u$2("div", { style: {
|
|
17818
|
+
const cardsView = (u$2(k$3, { children: [hasEventSelection && (u$2(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, onInstancePreview: (instanceId, eventTypeId) => void handleUpcomingEventSelect(instanceId, eventTypeId), isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, categories: voucherCategories, systemConfig: systemConfig, isFormOpen: false, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: null, voucherPurchaseResult: null, isSuccess: false, showStandaloneCard: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled), onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => { } })), isStandaloneVoucherMode && isLoading && !voucherConfig && (u$2("div", { style: { padding: "24px", textAlign: "center" }, children: u$2("div", { style: {
|
|
17272
17819
|
display: "inline-block",
|
|
17273
17820
|
width: "32px",
|
|
17274
17821
|
height: "32px",
|
|
@@ -17309,13 +17856,17 @@
|
|
|
17309
17856
|
};
|
|
17310
17857
|
};
|
|
17311
17858
|
const backHandlers = getBackHandlers();
|
|
17312
|
-
return (u$2(StyleProvider, { config: config, children: [u$2("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (u$2(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), shouldRenderUpsells && (u$2(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (u$2(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: backHandlers.onClose, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), u$2(
|
|
17859
|
+
return (u$2(StyleProvider, { config: config, children: [u$2("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (u$2(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), shouldRenderUpsells && (u$2(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (u$2(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: backHandlers.onClose, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), shouldRenderFreeformSelection && selectedEventType?.bookingMode === "freeform" && (u$2(FreeformSelection, { config: config, eventType: selectedEventType, isOpen: currentStep === "freeform", onClose: () => {
|
|
17860
|
+
setCurrentStep("eventTypes");
|
|
17861
|
+
setShouldRenderFreeformSelection(false);
|
|
17862
|
+
}, onSuccess: handleBookingSuccess, onError: handleBookingError, systemConfig: systemConfig })), u$2(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
|
|
17313
17863
|
setIsSuccess(false);
|
|
17314
17864
|
setCurrentStep("eventTypes");
|
|
17315
17865
|
setSuccessPaymentId(null);
|
|
17316
17866
|
setShouldRenderInstanceSelection(false);
|
|
17317
17867
|
setShouldRenderUpsells(false);
|
|
17318
17868
|
setShouldRenderBookingForm(false);
|
|
17869
|
+
setShouldRenderFreeformSelection(false);
|
|
17319
17870
|
setSelectedUpsells([]);
|
|
17320
17871
|
setUpsells([]);
|
|
17321
17872
|
const url = new URL(window.location.href);
|
|
@@ -17325,7 +17876,7 @@
|
|
|
17325
17876
|
url.searchParams.delete("mollie_payment_id");
|
|
17326
17877
|
url.searchParams.delete("mollie_status");
|
|
17327
17878
|
window.history.replaceState({}, "", url.toString());
|
|
17328
|
-
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId }), u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: isVoucherFormOpen, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, voucherPurchaseResult: voucherPurchaseResult, isSuccess: isSuccess, showStandaloneCard: false, onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => {
|
|
17879
|
+
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId }), u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, categories: voucherCategories, systemConfig: systemConfig, isFormOpen: isVoucherFormOpen, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, voucherPurchaseResult: voucherPurchaseResult, isSuccess: isSuccess, showStandaloneCard: false, onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => {
|
|
17329
17880
|
setIsSuccess(false);
|
|
17330
17881
|
setVoucherPurchaseResult(null);
|
|
17331
17882
|
const url = new URL(window.location.href);
|
|
@@ -17378,7 +17929,7 @@
|
|
|
17378
17929
|
}
|
|
17379
17930
|
}
|
|
17380
17931
|
|
|
17381
|
-
var css_248z = ".booking-widget-container{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;color:var(--bw-text-color,#1e293b);direction:ltr;display:block;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;position:relative;text-align:left}.booking-widget-container *,.booking-widget-container :after,.booking-widget-container :before{box-sizing:border-box;margin:0;padding:0}.booking-widget-container input,.booking-widget-container select,.booking-widget-container textarea{font-family:inherit;font-size:inherit;line-height:inherit}.booking-widget-container button{background:none;border:none;cursor:pointer;font-family:inherit;font-size:inherit}.booking-widget-container a{color:inherit;text-decoration:none}.booking-widget-container img{display:block;height:auto;max-width:100%;vertical-align:middle}.booking-widget-container ol,.booking-widget-container ul{list-style:none}.booking-widget-container h1,.booking-widget-container h2,.booking-widget-container h3,.booking-widget-container h4,.booking-widget-container h5,.booking-widget-container h6{font-size:inherit;font-weight:inherit}#booking-widget-portal{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);direction:ltr;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;text-align:left}#booking-widget-portal *,#booking-widget-portal :after,#booking-widget-portal :before{box-sizing:border-box}#booking-widget-portal-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);line-height:1.5}:root{--bw-highlight-color:#00b1aa;--bw-highlight-color-rgb:0,177,170;--bw-background-color:#f8fdfe;--bw-surface-color:#fff;--bw-text-color:#0e7490;--bw-text-muted:rgba(14,116,144,.7);--bw-border-color:#bae6fd;--bw-success-color:#38bdf8;--bw-warning-color:#fbbf24;--bw-error-color:#f43f5e;--bw-border-radius:18px;--bw-border-radius-small:calc(var(--bw-border-radius)*0.8);--bw-spacing:16px;--bw-spacing-large:24px;--bw-font-family:\"Inter\",system-ui,sans-serif;--bw-font-size:14px;--bw-font-size-large:18px;--bw-font-size-small:12px;--bw-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--bw-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--bw-highlight-muted:rgba(0,177,170,.1);--bw-highlight-subtle:rgba(0,177,170,.05);--bw-text-subtle:rgba(14,116,144,.4)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes shimmer{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes slide-in-right{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes slide-out-right{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(100%)}}@keyframes slide-in-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.animate-spin{animation:spin 1s linear infinite}.animate-shimmer{animation:shimmer 2s infinite}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-fade-in{animation:fade-in .2s ease-out}.animate-slide-in-up{animation:slide-in-up .3s ease-out}.animate-scale-in{animation:scale-in .2s ease-out}.skeleton-shimmer{overflow:hidden;position:relative}.skeleton-shimmer:after{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.3),transparent);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.bw-btn{letter-spacing:var(--bw-letter-spacing,normal);text-transform:var(--bw-text-transform,none);transition:all .2s ease!important}.bw-btn:hover:not(:disabled):not([disabled]){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-btn:active:not(:disabled):not([disabled]){box-shadow:0 2px 4px rgba(0,0,0,.1)}.bw-btn-primary:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);filter:brightness(1.1)}.bw-btn-secondary:hover:not(:disabled):not([disabled]){background-color:var(--bw-surface-color);border-color:var(--bw-highlight-color);color:var(--bw-highlight-color)}.bw-btn-ghost:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-muted)}.bw-btn-outline:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);color:var(--bw-button-text-color,#fff)}.bw-btn:disabled,.bw-btn[disabled]{cursor:not-allowed!important;opacity:.5!important}button[class*=bw-btn],button[style*=transition]{transition:all .2s ease!important}button[data-variant=primary]:hover:not(:disabled),button[style*=\"--bw-highlight-color\"]:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);filter:brightness(1.1);transform:translateY(-1px)}button[data-variant=secondary]:hover:not(:disabled){border-color:var(--bw-highlight-color)!important;color:var(--bw-highlight-color)!important}button[data-variant=ghost]:hover:not(:disabled){background-color:var(--bw-highlight-muted)!important}button[data-variant=outline]:hover:not(:disabled){background-color:var(--bw-highlight-color)!important;color:var(--bw-button-text-color,#fff)!important}.bw-button-hover:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-button-hover:active:not(:disabled){box-shadow:0 2px 4px rgba(0,0,0,.1);transform:translateY(0)}@media (max-width:768px){.sidebar-mobile{border-radius:0!important;max-width:100%!important;width:100%!important}}@media (max-width:600px){.event-type-list{gap:12px!important;padding:8px!important}.event-type-card{flex:1 1 100%!important;max-width:100%!important;padding:0!important}.event-type-img{height:160px!important}.event-type-title{font-size:1.1rem!important}.event-type-desc{font-size:.8rem!important;max-height:100px!important;min-height:100px!important}.event-type-content{padding:16px 24px!important}}.event-type-markdown{overflow:visible!important}.event-type-markdown p{color:var(--bw-text-muted);font-family:var(--bw-font-family);line-height:1.6;margin:0 0 8px}.event-type-markdown p:last-child{margin-bottom:0}.event-type-markdown h2{font-size:18px!important;font-weight:700!important;margin:12px 0 6px!important}.event-type-markdown h2,.event-type-markdown h3{color:var(--bw-text-color)!important;line-height:1.3!important}.event-type-markdown h3{font-size:16px!important;font-weight:600!important;margin:10px 0 4px!important}.event-type-markdown strong{color:var(--bw-text-color);font-weight:600}.event-type-markdown em{font-style:italic}.event-type-markdown u{text-decoration:underline}.event-type-markdown ul{list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ul li{color:var(--bw-text-muted)!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ul li:before{color:var(--bw-text-color)!important;content:\"•\"!important;font-weight:700!important;left:-16px!important;position:absolute!important;top:0!important}.event-type-markdown ol{counter-reset:list-counter!important;list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ol li{color:var(--bw-text-muted)!important;counter-increment:list-counter!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ol li:before{color:var(--bw-text-color)!important;content:counter(list-counter) \".\"!important;font-weight:700!important;left:-20px!important;position:absolute!important;top:0!important}.event-type-markdown blockquote{border-left:2px solid var(--bw-border-color);color:var(--bw-text-muted);font-style:italic;margin:4px 0;padding-left:12px}.event-type-markdown a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:var(--bw-text-color);font-weight:600;margin-bottom:.5em}.markdown-content h1{font-size:1.5em}.markdown-content h2{font-size:1.25em}.markdown-content h3{font-size:1.1em}.markdown-content p{line-height:1.6;margin-bottom:1em}.markdown-content ol,.markdown-content ul{margin-bottom:1em;padding-left:1.5em}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin-bottom:.25em}.markdown-content a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content a:hover{opacity:.8}.markdown-content strong{font-weight:600}.markdown-content em{font-style:italic}.markdown-content code{background:var(--bw-highlight-subtle);border-radius:4px;font-family:monospace;font-size:.9em;padding:.125em .25em}.markdown-content blockquote{border-left:3px solid var(--bw-highlight-color);color:var(--bw-text-muted);margin:1em 0;padding-left:1em}.print-only{display:none}.print-hidden{display:block}@media print{.print-only{display:block}.print-hidden{display:none!important}.print-booking-header{border-bottom:2px solid #000;display:block;margin-bottom:24px;padding-bottom:16px;text-align:center}.print-booking-header h1{font-size:24px;margin:0 0 8px}.print-booking-header .subtitle{color:#666;font-size:14px}.print-booking-card{border:1px solid #ccc;border-radius:8px;margin-bottom:16px;padding:16px;page-break-inside:avoid}.print-section-title{border-bottom:1px solid #ddd;display:block;font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px}.print-detail-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}.print-detail-item{margin-bottom:8px}.print-detail-label{color:#666;font-size:12px;margin-bottom:4px}.print-detail-value{font-size:14px;font-weight:600}.print-status-badge{border-radius:9999px;display:inline-block;font-size:12px;font-weight:600;padding:4px 12px}.print-status-paid{background-color:#dcfce7;color:#166534;display:inline-block}.print-participant{align-items:center;background-color:#f9fafb;border-radius:4px;display:flex;justify-content:space-between;margin-bottom:8px;padding:8px}.print-participant-name{font-weight:600}.print-participant-age{color:#666;font-size:12px}.print-payment-summary{display:block}.print-payment-row{border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:4px 0}.print-payment-row:last-child{border-bottom:none;font-weight:600}.print-footer{border-top:1px solid #ddd;color:#666;display:block;font-size:12px;margin-top:24px;padding-top:16px;text-align:center}.print-footer p{margin:4px 0}}";
|
|
17932
|
+
var css_248z = ".booking-widget-container{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;color:var(--bw-text-color,#1e293b);direction:ltr;display:block;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;position:relative;text-align:left}.booking-widget-container *,.booking-widget-container :after,.booking-widget-container :before{box-sizing:border-box;margin:0;padding:0}.booking-widget-container input,.booking-widget-container select,.booking-widget-container textarea{font-family:inherit;font-size:inherit;line-height:inherit}.booking-widget-container button{background:none;border:none;cursor:pointer;font-family:inherit;font-size:inherit}.booking-widget-container a{color:inherit;text-decoration:none}.booking-widget-container img{display:block;height:auto;max-width:100%;vertical-align:middle}.booking-widget-container ol,.booking-widget-container ul{list-style:none}.booking-widget-container h1,.booking-widget-container h2,.booking-widget-container h3,.booking-widget-container h4,.booking-widget-container h5,.booking-widget-container h6{font-size:inherit;font-weight:inherit}#booking-widget-portal{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);direction:ltr;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;text-align:left}#booking-widget-portal *,#booking-widget-portal :after,#booking-widget-portal :before{box-sizing:border-box}#booking-widget-portal-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);line-height:1.5}:root{--bw-highlight-color:#00b1aa;--bw-highlight-color-rgb:0,177,170;--bw-background-color:#f8fdfe;--bw-surface-color:#fff;--bw-text-color:#0e7490;--bw-text-muted:rgba(14,116,144,.7);--bw-border-color:#bae6fd;--bw-success-color:#38bdf8;--bw-warning-color:#fbbf24;--bw-error-color:#f43f5e;--bw-border-radius:18px;--bw-border-radius-small:calc(var(--bw-border-radius)*0.8);--bw-spacing:16px;--bw-spacing-large:24px;--bw-font-family:\"Inter\",system-ui,sans-serif;--bw-font-size:14px;--bw-font-size-large:18px;--bw-font-size-small:12px;--bw-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--bw-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--bw-highlight-muted:rgba(0,177,170,.1);--bw-highlight-subtle:rgba(0,177,170,.05);--bw-text-subtle:rgba(14,116,144,.4)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes shimmer{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes slide-in-right{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes slide-out-right{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(100%)}}@keyframes slide-in-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.animate-spin{animation:spin 1s linear infinite}.animate-shimmer{animation:shimmer 2s infinite}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-fade-in{animation:fade-in .2s ease-out}.animate-slide-in-up{animation:slide-in-up .3s ease-out}.animate-scale-in{animation:scale-in .2s ease-out}.skeleton-shimmer{overflow:hidden;position:relative}.skeleton-shimmer:after{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.3),transparent);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.booking-widget-container .bw-btn{letter-spacing:var(--bw-letter-spacing,normal);text-transform:var(--bw-text-transform,none);transition:all .2s ease!important}.booking-widget-container .bw-btn:hover:not(:disabled):not([disabled]){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.booking-widget-container .bw-btn:active:not(:disabled):not([disabled]){box-shadow:0 2px 4px rgba(0,0,0,.1)}.booking-widget-container .bw-btn-primary:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);filter:brightness(1.1)}.booking-widget-container .bw-btn-secondary:hover:not(:disabled):not([disabled]){background-color:var(--bw-surface-color);border-color:var(--bw-highlight-color);color:var(--bw-highlight-color)}.booking-widget-container .bw-btn-ghost:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-muted)}.booking-widget-container .bw-btn-outline:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);color:var(--bw-button-text-color,#fff)}.booking-widget-container .bw-btn:disabled,.booking-widget-container .bw-btn[disabled]{cursor:not-allowed!important;opacity:.5!important}.booking-widget-container button[class*=bw-btn],.booking-widget-container button[style*=transition]{transition:all .2s ease!important}.booking-widget-container button[data-variant=primary]:hover:not(:disabled),.booking-widget-container button[style*=\"--bw-highlight-color\"]:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);filter:brightness(1.1);transform:translateY(-1px)}.booking-widget-container button[data-variant=secondary]:hover:not(:disabled){border-color:var(--bw-highlight-color)!important;color:var(--bw-highlight-color)!important}.booking-widget-container button[data-variant=ghost]:hover:not(:disabled){background-color:var(--bw-highlight-muted)!important}.booking-widget-container button[data-variant=outline]:hover:not(:disabled){background-color:var(--bw-highlight-color)!important;color:var(--bw-button-text-color,#fff)!important}.booking-widget-container .bw-button-hover:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.booking-widget-container .bw-button-hover:active:not(:disabled){box-shadow:0 2px 4px rgba(0,0,0,.1);transform:translateY(0)}@media (max-width:768px){.sidebar-mobile{border-radius:0!important;max-width:100%!important;width:100%!important}}@media (max-width:600px){.event-type-list{gap:12px!important;padding:8px!important}.event-type-card{flex:1 1 100%!important;max-width:100%!important;padding:0!important}.event-type-img{height:160px!important}.event-type-title{font-size:1.1rem!important}.event-type-desc{font-size:.8rem!important;max-height:100px!important;min-height:100px!important}.event-type-content{padding:16px 24px!important}}.event-type-markdown{overflow:visible!important}.event-type-markdown p{color:var(--bw-text-muted);font-family:var(--bw-font-family);line-height:1.6;margin:0 0 8px}.event-type-markdown p:last-child{margin-bottom:0}.event-type-markdown h2{font-size:18px!important;font-weight:700!important;margin:12px 0 6px!important}.event-type-markdown h2,.event-type-markdown h3{color:var(--bw-text-color)!important;line-height:1.3!important}.event-type-markdown h3{font-size:16px!important;font-weight:600!important;margin:10px 0 4px!important}.event-type-markdown strong{color:var(--bw-text-color);font-weight:600}.event-type-markdown em{font-style:italic}.event-type-markdown u{text-decoration:underline}.event-type-markdown ul{list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ul li{color:var(--bw-text-muted)!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ul li:before{color:var(--bw-text-color)!important;content:\"•\"!important;font-weight:700!important;left:-16px!important;position:absolute!important;top:0!important}.event-type-markdown ol{counter-reset:list-counter!important;list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ol li{color:var(--bw-text-muted)!important;counter-increment:list-counter!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ol li:before{color:var(--bw-text-color)!important;content:counter(list-counter) \".\"!important;font-weight:700!important;left:-20px!important;position:absolute!important;top:0!important}.event-type-markdown blockquote{border-left:2px solid var(--bw-border-color);color:var(--bw-text-muted);font-style:italic;margin:4px 0;padding-left:12px}.event-type-markdown a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:var(--bw-text-color);font-weight:600;margin-bottom:.5em}.markdown-content h1{font-size:1.5em}.markdown-content h2{font-size:1.25em}.markdown-content h3{font-size:1.1em}.markdown-content p{line-height:1.6;margin-bottom:1em}.markdown-content ol,.markdown-content ul{margin-bottom:1em;padding-left:1.5em}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin-bottom:.25em}.markdown-content a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content a:hover{opacity:.8}.markdown-content strong{font-weight:600}.markdown-content em{font-style:italic}.markdown-content code{background:var(--bw-highlight-subtle);border-radius:4px;font-family:monospace;font-size:.9em;padding:.125em .25em}.markdown-content blockquote{border-left:3px solid var(--bw-highlight-color);color:var(--bw-text-muted);margin:1em 0;padding-left:1em}.print-only{display:none}.print-hidden{display:block}@media print{.print-only{display:block}.print-hidden{display:none!important}.print-booking-header{border-bottom:2px solid #000;display:block;margin-bottom:24px;padding-bottom:16px;text-align:center}.print-booking-header h1{font-size:24px;margin:0 0 8px}.print-booking-header .subtitle{color:#666;font-size:14px}.print-booking-card{border:1px solid #ccc;border-radius:8px;margin-bottom:16px;padding:16px;page-break-inside:avoid}.print-section-title{border-bottom:1px solid #ddd;display:block;font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px}.print-detail-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}.print-detail-item{margin-bottom:8px}.print-detail-label{color:#666;font-size:12px;margin-bottom:4px}.print-detail-value{font-size:14px;font-weight:600}.print-status-badge{border-radius:9999px;display:inline-block;font-size:12px;font-weight:600;padding:4px 12px}.print-status-paid{background-color:#dcfce7;color:#166534;display:inline-block}.print-participant{align-items:center;background-color:#f9fafb;border-radius:4px;display:flex;justify-content:space-between;margin-bottom:8px;padding:8px}.print-participant-name{font-weight:600}.print-participant-age{color:#666;font-size:12px}.print-payment-summary{display:block}.print-payment-row{border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:4px 0}.print-payment-row:last-child{border-bottom:none;font-weight:600}.print-footer{border-top:1px solid #ddd;color:#666;display:block;font-size:12px;margin-top:24px;padding-top:16px;text-align:center}.print-footer p{margin:4px 0}}";
|
|
17382
17933
|
styleInject(css_248z);
|
|
17383
17934
|
|
|
17384
17935
|
// Export init function for vanilla JS usage with Preact
|