@bigz-app/booking-widget 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/booking-widget.js +233 -131
- package/dist/booking-widget.js.map +1 -1
- package/dist/components/UniversalBookingWidget.d.ts +0 -7
- 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 +3 -1
- package/dist/components/booking/BookingSuccessModal.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 +233 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +234 -132
- package/dist/index.esm.js.map +1 -1
- package/dist/styles/StyleProvider.d.ts.map +1 -1
- package/dist/styles/shared-styles.d.ts.map +1 -1
- package/dist/utils/google-ads-tracking.d.ts +9 -19
- package/dist/utils/google-ads-tracking.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/booking-widget.js
CHANGED
|
@@ -338,6 +338,8 @@
|
|
|
338
338
|
"booking.participantName": "Name *",
|
|
339
339
|
"booking.participantNamePlaceholder": "Teilnehmername",
|
|
340
340
|
"booking.participantAge": "Alter",
|
|
341
|
+
"booking.participantLevel": "Level",
|
|
342
|
+
"booking.participantLevelPlaceholder": "Level wählen...",
|
|
341
343
|
"booking.addParticipant": "{{number}}. Teilnehmer hinzufügen",
|
|
342
344
|
"booking.maxParticipants": "Maximale Anzahl an Teilnehmern erreicht. Es sind nur noch {{count}} Plätze verfügbar.",
|
|
343
345
|
"booking.maxSpotsReached": "Maximal {{count}} Plätze verfügbar.",
|
|
@@ -486,7 +488,11 @@
|
|
|
486
488
|
"validation.emailInvalid": "Ungültiges E-Mail-Format",
|
|
487
489
|
"validation.emailDomainInvalid": "Ungültige E-Mail-Domain",
|
|
488
490
|
"validation.participantRequired": "Mindestens ein Teilnehmer erforderlich",
|
|
491
|
+
"validation.ageRequired": "Alter ist erforderlich",
|
|
492
|
+
"validation.levelRequired": "Bitte ein Level auswählen",
|
|
489
493
|
"validation.acceptTerms": "Bitte akzeptiere die Allgemeinen Geschäftsbedingungen",
|
|
494
|
+
"level.beginner": "Anfänger",
|
|
495
|
+
"level.advanced": "Fortgeschritten",
|
|
490
496
|
// Sidebar
|
|
491
497
|
"sidebar.close": "Schließen",
|
|
492
498
|
// Promo
|
|
@@ -608,6 +614,8 @@
|
|
|
608
614
|
"booking.participantName": "Name *",
|
|
609
615
|
"booking.participantNamePlaceholder": "Participant name",
|
|
610
616
|
"booking.participantAge": "Age",
|
|
617
|
+
"booking.participantLevel": "Level",
|
|
618
|
+
"booking.participantLevelPlaceholder": "Select level...",
|
|
611
619
|
"booking.addParticipant": "Add participant {{number}}",
|
|
612
620
|
"booking.maxParticipants": "Maximum number of participants reached. Only {{count}} spots are available.",
|
|
613
621
|
"booking.maxSpotsReached": "Maximum {{count}} spots available.",
|
|
@@ -756,7 +764,11 @@
|
|
|
756
764
|
"validation.emailInvalid": "Invalid email format",
|
|
757
765
|
"validation.emailDomainInvalid": "Invalid email domain",
|
|
758
766
|
"validation.participantRequired": "At least one participant is required",
|
|
767
|
+
"validation.ageRequired": "Age is required",
|
|
768
|
+
"validation.levelRequired": "Please select a level",
|
|
759
769
|
"validation.acceptTerms": "Please accept the terms and conditions",
|
|
770
|
+
"level.beginner": "Beginner",
|
|
771
|
+
"level.advanced": "Advanced",
|
|
760
772
|
// Sidebar
|
|
761
773
|
"sidebar.close": "Close",
|
|
762
774
|
// Promo
|
|
@@ -878,6 +890,8 @@
|
|
|
878
890
|
"booking.participantName": "Nombre *",
|
|
879
891
|
"booking.participantNamePlaceholder": "Nombre del participante",
|
|
880
892
|
"booking.participantAge": "Edad",
|
|
893
|
+
"booking.participantLevel": "Nivel",
|
|
894
|
+
"booking.participantLevelPlaceholder": "Seleccionar nivel...",
|
|
881
895
|
"booking.addParticipant": "Añadir participante {{number}}",
|
|
882
896
|
"booking.maxParticipants": "Número máximo de participantes alcanzado. Solo quedan {{count}} plazas disponibles.",
|
|
883
897
|
"booking.maxSpotsReached": "Máximo {{count}} plazas disponibles.",
|
|
@@ -1026,7 +1040,11 @@
|
|
|
1026
1040
|
"validation.emailInvalid": "Formato de correo electrónico inválido",
|
|
1027
1041
|
"validation.emailDomainInvalid": "Dominio de correo electrónico inválido",
|
|
1028
1042
|
"validation.participantRequired": "Se requiere al menos un participante",
|
|
1043
|
+
"validation.ageRequired": "La edad es obligatoria",
|
|
1044
|
+
"validation.levelRequired": "Selecciona un nivel",
|
|
1029
1045
|
"validation.acceptTerms": "Por favor, acepta los términos y condiciones",
|
|
1046
|
+
"level.beginner": "Principiante",
|
|
1047
|
+
"level.advanced": "Avanzado",
|
|
1030
1048
|
// Sidebar
|
|
1031
1049
|
"sidebar.close": "Cerrar",
|
|
1032
1050
|
// Promo
|
|
@@ -1148,6 +1166,8 @@
|
|
|
1148
1166
|
"booking.participantName": "Nome *",
|
|
1149
1167
|
"booking.participantNamePlaceholder": "Nome do participante",
|
|
1150
1168
|
"booking.participantAge": "Idade",
|
|
1169
|
+
"booking.participantLevel": "Nível",
|
|
1170
|
+
"booking.participantLevelPlaceholder": "Selecionar nível...",
|
|
1151
1171
|
"booking.addParticipant": "Adicionar participante {{number}}",
|
|
1152
1172
|
"booking.maxParticipants": "Número máximo de participantes atingido. Apenas {{count}} lugares disponíveis.",
|
|
1153
1173
|
"booking.maxSpotsReached": "Máximo {{count}} lugares disponíveis.",
|
|
@@ -1296,7 +1316,11 @@
|
|
|
1296
1316
|
"validation.emailInvalid": "Formato de email inválido",
|
|
1297
1317
|
"validation.emailDomainInvalid": "Domínio de email inválido",
|
|
1298
1318
|
"validation.participantRequired": "É necessário pelo menos um participante",
|
|
1319
|
+
"validation.ageRequired": "A idade é obrigatória",
|
|
1320
|
+
"validation.levelRequired": "Por favor selecione um nível",
|
|
1299
1321
|
"validation.acceptTerms": "Por favor, aceite os termos e condições",
|
|
1322
|
+
"level.beginner": "Iniciante",
|
|
1323
|
+
"level.advanced": "Avançado",
|
|
1300
1324
|
// Sidebar
|
|
1301
1325
|
"sidebar.close": "Fechar",
|
|
1302
1326
|
// Promo
|
|
@@ -1418,6 +1442,8 @@
|
|
|
1418
1442
|
"booking.participantName": "Namn *",
|
|
1419
1443
|
"booking.participantNamePlaceholder": "Deltagarens namn",
|
|
1420
1444
|
"booking.participantAge": "Ålder",
|
|
1445
|
+
"booking.participantLevel": "Nivå",
|
|
1446
|
+
"booking.participantLevelPlaceholder": "Välj nivå...",
|
|
1421
1447
|
"booking.addParticipant": "Lägg till deltagare {{number}}",
|
|
1422
1448
|
"booking.maxParticipants": "Maximalt antal deltagare uppnått. Bara {{count}} platser är tillgängliga.",
|
|
1423
1449
|
"booking.maxSpotsReached": "Maximalt {{count}} platser tillgängliga.",
|
|
@@ -1566,7 +1592,11 @@
|
|
|
1566
1592
|
"validation.emailInvalid": "Ogiltigt e-postformat",
|
|
1567
1593
|
"validation.emailDomainInvalid": "Ogiltig e-postdomän",
|
|
1568
1594
|
"validation.participantRequired": "Minst en deltagare krävs",
|
|
1595
|
+
"validation.ageRequired": "Ålder krävs",
|
|
1596
|
+
"validation.levelRequired": "Välj en nivå",
|
|
1569
1597
|
"validation.acceptTerms": "Acceptera villkoren",
|
|
1598
|
+
"level.beginner": "Nybörjare",
|
|
1599
|
+
"level.advanced": "Avancerad",
|
|
1570
1600
|
// Sidebar
|
|
1571
1601
|
"sidebar.close": "Stäng",
|
|
1572
1602
|
// Promo
|
|
@@ -1659,18 +1689,9 @@
|
|
|
1659
1689
|
}
|
|
1660
1690
|
const I18nContext = R$2(null);
|
|
1661
1691
|
function I18nProvider({ configLocale, children }) {
|
|
1662
|
-
// Priority:
|
|
1663
|
-
//
|
|
1664
|
-
const [overrideLocale, setOverrideLocale] = d$1(() =>
|
|
1665
|
-
if (configLocale)
|
|
1666
|
-
return null;
|
|
1667
|
-
return readPersistedLocale();
|
|
1668
|
-
});
|
|
1669
|
-
y$1(() => {
|
|
1670
|
-
if (configLocale) {
|
|
1671
|
-
setOverrideLocale(null);
|
|
1672
|
-
}
|
|
1673
|
-
}, [configLocale]);
|
|
1692
|
+
// Priority: persisted user choice > configLocale (org default) > browser language > "de"
|
|
1693
|
+
// This keeps org locale as default, but remembers explicit user overrides across reloads.
|
|
1694
|
+
const [overrideLocale, setOverrideLocale] = d$1(() => readPersistedLocale());
|
|
1674
1695
|
const locale = overrideLocale ?? resolveLocale(configLocale);
|
|
1675
1696
|
const handleSetLocale = q$2((next) => {
|
|
1676
1697
|
persistLocale(next);
|
|
@@ -1802,6 +1823,16 @@
|
|
|
1802
1823
|
// If semantic resolution fails, use fallback or return the original value
|
|
1803
1824
|
return fallbackValue || colorValue;
|
|
1804
1825
|
};
|
|
1826
|
+
// Legacy theme name redirects (old name → new name)
|
|
1827
|
+
const legacyThemeRedirects = {
|
|
1828
|
+
"light-fresh": "teal-minimal",
|
|
1829
|
+
"light-elegant": "blue-business",
|
|
1830
|
+
"light-vibrant": "orange-raw",
|
|
1831
|
+
"light-professional": "blue-business",
|
|
1832
|
+
"dark-night": "navy-night",
|
|
1833
|
+
"dark-modern": "navy-night",
|
|
1834
|
+
"dark-forest": "green-deep",
|
|
1835
|
+
};
|
|
1805
1836
|
// Predefined themes
|
|
1806
1837
|
const themes = {
|
|
1807
1838
|
// --- Light Themes ---
|
|
@@ -1948,7 +1979,9 @@
|
|
|
1948
1979
|
}, []);
|
|
1949
1980
|
// PERFORMANCE OPTIMIZATION: Memoize style calculations
|
|
1950
1981
|
const themedStyles = T$2(() => {
|
|
1951
|
-
const
|
|
1982
|
+
const rawThemeName = config.theme || "teal-minimal";
|
|
1983
|
+
// Redirect legacy theme names to new names
|
|
1984
|
+
const themeName = legacyThemeRedirects[rawThemeName] || rawThemeName;
|
|
1952
1985
|
const themeDefaults = themes[themeName] || themes["teal-minimal"];
|
|
1953
1986
|
const getCSSValue = (value, fallback) => {
|
|
1954
1987
|
if (!value)
|
|
@@ -11309,16 +11342,37 @@
|
|
|
11309
11342
|
ZodUnion.create;
|
|
11310
11343
|
ZodIntersection.create;
|
|
11311
11344
|
ZodTuple.create;
|
|
11312
|
-
ZodEnum.create;
|
|
11345
|
+
const enumType = ZodEnum.create;
|
|
11313
11346
|
ZodPromise.create;
|
|
11314
11347
|
ZodOptional.create;
|
|
11315
11348
|
ZodNullable.create;
|
|
11349
|
+
const preprocessType = ZodEffects.createWithPreprocess;
|
|
11316
11350
|
|
|
11317
|
-
const
|
|
11318
|
-
name:
|
|
11351
|
+
const DEFAULT_PARTICIPANT_FIELDS_CONFIG = {
|
|
11352
|
+
name: { enabled: true, required: true },
|
|
11353
|
+
age: { enabled: true, required: false },
|
|
11354
|
+
level: { enabled: false, required: false },
|
|
11355
|
+
};
|
|
11356
|
+
const participantSchema = (t, fieldsConfig) => objectType({
|
|
11357
|
+
name: stringType().trim().optional(),
|
|
11319
11358
|
age: numberType().min(0).max(120).optional(),
|
|
11359
|
+
level: preprocessType((value) => (value === "" ? undefined : value), enumType(["beginner", "advanced"]).optional()),
|
|
11360
|
+
})
|
|
11361
|
+
.superRefine((value, ctx) => {
|
|
11362
|
+
if (fieldsConfig.name.required && (!value.name || value.name.trim().length < 1)) {
|
|
11363
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.nameRequired"), path: ["name"] });
|
|
11364
|
+
}
|
|
11365
|
+
if (!fieldsConfig.name.enabled && value.name && value.name.trim().length > 0) {
|
|
11366
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.nameRequired"), path: ["name"] });
|
|
11367
|
+
}
|
|
11368
|
+
if (fieldsConfig.age.required && typeof value.age !== "number") {
|
|
11369
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.ageRequired"), path: ["age"] });
|
|
11370
|
+
}
|
|
11371
|
+
if (fieldsConfig.level.required && !value.level) {
|
|
11372
|
+
ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.levelRequired"), path: ["level"] });
|
|
11373
|
+
}
|
|
11320
11374
|
});
|
|
11321
|
-
function createBookingFormSchema(t) {
|
|
11375
|
+
function createBookingFormSchema(t, fieldsConfig = DEFAULT_PARTICIPANT_FIELDS_CONFIG) {
|
|
11322
11376
|
const tr = t ?? ((key) => key);
|
|
11323
11377
|
return objectType({
|
|
11324
11378
|
customerName: stringType().trim().min(2, tr("validation.nameMinLength")),
|
|
@@ -11328,7 +11382,7 @@
|
|
|
11328
11382
|
.email(tr("validation.emailInvalid"))
|
|
11329
11383
|
.regex(/\.[a-zA-Z]{2,}$/, tr("validation.emailDomainInvalid")),
|
|
11330
11384
|
customerPhone: stringType().trim().optional(),
|
|
11331
|
-
participants: arrayType(participantSchema(tr)).min(1, tr("validation.participantRequired")),
|
|
11385
|
+
participants: arrayType(participantSchema(tr, fieldsConfig)).min(1, tr("validation.participantRequired")),
|
|
11332
11386
|
discountCode: stringType().trim().optional(),
|
|
11333
11387
|
comment: stringType().trim().optional(),
|
|
11334
11388
|
acceptTerms: booleanType().refine((val) => val === true, {
|
|
@@ -11471,7 +11525,8 @@
|
|
|
11471
11525
|
gap: "8px",
|
|
11472
11526
|
marginTop: "10px",
|
|
11473
11527
|
paddingTop: "10px",
|
|
11474
|
-
|
|
11528
|
+
paddingBottom: "25px",
|
|
11529
|
+
borderBottom: "1px dashed var(--bw-border-color)",
|
|
11475
11530
|
},
|
|
11476
11531
|
label: {
|
|
11477
11532
|
display: "inline-flex",
|
|
@@ -11529,6 +11584,8 @@
|
|
|
11529
11584
|
const { locale } = useLocale();
|
|
11530
11585
|
const timezone = useTimezone();
|
|
11531
11586
|
const roundEnabled = systemConfig?.roundPricesEnabled !== false;
|
|
11587
|
+
const participantFieldsConfig = eventDetails.participantFieldsConfig ?? DEFAULT_PARTICIPANT_FIELDS_CONFIG;
|
|
11588
|
+
const participantLevelOptions = eventDetails.participantLevelOptions ?? ["beginner", "advanced"];
|
|
11532
11589
|
const roundDiscountUp = (minorUnits) => Math.ceil(minorUnits / 100) * 100;
|
|
11533
11590
|
const calcPercentDiscountAmount = (baseAmount, basisPoints, round) => {
|
|
11534
11591
|
const raw = Math.round((baseAmount * basisPoints) / 10000);
|
|
@@ -11541,18 +11598,19 @@
|
|
|
11541
11598
|
// Per-participant upsell selections: participantIndex -> array of upsell package IDs
|
|
11542
11599
|
const [participantUpsells, setParticipantUpsells] = d$1({});
|
|
11543
11600
|
const form = useForm({
|
|
11544
|
-
resolver: t(createBookingFormSchema(t$1)),
|
|
11601
|
+
resolver: t(createBookingFormSchema(t$1, participantFieldsConfig)),
|
|
11545
11602
|
defaultValues: {
|
|
11546
11603
|
customerName: "",
|
|
11547
11604
|
customerEmail: "",
|
|
11548
11605
|
customerPhone: "",
|
|
11549
|
-
participants: [{ name: "" }],
|
|
11606
|
+
participants: [{ name: "", level: undefined }],
|
|
11550
11607
|
discountCode: "",
|
|
11551
11608
|
comment: "",
|
|
11552
11609
|
acceptTerms: false,
|
|
11553
11610
|
},
|
|
11554
11611
|
});
|
|
11555
11612
|
const watchedParticipants = form.watch("participants");
|
|
11613
|
+
const participantCount = watchedParticipants.length;
|
|
11556
11614
|
const watchedCustomerName = form.watch("customerName");
|
|
11557
11615
|
const watchedCustomerEmail = form.watch("customerEmail");
|
|
11558
11616
|
const watchedComment = form.watch("comment");
|
|
@@ -11594,14 +11652,13 @@
|
|
|
11594
11652
|
const calculateBaseTotal = q$2(() => {
|
|
11595
11653
|
if (!eventDetails)
|
|
11596
11654
|
return 0;
|
|
11597
|
-
return eventDetails.price *
|
|
11598
|
-
}, [eventDetails,
|
|
11655
|
+
return eventDetails.price * participantCount;
|
|
11656
|
+
}, [eventDetails, participantCount]);
|
|
11599
11657
|
// Calculate upsells total based on per-participant selections
|
|
11600
11658
|
const calculateUpsellsTotal = q$2(() => {
|
|
11601
11659
|
let total = 0;
|
|
11602
|
-
watchedParticipants.forEach((
|
|
11603
|
-
|
|
11604
|
-
if (participant.name.trim()) {
|
|
11660
|
+
watchedParticipants.forEach((_, index) => {
|
|
11661
|
+
if (participantCount > 0) {
|
|
11605
11662
|
const participantUpsellIds = participantUpsells[index] || [];
|
|
11606
11663
|
participantUpsellIds.forEach(upsellId => {
|
|
11607
11664
|
const upsell = upsells.find(u => u.id === upsellId);
|
|
@@ -11612,7 +11669,7 @@
|
|
|
11612
11669
|
}
|
|
11613
11670
|
});
|
|
11614
11671
|
return total;
|
|
11615
|
-
}, [participantUpsells, upsells, watchedParticipants]);
|
|
11672
|
+
}, [participantUpsells, upsells, watchedParticipants, participantCount]);
|
|
11616
11673
|
const calculateTotalDiscount = q$2(() => {
|
|
11617
11674
|
return appliedVouchers.reduce((total, voucher) => {
|
|
11618
11675
|
if (voucher.type === "discount") {
|
|
@@ -11633,8 +11690,7 @@
|
|
|
11633
11690
|
const calculateDeposit = () => {
|
|
11634
11691
|
if (!eventDetails || !eventDetails.deposit)
|
|
11635
11692
|
return 0;
|
|
11636
|
-
|
|
11637
|
-
return eventDetails.deposit * participantCount;
|
|
11693
|
+
return eventDetails.deposit * watchedParticipants.length;
|
|
11638
11694
|
};
|
|
11639
11695
|
const baseTotal = calculateBaseTotal();
|
|
11640
11696
|
const upsellsTotal = calculateUpsellsTotal();
|
|
@@ -11651,8 +11707,8 @@
|
|
|
11651
11707
|
// Includes participantIndices to track which participants selected each upsell
|
|
11652
11708
|
const aggregatedUpsellSelections = q$2(() => {
|
|
11653
11709
|
const upsellParticipantMap = {};
|
|
11654
|
-
watchedParticipants.forEach((
|
|
11655
|
-
if (
|
|
11710
|
+
watchedParticipants.forEach((_, index) => {
|
|
11711
|
+
if (participantCount > 0) {
|
|
11656
11712
|
const participantUpsellIds = participantUpsells[index] || [];
|
|
11657
11713
|
participantUpsellIds.forEach(upsellId => {
|
|
11658
11714
|
if (!upsellParticipantMap[upsellId]) {
|
|
@@ -11686,15 +11742,17 @@
|
|
|
11686
11742
|
setAppliedVouchers((prev) => prev.filter((v) => v.code !== code));
|
|
11687
11743
|
}, []);
|
|
11688
11744
|
const isReadyForPayment = () => {
|
|
11689
|
-
const participantsWithNames = watchedParticipants.filter((p) => p.name
|
|
11745
|
+
const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
|
|
11690
11746
|
const totalParticipantRows = watchedParticipants.length;
|
|
11691
|
-
const allParticipantsHaveNames =
|
|
11747
|
+
const allParticipantsHaveNames = participantFieldsConfig.name.required
|
|
11748
|
+
? participantsWithNames === totalParticipantRows
|
|
11749
|
+
: true;
|
|
11692
11750
|
const participantsWithinLimit = participantsWithNames <= (eventDetails?.availableSpots || 0);
|
|
11693
11751
|
const hasValidCustomerName = watchedCustomerName && watchedCustomerName.trim().length >= 2;
|
|
11694
11752
|
const hasValidCustomerEmail = watchedCustomerEmail && watchedCustomerEmail.trim().length > 0 && !customerEmailError;
|
|
11695
11753
|
return allParticipantsHaveNames &&
|
|
11696
11754
|
participantsWithinLimit &&
|
|
11697
|
-
participantsWithNames > 0 &&
|
|
11755
|
+
(participantFieldsConfig.name.required ? participantsWithNames > 0 : totalParticipantRows > 0) &&
|
|
11698
11756
|
hasValidCustomerName &&
|
|
11699
11757
|
hasValidCustomerEmail &&
|
|
11700
11758
|
watchedAcceptTerms;
|
|
@@ -11702,7 +11760,7 @@
|
|
|
11702
11760
|
y$1(() => {
|
|
11703
11761
|
if (appliedVouchers.length > 0) {
|
|
11704
11762
|
const newBaseTotal = eventDetails?.price
|
|
11705
|
-
? eventDetails.price * watchedParticipants.
|
|
11763
|
+
? eventDetails.price * watchedParticipants.length
|
|
11706
11764
|
: 0;
|
|
11707
11765
|
const currentUpsellsTotal = calculateUpsellsTotal();
|
|
11708
11766
|
const orderTotal = newBaseTotal + currentUpsellsTotal;
|
|
@@ -11747,7 +11805,7 @@
|
|
|
11747
11805
|
const currentParticipants = form.getValues("participants");
|
|
11748
11806
|
const availableSpots = eventDetails?.availableSpots || 0;
|
|
11749
11807
|
if (currentParticipants.length < availableSpots) {
|
|
11750
|
-
form.setValue("participants", [...currentParticipants, { name: "" }]);
|
|
11808
|
+
form.setValue("participants", [...currentParticipants, { name: "", level: undefined }]);
|
|
11751
11809
|
}
|
|
11752
11810
|
else {
|
|
11753
11811
|
alert(t$1("booking.maxParticipants", { count: availableSpots }));
|
|
@@ -11866,7 +11924,7 @@
|
|
|
11866
11924
|
justifyContent: "space-between",
|
|
11867
11925
|
alignItems: "center",
|
|
11868
11926
|
marginBottom: "16px",
|
|
11869
|
-
}, children: u$2("h2", { style: { ...sectionHeaderStyles$1, marginBottom: 0 }, children: t$1("booking.participants") }) }), u$2("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [watchedParticipants.map((_, index) => (u$2("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [u$2("div", { style: { display: "flex", gap: "12px", alignItems: "center" }, children: [u$2("div", { style: { flex: 1 }, children: [u$2("label", { htmlFor: `participant-name-${index}`, style: labelStyles$1, children: t$1("booking.participantName") }), u$2("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 && (u$2("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.name?.message }))] }), u$2("div", { style: { width: "80px" }, children: [u$2("label", { htmlFor: `participant-age-${index}`, style: labelStyles$1, children: t$1("booking.participantAge") }), u$2("input", { id: `participant-age-${index}`, ...form.register(`participants.${index}.age`, {
|
|
11927
|
+
}, children: u$2("h2", { style: { ...sectionHeaderStyles$1, marginBottom: 0 }, children: t$1("booking.participants") }) }), u$2("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [watchedParticipants.map((_, index) => (u$2("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [u$2("div", { style: { display: "flex", gap: "12px", alignItems: "center" }, children: [participantFieldsConfig.name.enabled && (u$2("div", { style: { flex: 1 }, children: [u$2("label", { htmlFor: `participant-name-${index}`, style: labelStyles$1, children: t$1("booking.participantName") }), u$2("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 && (u$2("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.name?.message }))] })), participantFieldsConfig.age.enabled && (u$2("div", { style: { width: "80px" }, children: [u$2("label", { htmlFor: `participant-age-${index}`, style: labelStyles$1, children: t$1("booking.participantAge") }), u$2("input", { id: `participant-age-${index}`, ...form.register(`participants.${index}.age`, {
|
|
11870
11928
|
setValueAs: (value) => {
|
|
11871
11929
|
if (value === "" || value === null || value === undefined) {
|
|
11872
11930
|
return undefined;
|
|
@@ -11874,7 +11932,7 @@
|
|
|
11874
11932
|
const num = Number(value);
|
|
11875
11933
|
return Number.isNaN(num) ? undefined : num;
|
|
11876
11934
|
},
|
|
11877
|
-
}), type: "number", min: "0", max: "120", style: inputStyles$1, placeholder: "25" })] }), watchedParticipants.length > 1 && (u$2("div", { children: [u$2("label", { style: { ...labelStyles$1, visibility: "hidden" }, children: "\u00A0" }), u$2("button", { type: "button", onClick: () => removeParticipant(index), style: {
|
|
11935
|
+
}), type: "number", min: "0", max: "120", style: inputStyles$1, placeholder: "25" })] })), watchedParticipants.length > 1 && (u$2("div", { children: [u$2("label", { style: { ...labelStyles$1, visibility: "hidden" }, children: "\u00A0" }), u$2("button", { type: "button", onClick: () => removeParticipant(index), style: {
|
|
11878
11936
|
color: "var(--bw-error-color)",
|
|
11879
11937
|
backgroundColor: "var(--bw-surface-color)",
|
|
11880
11938
|
border: "1px solid var(--bw-border-color)",
|
|
@@ -11890,7 +11948,7 @@
|
|
|
11890
11948
|
fontWeight: 700,
|
|
11891
11949
|
fontFamily: "var(--bw-font-family)",
|
|
11892
11950
|
padding: 0,
|
|
11893
|
-
}, children: "\u00D7" })] }))] }), upsells.length > 0 && (u$2("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
|
|
11951
|
+
}, children: "\u00D7" })] }))] }), participantFieldsConfig.level.enabled && (u$2("div", { style: { minWidth: "140px" }, children: [u$2("label", { htmlFor: `participant-level-${index}`, style: labelStyles$1, children: t$1("booking.participantLevel") }), u$2("select", { id: `participant-level-${index}`, ...form.register(`participants.${index}.level`), style: inputStyles$1, children: [u$2("option", { value: "", children: t$1("booking.participantLevelPlaceholder") }), participantLevelOptions.map((level) => (u$2("option", { value: level, children: t$1(`level.${level}`) }, level)))] }), form.formState.errors.participants?.[index]?.level && (u$2("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.level?.message }))] })), upsells.length > 0 && (u$2("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
|
|
11894
11952
|
const isSelected = (participantUpsells[index] || []).includes(upsell.id);
|
|
11895
11953
|
return (u$2("label", { htmlFor: `upsell-${index}-${upsell.id}`, style: isSelected ? participantUpsellStyles.labelSelected : participantUpsellStyles.label, children: [u$2("input", { id: `upsell-${index}-${upsell.id}`, type: "checkbox", style: participantUpsellStyles.checkbox, checked: isSelected, onChange: () => toggleParticipantUpsell(index, upsell.id) }), u$2("span", { style: { fontWeight: 500 }, children: upsell.name }), u$2("span", { style: { fontSize: "12px", opacity: 0.8 }, children: ["(+", formatCurrency(upsell.price), ")"] })] }, upsell.id));
|
|
11896
11954
|
}) }))] }, index))), watchedParticipants.length < eventDetails.availableSpots ? (u$2("div", { style: {
|
|
@@ -11919,9 +11977,9 @@
|
|
|
11919
11977
|
color: "var(--bw-text-color)",
|
|
11920
11978
|
fontWeight: 500,
|
|
11921
11979
|
fontFamily: "var(--bw-font-family)",
|
|
11922
|
-
}, children: [u$2("span", { style: { fontWeight: 200 }, children: [watchedParticipants.length > 1 ? watchedParticipants.
|
|
11980
|
+
}, children: [u$2("span", { style: { fontWeight: 200 }, children: [watchedParticipants.length > 1 ? watchedParticipants.length : 1, " x "] }), " ", formatCurrency(eventDetails.price)] })] }), upsellsTotal > 0 && (u$2("div", { style: { marginTop: "8px", paddingTop: "8px", borderTop: "1px dashed var(--bw-border-color)" }, children: [u$2("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) => {
|
|
11923
11981
|
// Count how many participants have this upsell selected
|
|
11924
|
-
const countWithUpsell = watchedParticipants.filter((
|
|
11982
|
+
const countWithUpsell = watchedParticipants.filter((_, idx) => (participantUpsells[idx] || []).includes(upsell.id)).length;
|
|
11925
11983
|
if (countWithUpsell === 0)
|
|
11926
11984
|
return null;
|
|
11927
11985
|
const upsellLineTotal = upsell.price * countWithUpsell;
|
|
@@ -12022,15 +12080,17 @@
|
|
|
12022
12080
|
}, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), u$2("div", { ref: paymentSectionRef, children: (stripePromise || systemConfig?.paymentProvider === "mollie") &&
|
|
12023
12081
|
(() => {
|
|
12024
12082
|
if (!isReadyForPayment()) {
|
|
12025
|
-
const participantsWithNames = watchedParticipants.filter((p) => p.name
|
|
12083
|
+
const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
|
|
12026
12084
|
const totalParticipantRows = watchedParticipants.length;
|
|
12027
12085
|
const participantsWithoutNames = totalParticipantRows - participantsWithNames;
|
|
12028
12086
|
const missing = [];
|
|
12029
|
-
if (
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12087
|
+
if (participantFieldsConfig.name.required) {
|
|
12088
|
+
if (participantsWithNames === 0) {
|
|
12089
|
+
missing.push(t$1("payment.needParticipant"));
|
|
12090
|
+
}
|
|
12091
|
+
else if (participantsWithoutNames > 0) {
|
|
12092
|
+
missing.push(t$1("payment.needAllNames", { count: totalParticipantRows }));
|
|
12093
|
+
}
|
|
12034
12094
|
}
|
|
12035
12095
|
if (participantsWithNames > (eventDetails?.availableSpots || 0)) {
|
|
12036
12096
|
missing.push(t$1("payment.reduceParticipants", { count: eventDetails?.availableSpots || 0 }));
|
|
@@ -12074,121 +12134,145 @@
|
|
|
12074
12134
|
}
|
|
12075
12135
|
|
|
12076
12136
|
/**
|
|
12077
|
-
* Google Ads
|
|
12137
|
+
* Google Ads Tracking Utility
|
|
12078
12138
|
*
|
|
12079
|
-
*
|
|
12080
|
-
|
|
12081
|
-
/**
|
|
12082
|
-
* Check if gtag is available in current or parent window
|
|
12139
|
+
* Handles pageview tracking (widget load) and conversion tracking (successful booking).
|
|
12140
|
+
* Supports both direct gtag.js and GTM dataLayer setups.
|
|
12083
12141
|
*/
|
|
12084
12142
|
function isGtagAvailable() {
|
|
12085
|
-
if (typeof window === "undefined")
|
|
12143
|
+
if (typeof window === "undefined")
|
|
12086
12144
|
return false;
|
|
12087
|
-
|
|
12088
|
-
// Check current window
|
|
12089
|
-
if (typeof window.gtag === "function") {
|
|
12145
|
+
if (typeof window.gtag === "function")
|
|
12090
12146
|
return true;
|
|
12091
|
-
}
|
|
12092
|
-
// Check parent window (for iframe/widget scenarios)
|
|
12093
12147
|
if (window !== window.parent) {
|
|
12094
12148
|
try {
|
|
12095
|
-
if (typeof window.parent?.gtag === "function")
|
|
12149
|
+
if (typeof window.parent?.gtag === "function")
|
|
12096
12150
|
return true;
|
|
12097
|
-
}
|
|
12098
12151
|
}
|
|
12099
12152
|
catch {
|
|
12100
|
-
//
|
|
12153
|
+
// Cross-origin
|
|
12101
12154
|
}
|
|
12102
12155
|
}
|
|
12103
12156
|
return false;
|
|
12104
12157
|
}
|
|
12105
|
-
/**
|
|
12106
|
-
* Initialize gtag if not already available
|
|
12107
|
-
*/
|
|
12108
12158
|
function initializeGtag(tagId) {
|
|
12109
|
-
if (typeof window === "undefined")
|
|
12159
|
+
if (typeof window === "undefined" || isGtagAvailable())
|
|
12110
12160
|
return;
|
|
12111
|
-
}
|
|
12112
|
-
// Skip if gtag already exists
|
|
12113
|
-
if (isGtagAvailable()) {
|
|
12114
|
-
return;
|
|
12115
|
-
}
|
|
12116
|
-
// Initialize dataLayer and gtag function
|
|
12117
12161
|
window.dataLayer = window.dataLayer || [];
|
|
12118
12162
|
window.gtag = (...args) => {
|
|
12119
12163
|
window.dataLayer.push(args);
|
|
12120
12164
|
};
|
|
12121
|
-
// Set current timestamp
|
|
12122
12165
|
window.gtag("js", new Date());
|
|
12123
|
-
// Load gtag script
|
|
12124
12166
|
const script = document.createElement("script");
|
|
12125
12167
|
script.async = true;
|
|
12126
12168
|
script.src = `https://www.googletagmanager.com/gtag/js?id=${tagId}`;
|
|
12127
12169
|
document.head.appendChild(script);
|
|
12128
|
-
// Configure the tag
|
|
12129
12170
|
window.gtag("config", tagId, {
|
|
12130
12171
|
anonymize_ip: true,
|
|
12131
12172
|
allow_google_signals: false,
|
|
12132
12173
|
allow_ad_personalization_signals: false,
|
|
12133
12174
|
});
|
|
12134
12175
|
}
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
return;
|
|
12176
|
+
function getGtag() {
|
|
12177
|
+
if (typeof window === "undefined")
|
|
12178
|
+
return null;
|
|
12179
|
+
if (typeof window.gtag === "function") {
|
|
12180
|
+
return window.gtag;
|
|
12141
12181
|
}
|
|
12142
|
-
|
|
12143
|
-
// Try parent window gtag if current window doesn't have it
|
|
12144
|
-
if (typeof gtag !== "function" && window !== window.parent) {
|
|
12182
|
+
if (window !== window.parent) {
|
|
12145
12183
|
try {
|
|
12146
|
-
|
|
12184
|
+
const parentGtag = window.parent?.gtag;
|
|
12185
|
+
if (typeof parentGtag === "function")
|
|
12186
|
+
return parentGtag;
|
|
12147
12187
|
}
|
|
12148
12188
|
catch {
|
|
12149
|
-
//
|
|
12189
|
+
// Cross-origin
|
|
12150
12190
|
}
|
|
12151
12191
|
}
|
|
12152
|
-
|
|
12192
|
+
return null;
|
|
12193
|
+
}
|
|
12194
|
+
/**
|
|
12195
|
+
* Push an event to the dataLayer for GTM visibility,
|
|
12196
|
+
* then also fire via gtag for direct Google Ads tracking.
|
|
12197
|
+
*/
|
|
12198
|
+
function sendEvent(eventName, params) {
|
|
12199
|
+
if (typeof window === "undefined")
|
|
12153
12200
|
return;
|
|
12201
|
+
// GTM dataLayer push (object format — visible in Tag Assistant)
|
|
12202
|
+
window.dataLayer = window.dataLayer || [];
|
|
12203
|
+
window.dataLayer.push({
|
|
12204
|
+
event: eventName,
|
|
12205
|
+
...params,
|
|
12206
|
+
});
|
|
12207
|
+
// gtag call (array format — processed by gtag.js for Google Ads)
|
|
12208
|
+
const gtag = getGtag();
|
|
12209
|
+
if (gtag) {
|
|
12210
|
+
gtag("event", eventName, params);
|
|
12154
12211
|
}
|
|
12155
|
-
|
|
12156
|
-
|
|
12212
|
+
}
|
|
12213
|
+
function sendConversion(config) {
|
|
12214
|
+
const params = {
|
|
12157
12215
|
send_to: `${config.tagId}/${config.conversionId}`,
|
|
12158
12216
|
};
|
|
12159
|
-
// Add optional parameters
|
|
12160
12217
|
if (config.conversionValue !== undefined) {
|
|
12161
|
-
|
|
12218
|
+
params.value = config.conversionValue;
|
|
12162
12219
|
}
|
|
12163
12220
|
if (config.conversionCurrency) {
|
|
12164
|
-
|
|
12221
|
+
params.currency = config.conversionCurrency;
|
|
12165
12222
|
}
|
|
12166
12223
|
if (config.transactionId) {
|
|
12167
|
-
|
|
12224
|
+
params.transaction_id = config.transactionId;
|
|
12168
12225
|
}
|
|
12169
|
-
|
|
12170
|
-
gtag("event", "conversion", conversionData);
|
|
12226
|
+
sendEvent("conversion", params);
|
|
12171
12227
|
}
|
|
12172
12228
|
/**
|
|
12173
|
-
*
|
|
12174
|
-
* Waits 1500ms, checks/initializes gtag, then sends conversion
|
|
12229
|
+
* Track widget pageview (fired once on widget mount).
|
|
12175
12230
|
*/
|
|
12176
|
-
function
|
|
12177
|
-
|
|
12178
|
-
if (!config.tagId || !config.conversionId) {
|
|
12231
|
+
function handleGoogleAdsPageview(tagId, consent) {
|
|
12232
|
+
if (!tagId || false || typeof window === "undefined")
|
|
12179
12233
|
return;
|
|
12234
|
+
if (!isGtagAvailable()) {
|
|
12235
|
+
initializeGtag(tagId);
|
|
12180
12236
|
}
|
|
12181
|
-
|
|
12237
|
+
const fire = () => sendEvent("widget_pageview", {
|
|
12238
|
+
send_to: tagId,
|
|
12239
|
+
page_location: window.location.href,
|
|
12240
|
+
page_title: document.title,
|
|
12241
|
+
});
|
|
12242
|
+
if (isGtagAvailable()) {
|
|
12243
|
+
fire();
|
|
12244
|
+
return;
|
|
12245
|
+
}
|
|
12246
|
+
const script = document.querySelector(`script[src*="googletagmanager.com/gtag/js?id=${tagId}"]`);
|
|
12247
|
+
if (script) {
|
|
12248
|
+
script.addEventListener("load", fire, { once: true });
|
|
12249
|
+
}
|
|
12250
|
+
}
|
|
12251
|
+
/**
|
|
12252
|
+
* Handle Google Ads conversion tracking.
|
|
12253
|
+
* Waits 1500ms for the success page to settle, then fires.
|
|
12254
|
+
*/
|
|
12255
|
+
function handleGoogleAdsConversion(config) {
|
|
12256
|
+
if (!config.tagId || !config.conversionId)
|
|
12257
|
+
return;
|
|
12182
12258
|
setTimeout(() => {
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12259
|
+
if (isGtagAvailable()) {
|
|
12260
|
+
sendConversion(config);
|
|
12261
|
+
return;
|
|
12262
|
+
}
|
|
12263
|
+
initializeGtag(config.tagId);
|
|
12264
|
+
const script = document.querySelector(`script[src*="googletagmanager.com/gtag/js?id=${config.tagId}"]`);
|
|
12265
|
+
if (script) {
|
|
12266
|
+
script.addEventListener("load", () => sendConversion(config));
|
|
12267
|
+
script.addEventListener("error", () => sendConversion(config));
|
|
12268
|
+
}
|
|
12269
|
+
else {
|
|
12270
|
+
sendConversion(config);
|
|
12186
12271
|
}
|
|
12187
|
-
sendConversion(config);
|
|
12188
12272
|
}, 1500);
|
|
12189
12273
|
}
|
|
12190
12274
|
|
|
12191
|
-
const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId, }) => {
|
|
12275
|
+
const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId, googleAdsConfig: googleAdsConfigProp, }) => {
|
|
12192
12276
|
const t = useTranslations();
|
|
12193
12277
|
const { locale } = useLocale();
|
|
12194
12278
|
const timezone = useTimezone();
|
|
@@ -12205,7 +12289,7 @@
|
|
|
12205
12289
|
try {
|
|
12206
12290
|
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/get-booking-by-payment"), {
|
|
12207
12291
|
method: "POST",
|
|
12208
|
-
headers: createApiHeaders(config),
|
|
12292
|
+
headers: createApiHeaders(config, locale),
|
|
12209
12293
|
body: JSON.stringify(createRequestBody(config, {
|
|
12210
12294
|
paymentIntentId: targetPaymentIntentId,
|
|
12211
12295
|
})),
|
|
@@ -12237,20 +12321,16 @@
|
|
|
12237
12321
|
});
|
|
12238
12322
|
setPaymentStatus(data.stripePaymentIntent?.status || data.order.status);
|
|
12239
12323
|
const finalPaymentStatus = data.stripePaymentIntent?.status || data.order.status;
|
|
12324
|
+
const adsConfig = googleAdsConfigProp ?? data.googleAdsConfig;
|
|
12240
12325
|
if (finalPaymentStatus === "succeeded" &&
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
config.googleAds?.consent !== false) {
|
|
12244
|
-
// Prepare conversion tracking data
|
|
12245
|
-
const conversionValue = data.order.total / 100;
|
|
12246
|
-
const transactionId = data.order.id;
|
|
12247
|
-
// Track the conversion
|
|
12326
|
+
adsConfig?.tagId &&
|
|
12327
|
+
adsConfig?.conversionId) {
|
|
12248
12328
|
handleGoogleAdsConversion({
|
|
12249
|
-
tagId:
|
|
12250
|
-
conversionId:
|
|
12251
|
-
conversionValue,
|
|
12252
|
-
conversionCurrency:
|
|
12253
|
-
transactionId,
|
|
12329
|
+
tagId: adsConfig.tagId,
|
|
12330
|
+
conversionId: adsConfig.conversionId,
|
|
12331
|
+
conversionValue: data.order.total / 100,
|
|
12332
|
+
conversionCurrency: adsConfig.conversionCurrency || "EUR",
|
|
12333
|
+
transactionId: data.order.id,
|
|
12254
12334
|
});
|
|
12255
12335
|
}
|
|
12256
12336
|
}
|
|
@@ -12439,7 +12519,7 @@
|
|
|
12439
12519
|
flexDirection: "column",
|
|
12440
12520
|
gap: "var(--bw-spacing-small)",
|
|
12441
12521
|
}, children: formData.participants
|
|
12442
|
-
.filter((p) => p.name
|
|
12522
|
+
.filter((p) => p.name?.trim() || p.age || p.level)
|
|
12443
12523
|
.map((participant, index) => (u$2("div", { className: "print-participant", style: {
|
|
12444
12524
|
display: "flex",
|
|
12445
12525
|
justifyContent: "space-between",
|
|
@@ -12450,11 +12530,15 @@
|
|
|
12450
12530
|
}, children: u$2("div", { className: "print-participant-info", children: [u$2("div", { className: "print-participant-name", style: {
|
|
12451
12531
|
color: "var(--bw-text-color)",
|
|
12452
12532
|
fontFamily: "var(--bw-font-family)",
|
|
12453
|
-
}, children: participant.name }), participant.age && (u$2("div", { className: "print-participant-age", style: {
|
|
12533
|
+
}, children: participant.name || `#${index + 1}` }), participant.age && (u$2("div", { className: "print-participant-age", style: {
|
|
12454
12534
|
color: "var(--bw-text-muted)",
|
|
12455
12535
|
fontSize: "var(--bw-font-size-small)",
|
|
12456
12536
|
fontFamily: "var(--bw-font-family)",
|
|
12457
|
-
}, children: t("success.age", { age: participant.age }) }))
|
|
12537
|
+
}, children: t("success.age", { age: participant.age }) })), participant.level && (u$2("div", { style: {
|
|
12538
|
+
color: "var(--bw-text-muted)",
|
|
12539
|
+
fontSize: "var(--bw-font-size-small)",
|
|
12540
|
+
fontFamily: "var(--bw-font-family)",
|
|
12541
|
+
}, children: [t("booking.participantLevel"), ": ", t(`level.${participant.level}`)] }))] }) }, index))) }) })] })), u$2("div", { className: "print-booking-card", style: {
|
|
12458
12542
|
backgroundColor: "var(--bw-surface-color)",
|
|
12459
12543
|
border: `1px solid var(--bw-border-color)`,
|
|
12460
12544
|
borderRadius: "var(--bw-border-radius)",
|
|
@@ -15511,6 +15595,13 @@
|
|
|
15511
15595
|
const [shouldRenderInstanceSelection, setShouldRenderInstanceSelection] = d$1(false);
|
|
15512
15596
|
const [shouldRenderUpsells, setShouldRenderUpsells] = d$1(false);
|
|
15513
15597
|
const [shouldRenderBookingForm, setShouldRenderBookingForm] = d$1(false);
|
|
15598
|
+
// Google Ads config (received from API, set once from the first API response)
|
|
15599
|
+
const [googleAdsConfig, setGoogleAdsConfig] = d$1(null);
|
|
15600
|
+
const extractGoogleAdsConfig = (data) => {
|
|
15601
|
+
if (!googleAdsConfig && data?.googleAdsConfig?.tagId) {
|
|
15602
|
+
setGoogleAdsConfig(data.googleAdsConfig);
|
|
15603
|
+
}
|
|
15604
|
+
};
|
|
15514
15605
|
// Promo dialog state
|
|
15515
15606
|
const [showPromoDialog, setShowPromoDialog] = d$1(false);
|
|
15516
15607
|
const [widgetContainerRef, setWidgetContainerRef] = d$1(null);
|
|
@@ -15594,6 +15685,7 @@
|
|
|
15594
15685
|
image: resolvedImage,
|
|
15595
15686
|
};
|
|
15596
15687
|
setVoucherConfig(mergedConfig);
|
|
15688
|
+
extractGoogleAdsConfig(data);
|
|
15597
15689
|
setVoucherEventTypes(data.eventTypes || []);
|
|
15598
15690
|
// Set system config for payment processing
|
|
15599
15691
|
if (data.paymentProvider) {
|
|
@@ -15616,6 +15708,14 @@
|
|
|
15616
15708
|
setIsLoadingVoucherConfig(false);
|
|
15617
15709
|
}
|
|
15618
15710
|
};
|
|
15711
|
+
// Fire widget pageview once when Google Ads config is received from API
|
|
15712
|
+
const pageviewFiredRef = A$2(false);
|
|
15713
|
+
y$1(() => {
|
|
15714
|
+
if (!pageviewFiredRef.current && googleAdsConfig?.tagId) {
|
|
15715
|
+
pageviewFiredRef.current = true;
|
|
15716
|
+
handleGoogleAdsPageview(googleAdsConfig.tagId);
|
|
15717
|
+
}
|
|
15718
|
+
}, [googleAdsConfig]);
|
|
15619
15719
|
// Determine initial step and load data
|
|
15620
15720
|
y$1(() => {
|
|
15621
15721
|
const initializeWidget = async () => {
|
|
@@ -15829,6 +15929,7 @@
|
|
|
15829
15929
|
onWidgetLanguage?.(wl);
|
|
15830
15930
|
onTimezone?.(wl.timezone);
|
|
15831
15931
|
}
|
|
15932
|
+
extractGoogleAdsConfig(data);
|
|
15832
15933
|
setEventTypes(data.eventTypes);
|
|
15833
15934
|
if (isSingleEventTypeMode && data.eventTypes.length === 1) {
|
|
15834
15935
|
setSelectedEventType(data.eventTypes[0]);
|
|
@@ -15865,6 +15966,7 @@
|
|
|
15865
15966
|
onWidgetLanguage?.(wl);
|
|
15866
15967
|
onTimezone?.(wl.timezone);
|
|
15867
15968
|
}
|
|
15969
|
+
extractGoogleAdsConfig(data);
|
|
15868
15970
|
setUpcomingEvents(data.upcomingEvents || []);
|
|
15869
15971
|
}
|
|
15870
15972
|
else {
|
|
@@ -15900,6 +16002,7 @@
|
|
|
15900
16002
|
onWidgetLanguage?.(wl);
|
|
15901
16003
|
onTimezone?.(wl.timezone);
|
|
15902
16004
|
}
|
|
16005
|
+
extractGoogleAdsConfig(data);
|
|
15903
16006
|
setSpecials(data.specials || []);
|
|
15904
16007
|
}
|
|
15905
16008
|
else {
|
|
@@ -15926,6 +16029,7 @@
|
|
|
15926
16029
|
onWidgetLanguage?.(wl);
|
|
15927
16030
|
onTimezone?.(wl.timezone);
|
|
15928
16031
|
}
|
|
16032
|
+
extractGoogleAdsConfig(data);
|
|
15929
16033
|
setEventInstances(data.eventInstances);
|
|
15930
16034
|
if (data.paymentProvider) {
|
|
15931
16035
|
setSystemConfig({
|
|
@@ -15986,6 +16090,7 @@
|
|
|
15986
16090
|
onWidgetLanguage?.(wl);
|
|
15987
16091
|
onTimezone?.(wl.timezone);
|
|
15988
16092
|
}
|
|
16093
|
+
extractGoogleAdsConfig(data);
|
|
15989
16094
|
setEventDetails(data.eventDetails);
|
|
15990
16095
|
setSystemConfig({
|
|
15991
16096
|
paymentProvider: data.paymentProvider,
|
|
@@ -16055,6 +16160,7 @@
|
|
|
16055
16160
|
onWidgetLanguage?.(wl);
|
|
16056
16161
|
onTimezone?.(wl.timezone);
|
|
16057
16162
|
}
|
|
16163
|
+
extractGoogleAdsConfig(data);
|
|
16058
16164
|
return data.upsells || [];
|
|
16059
16165
|
}
|
|
16060
16166
|
else {
|
|
@@ -16468,7 +16574,7 @@
|
|
|
16468
16574
|
url.searchParams.delete("mollie_payment_id");
|
|
16469
16575
|
url.searchParams.delete("mollie_status");
|
|
16470
16576
|
window.history.replaceState({}, "", url.toString());
|
|
16471
|
-
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16577
|
+
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16472
16578
|
}
|
|
16473
16579
|
if (viewMode === "specials" && showingPreview) {
|
|
16474
16580
|
return (u$2(StyleProvider, { config: config, children: [u$2("div", { ref: setWidgetContainerRef, children: [u$2(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (u$2(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
|
|
@@ -16493,7 +16599,7 @@
|
|
|
16493
16599
|
setShouldRenderBookingForm(false);
|
|
16494
16600
|
setSelectedUpsells([]);
|
|
16495
16601
|
setUpsells([]);
|
|
16496
|
-
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16602
|
+
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16497
16603
|
}
|
|
16498
16604
|
if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
|
|
16499
16605
|
return (u$2(StyleProvider, { config: config, children: [u$2("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (u$2(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
|
|
@@ -16516,7 +16622,7 @@
|
|
|
16516
16622
|
url.searchParams.delete("mollie_payment_id");
|
|
16517
16623
|
url.searchParams.delete("mollie_status");
|
|
16518
16624
|
window.history.replaceState({}, "", url.toString());
|
|
16519
|
-
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16625
|
+
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16520
16626
|
}
|
|
16521
16627
|
if (viewMode === "button" && (isSingleEventTypeMode || isDirectInstanceMode)) {
|
|
16522
16628
|
return (u$2(StyleProvider, { config: config, children: [u$2("div", { ref: setWidgetContainerRef, style: {
|
|
@@ -16562,7 +16668,7 @@
|
|
|
16562
16668
|
url.searchParams.delete("mollie_payment_id");
|
|
16563
16669
|
url.searchParams.delete("mollie_status");
|
|
16564
16670
|
window.history.replaceState({}, "", url.toString());
|
|
16565
|
-
}, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16671
|
+
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16566
16672
|
}
|
|
16567
16673
|
// Cards mode (default) - show event type selection with optional voucher card
|
|
16568
16674
|
const cardsView = (u$2(k$3, { children: [hasEventSelection && (u$2(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, onInstancePreview: (instanceId, eventTypeId) => void handleUpcomingEventSelect(instanceId, eventTypeId), isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: false, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: null, voucherPurchaseResult: null, isSuccess: false, showStandaloneCard: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled), onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => { } })), isStandaloneVoucherMode && isLoading && !voucherConfig && (u$2("div", { style: { padding: "24px", textAlign: "center" }, children: u$2("div", { style: {
|
|
@@ -16618,7 +16724,7 @@
|
|
|
16618
16724
|
url.searchParams.delete("mollie_payment_id");
|
|
16619
16725
|
url.searchParams.delete("mollie_status");
|
|
16620
16726
|
window.history.replaceState({}, "", url.toString());
|
|
16621
|
-
}, config: config, onError: setError, paymentIntentId: successPaymentId }), u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: isVoucherFormOpen, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, voucherPurchaseResult: voucherPurchaseResult, isSuccess: isSuccess, showStandaloneCard: false, onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => {
|
|
16727
|
+
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId }), u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: isVoucherFormOpen, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, voucherPurchaseResult: voucherPurchaseResult, isSuccess: isSuccess, showStandaloneCard: false, onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => {
|
|
16622
16728
|
setIsSuccess(false);
|
|
16623
16729
|
setVoucherPurchaseResult(null);
|
|
16624
16730
|
const url = new URL(window.location.href);
|
|
@@ -16633,11 +16739,7 @@
|
|
|
16633
16739
|
function UniversalBookingWidget(props) {
|
|
16634
16740
|
const [languagePolicy, setLanguagePolicy] = d$1(null);
|
|
16635
16741
|
const [orgTimezone, setOrgTimezone] = d$1("Europe/Berlin");
|
|
16636
|
-
const
|
|
16637
|
-
? languagePolicy.organizationLocale
|
|
16638
|
-
: undefined;
|
|
16639
|
-
const i18nConfigLocale = serverLockedLocale !== undefined ? serverLockedLocale : props.config.locale;
|
|
16640
|
-
const providerProps = i18nConfigLocale ? { configLocale: i18nConfigLocale } : {};
|
|
16742
|
+
const providerProps = props.config.locale ? { configLocale: props.config.locale } : {};
|
|
16641
16743
|
const showLanguagePicker = props.config.showLanguagePicker !== false &&
|
|
16642
16744
|
(languagePolicy === null || languagePolicy.multiLanguageEnabled);
|
|
16643
16745
|
return (u$2(I18nProvider, { ...providerProps, children: u$2(ShowLanguagePickerProvider, { value: showLanguagePicker, children: u$2(TimezoneProvider, { value: orgTimezone, children: u$2(UniversalBookingWidgetInner, { ...props, onWidgetLanguage: setLanguagePolicy, onTimezone: setOrgTimezone }) }) }) }));
|