@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.
@@ -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: configLocale (site owner) > persisted user choice > browser language > "de"
1663
- // If configLocale is set, the site owner has locked the language - don't restore user choice.
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 themeName = config.theme || "teal-minimal";
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 participantSchema = (t) => objectType({
11318
- name: stringType().trim().min(1, t("validation.nameRequired")),
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
- borderTop: "1px dashed var(--bw-border-color)",
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 * watchedParticipants.filter((p) => p.name.trim()).length;
11598
- }, [eventDetails, watchedParticipants]);
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((participant, index) => {
11603
- // Only count upsells for participants with names
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
- const participantCount = watchedParticipants.filter((p) => p.name.trim()).length;
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((participant, index) => {
11655
- if (participant.name.trim()) {
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.trim()).length;
11745
+ const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
11690
11746
  const totalParticipantRows = watchedParticipants.length;
11691
- const allParticipantsHaveNames = participantsWithNames === totalParticipantRows;
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.filter((p) => p.name.trim()).length
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.filter((p) => p.name.trim()).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) => {
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((p, idx) => p.name.trim() && (participantUpsells[idx] || []).includes(upsell.id)).length;
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.trim()).length;
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 (participantsWithNames === 0) {
12030
- missing.push(t$1("payment.needParticipant"));
12031
- }
12032
- else if (participantsWithoutNames > 0) {
12033
- missing.push(t$1("payment.needAllNames", { count: totalParticipantRows }));
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 Conversion Tracking Utility
12137
+ * Google Ads Tracking Utility
12078
12138
  *
12079
- * Simplified utility that waits 1500ms, checks/initializes gtag, and sends conversion.
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
- // Cannot access parent window (cross-origin)
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
- * Send conversion event using available gtag
12137
- */
12138
- function sendConversion(config) {
12139
- if (typeof window === "undefined") {
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
- let gtag = window.gtag;
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
- gtag = window.parent?.gtag;
12184
+ const parentGtag = window.parent?.gtag;
12185
+ if (typeof parentGtag === "function")
12186
+ return parentGtag;
12147
12187
  }
12148
12188
  catch {
12149
- // Cannot access parent window (cross-origin)
12189
+ // Cross-origin
12150
12190
  }
12151
12191
  }
12152
- if (typeof gtag !== "function") {
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
- // Build conversion data
12156
- const conversionData = {
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
- conversionData.value = config.conversionValue;
12218
+ params.value = config.conversionValue;
12162
12219
  }
12163
12220
  if (config.conversionCurrency) {
12164
- conversionData.currency = config.conversionCurrency;
12221
+ params.currency = config.conversionCurrency;
12165
12222
  }
12166
12223
  if (config.transactionId) {
12167
- conversionData.transaction_id = config.transactionId;
12224
+ params.transaction_id = config.transactionId;
12168
12225
  }
12169
- // Send conversion event
12170
- gtag("event", "conversion", conversionData);
12226
+ sendEvent("conversion", params);
12171
12227
  }
12172
12228
  /**
12173
- * Main function to handle Google Ads conversion tracking
12174
- * Waits 1500ms, checks/initializes gtag, then sends conversion
12229
+ * Track widget pageview (fired once on widget mount).
12175
12230
  */
12176
- function handleGoogleAdsConversion(config) {
12177
- // Validate required config
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
- // Wait 1500ms before proceeding
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
- // Check if gtag is available, initialize if not
12184
- if (!isGtagAvailable()) {
12185
- initializeGtag(config.tagId);
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
- config.googleAds?.tagId &&
12242
- config.googleAds?.conversionId &&
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: config.googleAds.tagId,
12250
- conversionId: config.googleAds.conversionId,
12251
- conversionValue,
12252
- conversionCurrency: config.googleAds.conversionCurrency || "EUR",
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.trim())
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 }) }))] }) }, index))) }) })] })), u$2("div", { className: "print-booking-card", style: {
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 serverLockedLocale = languagePolicy && !languagePolicy.multiLanguageEnabled
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 }) }) }) }));