@bigz-app/booking-widget 1.4.5 → 1.5.2

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.
Files changed (54) hide show
  1. package/dist/booking-widget.js +710 -112
  2. package/dist/booking-widget.js.map +1 -1
  3. package/dist/components/UniversalBookingWidget.d.ts +21 -105
  4. package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
  5. package/dist/components/booking/MolliePaymentForm.d.ts +11 -1
  6. package/dist/components/booking/MolliePaymentForm.d.ts.map +1 -1
  7. package/dist/components/booking/StripePaymentForm.d.ts +11 -1
  8. package/dist/components/booking/StripePaymentForm.d.ts.map +1 -1
  9. package/dist/components/events/EventInstanceSelection.d.ts +3 -1
  10. package/dist/components/events/EventInstanceSelection.d.ts.map +1 -1
  11. package/dist/components/events/EventTypeSelection.d.ts +17 -1
  12. package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
  13. package/dist/components/events/FreeformSelection.d.ts +49 -0
  14. package/dist/components/events/FreeformSelection.d.ts.map +1 -0
  15. package/dist/components/events/NextEventsPreview.d.ts +1 -1
  16. package/dist/components/events/NextEventsPreview.d.ts.map +1 -1
  17. package/dist/components/events/SpecialsView.d.ts +1 -1
  18. package/dist/components/events/SpecialsView.d.ts.map +1 -1
  19. package/dist/components/events/index.d.ts +1 -0
  20. package/dist/components/events/index.d.ts.map +1 -1
  21. package/dist/components/events/types.d.ts +42 -0
  22. package/dist/components/events/types.d.ts.map +1 -0
  23. package/dist/components/upsells/UpsellCard.d.ts +1 -1
  24. package/dist/components/upsells/UpsellCard.d.ts.map +1 -1
  25. package/dist/components/upsells/UpsellsStep.d.ts +2 -44
  26. package/dist/components/upsells/UpsellsStep.d.ts.map +1 -1
  27. package/dist/components/upsells/types.d.ts +44 -0
  28. package/dist/components/upsells/types.d.ts.map +1 -0
  29. package/dist/components/voucher/VoucherIntegration.d.ts +3 -2
  30. package/dist/components/voucher/VoucherIntegration.d.ts.map +1 -1
  31. package/dist/components/voucher/VoucherPurchaseForm.d.ts +9 -16
  32. package/dist/components/voucher/VoucherPurchaseForm.d.ts.map +1 -1
  33. package/dist/components/voucher/index.d.ts +1 -1
  34. package/dist/components/voucher/index.d.ts.map +1 -1
  35. package/dist/components/voucher/types.d.ts +15 -0
  36. package/dist/components/voucher/types.d.ts.map +1 -0
  37. package/dist/components/voucher/useVoucherConfig.d.ts +6 -0
  38. package/dist/components/voucher/useVoucherConfig.d.ts.map +1 -1
  39. package/dist/config.d.ts +66 -0
  40. package/dist/config.d.ts.map +1 -0
  41. package/dist/i18n/locales/de.d.ts.map +1 -1
  42. package/dist/i18n/locales/en.d.ts.map +1 -1
  43. package/dist/i18n/locales/es.d.ts.map +1 -1
  44. package/dist/i18n/locales/pt.d.ts.map +1 -1
  45. package/dist/i18n/locales/sv.d.ts.map +1 -1
  46. package/dist/index.cjs +710 -112
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.esm.js +710 -112
  49. package/dist/index.esm.js.map +1 -1
  50. package/dist/styles/StyleProvider.d.ts +1 -1
  51. package/dist/styles/StyleProvider.d.ts.map +1 -1
  52. package/dist/utils.d.ts +1 -1
  53. package/dist/utils.d.ts.map +1 -1
  54. package/package.json +1 -1
@@ -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}}",
@@ -757,6 +779,21 @@
757
779
  "instances.bestPrice": "¡¡¡mejor precio!!!",
758
780
  "instances.goodPrice": "buen precio",
759
781
  "instances.oclock": "",
782
+ // Lista de espera
783
+ "waitlist.joinCta": "Lista de espera",
784
+ "waitlist.title": "Apuntarse a la lista de espera",
785
+ "waitlist.subtitle": "{{name}} está agotado. Deja tus datos y te contactaremos si se libera un lugar.",
786
+ "waitlist.name": "Nombre",
787
+ "waitlist.email": "Correo electrónico",
788
+ "waitlist.phone": "Teléfono (opcional)",
789
+ "waitlist.requiredFields": "Por favor, introduce tu nombre y correo electrónico.",
790
+ "waitlist.join": "Apuntarse",
791
+ "waitlist.submitting": "Apuntando...",
792
+ "waitlist.cancel": "Cancelar",
793
+ "waitlist.submitError": "Algo salió mal. Por favor, inténtalo de nuevo.",
794
+ "waitlist.successTitle": "¡Estás en la lista!",
795
+ "waitlist.successMessage": "Eres el número {{position}} en la lista de espera. Te contactaremos si se libera un lugar.",
796
+ "waitlist.done": "Listo",
760
797
  // Next events preview
761
798
  "nextEvents.title": "Próximas fechas disponibles",
762
799
  "nextEvents.subtitle": "Selecciona una fecha o consulta todas las fechas disponibles",
@@ -860,6 +897,13 @@
860
897
  "voucher.monetaryTypeDesc": "Elige cualquier cantidad",
861
898
  "voucher.eventType": "Vale de experiencia",
862
899
  "voucher.eventTypeDesc": "Para una experiencia específica",
900
+ "voucher.categoryType": "Vale de categoría",
901
+ "voucher.categoryTypeDesc": "Para cualquier experiencia de una categoría",
902
+ "voucher.categoryVoucher": "Vale de categoría",
903
+ "voucher.selectCategory": "Seleccionar categoría",
904
+ "voucher.categoryLabel": "Categoría",
905
+ "voucher.selectCategoryPlaceholder": "Elige una categoría...",
906
+ "voucher.categoryAmount": "Valor del vale",
863
907
  "voucher.selectAmount": "Selecciona el importe",
864
908
  "voucher.customAmount": "O introduce un importe personalizado",
865
909
  "voucher.amountRange": "El importe debe estar entre {{min}} y {{max}}",
@@ -1047,6 +1091,21 @@
1047
1091
  "instances.bestPrice": "melhor preço !!!",
1048
1092
  "instances.goodPrice": "bom preço",
1049
1093
  "instances.oclock": "",
1094
+ // Lista de espera
1095
+ "waitlist.joinCta": "Lista de espera",
1096
+ "waitlist.title": "Entrar na lista de espera",
1097
+ "waitlist.subtitle": "{{name}} está esgotado. Deixe os seus dados e entraremos em contacto se surgir uma vaga.",
1098
+ "waitlist.name": "Nome",
1099
+ "waitlist.email": "E-mail",
1100
+ "waitlist.phone": "Telefone (opcional)",
1101
+ "waitlist.requiredFields": "Por favor, introduza o seu nome e e-mail.",
1102
+ "waitlist.join": "Inscrever-se",
1103
+ "waitlist.submitting": "A inscrever...",
1104
+ "waitlist.cancel": "Cancelar",
1105
+ "waitlist.submitError": "Algo correu mal. Por favor, tente novamente.",
1106
+ "waitlist.successTitle": "Está na lista!",
1107
+ "waitlist.successMessage": "É o número {{position}} na lista de espera. Entraremos em contacto se surgir uma vaga.",
1108
+ "waitlist.done": "Concluído",
1050
1109
  // Next events preview
1051
1110
  "nextEvents.title": "Próximas datas disponíveis",
1052
1111
  "nextEvents.subtitle": "Selecione uma data ou veja todas as datas disponíveis",
@@ -1150,6 +1209,13 @@
1150
1209
  "voucher.monetaryTypeDesc": "Escolha qualquer quantia",
1151
1210
  "voucher.eventType": "Vale de experiência",
1152
1211
  "voucher.eventTypeDesc": "Para uma experiência específica",
1212
+ "voucher.categoryType": "Vale de categoria",
1213
+ "voucher.categoryTypeDesc": "Para qualquer experiência de uma categoria",
1214
+ "voucher.categoryVoucher": "Vale de categoria",
1215
+ "voucher.selectCategory": "Selecionar categoria",
1216
+ "voucher.categoryLabel": "Categoria",
1217
+ "voucher.selectCategoryPlaceholder": "Escolha uma categoria...",
1218
+ "voucher.categoryAmount": "Valor do vale",
1153
1219
  "voucher.selectAmount": "Selecione o valor",
1154
1220
  "voucher.customAmount": "Ou introduza um valor personalizado",
1155
1221
  "voucher.amountRange": "O valor deve estar entre {{min}} e {{max}}",
@@ -1337,6 +1403,21 @@
1337
1403
  "instances.bestPrice": "bästa pris !!!",
1338
1404
  "instances.goodPrice": "bra pris",
1339
1405
  "instances.oclock": "",
1406
+ // Väntelista
1407
+ "waitlist.joinCta": "Ställ dig i kö",
1408
+ "waitlist.title": "Ställ dig i kö",
1409
+ "waitlist.subtitle": "{{name}} är fullbokat. Lämna dina uppgifter så kontaktar vi dig om en plats blir ledig.",
1410
+ "waitlist.name": "Namn",
1411
+ "waitlist.email": "E-post",
1412
+ "waitlist.phone": "Telefon (valfritt)",
1413
+ "waitlist.requiredFields": "Ange ditt namn och din e-postadress.",
1414
+ "waitlist.join": "Ställ dig i kö",
1415
+ "waitlist.submitting": "Anmäler...",
1416
+ "waitlist.cancel": "Avbryt",
1417
+ "waitlist.submitError": "Något gick fel. Försök igen.",
1418
+ "waitlist.successTitle": "Du står i kön!",
1419
+ "waitlist.successMessage": "Du är nummer {{position}} på väntelistan. Vi hör av oss om en plats blir ledig.",
1420
+ "waitlist.done": "Klar",
1340
1421
  // Next events preview
1341
1422
  "nextEvents.title": "Nästa tillgängliga datum",
1342
1423
  "nextEvents.subtitle": "Välj ett datum eller visa alla tillgängliga datum",
@@ -1440,6 +1521,13 @@
1440
1521
  "voucher.monetaryTypeDesc": "Välj valfritt belopp",
1441
1522
  "voucher.eventType": "Upplevelsebevis",
1442
1523
  "voucher.eventTypeDesc": "För en specifik upplevelse",
1524
+ "voucher.categoryType": "Kategoripresent",
1525
+ "voucher.categoryTypeDesc": "För alla upplevelser i en kategori",
1526
+ "voucher.categoryVoucher": "Kategoripresent",
1527
+ "voucher.selectCategory": "Välj kategori",
1528
+ "voucher.categoryLabel": "Kategori",
1529
+ "voucher.selectCategoryPlaceholder": "Välj en kategori...",
1530
+ "voucher.categoryAmount": "Presentkortsvärde",
1443
1531
  "voucher.selectAmount": "Välj belopp",
1444
1532
  "voucher.customAmount": "Eller ange eget belopp",
1445
1533
  "voucher.amountRange": "Beloppet måste vara mellan {{min}} och {{max}}",
@@ -6478,7 +6566,7 @@
6478
6566
  document.head.appendChild(script);
6479
6567
  });
6480
6568
  }
6481
- function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess: _onSuccess, onError, upsellSelections = [], mollieProfileId, mollieTestmode, }) {
6569
+ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess: _onSuccess, onError, upsellSelections = [], mollieProfileId, mollieTestmode, freeformSelection, }) {
6482
6570
  const t = useTranslations();
6483
6571
  const { locale } = useLocale();
6484
6572
  const [isLoading, setIsLoading] = d$1(false);
@@ -6492,7 +6580,9 @@
6492
6580
  const cardFormRef = A$2(null);
6493
6581
  const cardContainerRef = A$2(null);
6494
6582
  const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
6495
- const isFullyCoveredByGiftCards = isGiftCardFullyCovered(giftCards, eventDetails?.price || 0, participantCount, discountCode?.discountAmount || 0);
6583
+ const isFullyCoveredByGiftCards = freeformSelection
6584
+ ? false
6585
+ : isGiftCardFullyCovered(giftCards, eventDetails?.price || 0, participantCount, discountCode?.discountAmount || 0);
6496
6586
  const isCreditCard = selectedMethod === "creditcard";
6497
6587
  y$1(() => {
6498
6588
  if (isFullyCoveredByGiftCards)
@@ -6602,6 +6692,9 @@
6602
6692
  return (u$2(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: _onSuccess, onError: onError, upsellSelections: upsellSelections }));
6603
6693
  }
6604
6694
  const validateBeforePayment = () => {
6695
+ if (freeformSelection) {
6696
+ return null;
6697
+ }
6605
6698
  const participantCount = formData.participants.filter((p) => p.name?.trim()).length;
6606
6699
  const availableSpots = eventDetails.availableSpots || 0;
6607
6700
  if (participantCount > availableSpots) {
@@ -6613,21 +6706,41 @@
6613
6706
  return null;
6614
6707
  };
6615
6708
  const buildRequestData = (cardToken) => ({
6616
- eventInstanceId: config.eventInstanceId || eventDetails.id,
6617
- organizationId: config.organizationId,
6618
- amount: Math.round(totalAmount),
6619
- currency: "EUR",
6620
- participants: formData.participants.filter((p) => p.name?.trim()),
6621
- discountCode: discountCode?.code,
6622
- giftCardCodes: giftCards?.map((gc) => gc.code) || [],
6623
- customerName: formData.customerName?.trim(),
6624
- customerEmail: formData.customerEmail?.trim(),
6625
- customerPhone: formData.customerPhone?.trim(),
6626
- comment: formData.comment?.trim(),
6627
- ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6628
- ...(cardToken && { cardToken }),
6629
- ...(selectedMethod && !cardToken && { method: selectedMethod }),
6630
- ...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
6709
+ ...(freeformSelection
6710
+ ? {
6711
+ eventTypeId: freeformSelection.eventTypeId,
6712
+ organizationId: config.organizationId,
6713
+ start: freeformSelection.start,
6714
+ end: freeformSelection.end,
6715
+ requestedUnits: freeformSelection.requestedUnits,
6716
+ amount: Math.round(totalAmount),
6717
+ currency: "EUR",
6718
+ customerName: freeformSelection.customerName.trim(),
6719
+ customerEmail: freeformSelection.customerEmail.trim(),
6720
+ customerPhone: freeformSelection.customerPhone?.trim(),
6721
+ comment: freeformSelection.comment?.trim(),
6722
+ locale,
6723
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6724
+ ...(cardToken && { cardToken }),
6725
+ ...(selectedMethod && !cardToken && { method: selectedMethod }),
6726
+ }
6727
+ : {
6728
+ eventInstanceId: config.eventInstanceId || eventDetails.id,
6729
+ organizationId: config.organizationId,
6730
+ amount: Math.round(totalAmount),
6731
+ currency: "EUR",
6732
+ participants: formData.participants.filter((p) => p.name?.trim()),
6733
+ discountCode: discountCode?.code,
6734
+ giftCardCodes: giftCards?.map((gc) => gc.code) || [],
6735
+ customerName: formData.customerName?.trim(),
6736
+ customerEmail: formData.customerEmail?.trim(),
6737
+ customerPhone: formData.customerPhone?.trim(),
6738
+ comment: formData.comment?.trim(),
6739
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6740
+ ...(cardToken && { cardToken }),
6741
+ ...(selectedMethod && !cardToken && { method: selectedMethod }),
6742
+ ...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
6743
+ }),
6631
6744
  });
6632
6745
  const handleCardPayment = async () => {
6633
6746
  if (!mollieRef.current)
@@ -6645,7 +6758,9 @@
6645
6758
  setPaymentError(tokenError?.message || t("error.createPayment"));
6646
6759
  return;
6647
6760
  }
6648
- const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/create-mollie-payment"), {
6761
+ const response = await fetch(getApiUrl(config.apiBaseUrl, freeformSelection
6762
+ ? "/booking/create-freeform-mollie-payment"
6763
+ : "/booking/create-mollie-payment"), {
6649
6764
  method: "POST",
6650
6765
  headers: createApiHeaders(config, locale),
6651
6766
  body: JSON.stringify(createRequestBody(config, buildRequestData(token))),
@@ -6681,7 +6796,9 @@
6681
6796
  setPaymentError(validationError);
6682
6797
  return;
6683
6798
  }
6684
- const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/create-mollie-payment"), {
6799
+ const response = await fetch(getApiUrl(config.apiBaseUrl, freeformSelection
6800
+ ? "/booking/create-freeform-mollie-payment"
6801
+ : "/booking/create-mollie-payment"), {
6685
6802
  method: "POST",
6686
6803
  headers: createApiHeaders(config, locale),
6687
6804
  body: JSON.stringify(createRequestBody(config, buildRequestData())),
@@ -6709,7 +6826,7 @@
6709
6826
  ? t("button.processingPayment")
6710
6827
  : selectedMethodData
6711
6828
  ? t("payment.payWithMethod", { method: selectedMethodData.description })
6712
- : totalAmount < eventDetails.price * formData.participants.filter((p) => p.name?.trim()).length
6829
+ : !freeformSelection && totalAmount < eventDetails.price * formData.participants.filter((p) => p.name?.trim()).length
6713
6830
  ? t("button.depositAndBook")
6714
6831
  : t("button.bookNow");
6715
6832
  const isPayDisabled = isLoading || !selectedMethod || (isCreditCard && !isMollieReady);
@@ -7016,12 +7133,18 @@
7016
7133
  };
7017
7134
 
7018
7135
  // Inner component that uses the Stripe hooks
7019
- function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onError, }) {
7136
+ function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onError, freeformSelection, }) {
7020
7137
  const t = useTranslations();
7021
- const participantCount = formData.participants.filter((p) => p.name.trim()).length;
7022
- const fullAmount = eventDetails.price * participantCount;
7023
- const submitLabel = totalAmount < fullAmount ? t("button.depositAndBook") : t("button.bookNow");
7138
+ const isFreeform = Boolean(freeformSelection);
7139
+ const participantCount = isFreeform
7140
+ ? 1
7141
+ : formData.participants.filter((p) => p.name.trim()).length;
7142
+ const fullAmount = isFreeform ? totalAmount : eventDetails.price * participantCount;
7143
+ const submitLabel = !isFreeform && totalAmount < fullAmount ? t("button.depositAndBook") : t("button.bookNow");
7024
7144
  return (u$2(StripeElementsPaymentForm, { onValidate: () => {
7145
+ if (isFreeform) {
7146
+ return null;
7147
+ }
7025
7148
  const availableSpots = eventDetails.availableSpots || 0;
7026
7149
  if (participantCount > availableSpots) {
7027
7150
  return t("payment.tooManyParticipants", { count: participantCount, available: availableSpots });
@@ -7043,7 +7166,7 @@
7043
7166
  },
7044
7167
  }, submitContent: u$2(k$3, { children: submitLabel }), loadingContent: u$2(k$3, { children: [spinner("var(--bw-surface-color)"), " ", t("button.processingPayment")] }) }));
7045
7168
  }
7046
- function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripeAppearance, upsellSelections = [], }) {
7169
+ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripeAppearance, upsellSelections = [], freeformSelection, }) {
7047
7170
  const t = useTranslations();
7048
7171
  const { locale } = useLocale();
7049
7172
  const [clientSecret, setClientSecret] = d$1(null);
@@ -7063,7 +7186,9 @@
7063
7186
  return loadStripe(systemConfig.stripePublishableKey, stripeOptions);
7064
7187
  }, [systemConfig?.stripePublishableKey, systemConfig?.connectedAccountId, locale]);
7065
7188
  const storageKey = typeof window !== "undefined"
7066
- ? `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
7189
+ ? freeformSelection
7190
+ ? `bw_pi_freeform_${config?.organizationId}_${freeformSelection.eventTypeId}_${freeformSelection.start}_${freeformSelection.end}_${freeformSelection.requestedUnits}`
7191
+ : `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
7067
7192
  : "";
7068
7193
  const PAYMENT_INTENT_TTL = 24 * 60 * 60 * 1000;
7069
7194
  function loadPersistedPaymentIntent() {
@@ -7118,48 +7243,82 @@
7118
7243
  }, [paymentIntentId]);
7119
7244
  y$1(() => {
7120
7245
  const createStripePayment = async () => {
7121
- if (!systemConfig || !eventDetails || !formData.participants?.length) {
7246
+ if (!systemConfig || !eventDetails) {
7247
+ return;
7248
+ }
7249
+ if (freeformSelection) {
7250
+ if (!freeformSelection.customerEmail?.trim() ||
7251
+ !freeformSelection.customerName?.trim() ||
7252
+ !freeformSelection.start ||
7253
+ !freeformSelection.end) {
7254
+ return;
7255
+ }
7256
+ }
7257
+ else if (!formData.participants?.length) {
7122
7258
  return;
7123
7259
  }
7124
7260
  const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
7125
- if (participantCount === 0 || !formData.customerEmail?.trim() || !formData.customerName?.trim()) {
7261
+ if (!freeformSelection &&
7262
+ (participantCount === 0 || !formData.customerEmail?.trim() || !formData.customerName?.trim())) {
7126
7263
  return;
7127
7264
  }
7128
7265
  setIsCreatingPaymentIntent(true);
7129
7266
  setPaymentError(null);
7130
7267
  try {
7131
- const requestData = {
7132
- eventInstanceId: config.eventInstanceId || eventDetails.id,
7133
- organizationId: config.organizationId,
7134
- amount: Math.round(totalAmount),
7135
- currency: "eur",
7136
- participants: formData.participants.filter((p) => p.name?.trim()),
7137
- discountCode: discountCode?.code,
7138
- giftCardCodes: giftCards?.map((gc) => gc.code) || [],
7139
- customerName: formData.customerName?.trim(),
7140
- customerEmail: formData.customerEmail?.trim(),
7141
- customerPhone: formData.customerPhone?.trim(),
7142
- comment: formData.comment?.trim(),
7143
- ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
7144
- ...(paymentIntentId && { paymentIntentId }),
7145
- ...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
7146
- };
7147
- if (!requestData.eventInstanceId) {
7148
- throw new Error("Event instance ID is required");
7149
- }
7268
+ const endpoint = freeformSelection
7269
+ ? "/booking/create-freeform-stripe-payment"
7270
+ : "/booking/create-stripe-payment";
7271
+ const requestData = freeformSelection
7272
+ ? {
7273
+ eventTypeId: freeformSelection.eventTypeId,
7274
+ organizationId: config.organizationId,
7275
+ start: freeformSelection.start,
7276
+ end: freeformSelection.end,
7277
+ requestedUnits: freeformSelection.requestedUnits,
7278
+ amount: Math.round(totalAmount),
7279
+ currency: "eur",
7280
+ customerName: freeformSelection.customerName.trim(),
7281
+ customerEmail: freeformSelection.customerEmail.trim(),
7282
+ customerPhone: freeformSelection.customerPhone?.trim(),
7283
+ comment: freeformSelection.comment?.trim(),
7284
+ locale,
7285
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
7286
+ ...(paymentIntentId && { paymentIntentId }),
7287
+ }
7288
+ : {
7289
+ eventInstanceId: config.eventInstanceId || eventDetails.id,
7290
+ organizationId: config.organizationId,
7291
+ amount: Math.round(totalAmount),
7292
+ currency: "eur",
7293
+ participants: formData.participants.filter((p) => p.name?.trim()),
7294
+ discountCode: discountCode?.code,
7295
+ giftCardCodes: giftCards?.map((gc) => gc.code) || [],
7296
+ customerName: formData.customerName?.trim(),
7297
+ customerEmail: formData.customerEmail?.trim(),
7298
+ customerPhone: formData.customerPhone?.trim(),
7299
+ comment: formData.comment?.trim(),
7300
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
7301
+ ...(paymentIntentId && { paymentIntentId }),
7302
+ ...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
7303
+ };
7150
7304
  if (!requestData.organizationId) {
7151
7305
  throw new Error("Organization ID is required");
7152
7306
  }
7153
- if (!requestData.amount || requestData.amount <= 0) {
7307
+ if (requestData.amount === undefined || requestData.amount < 0) {
7154
7308
  throw new Error("Valid amount is required");
7155
7309
  }
7156
- if (!requestData.participants || requestData.participants.length === 0) {
7310
+ if (!freeformSelection && "eventInstanceId" in requestData && !requestData.eventInstanceId) {
7311
+ throw new Error("Event instance ID is required");
7312
+ }
7313
+ if (!freeformSelection &&
7314
+ "participants" in requestData &&
7315
+ (!requestData.participants || requestData.participants.length === 0)) {
7157
7316
  throw new Error("At least one participant is required");
7158
7317
  }
7159
7318
  if (!requestData.customerEmail) {
7160
7319
  throw new Error("Customer email is required");
7161
7320
  }
7162
- const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/create-stripe-payment"), {
7321
+ const response = await fetch(getApiUrl(config.apiBaseUrl, endpoint), {
7163
7322
  method: "POST",
7164
7323
  headers: createApiHeaders(config, locale),
7165
7324
  body: JSON.stringify(createRequestBody(config, requestData)),
@@ -7204,9 +7363,13 @@
7204
7363
  giftCards,
7205
7364
  config,
7206
7365
  upsellSelections,
7366
+ freeformSelection,
7367
+ locale,
7207
7368
  ]);
7208
7369
  const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
7209
- const isFullyCoveredByGiftCards = isGiftCardFullyCovered(giftCards, eventDetails?.price || 0, participantCount, discountCode?.discountAmount || 0);
7370
+ const isFullyCoveredByGiftCards = freeformSelection
7371
+ ? false
7372
+ : isGiftCardFullyCovered(giftCards, eventDetails?.price || 0, participantCount, discountCode?.discountAmount || 0);
7210
7373
  if (isFullyCoveredByGiftCards && totalAmount <= 0) {
7211
7374
  return (u$2(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: onSuccess, onError: onError, upsellSelections: upsellSelections }));
7212
7375
  }
@@ -7245,7 +7408,7 @@
7245
7408
  setPaymentIntentId(null);
7246
7409
  setClientSecret(null);
7247
7410
  onSuccess(result);
7248
- }, onError: onError }) }));
7411
+ }, onError: onError, ...(freeformSelection ? { freeformSelection } : {}) }) }));
7249
7412
  }
7250
7413
 
7251
7414
  const HOLD_DURATION_SECONDS = 15 * 60;
@@ -13505,20 +13668,22 @@
13505
13668
  const labelStyles = formStyles.label;
13506
13669
  const inputStyles = formStyles.input;
13507
13670
  const errorTextStyles = formStyles.error;
13508
- function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClose, onSuccess, onError, systemConfig, preselectedEventTypeId, isLoadingEventTypes = false, }) {
13671
+ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, categories, isOpen, onClose, onSuccess, onError, systemConfig, preselectedEventTypeId, isLoadingEventTypes = false, }) {
13509
13672
  const t = useTranslations();
13510
13673
  const { locale } = useLocale();
13511
13674
  const paymentProvider = systemConfig?.paymentProvider ?? "stripe";
13512
13675
  // 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
13676
  const canUseMonetaryVoucherType = voucherConfig.allowMonetaryVouchers !== false;
13516
13677
  const canUseEventVoucherType = voucherConfig.allowEventVouchers;
13517
- const showVoucherTypeSelection = canUseMonetaryVoucherType && canUseEventVoucherType && !isLoadingEventTypes;
13678
+ const canUseCategoryVoucherType = canUseEventVoucherType && categories.length > 0;
13679
+ const availableTypeCount = [canUseMonetaryVoucherType, canUseEventVoucherType, canUseCategoryVoucherType].filter(Boolean).length;
13680
+ const showVoucherTypeSelection = availableTypeCount > 1 && !isLoadingEventTypes;
13518
13681
  const [voucherType, setVoucherType] = d$1(canUseMonetaryVoucherType ? "monetary" : "event");
13519
13682
  const [monetaryAmount, setMonetaryAmount] = d$1(voucherConfig.monetaryPresets[2] || 10000);
13520
13683
  const [selectedEventTypeId, setSelectedEventTypeId] = d$1(null);
13521
13684
  const [eventQuantity, setEventQuantity] = d$1(1);
13685
+ const [selectedCategoryId, setSelectedCategoryId] = d$1(null);
13686
+ const [categoryAmount, setCategoryAmount] = d$1(10000);
13522
13687
  const [recipientName, setRecipientName] = d$1("");
13523
13688
  const [message, setMessage] = d$1("");
13524
13689
  const [customerName, setCustomerName] = d$1("");
@@ -13578,6 +13743,9 @@
13578
13743
  if (voucherType === "monetary") {
13579
13744
  return monetaryAmount;
13580
13745
  }
13746
+ else if (voucherType === "category") {
13747
+ return categoryAmount;
13748
+ }
13581
13749
  else {
13582
13750
  const eventType = eventTypes.find((et) => et.id === selectedEventTypeId);
13583
13751
  if (eventType) {
@@ -13585,7 +13753,7 @@
13585
13753
  }
13586
13754
  return 0;
13587
13755
  }
13588
- }, [voucherType, monetaryAmount, selectedEventTypeId, eventQuantity, eventTypes]);
13756
+ }, [voucherType, monetaryAmount, selectedEventTypeId, eventQuantity, eventTypes, categoryAmount]);
13589
13757
  const selectedEventType = T$2(() => {
13590
13758
  return eventTypes.find((et) => et.id === selectedEventTypeId);
13591
13759
  }, [eventTypes, selectedEventTypeId]);
@@ -13594,6 +13762,8 @@
13594
13762
  voucherType,
13595
13763
  selectedEventTypeId,
13596
13764
  eventQuantity,
13765
+ selectedCategoryId,
13766
+ categoryAmount,
13597
13767
  recipientName: recipientName.trim(),
13598
13768
  message: message.trim(),
13599
13769
  customerName: customerName.trim(),
@@ -13603,6 +13773,8 @@
13603
13773
  voucherType,
13604
13774
  selectedEventTypeId,
13605
13775
  eventQuantity,
13776
+ selectedCategoryId,
13777
+ categoryAmount,
13606
13778
  recipientName,
13607
13779
  message,
13608
13780
  customerName,
@@ -13621,24 +13793,37 @@
13621
13793
  return false;
13622
13794
  if (voucherType === "event" && !selectedEventTypeId)
13623
13795
  return false;
13796
+ if (voucherType === "category" && !selectedCategoryId)
13797
+ return false;
13624
13798
  return true;
13625
- }, [customerName, customerEmail, acceptTerms, totalAmount, voucherConfig, voucherType, selectedEventTypeId]);
13799
+ }, [customerName, customerEmail, acceptTerms, totalAmount, voucherType, selectedEventTypeId, selectedCategoryId]);
13626
13800
  const defaultMonetaryAmount = T$2(() => voucherConfig.monetaryPresets[2] || voucherConfig.monetaryPresets[0] || 1000, [voucherConfig.monetaryPresets]);
13627
13801
  const handleVoucherTypeChange = q$2((nextType) => {
13628
13802
  setVoucherType(nextType);
13629
13803
  setPaymentError(null);
13630
13804
  if (nextType === "event") {
13631
- // Keep voucher type mutually exclusive: entering event flow resets amount selection state.
13632
13805
  setMonetaryAmount(defaultMonetaryAmount);
13806
+ setSelectedCategoryId(null);
13807
+ setCategoryAmount(10000);
13633
13808
  if (eventTypes.length === 1) {
13634
13809
  setSelectedEventTypeId(eventTypes[0]?.id ?? null);
13635
13810
  }
13636
13811
  return;
13637
13812
  }
13638
- // Keep voucher type mutually exclusive: entering amount flow clears event selection.
13813
+ if (nextType === "category") {
13814
+ setMonetaryAmount(defaultMonetaryAmount);
13815
+ setSelectedEventTypeId(null);
13816
+ setEventQuantity(1);
13817
+ if (categories.length === 1) {
13818
+ setSelectedCategoryId(categories[0]?.id ?? null);
13819
+ }
13820
+ return;
13821
+ }
13639
13822
  setSelectedEventTypeId(null);
13640
13823
  setEventQuantity(1);
13641
- }, [defaultMonetaryAmount, eventTypes]);
13824
+ setSelectedCategoryId(null);
13825
+ setCategoryAmount(10000);
13826
+ }, [defaultMonetaryAmount, eventTypes, categories]);
13642
13827
  y$1(() => {
13643
13828
  if (!canUseMonetaryVoucherType && voucherType === "monetary") {
13644
13829
  setVoucherType("event");
@@ -13679,9 +13864,12 @@
13679
13864
  organizationId: config.organizationId,
13680
13865
  amount: totalAmount,
13681
13866
  currency: "eur",
13682
- voucherType,
13683
- monetaryValue: voucherType === "monetary" ? totalAmount : undefined,
13867
+ voucherType: voucherType === "category" ? "monetary" : voucherType,
13868
+ monetaryValue: voucherType === "monetary" ? totalAmount
13869
+ : voucherType === "category" ? categoryAmount
13870
+ : undefined,
13684
13871
  eventTypeId: voucherType === "event" ? selectedEventTypeId : undefined,
13872
+ eventCategoryId: voucherType === "category" ? selectedCategoryId : undefined,
13685
13873
  eventAmount: voucherType === "event" ? eventQuantity : undefined,
13686
13874
  recipientName: recipientName.trim() || undefined,
13687
13875
  message: message.trim() || undefined,
@@ -13872,7 +14060,30 @@
13872
14060
  fontSize: "13px",
13873
14061
  color: "var(--bw-text-muted)",
13874
14062
  marginTop: "4px",
13875
- }, children: t("voucher.eventTypeDesc") })] }))] })] })), voucherType === "monetary" && (u$2("div", { style: cardStyles, children: [u$2("h2", { style: sectionHeaderStyles, children: t("voucher.selectAmount") }), u$2("div", { style: {
14063
+ }, children: t("voucher.eventTypeDesc") })] })), canUseCategoryVoucherType && (u$2("button", { type: "button", onClick: () => handleVoucherTypeChange("category"), style: {
14064
+ flex: 1,
14065
+ padding: "16px",
14066
+ borderRadius: "var(--bw-border-radius)",
14067
+ border: voucherType === "category"
14068
+ ? "2px solid var(--bw-highlight-color)"
14069
+ : "1px solid var(--bw-border-color)",
14070
+ backgroundColor: voucherType === "category"
14071
+ ? "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)"
14072
+ : "var(--bw-surface-color)",
14073
+ cursor: "pointer",
14074
+ fontFamily: "var(--bw-font-family)",
14075
+ transition: "all 0.2s ease",
14076
+ textAlign: "center",
14077
+ }, children: [u$2("div", { style: {
14078
+ fontWeight: 600,
14079
+ color: voucherType === "category"
14080
+ ? "var(--bw-highlight-color)"
14081
+ : "var(--bw-text-color)",
14082
+ }, children: t("voucher.categoryType") }), u$2("div", { style: {
14083
+ fontSize: "13px",
14084
+ color: "var(--bw-text-muted)",
14085
+ marginTop: "4px",
14086
+ }, 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
14087
  display: "grid",
13877
14088
  gridTemplateColumns: "repeat(3, 1fr)",
13878
14089
  gap: "8px",
@@ -13927,7 +14138,50 @@
13927
14138
  fontSize: "18px",
13928
14139
  color: "var(--bw-highlight-color)",
13929
14140
  fontFamily: "var(--bw-font-family)",
13930
- }, children: formatCurrency(totalAmount) })] }) }))] })), u$2("div", { style: cardStyles, children: [u$2("h2", { style: sectionHeaderStyles, children: t("voucher.recipientSection") }), u$2("p", { style: {
14141
+ }, 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: {
14142
+ ...inputStyles,
14143
+ cursor: categories.length === 1 ? "not-allowed" : "pointer",
14144
+ }, 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: {
14145
+ display: "grid",
14146
+ gridTemplateColumns: "repeat(3, 1fr)",
14147
+ gap: "8px",
14148
+ marginTop: "4px",
14149
+ }, children: voucherConfig.monetaryPresets.map((preset) => (u$2("button", { type: "button", onClick: () => setCategoryAmount(preset), style: {
14150
+ padding: "10px",
14151
+ borderRadius: "var(--bw-border-radius)",
14152
+ border: categoryAmount === preset
14153
+ ? "2px solid var(--bw-highlight-color)"
14154
+ : "1px solid var(--bw-border-color)",
14155
+ backgroundColor: categoryAmount === preset
14156
+ ? "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)"
14157
+ : "var(--bw-surface-color)",
14158
+ cursor: "pointer",
14159
+ fontFamily: "var(--bw-font-family)",
14160
+ fontWeight: 600,
14161
+ fontSize: "14px",
14162
+ color: categoryAmount === preset
14163
+ ? "var(--bw-highlight-color)"
14164
+ : "var(--bw-text-color)",
14165
+ transition: "all 0.2s ease",
14166
+ }, children: formatCurrency(preset) }, preset))) })] }))] }) }), selectedCategoryId && categoryAmount > 0 && (u$2("div", { style: {
14167
+ marginTop: "16px",
14168
+ padding: "12px",
14169
+ backgroundColor: "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)",
14170
+ borderRadius: "var(--bw-border-radius)",
14171
+ border: "1px solid var(--bw-highlight-color)",
14172
+ }, children: u$2("div", { style: {
14173
+ display: "flex",
14174
+ justifyContent: "space-between",
14175
+ alignItems: "center",
14176
+ }, children: [u$2("span", { style: {
14177
+ color: "var(--bw-text-color)",
14178
+ fontFamily: "var(--bw-font-family)",
14179
+ }, children: categories.find((c) => c.id === selectedCategoryId)?.name }), u$2("span", { style: {
14180
+ fontWeight: 700,
14181
+ fontSize: "18px",
14182
+ color: "var(--bw-highlight-color)",
14183
+ fontFamily: "var(--bw-font-family)",
14184
+ }, children: formatCurrency(categoryAmount) })] }) }))] })), u$2("div", { style: cardStyles, children: [u$2("h2", { style: sectionHeaderStyles, children: t("voucher.recipientSection") }), u$2("p", { style: {
13931
14185
  fontSize: "13px",
13932
14186
  color: "var(--bw-text-muted)",
13933
14187
  fontFamily: "var(--bw-font-family)",
@@ -13960,7 +14214,9 @@
13960
14214
  fontFamily: "var(--bw-font-family)",
13961
14215
  }, children: voucherType === "monetary"
13962
14216
  ? t("voucher.monetaryVoucher")
13963
- : `${eventQuantity}x ${selectedEventType?.name || t("voucher.eventVoucher")}` }), recipientName && (u$2("div", { style: {
14217
+ : voucherType === "category"
14218
+ ? categories.find((c) => c.id === selectedCategoryId)?.name || t("voucher.categoryVoucher")
14219
+ : `${eventQuantity}x ${selectedEventType?.name || t("voucher.eventVoucher")}` }), recipientName && (u$2("div", { style: {
13964
14220
  fontSize: "13px",
13965
14221
  color: "var(--bw-text-muted)",
13966
14222
  fontFamily: "var(--bw-font-family)",
@@ -14236,7 +14492,7 @@
14236
14492
  }, children: t("voucher.buyAsGift") })] }));
14237
14493
  }
14238
14494
 
14239
- function VoucherIntegration({ config, voucherConfig, eventTypes, systemConfig, isFormOpen, isLoadingConfig, preselectedEventTypeId, voucherPurchaseResult, isSuccess, showStandaloneCard, onCardClick, onFormClose, onSuccess, onError, onSuccessModalClose, }) {
14495
+ function VoucherIntegration({ config, voucherConfig, eventTypes, categories, systemConfig, isFormOpen, isLoadingConfig, preselectedEventTypeId, voucherPurchaseResult, isSuccess, showStandaloneCard, onCardClick, onFormClose, onSuccess, onError, onSuccessModalClose, }) {
14240
14496
  if (!voucherConfig?.enabled) {
14241
14497
  return null;
14242
14498
  }
@@ -14247,7 +14503,7 @@
14247
14503
  justifyContent: "center",
14248
14504
  }, children: u$2(VoucherPurchaseCard, { config: voucherConfig, minEventPrice: eventTypes.length > 0
14249
14505
  ? 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 }))] }));
14506
+ : 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
14507
  }
14252
14508
 
14253
14509
  // Helper function to preprocess markdown for underline support
@@ -14307,7 +14563,7 @@
14307
14563
  pointerEvents: "none",
14308
14564
  }, children: text }))] }));
14309
14565
  }
14310
- function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
14566
+ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, showImages = true, }) {
14311
14567
  const t = useTranslations();
14312
14568
  const { locale } = useLocale();
14313
14569
  const timezone = useTimezone();
@@ -14386,6 +14642,18 @@
14386
14642
  gridAutoRows: "1fr",
14387
14643
  }, children: eventTypes.map((eventType) => {
14388
14644
  const isAvailable = eventType.hasAvailableInstances;
14645
+ const hasImage = showImages && eventType.images && eventType.images.length > 0;
14646
+ const availabilityBadge = (u$2("div", { style: {
14647
+ padding: "4px 8px",
14648
+ borderRadius: "var(--bw-border-radius-small)",
14649
+ fontSize: "12px",
14650
+ fontWeight: 600,
14651
+ color: "#ffffff",
14652
+ fontFamily: "var(--bw-font-family)",
14653
+ backgroundColor: isAvailable
14654
+ ? "var(--bw-success-color)"
14655
+ : "var(--bw-error-color)",
14656
+ }, children: isAvailable ? t("events.spotsAvailable") : t("events.soldOut") }));
14389
14657
  return (u$2("div", { className: "event-type-card", style: {
14390
14658
  position: "relative",
14391
14659
  backgroundColor: "var(--bw-surface-color)",
@@ -14397,17 +14665,7 @@
14397
14665
  fontFamily: "var(--bw-font-family)",
14398
14666
  cursor: isAvailable ? "pointer" : "not-allowed",
14399
14667
  opacity: isAvailable ? 1 : 0.6,
14400
- }, onClick: () => isAvailable && onEventTypeSelect(eventType), children: [u$2("div", { style: { position: "absolute", top: "16px", right: "16px", zIndex: 10 }, children: u$2("div", { style: {
14401
- padding: "4px 8px",
14402
- borderRadius: "var(--bw-border-radius-small)",
14403
- fontSize: "12px",
14404
- fontWeight: 600,
14405
- color: "#ffffff",
14406
- fontFamily: "var(--bw-font-family)",
14407
- backgroundColor: isAvailable
14408
- ? "var(--bw-success-color)"
14409
- : "var(--bw-error-color)",
14410
- }, children: isAvailable ? t("events.spotsAvailable") : t("events.soldOut") }) }), eventType.images && eventType.images.length > 0 && (u$2(k$3, { children: [u$2("div", { style: { position: "absolute", top: "16px", left: "16px", zIndex: 10 }, children: u$2("div", { style: {
14668
+ }, onClick: () => isAvailable && onEventTypeSelect(eventType), children: [hasImage && (u$2("div", { style: { position: "absolute", top: "16px", right: "16px", zIndex: 10 }, children: availabilityBadge })), hasImage && (u$2(k$3, { children: [u$2("div", { style: { position: "absolute", top: "16px", left: "16px", zIndex: 10 }, children: u$2("div", { style: {
14411
14669
  fontSize: "13px",
14412
14670
  color: "var(--bw-surface-color)",
14413
14671
  fontWeight: 600,
@@ -14421,7 +14679,7 @@
14421
14679
  flexDirection: "column",
14422
14680
  justifyContent: "space-between",
14423
14681
  height: "540px",
14424
- }, children: [u$2("div", { children: [u$2("h2", { className: "event-type-title", style: {
14682
+ }, children: [u$2("div", { children: [!hasImage && (u$2("div", { style: { display: "flex", justifyContent: "flex-end", margin: "0 0 8px 0" }, children: availabilityBadge })), u$2("h2", { className: "event-type-title", style: {
14425
14683
  fontSize: "clamp(1.1rem, 2.5vw, 24px)",
14426
14684
  fontWeight: 700,
14427
14685
  color: "var(--bw-text-color)",
@@ -14693,16 +14951,16 @@
14693
14951
  }
14694
14952
 
14695
14953
  const getAllocationBadgeInfo = (availableSpots, maxParticipants, t) => {
14696
- const availabilityRatio = availableSpots / maxParticipants;
14697
- if (availabilityRatio >= 0.6)
14698
- return null;
14699
- if (availabilityRatio === 0) {
14954
+ if (maxParticipants <= 0 || availableSpots <= 0) {
14700
14955
  return {
14701
14956
  text: t("events.soldOut"),
14702
14957
  backgroundColor: "#7f1d1d",
14703
14958
  textColor: "#fca5a5",
14704
14959
  };
14705
14960
  }
14961
+ const availabilityRatio = availableSpots / maxParticipants;
14962
+ if (availabilityRatio >= 0.6)
14963
+ return null;
14706
14964
  if (availabilityRatio <= 0.3) {
14707
14965
  return {
14708
14966
  text: t("instances.almostSoldOut"),
@@ -14816,7 +15074,7 @@
14816
15074
  boxShadow: displayInfo ? "0 2px 4px rgba(0, 0, 0, 0.2)" : "none",
14817
15075
  }, children: formatCurrency(price) }));
14818
15076
  };
14819
- function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, apiBaseUrl, organizationId, }) {
15077
+ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, apiBaseUrl, organizationId, waitlistEnabled = false, }) {
14820
15078
  const t = useTranslations();
14821
15079
  const { locale } = useLocale();
14822
15080
  const timezone = useTimezone();
@@ -15001,12 +15259,13 @@
15001
15259
  ? "0 2px 4px rgba(0, 0, 0, 0.2)"
15002
15260
  : undefined,
15003
15261
  }, children: `${t("common.from")} ${formatCurrency(minPrice)}` }), isOpen: openGroups.has(key), onToggle: () => toggleGroup(key), children: u$2("div", { style: { display: "flex", flexDirection: "column", gap: "12px", paddingTop: "12px" }, children: events.map((event) => {
15004
- const availableSpots = event.maxParticipants - event.participantCount;
15005
- const isFullyBooked = availableSpots === 0;
15262
+ const availableSpots = event.availableSpots;
15006
15263
  const startDate = new Date(event.startTime);
15007
15264
  const isPastEvent = today.toISOString() >= startDate.toISOString();
15008
- const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
15009
- const canWaitlist = isFullyBooked &&
15265
+ const isUnavailable = availableSpots <= 0 || !event.bookingOpen;
15266
+ const isDisabled = isUnavailable || isPastEvent;
15267
+ const canWaitlist = waitlistEnabled &&
15268
+ isUnavailable &&
15010
15269
  !isPastEvent &&
15011
15270
  !!apiBaseUrl &&
15012
15271
  !!organizationId;
@@ -15043,7 +15302,7 @@
15043
15302
  color: "var(--bw-highlight-color)",
15044
15303
  opacity: 0.8,
15045
15304
  fontSize: "32px",
15046
- }, children: spinner() }) })), u$2(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices, t: t }), u$2(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants, t: t }), u$2("div", { style: {
15305
+ }, children: spinner() }) })), u$2(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices, t: t }), u$2(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.effectiveMaxParticipants ?? event.maxParticipants, t: t }), u$2("div", { style: {
15047
15306
  display: "flex",
15048
15307
  justifyContent: "space-between",
15049
15308
  width: "100%",
@@ -15220,18 +15479,19 @@
15220
15479
  margin: "0 auto",
15221
15480
  padding: "12px 16px",
15222
15481
  fontFamily: "var(--bw-font-family)",
15223
- }, children: [u$2("div", { style: { display: "flex", flexDirection: "column", gap: "12px", marginBottom: "10px" }, children: events.map((event) => {
15224
- const availableSpots = event.maxParticipants - event.participantCount;
15225
- const isFullyBooked = availableSpots === 0;
15482
+ }, children: [u$2("div", { style: { display: "flex", flexDirection: "column", gap: "12px", marginBottom: "10px" }, children: events
15483
+ .filter((event) => {
15226
15484
  const startDate = new Date(event.startTime);
15227
15485
  const isPastEvent = today.toISOString() >= startDate.toISOString();
15228
- const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
15229
- const availabilityRatio = availableSpots / event.maxParticipants;
15486
+ return !isPastEvent && event.availableSpots > 0 && event.bookingOpen;
15487
+ })
15488
+ .map((event) => {
15489
+ const availableSpots = event.availableSpots;
15490
+ const startDate = new Date(event.startTime);
15230
15491
  const allocationBadge = (() => {
15492
+ const availabilityRatio = availableSpots / event.maxParticipants;
15231
15493
  if (availabilityRatio >= 0.6)
15232
15494
  return null;
15233
- if (availabilityRatio === 0)
15234
- return { text: t("events.soldOut"), backgroundColor: "#7f1d1d", textColor: "#fca5a5" };
15235
15495
  if (availabilityRatio <= 0.3)
15236
15496
  return { text: t("instances.almostSoldOut"), backgroundColor: "#7f1d1d", textColor: "#fca5a5" };
15237
15497
  return { text: t("instances.popularDate"), backgroundColor: "#b45309", textColor: "#fbbf24" };
@@ -15245,13 +15505,9 @@
15245
15505
  padding: "16px 10px",
15246
15506
  transition: "all 0.2s ease",
15247
15507
  fontFamily: "var(--bw-font-family)",
15248
- opacity: isDisabled ? 0.3 : 1,
15249
- filter: isDisabled ? "grayscale(40%)" : "none",
15250
- cursor: isDisabled ? "not-allowed" : "pointer",
15508
+ cursor: "pointer",
15251
15509
  }, onClick: () => {
15252
- if (!isDisabled) {
15253
- handleEventSelect(event.id);
15254
- }
15510
+ handleEventSelect(event.id);
15255
15511
  }, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (u$2("div", { style: {
15256
15512
  position: "absolute",
15257
15513
  inset: 0,
@@ -15506,7 +15762,7 @@
15506
15762
  alignItems: "center",
15507
15763
  backgroundColor: "var(--bw-background-color)",
15508
15764
  }, children: [u$2("span", { style: { fontSize: "12px", color: "var(--bw-text-muted)" }, children: isFullyBooked
15509
- ? t("common.fullyBooked")
15765
+ ? t("events.soldOut")
15510
15766
  : t("specials.spotsLeft").replace("{{count}}", String(special.availableSpots)) }), u$2("span", { style: {
15511
15767
  fontSize: "13px",
15512
15768
  fontWeight: 600,
@@ -15515,6 +15771,312 @@
15515
15771
  }) })] }));
15516
15772
  }
15517
15773
 
15774
+ function toMinutesForUnit(unit) {
15775
+ switch (unit.toLowerCase()) {
15776
+ case "minute":
15777
+ case "minutes":
15778
+ return 1;
15779
+ case "hour":
15780
+ case "hours":
15781
+ return 60;
15782
+ case "day":
15783
+ case "days":
15784
+ return 24 * 60;
15785
+ default:
15786
+ return 1;
15787
+ }
15788
+ }
15789
+ // --- Timezone helpers ------------------------------------------------------
15790
+ // The booking is evaluated server-side against the organization's local
15791
+ // operating hours. The customer's browser may be in a different timezone, so we
15792
+ // interpret the datetime-local field as a wall-clock time in the ORG timezone
15793
+ // (not the browser's) and convert to a UTC instant for the API.
15794
+ function getTzParts(date, timeZone) {
15795
+ const dtf = new Intl.DateTimeFormat("en-CA", {
15796
+ timeZone,
15797
+ hour12: false,
15798
+ year: "numeric",
15799
+ month: "2-digit",
15800
+ day: "2-digit",
15801
+ hour: "2-digit",
15802
+ minute: "2-digit",
15803
+ second: "2-digit",
15804
+ });
15805
+ const parts = {};
15806
+ for (const part of dtf.formatToParts(date)) {
15807
+ if (part.type !== "literal")
15808
+ parts[part.type] = part.value;
15809
+ }
15810
+ return parts;
15811
+ }
15812
+ function tzOffsetMs(utcMs, timeZone) {
15813
+ const p = getTzParts(new Date(utcMs), timeZone);
15814
+ const hour = p.hour === "24" ? "00" : p.hour;
15815
+ const asLocalUtc = Date.UTC(Number(p.year), Number(p.month) - 1, Number(p.day), Number(hour), Number(p.minute), Number(p.second));
15816
+ return asLocalUtc - utcMs;
15817
+ }
15818
+ /** Convert an org-tz wall-clock string ("YYYY-MM-DDTHH:mm") to a UTC instant. */
15819
+ function zonedWallTimeToUtc(wallTime, timeZone) {
15820
+ const [datePart, timePart] = wallTime.split("T");
15821
+ if (!datePart || !timePart)
15822
+ return new Date(NaN);
15823
+ const [y, mo, d] = datePart.split("-").map(Number);
15824
+ const [h, mi] = timePart.split(":").map(Number);
15825
+ const naiveUtc = Date.UTC(y, (mo ?? 1) - 1, d, h ?? 0, mi ?? 0);
15826
+ // Single-pass offset correction (good enough outside the ~1h DST fold).
15827
+ const offset = tzOffsetMs(naiveUtc, timeZone);
15828
+ return new Date(naiveUtc - offset);
15829
+ }
15830
+ /** Render a UTC instant as an org-tz datetime-local input value. */
15831
+ function utcToZonedInputValue(date, timeZone) {
15832
+ const p = getTzParts(date, timeZone);
15833
+ const hour = p.hour === "24" ? "00" : p.hour;
15834
+ return `${p.year}-${p.month}-${p.day}T${hour}:${p.minute}`;
15835
+ }
15836
+ /** Format a UTC instant for display in the org timezone. */
15837
+ function formatInTimeZone(date, timeZone, locale) {
15838
+ return new Intl.DateTimeFormat(locale, {
15839
+ timeZone,
15840
+ dateStyle: "medium",
15841
+ timeStyle: "short",
15842
+ }).format(date);
15843
+ }
15844
+ function calculateTieredPrice(durationUnits, config) {
15845
+ const tiers = [...(config.tiers ?? [])]
15846
+ .filter((tier) => tier.unitCount > 0 && tier.price >= 0)
15847
+ .sort((a, b) => b.unitCount - a.unitCount);
15848
+ if (tiers.length === 0) {
15849
+ return 0;
15850
+ }
15851
+ let total = 0;
15852
+ let remaining = durationUnits;
15853
+ for (const tier of tiers) {
15854
+ const count = Math.floor(remaining / tier.unitCount);
15855
+ if (count <= 0)
15856
+ continue;
15857
+ total += count * tier.price;
15858
+ remaining -= count * tier.unitCount;
15859
+ }
15860
+ if (remaining > 0) {
15861
+ if (typeof config.perUnitPrice === "number") {
15862
+ total += remaining * config.perUnitPrice;
15863
+ }
15864
+ else {
15865
+ total += tiers[tiers.length - 1]?.price ?? 0;
15866
+ }
15867
+ }
15868
+ return total;
15869
+ }
15870
+ function FreeformSelection({ config, eventType, systemConfig, isOpen, onClose, onSuccess, onError, }) {
15871
+ const t = useTranslations();
15872
+ const { locale } = useLocale();
15873
+ const freeformConfig = eventType.freeformConfig;
15874
+ const timeZone = eventType.organizationTimezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
15875
+ const isNamedResource = eventType.resourceKind === "named";
15876
+ const maxUnits = isNamedResource ? 1 : Math.max(1, eventType.resourceQuantity ?? 1);
15877
+ const now = T$2(() => {
15878
+ const next = new Date();
15879
+ next.setMinutes(next.getMinutes() + 30, 0, 0);
15880
+ return next;
15881
+ }, []);
15882
+ const [startInput, setStartInput] = d$1(utcToZonedInputValue(now, timeZone));
15883
+ const [duration, setDuration] = d$1(Math.max(1, freeformConfig?.minDuration ?? 1));
15884
+ const [requestedUnits, setRequestedUnits] = d$1(1);
15885
+ const [customerName, setCustomerName] = d$1("");
15886
+ const [customerEmail, setCustomerEmail] = d$1("");
15887
+ const [customerPhone, setCustomerPhone] = d$1("");
15888
+ const [comment, setComment] = d$1("");
15889
+ const [acceptTerms, setAcceptTerms] = d$1(false);
15890
+ const [checkoutStep, setCheckoutStep] = d$1("details");
15891
+ const [availability, setAvailability] = d$1(null);
15892
+ const [availabilityError, setAvailabilityError] = d$1(null);
15893
+ const [isCheckingAvailability, setIsCheckingAvailability] = d$1(false);
15894
+ y$1(() => {
15895
+ if (!isOpen)
15896
+ return;
15897
+ setCheckoutStep("details");
15898
+ }, [isOpen]);
15899
+ y$1(() => {
15900
+ if (!freeformConfig)
15901
+ return;
15902
+ setDuration(Math.max(1, freeformConfig.minDuration));
15903
+ setRequestedUnits(1);
15904
+ setAvailability(null);
15905
+ setAvailabilityError(null);
15906
+ }, [freeformConfig?.resourceId]);
15907
+ const unitMinutes = toMinutesForUnit(freeformConfig?.unit ?? "hour");
15908
+ const startDate = T$2(() => zonedWallTimeToUtc(startInput, timeZone), [startInput, timeZone]);
15909
+ const endDate = T$2(() => new Date(startDate.getTime() + duration * unitMinutes * 60000), [duration, startDate, unitMinutes]);
15910
+ y$1(() => {
15911
+ if (!isOpen || !freeformConfig)
15912
+ return;
15913
+ if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime()))
15914
+ return;
15915
+ const timer = setTimeout(async () => {
15916
+ setIsCheckingAvailability(true);
15917
+ setAvailabilityError(null);
15918
+ try {
15919
+ const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/freeform-availability"), {
15920
+ method: "POST",
15921
+ headers: createApiHeaders(config, locale),
15922
+ body: JSON.stringify(createRequestBody(config, {
15923
+ eventTypeId: eventType.id,
15924
+ requestedUnits,
15925
+ start: startDate.toISOString(),
15926
+ end: endDate.toISOString(),
15927
+ })),
15928
+ });
15929
+ const data = await response.json();
15930
+ if (!response.ok) {
15931
+ setAvailability(null);
15932
+ setAvailabilityError(data.error || t("error.loadBookingData"));
15933
+ return;
15934
+ }
15935
+ setAvailability({
15936
+ available: Boolean(data.available),
15937
+ freeUnits: Number(data.freeUnits ?? 0),
15938
+ capacityUnits: Number(data.capacityUnits ?? 0),
15939
+ usedUnits: Number(data.usedUnits ?? 0),
15940
+ reservationUsedUnits: Number(data.reservationUsedUnits ?? 0),
15941
+ blockUsedUnits: Number(data.blockUsedUnits ?? 0),
15942
+ durationUnits: Number(data.durationUnits ?? 0),
15943
+ roundedDurationMinutes: Number(data.roundedDurationMinutes ?? 0),
15944
+ });
15945
+ }
15946
+ catch (_error) {
15947
+ setAvailability(null);
15948
+ setAvailabilityError(t("error.loadBookingData"));
15949
+ }
15950
+ finally {
15951
+ setIsCheckingAvailability(false);
15952
+ }
15953
+ }, 350);
15954
+ return () => clearTimeout(timer);
15955
+ }, [
15956
+ config,
15957
+ endDate,
15958
+ eventType.id,
15959
+ freeformConfig,
15960
+ isOpen,
15961
+ locale,
15962
+ requestedUnits,
15963
+ startDate,
15964
+ t,
15965
+ ]);
15966
+ const billedDurationUnits = availability?.durationUnits ?? Math.max(1, Math.ceil(duration / Math.max(1, freeformConfig?.stepDuration ?? 1)));
15967
+ const basePrice = T$2(() => {
15968
+ if (!freeformConfig)
15969
+ return 0;
15970
+ if (freeformConfig.pricingModel === "perUnit") {
15971
+ return (freeformConfig.perUnitPrice ?? 0) * billedDurationUnits;
15972
+ }
15973
+ return calculateTieredPrice(billedDurationUnits, freeformConfig);
15974
+ }, [billedDurationUnits, freeformConfig]);
15975
+ const totalPrice = Math.max(0, basePrice * requestedUnits);
15976
+ const canContinueToPayment = !!freeformConfig &&
15977
+ customerName.trim().length >= 2 &&
15978
+ customerEmail.trim().length > 3 &&
15979
+ acceptTerms &&
15980
+ !!availability?.available &&
15981
+ !isCheckingAvailability;
15982
+ const freeformSelection = T$2(() => ({
15983
+ eventTypeId: eventType.id,
15984
+ start: startDate.toISOString(),
15985
+ end: endDate.toISOString(),
15986
+ requestedUnits,
15987
+ customerName: customerName.trim(),
15988
+ customerEmail: customerEmail.trim(),
15989
+ customerPhone: customerPhone.trim(),
15990
+ comment: comment.trim(),
15991
+ }), [
15992
+ comment,
15993
+ customerEmail,
15994
+ customerName,
15995
+ customerPhone,
15996
+ endDate,
15997
+ eventType.id,
15998
+ requestedUnits,
15999
+ startDate,
16000
+ ]);
16001
+ const paymentFormData = T$2(() => ({
16002
+ customerName: customerName.trim(),
16003
+ customerEmail: customerEmail.trim(),
16004
+ customerPhone: customerPhone.trim(),
16005
+ participants: [{ name: customerName.trim() || customerEmail.trim(), level: undefined }],
16006
+ comment: comment.trim(),
16007
+ }), [comment, customerEmail, customerName, customerPhone]);
16008
+ const eventDetailsForPayment = T$2(() => ({
16009
+ id: eventType.id,
16010
+ name: eventType.name,
16011
+ startTime: startDate.toISOString(),
16012
+ endTime: endDate.toISOString(),
16013
+ price: basePrice,
16014
+ maxParticipants: availability?.capacityUnits ?? requestedUnits,
16015
+ participantCount: 0,
16016
+ availableSpots: availability?.freeUnits ?? requestedUnits,
16017
+ durationDays: 1,
16018
+ durationPerDay: availability?.roundedDurationMinutes ?? duration * unitMinutes,
16019
+ images: eventType.images ?? [],
16020
+ category: eventType.category,
16021
+ organization: {
16022
+ id: config.organizationId,
16023
+ name: "",
16024
+ },
16025
+ bookingOpen: availability?.available ?? false,
16026
+ }), [
16027
+ availability?.available,
16028
+ availability?.capacityUnits,
16029
+ availability?.freeUnits,
16030
+ availability?.roundedDurationMinutes,
16031
+ basePrice,
16032
+ config.organizationId,
16033
+ duration,
16034
+ endDate,
16035
+ eventType.category,
16036
+ eventType.id,
16037
+ eventType.images,
16038
+ eventType.name,
16039
+ requestedUnits,
16040
+ startDate,
16041
+ unitMinutes,
16042
+ ]);
16043
+ const footer = (u$2("div", { style: { width: "100%", display: "flex", gap: "12px" }, children: [u$2("button", { type: "button", onClick: () => {
16044
+ if (checkoutStep === "payment") {
16045
+ setCheckoutStep("details");
16046
+ }
16047
+ else {
16048
+ onClose();
16049
+ }
16050
+ }, style: {
16051
+ flex: 1,
16052
+ padding: "12px 16px",
16053
+ border: "1px solid var(--bw-border-color)",
16054
+ borderRadius: "var(--bw-border-radius)",
16055
+ backgroundColor: "var(--bw-surface-color)",
16056
+ color: "var(--bw-text-color)",
16057
+ fontFamily: "var(--bw-font-family)",
16058
+ fontWeight: 600,
16059
+ cursor: "pointer",
16060
+ }, children: checkoutStep === "payment" ? t("button.backToDetails") : t("common.back") }), checkoutStep === "details" && (u$2("button", { type: "button", onClick: () => setCheckoutStep("payment"), disabled: !canContinueToPayment, style: {
16061
+ flex: 1,
16062
+ padding: "12px 16px",
16063
+ border: "none",
16064
+ borderRadius: "var(--bw-border-radius)",
16065
+ backgroundColor: "var(--bw-highlight-color)",
16066
+ color: "var(--bw-button-text-color, #ffffff)",
16067
+ fontFamily: "var(--bw-font-family)",
16068
+ fontWeight: 700,
16069
+ cursor: canContinueToPayment ? "pointer" : "not-allowed",
16070
+ opacity: canContinueToPayment ? 1 : 0.6,
16071
+ }, children: t("button.continueToPayment") }))] }));
16072
+ if (!freeformConfig) {
16073
+ 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") }) }));
16074
+ }
16075
+ 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
16076
+ ? t("booking.freeformUnitsAvailable", { count: availability.freeUnits })
16077
+ : 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 }))] })] })) }) }));
16078
+ }
16079
+
15518
16080
  const getThemeConfig = (theme = "generic") => {
15519
16081
  switch (theme) {
15520
16082
  case "christmas":
@@ -16149,12 +16711,14 @@
16149
16711
  const [isSuccess, setIsSuccess] = d$1(false);
16150
16712
  const [successPaymentId, setSuccessPaymentId] = d$1(null);
16151
16713
  const [systemConfig, setSystemConfig] = d$1(null);
16714
+ const [waitlistEnabled, setWaitlistEnabled] = d$1(false);
16152
16715
  // When true, loadEventInstances skips the single-instance auto-select (used by handleShowAllEvents)
16153
16716
  const skipInstanceAutoSelectRef = A$2(false);
16154
16717
  // PERFORMANCE OPTIMIZATION: Lazy component loading
16155
16718
  const [shouldRenderInstanceSelection, setShouldRenderInstanceSelection] = d$1(false);
16156
16719
  const [shouldRenderUpsells, setShouldRenderUpsells] = d$1(false);
16157
16720
  const [shouldRenderBookingForm, setShouldRenderBookingForm] = d$1(false);
16721
+ const [shouldRenderFreeformSelection, setShouldRenderFreeformSelection] = d$1(false);
16158
16722
  // Google Ads config (received from API, set once from the first API response)
16159
16723
  const [googleAdsConfig, setGoogleAdsConfig] = d$1(null);
16160
16724
  const extractGoogleAdsConfig = (data) => {
@@ -16168,6 +16732,7 @@
16168
16732
  // Voucher purchase state
16169
16733
  const [voucherConfig, setVoucherConfig] = d$1(null);
16170
16734
  const [voucherEventTypes, setVoucherEventTypes] = d$1([]);
16735
+ const [voucherCategories, setVoucherCategories] = d$1([]);
16171
16736
  const [isLoadingVoucherConfig, setIsLoadingVoucherConfig] = d$1(false);
16172
16737
  const [isVoucherFormOpen, setIsVoucherFormOpen] = d$1(false);
16173
16738
  const [voucherPurchaseResult, setVoucherPurchaseResult] = d$1(null);
@@ -16247,6 +16812,7 @@
16247
16812
  setVoucherConfig(mergedConfig);
16248
16813
  extractGoogleAdsConfig(data);
16249
16814
  setVoucherEventTypes(data.eventTypes || []);
16815
+ setVoucherCategories(data.categories || []);
16250
16816
  // Set system config for payment processing
16251
16817
  if (data.paymentProvider) {
16252
16818
  setSystemConfig({
@@ -16275,7 +16841,7 @@
16275
16841
  if (!analyticsInitRef.current && config.organizationId) {
16276
16842
  analyticsInitRef.current = true;
16277
16843
  initAnalytics(config.apiBaseUrl, config.organizationId, {
16278
- partnerContractId: config.partnerContractId,
16844
+ ...(config.partnerContractId ? { partnerContractId: config.partnerContractId } : {}),
16279
16845
  });
16280
16846
  trackEvent("widget_loaded", {
16281
16847
  viewMode,
@@ -16639,6 +17205,7 @@
16639
17205
  }
16640
17206
  extractGoogleAdsConfig(data);
16641
17207
  setEventInstances(data.eventInstances);
17208
+ setWaitlistEnabled(data.waitlistEnabled ?? false);
16642
17209
  if (data.paymentProvider) {
16643
17210
  setSystemConfig({
16644
17211
  paymentProvider: data.paymentProvider,
@@ -16754,6 +17321,18 @@
16754
17321
  const handleEventTypeSelect = async (eventType) => {
16755
17322
  trackEvent("event_type_selected", { eventTypeId: eventType.id, eventTypeName: eventType.name });
16756
17323
  setSelectedEventType(eventType);
17324
+ if (eventType.bookingMode === "freeform") {
17325
+ setCurrentStep("freeform");
17326
+ setShouldRenderFreeformSelection(true);
17327
+ setIsLoadingEventInstances(true);
17328
+ try {
17329
+ await loadEventInstances(eventType.id);
17330
+ }
17331
+ finally {
17332
+ setIsLoadingEventInstances(false);
17333
+ }
17334
+ return;
17335
+ }
16757
17336
  setCurrentStep("eventInstances");
16758
17337
  setShouldRenderInstanceSelection(true);
16759
17338
  setIsLoadingEventInstances(true);
@@ -16844,6 +17423,7 @@
16844
17423
  setSuccessPaymentId(result.paymentIntent.id);
16845
17424
  setSidebarOpen(false);
16846
17425
  setShouldRenderBookingForm(false);
17426
+ setShouldRenderFreeformSelection(false);
16847
17427
  setBookingPersistedState(null);
16848
17428
  config.onSuccess?.(result);
16849
17429
  };
@@ -17159,6 +17739,7 @@
17159
17739
  setShouldRenderInstanceSelection(false);
17160
17740
  setShouldRenderUpsells(false);
17161
17741
  setShouldRenderBookingForm(false);
17742
+ setShouldRenderFreeformSelection(false);
17162
17743
  setSelectedUpsells([]);
17163
17744
  setUpsells([]);
17164
17745
  setBookingPersistedState(null);
@@ -17180,6 +17761,7 @@
17180
17761
  setShouldRenderInstanceSelection(false);
17181
17762
  setShouldRenderUpsells(false);
17182
17763
  setShouldRenderBookingForm(false);
17764
+ setShouldRenderFreeformSelection(false);
17183
17765
  setSelectedUpsells([]);
17184
17766
  setUpsells([]);
17185
17767
  setBookingPersistedState(null);
@@ -17192,13 +17774,14 @@
17192
17774
  }, isOpen: currentStep === "eventInstances", onClose: () => {
17193
17775
  setShowingPreview(true);
17194
17776
  setCurrentStep("eventTypes");
17195
- }, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), u$2(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17777
+ }, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId, waitlistEnabled: waitlistEnabled })), u$2(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17196
17778
  setIsSuccess(false);
17197
17779
  setCurrentStep("eventTypes");
17198
17780
  setShowingPreview(true);
17199
17781
  setSuccessPaymentId(null);
17200
17782
  setShouldRenderInstanceSelection(false);
17201
17783
  setShouldRenderBookingForm(false);
17784
+ setShouldRenderFreeformSelection(false);
17202
17785
  const url = new URL(window.location.href);
17203
17786
  url.searchParams.delete("payment_intent");
17204
17787
  url.searchParams.delete("payment_intent_client_secret");
@@ -17242,13 +17825,23 @@
17242
17825
  setCurrentStep("booking");
17243
17826
  setShouldRenderBookingForm(true);
17244
17827
  }
17828
+ else if (selectedEventType?.bookingMode === "freeform") {
17829
+ setCurrentStep("freeform");
17830
+ setShouldRenderFreeformSelection(true);
17831
+ }
17245
17832
  else {
17246
17833
  setCurrentStep("eventInstances");
17247
17834
  setSidebarOpen(true);
17248
17835
  setShouldRenderInstanceSelection(true);
17249
17836
  }
17250
17837
  }, children: [isButtonBusy && u$2(Spinner, { size: 20, borderColor: "var(--bw-button-text-color, #ffffff)" }), config.buttonText ||
17251
- (isDirectInstanceMode ? t("button.bookNow") : 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 })), u$2(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17838
+ (isDirectInstanceMode || selectedEventType?.bookingMode === "freeform"
17839
+ ? t("button.bookNow")
17840
+ : 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, waitlistEnabled: waitlistEnabled })), 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: () => {
17841
+ setCurrentStep("eventTypes");
17842
+ setSidebarOpen(false);
17843
+ setShouldRenderFreeformSelection(false);
17844
+ }, onSuccess: handleBookingSuccess, onError: handleBookingError, systemConfig: systemConfig })), u$2(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17252
17845
  setIsSuccess(false);
17253
17846
  setCurrentStep("eventTypes");
17254
17847
  setSidebarOpen(false);
@@ -17256,6 +17849,7 @@
17256
17849
  setShouldRenderInstanceSelection(false);
17257
17850
  setShouldRenderUpsells(false);
17258
17851
  setShouldRenderBookingForm(false);
17852
+ setShouldRenderFreeformSelection(false);
17259
17853
  setSelectedUpsells([]);
17260
17854
  setUpsells([]);
17261
17855
  const url = new URL(window.location.href);
@@ -17268,7 +17862,7 @@
17268
17862
  }, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
17269
17863
  }
17270
17864
  // 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: {
17865
+ 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, showImages: config.showEventImages !== false })), 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
17866
  display: "inline-block",
17273
17867
  width: "32px",
17274
17868
  height: "32px",
@@ -17309,13 +17903,17 @@
17309
17903
  };
17310
17904
  };
17311
17905
  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(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
17906
+ 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, waitlistEnabled: waitlistEnabled })), 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: () => {
17907
+ setCurrentStep("eventTypes");
17908
+ setShouldRenderFreeformSelection(false);
17909
+ }, onSuccess: handleBookingSuccess, onError: handleBookingError, systemConfig: systemConfig })), u$2(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
17313
17910
  setIsSuccess(false);
17314
17911
  setCurrentStep("eventTypes");
17315
17912
  setSuccessPaymentId(null);
17316
17913
  setShouldRenderInstanceSelection(false);
17317
17914
  setShouldRenderUpsells(false);
17318
17915
  setShouldRenderBookingForm(false);
17916
+ setShouldRenderFreeformSelection(false);
17319
17917
  setSelectedUpsells([]);
17320
17918
  setUpsells([]);
17321
17919
  const url = new URL(window.location.href);
@@ -17325,7 +17923,7 @@
17325
17923
  url.searchParams.delete("mollie_payment_id");
17326
17924
  url.searchParams.delete("mollie_status");
17327
17925
  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: () => {
17926
+ }, 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
17927
  setIsSuccess(false);
17330
17928
  setVoucherPurchaseResult(null);
17331
17929
  const url = new URL(window.location.href);
@@ -17378,7 +17976,7 @@
17378
17976
  }
17379
17977
  }
17380
17978
 
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}}";
17979
+ 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
17980
  styleInject(css_248z);
17383
17981
 
17384
17982
  // Export init function for vanilla JS usage with Preact