@session-services/web-elements 0.1.30 → 0.1.32
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/cjs/loader.cjs.js +1 -1
- package/dist/cjs/session-services-elements.cjs.js +1 -1
- package/dist/cjs/session-services-ticketing.cjs.entry.js +1645 -22994
- package/dist/collection/components/session-services-ticketing/bot-detection.js +83 -0
- package/dist/collection/components/session-services-ticketing/bot-detection.test.js +74 -0
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.css +164 -36
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.js +697 -219
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.js +25 -4
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.test.js +19 -2
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.test.js +540 -19
- package/dist/components/session-services-ticketing.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/session-services-elements.js +1 -1
- package/dist/esm/session-services-ticketing.entry.js +1645 -22994
- package/dist/session-services-elements/p-7a1b9685.entry.js +1 -0
- package/dist/session-services-elements/session-services-elements.esm.js +1 -1
- package/dist/types/components/session-services-ticketing/bot-detection.d.ts +13 -0
- package/dist/types/components/session-services-ticketing/bot-detection.test.d.ts +1 -0
- package/dist/types/components/session-services-ticketing/session-services-ticketing.d.ts +52 -5
- package/dist/types/components/session-services-ticketing/session-services-ticketing.logic.d.ts +8 -1
- package/package.json +2 -2
- package/dist/session-services-elements/p-4dd3f723.entry.js +0 -1
|
@@ -1,15 +1,58 @@
|
|
|
1
1
|
import { createClient } from "@session-services/sdk";
|
|
2
|
-
import { buildOrderChannel, createAnalytics,
|
|
3
|
-
import {
|
|
2
|
+
import { buildOrderChannel, createAnalytics, } from "@session-services/sdk/analytics";
|
|
3
|
+
import { fromMinorUnits } from "@session-services/utils/currency";
|
|
4
|
+
import { parseNaiveDateTime } from "@session-services/utils/naive-datetime";
|
|
4
5
|
import { isSectionSoldOut } from "@session-services/utils/ticket-availability";
|
|
5
6
|
import { normalizeCouponCode } from "@session-services/utils/ticket-quote";
|
|
6
7
|
import { Host,
|
|
7
8
|
// biome-ignore lint/correctness/noUnusedImports: h is required by Stencil JSX transform
|
|
8
9
|
h, } from "@stencil/core";
|
|
9
|
-
import {
|
|
10
|
+
import { detectBot, isProductionApi } from "./bot-detection";
|
|
11
|
+
import { admissionPriceSavings, discountCouponFromSearch, eventTimeZone, getAdmissionDisplayState, getAdmissionPriceDisplay, getTicketingState, parseSelectionKey, previewQuoteItems, priceTotal, quoteItemsBySelection, selectionKey, topLevelTicketItems, visibleAdmissions, } from "./session-services-ticketing.logic";
|
|
10
12
|
const quoteTotal = (quote) => quote.price?.total ?? 0;
|
|
13
|
+
function majorUnits(value, currency) {
|
|
14
|
+
return fromMinorUnits(value, currency ?? "");
|
|
15
|
+
}
|
|
16
|
+
function priceAmount(price, currency) {
|
|
17
|
+
return majorUnits(priceTotal(price), price?.currency ?? currency);
|
|
18
|
+
}
|
|
19
|
+
function quoteAmount(quote, currency) {
|
|
20
|
+
return majorUnits(quoteTotal(quote), quote.price?.currency ?? currency);
|
|
21
|
+
}
|
|
11
22
|
const QUOTE_DEBOUNCE_MS = 150;
|
|
23
|
+
const REQUEST_TIMEOUT_MS = 15_000;
|
|
12
24
|
const STICKY_CHECKOUT_SCROLL_OFFSET_PX = 100;
|
|
25
|
+
const CHECKOUT_IDEMPOTENCY_PREFIX = "session-services:checkout:";
|
|
26
|
+
function createCheckoutIdempotencyKey() {
|
|
27
|
+
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
28
|
+
return `elements_${Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
29
|
+
}
|
|
30
|
+
async function checkoutStorageName(identity) {
|
|
31
|
+
if (!globalThis.crypto?.subtle)
|
|
32
|
+
return undefined;
|
|
33
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(identity));
|
|
34
|
+
return `${CHECKOUT_IDEMPOTENCY_PREFIX}${Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
35
|
+
}
|
|
36
|
+
function storedCheckoutIdempotencyKey(name) {
|
|
37
|
+
if (!name || typeof window === "undefined")
|
|
38
|
+
return undefined;
|
|
39
|
+
try {
|
|
40
|
+
return window.sessionStorage?.getItem(name) || undefined;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function storeCheckoutIdempotencyKey(name, key) {
|
|
47
|
+
if (!name || typeof window === "undefined")
|
|
48
|
+
return;
|
|
49
|
+
try {
|
|
50
|
+
window.sessionStorage?.setItem(name, key);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// In-memory state still keeps retries idempotent when storage is blocked.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
13
56
|
function objectValue(value, key) {
|
|
14
57
|
if (!value || typeof value !== "object")
|
|
15
58
|
return undefined;
|
|
@@ -28,6 +71,29 @@ function isPasscodeAccessError(err) {
|
|
|
28
71
|
return true;
|
|
29
72
|
return errorCode(err) === "UNAUTHORIZED" && /passcode/i.test(message);
|
|
30
73
|
}
|
|
74
|
+
function classifyLoadError(err) {
|
|
75
|
+
if (isPasscodeAccessError(err))
|
|
76
|
+
return "access";
|
|
77
|
+
if (errorCode(err) === "NOT_FOUND")
|
|
78
|
+
return "not_found";
|
|
79
|
+
return "transient";
|
|
80
|
+
}
|
|
81
|
+
async function withTimeout(controller, request) {
|
|
82
|
+
let timer;
|
|
83
|
+
const timeout = new Promise((_, reject) => {
|
|
84
|
+
timer = setTimeout(() => {
|
|
85
|
+
controller.abort();
|
|
86
|
+
reject(new Error("The request timed out"));
|
|
87
|
+
}, REQUEST_TIMEOUT_MS);
|
|
88
|
+
});
|
|
89
|
+
try {
|
|
90
|
+
return await Promise.race([request(controller.signal), timeout]);
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
if (timer)
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
31
97
|
function parseJson(value) {
|
|
32
98
|
try {
|
|
33
99
|
return JSON.parse(value);
|
|
@@ -85,7 +151,7 @@ function quoteProductContext({ event, admission, section, }) {
|
|
|
85
151
|
}
|
|
86
152
|
function quoteProductPrice(item) {
|
|
87
153
|
return {
|
|
88
|
-
price:
|
|
154
|
+
price: priceAmount(item.price),
|
|
89
155
|
price_base: item.price?.base,
|
|
90
156
|
price_booking_fee: item.price?.booking,
|
|
91
157
|
};
|
|
@@ -114,8 +180,13 @@ export class SessionsTicketing {
|
|
|
114
180
|
selectedItems = new Map();
|
|
115
181
|
quote = null;
|
|
116
182
|
previewQuote = null;
|
|
117
|
-
|
|
183
|
+
initialLoading = true;
|
|
184
|
+
eventLoading = false;
|
|
185
|
+
retryLoading = false;
|
|
186
|
+
passcodeLoading = false;
|
|
118
187
|
error = null;
|
|
188
|
+
errorKind = null;
|
|
189
|
+
refreshError = null;
|
|
119
190
|
quoteLoading = false;
|
|
120
191
|
previewLoading = false;
|
|
121
192
|
quoteError = null;
|
|
@@ -127,8 +198,10 @@ export class SessionsTicketing {
|
|
|
127
198
|
passcodeDenied = false;
|
|
128
199
|
selectionPrompt = false;
|
|
129
200
|
checkoutLoading = false;
|
|
201
|
+
checkoutError = null;
|
|
130
202
|
currentTheme = "light";
|
|
131
203
|
openModalAdmissionId = null;
|
|
204
|
+
verificationRequired = false;
|
|
132
205
|
checkoutInit;
|
|
133
206
|
cartUpdate;
|
|
134
207
|
errorOccurred;
|
|
@@ -138,34 +211,72 @@ export class SessionsTicketing {
|
|
|
138
211
|
mediaQueryList;
|
|
139
212
|
requestId = 0;
|
|
140
213
|
previewRequestId = 0;
|
|
214
|
+
eventRequestId = 0;
|
|
215
|
+
checkoutRequestId = 0;
|
|
216
|
+
couponRequestId = 0;
|
|
217
|
+
initializeId = 0;
|
|
218
|
+
initialSettled = false;
|
|
219
|
+
active = true;
|
|
220
|
+
trackedIdentity;
|
|
221
|
+
quoteRevision;
|
|
222
|
+
checkoutIdempotencyKeys = new Map();
|
|
223
|
+
eventController;
|
|
224
|
+
quoteController;
|
|
225
|
+
previewController;
|
|
226
|
+
checkoutController;
|
|
227
|
+
couponController;
|
|
141
228
|
quoteTimer;
|
|
142
229
|
promptTimer;
|
|
143
|
-
|
|
230
|
+
botBypassed = false;
|
|
231
|
+
modalTrigger;
|
|
232
|
+
modalFocusPending = false;
|
|
233
|
+
modalRestorePending = false;
|
|
234
|
+
connectedCallback() {
|
|
235
|
+
this.active = true;
|
|
144
236
|
if (typeof window !== "undefined") {
|
|
145
237
|
window.addEventListener("popstate", this.handleDiscountQueryChange);
|
|
146
238
|
}
|
|
147
|
-
await this.initializeComponent();
|
|
148
239
|
}
|
|
149
|
-
|
|
150
|
-
|
|
240
|
+
componentWillLoad() {
|
|
241
|
+
this.startInitialization();
|
|
242
|
+
}
|
|
243
|
+
componentDidRender() {
|
|
244
|
+
if (this.modalFocusPending) {
|
|
245
|
+
const dialog = this.host.shadowRoot?.querySelector(".modal-content");
|
|
246
|
+
if (!dialog)
|
|
247
|
+
return;
|
|
248
|
+
this.modalFocusPending = false;
|
|
249
|
+
dialog.querySelector("button")?.focus();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!this.modalRestorePending)
|
|
253
|
+
return;
|
|
254
|
+
this.modalRestorePending = false;
|
|
255
|
+
this.modalTrigger?.focus();
|
|
256
|
+
this.modalTrigger = undefined;
|
|
151
257
|
}
|
|
152
|
-
|
|
153
|
-
|
|
258
|
+
propsChanged() {
|
|
259
|
+
this.startInitialization();
|
|
260
|
+
}
|
|
261
|
+
couponCodeChanged() {
|
|
262
|
+
this.setInitialCoupon().catch((err) => {
|
|
263
|
+
this.setCouponError(errorMessage(err, "Failed to update coupon"));
|
|
264
|
+
});
|
|
154
265
|
}
|
|
155
266
|
themeChanged() {
|
|
156
267
|
this.setupTheme();
|
|
157
268
|
}
|
|
158
269
|
disconnectedCallback() {
|
|
159
|
-
|
|
270
|
+
this.active = false;
|
|
271
|
+
this.initializeId++;
|
|
272
|
+
this.invalidateRequests();
|
|
273
|
+
if (typeof window !== "undefined" && window.removeEventListener) {
|
|
160
274
|
window.removeEventListener("popstate", this.handleDiscountQueryChange);
|
|
161
275
|
}
|
|
162
276
|
if (this.mediaQueryList) {
|
|
163
277
|
this.mediaQueryList.removeEventListener("change", this.handleSystemThemeChange);
|
|
164
278
|
}
|
|
165
|
-
|
|
166
|
-
if (this.openModalAdmissionId) {
|
|
167
|
-
document.removeEventListener("keydown", this.handleModalKeyDown);
|
|
168
|
-
}
|
|
279
|
+
document.removeEventListener("keydown", this.handleModalKeyDown);
|
|
169
280
|
this.clearQuoteTimer();
|
|
170
281
|
this.clearPromptTimer();
|
|
171
282
|
}
|
|
@@ -174,56 +285,144 @@ export class SessionsTicketing {
|
|
|
174
285
|
return;
|
|
175
286
|
this.setInitialCoupon().catch(() => undefined);
|
|
176
287
|
};
|
|
288
|
+
continueAfterVerification = async () => {
|
|
289
|
+
this.botBypassed = true;
|
|
290
|
+
this.verificationRequired = false;
|
|
291
|
+
const pending = this.initializeComponent();
|
|
292
|
+
const id = this.initializeId;
|
|
293
|
+
try {
|
|
294
|
+
await pending;
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
if (id === this.initializeId)
|
|
298
|
+
this.handleInitializationError(err);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
startInitialization() {
|
|
302
|
+
const pending = this.initializeComponent();
|
|
303
|
+
const id = this.initializeId;
|
|
304
|
+
pending.catch((err) => {
|
|
305
|
+
if (id === this.initializeId)
|
|
306
|
+
this.handleInitializationError(err);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
handleInitializationError = (err) => {
|
|
310
|
+
const error = errorMessage(err, "Failed to load event data");
|
|
311
|
+
this.error = error;
|
|
312
|
+
this.errorKind = "transient";
|
|
313
|
+
this.finishInitialLoad(!this.initialSettled);
|
|
314
|
+
this.errorOccurred.emit({ error });
|
|
315
|
+
};
|
|
177
316
|
async initializeComponent() {
|
|
317
|
+
const id = ++this.initializeId;
|
|
318
|
+
const initial = !this.initialSettled;
|
|
319
|
+
this.invalidateRequests();
|
|
178
320
|
this.error = null;
|
|
321
|
+
this.errorKind = null;
|
|
322
|
+
this.refreshError = null;
|
|
179
323
|
this.event = null;
|
|
180
324
|
this.selectedItems = new Map();
|
|
181
325
|
this.quote = null;
|
|
182
326
|
this.previewQuote = null;
|
|
183
327
|
this.quoteError = null;
|
|
328
|
+
this.checkoutError = null;
|
|
329
|
+
this.quoteLoading = false;
|
|
330
|
+
this.previewLoading = false;
|
|
331
|
+
this.checkoutLoading = false;
|
|
184
332
|
this.couponError = null;
|
|
185
333
|
this.couponInput = this.initialCouponCode();
|
|
186
334
|
this.appliedCouponCode = this.couponInput;
|
|
187
335
|
this.activePasscode = this.passcode || "";
|
|
188
336
|
this.passcodeInput = this.activePasscode;
|
|
189
337
|
this.passcodeDenied = false;
|
|
338
|
+
this.verificationRequired = false;
|
|
339
|
+
this.initialLoading = initial;
|
|
340
|
+
this.eventLoading = !initial;
|
|
341
|
+
this.retryLoading = false;
|
|
342
|
+
this.passcodeLoading = false;
|
|
190
343
|
if (!this.tenantId || !this.eventId) {
|
|
191
344
|
this.error = "Missing required configuration: tenantId and eventId";
|
|
192
|
-
this.
|
|
345
|
+
this.errorKind = "configuration";
|
|
346
|
+
this.finishInitialLoad(initial);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
this.setupTheme();
|
|
350
|
+
if (!this.botBypassed && isProductionApi(this.apiUrl) && detectBot().bot) {
|
|
351
|
+
this.verificationRequired = true;
|
|
352
|
+
this.finishInitialLoad(initial);
|
|
193
353
|
return;
|
|
194
354
|
}
|
|
195
355
|
this.client = createClient({
|
|
196
356
|
environment: this.apiUrl,
|
|
197
357
|
tenantId: this.tenantId,
|
|
198
358
|
});
|
|
199
|
-
this.
|
|
200
|
-
await this.loadEventData(
|
|
359
|
+
this.analytics = undefined;
|
|
360
|
+
const result = await this.loadEventData({
|
|
361
|
+
mode: initial ? "initial" : "refresh",
|
|
362
|
+
});
|
|
363
|
+
if (id !== this.initializeId || result.status !== "success")
|
|
364
|
+
return;
|
|
365
|
+
this.handleLoadedEvent(result.event);
|
|
366
|
+
await this.updatePreviewQuote();
|
|
367
|
+
}
|
|
368
|
+
handleLoadedEvent(event) {
|
|
369
|
+
const identity = `${this.apiUrl}::${this.tenantId}::${this.eventId}`;
|
|
370
|
+
this.setupAnalytics(event);
|
|
371
|
+
if (identity === this.trackedIdentity)
|
|
372
|
+
return;
|
|
373
|
+
this.trackedIdentity = identity;
|
|
374
|
+
this.trackLoadedEvent(event);
|
|
375
|
+
}
|
|
376
|
+
finishInitialLoad(initial) {
|
|
377
|
+
if (initial) {
|
|
378
|
+
this.initialSettled = true;
|
|
379
|
+
this.initialLoading = false;
|
|
380
|
+
}
|
|
381
|
+
this.eventLoading = false;
|
|
382
|
+
}
|
|
383
|
+
invalidateRequests() {
|
|
384
|
+
this.eventRequestId++;
|
|
385
|
+
this.requestId++;
|
|
386
|
+
this.previewRequestId++;
|
|
387
|
+
this.checkoutRequestId++;
|
|
388
|
+
this.couponRequestId++;
|
|
389
|
+
this.eventController?.abort();
|
|
390
|
+
this.quoteController?.abort();
|
|
391
|
+
this.previewController?.abort();
|
|
392
|
+
this.checkoutController?.abort();
|
|
393
|
+
this.couponController?.abort();
|
|
394
|
+
this.eventController = undefined;
|
|
395
|
+
this.quoteController = undefined;
|
|
396
|
+
this.previewController = undefined;
|
|
397
|
+
this.checkoutController = undefined;
|
|
398
|
+
this.couponController = undefined;
|
|
399
|
+
this.clearQuoteTimer();
|
|
400
|
+
this.quoteRevision = undefined;
|
|
401
|
+
this.quoteLoading = false;
|
|
402
|
+
this.previewLoading = false;
|
|
403
|
+
this.checkoutLoading = false;
|
|
201
404
|
}
|
|
202
405
|
async setInitialCoupon() {
|
|
203
406
|
const coupon = this.initialCouponCode();
|
|
204
407
|
this.couponInput = coupon;
|
|
205
|
-
this.quote = null;
|
|
206
|
-
this.previewQuote = this.activeInitialPreviewQuote(coupon);
|
|
207
408
|
this.quoteError = null;
|
|
208
409
|
this.couponError = null;
|
|
209
|
-
if (!
|
|
210
|
-
this.appliedCouponCode =
|
|
211
|
-
|
|
410
|
+
if (!this.event) {
|
|
411
|
+
this.appliedCouponCode = coupon;
|
|
412
|
+
this.previewQuote = this.activeInitialPreviewQuote(coupon);
|
|
212
413
|
return;
|
|
213
414
|
}
|
|
214
415
|
if (this.selectedItems.size > 0) {
|
|
215
416
|
await this.applyCartCoupon(coupon);
|
|
216
417
|
return;
|
|
217
418
|
}
|
|
218
|
-
if (this.previewQuote)
|
|
219
|
-
return;
|
|
220
419
|
await this.applyPreviewCoupon(coupon);
|
|
221
420
|
}
|
|
222
421
|
initialCouponCode() {
|
|
223
422
|
const prop = normalizeCouponCode(this.couponCode);
|
|
224
423
|
if (prop)
|
|
225
424
|
return prop;
|
|
226
|
-
if (typeof window === "undefined")
|
|
425
|
+
if (typeof window === "undefined" || !window.location)
|
|
227
426
|
return "";
|
|
228
427
|
return normalizeCouponCode(discountCouponFromSearch(window.location.search));
|
|
229
428
|
}
|
|
@@ -343,7 +542,7 @@ export class SessionsTicketing {
|
|
|
343
542
|
sku: adm.id,
|
|
344
543
|
name: adm.name,
|
|
345
544
|
category: event.name,
|
|
346
|
-
price:
|
|
545
|
+
price: priceAmount(adm.price, event.currency),
|
|
347
546
|
quantity: 1,
|
|
348
547
|
position: index + 1,
|
|
349
548
|
admission_id: adm.id,
|
|
@@ -354,38 +553,106 @@ export class SessionsTicketing {
|
|
|
354
553
|
capacity: adm.capacity,
|
|
355
554
|
}));
|
|
356
555
|
}
|
|
357
|
-
async loadEventData() {
|
|
556
|
+
async loadEventData(options = {}) {
|
|
557
|
+
const mode = options.mode ?? "refresh";
|
|
558
|
+
const client = this.client;
|
|
559
|
+
const eventId = this.eventId;
|
|
560
|
+
if (!client || !this.tenantId || !eventId) {
|
|
561
|
+
const error = "Missing required configuration: tenantId and eventId";
|
|
562
|
+
this.error = error;
|
|
563
|
+
this.errorKind = "configuration";
|
|
564
|
+
return { status: "error", error, kind: "configuration" };
|
|
565
|
+
}
|
|
566
|
+
const id = ++this.eventRequestId;
|
|
567
|
+
const controller = new AbortController();
|
|
568
|
+
this.eventController?.abort();
|
|
569
|
+
this.eventController = controller;
|
|
570
|
+
if (mode === "coupon")
|
|
571
|
+
this.couponController = controller;
|
|
572
|
+
if (mode === "initial")
|
|
573
|
+
this.initialLoading = true;
|
|
574
|
+
if (mode === "refresh")
|
|
575
|
+
this.eventLoading = true;
|
|
576
|
+
if (mode === "retry")
|
|
577
|
+
this.retryLoading = true;
|
|
578
|
+
if (mode === "passcode")
|
|
579
|
+
this.passcodeLoading = true;
|
|
358
580
|
try {
|
|
359
|
-
|
|
360
|
-
|
|
581
|
+
if (mode !== "coupon" && mode !== "passcode") {
|
|
582
|
+
this.error = null;
|
|
583
|
+
this.errorKind = null;
|
|
584
|
+
this.refreshError = null;
|
|
585
|
+
}
|
|
586
|
+
const response = await withTimeout(controller, (signal) => client.event.get({
|
|
587
|
+
key: eventId,
|
|
588
|
+
couponCode: (options.couponCode ?? this.appliedCouponCode) || undefined,
|
|
589
|
+
passcode: (options.passcode ?? this.activePasscode) || undefined,
|
|
590
|
+
}, { signal }));
|
|
591
|
+
if (!this.isEventRequestCurrent(id, eventId, controller)) {
|
|
592
|
+
return { status: "stale" };
|
|
593
|
+
}
|
|
594
|
+
if (options.commit !== false) {
|
|
595
|
+
this.event = response.event;
|
|
596
|
+
this.pruneSelectedItems(response.event);
|
|
597
|
+
}
|
|
361
598
|
this.passcodeDenied = false;
|
|
362
|
-
|
|
363
|
-
key: this.eventId,
|
|
364
|
-
couponCode: this.appliedCouponCode || undefined,
|
|
365
|
-
passcode: this.activePasscode || undefined,
|
|
366
|
-
});
|
|
367
|
-
this.event = response.event;
|
|
368
|
-
this.pruneSelectedItems(response.event);
|
|
369
|
-
this.trackLoadedEvent(response.event);
|
|
370
|
-
await this.updatePreviewQuote();
|
|
599
|
+
return { status: "success", event: response.event };
|
|
371
600
|
}
|
|
372
601
|
catch (err) {
|
|
373
|
-
this.
|
|
374
|
-
|
|
375
|
-
|
|
602
|
+
if (!this.isEventRequestCurrent(id, eventId, controller)) {
|
|
603
|
+
return { status: "stale" };
|
|
604
|
+
}
|
|
605
|
+
const kind = classifyLoadError(err);
|
|
606
|
+
const error = errorMessage(err, "Failed to load event data");
|
|
607
|
+
this.passcodeDenied = kind === "access";
|
|
608
|
+
if (mode !== "coupon") {
|
|
609
|
+
if (this.event && (mode === "refresh" || mode === "retry")) {
|
|
610
|
+
this.refreshError = error;
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
this.error = error;
|
|
614
|
+
this.errorKind = kind;
|
|
615
|
+
}
|
|
616
|
+
this.errorOccurred.emit({ error });
|
|
617
|
+
}
|
|
618
|
+
return { status: "error", error, kind };
|
|
376
619
|
}
|
|
377
620
|
finally {
|
|
378
|
-
this.
|
|
621
|
+
if (id === this.eventRequestId) {
|
|
622
|
+
if (mode === "initial")
|
|
623
|
+
this.finishInitialLoad(true);
|
|
624
|
+
if (mode === "refresh")
|
|
625
|
+
this.eventLoading = false;
|
|
626
|
+
if (mode === "retry")
|
|
627
|
+
this.retryLoading = false;
|
|
628
|
+
if (mode === "passcode")
|
|
629
|
+
this.passcodeLoading = false;
|
|
630
|
+
if (this.eventController === controller)
|
|
631
|
+
this.eventController = undefined;
|
|
632
|
+
if (this.couponController === controller)
|
|
633
|
+
this.couponController = undefined;
|
|
634
|
+
}
|
|
379
635
|
}
|
|
380
636
|
}
|
|
637
|
+
isEventRequestCurrent(id, eventId, controller) {
|
|
638
|
+
return (this.active &&
|
|
639
|
+
id === this.eventRequestId &&
|
|
640
|
+
eventId === this.eventId &&
|
|
641
|
+
this.eventController === controller);
|
|
642
|
+
}
|
|
381
643
|
pruneSelectedItems(event) {
|
|
644
|
+
if (this.openModalAdmissionId &&
|
|
645
|
+
!event.admissions.some((admission) => admission.id === this.openModalAdmissionId)) {
|
|
646
|
+
this.closeModal();
|
|
647
|
+
}
|
|
382
648
|
const keys = new Set(event.admissions.map((admission) => selectionKey(admission.id, admission.sectionId ?? null)));
|
|
383
649
|
const selected = Array.from(this.selectedItems).filter(([key]) => keys.has(key));
|
|
384
650
|
if (selected.length === this.selectedItems.size)
|
|
385
651
|
return;
|
|
386
652
|
this.selectedItems = new Map(selected);
|
|
387
|
-
this.
|
|
388
|
-
this.
|
|
653
|
+
this.invalidateQuote();
|
|
654
|
+
if (this.selectedItems.size > 0)
|
|
655
|
+
this.scheduleQuoteUpdate();
|
|
389
656
|
}
|
|
390
657
|
trackLoadedEvent(event) {
|
|
391
658
|
if (!this.setupAnalytics(event))
|
|
@@ -433,8 +700,8 @@ export class SessionsTicketing {
|
|
|
433
700
|
sku: this.eventId,
|
|
434
701
|
name: event.name,
|
|
435
702
|
category: "Event",
|
|
436
|
-
value: lowestPrice
|
|
437
|
-
price: lowestPrice
|
|
703
|
+
value: majorUnits(lowestPrice, event.currency),
|
|
704
|
+
price: majorUnits(lowestPrice, event.currency),
|
|
438
705
|
event_date: event.entryStartsAt,
|
|
439
706
|
venue_name: eventVenueName(event),
|
|
440
707
|
venue_city: event.location?.city,
|
|
@@ -445,6 +712,8 @@ export class SessionsTicketing {
|
|
|
445
712
|
await this.setQuantity(admissionId, sectionId, (this.selectedItems.get(key) || 0) + delta);
|
|
446
713
|
}
|
|
447
714
|
async setQuantity(admissionId, sectionId, quantity) {
|
|
715
|
+
if (this.checkoutLoading)
|
|
716
|
+
return;
|
|
448
717
|
const key = selectionKey(admissionId, sectionId);
|
|
449
718
|
const admission = this.event?.admissions.find((a) => a.id === admissionId);
|
|
450
719
|
if (!admission)
|
|
@@ -455,6 +724,8 @@ export class SessionsTicketing {
|
|
|
455
724
|
return;
|
|
456
725
|
this.selectionPrompt = false;
|
|
457
726
|
this.setSelectedQuantity(key, next);
|
|
727
|
+
this.invalidateQuote();
|
|
728
|
+
this.checkoutError = null;
|
|
458
729
|
const section = sectionId
|
|
459
730
|
? this.event?.sections.find((s) => s.id === sectionId)
|
|
460
731
|
: null;
|
|
@@ -500,7 +771,7 @@ export class SessionsTicketing {
|
|
|
500
771
|
name: change.admission.name,
|
|
501
772
|
category: this.event?.name,
|
|
502
773
|
variant: change.section?.name,
|
|
503
|
-
price:
|
|
774
|
+
price: priceAmount(change.admission.price, this.event?.currency),
|
|
504
775
|
quantity: Math.abs(change.delta),
|
|
505
776
|
admission_id: change.admission.id,
|
|
506
777
|
section_id: optionalId(change.sectionId),
|
|
@@ -510,19 +781,26 @@ export class SessionsTicketing {
|
|
|
510
781
|
async updateQuote() {
|
|
511
782
|
this.clearQuoteTimer();
|
|
512
783
|
if (this.selectedItems.size === 0) {
|
|
784
|
+
this.invalidateQuote();
|
|
513
785
|
this.quote = null;
|
|
514
786
|
this.quoteError = null;
|
|
515
787
|
this.quoteLoading = false;
|
|
516
788
|
await this.updatePreviewQuote();
|
|
517
789
|
return;
|
|
518
790
|
}
|
|
519
|
-
this.requestId
|
|
520
|
-
const
|
|
791
|
+
const currentId = ++this.requestId;
|
|
792
|
+
const eventId = this.eventId;
|
|
793
|
+
const coupon = this.appliedCouponCode;
|
|
794
|
+
const items = this.getQuoteRequestItems();
|
|
795
|
+
const revision = this.quoteRevisionFor(eventId, coupon, items);
|
|
796
|
+
const controller = new AbortController();
|
|
797
|
+
this.quoteController?.abort();
|
|
798
|
+
this.quoteController = controller;
|
|
521
799
|
try {
|
|
522
800
|
this.quoteLoading = true;
|
|
523
801
|
this.quoteError = null;
|
|
524
|
-
const response = await this.fetchCartQuote(
|
|
525
|
-
this.applyQuote(response.quote, currentId);
|
|
802
|
+
const response = await this.fetchCartQuote(eventId, items, coupon, controller);
|
|
803
|
+
this.applyQuote(response.quote, currentId, revision);
|
|
526
804
|
}
|
|
527
805
|
catch (_err) {
|
|
528
806
|
this.handleQuoteError(currentId);
|
|
@@ -530,6 +808,9 @@ export class SessionsTicketing {
|
|
|
530
808
|
finally {
|
|
531
809
|
if (currentId === this.requestId) {
|
|
532
810
|
this.quoteLoading = false;
|
|
811
|
+
if (this.quoteController === controller) {
|
|
812
|
+
this.quoteController = undefined;
|
|
813
|
+
}
|
|
533
814
|
}
|
|
534
815
|
}
|
|
535
816
|
}
|
|
@@ -551,6 +832,26 @@ export class SessionsTicketing {
|
|
|
551
832
|
clearTimeout(this.quoteTimer);
|
|
552
833
|
this.quoteTimer = undefined;
|
|
553
834
|
}
|
|
835
|
+
invalidateQuote() {
|
|
836
|
+
this.requestId++;
|
|
837
|
+
this.couponRequestId++;
|
|
838
|
+
this.checkoutRequestId++;
|
|
839
|
+
this.quoteController?.abort();
|
|
840
|
+
this.previewController?.abort();
|
|
841
|
+
this.checkoutController?.abort();
|
|
842
|
+
this.couponController?.abort();
|
|
843
|
+
this.quoteController = undefined;
|
|
844
|
+
this.previewController = undefined;
|
|
845
|
+
this.checkoutController = undefined;
|
|
846
|
+
this.couponController = undefined;
|
|
847
|
+
this.quoteRevision = undefined;
|
|
848
|
+
this.quote = null;
|
|
849
|
+
this.quoteError = null;
|
|
850
|
+
this.quoteLoading = false;
|
|
851
|
+
this.previewLoading = false;
|
|
852
|
+
this.checkoutError = null;
|
|
853
|
+
this.checkoutLoading = false;
|
|
854
|
+
}
|
|
554
855
|
clearPromptTimer() {
|
|
555
856
|
if (!this.promptTimer)
|
|
556
857
|
return;
|
|
@@ -575,22 +876,23 @@ export class SessionsTicketing {
|
|
|
575
876
|
};
|
|
576
877
|
});
|
|
577
878
|
}
|
|
578
|
-
fetchCartQuote(couponCode) {
|
|
579
|
-
return this.client.order.quote({
|
|
580
|
-
eventId
|
|
581
|
-
items
|
|
879
|
+
fetchCartQuote(eventId, items, couponCode, controller) {
|
|
880
|
+
return withTimeout(controller, (signal) => this.client.order.quote({
|
|
881
|
+
eventId,
|
|
882
|
+
items,
|
|
582
883
|
couponCode: couponCode || undefined,
|
|
583
|
-
});
|
|
884
|
+
}, { signal }));
|
|
584
885
|
}
|
|
585
|
-
applyQuote(quote, requestId) {
|
|
886
|
+
applyQuote(quote, requestId, revision = this.currentQuoteRevision()) {
|
|
586
887
|
if (requestId !== this.requestId)
|
|
587
888
|
return;
|
|
588
889
|
this.quote = quote;
|
|
890
|
+
this.quoteRevision = revision;
|
|
589
891
|
this.quoteError = null;
|
|
590
892
|
this.cartUpdate.emit(this.quote);
|
|
591
893
|
this.trackEvent("Cart Viewed", {
|
|
592
894
|
products: this.getCartProducts(),
|
|
593
|
-
value:
|
|
895
|
+
value: quoteAmount(this.quote, this.event?.currency),
|
|
594
896
|
currency: this.event?.currency,
|
|
595
897
|
});
|
|
596
898
|
}
|
|
@@ -598,13 +900,22 @@ export class SessionsTicketing {
|
|
|
598
900
|
if (requestId !== this.requestId)
|
|
599
901
|
return;
|
|
600
902
|
this.quote = null;
|
|
903
|
+
this.quoteRevision = undefined;
|
|
601
904
|
this.quoteError = "Failed to update cart. Please try again.";
|
|
602
905
|
this.errorOccurred.emit({ error: this.quoteError });
|
|
603
906
|
}
|
|
907
|
+
quoteRevisionFor(eventId, coupon, items) {
|
|
908
|
+
return JSON.stringify({ eventId, coupon, items });
|
|
909
|
+
}
|
|
910
|
+
currentQuoteRevision() {
|
|
911
|
+
return this.quoteRevisionFor(this.eventId, this.appliedCouponCode, this.getQuoteRequestItems());
|
|
912
|
+
}
|
|
604
913
|
async updatePreviewQuote() {
|
|
605
914
|
const event = this.event;
|
|
606
915
|
if (!event || !this.appliedCouponCode) {
|
|
607
916
|
this.previewRequestId++;
|
|
917
|
+
this.previewController?.abort();
|
|
918
|
+
this.previewController = undefined;
|
|
608
919
|
this.previewLoading = false;
|
|
609
920
|
this.previewQuote = null;
|
|
610
921
|
return;
|
|
@@ -625,35 +936,41 @@ export class SessionsTicketing {
|
|
|
625
936
|
}
|
|
626
937
|
this.previewRequestId++;
|
|
627
938
|
const currentId = this.previewRequestId;
|
|
939
|
+
const coupon = this.appliedCouponCode;
|
|
940
|
+
const eventId = this.eventId;
|
|
941
|
+
const controller = new AbortController();
|
|
942
|
+
this.previewController?.abort();
|
|
943
|
+
this.previewController = controller;
|
|
628
944
|
try {
|
|
629
945
|
this.previewLoading = true;
|
|
630
|
-
const response = await this.fetchPreviewQuote(
|
|
631
|
-
if (currentId === this.previewRequestId
|
|
946
|
+
const response = await this.fetchPreviewQuote(event, coupon, controller);
|
|
947
|
+
if (currentId === this.previewRequestId &&
|
|
948
|
+
eventId === this.eventId &&
|
|
949
|
+
coupon === this.appliedCouponCode) {
|
|
632
950
|
this.previewQuote = response.quote;
|
|
633
951
|
}
|
|
634
952
|
}
|
|
635
953
|
catch (_err) {
|
|
636
954
|
if (currentId === this.previewRequestId) {
|
|
637
955
|
this.previewQuote = null;
|
|
638
|
-
this.appliedCouponCode = "";
|
|
639
956
|
this.setCouponError();
|
|
640
957
|
}
|
|
641
958
|
}
|
|
642
959
|
finally {
|
|
643
960
|
if (currentId === this.previewRequestId) {
|
|
644
961
|
this.previewLoading = false;
|
|
962
|
+
if (this.previewController === controller) {
|
|
963
|
+
this.previewController = undefined;
|
|
964
|
+
}
|
|
645
965
|
}
|
|
646
966
|
}
|
|
647
967
|
}
|
|
648
|
-
fetchPreviewQuote(couponCode) {
|
|
649
|
-
|
|
650
|
-
if (!event)
|
|
651
|
-
throw new Error("Missing event");
|
|
652
|
-
return this.client.order.quote({
|
|
968
|
+
fetchPreviewQuote(event, couponCode, controller) {
|
|
969
|
+
return withTimeout(controller, (signal) => this.client.order.quote({
|
|
653
970
|
eventId: event.id,
|
|
654
971
|
items: previewQuoteItems(event),
|
|
655
972
|
couponCode,
|
|
656
|
-
});
|
|
973
|
+
}, { signal }));
|
|
657
974
|
}
|
|
658
975
|
handleCouponInput = (e) => {
|
|
659
976
|
if (!(e.target instanceof HTMLInputElement))
|
|
@@ -685,136 +1002,136 @@ export class SessionsTicketing {
|
|
|
685
1002
|
await this.removeCartCoupon();
|
|
686
1003
|
return;
|
|
687
1004
|
}
|
|
688
|
-
this.
|
|
689
|
-
const coupon = this.appliedCouponCode;
|
|
690
|
-
this.appliedCouponCode = "";
|
|
691
|
-
await this.loadEventData();
|
|
692
|
-
this.previewRequestId++;
|
|
693
|
-
this.previewLoading = false;
|
|
694
|
-
this.previewQuote = null;
|
|
695
|
-
this.trackCouponRemoved(coupon, null);
|
|
1005
|
+
await this.applyPreviewCoupon("");
|
|
696
1006
|
}
|
|
697
1007
|
async applyCartCoupon(coupon) {
|
|
698
|
-
this.
|
|
699
|
-
const currentId = this.requestId;
|
|
1008
|
+
const operationId = ++this.couponRequestId;
|
|
1009
|
+
const currentId = ++this.requestId;
|
|
700
1010
|
const previousCoupon = this.appliedCouponCode;
|
|
1011
|
+
const items = this.getQuoteRequestItems();
|
|
1012
|
+
const eventId = this.eventId;
|
|
1013
|
+
const revision = this.quoteRevisionFor(eventId, coupon, items);
|
|
1014
|
+
const controller = new AbortController();
|
|
1015
|
+
this.quoteController?.abort();
|
|
1016
|
+
this.quoteController = controller;
|
|
701
1017
|
try {
|
|
702
1018
|
this.quoteLoading = true;
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
1019
|
+
const loaded = await this.loadEventData({
|
|
1020
|
+
commit: false,
|
|
1021
|
+
couponCode: coupon,
|
|
1022
|
+
mode: "coupon",
|
|
1023
|
+
});
|
|
1024
|
+
if (!this.isCouponOperationCurrent(operationId, currentId))
|
|
709
1025
|
return;
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
1026
|
+
if (loaded.status !== "success")
|
|
1027
|
+
throw new Error("Coupon load failed");
|
|
1028
|
+
const response = await this.fetchCartQuote(eventId, items, coupon, controller);
|
|
1029
|
+
if (!this.isCouponOperationCurrent(operationId, currentId))
|
|
713
1030
|
return;
|
|
1031
|
+
this.event = loaded.event;
|
|
714
1032
|
this.appliedCouponCode = coupon;
|
|
1033
|
+
this.couponInput = coupon;
|
|
715
1034
|
this.previewQuote = null;
|
|
716
|
-
this.
|
|
717
|
-
this.
|
|
1035
|
+
this.pruneSelectedItems(loaded.event);
|
|
1036
|
+
if (revision !== this.currentQuoteRevision()) {
|
|
1037
|
+
this.invalidateQuote();
|
|
1038
|
+
this.scheduleQuoteUpdate();
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
this.applyQuote(response.quote, currentId, revision);
|
|
1042
|
+
if (coupon) {
|
|
1043
|
+
this.trackCouponApplied(coupon, response.quote);
|
|
1044
|
+
}
|
|
1045
|
+
else {
|
|
1046
|
+
this.trackCouponRemoved(previousCoupon, response.quote);
|
|
1047
|
+
}
|
|
718
1048
|
}
|
|
719
1049
|
catch (_err) {
|
|
720
|
-
if (
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
this.
|
|
724
|
-
|
|
1050
|
+
if (!this.isCouponOperationCurrent(operationId, currentId))
|
|
1051
|
+
return;
|
|
1052
|
+
this.couponInput = previousCoupon || coupon;
|
|
1053
|
+
this.setCouponError(coupon
|
|
1054
|
+
? undefined
|
|
1055
|
+
: "Failed to remove coupon. Your current discount is still applied.");
|
|
1056
|
+
if (coupon)
|
|
1057
|
+
this.trackCouponDenied(coupon);
|
|
725
1058
|
}
|
|
726
1059
|
finally {
|
|
727
1060
|
if (currentId === this.requestId) {
|
|
728
1061
|
this.quoteLoading = false;
|
|
1062
|
+
if (this.quoteController === controller) {
|
|
1063
|
+
this.quoteController = undefined;
|
|
1064
|
+
}
|
|
729
1065
|
}
|
|
730
1066
|
}
|
|
731
1067
|
}
|
|
732
1068
|
async applyPreviewCoupon(coupon) {
|
|
733
1069
|
const previousCoupon = this.appliedCouponCode;
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
this.appliedCouponCode = previousCoupon;
|
|
740
|
-
this.setCouponError();
|
|
741
|
-
this.trackCouponDenied(coupon);
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
const event = this.event;
|
|
745
|
-
if (!event)
|
|
746
|
-
return;
|
|
747
|
-
const initialQuote = this.activeInitialPreviewQuote(coupon);
|
|
748
|
-
if (initialQuote) {
|
|
749
|
-
this.appliedCouponCode = coupon;
|
|
750
|
-
this.previewQuote = initialQuote;
|
|
751
|
-
this.trackCouponApplied(coupon, initialQuote);
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
if (previewQuoteItems(event).length === 0) {
|
|
755
|
-
this.appliedCouponCode = coupon;
|
|
756
|
-
this.previewQuote = null;
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
this.previewRequestId++;
|
|
760
|
-
const currentId = this.previewRequestId;
|
|
1070
|
+
const operationId = ++this.couponRequestId;
|
|
1071
|
+
const currentId = ++this.previewRequestId;
|
|
1072
|
+
const controller = new AbortController();
|
|
1073
|
+
this.previewController?.abort();
|
|
1074
|
+
this.previewController = controller;
|
|
761
1075
|
try {
|
|
762
1076
|
this.previewLoading = true;
|
|
763
|
-
const
|
|
764
|
-
|
|
1077
|
+
const loaded = await this.loadEventData({
|
|
1078
|
+
commit: false,
|
|
1079
|
+
couponCode: coupon,
|
|
1080
|
+
mode: "coupon",
|
|
1081
|
+
});
|
|
1082
|
+
if (!this.isPreviewCouponOperationCurrent(operationId, currentId))
|
|
1083
|
+
return;
|
|
1084
|
+
if (loaded.status !== "success")
|
|
1085
|
+
throw new Error("Coupon load failed");
|
|
1086
|
+
const initialQuote = this.activeInitialPreviewQuote(coupon);
|
|
1087
|
+
const response = initialQuote || !coupon || previewQuoteItems(loaded.event).length === 0
|
|
1088
|
+
? null
|
|
1089
|
+
: await this.fetchPreviewQuote(loaded.event, coupon, controller);
|
|
1090
|
+
if (!this.isPreviewCouponOperationCurrent(operationId, currentId))
|
|
765
1091
|
return;
|
|
1092
|
+
this.event = loaded.event;
|
|
1093
|
+
this.pruneSelectedItems(loaded.event);
|
|
766
1094
|
this.appliedCouponCode = coupon;
|
|
767
|
-
this.
|
|
768
|
-
this.
|
|
1095
|
+
this.couponInput = coupon;
|
|
1096
|
+
this.previewQuote = initialQuote ?? response?.quote ?? null;
|
|
1097
|
+
if (coupon) {
|
|
1098
|
+
this.trackCouponApplied(coupon, this.previewQuote);
|
|
1099
|
+
}
|
|
1100
|
+
else {
|
|
1101
|
+
this.trackCouponRemoved(previousCoupon, null);
|
|
1102
|
+
}
|
|
769
1103
|
}
|
|
770
1104
|
catch (_err) {
|
|
771
|
-
if (
|
|
772
|
-
|
|
1105
|
+
if (!this.isPreviewCouponOperationCurrent(operationId, currentId))
|
|
1106
|
+
return;
|
|
1107
|
+
this.couponInput = previousCoupon || coupon;
|
|
1108
|
+
this.setCouponError(coupon
|
|
1109
|
+
? undefined
|
|
1110
|
+
: "Failed to remove coupon. Your current discount is still applied.");
|
|
1111
|
+
if (coupon)
|
|
773
1112
|
this.trackCouponDenied(coupon);
|
|
774
|
-
}
|
|
775
1113
|
}
|
|
776
1114
|
finally {
|
|
777
1115
|
if (currentId === this.previewRequestId) {
|
|
778
1116
|
this.previewLoading = false;
|
|
1117
|
+
if (this.previewController === controller) {
|
|
1118
|
+
this.previewController = undefined;
|
|
1119
|
+
}
|
|
779
1120
|
}
|
|
780
1121
|
}
|
|
781
1122
|
}
|
|
782
1123
|
async removeCartCoupon() {
|
|
783
|
-
this.
|
|
784
|
-
const currentId = this.requestId;
|
|
785
|
-
const coupon = this.appliedCouponCode;
|
|
786
|
-
try {
|
|
787
|
-
this.quoteLoading = true;
|
|
788
|
-
this.appliedCouponCode = "";
|
|
789
|
-
await this.loadEventData();
|
|
790
|
-
if (this.selectedItems.size === 0) {
|
|
791
|
-
this.couponInput = "";
|
|
792
|
-
this.previewQuote = null;
|
|
793
|
-
this.trackCouponRemoved(coupon, null);
|
|
794
|
-
return;
|
|
795
|
-
}
|
|
796
|
-
const response = await this.fetchCartQuote("");
|
|
797
|
-
if (currentId !== this.requestId)
|
|
798
|
-
return;
|
|
799
|
-
this.couponInput = "";
|
|
800
|
-
this.appliedCouponCode = "";
|
|
801
|
-
this.previewQuote = null;
|
|
802
|
-
this.applyQuote(response.quote, currentId);
|
|
803
|
-
this.trackCouponRemoved(coupon, response.quote);
|
|
804
|
-
}
|
|
805
|
-
catch (_err) {
|
|
806
|
-
this.handleCouponError(currentId, "Failed to remove coupon. Please try again.");
|
|
807
|
-
}
|
|
808
|
-
finally {
|
|
809
|
-
if (currentId === this.requestId) {
|
|
810
|
-
this.quoteLoading = false;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
1124
|
+
await this.applyCartCoupon("");
|
|
813
1125
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1126
|
+
isCouponOperationCurrent(operationId, requestId) {
|
|
1127
|
+
return (this.active &&
|
|
1128
|
+
operationId === this.couponRequestId &&
|
|
1129
|
+
requestId === this.requestId);
|
|
1130
|
+
}
|
|
1131
|
+
isPreviewCouponOperationCurrent(operationId, requestId) {
|
|
1132
|
+
return (this.active &&
|
|
1133
|
+
operationId === this.couponRequestId &&
|
|
1134
|
+
requestId === this.previewRequestId);
|
|
818
1135
|
}
|
|
819
1136
|
setCouponError(message = "Failed to apply coupon. Please check the code.") {
|
|
820
1137
|
this.couponError = message;
|
|
@@ -831,8 +1148,8 @@ export class SessionsTicketing {
|
|
|
831
1148
|
this.trackEvent("Coupon Applied", {
|
|
832
1149
|
coupon,
|
|
833
1150
|
products: this.getProductsForQuote(quote),
|
|
834
|
-
total: quote ?
|
|
835
|
-
value: quote ?
|
|
1151
|
+
total: quote ? quoteAmount(quote, this.event?.currency) : 0,
|
|
1152
|
+
value: quote ? quoteAmount(quote, this.event?.currency) : 0,
|
|
836
1153
|
});
|
|
837
1154
|
}
|
|
838
1155
|
trackCouponDenied(coupon) {
|
|
@@ -849,8 +1166,8 @@ export class SessionsTicketing {
|
|
|
849
1166
|
this.trackEvent("Coupon Removed", {
|
|
850
1167
|
coupon,
|
|
851
1168
|
products: this.getProductsForQuote(quote),
|
|
852
|
-
total: quote ?
|
|
853
|
-
value: quote ?
|
|
1169
|
+
total: quote ? quoteAmount(quote, this.event?.currency) : 0,
|
|
1170
|
+
value: quote ? quoteAmount(quote, this.event?.currency) : 0,
|
|
854
1171
|
});
|
|
855
1172
|
}
|
|
856
1173
|
handlePasscodeInput = (e) => {
|
|
@@ -860,33 +1177,93 @@ export class SessionsTicketing {
|
|
|
860
1177
|
};
|
|
861
1178
|
async submitPasscode(e) {
|
|
862
1179
|
e.preventDefault();
|
|
863
|
-
|
|
864
|
-
|
|
1180
|
+
const passcode = this.passcodeInput.trim();
|
|
1181
|
+
this.activePasscode = passcode;
|
|
1182
|
+
const result = await this.loadEventData({ mode: "passcode", passcode });
|
|
1183
|
+
if (result.status !== "success")
|
|
1184
|
+
return;
|
|
1185
|
+
this.error = null;
|
|
1186
|
+
this.errorKind = null;
|
|
1187
|
+
this.handleLoadedEvent(result.event);
|
|
1188
|
+
await this.updatePreviewQuote();
|
|
1189
|
+
}
|
|
1190
|
+
async retryEventLoad() {
|
|
1191
|
+
if (!this.tenantId || !this.eventId) {
|
|
1192
|
+
await this.initializeComponent();
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
const result = await this.loadEventData({ mode: "retry" });
|
|
1196
|
+
if (result.status !== "success")
|
|
1197
|
+
return;
|
|
1198
|
+
this.handleLoadedEvent(result.event);
|
|
1199
|
+
await this.updatePreviewQuote();
|
|
865
1200
|
}
|
|
866
1201
|
async initiateCheckout() {
|
|
867
1202
|
if (this.isCheckoutDisabled())
|
|
868
1203
|
return;
|
|
869
|
-
|
|
1204
|
+
const quote = this.quote;
|
|
1205
|
+
if (!quote)
|
|
870
1206
|
return;
|
|
1207
|
+
const id = ++this.checkoutRequestId;
|
|
1208
|
+
const eventId = this.eventId;
|
|
1209
|
+
const coupon = this.appliedCouponCode;
|
|
1210
|
+
const revision = this.currentQuoteRevision();
|
|
1211
|
+
const controller = new AbortController();
|
|
1212
|
+
this.checkoutController?.abort();
|
|
1213
|
+
this.checkoutController = controller;
|
|
871
1214
|
try {
|
|
872
1215
|
this.checkoutLoading = true;
|
|
873
|
-
this.
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1216
|
+
this.checkoutError = null;
|
|
1217
|
+
const idempotencyKey = await this.getCheckoutIdempotencyKey(revision);
|
|
1218
|
+
if (!this.isCheckoutRequestCurrent(id, eventId, coupon, revision))
|
|
1219
|
+
return;
|
|
1220
|
+
const result = await withTimeout(controller, (signal) => this.client.order.create({
|
|
1221
|
+
eventId,
|
|
1222
|
+
items: this.getCheckoutItems(quote),
|
|
1223
|
+
couponCode: coupon || undefined,
|
|
878
1224
|
returnUrl: this.returnUrl,
|
|
879
1225
|
channel: buildOrderChannel("elements"),
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
this.
|
|
1226
|
+
idempotencyKey,
|
|
1227
|
+
}, { signal }));
|
|
1228
|
+
if (!this.isCheckoutRequestCurrent(id, eventId, coupon, revision))
|
|
1229
|
+
return;
|
|
1230
|
+
this.trackCheckoutStarted(quote, result.order.id);
|
|
1231
|
+
this.handleCheckoutOrder(result.order, id);
|
|
883
1232
|
}
|
|
884
1233
|
catch (_err) {
|
|
885
|
-
this.
|
|
1234
|
+
if (this.isCheckoutRequestCurrent(id, eventId, coupon, revision)) {
|
|
1235
|
+
this.handleCheckoutError();
|
|
1236
|
+
}
|
|
886
1237
|
}
|
|
1238
|
+
finally {
|
|
1239
|
+
if (id === this.checkoutRequestId &&
|
|
1240
|
+
this.checkoutController === controller) {
|
|
1241
|
+
this.checkoutController = undefined;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
async getCheckoutIdempotencyKey(revision) {
|
|
1246
|
+
const current = this.checkoutIdempotencyKeys.get(revision);
|
|
1247
|
+
if (current)
|
|
1248
|
+
return current;
|
|
1249
|
+
const name = await checkoutStorageName(`${this.apiUrl}\u0000${this.tenantId}\u0000${revision}`);
|
|
1250
|
+
const stored = storedCheckoutIdempotencyKey(name);
|
|
1251
|
+
const key = stored ?? createCheckoutIdempotencyKey();
|
|
1252
|
+
this.checkoutIdempotencyKeys.set(revision, key);
|
|
1253
|
+
if (!stored)
|
|
1254
|
+
storeCheckoutIdempotencyKey(name, key);
|
|
1255
|
+
return key;
|
|
1256
|
+
}
|
|
1257
|
+
isCheckoutRequestCurrent(id, eventId, coupon, revision) {
|
|
1258
|
+
return (this.active &&
|
|
1259
|
+
id === this.checkoutRequestId &&
|
|
1260
|
+
eventId === this.eventId &&
|
|
1261
|
+
coupon === this.appliedCouponCode &&
|
|
1262
|
+
revision === this.currentQuoteRevision() &&
|
|
1263
|
+
revision === this.quoteRevision);
|
|
887
1264
|
}
|
|
888
1265
|
trackCheckoutStarted(quote, orderId) {
|
|
889
|
-
const total =
|
|
1266
|
+
const total = quoteAmount(quote, this.event?.currency);
|
|
890
1267
|
this.trackEvent("Checkout Started", {
|
|
891
1268
|
order_id: orderId,
|
|
892
1269
|
value: total,
|
|
@@ -910,7 +1287,9 @@ export class SessionsTicketing {
|
|
|
910
1287
|
sectionId: item.sectionId,
|
|
911
1288
|
}));
|
|
912
1289
|
}
|
|
913
|
-
handleCheckoutOrder(order) {
|
|
1290
|
+
handleCheckoutOrder(order, requestId) {
|
|
1291
|
+
if (!this.active || requestId !== this.checkoutRequestId)
|
|
1292
|
+
return;
|
|
914
1293
|
this.checkoutInit.emit({
|
|
915
1294
|
orderId: order.id,
|
|
916
1295
|
checkoutUrl: order.checkoutUrl,
|
|
@@ -922,8 +1301,9 @@ export class SessionsTicketing {
|
|
|
922
1301
|
this.checkoutLoading = false;
|
|
923
1302
|
}
|
|
924
1303
|
handleCheckoutError() {
|
|
925
|
-
this.
|
|
926
|
-
|
|
1304
|
+
this.checkoutError =
|
|
1305
|
+
"We couldn’t open checkout. Your tickets are still selected, so you can try again.";
|
|
1306
|
+
this.errorOccurred.emit({ error: this.checkoutError });
|
|
927
1307
|
this.checkoutLoading = false;
|
|
928
1308
|
}
|
|
929
1309
|
handleStickyCheckoutClick(state) {
|
|
@@ -938,7 +1318,9 @@ export class SessionsTicketing {
|
|
|
938
1318
|
const target = this.host.shadowRoot?.querySelector(".tickets-wrapper") ??
|
|
939
1319
|
this.host;
|
|
940
1320
|
window.scrollTo({
|
|
941
|
-
behavior: "
|
|
1321
|
+
behavior: window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true
|
|
1322
|
+
? "auto"
|
|
1323
|
+
: "smooth",
|
|
942
1324
|
top: Math.max(target.getBoundingClientRect().top +
|
|
943
1325
|
window.scrollY -
|
|
944
1326
|
STICKY_CHECKOUT_SCROLL_OFFSET_PX, 0),
|
|
@@ -985,31 +1367,55 @@ export class SessionsTicketing {
|
|
|
985
1367
|
return this.event?.sections.find((section) => section.id === item.sectionId);
|
|
986
1368
|
}
|
|
987
1369
|
formatDateTimeRange(startDate, endDate) {
|
|
988
|
-
|
|
1370
|
+
const zone = this.event ? eventTimeZone(this.event) : "UTC";
|
|
1371
|
+
try {
|
|
1372
|
+
const start = parseNaiveDateTime(startDate, zone);
|
|
1373
|
+
const end = parseNaiveDateTime(endDate, zone);
|
|
1374
|
+
const day = this.dateFormatter({
|
|
1375
|
+
day: "numeric",
|
|
1376
|
+
month: "short",
|
|
1377
|
+
timeZone: zone,
|
|
1378
|
+
weekday: "short",
|
|
1379
|
+
}).format(start);
|
|
1380
|
+
const time = this.dateFormatter({
|
|
1381
|
+
hour: "numeric",
|
|
1382
|
+
minute: "2-digit",
|
|
1383
|
+
timeZone: zone,
|
|
1384
|
+
});
|
|
1385
|
+
return `${day} · ${time.format(start)} – ${time.format(end)}`;
|
|
1386
|
+
}
|
|
1387
|
+
catch {
|
|
1388
|
+
return `${startDate} – ${endDate}`;
|
|
1389
|
+
}
|
|
989
1390
|
}
|
|
990
|
-
|
|
1391
|
+
dateFormatter(options) {
|
|
1392
|
+
try {
|
|
1393
|
+
return new Intl.DateTimeFormat(this.locale, options);
|
|
1394
|
+
}
|
|
1395
|
+
catch {
|
|
1396
|
+
return new Intl.DateTimeFormat("en-US", options);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
formatPriceValue(priceInMinorUnits) {
|
|
991
1400
|
const currency = this.event?.currency;
|
|
992
1401
|
if (!currency) {
|
|
993
1402
|
return new Intl.NumberFormat(this.locale, {
|
|
994
1403
|
style: "decimal",
|
|
995
1404
|
minimumFractionDigits: 2,
|
|
996
1405
|
maximumFractionDigits: 2,
|
|
997
|
-
}).format(
|
|
1406
|
+
}).format(majorUnits(priceInMinorUnits));
|
|
998
1407
|
}
|
|
999
1408
|
try {
|
|
1000
|
-
return formatPrice(priceInCents, currency);
|
|
1001
|
-
}
|
|
1002
|
-
catch (error) {
|
|
1003
|
-
console.error("formatPrice failed, using fallback", {
|
|
1004
|
-
error,
|
|
1005
|
-
currency,
|
|
1006
|
-
priceInCents,
|
|
1007
|
-
locale: this.locale,
|
|
1008
|
-
});
|
|
1009
1409
|
return new Intl.NumberFormat(this.locale, {
|
|
1010
1410
|
style: "currency",
|
|
1011
1411
|
currency,
|
|
1012
|
-
}).format(
|
|
1412
|
+
}).format(majorUnits(priceInMinorUnits, currency));
|
|
1413
|
+
}
|
|
1414
|
+
catch {
|
|
1415
|
+
return new Intl.NumberFormat("en-US", {
|
|
1416
|
+
style: "currency",
|
|
1417
|
+
currency,
|
|
1418
|
+
}).format(majorUnits(priceInMinorUnits, currency));
|
|
1013
1419
|
}
|
|
1014
1420
|
}
|
|
1015
1421
|
handleQuantityDecrease = async (admissionId, sectionId) => {
|
|
@@ -1025,16 +1431,47 @@ export class SessionsTicketing {
|
|
|
1025
1431
|
};
|
|
1026
1432
|
openModal = (admissionId, e) => {
|
|
1027
1433
|
e.stopPropagation();
|
|
1434
|
+
this.modalTrigger = e.currentTarget;
|
|
1435
|
+
this.modalFocusPending = true;
|
|
1028
1436
|
this.openModalAdmissionId = admissionId;
|
|
1029
1437
|
document.addEventListener("keydown", this.handleModalKeyDown);
|
|
1030
1438
|
};
|
|
1031
1439
|
closeModal = () => {
|
|
1032
1440
|
this.openModalAdmissionId = null;
|
|
1033
|
-
|
|
1441
|
+
this.modalFocusPending = false;
|
|
1442
|
+
this.modalRestorePending = true;
|
|
1443
|
+
if (typeof document !== "undefined") {
|
|
1444
|
+
document.removeEventListener("keydown", this.handleModalKeyDown);
|
|
1445
|
+
}
|
|
1034
1446
|
};
|
|
1035
1447
|
handleModalKeyDown = (e) => {
|
|
1036
1448
|
if (e.key === "Escape") {
|
|
1449
|
+
e.preventDefault();
|
|
1037
1450
|
this.closeModal();
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
if (e.key !== "Tab")
|
|
1454
|
+
return;
|
|
1455
|
+
const dialog = this.host.shadowRoot?.querySelector(".modal-content");
|
|
1456
|
+
if (!dialog)
|
|
1457
|
+
return;
|
|
1458
|
+
const focusable = Array.from(dialog.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex="-1"])'));
|
|
1459
|
+
if (focusable.length === 0) {
|
|
1460
|
+
e.preventDefault();
|
|
1461
|
+
dialog.focus();
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
const first = focusable[0];
|
|
1465
|
+
const last = focusable.at(-1);
|
|
1466
|
+
const active = this.host.shadowRoot?.activeElement;
|
|
1467
|
+
if (e.shiftKey && active === first) {
|
|
1468
|
+
e.preventDefault();
|
|
1469
|
+
last?.focus();
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
if (!e.shiftKey && active === last) {
|
|
1473
|
+
e.preventDefault();
|
|
1474
|
+
first?.focus();
|
|
1038
1475
|
}
|
|
1039
1476
|
};
|
|
1040
1477
|
handleBackdropClick = (e) => {
|
|
@@ -1054,6 +1491,7 @@ export class SessionsTicketing {
|
|
|
1054
1491
|
return getAdmissionDisplayState({
|
|
1055
1492
|
admission,
|
|
1056
1493
|
event: this.event,
|
|
1494
|
+
locale: this.locale,
|
|
1057
1495
|
section: this.getSection(sectionId),
|
|
1058
1496
|
}).maxQuantity;
|
|
1059
1497
|
}
|
|
@@ -1112,17 +1550,18 @@ export class SessionsTicketing {
|
|
|
1112
1550
|
}
|
|
1113
1551
|
if (mode === "door")
|
|
1114
1552
|
return null;
|
|
1115
|
-
return this.renderQuantityControls(admission, sectionId, state.disabled, state.maxQuantity);
|
|
1553
|
+
return this.renderQuantityControls(admission, sectionId, state.disabled || this.checkoutLoading, state.maxQuantity);
|
|
1116
1554
|
}
|
|
1117
1555
|
renderAdmission(admission, event, section, isSectionSoldOut = false, mode = "default") {
|
|
1118
1556
|
const state = getAdmissionDisplayState({
|
|
1119
1557
|
admission,
|
|
1120
1558
|
event,
|
|
1559
|
+
locale: this.locale,
|
|
1121
1560
|
section,
|
|
1122
1561
|
sectionSoldOut: isSectionSoldOut,
|
|
1123
1562
|
});
|
|
1124
1563
|
const sectionId = section?.id || null;
|
|
1125
|
-
return (h("section", { class: this.getAdmissionClassName(state, section), key: selectionKey(admission.id, sectionId),
|
|
1564
|
+
return (h("section", { class: this.getAdmissionClassName(state, section), key: selectionKey(admission.id, sectionId), "aria-label": this.getAdmissionAriaLabel(admission, section, state), "data-testid": "event-admission-card", "data-admission-id": admission.id, "data-admission-name": admission.name, "data-section-id": section?.id ?? "" }, h("div", { class: "admission-content" }, h("div", { class: "admission-details" }, this.renderAdmissionHeader(admission), this.renderAdmissionBody(admission, event, sectionId, state, mode)))));
|
|
1126
1565
|
}
|
|
1127
1566
|
getAdmissionClassName(state, section) {
|
|
1128
1567
|
return `admission-card ${state.soldOut ? "sold-out" : ""} ${state.disabled && !state.soldOut ? "unavailable" : ""} ${section ? "" : "solo-admission"}`;
|
|
@@ -1164,34 +1603,49 @@ export class SessionsTicketing {
|
|
|
1164
1603
|
return (h("div", { class: `ticket-section ${soldOut ? "section-sold-out" : ""}`, key: section.id }, this.renderSectionHeader(section, admissions), h("div", { class: "admissions-list" }, admissions.map((admission) => this.renderAdmission(admission, event, section, soldOut, mode)))));
|
|
1165
1604
|
}
|
|
1166
1605
|
renderLoadingState() {
|
|
1167
|
-
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, h("div", { class: "tickets-wrapper" }, h("h2", { class: "tickets-heading" }, "Get Tickets"), h("div", { class: "ticket-section loading-ticket-section" }, h("div", { class: "admissions-list" }, [0, 1, 2].map((item) => (h("div", { "aria-hidden": "true", class: "admission-card loading-admission", key: item })))))))));
|
|
1606
|
+
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container", role: "status", "aria-live": "polite", "aria-label": "Loading ticket options" }, h("span", { class: "sr-only" }, "Loading ticket options"), h("div", { class: "tickets-wrapper" }, h("h2", { class: "tickets-heading" }, "Get Tickets"), h("div", { class: "ticket-section loading-ticket-section" }, h("div", { class: "admissions-list" }, [0, 1, 2].map((item) => (h("div", { "aria-hidden": "true", class: "admission-card loading-admission", key: item })))))))));
|
|
1168
1607
|
}
|
|
1169
1608
|
renderErrorState() {
|
|
1170
|
-
if (this.
|
|
1609
|
+
if (this.errorKind === "access")
|
|
1171
1610
|
return this.renderPasscodeState();
|
|
1172
|
-
|
|
1611
|
+
if (this.errorKind === "not_found")
|
|
1612
|
+
return this.renderNotFoundState();
|
|
1613
|
+
const configuration = this.errorKind === "configuration";
|
|
1614
|
+
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, h("div", { class: "message-state", role: "alert", "aria-live": "assertive" }, h("strong", null, configuration
|
|
1615
|
+
? "Ticket widget needs setup"
|
|
1616
|
+
: "Tickets didn’t load"), h("span", null, configuration
|
|
1617
|
+
? "This ticket widget is missing its event or tenant details."
|
|
1618
|
+
: "Something got in the way. Try again to load the ticket options."), !configuration && (h("button", { type: "button", class: "checkout-btn", onClick: () => this.retryEventLoad(), disabled: this.retryLoading }, this.retryLoading ? "Loading tickets…" : "Try again"))))));
|
|
1619
|
+
}
|
|
1620
|
+
renderUpdatingState() {
|
|
1621
|
+
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container", role: "status", "aria-live": "polite" }, h("div", { class: "message-state" }, h("strong", null, "Loading ticket options\u2026"), h("span", null, "This should only take a moment.")))));
|
|
1622
|
+
}
|
|
1623
|
+
renderVerificationState() {
|
|
1624
|
+
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, h("section", { class: "message-state", "aria-labelledby": "ticket-verification-title", "aria-describedby": "ticket-verification-description" }, h("h2", { id: "ticket-verification-title" }, "Let\u2019s get your tickets"), h("p", { id: "ticket-verification-description" }, "We just need you to confirm before we load the ticket options."), h("button", { type: "button", class: "checkout-btn", onClick: this.continueAfterVerification }, "Load tickets")))));
|
|
1173
1625
|
}
|
|
1174
1626
|
renderPasscodeState() {
|
|
1175
|
-
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, h("form", { class: "passcode-form", onSubmit: (e) => this.submitPasscode(e) }, h("h2", null, "Enter passcode"), h("p", null, "Enter the event passcode to view tickets and event details."), h("input", { type: "text", name: "passcode", value: this.passcodeInput, onInput: this.handlePasscodeInput, autocomplete: "one-time-code", placeholder: "Passcode", "aria-label": "Passcode" }), this.passcodeDenied && this.activePasscode && (h("span", { class: "error-message" }, "
|
|
1627
|
+
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, h("form", { class: "passcode-form", onSubmit: (e) => this.submitPasscode(e) }, h("h2", null, "Enter passcode"), h("p", null, "Enter the event passcode to view tickets and event details."), h("input", { type: "text", name: "passcode", value: this.passcodeInput, onInput: this.handlePasscodeInput, autocomplete: "one-time-code", placeholder: "Passcode", "aria-label": "Passcode", "aria-invalid": this.passcodeDenied, "aria-describedby": this.passcodeDenied ? "ticket-passcode-error" : undefined, disabled: this.passcodeLoading }), this.passcodeDenied && this.activePasscode && (h("span", { id: "ticket-passcode-error", class: "error-message", role: "alert", "aria-live": "assertive" }, "That passcode didn\u2019t work. Check it and try again.")), h("button", { type: "submit", class: "checkout-btn", disabled: !this.passcodeInput.trim() || this.passcodeLoading }, this.passcodeLoading ? "Unlocking…" : "Unlock event")))));
|
|
1176
1628
|
}
|
|
1177
1629
|
renderNotFoundState() {
|
|
1178
|
-
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, h("div", { class: "message-state" }, h("strong", null, "Event not found"), h("span", null, "We couldn\u2019t find this event. The link may be incorrect or expired.")))));
|
|
1630
|
+
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, h("div", { class: "message-state", role: "alert" }, h("strong", null, "Event not found"), h("span", null, "We couldn\u2019t find this event. The link may be incorrect or expired.")))));
|
|
1179
1631
|
}
|
|
1180
1632
|
renderCouponPanel() {
|
|
1181
1633
|
const hasInput = this.couponInput.trim().length > 0;
|
|
1182
1634
|
const busy = this.quoteLoading || this.previewLoading || this.checkoutLoading;
|
|
1183
1635
|
const canApply = hasInput && !busy;
|
|
1184
|
-
return (h("div", { class: "coupon-panel" }, h("div", { class: "coupon-row" }, h("input", { id: "coupon-code", name: "coupon-code", placeholder: "Coupon Code", value: this.couponInput, onInput: this.handleCouponInput, onKeyDown: this.handleCouponKeyDown, autocomplete: "off", disabled: busy, "aria-label": "Coupon code", "aria-invalid": Boolean(this.couponError) }), this.appliedCouponCode ? (h("button", { type: "button", class: "coupon-remove", onClick: () => this.removeCoupon(), disabled: busy, "aria-label": "Remove coupon" }, "Remove")) : (h("button", { type: "button", class: "coupon-apply", onClick: () => this.applyCoupon(), disabled: !canApply }, "Apply"))), this.couponError && (h("span", { class: "error-message" }, this.couponError))));
|
|
1636
|
+
return (h("div", { class: "coupon-panel" }, h("div", { class: "coupon-row" }, h("input", { id: "coupon-code", name: "coupon-code", placeholder: "Coupon Code", value: this.appliedCouponCode || this.couponInput, onInput: this.handleCouponInput, onKeyDown: this.handleCouponKeyDown, autocomplete: "off", disabled: busy || Boolean(this.appliedCouponCode), "aria-label": "Coupon code", "aria-invalid": Boolean(this.couponError), "aria-describedby": this.couponError ? "coupon-error" : undefined }), this.appliedCouponCode ? (h("button", { type: "button", class: "coupon-remove", onClick: () => this.removeCoupon(), disabled: busy, "aria-label": "Remove coupon" }, "Remove")) : (h("button", { type: "button", class: "coupon-apply", onClick: () => this.applyCoupon(), disabled: !canApply }, "Apply"))), this.couponError && (h("span", { id: "coupon-error", class: "error-message", role: "alert", "aria-live": "assertive" }, this.couponError))));
|
|
1185
1637
|
}
|
|
1186
1638
|
isCheckoutDisabled() {
|
|
1187
1639
|
return (this.quoteLoading ||
|
|
1188
1640
|
this.checkoutLoading ||
|
|
1189
|
-
|
|
1190
|
-
|
|
1641
|
+
!hasQuoteItems(this.quote) ||
|
|
1642
|
+
this.quoteRevision !== this.currentQuoteRevision());
|
|
1191
1643
|
}
|
|
1192
1644
|
getCheckoutButtonText() {
|
|
1193
1645
|
if (this.checkoutLoading)
|
|
1194
1646
|
return "Processing...";
|
|
1647
|
+
if (this.checkoutError)
|
|
1648
|
+
return "Try checkout again";
|
|
1195
1649
|
if (this.quote)
|
|
1196
1650
|
return this.getQuoteCheckoutText();
|
|
1197
1651
|
return "Pay";
|
|
@@ -1248,13 +1702,16 @@ export class SessionsTicketing {
|
|
|
1248
1702
|
return this.formatPriceValue(total);
|
|
1249
1703
|
}
|
|
1250
1704
|
renderCheckoutActions() {
|
|
1251
|
-
return (h("div", { class: "checkout-actions" }, this.renderCouponPanel(), this.quoteError && h("div", { class: "error-message" }, this.quoteError), h("button", { type: "button", class: "checkout-btn", onClick: () => this.initiateCheckout(), disabled: this.isCheckoutDisabled(), "aria-label": this.quoteLoading ? "Updating" : undefined, "data-testid": "event-checkout-cta-desktop" }, this.renderCheckoutButtonContent())));
|
|
1705
|
+
return (h("div", { class: "checkout-actions" }, this.renderCouponPanel(), this.quoteError && (h("div", { class: "error-message", role: "alert", "aria-live": "assertive" }, this.quoteError, h("button", { type: "button", class: "coupon-apply", onClick: () => this.updateQuote(), disabled: this.quoteLoading }, "Try again"))), this.checkoutError && (h("div", { class: "error-message", role: "alert", "aria-live": "assertive" }, this.checkoutError)), h("button", { type: "button", class: "checkout-btn", onClick: () => this.initiateCheckout(), disabled: this.isCheckoutDisabled(), "aria-label": this.quoteLoading ? "Updating" : undefined, "data-testid": "event-checkout-cta-desktop" }, this.renderCheckoutButtonContent())));
|
|
1252
1706
|
}
|
|
1253
1707
|
renderStickyCheckout(state) {
|
|
1254
1708
|
if (!this.stickyCheckout)
|
|
1255
1709
|
return null;
|
|
1256
1710
|
if (!state.showTicketing)
|
|
1257
1711
|
return null;
|
|
1712
|
+
if (state.isDoorOnly) {
|
|
1713
|
+
return (h("div", { class: "sticky-checkout-bar" }, h("div", { class: "sticky-door-info" }, h("strong", null, "Tickets available at the door"), h("span", null, "Buy yours at the venue when you arrive."))));
|
|
1714
|
+
}
|
|
1258
1715
|
return (h("div", { class: "sticky-checkout-bar" }, h("button", { type: "button", class: `checkout-btn sticky-checkout-btn ${this.selectionPrompt ? "selection-prompt" : ""}`, onClick: () => this.handleStickyCheckoutClick(state), disabled: this.isStickyCheckoutDisabled(state), "aria-label": this.quoteLoading ? "Updating" : undefined }, this.renderStickyCheckoutButtonContent(state))));
|
|
1259
1716
|
}
|
|
1260
1717
|
renderStickyExternalTicketing(state) {
|
|
@@ -1274,7 +1731,7 @@ export class SessionsTicketing {
|
|
|
1274
1731
|
return null;
|
|
1275
1732
|
return (
|
|
1276
1733
|
// biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: backdrop click-to-close is a common modal pattern, keyboard handled via Escape key
|
|
1277
|
-
h("div", { class: "modal-backdrop", onClick: this.handleBackdropClick }, h("div", { class: "modal-content", role: "dialog", "aria-modal": "true", "aria-labelledby": "modal-title" }, h("button", { type: "button", class: "modal-close-btn", onClick: () => this.closeModal(), "aria-label": "Close" }, CloseIcon()), h("div", { class: "modal-header" }, h("h2", { id: "modal-title", class: "modal-title" }, admission.name), admission.subtitle && (h("p", { class: "modal-subtitle" }, admission.subtitle))), h("p", { class: "modal-description" }, admission.description))));
|
|
1734
|
+
h("div", { class: "modal-backdrop", onClick: this.handleBackdropClick }, h("div", { class: "modal-content", role: "dialog", "aria-modal": "true", "aria-labelledby": "modal-title", "aria-describedby": "modal-description", tabindex: "-1" }, h("button", { type: "button", class: "modal-close-btn", onClick: () => this.closeModal(), "aria-label": "Close" }, CloseIcon()), h("div", { class: "modal-header" }, h("h2", { id: "modal-title", class: "modal-title" }, admission.name), admission.subtitle && (h("p", { class: "modal-subtitle" }, admission.subtitle))), h("p", { id: "modal-description", class: "modal-description" }, admission.description))));
|
|
1278
1735
|
}
|
|
1279
1736
|
renderTicketSections(event) {
|
|
1280
1737
|
const state = getTicketingState(event);
|
|
@@ -1301,16 +1758,28 @@ export class SessionsTicketing {
|
|
|
1301
1758
|
return (h("div", { class: "door-alert" }, h("strong", null, "Tickets are available at ", location), h("span", null, "Online sales are closed. Tickets are available at the venue when you arrive.")));
|
|
1302
1759
|
}
|
|
1303
1760
|
loadingView() {
|
|
1304
|
-
return this.
|
|
1761
|
+
return this.initialLoading ? this.renderLoadingState() : undefined;
|
|
1305
1762
|
}
|
|
1306
1763
|
errorView() {
|
|
1307
1764
|
return this.error && !this.event ? this.renderErrorState() : undefined;
|
|
1308
1765
|
}
|
|
1766
|
+
verificationView() {
|
|
1767
|
+
return this.verificationRequired
|
|
1768
|
+
? this.renderVerificationState()
|
|
1769
|
+
: undefined;
|
|
1770
|
+
}
|
|
1309
1771
|
missingEventView() {
|
|
1310
|
-
|
|
1772
|
+
if (this.event)
|
|
1773
|
+
return undefined;
|
|
1774
|
+
if (this.eventLoading || this.retryLoading)
|
|
1775
|
+
return this.renderUpdatingState();
|
|
1776
|
+
return this.renderNotFoundState();
|
|
1311
1777
|
}
|
|
1312
1778
|
pendingView() {
|
|
1313
|
-
return this.loadingView() ??
|
|
1779
|
+
return (this.loadingView() ??
|
|
1780
|
+
this.verificationView() ??
|
|
1781
|
+
this.errorView() ??
|
|
1782
|
+
this.missingEventView());
|
|
1314
1783
|
}
|
|
1315
1784
|
renderReadyState() {
|
|
1316
1785
|
const event = this.event;
|
|
@@ -1318,9 +1787,11 @@ export class SessionsTicketing {
|
|
|
1318
1787
|
return this.renderNotFoundState();
|
|
1319
1788
|
const state = getTicketingState(event);
|
|
1320
1789
|
if (state.showExternalTicketing) {
|
|
1321
|
-
return (h(Host, { "data-theme": this.currentTheme, "data-sticky-checkout": this.stickyCheckout ? "true" : undefined }, h("div", { class: "session-services-ticketing-container" }, this.renderExternalTicketing(state)), this.renderStickyExternalTicketing(state)));
|
|
1790
|
+
return (h(Host, { "data-theme": this.currentTheme, "data-sticky-checkout": this.stickyCheckout ? "true" : undefined }, h("div", { class: "session-services-ticketing-container", "aria-busy": this.eventLoading ? "true" : "false" }, this.renderExternalTicketing(state)), this.renderStickyExternalTicketing(state)));
|
|
1322
1791
|
}
|
|
1323
|
-
return (h(Host, { "data-theme": this.currentTheme, "data-sticky-checkout": this.stickyCheckout ? "true" : undefined }, h("div", { class: "session-services-ticketing-container"
|
|
1792
|
+
return (h(Host, { "data-theme": this.currentTheme, "data-sticky-checkout": this.stickyCheckout ? "true" : undefined }, h("div", { class: "session-services-ticketing-container", "aria-busy": this.eventLoading || this.retryLoading || this.passcodeLoading
|
|
1793
|
+
? "true"
|
|
1794
|
+
: "false", "aria-hidden": this.openModalAdmissionId ? "true" : undefined, inert: this.openModalAdmissionId ? true : undefined }, this.eventLoading && (h("div", { class: "inline-status", role: "status", "aria-live": "polite" }, "Updating ticket options\u2026")), this.refreshError && (h("div", { class: "error-message", role: "alert", "aria-live": "assertive" }, h("span", null, "We couldn\u2019t refresh the latest ticket details."), h("button", { type: "button", class: "inline-retry-btn", onClick: () => this.retryEventLoad(), disabled: this.retryLoading }, this.retryLoading ? "Refreshing…" : "Try again"))), state.showTicketing
|
|
1324
1795
|
? this.renderTicketSections(event)
|
|
1325
1796
|
: this.renderUnavailableState(state), !state.isDoorOnly &&
|
|
1326
1797
|
state.showTicketing &&
|
|
@@ -1500,7 +1971,7 @@ export class SessionsTicketing {
|
|
|
1500
1971
|
"mutable": false,
|
|
1501
1972
|
"complexType": {
|
|
1502
1973
|
"original": "OrderQuote | string",
|
|
1503
|
-
"resolved": "string | { items: { id: string; kind: \"ADMISSION\" | \"ADDON\"; quantity: number; price: { net: number; total: number; gross: number; base: number; booking: number; refunded: number; discount: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; }; admissionId?: string; sectionId?: string; admission?: { name: string; capacity: number; limit: number; order: number; entryStartsAt: string; entryEndsAt: string; saleStartsAt: string; saleEndsAt: string; saleEndsBehavior: \"SALE_ENDED\" | \"SOLD_OUT\" | \"DOOR\"; price: { base: number; booking: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; }; visibility: \"ALWAYS\" | \"HIDDEN\" | \"AFTER_ADMISSION\"; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; subtitle?: string; description?: string; available?: number; sold?: number; reserved?: number; createdAt?: string; updatedAt?: string; visibleAfterAdmissionId?: string; sectionId?: string; }; section?: { name: string; capacity: number; limit: number; order: number; visibility: \"ALWAYS\" | \"
|
|
1974
|
+
"resolved": "string | { items: { id: string; kind: \"ADMISSION\" | \"ADDON\"; quantity: number; price: { net: number; total: number; gross: number; base: number; booking: number; refunded: number; discount: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; }; admissionId?: string; sectionId?: string; admission?: { name: string; capacity: number; limit: number; order: number; entryStartsAt: string; entryEndsAt: string; saleStartsAt: string; saleEndsAt: string; saleEndsBehavior: \"SALE_ENDED\" | \"SOLD_OUT\" | \"DOOR\"; price: { base: number; booking: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; override?: { booking: number; }; }; visibility: \"ALWAYS\" | \"HIDDEN\" | \"AFTER_ADMISSION\"; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; subtitle?: string; description?: string; available?: number; sold?: number; reserved?: number; createdAt?: string; updatedAt?: string; visibleAfterAdmissionId?: string; sectionId?: string; }; section?: { name: string; capacity: number; limit: number; order: number; visibility: \"ALWAYS\" | \"HIDDEN\" | \"STACKED\"; location: { createdAt: string; updatedAt: string; name: string; address: string; country: string; latitude: number; longitude: number; timeZone: string; id?: string; googlePlaceId?: string; city?: string; state?: string; postcode?: string; types?: string[]; description?: string; image?: { src: string; alt: string; width: number; height: number; }; venueId?: string; }; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; createdAt?: string; updatedAt?: string; available?: number; sold?: number; reserved?: number; }; }[]; price: { net: number; total: number; gross: number; base: number; booking: number; shipping: number; sms: number; refunded: number; discount: number; gateway: number; dispute: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; }; couponCode?: { name: string; active: boolean; couponId: string; teamId: string; id?: string; createdAt?: string; updatedAt?: string; uses?: number; }; stripeConnectAvailability?: { available: boolean; reasons: (\"FEATURE_DISABLED\" | \"UNSUPPORTED_CURRENCY\" | \"NOT_ROUTED\" | \"NO_CONNECTED_GATEWAY\" | \"CHARGES_DISABLED\" | \"INCOMPATIBLE_FEE_STRUCTURE\")[]; supportedCurrencies: (\"AUD\" | \"NZD\" | \"USD\")[]; }; }",
|
|
1504
1975
|
"references": {
|
|
1505
1976
|
"OrderQuote": {
|
|
1506
1977
|
"location": "import",
|
|
@@ -1628,8 +2099,13 @@ export class SessionsTicketing {
|
|
|
1628
2099
|
"selectedItems": {},
|
|
1629
2100
|
"quote": {},
|
|
1630
2101
|
"previewQuote": {},
|
|
1631
|
-
"
|
|
2102
|
+
"initialLoading": {},
|
|
2103
|
+
"eventLoading": {},
|
|
2104
|
+
"retryLoading": {},
|
|
2105
|
+
"passcodeLoading": {},
|
|
1632
2106
|
"error": {},
|
|
2107
|
+
"errorKind": {},
|
|
2108
|
+
"refreshError": {},
|
|
1633
2109
|
"quoteLoading": {},
|
|
1634
2110
|
"previewLoading": {},
|
|
1635
2111
|
"quoteError": {},
|
|
@@ -1641,8 +2117,10 @@ export class SessionsTicketing {
|
|
|
1641
2117
|
"passcodeDenied": {},
|
|
1642
2118
|
"selectionPrompt": {},
|
|
1643
2119
|
"checkoutLoading": {},
|
|
2120
|
+
"checkoutError": {},
|
|
1644
2121
|
"currentTheme": {},
|
|
1645
|
-
"openModalAdmissionId": {}
|
|
2122
|
+
"openModalAdmissionId": {},
|
|
2123
|
+
"verificationRequired": {}
|
|
1646
2124
|
};
|
|
1647
2125
|
}
|
|
1648
2126
|
static get events() {
|
|
@@ -1673,7 +2151,7 @@ export class SessionsTicketing {
|
|
|
1673
2151
|
},
|
|
1674
2152
|
"complexType": {
|
|
1675
2153
|
"original": "OrderQuote",
|
|
1676
|
-
"resolved": "{ items: { id: string; kind: \"ADMISSION\" | \"ADDON\"; quantity: number; price: { net: number; total: number; gross: number; base: number; booking: number; refunded: number; discount: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; }; admissionId?: string; sectionId?: string; admission?: { name: string; capacity: number; limit: number; order: number; entryStartsAt: string; entryEndsAt: string; saleStartsAt: string; saleEndsAt: string; saleEndsBehavior: \"SALE_ENDED\" | \"SOLD_OUT\" | \"DOOR\"; price: { base: number; booking: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; }; visibility: \"ALWAYS\" | \"HIDDEN\" | \"AFTER_ADMISSION\"; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; subtitle?: string; description?: string; available?: number; sold?: number; reserved?: number; createdAt?: string; updatedAt?: string; visibleAfterAdmissionId?: string; sectionId?: string; }; section?: { name: string; capacity: number; limit: number; order: number; visibility: \"ALWAYS\" | \"
|
|
2154
|
+
"resolved": "{ items: { id: string; kind: \"ADMISSION\" | \"ADDON\"; quantity: number; price: { net: number; total: number; gross: number; base: number; booking: number; refunded: number; discount: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; }; admissionId?: string; sectionId?: string; admission?: { name: string; capacity: number; limit: number; order: number; entryStartsAt: string; entryEndsAt: string; saleStartsAt: string; saleEndsAt: string; saleEndsBehavior: \"SALE_ENDED\" | \"SOLD_OUT\" | \"DOOR\"; price: { base: number; booking: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; override?: { booking: number; }; }; visibility: \"ALWAYS\" | \"HIDDEN\" | \"AFTER_ADMISSION\"; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; subtitle?: string; description?: string; available?: number; sold?: number; reserved?: number; createdAt?: string; updatedAt?: string; visibleAfterAdmissionId?: string; sectionId?: string; }; section?: { name: string; capacity: number; limit: number; order: number; visibility: \"ALWAYS\" | \"HIDDEN\" | \"STACKED\"; location: { createdAt: string; updatedAt: string; name: string; address: string; country: string; latitude: number; longitude: number; timeZone: string; id?: string; googlePlaceId?: string; city?: string; state?: string; postcode?: string; types?: string[]; description?: string; image?: { src: string; alt: string; width: number; height: number; }; venueId?: string; }; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; createdAt?: string; updatedAt?: string; available?: number; sold?: number; reserved?: number; }; }[]; price: { net: number; total: number; gross: number; base: number; booking: number; shipping: number; sms: number; refunded: number; discount: number; gateway: number; dispute: number; currency: \"AUD\" | \"AED\" | \"ALL\" | \"AMD\" | \"ANG\" | \"AOA\" | \"ARS\" | \"AWG\" | \"AZN\" | \"BAM\" | \"BBD\" | \"BDT\" | \"BGN\" | \"BHD\" | \"BIF\" | \"BMD\" | \"BND\" | \"BOB\" | \"BRL\" | \"BSD\" | \"BTN\" | \"BWP\" | \"BZD\" | \"CAD\" | \"CHF\" | \"CLP\" | \"CNY\" | \"COP\" | \"CRC\" | \"CVE\" | \"CZK\" | \"DJF\" | \"DKK\" | \"DOP\" | \"DZD\" | \"EGP\" | \"ERN\" | \"ETB\" | \"EUR\" | \"FJD\" | \"FKP\" | \"GBP\" | \"GEL\" | \"GHS\" | \"GIP\" | \"GMD\" | \"GNF\" | \"GTQ\" | \"GYD\" | \"HKD\" | \"HNL\" | \"HUF\" | \"IDR\" | \"ILS\" | \"INR\" | \"ISK\" | \"JMD\" | \"JOD\" | \"JPY\" | \"KES\" | \"KGS\" | \"KHR\" | \"KMF\" | \"KRW\" | \"KWD\" | \"KYD\" | \"KZT\" | \"LAK\" | \"LBP\" | \"LKR\" | \"LRD\" | \"LSL\" | \"LYD\" | \"MAD\" | \"MDL\" | \"MGA\" | \"MKD\" | \"MNT\" | \"MOP\" | \"MUR\" | \"MVR\" | \"MWK\" | \"MXN\" | \"MYR\" | \"NAD\" | \"NGN\" | \"NIO\" | \"NOK\" | \"NPR\" | \"NZD\" | \"OMR\" | \"PAB\" | \"PEN\" | \"PGK\" | \"PHP\" | \"PKR\" | \"PLN\" | \"PYG\" | \"QAR\" | \"RON\" | \"RSD\" | \"RWF\" | \"SAR\" | \"SBD\" | \"SCR\" | \"SEK\" | \"SGD\" | \"SHP\" | \"SLE\" | \"SRD\" | \"SVC\" | \"SZL\" | \"THB\" | \"TJS\" | \"TMT\" | \"TND\" | \"TOP\" | \"TRY\" | \"TTD\" | \"TWD\" | \"TZS\" | \"UAH\" | \"UGX\" | \"USD\" | \"UYU\" | \"UZS\" | \"VND\" | \"VUV\" | \"WST\" | \"XAF\" | \"XCD\" | \"XOF\" | \"XPF\" | \"ZAR\" | \"ZMW\" | \"ZWL\"; }; couponCode?: { name: string; active: boolean; couponId: string; teamId: string; id?: string; createdAt?: string; updatedAt?: string; uses?: number; }; stripeConnectAvailability?: { available: boolean; reasons: (\"FEATURE_DISABLED\" | \"UNSUPPORTED_CURRENCY\" | \"NOT_ROUTED\" | \"NO_CONNECTED_GATEWAY\" | \"CHARGES_DISABLED\" | \"INCOMPATIBLE_FEE_STRUCTURE\")[]; supportedCurrencies: (\"AUD\" | \"NZD\" | \"USD\")[]; }; }",
|
|
1677
2155
|
"references": {
|
|
1678
2156
|
"OrderQuote": {
|
|
1679
2157
|
"location": "import",
|