@bigz-app/booking-widget 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/booking-widget.js +620 -148
  2. package/dist/booking-widget.js.map +1 -1
  3. package/dist/components/UniversalBookingWidget.d.ts +52 -2
  4. package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
  5. package/dist/components/events/EventTypeSelection.d.ts +14 -1
  6. package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
  7. package/dist/components/events/SpecialsView.d.ts +13 -0
  8. package/dist/components/events/SpecialsView.d.ts.map +1 -0
  9. package/dist/components/events/index.d.ts +1 -0
  10. package/dist/components/events/index.d.ts.map +1 -1
  11. package/dist/components/shared/Button.d.ts.map +1 -1
  12. package/dist/components/shared/DialogPortal.d.ts.map +1 -1
  13. package/dist/components/upsells/UpsellCard.d.ts +2 -0
  14. package/dist/components/upsells/UpsellCard.d.ts.map +1 -1
  15. package/dist/components/upsells/UpsellsStep.d.ts +2 -0
  16. package/dist/components/upsells/UpsellsStep.d.ts.map +1 -1
  17. package/dist/components/voucher/VoucherIntegration.d.ts +23 -0
  18. package/dist/components/voucher/VoucherIntegration.d.ts.map +1 -0
  19. package/dist/components/voucher/index.d.ts +2 -0
  20. package/dist/components/voucher/index.d.ts.map +1 -1
  21. package/dist/components/voucher/useVoucherConfig.d.ts +57 -0
  22. package/dist/components/voucher/useVoucherConfig.d.ts.map +1 -0
  23. package/dist/i18n/locales/de.d.ts.map +1 -1
  24. package/dist/i18n/locales/en.d.ts.map +1 -1
  25. package/dist/i18n/locales/es.d.ts.map +1 -1
  26. package/dist/i18n/locales/pt.d.ts.map +1 -1
  27. package/dist/i18n/locales/sv.d.ts.map +1 -1
  28. package/dist/index.cjs +620 -148
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.esm.js +620 -148
  31. package/dist/index.esm.js.map +1 -1
  32. package/dist/styles/StyleProvider.d.ts.map +1 -1
  33. package/dist/styles/shared-styles.d.ts +1 -0
  34. package/dist/styles/shared-styles.d.ts.map +1 -1
  35. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -253,6 +253,7 @@ const de$1 = {
253
253
  "events.soldOut": "Ausgebucht",
254
254
  "events.availableFrom": "Freie Plätze ab {{date}}",
255
255
  "events.noAvailableDates": "Keine Termine frei",
256
+ "events.previewSectionTitle": "Specials & nächste Termine",
256
257
  // Event instances
257
258
  "instances.title": "Terminauswahl",
258
259
  "instances.noAvailable": "Keine verfügbaren Termine",
@@ -269,6 +270,15 @@ const de$1 = {
269
270
  "nextEvents.noUpcomingMessage": "Aktuell sind keine Termine verfügbar. Bitte schaue später noch einmal vorbei oder kontaktiere uns direkt.",
270
271
  "nextEvents.showAll": "Alle Events anzeigen",
271
272
  "nextEvents.priceOnRequest": "Preis auf Anfrage",
273
+ // Specials view
274
+ "specials.title": "Sonderangebote",
275
+ "specials.subtitle": "Unsere aktuellen Angebote auf einen Blick",
276
+ "specials.noSpecials": "Keine Sonderangebote",
277
+ "specials.noSpecialsMessage": "Derzeit sind keine Sonderangebote verfügbar. Bitte schaue später noch einmal vorbei.",
278
+ "specials.save": "Spare {{amount}}",
279
+ "specials.savePercent": "{{percent}}% Rabatt",
280
+ "specials.spotsLeft": "Noch {{count}} Plätze frei",
281
+ "specials.bookNow": "Jetzt buchen",
272
282
  // Booking form
273
283
  "booking.title": "Buchung - {{name}}",
274
284
  "booking.notPossible": "Buchung nicht möglich",
@@ -513,6 +523,7 @@ const en = {
513
523
  "events.soldOut": "Sold out",
514
524
  "events.availableFrom": "Available from {{date}}",
515
525
  "events.noAvailableDates": "No dates available",
526
+ "events.previewSectionTitle": "Specials & upcoming dates",
516
527
  // Event instances
517
528
  "instances.title": "Select a date",
518
529
  "instances.noAvailable": "No available dates",
@@ -529,6 +540,15 @@ const en = {
529
540
  "nextEvents.noUpcomingMessage": "There are currently no dates available. Please check back later or contact us directly.",
530
541
  "nextEvents.showAll": "Show all events",
531
542
  "nextEvents.priceOnRequest": "Price on request",
543
+ // Specials view
544
+ "specials.title": "Special Offers",
545
+ "specials.subtitle": "Our current deals at a glance",
546
+ "specials.noSpecials": "No special offers",
547
+ "specials.noSpecialsMessage": "There are currently no special offers available. Please check back later.",
548
+ "specials.save": "Save {{amount}}",
549
+ "specials.savePercent": "{{percent}}% off",
550
+ "specials.spotsLeft": "{{count}} spots left",
551
+ "specials.bookNow": "Book now",
532
552
  // Booking form
533
553
  "booking.title": "Booking - {{name}}",
534
554
  "booking.notPossible": "Booking not possible",
@@ -773,6 +793,7 @@ const es = {
773
793
  "events.soldOut": "Agotado",
774
794
  "events.availableFrom": "Disponible desde {{date}}",
775
795
  "events.noAvailableDates": "Sin fechas disponibles",
796
+ "events.previewSectionTitle": "Especiales & próximas fechas",
776
797
  // Event instances
777
798
  "instances.title": "Seleccionar fecha",
778
799
  "instances.noAvailable": "Sin fechas disponibles",
@@ -789,6 +810,15 @@ const es = {
789
810
  "nextEvents.noUpcomingMessage": "Actualmente no hay fechas disponibles. Por favor, vuelve más tarde o contáctanos directamente.",
790
811
  "nextEvents.showAll": "Mostrar todos los eventos",
791
812
  "nextEvents.priceOnRequest": "Precio bajo consulta",
813
+ // Specials view
814
+ "specials.title": "Ofertas especiales",
815
+ "specials.subtitle": "Nuestras ofertas actuales de un vistazo",
816
+ "specials.noSpecials": "Sin ofertas especiales",
817
+ "specials.noSpecialsMessage": "Actualmente no hay ofertas especiales disponibles. Vuelve más tarde.",
818
+ "specials.save": "Ahorra {{amount}}",
819
+ "specials.savePercent": "{{percent}}% de descuento",
820
+ "specials.spotsLeft": "{{count}} plazas disponibles",
821
+ "specials.bookNow": "Reservar ahora",
792
822
  // Booking form
793
823
  "booking.title": "Reserva - {{name}}",
794
824
  "booking.notPossible": "Reserva no posible",
@@ -1033,6 +1063,7 @@ const pt = {
1033
1063
  "events.soldOut": "Esgotado",
1034
1064
  "events.availableFrom": "Disponível a partir de {{date}}",
1035
1065
  "events.noAvailableDates": "Sem datas disponíveis",
1066
+ "events.previewSectionTitle": "Especiais & próximas datas",
1036
1067
  // Event instances
1037
1068
  "instances.title": "Selecionar data",
1038
1069
  "instances.noAvailable": "Sem datas disponíveis",
@@ -1049,6 +1080,15 @@ const pt = {
1049
1080
  "nextEvents.noUpcomingMessage": "Atualmente não há datas disponíveis. Por favor, volte mais tarde ou contacte-nos diretamente.",
1050
1081
  "nextEvents.showAll": "Mostrar todos os eventos",
1051
1082
  "nextEvents.priceOnRequest": "Preço sob consulta",
1083
+ // Specials view
1084
+ "specials.title": "Ofertas especiais",
1085
+ "specials.subtitle": "As nossas ofertas atuais num relance",
1086
+ "specials.noSpecials": "Sem ofertas especiais",
1087
+ "specials.noSpecialsMessage": "Atualmente não há ofertas especiais disponíveis. Por favor, volte mais tarde.",
1088
+ "specials.save": "Poupe {{amount}}",
1089
+ "specials.savePercent": "{{percent}}% de desconto",
1090
+ "specials.spotsLeft": "{{count}} lugares disponíveis",
1091
+ "specials.bookNow": "Reservar agora",
1052
1092
  // Booking form
1053
1093
  "booking.title": "Reserva - {{name}}",
1054
1094
  "booking.notPossible": "Reserva não possível",
@@ -1293,6 +1333,7 @@ const sv = {
1293
1333
  "events.soldOut": "Fullbokat",
1294
1334
  "events.availableFrom": "Lediga platser från {{date}}",
1295
1335
  "events.noAvailableDates": "Inga datum lediga",
1336
+ "events.previewSectionTitle": "Specials & kommande datum",
1296
1337
  // Event instances
1297
1338
  "instances.title": "Välj datum",
1298
1339
  "instances.noAvailable": "Inga tillgängliga datum",
@@ -1309,6 +1350,15 @@ const sv = {
1309
1350
  "nextEvents.noUpcomingMessage": "Det finns för närvarande inga datum tillgängliga. Kom tillbaka senare eller kontakta oss direkt.",
1310
1351
  "nextEvents.showAll": "Visa alla evenemang",
1311
1352
  "nextEvents.priceOnRequest": "Pris på förfrågan",
1353
+ // Specials view
1354
+ "specials.title": "Specialerbjudanden",
1355
+ "specials.subtitle": "Våra aktuella erbjudanden i korthet",
1356
+ "specials.noSpecials": "Inga specialerbjudanden",
1357
+ "specials.noSpecialsMessage": "Det finns för närvarande inga specialerbjudanden tillgängliga. Kom tillbaka senare.",
1358
+ "specials.save": "Spara {{amount}}",
1359
+ "specials.savePercent": "{{percent}}% rabatt",
1360
+ "specials.spotsLeft": "{{count}} platser kvar",
1361
+ "specials.bookNow": "Boka nu",
1312
1362
  // Booking form
1313
1363
  "booking.title": "Bokning - {{name}}",
1314
1364
  "booking.notPossible": "Bokning inte möjlig",
@@ -1714,129 +1764,143 @@ const resolveSemanticColor = (colorValue, fallbackValue) => {
1714
1764
  // If semantic resolution fails, use fallback or return the original value
1715
1765
  return fallbackValue || colorValue;
1716
1766
  };
1717
- // Predefined themes (modern, accessibility-tested)
1767
+ // Predefined themes
1718
1768
  const themes = {
1719
1769
  // --- Light Themes ---
1720
- "light-fresh": {
1721
- highlight: "#00b1aa", // accent-strong
1722
- background: "#f8fdfe", // neutral-strong (background)
1723
- surface: "#ffffff", // card (pure white)
1724
- text: "#0e7490", // Turquoise 800
1725
- border: "#bae6fd", // Blue 200
1726
- success: "#38bdf8", // Blue 400
1727
- warning: "#fbbf24", // Amber 400
1728
- error: "#f43f5e", // Rose 500
1729
- borderRadius: "18px", // Very rounded corners
1770
+ "teal-minimal": {
1771
+ highlight: "#00b1aa",
1772
+ background: "#f8fdfe",
1773
+ surface: "#ffffff",
1774
+ text: "#0e7490",
1775
+ border: "#bae6fd",
1776
+ success: "#38bdf8",
1777
+ warning: "#fbbf24",
1778
+ error: "#f43f5e",
1779
+ borderRadius: "18px",
1730
1780
  fontFamily: "'Inter', system-ui, sans-serif",
1731
1781
  },
1732
- "light-elegant": {
1733
- highlight: "#8b5cf6", // Violet 500
1734
- background: "#f5f3ff", // Violet 50
1735
- surface: "#ffffff", // White
1736
- text: "#4c1d95", // Violet 900
1737
- border: "#ede9fe", // Violet 100
1738
- success: "#16a34a", // Green 600
1739
- warning: "#ca8a04", // Yellow 600
1740
- error: "#dc2626", // Red 600
1741
- borderRadius: "12px",
1742
- fontFamily: "'Playfair Display', serif",
1782
+ "blue-business": {
1783
+ highlight: "#2563eb",
1784
+ background: "#f8fafc",
1785
+ surface: "#ffffff",
1786
+ text: "#0f172a",
1787
+ border: "#cbd5e1",
1788
+ success: "#059669",
1789
+ warning: "#d97706",
1790
+ error: "#b91c1c",
1791
+ borderRadius: "6px",
1792
+ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
1743
1793
  },
1744
- "light-vibrant": {
1745
- highlight: "#ed702d", // blue-500 - bright blue accent
1746
- background: "#1f2630", // slate-900 - dark background
1747
- surface: "#1f2630", // slate-800 - dark cards
1748
- text: "#f1f5f9", // slate-100 - light text
1749
- border: "#ed702d", // slate-700 - subtle borders
1750
- success: "#22c55e", // green-500
1751
- warning: "#eab308", // yellow-500
1752
- error: "#ef4444", // red-500
1794
+ "orange-raw": {
1795
+ highlight: "#ed702d",
1796
+ background: "#1f2630",
1797
+ surface: "#1f2630",
1798
+ text: "#f1f5f9",
1799
+ border: "#ed702d",
1800
+ success: "#22c55e",
1801
+ warning: "#eab308",
1802
+ error: "#ef4444",
1753
1803
  borderRadius: "0px",
1754
1804
  fontFamily: "Inter, system-ui, sans-serif",
1755
1805
  },
1756
- "light-professional": {
1757
- highlight: "#2563eb", // Blue 600
1758
- background: "#f8fafc", // Slate 50
1759
- surface: "#ffffff", // White
1760
- text: "#1e293b", // Slate 800
1761
- border: "#e2e8f0", // Slate 200
1762
- success: "#059669", // Emerald 600
1763
- warning: "#d97706", // Amber 600
1764
- error: "#b91c1c", // Red 700
1765
- borderRadius: "4px",
1766
- fontFamily: "system-ui, -apple-system, sans-serif",
1767
- },
1768
- "dark-night": {
1769
- highlight: "#3b82f6", // blue-500 - bright blue accent
1770
- background: "#0f172a", // slate-900 - dark background
1771
- surface: "#1e293b", // slate-800 - dark cards
1772
- text: "#f1f5f9", // slate-100 - light text
1773
- border: "#334155", // slate-700 - subtle borders
1774
- success: "#22c55e", // green-500
1775
- warning: "#eab308", // yellow-500
1776
- error: "#ef4444", // red-500
1777
- borderRadius: "8px",
1778
- fontFamily: "Inter, system-ui, sans-serif",
1779
- },
1780
- "dark-modern": {
1781
- highlight: "#3b82f6", // blue-500 - bright blue accent
1782
- background: "#0f172a", // slate-900 - dark background
1783
- surface: "#1e293b", // slate-800 - dark cards
1784
- text: "#f1f5f9", // slate-100 - light text
1785
- border: "#334155", // slate-700 - subtle borders
1786
- success: "#22c55e", // green-500
1787
- warning: "#eab308", // yellow-500
1788
- error: "#ef4444", // red-500
1789
- borderRadius: "8px",
1790
- fontFamily: "Inter, system-ui, sans-serif",
1806
+ // --- Dark Themes ---
1807
+ "navy-night": {
1808
+ highlight: "#60a5fa",
1809
+ background: "#0b1120",
1810
+ surface: "#111827",
1811
+ text: "#e2e8f0",
1812
+ border: "#1e3a5f",
1813
+ success: "#34d399",
1814
+ warning: "#fbbf24",
1815
+ error: "#f87171",
1816
+ borderRadius: "10px",
1817
+ fontFamily: "'Outfit', system-ui, sans-serif",
1791
1818
  },
1792
- "dark-forest": {
1793
- highlight: "#34d399", // Emerald 400
1794
- background: "#05140d",
1795
- surface: "#062215",
1796
- text: "#d1fae5", // Emerald 100
1797
- border: "#043322",
1798
- success: "#4ade80", // Green 400
1799
- warning: "#facc15", // Yellow 400
1800
- error: "#f87171", // Red 400
1819
+ "green-deep": {
1820
+ highlight: "#34d399",
1821
+ background: "#030d07",
1822
+ surface: "#051a0e",
1823
+ text: "#d1fae5",
1824
+ border: "#064e20",
1825
+ success: "#4ade80",
1826
+ warning: "#facc15",
1827
+ error: "#f87171",
1801
1828
  borderRadius: "12px",
1802
- fontFamily: "system-ui, -apple-system, sans-serif",
1829
+ fontFamily: "'Instrument Sans', system-ui, sans-serif",
1803
1830
  },
1804
- "dark-matrix": {
1805
- highlight: "#33ff33",
1831
+ "green-matrix": {
1832
+ highlight: "#39ff14",
1806
1833
  background: "#000000",
1807
- surface: "#0a0a0a",
1808
- text: "#00ff00",
1809
- border: "#1a1a1a",
1810
- success: "#33ff33",
1834
+ surface: "#060f06",
1835
+ text: "#00ff41",
1836
+ border: "#0d2b0d",
1837
+ success: "#39ff14",
1811
1838
  warning: "#ffff00",
1812
1839
  error: "#ff3333",
1813
1840
  borderRadius: "0px",
1814
- fontFamily: "'Courier New', monospace",
1841
+ fontFamily: "'Share Tech Mono', monospace",
1815
1842
  },
1816
- "dark-luxury": {
1817
- highlight: "#fde047", // Yellow 300
1818
- background: "#1c1917", // Stone 900
1819
- surface: "#292524", // Stone 800
1820
- text: "#fafaf9", // Stone 50
1821
- border: "#44403c", // Stone 700
1822
- success: "#a3e635", // Lime 400
1823
- warning: "#f59e0b", // Amber 500
1824
- error: "#fca5a5", // Red 300
1843
+ "gold-luxury": {
1844
+ highlight: "#fde047",
1845
+ background: "#1c1917",
1846
+ surface: "#292524",
1847
+ text: "#fafaf9",
1848
+ border: "#44403c",
1849
+ success: "#a3e635",
1850
+ warning: "#f59e0b",
1851
+ error: "#fca5a5",
1825
1852
  borderRadius: "24px",
1826
- fontFamily: "'Cinzel', serif",
1853
+ fontFamily: "'Bodoni Moda', serif",
1827
1854
  },
1828
- "dark-energetic": {
1829
- highlight: "#d946ef", // Fuchsia 500
1830
- background: "#2e1046",
1831
- surface: "#401561",
1832
- text: "#f3e8ff", // Purple 50
1833
- border: "#581c87", // Purple 900
1834
- success: "#4ade80", // Green 400
1835
- warning: "#facc15", // Yellow 400
1836
- error: "#f87171", // Red 400
1855
+ "purple-electric": {
1856
+ highlight: "#d946ef",
1857
+ background: "#110820",
1858
+ surface: "#1a0d30",
1859
+ text: "#f3e8ff",
1860
+ border: "#3b0764",
1861
+ success: "#4ade80",
1862
+ warning: "#facc15",
1863
+ error: "#f87171",
1837
1864
  borderRadius: "14px",
1838
1865
  fontFamily: "'Geologica', sans-serif",
1839
1866
  },
1867
+ "dark-neon-brutalism": {
1868
+ highlight: "#00e5cc",
1869
+ background: "#0e1420",
1870
+ surface: "#0e1420",
1871
+ text: "#e8eef5",
1872
+ border: "#00e5cc",
1873
+ success: "#00e5cc",
1874
+ warning: "#ffe45e",
1875
+ error: "#ff4d6d",
1876
+ borderRadius: "4px",
1877
+ fontFamily: "'JetBrains Mono', monospace",
1878
+ buttonTextColor: "#0e1420",
1879
+ },
1880
+ "rose-editorial": {
1881
+ highlight: "#be3455",
1882
+ background: "#fdf8f5",
1883
+ surface: "#ffffff",
1884
+ text: "#1a0a0f",
1885
+ border: "#f2d5db",
1886
+ success: "#2d6a4f",
1887
+ warning: "#b5451b",
1888
+ error: "#9b1d20",
1889
+ borderRadius: "0px",
1890
+ fontFamily: "'Cormorant', serif",
1891
+ },
1892
+ "amber-retro": {
1893
+ highlight: "#f59e0b",
1894
+ background: "#1a1008",
1895
+ surface: "#241a0a",
1896
+ text: "#fef3c7",
1897
+ border: "#78350f",
1898
+ success: "#84cc16",
1899
+ warning: "#f59e0b",
1900
+ error: "#ef4444",
1901
+ borderRadius: "6px",
1902
+ fontFamily: "'Syne', sans-serif",
1903
+ },
1840
1904
  };
1841
1905
  const StyleProvider = ({ config, children, }) => {
1842
1906
  // Track hydration state to prevent mismatches
@@ -1846,8 +1910,8 @@ const StyleProvider = ({ config, children, }) => {
1846
1910
  }, []);
1847
1911
  // PERFORMANCE OPTIMIZATION: Memoize style calculations
1848
1912
  const themedStyles = React.useMemo(() => {
1849
- const themeName = config.theme || "light-fresh";
1850
- const themeDefaults = themes[themeName] || themes["light-fresh"];
1913
+ const themeName = config.theme || "teal-minimal";
1914
+ const themeDefaults = themes[themeName] || themes["teal-minimal"];
1851
1915
  const getCSSValue = (value, fallback) => {
1852
1916
  if (!value)
1853
1917
  return fallback;
@@ -1917,6 +1981,7 @@ const StyleProvider = ({ config, children, }) => {
1917
1981
  "--bw-surface-color": finalColors.surface,
1918
1982
  "--bw-text-color": finalColors.text,
1919
1983
  "--bw-text-muted": addOpacity(finalColors.text, 0.7),
1984
+ "--bw-button-text-color": themeDefaults.buttonTextColor || "#ffffff",
1920
1985
  "--bw-border-color": finalColors.border,
1921
1986
  "--bw-success-color": finalColors.success,
1922
1987
  "--bw-warning-color": finalColors.warning,
@@ -1934,7 +1999,7 @@ const StyleProvider = ({ config, children, }) => {
1934
1999
  "--bw-highlight-muted": addOpacity(finalColors.highlight, 0.1),
1935
2000
  "--bw-highlight-subtle": addOpacity(finalColors.highlight, 0.05),
1936
2001
  "--bw-text-subtle": addOpacity(finalColors.text, 0.4),
1937
- colorScheme: themeName.startsWith("dark-") || themeName === "dark-night" ? "dark" : "light",
2002
+ colorScheme: (["navy-night", "green-deep", "green-matrix", "gold-luxury", "purple-electric", "dark-neon-brutalism", "amber-retro", "orange-raw"].includes(themeName) || themeName.startsWith("dark-")) ? "dark" : "light",
1938
2003
  };
1939
2004
  }, [
1940
2005
  config.theme,
@@ -4488,6 +4553,7 @@ function DialogWrapper({ isOpen, onClose, children, maxWidth = "700px", classNam
4488
4553
  "--bw-font-family": computedStyles.getPropertyValue("--bw-font-family").trim() || "system-ui, sans-serif",
4489
4554
  "--bw-shadow-md": computedStyles.getPropertyValue("--bw-shadow-md").trim() ||
4490
4555
  "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
4556
+ "--bw-button-text-color": computedStyles.getPropertyValue("--bw-button-text-color").trim() || "#ffffff",
4491
4557
  };
4492
4558
  setFallbackStyles(fallbacks);
4493
4559
  }
@@ -11197,11 +11263,13 @@ const buttonBase = {
11197
11263
  whiteSpace: "nowrap",
11198
11264
  border: "none",
11199
11265
  };
11266
+ // CSS class name for button hover effects
11267
+ const buttonClassName = "bw-button-hover";
11200
11268
  const buttonStyles = {
11201
11269
  primary: {
11202
11270
  ...buttonBase,
11203
11271
  backgroundColor: "var(--bw-highlight-color)",
11204
- color: "#ffffff",
11272
+ color: "var(--bw-button-text-color, #ffffff)",
11205
11273
  border: "none",
11206
11274
  },
11207
11275
  secondary: {
@@ -12729,8 +12797,8 @@ function VoucherPurchaseCard({ config, minEventPrice, fallbackImages = [], onCli
12729
12797
  const displayPrice = !config.allowMonetaryVouchers && minEventPrice && minEventPrice > 0
12730
12798
  ? minEventPrice
12731
12799
  : minEventPrice && minEventPrice > 0
12732
- ? Math.min(config.monetaryPresets[0] || 2500, minEventPrice)
12733
- : config.monetaryPresets[0] || 2500;
12800
+ ? Math.min(config.monetaryPresets[0] || 2000, minEventPrice)
12801
+ : config.monetaryPresets[0] || 2000;
12734
12802
  const slideshowImages = React.useMemo(() => Array.from(new Set(fallbackImages.filter(Boolean))).slice(0, 5), [fallbackImages]);
12735
12803
  const hasSlideshow = !config.image && slideshowImages.length > 0;
12736
12804
  React.useEffect(() => {
@@ -13384,7 +13452,7 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
13384
13452
  color: "var(--bw-text-muted)",
13385
13453
  fontFamily: "var(--bw-font-family)",
13386
13454
  }, children: [t("voucher.for"), " ", recipientName] }))] })] }), jsxRuntime.jsx("div", { style: {
13387
- fontSize: "24px",
13455
+ fontSize: "clamp(18px, 4vw, 22px)",
13388
13456
  fontWeight: 700,
13389
13457
  color: "var(--bw-highlight-color)",
13390
13458
  fontFamily: "var(--bw-font-family)",
@@ -13655,6 +13723,20 @@ function VoucherAttachment({ onClick }) {
13655
13723
  }, children: t("voucher.buyAsGift") })] }));
13656
13724
  }
13657
13725
 
13726
+ function VoucherIntegration({ config, voucherConfig, eventTypes, systemConfig, isFormOpen, isLoadingConfig, preselectedEventTypeId, voucherPurchaseResult, isSuccess, showStandaloneCard, onCardClick, onFormClose, onSuccess, onError, onSuccessModalClose, }) {
13727
+ if (!voucherConfig?.enabled) {
13728
+ return null;
13729
+ }
13730
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [showStandaloneCard && (jsxRuntime.jsx("div", { style: { padding: "0" }, children: jsxRuntime.jsx("div", { style: {
13731
+ display: "grid",
13732
+ gridTemplateColumns: "minmax(350px, 500px)",
13733
+ gap: "24px",
13734
+ justifyContent: "center",
13735
+ }, children: jsxRuntime.jsx(VoucherPurchaseCard, { config: voucherConfig, minEventPrice: eventTypes.length > 0
13736
+ ? Math.min(...eventTypes.map((et) => et.maxPrice))
13737
+ : undefined, fallbackImages: eventTypes.flatMap((eventType) => eventType.images || []), onClick: onCardClick, standalone: true }) }) })), jsxRuntime.jsx(VoucherPurchaseForm, { config: config, voucherConfig: voucherConfig, eventTypes: eventTypes, isOpen: isFormOpen, onClose: onFormClose, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, preselectedEventTypeId: preselectedEventTypeId, isLoadingEventTypes: isLoadingConfig }), isSuccess && voucherPurchaseResult && (jsxRuntime.jsx(VoucherSuccessModal, { isOpen: true, onClose: onSuccessModalClose, result: voucherPurchaseResult }))] }));
13738
+ }
13739
+
13658
13740
  // Helper function to preprocess markdown for underline support
13659
13741
  const preprocessMarkdown = (markdown) => {
13660
13742
  // Convert double underscores to HTML underline tags for React Markdown
@@ -13674,8 +13756,48 @@ function formatDurationInfo(info, t) {
13674
13756
  const rest = formatted.slice(0, -1).join(", ");
13675
13757
  return `${t("duration.optionally")} ${rest} ${t("duration.or")} ${last} ${unitPlural}`;
13676
13758
  }
13677
- function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
13759
+ function InfoBadge({ text }) {
13760
+ const [open, setOpen] = React.useState(false);
13761
+ const ref = React.useRef(null);
13762
+ return (jsxRuntime.jsxs("span", { ref: ref, onClick: (e) => { e.stopPropagation(); setOpen((v) => !v); }, style: {
13763
+ position: "relative",
13764
+ display: "inline-flex",
13765
+ alignItems: "center",
13766
+ justifyContent: "center",
13767
+ width: "16px",
13768
+ height: "16px",
13769
+ borderRadius: "50%",
13770
+ border: "1px solid var(--bw-highlight-color)",
13771
+ color: "var(--bw-highlight-color)",
13772
+ fontSize: "9px",
13773
+ fontWeight: 700,
13774
+ cursor: "pointer",
13775
+ flexShrink: 0,
13776
+ userSelect: "none",
13777
+ }, children: ["i", open && (jsxRuntime.jsx("span", { style: {
13778
+ position: "absolute",
13779
+ bottom: "calc(100% + 6px)",
13780
+ right: 0,
13781
+ backgroundColor: "var(--bw-surface-color)",
13782
+ border: "1px solid var(--bw-border-color)",
13783
+ borderRadius: "var(--bw-border-radius-small)",
13784
+ boxShadow: "var(--bw-shadow-md)",
13785
+ padding: "6px 10px",
13786
+ fontSize: "14px",
13787
+ color: "var(--bw-text-color)",
13788
+ fontWeight: 400,
13789
+ whiteSpace: "normal",
13790
+ width: "160px",
13791
+ lineHeight: 1.4,
13792
+ zIndex: 100,
13793
+ textAlign: "left",
13794
+ pointerEvents: "none",
13795
+ }, children: text }))] }));
13796
+ }
13797
+ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
13678
13798
  const t = useTranslations();
13799
+ const { locale } = useLocale();
13800
+ const timezone = useTimezone();
13679
13801
  // State for details dialog
13680
13802
  const [detailsDialogOpen, setDetailsDialogOpen] = React.useState(false);
13681
13803
  const [selectedEventTypeForDetails, setSelectedEventTypeForDetails] = React.useState(null);
@@ -13785,7 +13907,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
13785
13907
  display: "flex",
13786
13908
  flexDirection: "column",
13787
13909
  justifyContent: "space-between",
13788
- height: "400px",
13910
+ height: "490px",
13789
13911
  }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("h2", { className: "event-type-title", style: {
13790
13912
  fontSize: "clamp(1.1rem, 2.5vw, 24px)",
13791
13913
  fontWeight: 700,
@@ -13866,7 +13988,43 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
13866
13988
  color: "var(--bw-text-color)",
13867
13989
  fontFamily: "var(--bw-font-family)",
13868
13990
  textAlign: "right",
13869
- }, children: jsxRuntime.jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), jsxRuntime.jsxs("div", { style: {
13991
+ }, children: jsxRuntime.jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), (() => {
13992
+ const preview = eventType.cardPreview ?? [];
13993
+ return (jsxRuntime.jsxs("div", { style: {
13994
+ marginTop: "12px",
13995
+ borderTop: "1px solid var(--bw-border-color)",
13996
+ paddingTop: "8px",
13997
+ marginBottom: "16px",
13998
+ }, children: [jsxRuntime.jsx("div", { style: {
13999
+ fontSize: "11px",
14000
+ fontWeight: 700,
14001
+ color: "var(--bw-text-muted)",
14002
+ textTransform: "uppercase",
14003
+ letterSpacing: "0.05em",
14004
+ marginBottom: "4px",
14005
+ }, children: t("events.previewSectionTitle") }), jsxRuntime.jsx("div", { style: { height: "102px" }, children: Array.from({ length: 3 }).map((_, i) => {
14006
+ const item = preview[i];
14007
+ if (!item)
14008
+ return jsxRuntime.jsx("div", { style: { height: "34px" } }, i);
14009
+ const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
14010
+ return (jsxRuntime.jsxs("div", { onClick: (e) => { e.stopPropagation(); onInstancePreview?.(item.id, eventType.id); }, onMouseEnter: (e) => { if (onInstancePreview)
14011
+ e.currentTarget.style.backgroundColor = "var(--bw-border-color)"; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
14012
+ height: "34px",
14013
+ display: "grid",
14014
+ gridTemplateColumns: "auto 1fr auto 20px",
14015
+ alignItems: "center",
14016
+ gap: "8px",
14017
+ width: "100%",
14018
+ fontSize: "13px",
14019
+ color: "var(--bw-text-muted)",
14020
+ cursor: onInstancePreview ? "pointer" : "default",
14021
+ borderRadius: "4px",
14022
+ padding: "0 2px",
14023
+ transition: "background 0.15s",
14024
+ boxSizing: "border-box",
14025
+ }, children: [jsxRuntime.jsxs("span", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: "3px" }, children: [item.isSpecial && (jsxRuntime.jsx("span", { style: { color: "var(--bw-highlight-color)", fontSize: "11px", lineHeight: 1 }, children: "\u2605" })), formatWeekday(item.startTime, timezone, locale), " ", formatDate(item.startTime, timezone, locale)] }), jsxRuntime.jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, children: item.name }), jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [hasDiscount && (jsxRuntime.jsx("span", { style: { textDecoration: "line-through", opacity: 0.55, fontSize: "11px" }, children: formatCurrency(item.basePrice) })), jsxRuntime.jsx("span", { style: { fontWeight: 700, color: item.isSpecial ? "var(--bw-highlight-color)" : "var(--bw-text-color)" }, children: formatCurrency(item.price) })] }), item.specialDescription ? (jsxRuntime.jsx(InfoBadge, { text: item.specialDescription })) : (jsxRuntime.jsx("span", {}))] }, item.id));
14026
+ }) })] }));
14027
+ })(), jsxRuntime.jsxs("div", { style: {
13870
14028
  display: "flex",
13871
14029
  justifyContent: "flex-end",
13872
14030
  alignItems: "center",
@@ -13881,7 +14039,6 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
13881
14039
  backgroundColor: "var(--bw-surface-color)",
13882
14040
  padding: "12px",
13883
14041
  borderRadius: "var(--bw-border-radius)",
13884
- fontSize: "clamp(0.8rem, 2vw, 16px)",
13885
14042
  fontWeight: 600,
13886
14043
  fontFamily: "var(--bw-font-family)",
13887
14044
  display: "flex",
@@ -13894,7 +14051,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
13894
14051
  }, children: t("button.moreDetails") })), isAvailable && (jsxRuntime.jsxs("div", { style: {
13895
14052
  backgroundColor: "var(--bw-highlight-color)",
13896
14053
  color: "var(--bw-surface-color)",
13897
- padding: "12px 24px",
14054
+ padding: "12px 14px",
13898
14055
  borderRadius: "var(--bw-border-radius)",
13899
14056
  fontSize: "clamp(1rem, 2vw, 16px)",
13900
14057
  fontWeight: 600,
@@ -14552,6 +14709,172 @@ function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText
14552
14709
  }, children: "\u27F3" }), t("common.loading")] })) : (showAllButtonText) }) }))] }));
14553
14710
  }
14554
14711
 
14712
+ function SpecialsView({ specials, onEventSelect, isLoading = false, showSavingsAmount = true, showSavingsPercent = false, emptyStateText, isLoadingEventDetails = false, }) {
14713
+ const t = useTranslations();
14714
+ const { locale } = useLocale();
14715
+ const timezone = useTimezone();
14716
+ const [selectedId, setSelectedId] = React.useState(null);
14717
+ const handleSelect = (id) => {
14718
+ setSelectedId(id);
14719
+ onEventSelect(id);
14720
+ };
14721
+ if (isLoading) {
14722
+ return jsxRuntime.jsx(NextEventsSkeleton, { count: 3 });
14723
+ }
14724
+ if (specials.length === 0) {
14725
+ return (jsxRuntime.jsx("div", { style: { maxWidth: "500px", margin: "0 auto", padding: "16px" }, children: jsxRuntime.jsxs("div", { style: {
14726
+ display: "flex",
14727
+ flexDirection: "column",
14728
+ alignItems: "center",
14729
+ justifyContent: "center",
14730
+ textAlign: "center",
14731
+ backgroundColor: "var(--bw-surface-color)",
14732
+ border: "1px solid var(--bw-border-color)",
14733
+ borderRadius: "var(--bw-border-radius)",
14734
+ padding: "24px",
14735
+ fontFamily: "var(--bw-font-family)",
14736
+ minHeight: "300px",
14737
+ }, children: [jsxRuntime.jsx("div", { style: {
14738
+ display: "flex",
14739
+ alignItems: "center",
14740
+ justifyContent: "center",
14741
+ borderRadius: "50%",
14742
+ width: "64px",
14743
+ height: "64px",
14744
+ backgroundColor: "var(--bw-highlight-color)",
14745
+ marginBottom: "16px",
14746
+ fontSize: "32px",
14747
+ color: "#ffffff",
14748
+ opacity: 0.8,
14749
+ }, children: "\uD83C\uDFF7\uFE0F" }), jsxRuntime.jsx("h3", { style: {
14750
+ fontWeight: 600,
14751
+ margin: "0 0 8px 0",
14752
+ fontSize: "20px",
14753
+ color: "var(--bw-text-color)",
14754
+ fontFamily: "var(--bw-font-family)",
14755
+ }, children: t("specials.noSpecials") }), jsxRuntime.jsx("p", { style: {
14756
+ margin: 0,
14757
+ color: "var(--bw-text-muted)",
14758
+ fontSize: "16px",
14759
+ lineHeight: 1.6,
14760
+ fontFamily: "var(--bw-font-family)",
14761
+ maxWidth: "400px",
14762
+ }, children: emptyStateText ?? t("specials.noSpecialsMessage") })] }) }));
14763
+ }
14764
+ return (jsxRuntime.jsxs("div", { style: {
14765
+ maxWidth: "500px",
14766
+ margin: "0 auto",
14767
+ padding: "16px",
14768
+ fontFamily: "var(--bw-font-family)",
14769
+ }, children: [jsxRuntime.jsxs("div", { style: { textAlign: "center", marginBottom: "24px" }, children: [jsxRuntime.jsx("h2", { style: {
14770
+ fontWeight: 600,
14771
+ margin: "0 0 8px 0",
14772
+ fontSize: "18px",
14773
+ color: "var(--bw-text-color)",
14774
+ fontFamily: "var(--bw-font-family)",
14775
+ }, children: t("specials.title") }), jsxRuntime.jsx("p", { style: {
14776
+ margin: 0,
14777
+ fontSize: "16px",
14778
+ color: "var(--bw-text-muted)",
14779
+ fontFamily: "var(--bw-font-family)",
14780
+ }, children: t("specials.subtitle") })] }), jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: specials.map((special) => {
14781
+ const isFullyBooked = special.availableSpots === 0;
14782
+ const isDisabled = isFullyBooked || !special.bookingOpen;
14783
+ const hasDiscount = special.basePrice > 0 && special.price < special.basePrice;
14784
+ return (jsxRuntime.jsxs("div", { style: {
14785
+ position: "relative",
14786
+ backgroundColor: "var(--bw-surface-color)",
14787
+ borderRadius: "var(--bw-border-radius)",
14788
+ border: "1px solid var(--bw-highlight-color)",
14789
+ overflow: "hidden",
14790
+ opacity: isDisabled ? 0.5 : 1,
14791
+ cursor: isDisabled ? "not-allowed" : "pointer",
14792
+ transition: "all 0.2s ease",
14793
+ }, onClick: () => {
14794
+ if (!isDisabled)
14795
+ handleSelect(special.id);
14796
+ }, children: [selectedId === special.id && isLoadingEventDetails && (jsxRuntime.jsx("div", { style: {
14797
+ position: "absolute",
14798
+ inset: 0,
14799
+ display: "flex",
14800
+ alignItems: "center",
14801
+ justifyContent: "center",
14802
+ backgroundColor: "rgba(15, 23, 42, 0.8)",
14803
+ borderRadius: "var(--bw-border-radius)",
14804
+ zIndex: 10,
14805
+ }, children: jsxRuntime.jsx("div", { style: { fontSize: "32px", color: "var(--bw-highlight-color)", animation: "spin 1s linear infinite" }, children: "\u27F3" }) })), jsxRuntime.jsxs("div", { style: { display: "flex", gap: "12px", padding: "12px" }, children: [special.images.length > 0 && (jsxRuntime.jsx("div", { style: {
14806
+ flexShrink: 0,
14807
+ width: "72px",
14808
+ height: "72px",
14809
+ borderRadius: "var(--bw-border-radius-small)",
14810
+ overflow: "hidden",
14811
+ }, children: jsxRuntime.jsx("img", { src: special.images[0], alt: special.eventTypeName, style: { width: "100%", height: "100%", objectFit: "cover" } }) })), jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [jsxRuntime.jsx("div", { style: { marginBottom: "4px" }, children: jsxRuntime.jsx("span", { style: {
14812
+ fontSize: "11px",
14813
+ fontWeight: 600,
14814
+ color: "var(--bw-text-muted)",
14815
+ textTransform: "uppercase",
14816
+ letterSpacing: "0.05em",
14817
+ }, children: special.categoryName }) }), jsxRuntime.jsx("h4", { style: {
14818
+ margin: "0 0 2px 0",
14819
+ fontSize: "15px",
14820
+ fontWeight: 600,
14821
+ color: "var(--bw-text-color)",
14822
+ lineHeight: 1.3,
14823
+ overflow: "hidden",
14824
+ textOverflow: "ellipsis",
14825
+ whiteSpace: "nowrap",
14826
+ }, children: special.eventTypeName }), jsxRuntime.jsxs("div", { style: {
14827
+ fontSize: "13px",
14828
+ fontWeight: 500,
14829
+ color: "var(--bw-highlight-color)",
14830
+ marginBottom: "2px",
14831
+ overflow: "hidden",
14832
+ textOverflow: "ellipsis",
14833
+ whiteSpace: "nowrap",
14834
+ }, children: ["\u2605 ", special.name] }), special.specialDescription && (jsxRuntime.jsx("div", { style: {
14835
+ fontSize: "12px",
14836
+ color: "var(--bw-text-muted)",
14837
+ marginBottom: "6px",
14838
+ lineHeight: 1.4,
14839
+ }, children: special.specialDescription })), jsxRuntime.jsxs("div", { style: { fontSize: "13px", color: "var(--bw-text-muted)", marginBottom: "8px" }, children: [formatWeekday(special.startTime, timezone, locale), ",", " ", formatDate(special.startTime, timezone, locale)] }), jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }, children: [hasDiscount && (jsxRuntime.jsx("span", { style: {
14840
+ fontSize: "13px",
14841
+ color: "var(--bw-text-muted)",
14842
+ textDecoration: "line-through",
14843
+ }, children: formatCurrency(special.basePrice) })), jsxRuntime.jsx("span", { style: {
14844
+ fontSize: "16px",
14845
+ fontWeight: 700,
14846
+ color: "var(--bw-highlight-color)",
14847
+ }, children: formatCurrency(special.price) }), hasDiscount && showSavingsAmount && (jsxRuntime.jsx("span", { style: {
14848
+ fontSize: "12px",
14849
+ fontWeight: 600,
14850
+ color: "#ffffff",
14851
+ backgroundColor: "var(--bw-success-color, #22c55e)",
14852
+ borderRadius: "4px",
14853
+ padding: "2px 6px",
14854
+ }, children: t("specials.save").replace("{{amount}}", formatCurrency(special.savings)) })), hasDiscount && showSavingsPercent && (jsxRuntime.jsx("span", { style: {
14855
+ fontSize: "12px",
14856
+ fontWeight: 600,
14857
+ color: "#ffffff",
14858
+ backgroundColor: "var(--bw-success-color, #22c55e)",
14859
+ borderRadius: "4px",
14860
+ padding: "2px 6px",
14861
+ }, children: t("specials.savePercent").replace("{{percent}}", String(special.savingsPercent)) }))] })] })] }), jsxRuntime.jsxs("div", { style: {
14862
+ borderTop: "1px solid var(--bw-border-color)",
14863
+ padding: "8px 12px",
14864
+ display: "flex",
14865
+ justifyContent: "space-between",
14866
+ alignItems: "center",
14867
+ backgroundColor: "var(--bw-background-color)",
14868
+ }, children: [jsxRuntime.jsx("span", { style: { fontSize: "12px", color: "var(--bw-text-muted)" }, children: isFullyBooked
14869
+ ? t("common.fullyBooked")
14870
+ : t("specials.spotsLeft").replace("{{count}}", String(special.availableSpots)) }), jsxRuntime.jsxs("span", { style: {
14871
+ fontSize: "13px",
14872
+ fontWeight: 600,
14873
+ color: "var(--bw-highlight-color)",
14874
+ }, children: [t("specials.bookNow"), " \u2192"] })] })] }, special.id));
14875
+ }) })] }));
14876
+ }
14877
+
14555
14878
  const getThemeConfig = (theme = "generic") => {
14556
14879
  switch (theme) {
14557
14880
  case "christmas":
@@ -14811,8 +15134,8 @@ const cardDisabledStyles = {
14811
15134
  };
14812
15135
  const checkboxContainerStyles = {
14813
15136
  position: "absolute",
14814
- top: "12px",
14815
- right: "12px",
15137
+ bottom: "12px",
15138
+ left: "12px",
14816
15139
  zIndex: 1,
14817
15140
  };
14818
15141
  const checkboxInnerStyles = {
@@ -14907,6 +15230,7 @@ const priceContainerStyles = {
14907
15230
  alignItems: "flex-end",
14908
15231
  marginTop: "8px",
14909
15232
  paddingTop: "8px",
15233
+ paddingBottom: "0px",
14910
15234
  borderTop: "1px solid var(--bw-border-color)",
14911
15235
  };
14912
15236
  const pricePerPersonStyles = {
@@ -14998,7 +15322,7 @@ function UpsellsStep({ upsells, selectedUpsells, participantCount, isLoading, is
14998
15322
  };
14999
15323
  const selectedTotal = calculateTotal();
15000
15324
  const selectedCount = selectedUpsells.length;
15001
- const footerContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), children: t("common.back") }), jsxRuntime.jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
15325
+ const footerContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), className: buttonClassName, children: t("common.back") }), jsxRuntime.jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), className: buttonClassName, children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
15002
15326
  return (jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: onClose, title: t("upsells.title"), footer: footerContent, children: jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", padding: "16px 16px" }, children: [isLoading && (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "12px", padding: "40px 20px", ...textStyles.muted }, children: [spinner(), jsxRuntime.jsx("span", { children: t("upsells.loading") })] })), !isLoading && upsells.length === 0 && (jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "40px 20px", ...textStyles.muted }, children: jsxRuntime.jsx("p", { children: t("upsells.noExtras") }) })), !isLoading && upsells.length > 0 && (jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", flex: 1, overflowY: "auto", paddingBottom: "16px" }, children: upsells.map((upsell) => (jsxRuntime.jsx(UpsellCard, { upsell: upsell, isSelected: isSelected(upsell.id), participantCount: participantCount, onSelect: () => selectUpsell(upsell.id) }, upsell.id))) })), selectedCount > 0 && (jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: "16px", paddingBottom: "16px", paddingTop: "16px", borderTop: "1px solid var(--bw-border-color)", fontSize: "14px" }, children: [jsxRuntime.jsx("span", { style: textStyles.muted, children: selectedCount === 1 ? t("upsells.selected", { count: selectedCount }) : t("upsells.selectedPlural", { count: selectedCount }) }), jsxRuntime.jsxs("span", { style: { fontWeight: 600, color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: ["+", formatCurrency(selectedTotal)] })] }))] }) }));
15003
15327
  }
15004
15328
 
@@ -15037,7 +15361,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15037
15361
  // Detect single event type mode and direct instance mode
15038
15362
  const isSingleEventTypeMode = !!config.eventTypeId && !config.categoryId && !config.eventTypeIds;
15039
15363
  const isDirectInstanceMode = !!config.eventInstanceId;
15040
- const isMultiEventListingContext = Boolean(config.categoryId || config.eventTypeIds?.length);
15041
15364
  // Voucher card integration logic:
15042
15365
  // - For standalone voucher mode: always show voucher card (it's the main content)
15043
15366
  // - For event listings (single or multi): show by default unless explicitly disabled
@@ -15047,6 +15370,8 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15047
15370
  : (config.voucherIntegration ?? (hasEventSelection && !isDirectInstanceMode));
15048
15371
  // Selection flow state
15049
15372
  const [currentStep, setCurrentStep] = React.useState("eventTypes");
15373
+ // Tracks where to return when closing the booking form
15374
+ const bookingReturnStep = React.useRef("eventInstances");
15050
15375
  const [eventTypes, setEventTypes] = React.useState([]);
15051
15376
  const [selectedEventType, setSelectedEventType] = React.useState(null);
15052
15377
  const [eventInstances, setEventInstances] = React.useState([]);
@@ -15059,6 +15384,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15059
15384
  // State for upcoming events (next-events view mode)
15060
15385
  const [upcomingEvents, setUpcomingEvents] = React.useState([]);
15061
15386
  const [showingPreview, setShowingPreview] = React.useState(true);
15387
+ // State for specials view mode
15388
+ const [specials, setSpecials] = React.useState([]);
15389
+ const [isLoadingSpecials, setIsLoadingSpecials] = React.useState(false);
15062
15390
  // New: sidebar open state for single event type mode
15063
15391
  const [sidebarOpen, setSidebarOpen] = React.useState(false);
15064
15392
  // Booking flow state
@@ -15094,8 +15422,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15094
15422
  // Load voucher config if:
15095
15423
  // - Explicit voucher params are set
15096
15424
  // - OR voucherIntegration is explicitly set (even to true/false)
15097
- // - OR it's a multi-event listing context (may want to show voucher card)
15098
- if (!isVoucherModeRequested && !isMultiEventListingContext)
15425
+ // - OR it's an event listing context (single or multi) that may want to show voucher card
15426
+ const shouldLoadVoucherConfig = isVoucherModeRequested || hasEventSelection;
15427
+ if (!shouldLoadVoucherConfig)
15099
15428
  return;
15100
15429
  setIsLoadingVoucherConfig(true);
15101
15430
  try {
@@ -15148,10 +15477,10 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15148
15477
  const mergedConfig = {
15149
15478
  enabled: true,
15150
15479
  monetaryPresets: allowMonetaryVouchers
15151
- ? (presetFromValue ?? serverConfig.monetaryPresets ?? [2500, 5000, 10000, 15000, 20000])
15480
+ ? (presetFromValue ?? serverConfig.monetaryPresets ?? [2000, 5000, 10000, 15000, 20000, 25000, 30000, 40000, 50000])
15152
15481
  : [],
15153
- minAmount: serverConfig.minAmount || 1000,
15154
- maxAmount: serverConfig.maxAmount || 100000,
15482
+ minAmount: serverConfig.minAmount || 500,
15483
+ maxAmount: serverConfig.maxAmount || 1000000,
15155
15484
  allowMonetaryVouchers,
15156
15485
  allowEventVouchers,
15157
15486
  title: serverConfig.title,
@@ -15186,8 +15515,8 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15186
15515
  const initializeWidget = async () => {
15187
15516
  try {
15188
15517
  setIsLoading(true);
15189
- // Load voucher config in parallel if voucher mode is requested or it's a multi-event context
15190
- if (isVoucherModeRequested || isMultiEventListingContext) {
15518
+ // Load voucher config in parallel if voucher mode is requested or there's event selection
15519
+ if (isVoucherModeRequested || hasEventSelection) {
15191
15520
  void loadVoucherConfig();
15192
15521
  }
15193
15522
  // Direct instance selection (old behavior)
@@ -15208,6 +15537,11 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15208
15537
  await loadUpcomingEvents();
15209
15538
  return;
15210
15539
  }
15540
+ // Specials view mode: load special offers
15541
+ if (viewMode === "specials") {
15542
+ await loadSpecials();
15543
+ return;
15544
+ }
15211
15545
  // Single event type mode: load event type and instances, but don't open sidebar yet
15212
15546
  if (isSingleEventTypeMode) {
15213
15547
  await loadEventTypes();
@@ -15233,7 +15567,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15233
15567
  }
15234
15568
  };
15235
15569
  void initializeWidget();
15236
- }, [config, isVoucherModeRequested, hasEventSelection, isStandaloneVoucherMode, isMultiEventListingContext]);
15570
+ }, [config, isVoucherModeRequested, hasEventSelection, isStandaloneVoucherMode]);
15237
15571
  // Re-fetch translated content when locale changes (skip initial mount)
15238
15572
  const prevLocaleRef = React.useRef(locale);
15239
15573
  React.useEffect(() => {
@@ -15431,6 +15765,45 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15431
15765
  setError(data.error || t("error.loadUpcomingEvents"));
15432
15766
  }
15433
15767
  };
15768
+ const loadSpecials = async () => {
15769
+ setIsLoadingSpecials(true);
15770
+ const specialsSettings = config.specialsSettings ?? {};
15771
+ const requestBody = {
15772
+ organizationId: config.organizationId,
15773
+ limit: specialsSettings.count ?? 20,
15774
+ };
15775
+ if (config.categoryId) {
15776
+ requestBody.categoryId = config.categoryId;
15777
+ }
15778
+ else if (config.eventTypeIds) {
15779
+ requestBody.eventTypeIds = config.eventTypeIds;
15780
+ }
15781
+ else if (config.eventTypeId) {
15782
+ requestBody.eventTypeId = config.eventTypeId;
15783
+ }
15784
+ try {
15785
+ const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/specials"), {
15786
+ method: "POST",
15787
+ headers: createApiHeaders(config, locale),
15788
+ body: JSON.stringify(requestBody),
15789
+ });
15790
+ const data = await response.json();
15791
+ if (response.ok) {
15792
+ const wl = extractWidgetLanguagePayload(data);
15793
+ if (wl) {
15794
+ onWidgetLanguage?.(wl);
15795
+ onTimezone?.(wl.timezone);
15796
+ }
15797
+ setSpecials(data.specials || []);
15798
+ }
15799
+ else {
15800
+ setError(data.error || t("error.loadUpcomingEvents"));
15801
+ }
15802
+ }
15803
+ finally {
15804
+ setIsLoadingSpecials(false);
15805
+ }
15806
+ };
15434
15807
  const loadEventInstances = async (eventTypeId) => {
15435
15808
  const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/event-instances"), {
15436
15809
  method: "POST",
@@ -15615,6 +15988,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15615
15988
  // Event instance selection handlers
15616
15989
  const handleEventInstanceSelect = async (eventInstance) => {
15617
15990
  setSelectedEventInstance(eventInstance);
15991
+ bookingReturnStep.current = "eventInstances";
15618
15992
  // Set default participant count for upsell calculations
15619
15993
  const defaultParticipantCount = 1;
15620
15994
  setTempParticipantCount(defaultParticipantCount);
@@ -15627,7 +16001,14 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15627
16001
  if (availableUpsells.length > 0) {
15628
16002
  // Show upsells step
15629
16003
  setUpsells(availableUpsells);
15630
- setSelectedUpsells([]);
16004
+ // Pre-select default-checked upsells
16005
+ const defaultSelections = availableUpsells
16006
+ .filter((upsell) => upsell.defaultChecked && upsell.available)
16007
+ .map((upsell) => ({
16008
+ upsellPackageId: upsell.id,
16009
+ quantity: defaultParticipantCount,
16010
+ }));
16011
+ setSelectedUpsells(defaultSelections);
15631
16012
  setCurrentStep("upsells");
15632
16013
  setIsLoadingUpsells(false);
15633
16014
  return; // Don't proceed to booking yet
@@ -15658,7 +16039,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15658
16039
  setEventInstances([]);
15659
16040
  };
15660
16041
  const handleBackToEventInstances = () => {
15661
- setCurrentStep("eventInstances");
16042
+ setCurrentStep(bookingReturnStep.current);
15662
16043
  setSelectedEventInstance(null);
15663
16044
  setEventDetails(null);
15664
16045
  };
@@ -15692,8 +16073,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15692
16073
  }
15693
16074
  };
15694
16075
  const handleUpsellsBack = () => {
15695
- // Go back to event instance selection
15696
- setCurrentStep("eventInstances");
16076
+ setCurrentStep(bookingReturnStep.current);
15697
16077
  setSelectedUpsells([]);
15698
16078
  setUpsells([]);
15699
16079
  };
@@ -15728,10 +16108,36 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15728
16108
  setError(errorMessage);
15729
16109
  config.onError?.(errorMessage);
15730
16110
  };
15731
- const handleUpcomingEventSelect = async (eventInstanceId) => {
16111
+ const handleUpcomingEventSelect = async (eventInstanceId, eventTypeId) => {
16112
+ // Resolve the event type — may come from card preview (eventTypeId provided) or
16113
+ // from the next-events list where selectedEventType is already set
16114
+ const resolvedEventType = eventTypeId != null
16115
+ ? (eventTypes.find((et) => et.id === eventTypeId) ?? selectedEventType)
16116
+ : selectedEventType;
16117
+ if (resolvedEventType && resolvedEventType !== selectedEventType) {
16118
+ setSelectedEventType(resolvedEventType);
16119
+ }
16120
+ // Check if this is coming from a card preview (eventTypeId was provided)
16121
+ // In that case, we need to load event instances so back navigation works properly
16122
+ const isFromCardPreview = eventTypeId != null;
16123
+ if (isFromCardPreview && resolvedEventType) {
16124
+ // Load event instances in background so back navigation shows instances
16125
+ setShouldRenderInstanceSelection(true);
16126
+ void loadEventInstances(resolvedEventType.id);
16127
+ // Record that we should return to instance selection (not event types)
16128
+ bookingReturnStep.current = "eventInstances";
16129
+ }
16130
+ else {
16131
+ // Record where to return when the booking form is closed
16132
+ bookingReturnStep.current = currentStep === "eventInstances" ? "eventInstances" : "eventTypes";
16133
+ }
16134
+ // First try to find the event in upcomingEvents (for next-events view mode)
15732
16135
  const upcomingEvent = upcomingEvents.find((event) => event.id === eventInstanceId);
15733
- if (upcomingEvent) {
15734
- const eventInstance = {
16136
+ // If not found in upcomingEvents, try to find in card preview items
16137
+ const cardPreviewItem = !upcomingEvent && resolvedEventType?.cardPreview?.find((item) => item.id === eventInstanceId);
16138
+ // Build the event instance from either source
16139
+ const eventInstance = upcomingEvent
16140
+ ? {
15735
16141
  id: upcomingEvent.id,
15736
16142
  name: upcomingEvent.name,
15737
16143
  startTime: upcomingEvent.startTime,
@@ -15745,13 +16151,63 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15745
16151
  bookingOpen: upcomingEvent.bookingOpen,
15746
16152
  ...(upcomingEvent.deposit !== undefined && { deposit: upcomingEvent.deposit }),
15747
16153
  ...(upcomingEvent.notes !== undefined && { notes: upcomingEvent.notes }),
15748
- };
16154
+ }
16155
+ : cardPreviewItem
16156
+ ? {
16157
+ id: cardPreviewItem.id,
16158
+ name: cardPreviewItem.name,
16159
+ startTime: cardPreviewItem.startTime,
16160
+ endTime: cardPreviewItem.startTime,
16161
+ price: cardPreviewItem.price,
16162
+ maxParticipants: cardPreviewItem.availableSpots + 1,
16163
+ participantCount: 1,
16164
+ availableSpots: cardPreviewItem.availableSpots,
16165
+ durationDays: 1,
16166
+ durationPerDay: 1,
16167
+ bookingOpen: true,
16168
+ }
16169
+ : null;
16170
+ if (eventInstance) {
15749
16171
  setSelectedEventInstance(eventInstance);
15750
16172
  }
16173
+ setError(null);
16174
+ // Check for upsells before going to booking (same as handleEventInstanceSelect)
16175
+ const eventTypeForUpsells = resolvedEventType;
16176
+ if (eventTypeForUpsells) {
16177
+ const defaultParticipantCount = 1;
16178
+ setTempParticipantCount(defaultParticipantCount);
16179
+ setIsLoadingUpsells(true);
16180
+ setShouldRenderUpsells(true);
16181
+ try {
16182
+ const availableUpsells = await loadUpsells(eventTypeForUpsells.id, eventInstanceId, defaultParticipantCount);
16183
+ if (availableUpsells.length > 0) {
16184
+ setUpsells(availableUpsells);
16185
+ // Pre-select default-checked upsells
16186
+ const defaultSelections = availableUpsells
16187
+ .filter((upsell) => upsell.defaultChecked && upsell.available)
16188
+ .map((upsell) => ({
16189
+ upsellPackageId: upsell.id,
16190
+ quantity: defaultParticipantCount,
16191
+ }));
16192
+ setSelectedUpsells(defaultSelections);
16193
+ setCurrentStep("upsells");
16194
+ setIsLoadingUpsells(false);
16195
+ // Load event details in background for when user continues past upsells
16196
+ void loadEventDetails(eventInstanceId);
16197
+ return;
16198
+ }
16199
+ }
16200
+ catch (err) {
16201
+ console.error("Error loading upsells:", err);
16202
+ }
16203
+ finally {
16204
+ setIsLoadingUpsells(false);
16205
+ }
16206
+ }
16207
+ // No upsells — go directly to booking
15751
16208
  setCurrentStep("booking");
15752
16209
  setShouldRenderBookingForm(true);
15753
16210
  setIsLoadingEventDetails(true);
15754
- setError(null);
15755
16211
  try {
15756
16212
  await loadEventDetails(eventInstanceId);
15757
16213
  }
@@ -15908,6 +16364,31 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15908
16364
  window.history.replaceState({}, "", url.toString());
15909
16365
  }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
15910
16366
  }
16367
+ if (viewMode === "specials" && showingPreview) {
16368
+ return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
16369
+ setCurrentStep("eventTypes");
16370
+ setShowingPreview(true);
16371
+ setEventDetails(null);
16372
+ }, onBackToEventTypes: () => {
16373
+ setCurrentStep("eventTypes");
16374
+ setShowingPreview(true);
16375
+ setEventDetails(null);
16376
+ }, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => {
16377
+ setCurrentStep("eventTypes");
16378
+ setShowingPreview(true);
16379
+ setEventDetails(null);
16380
+ }, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16381
+ setIsSuccess(false);
16382
+ setCurrentStep("eventTypes");
16383
+ setShowingPreview(true);
16384
+ setSuccessPaymentId(null);
16385
+ setShouldRenderInstanceSelection(false);
16386
+ setShouldRenderUpsells(false);
16387
+ setShouldRenderBookingForm(false);
16388
+ setSelectedUpsells([]);
16389
+ setUpsells([]);
16390
+ }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16391
+ }
15911
16392
  if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
15912
16393
  return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
15913
16394
  setShowingPreview(true);
@@ -15978,14 +16459,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15978
16459
  }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
15979
16460
  }
15980
16461
  // Cards mode (default) - show event type selection with optional voucher card
15981
- const cardsView = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasEventSelection && (jsxRuntime.jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), voucherConfig?.enabled && voucherCardIntegrationEnabled && isStandaloneVoucherMode && (jsxRuntime.jsx("div", { style: { padding: hasEventSelection ? "0 0 24px 0" : "0" }, children: jsxRuntime.jsx("div", { style: {
15982
- display: "grid",
15983
- gridTemplateColumns: "minmax(350px, 500px)",
15984
- gap: "24px",
15985
- justifyContent: "center",
15986
- }, children: jsxRuntime.jsx(VoucherPurchaseCard, { config: voucherConfig, minEventPrice: voucherEventTypes.length > 0
15987
- ? Math.min(...voucherEventTypes.map((et) => et.maxPrice))
15988
- : undefined, fallbackImages: voucherEventTypes.flatMap((eventType) => eventType.images || []), onClick: handleVoucherCardClick, standalone: true }) }) })), isStandaloneVoucherMode && isLoading && !voucherConfig && (jsxRuntime.jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsxRuntime.jsx("div", { style: {
16462
+ const cardsView = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasEventSelection && (jsxRuntime.jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, onInstancePreview: (instanceId, eventTypeId) => void handleUpcomingEventSelect(instanceId, eventTypeId), isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (jsxRuntime.jsx(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: false, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: null, voucherPurchaseResult: null, isSuccess: false, showStandaloneCard: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled), onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => { } })), isStandaloneVoucherMode && isLoading && !voucherConfig && (jsxRuntime.jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsxRuntime.jsx("div", { style: {
15989
16463
  display: "inline-block",
15990
16464
  width: "32px",
15991
16465
  height: "32px",
@@ -16038,19 +16512,17 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16038
16512
  url.searchParams.delete("mollie_payment_id");
16039
16513
  url.searchParams.delete("mollie_status");
16040
16514
  window.history.replaceState({}, "", url.toString());
16041
- }, config: config, onError: setError, paymentIntentId: successPaymentId }), isSuccess && voucherPurchaseResult && (jsxRuntime.jsx(VoucherSuccessModal, { isOpen: true, onClose: () => {
16515
+ }, config: config, onError: setError, paymentIntentId: successPaymentId }), jsxRuntime.jsx(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: () => {
16042
16516
  setIsSuccess(false);
16043
16517
  setVoucherPurchaseResult(null);
16044
16518
  const url = new URL(window.location.href);
16045
- // Clean up Stripe params
16046
16519
  url.searchParams.delete("payment_intent");
16047
16520
  url.searchParams.delete("payment_intent_client_secret");
16048
16521
  url.searchParams.delete("redirect_status");
16049
- // Clean up Mollie params
16050
16522
  url.searchParams.delete("mollie_payment_id");
16051
16523
  url.searchParams.delete("mollie_status");
16052
16524
  window.history.replaceState({}, "", url.toString());
16053
- }, result: voucherPurchaseResult })), voucherConfig?.enabled && (jsxRuntime.jsx(VoucherPurchaseForm, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, isOpen: isVoucherFormOpen, onClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, systemConfig: systemConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, isLoadingEventTypes: isLoadingVoucherConfig }))] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16525
+ } })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16054
16526
  }
16055
16527
  function UniversalBookingWidget(props) {
16056
16528
  const [languagePolicy, setLanguagePolicy] = React.useState(null);
@@ -16092,7 +16564,7 @@ function styleInject(css, ref) {
16092
16564
  }
16093
16565
  }
16094
16566
 
16095
- var css_248z = ".booking-widget-container{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;color:var(--bw-text-color,#1e293b);direction:ltr;display:block;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;position:relative;text-align:left}.booking-widget-container *,.booking-widget-container :after,.booking-widget-container :before{box-sizing:border-box;margin:0;padding:0}.booking-widget-container input,.booking-widget-container select,.booking-widget-container textarea{font-family:inherit;font-size:inherit;line-height:inherit}.booking-widget-container button{background:none;border:none;cursor:pointer;font-family:inherit;font-size:inherit}.booking-widget-container a{color:inherit;text-decoration:none}.booking-widget-container img{display:block;height:auto;max-width:100%;vertical-align:middle}.booking-widget-container ol,.booking-widget-container ul{list-style:none}.booking-widget-container h1,.booking-widget-container h2,.booking-widget-container h3,.booking-widget-container h4,.booking-widget-container h5,.booking-widget-container h6{font-size:inherit;font-weight:inherit}#booking-widget-portal{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);direction:ltr;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;text-align:left}#booking-widget-portal *,#booking-widget-portal :after,#booking-widget-portal :before{box-sizing:border-box}#booking-widget-portal-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);line-height:1.5}:root{--bw-highlight-color:#00b1aa;--bw-highlight-color-rgb:0,177,170;--bw-background-color:#f8fdfe;--bw-surface-color:#fff;--bw-text-color:#0e7490;--bw-text-muted:rgba(14,116,144,.7);--bw-border-color:#bae6fd;--bw-success-color:#38bdf8;--bw-warning-color:#fbbf24;--bw-error-color:#f43f5e;--bw-border-radius:18px;--bw-border-radius-small:calc(var(--bw-border-radius)*0.8);--bw-spacing:16px;--bw-spacing-large:24px;--bw-font-family:\"Inter\",system-ui,sans-serif;--bw-font-size:14px;--bw-font-size-large:18px;--bw-font-size-small:12px;--bw-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--bw-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--bw-highlight-muted:rgba(0,177,170,.1);--bw-highlight-subtle:rgba(0,177,170,.05);--bw-text-subtle:rgba(14,116,144,.4)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes shimmer{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes slide-in-right{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes slide-out-right{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(100%)}}@keyframes slide-in-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.animate-spin{animation:spin 1s linear infinite}.animate-shimmer{animation:shimmer 2s infinite}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-fade-in{animation:fade-in .2s ease-out}.animate-slide-in-up{animation:slide-in-up .3s ease-out}.animate-scale-in{animation:scale-in .2s ease-out}.skeleton-shimmer{overflow:hidden;position:relative}.skeleton-shimmer:after{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.3),transparent);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}@media (max-width:768px){.sidebar-mobile{border-radius:0!important;max-width:100%!important;width:100%!important}}@media (max-width:600px){.event-type-list{gap:12px!important;padding:8px!important}.event-type-card{flex:1 1 100%!important;max-width:100%!important;padding:0!important}.event-type-img{height:160px!important}.event-type-title{font-size:1.1rem!important}.event-type-desc{font-size:.8rem!important;max-height:100px!important;min-height:100px!important}.event-type-content{padding:16px 24px!important}}.event-type-markdown{overflow:visible!important}.event-type-markdown p{color:var(--bw-text-muted);font-family:var(--bw-font-family);line-height:1.6;margin:0 0 8px}.event-type-markdown p:last-child{margin-bottom:0}.event-type-markdown h2{font-size:18px!important;font-weight:700!important;margin:12px 0 6px!important}.event-type-markdown h2,.event-type-markdown h3{color:var(--bw-text-color)!important;line-height:1.3!important}.event-type-markdown h3{font-size:16px!important;font-weight:600!important;margin:10px 0 4px!important}.event-type-markdown strong{color:var(--bw-text-color);font-weight:600}.event-type-markdown em{font-style:italic}.event-type-markdown u{text-decoration:underline}.event-type-markdown ul{list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ul li{color:var(--bw-text-muted)!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ul li:before{color:var(--bw-text-color)!important;content:\"•\"!important;font-weight:700!important;left:-16px!important;position:absolute!important;top:0!important}.event-type-markdown ol{counter-reset:list-counter!important;list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ol li{color:var(--bw-text-muted)!important;counter-increment:list-counter!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ol li:before{color:var(--bw-text-color)!important;content:counter(list-counter) \".\"!important;font-weight:700!important;left:-20px!important;position:absolute!important;top:0!important}.event-type-markdown blockquote{border-left:2px solid var(--bw-border-color);color:var(--bw-text-muted);font-style:italic;margin:4px 0;padding-left:12px}.event-type-markdown a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:var(--bw-text-color);font-weight:600;margin-bottom:.5em}.markdown-content h1{font-size:1.5em}.markdown-content h2{font-size:1.25em}.markdown-content h3{font-size:1.1em}.markdown-content p{line-height:1.6;margin-bottom:1em}.markdown-content ol,.markdown-content ul{margin-bottom:1em;padding-left:1.5em}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin-bottom:.25em}.markdown-content a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content a:hover{opacity:.8}.markdown-content strong{font-weight:600}.markdown-content em{font-style:italic}.markdown-content code{background:var(--bw-highlight-subtle);border-radius:4px;font-family:monospace;font-size:.9em;padding:.125em .25em}.markdown-content blockquote{border-left:3px solid var(--bw-highlight-color);color:var(--bw-text-muted);margin:1em 0;padding-left:1em}.print-only{display:none}.print-hidden{display:block}@media print{.print-only{display:block}.print-hidden{display:none!important}.print-booking-header{border-bottom:2px solid #000;display:block;margin-bottom:24px;padding-bottom:16px;text-align:center}.print-booking-header h1{font-size:24px;margin:0 0 8px}.print-booking-header .subtitle{color:#666;font-size:14px}.print-booking-card{border:1px solid #ccc;border-radius:8px;margin-bottom:16px;padding:16px;page-break-inside:avoid}.print-section-title{border-bottom:1px solid #ddd;display:block;font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px}.print-detail-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}.print-detail-item{margin-bottom:8px}.print-detail-label{color:#666;font-size:12px;margin-bottom:4px}.print-detail-value{font-size:14px;font-weight:600}.print-status-badge{border-radius:9999px;display:inline-block;font-size:12px;font-weight:600;padding:4px 12px}.print-status-paid{background-color:#dcfce7;color:#166534;display:inline-block}.print-participant{align-items:center;background-color:#f9fafb;border-radius:4px;display:flex;justify-content:space-between;margin-bottom:8px;padding:8px}.print-participant-name{font-weight:600}.print-participant-age{color:#666;font-size:12px}.print-payment-summary{display:block}.print-payment-row{border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:4px 0}.print-payment-row:last-child{border-bottom:none;font-weight:600}.print-footer{border-top:1px solid #ddd;color:#666;display:block;font-size:12px;margin-top:24px;padding-top:16px;text-align:center}.print-footer p{margin:4px 0}}";
16567
+ var css_248z = ".booking-widget-container{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;color:var(--bw-text-color,#1e293b);direction:ltr;display:block;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;position:relative;text-align:left}.booking-widget-container *,.booking-widget-container :after,.booking-widget-container :before{box-sizing:border-box;margin:0;padding:0}.booking-widget-container input,.booking-widget-container select,.booking-widget-container textarea{font-family:inherit;font-size:inherit;line-height:inherit}.booking-widget-container button{background:none;border:none;cursor:pointer;font-family:inherit;font-size:inherit}.booking-widget-container a{color:inherit;text-decoration:none}.booking-widget-container img{display:block;height:auto;max-width:100%;vertical-align:middle}.booking-widget-container ol,.booking-widget-container ul{list-style:none}.booking-widget-container h1,.booking-widget-container h2,.booking-widget-container h3,.booking-widget-container h4,.booking-widget-container h5,.booking-widget-container h6{font-size:inherit;font-weight:inherit}#booking-widget-portal{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);direction:ltr;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;text-align:left}#booking-widget-portal *,#booking-widget-portal :after,#booking-widget-portal :before{box-sizing:border-box}#booking-widget-portal-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);line-height:1.5}:root{--bw-highlight-color:#00b1aa;--bw-highlight-color-rgb:0,177,170;--bw-background-color:#f8fdfe;--bw-surface-color:#fff;--bw-text-color:#0e7490;--bw-text-muted:rgba(14,116,144,.7);--bw-border-color:#bae6fd;--bw-success-color:#38bdf8;--bw-warning-color:#fbbf24;--bw-error-color:#f43f5e;--bw-border-radius:18px;--bw-border-radius-small:calc(var(--bw-border-radius)*0.8);--bw-spacing:16px;--bw-spacing-large:24px;--bw-font-family:\"Inter\",system-ui,sans-serif;--bw-font-size:14px;--bw-font-size-large:18px;--bw-font-size-small:12px;--bw-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--bw-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--bw-highlight-muted:rgba(0,177,170,.1);--bw-highlight-subtle:rgba(0,177,170,.05);--bw-text-subtle:rgba(14,116,144,.4)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes shimmer{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes slide-in-right{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes slide-out-right{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(100%)}}@keyframes slide-in-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.animate-spin{animation:spin 1s linear infinite}.animate-shimmer{animation:shimmer 2s infinite}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-fade-in{animation:fade-in .2s ease-out}.animate-slide-in-up{animation:slide-in-up .3s ease-out}.animate-scale-in{animation:scale-in .2s ease-out}.skeleton-shimmer{overflow:hidden;position:relative}.skeleton-shimmer:after{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.3),transparent);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.bw-btn{transition:all .2s ease!important}.bw-btn:hover:not(:disabled):not([disabled]){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-btn:active:not(:disabled):not([disabled]){box-shadow:0 2px 4px rgba(0,0,0,.1)}.bw-btn-primary:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);filter:brightness(1.1)}.bw-btn-secondary:hover:not(:disabled):not([disabled]){background-color:var(--bw-surface-color);border-color:var(--bw-highlight-color);color:var(--bw-highlight-color)}.bw-btn-ghost:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-muted)}.bw-btn-outline:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);color:var(--bw-button-text-color,#fff)}.bw-btn:disabled,.bw-btn[disabled]{cursor:not-allowed!important;opacity:.5!important}button[class*=bw-btn],button[style*=transition]{transition:all .2s ease!important}button[data-variant=primary]:hover:not(:disabled),button[style*=\"--bw-highlight-color\"]:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);filter:brightness(1.1);transform:translateY(-1px)}button[data-variant=secondary]:hover:not(:disabled){border-color:var(--bw-highlight-color)!important;color:var(--bw-highlight-color)!important}button[data-variant=ghost]:hover:not(:disabled){background-color:var(--bw-highlight-muted)!important}button[data-variant=outline]:hover:not(:disabled){background-color:var(--bw-highlight-color)!important;color:var(--bw-button-text-color,#fff)!important}.bw-button-hover:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-button-hover:active:not(:disabled){box-shadow:0 2px 4px rgba(0,0,0,.1);transform:translateY(0)}@media (max-width:768px){.sidebar-mobile{border-radius:0!important;max-width:100%!important;width:100%!important}}@media (max-width:600px){.event-type-list{gap:12px!important;padding:8px!important}.event-type-card{flex:1 1 100%!important;max-width:100%!important;padding:0!important}.event-type-img{height:160px!important}.event-type-title{font-size:1.1rem!important}.event-type-desc{font-size:.8rem!important;max-height:100px!important;min-height:100px!important}.event-type-content{padding:16px 24px!important}}.event-type-markdown{overflow:visible!important}.event-type-markdown p{color:var(--bw-text-muted);font-family:var(--bw-font-family);line-height:1.6;margin:0 0 8px}.event-type-markdown p:last-child{margin-bottom:0}.event-type-markdown h2{font-size:18px!important;font-weight:700!important;margin:12px 0 6px!important}.event-type-markdown h2,.event-type-markdown h3{color:var(--bw-text-color)!important;line-height:1.3!important}.event-type-markdown h3{font-size:16px!important;font-weight:600!important;margin:10px 0 4px!important}.event-type-markdown strong{color:var(--bw-text-color);font-weight:600}.event-type-markdown em{font-style:italic}.event-type-markdown u{text-decoration:underline}.event-type-markdown ul{list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ul li{color:var(--bw-text-muted)!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ul li:before{color:var(--bw-text-color)!important;content:\"•\"!important;font-weight:700!important;left:-16px!important;position:absolute!important;top:0!important}.event-type-markdown ol{counter-reset:list-counter!important;list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ol li{color:var(--bw-text-muted)!important;counter-increment:list-counter!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ol li:before{color:var(--bw-text-color)!important;content:counter(list-counter) \".\"!important;font-weight:700!important;left:-20px!important;position:absolute!important;top:0!important}.event-type-markdown blockquote{border-left:2px solid var(--bw-border-color);color:var(--bw-text-muted);font-style:italic;margin:4px 0;padding-left:12px}.event-type-markdown a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:var(--bw-text-color);font-weight:600;margin-bottom:.5em}.markdown-content h1{font-size:1.5em}.markdown-content h2{font-size:1.25em}.markdown-content h3{font-size:1.1em}.markdown-content p{line-height:1.6;margin-bottom:1em}.markdown-content ol,.markdown-content ul{margin-bottom:1em;padding-left:1.5em}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin-bottom:.25em}.markdown-content a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content a:hover{opacity:.8}.markdown-content strong{font-weight:600}.markdown-content em{font-style:italic}.markdown-content code{background:var(--bw-highlight-subtle);border-radius:4px;font-family:monospace;font-size:.9em;padding:.125em .25em}.markdown-content blockquote{border-left:3px solid var(--bw-highlight-color);color:var(--bw-text-muted);margin:1em 0;padding-left:1em}.print-only{display:none}.print-hidden{display:block}@media print{.print-only{display:block}.print-hidden{display:none!important}.print-booking-header{border-bottom:2px solid #000;display:block;margin-bottom:24px;padding-bottom:16px;text-align:center}.print-booking-header h1{font-size:24px;margin:0 0 8px}.print-booking-header .subtitle{color:#666;font-size:14px}.print-booking-card{border:1px solid #ccc;border-radius:8px;margin-bottom:16px;padding:16px;page-break-inside:avoid}.print-section-title{border-bottom:1px solid #ddd;display:block;font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px}.print-detail-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}.print-detail-item{margin-bottom:8px}.print-detail-label{color:#666;font-size:12px;margin-bottom:4px}.print-detail-value{font-size:14px;font-weight:600}.print-status-badge{border-radius:9999px;display:inline-block;font-size:12px;font-weight:600;padding:4px 12px}.print-status-paid{background-color:#dcfce7;color:#166534;display:inline-block}.print-participant{align-items:center;background-color:#f9fafb;border-radius:4px;display:flex;justify-content:space-between;margin-bottom:8px;padding:8px}.print-participant-name{font-weight:600}.print-participant-age{color:#666;font-size:12px}.print-payment-summary{display:block}.print-payment-row{border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:4px 0}.print-payment-row:last-child{border-bottom:none;font-weight:600}.print-footer{border-top:1px solid #ddd;color:#666;display:block;font-size:12px;margin-top:24px;padding-top:16px;text-align:center}.print-footer p{margin:4px 0}}";
16096
16568
  styleInject(css_248z);
16097
16569
 
16098
16570
  // Export init function for vanilla JS usage