@bigz-app/booking-widget 1.4.4 → 1.4.5

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 +17305 -17103
  2. package/dist/booking-widget.js.map +1 -1
  3. package/dist/components/UniversalBookingWidget.d.ts +12 -4
  4. package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
  5. package/dist/components/booking/BookingForm.d.ts +1 -3
  6. package/dist/components/booking/BookingForm.d.ts.map +1 -1
  7. package/dist/components/booking/GiftCardOnlyBooking.d.ts.map +1 -1
  8. package/dist/components/booking/HoldCountdown.d.ts +10 -0
  9. package/dist/components/booking/HoldCountdown.d.ts.map +1 -0
  10. package/dist/components/booking/MolliePaymentForm.d.ts.map +1 -1
  11. package/dist/components/booking/StripePaymentForm.d.ts +1 -2
  12. package/dist/components/booking/StripePaymentForm.d.ts.map +1 -1
  13. package/dist/components/events/EventInstanceSelection.d.ts +3 -1
  14. package/dist/components/events/EventInstanceSelection.d.ts.map +1 -1
  15. package/dist/components/events/EventTypeDetailsDialog.d.ts +0 -1
  16. package/dist/components/events/EventTypeDetailsDialog.d.ts.map +1 -1
  17. package/dist/components/events/EventTypeSelection.d.ts +1 -1
  18. package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
  19. package/dist/components/events/WaitlistDialog.d.ts +10 -0
  20. package/dist/components/events/WaitlistDialog.d.ts.map +1 -0
  21. package/dist/components/voucher/VoucherPurchaseForm.d.ts +0 -1
  22. package/dist/components/voucher/VoucherPurchaseForm.d.ts.map +1 -1
  23. package/dist/components/voucher/useVoucherConfig.d.ts +0 -1
  24. package/dist/components/voucher/useVoucherConfig.d.ts.map +1 -1
  25. package/dist/i18n/locales/de.d.ts.map +1 -1
  26. package/dist/i18n/locales/en.d.ts.map +1 -1
  27. package/dist/index.cjs +561 -359
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.esm.js +548 -346
  30. package/dist/index.esm.js.map +1 -1
  31. package/dist/utils/analytics.d.ts +3 -1
  32. package/dist/utils/analytics.d.ts.map +1 -1
  33. package/dist/utils.d.ts.map +1 -1
  34. package/dist/validation/booking-schema.d.ts +4 -4
  35. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -6,188 +6,24 @@ var jsxRuntime = require('react/jsx-runtime');
6
6
  var ReactDOM = require('react-dom');
7
7
 
8
8
  function _interopNamespaceDefault(e) {
9
- var n = Object.create(null);
10
- if (e) {
11
- Object.keys(e).forEach(function (k) {
12
- if (k !== 'default') {
13
- var d = Object.getOwnPropertyDescriptor(e, k);
14
- Object.defineProperty(n, k, d.get ? d : {
15
- enumerable: true,
16
- get: function () { return e[k]; }
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
17
19
  });
18
- }
19
- });
20
- }
21
- n.default = e;
22
- return Object.freeze(n);
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
23
  }
24
24
 
25
25
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
26
26
 
27
- var V3_URL = 'https://js.stripe.com/v3';
28
- var V3_URL_REGEX = /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;
29
- var EXISTING_SCRIPT_MESSAGE = 'loadStripe.setLoadParameters was called but an existing Stripe.js script already exists in the document; existing script parameters will be used';
30
- var findScript = function findScript() {
31
- var scripts = document.querySelectorAll("script[src^=\"".concat(V3_URL, "\"]"));
32
-
33
- for (var i = 0; i < scripts.length; i++) {
34
- var script = scripts[i];
35
-
36
- if (!V3_URL_REGEX.test(script.src)) {
37
- continue;
38
- }
39
-
40
- return script;
41
- }
42
-
43
- return null;
44
- };
45
-
46
- var injectScript = function injectScript(params) {
47
- var queryString = '';
48
- var script = document.createElement('script');
49
- script.src = "".concat(V3_URL).concat(queryString);
50
- var headOrBody = document.head || document.body;
51
-
52
- if (!headOrBody) {
53
- throw new Error('Expected document.body not to be null. Stripe.js requires a <body> element.');
54
- }
55
-
56
- headOrBody.appendChild(script);
57
- return script;
58
- };
59
-
60
- var registerWrapper = function registerWrapper(stripe, startTime) {
61
- if (!stripe || !stripe._registerWrapper) {
62
- return;
63
- }
64
-
65
- stripe._registerWrapper({
66
- name: 'stripe-js',
67
- version: "4.6.0",
68
- startTime: startTime
69
- });
70
- };
71
-
72
- var stripePromise = null;
73
- var onErrorListener = null;
74
- var onLoadListener = null;
75
-
76
- var onError = function onError(reject) {
77
- return function () {
78
- reject(new Error('Failed to load Stripe.js'));
79
- };
80
- };
81
-
82
- var onLoad = function onLoad(resolve, reject) {
83
- return function () {
84
- if (window.Stripe) {
85
- resolve(window.Stripe);
86
- } else {
87
- reject(new Error('Stripe.js not available'));
88
- }
89
- };
90
- };
91
-
92
- var loadScript = function loadScript(params) {
93
- // Ensure that we only attempt to load Stripe.js at most once
94
- if (stripePromise !== null) {
95
- return stripePromise;
96
- }
97
-
98
- stripePromise = new Promise(function (resolve, reject) {
99
- if (typeof window === 'undefined' || typeof document === 'undefined') {
100
- // Resolve to null when imported server side. This makes the module
101
- // safe to import in an isomorphic code base.
102
- resolve(null);
103
- return;
104
- }
105
-
106
- if (window.Stripe) {
107
- resolve(window.Stripe);
108
- return;
109
- }
110
-
111
- try {
112
- var script = findScript();
113
-
114
- if (script && params) ; else if (!script) {
115
- script = injectScript(params);
116
- } else if (script && onLoadListener !== null && onErrorListener !== null) {
117
- var _script$parentNode;
118
-
119
- // remove event listeners
120
- script.removeEventListener('load', onLoadListener);
121
- script.removeEventListener('error', onErrorListener); // if script exists, but we are reloading due to an error,
122
- // reload script to trigger 'load' event
123
-
124
- (_script$parentNode = script.parentNode) === null || _script$parentNode === void 0 ? void 0 : _script$parentNode.removeChild(script);
125
- script = injectScript(params);
126
- }
127
-
128
- onLoadListener = onLoad(resolve, reject);
129
- onErrorListener = onError(reject);
130
- script.addEventListener('load', onLoadListener);
131
- script.addEventListener('error', onErrorListener);
132
- } catch (error) {
133
- reject(error);
134
- return;
135
- }
136
- }); // Resets stripePromise on error
137
-
138
- return stripePromise["catch"](function (error) {
139
- stripePromise = null;
140
- return Promise.reject(error);
141
- });
142
- };
143
- var initStripe = function initStripe(maybeStripe, args, startTime) {
144
- if (maybeStripe === null) {
145
- return null;
146
- }
147
-
148
- var stripe = maybeStripe.apply(undefined, args);
149
- registerWrapper(stripe, startTime);
150
- return stripe;
151
- }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
152
-
153
- var stripePromise$1;
154
- var loadCalled = false;
155
-
156
- var getStripePromise = function getStripePromise() {
157
- if (stripePromise$1) {
158
- return stripePromise$1;
159
- }
160
-
161
- stripePromise$1 = loadScript(null)["catch"](function (error) {
162
- // clear cache on error
163
- stripePromise$1 = null;
164
- return Promise.reject(error);
165
- });
166
- return stripePromise$1;
167
- }; // Execute our own script injection after a tick to give users time to do their
168
- // own script injection.
169
-
170
-
171
- Promise.resolve().then(function () {
172
- return getStripePromise();
173
- })["catch"](function (error) {
174
- if (!loadCalled) {
175
- console.warn(error);
176
- }
177
- });
178
- var loadStripe = function loadStripe() {
179
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
180
- args[_key] = arguments[_key];
181
- }
182
-
183
- loadCalled = true;
184
- var startTime = Date.now(); // if previous attempts are unsuccessful, will re-load script
185
-
186
- return getStripePromise().then(function (maybeStripe) {
187
- return initStripe(maybeStripe, args, startTime);
188
- });
189
- };
190
-
191
27
  const de$1 = {
192
28
  // Common
193
29
  "common.back": "← Zurück",
@@ -267,6 +103,24 @@ const de$1 = {
267
103
  "instances.bestPrice": "bester Preis !!!",
268
104
  "instances.goodPrice": "günstiger Preis",
269
105
  "instances.oclock": "Uhr",
106
+ // Warteliste
107
+ "waitlist.joinCta": "Auf die Warteliste",
108
+ "waitlist.title": "Auf die Warteliste setzen",
109
+ "waitlist.subtitle": "{{name}} ist ausgebucht. Hinterlassen Sie Ihre Daten und wir melden uns, falls ein Platz frei wird.",
110
+ "waitlist.name": "Name",
111
+ "waitlist.email": "E-Mail",
112
+ "waitlist.phone": "Telefon (optional)",
113
+ "waitlist.requiredFields": "Bitte geben Sie Ihren Namen und Ihre E-Mail-Adresse ein.",
114
+ "waitlist.join": "Eintragen",
115
+ "waitlist.submitting": "Wird eingetragen...",
116
+ "waitlist.cancel": "Abbrechen",
117
+ "waitlist.submitError": "Etwas ist schiefgelaufen. Bitte versuchen Sie es erneut.",
118
+ "waitlist.successTitle": "Sie sind auf der Liste!",
119
+ "waitlist.successMessage": "Sie sind Nummer {{position}} auf der Warteliste. Wir melden uns, sobald ein Platz frei wird.",
120
+ "waitlist.done": "Fertig",
121
+ // Reservierungs-Countdown
122
+ "hold.reservedFor": "Platz reserviert für {{time}}",
123
+ "hold.expired": "Ihre Reservierung ist abgelaufen",
270
124
  // Next events preview
271
125
  "nextEvents.title": "Nächste verfügbare Termine",
272
126
  "nextEvents.subtitle": "Wähle einen Termin aus oder zeige alle verfügbaren Termine an",
@@ -557,6 +411,24 @@ const en = {
557
411
  "instances.bestPrice": "best price !!!",
558
412
  "instances.goodPrice": "good price",
559
413
  "instances.oclock": "",
414
+ // Waitlist
415
+ "waitlist.joinCta": "Join waitlist",
416
+ "waitlist.title": "Join the waitlist",
417
+ "waitlist.subtitle": "{{name}} is fully booked. Leave your details and we'll contact you if a spot opens up.",
418
+ "waitlist.name": "Name",
419
+ "waitlist.email": "Email",
420
+ "waitlist.phone": "Phone (optional)",
421
+ "waitlist.requiredFields": "Please enter your name and email.",
422
+ "waitlist.join": "Join waitlist",
423
+ "waitlist.submitting": "Joining...",
424
+ "waitlist.cancel": "Cancel",
425
+ "waitlist.submitError": "Something went wrong. Please try again.",
426
+ "waitlist.successTitle": "You're on the list!",
427
+ "waitlist.successMessage": "You are number {{position}} on the waitlist. We'll be in touch if a spot becomes available.",
428
+ "waitlist.done": "Done",
429
+ // Slot hold countdown
430
+ "hold.reservedFor": "Slot reserved for {{time}}",
431
+ "hold.expired": "Your reservation has expired",
560
432
  // Next events preview
561
433
  "nextEvents.title": "Next available dates",
562
434
  "nextEvents.subtitle": "Select a date or view all available dates",
@@ -2262,6 +2134,7 @@ const inferConfigFromUrl = (baseConfig) => {
2262
2134
  const categoryId = urlParams.get("categoryId") || urlParams.get("category");
2263
2135
  const eventTypeId = urlParams.get("eventTypeId") || urlParams.get("type");
2264
2136
  const eventTypeIds = urlParams.get("eventTypeIds") || urlParams.get("types");
2137
+ const partnerContractId = urlParams.get("partnerContractId") || urlParams.get("contractId");
2265
2138
  const voucherValue = urlParams.get("voucherValue");
2266
2139
  const voucherCategory = urlParams.get("voucherCategory");
2267
2140
  const voucherEventType = urlParams.get("voucherEventType");
@@ -2274,11 +2147,14 @@ const inferConfigFromUrl = (baseConfig) => {
2274
2147
  : ["0", "false", "no", "off"].includes(voucherCardIntegration.toLowerCase())
2275
2148
  ? false
2276
2149
  : undefined;
2277
- const omitSelectionKeys = ({ categoryId: _c, eventTypeId: _e, eventTypeIds: _es, eventInstanceId: _i, voucherValue: _vv, voucherCategory: _vc, voucherEventType: _ve, voucherImageId: _vi, ...rest }) => rest;
2150
+ const omitSelectionKeys = ({ categoryId: _c, eventTypeId: _e, eventTypeIds: _es, eventInstanceId: _i, voucherValue: _vv, voucherCategory: _vc, voucherEventType: _ve, voucherImageId: _vi, partnerContractId: _pc, ...rest }) => rest;
2278
2151
  if (eventInstanceId && !Number.isNaN(Number(eventInstanceId))) {
2279
2152
  return {
2280
2153
  ...omitSelectionKeys(baseConfig),
2281
2154
  eventInstanceId: Number(eventInstanceId),
2155
+ ...(partnerContractId
2156
+ ? { partnerContractId }
2157
+ : {}),
2282
2158
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2283
2159
  };
2284
2160
  }
@@ -2286,6 +2162,9 @@ const inferConfigFromUrl = (baseConfig) => {
2286
2162
  return {
2287
2163
  ...omitSelectionKeys(baseConfig),
2288
2164
  categoryId: Number(categoryId),
2165
+ ...(partnerContractId
2166
+ ? { partnerContractId }
2167
+ : {}),
2289
2168
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2290
2169
  };
2291
2170
  }
@@ -2293,6 +2172,9 @@ const inferConfigFromUrl = (baseConfig) => {
2293
2172
  return {
2294
2173
  ...omitSelectionKeys(baseConfig),
2295
2174
  eventTypeId: Number(eventTypeId),
2175
+ ...(partnerContractId
2176
+ ? { partnerContractId }
2177
+ : {}),
2296
2178
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2297
2179
  };
2298
2180
  }
@@ -2305,6 +2187,9 @@ const inferConfigFromUrl = (baseConfig) => {
2305
2187
  return {
2306
2188
  ...omitSelectionKeys(baseConfig),
2307
2189
  eventTypeIds: typeIds,
2190
+ ...(partnerContractId
2191
+ ? { partnerContractId }
2192
+ : {}),
2308
2193
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2309
2194
  };
2310
2195
  }
@@ -2321,6 +2206,9 @@ const inferConfigFromUrl = (baseConfig) => {
2321
2206
  if (parsedVoucherValues.length || parsedVoucherCategories.length || parsedVoucherEventTypes.length) {
2322
2207
  return {
2323
2208
  ...baseConfig,
2209
+ ...(partnerContractId
2210
+ ? { partnerContractId }
2211
+ : {}),
2324
2212
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2325
2213
  ...(parsedVoucherValues.length > 0
2326
2214
  ? { voucherValue: parsedVoucherValues.length === 1 ? parsedVoucherValues[0] : parsedVoucherValues }
@@ -2338,6 +2226,9 @@ const inferConfigFromUrl = (baseConfig) => {
2338
2226
  }
2339
2227
  return {
2340
2228
  ...baseConfig,
2229
+ ...(partnerContractId
2230
+ ? { partnerContractId }
2231
+ : {}),
2341
2232
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2342
2233
  };
2343
2234
  };
@@ -6350,6 +6241,7 @@ function GiftCardOnlyBooking({ config, eventDetails, formData, discountCode, gif
6350
6241
  customerPhone: formData.customerPhone?.trim(),
6351
6242
  comment: formData.comment?.trim(),
6352
6243
  paymentMethod: "gift_card",
6244
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6353
6245
  ...(upsellSelections.length > 0 && { upsellSelections }),
6354
6246
  };
6355
6247
  const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/create-gift-card-booking"), {
@@ -6626,6 +6518,7 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
6626
6518
  customerEmail: formData.customerEmail?.trim(),
6627
6519
  customerPhone: formData.customerPhone?.trim(),
6628
6520
  comment: formData.comment?.trim(),
6521
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6629
6522
  ...(cardToken && { cardToken }),
6630
6523
  ...(selectedMethod && !cardToken && { method: selectedMethod }),
6631
6524
  ...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
@@ -6852,6 +6745,170 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
6852
6745
  }, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner("var(--bw-surface-color)"), " ", buttonLabel] })) : (buttonLabel) }))] }));
6853
6746
  }
6854
6747
 
6748
+ var V3_URL = 'https://js.stripe.com/v3';
6749
+ var V3_URL_REGEX = /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;
6750
+ var EXISTING_SCRIPT_MESSAGE = 'loadStripe.setLoadParameters was called but an existing Stripe.js script already exists in the document; existing script parameters will be used';
6751
+ var findScript = function findScript() {
6752
+ var scripts = document.querySelectorAll("script[src^=\"".concat(V3_URL, "\"]"));
6753
+
6754
+ for (var i = 0; i < scripts.length; i++) {
6755
+ var script = scripts[i];
6756
+
6757
+ if (!V3_URL_REGEX.test(script.src)) {
6758
+ continue;
6759
+ }
6760
+
6761
+ return script;
6762
+ }
6763
+
6764
+ return null;
6765
+ };
6766
+
6767
+ var injectScript = function injectScript(params) {
6768
+ var queryString = '';
6769
+ var script = document.createElement('script');
6770
+ script.src = "".concat(V3_URL).concat(queryString);
6771
+ var headOrBody = document.head || document.body;
6772
+
6773
+ if (!headOrBody) {
6774
+ throw new Error('Expected document.body not to be null. Stripe.js requires a <body> element.');
6775
+ }
6776
+
6777
+ headOrBody.appendChild(script);
6778
+ return script;
6779
+ };
6780
+
6781
+ var registerWrapper = function registerWrapper(stripe, startTime) {
6782
+ if (!stripe || !stripe._registerWrapper) {
6783
+ return;
6784
+ }
6785
+
6786
+ stripe._registerWrapper({
6787
+ name: 'stripe-js',
6788
+ version: "4.6.0",
6789
+ startTime: startTime
6790
+ });
6791
+ };
6792
+
6793
+ var stripePromise = null;
6794
+ var onErrorListener = null;
6795
+ var onLoadListener = null;
6796
+
6797
+ var onError = function onError(reject) {
6798
+ return function () {
6799
+ reject(new Error('Failed to load Stripe.js'));
6800
+ };
6801
+ };
6802
+
6803
+ var onLoad = function onLoad(resolve, reject) {
6804
+ return function () {
6805
+ if (window.Stripe) {
6806
+ resolve(window.Stripe);
6807
+ } else {
6808
+ reject(new Error('Stripe.js not available'));
6809
+ }
6810
+ };
6811
+ };
6812
+
6813
+ var loadScript = function loadScript(params) {
6814
+ // Ensure that we only attempt to load Stripe.js at most once
6815
+ if (stripePromise !== null) {
6816
+ return stripePromise;
6817
+ }
6818
+
6819
+ stripePromise = new Promise(function (resolve, reject) {
6820
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
6821
+ // Resolve to null when imported server side. This makes the module
6822
+ // safe to import in an isomorphic code base.
6823
+ resolve(null);
6824
+ return;
6825
+ }
6826
+
6827
+ if (window.Stripe) {
6828
+ resolve(window.Stripe);
6829
+ return;
6830
+ }
6831
+
6832
+ try {
6833
+ var script = findScript();
6834
+
6835
+ if (script && params) ; else if (!script) {
6836
+ script = injectScript(params);
6837
+ } else if (script && onLoadListener !== null && onErrorListener !== null) {
6838
+ var _script$parentNode;
6839
+
6840
+ // remove event listeners
6841
+ script.removeEventListener('load', onLoadListener);
6842
+ script.removeEventListener('error', onErrorListener); // if script exists, but we are reloading due to an error,
6843
+ // reload script to trigger 'load' event
6844
+
6845
+ (_script$parentNode = script.parentNode) === null || _script$parentNode === void 0 ? void 0 : _script$parentNode.removeChild(script);
6846
+ script = injectScript(params);
6847
+ }
6848
+
6849
+ onLoadListener = onLoad(resolve, reject);
6850
+ onErrorListener = onError(reject);
6851
+ script.addEventListener('load', onLoadListener);
6852
+ script.addEventListener('error', onErrorListener);
6853
+ } catch (error) {
6854
+ reject(error);
6855
+ return;
6856
+ }
6857
+ }); // Resets stripePromise on error
6858
+
6859
+ return stripePromise["catch"](function (error) {
6860
+ stripePromise = null;
6861
+ return Promise.reject(error);
6862
+ });
6863
+ };
6864
+ var initStripe = function initStripe(maybeStripe, args, startTime) {
6865
+ if (maybeStripe === null) {
6866
+ return null;
6867
+ }
6868
+
6869
+ var stripe = maybeStripe.apply(undefined, args);
6870
+ registerWrapper(stripe, startTime);
6871
+ return stripe;
6872
+ }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
6873
+
6874
+ var stripePromise$1;
6875
+ var loadCalled = false;
6876
+
6877
+ var getStripePromise = function getStripePromise() {
6878
+ if (stripePromise$1) {
6879
+ return stripePromise$1;
6880
+ }
6881
+
6882
+ stripePromise$1 = loadScript(null)["catch"](function (error) {
6883
+ // clear cache on error
6884
+ stripePromise$1 = null;
6885
+ return Promise.reject(error);
6886
+ });
6887
+ return stripePromise$1;
6888
+ }; // Execute our own script injection after a tick to give users time to do their
6889
+ // own script injection.
6890
+
6891
+
6892
+ Promise.resolve().then(function () {
6893
+ return getStripePromise();
6894
+ })["catch"](function (error) {
6895
+ if (!loadCalled) {
6896
+ console.warn(error);
6897
+ }
6898
+ });
6899
+ var loadStripe = function loadStripe() {
6900
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
6901
+ args[_key] = arguments[_key];
6902
+ }
6903
+
6904
+ loadCalled = true;
6905
+ var startTime = Date.now(); // if previous attempts are unsuccessful, will re-load script
6906
+
6907
+ return getStripePromise().then(function (maybeStripe) {
6908
+ return initStripe(maybeStripe, args, startTime);
6909
+ });
6910
+ };
6911
+
6855
6912
  // Inner component that uses the Stripe hooks
6856
6913
  function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onError, }) {
6857
6914
  const t = useTranslations();
@@ -6880,13 +6937,25 @@ function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onEr
6880
6937
  },
6881
6938
  }, submitContent: jsxRuntime.jsx(jsxRuntime.Fragment, { children: submitLabel }), loadingContent: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner("var(--bw-surface-color)"), " ", t("button.processingPayment")] }) }));
6882
6939
  }
6883
- function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripePromise, stripeAppearance, upsellSelections = [], }) {
6940
+ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripeAppearance, upsellSelections = [], }) {
6884
6941
  const t = useTranslations();
6885
6942
  const { locale } = useLocale();
6886
6943
  const [clientSecret, setClientSecret] = React.useState(null);
6887
6944
  const [paymentIntentId, setPaymentIntentId] = React.useState(null);
6888
6945
  const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = React.useState(false);
6889
6946
  const [paymentError, setPaymentError] = React.useState(null);
6947
+ const stripePromise = React.useMemo(() => {
6948
+ if (!systemConfig?.stripePublishableKey) {
6949
+ return null;
6950
+ }
6951
+ const stripeOptions = {
6952
+ locale: locale,
6953
+ };
6954
+ if (systemConfig.connectedAccountId) {
6955
+ stripeOptions.stripeAccount = systemConfig.connectedAccountId;
6956
+ }
6957
+ return loadStripe(systemConfig.stripePublishableKey, stripeOptions);
6958
+ }, [systemConfig?.stripePublishableKey, systemConfig?.connectedAccountId, locale]);
6890
6959
  const storageKey = typeof window !== "undefined"
6891
6960
  ? `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
6892
6961
  : "";
@@ -6965,6 +7034,7 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
6965
7034
  customerEmail: formData.customerEmail?.trim(),
6966
7035
  customerPhone: formData.customerPhone?.trim(),
6967
7036
  comment: formData.comment?.trim(),
7037
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6968
7038
  ...(paymentIntentId && { paymentIntentId }),
6969
7039
  ...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
6970
7040
  };
@@ -7034,7 +7104,7 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
7034
7104
  if (isFullyCoveredByGiftCards && totalAmount <= 0) {
7035
7105
  return (jsxRuntime.jsx(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: onSuccess, onError: onError, upsellSelections: upsellSelections }));
7036
7106
  }
7037
- if (isCreatingPaymentIntent || !clientSecret) {
7107
+ if (isCreatingPaymentIntent || !clientSecret || !stripePromise) {
7038
7108
  return (jsxRuntime.jsxs("div", { style: {
7039
7109
  display: "flex",
7040
7110
  alignItems: "center",
@@ -7072,6 +7142,49 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
7072
7142
  }, onError: onError }) }));
7073
7143
  }
7074
7144
 
7145
+ const HOLD_DURATION_SECONDS = 15 * 60;
7146
+ /**
7147
+ * Shows the remaining time on the 15-minute slot hold that the server places on
7148
+ * the pending order while the customer completes checkout. The timer is started
7149
+ * client-side when the payment step opens — matching the moment the pending
7150
+ * order (and its `holdExpiresAt`) is created server-side.
7151
+ */
7152
+ function HoldCountdown({ onExpire }) {
7153
+ const t = useTranslations();
7154
+ const [remaining, setRemaining] = React.useState(HOLD_DURATION_SECONDS);
7155
+ React.useEffect(() => {
7156
+ const startedAt = Date.now();
7157
+ const interval = setInterval(() => {
7158
+ const elapsed = Math.floor((Date.now() - startedAt) / 1000);
7159
+ const left = Math.max(0, HOLD_DURATION_SECONDS - elapsed);
7160
+ setRemaining(left);
7161
+ if (left <= 0) {
7162
+ clearInterval(interval);
7163
+ onExpire?.();
7164
+ }
7165
+ }, 1000);
7166
+ return () => clearInterval(interval);
7167
+ }, [onExpire]);
7168
+ const minutes = Math.floor(remaining / 60);
7169
+ const seconds = remaining % 60;
7170
+ const formatted = `${minutes}:${String(seconds).padStart(2, "0")}`;
7171
+ const isLow = remaining <= 60;
7172
+ return (jsxRuntime.jsxs("div", { style: {
7173
+ display: "flex",
7174
+ alignItems: "center",
7175
+ gap: "8px",
7176
+ padding: "10px 14px",
7177
+ borderRadius: "var(--bw-border-radius)",
7178
+ border: `1px solid ${isLow ? "var(--bw-error-color)" : "var(--bw-border-color)"}`,
7179
+ backgroundColor: "var(--bw-surface-color)",
7180
+ color: isLow ? "var(--bw-error-color)" : "var(--bw-text-color)",
7181
+ fontFamily: "var(--bw-font-family)",
7182
+ fontSize: "14px",
7183
+ }, children: [jsxRuntime.jsx(IconClock, {}), jsxRuntime.jsx("span", { children: remaining > 0
7184
+ ? t("hold.reservedFor", { time: formatted })
7185
+ : t("hold.expired") })] }));
7186
+ }
7187
+
7075
7188
  const FLAG_EMOJIS = {
7076
7189
  de: "🇩🇪",
7077
7190
  en: "🇬🇧",
@@ -11483,6 +11596,11 @@ const buttonStyles = {
11483
11596
  color: "var(--bw-text-muted)",
11484
11597
  border: "1px solid var(--bw-border-color)",
11485
11598
  },
11599
+ // Size variants
11600
+ small: {
11601
+ padding: "8px 12px",
11602
+ fontSize: "13px",
11603
+ },
11486
11604
  // Full width modifier
11487
11605
  fullWidth: {
11488
11606
  width: "100%",
@@ -11555,6 +11673,12 @@ const sectionStyles = {
11555
11673
  // TEXT
11556
11674
  // ============================================
11557
11675
  const textStyles = {
11676
+ body: {
11677
+ fontSize: "14px",
11678
+ color: "var(--bw-text-color)",
11679
+ fontFamily: "var(--bw-font-family)",
11680
+ lineHeight: 1.5,
11681
+ },
11558
11682
  muted: {
11559
11683
  fontSize: "14px",
11560
11684
  color: "var(--bw-text-muted)",
@@ -11624,7 +11748,7 @@ const sectionHeaderStyles$1 = sectionStyles.header;
11624
11748
  const labelStyles$1 = formStyles.label;
11625
11749
  const inputStyles$1 = formStyles.input;
11626
11750
  const errorTextStyles$1 = formStyles.error;
11627
- function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError, isOpen, onClose, systemConfig, selectedUpsells = [], upsells = [], persistedState = null, onPersistedStateChange, }) {
11751
+ function BookingForm({ config, eventDetails, onSuccess, onError, isOpen, onClose, systemConfig, selectedUpsells = [], upsells = [], persistedState = null, onPersistedStateChange, }) {
11628
11752
  const t$1 = useTranslations();
11629
11753
  const { locale } = useLocale();
11630
11754
  const timezone = useTimezone();
@@ -11824,7 +11948,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
11824
11948
  newTotal: appliedDiscountCode.newTotal,
11825
11949
  }
11826
11950
  : null;
11827
- const hasPaymentProvider = Boolean(stripePromise || systemConfig?.paymentProvider === "mollie");
11951
+ const hasPaymentProvider = systemConfig?.paymentProvider === "stripe" || systemConfig?.paymentProvider === "mollie";
11828
11952
  const handleVoucherValidated = React.useCallback((voucher, _error) => {
11829
11953
  if (voucher) {
11830
11954
  setAppliedVouchers((prev) => [...prev, voucher]);
@@ -12090,7 +12214,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
12090
12214
  ...inputStyles$1,
12091
12215
  resize: "vertical",
12092
12216
  minHeight: "80px",
12093
- } })] })] })] })), checkoutStep === "payment" && (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "24px" }, children: [jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: "16px" }, children: t$1("summary.title") }), jsxRuntime.jsxs("div", { style: { marginTop: "10px", display: "flex", flexDirection: "column", gap: "12px" }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t$1("booking.price") }), jsxRuntime.jsxs("div", { style: {
12217
+ } })] })] })] })), checkoutStep === "payment" && (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "24px" }, children: [jsxRuntime.jsx(HoldCountdown, {}), jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: "16px" }, children: t$1("summary.title") }), jsxRuntime.jsxs("div", { style: { marginTop: "10px", display: "flex", flexDirection: "column", gap: "12px" }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t$1("booking.price") }), jsxRuntime.jsxs("div", { style: {
12094
12218
  color: "var(--bw-text-color)",
12095
12219
  fontWeight: 500,
12096
12220
  fontFamily: "var(--bw-font-family)",
@@ -12194,7 +12318,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
12194
12318
  fontFamily: "var(--bw-font-family)",
12195
12319
  marginTop: "8px",
12196
12320
  textAlign: "right",
12197
- }, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), depositsOnlyBlocked && (jsxRuntime.jsx("div", { style: cardStyles$1, children: jsxRuntime.jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.depositNotConfigured") }) })), !hasPaymentProvider && !depositsOnlyBlocked && (jsxRuntime.jsx("div", { style: cardStyles$1, children: jsxRuntime.jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.paymentUnavailable") }) })), hasPaymentProvider && !depositsOnlyBlocked && (systemConfig?.paymentProvider === "mollie" ? (jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsxRuntime.jsx(MolliePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, upsellSelections: aggregatedUpsellSelections(), mollieProfileId: systemConfig?.mollieProfileId, mollieTestmode: systemConfig?.mollieTestmode })] })) : (jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsxRuntime.jsx(StripePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig ?? null, stripePromise: stripePromise, stripeAppearance: stripeAppearance, upsellSelections: aggregatedUpsellSelections() })] })))] }))] }) }));
12321
+ }, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), depositsOnlyBlocked && (jsxRuntime.jsx("div", { style: cardStyles$1, children: jsxRuntime.jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.depositNotConfigured") }) })), !hasPaymentProvider && !depositsOnlyBlocked && (jsxRuntime.jsx("div", { style: cardStyles$1, children: jsxRuntime.jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.paymentUnavailable") }) })), hasPaymentProvider && !depositsOnlyBlocked && (systemConfig?.paymentProvider === "mollie" ? (jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsxRuntime.jsx(MolliePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, upsellSelections: aggregatedUpsellSelections(), mollieProfileId: systemConfig?.mollieProfileId, mollieTestmode: systemConfig?.mollieTestmode })] })) : (jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsxRuntime.jsx(StripePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig ?? null, stripeAppearance: stripeAppearance, upsellSelections: aggregatedUpsellSelections() })] })))] }))] }) }));
12198
12322
  }
12199
12323
 
12200
12324
  /**
@@ -13303,6 +13427,8 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
13303
13427
  const [stripeAppearance, setStripeAppearance] = React.useState(null);
13304
13428
  // Initialize Stripe
13305
13429
  React.useEffect(() => {
13430
+ if (!isOpen)
13431
+ return;
13306
13432
  if (paymentProvider !== "stripe")
13307
13433
  return;
13308
13434
  if (systemConfig?.stripePublishableKey && !stripePromise) {
@@ -13314,7 +13440,7 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
13314
13440
  }
13315
13441
  setStripePromise(loadStripe(systemConfig.stripePublishableKey, stripeOptions));
13316
13442
  }
13317
- }, [paymentProvider, systemConfig, locale, stripePromise]);
13443
+ }, [isOpen, paymentProvider, systemConfig, locale, stripePromise]);
13318
13444
  // Initialize Stripe appearance
13319
13445
  React.useEffect(() => {
13320
13446
  const container = document.querySelector(".booking-widget-container");
@@ -14278,8 +14404,17 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14278
14404
  if (!item)
14279
14405
  return jsxRuntime.jsx("div", { style: { height: "34px" } }, i);
14280
14406
  const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
14281
- return (jsxRuntime.jsxs("div", { onClick: (e) => { e.stopPropagation(); onInstancePreview?.(item.id, eventType.id); }, onMouseEnter: (e) => { if (onInstancePreview)
14282
- e.currentTarget.style.backgroundColor = "var(--bw-border-color)"; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
14407
+ const isRowBookable = item.bookingOpen && item.availableSpots > 0;
14408
+ return (jsxRuntime.jsxs("div", { onClick: (e) => {
14409
+ e.stopPropagation();
14410
+ if (isRowBookable) {
14411
+ onInstancePreview?.(item.id, eventType.id);
14412
+ }
14413
+ }, onMouseEnter: (e) => {
14414
+ if (onInstancePreview && isRowBookable) {
14415
+ e.currentTarget.style.backgroundColor = "var(--bw-highlight-subtle)";
14416
+ }
14417
+ }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
14283
14418
  height: "34px",
14284
14419
  display: "grid",
14285
14420
  gridTemplateColumns: "auto 1fr auto 20px",
@@ -14288,12 +14423,13 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14288
14423
  width: "100%",
14289
14424
  fontSize: "13px",
14290
14425
  color: "var(--bw-text-muted)",
14291
- cursor: onInstancePreview ? "pointer" : "default",
14426
+ cursor: onInstancePreview && isRowBookable ? "pointer" : "default",
14292
14427
  borderRadius: "4px",
14293
14428
  padding: "0 2px",
14294
14429
  transition: "background 0.15s",
14295
14430
  boxSizing: "border-box",
14296
- }, 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));
14431
+ opacity: isRowBookable ? 1 : 0.55,
14432
+ }, 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.isSpecial && item.specialDescription ? (jsxRuntime.jsx(InfoBadge, { text: item.specialDescription })) : (jsxRuntime.jsx("span", {}))] }, item.id));
14297
14433
  }) }), hasMoreDates && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
14298
14434
  e.stopPropagation();
14299
14435
  onEventTypeSelect(eventType);
@@ -14382,6 +14518,74 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14382
14518
  }) }) })), jsxRuntime.jsx(PaymentLogosStrip, {}), jsxRuntime.jsx(EventTypeDetailsDialog, { isOpen: detailsDialogOpen, onClose: handleCloseDetails, eventType: selectedEventTypeForDetails, onEventTypeSelect: onEventTypeSelect })] }));
14383
14519
  }
14384
14520
 
14521
+ function WaitlistDialog({ apiBaseUrl, organizationId, eventInstanceId, eventName, onClose, }) {
14522
+ const t = useTranslations();
14523
+ const [name, setName] = React.useState("");
14524
+ const [email, setEmail] = React.useState("");
14525
+ const [phone, setPhone] = React.useState("");
14526
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
14527
+ const [error, setError] = React.useState(null);
14528
+ const [position, setPosition] = React.useState(null);
14529
+ const handleSubmit = async () => {
14530
+ setError(null);
14531
+ if (!name.trim() || !email.trim()) {
14532
+ setError(t("waitlist.requiredFields"));
14533
+ return;
14534
+ }
14535
+ setIsSubmitting(true);
14536
+ try {
14537
+ const response = await fetch(getApiUrl(apiBaseUrl, "/booking/waitlist"), {
14538
+ method: "POST",
14539
+ headers: { "Content-Type": "application/json" },
14540
+ body: JSON.stringify({
14541
+ organizationId,
14542
+ eventInstanceId,
14543
+ name: name.trim(),
14544
+ email: email.trim(),
14545
+ phone: phone.trim() || undefined,
14546
+ }),
14547
+ });
14548
+ if (!response.ok) {
14549
+ throw new Error("request_failed");
14550
+ }
14551
+ const data = (await response.json());
14552
+ setPosition(data.position ?? null);
14553
+ }
14554
+ catch {
14555
+ setError(t("waitlist.submitError"));
14556
+ }
14557
+ finally {
14558
+ setIsSubmitting(false);
14559
+ }
14560
+ };
14561
+ return (jsxRuntime.jsx("div", { style: {
14562
+ position: "fixed",
14563
+ inset: 0,
14564
+ zIndex: 1000,
14565
+ display: "flex",
14566
+ alignItems: "center",
14567
+ justifyContent: "center",
14568
+ backgroundColor: "rgba(15, 23, 42, 0.6)",
14569
+ padding: "16px",
14570
+ fontFamily: "var(--bw-font-family)",
14571
+ }, onClick: onClose, children: jsxRuntime.jsx("div", { style: {
14572
+ width: "100%",
14573
+ maxWidth: "420px",
14574
+ backgroundColor: "var(--bw-surface-color)",
14575
+ border: "1px solid var(--bw-border-color)",
14576
+ borderRadius: "var(--bw-border-radius)",
14577
+ padding: "24px",
14578
+ }, onClick: (e) => e.stopPropagation(), children: position !== null ? (jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [jsxRuntime.jsx("h3", { style: {
14579
+ margin: "0 0 8px",
14580
+ color: "var(--bw-highlight-color)",
14581
+ fontSize: "18px",
14582
+ }, children: t("waitlist.successTitle") }), jsxRuntime.jsx("p", { style: mergeStyles(textStyles.body, { marginBottom: "20px" }), children: t("waitlist.successMessage", { position }) }), jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), onClick: onClose, children: t("waitlist.done") })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("h3", { style: {
14583
+ margin: "0 0 4px",
14584
+ color: "var(--bw-highlight-color)",
14585
+ fontSize: "18px",
14586
+ }, children: t("waitlist.title") }), jsxRuntime.jsx("p", { style: mergeStyles(textStyles.muted, { marginBottom: "16px" }), children: t("waitlist.subtitle", { name: eventName }) }), jsxRuntime.jsxs("div", { style: { marginBottom: "12px" }, children: [jsxRuntime.jsx("label", { style: formStyles.label, htmlFor: "wl-name", children: t("waitlist.name") }), jsxRuntime.jsx("input", { id: "wl-name", style: formStyles.input, value: name, onChange: (e) => setName(e.target.value) })] }), jsxRuntime.jsxs("div", { style: { marginBottom: "12px" }, children: [jsxRuntime.jsx("label", { style: formStyles.label, htmlFor: "wl-email", children: t("waitlist.email") }), jsxRuntime.jsx("input", { id: "wl-email", type: "email", style: formStyles.input, value: email, onChange: (e) => setEmail(e.target.value) })] }), jsxRuntime.jsxs("div", { style: { marginBottom: "12px" }, children: [jsxRuntime.jsx("label", { style: formStyles.label, htmlFor: "wl-phone", children: t("waitlist.phone") }), jsxRuntime.jsx("input", { id: "wl-phone", type: "tel", style: formStyles.input, value: phone, onChange: (e) => setPhone(e.target.value) })] }), error && jsxRuntime.jsx("p", { style: formStyles.error, children: error }), jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", marginTop: "16px" }, children: [jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), onClick: onClose, disabled: isSubmitting, children: t("waitlist.cancel") }), jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), onClick: handleSubmit, disabled: isSubmitting, children: isSubmitting ? t("waitlist.submitting") : t("waitlist.join") })] })] })) }) }));
14587
+ }
14588
+
14385
14589
  const getAllocationBadgeInfo = (availableSpots, maxParticipants, t) => {
14386
14590
  const availabilityRatio = availableSpots / maxParticipants;
14387
14591
  if (availabilityRatio >= 0.6)
@@ -14506,12 +14710,13 @@ const PriceDisplay = ({ price, yearPrices, t }) => {
14506
14710
  boxShadow: displayInfo ? "0 2px 4px rgba(0, 0, 0, 0.2)" : "none",
14507
14711
  }, children: formatCurrency(price) }));
14508
14712
  };
14509
- function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, }) {
14713
+ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, apiBaseUrl, organizationId, }) {
14510
14714
  const t = useTranslations();
14511
14715
  const { locale } = useLocale();
14512
14716
  const timezone = useTimezone();
14513
14717
  const [selectedEventInstanceId, setSelectedEventInstanceId] = React.useState(null);
14514
14718
  const [openGroups, setOpenGroups] = React.useState(new Set());
14719
+ const [waitlistEvent, setWaitlistEvent] = React.useState(null);
14515
14720
  const getMonthPriceDisplayInfo = (minPrice) => {
14516
14721
  return getPriceDisplayInfo(minPrice, yearPrices, t);
14517
14722
  };
@@ -14672,116 +14877,127 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
14672
14877
  fontFamily: "var(--bw-font-family)",
14673
14878
  }, children: t("instances.noAvailable") }), jsxRuntime.jsx("p", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t("instances.noAvailableMessage") })] }) }) }));
14674
14879
  }
14675
- return (jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name}`, footer: footerNav, children: jsxRuntime.jsxs("div", { style: { padding: "20px 10px" }, children: [jsxRuntime.jsx(FlowProgress, { currentStep: "date", hasUpsellsStep: hasUpsellsStep }), jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: monthYearGroups.map(({ key, label, events, minPrice, year }, idx) => {
14676
- const monthPriceDisplayInfo = getMonthPriceDisplayInfo(minPrice);
14677
- return (jsxRuntime.jsxs(React.Fragment, { children: [idx > 0 && monthYearGroups[idx - 1].year !== year && (jsxRuntime.jsx("div", { style: { height: "1px", backgroundColor: "var(--bw-border-color)", margin: "4px 0" } })), jsxRuntime.jsx(Accordion, { title: label, priceInfo: jsxRuntime.jsx("div", { style: {
14678
- fontSize: "16px",
14679
- fontWeight: 500,
14680
- marginLeft: "auto",
14681
- padding: "4px 8px",
14682
- borderRadius: "var(--bw-border-radius-small)",
14683
- backgroundColor: monthPriceDisplayInfo
14684
- ? monthPriceDisplayInfo.backgroundColor
14685
- : "#14532d",
14686
- color: monthPriceDisplayInfo
14687
- ? monthPriceDisplayInfo.textColor
14688
- : "#4ade80",
14689
- boxShadow: monthPriceDisplayInfo
14690
- ? "0 2px 4px rgba(0, 0, 0, 0.2)"
14691
- : undefined,
14692
- }, children: `${t("common.from")} ${formatCurrency(minPrice)}` }), isOpen: openGroups.has(key), onToggle: () => toggleGroup(key), children: jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", paddingTop: "12px" }, children: events.map((event) => {
14693
- const availableSpots = event.maxParticipants - event.participantCount;
14694
- const isFullyBooked = availableSpots === 0;
14695
- const startDate = new Date(event.startTime);
14696
- const isPastEvent = today.toISOString() >= startDate.toISOString();
14697
- const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
14698
- return (jsxRuntime.jsxs("div", { style: {
14699
- position: "relative",
14700
- border: "1px solid var(--bw-border-color)",
14701
- backgroundColor: "var(--bw-surface-color)",
14702
- borderRadius: "var(--bw-border-radius)",
14703
- padding: "16px 10px",
14704
- transition: "all 0.2s ease",
14705
- fontFamily: "var(--bw-font-family)",
14706
- opacity: isDisabled ? 0.3 : 1,
14707
- filter: isDisabled ? "grayscale(40%)" : "none",
14708
- cursor: isDisabled ? "not-allowed" : "pointer",
14709
- }, onClick: () => {
14710
- if (!isDisabled) {
14711
- handleEventInstanceSelect(event);
14712
- }
14713
- }, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (jsxRuntime.jsx("div", { style: {
14714
- position: "absolute",
14715
- inset: 0,
14716
- backgroundColor: "rgba(15, 23, 42, 0.8)",
14717
- borderRadius: "var(--bw-border-radius)",
14718
- display: "flex",
14719
- alignItems: "center",
14720
- justifyContent: "center",
14721
- }, children: jsxRuntime.jsx("div", { style: {
14722
- width: "32px",
14723
- height: "32px",
14724
- color: "var(--bw-highlight-color)",
14725
- opacity: 0.8,
14726
- fontSize: "32px",
14727
- }, children: spinner() }) })), jsxRuntime.jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices, t: t }), jsxRuntime.jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants, t: t }), jsxRuntime.jsxs("div", { style: {
14728
- display: "flex",
14729
- justifyContent: "space-between",
14730
- width: "100%",
14731
- alignItems: "flex-start",
14732
- gap: "12px",
14733
- marginBottom: "4px",
14734
- }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "12px" }, children: [jsxRuntime.jsx("div", { style: {
14735
- fontSize: "16px",
14736
- transition: "all 0.2s ease",
14737
- borderRadius: "var(--bw-border-radius-small)",
14738
- borderTop: "4px solid var(--bw-border-color)",
14739
- border: "1px solid var(--bw-border-color)",
14740
- width: "40px",
14741
- height: "40px",
14742
- display: "flex",
14743
- alignItems: "center",
14744
- justifyContent: "center",
14745
- fontWeight: 700,
14746
- color: "var(--bw-text-color)",
14747
- backgroundColor: "var(--bw-background-color)",
14748
- }, children: startDate.getDate() }), jsxRuntime.jsxs("div", { style: {
14749
- fontSize: "16px",
14750
- color: "var(--bw-text-color)",
14751
- display: "flex",
14752
- flexDirection: "column",
14753
- alignItems: "flex-start",
14754
- justifyContent: "flex-start",
14755
- lineHeight: 1.25,
14756
- }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.startTime, timezone, locale) }), formatWeekday(event.startTime, timezone, locale) !==
14757
- formatWeekday(event.endTime, timezone, locale) && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.endTime, timezone, locale) })] }))] }), jsxRuntime.jsx("div", { children: formatWeekday(event.startTime, timezone, locale) ===
14758
- formatWeekday(event.endTime, timezone, locale) ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime, timezone, locale) }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime, timezone, locale) })] })) : (jsxRuntime.jsxs("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime, timezone, locale), " ", t("instances.oclock")] })) })] }), jsxRuntime.jsxs("span", { style: {
14759
- fontSize: "12px",
14760
- fontWeight: 400,
14761
- color: "var(--bw-text-muted)",
14762
- marginLeft: "6px",
14763
- backgroundColor: "rgba(0, 0, 0, 0.05)",
14764
- whiteSpace: "nowrap",
14765
- padding: "2px 6px",
14766
- borderRadius: "var(--bw-border-radius-small)",
14767
- }, children: [event.durationDays, " ", event.durationDays > 1 ? t("common.days") : t("common.day")] })] }), jsxRuntime.jsx("div", { style: {
14768
- textAlign: "right",
14769
- display: "flex",
14770
- flexDirection: "column",
14771
- alignItems: "flex-end",
14772
- }, children: jsxRuntime.jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices, t: t }) })] }), event.name !== selectedEventType?.name && (jsxRuntime.jsx("h4", { style: {
14773
- fontSize: "16px",
14774
- fontWeight: 600,
14775
- color: "var(--bw-text-color)",
14776
- lineHeight: 1.25,
14777
- margin: "0 0 2px 0",
14778
- display: "flex",
14779
- alignItems: "center",
14780
- gap: "8px",
14781
- maxWidth: "230px",
14782
- }, children: event.name }))] }, event.id));
14783
- }) }) })] }, key));
14784
- }) })] }) }));
14880
+ return (jsxRuntime.jsxs(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name}`, footer: footerNav, children: [jsxRuntime.jsxs("div", { style: { padding: "20px 10px" }, children: [jsxRuntime.jsx(FlowProgress, { currentStep: "date", hasUpsellsStep: hasUpsellsStep }), jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: monthYearGroups.map(({ key, label, events, minPrice, year }, idx) => {
14881
+ const monthPriceDisplayInfo = getMonthPriceDisplayInfo(minPrice);
14882
+ return (jsxRuntime.jsxs(React.Fragment, { children: [idx > 0 && monthYearGroups[idx - 1].year !== year && (jsxRuntime.jsx("div", { style: { height: "1px", backgroundColor: "var(--bw-border-color)", margin: "4px 0" } })), jsxRuntime.jsx(Accordion, { title: label, priceInfo: jsxRuntime.jsx("div", { style: {
14883
+ fontSize: "16px",
14884
+ fontWeight: 500,
14885
+ marginLeft: "auto",
14886
+ padding: "4px 8px",
14887
+ borderRadius: "var(--bw-border-radius-small)",
14888
+ backgroundColor: monthPriceDisplayInfo
14889
+ ? monthPriceDisplayInfo.backgroundColor
14890
+ : "#14532d",
14891
+ color: monthPriceDisplayInfo
14892
+ ? monthPriceDisplayInfo.textColor
14893
+ : "#4ade80",
14894
+ boxShadow: monthPriceDisplayInfo
14895
+ ? "0 2px 4px rgba(0, 0, 0, 0.2)"
14896
+ : undefined,
14897
+ }, children: `${t("common.from")} ${formatCurrency(minPrice)}` }), isOpen: openGroups.has(key), onToggle: () => toggleGroup(key), children: jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", paddingTop: "12px" }, children: events.map((event) => {
14898
+ const availableSpots = event.maxParticipants - event.participantCount;
14899
+ const isFullyBooked = availableSpots === 0;
14900
+ const startDate = new Date(event.startTime);
14901
+ const isPastEvent = today.toISOString() >= startDate.toISOString();
14902
+ const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
14903
+ const canWaitlist = isFullyBooked &&
14904
+ !isPastEvent &&
14905
+ !!apiBaseUrl &&
14906
+ !!organizationId;
14907
+ return (jsxRuntime.jsxs("div", { style: {
14908
+ position: "relative",
14909
+ border: "1px solid var(--bw-border-color)",
14910
+ backgroundColor: "var(--bw-surface-color)",
14911
+ borderRadius: "var(--bw-border-radius)",
14912
+ padding: "16px 10px",
14913
+ transition: "all 0.2s ease",
14914
+ fontFamily: "var(--bw-font-family)",
14915
+ opacity: isDisabled && !canWaitlist ? 0.3 : 1,
14916
+ filter: isDisabled && !canWaitlist ? "grayscale(40%)" : "none",
14917
+ cursor: isDisabled
14918
+ ? canWaitlist
14919
+ ? "default"
14920
+ : "not-allowed"
14921
+ : "pointer",
14922
+ }, onClick: () => {
14923
+ if (!isDisabled) {
14924
+ handleEventInstanceSelect(event);
14925
+ }
14926
+ }, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (jsxRuntime.jsx("div", { style: {
14927
+ position: "absolute",
14928
+ inset: 0,
14929
+ backgroundColor: "rgba(15, 23, 42, 0.8)",
14930
+ borderRadius: "var(--bw-border-radius)",
14931
+ display: "flex",
14932
+ alignItems: "center",
14933
+ justifyContent: "center",
14934
+ }, children: jsxRuntime.jsx("div", { style: {
14935
+ width: "32px",
14936
+ height: "32px",
14937
+ color: "var(--bw-highlight-color)",
14938
+ opacity: 0.8,
14939
+ fontSize: "32px",
14940
+ }, children: spinner() }) })), jsxRuntime.jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices, t: t }), jsxRuntime.jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants, t: t }), jsxRuntime.jsxs("div", { style: {
14941
+ display: "flex",
14942
+ justifyContent: "space-between",
14943
+ width: "100%",
14944
+ alignItems: "flex-start",
14945
+ gap: "12px",
14946
+ marginBottom: "4px",
14947
+ }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "12px" }, children: [jsxRuntime.jsx("div", { style: {
14948
+ fontSize: "16px",
14949
+ transition: "all 0.2s ease",
14950
+ borderRadius: "var(--bw-border-radius-small)",
14951
+ borderTop: "4px solid var(--bw-border-color)",
14952
+ border: "1px solid var(--bw-border-color)",
14953
+ width: "40px",
14954
+ height: "40px",
14955
+ display: "flex",
14956
+ alignItems: "center",
14957
+ justifyContent: "center",
14958
+ fontWeight: 700,
14959
+ color: "var(--bw-text-color)",
14960
+ backgroundColor: "var(--bw-background-color)",
14961
+ }, children: startDate.getDate() }), jsxRuntime.jsxs("div", { style: {
14962
+ fontSize: "16px",
14963
+ color: "var(--bw-text-color)",
14964
+ display: "flex",
14965
+ flexDirection: "column",
14966
+ alignItems: "flex-start",
14967
+ justifyContent: "flex-start",
14968
+ lineHeight: 1.25,
14969
+ }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.startTime, timezone, locale) }), formatWeekday(event.startTime, timezone, locale) !==
14970
+ formatWeekday(event.endTime, timezone, locale) && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.endTime, timezone, locale) })] }))] }), jsxRuntime.jsx("div", { children: formatWeekday(event.startTime, timezone, locale) ===
14971
+ formatWeekday(event.endTime, timezone, locale) ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime, timezone, locale) }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime, timezone, locale) })] })) : (jsxRuntime.jsxs("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime, timezone, locale), " ", t("instances.oclock")] })) })] }), jsxRuntime.jsxs("span", { style: {
14972
+ fontSize: "12px",
14973
+ fontWeight: 400,
14974
+ color: "var(--bw-text-muted)",
14975
+ marginLeft: "6px",
14976
+ backgroundColor: "rgba(0, 0, 0, 0.05)",
14977
+ whiteSpace: "nowrap",
14978
+ padding: "2px 6px",
14979
+ borderRadius: "var(--bw-border-radius-small)",
14980
+ }, children: [event.durationDays, " ", event.durationDays > 1 ? t("common.days") : t("common.day")] })] }), jsxRuntime.jsx("div", { style: {
14981
+ textAlign: "right",
14982
+ display: "flex",
14983
+ flexDirection: "column",
14984
+ alignItems: "flex-end",
14985
+ }, children: jsxRuntime.jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices, t: t }) })] }), event.name !== selectedEventType?.name && (jsxRuntime.jsx("h4", { style: {
14986
+ fontSize: "16px",
14987
+ fontWeight: 600,
14988
+ color: "var(--bw-text-color)",
14989
+ lineHeight: 1.25,
14990
+ margin: "0 0 2px 0",
14991
+ display: "flex",
14992
+ alignItems: "center",
14993
+ gap: "8px",
14994
+ maxWidth: "230px",
14995
+ }, children: event.name })), canWaitlist && (jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth, buttonStyles.small, { marginTop: "8px" }), onClick: (e) => {
14996
+ e.stopPropagation();
14997
+ setWaitlistEvent(event);
14998
+ }, children: t("waitlist.joinCta") }))] }, event.id));
14999
+ }) }) })] }, key));
15000
+ }) })] }), waitlistEvent && apiBaseUrl && organizationId && (jsxRuntime.jsx(WaitlistDialog, { apiBaseUrl: apiBaseUrl, organizationId: organizationId, eventInstanceId: waitlistEvent.id, eventName: waitlistEvent.name, onClose: () => setWaitlistEvent(null) }))] }));
14785
15001
  }
14786
15002
 
14787
15003
  function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText, showAllButton, isLoadingEventDetails = false, isLoadingShowAll = false, isLoading = false, count = 3, }) {
@@ -15675,6 +15891,7 @@ let buffer = [];
15675
15891
  let flushTimer = null;
15676
15892
  let currentApiBaseUrl = "";
15677
15893
  let currentOrganizationId = "";
15894
+ let currentPartnerContractId = null;
15678
15895
  const FLUSH_INTERVAL_MS = 3000;
15679
15896
  function flush() {
15680
15897
  if (buffer.length === 0)
@@ -15716,9 +15933,10 @@ function scheduleFlush() {
15716
15933
  /**
15717
15934
  * Initialise the analytics module. Must be called once before `trackEvent`.
15718
15935
  */
15719
- function initAnalytics(apiBaseUrl, organizationId) {
15936
+ function initAnalytics(apiBaseUrl, organizationId, options) {
15720
15937
  currentApiBaseUrl = apiBaseUrl;
15721
15938
  currentOrganizationId = organizationId;
15939
+ currentPartnerContractId = options?.partnerContractId?.trim() || null;
15722
15940
  if (typeof window !== "undefined") {
15723
15941
  window.addEventListener("pagehide", flush);
15724
15942
  window.addEventListener("visibilitychange", () => {
@@ -15735,9 +15953,12 @@ function trackEvent(event, properties = {}) {
15735
15953
  return;
15736
15954
  if (!currentOrganizationId)
15737
15955
  return;
15956
+ const mergedProperties = currentPartnerContractId
15957
+ ? { ...properties, partnerContractId: currentPartnerContractId }
15958
+ : properties;
15738
15959
  buffer.push({
15739
15960
  event,
15740
- properties,
15961
+ properties: mergedProperties,
15741
15962
  domain: window.location.hostname,
15742
15963
  url: window.location.href,
15743
15964
  referrer: document.referrer,
@@ -15813,7 +16034,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15813
16034
  const [sidebarOpen, setSidebarOpen] = React.useState(false);
15814
16035
  // Booking flow state
15815
16036
  const [eventDetails, setEventDetails] = React.useState(null);
15816
- const [stripePromise, setStripePromise] = React.useState(null);
15817
16037
  // SEPARATED LOADING STATES
15818
16038
  const [isLoading, setIsLoading] = React.useState(true);
15819
16039
  const [isLoadingEventInstances, setIsLoadingEventInstances] = React.useState(false);
@@ -15948,7 +16168,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15948
16168
  React.useEffect(() => {
15949
16169
  if (!analyticsInitRef.current && config.organizationId) {
15950
16170
  analyticsInitRef.current = true;
15951
- initAnalytics(config.apiBaseUrl, config.organizationId);
16171
+ initAnalytics(config.apiBaseUrl, config.organizationId, {
16172
+ partnerContractId: config.partnerContractId,
16173
+ });
15952
16174
  trackEvent("widget_loaded", {
15953
16175
  viewMode,
15954
16176
  eventTypeId: config.eventTypeId,
@@ -15957,7 +16179,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15957
16179
  isStandaloneVoucherMode,
15958
16180
  });
15959
16181
  }
15960
- }, [config.organizationId, config.apiBaseUrl]);
16182
+ }, [config.organizationId, config.apiBaseUrl, config.partnerContractId, viewMode, config.eventTypeId, config.categoryId, config.eventInstanceId, isStandaloneVoucherMode]);
15961
16183
  // Fire widget pageview once when Google Ads config is received from API
15962
16184
  const pageviewFiredRef = React.useRef(false);
15963
16185
  React.useEffect(() => {
@@ -15971,6 +16193,23 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15971
16193
  const initializeWidget = async () => {
15972
16194
  try {
15973
16195
  setIsLoading(true);
16196
+ if (config.partnerContractId) {
16197
+ const validationResponse = await fetch(getApiUrl(config.apiBaseUrl, `/booking/validate-partner?organizationId=${encodeURIComponent(config.organizationId)}&contractId=${config.partnerContractId}`), {
16198
+ method: "GET",
16199
+ headers: createApiHeaders(config, locale),
16200
+ });
16201
+ if (!validationResponse.ok) {
16202
+ setError("Invalid partner configuration");
16203
+ config.onError?.("Invalid partner configuration");
16204
+ return;
16205
+ }
16206
+ const validationData = (await validationResponse.json());
16207
+ if (!validationData.valid) {
16208
+ setError("Invalid partner configuration");
16209
+ config.onError?.("Invalid partner configuration");
16210
+ return;
16211
+ }
16212
+ }
15974
16213
  // Load voucher config in parallel if voucher mode is requested or there's event selection
15975
16214
  if (isVoucherModeRequested || hasEventSelection) {
15976
16215
  void loadVoucherConfig();
@@ -16106,11 +16345,18 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16106
16345
  setVoucherPurchaseResult(voucherData.voucherResult);
16107
16346
  setIsSuccess(true);
16108
16347
  setSuccessPaymentId(null);
16348
+ return;
16109
16349
  }
16110
16350
  }
16111
16351
  catch {
16112
- // Ignore lookup errors; webhook eventual consistency may delay data.
16113
- }
16352
+ // Fall back to booking success flow if voucher lookup fails.
16353
+ }
16354
+ // Not a voucher purchase: treat as a booking return and open the
16355
+ // booking confirmation, mirroring the Stripe redirect handler.
16356
+ trackEvent("booking_completed", { paymentIntentId: mollieReturn.molliePaymentId, source: "mollie_redirect" });
16357
+ setVoucherPurchaseResult(null);
16358
+ setSuccessPaymentId(mollieReturn.molliePaymentId);
16359
+ setIsSuccess(true);
16114
16360
  };
16115
16361
  if (!isLoading) {
16116
16362
  void handleMollieVoucherReturn();
@@ -16299,27 +16545,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16299
16545
  widgetPaymentMode: data.widgetPaymentMode ?? "all",
16300
16546
  });
16301
16547
  }
16302
- if (data.stripePublishableKey) {
16303
- const stripeOptions = {
16304
- locale: locale,
16305
- };
16306
- if (data.connectedAccountId) {
16307
- stripeOptions.stripeAccount = data.connectedAccountId;
16308
- }
16309
- if (typeof window !== "undefined" && window.document) {
16310
- try {
16311
- void (window.parent === window || window.parent.location.href);
16312
- if (!stripeOptions.betas) {
16313
- stripeOptions.betas = [];
16314
- }
16315
- }
16316
- catch {
16317
- console.warn("[WIDGET] Detected restricted environment, adjusting Stripe options");
16318
- stripeOptions.betas = [];
16319
- }
16320
- }
16321
- setStripePromise(loadStripe(data.stripePublishableKey, stripeOptions));
16322
- }
16323
16548
  if (!skipInstanceAutoSelectRef.current && data.eventInstances.length === 1) {
16324
16549
  setSelectedEventInstance(data.eventInstances[0]);
16325
16550
  setCurrentStep("booking");
@@ -16359,29 +16584,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16359
16584
  roundPricesEnabled: data.roundPricesEnabled ?? true,
16360
16585
  widgetPaymentMode: data.widgetPaymentMode ?? "all",
16361
16586
  });
16362
- if (!stripePromise && data.stripePublishableKey) {
16363
- const stripeOptions = {
16364
- locale: locale,
16365
- };
16366
- if (data.connectedAccountId) {
16367
- stripeOptions.stripeAccount = data.connectedAccountId;
16368
- }
16369
- if (typeof window !== "undefined" && window.document) {
16370
- try {
16371
- // Check if we have parent access (not in a cross-origin iframe)
16372
- void (window.parent === window || window.parent.location.href);
16373
- stripeOptions.apiVersion = "2025-02-24.acacia";
16374
- if (!stripeOptions.betas) {
16375
- stripeOptions.betas = [];
16376
- }
16377
- }
16378
- catch {
16379
- console.warn("[WIDGET] Detected restricted environment, adjusting Stripe options");
16380
- stripeOptions.betas = [];
16381
- }
16382
- }
16383
- setStripePromise(loadStripe(data.stripePublishableKey, stripeOptions));
16384
- }
16385
16587
  }
16386
16588
  else {
16387
16589
  const errorMessage = data.error || t("error.loadEventDetails");
@@ -16650,12 +16852,12 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16650
16852
  startTime: cardPreviewItem.startTime,
16651
16853
  endTime: cardPreviewItem.startTime,
16652
16854
  price: cardPreviewItem.price,
16653
- maxParticipants: cardPreviewItem.availableSpots + 1,
16654
- participantCount: 1,
16855
+ maxParticipants: Math.max(cardPreviewItem.availableSpots, 0),
16856
+ participantCount: 0,
16655
16857
  availableSpots: cardPreviewItem.availableSpots,
16656
16858
  durationDays: 1,
16657
16859
  durationPerDay: 1,
16658
- bookingOpen: true,
16860
+ bookingOpen: cardPreviewItem.bookingOpen && cardPreviewItem.availableSpots > 0,
16659
16861
  }
16660
16862
  : null;
16661
16863
  if (eventInstance) {
@@ -16843,7 +17045,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16843
17045
  }
16844
17046
  // Main view based on view mode
16845
17047
  if (viewMode === "next-events" && showingPreview) {
16846
- return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading, count: nextEventsSettings.count }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17048
+ return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading, count: nextEventsSettings.count }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16847
17049
  setIsSuccess(false);
16848
17050
  setCurrentStep("eventTypes");
16849
17051
  setShowingPreview(true);
@@ -16864,7 +17066,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16864
17066
  }, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16865
17067
  }
16866
17068
  if (viewMode === "specials" && showingPreview) {
16867
- 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, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17069
+ 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, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16868
17070
  setIsSuccess(false);
16869
17071
  setCurrentStep("eventTypes");
16870
17072
  setShowingPreview(true);
@@ -16884,7 +17086,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16884
17086
  }, isOpen: currentStep === "eventInstances", onClose: () => {
16885
17087
  setShowingPreview(true);
16886
17088
  setCurrentStep("eventTypes");
16887
- }, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17089
+ }, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16888
17090
  setIsSuccess(false);
16889
17091
  setCurrentStep("eventTypes");
16890
17092
  setShowingPreview(true);
@@ -16940,7 +17142,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16940
17142
  setShouldRenderInstanceSelection(true);
16941
17143
  }
16942
17144
  }, children: [isButtonBusy && jsxRuntime.jsx(Spinner, { size: 20, borderColor: "var(--bw-button-text-color, #ffffff)" }), config.buttonText ||
16943
- (isDirectInstanceMode ? t("button.bookNow") : t("button.viewDates"))] }), shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), shouldRenderUpsells && (jsxRuntime.jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17145
+ (isDirectInstanceMode ? t("button.bookNow") : t("button.viewDates"))] }), shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), shouldRenderUpsells && (jsxRuntime.jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16944
17146
  setIsSuccess(false);
16945
17147
  setCurrentStep("eventTypes");
16946
17148
  setSidebarOpen(false);
@@ -17001,7 +17203,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
17001
17203
  };
17002
17204
  };
17003
17205
  const backHandlers = getBackHandlers();
17004
- return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), shouldRenderUpsells && (jsxRuntime.jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: backHandlers.onClose, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
17206
+ return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), shouldRenderUpsells && (jsxRuntime.jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: backHandlers.onClose, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
17005
17207
  setIsSuccess(false);
17006
17208
  setCurrentStep("eventTypes");
17007
17209
  setSuccessPaymentId(null);