@session-services/web-elements 0.1.30 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/dist/cjs/loader.cjs.js +1 -1
  2. package/dist/cjs/session-services-elements.cjs.js +1 -1
  3. package/dist/cjs/session-services-ticketing.cjs.entry.js +1645 -22994
  4. package/dist/collection/components/session-services-ticketing/bot-detection.js +83 -0
  5. package/dist/collection/components/session-services-ticketing/bot-detection.test.js +74 -0
  6. package/dist/collection/components/session-services-ticketing/session-services-ticketing.css +164 -36
  7. package/dist/collection/components/session-services-ticketing/session-services-ticketing.js +697 -219
  8. package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.js +25 -4
  9. package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.test.js +19 -2
  10. package/dist/collection/components/session-services-ticketing/session-services-ticketing.test.js +540 -19
  11. package/dist/components/session-services-ticketing.js +1 -1
  12. package/dist/esm/loader.js +1 -1
  13. package/dist/esm/session-services-elements.js +1 -1
  14. package/dist/esm/session-services-ticketing.entry.js +1645 -22994
  15. package/dist/session-services-elements/p-d7264f6b.entry.js +1 -0
  16. package/dist/session-services-elements/session-services-elements.esm.js +1 -1
  17. package/dist/types/components/session-services-ticketing/bot-detection.d.ts +13 -0
  18. package/dist/types/components/session-services-ticketing/bot-detection.test.d.ts +1 -0
  19. package/dist/types/components/session-services-ticketing/session-services-ticketing.d.ts +52 -5
  20. package/dist/types/components/session-services-ticketing/session-services-ticketing.logic.d.ts +8 -1
  21. package/package.json +4 -4
  22. 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, formatPrice, } from "@session-services/sdk/analytics";
3
- import { formatNaiveDateTime } from "@session-services/utils/naive-datetime";
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 { admissionPriceSavings, discountCouponFromSearch, getAdmissionDisplayState, getAdmissionPriceDisplay, getTicketingState, parseSelectionKey, previewQuoteItems, priceTotal, quoteItemsBySelection, selectionKey, topLevelTicketItems, visibleAdmissions, } from "./session-services-ticketing.logic";
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: priceTotal(item.price) / 100,
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
- loading = true;
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
- async componentWillLoad() {
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
- async propsChanged() {
150
- await this.initializeComponent();
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
- async couponCodeChanged() {
153
- await this.setInitialCoupon();
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
- if (typeof window !== "undefined") {
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
- // Clean up modal listener if component unmounts while modal is open
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.loading = false;
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.setupTheme();
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 (!coupon) {
210
- this.appliedCouponCode = "";
211
- await this.updateQuote();
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: priceTotal(adm.price) / 100,
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
- this.loading = true;
360
- this.error = null;
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
- const response = await this.client.event.get({
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.passcodeDenied = isPasscodeAccessError(err);
374
- this.error = errorMessage(err, "Failed to load event data");
375
- this.errorOccurred.emit({ error: this.error });
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.loading = false;
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.quote = null;
388
- this.quoteError = null;
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 / 100,
437
- price: lowestPrice / 100,
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: priceTotal(change.admission.price) / 100,
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 currentId = this.requestId;
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(this.appliedCouponCode);
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: this.eventId,
581
- items: this.getQuoteRequestItems(),
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: quoteTotal(this.quote) / 100,
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(this.appliedCouponCode);
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
- const event = this.event;
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.couponInput = "";
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.requestId++;
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
- this.appliedCouponCode = coupon;
704
- await this.loadEventData();
705
- if (currentId !== this.requestId)
706
- return;
707
- if (this.selectedItems.size === 0) {
708
- await this.applyPreviewCoupon(coupon);
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
- const response = await this.fetchCartQuote(coupon);
712
- if (currentId !== this.requestId)
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.applyQuote(response.quote, currentId);
717
- this.trackCouponApplied(coupon, response.quote);
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 (currentId === this.requestId) {
721
- this.appliedCouponCode = previousCoupon;
722
- }
723
- this.handleCouponError(currentId);
724
- this.trackCouponDenied(coupon);
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
- this.appliedCouponCode = coupon;
735
- try {
736
- await this.loadEventData();
737
- }
738
- catch (_err) {
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 response = await this.fetchPreviewQuote(coupon);
764
- if (currentId !== this.previewRequestId)
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.previewQuote = response.quote;
768
- this.trackCouponApplied(coupon, response.quote);
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 (currentId === this.previewRequestId) {
772
- this.setCouponError();
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.requestId++;
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
- handleCouponError(requestId, message) {
815
- if (requestId !== this.requestId)
816
- return;
817
- this.setCouponError(message);
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 ? quoteTotal(quote) / 100 : 0,
835
- value: quote ? quoteTotal(quote) / 100 : 0,
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 ? quoteTotal(quote) / 100 : 0,
853
- value: quote ? quoteTotal(quote) / 100 : 0,
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
- this.activePasscode = this.passcodeInput.trim();
864
- await this.loadEventData();
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
- if (!this.quote)
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.quoteError = null;
874
- const result = await this.client.order.create({
875
- eventId: this.eventId,
876
- items: this.getCheckoutItems(this.quote),
877
- couponCode: this.appliedCouponCode || undefined,
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
- this.trackCheckoutStarted(this.quote, result.order.id);
882
- this.handleCheckoutOrder(result.order);
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.handleCheckoutError();
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 = quoteTotal(quote) / 100;
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.quoteError = "Failed to initiate checkout. Please try again.";
926
- this.errorOccurred.emit({ error: this.quoteError });
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: "smooth",
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
- return `${formatNaiveDateTime(startDate, "EEE, MMM d · h:mm a")} - ${formatNaiveDateTime(endDate, "h:mm a")}`;
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
- formatPriceValue(priceInCents) {
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(priceInCents / 100);
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(priceInCents / 100);
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
- document.removeEventListener("keydown", this.handleModalKeyDown);
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), tabindex: state.disabled ? -1 : 0, "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)))));
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.passcodeDenied)
1609
+ if (this.errorKind === "access")
1171
1610
  return this.renderPasscodeState();
1172
- return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, h("div", { class: "message-state" }, h("strong", null, "Tickets Couldn\u2019t Load"), h("span", null, "We couldn\u2019t load ticket details. Try again in a moment."), h("button", { type: "button", class: "retry-button", onClick: () => this.loadEventData() }, "Try Again")))));
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" }, "Incorrect passcode")), h("button", { type: "submit", class: "checkout-btn", disabled: !this.passcodeInput.trim() }, "Unlock event")))));
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: "inline-retry-btn", 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
- Boolean(this.quoteError) ||
1190
- !hasQuoteItems(this.quote));
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.loading ? this.renderLoadingState() : undefined;
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
- return this.event ? undefined : this.renderNotFoundState();
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() ?? this.errorView() ?? this.missingEventView();
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" }, state.showTicketing
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\" | \"STACKED\" | \"HIDDEN\"; 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; }; }",
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
- "loading": {},
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\" | \"STACKED\" | \"HIDDEN\"; 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; }; }",
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",