@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.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import React__default, { createContext, useState,
|
|
2
|
+
import React__default, { createContext, useState, useCallback, useMemo, useContext, useEffect, forwardRef, useRef, Fragment as Fragment$1 } from 'react';
|
|
3
3
|
import { createRoot } from 'react-dom/client';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import ReactDOM, { createPortal } from 'react-dom';
|
|
@@ -233,6 +233,7 @@ const de$1 = {
|
|
|
233
233
|
"events.soldOut": "Ausgebucht",
|
|
234
234
|
"events.availableFrom": "Freie Plätze ab {{date}}",
|
|
235
235
|
"events.noAvailableDates": "Keine Termine frei",
|
|
236
|
+
"events.previewSectionTitle": "Specials & nächste Termine",
|
|
236
237
|
// Event instances
|
|
237
238
|
"instances.title": "Terminauswahl",
|
|
238
239
|
"instances.noAvailable": "Keine verfügbaren Termine",
|
|
@@ -249,6 +250,15 @@ const de$1 = {
|
|
|
249
250
|
"nextEvents.noUpcomingMessage": "Aktuell sind keine Termine verfügbar. Bitte schaue später noch einmal vorbei oder kontaktiere uns direkt.",
|
|
250
251
|
"nextEvents.showAll": "Alle Events anzeigen",
|
|
251
252
|
"nextEvents.priceOnRequest": "Preis auf Anfrage",
|
|
253
|
+
// Specials view
|
|
254
|
+
"specials.title": "Sonderangebote",
|
|
255
|
+
"specials.subtitle": "Unsere aktuellen Angebote auf einen Blick",
|
|
256
|
+
"specials.noSpecials": "Keine Sonderangebote",
|
|
257
|
+
"specials.noSpecialsMessage": "Derzeit sind keine Sonderangebote verfügbar. Bitte schaue später noch einmal vorbei.",
|
|
258
|
+
"specials.save": "Spare {{amount}}",
|
|
259
|
+
"specials.savePercent": "{{percent}}% Rabatt",
|
|
260
|
+
"specials.spotsLeft": "Noch {{count}} Plätze frei",
|
|
261
|
+
"specials.bookNow": "Jetzt buchen",
|
|
252
262
|
// Booking form
|
|
253
263
|
"booking.title": "Buchung - {{name}}",
|
|
254
264
|
"booking.notPossible": "Buchung nicht möglich",
|
|
@@ -270,6 +280,8 @@ const de$1 = {
|
|
|
270
280
|
"booking.participantName": "Name *",
|
|
271
281
|
"booking.participantNamePlaceholder": "Teilnehmername",
|
|
272
282
|
"booking.participantAge": "Alter",
|
|
283
|
+
"booking.participantLevel": "Level",
|
|
284
|
+
"booking.participantLevelPlaceholder": "Level wählen...",
|
|
273
285
|
"booking.addParticipant": "{{number}}. Teilnehmer hinzufügen",
|
|
274
286
|
"booking.maxParticipants": "Maximale Anzahl an Teilnehmern erreicht. Es sind nur noch {{count}} Plätze verfügbar.",
|
|
275
287
|
"booking.maxSpotsReached": "Maximal {{count}} Plätze verfügbar.",
|
|
@@ -418,7 +430,11 @@ const de$1 = {
|
|
|
418
430
|
"validation.emailInvalid": "Ungültiges E-Mail-Format",
|
|
419
431
|
"validation.emailDomainInvalid": "Ungültige E-Mail-Domain",
|
|
420
432
|
"validation.participantRequired": "Mindestens ein Teilnehmer erforderlich",
|
|
433
|
+
"validation.ageRequired": "Alter ist erforderlich",
|
|
434
|
+
"validation.levelRequired": "Bitte ein Level auswählen",
|
|
421
435
|
"validation.acceptTerms": "Bitte akzeptiere die Allgemeinen Geschäftsbedingungen",
|
|
436
|
+
"level.beginner": "Anfänger",
|
|
437
|
+
"level.advanced": "Fortgeschritten",
|
|
422
438
|
// Sidebar
|
|
423
439
|
"sidebar.close": "Schließen",
|
|
424
440
|
// Promo
|
|
@@ -493,6 +509,7 @@ const en = {
|
|
|
493
509
|
"events.soldOut": "Sold out",
|
|
494
510
|
"events.availableFrom": "Available from {{date}}",
|
|
495
511
|
"events.noAvailableDates": "No dates available",
|
|
512
|
+
"events.previewSectionTitle": "Specials & upcoming dates",
|
|
496
513
|
// Event instances
|
|
497
514
|
"instances.title": "Select a date",
|
|
498
515
|
"instances.noAvailable": "No available dates",
|
|
@@ -509,6 +526,15 @@ const en = {
|
|
|
509
526
|
"nextEvents.noUpcomingMessage": "There are currently no dates available. Please check back later or contact us directly.",
|
|
510
527
|
"nextEvents.showAll": "Show all events",
|
|
511
528
|
"nextEvents.priceOnRequest": "Price on request",
|
|
529
|
+
// Specials view
|
|
530
|
+
"specials.title": "Special Offers",
|
|
531
|
+
"specials.subtitle": "Our current deals at a glance",
|
|
532
|
+
"specials.noSpecials": "No special offers",
|
|
533
|
+
"specials.noSpecialsMessage": "There are currently no special offers available. Please check back later.",
|
|
534
|
+
"specials.save": "Save {{amount}}",
|
|
535
|
+
"specials.savePercent": "{{percent}}% off",
|
|
536
|
+
"specials.spotsLeft": "{{count}} spots left",
|
|
537
|
+
"specials.bookNow": "Book now",
|
|
512
538
|
// Booking form
|
|
513
539
|
"booking.title": "Booking - {{name}}",
|
|
514
540
|
"booking.notPossible": "Booking not possible",
|
|
@@ -530,6 +556,8 @@ const en = {
|
|
|
530
556
|
"booking.participantName": "Name *",
|
|
531
557
|
"booking.participantNamePlaceholder": "Participant name",
|
|
532
558
|
"booking.participantAge": "Age",
|
|
559
|
+
"booking.participantLevel": "Level",
|
|
560
|
+
"booking.participantLevelPlaceholder": "Select level...",
|
|
533
561
|
"booking.addParticipant": "Add participant {{number}}",
|
|
534
562
|
"booking.maxParticipants": "Maximum number of participants reached. Only {{count}} spots are available.",
|
|
535
563
|
"booking.maxSpotsReached": "Maximum {{count}} spots available.",
|
|
@@ -678,7 +706,11 @@ const en = {
|
|
|
678
706
|
"validation.emailInvalid": "Invalid email format",
|
|
679
707
|
"validation.emailDomainInvalid": "Invalid email domain",
|
|
680
708
|
"validation.participantRequired": "At least one participant is required",
|
|
709
|
+
"validation.ageRequired": "Age is required",
|
|
710
|
+
"validation.levelRequired": "Please select a level",
|
|
681
711
|
"validation.acceptTerms": "Please accept the terms and conditions",
|
|
712
|
+
"level.beginner": "Beginner",
|
|
713
|
+
"level.advanced": "Advanced",
|
|
682
714
|
// Sidebar
|
|
683
715
|
"sidebar.close": "Close",
|
|
684
716
|
// Promo
|
|
@@ -753,6 +785,7 @@ const es = {
|
|
|
753
785
|
"events.soldOut": "Agotado",
|
|
754
786
|
"events.availableFrom": "Disponible desde {{date}}",
|
|
755
787
|
"events.noAvailableDates": "Sin fechas disponibles",
|
|
788
|
+
"events.previewSectionTitle": "Especiales & próximas fechas",
|
|
756
789
|
// Event instances
|
|
757
790
|
"instances.title": "Seleccionar fecha",
|
|
758
791
|
"instances.noAvailable": "Sin fechas disponibles",
|
|
@@ -769,6 +802,15 @@ const es = {
|
|
|
769
802
|
"nextEvents.noUpcomingMessage": "Actualmente no hay fechas disponibles. Por favor, vuelve más tarde o contáctanos directamente.",
|
|
770
803
|
"nextEvents.showAll": "Mostrar todos los eventos",
|
|
771
804
|
"nextEvents.priceOnRequest": "Precio bajo consulta",
|
|
805
|
+
// Specials view
|
|
806
|
+
"specials.title": "Ofertas especiales",
|
|
807
|
+
"specials.subtitle": "Nuestras ofertas actuales de un vistazo",
|
|
808
|
+
"specials.noSpecials": "Sin ofertas especiales",
|
|
809
|
+
"specials.noSpecialsMessage": "Actualmente no hay ofertas especiales disponibles. Vuelve más tarde.",
|
|
810
|
+
"specials.save": "Ahorra {{amount}}",
|
|
811
|
+
"specials.savePercent": "{{percent}}% de descuento",
|
|
812
|
+
"specials.spotsLeft": "{{count}} plazas disponibles",
|
|
813
|
+
"specials.bookNow": "Reservar ahora",
|
|
772
814
|
// Booking form
|
|
773
815
|
"booking.title": "Reserva - {{name}}",
|
|
774
816
|
"booking.notPossible": "Reserva no posible",
|
|
@@ -790,6 +832,8 @@ const es = {
|
|
|
790
832
|
"booking.participantName": "Nombre *",
|
|
791
833
|
"booking.participantNamePlaceholder": "Nombre del participante",
|
|
792
834
|
"booking.participantAge": "Edad",
|
|
835
|
+
"booking.participantLevel": "Nivel",
|
|
836
|
+
"booking.participantLevelPlaceholder": "Seleccionar nivel...",
|
|
793
837
|
"booking.addParticipant": "Añadir participante {{number}}",
|
|
794
838
|
"booking.maxParticipants": "Número máximo de participantes alcanzado. Solo quedan {{count}} plazas disponibles.",
|
|
795
839
|
"booking.maxSpotsReached": "Máximo {{count}} plazas disponibles.",
|
|
@@ -938,7 +982,11 @@ const es = {
|
|
|
938
982
|
"validation.emailInvalid": "Formato de correo electrónico inválido",
|
|
939
983
|
"validation.emailDomainInvalid": "Dominio de correo electrónico inválido",
|
|
940
984
|
"validation.participantRequired": "Se requiere al menos un participante",
|
|
985
|
+
"validation.ageRequired": "La edad es obligatoria",
|
|
986
|
+
"validation.levelRequired": "Selecciona un nivel",
|
|
941
987
|
"validation.acceptTerms": "Por favor, acepta los términos y condiciones",
|
|
988
|
+
"level.beginner": "Principiante",
|
|
989
|
+
"level.advanced": "Avanzado",
|
|
942
990
|
// Sidebar
|
|
943
991
|
"sidebar.close": "Cerrar",
|
|
944
992
|
// Promo
|
|
@@ -1013,6 +1061,7 @@ const pt = {
|
|
|
1013
1061
|
"events.soldOut": "Esgotado",
|
|
1014
1062
|
"events.availableFrom": "Disponível a partir de {{date}}",
|
|
1015
1063
|
"events.noAvailableDates": "Sem datas disponíveis",
|
|
1064
|
+
"events.previewSectionTitle": "Especiais & próximas datas",
|
|
1016
1065
|
// Event instances
|
|
1017
1066
|
"instances.title": "Selecionar data",
|
|
1018
1067
|
"instances.noAvailable": "Sem datas disponíveis",
|
|
@@ -1029,6 +1078,15 @@ const pt = {
|
|
|
1029
1078
|
"nextEvents.noUpcomingMessage": "Atualmente não há datas disponíveis. Por favor, volte mais tarde ou contacte-nos diretamente.",
|
|
1030
1079
|
"nextEvents.showAll": "Mostrar todos os eventos",
|
|
1031
1080
|
"nextEvents.priceOnRequest": "Preço sob consulta",
|
|
1081
|
+
// Specials view
|
|
1082
|
+
"specials.title": "Ofertas especiais",
|
|
1083
|
+
"specials.subtitle": "As nossas ofertas atuais num relance",
|
|
1084
|
+
"specials.noSpecials": "Sem ofertas especiais",
|
|
1085
|
+
"specials.noSpecialsMessage": "Atualmente não há ofertas especiais disponíveis. Por favor, volte mais tarde.",
|
|
1086
|
+
"specials.save": "Poupe {{amount}}",
|
|
1087
|
+
"specials.savePercent": "{{percent}}% de desconto",
|
|
1088
|
+
"specials.spotsLeft": "{{count}} lugares disponíveis",
|
|
1089
|
+
"specials.bookNow": "Reservar agora",
|
|
1032
1090
|
// Booking form
|
|
1033
1091
|
"booking.title": "Reserva - {{name}}",
|
|
1034
1092
|
"booking.notPossible": "Reserva não possível",
|
|
@@ -1050,6 +1108,8 @@ const pt = {
|
|
|
1050
1108
|
"booking.participantName": "Nome *",
|
|
1051
1109
|
"booking.participantNamePlaceholder": "Nome do participante",
|
|
1052
1110
|
"booking.participantAge": "Idade",
|
|
1111
|
+
"booking.participantLevel": "Nível",
|
|
1112
|
+
"booking.participantLevelPlaceholder": "Selecionar nível...",
|
|
1053
1113
|
"booking.addParticipant": "Adicionar participante {{number}}",
|
|
1054
1114
|
"booking.maxParticipants": "Número máximo de participantes atingido. Apenas {{count}} lugares disponíveis.",
|
|
1055
1115
|
"booking.maxSpotsReached": "Máximo {{count}} lugares disponíveis.",
|
|
@@ -1198,7 +1258,11 @@ const pt = {
|
|
|
1198
1258
|
"validation.emailInvalid": "Formato de email inválido",
|
|
1199
1259
|
"validation.emailDomainInvalid": "Domínio de email inválido",
|
|
1200
1260
|
"validation.participantRequired": "É necessário pelo menos um participante",
|
|
1261
|
+
"validation.ageRequired": "A idade é obrigatória",
|
|
1262
|
+
"validation.levelRequired": "Por favor selecione um nível",
|
|
1201
1263
|
"validation.acceptTerms": "Por favor, aceite os termos e condições",
|
|
1264
|
+
"level.beginner": "Iniciante",
|
|
1265
|
+
"level.advanced": "Avançado",
|
|
1202
1266
|
// Sidebar
|
|
1203
1267
|
"sidebar.close": "Fechar",
|
|
1204
1268
|
// Promo
|
|
@@ -1273,6 +1337,7 @@ const sv = {
|
|
|
1273
1337
|
"events.soldOut": "Fullbokat",
|
|
1274
1338
|
"events.availableFrom": "Lediga platser från {{date}}",
|
|
1275
1339
|
"events.noAvailableDates": "Inga datum lediga",
|
|
1340
|
+
"events.previewSectionTitle": "Specials & kommande datum",
|
|
1276
1341
|
// Event instances
|
|
1277
1342
|
"instances.title": "Välj datum",
|
|
1278
1343
|
"instances.noAvailable": "Inga tillgängliga datum",
|
|
@@ -1289,6 +1354,15 @@ const sv = {
|
|
|
1289
1354
|
"nextEvents.noUpcomingMessage": "Det finns för närvarande inga datum tillgängliga. Kom tillbaka senare eller kontakta oss direkt.",
|
|
1290
1355
|
"nextEvents.showAll": "Visa alla evenemang",
|
|
1291
1356
|
"nextEvents.priceOnRequest": "Pris på förfrågan",
|
|
1357
|
+
// Specials view
|
|
1358
|
+
"specials.title": "Specialerbjudanden",
|
|
1359
|
+
"specials.subtitle": "Våra aktuella erbjudanden i korthet",
|
|
1360
|
+
"specials.noSpecials": "Inga specialerbjudanden",
|
|
1361
|
+
"specials.noSpecialsMessage": "Det finns för närvarande inga specialerbjudanden tillgängliga. Kom tillbaka senare.",
|
|
1362
|
+
"specials.save": "Spara {{amount}}",
|
|
1363
|
+
"specials.savePercent": "{{percent}}% rabatt",
|
|
1364
|
+
"specials.spotsLeft": "{{count}} platser kvar",
|
|
1365
|
+
"specials.bookNow": "Boka nu",
|
|
1292
1366
|
// Booking form
|
|
1293
1367
|
"booking.title": "Bokning - {{name}}",
|
|
1294
1368
|
"booking.notPossible": "Bokning inte möjlig",
|
|
@@ -1310,6 +1384,8 @@ const sv = {
|
|
|
1310
1384
|
"booking.participantName": "Namn *",
|
|
1311
1385
|
"booking.participantNamePlaceholder": "Deltagarens namn",
|
|
1312
1386
|
"booking.participantAge": "Ålder",
|
|
1387
|
+
"booking.participantLevel": "Nivå",
|
|
1388
|
+
"booking.participantLevelPlaceholder": "Välj nivå...",
|
|
1313
1389
|
"booking.addParticipant": "Lägg till deltagare {{number}}",
|
|
1314
1390
|
"booking.maxParticipants": "Maximalt antal deltagare uppnått. Bara {{count}} platser är tillgängliga.",
|
|
1315
1391
|
"booking.maxSpotsReached": "Maximalt {{count}} platser tillgängliga.",
|
|
@@ -1458,7 +1534,11 @@ const sv = {
|
|
|
1458
1534
|
"validation.emailInvalid": "Ogiltigt e-postformat",
|
|
1459
1535
|
"validation.emailDomainInvalid": "Ogiltig e-postdomän",
|
|
1460
1536
|
"validation.participantRequired": "Minst en deltagare krävs",
|
|
1537
|
+
"validation.ageRequired": "Ålder krävs",
|
|
1538
|
+
"validation.levelRequired": "Välj en nivå",
|
|
1461
1539
|
"validation.acceptTerms": "Acceptera villkoren",
|
|
1540
|
+
"level.beginner": "Nybörjare",
|
|
1541
|
+
"level.advanced": "Avancerad",
|
|
1462
1542
|
// Sidebar
|
|
1463
1543
|
"sidebar.close": "Stäng",
|
|
1464
1544
|
// Promo
|
|
@@ -1551,18 +1631,9 @@ function persistLocale(locale) {
|
|
|
1551
1631
|
}
|
|
1552
1632
|
const I18nContext = createContext(null);
|
|
1553
1633
|
function I18nProvider({ configLocale, children }) {
|
|
1554
|
-
// Priority:
|
|
1555
|
-
//
|
|
1556
|
-
const [overrideLocale, setOverrideLocale] = useState(() =>
|
|
1557
|
-
if (configLocale)
|
|
1558
|
-
return null;
|
|
1559
|
-
return readPersistedLocale();
|
|
1560
|
-
});
|
|
1561
|
-
useEffect(() => {
|
|
1562
|
-
if (configLocale) {
|
|
1563
|
-
setOverrideLocale(null);
|
|
1564
|
-
}
|
|
1565
|
-
}, [configLocale]);
|
|
1634
|
+
// Priority: persisted user choice > configLocale (org default) > browser language > "de"
|
|
1635
|
+
// This keeps org locale as default, but remembers explicit user overrides across reloads.
|
|
1636
|
+
const [overrideLocale, setOverrideLocale] = useState(() => readPersistedLocale());
|
|
1566
1637
|
const locale = overrideLocale ?? resolveLocale(configLocale);
|
|
1567
1638
|
const handleSetLocale = useCallback((next) => {
|
|
1568
1639
|
persistLocale(next);
|
|
@@ -1694,129 +1765,153 @@ const resolveSemanticColor = (colorValue, fallbackValue) => {
|
|
|
1694
1765
|
// If semantic resolution fails, use fallback or return the original value
|
|
1695
1766
|
return fallbackValue || colorValue;
|
|
1696
1767
|
};
|
|
1697
|
-
//
|
|
1768
|
+
// Legacy theme name redirects (old name → new name)
|
|
1769
|
+
const legacyThemeRedirects = {
|
|
1770
|
+
"light-fresh": "teal-minimal",
|
|
1771
|
+
"light-elegant": "blue-business",
|
|
1772
|
+
"light-vibrant": "orange-raw",
|
|
1773
|
+
"light-professional": "blue-business",
|
|
1774
|
+
"dark-night": "navy-night",
|
|
1775
|
+
"dark-modern": "navy-night",
|
|
1776
|
+
"dark-forest": "green-deep",
|
|
1777
|
+
};
|
|
1778
|
+
// Predefined themes
|
|
1698
1779
|
const themes = {
|
|
1699
1780
|
// --- Light Themes ---
|
|
1700
|
-
"
|
|
1701
|
-
highlight: "#00b1aa",
|
|
1702
|
-
background: "#f8fdfe",
|
|
1703
|
-
surface: "#ffffff",
|
|
1704
|
-
text: "#0e7490",
|
|
1705
|
-
border: "#bae6fd",
|
|
1706
|
-
success: "#38bdf8",
|
|
1707
|
-
warning: "#fbbf24",
|
|
1708
|
-
error: "#f43f5e",
|
|
1709
|
-
borderRadius: "18px",
|
|
1781
|
+
"teal-minimal": {
|
|
1782
|
+
highlight: "#00b1aa",
|
|
1783
|
+
background: "#f8fdfe",
|
|
1784
|
+
surface: "#ffffff",
|
|
1785
|
+
text: "#0e7490",
|
|
1786
|
+
border: "#bae6fd",
|
|
1787
|
+
success: "#38bdf8",
|
|
1788
|
+
warning: "#fbbf24",
|
|
1789
|
+
error: "#f43f5e",
|
|
1790
|
+
borderRadius: "18px",
|
|
1710
1791
|
fontFamily: "'Inter', system-ui, sans-serif",
|
|
1711
1792
|
},
|
|
1712
|
-
"
|
|
1713
|
-
highlight: "#
|
|
1714
|
-
background: "#
|
|
1715
|
-
surface: "#ffffff",
|
|
1716
|
-
text: "#
|
|
1717
|
-
border: "#
|
|
1718
|
-
success: "#
|
|
1719
|
-
warning: "#
|
|
1720
|
-
error: "#
|
|
1721
|
-
borderRadius: "
|
|
1722
|
-
fontFamily: "'
|
|
1793
|
+
"blue-business": {
|
|
1794
|
+
highlight: "#2563eb",
|
|
1795
|
+
background: "#f8fafc",
|
|
1796
|
+
surface: "#ffffff",
|
|
1797
|
+
text: "#0f172a",
|
|
1798
|
+
border: "#cbd5e1",
|
|
1799
|
+
success: "#059669",
|
|
1800
|
+
warning: "#d97706",
|
|
1801
|
+
error: "#b91c1c",
|
|
1802
|
+
borderRadius: "6px",
|
|
1803
|
+
fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
|
|
1723
1804
|
},
|
|
1724
|
-
"
|
|
1725
|
-
highlight: "#ed702d",
|
|
1726
|
-
background: "#1f2630",
|
|
1727
|
-
surface: "#1f2630",
|
|
1728
|
-
text: "#f1f5f9",
|
|
1729
|
-
border: "#ed702d",
|
|
1730
|
-
success: "#22c55e",
|
|
1731
|
-
warning: "#eab308",
|
|
1732
|
-
error: "#ef4444",
|
|
1805
|
+
"orange-raw": {
|
|
1806
|
+
highlight: "#ed702d",
|
|
1807
|
+
background: "#1f2630",
|
|
1808
|
+
surface: "#1f2630",
|
|
1809
|
+
text: "#f1f5f9",
|
|
1810
|
+
border: "#ed702d",
|
|
1811
|
+
success: "#22c55e",
|
|
1812
|
+
warning: "#eab308",
|
|
1813
|
+
error: "#ef4444",
|
|
1733
1814
|
borderRadius: "0px",
|
|
1734
1815
|
fontFamily: "Inter, system-ui, sans-serif",
|
|
1735
1816
|
},
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1817
|
+
// --- Dark Themes ---
|
|
1818
|
+
"navy-night": {
|
|
1819
|
+
highlight: "#60a5fa",
|
|
1820
|
+
background: "#0b1120",
|
|
1821
|
+
surface: "#111827",
|
|
1822
|
+
text: "#e2e8f0",
|
|
1823
|
+
border: "#1e3a5f",
|
|
1824
|
+
success: "#34d399",
|
|
1825
|
+
warning: "#fbbf24",
|
|
1826
|
+
error: "#f87171",
|
|
1827
|
+
borderRadius: "10px",
|
|
1828
|
+
fontFamily: "'Outfit', system-ui, sans-serif",
|
|
1747
1829
|
},
|
|
1748
|
-
"
|
|
1749
|
-
highlight: "#
|
|
1750
|
-
background: "#
|
|
1751
|
-
surface: "#
|
|
1752
|
-
text: "#
|
|
1753
|
-
border: "#
|
|
1754
|
-
success: "#
|
|
1755
|
-
warning: "#
|
|
1756
|
-
error: "#
|
|
1757
|
-
borderRadius: "8px",
|
|
1758
|
-
fontFamily: "Inter, system-ui, sans-serif",
|
|
1759
|
-
},
|
|
1760
|
-
"dark-modern": {
|
|
1761
|
-
highlight: "#3b82f6", // blue-500 - bright blue accent
|
|
1762
|
-
background: "#0f172a", // slate-900 - dark background
|
|
1763
|
-
surface: "#1e293b", // slate-800 - dark cards
|
|
1764
|
-
text: "#f1f5f9", // slate-100 - light text
|
|
1765
|
-
border: "#334155", // slate-700 - subtle borders
|
|
1766
|
-
success: "#22c55e", // green-500
|
|
1767
|
-
warning: "#eab308", // yellow-500
|
|
1768
|
-
error: "#ef4444", // red-500
|
|
1769
|
-
borderRadius: "8px",
|
|
1770
|
-
fontFamily: "Inter, system-ui, sans-serif",
|
|
1771
|
-
},
|
|
1772
|
-
"dark-forest": {
|
|
1773
|
-
highlight: "#34d399", // Emerald 400
|
|
1774
|
-
background: "#05140d",
|
|
1775
|
-
surface: "#062215",
|
|
1776
|
-
text: "#d1fae5", // Emerald 100
|
|
1777
|
-
border: "#043322",
|
|
1778
|
-
success: "#4ade80", // Green 400
|
|
1779
|
-
warning: "#facc15", // Yellow 400
|
|
1780
|
-
error: "#f87171", // Red 400
|
|
1830
|
+
"green-deep": {
|
|
1831
|
+
highlight: "#34d399",
|
|
1832
|
+
background: "#030d07",
|
|
1833
|
+
surface: "#051a0e",
|
|
1834
|
+
text: "#d1fae5",
|
|
1835
|
+
border: "#064e20",
|
|
1836
|
+
success: "#4ade80",
|
|
1837
|
+
warning: "#facc15",
|
|
1838
|
+
error: "#f87171",
|
|
1781
1839
|
borderRadius: "12px",
|
|
1782
|
-
fontFamily: "system-ui,
|
|
1840
|
+
fontFamily: "'Instrument Sans', system-ui, sans-serif",
|
|
1783
1841
|
},
|
|
1784
|
-
"
|
|
1785
|
-
highlight: "#
|
|
1842
|
+
"green-matrix": {
|
|
1843
|
+
highlight: "#39ff14",
|
|
1786
1844
|
background: "#000000",
|
|
1787
|
-
surface: "#
|
|
1788
|
-
text: "#
|
|
1789
|
-
border: "#
|
|
1790
|
-
success: "#
|
|
1845
|
+
surface: "#060f06",
|
|
1846
|
+
text: "#00ff41",
|
|
1847
|
+
border: "#0d2b0d",
|
|
1848
|
+
success: "#39ff14",
|
|
1791
1849
|
warning: "#ffff00",
|
|
1792
1850
|
error: "#ff3333",
|
|
1793
1851
|
borderRadius: "0px",
|
|
1794
|
-
fontFamily: "'
|
|
1852
|
+
fontFamily: "'Share Tech Mono', monospace",
|
|
1795
1853
|
},
|
|
1796
|
-
"
|
|
1797
|
-
highlight: "#fde047",
|
|
1798
|
-
background: "#1c1917",
|
|
1799
|
-
surface: "#292524",
|
|
1800
|
-
text: "#fafaf9",
|
|
1801
|
-
border: "#44403c",
|
|
1802
|
-
success: "#a3e635",
|
|
1803
|
-
warning: "#f59e0b",
|
|
1804
|
-
error: "#fca5a5",
|
|
1854
|
+
"gold-luxury": {
|
|
1855
|
+
highlight: "#fde047",
|
|
1856
|
+
background: "#1c1917",
|
|
1857
|
+
surface: "#292524",
|
|
1858
|
+
text: "#fafaf9",
|
|
1859
|
+
border: "#44403c",
|
|
1860
|
+
success: "#a3e635",
|
|
1861
|
+
warning: "#f59e0b",
|
|
1862
|
+
error: "#fca5a5",
|
|
1805
1863
|
borderRadius: "24px",
|
|
1806
|
-
fontFamily: "'
|
|
1864
|
+
fontFamily: "'Bodoni Moda', serif",
|
|
1807
1865
|
},
|
|
1808
|
-
"
|
|
1809
|
-
highlight: "#d946ef",
|
|
1810
|
-
background: "#
|
|
1811
|
-
surface: "#
|
|
1812
|
-
text: "#f3e8ff",
|
|
1813
|
-
border: "#
|
|
1814
|
-
success: "#4ade80",
|
|
1815
|
-
warning: "#facc15",
|
|
1816
|
-
error: "#f87171",
|
|
1866
|
+
"purple-electric": {
|
|
1867
|
+
highlight: "#d946ef",
|
|
1868
|
+
background: "#110820",
|
|
1869
|
+
surface: "#1a0d30",
|
|
1870
|
+
text: "#f3e8ff",
|
|
1871
|
+
border: "#3b0764",
|
|
1872
|
+
success: "#4ade80",
|
|
1873
|
+
warning: "#facc15",
|
|
1874
|
+
error: "#f87171",
|
|
1817
1875
|
borderRadius: "14px",
|
|
1818
1876
|
fontFamily: "'Geologica', sans-serif",
|
|
1819
1877
|
},
|
|
1878
|
+
"dark-neon-brutalism": {
|
|
1879
|
+
highlight: "#00e5cc",
|
|
1880
|
+
background: "#0e1420",
|
|
1881
|
+
surface: "#0e1420",
|
|
1882
|
+
text: "#e8eef5",
|
|
1883
|
+
border: "#00e5cc",
|
|
1884
|
+
success: "#00e5cc",
|
|
1885
|
+
warning: "#ffe45e",
|
|
1886
|
+
error: "#ff4d6d",
|
|
1887
|
+
borderRadius: "4px",
|
|
1888
|
+
fontFamily: "'JetBrains Mono', monospace",
|
|
1889
|
+
buttonTextColor: "#0e1420",
|
|
1890
|
+
},
|
|
1891
|
+
"rose-editorial": {
|
|
1892
|
+
highlight: "#be3455",
|
|
1893
|
+
background: "#fdf8f5",
|
|
1894
|
+
surface: "#ffffff",
|
|
1895
|
+
text: "#1a0a0f",
|
|
1896
|
+
border: "#f2d5db",
|
|
1897
|
+
success: "#2d6a4f",
|
|
1898
|
+
warning: "#b5451b",
|
|
1899
|
+
error: "#9b1d20",
|
|
1900
|
+
borderRadius: "0px",
|
|
1901
|
+
fontFamily: "'Cormorant', serif",
|
|
1902
|
+
},
|
|
1903
|
+
"amber-retro": {
|
|
1904
|
+
highlight: "#f59e0b",
|
|
1905
|
+
background: "#1a1008",
|
|
1906
|
+
surface: "#241a0a",
|
|
1907
|
+
text: "#fef3c7",
|
|
1908
|
+
border: "#78350f",
|
|
1909
|
+
success: "#84cc16",
|
|
1910
|
+
warning: "#f59e0b",
|
|
1911
|
+
error: "#ef4444",
|
|
1912
|
+
borderRadius: "6px",
|
|
1913
|
+
fontFamily: "'Syne', sans-serif",
|
|
1914
|
+
},
|
|
1820
1915
|
};
|
|
1821
1916
|
const StyleProvider = ({ config, children, }) => {
|
|
1822
1917
|
// Track hydration state to prevent mismatches
|
|
@@ -1826,8 +1921,10 @@ const StyleProvider = ({ config, children, }) => {
|
|
|
1826
1921
|
}, []);
|
|
1827
1922
|
// PERFORMANCE OPTIMIZATION: Memoize style calculations
|
|
1828
1923
|
const themedStyles = useMemo(() => {
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1924
|
+
const rawThemeName = config.theme || "teal-minimal";
|
|
1925
|
+
// Redirect legacy theme names to new names
|
|
1926
|
+
const themeName = legacyThemeRedirects[rawThemeName] || rawThemeName;
|
|
1927
|
+
const themeDefaults = themes[themeName] || themes["teal-minimal"];
|
|
1831
1928
|
const getCSSValue = (value, fallback) => {
|
|
1832
1929
|
if (!value)
|
|
1833
1930
|
return fallback;
|
|
@@ -1897,6 +1994,7 @@ const StyleProvider = ({ config, children, }) => {
|
|
|
1897
1994
|
"--bw-surface-color": finalColors.surface,
|
|
1898
1995
|
"--bw-text-color": finalColors.text,
|
|
1899
1996
|
"--bw-text-muted": addOpacity(finalColors.text, 0.7),
|
|
1997
|
+
"--bw-button-text-color": themeDefaults.buttonTextColor || "#ffffff",
|
|
1900
1998
|
"--bw-border-color": finalColors.border,
|
|
1901
1999
|
"--bw-success-color": finalColors.success,
|
|
1902
2000
|
"--bw-warning-color": finalColors.warning,
|
|
@@ -1914,7 +2012,7 @@ const StyleProvider = ({ config, children, }) => {
|
|
|
1914
2012
|
"--bw-highlight-muted": addOpacity(finalColors.highlight, 0.1),
|
|
1915
2013
|
"--bw-highlight-subtle": addOpacity(finalColors.highlight, 0.05),
|
|
1916
2014
|
"--bw-text-subtle": addOpacity(finalColors.text, 0.4),
|
|
1917
|
-
colorScheme:
|
|
2015
|
+
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",
|
|
1918
2016
|
};
|
|
1919
2017
|
}, [
|
|
1920
2018
|
config.theme,
|
|
@@ -4468,6 +4566,7 @@ function DialogWrapper({ isOpen, onClose, children, maxWidth = "700px", classNam
|
|
|
4468
4566
|
"--bw-font-family": computedStyles.getPropertyValue("--bw-font-family").trim() || "system-ui, sans-serif",
|
|
4469
4567
|
"--bw-shadow-md": computedStyles.getPropertyValue("--bw-shadow-md").trim() ||
|
|
4470
4568
|
"0 4px 6px -1px rgba(0, 0, 0, 0.1)",
|
|
4569
|
+
"--bw-button-text-color": computedStyles.getPropertyValue("--bw-button-text-color").trim() || "#ffffff",
|
|
4471
4570
|
};
|
|
4472
4571
|
setFallbackStyles(fallbacks);
|
|
4473
4572
|
}
|
|
@@ -11117,16 +11216,37 @@ const objectType = ZodObject.create;
|
|
|
11117
11216
|
ZodUnion.create;
|
|
11118
11217
|
ZodIntersection.create;
|
|
11119
11218
|
ZodTuple.create;
|
|
11120
|
-
ZodEnum.create;
|
|
11219
|
+
const enumType = ZodEnum.create;
|
|
11121
11220
|
ZodPromise.create;
|
|
11122
11221
|
ZodOptional.create;
|
|
11123
11222
|
ZodNullable.create;
|
|
11223
|
+
const preprocessType = ZodEffects.createWithPreprocess;
|
|
11124
11224
|
|
|
11125
|
-
const
|
|
11126
|
-
name:
|
|
11225
|
+
const DEFAULT_PARTICIPANT_FIELDS_CONFIG = {
|
|
11226
|
+
name: { enabled: true, required: true },
|
|
11227
|
+
age: { enabled: true, required: false },
|
|
11228
|
+
level: { enabled: false, required: false },
|
|
11229
|
+
};
|
|
11230
|
+
const participantSchema = (t, fieldsConfig) => objectType({
|
|
11231
|
+
name: stringType().trim().optional(),
|
|
11127
11232
|
age: numberType().min(0).max(120).optional(),
|
|
11233
|
+
level: preprocessType((value) => (value === "" ? undefined : value), enumType(["beginner", "advanced"]).optional()),
|
|
11234
|
+
})
|
|
11235
|
+
.superRefine((value, ctx) => {
|
|
11236
|
+
if (fieldsConfig.name.required && (!value.name || value.name.trim().length < 1)) {
|
|
11237
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.nameRequired"), path: ["name"] });
|
|
11238
|
+
}
|
|
11239
|
+
if (!fieldsConfig.name.enabled && value.name && value.name.trim().length > 0) {
|
|
11240
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.nameRequired"), path: ["name"] });
|
|
11241
|
+
}
|
|
11242
|
+
if (fieldsConfig.age.required && typeof value.age !== "number") {
|
|
11243
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.ageRequired"), path: ["age"] });
|
|
11244
|
+
}
|
|
11245
|
+
if (fieldsConfig.level.required && !value.level) {
|
|
11246
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.levelRequired"), path: ["level"] });
|
|
11247
|
+
}
|
|
11128
11248
|
});
|
|
11129
|
-
function createBookingFormSchema(t) {
|
|
11249
|
+
function createBookingFormSchema(t, fieldsConfig = DEFAULT_PARTICIPANT_FIELDS_CONFIG) {
|
|
11130
11250
|
const tr = t ?? ((key) => key);
|
|
11131
11251
|
return objectType({
|
|
11132
11252
|
customerName: stringType().trim().min(2, tr("validation.nameMinLength")),
|
|
@@ -11136,7 +11256,7 @@ function createBookingFormSchema(t) {
|
|
|
11136
11256
|
.email(tr("validation.emailInvalid"))
|
|
11137
11257
|
.regex(/\.[a-zA-Z]{2,}$/, tr("validation.emailDomainInvalid")),
|
|
11138
11258
|
customerPhone: stringType().trim().optional(),
|
|
11139
|
-
participants: arrayType(participantSchema(tr)).min(1, tr("validation.participantRequired")),
|
|
11259
|
+
participants: arrayType(participantSchema(tr, fieldsConfig)).min(1, tr("validation.participantRequired")),
|
|
11140
11260
|
discountCode: stringType().trim().optional(),
|
|
11141
11261
|
comment: stringType().trim().optional(),
|
|
11142
11262
|
acceptTerms: booleanType().refine((val) => val === true, {
|
|
@@ -11177,11 +11297,13 @@ const buttonBase = {
|
|
|
11177
11297
|
whiteSpace: "nowrap",
|
|
11178
11298
|
border: "none",
|
|
11179
11299
|
};
|
|
11300
|
+
// CSS class name for button hover effects
|
|
11301
|
+
const buttonClassName = "bw-button-hover";
|
|
11180
11302
|
const buttonStyles = {
|
|
11181
11303
|
primary: {
|
|
11182
11304
|
...buttonBase,
|
|
11183
11305
|
backgroundColor: "var(--bw-highlight-color)",
|
|
11184
|
-
color: "#ffffff",
|
|
11306
|
+
color: "var(--bw-button-text-color, #ffffff)",
|
|
11185
11307
|
border: "none",
|
|
11186
11308
|
},
|
|
11187
11309
|
secondary: {
|
|
@@ -11277,7 +11399,8 @@ const participantUpsellStyles = {
|
|
|
11277
11399
|
gap: "8px",
|
|
11278
11400
|
marginTop: "10px",
|
|
11279
11401
|
paddingTop: "10px",
|
|
11280
|
-
|
|
11402
|
+
paddingBottom: "25px",
|
|
11403
|
+
borderBottom: "1px dashed var(--bw-border-color)",
|
|
11281
11404
|
},
|
|
11282
11405
|
label: {
|
|
11283
11406
|
display: "inline-flex",
|
|
@@ -11335,6 +11458,8 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11335
11458
|
const { locale } = useLocale();
|
|
11336
11459
|
const timezone = useTimezone();
|
|
11337
11460
|
const roundEnabled = systemConfig?.roundPricesEnabled !== false;
|
|
11461
|
+
const participantFieldsConfig = eventDetails.participantFieldsConfig ?? DEFAULT_PARTICIPANT_FIELDS_CONFIG;
|
|
11462
|
+
const participantLevelOptions = eventDetails.participantLevelOptions ?? ["beginner", "advanced"];
|
|
11338
11463
|
const roundDiscountUp = (minorUnits) => Math.ceil(minorUnits / 100) * 100;
|
|
11339
11464
|
const calcPercentDiscountAmount = (baseAmount, basisPoints, round) => {
|
|
11340
11465
|
const raw = Math.round((baseAmount * basisPoints) / 10000);
|
|
@@ -11347,18 +11472,19 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11347
11472
|
// Per-participant upsell selections: participantIndex -> array of upsell package IDs
|
|
11348
11473
|
const [participantUpsells, setParticipantUpsells] = useState({});
|
|
11349
11474
|
const form = useForm({
|
|
11350
|
-
resolver: t(createBookingFormSchema(t$1)),
|
|
11475
|
+
resolver: t(createBookingFormSchema(t$1, participantFieldsConfig)),
|
|
11351
11476
|
defaultValues: {
|
|
11352
11477
|
customerName: "",
|
|
11353
11478
|
customerEmail: "",
|
|
11354
11479
|
customerPhone: "",
|
|
11355
|
-
participants: [{ name: "" }],
|
|
11480
|
+
participants: [{ name: "", level: undefined }],
|
|
11356
11481
|
discountCode: "",
|
|
11357
11482
|
comment: "",
|
|
11358
11483
|
acceptTerms: false,
|
|
11359
11484
|
},
|
|
11360
11485
|
});
|
|
11361
11486
|
const watchedParticipants = form.watch("participants");
|
|
11487
|
+
const participantCount = watchedParticipants.length;
|
|
11362
11488
|
const watchedCustomerName = form.watch("customerName");
|
|
11363
11489
|
const watchedCustomerEmail = form.watch("customerEmail");
|
|
11364
11490
|
const watchedComment = form.watch("comment");
|
|
@@ -11400,14 +11526,13 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11400
11526
|
const calculateBaseTotal = useCallback(() => {
|
|
11401
11527
|
if (!eventDetails)
|
|
11402
11528
|
return 0;
|
|
11403
|
-
return eventDetails.price *
|
|
11404
|
-
}, [eventDetails,
|
|
11529
|
+
return eventDetails.price * participantCount;
|
|
11530
|
+
}, [eventDetails, participantCount]);
|
|
11405
11531
|
// Calculate upsells total based on per-participant selections
|
|
11406
11532
|
const calculateUpsellsTotal = useCallback(() => {
|
|
11407
11533
|
let total = 0;
|
|
11408
|
-
watchedParticipants.forEach((
|
|
11409
|
-
|
|
11410
|
-
if (participant.name.trim()) {
|
|
11534
|
+
watchedParticipants.forEach((_, index) => {
|
|
11535
|
+
if (participantCount > 0) {
|
|
11411
11536
|
const participantUpsellIds = participantUpsells[index] || [];
|
|
11412
11537
|
participantUpsellIds.forEach(upsellId => {
|
|
11413
11538
|
const upsell = upsells.find(u => u.id === upsellId);
|
|
@@ -11418,7 +11543,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11418
11543
|
}
|
|
11419
11544
|
});
|
|
11420
11545
|
return total;
|
|
11421
|
-
}, [participantUpsells, upsells, watchedParticipants]);
|
|
11546
|
+
}, [participantUpsells, upsells, watchedParticipants, participantCount]);
|
|
11422
11547
|
const calculateTotalDiscount = useCallback(() => {
|
|
11423
11548
|
return appliedVouchers.reduce((total, voucher) => {
|
|
11424
11549
|
if (voucher.type === "discount") {
|
|
@@ -11439,8 +11564,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11439
11564
|
const calculateDeposit = () => {
|
|
11440
11565
|
if (!eventDetails || !eventDetails.deposit)
|
|
11441
11566
|
return 0;
|
|
11442
|
-
|
|
11443
|
-
return eventDetails.deposit * participantCount;
|
|
11567
|
+
return eventDetails.deposit * watchedParticipants.length;
|
|
11444
11568
|
};
|
|
11445
11569
|
const baseTotal = calculateBaseTotal();
|
|
11446
11570
|
const upsellsTotal = calculateUpsellsTotal();
|
|
@@ -11457,8 +11581,8 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11457
11581
|
// Includes participantIndices to track which participants selected each upsell
|
|
11458
11582
|
const aggregatedUpsellSelections = useCallback(() => {
|
|
11459
11583
|
const upsellParticipantMap = {};
|
|
11460
|
-
watchedParticipants.forEach((
|
|
11461
|
-
if (
|
|
11584
|
+
watchedParticipants.forEach((_, index) => {
|
|
11585
|
+
if (participantCount > 0) {
|
|
11462
11586
|
const participantUpsellIds = participantUpsells[index] || [];
|
|
11463
11587
|
participantUpsellIds.forEach(upsellId => {
|
|
11464
11588
|
if (!upsellParticipantMap[upsellId]) {
|
|
@@ -11492,15 +11616,17 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11492
11616
|
setAppliedVouchers((prev) => prev.filter((v) => v.code !== code));
|
|
11493
11617
|
}, []);
|
|
11494
11618
|
const isReadyForPayment = () => {
|
|
11495
|
-
const participantsWithNames = watchedParticipants.filter((p) => p.name
|
|
11619
|
+
const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
|
|
11496
11620
|
const totalParticipantRows = watchedParticipants.length;
|
|
11497
|
-
const allParticipantsHaveNames =
|
|
11621
|
+
const allParticipantsHaveNames = participantFieldsConfig.name.required
|
|
11622
|
+
? participantsWithNames === totalParticipantRows
|
|
11623
|
+
: true;
|
|
11498
11624
|
const participantsWithinLimit = participantsWithNames <= (eventDetails?.availableSpots || 0);
|
|
11499
11625
|
const hasValidCustomerName = watchedCustomerName && watchedCustomerName.trim().length >= 2;
|
|
11500
11626
|
const hasValidCustomerEmail = watchedCustomerEmail && watchedCustomerEmail.trim().length > 0 && !customerEmailError;
|
|
11501
11627
|
return allParticipantsHaveNames &&
|
|
11502
11628
|
participantsWithinLimit &&
|
|
11503
|
-
participantsWithNames > 0 &&
|
|
11629
|
+
(participantFieldsConfig.name.required ? participantsWithNames > 0 : totalParticipantRows > 0) &&
|
|
11504
11630
|
hasValidCustomerName &&
|
|
11505
11631
|
hasValidCustomerEmail &&
|
|
11506
11632
|
watchedAcceptTerms;
|
|
@@ -11508,7 +11634,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11508
11634
|
useEffect(() => {
|
|
11509
11635
|
if (appliedVouchers.length > 0) {
|
|
11510
11636
|
const newBaseTotal = eventDetails?.price
|
|
11511
|
-
? eventDetails.price * watchedParticipants.
|
|
11637
|
+
? eventDetails.price * watchedParticipants.length
|
|
11512
11638
|
: 0;
|
|
11513
11639
|
const currentUpsellsTotal = calculateUpsellsTotal();
|
|
11514
11640
|
const orderTotal = newBaseTotal + currentUpsellsTotal;
|
|
@@ -11553,7 +11679,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11553
11679
|
const currentParticipants = form.getValues("participants");
|
|
11554
11680
|
const availableSpots = eventDetails?.availableSpots || 0;
|
|
11555
11681
|
if (currentParticipants.length < availableSpots) {
|
|
11556
|
-
form.setValue("participants", [...currentParticipants, { name: "" }]);
|
|
11682
|
+
form.setValue("participants", [...currentParticipants, { name: "", level: undefined }]);
|
|
11557
11683
|
}
|
|
11558
11684
|
else {
|
|
11559
11685
|
alert(t$1("booking.maxParticipants", { count: availableSpots }));
|
|
@@ -11672,7 +11798,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11672
11798
|
justifyContent: "space-between",
|
|
11673
11799
|
alignItems: "center",
|
|
11674
11800
|
marginBottom: "16px",
|
|
11675
|
-
}, children: jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: 0 }, children: t$1("booking.participants") }) }), jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [watchedParticipants.map((_, index) => (jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [jsxs("div", { style: { display: "flex", gap: "12px", alignItems: "center" }, children: [jsxs("div", { style: { flex: 1 }, children: [jsx("label", { htmlFor: `participant-name-${index}`, style: labelStyles$1, children: t$1("booking.participantName") }), 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 && (jsx("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.name?.message }))] }), jsxs("div", { style: { width: "80px" }, children: [jsx("label", { htmlFor: `participant-age-${index}`, style: labelStyles$1, children: t$1("booking.participantAge") }), jsx("input", { id: `participant-age-${index}`, ...form.register(`participants.${index}.age`, {
|
|
11801
|
+
}, children: jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: 0 }, children: t$1("booking.participants") }) }), jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [watchedParticipants.map((_, index) => (jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [jsxs("div", { style: { display: "flex", gap: "12px", alignItems: "center" }, children: [participantFieldsConfig.name.enabled && (jsxs("div", { style: { flex: 1 }, children: [jsx("label", { htmlFor: `participant-name-${index}`, style: labelStyles$1, children: t$1("booking.participantName") }), 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 && (jsx("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.name?.message }))] })), participantFieldsConfig.age.enabled && (jsxs("div", { style: { width: "80px" }, children: [jsx("label", { htmlFor: `participant-age-${index}`, style: labelStyles$1, children: t$1("booking.participantAge") }), jsx("input", { id: `participant-age-${index}`, ...form.register(`participants.${index}.age`, {
|
|
11676
11802
|
setValueAs: (value) => {
|
|
11677
11803
|
if (value === "" || value === null || value === undefined) {
|
|
11678
11804
|
return undefined;
|
|
@@ -11680,7 +11806,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11680
11806
|
const num = Number(value);
|
|
11681
11807
|
return Number.isNaN(num) ? undefined : num;
|
|
11682
11808
|
},
|
|
11683
|
-
}), type: "number", min: "0", max: "120", style: inputStyles$1, placeholder: "25" })] }), watchedParticipants.length > 1 && (jsxs("div", { children: [jsx("label", { style: { ...labelStyles$1, visibility: "hidden" }, children: "\u00A0" }), jsx("button", { type: "button", onClick: () => removeParticipant(index), style: {
|
|
11809
|
+
}), type: "number", min: "0", max: "120", style: inputStyles$1, placeholder: "25" })] })), watchedParticipants.length > 1 && (jsxs("div", { children: [jsx("label", { style: { ...labelStyles$1, visibility: "hidden" }, children: "\u00A0" }), jsx("button", { type: "button", onClick: () => removeParticipant(index), style: {
|
|
11684
11810
|
color: "var(--bw-error-color)",
|
|
11685
11811
|
backgroundColor: "var(--bw-surface-color)",
|
|
11686
11812
|
border: "1px solid var(--bw-border-color)",
|
|
@@ -11696,7 +11822,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11696
11822
|
fontWeight: 700,
|
|
11697
11823
|
fontFamily: "var(--bw-font-family)",
|
|
11698
11824
|
padding: 0,
|
|
11699
|
-
}, children: "\u00D7" })] }))] }), upsells.length > 0 && (jsx("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
|
|
11825
|
+
}, children: "\u00D7" })] }))] }), participantFieldsConfig.level.enabled && (jsxs("div", { style: { minWidth: "140px" }, children: [jsx("label", { htmlFor: `participant-level-${index}`, style: labelStyles$1, children: t$1("booking.participantLevel") }), jsxs("select", { id: `participant-level-${index}`, ...form.register(`participants.${index}.level`), style: inputStyles$1, children: [jsx("option", { value: "", children: t$1("booking.participantLevelPlaceholder") }), participantLevelOptions.map((level) => (jsx("option", { value: level, children: t$1(`level.${level}`) }, level)))] }), form.formState.errors.participants?.[index]?.level && (jsx("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.level?.message }))] })), upsells.length > 0 && (jsx("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
|
|
11700
11826
|
const isSelected = (participantUpsells[index] || []).includes(upsell.id);
|
|
11701
11827
|
return (jsxs("label", { htmlFor: `upsell-${index}-${upsell.id}`, style: isSelected ? participantUpsellStyles.labelSelected : participantUpsellStyles.label, children: [jsx("input", { id: `upsell-${index}-${upsell.id}`, type: "checkbox", style: participantUpsellStyles.checkbox, checked: isSelected, onChange: () => toggleParticipantUpsell(index, upsell.id) }), jsx("span", { style: { fontWeight: 500 }, children: upsell.name }), jsxs("span", { style: { fontSize: "12px", opacity: 0.8 }, children: ["(+", formatCurrency(upsell.price), ")"] })] }, upsell.id));
|
|
11702
11828
|
}) }))] }, index))), watchedParticipants.length < eventDetails.availableSpots ? (jsx("div", { style: {
|
|
@@ -11725,9 +11851,9 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11725
11851
|
color: "var(--bw-text-color)",
|
|
11726
11852
|
fontWeight: 500,
|
|
11727
11853
|
fontFamily: "var(--bw-font-family)",
|
|
11728
|
-
}, children: [jsxs("span", { style: { fontWeight: 200 }, children: [watchedParticipants.length > 1 ? watchedParticipants.
|
|
11854
|
+
}, children: [jsxs("span", { style: { fontWeight: 200 }, children: [watchedParticipants.length > 1 ? watchedParticipants.length : 1, " x "] }), " ", formatCurrency(eventDetails.price)] })] }), upsellsTotal > 0 && (jsxs("div", { style: { marginTop: "8px", paddingTop: "8px", borderTop: "1px dashed var(--bw-border-color)" }, children: [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) => {
|
|
11729
11855
|
// Count how many participants have this upsell selected
|
|
11730
|
-
const countWithUpsell = watchedParticipants.filter((
|
|
11856
|
+
const countWithUpsell = watchedParticipants.filter((_, idx) => (participantUpsells[idx] || []).includes(upsell.id)).length;
|
|
11731
11857
|
if (countWithUpsell === 0)
|
|
11732
11858
|
return null;
|
|
11733
11859
|
const upsellLineTotal = upsell.price * countWithUpsell;
|
|
@@ -11828,15 +11954,17 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11828
11954
|
}, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), jsx("div", { ref: paymentSectionRef, children: (stripePromise || systemConfig?.paymentProvider === "mollie") &&
|
|
11829
11955
|
(() => {
|
|
11830
11956
|
if (!isReadyForPayment()) {
|
|
11831
|
-
const participantsWithNames = watchedParticipants.filter((p) => p.name
|
|
11957
|
+
const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
|
|
11832
11958
|
const totalParticipantRows = watchedParticipants.length;
|
|
11833
11959
|
const participantsWithoutNames = totalParticipantRows - participantsWithNames;
|
|
11834
11960
|
const missing = [];
|
|
11835
|
-
if (
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11961
|
+
if (participantFieldsConfig.name.required) {
|
|
11962
|
+
if (participantsWithNames === 0) {
|
|
11963
|
+
missing.push(t$1("payment.needParticipant"));
|
|
11964
|
+
}
|
|
11965
|
+
else if (participantsWithoutNames > 0) {
|
|
11966
|
+
missing.push(t$1("payment.needAllNames", { count: totalParticipantRows }));
|
|
11967
|
+
}
|
|
11840
11968
|
}
|
|
11841
11969
|
if (participantsWithNames > (eventDetails?.availableSpots || 0)) {
|
|
11842
11970
|
missing.push(t$1("payment.reduceParticipants", { count: eventDetails?.availableSpots || 0 }));
|
|
@@ -12011,7 +12139,7 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
|
|
|
12011
12139
|
try {
|
|
12012
12140
|
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/get-booking-by-payment"), {
|
|
12013
12141
|
method: "POST",
|
|
12014
|
-
headers: createApiHeaders(config),
|
|
12142
|
+
headers: createApiHeaders(config, locale),
|
|
12015
12143
|
body: JSON.stringify(createRequestBody(config, {
|
|
12016
12144
|
paymentIntentId: targetPaymentIntentId,
|
|
12017
12145
|
})),
|
|
@@ -12245,7 +12373,7 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
|
|
|
12245
12373
|
flexDirection: "column",
|
|
12246
12374
|
gap: "var(--bw-spacing-small)",
|
|
12247
12375
|
}, children: formData.participants
|
|
12248
|
-
.filter((p) => p.name
|
|
12376
|
+
.filter((p) => p.name?.trim() || p.age || p.level)
|
|
12249
12377
|
.map((participant, index) => (jsx("div", { className: "print-participant", style: {
|
|
12250
12378
|
display: "flex",
|
|
12251
12379
|
justifyContent: "space-between",
|
|
@@ -12256,11 +12384,15 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
|
|
|
12256
12384
|
}, children: jsxs("div", { className: "print-participant-info", children: [jsx("div", { className: "print-participant-name", style: {
|
|
12257
12385
|
color: "var(--bw-text-color)",
|
|
12258
12386
|
fontFamily: "var(--bw-font-family)",
|
|
12259
|
-
}, children: participant.name }), participant.age && (jsx("div", { className: "print-participant-age", style: {
|
|
12387
|
+
}, children: participant.name || `#${index + 1}` }), participant.age && (jsx("div", { className: "print-participant-age", style: {
|
|
12260
12388
|
color: "var(--bw-text-muted)",
|
|
12261
12389
|
fontSize: "var(--bw-font-size-small)",
|
|
12262
12390
|
fontFamily: "var(--bw-font-family)",
|
|
12263
|
-
}, children: t("success.age", { age: participant.age }) }))
|
|
12391
|
+
}, children: t("success.age", { age: participant.age }) })), participant.level && (jsxs("div", { style: {
|
|
12392
|
+
color: "var(--bw-text-muted)",
|
|
12393
|
+
fontSize: "var(--bw-font-size-small)",
|
|
12394
|
+
fontFamily: "var(--bw-font-family)",
|
|
12395
|
+
}, children: [t("booking.participantLevel"), ": ", t(`level.${participant.level}`)] }))] }) }, index))) }) })] })), jsxs("div", { className: "print-booking-card", style: {
|
|
12264
12396
|
backgroundColor: "var(--bw-surface-color)",
|
|
12265
12397
|
border: `1px solid var(--bw-border-color)`,
|
|
12266
12398
|
borderRadius: "var(--bw-border-radius)",
|
|
@@ -13668,8 +13800,48 @@ function formatDurationInfo(info, t) {
|
|
|
13668
13800
|
const rest = formatted.slice(0, -1).join(", ");
|
|
13669
13801
|
return `${t("duration.optionally")} ${rest} ${t("duration.or")} ${last} ${unitPlural}`;
|
|
13670
13802
|
}
|
|
13671
|
-
function
|
|
13803
|
+
function InfoBadge({ text }) {
|
|
13804
|
+
const [open, setOpen] = useState(false);
|
|
13805
|
+
const ref = useRef(null);
|
|
13806
|
+
return (jsxs("span", { ref: ref, onClick: (e) => { e.stopPropagation(); setOpen((v) => !v); }, style: {
|
|
13807
|
+
position: "relative",
|
|
13808
|
+
display: "inline-flex",
|
|
13809
|
+
alignItems: "center",
|
|
13810
|
+
justifyContent: "center",
|
|
13811
|
+
width: "16px",
|
|
13812
|
+
height: "16px",
|
|
13813
|
+
borderRadius: "50%",
|
|
13814
|
+
border: "1px solid var(--bw-highlight-color)",
|
|
13815
|
+
color: "var(--bw-highlight-color)",
|
|
13816
|
+
fontSize: "9px",
|
|
13817
|
+
fontWeight: 700,
|
|
13818
|
+
cursor: "pointer",
|
|
13819
|
+
flexShrink: 0,
|
|
13820
|
+
userSelect: "none",
|
|
13821
|
+
}, children: ["i", open && (jsx("span", { style: {
|
|
13822
|
+
position: "absolute",
|
|
13823
|
+
bottom: "calc(100% + 6px)",
|
|
13824
|
+
right: 0,
|
|
13825
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
13826
|
+
border: "1px solid var(--bw-border-color)",
|
|
13827
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
13828
|
+
boxShadow: "var(--bw-shadow-md)",
|
|
13829
|
+
padding: "6px 10px",
|
|
13830
|
+
fontSize: "14px",
|
|
13831
|
+
color: "var(--bw-text-color)",
|
|
13832
|
+
fontWeight: 400,
|
|
13833
|
+
whiteSpace: "normal",
|
|
13834
|
+
width: "160px",
|
|
13835
|
+
lineHeight: 1.4,
|
|
13836
|
+
zIndex: 100,
|
|
13837
|
+
textAlign: "left",
|
|
13838
|
+
pointerEvents: "none",
|
|
13839
|
+
}, children: text }))] }));
|
|
13840
|
+
}
|
|
13841
|
+
function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
|
|
13672
13842
|
const t = useTranslations();
|
|
13843
|
+
const { locale } = useLocale();
|
|
13844
|
+
const timezone = useTimezone();
|
|
13673
13845
|
// State for details dialog
|
|
13674
13846
|
const [detailsDialogOpen, setDetailsDialogOpen] = useState(false);
|
|
13675
13847
|
const [selectedEventTypeForDetails, setSelectedEventTypeForDetails] = useState(null);
|
|
@@ -13779,7 +13951,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
13779
13951
|
display: "flex",
|
|
13780
13952
|
flexDirection: "column",
|
|
13781
13953
|
justifyContent: "space-between",
|
|
13782
|
-
height: "
|
|
13954
|
+
height: "490px",
|
|
13783
13955
|
}, children: [jsxs("div", { children: [jsx("h2", { className: "event-type-title", style: {
|
|
13784
13956
|
fontSize: "clamp(1.1rem, 2.5vw, 24px)",
|
|
13785
13957
|
fontWeight: 700,
|
|
@@ -13860,7 +14032,43 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
13860
14032
|
color: "var(--bw-text-color)",
|
|
13861
14033
|
fontFamily: "var(--bw-font-family)",
|
|
13862
14034
|
textAlign: "right",
|
|
13863
|
-
}, children: jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }),
|
|
14035
|
+
}, children: jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), (() => {
|
|
14036
|
+
const preview = eventType.cardPreview ?? [];
|
|
14037
|
+
return (jsxs("div", { style: {
|
|
14038
|
+
marginTop: "12px",
|
|
14039
|
+
borderTop: "1px solid var(--bw-border-color)",
|
|
14040
|
+
paddingTop: "8px",
|
|
14041
|
+
marginBottom: "16px",
|
|
14042
|
+
}, children: [jsx("div", { style: {
|
|
14043
|
+
fontSize: "11px",
|
|
14044
|
+
fontWeight: 700,
|
|
14045
|
+
color: "var(--bw-text-muted)",
|
|
14046
|
+
textTransform: "uppercase",
|
|
14047
|
+
letterSpacing: "0.05em",
|
|
14048
|
+
marginBottom: "4px",
|
|
14049
|
+
}, children: t("events.previewSectionTitle") }), jsx("div", { style: { height: "102px" }, children: Array.from({ length: 3 }).map((_, i) => {
|
|
14050
|
+
const item = preview[i];
|
|
14051
|
+
if (!item)
|
|
14052
|
+
return jsx("div", { style: { height: "34px" } }, i);
|
|
14053
|
+
const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
|
|
14054
|
+
return (jsxs("div", { onClick: (e) => { e.stopPropagation(); onInstancePreview?.(item.id, eventType.id); }, onMouseEnter: (e) => { if (onInstancePreview)
|
|
14055
|
+
e.currentTarget.style.backgroundColor = "var(--bw-border-color)"; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
|
|
14056
|
+
height: "34px",
|
|
14057
|
+
display: "grid",
|
|
14058
|
+
gridTemplateColumns: "auto 1fr auto 20px",
|
|
14059
|
+
alignItems: "center",
|
|
14060
|
+
gap: "8px",
|
|
14061
|
+
width: "100%",
|
|
14062
|
+
fontSize: "13px",
|
|
14063
|
+
color: "var(--bw-text-muted)",
|
|
14064
|
+
cursor: onInstancePreview ? "pointer" : "default",
|
|
14065
|
+
borderRadius: "4px",
|
|
14066
|
+
padding: "0 2px",
|
|
14067
|
+
transition: "background 0.15s",
|
|
14068
|
+
boxSizing: "border-box",
|
|
14069
|
+
}, children: [jsxs("span", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: "3px" }, children: [item.isSpecial && (jsx("span", { style: { color: "var(--bw-highlight-color)", fontSize: "11px", lineHeight: 1 }, children: "\u2605" })), formatWeekday(item.startTime, timezone, locale), " ", formatDate(item.startTime, timezone, locale)] }), jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, children: item.name }), jsxs("span", { style: { display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [hasDiscount && (jsx("span", { style: { textDecoration: "line-through", opacity: 0.55, fontSize: "11px" }, children: formatCurrency(item.basePrice) })), jsx("span", { style: { fontWeight: 700, color: item.isSpecial ? "var(--bw-highlight-color)" : "var(--bw-text-color)" }, children: formatCurrency(item.price) })] }), item.specialDescription ? (jsx(InfoBadge, { text: item.specialDescription })) : (jsx("span", {}))] }, item.id));
|
|
14070
|
+
}) })] }));
|
|
14071
|
+
})(), jsxs("div", { style: {
|
|
13864
14072
|
display: "flex",
|
|
13865
14073
|
justifyContent: "flex-end",
|
|
13866
14074
|
alignItems: "center",
|
|
@@ -13875,7 +14083,6 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
13875
14083
|
backgroundColor: "var(--bw-surface-color)",
|
|
13876
14084
|
padding: "12px",
|
|
13877
14085
|
borderRadius: "var(--bw-border-radius)",
|
|
13878
|
-
fontSize: "clamp(0.8rem, 2vw, 16px)",
|
|
13879
14086
|
fontWeight: 600,
|
|
13880
14087
|
fontFamily: "var(--bw-font-family)",
|
|
13881
14088
|
display: "flex",
|
|
@@ -13888,7 +14095,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
13888
14095
|
}, children: t("button.moreDetails") })), isAvailable && (jsxs("div", { style: {
|
|
13889
14096
|
backgroundColor: "var(--bw-highlight-color)",
|
|
13890
14097
|
color: "var(--bw-surface-color)",
|
|
13891
|
-
padding: "12px
|
|
14098
|
+
padding: "12px 14px",
|
|
13892
14099
|
borderRadius: "var(--bw-border-radius)",
|
|
13893
14100
|
fontSize: "clamp(1rem, 2vw, 16px)",
|
|
13894
14101
|
fontWeight: 600,
|
|
@@ -14546,6 +14753,172 @@ function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText
|
|
|
14546
14753
|
}, children: "\u27F3" }), t("common.loading")] })) : (showAllButtonText) }) }))] }));
|
|
14547
14754
|
}
|
|
14548
14755
|
|
|
14756
|
+
function SpecialsView({ specials, onEventSelect, isLoading = false, showSavingsAmount = true, showSavingsPercent = false, emptyStateText, isLoadingEventDetails = false, }) {
|
|
14757
|
+
const t = useTranslations();
|
|
14758
|
+
const { locale } = useLocale();
|
|
14759
|
+
const timezone = useTimezone();
|
|
14760
|
+
const [selectedId, setSelectedId] = useState(null);
|
|
14761
|
+
const handleSelect = (id) => {
|
|
14762
|
+
setSelectedId(id);
|
|
14763
|
+
onEventSelect(id);
|
|
14764
|
+
};
|
|
14765
|
+
if (isLoading) {
|
|
14766
|
+
return jsx(NextEventsSkeleton, { count: 3 });
|
|
14767
|
+
}
|
|
14768
|
+
if (specials.length === 0) {
|
|
14769
|
+
return (jsx("div", { style: { maxWidth: "500px", margin: "0 auto", padding: "16px" }, children: jsxs("div", { style: {
|
|
14770
|
+
display: "flex",
|
|
14771
|
+
flexDirection: "column",
|
|
14772
|
+
alignItems: "center",
|
|
14773
|
+
justifyContent: "center",
|
|
14774
|
+
textAlign: "center",
|
|
14775
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
14776
|
+
border: "1px solid var(--bw-border-color)",
|
|
14777
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14778
|
+
padding: "24px",
|
|
14779
|
+
fontFamily: "var(--bw-font-family)",
|
|
14780
|
+
minHeight: "300px",
|
|
14781
|
+
}, children: [jsx("div", { style: {
|
|
14782
|
+
display: "flex",
|
|
14783
|
+
alignItems: "center",
|
|
14784
|
+
justifyContent: "center",
|
|
14785
|
+
borderRadius: "50%",
|
|
14786
|
+
width: "64px",
|
|
14787
|
+
height: "64px",
|
|
14788
|
+
backgroundColor: "var(--bw-highlight-color)",
|
|
14789
|
+
marginBottom: "16px",
|
|
14790
|
+
fontSize: "32px",
|
|
14791
|
+
color: "#ffffff",
|
|
14792
|
+
opacity: 0.8,
|
|
14793
|
+
}, children: "\uD83C\uDFF7\uFE0F" }), jsx("h3", { style: {
|
|
14794
|
+
fontWeight: 600,
|
|
14795
|
+
margin: "0 0 8px 0",
|
|
14796
|
+
fontSize: "20px",
|
|
14797
|
+
color: "var(--bw-text-color)",
|
|
14798
|
+
fontFamily: "var(--bw-font-family)",
|
|
14799
|
+
}, children: t("specials.noSpecials") }), jsx("p", { style: {
|
|
14800
|
+
margin: 0,
|
|
14801
|
+
color: "var(--bw-text-muted)",
|
|
14802
|
+
fontSize: "16px",
|
|
14803
|
+
lineHeight: 1.6,
|
|
14804
|
+
fontFamily: "var(--bw-font-family)",
|
|
14805
|
+
maxWidth: "400px",
|
|
14806
|
+
}, children: emptyStateText ?? t("specials.noSpecialsMessage") })] }) }));
|
|
14807
|
+
}
|
|
14808
|
+
return (jsxs("div", { style: {
|
|
14809
|
+
maxWidth: "500px",
|
|
14810
|
+
margin: "0 auto",
|
|
14811
|
+
padding: "16px",
|
|
14812
|
+
fontFamily: "var(--bw-font-family)",
|
|
14813
|
+
}, children: [jsxs("div", { style: { textAlign: "center", marginBottom: "24px" }, children: [jsx("h2", { style: {
|
|
14814
|
+
fontWeight: 600,
|
|
14815
|
+
margin: "0 0 8px 0",
|
|
14816
|
+
fontSize: "18px",
|
|
14817
|
+
color: "var(--bw-text-color)",
|
|
14818
|
+
fontFamily: "var(--bw-font-family)",
|
|
14819
|
+
}, children: t("specials.title") }), jsx("p", { style: {
|
|
14820
|
+
margin: 0,
|
|
14821
|
+
fontSize: "16px",
|
|
14822
|
+
color: "var(--bw-text-muted)",
|
|
14823
|
+
fontFamily: "var(--bw-font-family)",
|
|
14824
|
+
}, children: t("specials.subtitle") })] }), jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: specials.map((special) => {
|
|
14825
|
+
const isFullyBooked = special.availableSpots === 0;
|
|
14826
|
+
const isDisabled = isFullyBooked || !special.bookingOpen;
|
|
14827
|
+
const hasDiscount = special.basePrice > 0 && special.price < special.basePrice;
|
|
14828
|
+
return (jsxs("div", { style: {
|
|
14829
|
+
position: "relative",
|
|
14830
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
14831
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14832
|
+
border: "1px solid var(--bw-highlight-color)",
|
|
14833
|
+
overflow: "hidden",
|
|
14834
|
+
opacity: isDisabled ? 0.5 : 1,
|
|
14835
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
14836
|
+
transition: "all 0.2s ease",
|
|
14837
|
+
}, onClick: () => {
|
|
14838
|
+
if (!isDisabled)
|
|
14839
|
+
handleSelect(special.id);
|
|
14840
|
+
}, children: [selectedId === special.id && isLoadingEventDetails && (jsx("div", { style: {
|
|
14841
|
+
position: "absolute",
|
|
14842
|
+
inset: 0,
|
|
14843
|
+
display: "flex",
|
|
14844
|
+
alignItems: "center",
|
|
14845
|
+
justifyContent: "center",
|
|
14846
|
+
backgroundColor: "rgba(15, 23, 42, 0.8)",
|
|
14847
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14848
|
+
zIndex: 10,
|
|
14849
|
+
}, children: jsx("div", { style: { fontSize: "32px", color: "var(--bw-highlight-color)", animation: "spin 1s linear infinite" }, children: "\u27F3" }) })), jsxs("div", { style: { display: "flex", gap: "12px", padding: "12px" }, children: [special.images.length > 0 && (jsx("div", { style: {
|
|
14850
|
+
flexShrink: 0,
|
|
14851
|
+
width: "72px",
|
|
14852
|
+
height: "72px",
|
|
14853
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
14854
|
+
overflow: "hidden",
|
|
14855
|
+
}, children: jsx("img", { src: special.images[0], alt: special.eventTypeName, style: { width: "100%", height: "100%", objectFit: "cover" } }) })), jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [jsx("div", { style: { marginBottom: "4px" }, children: jsx("span", { style: {
|
|
14856
|
+
fontSize: "11px",
|
|
14857
|
+
fontWeight: 600,
|
|
14858
|
+
color: "var(--bw-text-muted)",
|
|
14859
|
+
textTransform: "uppercase",
|
|
14860
|
+
letterSpacing: "0.05em",
|
|
14861
|
+
}, children: special.categoryName }) }), jsx("h4", { style: {
|
|
14862
|
+
margin: "0 0 2px 0",
|
|
14863
|
+
fontSize: "15px",
|
|
14864
|
+
fontWeight: 600,
|
|
14865
|
+
color: "var(--bw-text-color)",
|
|
14866
|
+
lineHeight: 1.3,
|
|
14867
|
+
overflow: "hidden",
|
|
14868
|
+
textOverflow: "ellipsis",
|
|
14869
|
+
whiteSpace: "nowrap",
|
|
14870
|
+
}, children: special.eventTypeName }), jsxs("div", { style: {
|
|
14871
|
+
fontSize: "13px",
|
|
14872
|
+
fontWeight: 500,
|
|
14873
|
+
color: "var(--bw-highlight-color)",
|
|
14874
|
+
marginBottom: "2px",
|
|
14875
|
+
overflow: "hidden",
|
|
14876
|
+
textOverflow: "ellipsis",
|
|
14877
|
+
whiteSpace: "nowrap",
|
|
14878
|
+
}, children: ["\u2605 ", special.name] }), special.specialDescription && (jsx("div", { style: {
|
|
14879
|
+
fontSize: "12px",
|
|
14880
|
+
color: "var(--bw-text-muted)",
|
|
14881
|
+
marginBottom: "6px",
|
|
14882
|
+
lineHeight: 1.4,
|
|
14883
|
+
}, children: special.specialDescription })), jsxs("div", { style: { fontSize: "13px", color: "var(--bw-text-muted)", marginBottom: "8px" }, children: [formatWeekday(special.startTime, timezone, locale), ",", " ", formatDate(special.startTime, timezone, locale)] }), jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }, children: [hasDiscount && (jsx("span", { style: {
|
|
14884
|
+
fontSize: "13px",
|
|
14885
|
+
color: "var(--bw-text-muted)",
|
|
14886
|
+
textDecoration: "line-through",
|
|
14887
|
+
}, children: formatCurrency(special.basePrice) })), jsx("span", { style: {
|
|
14888
|
+
fontSize: "16px",
|
|
14889
|
+
fontWeight: 700,
|
|
14890
|
+
color: "var(--bw-highlight-color)",
|
|
14891
|
+
}, children: formatCurrency(special.price) }), hasDiscount && showSavingsAmount && (jsx("span", { style: {
|
|
14892
|
+
fontSize: "12px",
|
|
14893
|
+
fontWeight: 600,
|
|
14894
|
+
color: "#ffffff",
|
|
14895
|
+
backgroundColor: "var(--bw-success-color, #22c55e)",
|
|
14896
|
+
borderRadius: "4px",
|
|
14897
|
+
padding: "2px 6px",
|
|
14898
|
+
}, children: t("specials.save").replace("{{amount}}", formatCurrency(special.savings)) })), hasDiscount && showSavingsPercent && (jsx("span", { style: {
|
|
14899
|
+
fontSize: "12px",
|
|
14900
|
+
fontWeight: 600,
|
|
14901
|
+
color: "#ffffff",
|
|
14902
|
+
backgroundColor: "var(--bw-success-color, #22c55e)",
|
|
14903
|
+
borderRadius: "4px",
|
|
14904
|
+
padding: "2px 6px",
|
|
14905
|
+
}, children: t("specials.savePercent").replace("{{percent}}", String(special.savingsPercent)) }))] })] })] }), jsxs("div", { style: {
|
|
14906
|
+
borderTop: "1px solid var(--bw-border-color)",
|
|
14907
|
+
padding: "8px 12px",
|
|
14908
|
+
display: "flex",
|
|
14909
|
+
justifyContent: "space-between",
|
|
14910
|
+
alignItems: "center",
|
|
14911
|
+
backgroundColor: "var(--bw-background-color)",
|
|
14912
|
+
}, children: [jsx("span", { style: { fontSize: "12px", color: "var(--bw-text-muted)" }, children: isFullyBooked
|
|
14913
|
+
? t("common.fullyBooked")
|
|
14914
|
+
: t("specials.spotsLeft").replace("{{count}}", String(special.availableSpots)) }), jsxs("span", { style: {
|
|
14915
|
+
fontSize: "13px",
|
|
14916
|
+
fontWeight: 600,
|
|
14917
|
+
color: "var(--bw-highlight-color)",
|
|
14918
|
+
}, children: [t("specials.bookNow"), " \u2192"] })] })] }, special.id));
|
|
14919
|
+
}) })] }));
|
|
14920
|
+
}
|
|
14921
|
+
|
|
14549
14922
|
const getThemeConfig = (theme = "generic") => {
|
|
14550
14923
|
switch (theme) {
|
|
14551
14924
|
case "christmas":
|
|
@@ -14805,8 +15178,8 @@ const cardDisabledStyles = {
|
|
|
14805
15178
|
};
|
|
14806
15179
|
const checkboxContainerStyles = {
|
|
14807
15180
|
position: "absolute",
|
|
14808
|
-
|
|
14809
|
-
|
|
15181
|
+
bottom: "12px",
|
|
15182
|
+
left: "12px",
|
|
14810
15183
|
zIndex: 1,
|
|
14811
15184
|
};
|
|
14812
15185
|
const checkboxInnerStyles = {
|
|
@@ -14901,6 +15274,7 @@ const priceContainerStyles = {
|
|
|
14901
15274
|
alignItems: "flex-end",
|
|
14902
15275
|
marginTop: "8px",
|
|
14903
15276
|
paddingTop: "8px",
|
|
15277
|
+
paddingBottom: "0px",
|
|
14904
15278
|
borderTop: "1px solid var(--bw-border-color)",
|
|
14905
15279
|
};
|
|
14906
15280
|
const pricePerPersonStyles = {
|
|
@@ -14992,7 +15366,7 @@ function UpsellsStep({ upsells, selectedUpsells, participantCount, isLoading, is
|
|
|
14992
15366
|
};
|
|
14993
15367
|
const selectedTotal = calculateTotal();
|
|
14994
15368
|
const selectedCount = selectedUpsells.length;
|
|
14995
|
-
const footerContent = (jsxs(Fragment, { children: [jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), children: t("common.back") }), jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
|
|
15369
|
+
const footerContent = (jsxs(Fragment, { children: [jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), className: buttonClassName, children: t("common.back") }), jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), className: buttonClassName, children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
|
|
14996
15370
|
return (jsx(Sidebar, { isOpen: isOpen, onClose: onClose, title: t("upsells.title"), footer: footerContent, children: jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", padding: "16px 16px" }, children: [isLoading && (jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "12px", padding: "40px 20px", ...textStyles.muted }, children: [spinner(), jsx("span", { children: t("upsells.loading") })] })), !isLoading && upsells.length === 0 && (jsx("div", { style: { textAlign: "center", padding: "40px 20px", ...textStyles.muted }, children: jsx("p", { children: t("upsells.noExtras") }) })), !isLoading && upsells.length > 0 && (jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", flex: 1, overflowY: "auto", paddingBottom: "16px" }, children: upsells.map((upsell) => (jsx(UpsellCard, { upsell: upsell, isSelected: isSelected(upsell.id), participantCount: participantCount, onSelect: () => selectUpsell(upsell.id) }, upsell.id))) })), selectedCount > 0 && (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: [jsx("span", { style: textStyles.muted, children: selectedCount === 1 ? t("upsells.selected", { count: selectedCount }) : t("upsells.selectedPlural", { count: selectedCount }) }), jsxs("span", { style: { fontWeight: 600, color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: ["+", formatCurrency(selectedTotal)] })] }))] }) }));
|
|
14997
15371
|
}
|
|
14998
15372
|
|
|
@@ -15040,6 +15414,8 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15040
15414
|
: (config.voucherIntegration ?? (hasEventSelection && !isDirectInstanceMode));
|
|
15041
15415
|
// Selection flow state
|
|
15042
15416
|
const [currentStep, setCurrentStep] = useState("eventTypes");
|
|
15417
|
+
// Tracks where to return when closing the booking form
|
|
15418
|
+
const bookingReturnStep = useRef("eventInstances");
|
|
15043
15419
|
const [eventTypes, setEventTypes] = useState([]);
|
|
15044
15420
|
const [selectedEventType, setSelectedEventType] = useState(null);
|
|
15045
15421
|
const [eventInstances, setEventInstances] = useState([]);
|
|
@@ -15052,6 +15428,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15052
15428
|
// State for upcoming events (next-events view mode)
|
|
15053
15429
|
const [upcomingEvents, setUpcomingEvents] = useState([]);
|
|
15054
15430
|
const [showingPreview, setShowingPreview] = useState(true);
|
|
15431
|
+
// State for specials view mode
|
|
15432
|
+
const [specials, setSpecials] = useState([]);
|
|
15433
|
+
const [isLoadingSpecials, setIsLoadingSpecials] = useState(false);
|
|
15055
15434
|
// New: sidebar open state for single event type mode
|
|
15056
15435
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
15057
15436
|
// Booking flow state
|
|
@@ -15202,6 +15581,11 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15202
15581
|
await loadUpcomingEvents();
|
|
15203
15582
|
return;
|
|
15204
15583
|
}
|
|
15584
|
+
// Specials view mode: load special offers
|
|
15585
|
+
if (viewMode === "specials") {
|
|
15586
|
+
await loadSpecials();
|
|
15587
|
+
return;
|
|
15588
|
+
}
|
|
15205
15589
|
// Single event type mode: load event type and instances, but don't open sidebar yet
|
|
15206
15590
|
if (isSingleEventTypeMode) {
|
|
15207
15591
|
await loadEventTypes();
|
|
@@ -15425,6 +15809,45 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15425
15809
|
setError(data.error || t("error.loadUpcomingEvents"));
|
|
15426
15810
|
}
|
|
15427
15811
|
};
|
|
15812
|
+
const loadSpecials = async () => {
|
|
15813
|
+
setIsLoadingSpecials(true);
|
|
15814
|
+
const specialsSettings = config.specialsSettings ?? {};
|
|
15815
|
+
const requestBody = {
|
|
15816
|
+
organizationId: config.organizationId,
|
|
15817
|
+
limit: specialsSettings.count ?? 20,
|
|
15818
|
+
};
|
|
15819
|
+
if (config.categoryId) {
|
|
15820
|
+
requestBody.categoryId = config.categoryId;
|
|
15821
|
+
}
|
|
15822
|
+
else if (config.eventTypeIds) {
|
|
15823
|
+
requestBody.eventTypeIds = config.eventTypeIds;
|
|
15824
|
+
}
|
|
15825
|
+
else if (config.eventTypeId) {
|
|
15826
|
+
requestBody.eventTypeId = config.eventTypeId;
|
|
15827
|
+
}
|
|
15828
|
+
try {
|
|
15829
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/specials"), {
|
|
15830
|
+
method: "POST",
|
|
15831
|
+
headers: createApiHeaders(config, locale),
|
|
15832
|
+
body: JSON.stringify(requestBody),
|
|
15833
|
+
});
|
|
15834
|
+
const data = await response.json();
|
|
15835
|
+
if (response.ok) {
|
|
15836
|
+
const wl = extractWidgetLanguagePayload(data);
|
|
15837
|
+
if (wl) {
|
|
15838
|
+
onWidgetLanguage?.(wl);
|
|
15839
|
+
onTimezone?.(wl.timezone);
|
|
15840
|
+
}
|
|
15841
|
+
setSpecials(data.specials || []);
|
|
15842
|
+
}
|
|
15843
|
+
else {
|
|
15844
|
+
setError(data.error || t("error.loadUpcomingEvents"));
|
|
15845
|
+
}
|
|
15846
|
+
}
|
|
15847
|
+
finally {
|
|
15848
|
+
setIsLoadingSpecials(false);
|
|
15849
|
+
}
|
|
15850
|
+
};
|
|
15428
15851
|
const loadEventInstances = async (eventTypeId) => {
|
|
15429
15852
|
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/event-instances"), {
|
|
15430
15853
|
method: "POST",
|
|
@@ -15609,6 +16032,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15609
16032
|
// Event instance selection handlers
|
|
15610
16033
|
const handleEventInstanceSelect = async (eventInstance) => {
|
|
15611
16034
|
setSelectedEventInstance(eventInstance);
|
|
16035
|
+
bookingReturnStep.current = "eventInstances";
|
|
15612
16036
|
// Set default participant count for upsell calculations
|
|
15613
16037
|
const defaultParticipantCount = 1;
|
|
15614
16038
|
setTempParticipantCount(defaultParticipantCount);
|
|
@@ -15621,7 +16045,14 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15621
16045
|
if (availableUpsells.length > 0) {
|
|
15622
16046
|
// Show upsells step
|
|
15623
16047
|
setUpsells(availableUpsells);
|
|
15624
|
-
|
|
16048
|
+
// Pre-select default-checked upsells
|
|
16049
|
+
const defaultSelections = availableUpsells
|
|
16050
|
+
.filter((upsell) => upsell.defaultChecked && upsell.available)
|
|
16051
|
+
.map((upsell) => ({
|
|
16052
|
+
upsellPackageId: upsell.id,
|
|
16053
|
+
quantity: defaultParticipantCount,
|
|
16054
|
+
}));
|
|
16055
|
+
setSelectedUpsells(defaultSelections);
|
|
15625
16056
|
setCurrentStep("upsells");
|
|
15626
16057
|
setIsLoadingUpsells(false);
|
|
15627
16058
|
return; // Don't proceed to booking yet
|
|
@@ -15652,7 +16083,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15652
16083
|
setEventInstances([]);
|
|
15653
16084
|
};
|
|
15654
16085
|
const handleBackToEventInstances = () => {
|
|
15655
|
-
setCurrentStep(
|
|
16086
|
+
setCurrentStep(bookingReturnStep.current);
|
|
15656
16087
|
setSelectedEventInstance(null);
|
|
15657
16088
|
setEventDetails(null);
|
|
15658
16089
|
};
|
|
@@ -15686,8 +16117,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15686
16117
|
}
|
|
15687
16118
|
};
|
|
15688
16119
|
const handleUpsellsBack = () => {
|
|
15689
|
-
|
|
15690
|
-
setCurrentStep("eventInstances");
|
|
16120
|
+
setCurrentStep(bookingReturnStep.current);
|
|
15691
16121
|
setSelectedUpsells([]);
|
|
15692
16122
|
setUpsells([]);
|
|
15693
16123
|
};
|
|
@@ -15722,10 +16152,36 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15722
16152
|
setError(errorMessage);
|
|
15723
16153
|
config.onError?.(errorMessage);
|
|
15724
16154
|
};
|
|
15725
|
-
const handleUpcomingEventSelect = async (eventInstanceId) => {
|
|
16155
|
+
const handleUpcomingEventSelect = async (eventInstanceId, eventTypeId) => {
|
|
16156
|
+
// Resolve the event type — may come from card preview (eventTypeId provided) or
|
|
16157
|
+
// from the next-events list where selectedEventType is already set
|
|
16158
|
+
const resolvedEventType = eventTypeId != null
|
|
16159
|
+
? (eventTypes.find((et) => et.id === eventTypeId) ?? selectedEventType)
|
|
16160
|
+
: selectedEventType;
|
|
16161
|
+
if (resolvedEventType && resolvedEventType !== selectedEventType) {
|
|
16162
|
+
setSelectedEventType(resolvedEventType);
|
|
16163
|
+
}
|
|
16164
|
+
// Check if this is coming from a card preview (eventTypeId was provided)
|
|
16165
|
+
// In that case, we need to load event instances so back navigation works properly
|
|
16166
|
+
const isFromCardPreview = eventTypeId != null;
|
|
16167
|
+
if (isFromCardPreview && resolvedEventType) {
|
|
16168
|
+
// Load event instances in background so back navigation shows instances
|
|
16169
|
+
setShouldRenderInstanceSelection(true);
|
|
16170
|
+
void loadEventInstances(resolvedEventType.id);
|
|
16171
|
+
// Record that we should return to instance selection (not event types)
|
|
16172
|
+
bookingReturnStep.current = "eventInstances";
|
|
16173
|
+
}
|
|
16174
|
+
else {
|
|
16175
|
+
// Record where to return when the booking form is closed
|
|
16176
|
+
bookingReturnStep.current = currentStep === "eventInstances" ? "eventInstances" : "eventTypes";
|
|
16177
|
+
}
|
|
16178
|
+
// First try to find the event in upcomingEvents (for next-events view mode)
|
|
15726
16179
|
const upcomingEvent = upcomingEvents.find((event) => event.id === eventInstanceId);
|
|
15727
|
-
|
|
15728
|
-
|
|
16180
|
+
// If not found in upcomingEvents, try to find in card preview items
|
|
16181
|
+
const cardPreviewItem = !upcomingEvent && resolvedEventType?.cardPreview?.find((item) => item.id === eventInstanceId);
|
|
16182
|
+
// Build the event instance from either source
|
|
16183
|
+
const eventInstance = upcomingEvent
|
|
16184
|
+
? {
|
|
15729
16185
|
id: upcomingEvent.id,
|
|
15730
16186
|
name: upcomingEvent.name,
|
|
15731
16187
|
startTime: upcomingEvent.startTime,
|
|
@@ -15739,13 +16195,63 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15739
16195
|
bookingOpen: upcomingEvent.bookingOpen,
|
|
15740
16196
|
...(upcomingEvent.deposit !== undefined && { deposit: upcomingEvent.deposit }),
|
|
15741
16197
|
...(upcomingEvent.notes !== undefined && { notes: upcomingEvent.notes }),
|
|
15742
|
-
}
|
|
16198
|
+
}
|
|
16199
|
+
: cardPreviewItem
|
|
16200
|
+
? {
|
|
16201
|
+
id: cardPreviewItem.id,
|
|
16202
|
+
name: cardPreviewItem.name,
|
|
16203
|
+
startTime: cardPreviewItem.startTime,
|
|
16204
|
+
endTime: cardPreviewItem.startTime,
|
|
16205
|
+
price: cardPreviewItem.price,
|
|
16206
|
+
maxParticipants: cardPreviewItem.availableSpots + 1,
|
|
16207
|
+
participantCount: 1,
|
|
16208
|
+
availableSpots: cardPreviewItem.availableSpots,
|
|
16209
|
+
durationDays: 1,
|
|
16210
|
+
durationPerDay: 1,
|
|
16211
|
+
bookingOpen: true,
|
|
16212
|
+
}
|
|
16213
|
+
: null;
|
|
16214
|
+
if (eventInstance) {
|
|
15743
16215
|
setSelectedEventInstance(eventInstance);
|
|
15744
16216
|
}
|
|
16217
|
+
setError(null);
|
|
16218
|
+
// Check for upsells before going to booking (same as handleEventInstanceSelect)
|
|
16219
|
+
const eventTypeForUpsells = resolvedEventType;
|
|
16220
|
+
if (eventTypeForUpsells) {
|
|
16221
|
+
const defaultParticipantCount = 1;
|
|
16222
|
+
setTempParticipantCount(defaultParticipantCount);
|
|
16223
|
+
setIsLoadingUpsells(true);
|
|
16224
|
+
setShouldRenderUpsells(true);
|
|
16225
|
+
try {
|
|
16226
|
+
const availableUpsells = await loadUpsells(eventTypeForUpsells.id, eventInstanceId, defaultParticipantCount);
|
|
16227
|
+
if (availableUpsells.length > 0) {
|
|
16228
|
+
setUpsells(availableUpsells);
|
|
16229
|
+
// Pre-select default-checked upsells
|
|
16230
|
+
const defaultSelections = availableUpsells
|
|
16231
|
+
.filter((upsell) => upsell.defaultChecked && upsell.available)
|
|
16232
|
+
.map((upsell) => ({
|
|
16233
|
+
upsellPackageId: upsell.id,
|
|
16234
|
+
quantity: defaultParticipantCount,
|
|
16235
|
+
}));
|
|
16236
|
+
setSelectedUpsells(defaultSelections);
|
|
16237
|
+
setCurrentStep("upsells");
|
|
16238
|
+
setIsLoadingUpsells(false);
|
|
16239
|
+
// Load event details in background for when user continues past upsells
|
|
16240
|
+
void loadEventDetails(eventInstanceId);
|
|
16241
|
+
return;
|
|
16242
|
+
}
|
|
16243
|
+
}
|
|
16244
|
+
catch (err) {
|
|
16245
|
+
console.error("Error loading upsells:", err);
|
|
16246
|
+
}
|
|
16247
|
+
finally {
|
|
16248
|
+
setIsLoadingUpsells(false);
|
|
16249
|
+
}
|
|
16250
|
+
}
|
|
16251
|
+
// No upsells — go directly to booking
|
|
15745
16252
|
setCurrentStep("booking");
|
|
15746
16253
|
setShouldRenderBookingForm(true);
|
|
15747
16254
|
setIsLoadingEventDetails(true);
|
|
15748
|
-
setError(null);
|
|
15749
16255
|
try {
|
|
15750
16256
|
await loadEventDetails(eventInstanceId);
|
|
15751
16257
|
}
|
|
@@ -15902,6 +16408,31 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15902
16408
|
window.history.replaceState({}, "", url.toString());
|
|
15903
16409
|
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
15904
16410
|
}
|
|
16411
|
+
if (viewMode === "specials" && showingPreview) {
|
|
16412
|
+
return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [jsx(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
|
|
16413
|
+
setCurrentStep("eventTypes");
|
|
16414
|
+
setShowingPreview(true);
|
|
16415
|
+
setEventDetails(null);
|
|
16416
|
+
}, onBackToEventTypes: () => {
|
|
16417
|
+
setCurrentStep("eventTypes");
|
|
16418
|
+
setShowingPreview(true);
|
|
16419
|
+
setEventDetails(null);
|
|
16420
|
+
}, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => {
|
|
16421
|
+
setCurrentStep("eventTypes");
|
|
16422
|
+
setShowingPreview(true);
|
|
16423
|
+
setEventDetails(null);
|
|
16424
|
+
}, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
16425
|
+
setIsSuccess(false);
|
|
16426
|
+
setCurrentStep("eventTypes");
|
|
16427
|
+
setShowingPreview(true);
|
|
16428
|
+
setSuccessPaymentId(null);
|
|
16429
|
+
setShouldRenderInstanceSelection(false);
|
|
16430
|
+
setShouldRenderUpsells(false);
|
|
16431
|
+
setShouldRenderBookingForm(false);
|
|
16432
|
+
setSelectedUpsells([]);
|
|
16433
|
+
setUpsells([]);
|
|
16434
|
+
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16435
|
+
}
|
|
15905
16436
|
if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
|
|
15906
16437
|
return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
|
|
15907
16438
|
setShowingPreview(true);
|
|
@@ -15972,7 +16503,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15972
16503
|
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
15973
16504
|
}
|
|
15974
16505
|
// Cards mode (default) - show event type selection with optional voucher card
|
|
15975
|
-
const cardsView = (jsxs(Fragment, { children: [hasEventSelection && (jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (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 && (jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsx("div", { style: {
|
|
16506
|
+
const cardsView = (jsxs(Fragment, { children: [hasEventSelection && (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 && (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 && (jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsx("div", { style: {
|
|
15976
16507
|
display: "inline-block",
|
|
15977
16508
|
width: "32px",
|
|
15978
16509
|
height: "32px",
|
|
@@ -16040,11 +16571,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16040
16571
|
function UniversalBookingWidget(props) {
|
|
16041
16572
|
const [languagePolicy, setLanguagePolicy] = useState(null);
|
|
16042
16573
|
const [orgTimezone, setOrgTimezone] = useState("Europe/Berlin");
|
|
16043
|
-
const
|
|
16044
|
-
? languagePolicy.organizationLocale
|
|
16045
|
-
: undefined;
|
|
16046
|
-
const i18nConfigLocale = serverLockedLocale !== undefined ? serverLockedLocale : props.config.locale;
|
|
16047
|
-
const providerProps = i18nConfigLocale ? { configLocale: i18nConfigLocale } : {};
|
|
16574
|
+
const providerProps = props.config.locale ? { configLocale: props.config.locale } : {};
|
|
16048
16575
|
const showLanguagePicker = props.config.showLanguagePicker !== false &&
|
|
16049
16576
|
(languagePolicy === null || languagePolicy.multiLanguageEnabled);
|
|
16050
16577
|
return (jsx(I18nProvider, { ...providerProps, children: jsx(ShowLanguagePickerProvider, { value: showLanguagePicker, children: jsx(TimezoneProvider, { value: orgTimezone, children: jsx(UniversalBookingWidgetInner, { ...props, onWidgetLanguage: setLanguagePolicy, onTimezone: setOrgTimezone }) }) }) }));
|
|
@@ -16077,7 +16604,7 @@ function styleInject(css, ref) {
|
|
|
16077
16604
|
}
|
|
16078
16605
|
}
|
|
16079
16606
|
|
|
16080
|
-
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}}";
|
|
16607
|
+
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}}";
|
|
16081
16608
|
styleInject(css_248z);
|
|
16082
16609
|
|
|
16083
16610
|
// Export init function for vanilla JS usage
|