@bigz-app/booking-widget 1.4.4 → 1.5.1
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 +17856 -17103
- package/dist/booking-widget.js.map +1 -1
- package/dist/components/UniversalBookingWidget.d.ts +30 -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 +11 -1
- package/dist/components/booking/MolliePaymentForm.d.ts.map +1 -1
- package/dist/components/booking/StripePaymentForm.d.ts +11 -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 +16 -1
- package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
- package/dist/components/events/FreeformSelection.d.ts +49 -0
- package/dist/components/events/FreeformSelection.d.ts.map +1 -0
- package/dist/components/events/WaitlistDialog.d.ts +10 -0
- package/dist/components/events/WaitlistDialog.d.ts.map +1 -0
- package/dist/components/events/index.d.ts +1 -0
- package/dist/components/events/index.d.ts.map +1 -1
- package/dist/components/voucher/VoucherIntegration.d.ts +3 -2
- package/dist/components/voucher/VoucherIntegration.d.ts.map +1 -1
- package/dist/components/voucher/VoucherPurchaseForm.d.ts +8 -3
- package/dist/components/voucher/VoucherPurchaseForm.d.ts.map +1 -1
- package/dist/components/voucher/index.d.ts +1 -1
- package/dist/components/voucher/index.d.ts.map +1 -1
- package/dist/components/voucher/useVoucherConfig.d.ts +6 -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/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/sv.d.ts.map +1 -1
- package/dist/index.cjs +1235 -482
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +1228 -475
- 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.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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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",
|
|
@@ -315,6 +169,10 @@ const de$1 = {
|
|
|
315
169
|
"booking.stepUpsells": "Extras",
|
|
316
170
|
"booking.stepDetails": "Angaben",
|
|
317
171
|
"booking.stepPayment": "Zahlung",
|
|
172
|
+
"booking.steps": "{{count}} Schritt(e)",
|
|
173
|
+
"booking.freeformUnitsAvailable": "{{count}} Einheiten verfuegbar",
|
|
174
|
+
"booking.freeformNotAvailable": "Das gewaehlte Zeitfenster ist nicht verfuegbar",
|
|
175
|
+
"booking.freeformMissingConfig": "Die Freeform-Konfiguration fuer diesen Event-Typ fehlt.",
|
|
318
176
|
// Booking summary
|
|
319
177
|
"summary.title": "Buchungszusammenfassung",
|
|
320
178
|
"summary.subtotal": "Zwischensumme:",
|
|
@@ -370,6 +228,13 @@ const de$1 = {
|
|
|
370
228
|
"voucher.monetaryTypeDesc": "Beliebigen Betrag wählen",
|
|
371
229
|
"voucher.eventType": "Erlebnisgutschein",
|
|
372
230
|
"voucher.eventTypeDesc": "Für ein bestimmtes Erlebnis",
|
|
231
|
+
"voucher.categoryType": "Kategoriegutschein",
|
|
232
|
+
"voucher.categoryTypeDesc": "Für ein beliebiges Erlebnis einer Kategorie",
|
|
233
|
+
"voucher.categoryVoucher": "Kategoriegutschein",
|
|
234
|
+
"voucher.selectCategory": "Kategorie wählen",
|
|
235
|
+
"voucher.categoryLabel": "Kategorie",
|
|
236
|
+
"voucher.selectCategoryPlaceholder": "Kategorie auswählen...",
|
|
237
|
+
"voucher.categoryAmount": "Gutscheinwert",
|
|
373
238
|
"voucher.selectAmount": "Betrag wählen",
|
|
374
239
|
"voucher.customAmount": "Oder eigenen Betrag eingeben",
|
|
375
240
|
"voucher.amountRange": "Betrag muss zwischen {{min}} und {{max}} liegen",
|
|
@@ -557,6 +422,24 @@ const en = {
|
|
|
557
422
|
"instances.bestPrice": "best price !!!",
|
|
558
423
|
"instances.goodPrice": "good price",
|
|
559
424
|
"instances.oclock": "",
|
|
425
|
+
// Waitlist
|
|
426
|
+
"waitlist.joinCta": "Join waitlist",
|
|
427
|
+
"waitlist.title": "Join the waitlist",
|
|
428
|
+
"waitlist.subtitle": "{{name}} is fully booked. Leave your details and we'll contact you if a spot opens up.",
|
|
429
|
+
"waitlist.name": "Name",
|
|
430
|
+
"waitlist.email": "Email",
|
|
431
|
+
"waitlist.phone": "Phone (optional)",
|
|
432
|
+
"waitlist.requiredFields": "Please enter your name and email.",
|
|
433
|
+
"waitlist.join": "Join waitlist",
|
|
434
|
+
"waitlist.submitting": "Joining...",
|
|
435
|
+
"waitlist.cancel": "Cancel",
|
|
436
|
+
"waitlist.submitError": "Something went wrong. Please try again.",
|
|
437
|
+
"waitlist.successTitle": "You're on the list!",
|
|
438
|
+
"waitlist.successMessage": "You are number {{position}} on the waitlist. We'll be in touch if a spot becomes available.",
|
|
439
|
+
"waitlist.done": "Done",
|
|
440
|
+
// Slot hold countdown
|
|
441
|
+
"hold.reservedFor": "Slot reserved for {{time}}",
|
|
442
|
+
"hold.expired": "Your reservation has expired",
|
|
560
443
|
// Next events preview
|
|
561
444
|
"nextEvents.title": "Next available dates",
|
|
562
445
|
"nextEvents.subtitle": "Select a date or view all available dates",
|
|
@@ -605,6 +488,10 @@ const en = {
|
|
|
605
488
|
"booking.stepUpsells": "Extras",
|
|
606
489
|
"booking.stepDetails": "Details",
|
|
607
490
|
"booking.stepPayment": "Payment",
|
|
491
|
+
"booking.steps": "{{count}} step(s)",
|
|
492
|
+
"booking.freeformUnitsAvailable": "{{count}} units available",
|
|
493
|
+
"booking.freeformNotAvailable": "Selected window is not available",
|
|
494
|
+
"booking.freeformMissingConfig": "Missing freeform configuration for this event type.",
|
|
608
495
|
// Booking summary
|
|
609
496
|
"summary.title": "Booking Summary",
|
|
610
497
|
"summary.subtotal": "Subtotal:",
|
|
@@ -660,6 +547,13 @@ const en = {
|
|
|
660
547
|
"voucher.monetaryTypeDesc": "Choose any amount",
|
|
661
548
|
"voucher.eventType": "Event Voucher",
|
|
662
549
|
"voucher.eventTypeDesc": "For a specific experience",
|
|
550
|
+
"voucher.categoryType": "Category Voucher",
|
|
551
|
+
"voucher.categoryTypeDesc": "For any experience in a category",
|
|
552
|
+
"voucher.categoryVoucher": "Category Voucher",
|
|
553
|
+
"voucher.selectCategory": "Select Category",
|
|
554
|
+
"voucher.categoryLabel": "Category",
|
|
555
|
+
"voucher.selectCategoryPlaceholder": "Choose a category...",
|
|
556
|
+
"voucher.categoryAmount": "Voucher Value",
|
|
663
557
|
"voucher.selectAmount": "Select Amount",
|
|
664
558
|
"voucher.customAmount": "Or enter custom amount",
|
|
665
559
|
"voucher.amountRange": "Amount must be between {{min}} and {{max}}",
|
|
@@ -950,6 +844,13 @@ const es = {
|
|
|
950
844
|
"voucher.monetaryTypeDesc": "Elige cualquier cantidad",
|
|
951
845
|
"voucher.eventType": "Vale de experiencia",
|
|
952
846
|
"voucher.eventTypeDesc": "Para una experiencia específica",
|
|
847
|
+
"voucher.categoryType": "Vale de categoría",
|
|
848
|
+
"voucher.categoryTypeDesc": "Para cualquier experiencia de una categoría",
|
|
849
|
+
"voucher.categoryVoucher": "Vale de categoría",
|
|
850
|
+
"voucher.selectCategory": "Seleccionar categoría",
|
|
851
|
+
"voucher.categoryLabel": "Categoría",
|
|
852
|
+
"voucher.selectCategoryPlaceholder": "Elige una categoría...",
|
|
853
|
+
"voucher.categoryAmount": "Valor del vale",
|
|
953
854
|
"voucher.selectAmount": "Selecciona el importe",
|
|
954
855
|
"voucher.customAmount": "O introduce un importe personalizado",
|
|
955
856
|
"voucher.amountRange": "El importe debe estar entre {{min}} y {{max}}",
|
|
@@ -1240,6 +1141,13 @@ const pt = {
|
|
|
1240
1141
|
"voucher.monetaryTypeDesc": "Escolha qualquer quantia",
|
|
1241
1142
|
"voucher.eventType": "Vale de experiência",
|
|
1242
1143
|
"voucher.eventTypeDesc": "Para uma experiência específica",
|
|
1144
|
+
"voucher.categoryType": "Vale de categoria",
|
|
1145
|
+
"voucher.categoryTypeDesc": "Para qualquer experiência de uma categoria",
|
|
1146
|
+
"voucher.categoryVoucher": "Vale de categoria",
|
|
1147
|
+
"voucher.selectCategory": "Selecionar categoria",
|
|
1148
|
+
"voucher.categoryLabel": "Categoria",
|
|
1149
|
+
"voucher.selectCategoryPlaceholder": "Escolha uma categoria...",
|
|
1150
|
+
"voucher.categoryAmount": "Valor do vale",
|
|
1243
1151
|
"voucher.selectAmount": "Selecione o valor",
|
|
1244
1152
|
"voucher.customAmount": "Ou introduza um valor personalizado",
|
|
1245
1153
|
"voucher.amountRange": "O valor deve estar entre {{min}} e {{max}}",
|
|
@@ -1530,6 +1438,13 @@ const sv = {
|
|
|
1530
1438
|
"voucher.monetaryTypeDesc": "Välj valfritt belopp",
|
|
1531
1439
|
"voucher.eventType": "Upplevelsebevis",
|
|
1532
1440
|
"voucher.eventTypeDesc": "För en specifik upplevelse",
|
|
1441
|
+
"voucher.categoryType": "Kategoripresent",
|
|
1442
|
+
"voucher.categoryTypeDesc": "För alla upplevelser i en kategori",
|
|
1443
|
+
"voucher.categoryVoucher": "Kategoripresent",
|
|
1444
|
+
"voucher.selectCategory": "Välj kategori",
|
|
1445
|
+
"voucher.categoryLabel": "Kategori",
|
|
1446
|
+
"voucher.selectCategoryPlaceholder": "Välj en kategori...",
|
|
1447
|
+
"voucher.categoryAmount": "Presentkortsvärde",
|
|
1533
1448
|
"voucher.selectAmount": "Välj belopp",
|
|
1534
1449
|
"voucher.customAmount": "Eller ange eget belopp",
|
|
1535
1450
|
"voucher.amountRange": "Beloppet måste vara mellan {{min}} och {{max}}",
|
|
@@ -2262,6 +2177,7 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2262
2177
|
const categoryId = urlParams.get("categoryId") || urlParams.get("category");
|
|
2263
2178
|
const eventTypeId = urlParams.get("eventTypeId") || urlParams.get("type");
|
|
2264
2179
|
const eventTypeIds = urlParams.get("eventTypeIds") || urlParams.get("types");
|
|
2180
|
+
const partnerContractId = urlParams.get("partnerContractId") || urlParams.get("contractId");
|
|
2265
2181
|
const voucherValue = urlParams.get("voucherValue");
|
|
2266
2182
|
const voucherCategory = urlParams.get("voucherCategory");
|
|
2267
2183
|
const voucherEventType = urlParams.get("voucherEventType");
|
|
@@ -2274,11 +2190,14 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2274
2190
|
: ["0", "false", "no", "off"].includes(voucherCardIntegration.toLowerCase())
|
|
2275
2191
|
? false
|
|
2276
2192
|
: undefined;
|
|
2277
|
-
const omitSelectionKeys = ({ categoryId: _c, eventTypeId: _e, eventTypeIds: _es, eventInstanceId: _i, voucherValue: _vv, voucherCategory: _vc, voucherEventType: _ve, voucherImageId: _vi, ...rest }) => rest;
|
|
2193
|
+
const omitSelectionKeys = ({ categoryId: _c, eventTypeId: _e, eventTypeIds: _es, eventInstanceId: _i, voucherValue: _vv, voucherCategory: _vc, voucherEventType: _ve, voucherImageId: _vi, partnerContractId: _pc, ...rest }) => rest;
|
|
2278
2194
|
if (eventInstanceId && !Number.isNaN(Number(eventInstanceId))) {
|
|
2279
2195
|
return {
|
|
2280
2196
|
...omitSelectionKeys(baseConfig),
|
|
2281
2197
|
eventInstanceId: Number(eventInstanceId),
|
|
2198
|
+
...(partnerContractId
|
|
2199
|
+
? { partnerContractId }
|
|
2200
|
+
: {}),
|
|
2282
2201
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2283
2202
|
};
|
|
2284
2203
|
}
|
|
@@ -2286,6 +2205,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2286
2205
|
return {
|
|
2287
2206
|
...omitSelectionKeys(baseConfig),
|
|
2288
2207
|
categoryId: Number(categoryId),
|
|
2208
|
+
...(partnerContractId
|
|
2209
|
+
? { partnerContractId }
|
|
2210
|
+
: {}),
|
|
2289
2211
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2290
2212
|
};
|
|
2291
2213
|
}
|
|
@@ -2293,6 +2215,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2293
2215
|
return {
|
|
2294
2216
|
...omitSelectionKeys(baseConfig),
|
|
2295
2217
|
eventTypeId: Number(eventTypeId),
|
|
2218
|
+
...(partnerContractId
|
|
2219
|
+
? { partnerContractId }
|
|
2220
|
+
: {}),
|
|
2296
2221
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2297
2222
|
};
|
|
2298
2223
|
}
|
|
@@ -2305,6 +2230,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2305
2230
|
return {
|
|
2306
2231
|
...omitSelectionKeys(baseConfig),
|
|
2307
2232
|
eventTypeIds: typeIds,
|
|
2233
|
+
...(partnerContractId
|
|
2234
|
+
? { partnerContractId }
|
|
2235
|
+
: {}),
|
|
2308
2236
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2309
2237
|
};
|
|
2310
2238
|
}
|
|
@@ -2321,6 +2249,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2321
2249
|
if (parsedVoucherValues.length || parsedVoucherCategories.length || parsedVoucherEventTypes.length) {
|
|
2322
2250
|
return {
|
|
2323
2251
|
...baseConfig,
|
|
2252
|
+
...(partnerContractId
|
|
2253
|
+
? { partnerContractId }
|
|
2254
|
+
: {}),
|
|
2324
2255
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2325
2256
|
...(parsedVoucherValues.length > 0
|
|
2326
2257
|
? { voucherValue: parsedVoucherValues.length === 1 ? parsedVoucherValues[0] : parsedVoucherValues }
|
|
@@ -2338,6 +2269,9 @@ const inferConfigFromUrl = (baseConfig) => {
|
|
|
2338
2269
|
}
|
|
2339
2270
|
return {
|
|
2340
2271
|
...baseConfig,
|
|
2272
|
+
...(partnerContractId
|
|
2273
|
+
? { partnerContractId }
|
|
2274
|
+
: {}),
|
|
2341
2275
|
...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
|
|
2342
2276
|
};
|
|
2343
2277
|
};
|
|
@@ -6350,6 +6284,7 @@ function GiftCardOnlyBooking({ config, eventDetails, formData, discountCode, gif
|
|
|
6350
6284
|
customerPhone: formData.customerPhone?.trim(),
|
|
6351
6285
|
comment: formData.comment?.trim(),
|
|
6352
6286
|
paymentMethod: "gift_card",
|
|
6287
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
6353
6288
|
...(upsellSelections.length > 0 && { upsellSelections }),
|
|
6354
6289
|
};
|
|
6355
6290
|
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/create-gift-card-booking"), {
|
|
@@ -6480,7 +6415,7 @@ function loadMollieScript() {
|
|
|
6480
6415
|
document.head.appendChild(script);
|
|
6481
6416
|
});
|
|
6482
6417
|
}
|
|
6483
|
-
function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess: _onSuccess, onError, upsellSelections = [], mollieProfileId, mollieTestmode, }) {
|
|
6418
|
+
function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess: _onSuccess, onError, upsellSelections = [], mollieProfileId, mollieTestmode, freeformSelection, }) {
|
|
6484
6419
|
const t = useTranslations();
|
|
6485
6420
|
const { locale } = useLocale();
|
|
6486
6421
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
@@ -6494,7 +6429,9 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6494
6429
|
const cardFormRef = React.useRef(null);
|
|
6495
6430
|
const cardContainerRef = React.useRef(null);
|
|
6496
6431
|
const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
|
|
6497
|
-
const isFullyCoveredByGiftCards =
|
|
6432
|
+
const isFullyCoveredByGiftCards = freeformSelection
|
|
6433
|
+
? false
|
|
6434
|
+
: isGiftCardFullyCovered(giftCards, eventDetails?.price || 0, participantCount, discountCode?.discountAmount || 0);
|
|
6498
6435
|
const isCreditCard = selectedMethod === "creditcard";
|
|
6499
6436
|
React.useEffect(() => {
|
|
6500
6437
|
if (isFullyCoveredByGiftCards)
|
|
@@ -6604,6 +6541,9 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6604
6541
|
return (jsxRuntime.jsx(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: _onSuccess, onError: onError, upsellSelections: upsellSelections }));
|
|
6605
6542
|
}
|
|
6606
6543
|
const validateBeforePayment = () => {
|
|
6544
|
+
if (freeformSelection) {
|
|
6545
|
+
return null;
|
|
6546
|
+
}
|
|
6607
6547
|
const participantCount = formData.participants.filter((p) => p.name?.trim()).length;
|
|
6608
6548
|
const availableSpots = eventDetails.availableSpots || 0;
|
|
6609
6549
|
if (participantCount > availableSpots) {
|
|
@@ -6615,20 +6555,41 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6615
6555
|
return null;
|
|
6616
6556
|
};
|
|
6617
6557
|
const buildRequestData = (cardToken) => ({
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6558
|
+
...(freeformSelection
|
|
6559
|
+
? {
|
|
6560
|
+
eventTypeId: freeformSelection.eventTypeId,
|
|
6561
|
+
organizationId: config.organizationId,
|
|
6562
|
+
start: freeformSelection.start,
|
|
6563
|
+
end: freeformSelection.end,
|
|
6564
|
+
requestedUnits: freeformSelection.requestedUnits,
|
|
6565
|
+
amount: Math.round(totalAmount),
|
|
6566
|
+
currency: "EUR",
|
|
6567
|
+
customerName: freeformSelection.customerName.trim(),
|
|
6568
|
+
customerEmail: freeformSelection.customerEmail.trim(),
|
|
6569
|
+
customerPhone: freeformSelection.customerPhone?.trim(),
|
|
6570
|
+
comment: freeformSelection.comment?.trim(),
|
|
6571
|
+
locale,
|
|
6572
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
6573
|
+
...(cardToken && { cardToken }),
|
|
6574
|
+
...(selectedMethod && !cardToken && { method: selectedMethod }),
|
|
6575
|
+
}
|
|
6576
|
+
: {
|
|
6577
|
+
eventInstanceId: config.eventInstanceId || eventDetails.id,
|
|
6578
|
+
organizationId: config.organizationId,
|
|
6579
|
+
amount: Math.round(totalAmount),
|
|
6580
|
+
currency: "EUR",
|
|
6581
|
+
participants: formData.participants.filter((p) => p.name?.trim()),
|
|
6582
|
+
discountCode: discountCode?.code,
|
|
6583
|
+
giftCardCodes: giftCards?.map((gc) => gc.code) || [],
|
|
6584
|
+
customerName: formData.customerName?.trim(),
|
|
6585
|
+
customerEmail: formData.customerEmail?.trim(),
|
|
6586
|
+
customerPhone: formData.customerPhone?.trim(),
|
|
6587
|
+
comment: formData.comment?.trim(),
|
|
6588
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
6589
|
+
...(cardToken && { cardToken }),
|
|
6590
|
+
...(selectedMethod && !cardToken && { method: selectedMethod }),
|
|
6591
|
+
...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
|
|
6592
|
+
}),
|
|
6632
6593
|
});
|
|
6633
6594
|
const handleCardPayment = async () => {
|
|
6634
6595
|
if (!mollieRef.current)
|
|
@@ -6646,7 +6607,9 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6646
6607
|
setPaymentError(tokenError?.message || t("error.createPayment"));
|
|
6647
6608
|
return;
|
|
6648
6609
|
}
|
|
6649
|
-
const response = await fetch(getApiUrl(config.apiBaseUrl,
|
|
6610
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, freeformSelection
|
|
6611
|
+
? "/booking/create-freeform-mollie-payment"
|
|
6612
|
+
: "/booking/create-mollie-payment"), {
|
|
6650
6613
|
method: "POST",
|
|
6651
6614
|
headers: createApiHeaders(config, locale),
|
|
6652
6615
|
body: JSON.stringify(createRequestBody(config, buildRequestData(token))),
|
|
@@ -6682,7 +6645,9 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6682
6645
|
setPaymentError(validationError);
|
|
6683
6646
|
return;
|
|
6684
6647
|
}
|
|
6685
|
-
const response = await fetch(getApiUrl(config.apiBaseUrl,
|
|
6648
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, freeformSelection
|
|
6649
|
+
? "/booking/create-freeform-mollie-payment"
|
|
6650
|
+
: "/booking/create-mollie-payment"), {
|
|
6686
6651
|
method: "POST",
|
|
6687
6652
|
headers: createApiHeaders(config, locale),
|
|
6688
6653
|
body: JSON.stringify(createRequestBody(config, buildRequestData())),
|
|
@@ -6710,7 +6675,7 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6710
6675
|
? t("button.processingPayment")
|
|
6711
6676
|
: selectedMethodData
|
|
6712
6677
|
? t("payment.payWithMethod", { method: selectedMethodData.description })
|
|
6713
|
-
: totalAmount < eventDetails.price * formData.participants.filter((p) => p.name?.trim()).length
|
|
6678
|
+
: !freeformSelection && totalAmount < eventDetails.price * formData.participants.filter((p) => p.name?.trim()).length
|
|
6714
6679
|
? t("button.depositAndBook")
|
|
6715
6680
|
: t("button.bookNow");
|
|
6716
6681
|
const isPayDisabled = isLoading || !selectedMethod || (isCreditCard && !isMollieReady);
|
|
@@ -6852,68 +6817,252 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6852
6817
|
}, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner("var(--bw-surface-color)"), " ", buttonLabel] })) : (buttonLabel) }))] }));
|
|
6853
6818
|
}
|
|
6854
6819
|
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
if (participantCount === 0) {
|
|
6867
|
-
return t("payment.needOneParticipant");
|
|
6868
|
-
}
|
|
6869
|
-
return null;
|
|
6870
|
-
}, onSuccess: (paymentIntent) => onSuccess({
|
|
6871
|
-
paymentIntent,
|
|
6872
|
-
}), onError: onError, paymentElementOptions: {
|
|
6873
|
-
layout: "accordion",
|
|
6874
|
-
defaultValues: {
|
|
6875
|
-
billingDetails: {
|
|
6876
|
-
name: formData.customerName,
|
|
6877
|
-
email: formData.customerEmail,
|
|
6878
|
-
phone: formData.customerPhone,
|
|
6879
|
-
},
|
|
6880
|
-
},
|
|
6881
|
-
}, submitContent: jsxRuntime.jsx(jsxRuntime.Fragment, { children: submitLabel }), loadingContent: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner("var(--bw-surface-color)"), " ", t("button.processingPayment")] }) }));
|
|
6882
|
-
}
|
|
6883
|
-
function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripePromise, stripeAppearance, upsellSelections = [], }) {
|
|
6884
|
-
const t = useTranslations();
|
|
6885
|
-
const { locale } = useLocale();
|
|
6886
|
-
const [clientSecret, setClientSecret] = React.useState(null);
|
|
6887
|
-
const [paymentIntentId, setPaymentIntentId] = React.useState(null);
|
|
6888
|
-
const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = React.useState(false);
|
|
6889
|
-
const [paymentError, setPaymentError] = React.useState(null);
|
|
6890
|
-
const storageKey = typeof window !== "undefined"
|
|
6891
|
-
? `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
|
|
6892
|
-
: "";
|
|
6893
|
-
const PAYMENT_INTENT_TTL = 24 * 60 * 60 * 1000;
|
|
6894
|
-
function loadPersistedPaymentIntent() {
|
|
6895
|
-
if (typeof window === "undefined" || !storageKey)
|
|
6896
|
-
return null;
|
|
6897
|
-
try {
|
|
6898
|
-
const raw = window.localStorage.getItem(storageKey);
|
|
6899
|
-
if (!raw)
|
|
6900
|
-
return null;
|
|
6901
|
-
const parsed = JSON.parse(raw);
|
|
6902
|
-
if (!parsed?.id || !parsed?.ts)
|
|
6903
|
-
return null;
|
|
6904
|
-
if (Date.now() - parsed.ts > PAYMENT_INTENT_TTL) {
|
|
6905
|
-
window.localStorage.removeItem(storageKey);
|
|
6906
|
-
return null;
|
|
6907
|
-
}
|
|
6908
|
-
return parsed.id;
|
|
6909
|
-
}
|
|
6910
|
-
catch {
|
|
6911
|
-
return null;
|
|
6912
|
-
}
|
|
6820
|
+
var V3_URL = 'https://js.stripe.com/v3';
|
|
6821
|
+
var V3_URL_REGEX = /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;
|
|
6822
|
+
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';
|
|
6823
|
+
var findScript = function findScript() {
|
|
6824
|
+
var scripts = document.querySelectorAll("script[src^=\"".concat(V3_URL, "\"]"));
|
|
6825
|
+
|
|
6826
|
+
for (var i = 0; i < scripts.length; i++) {
|
|
6827
|
+
var script = scripts[i];
|
|
6828
|
+
|
|
6829
|
+
if (!V3_URL_REGEX.test(script.src)) {
|
|
6830
|
+
continue;
|
|
6913
6831
|
}
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6832
|
+
|
|
6833
|
+
return script;
|
|
6834
|
+
}
|
|
6835
|
+
|
|
6836
|
+
return null;
|
|
6837
|
+
};
|
|
6838
|
+
|
|
6839
|
+
var injectScript = function injectScript(params) {
|
|
6840
|
+
var queryString = '';
|
|
6841
|
+
var script = document.createElement('script');
|
|
6842
|
+
script.src = "".concat(V3_URL).concat(queryString);
|
|
6843
|
+
var headOrBody = document.head || document.body;
|
|
6844
|
+
|
|
6845
|
+
if (!headOrBody) {
|
|
6846
|
+
throw new Error('Expected document.body not to be null. Stripe.js requires a <body> element.');
|
|
6847
|
+
}
|
|
6848
|
+
|
|
6849
|
+
headOrBody.appendChild(script);
|
|
6850
|
+
return script;
|
|
6851
|
+
};
|
|
6852
|
+
|
|
6853
|
+
var registerWrapper = function registerWrapper(stripe, startTime) {
|
|
6854
|
+
if (!stripe || !stripe._registerWrapper) {
|
|
6855
|
+
return;
|
|
6856
|
+
}
|
|
6857
|
+
|
|
6858
|
+
stripe._registerWrapper({
|
|
6859
|
+
name: 'stripe-js',
|
|
6860
|
+
version: "4.6.0",
|
|
6861
|
+
startTime: startTime
|
|
6862
|
+
});
|
|
6863
|
+
};
|
|
6864
|
+
|
|
6865
|
+
var stripePromise = null;
|
|
6866
|
+
var onErrorListener = null;
|
|
6867
|
+
var onLoadListener = null;
|
|
6868
|
+
|
|
6869
|
+
var onError = function onError(reject) {
|
|
6870
|
+
return function () {
|
|
6871
|
+
reject(new Error('Failed to load Stripe.js'));
|
|
6872
|
+
};
|
|
6873
|
+
};
|
|
6874
|
+
|
|
6875
|
+
var onLoad = function onLoad(resolve, reject) {
|
|
6876
|
+
return function () {
|
|
6877
|
+
if (window.Stripe) {
|
|
6878
|
+
resolve(window.Stripe);
|
|
6879
|
+
} else {
|
|
6880
|
+
reject(new Error('Stripe.js not available'));
|
|
6881
|
+
}
|
|
6882
|
+
};
|
|
6883
|
+
};
|
|
6884
|
+
|
|
6885
|
+
var loadScript = function loadScript(params) {
|
|
6886
|
+
// Ensure that we only attempt to load Stripe.js at most once
|
|
6887
|
+
if (stripePromise !== null) {
|
|
6888
|
+
return stripePromise;
|
|
6889
|
+
}
|
|
6890
|
+
|
|
6891
|
+
stripePromise = new Promise(function (resolve, reject) {
|
|
6892
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
6893
|
+
// Resolve to null when imported server side. This makes the module
|
|
6894
|
+
// safe to import in an isomorphic code base.
|
|
6895
|
+
resolve(null);
|
|
6896
|
+
return;
|
|
6897
|
+
}
|
|
6898
|
+
|
|
6899
|
+
if (window.Stripe) {
|
|
6900
|
+
resolve(window.Stripe);
|
|
6901
|
+
return;
|
|
6902
|
+
}
|
|
6903
|
+
|
|
6904
|
+
try {
|
|
6905
|
+
var script = findScript();
|
|
6906
|
+
|
|
6907
|
+
if (script && params) ; else if (!script) {
|
|
6908
|
+
script = injectScript(params);
|
|
6909
|
+
} else if (script && onLoadListener !== null && onErrorListener !== null) {
|
|
6910
|
+
var _script$parentNode;
|
|
6911
|
+
|
|
6912
|
+
// remove event listeners
|
|
6913
|
+
script.removeEventListener('load', onLoadListener);
|
|
6914
|
+
script.removeEventListener('error', onErrorListener); // if script exists, but we are reloading due to an error,
|
|
6915
|
+
// reload script to trigger 'load' event
|
|
6916
|
+
|
|
6917
|
+
(_script$parentNode = script.parentNode) === null || _script$parentNode === void 0 ? void 0 : _script$parentNode.removeChild(script);
|
|
6918
|
+
script = injectScript(params);
|
|
6919
|
+
}
|
|
6920
|
+
|
|
6921
|
+
onLoadListener = onLoad(resolve, reject);
|
|
6922
|
+
onErrorListener = onError(reject);
|
|
6923
|
+
script.addEventListener('load', onLoadListener);
|
|
6924
|
+
script.addEventListener('error', onErrorListener);
|
|
6925
|
+
} catch (error) {
|
|
6926
|
+
reject(error);
|
|
6927
|
+
return;
|
|
6928
|
+
}
|
|
6929
|
+
}); // Resets stripePromise on error
|
|
6930
|
+
|
|
6931
|
+
return stripePromise["catch"](function (error) {
|
|
6932
|
+
stripePromise = null;
|
|
6933
|
+
return Promise.reject(error);
|
|
6934
|
+
});
|
|
6935
|
+
};
|
|
6936
|
+
var initStripe = function initStripe(maybeStripe, args, startTime) {
|
|
6937
|
+
if (maybeStripe === null) {
|
|
6938
|
+
return null;
|
|
6939
|
+
}
|
|
6940
|
+
|
|
6941
|
+
var stripe = maybeStripe.apply(undefined, args);
|
|
6942
|
+
registerWrapper(stripe, startTime);
|
|
6943
|
+
return stripe;
|
|
6944
|
+
}; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
6945
|
+
|
|
6946
|
+
var stripePromise$1;
|
|
6947
|
+
var loadCalled = false;
|
|
6948
|
+
|
|
6949
|
+
var getStripePromise = function getStripePromise() {
|
|
6950
|
+
if (stripePromise$1) {
|
|
6951
|
+
return stripePromise$1;
|
|
6952
|
+
}
|
|
6953
|
+
|
|
6954
|
+
stripePromise$1 = loadScript(null)["catch"](function (error) {
|
|
6955
|
+
// clear cache on error
|
|
6956
|
+
stripePromise$1 = null;
|
|
6957
|
+
return Promise.reject(error);
|
|
6958
|
+
});
|
|
6959
|
+
return stripePromise$1;
|
|
6960
|
+
}; // Execute our own script injection after a tick to give users time to do their
|
|
6961
|
+
// own script injection.
|
|
6962
|
+
|
|
6963
|
+
|
|
6964
|
+
Promise.resolve().then(function () {
|
|
6965
|
+
return getStripePromise();
|
|
6966
|
+
})["catch"](function (error) {
|
|
6967
|
+
if (!loadCalled) {
|
|
6968
|
+
console.warn(error);
|
|
6969
|
+
}
|
|
6970
|
+
});
|
|
6971
|
+
var loadStripe = function loadStripe() {
|
|
6972
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
6973
|
+
args[_key] = arguments[_key];
|
|
6974
|
+
}
|
|
6975
|
+
|
|
6976
|
+
loadCalled = true;
|
|
6977
|
+
var startTime = Date.now(); // if previous attempts are unsuccessful, will re-load script
|
|
6978
|
+
|
|
6979
|
+
return getStripePromise().then(function (maybeStripe) {
|
|
6980
|
+
return initStripe(maybeStripe, args, startTime);
|
|
6981
|
+
});
|
|
6982
|
+
};
|
|
6983
|
+
|
|
6984
|
+
// Inner component that uses the Stripe hooks
|
|
6985
|
+
function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onError, freeformSelection, }) {
|
|
6986
|
+
const t = useTranslations();
|
|
6987
|
+
const isFreeform = Boolean(freeformSelection);
|
|
6988
|
+
const participantCount = isFreeform
|
|
6989
|
+
? 1
|
|
6990
|
+
: formData.participants.filter((p) => p.name.trim()).length;
|
|
6991
|
+
const fullAmount = isFreeform ? totalAmount : eventDetails.price * participantCount;
|
|
6992
|
+
const submitLabel = !isFreeform && totalAmount < fullAmount ? t("button.depositAndBook") : t("button.bookNow");
|
|
6993
|
+
return (jsxRuntime.jsx(StripeElementsPaymentForm, { onValidate: () => {
|
|
6994
|
+
if (isFreeform) {
|
|
6995
|
+
return null;
|
|
6996
|
+
}
|
|
6997
|
+
const availableSpots = eventDetails.availableSpots || 0;
|
|
6998
|
+
if (participantCount > availableSpots) {
|
|
6999
|
+
return t("payment.tooManyParticipants", { count: participantCount, available: availableSpots });
|
|
7000
|
+
}
|
|
7001
|
+
if (participantCount === 0) {
|
|
7002
|
+
return t("payment.needOneParticipant");
|
|
7003
|
+
}
|
|
7004
|
+
return null;
|
|
7005
|
+
}, onSuccess: (paymentIntent) => onSuccess({
|
|
7006
|
+
paymentIntent,
|
|
7007
|
+
}), onError: onError, paymentElementOptions: {
|
|
7008
|
+
layout: "accordion",
|
|
7009
|
+
defaultValues: {
|
|
7010
|
+
billingDetails: {
|
|
7011
|
+
name: formData.customerName,
|
|
7012
|
+
email: formData.customerEmail,
|
|
7013
|
+
phone: formData.customerPhone,
|
|
7014
|
+
},
|
|
7015
|
+
},
|
|
7016
|
+
}, submitContent: jsxRuntime.jsx(jsxRuntime.Fragment, { children: submitLabel }), loadingContent: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner("var(--bw-surface-color)"), " ", t("button.processingPayment")] }) }));
|
|
7017
|
+
}
|
|
7018
|
+
function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripeAppearance, upsellSelections = [], freeformSelection, }) {
|
|
7019
|
+
const t = useTranslations();
|
|
7020
|
+
const { locale } = useLocale();
|
|
7021
|
+
const [clientSecret, setClientSecret] = React.useState(null);
|
|
7022
|
+
const [paymentIntentId, setPaymentIntentId] = React.useState(null);
|
|
7023
|
+
const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = React.useState(false);
|
|
7024
|
+
const [paymentError, setPaymentError] = React.useState(null);
|
|
7025
|
+
const stripePromise = React.useMemo(() => {
|
|
7026
|
+
if (!systemConfig?.stripePublishableKey) {
|
|
7027
|
+
return null;
|
|
7028
|
+
}
|
|
7029
|
+
const stripeOptions = {
|
|
7030
|
+
locale: locale,
|
|
7031
|
+
};
|
|
7032
|
+
if (systemConfig.connectedAccountId) {
|
|
7033
|
+
stripeOptions.stripeAccount = systemConfig.connectedAccountId;
|
|
7034
|
+
}
|
|
7035
|
+
return loadStripe(systemConfig.stripePublishableKey, stripeOptions);
|
|
7036
|
+
}, [systemConfig?.stripePublishableKey, systemConfig?.connectedAccountId, locale]);
|
|
7037
|
+
const storageKey = typeof window !== "undefined"
|
|
7038
|
+
? freeformSelection
|
|
7039
|
+
? `bw_pi_freeform_${config?.organizationId}_${freeformSelection.eventTypeId}_${freeformSelection.start}_${freeformSelection.end}_${freeformSelection.requestedUnits}`
|
|
7040
|
+
: `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
|
|
7041
|
+
: "";
|
|
7042
|
+
const PAYMENT_INTENT_TTL = 24 * 60 * 60 * 1000;
|
|
7043
|
+
function loadPersistedPaymentIntent() {
|
|
7044
|
+
if (typeof window === "undefined" || !storageKey)
|
|
7045
|
+
return null;
|
|
7046
|
+
try {
|
|
7047
|
+
const raw = window.localStorage.getItem(storageKey);
|
|
7048
|
+
if (!raw)
|
|
7049
|
+
return null;
|
|
7050
|
+
const parsed = JSON.parse(raw);
|
|
7051
|
+
if (!parsed?.id || !parsed?.ts)
|
|
7052
|
+
return null;
|
|
7053
|
+
if (Date.now() - parsed.ts > PAYMENT_INTENT_TTL) {
|
|
7054
|
+
window.localStorage.removeItem(storageKey);
|
|
7055
|
+
return null;
|
|
7056
|
+
}
|
|
7057
|
+
return parsed.id;
|
|
7058
|
+
}
|
|
7059
|
+
catch {
|
|
7060
|
+
return null;
|
|
7061
|
+
}
|
|
7062
|
+
}
|
|
7063
|
+
function persistPaymentIntent(id) {
|
|
7064
|
+
if (typeof window === "undefined" || !storageKey || !id)
|
|
7065
|
+
return;
|
|
6917
7066
|
try {
|
|
6918
7067
|
const payload = { id, ts: Date.now() };
|
|
6919
7068
|
window.localStorage.setItem(storageKey, JSON.stringify(payload));
|
|
@@ -6943,47 +7092,82 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
6943
7092
|
}, [paymentIntentId]);
|
|
6944
7093
|
React.useEffect(() => {
|
|
6945
7094
|
const createStripePayment = async () => {
|
|
6946
|
-
if (!systemConfig || !eventDetails
|
|
7095
|
+
if (!systemConfig || !eventDetails) {
|
|
7096
|
+
return;
|
|
7097
|
+
}
|
|
7098
|
+
if (freeformSelection) {
|
|
7099
|
+
if (!freeformSelection.customerEmail?.trim() ||
|
|
7100
|
+
!freeformSelection.customerName?.trim() ||
|
|
7101
|
+
!freeformSelection.start ||
|
|
7102
|
+
!freeformSelection.end) {
|
|
7103
|
+
return;
|
|
7104
|
+
}
|
|
7105
|
+
}
|
|
7106
|
+
else if (!formData.participants?.length) {
|
|
6947
7107
|
return;
|
|
6948
7108
|
}
|
|
6949
7109
|
const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
|
|
6950
|
-
if (
|
|
7110
|
+
if (!freeformSelection &&
|
|
7111
|
+
(participantCount === 0 || !formData.customerEmail?.trim() || !formData.customerName?.trim())) {
|
|
6951
7112
|
return;
|
|
6952
7113
|
}
|
|
6953
7114
|
setIsCreatingPaymentIntent(true);
|
|
6954
7115
|
setPaymentError(null);
|
|
6955
7116
|
try {
|
|
6956
|
-
const
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
7117
|
+
const endpoint = freeformSelection
|
|
7118
|
+
? "/booking/create-freeform-stripe-payment"
|
|
7119
|
+
: "/booking/create-stripe-payment";
|
|
7120
|
+
const requestData = freeformSelection
|
|
7121
|
+
? {
|
|
7122
|
+
eventTypeId: freeformSelection.eventTypeId,
|
|
7123
|
+
organizationId: config.organizationId,
|
|
7124
|
+
start: freeformSelection.start,
|
|
7125
|
+
end: freeformSelection.end,
|
|
7126
|
+
requestedUnits: freeformSelection.requestedUnits,
|
|
7127
|
+
amount: Math.round(totalAmount),
|
|
7128
|
+
currency: "eur",
|
|
7129
|
+
customerName: freeformSelection.customerName.trim(),
|
|
7130
|
+
customerEmail: freeformSelection.customerEmail.trim(),
|
|
7131
|
+
customerPhone: freeformSelection.customerPhone?.trim(),
|
|
7132
|
+
comment: freeformSelection.comment?.trim(),
|
|
7133
|
+
locale,
|
|
7134
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
7135
|
+
...(paymentIntentId && { paymentIntentId }),
|
|
7136
|
+
}
|
|
7137
|
+
: {
|
|
7138
|
+
eventInstanceId: config.eventInstanceId || eventDetails.id,
|
|
7139
|
+
organizationId: config.organizationId,
|
|
7140
|
+
amount: Math.round(totalAmount),
|
|
7141
|
+
currency: "eur",
|
|
7142
|
+
participants: formData.participants.filter((p) => p.name?.trim()),
|
|
7143
|
+
discountCode: discountCode?.code,
|
|
7144
|
+
giftCardCodes: giftCards?.map((gc) => gc.code) || [],
|
|
7145
|
+
customerName: formData.customerName?.trim(),
|
|
7146
|
+
customerEmail: formData.customerEmail?.trim(),
|
|
7147
|
+
customerPhone: formData.customerPhone?.trim(),
|
|
7148
|
+
comment: formData.comment?.trim(),
|
|
7149
|
+
...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
|
|
7150
|
+
...(paymentIntentId && { paymentIntentId }),
|
|
7151
|
+
...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
|
|
7152
|
+
};
|
|
6974
7153
|
if (!requestData.organizationId) {
|
|
6975
7154
|
throw new Error("Organization ID is required");
|
|
6976
7155
|
}
|
|
6977
|
-
if (
|
|
7156
|
+
if (requestData.amount === undefined || requestData.amount < 0) {
|
|
6978
7157
|
throw new Error("Valid amount is required");
|
|
6979
7158
|
}
|
|
6980
|
-
if (!requestData
|
|
7159
|
+
if (!freeformSelection && "eventInstanceId" in requestData && !requestData.eventInstanceId) {
|
|
7160
|
+
throw new Error("Event instance ID is required");
|
|
7161
|
+
}
|
|
7162
|
+
if (!freeformSelection &&
|
|
7163
|
+
"participants" in requestData &&
|
|
7164
|
+
(!requestData.participants || requestData.participants.length === 0)) {
|
|
6981
7165
|
throw new Error("At least one participant is required");
|
|
6982
7166
|
}
|
|
6983
7167
|
if (!requestData.customerEmail) {
|
|
6984
7168
|
throw new Error("Customer email is required");
|
|
6985
7169
|
}
|
|
6986
|
-
const response = await fetch(getApiUrl(config.apiBaseUrl,
|
|
7170
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, endpoint), {
|
|
6987
7171
|
method: "POST",
|
|
6988
7172
|
headers: createApiHeaders(config, locale),
|
|
6989
7173
|
body: JSON.stringify(createRequestBody(config, requestData)),
|
|
@@ -7028,13 +7212,17 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
7028
7212
|
giftCards,
|
|
7029
7213
|
config,
|
|
7030
7214
|
upsellSelections,
|
|
7215
|
+
freeformSelection,
|
|
7216
|
+
locale,
|
|
7031
7217
|
]);
|
|
7032
7218
|
const participantCount = formData.participants?.filter((p) => p.name?.trim()).length || 0;
|
|
7033
|
-
const isFullyCoveredByGiftCards =
|
|
7219
|
+
const isFullyCoveredByGiftCards = freeformSelection
|
|
7220
|
+
? false
|
|
7221
|
+
: isGiftCardFullyCovered(giftCards, eventDetails?.price || 0, participantCount, discountCode?.discountAmount || 0);
|
|
7034
7222
|
if (isFullyCoveredByGiftCards && totalAmount <= 0) {
|
|
7035
7223
|
return (jsxRuntime.jsx(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: onSuccess, onError: onError, upsellSelections: upsellSelections }));
|
|
7036
7224
|
}
|
|
7037
|
-
if (isCreatingPaymentIntent || !clientSecret) {
|
|
7225
|
+
if (isCreatingPaymentIntent || !clientSecret || !stripePromise) {
|
|
7038
7226
|
return (jsxRuntime.jsxs("div", { style: {
|
|
7039
7227
|
display: "flex",
|
|
7040
7228
|
alignItems: "center",
|
|
@@ -7069,7 +7257,50 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
|
|
|
7069
7257
|
setPaymentIntentId(null);
|
|
7070
7258
|
setClientSecret(null);
|
|
7071
7259
|
onSuccess(result);
|
|
7072
|
-
}, onError: onError }) }));
|
|
7260
|
+
}, onError: onError, ...(freeformSelection ? { freeformSelection } : {}) }) }));
|
|
7261
|
+
}
|
|
7262
|
+
|
|
7263
|
+
const HOLD_DURATION_SECONDS = 15 * 60;
|
|
7264
|
+
/**
|
|
7265
|
+
* Shows the remaining time on the 15-minute slot hold that the server places on
|
|
7266
|
+
* the pending order while the customer completes checkout. The timer is started
|
|
7267
|
+
* client-side when the payment step opens — matching the moment the pending
|
|
7268
|
+
* order (and its `holdExpiresAt`) is created server-side.
|
|
7269
|
+
*/
|
|
7270
|
+
function HoldCountdown({ onExpire }) {
|
|
7271
|
+
const t = useTranslations();
|
|
7272
|
+
const [remaining, setRemaining] = React.useState(HOLD_DURATION_SECONDS);
|
|
7273
|
+
React.useEffect(() => {
|
|
7274
|
+
const startedAt = Date.now();
|
|
7275
|
+
const interval = setInterval(() => {
|
|
7276
|
+
const elapsed = Math.floor((Date.now() - startedAt) / 1000);
|
|
7277
|
+
const left = Math.max(0, HOLD_DURATION_SECONDS - elapsed);
|
|
7278
|
+
setRemaining(left);
|
|
7279
|
+
if (left <= 0) {
|
|
7280
|
+
clearInterval(interval);
|
|
7281
|
+
onExpire?.();
|
|
7282
|
+
}
|
|
7283
|
+
}, 1000);
|
|
7284
|
+
return () => clearInterval(interval);
|
|
7285
|
+
}, [onExpire]);
|
|
7286
|
+
const minutes = Math.floor(remaining / 60);
|
|
7287
|
+
const seconds = remaining % 60;
|
|
7288
|
+
const formatted = `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
7289
|
+
const isLow = remaining <= 60;
|
|
7290
|
+
return (jsxRuntime.jsxs("div", { style: {
|
|
7291
|
+
display: "flex",
|
|
7292
|
+
alignItems: "center",
|
|
7293
|
+
gap: "8px",
|
|
7294
|
+
padding: "10px 14px",
|
|
7295
|
+
borderRadius: "var(--bw-border-radius)",
|
|
7296
|
+
border: `1px solid ${isLow ? "var(--bw-error-color)" : "var(--bw-border-color)"}`,
|
|
7297
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
7298
|
+
color: isLow ? "var(--bw-error-color)" : "var(--bw-text-color)",
|
|
7299
|
+
fontFamily: "var(--bw-font-family)",
|
|
7300
|
+
fontSize: "14px",
|
|
7301
|
+
}, children: [jsxRuntime.jsx(IconClock, {}), jsxRuntime.jsx("span", { children: remaining > 0
|
|
7302
|
+
? t("hold.reservedFor", { time: formatted })
|
|
7303
|
+
: t("hold.expired") })] }));
|
|
7073
7304
|
}
|
|
7074
7305
|
|
|
7075
7306
|
const FLAG_EMOJIS = {
|
|
@@ -11483,6 +11714,11 @@ const buttonStyles = {
|
|
|
11483
11714
|
color: "var(--bw-text-muted)",
|
|
11484
11715
|
border: "1px solid var(--bw-border-color)",
|
|
11485
11716
|
},
|
|
11717
|
+
// Size variants
|
|
11718
|
+
small: {
|
|
11719
|
+
padding: "8px 12px",
|
|
11720
|
+
fontSize: "13px",
|
|
11721
|
+
},
|
|
11486
11722
|
// Full width modifier
|
|
11487
11723
|
fullWidth: {
|
|
11488
11724
|
width: "100%",
|
|
@@ -11555,6 +11791,12 @@ const sectionStyles = {
|
|
|
11555
11791
|
// TEXT
|
|
11556
11792
|
// ============================================
|
|
11557
11793
|
const textStyles = {
|
|
11794
|
+
body: {
|
|
11795
|
+
fontSize: "14px",
|
|
11796
|
+
color: "var(--bw-text-color)",
|
|
11797
|
+
fontFamily: "var(--bw-font-family)",
|
|
11798
|
+
lineHeight: 1.5,
|
|
11799
|
+
},
|
|
11558
11800
|
muted: {
|
|
11559
11801
|
fontSize: "14px",
|
|
11560
11802
|
color: "var(--bw-text-muted)",
|
|
@@ -11624,7 +11866,7 @@ const sectionHeaderStyles$1 = sectionStyles.header;
|
|
|
11624
11866
|
const labelStyles$1 = formStyles.label;
|
|
11625
11867
|
const inputStyles$1 = formStyles.input;
|
|
11626
11868
|
const errorTextStyles$1 = formStyles.error;
|
|
11627
|
-
function BookingForm({ config, eventDetails,
|
|
11869
|
+
function BookingForm({ config, eventDetails, onSuccess, onError, isOpen, onClose, systemConfig, selectedUpsells = [], upsells = [], persistedState = null, onPersistedStateChange, }) {
|
|
11628
11870
|
const t$1 = useTranslations();
|
|
11629
11871
|
const { locale } = useLocale();
|
|
11630
11872
|
const timezone = useTimezone();
|
|
@@ -11824,7 +12066,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11824
12066
|
newTotal: appliedDiscountCode.newTotal,
|
|
11825
12067
|
}
|
|
11826
12068
|
: null;
|
|
11827
|
-
const hasPaymentProvider =
|
|
12069
|
+
const hasPaymentProvider = systemConfig?.paymentProvider === "stripe" || systemConfig?.paymentProvider === "mollie";
|
|
11828
12070
|
const handleVoucherValidated = React.useCallback((voucher, _error) => {
|
|
11829
12071
|
if (voucher) {
|
|
11830
12072
|
setAppliedVouchers((prev) => [...prev, voucher]);
|
|
@@ -12090,7 +12332,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
12090
12332
|
...inputStyles$1,
|
|
12091
12333
|
resize: "vertical",
|
|
12092
12334
|
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: {
|
|
12335
|
+
} })] })] })] })), 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
12336
|
color: "var(--bw-text-color)",
|
|
12095
12337
|
fontWeight: 500,
|
|
12096
12338
|
fontFamily: "var(--bw-font-family)",
|
|
@@ -12194,7 +12436,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
12194
12436
|
fontFamily: "var(--bw-font-family)",
|
|
12195
12437
|
marginTop: "8px",
|
|
12196
12438
|
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,
|
|
12439
|
+
}, 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
12440
|
}
|
|
12199
12441
|
|
|
12200
12442
|
/**
|
|
@@ -13275,20 +13517,22 @@ const sectionHeaderStyles = sectionStyles.header;
|
|
|
13275
13517
|
const labelStyles = formStyles.label;
|
|
13276
13518
|
const inputStyles = formStyles.input;
|
|
13277
13519
|
const errorTextStyles = formStyles.error;
|
|
13278
|
-
function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClose, onSuccess, onError, systemConfig, preselectedEventTypeId, isLoadingEventTypes = false, }) {
|
|
13520
|
+
function VoucherPurchaseForm({ config, voucherConfig, eventTypes, categories, isOpen, onClose, onSuccess, onError, systemConfig, preselectedEventTypeId, isLoadingEventTypes = false, }) {
|
|
13279
13521
|
const t = useTranslations();
|
|
13280
13522
|
const { locale } = useLocale();
|
|
13281
13523
|
const paymentProvider = systemConfig?.paymentProvider ?? "stripe";
|
|
13282
13524
|
// Form state
|
|
13283
|
-
// Show voucher type selection if both monetary and event vouchers are allowed
|
|
13284
|
-
// The event type selection will handle the case when no event types are available
|
|
13285
13525
|
const canUseMonetaryVoucherType = voucherConfig.allowMonetaryVouchers !== false;
|
|
13286
13526
|
const canUseEventVoucherType = voucherConfig.allowEventVouchers;
|
|
13287
|
-
const
|
|
13527
|
+
const canUseCategoryVoucherType = canUseEventVoucherType && categories.length > 0;
|
|
13528
|
+
const availableTypeCount = [canUseMonetaryVoucherType, canUseEventVoucherType, canUseCategoryVoucherType].filter(Boolean).length;
|
|
13529
|
+
const showVoucherTypeSelection = availableTypeCount > 1 && !isLoadingEventTypes;
|
|
13288
13530
|
const [voucherType, setVoucherType] = React.useState(canUseMonetaryVoucherType ? "monetary" : "event");
|
|
13289
13531
|
const [monetaryAmount, setMonetaryAmount] = React.useState(voucherConfig.monetaryPresets[2] || 10000);
|
|
13290
13532
|
const [selectedEventTypeId, setSelectedEventTypeId] = React.useState(null);
|
|
13291
13533
|
const [eventQuantity, setEventQuantity] = React.useState(1);
|
|
13534
|
+
const [selectedCategoryId, setSelectedCategoryId] = React.useState(null);
|
|
13535
|
+
const [categoryAmount, setCategoryAmount] = React.useState(10000);
|
|
13292
13536
|
const [recipientName, setRecipientName] = React.useState("");
|
|
13293
13537
|
const [message, setMessage] = React.useState("");
|
|
13294
13538
|
const [customerName, setCustomerName] = React.useState("");
|
|
@@ -13303,6 +13547,8 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13303
13547
|
const [stripeAppearance, setStripeAppearance] = React.useState(null);
|
|
13304
13548
|
// Initialize Stripe
|
|
13305
13549
|
React.useEffect(() => {
|
|
13550
|
+
if (!isOpen)
|
|
13551
|
+
return;
|
|
13306
13552
|
if (paymentProvider !== "stripe")
|
|
13307
13553
|
return;
|
|
13308
13554
|
if (systemConfig?.stripePublishableKey && !stripePromise) {
|
|
@@ -13314,7 +13560,7 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13314
13560
|
}
|
|
13315
13561
|
setStripePromise(loadStripe(systemConfig.stripePublishableKey, stripeOptions));
|
|
13316
13562
|
}
|
|
13317
|
-
}, [paymentProvider, systemConfig, locale, stripePromise]);
|
|
13563
|
+
}, [isOpen, paymentProvider, systemConfig, locale, stripePromise]);
|
|
13318
13564
|
// Initialize Stripe appearance
|
|
13319
13565
|
React.useEffect(() => {
|
|
13320
13566
|
const container = document.querySelector(".booking-widget-container");
|
|
@@ -13346,6 +13592,9 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13346
13592
|
if (voucherType === "monetary") {
|
|
13347
13593
|
return monetaryAmount;
|
|
13348
13594
|
}
|
|
13595
|
+
else if (voucherType === "category") {
|
|
13596
|
+
return categoryAmount;
|
|
13597
|
+
}
|
|
13349
13598
|
else {
|
|
13350
13599
|
const eventType = eventTypes.find((et) => et.id === selectedEventTypeId);
|
|
13351
13600
|
if (eventType) {
|
|
@@ -13353,7 +13602,7 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13353
13602
|
}
|
|
13354
13603
|
return 0;
|
|
13355
13604
|
}
|
|
13356
|
-
}, [voucherType, monetaryAmount, selectedEventTypeId, eventQuantity, eventTypes]);
|
|
13605
|
+
}, [voucherType, monetaryAmount, selectedEventTypeId, eventQuantity, eventTypes, categoryAmount]);
|
|
13357
13606
|
const selectedEventType = React.useMemo(() => {
|
|
13358
13607
|
return eventTypes.find((et) => et.id === selectedEventTypeId);
|
|
13359
13608
|
}, [eventTypes, selectedEventTypeId]);
|
|
@@ -13362,6 +13611,8 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13362
13611
|
voucherType,
|
|
13363
13612
|
selectedEventTypeId,
|
|
13364
13613
|
eventQuantity,
|
|
13614
|
+
selectedCategoryId,
|
|
13615
|
+
categoryAmount,
|
|
13365
13616
|
recipientName: recipientName.trim(),
|
|
13366
13617
|
message: message.trim(),
|
|
13367
13618
|
customerName: customerName.trim(),
|
|
@@ -13371,6 +13622,8 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13371
13622
|
voucherType,
|
|
13372
13623
|
selectedEventTypeId,
|
|
13373
13624
|
eventQuantity,
|
|
13625
|
+
selectedCategoryId,
|
|
13626
|
+
categoryAmount,
|
|
13374
13627
|
recipientName,
|
|
13375
13628
|
message,
|
|
13376
13629
|
customerName,
|
|
@@ -13389,24 +13642,37 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13389
13642
|
return false;
|
|
13390
13643
|
if (voucherType === "event" && !selectedEventTypeId)
|
|
13391
13644
|
return false;
|
|
13645
|
+
if (voucherType === "category" && !selectedCategoryId)
|
|
13646
|
+
return false;
|
|
13392
13647
|
return true;
|
|
13393
|
-
}, [customerName, customerEmail, acceptTerms, totalAmount,
|
|
13648
|
+
}, [customerName, customerEmail, acceptTerms, totalAmount, voucherType, selectedEventTypeId, selectedCategoryId]);
|
|
13394
13649
|
const defaultMonetaryAmount = React.useMemo(() => voucherConfig.monetaryPresets[2] || voucherConfig.monetaryPresets[0] || 1000, [voucherConfig.monetaryPresets]);
|
|
13395
13650
|
const handleVoucherTypeChange = React.useCallback((nextType) => {
|
|
13396
13651
|
setVoucherType(nextType);
|
|
13397
13652
|
setPaymentError(null);
|
|
13398
13653
|
if (nextType === "event") {
|
|
13399
|
-
// Keep voucher type mutually exclusive: entering event flow resets amount selection state.
|
|
13400
13654
|
setMonetaryAmount(defaultMonetaryAmount);
|
|
13655
|
+
setSelectedCategoryId(null);
|
|
13656
|
+
setCategoryAmount(10000);
|
|
13401
13657
|
if (eventTypes.length === 1) {
|
|
13402
13658
|
setSelectedEventTypeId(eventTypes[0]?.id ?? null);
|
|
13403
13659
|
}
|
|
13404
13660
|
return;
|
|
13405
13661
|
}
|
|
13406
|
-
|
|
13662
|
+
if (nextType === "category") {
|
|
13663
|
+
setMonetaryAmount(defaultMonetaryAmount);
|
|
13664
|
+
setSelectedEventTypeId(null);
|
|
13665
|
+
setEventQuantity(1);
|
|
13666
|
+
if (categories.length === 1) {
|
|
13667
|
+
setSelectedCategoryId(categories[0]?.id ?? null);
|
|
13668
|
+
}
|
|
13669
|
+
return;
|
|
13670
|
+
}
|
|
13407
13671
|
setSelectedEventTypeId(null);
|
|
13408
13672
|
setEventQuantity(1);
|
|
13409
|
-
|
|
13673
|
+
setSelectedCategoryId(null);
|
|
13674
|
+
setCategoryAmount(10000);
|
|
13675
|
+
}, [defaultMonetaryAmount, eventTypes, categories]);
|
|
13410
13676
|
React.useEffect(() => {
|
|
13411
13677
|
if (!canUseMonetaryVoucherType && voucherType === "monetary") {
|
|
13412
13678
|
setVoucherType("event");
|
|
@@ -13447,9 +13713,12 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13447
13713
|
organizationId: config.organizationId,
|
|
13448
13714
|
amount: totalAmount,
|
|
13449
13715
|
currency: "eur",
|
|
13450
|
-
voucherType,
|
|
13451
|
-
monetaryValue: voucherType === "monetary" ? totalAmount
|
|
13716
|
+
voucherType: voucherType === "category" ? "monetary" : voucherType,
|
|
13717
|
+
monetaryValue: voucherType === "monetary" ? totalAmount
|
|
13718
|
+
: voucherType === "category" ? categoryAmount
|
|
13719
|
+
: undefined,
|
|
13452
13720
|
eventTypeId: voucherType === "event" ? selectedEventTypeId : undefined,
|
|
13721
|
+
eventCategoryId: voucherType === "category" ? selectedCategoryId : undefined,
|
|
13453
13722
|
eventAmount: voucherType === "event" ? eventQuantity : undefined,
|
|
13454
13723
|
recipientName: recipientName.trim() || undefined,
|
|
13455
13724
|
message: message.trim() || undefined,
|
|
@@ -13640,7 +13909,30 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13640
13909
|
fontSize: "13px",
|
|
13641
13910
|
color: "var(--bw-text-muted)",
|
|
13642
13911
|
marginTop: "4px",
|
|
13643
|
-
}, children: t("voucher.eventTypeDesc") })] }))
|
|
13912
|
+
}, children: t("voucher.eventTypeDesc") })] })), canUseCategoryVoucherType && (jsxRuntime.jsxs("button", { type: "button", onClick: () => handleVoucherTypeChange("category"), style: {
|
|
13913
|
+
flex: 1,
|
|
13914
|
+
padding: "16px",
|
|
13915
|
+
borderRadius: "var(--bw-border-radius)",
|
|
13916
|
+
border: voucherType === "category"
|
|
13917
|
+
? "2px solid var(--bw-highlight-color)"
|
|
13918
|
+
: "1px solid var(--bw-border-color)",
|
|
13919
|
+
backgroundColor: voucherType === "category"
|
|
13920
|
+
? "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)"
|
|
13921
|
+
: "var(--bw-surface-color)",
|
|
13922
|
+
cursor: "pointer",
|
|
13923
|
+
fontFamily: "var(--bw-font-family)",
|
|
13924
|
+
transition: "all 0.2s ease",
|
|
13925
|
+
textAlign: "center",
|
|
13926
|
+
}, children: [jsxRuntime.jsx("div", { style: {
|
|
13927
|
+
fontWeight: 600,
|
|
13928
|
+
color: voucherType === "category"
|
|
13929
|
+
? "var(--bw-highlight-color)"
|
|
13930
|
+
: "var(--bw-text-color)",
|
|
13931
|
+
}, children: t("voucher.categoryType") }), jsxRuntime.jsx("div", { style: {
|
|
13932
|
+
fontSize: "13px",
|
|
13933
|
+
color: "var(--bw-text-muted)",
|
|
13934
|
+
marginTop: "4px",
|
|
13935
|
+
}, children: t("voucher.categoryTypeDesc") })] }))] })] })), voucherType === "monetary" && (jsxRuntime.jsxs("div", { style: cardStyles, children: [jsxRuntime.jsx("h2", { style: sectionHeaderStyles, children: t("voucher.selectAmount") }), jsxRuntime.jsx("div", { style: {
|
|
13644
13936
|
display: "grid",
|
|
13645
13937
|
gridTemplateColumns: "repeat(3, 1fr)",
|
|
13646
13938
|
gap: "8px",
|
|
@@ -13695,7 +13987,50 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13695
13987
|
fontSize: "18px",
|
|
13696
13988
|
color: "var(--bw-highlight-color)",
|
|
13697
13989
|
fontFamily: "var(--bw-font-family)",
|
|
13698
|
-
}, children: formatCurrency(totalAmount) })] }) }))] })), jsxRuntime.jsxs("div", { style: cardStyles, children: [jsxRuntime.jsx("h2", { style: sectionHeaderStyles, children: t("voucher.
|
|
13990
|
+
}, children: formatCurrency(totalAmount) })] }) }))] })), voucherType === "category" && (jsxRuntime.jsxs("div", { style: cardStyles, children: [jsxRuntime.jsx("h2", { style: sectionHeaderStyles, children: t("voucher.selectCategory") }), jsxRuntime.jsx("div", { style: { marginTop: "12px" }, children: jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { htmlFor: "category-select", style: labelStyles, children: t("voucher.categoryLabel") }), jsxRuntime.jsxs("select", { id: "category-select", value: selectedCategoryId || "", onChange: (e) => setSelectedCategoryId(e.target.value ? Number(e.target.value) : null), disabled: categories.length === 1, style: {
|
|
13991
|
+
...inputStyles,
|
|
13992
|
+
cursor: categories.length === 1 ? "not-allowed" : "pointer",
|
|
13993
|
+
}, children: [categories.length > 1 && (jsxRuntime.jsx("option", { value: "", children: t("voucher.selectCategoryPlaceholder") })), categories.map((cat) => (jsxRuntime.jsx("option", { value: cat.id, children: cat.name }, cat.id)))] })] }), selectedCategoryId && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { htmlFor: "category-amount", style: labelStyles, children: t("voucher.categoryAmount") }), jsxRuntime.jsx("div", { style: {
|
|
13994
|
+
display: "grid",
|
|
13995
|
+
gridTemplateColumns: "repeat(3, 1fr)",
|
|
13996
|
+
gap: "8px",
|
|
13997
|
+
marginTop: "4px",
|
|
13998
|
+
}, children: voucherConfig.monetaryPresets.map((preset) => (jsxRuntime.jsx("button", { type: "button", onClick: () => setCategoryAmount(preset), style: {
|
|
13999
|
+
padding: "10px",
|
|
14000
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14001
|
+
border: categoryAmount === preset
|
|
14002
|
+
? "2px solid var(--bw-highlight-color)"
|
|
14003
|
+
: "1px solid var(--bw-border-color)",
|
|
14004
|
+
backgroundColor: categoryAmount === preset
|
|
14005
|
+
? "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)"
|
|
14006
|
+
: "var(--bw-surface-color)",
|
|
14007
|
+
cursor: "pointer",
|
|
14008
|
+
fontFamily: "var(--bw-font-family)",
|
|
14009
|
+
fontWeight: 600,
|
|
14010
|
+
fontSize: "14px",
|
|
14011
|
+
color: categoryAmount === preset
|
|
14012
|
+
? "var(--bw-highlight-color)"
|
|
14013
|
+
: "var(--bw-text-color)",
|
|
14014
|
+
transition: "all 0.2s ease",
|
|
14015
|
+
}, children: formatCurrency(preset) }, preset))) })] }))] }) }), selectedCategoryId && categoryAmount > 0 && (jsxRuntime.jsx("div", { style: {
|
|
14016
|
+
marginTop: "16px",
|
|
14017
|
+
padding: "12px",
|
|
14018
|
+
backgroundColor: "rgba(var(--bw-highlight-color-rgb, 0, 177, 170), 0.1)",
|
|
14019
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14020
|
+
border: "1px solid var(--bw-highlight-color)",
|
|
14021
|
+
}, children: jsxRuntime.jsxs("div", { style: {
|
|
14022
|
+
display: "flex",
|
|
14023
|
+
justifyContent: "space-between",
|
|
14024
|
+
alignItems: "center",
|
|
14025
|
+
}, children: [jsxRuntime.jsx("span", { style: {
|
|
14026
|
+
color: "var(--bw-text-color)",
|
|
14027
|
+
fontFamily: "var(--bw-font-family)",
|
|
14028
|
+
}, children: categories.find((c) => c.id === selectedCategoryId)?.name }), jsxRuntime.jsx("span", { style: {
|
|
14029
|
+
fontWeight: 700,
|
|
14030
|
+
fontSize: "18px",
|
|
14031
|
+
color: "var(--bw-highlight-color)",
|
|
14032
|
+
fontFamily: "var(--bw-font-family)",
|
|
14033
|
+
}, children: formatCurrency(categoryAmount) })] }) }))] })), jsxRuntime.jsxs("div", { style: cardStyles, children: [jsxRuntime.jsx("h2", { style: sectionHeaderStyles, children: t("voucher.recipientSection") }), jsxRuntime.jsx("p", { style: {
|
|
13699
14034
|
fontSize: "13px",
|
|
13700
14035
|
color: "var(--bw-text-muted)",
|
|
13701
14036
|
fontFamily: "var(--bw-font-family)",
|
|
@@ -13728,7 +14063,9 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
|
|
|
13728
14063
|
fontFamily: "var(--bw-font-family)",
|
|
13729
14064
|
}, children: voucherType === "monetary"
|
|
13730
14065
|
? t("voucher.monetaryVoucher")
|
|
13731
|
-
:
|
|
14066
|
+
: voucherType === "category"
|
|
14067
|
+
? categories.find((c) => c.id === selectedCategoryId)?.name || t("voucher.categoryVoucher")
|
|
14068
|
+
: `${eventQuantity}x ${selectedEventType?.name || t("voucher.eventVoucher")}` }), recipientName && (jsxRuntime.jsxs("div", { style: {
|
|
13732
14069
|
fontSize: "13px",
|
|
13733
14070
|
color: "var(--bw-text-muted)",
|
|
13734
14071
|
fontFamily: "var(--bw-font-family)",
|
|
@@ -14004,7 +14341,7 @@ function VoucherAttachment({ onClick }) {
|
|
|
14004
14341
|
}, children: t("voucher.buyAsGift") })] }));
|
|
14005
14342
|
}
|
|
14006
14343
|
|
|
14007
|
-
function VoucherIntegration({ config, voucherConfig, eventTypes, systemConfig, isFormOpen, isLoadingConfig, preselectedEventTypeId, voucherPurchaseResult, isSuccess, showStandaloneCard, onCardClick, onFormClose, onSuccess, onError, onSuccessModalClose, }) {
|
|
14344
|
+
function VoucherIntegration({ config, voucherConfig, eventTypes, categories, systemConfig, isFormOpen, isLoadingConfig, preselectedEventTypeId, voucherPurchaseResult, isSuccess, showStandaloneCard, onCardClick, onFormClose, onSuccess, onError, onSuccessModalClose, }) {
|
|
14008
14345
|
if (!voucherConfig?.enabled) {
|
|
14009
14346
|
return null;
|
|
14010
14347
|
}
|
|
@@ -14015,7 +14352,7 @@ function VoucherIntegration({ config, voucherConfig, eventTypes, systemConfig, i
|
|
|
14015
14352
|
justifyContent: "center",
|
|
14016
14353
|
}, children: jsxRuntime.jsx(VoucherPurchaseCard, { config: voucherConfig, minEventPrice: eventTypes.length > 0
|
|
14017
14354
|
? Math.min(...eventTypes.map((et) => et.maxPrice))
|
|
14018
|
-
: undefined, fallbackImages: eventTypes.flatMap((eventType) => eventType.images || []), onClick: onCardClick, standalone: true }) }) })), jsxRuntime.jsx(VoucherPurchaseForm, { config: config, voucherConfig: voucherConfig, eventTypes: eventTypes, isOpen: isFormOpen, onClose: onFormClose, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, preselectedEventTypeId: preselectedEventTypeId, isLoadingEventTypes: isLoadingConfig }), isSuccess && voucherPurchaseResult && (jsxRuntime.jsx(VoucherSuccessModal, { isOpen: true, onClose: onSuccessModalClose, result: voucherPurchaseResult }))] }));
|
|
14355
|
+
: undefined, fallbackImages: eventTypes.flatMap((eventType) => eventType.images || []), onClick: onCardClick, standalone: true }) }) })), jsxRuntime.jsx(VoucherPurchaseForm, { config: config, voucherConfig: voucherConfig, eventTypes: eventTypes, categories: categories, isOpen: isFormOpen, onClose: onFormClose, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, preselectedEventTypeId: preselectedEventTypeId, isLoadingEventTypes: isLoadingConfig }), isSuccess && voucherPurchaseResult && (jsxRuntime.jsx(VoucherSuccessModal, { isOpen: true, onClose: onSuccessModalClose, result: voucherPurchaseResult }))] }));
|
|
14019
14356
|
}
|
|
14020
14357
|
|
|
14021
14358
|
// Helper function to preprocess markdown for underline support
|
|
@@ -14278,8 +14615,17 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
|
|
|
14278
14615
|
if (!item)
|
|
14279
14616
|
return jsxRuntime.jsx("div", { style: { height: "34px" } }, i);
|
|
14280
14617
|
const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
|
|
14281
|
-
|
|
14282
|
-
|
|
14618
|
+
const isRowBookable = item.bookingOpen && item.availableSpots > 0;
|
|
14619
|
+
return (jsxRuntime.jsxs("div", { onClick: (e) => {
|
|
14620
|
+
e.stopPropagation();
|
|
14621
|
+
if (isRowBookable) {
|
|
14622
|
+
onInstancePreview?.(item.id, eventType.id);
|
|
14623
|
+
}
|
|
14624
|
+
}, onMouseEnter: (e) => {
|
|
14625
|
+
if (onInstancePreview && isRowBookable) {
|
|
14626
|
+
e.currentTarget.style.backgroundColor = "var(--bw-highlight-subtle)";
|
|
14627
|
+
}
|
|
14628
|
+
}, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
|
|
14283
14629
|
height: "34px",
|
|
14284
14630
|
display: "grid",
|
|
14285
14631
|
gridTemplateColumns: "auto 1fr auto 20px",
|
|
@@ -14288,12 +14634,13 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
|
|
|
14288
14634
|
width: "100%",
|
|
14289
14635
|
fontSize: "13px",
|
|
14290
14636
|
color: "var(--bw-text-muted)",
|
|
14291
|
-
cursor: onInstancePreview ? "pointer" : "default",
|
|
14637
|
+
cursor: onInstancePreview && isRowBookable ? "pointer" : "default",
|
|
14292
14638
|
borderRadius: "4px",
|
|
14293
14639
|
padding: "0 2px",
|
|
14294
14640
|
transition: "background 0.15s",
|
|
14295
14641
|
boxSizing: "border-box",
|
|
14296
|
-
|
|
14642
|
+
opacity: isRowBookable ? 1 : 0.55,
|
|
14643
|
+
}, 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
14644
|
}) }), hasMoreDates && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
14298
14645
|
e.stopPropagation();
|
|
14299
14646
|
onEventTypeSelect(eventType);
|
|
@@ -14382,6 +14729,74 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
|
|
|
14382
14729
|
}) }) })), jsxRuntime.jsx(PaymentLogosStrip, {}), jsxRuntime.jsx(EventTypeDetailsDialog, { isOpen: detailsDialogOpen, onClose: handleCloseDetails, eventType: selectedEventTypeForDetails, onEventTypeSelect: onEventTypeSelect })] }));
|
|
14383
14730
|
}
|
|
14384
14731
|
|
|
14732
|
+
function WaitlistDialog({ apiBaseUrl, organizationId, eventInstanceId, eventName, onClose, }) {
|
|
14733
|
+
const t = useTranslations();
|
|
14734
|
+
const [name, setName] = React.useState("");
|
|
14735
|
+
const [email, setEmail] = React.useState("");
|
|
14736
|
+
const [phone, setPhone] = React.useState("");
|
|
14737
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
14738
|
+
const [error, setError] = React.useState(null);
|
|
14739
|
+
const [position, setPosition] = React.useState(null);
|
|
14740
|
+
const handleSubmit = async () => {
|
|
14741
|
+
setError(null);
|
|
14742
|
+
if (!name.trim() || !email.trim()) {
|
|
14743
|
+
setError(t("waitlist.requiredFields"));
|
|
14744
|
+
return;
|
|
14745
|
+
}
|
|
14746
|
+
setIsSubmitting(true);
|
|
14747
|
+
try {
|
|
14748
|
+
const response = await fetch(getApiUrl(apiBaseUrl, "/booking/waitlist"), {
|
|
14749
|
+
method: "POST",
|
|
14750
|
+
headers: { "Content-Type": "application/json" },
|
|
14751
|
+
body: JSON.stringify({
|
|
14752
|
+
organizationId,
|
|
14753
|
+
eventInstanceId,
|
|
14754
|
+
name: name.trim(),
|
|
14755
|
+
email: email.trim(),
|
|
14756
|
+
phone: phone.trim() || undefined,
|
|
14757
|
+
}),
|
|
14758
|
+
});
|
|
14759
|
+
if (!response.ok) {
|
|
14760
|
+
throw new Error("request_failed");
|
|
14761
|
+
}
|
|
14762
|
+
const data = (await response.json());
|
|
14763
|
+
setPosition(data.position ?? null);
|
|
14764
|
+
}
|
|
14765
|
+
catch {
|
|
14766
|
+
setError(t("waitlist.submitError"));
|
|
14767
|
+
}
|
|
14768
|
+
finally {
|
|
14769
|
+
setIsSubmitting(false);
|
|
14770
|
+
}
|
|
14771
|
+
};
|
|
14772
|
+
return (jsxRuntime.jsx("div", { style: {
|
|
14773
|
+
position: "fixed",
|
|
14774
|
+
inset: 0,
|
|
14775
|
+
zIndex: 1000,
|
|
14776
|
+
display: "flex",
|
|
14777
|
+
alignItems: "center",
|
|
14778
|
+
justifyContent: "center",
|
|
14779
|
+
backgroundColor: "rgba(15, 23, 42, 0.6)",
|
|
14780
|
+
padding: "16px",
|
|
14781
|
+
fontFamily: "var(--bw-font-family)",
|
|
14782
|
+
}, onClick: onClose, children: jsxRuntime.jsx("div", { style: {
|
|
14783
|
+
width: "100%",
|
|
14784
|
+
maxWidth: "420px",
|
|
14785
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
14786
|
+
border: "1px solid var(--bw-border-color)",
|
|
14787
|
+
borderRadius: "var(--bw-border-radius)",
|
|
14788
|
+
padding: "24px",
|
|
14789
|
+
}, onClick: (e) => e.stopPropagation(), children: position !== null ? (jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [jsxRuntime.jsx("h3", { style: {
|
|
14790
|
+
margin: "0 0 8px",
|
|
14791
|
+
color: "var(--bw-highlight-color)",
|
|
14792
|
+
fontSize: "18px",
|
|
14793
|
+
}, 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: {
|
|
14794
|
+
margin: "0 0 4px",
|
|
14795
|
+
color: "var(--bw-highlight-color)",
|
|
14796
|
+
fontSize: "18px",
|
|
14797
|
+
}, 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") })] })] })) }) }));
|
|
14798
|
+
}
|
|
14799
|
+
|
|
14385
14800
|
const getAllocationBadgeInfo = (availableSpots, maxParticipants, t) => {
|
|
14386
14801
|
const availabilityRatio = availableSpots / maxParticipants;
|
|
14387
14802
|
if (availabilityRatio >= 0.6)
|
|
@@ -14506,12 +14921,13 @@ const PriceDisplay = ({ price, yearPrices, t }) => {
|
|
|
14506
14921
|
boxShadow: displayInfo ? "0 2px 4px rgba(0, 0, 0, 0.2)" : "none",
|
|
14507
14922
|
}, children: formatCurrency(price) }));
|
|
14508
14923
|
};
|
|
14509
|
-
function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, }) {
|
|
14924
|
+
function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, apiBaseUrl, organizationId, }) {
|
|
14510
14925
|
const t = useTranslations();
|
|
14511
14926
|
const { locale } = useLocale();
|
|
14512
14927
|
const timezone = useTimezone();
|
|
14513
14928
|
const [selectedEventInstanceId, setSelectedEventInstanceId] = React.useState(null);
|
|
14514
14929
|
const [openGroups, setOpenGroups] = React.useState(new Set());
|
|
14930
|
+
const [waitlistEvent, setWaitlistEvent] = React.useState(null);
|
|
14515
14931
|
const getMonthPriceDisplayInfo = (minPrice) => {
|
|
14516
14932
|
return getPriceDisplayInfo(minPrice, yearPrices, t);
|
|
14517
14933
|
};
|
|
@@ -14672,116 +15088,127 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
|
|
|
14672
15088
|
fontFamily: "var(--bw-font-family)",
|
|
14673
15089
|
}, children: t("instances.noAvailable") }), jsxRuntime.jsx("p", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t("instances.noAvailableMessage") })] }) }) }));
|
|
14674
15090
|
}
|
|
14675
|
-
return (jsxRuntime.
|
|
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
|
-
|
|
14765
|
-
|
|
14766
|
-
|
|
14767
|
-
|
|
14768
|
-
|
|
14769
|
-
|
|
14770
|
-
|
|
14771
|
-
|
|
14772
|
-
|
|
14773
|
-
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
|
|
14777
|
-
|
|
14778
|
-
|
|
14779
|
-
|
|
14780
|
-
|
|
14781
|
-
|
|
14782
|
-
|
|
14783
|
-
|
|
14784
|
-
|
|
15091
|
+
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) => {
|
|
15092
|
+
const monthPriceDisplayInfo = getMonthPriceDisplayInfo(minPrice);
|
|
15093
|
+
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: {
|
|
15094
|
+
fontSize: "16px",
|
|
15095
|
+
fontWeight: 500,
|
|
15096
|
+
marginLeft: "auto",
|
|
15097
|
+
padding: "4px 8px",
|
|
15098
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
15099
|
+
backgroundColor: monthPriceDisplayInfo
|
|
15100
|
+
? monthPriceDisplayInfo.backgroundColor
|
|
15101
|
+
: "#14532d",
|
|
15102
|
+
color: monthPriceDisplayInfo
|
|
15103
|
+
? monthPriceDisplayInfo.textColor
|
|
15104
|
+
: "#4ade80",
|
|
15105
|
+
boxShadow: monthPriceDisplayInfo
|
|
15106
|
+
? "0 2px 4px rgba(0, 0, 0, 0.2)"
|
|
15107
|
+
: undefined,
|
|
15108
|
+
}, 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) => {
|
|
15109
|
+
const availableSpots = event.maxParticipants - event.participantCount;
|
|
15110
|
+
const isFullyBooked = availableSpots === 0;
|
|
15111
|
+
const startDate = new Date(event.startTime);
|
|
15112
|
+
const isPastEvent = today.toISOString() >= startDate.toISOString();
|
|
15113
|
+
const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
|
|
15114
|
+
const canWaitlist = isFullyBooked &&
|
|
15115
|
+
!isPastEvent &&
|
|
15116
|
+
!!apiBaseUrl &&
|
|
15117
|
+
!!organizationId;
|
|
15118
|
+
return (jsxRuntime.jsxs("div", { style: {
|
|
15119
|
+
position: "relative",
|
|
15120
|
+
border: "1px solid var(--bw-border-color)",
|
|
15121
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
15122
|
+
borderRadius: "var(--bw-border-radius)",
|
|
15123
|
+
padding: "16px 10px",
|
|
15124
|
+
transition: "all 0.2s ease",
|
|
15125
|
+
fontFamily: "var(--bw-font-family)",
|
|
15126
|
+
opacity: isDisabled && !canWaitlist ? 0.3 : 1,
|
|
15127
|
+
filter: isDisabled && !canWaitlist ? "grayscale(40%)" : "none",
|
|
15128
|
+
cursor: isDisabled
|
|
15129
|
+
? canWaitlist
|
|
15130
|
+
? "default"
|
|
15131
|
+
: "not-allowed"
|
|
15132
|
+
: "pointer",
|
|
15133
|
+
}, onClick: () => {
|
|
15134
|
+
if (!isDisabled) {
|
|
15135
|
+
handleEventInstanceSelect(event);
|
|
15136
|
+
}
|
|
15137
|
+
}, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (jsxRuntime.jsx("div", { style: {
|
|
15138
|
+
position: "absolute",
|
|
15139
|
+
inset: 0,
|
|
15140
|
+
backgroundColor: "rgba(15, 23, 42, 0.8)",
|
|
15141
|
+
borderRadius: "var(--bw-border-radius)",
|
|
15142
|
+
display: "flex",
|
|
15143
|
+
alignItems: "center",
|
|
15144
|
+
justifyContent: "center",
|
|
15145
|
+
}, children: jsxRuntime.jsx("div", { style: {
|
|
15146
|
+
width: "32px",
|
|
15147
|
+
height: "32px",
|
|
15148
|
+
color: "var(--bw-highlight-color)",
|
|
15149
|
+
opacity: 0.8,
|
|
15150
|
+
fontSize: "32px",
|
|
15151
|
+
}, 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: {
|
|
15152
|
+
display: "flex",
|
|
15153
|
+
justifyContent: "space-between",
|
|
15154
|
+
width: "100%",
|
|
15155
|
+
alignItems: "flex-start",
|
|
15156
|
+
gap: "12px",
|
|
15157
|
+
marginBottom: "4px",
|
|
15158
|
+
}, children: [jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "12px" }, children: [jsxRuntime.jsx("div", { style: {
|
|
15159
|
+
fontSize: "16px",
|
|
15160
|
+
transition: "all 0.2s ease",
|
|
15161
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
15162
|
+
borderTop: "4px solid var(--bw-border-color)",
|
|
15163
|
+
border: "1px solid var(--bw-border-color)",
|
|
15164
|
+
width: "40px",
|
|
15165
|
+
height: "40px",
|
|
15166
|
+
display: "flex",
|
|
15167
|
+
alignItems: "center",
|
|
15168
|
+
justifyContent: "center",
|
|
15169
|
+
fontWeight: 700,
|
|
15170
|
+
color: "var(--bw-text-color)",
|
|
15171
|
+
backgroundColor: "var(--bw-background-color)",
|
|
15172
|
+
}, children: startDate.getDate() }), jsxRuntime.jsxs("div", { style: {
|
|
15173
|
+
fontSize: "16px",
|
|
15174
|
+
color: "var(--bw-text-color)",
|
|
15175
|
+
display: "flex",
|
|
15176
|
+
flexDirection: "column",
|
|
15177
|
+
alignItems: "flex-start",
|
|
15178
|
+
justifyContent: "flex-start",
|
|
15179
|
+
lineHeight: 1.25,
|
|
15180
|
+
}, 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) !==
|
|
15181
|
+
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) ===
|
|
15182
|
+
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: {
|
|
15183
|
+
fontSize: "12px",
|
|
15184
|
+
fontWeight: 400,
|
|
15185
|
+
color: "var(--bw-text-muted)",
|
|
15186
|
+
marginLeft: "6px",
|
|
15187
|
+
backgroundColor: "rgba(0, 0, 0, 0.05)",
|
|
15188
|
+
whiteSpace: "nowrap",
|
|
15189
|
+
padding: "2px 6px",
|
|
15190
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
15191
|
+
}, children: [event.durationDays, " ", event.durationDays > 1 ? t("common.days") : t("common.day")] })] }), jsxRuntime.jsx("div", { style: {
|
|
15192
|
+
textAlign: "right",
|
|
15193
|
+
display: "flex",
|
|
15194
|
+
flexDirection: "column",
|
|
15195
|
+
alignItems: "flex-end",
|
|
15196
|
+
}, children: jsxRuntime.jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices, t: t }) })] }), event.name !== selectedEventType?.name && (jsxRuntime.jsx("h4", { style: {
|
|
15197
|
+
fontSize: "16px",
|
|
15198
|
+
fontWeight: 600,
|
|
15199
|
+
color: "var(--bw-text-color)",
|
|
15200
|
+
lineHeight: 1.25,
|
|
15201
|
+
margin: "0 0 2px 0",
|
|
15202
|
+
display: "flex",
|
|
15203
|
+
alignItems: "center",
|
|
15204
|
+
gap: "8px",
|
|
15205
|
+
maxWidth: "230px",
|
|
15206
|
+
}, children: event.name })), canWaitlist && (jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth, buttonStyles.small, { marginTop: "8px" }), onClick: (e) => {
|
|
15207
|
+
e.stopPropagation();
|
|
15208
|
+
setWaitlistEvent(event);
|
|
15209
|
+
}, children: t("waitlist.joinCta") }))] }, event.id));
|
|
15210
|
+
}) }) })] }, key));
|
|
15211
|
+
}) })] }), waitlistEvent && apiBaseUrl && organizationId && (jsxRuntime.jsx(WaitlistDialog, { apiBaseUrl: apiBaseUrl, organizationId: organizationId, eventInstanceId: waitlistEvent.id, eventName: waitlistEvent.name, onClose: () => setWaitlistEvent(null) }))] }));
|
|
14785
15212
|
}
|
|
14786
15213
|
|
|
14787
15214
|
function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText, showAllButton, isLoadingEventDetails = false, isLoadingShowAll = false, isLoading = false, count = 3, }) {
|
|
@@ -15193,6 +15620,312 @@ function SpecialsView({ specials, onEventSelect, isLoading = false, showSavingsA
|
|
|
15193
15620
|
}) })] }));
|
|
15194
15621
|
}
|
|
15195
15622
|
|
|
15623
|
+
function toMinutesForUnit(unit) {
|
|
15624
|
+
switch (unit.toLowerCase()) {
|
|
15625
|
+
case "minute":
|
|
15626
|
+
case "minutes":
|
|
15627
|
+
return 1;
|
|
15628
|
+
case "hour":
|
|
15629
|
+
case "hours":
|
|
15630
|
+
return 60;
|
|
15631
|
+
case "day":
|
|
15632
|
+
case "days":
|
|
15633
|
+
return 24 * 60;
|
|
15634
|
+
default:
|
|
15635
|
+
return 1;
|
|
15636
|
+
}
|
|
15637
|
+
}
|
|
15638
|
+
// --- Timezone helpers ------------------------------------------------------
|
|
15639
|
+
// The booking is evaluated server-side against the organization's local
|
|
15640
|
+
// operating hours. The customer's browser may be in a different timezone, so we
|
|
15641
|
+
// interpret the datetime-local field as a wall-clock time in the ORG timezone
|
|
15642
|
+
// (not the browser's) and convert to a UTC instant for the API.
|
|
15643
|
+
function getTzParts(date, timeZone) {
|
|
15644
|
+
const dtf = new Intl.DateTimeFormat("en-CA", {
|
|
15645
|
+
timeZone,
|
|
15646
|
+
hour12: false,
|
|
15647
|
+
year: "numeric",
|
|
15648
|
+
month: "2-digit",
|
|
15649
|
+
day: "2-digit",
|
|
15650
|
+
hour: "2-digit",
|
|
15651
|
+
minute: "2-digit",
|
|
15652
|
+
second: "2-digit",
|
|
15653
|
+
});
|
|
15654
|
+
const parts = {};
|
|
15655
|
+
for (const part of dtf.formatToParts(date)) {
|
|
15656
|
+
if (part.type !== "literal")
|
|
15657
|
+
parts[part.type] = part.value;
|
|
15658
|
+
}
|
|
15659
|
+
return parts;
|
|
15660
|
+
}
|
|
15661
|
+
function tzOffsetMs(utcMs, timeZone) {
|
|
15662
|
+
const p = getTzParts(new Date(utcMs), timeZone);
|
|
15663
|
+
const hour = p.hour === "24" ? "00" : p.hour;
|
|
15664
|
+
const asLocalUtc = Date.UTC(Number(p.year), Number(p.month) - 1, Number(p.day), Number(hour), Number(p.minute), Number(p.second));
|
|
15665
|
+
return asLocalUtc - utcMs;
|
|
15666
|
+
}
|
|
15667
|
+
/** Convert an org-tz wall-clock string ("YYYY-MM-DDTHH:mm") to a UTC instant. */
|
|
15668
|
+
function zonedWallTimeToUtc(wallTime, timeZone) {
|
|
15669
|
+
const [datePart, timePart] = wallTime.split("T");
|
|
15670
|
+
if (!datePart || !timePart)
|
|
15671
|
+
return new Date(NaN);
|
|
15672
|
+
const [y, mo, d] = datePart.split("-").map(Number);
|
|
15673
|
+
const [h, mi] = timePart.split(":").map(Number);
|
|
15674
|
+
const naiveUtc = Date.UTC(y, (mo ?? 1) - 1, d, h ?? 0, mi ?? 0);
|
|
15675
|
+
// Single-pass offset correction (good enough outside the ~1h DST fold).
|
|
15676
|
+
const offset = tzOffsetMs(naiveUtc, timeZone);
|
|
15677
|
+
return new Date(naiveUtc - offset);
|
|
15678
|
+
}
|
|
15679
|
+
/** Render a UTC instant as an org-tz datetime-local input value. */
|
|
15680
|
+
function utcToZonedInputValue(date, timeZone) {
|
|
15681
|
+
const p = getTzParts(date, timeZone);
|
|
15682
|
+
const hour = p.hour === "24" ? "00" : p.hour;
|
|
15683
|
+
return `${p.year}-${p.month}-${p.day}T${hour}:${p.minute}`;
|
|
15684
|
+
}
|
|
15685
|
+
/** Format a UTC instant for display in the org timezone. */
|
|
15686
|
+
function formatInTimeZone(date, timeZone, locale) {
|
|
15687
|
+
return new Intl.DateTimeFormat(locale, {
|
|
15688
|
+
timeZone,
|
|
15689
|
+
dateStyle: "medium",
|
|
15690
|
+
timeStyle: "short",
|
|
15691
|
+
}).format(date);
|
|
15692
|
+
}
|
|
15693
|
+
function calculateTieredPrice(durationUnits, config) {
|
|
15694
|
+
const tiers = [...(config.tiers ?? [])]
|
|
15695
|
+
.filter((tier) => tier.unitCount > 0 && tier.price >= 0)
|
|
15696
|
+
.sort((a, b) => b.unitCount - a.unitCount);
|
|
15697
|
+
if (tiers.length === 0) {
|
|
15698
|
+
return 0;
|
|
15699
|
+
}
|
|
15700
|
+
let total = 0;
|
|
15701
|
+
let remaining = durationUnits;
|
|
15702
|
+
for (const tier of tiers) {
|
|
15703
|
+
const count = Math.floor(remaining / tier.unitCount);
|
|
15704
|
+
if (count <= 0)
|
|
15705
|
+
continue;
|
|
15706
|
+
total += count * tier.price;
|
|
15707
|
+
remaining -= count * tier.unitCount;
|
|
15708
|
+
}
|
|
15709
|
+
if (remaining > 0) {
|
|
15710
|
+
if (typeof config.perUnitPrice === "number") {
|
|
15711
|
+
total += remaining * config.perUnitPrice;
|
|
15712
|
+
}
|
|
15713
|
+
else {
|
|
15714
|
+
total += tiers[tiers.length - 1]?.price ?? 0;
|
|
15715
|
+
}
|
|
15716
|
+
}
|
|
15717
|
+
return total;
|
|
15718
|
+
}
|
|
15719
|
+
function FreeformSelection({ config, eventType, systemConfig, isOpen, onClose, onSuccess, onError, }) {
|
|
15720
|
+
const t = useTranslations();
|
|
15721
|
+
const { locale } = useLocale();
|
|
15722
|
+
const freeformConfig = eventType.freeformConfig;
|
|
15723
|
+
const timeZone = eventType.organizationTimezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
15724
|
+
const isNamedResource = eventType.resourceKind === "named";
|
|
15725
|
+
const maxUnits = isNamedResource ? 1 : Math.max(1, eventType.resourceQuantity ?? 1);
|
|
15726
|
+
const now = React.useMemo(() => {
|
|
15727
|
+
const next = new Date();
|
|
15728
|
+
next.setMinutes(next.getMinutes() + 30, 0, 0);
|
|
15729
|
+
return next;
|
|
15730
|
+
}, []);
|
|
15731
|
+
const [startInput, setStartInput] = React.useState(utcToZonedInputValue(now, timeZone));
|
|
15732
|
+
const [duration, setDuration] = React.useState(Math.max(1, freeformConfig?.minDuration ?? 1));
|
|
15733
|
+
const [requestedUnits, setRequestedUnits] = React.useState(1);
|
|
15734
|
+
const [customerName, setCustomerName] = React.useState("");
|
|
15735
|
+
const [customerEmail, setCustomerEmail] = React.useState("");
|
|
15736
|
+
const [customerPhone, setCustomerPhone] = React.useState("");
|
|
15737
|
+
const [comment, setComment] = React.useState("");
|
|
15738
|
+
const [acceptTerms, setAcceptTerms] = React.useState(false);
|
|
15739
|
+
const [checkoutStep, setCheckoutStep] = React.useState("details");
|
|
15740
|
+
const [availability, setAvailability] = React.useState(null);
|
|
15741
|
+
const [availabilityError, setAvailabilityError] = React.useState(null);
|
|
15742
|
+
const [isCheckingAvailability, setIsCheckingAvailability] = React.useState(false);
|
|
15743
|
+
React.useEffect(() => {
|
|
15744
|
+
if (!isOpen)
|
|
15745
|
+
return;
|
|
15746
|
+
setCheckoutStep("details");
|
|
15747
|
+
}, [isOpen]);
|
|
15748
|
+
React.useEffect(() => {
|
|
15749
|
+
if (!freeformConfig)
|
|
15750
|
+
return;
|
|
15751
|
+
setDuration(Math.max(1, freeformConfig.minDuration));
|
|
15752
|
+
setRequestedUnits(1);
|
|
15753
|
+
setAvailability(null);
|
|
15754
|
+
setAvailabilityError(null);
|
|
15755
|
+
}, [freeformConfig?.resourceId]);
|
|
15756
|
+
const unitMinutes = toMinutesForUnit(freeformConfig?.unit ?? "hour");
|
|
15757
|
+
const startDate = React.useMemo(() => zonedWallTimeToUtc(startInput, timeZone), [startInput, timeZone]);
|
|
15758
|
+
const endDate = React.useMemo(() => new Date(startDate.getTime() + duration * unitMinutes * 60000), [duration, startDate, unitMinutes]);
|
|
15759
|
+
React.useEffect(() => {
|
|
15760
|
+
if (!isOpen || !freeformConfig)
|
|
15761
|
+
return;
|
|
15762
|
+
if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime()))
|
|
15763
|
+
return;
|
|
15764
|
+
const timer = setTimeout(async () => {
|
|
15765
|
+
setIsCheckingAvailability(true);
|
|
15766
|
+
setAvailabilityError(null);
|
|
15767
|
+
try {
|
|
15768
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/freeform-availability"), {
|
|
15769
|
+
method: "POST",
|
|
15770
|
+
headers: createApiHeaders(config, locale),
|
|
15771
|
+
body: JSON.stringify(createRequestBody(config, {
|
|
15772
|
+
eventTypeId: eventType.id,
|
|
15773
|
+
requestedUnits,
|
|
15774
|
+
start: startDate.toISOString(),
|
|
15775
|
+
end: endDate.toISOString(),
|
|
15776
|
+
})),
|
|
15777
|
+
});
|
|
15778
|
+
const data = await response.json();
|
|
15779
|
+
if (!response.ok) {
|
|
15780
|
+
setAvailability(null);
|
|
15781
|
+
setAvailabilityError(data.error || t("error.loadBookingData"));
|
|
15782
|
+
return;
|
|
15783
|
+
}
|
|
15784
|
+
setAvailability({
|
|
15785
|
+
available: Boolean(data.available),
|
|
15786
|
+
freeUnits: Number(data.freeUnits ?? 0),
|
|
15787
|
+
capacityUnits: Number(data.capacityUnits ?? 0),
|
|
15788
|
+
usedUnits: Number(data.usedUnits ?? 0),
|
|
15789
|
+
reservationUsedUnits: Number(data.reservationUsedUnits ?? 0),
|
|
15790
|
+
blockUsedUnits: Number(data.blockUsedUnits ?? 0),
|
|
15791
|
+
durationUnits: Number(data.durationUnits ?? 0),
|
|
15792
|
+
roundedDurationMinutes: Number(data.roundedDurationMinutes ?? 0),
|
|
15793
|
+
});
|
|
15794
|
+
}
|
|
15795
|
+
catch (_error) {
|
|
15796
|
+
setAvailability(null);
|
|
15797
|
+
setAvailabilityError(t("error.loadBookingData"));
|
|
15798
|
+
}
|
|
15799
|
+
finally {
|
|
15800
|
+
setIsCheckingAvailability(false);
|
|
15801
|
+
}
|
|
15802
|
+
}, 350);
|
|
15803
|
+
return () => clearTimeout(timer);
|
|
15804
|
+
}, [
|
|
15805
|
+
config,
|
|
15806
|
+
endDate,
|
|
15807
|
+
eventType.id,
|
|
15808
|
+
freeformConfig,
|
|
15809
|
+
isOpen,
|
|
15810
|
+
locale,
|
|
15811
|
+
requestedUnits,
|
|
15812
|
+
startDate,
|
|
15813
|
+
t,
|
|
15814
|
+
]);
|
|
15815
|
+
const billedDurationUnits = availability?.durationUnits ?? Math.max(1, Math.ceil(duration / Math.max(1, freeformConfig?.stepDuration ?? 1)));
|
|
15816
|
+
const basePrice = React.useMemo(() => {
|
|
15817
|
+
if (!freeformConfig)
|
|
15818
|
+
return 0;
|
|
15819
|
+
if (freeformConfig.pricingModel === "perUnit") {
|
|
15820
|
+
return (freeformConfig.perUnitPrice ?? 0) * billedDurationUnits;
|
|
15821
|
+
}
|
|
15822
|
+
return calculateTieredPrice(billedDurationUnits, freeformConfig);
|
|
15823
|
+
}, [billedDurationUnits, freeformConfig]);
|
|
15824
|
+
const totalPrice = Math.max(0, basePrice * requestedUnits);
|
|
15825
|
+
const canContinueToPayment = !!freeformConfig &&
|
|
15826
|
+
customerName.trim().length >= 2 &&
|
|
15827
|
+
customerEmail.trim().length > 3 &&
|
|
15828
|
+
acceptTerms &&
|
|
15829
|
+
!!availability?.available &&
|
|
15830
|
+
!isCheckingAvailability;
|
|
15831
|
+
const freeformSelection = React.useMemo(() => ({
|
|
15832
|
+
eventTypeId: eventType.id,
|
|
15833
|
+
start: startDate.toISOString(),
|
|
15834
|
+
end: endDate.toISOString(),
|
|
15835
|
+
requestedUnits,
|
|
15836
|
+
customerName: customerName.trim(),
|
|
15837
|
+
customerEmail: customerEmail.trim(),
|
|
15838
|
+
customerPhone: customerPhone.trim(),
|
|
15839
|
+
comment: comment.trim(),
|
|
15840
|
+
}), [
|
|
15841
|
+
comment,
|
|
15842
|
+
customerEmail,
|
|
15843
|
+
customerName,
|
|
15844
|
+
customerPhone,
|
|
15845
|
+
endDate,
|
|
15846
|
+
eventType.id,
|
|
15847
|
+
requestedUnits,
|
|
15848
|
+
startDate,
|
|
15849
|
+
]);
|
|
15850
|
+
const paymentFormData = React.useMemo(() => ({
|
|
15851
|
+
customerName: customerName.trim(),
|
|
15852
|
+
customerEmail: customerEmail.trim(),
|
|
15853
|
+
customerPhone: customerPhone.trim(),
|
|
15854
|
+
participants: [{ name: customerName.trim() || customerEmail.trim(), level: undefined }],
|
|
15855
|
+
comment: comment.trim(),
|
|
15856
|
+
}), [comment, customerEmail, customerName, customerPhone]);
|
|
15857
|
+
const eventDetailsForPayment = React.useMemo(() => ({
|
|
15858
|
+
id: eventType.id,
|
|
15859
|
+
name: eventType.name,
|
|
15860
|
+
startTime: startDate.toISOString(),
|
|
15861
|
+
endTime: endDate.toISOString(),
|
|
15862
|
+
price: basePrice,
|
|
15863
|
+
maxParticipants: availability?.capacityUnits ?? requestedUnits,
|
|
15864
|
+
participantCount: 0,
|
|
15865
|
+
availableSpots: availability?.freeUnits ?? requestedUnits,
|
|
15866
|
+
durationDays: 1,
|
|
15867
|
+
durationPerDay: availability?.roundedDurationMinutes ?? duration * unitMinutes,
|
|
15868
|
+
images: eventType.images ?? [],
|
|
15869
|
+
category: eventType.category,
|
|
15870
|
+
organization: {
|
|
15871
|
+
id: config.organizationId,
|
|
15872
|
+
name: "",
|
|
15873
|
+
},
|
|
15874
|
+
bookingOpen: availability?.available ?? false,
|
|
15875
|
+
}), [
|
|
15876
|
+
availability?.available,
|
|
15877
|
+
availability?.capacityUnits,
|
|
15878
|
+
availability?.freeUnits,
|
|
15879
|
+
availability?.roundedDurationMinutes,
|
|
15880
|
+
basePrice,
|
|
15881
|
+
config.organizationId,
|
|
15882
|
+
duration,
|
|
15883
|
+
endDate,
|
|
15884
|
+
eventType.category,
|
|
15885
|
+
eventType.id,
|
|
15886
|
+
eventType.images,
|
|
15887
|
+
eventType.name,
|
|
15888
|
+
requestedUnits,
|
|
15889
|
+
startDate,
|
|
15890
|
+
unitMinutes,
|
|
15891
|
+
]);
|
|
15892
|
+
const footer = (jsxRuntime.jsxs("div", { style: { width: "100%", display: "flex", gap: "12px" }, children: [jsxRuntime.jsx("button", { type: "button", onClick: () => {
|
|
15893
|
+
if (checkoutStep === "payment") {
|
|
15894
|
+
setCheckoutStep("details");
|
|
15895
|
+
}
|
|
15896
|
+
else {
|
|
15897
|
+
onClose();
|
|
15898
|
+
}
|
|
15899
|
+
}, style: {
|
|
15900
|
+
flex: 1,
|
|
15901
|
+
padding: "12px 16px",
|
|
15902
|
+
border: "1px solid var(--bw-border-color)",
|
|
15903
|
+
borderRadius: "var(--bw-border-radius)",
|
|
15904
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
15905
|
+
color: "var(--bw-text-color)",
|
|
15906
|
+
fontFamily: "var(--bw-font-family)",
|
|
15907
|
+
fontWeight: 600,
|
|
15908
|
+
cursor: "pointer",
|
|
15909
|
+
}, children: checkoutStep === "payment" ? t("button.backToDetails") : t("common.back") }), checkoutStep === "details" && (jsxRuntime.jsx("button", { type: "button", onClick: () => setCheckoutStep("payment"), disabled: !canContinueToPayment, style: {
|
|
15910
|
+
flex: 1,
|
|
15911
|
+
padding: "12px 16px",
|
|
15912
|
+
border: "none",
|
|
15913
|
+
borderRadius: "var(--bw-border-radius)",
|
|
15914
|
+
backgroundColor: "var(--bw-highlight-color)",
|
|
15915
|
+
color: "var(--bw-button-text-color, #ffffff)",
|
|
15916
|
+
fontFamily: "var(--bw-font-family)",
|
|
15917
|
+
fontWeight: 700,
|
|
15918
|
+
cursor: canContinueToPayment ? "pointer" : "not-allowed",
|
|
15919
|
+
opacity: canContinueToPayment ? 1 : 0.6,
|
|
15920
|
+
}, children: t("button.continueToPayment") }))] }));
|
|
15921
|
+
if (!freeformConfig) {
|
|
15922
|
+
return (jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: onClose, title: eventType.name, children: jsxRuntime.jsx("div", { style: { padding: "16px", color: "var(--bw-error-color)", fontFamily: "var(--bw-font-family)" }, children: t("booking.freeformMissingConfig") }) }));
|
|
15923
|
+
}
|
|
15924
|
+
return (jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: onClose, title: eventType.name, footer: footer, children: jsxRuntime.jsx("div", { style: { padding: "16px", display: "flex", flexDirection: "column", gap: "16px" }, children: checkoutStep === "details" ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { style: { border: "1px solid var(--bw-border-color)", borderRadius: "var(--bw-border-radius)", padding: "14px", backgroundColor: "var(--bw-surface-color)" }, children: [jsxRuntime.jsx("h3", { style: { margin: "0 0 12px 0", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-color)" }, children: t("booking.eventDetails") }), jsxRuntime.jsxs("div", { style: { display: "grid", gap: "10px" }, children: [jsxRuntime.jsxs("label", { style: { display: "grid", gap: "6px", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-muted)" }, children: [t("booking.date"), jsxRuntime.jsx("input", { type: "datetime-local", value: startInput, onChange: (event) => setStartInput(event.target.value), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } })] }), jsxRuntime.jsxs("label", { style: { display: "grid", gap: "6px", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-muted)" }, children: [t("booking.duration"), jsxRuntime.jsx("input", { type: "number", min: freeformConfig.minDuration, max: freeformConfig.maxDuration, step: freeformConfig.stepDuration, value: duration, onChange: (event) => setDuration(Number(event.target.value)), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } })] }), !isNamedResource && (jsxRuntime.jsxs("label", { style: { display: "grid", gap: "6px", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-muted)" }, children: [t("voucher.quantity"), jsxRuntime.jsx("input", { type: "number", min: 1, max: maxUnits, value: requestedUnits, onChange: (event) => setRequestedUnits(Math.min(maxUnits, Math.max(1, Number(event.target.value)))), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } })] }))] }), jsxRuntime.jsxs("div", { style: { marginTop: "12px", fontSize: "13px", color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: [formatInTimeZone(startDate, timeZone, locale), " - ", formatInTimeZone(endDate, timeZone, locale), eventType.organizationTimezone ? ` (${timeZone})` : ""] }), isCheckingAvailability && (jsxRuntime.jsx("div", { style: { marginTop: "8px", fontSize: "13px", color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t("common.loading") })), availabilityError && (jsxRuntime.jsx("div", { style: { marginTop: "8px", fontSize: "13px", color: "var(--bw-error-color)", fontFamily: "var(--bw-font-family)" }, children: availabilityError })), !availabilityError && availability && (jsxRuntime.jsx("div", { style: { marginTop: "8px", fontSize: "13px", color: availability.available ? "var(--bw-success-color)" : "var(--bw-error-color)", fontFamily: "var(--bw-font-family)" }, children: availability.available
|
|
15925
|
+
? t("booking.freeformUnitsAvailable", { count: availability.freeUnits })
|
|
15926
|
+
: t("booking.freeformNotAvailable") }))] }), jsxRuntime.jsxs("div", { style: { border: "1px solid var(--bw-border-color)", borderRadius: "var(--bw-border-radius)", padding: "14px", backgroundColor: "var(--bw-surface-color)" }, children: [jsxRuntime.jsx("h3", { style: { margin: "0 0 12px 0", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-color)" }, children: t("booking.contactInfo") }), jsxRuntime.jsxs("div", { style: { display: "grid", gap: "10px" }, children: [jsxRuntime.jsx("input", { type: "text", placeholder: t("booking.namePlaceholder"), value: customerName, onChange: (event) => setCustomerName(event.target.value), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } }), jsxRuntime.jsx("input", { type: "email", placeholder: t("booking.emailPlaceholder"), value: customerEmail, onChange: (event) => setCustomerEmail(event.target.value), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } }), jsxRuntime.jsx("input", { type: "tel", placeholder: t("booking.phonePlaceholder"), value: customerPhone, onChange: (event) => setCustomerPhone(event.target.value), style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)" } }), jsxRuntime.jsx("textarea", { placeholder: t("booking.commentPlaceholder"), value: comment, onChange: (event) => setComment(event.target.value), rows: 3, style: { padding: "10px", borderRadius: "var(--bw-border-radius-small)", border: "1px solid var(--bw-border-color)", backgroundColor: "var(--bw-background-color)", color: "var(--bw-text-color)", resize: "vertical" } }), jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: "8px", fontFamily: "var(--bw-font-family)", fontSize: "13px", color: "var(--bw-text-muted)" }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: acceptTerms, onChange: (event) => setAcceptTerms(event.target.checked) }), t("booking.acceptTerms"), " ", t("booking.terms")] })] })] }), jsxRuntime.jsxs("div", { style: { border: "1px solid var(--bw-border-color)", borderRadius: "var(--bw-border-radius)", padding: "14px", backgroundColor: "var(--bw-surface-color)" }, children: [jsxRuntime.jsx("h3", { style: { margin: "0 0 12px 0", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-color)" }, children: t("summary.title") }), jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "14px" }, children: [jsxRuntime.jsx("span", { children: t("booking.duration") }), jsxRuntime.jsx("span", { children: t("booking.steps", { count: billedDurationUnits }) })] }), jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "14px", marginTop: "6px" }, children: [jsxRuntime.jsx("span", { children: t("booking.price") }), jsxRuntime.jsxs("span", { children: [formatCurrency(basePrice), " x ", requestedUnits] })] }), jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", color: "var(--bw-text-color)", fontFamily: "var(--bw-font-family)", fontSize: "20px", fontWeight: 700, marginTop: "10px" }, children: [jsxRuntime.jsx("span", { children: t("summary.totalAmount") }), jsxRuntime.jsx("span", { children: formatCurrency(totalPrice) })] })] })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(HoldCountdown, {}), jsxRuntime.jsxs("div", { style: { border: "1px solid var(--bw-border-color)", borderRadius: "var(--bw-border-radius)", padding: "14px", backgroundColor: "var(--bw-surface-color)" }, children: [jsxRuntime.jsx("h3", { style: { margin: "0 0 12px 0", fontFamily: "var(--bw-font-family)", color: "var(--bw-text-color)" }, children: t("summary.payment") }), !systemConfig?.paymentProvider && (jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--bw-error-color)", fontFamily: "var(--bw-font-family)" }, children: t("booking.paymentUnavailable") })), systemConfig?.paymentProvider === "mollie" && (jsxRuntime.jsx(MolliePaymentForm, { config: config, eventDetails: eventDetailsForPayment, formData: paymentFormData, totalAmount: totalPrice, discountCode: null, giftCards: [], onSuccess: onSuccess, onError: onError, mollieProfileId: systemConfig?.mollieProfileId, mollieTestmode: systemConfig?.mollieTestmode, freeformSelection: freeformSelection })), systemConfig?.paymentProvider === "stripe" && (jsxRuntime.jsx(StripePaymentForm, { config: config, eventDetails: eventDetailsForPayment, formData: paymentFormData, totalAmount: totalPrice, discountCode: null, giftCards: [], onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, freeformSelection: freeformSelection }))] })] })) }) }));
|
|
15927
|
+
}
|
|
15928
|
+
|
|
15196
15929
|
const getThemeConfig = (theme = "generic") => {
|
|
15197
15930
|
switch (theme) {
|
|
15198
15931
|
case "christmas":
|
|
@@ -15675,6 +16408,7 @@ let buffer = [];
|
|
|
15675
16408
|
let flushTimer = null;
|
|
15676
16409
|
let currentApiBaseUrl = "";
|
|
15677
16410
|
let currentOrganizationId = "";
|
|
16411
|
+
let currentPartnerContractId = null;
|
|
15678
16412
|
const FLUSH_INTERVAL_MS = 3000;
|
|
15679
16413
|
function flush() {
|
|
15680
16414
|
if (buffer.length === 0)
|
|
@@ -15716,9 +16450,10 @@ function scheduleFlush() {
|
|
|
15716
16450
|
/**
|
|
15717
16451
|
* Initialise the analytics module. Must be called once before `trackEvent`.
|
|
15718
16452
|
*/
|
|
15719
|
-
function initAnalytics(apiBaseUrl, organizationId) {
|
|
16453
|
+
function initAnalytics(apiBaseUrl, organizationId, options) {
|
|
15720
16454
|
currentApiBaseUrl = apiBaseUrl;
|
|
15721
16455
|
currentOrganizationId = organizationId;
|
|
16456
|
+
currentPartnerContractId = options?.partnerContractId?.trim() || null;
|
|
15722
16457
|
if (typeof window !== "undefined") {
|
|
15723
16458
|
window.addEventListener("pagehide", flush);
|
|
15724
16459
|
window.addEventListener("visibilitychange", () => {
|
|
@@ -15735,9 +16470,12 @@ function trackEvent(event, properties = {}) {
|
|
|
15735
16470
|
return;
|
|
15736
16471
|
if (!currentOrganizationId)
|
|
15737
16472
|
return;
|
|
16473
|
+
const mergedProperties = currentPartnerContractId
|
|
16474
|
+
? { ...properties, partnerContractId: currentPartnerContractId }
|
|
16475
|
+
: properties;
|
|
15738
16476
|
buffer.push({
|
|
15739
16477
|
event,
|
|
15740
|
-
properties,
|
|
16478
|
+
properties: mergedProperties,
|
|
15741
16479
|
domain: window.location.hostname,
|
|
15742
16480
|
url: window.location.href,
|
|
15743
16481
|
referrer: document.referrer,
|
|
@@ -15813,7 +16551,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15813
16551
|
const [sidebarOpen, setSidebarOpen] = React.useState(false);
|
|
15814
16552
|
// Booking flow state
|
|
15815
16553
|
const [eventDetails, setEventDetails] = React.useState(null);
|
|
15816
|
-
const [stripePromise, setStripePromise] = React.useState(null);
|
|
15817
16554
|
// SEPARATED LOADING STATES
|
|
15818
16555
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
15819
16556
|
const [isLoadingEventInstances, setIsLoadingEventInstances] = React.useState(false);
|
|
@@ -15829,6 +16566,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15829
16566
|
const [shouldRenderInstanceSelection, setShouldRenderInstanceSelection] = React.useState(false);
|
|
15830
16567
|
const [shouldRenderUpsells, setShouldRenderUpsells] = React.useState(false);
|
|
15831
16568
|
const [shouldRenderBookingForm, setShouldRenderBookingForm] = React.useState(false);
|
|
16569
|
+
const [shouldRenderFreeformSelection, setShouldRenderFreeformSelection] = React.useState(false);
|
|
15832
16570
|
// Google Ads config (received from API, set once from the first API response)
|
|
15833
16571
|
const [googleAdsConfig, setGoogleAdsConfig] = React.useState(null);
|
|
15834
16572
|
const extractGoogleAdsConfig = (data) => {
|
|
@@ -15842,6 +16580,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15842
16580
|
// Voucher purchase state
|
|
15843
16581
|
const [voucherConfig, setVoucherConfig] = React.useState(null);
|
|
15844
16582
|
const [voucherEventTypes, setVoucherEventTypes] = React.useState([]);
|
|
16583
|
+
const [voucherCategories, setVoucherCategories] = React.useState([]);
|
|
15845
16584
|
const [isLoadingVoucherConfig, setIsLoadingVoucherConfig] = React.useState(false);
|
|
15846
16585
|
const [isVoucherFormOpen, setIsVoucherFormOpen] = React.useState(false);
|
|
15847
16586
|
const [voucherPurchaseResult, setVoucherPurchaseResult] = React.useState(null);
|
|
@@ -15921,6 +16660,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15921
16660
|
setVoucherConfig(mergedConfig);
|
|
15922
16661
|
extractGoogleAdsConfig(data);
|
|
15923
16662
|
setVoucherEventTypes(data.eventTypes || []);
|
|
16663
|
+
setVoucherCategories(data.categories || []);
|
|
15924
16664
|
// Set system config for payment processing
|
|
15925
16665
|
if (data.paymentProvider) {
|
|
15926
16666
|
setSystemConfig({
|
|
@@ -15948,7 +16688,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15948
16688
|
React.useEffect(() => {
|
|
15949
16689
|
if (!analyticsInitRef.current && config.organizationId) {
|
|
15950
16690
|
analyticsInitRef.current = true;
|
|
15951
|
-
initAnalytics(config.apiBaseUrl, config.organizationId
|
|
16691
|
+
initAnalytics(config.apiBaseUrl, config.organizationId, {
|
|
16692
|
+
...(config.partnerContractId ? { partnerContractId: config.partnerContractId } : {}),
|
|
16693
|
+
});
|
|
15952
16694
|
trackEvent("widget_loaded", {
|
|
15953
16695
|
viewMode,
|
|
15954
16696
|
eventTypeId: config.eventTypeId,
|
|
@@ -15957,7 +16699,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15957
16699
|
isStandaloneVoucherMode,
|
|
15958
16700
|
});
|
|
15959
16701
|
}
|
|
15960
|
-
}, [config.organizationId, config.apiBaseUrl]);
|
|
16702
|
+
}, [config.organizationId, config.apiBaseUrl, config.partnerContractId, viewMode, config.eventTypeId, config.categoryId, config.eventInstanceId, isStandaloneVoucherMode]);
|
|
15961
16703
|
// Fire widget pageview once when Google Ads config is received from API
|
|
15962
16704
|
const pageviewFiredRef = React.useRef(false);
|
|
15963
16705
|
React.useEffect(() => {
|
|
@@ -15971,6 +16713,23 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
15971
16713
|
const initializeWidget = async () => {
|
|
15972
16714
|
try {
|
|
15973
16715
|
setIsLoading(true);
|
|
16716
|
+
if (config.partnerContractId) {
|
|
16717
|
+
const validationResponse = await fetch(getApiUrl(config.apiBaseUrl, `/booking/validate-partner?organizationId=${encodeURIComponent(config.organizationId)}&contractId=${config.partnerContractId}`), {
|
|
16718
|
+
method: "GET",
|
|
16719
|
+
headers: createApiHeaders(config, locale),
|
|
16720
|
+
});
|
|
16721
|
+
if (!validationResponse.ok) {
|
|
16722
|
+
setError("Invalid partner configuration");
|
|
16723
|
+
config.onError?.("Invalid partner configuration");
|
|
16724
|
+
return;
|
|
16725
|
+
}
|
|
16726
|
+
const validationData = (await validationResponse.json());
|
|
16727
|
+
if (!validationData.valid) {
|
|
16728
|
+
setError("Invalid partner configuration");
|
|
16729
|
+
config.onError?.("Invalid partner configuration");
|
|
16730
|
+
return;
|
|
16731
|
+
}
|
|
16732
|
+
}
|
|
15974
16733
|
// Load voucher config in parallel if voucher mode is requested or there's event selection
|
|
15975
16734
|
if (isVoucherModeRequested || hasEventSelection) {
|
|
15976
16735
|
void loadVoucherConfig();
|
|
@@ -16106,11 +16865,18 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16106
16865
|
setVoucherPurchaseResult(voucherData.voucherResult);
|
|
16107
16866
|
setIsSuccess(true);
|
|
16108
16867
|
setSuccessPaymentId(null);
|
|
16868
|
+
return;
|
|
16109
16869
|
}
|
|
16110
16870
|
}
|
|
16111
16871
|
catch {
|
|
16112
|
-
//
|
|
16113
|
-
}
|
|
16872
|
+
// Fall back to booking success flow if voucher lookup fails.
|
|
16873
|
+
}
|
|
16874
|
+
// Not a voucher purchase: treat as a booking return and open the
|
|
16875
|
+
// booking confirmation, mirroring the Stripe redirect handler.
|
|
16876
|
+
trackEvent("booking_completed", { paymentIntentId: mollieReturn.molliePaymentId, source: "mollie_redirect" });
|
|
16877
|
+
setVoucherPurchaseResult(null);
|
|
16878
|
+
setSuccessPaymentId(mollieReturn.molliePaymentId);
|
|
16879
|
+
setIsSuccess(true);
|
|
16114
16880
|
};
|
|
16115
16881
|
if (!isLoading) {
|
|
16116
16882
|
void handleMollieVoucherReturn();
|
|
@@ -16299,27 +17065,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16299
17065
|
widgetPaymentMode: data.widgetPaymentMode ?? "all",
|
|
16300
17066
|
});
|
|
16301
17067
|
}
|
|
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
17068
|
if (!skipInstanceAutoSelectRef.current && data.eventInstances.length === 1) {
|
|
16324
17069
|
setSelectedEventInstance(data.eventInstances[0]);
|
|
16325
17070
|
setCurrentStep("booking");
|
|
@@ -16359,29 +17104,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16359
17104
|
roundPricesEnabled: data.roundPricesEnabled ?? true,
|
|
16360
17105
|
widgetPaymentMode: data.widgetPaymentMode ?? "all",
|
|
16361
17106
|
});
|
|
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
17107
|
}
|
|
16386
17108
|
else {
|
|
16387
17109
|
const errorMessage = data.error || t("error.loadEventDetails");
|
|
@@ -16446,6 +17168,18 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16446
17168
|
const handleEventTypeSelect = async (eventType) => {
|
|
16447
17169
|
trackEvent("event_type_selected", { eventTypeId: eventType.id, eventTypeName: eventType.name });
|
|
16448
17170
|
setSelectedEventType(eventType);
|
|
17171
|
+
if (eventType.bookingMode === "freeform") {
|
|
17172
|
+
setCurrentStep("freeform");
|
|
17173
|
+
setShouldRenderFreeformSelection(true);
|
|
17174
|
+
setIsLoadingEventInstances(true);
|
|
17175
|
+
try {
|
|
17176
|
+
await loadEventInstances(eventType.id);
|
|
17177
|
+
}
|
|
17178
|
+
finally {
|
|
17179
|
+
setIsLoadingEventInstances(false);
|
|
17180
|
+
}
|
|
17181
|
+
return;
|
|
17182
|
+
}
|
|
16449
17183
|
setCurrentStep("eventInstances");
|
|
16450
17184
|
setShouldRenderInstanceSelection(true);
|
|
16451
17185
|
setIsLoadingEventInstances(true);
|
|
@@ -16536,6 +17270,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16536
17270
|
setSuccessPaymentId(result.paymentIntent.id);
|
|
16537
17271
|
setSidebarOpen(false);
|
|
16538
17272
|
setShouldRenderBookingForm(false);
|
|
17273
|
+
setShouldRenderFreeformSelection(false);
|
|
16539
17274
|
setBookingPersistedState(null);
|
|
16540
17275
|
config.onSuccess?.(result);
|
|
16541
17276
|
};
|
|
@@ -16650,12 +17385,12 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16650
17385
|
startTime: cardPreviewItem.startTime,
|
|
16651
17386
|
endTime: cardPreviewItem.startTime,
|
|
16652
17387
|
price: cardPreviewItem.price,
|
|
16653
|
-
maxParticipants: cardPreviewItem.availableSpots
|
|
16654
|
-
participantCount:
|
|
17388
|
+
maxParticipants: Math.max(cardPreviewItem.availableSpots, 0),
|
|
17389
|
+
participantCount: 0,
|
|
16655
17390
|
availableSpots: cardPreviewItem.availableSpots,
|
|
16656
17391
|
durationDays: 1,
|
|
16657
17392
|
durationPerDay: 1,
|
|
16658
|
-
bookingOpen:
|
|
17393
|
+
bookingOpen: cardPreviewItem.bookingOpen && cardPreviewItem.availableSpots > 0,
|
|
16659
17394
|
}
|
|
16660
17395
|
: null;
|
|
16661
17396
|
if (eventInstance) {
|
|
@@ -16843,7 +17578,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16843
17578
|
}
|
|
16844
17579
|
// Main view based on view mode
|
|
16845
17580
|
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,
|
|
17581
|
+
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
17582
|
setIsSuccess(false);
|
|
16848
17583
|
setCurrentStep("eventTypes");
|
|
16849
17584
|
setShowingPreview(true);
|
|
@@ -16851,6 +17586,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16851
17586
|
setShouldRenderInstanceSelection(false);
|
|
16852
17587
|
setShouldRenderUpsells(false);
|
|
16853
17588
|
setShouldRenderBookingForm(false);
|
|
17589
|
+
setShouldRenderFreeformSelection(false);
|
|
16854
17590
|
setSelectedUpsells([]);
|
|
16855
17591
|
setUpsells([]);
|
|
16856
17592
|
setBookingPersistedState(null);
|
|
@@ -16864,7 +17600,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16864
17600
|
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16865
17601
|
}
|
|
16866
17602
|
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,
|
|
17603
|
+
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
17604
|
setIsSuccess(false);
|
|
16869
17605
|
setCurrentStep("eventTypes");
|
|
16870
17606
|
setShowingPreview(true);
|
|
@@ -16872,6 +17608,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16872
17608
|
setShouldRenderInstanceSelection(false);
|
|
16873
17609
|
setShouldRenderUpsells(false);
|
|
16874
17610
|
setShouldRenderBookingForm(false);
|
|
17611
|
+
setShouldRenderFreeformSelection(false);
|
|
16875
17612
|
setSelectedUpsells([]);
|
|
16876
17613
|
setUpsells([]);
|
|
16877
17614
|
setBookingPersistedState(null);
|
|
@@ -16884,13 +17621,14 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16884
17621
|
}, isOpen: currentStep === "eventInstances", onClose: () => {
|
|
16885
17622
|
setShowingPreview(true);
|
|
16886
17623
|
setCurrentStep("eventTypes");
|
|
16887
|
-
}, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
17624
|
+
}, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
16888
17625
|
setIsSuccess(false);
|
|
16889
17626
|
setCurrentStep("eventTypes");
|
|
16890
17627
|
setShowingPreview(true);
|
|
16891
17628
|
setSuccessPaymentId(null);
|
|
16892
17629
|
setShouldRenderInstanceSelection(false);
|
|
16893
17630
|
setShouldRenderBookingForm(false);
|
|
17631
|
+
setShouldRenderFreeformSelection(false);
|
|
16894
17632
|
const url = new URL(window.location.href);
|
|
16895
17633
|
url.searchParams.delete("payment_intent");
|
|
16896
17634
|
url.searchParams.delete("payment_intent_client_secret");
|
|
@@ -16934,13 +17672,23 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16934
17672
|
setCurrentStep("booking");
|
|
16935
17673
|
setShouldRenderBookingForm(true);
|
|
16936
17674
|
}
|
|
17675
|
+
else if (selectedEventType?.bookingMode === "freeform") {
|
|
17676
|
+
setCurrentStep("freeform");
|
|
17677
|
+
setShouldRenderFreeformSelection(true);
|
|
17678
|
+
}
|
|
16937
17679
|
else {
|
|
16938
17680
|
setCurrentStep("eventInstances");
|
|
16939
17681
|
setSidebarOpen(true);
|
|
16940
17682
|
setShouldRenderInstanceSelection(true);
|
|
16941
17683
|
}
|
|
16942
17684
|
}, children: [isButtonBusy && jsxRuntime.jsx(Spinner, { size: 20, borderColor: "var(--bw-button-text-color, #ffffff)" }), config.buttonText ||
|
|
16943
|
-
(isDirectInstanceMode
|
|
17685
|
+
(isDirectInstanceMode || selectedEventType?.bookingMode === "freeform"
|
|
17686
|
+
? t("button.bookNow")
|
|
17687
|
+
: 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 })), shouldRenderFreeformSelection && selectedEventType?.bookingMode === "freeform" && (jsxRuntime.jsx(FreeformSelection, { config: config, eventType: selectedEventType, isOpen: currentStep === "freeform", onClose: () => {
|
|
17688
|
+
setCurrentStep("eventTypes");
|
|
17689
|
+
setSidebarOpen(false);
|
|
17690
|
+
setShouldRenderFreeformSelection(false);
|
|
17691
|
+
}, onSuccess: handleBookingSuccess, onError: handleBookingError, systemConfig: systemConfig })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
16944
17692
|
setIsSuccess(false);
|
|
16945
17693
|
setCurrentStep("eventTypes");
|
|
16946
17694
|
setSidebarOpen(false);
|
|
@@ -16948,6 +17696,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16948
17696
|
setShouldRenderInstanceSelection(false);
|
|
16949
17697
|
setShouldRenderUpsells(false);
|
|
16950
17698
|
setShouldRenderBookingForm(false);
|
|
17699
|
+
setShouldRenderFreeformSelection(false);
|
|
16951
17700
|
setSelectedUpsells([]);
|
|
16952
17701
|
setUpsells([]);
|
|
16953
17702
|
const url = new URL(window.location.href);
|
|
@@ -16960,7 +17709,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
16960
17709
|
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
16961
17710
|
}
|
|
16962
17711
|
// Cards mode (default) - show event type selection with optional voucher card
|
|
16963
|
-
const cardsView = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasEventSelection && (jsxRuntime.jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, onInstancePreview: (instanceId, eventTypeId) => void handleUpcomingEventSelect(instanceId, eventTypeId), isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (jsxRuntime.jsx(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: false, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: null, voucherPurchaseResult: null, isSuccess: false, showStandaloneCard: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled), onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => { } })), isStandaloneVoucherMode && isLoading && !voucherConfig && (jsxRuntime.jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsxRuntime.jsx("div", { style: {
|
|
17712
|
+
const cardsView = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasEventSelection && (jsxRuntime.jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, onInstancePreview: (instanceId, eventTypeId) => void handleUpcomingEventSelect(instanceId, eventTypeId), isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (jsxRuntime.jsx(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, categories: voucherCategories, systemConfig: systemConfig, isFormOpen: false, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: null, voucherPurchaseResult: null, isSuccess: false, showStandaloneCard: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled), onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => { } })), isStandaloneVoucherMode && isLoading && !voucherConfig && (jsxRuntime.jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsxRuntime.jsx("div", { style: {
|
|
16964
17713
|
display: "inline-block",
|
|
16965
17714
|
width: "32px",
|
|
16966
17715
|
height: "32px",
|
|
@@ -17001,13 +17750,17 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
17001
17750
|
};
|
|
17002
17751
|
};
|
|
17003
17752
|
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,
|
|
17753
|
+
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 })), shouldRenderFreeformSelection && selectedEventType?.bookingMode === "freeform" && (jsxRuntime.jsx(FreeformSelection, { config: config, eventType: selectedEventType, isOpen: currentStep === "freeform", onClose: () => {
|
|
17754
|
+
setCurrentStep("eventTypes");
|
|
17755
|
+
setShouldRenderFreeformSelection(false);
|
|
17756
|
+
}, onSuccess: handleBookingSuccess, onError: handleBookingError, systemConfig: systemConfig })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
|
|
17005
17757
|
setIsSuccess(false);
|
|
17006
17758
|
setCurrentStep("eventTypes");
|
|
17007
17759
|
setSuccessPaymentId(null);
|
|
17008
17760
|
setShouldRenderInstanceSelection(false);
|
|
17009
17761
|
setShouldRenderUpsells(false);
|
|
17010
17762
|
setShouldRenderBookingForm(false);
|
|
17763
|
+
setShouldRenderFreeformSelection(false);
|
|
17011
17764
|
setSelectedUpsells([]);
|
|
17012
17765
|
setUpsells([]);
|
|
17013
17766
|
const url = new URL(window.location.href);
|
|
@@ -17017,7 +17770,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
17017
17770
|
url.searchParams.delete("mollie_payment_id");
|
|
17018
17771
|
url.searchParams.delete("mollie_status");
|
|
17019
17772
|
window.history.replaceState({}, "", url.toString());
|
|
17020
|
-
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId }), jsxRuntime.jsx(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: isVoucherFormOpen, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, voucherPurchaseResult: voucherPurchaseResult, isSuccess: isSuccess, showStandaloneCard: false, onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => {
|
|
17773
|
+
}, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId }), jsxRuntime.jsx(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, categories: voucherCategories, systemConfig: systemConfig, isFormOpen: isVoucherFormOpen, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, voucherPurchaseResult: voucherPurchaseResult, isSuccess: isSuccess, showStandaloneCard: false, onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => {
|
|
17021
17774
|
setIsSuccess(false);
|
|
17022
17775
|
setVoucherPurchaseResult(null);
|
|
17023
17776
|
const url = new URL(window.location.href);
|
|
@@ -17065,7 +17818,7 @@ function styleInject(css, ref) {
|
|
|
17065
17818
|
}
|
|
17066
17819
|
}
|
|
17067
17820
|
|
|
17068
|
-
var css_248z = ".booking-widget-container{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;color:var(--bw-text-color,#1e293b);direction:ltr;display:block;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;position:relative;text-align:left}.booking-widget-container *,.booking-widget-container :after,.booking-widget-container :before{box-sizing:border-box;margin:0;padding:0}.booking-widget-container input,.booking-widget-container select,.booking-widget-container textarea{font-family:inherit;font-size:inherit;line-height:inherit}.booking-widget-container button{background:none;border:none;cursor:pointer;font-family:inherit;font-size:inherit}.booking-widget-container a{color:inherit;text-decoration:none}.booking-widget-container img{display:block;height:auto;max-width:100%;vertical-align:middle}.booking-widget-container ol,.booking-widget-container ul{list-style:none}.booking-widget-container h1,.booking-widget-container h2,.booking-widget-container h3,.booking-widget-container h4,.booking-widget-container h5,.booking-widget-container h6{font-size:inherit;font-weight:inherit}#booking-widget-portal{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);direction:ltr;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;text-align:left}#booking-widget-portal *,#booking-widget-portal :after,#booking-widget-portal :before{box-sizing:border-box}#booking-widget-portal-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);line-height:1.5}:root{--bw-highlight-color:#00b1aa;--bw-highlight-color-rgb:0,177,170;--bw-background-color:#f8fdfe;--bw-surface-color:#fff;--bw-text-color:#0e7490;--bw-text-muted:rgba(14,116,144,.7);--bw-border-color:#bae6fd;--bw-success-color:#38bdf8;--bw-warning-color:#fbbf24;--bw-error-color:#f43f5e;--bw-border-radius:18px;--bw-border-radius-small:calc(var(--bw-border-radius)*0.8);--bw-spacing:16px;--bw-spacing-large:24px;--bw-font-family:\"Inter\",system-ui,sans-serif;--bw-font-size:14px;--bw-font-size-large:18px;--bw-font-size-small:12px;--bw-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--bw-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--bw-highlight-muted:rgba(0,177,170,.1);--bw-highlight-subtle:rgba(0,177,170,.05);--bw-text-subtle:rgba(14,116,144,.4)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes shimmer{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes slide-in-right{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes slide-out-right{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(100%)}}@keyframes slide-in-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.animate-spin{animation:spin 1s linear infinite}.animate-shimmer{animation:shimmer 2s infinite}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-fade-in{animation:fade-in .2s ease-out}.animate-slide-in-up{animation:slide-in-up .3s ease-out}.animate-scale-in{animation:scale-in .2s ease-out}.skeleton-shimmer{overflow:hidden;position:relative}.skeleton-shimmer:after{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.3),transparent);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.bw-btn{letter-spacing:var(--bw-letter-spacing,normal);text-transform:var(--bw-text-transform,none);transition:all .2s ease!important}.bw-btn:hover:not(:disabled):not([disabled]){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-btn:active:not(:disabled):not([disabled]){box-shadow:0 2px 4px rgba(0,0,0,.1)}.bw-btn-primary:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);filter:brightness(1.1)}.bw-btn-secondary:hover:not(:disabled):not([disabled]){background-color:var(--bw-surface-color);border-color:var(--bw-highlight-color);color:var(--bw-highlight-color)}.bw-btn-ghost:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-muted)}.bw-btn-outline:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);color:var(--bw-button-text-color,#fff)}.bw-btn:disabled,.bw-btn[disabled]{cursor:not-allowed!important;opacity:.5!important}button[class*=bw-btn],button[style*=transition]{transition:all .2s ease!important}button[data-variant=primary]:hover:not(:disabled),button[style*=\"--bw-highlight-color\"]:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);filter:brightness(1.1);transform:translateY(-1px)}button[data-variant=secondary]:hover:not(:disabled){border-color:var(--bw-highlight-color)!important;color:var(--bw-highlight-color)!important}button[data-variant=ghost]:hover:not(:disabled){background-color:var(--bw-highlight-muted)!important}button[data-variant=outline]:hover:not(:disabled){background-color:var(--bw-highlight-color)!important;color:var(--bw-button-text-color,#fff)!important}.bw-button-hover:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-button-hover:active:not(:disabled){box-shadow:0 2px 4px rgba(0,0,0,.1);transform:translateY(0)}@media (max-width:768px){.sidebar-mobile{border-radius:0!important;max-width:100%!important;width:100%!important}}@media (max-width:600px){.event-type-list{gap:12px!important;padding:8px!important}.event-type-card{flex:1 1 100%!important;max-width:100%!important;padding:0!important}.event-type-img{height:160px!important}.event-type-title{font-size:1.1rem!important}.event-type-desc{font-size:.8rem!important;max-height:100px!important;min-height:100px!important}.event-type-content{padding:16px 24px!important}}.event-type-markdown{overflow:visible!important}.event-type-markdown p{color:var(--bw-text-muted);font-family:var(--bw-font-family);line-height:1.6;margin:0 0 8px}.event-type-markdown p:last-child{margin-bottom:0}.event-type-markdown h2{font-size:18px!important;font-weight:700!important;margin:12px 0 6px!important}.event-type-markdown h2,.event-type-markdown h3{color:var(--bw-text-color)!important;line-height:1.3!important}.event-type-markdown h3{font-size:16px!important;font-weight:600!important;margin:10px 0 4px!important}.event-type-markdown strong{color:var(--bw-text-color);font-weight:600}.event-type-markdown em{font-style:italic}.event-type-markdown u{text-decoration:underline}.event-type-markdown ul{list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ul li{color:var(--bw-text-muted)!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ul li:before{color:var(--bw-text-color)!important;content:\"•\"!important;font-weight:700!important;left:-16px!important;position:absolute!important;top:0!important}.event-type-markdown ol{counter-reset:list-counter!important;list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ol li{color:var(--bw-text-muted)!important;counter-increment:list-counter!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ol li:before{color:var(--bw-text-color)!important;content:counter(list-counter) \".\"!important;font-weight:700!important;left:-20px!important;position:absolute!important;top:0!important}.event-type-markdown blockquote{border-left:2px solid var(--bw-border-color);color:var(--bw-text-muted);font-style:italic;margin:4px 0;padding-left:12px}.event-type-markdown a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:var(--bw-text-color);font-weight:600;margin-bottom:.5em}.markdown-content h1{font-size:1.5em}.markdown-content h2{font-size:1.25em}.markdown-content h3{font-size:1.1em}.markdown-content p{line-height:1.6;margin-bottom:1em}.markdown-content ol,.markdown-content ul{margin-bottom:1em;padding-left:1.5em}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin-bottom:.25em}.markdown-content a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content a:hover{opacity:.8}.markdown-content strong{font-weight:600}.markdown-content em{font-style:italic}.markdown-content code{background:var(--bw-highlight-subtle);border-radius:4px;font-family:monospace;font-size:.9em;padding:.125em .25em}.markdown-content blockquote{border-left:3px solid var(--bw-highlight-color);color:var(--bw-text-muted);margin:1em 0;padding-left:1em}.print-only{display:none}.print-hidden{display:block}@media print{.print-only{display:block}.print-hidden{display:none!important}.print-booking-header{border-bottom:2px solid #000;display:block;margin-bottom:24px;padding-bottom:16px;text-align:center}.print-booking-header h1{font-size:24px;margin:0 0 8px}.print-booking-header .subtitle{color:#666;font-size:14px}.print-booking-card{border:1px solid #ccc;border-radius:8px;margin-bottom:16px;padding:16px;page-break-inside:avoid}.print-section-title{border-bottom:1px solid #ddd;display:block;font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px}.print-detail-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}.print-detail-item{margin-bottom:8px}.print-detail-label{color:#666;font-size:12px;margin-bottom:4px}.print-detail-value{font-size:14px;font-weight:600}.print-status-badge{border-radius:9999px;display:inline-block;font-size:12px;font-weight:600;padding:4px 12px}.print-status-paid{background-color:#dcfce7;color:#166534;display:inline-block}.print-participant{align-items:center;background-color:#f9fafb;border-radius:4px;display:flex;justify-content:space-between;margin-bottom:8px;padding:8px}.print-participant-name{font-weight:600}.print-participant-age{color:#666;font-size:12px}.print-payment-summary{display:block}.print-payment-row{border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:4px 0}.print-payment-row:last-child{border-bottom:none;font-weight:600}.print-footer{border-top:1px solid #ddd;color:#666;display:block;font-size:12px;margin-top:24px;padding-top:16px;text-align:center}.print-footer p{margin:4px 0}}";
|
|
17821
|
+
var css_248z = ".booking-widget-container{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;color:var(--bw-text-color,#1e293b);direction:ltr;display:block;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;position:relative;text-align:left}.booking-widget-container *,.booking-widget-container :after,.booking-widget-container :before{box-sizing:border-box;margin:0;padding:0}.booking-widget-container input,.booking-widget-container select,.booking-widget-container textarea{font-family:inherit;font-size:inherit;line-height:inherit}.booking-widget-container button{background:none;border:none;cursor:pointer;font-family:inherit;font-size:inherit}.booking-widget-container a{color:inherit;text-decoration:none}.booking-widget-container img{display:block;height:auto;max-width:100%;vertical-align:middle}.booking-widget-container ol,.booking-widget-container ul{list-style:none}.booking-widget-container h1,.booking-widget-container h2,.booking-widget-container h3,.booking-widget-container h4,.booking-widget-container h5,.booking-widget-container h6{font-size:inherit;font-weight:inherit}#booking-widget-portal{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);direction:ltr;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;text-align:left}#booking-widget-portal *,#booking-widget-portal :after,#booking-widget-portal :before{box-sizing:border-box}#booking-widget-portal-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);line-height:1.5}:root{--bw-highlight-color:#00b1aa;--bw-highlight-color-rgb:0,177,170;--bw-background-color:#f8fdfe;--bw-surface-color:#fff;--bw-text-color:#0e7490;--bw-text-muted:rgba(14,116,144,.7);--bw-border-color:#bae6fd;--bw-success-color:#38bdf8;--bw-warning-color:#fbbf24;--bw-error-color:#f43f5e;--bw-border-radius:18px;--bw-border-radius-small:calc(var(--bw-border-radius)*0.8);--bw-spacing:16px;--bw-spacing-large:24px;--bw-font-family:\"Inter\",system-ui,sans-serif;--bw-font-size:14px;--bw-font-size-large:18px;--bw-font-size-small:12px;--bw-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--bw-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--bw-highlight-muted:rgba(0,177,170,.1);--bw-highlight-subtle:rgba(0,177,170,.05);--bw-text-subtle:rgba(14,116,144,.4)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes shimmer{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes slide-in-right{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes slide-out-right{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(100%)}}@keyframes slide-in-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.animate-spin{animation:spin 1s linear infinite}.animate-shimmer{animation:shimmer 2s infinite}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-fade-in{animation:fade-in .2s ease-out}.animate-slide-in-up{animation:slide-in-up .3s ease-out}.animate-scale-in{animation:scale-in .2s ease-out}.skeleton-shimmer{overflow:hidden;position:relative}.skeleton-shimmer:after{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.3),transparent);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.booking-widget-container .bw-btn{letter-spacing:var(--bw-letter-spacing,normal);text-transform:var(--bw-text-transform,none);transition:all .2s ease!important}.booking-widget-container .bw-btn:hover:not(:disabled):not([disabled]){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.booking-widget-container .bw-btn:active:not(:disabled):not([disabled]){box-shadow:0 2px 4px rgba(0,0,0,.1)}.booking-widget-container .bw-btn-primary:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);filter:brightness(1.1)}.booking-widget-container .bw-btn-secondary:hover:not(:disabled):not([disabled]){background-color:var(--bw-surface-color);border-color:var(--bw-highlight-color);color:var(--bw-highlight-color)}.booking-widget-container .bw-btn-ghost:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-muted)}.booking-widget-container .bw-btn-outline:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);color:var(--bw-button-text-color,#fff)}.booking-widget-container .bw-btn:disabled,.booking-widget-container .bw-btn[disabled]{cursor:not-allowed!important;opacity:.5!important}.booking-widget-container button[class*=bw-btn],.booking-widget-container button[style*=transition]{transition:all .2s ease!important}.booking-widget-container button[data-variant=primary]:hover:not(:disabled),.booking-widget-container button[style*=\"--bw-highlight-color\"]:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);filter:brightness(1.1);transform:translateY(-1px)}.booking-widget-container button[data-variant=secondary]:hover:not(:disabled){border-color:var(--bw-highlight-color)!important;color:var(--bw-highlight-color)!important}.booking-widget-container button[data-variant=ghost]:hover:not(:disabled){background-color:var(--bw-highlight-muted)!important}.booking-widget-container button[data-variant=outline]:hover:not(:disabled){background-color:var(--bw-highlight-color)!important;color:var(--bw-button-text-color,#fff)!important}.booking-widget-container .bw-button-hover:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.booking-widget-container .bw-button-hover:active:not(:disabled){box-shadow:0 2px 4px rgba(0,0,0,.1);transform:translateY(0)}@media (max-width:768px){.sidebar-mobile{border-radius:0!important;max-width:100%!important;width:100%!important}}@media (max-width:600px){.event-type-list{gap:12px!important;padding:8px!important}.event-type-card{flex:1 1 100%!important;max-width:100%!important;padding:0!important}.event-type-img{height:160px!important}.event-type-title{font-size:1.1rem!important}.event-type-desc{font-size:.8rem!important;max-height:100px!important;min-height:100px!important}.event-type-content{padding:16px 24px!important}}.event-type-markdown{overflow:visible!important}.event-type-markdown p{color:var(--bw-text-muted);font-family:var(--bw-font-family);line-height:1.6;margin:0 0 8px}.event-type-markdown p:last-child{margin-bottom:0}.event-type-markdown h2{font-size:18px!important;font-weight:700!important;margin:12px 0 6px!important}.event-type-markdown h2,.event-type-markdown h3{color:var(--bw-text-color)!important;line-height:1.3!important}.event-type-markdown h3{font-size:16px!important;font-weight:600!important;margin:10px 0 4px!important}.event-type-markdown strong{color:var(--bw-text-color);font-weight:600}.event-type-markdown em{font-style:italic}.event-type-markdown u{text-decoration:underline}.event-type-markdown ul{list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ul li{color:var(--bw-text-muted)!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ul li:before{color:var(--bw-text-color)!important;content:\"•\"!important;font-weight:700!important;left:-16px!important;position:absolute!important;top:0!important}.event-type-markdown ol{counter-reset:list-counter!important;list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ol li{color:var(--bw-text-muted)!important;counter-increment:list-counter!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ol li:before{color:var(--bw-text-color)!important;content:counter(list-counter) \".\"!important;font-weight:700!important;left:-20px!important;position:absolute!important;top:0!important}.event-type-markdown blockquote{border-left:2px solid var(--bw-border-color);color:var(--bw-text-muted);font-style:italic;margin:4px 0;padding-left:12px}.event-type-markdown a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:var(--bw-text-color);font-weight:600;margin-bottom:.5em}.markdown-content h1{font-size:1.5em}.markdown-content h2{font-size:1.25em}.markdown-content h3{font-size:1.1em}.markdown-content p{line-height:1.6;margin-bottom:1em}.markdown-content ol,.markdown-content ul{margin-bottom:1em;padding-left:1.5em}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin-bottom:.25em}.markdown-content a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content a:hover{opacity:.8}.markdown-content strong{font-weight:600}.markdown-content em{font-style:italic}.markdown-content code{background:var(--bw-highlight-subtle);border-radius:4px;font-family:monospace;font-size:.9em;padding:.125em .25em}.markdown-content blockquote{border-left:3px solid var(--bw-highlight-color);color:var(--bw-text-muted);margin:1em 0;padding-left:1em}.print-only{display:none}.print-hidden{display:block}@media print{.print-only{display:block}.print-hidden{display:none!important}.print-booking-header{border-bottom:2px solid #000;display:block;margin-bottom:24px;padding-bottom:16px;text-align:center}.print-booking-header h1{font-size:24px;margin:0 0 8px}.print-booking-header .subtitle{color:#666;font-size:14px}.print-booking-card{border:1px solid #ccc;border-radius:8px;margin-bottom:16px;padding:16px;page-break-inside:avoid}.print-section-title{border-bottom:1px solid #ddd;display:block;font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px}.print-detail-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}.print-detail-item{margin-bottom:8px}.print-detail-label{color:#666;font-size:12px;margin-bottom:4px}.print-detail-value{font-size:14px;font-weight:600}.print-status-badge{border-radius:9999px;display:inline-block;font-size:12px;font-weight:600;padding:4px 12px}.print-status-paid{background-color:#dcfce7;color:#166534;display:inline-block}.print-participant{align-items:center;background-color:#f9fafb;border-radius:4px;display:flex;justify-content:space-between;margin-bottom:8px;padding:8px}.print-participant-name{font-weight:600}.print-participant-age{color:#666;font-size:12px}.print-payment-summary{display:block}.print-payment-row{border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:4px 0}.print-payment-row:last-child{border-bottom:none;font-weight:600}.print-footer{border-top:1px solid #ddd;color:#666;display:block;font-size:12px;margin-top:24px;padding-top:16px;text-align:center}.print-footer p{margin:4px 0}}";
|
|
17069
17822
|
styleInject(css_248z);
|
|
17070
17823
|
|
|
17071
17824
|
// Export init function for vanilla JS usage
|