@bigz-app/booking-widget 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/booking-widget.js +707 -180
- package/dist/booking-widget.js.map +1 -1
- package/dist/components/UniversalBookingWidget.d.ts +52 -2
- package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
- package/dist/components/booking/BookingForm.d.ts +3 -0
- package/dist/components/booking/BookingForm.d.ts.map +1 -1
- package/dist/components/booking/BookingSuccessModal.d.ts.map +1 -1
- package/dist/components/events/EventTypeSelection.d.ts +14 -1
- package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
- package/dist/components/events/SpecialsView.d.ts +13 -0
- package/dist/components/events/SpecialsView.d.ts.map +1 -0
- package/dist/components/events/index.d.ts +1 -0
- package/dist/components/events/index.d.ts.map +1 -1
- package/dist/components/shared/Button.d.ts.map +1 -1
- package/dist/components/shared/DialogPortal.d.ts.map +1 -1
- package/dist/components/upsells/UpsellCard.d.ts +2 -0
- package/dist/components/upsells/UpsellCard.d.ts.map +1 -1
- package/dist/components/upsells/UpsellsStep.d.ts +2 -0
- package/dist/components/upsells/UpsellsStep.d.ts.map +1 -1
- package/dist/i18n/i18n-context.d.ts.map +1 -1
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/sv.d.ts.map +1 -1
- package/dist/index.cjs +707 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +708 -181
- package/dist/index.esm.js.map +1 -1
- package/dist/styles/StyleProvider.d.ts.map +1 -1
- package/dist/styles/shared-styles.d.ts +1 -0
- package/dist/styles/shared-styles.d.ts.map +1 -1
- package/dist/validation/booking-schema.d.ts +54 -13
- package/dist/validation/booking-schema.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -253,6 +253,7 @@ const de$1 = {
|
|
|
253
253
|
"events.soldOut": "Ausgebucht",
|
|
254
254
|
"events.availableFrom": "Freie Plätze ab {{date}}",
|
|
255
255
|
"events.noAvailableDates": "Keine Termine frei",
|
|
256
|
+
"events.previewSectionTitle": "Specials & nächste Termine",
|
|
256
257
|
// Event instances
|
|
257
258
|
"instances.title": "Terminauswahl",
|
|
258
259
|
"instances.noAvailable": "Keine verfügbaren Termine",
|
|
@@ -269,6 +270,15 @@ const de$1 = {
|
|
|
269
270
|
"nextEvents.noUpcomingMessage": "Aktuell sind keine Termine verfügbar. Bitte schaue später noch einmal vorbei oder kontaktiere uns direkt.",
|
|
270
271
|
"nextEvents.showAll": "Alle Events anzeigen",
|
|
271
272
|
"nextEvents.priceOnRequest": "Preis auf Anfrage",
|
|
273
|
+
// Specials view
|
|
274
|
+
"specials.title": "Sonderangebote",
|
|
275
|
+
"specials.subtitle": "Unsere aktuellen Angebote auf einen Blick",
|
|
276
|
+
"specials.noSpecials": "Keine Sonderangebote",
|
|
277
|
+
"specials.noSpecialsMessage": "Derzeit sind keine Sonderangebote verfügbar. Bitte schaue später noch einmal vorbei.",
|
|
278
|
+
"specials.save": "Spare {{amount}}",
|
|
279
|
+
"specials.savePercent": "{{percent}}% Rabatt",
|
|
280
|
+
"specials.spotsLeft": "Noch {{count}} Plätze frei",
|
|
281
|
+
"specials.bookNow": "Jetzt buchen",
|
|
272
282
|
// Booking form
|
|
273
283
|
"booking.title": "Buchung - {{name}}",
|
|
274
284
|
"booking.notPossible": "Buchung nicht möglich",
|
|
@@ -290,6 +300,8 @@ const de$1 = {
|
|
|
290
300
|
"booking.participantName": "Name *",
|
|
291
301
|
"booking.participantNamePlaceholder": "Teilnehmername",
|
|
292
302
|
"booking.participantAge": "Alter",
|
|
303
|
+
"booking.participantLevel": "Level",
|
|
304
|
+
"booking.participantLevelPlaceholder": "Level wählen...",
|
|
293
305
|
"booking.addParticipant": "{{number}}. Teilnehmer hinzufügen",
|
|
294
306
|
"booking.maxParticipants": "Maximale Anzahl an Teilnehmern erreicht. Es sind nur noch {{count}} Plätze verfügbar.",
|
|
295
307
|
"booking.maxSpotsReached": "Maximal {{count}} Plätze verfügbar.",
|
|
@@ -438,7 +450,11 @@ const de$1 = {
|
|
|
438
450
|
"validation.emailInvalid": "Ungültiges E-Mail-Format",
|
|
439
451
|
"validation.emailDomainInvalid": "Ungültige E-Mail-Domain",
|
|
440
452
|
"validation.participantRequired": "Mindestens ein Teilnehmer erforderlich",
|
|
453
|
+
"validation.ageRequired": "Alter ist erforderlich",
|
|
454
|
+
"validation.levelRequired": "Bitte ein Level auswählen",
|
|
441
455
|
"validation.acceptTerms": "Bitte akzeptiere die Allgemeinen Geschäftsbedingungen",
|
|
456
|
+
"level.beginner": "Anfänger",
|
|
457
|
+
"level.advanced": "Fortgeschritten",
|
|
442
458
|
// Sidebar
|
|
443
459
|
"sidebar.close": "Schließen",
|
|
444
460
|
// Promo
|
|
@@ -513,6 +529,7 @@ const en = {
|
|
|
513
529
|
"events.soldOut": "Sold out",
|
|
514
530
|
"events.availableFrom": "Available from {{date}}",
|
|
515
531
|
"events.noAvailableDates": "No dates available",
|
|
532
|
+
"events.previewSectionTitle": "Specials & upcoming dates",
|
|
516
533
|
// Event instances
|
|
517
534
|
"instances.title": "Select a date",
|
|
518
535
|
"instances.noAvailable": "No available dates",
|
|
@@ -529,6 +546,15 @@ const en = {
|
|
|
529
546
|
"nextEvents.noUpcomingMessage": "There are currently no dates available. Please check back later or contact us directly.",
|
|
530
547
|
"nextEvents.showAll": "Show all events",
|
|
531
548
|
"nextEvents.priceOnRequest": "Price on request",
|
|
549
|
+
// Specials view
|
|
550
|
+
"specials.title": "Special Offers",
|
|
551
|
+
"specials.subtitle": "Our current deals at a glance",
|
|
552
|
+
"specials.noSpecials": "No special offers",
|
|
553
|
+
"specials.noSpecialsMessage": "There are currently no special offers available. Please check back later.",
|
|
554
|
+
"specials.save": "Save {{amount}}",
|
|
555
|
+
"specials.savePercent": "{{percent}}% off",
|
|
556
|
+
"specials.spotsLeft": "{{count}} spots left",
|
|
557
|
+
"specials.bookNow": "Book now",
|
|
532
558
|
// Booking form
|
|
533
559
|
"booking.title": "Booking - {{name}}",
|
|
534
560
|
"booking.notPossible": "Booking not possible",
|
|
@@ -550,6 +576,8 @@ const en = {
|
|
|
550
576
|
"booking.participantName": "Name *",
|
|
551
577
|
"booking.participantNamePlaceholder": "Participant name",
|
|
552
578
|
"booking.participantAge": "Age",
|
|
579
|
+
"booking.participantLevel": "Level",
|
|
580
|
+
"booking.participantLevelPlaceholder": "Select level...",
|
|
553
581
|
"booking.addParticipant": "Add participant {{number}}",
|
|
554
582
|
"booking.maxParticipants": "Maximum number of participants reached. Only {{count}} spots are available.",
|
|
555
583
|
"booking.maxSpotsReached": "Maximum {{count}} spots available.",
|
|
@@ -698,7 +726,11 @@ const en = {
|
|
|
698
726
|
"validation.emailInvalid": "Invalid email format",
|
|
699
727
|
"validation.emailDomainInvalid": "Invalid email domain",
|
|
700
728
|
"validation.participantRequired": "At least one participant is required",
|
|
729
|
+
"validation.ageRequired": "Age is required",
|
|
730
|
+
"validation.levelRequired": "Please select a level",
|
|
701
731
|
"validation.acceptTerms": "Please accept the terms and conditions",
|
|
732
|
+
"level.beginner": "Beginner",
|
|
733
|
+
"level.advanced": "Advanced",
|
|
702
734
|
// Sidebar
|
|
703
735
|
"sidebar.close": "Close",
|
|
704
736
|
// Promo
|
|
@@ -773,6 +805,7 @@ const es = {
|
|
|
773
805
|
"events.soldOut": "Agotado",
|
|
774
806
|
"events.availableFrom": "Disponible desde {{date}}",
|
|
775
807
|
"events.noAvailableDates": "Sin fechas disponibles",
|
|
808
|
+
"events.previewSectionTitle": "Especiales & próximas fechas",
|
|
776
809
|
// Event instances
|
|
777
810
|
"instances.title": "Seleccionar fecha",
|
|
778
811
|
"instances.noAvailable": "Sin fechas disponibles",
|
|
@@ -789,6 +822,15 @@ const es = {
|
|
|
789
822
|
"nextEvents.noUpcomingMessage": "Actualmente no hay fechas disponibles. Por favor, vuelve más tarde o contáctanos directamente.",
|
|
790
823
|
"nextEvents.showAll": "Mostrar todos los eventos",
|
|
791
824
|
"nextEvents.priceOnRequest": "Precio bajo consulta",
|
|
825
|
+
// Specials view
|
|
826
|
+
"specials.title": "Ofertas especiales",
|
|
827
|
+
"specials.subtitle": "Nuestras ofertas actuales de un vistazo",
|
|
828
|
+
"specials.noSpecials": "Sin ofertas especiales",
|
|
829
|
+
"specials.noSpecialsMessage": "Actualmente no hay ofertas especiales disponibles. Vuelve más tarde.",
|
|
830
|
+
"specials.save": "Ahorra {{amount}}",
|
|
831
|
+
"specials.savePercent": "{{percent}}% de descuento",
|
|
832
|
+
"specials.spotsLeft": "{{count}} plazas disponibles",
|
|
833
|
+
"specials.bookNow": "Reservar ahora",
|
|
792
834
|
// Booking form
|
|
793
835
|
"booking.title": "Reserva - {{name}}",
|
|
794
836
|
"booking.notPossible": "Reserva no posible",
|
|
@@ -810,6 +852,8 @@ const es = {
|
|
|
810
852
|
"booking.participantName": "Nombre *",
|
|
811
853
|
"booking.participantNamePlaceholder": "Nombre del participante",
|
|
812
854
|
"booking.participantAge": "Edad",
|
|
855
|
+
"booking.participantLevel": "Nivel",
|
|
856
|
+
"booking.participantLevelPlaceholder": "Seleccionar nivel...",
|
|
813
857
|
"booking.addParticipant": "Añadir participante {{number}}",
|
|
814
858
|
"booking.maxParticipants": "Número máximo de participantes alcanzado. Solo quedan {{count}} plazas disponibles.",
|
|
815
859
|
"booking.maxSpotsReached": "Máximo {{count}} plazas disponibles.",
|
|
@@ -958,7 +1002,11 @@ const es = {
|
|
|
958
1002
|
"validation.emailInvalid": "Formato de correo electrónico inválido",
|
|
959
1003
|
"validation.emailDomainInvalid": "Dominio de correo electrónico inválido",
|
|
960
1004
|
"validation.participantRequired": "Se requiere al menos un participante",
|
|
1005
|
+
"validation.ageRequired": "La edad es obligatoria",
|
|
1006
|
+
"validation.levelRequired": "Selecciona un nivel",
|
|
961
1007
|
"validation.acceptTerms": "Por favor, acepta los términos y condiciones",
|
|
1008
|
+
"level.beginner": "Principiante",
|
|
1009
|
+
"level.advanced": "Avanzado",
|
|
962
1010
|
// Sidebar
|
|
963
1011
|
"sidebar.close": "Cerrar",
|
|
964
1012
|
// Promo
|
|
@@ -1033,6 +1081,7 @@ const pt = {
|
|
|
1033
1081
|
"events.soldOut": "Esgotado",
|
|
1034
1082
|
"events.availableFrom": "Disponível a partir de {{date}}",
|
|
1035
1083
|
"events.noAvailableDates": "Sem datas disponíveis",
|
|
1084
|
+
"events.previewSectionTitle": "Especiais & próximas datas",
|
|
1036
1085
|
// Event instances
|
|
1037
1086
|
"instances.title": "Selecionar data",
|
|
1038
1087
|
"instances.noAvailable": "Sem datas disponíveis",
|
|
@@ -1049,6 +1098,15 @@ const pt = {
|
|
|
1049
1098
|
"nextEvents.noUpcomingMessage": "Atualmente não há datas disponíveis. Por favor, volte mais tarde ou contacte-nos diretamente.",
|
|
1050
1099
|
"nextEvents.showAll": "Mostrar todos os eventos",
|
|
1051
1100
|
"nextEvents.priceOnRequest": "Preço sob consulta",
|
|
1101
|
+
// Specials view
|
|
1102
|
+
"specials.title": "Ofertas especiais",
|
|
1103
|
+
"specials.subtitle": "As nossas ofertas atuais num relance",
|
|
1104
|
+
"specials.noSpecials": "Sem ofertas especiais",
|
|
1105
|
+
"specials.noSpecialsMessage": "Atualmente não há ofertas especiais disponíveis. Por favor, volte mais tarde.",
|
|
1106
|
+
"specials.save": "Poupe {{amount}}",
|
|
1107
|
+
"specials.savePercent": "{{percent}}% de desconto",
|
|
1108
|
+
"specials.spotsLeft": "{{count}} lugares disponíveis",
|
|
1109
|
+
"specials.bookNow": "Reservar agora",
|
|
1052
1110
|
// Booking form
|
|
1053
1111
|
"booking.title": "Reserva - {{name}}",
|
|
1054
1112
|
"booking.notPossible": "Reserva não possível",
|
|
@@ -1070,6 +1128,8 @@ const pt = {
|
|
|
1070
1128
|
"booking.participantName": "Nome *",
|
|
1071
1129
|
"booking.participantNamePlaceholder": "Nome do participante",
|
|
1072
1130
|
"booking.participantAge": "Idade",
|
|
1131
|
+
"booking.participantLevel": "Nível",
|
|
1132
|
+
"booking.participantLevelPlaceholder": "Selecionar nível...",
|
|
1073
1133
|
"booking.addParticipant": "Adicionar participante {{number}}",
|
|
1074
1134
|
"booking.maxParticipants": "Número máximo de participantes atingido. Apenas {{count}} lugares disponíveis.",
|
|
1075
1135
|
"booking.maxSpotsReached": "Máximo {{count}} lugares disponíveis.",
|
|
@@ -1218,7 +1278,11 @@ const pt = {
|
|
|
1218
1278
|
"validation.emailInvalid": "Formato de email inválido",
|
|
1219
1279
|
"validation.emailDomainInvalid": "Domínio de email inválido",
|
|
1220
1280
|
"validation.participantRequired": "É necessário pelo menos um participante",
|
|
1281
|
+
"validation.ageRequired": "A idade é obrigatória",
|
|
1282
|
+
"validation.levelRequired": "Por favor selecione um nível",
|
|
1221
1283
|
"validation.acceptTerms": "Por favor, aceite os termos e condições",
|
|
1284
|
+
"level.beginner": "Iniciante",
|
|
1285
|
+
"level.advanced": "Avançado",
|
|
1222
1286
|
// Sidebar
|
|
1223
1287
|
"sidebar.close": "Fechar",
|
|
1224
1288
|
// Promo
|
|
@@ -1293,6 +1357,7 @@ const sv = {
|
|
|
1293
1357
|
"events.soldOut": "Fullbokat",
|
|
1294
1358
|
"events.availableFrom": "Lediga platser från {{date}}",
|
|
1295
1359
|
"events.noAvailableDates": "Inga datum lediga",
|
|
1360
|
+
"events.previewSectionTitle": "Specials & kommande datum",
|
|
1296
1361
|
// Event instances
|
|
1297
1362
|
"instances.title": "Välj datum",
|
|
1298
1363
|
"instances.noAvailable": "Inga tillgängliga datum",
|
|
@@ -1309,6 +1374,15 @@ const sv = {
|
|
|
1309
1374
|
"nextEvents.noUpcomingMessage": "Det finns för närvarande inga datum tillgängliga. Kom tillbaka senare eller kontakta oss direkt.",
|
|
1310
1375
|
"nextEvents.showAll": "Visa alla evenemang",
|
|
1311
1376
|
"nextEvents.priceOnRequest": "Pris på förfrågan",
|
|
1377
|
+
// Specials view
|
|
1378
|
+
"specials.title": "Specialerbjudanden",
|
|
1379
|
+
"specials.subtitle": "Våra aktuella erbjudanden i korthet",
|
|
1380
|
+
"specials.noSpecials": "Inga specialerbjudanden",
|
|
1381
|
+
"specials.noSpecialsMessage": "Det finns för närvarande inga specialerbjudanden tillgängliga. Kom tillbaka senare.",
|
|
1382
|
+
"specials.save": "Spara {{amount}}",
|
|
1383
|
+
"specials.savePercent": "{{percent}}% rabatt",
|
|
1384
|
+
"specials.spotsLeft": "{{count}} platser kvar",
|
|
1385
|
+
"specials.bookNow": "Boka nu",
|
|
1312
1386
|
// Booking form
|
|
1313
1387
|
"booking.title": "Bokning - {{name}}",
|
|
1314
1388
|
"booking.notPossible": "Bokning inte möjlig",
|
|
@@ -1330,6 +1404,8 @@ const sv = {
|
|
|
1330
1404
|
"booking.participantName": "Namn *",
|
|
1331
1405
|
"booking.participantNamePlaceholder": "Deltagarens namn",
|
|
1332
1406
|
"booking.participantAge": "Ålder",
|
|
1407
|
+
"booking.participantLevel": "Nivå",
|
|
1408
|
+
"booking.participantLevelPlaceholder": "Välj nivå...",
|
|
1333
1409
|
"booking.addParticipant": "Lägg till deltagare {{number}}",
|
|
1334
1410
|
"booking.maxParticipants": "Maximalt antal deltagare uppnått. Bara {{count}} platser är tillgängliga.",
|
|
1335
1411
|
"booking.maxSpotsReached": "Maximalt {{count}} platser tillgängliga.",
|
|
@@ -1478,7 +1554,11 @@ const sv = {
|
|
|
1478
1554
|
"validation.emailInvalid": "Ogiltigt e-postformat",
|
|
1479
1555
|
"validation.emailDomainInvalid": "Ogiltig e-postdomän",
|
|
1480
1556
|
"validation.participantRequired": "Minst en deltagare krävs",
|
|
1557
|
+
"validation.ageRequired": "Ålder krävs",
|
|
1558
|
+
"validation.levelRequired": "Välj en nivå",
|
|
1481
1559
|
"validation.acceptTerms": "Acceptera villkoren",
|
|
1560
|
+
"level.beginner": "Nybörjare",
|
|
1561
|
+
"level.advanced": "Avancerad",
|
|
1482
1562
|
// Sidebar
|
|
1483
1563
|
"sidebar.close": "Stäng",
|
|
1484
1564
|
// Promo
|
|
@@ -1571,18 +1651,9 @@ function persistLocale(locale) {
|
|
|
1571
1651
|
}
|
|
1572
1652
|
const I18nContext = React.createContext(null);
|
|
1573
1653
|
function I18nProvider({ configLocale, children }) {
|
|
1574
|
-
// Priority:
|
|
1575
|
-
//
|
|
1576
|
-
const [overrideLocale, setOverrideLocale] = React.useState(() =>
|
|
1577
|
-
if (configLocale)
|
|
1578
|
-
return null;
|
|
1579
|
-
return readPersistedLocale();
|
|
1580
|
-
});
|
|
1581
|
-
React.useEffect(() => {
|
|
1582
|
-
if (configLocale) {
|
|
1583
|
-
setOverrideLocale(null);
|
|
1584
|
-
}
|
|
1585
|
-
}, [configLocale]);
|
|
1654
|
+
// Priority: persisted user choice > configLocale (org default) > browser language > "de"
|
|
1655
|
+
// This keeps org locale as default, but remembers explicit user overrides across reloads.
|
|
1656
|
+
const [overrideLocale, setOverrideLocale] = React.useState(() => readPersistedLocale());
|
|
1586
1657
|
const locale = overrideLocale ?? resolveLocale(configLocale);
|
|
1587
1658
|
const handleSetLocale = React.useCallback((next) => {
|
|
1588
1659
|
persistLocale(next);
|
|
@@ -1714,129 +1785,153 @@ const resolveSemanticColor = (colorValue, fallbackValue) => {
|
|
|
1714
1785
|
// If semantic resolution fails, use fallback or return the original value
|
|
1715
1786
|
return fallbackValue || colorValue;
|
|
1716
1787
|
};
|
|
1717
|
-
//
|
|
1788
|
+
// Legacy theme name redirects (old name → new name)
|
|
1789
|
+
const legacyThemeRedirects = {
|
|
1790
|
+
"light-fresh": "teal-minimal",
|
|
1791
|
+
"light-elegant": "blue-business",
|
|
1792
|
+
"light-vibrant": "orange-raw",
|
|
1793
|
+
"light-professional": "blue-business",
|
|
1794
|
+
"dark-night": "navy-night",
|
|
1795
|
+
"dark-modern": "navy-night",
|
|
1796
|
+
"dark-forest": "green-deep",
|
|
1797
|
+
};
|
|
1798
|
+
// Predefined themes
|
|
1718
1799
|
const themes = {
|
|
1719
1800
|
// --- Light Themes ---
|
|
1720
|
-
"
|
|
1721
|
-
highlight: "#00b1aa",
|
|
1722
|
-
background: "#f8fdfe",
|
|
1723
|
-
surface: "#ffffff",
|
|
1724
|
-
text: "#0e7490",
|
|
1725
|
-
border: "#bae6fd",
|
|
1726
|
-
success: "#38bdf8",
|
|
1727
|
-
warning: "#fbbf24",
|
|
1728
|
-
error: "#f43f5e",
|
|
1729
|
-
borderRadius: "18px",
|
|
1801
|
+
"teal-minimal": {
|
|
1802
|
+
highlight: "#00b1aa",
|
|
1803
|
+
background: "#f8fdfe",
|
|
1804
|
+
surface: "#ffffff",
|
|
1805
|
+
text: "#0e7490",
|
|
1806
|
+
border: "#bae6fd",
|
|
1807
|
+
success: "#38bdf8",
|
|
1808
|
+
warning: "#fbbf24",
|
|
1809
|
+
error: "#f43f5e",
|
|
1810
|
+
borderRadius: "18px",
|
|
1730
1811
|
fontFamily: "'Inter', system-ui, sans-serif",
|
|
1731
1812
|
},
|
|
1732
|
-
"
|
|
1733
|
-
highlight: "#
|
|
1734
|
-
background: "#
|
|
1735
|
-
surface: "#ffffff",
|
|
1736
|
-
text: "#
|
|
1737
|
-
border: "#
|
|
1738
|
-
success: "#
|
|
1739
|
-
warning: "#
|
|
1740
|
-
error: "#
|
|
1741
|
-
borderRadius: "
|
|
1742
|
-
fontFamily: "'
|
|
1813
|
+
"blue-business": {
|
|
1814
|
+
highlight: "#2563eb",
|
|
1815
|
+
background: "#f8fafc",
|
|
1816
|
+
surface: "#ffffff",
|
|
1817
|
+
text: "#0f172a",
|
|
1818
|
+
border: "#cbd5e1",
|
|
1819
|
+
success: "#059669",
|
|
1820
|
+
warning: "#d97706",
|
|
1821
|
+
error: "#b91c1c",
|
|
1822
|
+
borderRadius: "6px",
|
|
1823
|
+
fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
|
|
1743
1824
|
},
|
|
1744
|
-
"
|
|
1745
|
-
highlight: "#ed702d",
|
|
1746
|
-
background: "#1f2630",
|
|
1747
|
-
surface: "#1f2630",
|
|
1748
|
-
text: "#f1f5f9",
|
|
1749
|
-
border: "#ed702d",
|
|
1750
|
-
success: "#22c55e",
|
|
1751
|
-
warning: "#eab308",
|
|
1752
|
-
error: "#ef4444",
|
|
1825
|
+
"orange-raw": {
|
|
1826
|
+
highlight: "#ed702d",
|
|
1827
|
+
background: "#1f2630",
|
|
1828
|
+
surface: "#1f2630",
|
|
1829
|
+
text: "#f1f5f9",
|
|
1830
|
+
border: "#ed702d",
|
|
1831
|
+
success: "#22c55e",
|
|
1832
|
+
warning: "#eab308",
|
|
1833
|
+
error: "#ef4444",
|
|
1753
1834
|
borderRadius: "0px",
|
|
1754
1835
|
fontFamily: "Inter, system-ui, sans-serif",
|
|
1755
1836
|
},
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1837
|
+
// --- Dark Themes ---
|
|
1838
|
+
"navy-night": {
|
|
1839
|
+
highlight: "#60a5fa",
|
|
1840
|
+
background: "#0b1120",
|
|
1841
|
+
surface: "#111827",
|
|
1842
|
+
text: "#e2e8f0",
|
|
1843
|
+
border: "#1e3a5f",
|
|
1844
|
+
success: "#34d399",
|
|
1845
|
+
warning: "#fbbf24",
|
|
1846
|
+
error: "#f87171",
|
|
1847
|
+
borderRadius: "10px",
|
|
1848
|
+
fontFamily: "'Outfit', system-ui, sans-serif",
|
|
1767
1849
|
},
|
|
1768
|
-
"
|
|
1769
|
-
highlight: "#
|
|
1770
|
-
background: "#
|
|
1771
|
-
surface: "#
|
|
1772
|
-
text: "#
|
|
1773
|
-
border: "#
|
|
1774
|
-
success: "#
|
|
1775
|
-
warning: "#
|
|
1776
|
-
error: "#
|
|
1777
|
-
borderRadius: "8px",
|
|
1778
|
-
fontFamily: "Inter, system-ui, sans-serif",
|
|
1779
|
-
},
|
|
1780
|
-
"dark-modern": {
|
|
1781
|
-
highlight: "#3b82f6", // blue-500 - bright blue accent
|
|
1782
|
-
background: "#0f172a", // slate-900 - dark background
|
|
1783
|
-
surface: "#1e293b", // slate-800 - dark cards
|
|
1784
|
-
text: "#f1f5f9", // slate-100 - light text
|
|
1785
|
-
border: "#334155", // slate-700 - subtle borders
|
|
1786
|
-
success: "#22c55e", // green-500
|
|
1787
|
-
warning: "#eab308", // yellow-500
|
|
1788
|
-
error: "#ef4444", // red-500
|
|
1789
|
-
borderRadius: "8px",
|
|
1790
|
-
fontFamily: "Inter, system-ui, sans-serif",
|
|
1791
|
-
},
|
|
1792
|
-
"dark-forest": {
|
|
1793
|
-
highlight: "#34d399", // Emerald 400
|
|
1794
|
-
background: "#05140d",
|
|
1795
|
-
surface: "#062215",
|
|
1796
|
-
text: "#d1fae5", // Emerald 100
|
|
1797
|
-
border: "#043322",
|
|
1798
|
-
success: "#4ade80", // Green 400
|
|
1799
|
-
warning: "#facc15", // Yellow 400
|
|
1800
|
-
error: "#f87171", // Red 400
|
|
1850
|
+
"green-deep": {
|
|
1851
|
+
highlight: "#34d399",
|
|
1852
|
+
background: "#030d07",
|
|
1853
|
+
surface: "#051a0e",
|
|
1854
|
+
text: "#d1fae5",
|
|
1855
|
+
border: "#064e20",
|
|
1856
|
+
success: "#4ade80",
|
|
1857
|
+
warning: "#facc15",
|
|
1858
|
+
error: "#f87171",
|
|
1801
1859
|
borderRadius: "12px",
|
|
1802
|
-
fontFamily: "system-ui,
|
|
1860
|
+
fontFamily: "'Instrument Sans', system-ui, sans-serif",
|
|
1803
1861
|
},
|
|
1804
|
-
"
|
|
1805
|
-
highlight: "#
|
|
1862
|
+
"green-matrix": {
|
|
1863
|
+
highlight: "#39ff14",
|
|
1806
1864
|
background: "#000000",
|
|
1807
|
-
surface: "#
|
|
1808
|
-
text: "#
|
|
1809
|
-
border: "#
|
|
1810
|
-
success: "#
|
|
1865
|
+
surface: "#060f06",
|
|
1866
|
+
text: "#00ff41",
|
|
1867
|
+
border: "#0d2b0d",
|
|
1868
|
+
success: "#39ff14",
|
|
1811
1869
|
warning: "#ffff00",
|
|
1812
1870
|
error: "#ff3333",
|
|
1813
1871
|
borderRadius: "0px",
|
|
1814
|
-
fontFamily: "'
|
|
1872
|
+
fontFamily: "'Share Tech Mono', monospace",
|
|
1815
1873
|
},
|
|
1816
|
-
"
|
|
1817
|
-
highlight: "#fde047",
|
|
1818
|
-
background: "#1c1917",
|
|
1819
|
-
surface: "#292524",
|
|
1820
|
-
text: "#fafaf9",
|
|
1821
|
-
border: "#44403c",
|
|
1822
|
-
success: "#a3e635",
|
|
1823
|
-
warning: "#f59e0b",
|
|
1824
|
-
error: "#fca5a5",
|
|
1874
|
+
"gold-luxury": {
|
|
1875
|
+
highlight: "#fde047",
|
|
1876
|
+
background: "#1c1917",
|
|
1877
|
+
surface: "#292524",
|
|
1878
|
+
text: "#fafaf9",
|
|
1879
|
+
border: "#44403c",
|
|
1880
|
+
success: "#a3e635",
|
|
1881
|
+
warning: "#f59e0b",
|
|
1882
|
+
error: "#fca5a5",
|
|
1825
1883
|
borderRadius: "24px",
|
|
1826
|
-
fontFamily: "'
|
|
1884
|
+
fontFamily: "'Bodoni Moda', serif",
|
|
1827
1885
|
},
|
|
1828
|
-
"
|
|
1829
|
-
highlight: "#d946ef",
|
|
1830
|
-
background: "#
|
|
1831
|
-
surface: "#
|
|
1832
|
-
text: "#f3e8ff",
|
|
1833
|
-
border: "#
|
|
1834
|
-
success: "#4ade80",
|
|
1835
|
-
warning: "#facc15",
|
|
1836
|
-
error: "#f87171",
|
|
1886
|
+
"purple-electric": {
|
|
1887
|
+
highlight: "#d946ef",
|
|
1888
|
+
background: "#110820",
|
|
1889
|
+
surface: "#1a0d30",
|
|
1890
|
+
text: "#f3e8ff",
|
|
1891
|
+
border: "#3b0764",
|
|
1892
|
+
success: "#4ade80",
|
|
1893
|
+
warning: "#facc15",
|
|
1894
|
+
error: "#f87171",
|
|
1837
1895
|
borderRadius: "14px",
|
|
1838
1896
|
fontFamily: "'Geologica', sans-serif",
|
|
1839
1897
|
},
|
|
1898
|
+
"dark-neon-brutalism": {
|
|
1899
|
+
highlight: "#00e5cc",
|
|
1900
|
+
background: "#0e1420",
|
|
1901
|
+
surface: "#0e1420",
|
|
1902
|
+
text: "#e8eef5",
|
|
1903
|
+
border: "#00e5cc",
|
|
1904
|
+
success: "#00e5cc",
|
|
1905
|
+
warning: "#ffe45e",
|
|
1906
|
+
error: "#ff4d6d",
|
|
1907
|
+
borderRadius: "4px",
|
|
1908
|
+
fontFamily: "'JetBrains Mono', monospace",
|
|
1909
|
+
buttonTextColor: "#0e1420",
|
|
1910
|
+
},
|
|
1911
|
+
"rose-editorial": {
|
|
1912
|
+
highlight: "#be3455",
|
|
1913
|
+
background: "#fdf8f5",
|
|
1914
|
+
surface: "#ffffff",
|
|
1915
|
+
text: "#1a0a0f",
|
|
1916
|
+
border: "#f2d5db",
|
|
1917
|
+
success: "#2d6a4f",
|
|
1918
|
+
warning: "#b5451b",
|
|
1919
|
+
error: "#9b1d20",
|
|
1920
|
+
borderRadius: "0px",
|
|
1921
|
+
fontFamily: "'Cormorant', serif",
|
|
1922
|
+
},
|
|
1923
|
+
"amber-retro": {
|
|
1924
|
+
highlight: "#f59e0b",
|
|
1925
|
+
background: "#1a1008",
|
|
1926
|
+
surface: "#241a0a",
|
|
1927
|
+
text: "#fef3c7",
|
|
1928
|
+
border: "#78350f",
|
|
1929
|
+
success: "#84cc16",
|
|
1930
|
+
warning: "#f59e0b",
|
|
1931
|
+
error: "#ef4444",
|
|
1932
|
+
borderRadius: "6px",
|
|
1933
|
+
fontFamily: "'Syne', sans-serif",
|
|
1934
|
+
},
|
|
1840
1935
|
};
|
|
1841
1936
|
const StyleProvider = ({ config, children, }) => {
|
|
1842
1937
|
// Track hydration state to prevent mismatches
|
|
@@ -1846,8 +1941,10 @@ const StyleProvider = ({ config, children, }) => {
|
|
|
1846
1941
|
}, []);
|
|
1847
1942
|
// PERFORMANCE OPTIMIZATION: Memoize style calculations
|
|
1848
1943
|
const themedStyles = React.useMemo(() => {
|
|
1849
|
-
const
|
|
1850
|
-
|
|
1944
|
+
const rawThemeName = config.theme || "teal-minimal";
|
|
1945
|
+
// Redirect legacy theme names to new names
|
|
1946
|
+
const themeName = legacyThemeRedirects[rawThemeName] || rawThemeName;
|
|
1947
|
+
const themeDefaults = themes[themeName] || themes["teal-minimal"];
|
|
1851
1948
|
const getCSSValue = (value, fallback) => {
|
|
1852
1949
|
if (!value)
|
|
1853
1950
|
return fallback;
|
|
@@ -1917,6 +2014,7 @@ const StyleProvider = ({ config, children, }) => {
|
|
|
1917
2014
|
"--bw-surface-color": finalColors.surface,
|
|
1918
2015
|
"--bw-text-color": finalColors.text,
|
|
1919
2016
|
"--bw-text-muted": addOpacity(finalColors.text, 0.7),
|
|
2017
|
+
"--bw-button-text-color": themeDefaults.buttonTextColor || "#ffffff",
|
|
1920
2018
|
"--bw-border-color": finalColors.border,
|
|
1921
2019
|
"--bw-success-color": finalColors.success,
|
|
1922
2020
|
"--bw-warning-color": finalColors.warning,
|
|
@@ -1934,7 +2032,7 @@ const StyleProvider = ({ config, children, }) => {
|
|
|
1934
2032
|
"--bw-highlight-muted": addOpacity(finalColors.highlight, 0.1),
|
|
1935
2033
|
"--bw-highlight-subtle": addOpacity(finalColors.highlight, 0.05),
|
|
1936
2034
|
"--bw-text-subtle": addOpacity(finalColors.text, 0.4),
|
|
1937
|
-
colorScheme:
|
|
2035
|
+
colorScheme: (["navy-night", "green-deep", "green-matrix", "gold-luxury", "purple-electric", "dark-neon-brutalism", "amber-retro", "orange-raw"].includes(themeName) || themeName.startsWith("dark-")) ? "dark" : "light",
|
|
1938
2036
|
};
|
|
1939
2037
|
}, [
|
|
1940
2038
|
config.theme,
|
|
@@ -4488,6 +4586,7 @@ function DialogWrapper({ isOpen, onClose, children, maxWidth = "700px", classNam
|
|
|
4488
4586
|
"--bw-font-family": computedStyles.getPropertyValue("--bw-font-family").trim() || "system-ui, sans-serif",
|
|
4489
4587
|
"--bw-shadow-md": computedStyles.getPropertyValue("--bw-shadow-md").trim() ||
|
|
4490
4588
|
"0 4px 6px -1px rgba(0, 0, 0, 0.1)",
|
|
4589
|
+
"--bw-button-text-color": computedStyles.getPropertyValue("--bw-button-text-color").trim() || "#ffffff",
|
|
4491
4590
|
};
|
|
4492
4591
|
setFallbackStyles(fallbacks);
|
|
4493
4592
|
}
|
|
@@ -11137,16 +11236,37 @@ const objectType = ZodObject.create;
|
|
|
11137
11236
|
ZodUnion.create;
|
|
11138
11237
|
ZodIntersection.create;
|
|
11139
11238
|
ZodTuple.create;
|
|
11140
|
-
ZodEnum.create;
|
|
11239
|
+
const enumType = ZodEnum.create;
|
|
11141
11240
|
ZodPromise.create;
|
|
11142
11241
|
ZodOptional.create;
|
|
11143
11242
|
ZodNullable.create;
|
|
11243
|
+
const preprocessType = ZodEffects.createWithPreprocess;
|
|
11144
11244
|
|
|
11145
|
-
const
|
|
11146
|
-
name:
|
|
11245
|
+
const DEFAULT_PARTICIPANT_FIELDS_CONFIG = {
|
|
11246
|
+
name: { enabled: true, required: true },
|
|
11247
|
+
age: { enabled: true, required: false },
|
|
11248
|
+
level: { enabled: false, required: false },
|
|
11249
|
+
};
|
|
11250
|
+
const participantSchema = (t, fieldsConfig) => objectType({
|
|
11251
|
+
name: stringType().trim().optional(),
|
|
11147
11252
|
age: numberType().min(0).max(120).optional(),
|
|
11253
|
+
level: preprocessType((value) => (value === "" ? undefined : value), enumType(["beginner", "advanced"]).optional()),
|
|
11254
|
+
})
|
|
11255
|
+
.superRefine((value, ctx) => {
|
|
11256
|
+
if (fieldsConfig.name.required && (!value.name || value.name.trim().length < 1)) {
|
|
11257
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.nameRequired"), path: ["name"] });
|
|
11258
|
+
}
|
|
11259
|
+
if (!fieldsConfig.name.enabled && value.name && value.name.trim().length > 0) {
|
|
11260
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.nameRequired"), path: ["name"] });
|
|
11261
|
+
}
|
|
11262
|
+
if (fieldsConfig.age.required && typeof value.age !== "number") {
|
|
11263
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.ageRequired"), path: ["age"] });
|
|
11264
|
+
}
|
|
11265
|
+
if (fieldsConfig.level.required && !value.level) {
|
|
11266
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.levelRequired"), path: ["level"] });
|
|
11267
|
+
}
|
|
11148
11268
|
});
|
|
11149
|
-
function createBookingFormSchema(t) {
|
|
11269
|
+
function createBookingFormSchema(t, fieldsConfig = DEFAULT_PARTICIPANT_FIELDS_CONFIG) {
|
|
11150
11270
|
const tr = t ?? ((key) => key);
|
|
11151
11271
|
return objectType({
|
|
11152
11272
|
customerName: stringType().trim().min(2, tr("validation.nameMinLength")),
|
|
@@ -11156,7 +11276,7 @@ function createBookingFormSchema(t) {
|
|
|
11156
11276
|
.email(tr("validation.emailInvalid"))
|
|
11157
11277
|
.regex(/\.[a-zA-Z]{2,}$/, tr("validation.emailDomainInvalid")),
|
|
11158
11278
|
customerPhone: stringType().trim().optional(),
|
|
11159
|
-
participants: arrayType(participantSchema(tr)).min(1, tr("validation.participantRequired")),
|
|
11279
|
+
participants: arrayType(participantSchema(tr, fieldsConfig)).min(1, tr("validation.participantRequired")),
|
|
11160
11280
|
discountCode: stringType().trim().optional(),
|
|
11161
11281
|
comment: stringType().trim().optional(),
|
|
11162
11282
|
acceptTerms: booleanType().refine((val) => val === true, {
|
|
@@ -11197,11 +11317,13 @@ const buttonBase = {
|
|
|
11197
11317
|
whiteSpace: "nowrap",
|
|
11198
11318
|
border: "none",
|
|
11199
11319
|
};
|
|
11320
|
+
// CSS class name for button hover effects
|
|
11321
|
+
const buttonClassName = "bw-button-hover";
|
|
11200
11322
|
const buttonStyles = {
|
|
11201
11323
|
primary: {
|
|
11202
11324
|
...buttonBase,
|
|
11203
11325
|
backgroundColor: "var(--bw-highlight-color)",
|
|
11204
|
-
color: "#ffffff",
|
|
11326
|
+
color: "var(--bw-button-text-color, #ffffff)",
|
|
11205
11327
|
border: "none",
|
|
11206
11328
|
},
|
|
11207
11329
|
secondary: {
|
|
@@ -11297,7 +11419,8 @@ const participantUpsellStyles = {
|
|
|
11297
11419
|
gap: "8px",
|
|
11298
11420
|
marginTop: "10px",
|
|
11299
11421
|
paddingTop: "10px",
|
|
11300
|
-
|
|
11422
|
+
paddingBottom: "25px",
|
|
11423
|
+
borderBottom: "1px dashed var(--bw-border-color)",
|
|
11301
11424
|
},
|
|
11302
11425
|
label: {
|
|
11303
11426
|
display: "inline-flex",
|
|
@@ -11355,6 +11478,8 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11355
11478
|
const { locale } = useLocale();
|
|
11356
11479
|
const timezone = useTimezone();
|
|
11357
11480
|
const roundEnabled = systemConfig?.roundPricesEnabled !== false;
|
|
11481
|
+
const participantFieldsConfig = eventDetails.participantFieldsConfig ?? DEFAULT_PARTICIPANT_FIELDS_CONFIG;
|
|
11482
|
+
const participantLevelOptions = eventDetails.participantLevelOptions ?? ["beginner", "advanced"];
|
|
11358
11483
|
const roundDiscountUp = (minorUnits) => Math.ceil(minorUnits / 100) * 100;
|
|
11359
11484
|
const calcPercentDiscountAmount = (baseAmount, basisPoints, round) => {
|
|
11360
11485
|
const raw = Math.round((baseAmount * basisPoints) / 10000);
|
|
@@ -11367,18 +11492,19 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11367
11492
|
// Per-participant upsell selections: participantIndex -> array of upsell package IDs
|
|
11368
11493
|
const [participantUpsells, setParticipantUpsells] = React.useState({});
|
|
11369
11494
|
const form = useForm({
|
|
11370
|
-
resolver: t(createBookingFormSchema(t$1)),
|
|
11495
|
+
resolver: t(createBookingFormSchema(t$1, participantFieldsConfig)),
|
|
11371
11496
|
defaultValues: {
|
|
11372
11497
|
customerName: "",
|
|
11373
11498
|
customerEmail: "",
|
|
11374
11499
|
customerPhone: "",
|
|
11375
|
-
participants: [{ name: "" }],
|
|
11500
|
+
participants: [{ name: "", level: undefined }],
|
|
11376
11501
|
discountCode: "",
|
|
11377
11502
|
comment: "",
|
|
11378
11503
|
acceptTerms: false,
|
|
11379
11504
|
},
|
|
11380
11505
|
});
|
|
11381
11506
|
const watchedParticipants = form.watch("participants");
|
|
11507
|
+
const participantCount = watchedParticipants.length;
|
|
11382
11508
|
const watchedCustomerName = form.watch("customerName");
|
|
11383
11509
|
const watchedCustomerEmail = form.watch("customerEmail");
|
|
11384
11510
|
const watchedComment = form.watch("comment");
|
|
@@ -11420,14 +11546,13 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11420
11546
|
const calculateBaseTotal = React.useCallback(() => {
|
|
11421
11547
|
if (!eventDetails)
|
|
11422
11548
|
return 0;
|
|
11423
|
-
return eventDetails.price *
|
|
11424
|
-
}, [eventDetails,
|
|
11549
|
+
return eventDetails.price * participantCount;
|
|
11550
|
+
}, [eventDetails, participantCount]);
|
|
11425
11551
|
// Calculate upsells total based on per-participant selections
|
|
11426
11552
|
const calculateUpsellsTotal = React.useCallback(() => {
|
|
11427
11553
|
let total = 0;
|
|
11428
|
-
watchedParticipants.forEach((
|
|
11429
|
-
|
|
11430
|
-
if (participant.name.trim()) {
|
|
11554
|
+
watchedParticipants.forEach((_, index) => {
|
|
11555
|
+
if (participantCount > 0) {
|
|
11431
11556
|
const participantUpsellIds = participantUpsells[index] || [];
|
|
11432
11557
|
participantUpsellIds.forEach(upsellId => {
|
|
11433
11558
|
const upsell = upsells.find(u => u.id === upsellId);
|
|
@@ -11438,7 +11563,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11438
11563
|
}
|
|
11439
11564
|
});
|
|
11440
11565
|
return total;
|
|
11441
|
-
}, [participantUpsells, upsells, watchedParticipants]);
|
|
11566
|
+
}, [participantUpsells, upsells, watchedParticipants, participantCount]);
|
|
11442
11567
|
const calculateTotalDiscount = React.useCallback(() => {
|
|
11443
11568
|
return appliedVouchers.reduce((total, voucher) => {
|
|
11444
11569
|
if (voucher.type === "discount") {
|
|
@@ -11459,8 +11584,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11459
11584
|
const calculateDeposit = () => {
|
|
11460
11585
|
if (!eventDetails || !eventDetails.deposit)
|
|
11461
11586
|
return 0;
|
|
11462
|
-
|
|
11463
|
-
return eventDetails.deposit * participantCount;
|
|
11587
|
+
return eventDetails.deposit * watchedParticipants.length;
|
|
11464
11588
|
};
|
|
11465
11589
|
const baseTotal = calculateBaseTotal();
|
|
11466
11590
|
const upsellsTotal = calculateUpsellsTotal();
|
|
@@ -11477,8 +11601,8 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11477
11601
|
// Includes participantIndices to track which participants selected each upsell
|
|
11478
11602
|
const aggregatedUpsellSelections = React.useCallback(() => {
|
|
11479
11603
|
const upsellParticipantMap = {};
|
|
11480
|
-
watchedParticipants.forEach((
|
|
11481
|
-
if (
|
|
11604
|
+
watchedParticipants.forEach((_, index) => {
|
|
11605
|
+
if (participantCount > 0) {
|
|
11482
11606
|
const participantUpsellIds = participantUpsells[index] || [];
|
|
11483
11607
|
participantUpsellIds.forEach(upsellId => {
|
|
11484
11608
|
if (!upsellParticipantMap[upsellId]) {
|
|
@@ -11512,15 +11636,17 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11512
11636
|
setAppliedVouchers((prev) => prev.filter((v) => v.code !== code));
|
|
11513
11637
|
}, []);
|
|
11514
11638
|
const isReadyForPayment = () => {
|
|
11515
|
-
const participantsWithNames = watchedParticipants.filter((p) => p.name
|
|
11639
|
+
const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
|
|
11516
11640
|
const totalParticipantRows = watchedParticipants.length;
|
|
11517
|
-
const allParticipantsHaveNames =
|
|
11641
|
+
const allParticipantsHaveNames = participantFieldsConfig.name.required
|
|
11642
|
+
? participantsWithNames === totalParticipantRows
|
|
11643
|
+
: true;
|
|
11518
11644
|
const participantsWithinLimit = participantsWithNames <= (eventDetails?.availableSpots || 0);
|
|
11519
11645
|
const hasValidCustomerName = watchedCustomerName && watchedCustomerName.trim().length >= 2;
|
|
11520
11646
|
const hasValidCustomerEmail = watchedCustomerEmail && watchedCustomerEmail.trim().length > 0 && !customerEmailError;
|
|
11521
11647
|
return allParticipantsHaveNames &&
|
|
11522
11648
|
participantsWithinLimit &&
|
|
11523
|
-
participantsWithNames > 0 &&
|
|
11649
|
+
(participantFieldsConfig.name.required ? participantsWithNames > 0 : totalParticipantRows > 0) &&
|
|
11524
11650
|
hasValidCustomerName &&
|
|
11525
11651
|
hasValidCustomerEmail &&
|
|
11526
11652
|
watchedAcceptTerms;
|
|
@@ -11528,7 +11654,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11528
11654
|
React.useEffect(() => {
|
|
11529
11655
|
if (appliedVouchers.length > 0) {
|
|
11530
11656
|
const newBaseTotal = eventDetails?.price
|
|
11531
|
-
? eventDetails.price * watchedParticipants.
|
|
11657
|
+
? eventDetails.price * watchedParticipants.length
|
|
11532
11658
|
: 0;
|
|
11533
11659
|
const currentUpsellsTotal = calculateUpsellsTotal();
|
|
11534
11660
|
const orderTotal = newBaseTotal + currentUpsellsTotal;
|
|
@@ -11573,7 +11699,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11573
11699
|
const currentParticipants = form.getValues("participants");
|
|
11574
11700
|
const availableSpots = eventDetails?.availableSpots || 0;
|
|
11575
11701
|
if (currentParticipants.length < availableSpots) {
|
|
11576
|
-
form.setValue("participants", [...currentParticipants, { name: "" }]);
|
|
11702
|
+
form.setValue("participants", [...currentParticipants, { name: "", level: undefined }]);
|
|
11577
11703
|
}
|
|
11578
11704
|
else {
|
|
11579
11705
|
alert(t$1("booking.maxParticipants", { count: availableSpots }));
|
|
@@ -11692,7 +11818,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11692
11818
|
justifyContent: "space-between",
|
|
11693
11819
|
alignItems: "center",
|
|
11694
11820
|
marginBottom: "16px",
|
|
11695
|
-
}, children: jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: 0 }, children: t$1("booking.participants") }) }), jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [watchedParticipants.map((_, index) => (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", gap: "12px", alignItems: "center" }, children: [jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [jsxRuntime.jsx("label", { htmlFor: `participant-name-${index}`, style: labelStyles$1, children: t$1("booking.participantName") }), jsxRuntime.jsx("input", { id: `participant-name-${index}`, ...form.register(`participants.${index}.name`), type: "text", style: inputStyles$1, placeholder: t$1("booking.participantNamePlaceholder") }), form.formState.errors.participants?.[index]?.name && (jsxRuntime.jsx("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.name?.message }))] }), jsxRuntime.jsxs("div", { style: { width: "80px" }, children: [jsxRuntime.jsx("label", { htmlFor: `participant-age-${index}`, style: labelStyles$1, children: t$1("booking.participantAge") }), jsxRuntime.jsx("input", { id: `participant-age-${index}`, ...form.register(`participants.${index}.age`, {
|
|
11821
|
+
}, children: jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: 0 }, children: t$1("booking.participants") }) }), jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [watchedParticipants.map((_, index) => (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", gap: "12px", alignItems: "center" }, children: [participantFieldsConfig.name.enabled && (jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [jsxRuntime.jsx("label", { htmlFor: `participant-name-${index}`, style: labelStyles$1, children: t$1("booking.participantName") }), jsxRuntime.jsx("input", { id: `participant-name-${index}`, ...form.register(`participants.${index}.name`), type: "text", style: inputStyles$1, placeholder: t$1("booking.participantNamePlaceholder") }), form.formState.errors.participants?.[index]?.name && (jsxRuntime.jsx("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.name?.message }))] })), participantFieldsConfig.age.enabled && (jsxRuntime.jsxs("div", { style: { width: "80px" }, children: [jsxRuntime.jsx("label", { htmlFor: `participant-age-${index}`, style: labelStyles$1, children: t$1("booking.participantAge") }), jsxRuntime.jsx("input", { id: `participant-age-${index}`, ...form.register(`participants.${index}.age`, {
|
|
11696
11822
|
setValueAs: (value) => {
|
|
11697
11823
|
if (value === "" || value === null || value === undefined) {
|
|
11698
11824
|
return undefined;
|
|
@@ -11700,7 +11826,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11700
11826
|
const num = Number(value);
|
|
11701
11827
|
return Number.isNaN(num) ? undefined : num;
|
|
11702
11828
|
},
|
|
11703
|
-
}), type: "number", min: "0", max: "120", style: inputStyles$1, placeholder: "25" })] }), watchedParticipants.length > 1 && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { style: { ...labelStyles$1, visibility: "hidden" }, children: "\u00A0" }), jsxRuntime.jsx("button", { type: "button", onClick: () => removeParticipant(index), style: {
|
|
11829
|
+
}), type: "number", min: "0", max: "120", style: inputStyles$1, placeholder: "25" })] })), watchedParticipants.length > 1 && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { style: { ...labelStyles$1, visibility: "hidden" }, children: "\u00A0" }), jsxRuntime.jsx("button", { type: "button", onClick: () => removeParticipant(index), style: {
|
|
11704
11830
|
color: "var(--bw-error-color)",
|
|
11705
11831
|
backgroundColor: "var(--bw-surface-color)",
|
|
11706
11832
|
border: "1px solid var(--bw-border-color)",
|
|
@@ -11716,7 +11842,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11716
11842
|
fontWeight: 700,
|
|
11717
11843
|
fontFamily: "var(--bw-font-family)",
|
|
11718
11844
|
padding: 0,
|
|
11719
|
-
}, children: "\u00D7" })] }))] }), upsells.length > 0 && (jsxRuntime.jsx("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
|
|
11845
|
+
}, children: "\u00D7" })] }))] }), participantFieldsConfig.level.enabled && (jsxRuntime.jsxs("div", { style: { minWidth: "140px" }, children: [jsxRuntime.jsx("label", { htmlFor: `participant-level-${index}`, style: labelStyles$1, children: t$1("booking.participantLevel") }), jsxRuntime.jsxs("select", { id: `participant-level-${index}`, ...form.register(`participants.${index}.level`), style: inputStyles$1, children: [jsxRuntime.jsx("option", { value: "", children: t$1("booking.participantLevelPlaceholder") }), participantLevelOptions.map((level) => (jsxRuntime.jsx("option", { value: level, children: t$1(`level.${level}`) }, level)))] }), form.formState.errors.participants?.[index]?.level && (jsxRuntime.jsx("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.level?.message }))] })), upsells.length > 0 && (jsxRuntime.jsx("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
|
|
11720
11846
|
const isSelected = (participantUpsells[index] || []).includes(upsell.id);
|
|
11721
11847
|
return (jsxRuntime.jsxs("label", { htmlFor: `upsell-${index}-${upsell.id}`, style: isSelected ? participantUpsellStyles.labelSelected : participantUpsellStyles.label, children: [jsxRuntime.jsx("input", { id: `upsell-${index}-${upsell.id}`, type: "checkbox", style: participantUpsellStyles.checkbox, checked: isSelected, onChange: () => toggleParticipantUpsell(index, upsell.id) }), jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: upsell.name }), jsxRuntime.jsxs("span", { style: { fontSize: "12px", opacity: 0.8 }, children: ["(+", formatCurrency(upsell.price), ")"] })] }, upsell.id));
|
|
11722
11848
|
}) }))] }, index))), watchedParticipants.length < eventDetails.availableSpots ? (jsxRuntime.jsx("div", { style: {
|
|
@@ -11745,9 +11871,9 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11745
11871
|
color: "var(--bw-text-color)",
|
|
11746
11872
|
fontWeight: 500,
|
|
11747
11873
|
fontFamily: "var(--bw-font-family)",
|
|
11748
|
-
}, children: [jsxRuntime.jsxs("span", { style: { fontWeight: 200 }, children: [watchedParticipants.length > 1 ? watchedParticipants.
|
|
11874
|
+
}, children: [jsxRuntime.jsxs("span", { style: { fontWeight: 200 }, children: [watchedParticipants.length > 1 ? watchedParticipants.length : 1, " x "] }), " ", formatCurrency(eventDetails.price)] })] }), upsellsTotal > 0 && (jsxRuntime.jsxs("div", { style: { marginTop: "8px", paddingTop: "8px", borderTop: "1px dashed var(--bw-border-color)" }, children: [jsxRuntime.jsxs("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "13px", display: "block", marginBottom: "4px" }, children: [t$1("common.extras"), ":"] }), upsells.map((upsell) => {
|
|
11749
11875
|
// Count how many participants have this upsell selected
|
|
11750
|
-
const countWithUpsell = watchedParticipants.filter((
|
|
11876
|
+
const countWithUpsell = watchedParticipants.filter((_, idx) => (participantUpsells[idx] || []).includes(upsell.id)).length;
|
|
11751
11877
|
if (countWithUpsell === 0)
|
|
11752
11878
|
return null;
|
|
11753
11879
|
const upsellLineTotal = upsell.price * countWithUpsell;
|
|
@@ -11848,15 +11974,17 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11848
11974
|
}, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), jsxRuntime.jsx("div", { ref: paymentSectionRef, children: (stripePromise || systemConfig?.paymentProvider === "mollie") &&
|
|
11849
11975
|
(() => {
|
|
11850
11976
|
if (!isReadyForPayment()) {
|
|
11851
|
-
const participantsWithNames = watchedParticipants.filter((p) => p.name
|
|
11977
|
+
const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
|
|
11852
11978
|
const totalParticipantRows = watchedParticipants.length;
|
|
11853
11979
|
const participantsWithoutNames = totalParticipantRows - participantsWithNames;
|
|
11854
11980
|
const missing = [];
|
|
11855
|
-
if (
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11981
|
+
if (participantFieldsConfig.name.required) {
|
|
11982
|
+
if (participantsWithNames === 0) {
|
|
11983
|
+
missing.push(t$1("payment.needParticipant"));
|
|
11984
|
+
}
|
|
11985
|
+
else if (participantsWithoutNames > 0) {
|
|
11986
|
+
missing.push(t$1("payment.needAllNames", { count: totalParticipantRows }));
|
|
11987
|
+
}
|
|
11860
11988
|
}
|
|
11861
11989
|
if (participantsWithNames > (eventDetails?.availableSpots || 0)) {
|
|
11862
11990
|
missing.push(t$1("payment.reduceParticipants", { count: eventDetails?.availableSpots || 0 }));
|
|
@@ -12031,7 +12159,7 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
|
|
|
12031
12159
|
try {
|
|
12032
12160
|
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/get-booking-by-payment"), {
|
|
12033
12161
|
method: "POST",
|
|
12034
|
-
headers: createApiHeaders(config),
|
|
12162
|
+
headers: createApiHeaders(config, locale),
|
|
12035
12163
|
body: JSON.stringify(createRequestBody(config, {
|
|
12036
12164
|
paymentIntentId: targetPaymentIntentId,
|
|
12037
12165
|
})),
|
|
@@ -12265,7 +12393,7 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
|
|
|
12265
12393
|
flexDirection: "column",
|
|
12266
12394
|
gap: "var(--bw-spacing-small)",
|
|
12267
12395
|
}, children: formData.participants
|
|
12268
|
-
.filter((p) => p.name
|
|
12396
|
+
.filter((p) => p.name?.trim() || p.age || p.level)
|
|
12269
12397
|
.map((participant, index) => (jsxRuntime.jsx("div", { className: "print-participant", style: {
|
|
12270
12398
|
display: "flex",
|
|
12271
12399
|
justifyContent: "space-between",
|
|
@@ -12276,11 +12404,15 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
|
|
|
12276
12404
|
}, children: jsxRuntime.jsxs("div", { className: "print-participant-info", children: [jsxRuntime.jsx("div", { className: "print-participant-name", style: {
|
|
12277
12405
|
color: "var(--bw-text-color)",
|
|
12278
12406
|
fontFamily: "var(--bw-font-family)",
|
|
12279
|
-
}, children: participant.name }), participant.age && (jsxRuntime.jsx("div", { className: "print-participant-age", style: {
|
|
12407
|
+
}, children: participant.name || `#${index + 1}` }), participant.age && (jsxRuntime.jsx("div", { className: "print-participant-age", style: {
|
|
12280
12408
|
color: "var(--bw-text-muted)",
|
|
12281
12409
|
fontSize: "var(--bw-font-size-small)",
|
|
12282
12410
|
fontFamily: "var(--bw-font-family)",
|
|
12283
|
-
}, children: t("success.age", { age: participant.age }) }))
|
|
12411
|
+
}, children: t("success.age", { age: participant.age }) })), participant.level && (jsxRuntime.jsxs("div", { style: {
|
|
12412
|
+
color: "var(--bw-text-muted)",
|
|
12413
|
+
fontSize: "var(--bw-font-size-small)",
|
|
12414
|
+
fontFamily: "var(--bw-font-family)",
|
|
12415
|
+
}, children: [t("booking.participantLevel"), ": ", t(`level.${participant.level}`)] }))] }) }, index))) }) })] })), jsxRuntime.jsxs("div", { className: "print-booking-card", style: {
|
|
12284
12416
|
backgroundColor: "var(--bw-surface-color)",
|
|
12285
12417
|
border: `1px solid var(--bw-border-color)`,
|
|
12286
12418
|
borderRadius: "var(--bw-border-radius)",
|
|
@@ -13688,8 +13820,48 @@ function formatDurationInfo(info, t) {
|
|
|
13688
13820
|
const rest = formatted.slice(0, -1).join(", ");
|
|
13689
13821
|
return `${t("duration.optionally")} ${rest} ${t("duration.or")} ${last} ${unitPlural}`;
|
|
13690
13822
|
}
|
|
13691
|
-
function
|
|
13823
|
+
function InfoBadge({ text }) {
|
|
13824
|
+
const [open, setOpen] = React.useState(false);
|
|
13825
|
+
const ref = React.useRef(null);
|
|
13826
|
+
return (jsxRuntime.jsxs("span", { ref: ref, onClick: (e) => { e.stopPropagation(); setOpen((v) => !v); }, style: {
|
|
13827
|
+
position: "relative",
|
|
13828
|
+
display: "inline-flex",
|
|
13829
|
+
alignItems: "center",
|
|
13830
|
+
justifyContent: "center",
|
|
13831
|
+
width: "16px",
|
|
13832
|
+
height: "16px",
|
|
13833
|
+
borderRadius: "50%",
|
|
13834
|
+
border: "1px solid var(--bw-highlight-color)",
|
|
13835
|
+
color: "var(--bw-highlight-color)",
|
|
13836
|
+
fontSize: "9px",
|
|
13837
|
+
fontWeight: 700,
|
|
13838
|
+
cursor: "pointer",
|
|
13839
|
+
flexShrink: 0,
|
|
13840
|
+
userSelect: "none",
|
|
13841
|
+
}, children: ["i", open && (jsxRuntime.jsx("span", { style: {
|
|
13842
|
+
position: "absolute",
|
|
13843
|
+
bottom: "calc(100% + 6px)",
|
|
13844
|
+
right: 0,
|
|
13845
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
13846
|
+
border: "1px solid var(--bw-border-color)",
|
|
13847
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
13848
|
+
boxShadow: "var(--bw-shadow-md)",
|
|
13849
|
+
padding: "6px 10px",
|
|
13850
|
+
fontSize: "14px",
|
|
13851
|
+
color: "var(--bw-text-color)",
|
|
13852
|
+
fontWeight: 400,
|
|
13853
|
+
whiteSpace: "normal",
|
|
13854
|
+
width: "160px",
|
|
13855
|
+
lineHeight: 1.4,
|
|
13856
|
+
zIndex: 100,
|
|
13857
|
+
textAlign: "left",
|
|
13858
|
+
pointerEvents: "none",
|
|
13859
|
+
}, children: text }))] }));
|
|
13860
|
+
}
|
|
13861
|
+
function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
|
|
13692
13862
|
const t = useTranslations();
|
|
13863
|
+
const { locale } = useLocale();
|
|
13864
|
+
const timezone = useTimezone();
|
|
13693
13865
|
// State for details dialog
|
|
13694
13866
|
const [detailsDialogOpen, setDetailsDialogOpen] = React.useState(false);
|
|
13695
13867
|
const [selectedEventTypeForDetails, setSelectedEventTypeForDetails] = React.useState(null);
|
|
@@ -13799,7 +13971,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
13799
13971
|
display: "flex",
|
|
13800
13972
|
flexDirection: "column",
|
|
13801
13973
|
justifyContent: "space-between",
|
|
13802
|
-
height: "
|
|
13974
|
+
height: "490px",
|
|
13803
13975
|
}, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("h2", { className: "event-type-title", style: {
|
|
13804
13976
|
fontSize: "clamp(1.1rem, 2.5vw, 24px)",
|
|
13805
13977
|
fontWeight: 700,
|
|
@@ -13880,7 +14052,43 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
13880
14052
|
color: "var(--bw-text-color)",
|
|
13881
14053
|
fontFamily: "var(--bw-font-family)",
|
|
13882
14054
|
textAlign: "right",
|
|
13883
|
-
}, children: jsxRuntime.jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }),
|
|
14055
|
+
}, children: jsxRuntime.jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), (() => {
|
|
14056
|
+
const preview = eventType.cardPreview ?? [];
|
|
14057
|
+
return (jsxRuntime.jsxs("div", { style: {
|
|
14058
|
+
marginTop: "12px",
|
|
14059
|
+
borderTop: "1px solid var(--bw-border-color)",
|
|
14060
|
+
paddingTop: "8px",
|
|
14061
|
+
marginBottom: "16px",
|
|
14062
|
+
}, children: [jsxRuntime.jsx("div", { style: {
|
|
14063
|
+
fontSize: "11px",
|
|
14064
|
+
fontWeight: 700,
|
|
14065
|
+
color: "var(--bw-text-muted)",
|
|
14066
|
+
textTransform: "uppercase",
|
|
14067
|
+
letterSpacing: "0.05em",
|
|
14068
|
+
marginBottom: "4px",
|
|
14069
|
+
}, children: t("events.previewSectionTitle") }), jsxRuntime.jsx("div", { style: { height: "102px" }, children: Array.from({ length: 3 }).map((_, i) => {
|
|
14070
|
+
const item = preview[i];
|
|
14071
|
+
if (!item)
|
|
14072
|
+
return jsxRuntime.jsx("div", { style: { height: "34px" } }, i);
|
|
14073
|
+
const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
|
|
14074
|
+
return (jsxRuntime.jsxs("div", { onClick: (e) => { e.stopPropagation(); onInstancePreview?.(item.id, eventType.id); }, onMouseEnter: (e) => { if (onInstancePreview)
|
|
14075
|
+
e.currentTarget.style.backgroundColor = "var(--bw-border-color)"; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
|
|
14076
|
+
height: "34px",
|
|
14077
|
+
display: "grid",
|
|
14078
|
+
gridTemplateColumns: "auto 1fr auto 20px",
|
|
14079
|
+
alignItems: "center",
|
|
14080
|
+
gap: "8px",
|
|
14081
|
+
width: "100%",
|
|
14082
|
+
fontSize: "13px",
|
|
14083
|
+
color: "var(--bw-text-muted)",
|
|
14084
|
+
cursor: onInstancePreview ? "pointer" : "default",
|
|
14085
|
+
borderRadius: "4px",
|
|
14086
|
+
padding: "0 2px",
|
|
14087
|
+
transition: "background 0.15s",
|
|
14088
|
+
boxSizing: "border-box",
|
|
14089
|
+
}, children: [jsxRuntime.jsxs("span", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: "3px" }, children: [item.isSpecial && (jsxRuntime.jsx("span", { style: { color: "var(--bw-highlight-color)", fontSize: "11px", lineHeight: 1 }, children: "\u2605" })), formatWeekday(item.startTime, timezone, locale), " ", formatDate(item.startTime, timezone, locale)] }), jsxRuntime.jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, children: item.name }), jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [hasDiscount && (jsxRuntime.jsx("span", { style: { textDecoration: "line-through", opacity: 0.55, fontSize: "11px" }, children: formatCurrency(item.basePrice) })), jsxRuntime.jsx("span", { style: { fontWeight: 700, color: item.isSpecial ? "var(--bw-highlight-color)" : "var(--bw-text-color)" }, children: formatCurrency(item.price) })] }), item.specialDescription ? (jsxRuntime.jsx(InfoBadge, { text: item.specialDescription })) : (jsxRuntime.jsx("span", {}))] }, item.id));
|
|
14090
|
+
}) })] }));
|
|
14091
|
+
})(), jsxRuntime.jsxs("div", { style: {
|
|
13884
14092
|
display: "flex",
|
|
13885
14093
|
justifyContent: "flex-end",
|
|
13886
14094
|
alignItems: "center",
|
|
@@ -13895,7 +14103,6 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
13895
14103
|
backgroundColor: "var(--bw-surface-color)",
|
|
13896
14104
|
padding: "12px",
|
|
13897
14105
|
borderRadius: "var(--bw-border-radius)",
|
|
13898
|
-
fontSize: "clamp(0.8rem, 2vw, 16px)",
|
|
13899
14106
|
fontWeight: 600,
|
|
13900
14107
|
fontFamily: "var(--bw-font-family)",
|
|
13901
14108
|
display: "flex",
|
|
@@ -13908,7 +14115,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
13908
14115
|
}, children: t("button.moreDetails") })), isAvailable && (jsxRuntime.jsxs("div", { style: {
|
|
13909
14116
|
backgroundColor: "var(--bw-highlight-color)",
|
|
13910
14117
|
color: "var(--bw-surface-color)",
|
|
13911
|
-
padding: "12px
|
|
14118
|
+
padding: "12px 14px",
|
|
13912
14119
|
borderRadius: "var(--bw-border-radius)",
|
|
13913
14120
|
fontSize: "clamp(1rem, 2vw, 16px)",
|
|
13914
14121
|
fontWeight: 600,
|
|
@@ -14566,6 +14773,172 @@ function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText
|
|
|
14566
14773
|
}, children: "\u27F3" }), t("common.loading")] })) : (showAllButtonText) }) }))] }));
|
|
14567
14774
|
}
|
|
14568
14775
|
|
|
14776
|
+
function SpecialsView({ specials, onEventSelect, isLoading = false, showSavingsAmount = true, showSavingsPercent = false, emptyStateText, isLoadingEventDetails = false, }) {
|
|
14777
|
+
const t = useTranslations();
|
|
14778
|
+
const { locale } = useLocale();
|
|
14779
|
+
const timezone = useTimezone();
|
|
14780
|
+
const [selectedId, setSelectedId] = React.useState(null);
|
|
14781
|
+
const handleSelect = (id) => {
|
|
14782
|
+
setSelectedId(id);
|
|
14783
|
+
onEventSelect(id);
|
|
14784
|
+
};
|
|
14785
|
+
if (isLoading) {
|
|
14786
|
+
return jsxRuntime.jsx(NextEventsSkeleton, { count: 3 });
|
|
14787
|
+
}
|
|
14788
|
+
if (specials.length === 0) {
|
|
14789
|
+
return (jsxRuntime.jsx("div", { style: { maxWidth: "500px", margin: "0 auto", padding: "16px" }, children: jsxRuntime.jsxs("div", { style: {
|
|
14790
|
+
display: "flex",
|
|
14791
|
+
flexDirection: "column",
|
|
14792
|
+
alignItems: "center",
|
|
14793
|
+
justifyContent: "center",
|
|
14794
|
+
textAlign: "center",
|
|
14795
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
14796
|
+
border: "1px solid var(--bw-border-color)",
|
|
14797
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14798
|
+
padding: "24px",
|
|
14799
|
+
fontFamily: "var(--bw-font-family)",
|
|
14800
|
+
minHeight: "300px",
|
|
14801
|
+
}, children: [jsxRuntime.jsx("div", { style: {
|
|
14802
|
+
display: "flex",
|
|
14803
|
+
alignItems: "center",
|
|
14804
|
+
justifyContent: "center",
|
|
14805
|
+
borderRadius: "50%",
|
|
14806
|
+
width: "64px",
|
|
14807
|
+
height: "64px",
|
|
14808
|
+
backgroundColor: "var(--bw-highlight-color)",
|
|
14809
|
+
marginBottom: "16px",
|
|
14810
|
+
fontSize: "32px",
|
|
14811
|
+
color: "#ffffff",
|
|
14812
|
+
opacity: 0.8,
|
|
14813
|
+
}, children: "\uD83C\uDFF7\uFE0F" }), jsxRuntime.jsx("h3", { style: {
|
|
14814
|
+
fontWeight: 600,
|
|
14815
|
+
margin: "0 0 8px 0",
|
|
14816
|
+
fontSize: "20px",
|
|
14817
|
+
color: "var(--bw-text-color)",
|
|
14818
|
+
fontFamily: "var(--bw-font-family)",
|
|
14819
|
+
}, children: t("specials.noSpecials") }), jsxRuntime.jsx("p", { style: {
|
|
14820
|
+
margin: 0,
|
|
14821
|
+
color: "var(--bw-text-muted)",
|
|
14822
|
+
fontSize: "16px",
|
|
14823
|
+
lineHeight: 1.6,
|
|
14824
|
+
fontFamily: "var(--bw-font-family)",
|
|
14825
|
+
maxWidth: "400px",
|
|
14826
|
+
}, children: emptyStateText ?? t("specials.noSpecialsMessage") })] }) }));
|
|
14827
|
+
}
|
|
14828
|
+
return (jsxRuntime.jsxs("div", { style: {
|
|
14829
|
+
maxWidth: "500px",
|
|
14830
|
+
margin: "0 auto",
|
|
14831
|
+
padding: "16px",
|
|
14832
|
+
fontFamily: "var(--bw-font-family)",
|
|
14833
|
+
}, children: [jsxRuntime.jsxs("div", { style: { textAlign: "center", marginBottom: "24px" }, children: [jsxRuntime.jsx("h2", { style: {
|
|
14834
|
+
fontWeight: 600,
|
|
14835
|
+
margin: "0 0 8px 0",
|
|
14836
|
+
fontSize: "18px",
|
|
14837
|
+
color: "var(--bw-text-color)",
|
|
14838
|
+
fontFamily: "var(--bw-font-family)",
|
|
14839
|
+
}, children: t("specials.title") }), jsxRuntime.jsx("p", { style: {
|
|
14840
|
+
margin: 0,
|
|
14841
|
+
fontSize: "16px",
|
|
14842
|
+
color: "var(--bw-text-muted)",
|
|
14843
|
+
fontFamily: "var(--bw-font-family)",
|
|
14844
|
+
}, children: t("specials.subtitle") })] }), jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: specials.map((special) => {
|
|
14845
|
+
const isFullyBooked = special.availableSpots === 0;
|
|
14846
|
+
const isDisabled = isFullyBooked || !special.bookingOpen;
|
|
14847
|
+
const hasDiscount = special.basePrice > 0 && special.price < special.basePrice;
|
|
14848
|
+
return (jsxRuntime.jsxs("div", { style: {
|
|
14849
|
+
position: "relative",
|
|
14850
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
14851
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14852
|
+
border: "1px solid var(--bw-highlight-color)",
|
|
14853
|
+
overflow: "hidden",
|
|
14854
|
+
opacity: isDisabled ? 0.5 : 1,
|
|
14855
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
14856
|
+
transition: "all 0.2s ease",
|
|
14857
|
+
}, onClick: () => {
|
|
14858
|
+
if (!isDisabled)
|
|
14859
|
+
handleSelect(special.id);
|
|
14860
|
+
}, children: [selectedId === special.id && isLoadingEventDetails && (jsxRuntime.jsx("div", { style: {
|
|
14861
|
+
position: "absolute",
|
|
14862
|
+
inset: 0,
|
|
14863
|
+
display: "flex",
|
|
14864
|
+
alignItems: "center",
|
|
14865
|
+
justifyContent: "center",
|
|
14866
|
+
backgroundColor: "rgba(15, 23, 42, 0.8)",
|
|
14867
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14868
|
+
zIndex: 10,
|
|
14869
|
+
}, children: jsxRuntime.jsx("div", { style: { fontSize: "32px", color: "var(--bw-highlight-color)", animation: "spin 1s linear infinite" }, children: "\u27F3" }) })), jsxRuntime.jsxs("div", { style: { display: "flex", gap: "12px", padding: "12px" }, children: [special.images.length > 0 && (jsxRuntime.jsx("div", { style: {
|
|
14870
|
+
flexShrink: 0,
|
|
14871
|
+
width: "72px",
|
|
14872
|
+
height: "72px",
|
|
14873
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
14874
|
+
overflow: "hidden",
|
|
14875
|
+
}, children: jsxRuntime.jsx("img", { src: special.images[0], alt: special.eventTypeName, style: { width: "100%", height: "100%", objectFit: "cover" } }) })), jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [jsxRuntime.jsx("div", { style: { marginBottom: "4px" }, children: jsxRuntime.jsx("span", { style: {
|
|
14876
|
+
fontSize: "11px",
|
|
14877
|
+
fontWeight: 600,
|
|
14878
|
+
color: "var(--bw-text-muted)",
|
|
14879
|
+
textTransform: "uppercase",
|
|
14880
|
+
letterSpacing: "0.05em",
|
|
14881
|
+
}, children: special.categoryName }) }), jsxRuntime.jsx("h4", { style: {
|
|
14882
|
+
margin: "0 0 2px 0",
|
|
14883
|
+
fontSize: "15px",
|
|
14884
|
+
fontWeight: 600,
|
|
14885
|
+
color: "var(--bw-text-color)",
|
|
14886
|
+
lineHeight: 1.3,
|
|
14887
|
+
overflow: "hidden",
|
|
14888
|
+
textOverflow: "ellipsis",
|
|
14889
|
+
whiteSpace: "nowrap",
|
|
14890
|
+
}, children: special.eventTypeName }), jsxRuntime.jsxs("div", { style: {
|
|
14891
|
+
fontSize: "13px",
|
|
14892
|
+
fontWeight: 500,
|
|
14893
|
+
color: "var(--bw-highlight-color)",
|
|
14894
|
+
marginBottom: "2px",
|
|
14895
|
+
overflow: "hidden",
|
|
14896
|
+
textOverflow: "ellipsis",
|
|
14897
|
+
whiteSpace: "nowrap",
|
|
14898
|
+
}, children: ["\u2605 ", special.name] }), special.specialDescription && (jsxRuntime.jsx("div", { style: {
|
|
14899
|
+
fontSize: "12px",
|
|
14900
|
+
color: "var(--bw-text-muted)",
|
|
14901
|
+
marginBottom: "6px",
|
|
14902
|
+
lineHeight: 1.4,
|
|
14903
|
+
}, children: special.specialDescription })), jsxRuntime.jsxs("div", { style: { fontSize: "13px", color: "var(--bw-text-muted)", marginBottom: "8px" }, children: [formatWeekday(special.startTime, timezone, locale), ",", " ", formatDate(special.startTime, timezone, locale)] }), jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }, children: [hasDiscount && (jsxRuntime.jsx("span", { style: {
|
|
14904
|
+
fontSize: "13px",
|
|
14905
|
+
color: "var(--bw-text-muted)",
|
|
14906
|
+
textDecoration: "line-through",
|
|
14907
|
+
}, children: formatCurrency(special.basePrice) })), jsxRuntime.jsx("span", { style: {
|
|
14908
|
+
fontSize: "16px",
|
|
14909
|
+
fontWeight: 700,
|
|
14910
|
+
color: "var(--bw-highlight-color)",
|
|
14911
|
+
}, children: formatCurrency(special.price) }), hasDiscount && showSavingsAmount && (jsxRuntime.jsx("span", { style: {
|
|
14912
|
+
fontSize: "12px",
|
|
14913
|
+
fontWeight: 600,
|
|
14914
|
+
color: "#ffffff",
|
|
14915
|
+
backgroundColor: "var(--bw-success-color, #22c55e)",
|
|
14916
|
+
borderRadius: "4px",
|
|
14917
|
+
padding: "2px 6px",
|
|
14918
|
+
}, children: t("specials.save").replace("{{amount}}", formatCurrency(special.savings)) })), hasDiscount && showSavingsPercent && (jsxRuntime.jsx("span", { style: {
|
|
14919
|
+
fontSize: "12px",
|
|
14920
|
+
fontWeight: 600,
|
|
14921
|
+
color: "#ffffff",
|
|
14922
|
+
backgroundColor: "var(--bw-success-color, #22c55e)",
|
|
14923
|
+
borderRadius: "4px",
|
|
14924
|
+
padding: "2px 6px",
|
|
14925
|
+
}, children: t("specials.savePercent").replace("{{percent}}", String(special.savingsPercent)) }))] })] })] }), jsxRuntime.jsxs("div", { style: {
|
|
14926
|
+
borderTop: "1px solid var(--bw-border-color)",
|
|
14927
|
+
padding: "8px 12px",
|
|
14928
|
+
display: "flex",
|
|
14929
|
+
justifyContent: "space-between",
|
|
14930
|
+
alignItems: "center",
|
|
14931
|
+
backgroundColor: "var(--bw-background-color)",
|
|
14932
|
+
}, children: [jsxRuntime.jsx("span", { style: { fontSize: "12px", color: "var(--bw-text-muted)" }, children: isFullyBooked
|
|
14933
|
+
? t("common.fullyBooked")
|
|
14934
|
+
: t("specials.spotsLeft").replace("{{count}}", String(special.availableSpots)) }), jsxRuntime.jsxs("span", { style: {
|
|
14935
|
+
fontSize: "13px",
|
|
14936
|
+
fontWeight: 600,
|
|
14937
|
+
color: "var(--bw-highlight-color)",
|
|
14938
|
+
}, children: [t("specials.bookNow"), " \u2192"] })] })] }, special.id));
|
|
14939
|
+
}) })] }));
|
|
14940
|
+
}
|
|
14941
|
+
|
|
14569
14942
|
const getThemeConfig = (theme = "generic") => {
|
|
14570
14943
|
switch (theme) {
|
|
14571
14944
|
case "christmas":
|
|
@@ -14825,8 +15198,8 @@ const cardDisabledStyles = {
|
|
|
14825
15198
|
};
|
|
14826
15199
|
const checkboxContainerStyles = {
|
|
14827
15200
|
position: "absolute",
|
|
14828
|
-
|
|
14829
|
-
|
|
15201
|
+
bottom: "12px",
|
|
15202
|
+
left: "12px",
|
|
14830
15203
|
zIndex: 1,
|
|
14831
15204
|
};
|
|
14832
15205
|
const checkboxInnerStyles = {
|
|
@@ -14921,6 +15294,7 @@ const priceContainerStyles = {
|
|
|
14921
15294
|
alignItems: "flex-end",
|
|
14922
15295
|
marginTop: "8px",
|
|
14923
15296
|
paddingTop: "8px",
|
|
15297
|
+
paddingBottom: "0px",
|
|
14924
15298
|
borderTop: "1px solid var(--bw-border-color)",
|
|
14925
15299
|
};
|
|
14926
15300
|
const pricePerPersonStyles = {
|
|
@@ -15012,7 +15386,7 @@ function UpsellsStep({ upsells, selectedUpsells, participantCount, isLoading, is
|
|
|
15012
15386
|
};
|
|
15013
15387
|
const selectedTotal = calculateTotal();
|
|
15014
15388
|
const selectedCount = selectedUpsells.length;
|
|
15015
|
-
const footerContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), children: t("common.back") }), jsxRuntime.jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
|
|
15389
|
+
const footerContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), className: buttonClassName, children: t("common.back") }), jsxRuntime.jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), className: buttonClassName, children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
|
|
15016
15390
|
return (jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: onClose, title: t("upsells.title"), footer: footerContent, children: jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", padding: "16px 16px" }, children: [isLoading && (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "12px", padding: "40px 20px", ...textStyles.muted }, children: [spinner(), jsxRuntime.jsx("span", { children: t("upsells.loading") })] })), !isLoading && upsells.length === 0 && (jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "40px 20px", ...textStyles.muted }, children: jsxRuntime.jsx("p", { children: t("upsells.noExtras") }) })), !isLoading && upsells.length > 0 && (jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", flex: 1, overflowY: "auto", paddingBottom: "16px" }, children: upsells.map((upsell) => (jsxRuntime.jsx(UpsellCard, { upsell: upsell, isSelected: isSelected(upsell.id), participantCount: participantCount, onSelect: () => selectUpsell(upsell.id) }, upsell.id))) })), selectedCount > 0 && (jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: "16px", paddingBottom: "16px", paddingTop: "16px", borderTop: "1px solid var(--bw-border-color)", fontSize: "14px" }, children: [jsxRuntime.jsx("span", { style: textStyles.muted, children: selectedCount === 1 ? t("upsells.selected", { count: selectedCount }) : t("upsells.selectedPlural", { count: selectedCount }) }), jsxRuntime.jsxs("span", { style: { fontWeight: 600, color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: ["+", formatCurrency(selectedTotal)] })] }))] }) }));
|
|
15017
15391
|
}
|
|
15018
15392
|
|
|
@@ -15060,6 +15434,8 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15060
15434
|
: (config.voucherIntegration ?? (hasEventSelection && !isDirectInstanceMode));
|
|
15061
15435
|
// Selection flow state
|
|
15062
15436
|
const [currentStep, setCurrentStep] = React.useState("eventTypes");
|
|
15437
|
+
// Tracks where to return when closing the booking form
|
|
15438
|
+
const bookingReturnStep = React.useRef("eventInstances");
|
|
15063
15439
|
const [eventTypes, setEventTypes] = React.useState([]);
|
|
15064
15440
|
const [selectedEventType, setSelectedEventType] = React.useState(null);
|
|
15065
15441
|
const [eventInstances, setEventInstances] = React.useState([]);
|
|
@@ -15072,6 +15448,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15072
15448
|
// State for upcoming events (next-events view mode)
|
|
15073
15449
|
const [upcomingEvents, setUpcomingEvents] = React.useState([]);
|
|
15074
15450
|
const [showingPreview, setShowingPreview] = React.useState(true);
|
|
15451
|
+
// State for specials view mode
|
|
15452
|
+
const [specials, setSpecials] = React.useState([]);
|
|
15453
|
+
const [isLoadingSpecials, setIsLoadingSpecials] = React.useState(false);
|
|
15075
15454
|
// New: sidebar open state for single event type mode
|
|
15076
15455
|
const [sidebarOpen, setSidebarOpen] = React.useState(false);
|
|
15077
15456
|
// Booking flow state
|
|
@@ -15222,6 +15601,11 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15222
15601
|
await loadUpcomingEvents();
|
|
15223
15602
|
return;
|
|
15224
15603
|
}
|
|
15604
|
+
// Specials view mode: load special offers
|
|
15605
|
+
if (viewMode === "specials") {
|
|
15606
|
+
await loadSpecials();
|
|
15607
|
+
return;
|
|
15608
|
+
}
|
|
15225
15609
|
// Single event type mode: load event type and instances, but don't open sidebar yet
|
|
15226
15610
|
if (isSingleEventTypeMode) {
|
|
15227
15611
|
await loadEventTypes();
|
|
@@ -15445,6 +15829,45 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15445
15829
|
setError(data.error || t("error.loadUpcomingEvents"));
|
|
15446
15830
|
}
|
|
15447
15831
|
};
|
|
15832
|
+
const loadSpecials = async () => {
|
|
15833
|
+
setIsLoadingSpecials(true);
|
|
15834
|
+
const specialsSettings = config.specialsSettings ?? {};
|
|
15835
|
+
const requestBody = {
|
|
15836
|
+
organizationId: config.organizationId,
|
|
15837
|
+
limit: specialsSettings.count ?? 20,
|
|
15838
|
+
};
|
|
15839
|
+
if (config.categoryId) {
|
|
15840
|
+
requestBody.categoryId = config.categoryId;
|
|
15841
|
+
}
|
|
15842
|
+
else if (config.eventTypeIds) {
|
|
15843
|
+
requestBody.eventTypeIds = config.eventTypeIds;
|
|
15844
|
+
}
|
|
15845
|
+
else if (config.eventTypeId) {
|
|
15846
|
+
requestBody.eventTypeId = config.eventTypeId;
|
|
15847
|
+
}
|
|
15848
|
+
try {
|
|
15849
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/specials"), {
|
|
15850
|
+
method: "POST",
|
|
15851
|
+
headers: createApiHeaders(config, locale),
|
|
15852
|
+
body: JSON.stringify(requestBody),
|
|
15853
|
+
});
|
|
15854
|
+
const data = await response.json();
|
|
15855
|
+
if (response.ok) {
|
|
15856
|
+
const wl = extractWidgetLanguagePayload(data);
|
|
15857
|
+
if (wl) {
|
|
15858
|
+
onWidgetLanguage?.(wl);
|
|
15859
|
+
onTimezone?.(wl.timezone);
|
|
15860
|
+
}
|
|
15861
|
+
setSpecials(data.specials || []);
|
|
15862
|
+
}
|
|
15863
|
+
else {
|
|
15864
|
+
setError(data.error || t("error.loadUpcomingEvents"));
|
|
15865
|
+
}
|
|
15866
|
+
}
|
|
15867
|
+
finally {
|
|
15868
|
+
setIsLoadingSpecials(false);
|
|
15869
|
+
}
|
|
15870
|
+
};
|
|
15448
15871
|
const loadEventInstances = async (eventTypeId) => {
|
|
15449
15872
|
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/event-instances"), {
|
|
15450
15873
|
method: "POST",
|
|
@@ -15629,6 +16052,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15629
16052
|
// Event instance selection handlers
|
|
15630
16053
|
const handleEventInstanceSelect = async (eventInstance) => {
|
|
15631
16054
|
setSelectedEventInstance(eventInstance);
|
|
16055
|
+
bookingReturnStep.current = "eventInstances";
|
|
15632
16056
|
// Set default participant count for upsell calculations
|
|
15633
16057
|
const defaultParticipantCount = 1;
|
|
15634
16058
|
setTempParticipantCount(defaultParticipantCount);
|
|
@@ -15641,7 +16065,14 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15641
16065
|
if (availableUpsells.length > 0) {
|
|
15642
16066
|
// Show upsells step
|
|
15643
16067
|
setUpsells(availableUpsells);
|
|
15644
|
-
|
|
16068
|
+
// Pre-select default-checked upsells
|
|
16069
|
+
const defaultSelections = availableUpsells
|
|
16070
|
+
.filter((upsell) => upsell.defaultChecked && upsell.available)
|
|
16071
|
+
.map((upsell) => ({
|
|
16072
|
+
upsellPackageId: upsell.id,
|
|
16073
|
+
quantity: defaultParticipantCount,
|
|
16074
|
+
}));
|
|
16075
|
+
setSelectedUpsells(defaultSelections);
|
|
15645
16076
|
setCurrentStep("upsells");
|
|
15646
16077
|
setIsLoadingUpsells(false);
|
|
15647
16078
|
return; // Don't proceed to booking yet
|
|
@@ -15672,7 +16103,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15672
16103
|
setEventInstances([]);
|
|
15673
16104
|
};
|
|
15674
16105
|
const handleBackToEventInstances = () => {
|
|
15675
|
-
setCurrentStep(
|
|
16106
|
+
setCurrentStep(bookingReturnStep.current);
|
|
15676
16107
|
setSelectedEventInstance(null);
|
|
15677
16108
|
setEventDetails(null);
|
|
15678
16109
|
};
|
|
@@ -15706,8 +16137,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15706
16137
|
}
|
|
15707
16138
|
};
|
|
15708
16139
|
const handleUpsellsBack = () => {
|
|
15709
|
-
|
|
15710
|
-
setCurrentStep("eventInstances");
|
|
16140
|
+
setCurrentStep(bookingReturnStep.current);
|
|
15711
16141
|
setSelectedUpsells([]);
|
|
15712
16142
|
setUpsells([]);
|
|
15713
16143
|
};
|
|
@@ -15742,10 +16172,36 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15742
16172
|
setError(errorMessage);
|
|
15743
16173
|
config.onError?.(errorMessage);
|
|
15744
16174
|
};
|
|
15745
|
-
const handleUpcomingEventSelect = async (eventInstanceId) => {
|
|
16175
|
+
const handleUpcomingEventSelect = async (eventInstanceId, eventTypeId) => {
|
|
16176
|
+
// Resolve the event type — may come from card preview (eventTypeId provided) or
|
|
16177
|
+
// from the next-events list where selectedEventType is already set
|
|
16178
|
+
const resolvedEventType = eventTypeId != null
|
|
16179
|
+
? (eventTypes.find((et) => et.id === eventTypeId) ?? selectedEventType)
|
|
16180
|
+
: selectedEventType;
|
|
16181
|
+
if (resolvedEventType && resolvedEventType !== selectedEventType) {
|
|
16182
|
+
setSelectedEventType(resolvedEventType);
|
|
16183
|
+
}
|
|
16184
|
+
// Check if this is coming from a card preview (eventTypeId was provided)
|
|
16185
|
+
// In that case, we need to load event instances so back navigation works properly
|
|
16186
|
+
const isFromCardPreview = eventTypeId != null;
|
|
16187
|
+
if (isFromCardPreview && resolvedEventType) {
|
|
16188
|
+
// Load event instances in background so back navigation shows instances
|
|
16189
|
+
setShouldRenderInstanceSelection(true);
|
|
16190
|
+
void loadEventInstances(resolvedEventType.id);
|
|
16191
|
+
// Record that we should return to instance selection (not event types)
|
|
16192
|
+
bookingReturnStep.current = "eventInstances";
|
|
16193
|
+
}
|
|
16194
|
+
else {
|
|
16195
|
+
// Record where to return when the booking form is closed
|
|
16196
|
+
bookingReturnStep.current = currentStep === "eventInstances" ? "eventInstances" : "eventTypes";
|
|
16197
|
+
}
|
|
16198
|
+
// First try to find the event in upcomingEvents (for next-events view mode)
|
|
15746
16199
|
const upcomingEvent = upcomingEvents.find((event) => event.id === eventInstanceId);
|
|
15747
|
-
|
|
15748
|
-
|
|
16200
|
+
// If not found in upcomingEvents, try to find in card preview items
|
|
16201
|
+
const cardPreviewItem = !upcomingEvent && resolvedEventType?.cardPreview?.find((item) => item.id === eventInstanceId);
|
|
16202
|
+
// Build the event instance from either source
|
|
16203
|
+
const eventInstance = upcomingEvent
|
|
16204
|
+
? {
|
|
15749
16205
|
id: upcomingEvent.id,
|
|
15750
16206
|
name: upcomingEvent.name,
|
|
15751
16207
|
startTime: upcomingEvent.startTime,
|
|
@@ -15759,13 +16215,63 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15759
16215
|
bookingOpen: upcomingEvent.bookingOpen,
|
|
15760
16216
|
...(upcomingEvent.deposit !== undefined && { deposit: upcomingEvent.deposit }),
|
|
15761
16217
|
...(upcomingEvent.notes !== undefined && { notes: upcomingEvent.notes }),
|
|
15762
|
-
}
|
|
16218
|
+
}
|
|
16219
|
+
: cardPreviewItem
|
|
16220
|
+
? {
|
|
16221
|
+
id: cardPreviewItem.id,
|
|
16222
|
+
name: cardPreviewItem.name,
|
|
16223
|
+
startTime: cardPreviewItem.startTime,
|
|
16224
|
+
endTime: cardPreviewItem.startTime,
|
|
16225
|
+
price: cardPreviewItem.price,
|
|
16226
|
+
maxParticipants: cardPreviewItem.availableSpots + 1,
|
|
16227
|
+
participantCount: 1,
|
|
16228
|
+
availableSpots: cardPreviewItem.availableSpots,
|
|
16229
|
+
durationDays: 1,
|
|
16230
|
+
durationPerDay: 1,
|
|
16231
|
+
bookingOpen: true,
|
|
16232
|
+
}
|
|
16233
|
+
: null;
|
|
16234
|
+
if (eventInstance) {
|
|
15763
16235
|
setSelectedEventInstance(eventInstance);
|
|
15764
16236
|
}
|
|
16237
|
+
setError(null);
|
|
16238
|
+
// Check for upsells before going to booking (same as handleEventInstanceSelect)
|
|
16239
|
+
const eventTypeForUpsells = resolvedEventType;
|
|
16240
|
+
if (eventTypeForUpsells) {
|
|
16241
|
+
const defaultParticipantCount = 1;
|
|
16242
|
+
setTempParticipantCount(defaultParticipantCount);
|
|
16243
|
+
setIsLoadingUpsells(true);
|
|
16244
|
+
setShouldRenderUpsells(true);
|
|
16245
|
+
try {
|
|
16246
|
+
const availableUpsells = await loadUpsells(eventTypeForUpsells.id, eventInstanceId, defaultParticipantCount);
|
|
16247
|
+
if (availableUpsells.length > 0) {
|
|
16248
|
+
setUpsells(availableUpsells);
|
|
16249
|
+
// Pre-select default-checked upsells
|
|
16250
|
+
const defaultSelections = availableUpsells
|
|
16251
|
+
.filter((upsell) => upsell.defaultChecked && upsell.available)
|
|
16252
|
+
.map((upsell) => ({
|
|
16253
|
+
upsellPackageId: upsell.id,
|
|
16254
|
+
quantity: defaultParticipantCount,
|
|
16255
|
+
}));
|
|
16256
|
+
setSelectedUpsells(defaultSelections);
|
|
16257
|
+
setCurrentStep("upsells");
|
|
16258
|
+
setIsLoadingUpsells(false);
|
|
16259
|
+
// Load event details in background for when user continues past upsells
|
|
16260
|
+
void loadEventDetails(eventInstanceId);
|
|
16261
|
+
return;
|
|
16262
|
+
}
|
|
16263
|
+
}
|
|
16264
|
+
catch (err) {
|
|
16265
|
+
console.error("Error loading upsells:", err);
|
|
16266
|
+
}
|
|
16267
|
+
finally {
|
|
16268
|
+
setIsLoadingUpsells(false);
|
|
16269
|
+
}
|
|
16270
|
+
}
|
|
16271
|
+
// No upsells — go directly to booking
|
|
15765
16272
|
setCurrentStep("booking");
|
|
15766
16273
|
setShouldRenderBookingForm(true);
|
|
15767
16274
|
setIsLoadingEventDetails(true);
|
|
15768
|
-
setError(null);
|
|
15769
16275
|
try {
|
|
15770
16276
|
await loadEventDetails(eventInstanceId);
|
|
15771
16277
|
}
|
|
@@ -15922,6 +16428,31 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15922
16428
|
window.history.replaceState({}, "", url.toString());
|
|
15923
16429
|
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
15924
16430
|
}
|
|
16431
|
+
if (viewMode === "specials" && showingPreview) {
|
|
16432
|
+
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
|
|
16433
|
+
setCurrentStep("eventTypes");
|
|
16434
|
+
setShowingPreview(true);
|
|
16435
|
+
setEventDetails(null);
|
|
16436
|
+
}, onBackToEventTypes: () => {
|
|
16437
|
+
setCurrentStep("eventTypes");
|
|
16438
|
+
setShowingPreview(true);
|
|
16439
|
+
setEventDetails(null);
|
|
16440
|
+
}, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => {
|
|
16441
|
+
setCurrentStep("eventTypes");
|
|
16442
|
+
setShowingPreview(true);
|
|
16443
|
+
setEventDetails(null);
|
|
16444
|
+
}, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
16445
|
+
setIsSuccess(false);
|
|
16446
|
+
setCurrentStep("eventTypes");
|
|
16447
|
+
setShowingPreview(true);
|
|
16448
|
+
setSuccessPaymentId(null);
|
|
16449
|
+
setShouldRenderInstanceSelection(false);
|
|
16450
|
+
setShouldRenderUpsells(false);
|
|
16451
|
+
setShouldRenderBookingForm(false);
|
|
16452
|
+
setSelectedUpsells([]);
|
|
16453
|
+
setUpsells([]);
|
|
16454
|
+
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16455
|
+
}
|
|
15925
16456
|
if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
|
|
15926
16457
|
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
|
|
15927
16458
|
setShowingPreview(true);
|
|
@@ -15992,7 +16523,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15992
16523
|
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
15993
16524
|
}
|
|
15994
16525
|
// Cards mode (default) - show event type selection with optional voucher card
|
|
15995
|
-
const cardsView = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasEventSelection && (jsxRuntime.jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (jsxRuntime.jsx(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 && (jsxRuntime.jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsxRuntime.jsx("div", { style: {
|
|
16526
|
+
const cardsView = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasEventSelection && (jsxRuntime.jsx(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 && (jsxRuntime.jsx(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 && (jsxRuntime.jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsxRuntime.jsx("div", { style: {
|
|
15996
16527
|
display: "inline-block",
|
|
15997
16528
|
width: "32px",
|
|
15998
16529
|
height: "32px",
|
|
@@ -16060,11 +16591,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16060
16591
|
function UniversalBookingWidget(props) {
|
|
16061
16592
|
const [languagePolicy, setLanguagePolicy] = React.useState(null);
|
|
16062
16593
|
const [orgTimezone, setOrgTimezone] = React.useState("Europe/Berlin");
|
|
16063
|
-
const
|
|
16064
|
-
? languagePolicy.organizationLocale
|
|
16065
|
-
: undefined;
|
|
16066
|
-
const i18nConfigLocale = serverLockedLocale !== undefined ? serverLockedLocale : props.config.locale;
|
|
16067
|
-
const providerProps = i18nConfigLocale ? { configLocale: i18nConfigLocale } : {};
|
|
16594
|
+
const providerProps = props.config.locale ? { configLocale: props.config.locale } : {};
|
|
16068
16595
|
const showLanguagePicker = props.config.showLanguagePicker !== false &&
|
|
16069
16596
|
(languagePolicy === null || languagePolicy.multiLanguageEnabled);
|
|
16070
16597
|
return (jsxRuntime.jsx(I18nProvider, { ...providerProps, children: jsxRuntime.jsx(ShowLanguagePickerProvider, { value: showLanguagePicker, children: jsxRuntime.jsx(TimezoneProvider, { value: orgTimezone, children: jsxRuntime.jsx(UniversalBookingWidgetInner, { ...props, onWidgetLanguage: setLanguagePolicy, onTimezone: setOrgTimezone }) }) }) }));
|
|
@@ -16097,7 +16624,7 @@ function styleInject(css, ref) {
|
|
|
16097
16624
|
}
|
|
16098
16625
|
}
|
|
16099
16626
|
|
|
16100
|
-
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%}@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}}";
|
|
16627
|
+
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{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}}";
|
|
16101
16628
|
styleInject(css_248z);
|
|
16102
16629
|
|
|
16103
16630
|
// Export init function for vanilla JS usage
|