@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.
- package/dist/booking-widget.js +17305 -17103
- package/dist/booking-widget.js.map +1 -1
- package/dist/components/UniversalBookingWidget.d.ts +12 -4
- package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
- package/dist/components/booking/BookingForm.d.ts +1 -3
- package/dist/components/booking/BookingForm.d.ts.map +1 -1
- package/dist/components/booking/GiftCardOnlyBooking.d.ts.map +1 -1
- package/dist/components/booking/HoldCountdown.d.ts +10 -0
- package/dist/components/booking/HoldCountdown.d.ts.map +1 -0
- package/dist/components/booking/MolliePaymentForm.d.ts.map +1 -1
- package/dist/components/booking/StripePaymentForm.d.ts +1 -2
- package/dist/components/booking/StripePaymentForm.d.ts.map +1 -1
- package/dist/components/events/EventInstanceSelection.d.ts +3 -1
- package/dist/components/events/EventInstanceSelection.d.ts.map +1 -1
- package/dist/components/events/EventTypeDetailsDialog.d.ts +0 -1
- package/dist/components/events/EventTypeDetailsDialog.d.ts.map +1 -1
- package/dist/components/events/EventTypeSelection.d.ts +1 -1
- package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
- package/dist/components/events/WaitlistDialog.d.ts +10 -0
- package/dist/components/events/WaitlistDialog.d.ts.map +1 -0
- package/dist/components/voucher/VoucherPurchaseForm.d.ts +0 -1
- package/dist/components/voucher/VoucherPurchaseForm.d.ts.map +1 -1
- package/dist/components/voucher/useVoucherConfig.d.ts +0 -1
- package/dist/components/voucher/useVoucherConfig.d.ts.map +1 -1
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/index.cjs +561 -359
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +548 -346
- package/dist/index.esm.js.map +1 -1
- package/dist/utils/analytics.d.ts +3 -1
- package/dist/utils/analytics.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/validation/booking-schema.d.ts +4 -4
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -4,170 +4,6 @@ import { createRoot } from 'react-dom/client';
|
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import ReactDOM, { createPortal } from 'react-dom';
|
|
6
6
|
|
|
7
|
-
var V3_URL = 'https://js.stripe.com/v3';
|
|
8
|
-
var V3_URL_REGEX = /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;
|
|
9
|
-
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';
|
|
10
|
-
var findScript = function findScript() {
|
|
11
|
-
var scripts = document.querySelectorAll("script[src^=\"".concat(V3_URL, "\"]"));
|
|
12
|
-
|
|
13
|
-
for (var i = 0; i < scripts.length; i++) {
|
|
14
|
-
var script = scripts[i];
|
|
15
|
-
|
|
16
|
-
if (!V3_URL_REGEX.test(script.src)) {
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return script;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return null;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
var injectScript = function injectScript(params) {
|
|
27
|
-
var queryString = '';
|
|
28
|
-
var script = document.createElement('script');
|
|
29
|
-
script.src = "".concat(V3_URL).concat(queryString);
|
|
30
|
-
var headOrBody = document.head || document.body;
|
|
31
|
-
|
|
32
|
-
if (!headOrBody) {
|
|
33
|
-
throw new Error('Expected document.body not to be null. Stripe.js requires a <body> element.');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
headOrBody.appendChild(script);
|
|
37
|
-
return script;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
var registerWrapper = function registerWrapper(stripe, startTime) {
|
|
41
|
-
if (!stripe || !stripe._registerWrapper) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
stripe._registerWrapper({
|
|
46
|
-
name: 'stripe-js',
|
|
47
|
-
version: "4.6.0",
|
|
48
|
-
startTime: startTime
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
var stripePromise = null;
|
|
53
|
-
var onErrorListener = null;
|
|
54
|
-
var onLoadListener = null;
|
|
55
|
-
|
|
56
|
-
var onError = function onError(reject) {
|
|
57
|
-
return function () {
|
|
58
|
-
reject(new Error('Failed to load Stripe.js'));
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
var onLoad = function onLoad(resolve, reject) {
|
|
63
|
-
return function () {
|
|
64
|
-
if (window.Stripe) {
|
|
65
|
-
resolve(window.Stripe);
|
|
66
|
-
} else {
|
|
67
|
-
reject(new Error('Stripe.js not available'));
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
var loadScript = function loadScript(params) {
|
|
73
|
-
// Ensure that we only attempt to load Stripe.js at most once
|
|
74
|
-
if (stripePromise !== null) {
|
|
75
|
-
return stripePromise;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
stripePromise = new Promise(function (resolve, reject) {
|
|
79
|
-
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
80
|
-
// Resolve to null when imported server side. This makes the module
|
|
81
|
-
// safe to import in an isomorphic code base.
|
|
82
|
-
resolve(null);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (window.Stripe) {
|
|
87
|
-
resolve(window.Stripe);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
var script = findScript();
|
|
93
|
-
|
|
94
|
-
if (script && params) ; else if (!script) {
|
|
95
|
-
script = injectScript(params);
|
|
96
|
-
} else if (script && onLoadListener !== null && onErrorListener !== null) {
|
|
97
|
-
var _script$parentNode;
|
|
98
|
-
|
|
99
|
-
// remove event listeners
|
|
100
|
-
script.removeEventListener('load', onLoadListener);
|
|
101
|
-
script.removeEventListener('error', onErrorListener); // if script exists, but we are reloading due to an error,
|
|
102
|
-
// reload script to trigger 'load' event
|
|
103
|
-
|
|
104
|
-
(_script$parentNode = script.parentNode) === null || _script$parentNode === void 0 ? void 0 : _script$parentNode.removeChild(script);
|
|
105
|
-
script = injectScript(params);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
onLoadListener = onLoad(resolve, reject);
|
|
109
|
-
onErrorListener = onError(reject);
|
|
110
|
-
script.addEventListener('load', onLoadListener);
|
|
111
|
-
script.addEventListener('error', onErrorListener);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
reject(error);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
}); // Resets stripePromise on error
|
|
117
|
-
|
|
118
|
-
return stripePromise["catch"](function (error) {
|
|
119
|
-
stripePromise = null;
|
|
120
|
-
return Promise.reject(error);
|
|
121
|
-
});
|
|
122
|
-
};
|
|
123
|
-
var initStripe = function initStripe(maybeStripe, args, startTime) {
|
|
124
|
-
if (maybeStripe === null) {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
var stripe = maybeStripe.apply(undefined, args);
|
|
129
|
-
registerWrapper(stripe, startTime);
|
|
130
|
-
return stripe;
|
|
131
|
-
}; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
132
|
-
|
|
133
|
-
var stripePromise$1;
|
|
134
|
-
var loadCalled = false;
|
|
135
|
-
|
|
136
|
-
var getStripePromise = function getStripePromise() {
|
|
137
|
-
if (stripePromise$1) {
|
|
138
|
-
return stripePromise$1;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
stripePromise$1 = loadScript(null)["catch"](function (error) {
|
|
142
|
-
// clear cache on error
|
|
143
|
-
stripePromise$1 = null;
|
|
144
|
-
return Promise.reject(error);
|
|
145
|
-
});
|
|
146
|
-
return stripePromise$1;
|
|
147
|
-
}; // Execute our own script injection after a tick to give users time to do their
|
|
148
|
-
// own script injection.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
Promise.resolve().then(function () {
|
|
152
|
-
return getStripePromise();
|
|
153
|
-
})["catch"](function (error) {
|
|
154
|
-
if (!loadCalled) {
|
|
155
|
-
console.warn(error);
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
var loadStripe = function loadStripe() {
|
|
159
|
-
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
160
|
-
args[_key] = arguments[_key];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
loadCalled = true;
|
|
164
|
-
var startTime = Date.now(); // if previous attempts are unsuccessful, will re-load script
|
|
165
|
-
|
|
166
|
-
return getStripePromise().then(function (maybeStripe) {
|
|
167
|
-
return initStripe(maybeStripe, args, startTime);
|
|
168
|
-
});
|
|
169
|
-
};
|
|
170
|
-
|
|
171
7
|
const de$1 = {
|
|
172
8
|
// Common
|
|
173
9
|
"common.back": "← Zurück",
|
|
@@ -247,6 +83,24 @@ const de$1 = {
|
|
|
247
83
|
"instances.bestPrice": "bester Preis !!!",
|
|
248
84
|
"instances.goodPrice": "günstiger Preis",
|
|
249
85
|
"instances.oclock": "Uhr",
|
|
86
|
+
// Warteliste
|
|
87
|
+
"waitlist.joinCta": "Auf die Warteliste",
|
|
88
|
+
"waitlist.title": "Auf die Warteliste setzen",
|
|
89
|
+
"waitlist.subtitle": "{{name}} ist ausgebucht. Hinterlassen Sie Ihre Daten und wir melden uns, falls ein Platz frei wird.",
|
|
90
|
+
"waitlist.name": "Name",
|
|
91
|
+
"waitlist.email": "E-Mail",
|
|
92
|
+
"waitlist.phone": "Telefon (optional)",
|
|
93
|
+
"waitlist.requiredFields": "Bitte geben Sie Ihren Namen und Ihre E-Mail-Adresse ein.",
|
|
94
|
+
"waitlist.join": "Eintragen",
|
|
95
|
+
"waitlist.submitting": "Wird eingetragen...",
|
|
96
|
+
"waitlist.cancel": "Abbrechen",
|
|
97
|
+
"waitlist.submitError": "Etwas ist schiefgelaufen. Bitte versuchen Sie es erneut.",
|
|
98
|
+
"waitlist.successTitle": "Sie sind auf der Liste!",
|
|
99
|
+
"waitlist.successMessage": "Sie sind Nummer {{position}} auf der Warteliste. Wir melden uns, sobald ein Platz frei wird.",
|
|
100
|
+
"waitlist.done": "Fertig",
|
|
101
|
+
// Reservierungs-Countdown
|
|
102
|
+
"hold.reservedFor": "Platz reserviert für {{time}}",
|
|
103
|
+
"hold.expired": "Ihre Reservierung ist abgelaufen",
|
|
250
104
|
// Next events preview
|
|
251
105
|
"nextEvents.title": "Nächste verfügbare Termine",
|
|
252
106
|
"nextEvents.subtitle": "Wähle einen Termin aus oder zeige alle verfügbaren Termine an",
|
|
@@ -537,6 +391,24 @@ const en = {
|
|
|
537
391
|
"instances.bestPrice": "best price !!!",
|
|
538
392
|
"instances.goodPrice": "good price",
|
|
539
393
|
"instances.oclock": "",
|
|
394
|
+
// Waitlist
|
|
395
|
+
"waitlist.joinCta": "Join waitlist",
|
|
396
|
+
"waitlist.title": "Join the waitlist",
|
|
397
|
+
"waitlist.subtitle": "{{name}} is fully booked. Leave your details and we'll contact you if a spot opens up.",
|
|
398
|
+
"waitlist.name": "Name",
|
|
399
|
+
"waitlist.email": "Email",
|
|
400
|
+
"waitlist.phone": "Phone (optional)",
|
|
401
|
+
"waitlist.requiredFields": "Please enter your name and email.",
|
|
402
|
+
"waitlist.join": "Join waitlist",
|
|
403
|
+
"waitlist.submitting": "Joining...",
|
|
404
|
+
"waitlist.cancel": "Cancel",
|
|
405
|
+
"waitlist.submitError": "Something went wrong. Please try again.",
|
|
406
|
+
"waitlist.successTitle": "You're on the list!",
|
|
407
|
+
"waitlist.successMessage": "You are number {{position}} on the waitlist. We'll be in touch if a spot becomes available.",
|
|
408
|
+
"waitlist.done": "Done",
|
|
409
|
+
// Slot hold countdown
|
|
410
|
+
"hold.reservedFor": "Slot reserved for {{time}}",
|
|
411
|
+
"hold.expired": "Your reservation has expired",
|
|
540
412
|
// Next events preview
|
|
541
413
|
"nextEvents.title": "Next available dates",
|
|
542
414
|
"nextEvents.subtitle": "Select a date or view all available dates",
|
|
@@ -2242,6 +2114,7 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2242
2114
|
const categoryId = urlParams.get("categoryId") || urlParams.get("category");
|
|
2243
2115
|
const eventTypeId = urlParams.get("eventTypeId") || urlParams.get("type");
|
|
2244
2116
|
const eventTypeIds = urlParams.get("eventTypeIds") || urlParams.get("types");
|
|
2117
|
+
const partnerContractId = urlParams.get("partnerContractId") || urlParams.get("contractId");
|
|
2245
2118
|
const voucherValue = urlParams.get("voucherValue");
|
|
2246
2119
|
const voucherCategory = urlParams.get("voucherCategory");
|
|
2247
2120
|
const voucherEventType = urlParams.get("voucherEventType");
|
|
@@ -2254,11 +2127,14 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2254
2127
|
: ["0", "false", "no", "off"].includes(voucherCardIntegration.toLowerCase())
|
|
2255
2128
|
? false
|
|
2256
2129
|
: undefined;
|
|
2257
|
-
const omitSelectionKeys = ({ categoryId: _c, eventTypeId: _e, eventTypeIds: _es, eventInstanceId: _i, voucherValue: _vv, voucherCategory: _vc, voucherEventType: _ve, voucherImageId: _vi, ...rest }) => rest;
|
|
2130
|
+
const omitSelectionKeys = ({ categoryId: _c, eventTypeId: _e, eventTypeIds: _es, eventInstanceId: _i, voucherValue: _vv, voucherCategory: _vc, voucherEventType: _ve, voucherImageId: _vi, partnerContractId: _pc, ...rest }) => rest;
|
|
2258
2131
|
if (eventInstanceId && !Number.isNaN(Number(eventInstanceId))) {
|
|
2259
2132
|
return {
|
|
2260
2133
|
...omitSelectionKeys(baseConfig),
|
|
2261
2134
|
eventInstanceId: Number(eventInstanceId),
|
|
2135
|
+
...(partnerContractId
|
|
2136
|
+
? { partnerContractId }
|
|
2137
|
+
: {}),
|
|
2262
2138
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2263
2139
|
};
|
|
2264
2140
|
}
|
|
@@ -2266,6 +2142,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2266
2142
|
return {
|
|
2267
2143
|
...omitSelectionKeys(baseConfig),
|
|
2268
2144
|
categoryId: Number(categoryId),
|
|
2145
|
+
...(partnerContractId
|
|
2146
|
+
? { partnerContractId }
|
|
2147
|
+
: {}),
|
|
2269
2148
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2270
2149
|
};
|
|
2271
2150
|
}
|
|
@@ -2273,6 +2152,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2273
2152
|
return {
|
|
2274
2153
|
...omitSelectionKeys(baseConfig),
|
|
2275
2154
|
eventTypeId: Number(eventTypeId),
|
|
2155
|
+
...(partnerContractId
|
|
2156
|
+
? { partnerContractId }
|
|
2157
|
+
: {}),
|
|
2276
2158
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2277
2159
|
};
|
|
2278
2160
|
}
|
|
@@ -2285,6 +2167,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2285
2167
|
return {
|
|
2286
2168
|
...omitSelectionKeys(baseConfig),
|
|
2287
2169
|
eventTypeIds: typeIds,
|
|
2170
|
+
...(partnerContractId
|
|
2171
|
+
? { partnerContractId }
|
|
2172
|
+
: {}),
|
|
2288
2173
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2289
2174
|
};
|
|
2290
2175
|
}
|
|
@@ -2301,6 +2186,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2301
2186
|
if (parsedVoucherValues.length || parsedVoucherCategories.length || parsedVoucherEventTypes.length) {
|
|
2302
2187
|
return {
|
|
2303
2188
|
...baseConfig,
|
|
2189
|
+
...(partnerContractId
|
|
2190
|
+
? { partnerContractId }
|
|
2191
|
+
: {}),
|
|
2304
2192
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2305
2193
|
...(parsedVoucherValues.length > 0
|
|
2306
2194
|
? { voucherValue: parsedVoucherValues.length === 1 ? parsedVoucherValues[0] : parsedVoucherValues }
|
|
@@ -2318,6 +2206,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2318
2206
|
}
|
|
2319
2207
|
return {
|
|
2320
2208
|
...baseConfig,
|
|
2209
|
+
...(partnerContractId
|
|
2210
|
+
? { partnerContractId }
|
|
2211
|
+
: {}),
|
|
2321
2212
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2322
2213
|
};
|
|
2323
2214
|
};
|
|
@@ -6330,6 +6221,7 @@ function GiftCardOnlyBooking({ config, eventDetails, formData, discountCode, gif
|
|
|
6330
6221
|
customerPhone: formData.customerPhone?.trim(),
|
|
6331
6222
|
comment: formData.comment?.trim(),
|
|
6332
6223
|
paymentMethod: "gift_card",
|
|
6224
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
6333
6225
|
...(upsellSelections.length > 0 && { upsellSelections }),
|
|
6334
6226
|
};
|
|
6335
6227
|
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/create-gift-card-booking"), {
|
|
@@ -6606,6 +6498,7 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6606
6498
|
customerEmail: formData.customerEmail?.trim(),
|
|
6607
6499
|
customerPhone: formData.customerPhone?.trim(),
|
|
6608
6500
|
comment: formData.comment?.trim(),
|
|
6501
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
6609
6502
|
...(cardToken && { cardToken }),
|
|
6610
6503
|
...(selectedMethod && !cardToken && { method: selectedMethod }),
|
|
6611
6504
|
...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
|
|
@@ -6832,6 +6725,170 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6832
6725
|
}, children: isLoading ? (jsxs(Fragment, { children: [spinner("var(--bw-surface-color)"), " ", buttonLabel] })) : (buttonLabel) }))] }));
|
|
6833
6726
|
}
|
|
6834
6727
|
|
|
6728
|
+
var V3_URL = 'https://js.stripe.com/v3';
|
|
6729
|
+
var V3_URL_REGEX = /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;
|
|
6730
|
+
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';
|
|
6731
|
+
var findScript = function findScript() {
|
|
6732
|
+
var scripts = document.querySelectorAll("script[src^=\"".concat(V3_URL, "\"]"));
|
|
6733
|
+
|
|
6734
|
+
for (var i = 0; i < scripts.length; i++) {
|
|
6735
|
+
var script = scripts[i];
|
|
6736
|
+
|
|
6737
|
+
if (!V3_URL_REGEX.test(script.src)) {
|
|
6738
|
+
continue;
|
|
6739
|
+
}
|
|
6740
|
+
|
|
6741
|
+
return script;
|
|
6742
|
+
}
|
|
6743
|
+
|
|
6744
|
+
return null;
|
|
6745
|
+
};
|
|
6746
|
+
|
|
6747
|
+
var injectScript = function injectScript(params) {
|
|
6748
|
+
var queryString = '';
|
|
6749
|
+
var script = document.createElement('script');
|
|
6750
|
+
script.src = "".concat(V3_URL).concat(queryString);
|
|
6751
|
+
var headOrBody = document.head || document.body;
|
|
6752
|
+
|
|
6753
|
+
if (!headOrBody) {
|
|
6754
|
+
throw new Error('Expected document.body not to be null. Stripe.js requires a <body> element.');
|
|
6755
|
+
}
|
|
6756
|
+
|
|
6757
|
+
headOrBody.appendChild(script);
|
|
6758
|
+
return script;
|
|
6759
|
+
};
|
|
6760
|
+
|
|
6761
|
+
var registerWrapper = function registerWrapper(stripe, startTime) {
|
|
6762
|
+
if (!stripe || !stripe._registerWrapper) {
|
|
6763
|
+
return;
|
|
6764
|
+
}
|
|
6765
|
+
|
|
6766
|
+
stripe._registerWrapper({
|
|
6767
|
+
name: 'stripe-js',
|
|
6768
|
+
version: "4.6.0",
|
|
6769
|
+
startTime: startTime
|
|
6770
|
+
});
|
|
6771
|
+
};
|
|
6772
|
+
|
|
6773
|
+
var stripePromise = null;
|
|
6774
|
+
var onErrorListener = null;
|
|
6775
|
+
var onLoadListener = null;
|
|
6776
|
+
|
|
6777
|
+
var onError = function onError(reject) {
|
|
6778
|
+
return function () {
|
|
6779
|
+
reject(new Error('Failed to load Stripe.js'));
|
|
6780
|
+
};
|
|
6781
|
+
};
|
|
6782
|
+
|
|
6783
|
+
var onLoad = function onLoad(resolve, reject) {
|
|
6784
|
+
return function () {
|
|
6785
|
+
if (window.Stripe) {
|
|
6786
|
+
resolve(window.Stripe);
|
|
6787
|
+
} else {
|
|
6788
|
+
reject(new Error('Stripe.js not available'));
|
|
6789
|
+
}
|
|
6790
|
+
};
|
|
6791
|
+
};
|
|
6792
|
+
|
|
6793
|
+
var loadScript = function loadScript(params) {
|
|
6794
|
+
// Ensure that we only attempt to load Stripe.js at most once
|
|
6795
|
+
if (stripePromise !== null) {
|
|
6796
|
+
return stripePromise;
|
|
6797
|
+
}
|
|
6798
|
+
|
|
6799
|
+
stripePromise = new Promise(function (resolve, reject) {
|
|
6800
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
6801
|
+
// Resolve to null when imported server side. This makes the module
|
|
6802
|
+
// safe to import in an isomorphic code base.
|
|
6803
|
+
resolve(null);
|
|
6804
|
+
return;
|
|
6805
|
+
}
|
|
6806
|
+
|
|
6807
|
+
if (window.Stripe) {
|
|
6808
|
+
resolve(window.Stripe);
|
|
6809
|
+
return;
|
|
6810
|
+
}
|
|
6811
|
+
|
|
6812
|
+
try {
|
|
6813
|
+
var script = findScript();
|
|
6814
|
+
|
|
6815
|
+
if (script && params) ; else if (!script) {
|
|
6816
|
+
script = injectScript(params);
|
|
6817
|
+
} else if (script && onLoadListener !== null && onErrorListener !== null) {
|
|
6818
|
+
var _script$parentNode;
|
|
6819
|
+
|
|
6820
|
+
// remove event listeners
|
|
6821
|
+
script.removeEventListener('load', onLoadListener);
|
|
6822
|
+
script.removeEventListener('error', onErrorListener); // if script exists, but we are reloading due to an error,
|
|
6823
|
+
// reload script to trigger 'load' event
|
|
6824
|
+
|
|
6825
|
+
(_script$parentNode = script.parentNode) === null || _script$parentNode === void 0 ? void 0 : _script$parentNode.removeChild(script);
|
|
6826
|
+
script = injectScript(params);
|
|
6827
|
+
}
|
|
6828
|
+
|
|
6829
|
+
onLoadListener = onLoad(resolve, reject);
|
|
6830
|
+
onErrorListener = onError(reject);
|
|
6831
|
+
script.addEventListener('load', onLoadListener);
|
|
6832
|
+
script.addEventListener('error', onErrorListener);
|
|
6833
|
+
} catch (error) {
|
|
6834
|
+
reject(error);
|
|
6835
|
+
return;
|
|
6836
|
+
}
|
|
6837
|
+
}); // Resets stripePromise on error
|
|
6838
|
+
|
|
6839
|
+
return stripePromise["catch"](function (error) {
|
|
6840
|
+
stripePromise = null;
|
|
6841
|
+
return Promise.reject(error);
|
|
6842
|
+
});
|
|
6843
|
+
};
|
|
6844
|
+
var initStripe = function initStripe(maybeStripe, args, startTime) {
|
|
6845
|
+
if (maybeStripe === null) {
|
|
6846
|
+
return null;
|
|
6847
|
+
}
|
|
6848
|
+
|
|
6849
|
+
var stripe = maybeStripe.apply(undefined, args);
|
|
6850
|
+
registerWrapper(stripe, startTime);
|
|
6851
|
+
return stripe;
|
|
6852
|
+
}; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
6853
|
+
|
|
6854
|
+
var stripePromise$1;
|
|
6855
|
+
var loadCalled = false;
|
|
6856
|
+
|
|
6857
|
+
var getStripePromise = function getStripePromise() {
|
|
6858
|
+
if (stripePromise$1) {
|
|
6859
|
+
return stripePromise$1;
|
|
6860
|
+
}
|
|
6861
|
+
|
|
6862
|
+
stripePromise$1 = loadScript(null)["catch"](function (error) {
|
|
6863
|
+
// clear cache on error
|
|
6864
|
+
stripePromise$1 = null;
|
|
6865
|
+
return Promise.reject(error);
|
|
6866
|
+
});
|
|
6867
|
+
return stripePromise$1;
|
|
6868
|
+
}; // Execute our own script injection after a tick to give users time to do their
|
|
6869
|
+
// own script injection.
|
|
6870
|
+
|
|
6871
|
+
|
|
6872
|
+
Promise.resolve().then(function () {
|
|
6873
|
+
return getStripePromise();
|
|
6874
|
+
})["catch"](function (error) {
|
|
6875
|
+
if (!loadCalled) {
|
|
6876
|
+
console.warn(error);
|
|
6877
|
+
}
|
|
6878
|
+
});
|
|
6879
|
+
var loadStripe = function loadStripe() {
|
|
6880
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
6881
|
+
args[_key] = arguments[_key];
|
|
6882
|
+
}
|
|
6883
|
+
|
|
6884
|
+
loadCalled = true;
|
|
6885
|
+
var startTime = Date.now(); // if previous attempts are unsuccessful, will re-load script
|
|
6886
|
+
|
|
6887
|
+
return getStripePromise().then(function (maybeStripe) {
|
|
6888
|
+
return initStripe(maybeStripe, args, startTime);
|
|
6889
|
+
});
|
|
6890
|
+
};
|
|
6891
|
+
|
|
6835
6892
|
// Inner component that uses the Stripe hooks
|
|
6836
6893
|
function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onError, }) {
|
|
6837
6894
|
const t = useTranslations();
|
|
@@ -6860,13 +6917,25 @@ function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onEr
|
|
|
6860
6917
|
},
|
|
6861
6918
|
}, submitContent: jsx(Fragment, { children: submitLabel }), loadingContent: jsxs(Fragment, { children: [spinner("var(--bw-surface-color)"), " ", t("button.processingPayment")] }) }));
|
|
6862
6919
|
}
|
|
6863
|
-
function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig,
|
|
6920
|
+
function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripeAppearance, upsellSelections = [], }) {
|
|
6864
6921
|
const t = useTranslations();
|
|
6865
6922
|
const { locale } = useLocale();
|
|
6866
6923
|
const [clientSecret, setClientSecret] = useState(null);
|
|
6867
6924
|
const [paymentIntentId, setPaymentIntentId] = useState(null);
|
|
6868
6925
|
const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = useState(false);
|
|
6869
6926
|
const [paymentError, setPaymentError] = useState(null);
|
|
6927
|
+
const stripePromise = useMemo(() => {
|
|
6928
|
+
if (!systemConfig?.stripePublishableKey) {
|
|
6929
|
+
return null;
|
|
6930
|
+
}
|
|
6931
|
+
const stripeOptions = {
|
|
6932
|
+
locale: locale,
|
|
6933
|
+
};
|
|
6934
|
+
if (systemConfig.connectedAccountId) {
|
|
6935
|
+
stripeOptions.stripeAccount = systemConfig.connectedAccountId;
|
|
6936
|
+
}
|
|
6937
|
+
return loadStripe(systemConfig.stripePublishableKey, stripeOptions);
|
|
6938
|
+
}, [systemConfig?.stripePublishableKey, systemConfig?.connectedAccountId, locale]);
|
|
6870
6939
|
const storageKey = typeof window !== "undefined"
|
|
6871
6940
|
? `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
|
|
6872
6941
|
: "";
|
|
@@ -6945,6 +7014,7 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6945
7014
|
customerEmail: formData.customerEmail?.trim(),
|
|
6946
7015
|
customerPhone: formData.customerPhone?.trim(),
|
|
6947
7016
|
comment: formData.comment?.trim(),
|
|
7017
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
6948
7018
|
...(paymentIntentId && { paymentIntentId }),
|
|
6949
7019
|
...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
|
|
6950
7020
|
};
|
|
@@ -7014,7 +7084,7 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
7014
7084
|
if (isFullyCoveredByGiftCards && totalAmount <= 0) {
|
|
7015
7085
|
return (jsx(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: onSuccess, onError: onError, upsellSelections: upsellSelections }));
|
|
7016
7086
|
}
|
|
7017
|
-
if (isCreatingPaymentIntent || !clientSecret) {
|
|
7087
|
+
if (isCreatingPaymentIntent || !clientSecret || !stripePromise) {
|
|
7018
7088
|
return (jsxs("div", { style: {
|
|
7019
7089
|
display: "flex",
|
|
7020
7090
|
alignItems: "center",
|
|
@@ -7052,6 +7122,49 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
7052
7122
|
}, onError: onError }) }));
|
|
7053
7123
|
}
|
|
7054
7124
|
|
|
7125
|
+
const HOLD_DURATION_SECONDS = 15 * 60;
|
|
7126
|
+
/**
|
|
7127
|
+
* Shows the remaining time on the 15-minute slot hold that the server places on
|
|
7128
|
+
* the pending order while the customer completes checkout. The timer is started
|
|
7129
|
+
* client-side when the payment step opens — matching the moment the pending
|
|
7130
|
+
* order (and its `holdExpiresAt`) is created server-side.
|
|
7131
|
+
*/
|
|
7132
|
+
function HoldCountdown({ onExpire }) {
|
|
7133
|
+
const t = useTranslations();
|
|
7134
|
+
const [remaining, setRemaining] = useState(HOLD_DURATION_SECONDS);
|
|
7135
|
+
useEffect(() => {
|
|
7136
|
+
const startedAt = Date.now();
|
|
7137
|
+
const interval = setInterval(() => {
|
|
7138
|
+
const elapsed = Math.floor((Date.now() - startedAt) / 1000);
|
|
7139
|
+
const left = Math.max(0, HOLD_DURATION_SECONDS - elapsed);
|
|
7140
|
+
setRemaining(left);
|
|
7141
|
+
if (left <= 0) {
|
|
7142
|
+
clearInterval(interval);
|
|
7143
|
+
onExpire?.();
|
|
7144
|
+
}
|
|
7145
|
+
}, 1000);
|
|
7146
|
+
return () => clearInterval(interval);
|
|
7147
|
+
}, [onExpire]);
|
|
7148
|
+
const minutes = Math.floor(remaining / 60);
|
|
7149
|
+
const seconds = remaining % 60;
|
|
7150
|
+
const formatted = `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
7151
|
+
const isLow = remaining <= 60;
|
|
7152
|
+
return (jsxs("div", { style: {
|
|
7153
|
+
display: "flex",
|
|
7154
|
+
alignItems: "center",
|
|
7155
|
+
gap: "8px",
|
|
7156
|
+
padding: "10px 14px",
|
|
7157
|
+
borderRadius: "var(--bw-border-radius)",
|
|
7158
|
+
border: `1px solid ${isLow ? "var(--bw-error-color)" : "var(--bw-border-color)"}`,
|
|
7159
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
7160
|
+
color: isLow ? "var(--bw-error-color)" : "var(--bw-text-color)",
|
|
7161
|
+
fontFamily: "var(--bw-font-family)",
|
|
7162
|
+
fontSize: "14px",
|
|
7163
|
+
}, children: [jsx(IconClock, {}), jsx("span", { children: remaining > 0
|
|
7164
|
+
? t("hold.reservedFor", { time: formatted })
|
|
7165
|
+
: t("hold.expired") })] }));
|
|
7166
|
+
}
|
|
7167
|
+
|
|
7055
7168
|
const FLAG_EMOJIS = {
|
|
7056
7169
|
de: "🇩🇪",
|
|
7057
7170
|
en: "🇬🇧",
|
|
@@ -11463,6 +11576,11 @@ const buttonStyles = {
|
|
|
11463
11576
|
color: "var(--bw-text-muted)",
|
|
11464
11577
|
border: "1px solid var(--bw-border-color)",
|
|
11465
11578
|
},
|
|
11579
|
+
// Size variants
|
|
11580
|
+
small: {
|
|
11581
|
+
padding: "8px 12px",
|
|
11582
|
+
fontSize: "13px",
|
|
11583
|
+
},
|
|
11466
11584
|
// Full width modifier
|
|
11467
11585
|
fullWidth: {
|
|
11468
11586
|
width: "100%",
|
|
@@ -11535,6 +11653,12 @@ const sectionStyles = {
|
|
|
11535
11653
|
// TEXT
|
|
11536
11654
|
// ============================================
|
|
11537
11655
|
const textStyles = {
|
|
11656
|
+
body: {
|
|
11657
|
+
fontSize: "14px",
|
|
11658
|
+
color: "var(--bw-text-color)",
|
|
11659
|
+
fontFamily: "var(--bw-font-family)",
|
|
11660
|
+
lineHeight: 1.5,
|
|
11661
|
+
},
|
|
11538
11662
|
muted: {
|
|
11539
11663
|
fontSize: "14px",
|
|
11540
11664
|
color: "var(--bw-text-muted)",
|
|
@@ -11604,7 +11728,7 @@ const sectionHeaderStyles$1 = sectionStyles.header;
|
|
|
11604
11728
|
const labelStyles$1 = formStyles.label;
|
|
11605
11729
|
const inputStyles$1 = formStyles.input;
|
|
11606
11730
|
const errorTextStyles$1 = formStyles.error;
|
|
11607
|
-
function BookingForm({ config, eventDetails,
|
|
11731
|
+
function BookingForm({ config, eventDetails, onSuccess, onError, isOpen, onClose, systemConfig, selectedUpsells = [], upsells = [], persistedState = null, onPersistedStateChange, }) {
|
|
11608
11732
|
const t$1 = useTranslations();
|
|
11609
11733
|
const { locale } = useLocale();
|
|
11610
11734
|
const timezone = useTimezone();
|
|
@@ -11804,7 +11928,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11804
11928
|
newTotal: appliedDiscountCode.newTotal,
|
|
11805
11929
|
}
|
|
11806
11930
|
: null;
|
|
11807
|
-
const hasPaymentProvider =
|
|
11931
|
+
const hasPaymentProvider = systemConfig?.paymentProvider === "stripe" || systemConfig?.paymentProvider === "mollie";
|
|
11808
11932
|
const handleVoucherValidated = useCallback((voucher, _error) => {
|
|
11809
11933
|
if (voucher) {
|
|
11810
11934
|
setAppliedVouchers((prev) => [...prev, voucher]);
|
|
@@ -12070,7 +12194,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
12070
12194
|
...inputStyles$1,
|
|
12071
12195
|
resize: "vertical",
|
|
12072
12196
|
minHeight: "80px",
|
|
12073
|
-
} })] })] })] })), checkoutStep === "payment" && (jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "24px" }, children: [jsxs("div", { style: cardStyles$1, children: [jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: "16px" }, children: t$1("summary.title") }), jsxs("div", { style: { marginTop: "10px", display: "flex", flexDirection: "column", gap: "12px" }, children: [jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsx("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t$1("booking.price") }), jsxs("div", { style: {
|
|
12197
|
+
} })] })] })] })), checkoutStep === "payment" && (jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "24px" }, children: [jsx(HoldCountdown, {}), jsxs("div", { style: cardStyles$1, children: [jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: "16px" }, children: t$1("summary.title") }), jsxs("div", { style: { marginTop: "10px", display: "flex", flexDirection: "column", gap: "12px" }, children: [jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsx("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t$1("booking.price") }), jsxs("div", { style: {
|
|
12074
12198
|
color: "var(--bw-text-color)",
|
|
12075
12199
|
fontWeight: 500,
|
|
12076
12200
|
fontFamily: "var(--bw-font-family)",
|
|
@@ -12174,7 +12298,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
12174
12298
|
fontFamily: "var(--bw-font-family)",
|
|
12175
12299
|
marginTop: "8px",
|
|
12176
12300
|
textAlign: "right",
|
|
12177
|
-
}, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), depositsOnlyBlocked && (jsx("div", { style: cardStyles$1, children: jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.depositNotConfigured") }) })), !hasPaymentProvider && !depositsOnlyBlocked && (jsx("div", { style: cardStyles$1, children: jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.paymentUnavailable") }) })), hasPaymentProvider && !depositsOnlyBlocked && (systemConfig?.paymentProvider === "mollie" ? (jsxs("div", { style: cardStyles$1, children: [jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), 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 })] })) : (jsxs("div", { style: cardStyles$1, children: [jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsx(StripePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig ?? null,
|
|
12301
|
+
}, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), depositsOnlyBlocked && (jsx("div", { style: cardStyles$1, children: jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.depositNotConfigured") }) })), !hasPaymentProvider && !depositsOnlyBlocked && (jsx("div", { style: cardStyles$1, children: jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.paymentUnavailable") }) })), hasPaymentProvider && !depositsOnlyBlocked && (systemConfig?.paymentProvider === "mollie" ? (jsxs("div", { style: cardStyles$1, children: [jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), 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 })] })) : (jsxs("div", { style: cardStyles$1, children: [jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), 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() })] })))] }))] }) }));
|
|
12178
12302
|
}
|
|
12179
12303
|
|
|
12180
12304
|
/**
|
|
@@ -13283,6 +13407,8 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13283
13407
|
const [stripeAppearance, setStripeAppearance] = useState(null);
|
|
13284
13408
|
// Initialize Stripe
|
|
13285
13409
|
useEffect(() => {
|
|
13410
|
+
if (!isOpen)
|
|
13411
|
+
return;
|
|
13286
13412
|
if (paymentProvider !== "stripe")
|
|
13287
13413
|
return;
|
|
13288
13414
|
if (systemConfig?.stripePublishableKey && !stripePromise) {
|
|
@@ -13294,7 +13420,7 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13294
13420
|
}
|
|
13295
13421
|
setStripePromise(loadStripe(systemConfig.stripePublishableKey, stripeOptions));
|
|
13296
13422
|
}
|
|
13297
|
-
}, [paymentProvider, systemConfig, locale, stripePromise]);
|
|
13423
|
+
}, [isOpen, paymentProvider, systemConfig, locale, stripePromise]);
|
|
13298
13424
|
// Initialize Stripe appearance
|
|
13299
13425
|
useEffect(() => {
|
|
13300
13426
|
const container = document.querySelector(".booking-widget-container");
|
|
@@ -14258,8 +14384,17 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
|
|
|
14258
14384
|
if (!item)
|
|
14259
14385
|
return jsx("div", { style: { height: "34px" } }, i);
|
|
14260
14386
|
const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
|
|
14261
|
-
|
|
14262
|
-
|
|
14387
|
+
const isRowBookable = item.bookingOpen && item.availableSpots > 0;
|
|
14388
|
+
return (jsxs("div", { onClick: (e) => {
|
|
14389
|
+
e.stopPropagation();
|
|
14390
|
+
if (isRowBookable) {
|
|
14391
|
+
onInstancePreview?.(item.id, eventType.id);
|
|
14392
|
+
}
|
|
14393
|
+
}, onMouseEnter: (e) => {
|
|
14394
|
+
if (onInstancePreview && isRowBookable) {
|
|
14395
|
+
e.currentTarget.style.backgroundColor = "var(--bw-highlight-subtle)";
|
|
14396
|
+
}
|
|
14397
|
+
}, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
|
|
14263
14398
|
height: "34px",
|
|
14264
14399
|
display: "grid",
|
|
14265
14400
|
gridTemplateColumns: "auto 1fr auto 20px",
|
|
@@ -14268,12 +14403,13 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
|
|
|
14268
14403
|
width: "100%",
|
|
14269
14404
|
fontSize: "13px",
|
|
14270
14405
|
color: "var(--bw-text-muted)",
|
|
14271
|
-
cursor: onInstancePreview ? "pointer" : "default",
|
|
14406
|
+
cursor: onInstancePreview && isRowBookable ? "pointer" : "default",
|
|
14272
14407
|
borderRadius: "4px",
|
|
14273
14408
|
padding: "0 2px",
|
|
14274
14409
|
transition: "background 0.15s",
|
|
14275
14410
|
boxSizing: "border-box",
|
|
14276
|
-
|
|
14411
|
+
opacity: isRowBookable ? 1 : 0.55,
|
|
14412
|
+
}, children: [jsxs("span", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: "3px" }, children: [item.isSpecial && (jsx("span", { style: { color: "var(--bw-highlight-color)", fontSize: "11px", lineHeight: 1 }, children: "\u2605" })), formatWeekday(item.startTime, timezone, locale), " ", formatDate(item.startTime, timezone, locale)] }), jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, children: item.name }), jsxs("span", { style: { display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [hasDiscount && (jsx("span", { style: { textDecoration: "line-through", opacity: 0.55, fontSize: "11px" }, children: formatCurrency(item.basePrice) })), jsx("span", { style: { fontWeight: 700, color: item.isSpecial ? "var(--bw-highlight-color)" : "var(--bw-text-color)" }, children: formatCurrency(item.price) })] }), item.isSpecial && item.specialDescription ? (jsx(InfoBadge, { text: item.specialDescription })) : (jsx("span", {}))] }, item.id));
|
|
14277
14413
|
}) }), hasMoreDates && (jsx("button", { type: "button", onClick: (e) => {
|
|
14278
14414
|
e.stopPropagation();
|
|
14279
14415
|
onEventTypeSelect(eventType);
|
|
@@ -14362,6 +14498,74 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
|
|
|
14362
14498
|
}) }) })), jsx(PaymentLogosStrip, {}), jsx(EventTypeDetailsDialog, { isOpen: detailsDialogOpen, onClose: handleCloseDetails, eventType: selectedEventTypeForDetails, onEventTypeSelect: onEventTypeSelect })] }));
|
|
14363
14499
|
}
|
|
14364
14500
|
|
|
14501
|
+
function WaitlistDialog({ apiBaseUrl, organizationId, eventInstanceId, eventName, onClose, }) {
|
|
14502
|
+
const t = useTranslations();
|
|
14503
|
+
const [name, setName] = useState("");
|
|
14504
|
+
const [email, setEmail] = useState("");
|
|
14505
|
+
const [phone, setPhone] = useState("");
|
|
14506
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
14507
|
+
const [error, setError] = useState(null);
|
|
14508
|
+
const [position, setPosition] = useState(null);
|
|
14509
|
+
const handleSubmit = async () => {
|
|
14510
|
+
setError(null);
|
|
14511
|
+
if (!name.trim() || !email.trim()) {
|
|
14512
|
+
setError(t("waitlist.requiredFields"));
|
|
14513
|
+
return;
|
|
14514
|
+
}
|
|
14515
|
+
setIsSubmitting(true);
|
|
14516
|
+
try {
|
|
14517
|
+
const response = await fetch(getApiUrl(apiBaseUrl, "/booking/waitlist"), {
|
|
14518
|
+
method: "POST",
|
|
14519
|
+
headers: { "Content-Type": "application/json" },
|
|
14520
|
+
body: JSON.stringify({
|
|
14521
|
+
organizationId,
|
|
14522
|
+
eventInstanceId,
|
|
14523
|
+
name: name.trim(),
|
|
14524
|
+
email: email.trim(),
|
|
14525
|
+
phone: phone.trim() || undefined,
|
|
14526
|
+
}),
|
|
14527
|
+
});
|
|
14528
|
+
if (!response.ok) {
|
|
14529
|
+
throw new Error("request_failed");
|
|
14530
|
+
}
|
|
14531
|
+
const data = (await response.json());
|
|
14532
|
+
setPosition(data.position ?? null);
|
|
14533
|
+
}
|
|
14534
|
+
catch {
|
|
14535
|
+
setError(t("waitlist.submitError"));
|
|
14536
|
+
}
|
|
14537
|
+
finally {
|
|
14538
|
+
setIsSubmitting(false);
|
|
14539
|
+
}
|
|
14540
|
+
};
|
|
14541
|
+
return (jsx("div", { style: {
|
|
14542
|
+
position: "fixed",
|
|
14543
|
+
inset: 0,
|
|
14544
|
+
zIndex: 1000,
|
|
14545
|
+
display: "flex",
|
|
14546
|
+
alignItems: "center",
|
|
14547
|
+
justifyContent: "center",
|
|
14548
|
+
backgroundColor: "rgba(15, 23, 42, 0.6)",
|
|
14549
|
+
padding: "16px",
|
|
14550
|
+
fontFamily: "var(--bw-font-family)",
|
|
14551
|
+
}, onClick: onClose, children: jsx("div", { style: {
|
|
14552
|
+
width: "100%",
|
|
14553
|
+
maxWidth: "420px",
|
|
14554
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
14555
|
+
border: "1px solid var(--bw-border-color)",
|
|
14556
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14557
|
+
padding: "24px",
|
|
14558
|
+
}, onClick: (e) => e.stopPropagation(), children: position !== null ? (jsxs("div", { style: { textAlign: "center" }, children: [jsx("h3", { style: {
|
|
14559
|
+
margin: "0 0 8px",
|
|
14560
|
+
color: "var(--bw-highlight-color)",
|
|
14561
|
+
fontSize: "18px",
|
|
14562
|
+
}, children: t("waitlist.successTitle") }), jsx("p", { style: mergeStyles(textStyles.body, { marginBottom: "20px" }), children: t("waitlist.successMessage", { position }) }), jsx("button", { type: "button", style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), onClick: onClose, children: t("waitlist.done") })] })) : (jsxs(Fragment, { children: [jsx("h3", { style: {
|
|
14563
|
+
margin: "0 0 4px",
|
|
14564
|
+
color: "var(--bw-highlight-color)",
|
|
14565
|
+
fontSize: "18px",
|
|
14566
|
+
}, children: t("waitlist.title") }), jsx("p", { style: mergeStyles(textStyles.muted, { marginBottom: "16px" }), children: t("waitlist.subtitle", { name: eventName }) }), jsxs("div", { style: { marginBottom: "12px" }, children: [jsx("label", { style: formStyles.label, htmlFor: "wl-name", children: t("waitlist.name") }), jsx("input", { id: "wl-name", style: formStyles.input, value: name, onChange: (e) => setName(e.target.value) })] }), jsxs("div", { style: { marginBottom: "12px" }, children: [jsx("label", { style: formStyles.label, htmlFor: "wl-email", children: t("waitlist.email") }), jsx("input", { id: "wl-email", type: "email", style: formStyles.input, value: email, onChange: (e) => setEmail(e.target.value) })] }), jsxs("div", { style: { marginBottom: "12px" }, children: [jsx("label", { style: formStyles.label, htmlFor: "wl-phone", children: t("waitlist.phone") }), jsx("input", { id: "wl-phone", type: "tel", style: formStyles.input, value: phone, onChange: (e) => setPhone(e.target.value) })] }), error && jsx("p", { style: formStyles.error, children: error }), jsxs("div", { style: { display: "flex", gap: "8px", marginTop: "16px" }, children: [jsx("button", { type: "button", style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), onClick: onClose, disabled: isSubmitting, children: t("waitlist.cancel") }), jsx("button", { type: "button", style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), onClick: handleSubmit, disabled: isSubmitting, children: isSubmitting ? t("waitlist.submitting") : t("waitlist.join") })] })] })) }) }));
|
|
14567
|
+
}
|
|
14568
|
+
|
|
14365
14569
|
const getAllocationBadgeInfo = (availableSpots, maxParticipants, t) => {
|
|
14366
14570
|
const availabilityRatio = availableSpots / maxParticipants;
|
|
14367
14571
|
if (availabilityRatio >= 0.6)
|
|
@@ -14486,12 +14690,13 @@ const PriceDisplay = ({ price, yearPrices, t }) => {
|
|
|
14486
14690
|
boxShadow: displayInfo ? "0 2px 4px rgba(0, 0, 0, 0.2)" : "none",
|
|
14487
14691
|
}, children: formatCurrency(price) }));
|
|
14488
14692
|
};
|
|
14489
|
-
function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, }) {
|
|
14693
|
+
function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, apiBaseUrl, organizationId, }) {
|
|
14490
14694
|
const t = useTranslations();
|
|
14491
14695
|
const { locale } = useLocale();
|
|
14492
14696
|
const timezone = useTimezone();
|
|
14493
14697
|
const [selectedEventInstanceId, setSelectedEventInstanceId] = useState(null);
|
|
14494
14698
|
const [openGroups, setOpenGroups] = useState(new Set());
|
|
14699
|
+
const [waitlistEvent, setWaitlistEvent] = useState(null);
|
|
14495
14700
|
const getMonthPriceDisplayInfo = (minPrice) => {
|
|
14496
14701
|
return getPriceDisplayInfo(minPrice, yearPrices, t);
|
|
14497
14702
|
};
|
|
@@ -14652,116 +14857,127 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
|
|
|
14652
14857
|
fontFamily: "var(--bw-font-family)",
|
|
14653
14858
|
}, children: t("instances.noAvailable") }), jsx("p", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t("instances.noAvailableMessage") })] }) }) }));
|
|
14654
14859
|
}
|
|
14655
|
-
return (
|
|
14656
|
-
|
|
14657
|
-
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
|
|
14661
|
-
|
|
14662
|
-
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14676
|
-
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
|
|
14688
|
-
|
|
14689
|
-
|
|
14690
|
-
|
|
14691
|
-
|
|
14692
|
-
|
|
14693
|
-
|
|
14694
|
-
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
|
|
14698
|
-
|
|
14699
|
-
|
|
14700
|
-
|
|
14701
|
-
|
|
14702
|
-
|
|
14703
|
-
|
|
14704
|
-
|
|
14705
|
-
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14709
|
-
|
|
14710
|
-
|
|
14711
|
-
|
|
14712
|
-
|
|
14713
|
-
|
|
14714
|
-
|
|
14715
|
-
|
|
14716
|
-
|
|
14717
|
-
|
|
14718
|
-
|
|
14719
|
-
|
|
14720
|
-
|
|
14721
|
-
|
|
14722
|
-
|
|
14723
|
-
|
|
14724
|
-
|
|
14725
|
-
|
|
14726
|
-
|
|
14727
|
-
|
|
14728
|
-
|
|
14729
|
-
|
|
14730
|
-
|
|
14731
|
-
|
|
14732
|
-
|
|
14733
|
-
|
|
14734
|
-
|
|
14735
|
-
|
|
14736
|
-
|
|
14737
|
-
|
|
14738
|
-
|
|
14739
|
-
|
|
14740
|
-
|
|
14741
|
-
|
|
14742
|
-
|
|
14743
|
-
|
|
14744
|
-
|
|
14745
|
-
|
|
14746
|
-
|
|
14747
|
-
|
|
14748
|
-
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
|
|
14752
|
-
|
|
14753
|
-
|
|
14754
|
-
|
|
14755
|
-
|
|
14756
|
-
|
|
14757
|
-
|
|
14758
|
-
|
|
14759
|
-
|
|
14760
|
-
|
|
14761
|
-
|
|
14762
|
-
|
|
14763
|
-
|
|
14764
|
-
|
|
14860
|
+
return (jsxs(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name}`, footer: footerNav, children: [jsxs("div", { style: { padding: "20px 10px" }, children: [jsx(FlowProgress, { currentStep: "date", hasUpsellsStep: hasUpsellsStep }), jsx("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: monthYearGroups.map(({ key, label, events, minPrice, year }, idx) => {
|
|
14861
|
+
const monthPriceDisplayInfo = getMonthPriceDisplayInfo(minPrice);
|
|
14862
|
+
return (jsxs(Fragment$1, { children: [idx > 0 && monthYearGroups[idx - 1].year !== year && (jsx("div", { style: { height: "1px", backgroundColor: "var(--bw-border-color)", margin: "4px 0" } })), jsx(Accordion, { title: label, priceInfo: jsx("div", { style: {
|
|
14863
|
+
fontSize: "16px",
|
|
14864
|
+
fontWeight: 500,
|
|
14865
|
+
marginLeft: "auto",
|
|
14866
|
+
padding: "4px 8px",
|
|
14867
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
14868
|
+
backgroundColor: monthPriceDisplayInfo
|
|
14869
|
+
? monthPriceDisplayInfo.backgroundColor
|
|
14870
|
+
: "#14532d",
|
|
14871
|
+
color: monthPriceDisplayInfo
|
|
14872
|
+
? monthPriceDisplayInfo.textColor
|
|
14873
|
+
: "#4ade80",
|
|
14874
|
+
boxShadow: monthPriceDisplayInfo
|
|
14875
|
+
? "0 2px 4px rgba(0, 0, 0, 0.2)"
|
|
14876
|
+
: undefined,
|
|
14877
|
+
}, children: `${t("common.from")} ${formatCurrency(minPrice)}` }), isOpen: openGroups.has(key), onToggle: () => toggleGroup(key), children: jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", paddingTop: "12px" }, children: events.map((event) => {
|
|
14878
|
+
const availableSpots = event.maxParticipants - event.participantCount;
|
|
14879
|
+
const isFullyBooked = availableSpots === 0;
|
|
14880
|
+
const startDate = new Date(event.startTime);
|
|
14881
|
+
const isPastEvent = today.toISOString() >= startDate.toISOString();
|
|
14882
|
+
const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
|
|
14883
|
+
const canWaitlist = isFullyBooked &&
|
|
14884
|
+
!isPastEvent &&
|
|
14885
|
+
!!apiBaseUrl &&
|
|
14886
|
+
!!organizationId;
|
|
14887
|
+
return (jsxs("div", { style: {
|
|
14888
|
+
position: "relative",
|
|
14889
|
+
border: "1px solid var(--bw-border-color)",
|
|
14890
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
14891
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14892
|
+
padding: "16px 10px",
|
|
14893
|
+
transition: "all 0.2s ease",
|
|
14894
|
+
fontFamily: "var(--bw-font-family)",
|
|
14895
|
+
opacity: isDisabled && !canWaitlist ? 0.3 : 1,
|
|
14896
|
+
filter: isDisabled && !canWaitlist ? "grayscale(40%)" : "none",
|
|
14897
|
+
cursor: isDisabled
|
|
14898
|
+
? canWaitlist
|
|
14899
|
+
? "default"
|
|
14900
|
+
: "not-allowed"
|
|
14901
|
+
: "pointer",
|
|
14902
|
+
}, onClick: () => {
|
|
14903
|
+
if (!isDisabled) {
|
|
14904
|
+
handleEventInstanceSelect(event);
|
|
14905
|
+
}
|
|
14906
|
+
}, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (jsx("div", { style: {
|
|
14907
|
+
position: "absolute",
|
|
14908
|
+
inset: 0,
|
|
14909
|
+
backgroundColor: "rgba(15, 23, 42, 0.8)",
|
|
14910
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14911
|
+
display: "flex",
|
|
14912
|
+
alignItems: "center",
|
|
14913
|
+
justifyContent: "center",
|
|
14914
|
+
}, children: jsx("div", { style: {
|
|
14915
|
+
width: "32px",
|
|
14916
|
+
height: "32px",
|
|
14917
|
+
color: "var(--bw-highlight-color)",
|
|
14918
|
+
opacity: 0.8,
|
|
14919
|
+
fontSize: "32px",
|
|
14920
|
+
}, children: spinner() }) })), jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices, t: t }), jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants, t: t }), jsxs("div", { style: {
|
|
14921
|
+
display: "flex",
|
|
14922
|
+
justifyContent: "space-between",
|
|
14923
|
+
width: "100%",
|
|
14924
|
+
alignItems: "flex-start",
|
|
14925
|
+
gap: "12px",
|
|
14926
|
+
marginBottom: "4px",
|
|
14927
|
+
}, children: [jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "12px" }, children: [jsx("div", { style: {
|
|
14928
|
+
fontSize: "16px",
|
|
14929
|
+
transition: "all 0.2s ease",
|
|
14930
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
14931
|
+
borderTop: "4px solid var(--bw-border-color)",
|
|
14932
|
+
border: "1px solid var(--bw-border-color)",
|
|
14933
|
+
width: "40px",
|
|
14934
|
+
height: "40px",
|
|
14935
|
+
display: "flex",
|
|
14936
|
+
alignItems: "center",
|
|
14937
|
+
justifyContent: "center",
|
|
14938
|
+
fontWeight: 700,
|
|
14939
|
+
color: "var(--bw-text-color)",
|
|
14940
|
+
backgroundColor: "var(--bw-background-color)",
|
|
14941
|
+
}, children: startDate.getDate() }), jsxs("div", { style: {
|
|
14942
|
+
fontSize: "16px",
|
|
14943
|
+
color: "var(--bw-text-color)",
|
|
14944
|
+
display: "flex",
|
|
14945
|
+
flexDirection: "column",
|
|
14946
|
+
alignItems: "flex-start",
|
|
14947
|
+
justifyContent: "flex-start",
|
|
14948
|
+
lineHeight: 1.25,
|
|
14949
|
+
}, children: [jsxs("div", { children: [jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.startTime, timezone, locale) }), formatWeekday(event.startTime, timezone, locale) !==
|
|
14950
|
+
formatWeekday(event.endTime, timezone, locale) && (jsxs(Fragment, { children: [jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.endTime, timezone, locale) })] }))] }), jsx("div", { children: formatWeekday(event.startTime, timezone, locale) ===
|
|
14951
|
+
formatWeekday(event.endTime, timezone, locale) ? (jsxs(Fragment, { children: [jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime, timezone, locale) }), jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime, timezone, locale) })] })) : (jsxs("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime, timezone, locale), " ", t("instances.oclock")] })) })] }), jsxs("span", { style: {
|
|
14952
|
+
fontSize: "12px",
|
|
14953
|
+
fontWeight: 400,
|
|
14954
|
+
color: "var(--bw-text-muted)",
|
|
14955
|
+
marginLeft: "6px",
|
|
14956
|
+
backgroundColor: "rgba(0, 0, 0, 0.05)",
|
|
14957
|
+
whiteSpace: "nowrap",
|
|
14958
|
+
padding: "2px 6px",
|
|
14959
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
14960
|
+
}, children: [event.durationDays, " ", event.durationDays > 1 ? t("common.days") : t("common.day")] })] }), jsx("div", { style: {
|
|
14961
|
+
textAlign: "right",
|
|
14962
|
+
display: "flex",
|
|
14963
|
+
flexDirection: "column",
|
|
14964
|
+
alignItems: "flex-end",
|
|
14965
|
+
}, children: jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices, t: t }) })] }), event.name !== selectedEventType?.name && (jsx("h4", { style: {
|
|
14966
|
+
fontSize: "16px",
|
|
14967
|
+
fontWeight: 600,
|
|
14968
|
+
color: "var(--bw-text-color)",
|
|
14969
|
+
lineHeight: 1.25,
|
|
14970
|
+
margin: "0 0 2px 0",
|
|
14971
|
+
display: "flex",
|
|
14972
|
+
alignItems: "center",
|
|
14973
|
+
gap: "8px",
|
|
14974
|
+
maxWidth: "230px",
|
|
14975
|
+
}, children: event.name })), canWaitlist && (jsx("button", { type: "button", style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth, buttonStyles.small, { marginTop: "8px" }), onClick: (e) => {
|
|
14976
|
+
e.stopPropagation();
|
|
14977
|
+
setWaitlistEvent(event);
|
|
14978
|
+
}, children: t("waitlist.joinCta") }))] }, event.id));
|
|
14979
|
+
}) }) })] }, key));
|
|
14980
|
+
}) })] }), waitlistEvent && apiBaseUrl && organizationId && (jsx(WaitlistDialog, { apiBaseUrl: apiBaseUrl, organizationId: organizationId, eventInstanceId: waitlistEvent.id, eventName: waitlistEvent.name, onClose: () => setWaitlistEvent(null) }))] }));
|
|
14765
14981
|
}
|
|
14766
14982
|
|
|
14767
14983
|
function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText, showAllButton, isLoadingEventDetails = false, isLoadingShowAll = false, isLoading = false, count = 3, }) {
|
|
@@ -15655,6 +15871,7 @@ let buffer = [];
|
|
|
15655
15871
|
let flushTimer = null;
|
|
15656
15872
|
let currentApiBaseUrl = "";
|
|
15657
15873
|
let currentOrganizationId = "";
|
|
15874
|
+
let currentPartnerContractId = null;
|
|
15658
15875
|
const FLUSH_INTERVAL_MS = 3000;
|
|
15659
15876
|
function flush() {
|
|
15660
15877
|
if (buffer.length === 0)
|
|
@@ -15696,9 +15913,10 @@ function scheduleFlush() {
|
|
|
15696
15913
|
/**
|
|
15697
15914
|
* Initialise the analytics module. Must be called once before `trackEvent`.
|
|
15698
15915
|
*/
|
|
15699
|
-
function initAnalytics(apiBaseUrl, organizationId) {
|
|
15916
|
+
function initAnalytics(apiBaseUrl, organizationId, options) {
|
|
15700
15917
|
currentApiBaseUrl = apiBaseUrl;
|
|
15701
15918
|
currentOrganizationId = organizationId;
|
|
15919
|
+
currentPartnerContractId = options?.partnerContractId?.trim() || null;
|
|
15702
15920
|
if (typeof window !== "undefined") {
|
|
15703
15921
|
window.addEventListener("pagehide", flush);
|
|
15704
15922
|
window.addEventListener("visibilitychange", () => {
|
|
@@ -15715,9 +15933,12 @@ function trackEvent(event, properties = {}) {
|
|
|
15715
15933
|
return;
|
|
15716
15934
|
if (!currentOrganizationId)
|
|
15717
15935
|
return;
|
|
15936
|
+
const mergedProperties = currentPartnerContractId
|
|
15937
|
+
? { ...properties, partnerContractId: currentPartnerContractId }
|
|
15938
|
+
: properties;
|
|
15718
15939
|
buffer.push({
|
|
15719
15940
|
event,
|
|
15720
|
-
properties,
|
|
15941
|
+
properties: mergedProperties,
|
|
15721
15942
|
domain: window.location.hostname,
|
|
15722
15943
|
url: window.location.href,
|
|
15723
15944
|
referrer: document.referrer,
|
|
@@ -15793,7 +16014,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15793
16014
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
15794
16015
|
// Booking flow state
|
|
15795
16016
|
const [eventDetails, setEventDetails] = useState(null);
|
|
15796
|
-
const [stripePromise, setStripePromise] = useState(null);
|
|
15797
16017
|
// SEPARATED LOADING STATES
|
|
15798
16018
|
const [isLoading, setIsLoading] = useState(true);
|
|
15799
16019
|
const [isLoadingEventInstances, setIsLoadingEventInstances] = useState(false);
|
|
@@ -15928,7 +16148,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15928
16148
|
useEffect(() => {
|
|
15929
16149
|
if (!analyticsInitRef.current && config.organizationId) {
|
|
15930
16150
|
analyticsInitRef.current = true;
|
|
15931
|
-
initAnalytics(config.apiBaseUrl, config.organizationId
|
|
16151
|
+
initAnalytics(config.apiBaseUrl, config.organizationId, {
|
|
16152
|
+
partnerContractId: config.partnerContractId,
|
|
16153
|
+
});
|
|
15932
16154
|
trackEvent("widget_loaded", {
|
|
15933
16155
|
viewMode,
|
|
15934
16156
|
eventTypeId: config.eventTypeId,
|
|
@@ -15937,7 +16159,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15937
16159
|
isStandaloneVoucherMode,
|
|
15938
16160
|
});
|
|
15939
16161
|
}
|
|
15940
|
-
}, [config.organizationId, config.apiBaseUrl]);
|
|
16162
|
+
}, [config.organizationId, config.apiBaseUrl, config.partnerContractId, viewMode, config.eventTypeId, config.categoryId, config.eventInstanceId, isStandaloneVoucherMode]);
|
|
15941
16163
|
// Fire widget pageview once when Google Ads config is received from API
|
|
15942
16164
|
const pageviewFiredRef = useRef(false);
|
|
15943
16165
|
useEffect(() => {
|
|
@@ -15951,6 +16173,23 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15951
16173
|
const initializeWidget = async () => {
|
|
15952
16174
|
try {
|
|
15953
16175
|
setIsLoading(true);
|
|
16176
|
+
if (config.partnerContractId) {
|
|
16177
|
+
const validationResponse = await fetch(getApiUrl(config.apiBaseUrl, `/booking/validate-partner?organizationId=${encodeURIComponent(config.organizationId)}&contractId=${config.partnerContractId}`), {
|
|
16178
|
+
method: "GET",
|
|
16179
|
+
headers: createApiHeaders(config, locale),
|
|
16180
|
+
});
|
|
16181
|
+
if (!validationResponse.ok) {
|
|
16182
|
+
setError("Invalid partner configuration");
|
|
16183
|
+
config.onError?.("Invalid partner configuration");
|
|
16184
|
+
return;
|
|
16185
|
+
}
|
|
16186
|
+
const validationData = (await validationResponse.json());
|
|
16187
|
+
if (!validationData.valid) {
|
|
16188
|
+
setError("Invalid partner configuration");
|
|
16189
|
+
config.onError?.("Invalid partner configuration");
|
|
16190
|
+
return;
|
|
16191
|
+
}
|
|
16192
|
+
}
|
|
15954
16193
|
// Load voucher config in parallel if voucher mode is requested or there's event selection
|
|
15955
16194
|
if (isVoucherModeRequested || hasEventSelection) {
|
|
15956
16195
|
void loadVoucherConfig();
|
|
@@ -16086,11 +16325,18 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16086
16325
|
setVoucherPurchaseResult(voucherData.voucherResult);
|
|
16087
16326
|
setIsSuccess(true);
|
|
16088
16327
|
setSuccessPaymentId(null);
|
|
16328
|
+
return;
|
|
16089
16329
|
}
|
|
16090
16330
|
}
|
|
16091
16331
|
catch {
|
|
16092
|
-
//
|
|
16093
|
-
}
|
|
16332
|
+
// Fall back to booking success flow if voucher lookup fails.
|
|
16333
|
+
}
|
|
16334
|
+
// Not a voucher purchase: treat as a booking return and open the
|
|
16335
|
+
// booking confirmation, mirroring the Stripe redirect handler.
|
|
16336
|
+
trackEvent("booking_completed", { paymentIntentId: mollieReturn.molliePaymentId, source: "mollie_redirect" });
|
|
16337
|
+
setVoucherPurchaseResult(null);
|
|
16338
|
+
setSuccessPaymentId(mollieReturn.molliePaymentId);
|
|
16339
|
+
setIsSuccess(true);
|
|
16094
16340
|
};
|
|
16095
16341
|
if (!isLoading) {
|
|
16096
16342
|
void handleMollieVoucherReturn();
|
|
@@ -16279,27 +16525,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16279
16525
|
widgetPaymentMode: data.widgetPaymentMode ?? "all",
|
|
16280
16526
|
});
|
|
16281
16527
|
}
|
|
16282
|
-
if (data.stripePublishableKey) {
|
|
16283
|
-
const stripeOptions = {
|
|
16284
|
-
locale: locale,
|
|
16285
|
-
};
|
|
16286
|
-
if (data.connectedAccountId) {
|
|
16287
|
-
stripeOptions.stripeAccount = data.connectedAccountId;
|
|
16288
|
-
}
|
|
16289
|
-
if (typeof window !== "undefined" && window.document) {
|
|
16290
|
-
try {
|
|
16291
|
-
void (window.parent === window || window.parent.location.href);
|
|
16292
|
-
if (!stripeOptions.betas) {
|
|
16293
|
-
stripeOptions.betas = [];
|
|
16294
|
-
}
|
|
16295
|
-
}
|
|
16296
|
-
catch {
|
|
16297
|
-
console.warn("[WIDGET] Detected restricted environment, adjusting Stripe options");
|
|
16298
|
-
stripeOptions.betas = [];
|
|
16299
|
-
}
|
|
16300
|
-
}
|
|
16301
|
-
setStripePromise(loadStripe(data.stripePublishableKey, stripeOptions));
|
|
16302
|
-
}
|
|
16303
16528
|
if (!skipInstanceAutoSelectRef.current && data.eventInstances.length === 1) {
|
|
16304
16529
|
setSelectedEventInstance(data.eventInstances[0]);
|
|
16305
16530
|
setCurrentStep("booking");
|
|
@@ -16339,29 +16564,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16339
16564
|
roundPricesEnabled: data.roundPricesEnabled ?? true,
|
|
16340
16565
|
widgetPaymentMode: data.widgetPaymentMode ?? "all",
|
|
16341
16566
|
});
|
|
16342
|
-
if (!stripePromise && data.stripePublishableKey) {
|
|
16343
|
-
const stripeOptions = {
|
|
16344
|
-
locale: locale,
|
|
16345
|
-
};
|
|
16346
|
-
if (data.connectedAccountId) {
|
|
16347
|
-
stripeOptions.stripeAccount = data.connectedAccountId;
|
|
16348
|
-
}
|
|
16349
|
-
if (typeof window !== "undefined" && window.document) {
|
|
16350
|
-
try {
|
|
16351
|
-
// Check if we have parent access (not in a cross-origin iframe)
|
|
16352
|
-
void (window.parent === window || window.parent.location.href);
|
|
16353
|
-
stripeOptions.apiVersion = "2025-02-24.acacia";
|
|
16354
|
-
if (!stripeOptions.betas) {
|
|
16355
|
-
stripeOptions.betas = [];
|
|
16356
|
-
}
|
|
16357
|
-
}
|
|
16358
|
-
catch {
|
|
16359
|
-
console.warn("[WIDGET] Detected restricted environment, adjusting Stripe options");
|
|
16360
|
-
stripeOptions.betas = [];
|
|
16361
|
-
}
|
|
16362
|
-
}
|
|
16363
|
-
setStripePromise(loadStripe(data.stripePublishableKey, stripeOptions));
|
|
16364
|
-
}
|
|
16365
16567
|
}
|
|
16366
16568
|
else {
|
|
16367
16569
|
const errorMessage = data.error || t("error.loadEventDetails");
|
|
@@ -16630,12 +16832,12 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16630
16832
|
startTime: cardPreviewItem.startTime,
|
|
16631
16833
|
endTime: cardPreviewItem.startTime,
|
|
16632
16834
|
price: cardPreviewItem.price,
|
|
16633
|
-
maxParticipants: cardPreviewItem.availableSpots
|
|
16634
|
-
participantCount:
|
|
16835
|
+
maxParticipants: Math.max(cardPreviewItem.availableSpots, 0),
|
|
16836
|
+
participantCount: 0,
|
|
16635
16837
|
availableSpots: cardPreviewItem.availableSpots,
|
|
16636
16838
|
durationDays: 1,
|
|
16637
16839
|
durationPerDay: 1,
|
|
16638
|
-
bookingOpen:
|
|
16840
|
+
bookingOpen: cardPreviewItem.bookingOpen && cardPreviewItem.availableSpots > 0,
|
|
16639
16841
|
}
|
|
16640
16842
|
: null;
|
|
16641
16843
|
if (eventInstance) {
|
|
@@ -16823,7 +17025,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16823
17025
|
}
|
|
16824
17026
|
// Main view based on view mode
|
|
16825
17027
|
if (viewMode === "next-events" && showingPreview) {
|
|
16826
|
-
return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [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 && (jsx(BookingForm, { config: config, eventDetails: eventDetails,
|
|
17028
|
+
return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [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 && (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 })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
16827
17029
|
setIsSuccess(false);
|
|
16828
17030
|
setCurrentStep("eventTypes");
|
|
16829
17031
|
setShowingPreview(true);
|
|
@@ -16844,7 +17046,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16844
17046
|
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16845
17047
|
}
|
|
16846
17048
|
if (viewMode === "specials" && showingPreview) {
|
|
16847
|
-
return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [jsx(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails,
|
|
17049
|
+
return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [jsx(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
16848
17050
|
setIsSuccess(false);
|
|
16849
17051
|
setCurrentStep("eventTypes");
|
|
16850
17052
|
setShowingPreview(true);
|
|
@@ -16864,7 +17066,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16864
17066
|
}, isOpen: currentStep === "eventInstances", onClose: () => {
|
|
16865
17067
|
setShowingPreview(true);
|
|
16866
17068
|
setCurrentStep("eventTypes");
|
|
16867
|
-
}, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
17069
|
+
}, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
16868
17070
|
setIsSuccess(false);
|
|
16869
17071
|
setCurrentStep("eventTypes");
|
|
16870
17072
|
setShowingPreview(true);
|
|
@@ -16920,7 +17122,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16920
17122
|
setShouldRenderInstanceSelection(true);
|
|
16921
17123
|
}
|
|
16922
17124
|
}, children: [isButtonBusy && jsx(Spinner, { size: 20, borderColor: "var(--bw-button-text-color, #ffffff)" }), config.buttonText ||
|
|
16923
|
-
(isDirectInstanceMode ? t("button.bookNow") : t("button.viewDates"))] }), shouldRenderInstanceSelection && (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 && (jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails,
|
|
17125
|
+
(isDirectInstanceMode ? t("button.bookNow") : t("button.viewDates"))] }), shouldRenderInstanceSelection && (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 && (jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (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 })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
16924
17126
|
setIsSuccess(false);
|
|
16925
17127
|
setCurrentStep("eventTypes");
|
|
16926
17128
|
setSidebarOpen(false);
|
|
@@ -16981,7 +17183,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16981
17183
|
};
|
|
16982
17184
|
};
|
|
16983
17185
|
const backHandlers = getBackHandlers();
|
|
16984
|
-
return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), shouldRenderUpsells && (jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails,
|
|
17186
|
+
return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (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 && (jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (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 })), jsx(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
|
|
16985
17187
|
setIsSuccess(false);
|
|
16986
17188
|
setCurrentStep("eventTypes");
|
|
16987
17189
|
setSuccessPaymentId(null);
|