@ticketlayer/elements 0.3.3 → 1.0.0

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 (179) hide show
  1. package/README.md +3 -2
  2. package/dist/cjs/{index-BpfoKd9Q.js → index-DanPUX0s.js} +1 -1
  3. package/dist/cjs/index.cjs.js +2 -1
  4. package/dist/cjs/loader.cjs.js +2 -2
  5. package/dist/cjs/ticket-selector-BYVL13k_.js +3623 -0
  6. package/dist/cjs/ticketlayer-elements.cjs.js +2 -2
  7. package/dist/cjs/tl-buy-modal.cjs.entry.js +4 -4
  8. package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +89 -71
  9. package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +62 -31
  10. package/dist/cjs/tl-cart-badge.cjs.entry.js +75 -62
  11. package/dist/cjs/tl-cart-drawer.cjs.entry.js +52 -24
  12. package/dist/cjs/tl-cart.cjs.entry.js +72 -98
  13. package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +52 -30
  14. package/dist/cjs/tl-event-card.cjs.entry.js +44 -105
  15. package/dist/cjs/tl-event-list.cjs.entry.js +73 -36
  16. package/dist/cjs/tl-login.cjs.entry.js +75 -141
  17. package/dist/cjs/tl-my-orders.cjs.entry.js +53 -92
  18. package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +205 -641
  19. package/dist/cjs/tl-order-confirmation.cjs.entry.js +72 -105
  20. package/dist/cjs/tl-order-tickets_2.cjs.entry.js +50 -76
  21. package/dist/cjs/tl-promo-code.cjs.entry.js +51 -47
  22. package/dist/cjs/tl-seat-map.cjs.entry.js +4 -4
  23. package/dist/cjs/web-query-Ce73jB4k.js +16 -0
  24. package/dist/collection/components/tl-buy-modal/tl-buy-modal.css +12 -19
  25. package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +2 -2
  26. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +41 -4
  27. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +105 -76
  28. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.css +2 -2
  29. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +73 -30
  30. package/dist/collection/components/tl-cart/tl-cart.css +52 -51
  31. package/dist/collection/components/tl-cart/tl-cart.js +82 -101
  32. package/dist/collection/components/tl-cart-badge/tl-cart-badge.css +11 -11
  33. package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +62 -62
  34. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.css +6 -6
  35. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +56 -24
  36. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.css +2 -2
  37. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +61 -29
  38. package/dist/collection/components/tl-event-card/event-card-adapter.js +11 -23
  39. package/dist/collection/components/tl-event-card/tl-event-card.css +22 -22
  40. package/dist/collection/components/tl-event-card/tl-event-card.js +51 -84
  41. package/dist/collection/components/tl-event-list/tl-event-list.css +17 -17
  42. package/dist/collection/components/tl-event-list/tl-event-list.js +82 -38
  43. package/dist/collection/components/tl-login/tl-login.css +26 -26
  44. package/dist/collection/components/tl-login/tl-login.js +83 -147
  45. package/dist/collection/components/tl-my-orders/tl-my-orders.css +40 -40
  46. package/dist/collection/components/tl-my-orders/tl-my-orders.js +58 -96
  47. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.css +54 -65
  48. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +137 -516
  49. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.css +38 -38
  50. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +81 -113
  51. package/dist/collection/components/tl-order-tickets/tl-order-tickets.css +9 -9
  52. package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +57 -92
  53. package/dist/collection/components/tl-promo-code/tl-promo-code.css +25 -25
  54. package/dist/collection/components/tl-promo-code/tl-promo-code.js +55 -50
  55. package/dist/collection/components/tl-seat-map/tl-seat-map.css +10 -10
  56. package/dist/collection/components/tl-seat-map/tl-seat-map.js +2 -2
  57. package/dist/collection/components/tl-ticket/tl-ticket.css +13 -13
  58. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.css +74 -49
  59. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +95 -135
  60. package/dist/collection/utils/cart-totals.js +9 -0
  61. package/dist/collection/utils/order-summary.js +15 -99
  62. package/dist/collection/utils/testing/deep-text.js +29 -0
  63. package/dist/collection/utils/testing/fixtures.js +290 -0
  64. package/dist/collection/utils/testing/mock-sdk.js +13 -2
  65. package/dist/collection/utils/web-query.js +12 -0
  66. package/dist/collection/utils/web-storage.js +22 -0
  67. package/dist/components/ticket-selector.js +1 -0
  68. package/dist/components/tl-buy-modal.js +1 -1
  69. package/dist/components/tl-buy-tickets-button.js +1 -1
  70. package/dist/components/tl-buy-tickets-wrapper2.js +1 -1
  71. package/dist/components/tl-cart-badge.js +1 -1
  72. package/dist/components/tl-cart-drawer.js +1 -1
  73. package/dist/components/tl-cart2.js +1 -1
  74. package/dist/components/tl-checkout-wrapper2.js +1 -1
  75. package/dist/components/tl-event-card2.js +1 -1
  76. package/dist/components/tl-event-list.js +1 -1
  77. package/dist/components/tl-login2.js +1 -1
  78. package/dist/components/tl-my-orders.js +1 -1
  79. package/dist/components/tl-occurrence-selector2.js +1 -1
  80. package/dist/components/tl-order-confirmation.js +1 -1
  81. package/dist/components/tl-order-tickets2.js +1 -1
  82. package/dist/components/tl-promo-code.js +1 -1
  83. package/dist/components/tl-seat-map.js +1 -1
  84. package/dist/components/tl-ticket-selector2.js +1 -1
  85. package/dist/components/tl-ticket2.js +1 -1
  86. package/dist/components/web-query.js +1 -0
  87. package/dist/elements.docs.json +36 -36
  88. package/dist/esm/{index-CNX8hZa8.js → index-imfxp765.js} +1 -1
  89. package/dist/esm/index.js +2 -1
  90. package/dist/esm/loader.js +3 -3
  91. package/dist/esm/ticket-selector-BwChnHVC.js +3606 -0
  92. package/dist/esm/ticketlayer-elements.js +3 -3
  93. package/dist/esm/tl-buy-modal.entry.js +4 -4
  94. package/dist/esm/tl-buy-tickets-button.entry.js +90 -72
  95. package/dist/esm/tl-buy-tickets-wrapper.entry.js +63 -32
  96. package/dist/esm/tl-cart-badge.entry.js +76 -63
  97. package/dist/esm/tl-cart-drawer.entry.js +53 -25
  98. package/dist/esm/tl-cart.entry.js +73 -99
  99. package/dist/esm/tl-checkout-wrapper.entry.js +53 -31
  100. package/dist/esm/tl-event-card.entry.js +45 -106
  101. package/dist/esm/tl-event-list.entry.js +74 -37
  102. package/dist/esm/tl-login.entry.js +76 -142
  103. package/dist/esm/tl-my-orders.entry.js +54 -93
  104. package/dist/esm/tl-occurrence-selector_2.entry.js +206 -642
  105. package/dist/esm/tl-order-confirmation.entry.js +73 -106
  106. package/dist/esm/tl-order-tickets_2.entry.js +51 -77
  107. package/dist/esm/tl-promo-code.entry.js +52 -48
  108. package/dist/esm/tl-seat-map.entry.js +4 -4
  109. package/dist/esm/web-query-Bdbb2lk5.js +14 -0
  110. package/dist/ticketlayer-elements/index.esm.js +1 -1
  111. package/dist/ticketlayer-elements/p-45923d2c.entry.js +1 -0
  112. package/dist/ticketlayer-elements/p-492adc77.entry.js +1 -0
  113. package/dist/ticketlayer-elements/p-4cfc9e53.entry.js +1 -0
  114. package/dist/ticketlayer-elements/p-5135630e.entry.js +1 -0
  115. package/dist/ticketlayer-elements/p-5bbce3b5.entry.js +1 -0
  116. package/dist/ticketlayer-elements/p-7d547721.entry.js +1 -0
  117. package/dist/ticketlayer-elements/p-7fe7b6ba.entry.js +1 -0
  118. package/dist/ticketlayer-elements/p-8122f0c1.entry.js +1 -0
  119. package/dist/ticketlayer-elements/p-98d40869.entry.js +1 -0
  120. package/dist/ticketlayer-elements/p-Bdbb2lk5.js +1 -0
  121. package/dist/ticketlayer-elements/p-BwChnHVC.js +1 -0
  122. package/dist/ticketlayer-elements/p-abec290a.entry.js +1 -0
  123. package/dist/ticketlayer-elements/p-ac79a3f6.entry.js +1 -0
  124. package/dist/ticketlayer-elements/p-d01f4429.entry.js +1 -0
  125. package/dist/ticketlayer-elements/p-d7ffaf46.entry.js +1 -0
  126. package/dist/ticketlayer-elements/p-e7170f3c.entry.js +1 -0
  127. package/dist/ticketlayer-elements/p-e787e862.entry.js +1 -0
  128. package/dist/ticketlayer-elements/p-f1ff931f.entry.js +1 -0
  129. package/dist/ticketlayer-elements/p-imfxp765.js +3 -0
  130. package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -1
  131. package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -1
  132. package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +40 -8
  133. package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +20 -1
  134. package/dist/types/components/tl-cart/tl-cart.d.ts +40 -32
  135. package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +22 -7
  136. package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +28 -5
  137. package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +18 -1
  138. package/dist/types/components/tl-event-card/event-card-adapter.d.ts +9 -20
  139. package/dist/types/components/tl-event-card/tl-event-card.d.ts +25 -28
  140. package/dist/types/components/tl-event-list/tl-event-list.d.ts +46 -6
  141. package/dist/types/components/tl-login/tl-login.d.ts +68 -22
  142. package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +30 -12
  143. package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +73 -161
  144. package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +48 -15
  145. package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +29 -31
  146. package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +28 -10
  147. package/dist/types/components/tl-ticket/tl-ticket.d.ts +2 -11
  148. package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +61 -32
  149. package/dist/types/components.d.ts +302 -62
  150. package/dist/types/utils/cart-totals.d.ts +10 -0
  151. package/dist/types/utils/order-summary.d.ts +16 -65
  152. package/dist/types/utils/testing/deep-text.d.ts +13 -0
  153. package/dist/types/utils/testing/fixtures.d.ts +225 -0
  154. package/dist/types/utils/testing/mock-sdk.d.ts +14 -1
  155. package/dist/types/utils/web-query.d.ts +18 -0
  156. package/dist/types/utils/web-storage.d.ts +16 -0
  157. package/elements.catalogue.json +676 -55
  158. package/package.json +14 -6
  159. package/dist/cjs/order-summary-ZMkJ5bez.js +0 -116
  160. package/dist/components/order-summary.js +0 -1
  161. package/dist/esm/order-summary-BGekdjN9.js +0 -103
  162. package/dist/ticketlayer-elements/p-0b1d16f8.entry.js +0 -1
  163. package/dist/ticketlayer-elements/p-1471f6d8.entry.js +0 -1
  164. package/dist/ticketlayer-elements/p-1c51cbc1.entry.js +0 -1
  165. package/dist/ticketlayer-elements/p-5169f68b.entry.js +0 -1
  166. package/dist/ticketlayer-elements/p-895ba1f3.entry.js +0 -1
  167. package/dist/ticketlayer-elements/p-8d5722e1.entry.js +0 -1
  168. package/dist/ticketlayer-elements/p-917d8e09.entry.js +0 -1
  169. package/dist/ticketlayer-elements/p-9629029a.entry.js +0 -1
  170. package/dist/ticketlayer-elements/p-BGekdjN9.js +0 -1
  171. package/dist/ticketlayer-elements/p-CNX8hZa8.js +0 -3
  172. package/dist/ticketlayer-elements/p-a22714d0.entry.js +0 -1
  173. package/dist/ticketlayer-elements/p-bf73044b.entry.js +0 -1
  174. package/dist/ticketlayer-elements/p-e02079fa.entry.js +0 -1
  175. package/dist/ticketlayer-elements/p-e04ace15.entry.js +0 -1
  176. package/dist/ticketlayer-elements/p-e6fab1d5.entry.js +0 -1
  177. package/dist/ticketlayer-elements/p-f826b753.entry.js +0 -1
  178. package/dist/ticketlayer-elements/p-fa639d88.entry.js +0 -1
  179. package/dist/ticketlayer-elements/p-fbaca948.entry.js +0 -1
@@ -1,8 +1,9 @@
1
- import { r as registerInstance, c as createEvent, h } from './index-CNX8hZa8.js';
2
- import { o as onSDKEvent, w as whenSDK } from './sdk-connector-CqhvfyKV.js';
3
- import { e as errorMessage, r as resolveOccurrence, o as orderEventName, c as formatDateTime, d as orderCustomerEmail, a as formatPrice, g as ticketItems } from './order-summary-BGekdjN9.js';
1
+ import { r as registerInstance, c as createEvent, h } from './index-imfxp765.js';
2
+ import { w as whenSDK, g as getSDK } from './sdk-connector-CqhvfyKV.js';
3
+ import { w as webQuery } from './web-query-Bdbb2lk5.js';
4
+ import { c as createElement, o as orderConfirmationCore } from './ticket-selector-BwChnHVC.js';
4
5
 
5
- const tlOrderConfirmationCss = () => `:host{display:block;font-family:var(--tl-font-family, system-ui, sans-serif);color:var(--tl-foreground, #111827)}.confirmation{text-align:center}.check{width:4rem;height:4rem;margin:0 auto var(--tl-spacing-4, 1rem);display:flex;align-items:center;justify-content:center;border-radius:9999px;background:var(--tl-success, #10b981);color:var(--tl-success-foreground, #ffffff)}.check svg{width:2rem;height:2rem}.title{margin:0 0 var(--tl-spacing-2, 0.5rem);font-size:1.875rem;font-weight:700;line-height:1.2}.lead{margin:0 0 var(--tl-spacing-6, 1.5rem);color:var(--tl-muted-foreground, #6b7280)}.error{margin:0 0 var(--tl-spacing-6, 1.5rem);font-size:0.875rem;color:var(--tl-error, #ef4444)}.summary{text-align:left;padding:var(--tl-spacing-6, 1.5rem);margin-bottom:var(--tl-spacing-6, 1.5rem);background:var(--tl-muted, #f9fafb);border:1px solid var(--tl-border, #e5e7eb);border-radius:var(--tl-radius-lg, 0.75rem)}.rows{display:grid;gap:var(--tl-spacing-2, 0.5rem);margin:0 0 var(--tl-spacing-4, 1rem);padding:0 0 var(--tl-spacing-4, 1rem);border-bottom:1px solid var(--tl-border, #e5e7eb)}.row{display:flex;justify-content:space-between;gap:var(--tl-spacing-4, 1rem);font-size:0.875rem}.row dt{color:var(--tl-muted-foreground, #6b7280)}.row dd{margin:0;text-align:right;overflow-wrap:anywhere}.reference{font-family:var(--tl-font-family-mono, ui-monospace, monospace);font-weight:600}.lines{list-style:none;margin:0;padding:0;display:grid;gap:var(--tl-spacing-2, 0.5rem)}.line{display:flex;justify-content:space-between;gap:var(--tl-spacing-4, 1rem);font-size:0.875rem}.totals{margin-top:var(--tl-spacing-3, 0.75rem);padding-top:var(--tl-spacing-3, 0.75rem);border-top:1px solid var(--tl-border, #e5e7eb);display:grid;gap:var(--tl-spacing-1, 0.25rem);font-size:0.875rem}.total-row{display:flex;justify-content:space-between;color:var(--tl-muted-foreground, #6b7280)}.total-row.discount{color:var(--tl-success, #10b981)}.total-row.total{margin-top:var(--tl-spacing-1, 0.25rem);font-size:1rem;font-weight:600;color:var(--tl-foreground, #111827)}.tickets{text-align:left}.subtitle{margin:0 0 var(--tl-spacing-4, 1rem);font-size:1.125rem;font-weight:600}.hint{margin:var(--tl-spacing-3, 0.75rem) 0 0;font-size:0.75rem;color:var(--tl-muted-foreground, #6b7280)}.skeleton-line,.skeleton-block{background:var(--tl-secondary, #f3f4f6);border-radius:var(--tl-radius-md, 0.5rem);animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;margin:0 auto var(--tl-spacing-3, 0.75rem)}.skeleton-line{height:1rem}.skeleton-line.short{width:40%;height:1.75rem}.skeleton-line.long{width:70%}.skeleton-block{height:8rem}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}`;
6
+ const tlOrderConfirmationCss = () => `:host{display:block;font-family:var(--tl-font-family);color:var(--tl-foreground)}.confirmation{text-align:center}.check{width:4rem;height:4rem;margin:0 auto var(--tl-spacing-4);display:flex;align-items:center;justify-content:center;border-radius:9999px;background:var(--tl-success);color:var(--tl-success-foreground)}.check svg{width:2rem;height:2rem}.title{margin:0 0 var(--tl-spacing-2);font-size:1.875rem;font-weight:700;line-height:1.2}.lead{margin:0 0 var(--tl-spacing-6);color:var(--tl-muted-foreground)}.error{margin:0 0 var(--tl-spacing-6);font-size:0.875rem;color:var(--tl-error)}.summary{text-align:left;padding:var(--tl-spacing-6);margin-bottom:var(--tl-spacing-6);background:var(--tl-muted);border:1px solid var(--tl-border);border-radius:var(--tl-radius-lg)}.rows{display:grid;gap:var(--tl-spacing-2);margin:0 0 var(--tl-spacing-4);padding:0 0 var(--tl-spacing-4);border-bottom:1px solid var(--tl-border)}.row{display:flex;justify-content:space-between;gap:var(--tl-spacing-4);font-size:0.875rem}.row dt{color:var(--tl-muted-foreground)}.row dd{margin:0;text-align:right;overflow-wrap:anywhere}.reference{font-family:var(--tl-font-family-mono);font-weight:600}.lines{list-style:none;margin:0;padding:0;display:grid;gap:var(--tl-spacing-2)}.line{display:flex;justify-content:space-between;gap:var(--tl-spacing-4);font-size:0.875rem}.totals{margin-top:var(--tl-spacing-3);padding-top:var(--tl-spacing-3);border-top:1px solid var(--tl-border);display:grid;gap:var(--tl-spacing-1);font-size:0.875rem}.total-row{display:flex;justify-content:space-between;color:var(--tl-muted-foreground)}.total-row.discount{color:var(--tl-success)}.total-row.total{margin-top:var(--tl-spacing-1);font-size:1rem;font-weight:600;color:var(--tl-foreground)}.tickets{text-align:left}.subtitle{margin:0 0 var(--tl-spacing-4);font-size:1.125rem;font-weight:600}.hint{margin:var(--tl-spacing-3) 0 0;font-size:0.75rem;color:var(--tl-muted-foreground)}.skeleton-line,.skeleton-block{background:var(--tl-secondary);border-radius:var(--tl-radius-md);animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;margin:0 auto var(--tl-spacing-3)}.skeleton-line{height:1rem}.skeleton-line.short{width:40%;height:1.75rem}.skeleton-line.long{width:70%}.skeleton-block{height:8rem}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}`;
6
7
 
7
8
  const TlOrderConfirmation = class {
8
9
  constructor(hostRef) {
@@ -13,129 +14,95 @@ const TlOrderConfirmation = class {
13
14
  this.showTickets = true;
14
15
  /** Columns for the ticket grid. */
15
16
  this.columns = 2;
16
- this.order = null;
17
- this.resolvedOrderId = null;
18
- this.loading = false;
19
- this.error = null;
20
- this.paymentFailed = false;
21
- this.occurrence = null;
22
- this.signedInCustomer = null;
23
- }
24
- watchOrderId(next) {
25
- this.resolvedOrderId = next || this.orderIdFromUrl();
26
- this.load();
17
+ this.element = createElement(orderConfirmationCore, {
18
+ client: () => getSDK(),
19
+ clientReady: () => whenSDK(),
20
+ query: webQuery,
21
+ emit: (event, detail) => this.forward(event, detail),
22
+ });
23
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
24
+ this.view = this.element.view;
27
25
  }
28
- async componentWillLoad() {
29
- const params = this.urlParams();
30
- // Stripe returns with ?redirect_status=; anything but succeeded is not paid.
31
- const redirect = params?.get('redirect_status');
32
- this.paymentFailed = !!redirect && redirect !== 'succeeded';
33
- this.resolvedOrderId = this.orderId || this.orderIdFromUrl();
34
- if (this.resolvedOrderId && !this.paymentFailed) {
35
- await this.load();
36
- }
26
+ /**
27
+ * A new order id is a new subject, and the core resolves it again the way
28
+ * the mount did - the prop first, the link second. Not a remount: the
29
+ * `checkout:completed` subscription belongs to the element rather than to
30
+ * the order it is showing.
31
+ */
32
+ watchOrderId() {
33
+ this.syncProps();
34
+ this.element.actions.orderIdChanged();
37
35
  }
38
- componentDidLoad() {
39
- // A page that mounts before checkout: pick the order up when the SDK completes it.
40
- this.unsubscribe = onSDKEvent('checkout:completed', (payload) => {
41
- const order = payload;
42
- if (!order?.id)
43
- return;
44
- this.paymentFailed = false;
45
- this.order = order;
46
- this.resolvedOrderId = order.id;
47
- this.tlOrderLoaded.emit({ order });
48
- // Re-read for tickets, which are issued after the order is created.
49
- this.load();
36
+ syncProps() {
37
+ this.element.setProps({
38
+ orderId: this.orderId,
39
+ customerEmail: this.customerEmail,
40
+ showTickets: this.showTickets,
41
+ columns: this.columns,
50
42
  });
51
43
  }
44
+ connectedCallback() {
45
+ this.syncProps();
46
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
47
+ this.view = view;
48
+ }));
49
+ this.view = this.element.view;
50
+ this.teardown = this.element.mount();
51
+ }
52
+ /**
53
+ * Off screen: run the mount's teardown, which drops the
54
+ * `checkout:completed` subscription. Not `destroy()`: Stencil calls this for
55
+ * a DOM move as well as a removal, and a destroyed core never mounts again.
56
+ */
52
57
  disconnectedCallback() {
58
+ this.teardown?.();
59
+ this.teardown = undefined;
53
60
  this.unsubscribe?.();
61
+ this.unsubscribe = undefined;
54
62
  }
55
- urlParams() {
56
- if (typeof window === 'undefined' || !window.location?.search)
57
- return null;
58
- return new URLSearchParams(window.location.search);
59
- }
60
- orderIdFromUrl() {
61
- return this.urlParams()?.get('orderId') || null;
62
- }
63
- async load() {
64
- const id = this.resolvedOrderId;
65
- if (!id)
66
- return;
67
- try {
68
- this.loading = !this.order;
69
- this.error = null;
70
- const sdk = await whenSDK();
71
- const orders = sdk.getOrdersManager?.();
72
- if (!orders)
73
- throw new Error('Orders are not available');
74
- const order = (await orders.get(id));
75
- if (!order)
76
- throw new Error('Order not found');
77
- this.order = order;
78
- this.signedInCustomer = sdk.getAuthManager?.()?.customer ?? null;
79
- this.tlOrderLoaded.emit({ order });
80
- this.resolveEvent(sdk, order);
81
- }
82
- catch (err) {
83
- console.error('[tl-order-confirmation] Failed to load order:', err);
84
- this.error = errorMessage(err, 'Failed to load your order');
85
- this.tlError.emit({ message: this.error });
86
- }
87
- finally {
88
- this.loading = false;
89
- }
90
- }
91
- // The order view has no event name or date; look them up from the first
92
- // ticket's occurrence. Non-blocking and best effort.
93
- async resolveEvent(sdk, order) {
94
- const occurrenceId = order.tickets?.find((t) => t.eventOccurrenceId)?.eventOccurrenceId;
95
- if (!occurrenceId)
96
- return;
97
- try {
98
- const resolved = await resolveOccurrence(sdk, occurrenceId);
99
- if (resolved && this.order?.id === order.id)
100
- this.occurrence = resolved;
101
- }
102
- catch {
103
- // Leave the event line at what the order gives us.
104
- }
63
+ forward(event, detail) {
64
+ if (event === 'tlOrderLoaded')
65
+ this.tlOrderLoaded.emit(detail);
66
+ else if (event === 'tlError')
67
+ this.tlError.emit(detail);
105
68
  }
106
- renderRows(order) {
107
- const email = this.customerEmail || orderCustomerEmail(order, this.signedInCustomer);
108
- const eventName = this.occurrence?.eventName || orderEventName(order);
109
- const startsAt = this.occurrence?.startsAt;
110
- return (h("dl", { class: "rows" }, h("div", { class: "row" }, h("dt", null, "Order reference"), h("dd", { class: "reference" }, order.orderNumber)), email && (h("div", { class: "row" }, h("dt", null, "Email"), h("dd", null, email))), eventName && (h("div", { class: "row" }, h("dt", null, "Event"), h("dd", null, eventName))), startsAt && (h("div", { class: "row" }, h("dt", null, "Date"), h("dd", null, formatDateTime(startsAt))))));
69
+ renderRows() {
70
+ return (h("dl", { class: "rows" }, this.view.derived.rows.map((row) => (h("div", { class: "row", key: row.label }, h("dt", null, row.label), h("dd", { class: row.reference ? 'reference' : undefined }, row.value))))));
111
71
  }
112
- renderTotals(order) {
113
- const c = order.currency || 'GBP';
114
- const fees = Number(order.totalFees) || 0;
115
- const tax = Number(order.totalTax) || 0;
116
- const discounts = Number(order.totalDiscounts) || 0;
117
- return (h("div", { class: "totals" }, h("div", { class: "total-row" }, h("span", null, "Subtotal"), h("span", null, formatPrice(order.subtotal, c))), fees > 0 && (h("div", { class: "total-row" }, h("span", null, "Fees"), h("span", null, formatPrice(order.totalFees, c)))), tax > 0 && (h("div", { class: "total-row" }, h("span", null, "Tax"), h("span", null, formatPrice(order.totalTax, c)))), discounts > 0 && (h("div", { class: "total-row discount" }, h("span", null, "Discount"), h("span", null, "-", formatPrice(order.totalDiscounts, c)))), h("div", { class: "total-row total" }, h("span", null, "Total"), h("span", null, formatPrice(order.total, c)))));
72
+ // Money comes from the core's orderTotals: the booking fee is its own row
73
+ // named after the channel's fee profile, the subtotal is the ticket lines
74
+ // alone (the order's own `subtotal` folds the fee line into it) and the
75
+ // total is the order's figure, so what is shown is what was charged.
76
+ renderTotals() {
77
+ const { strings, derived } = this.view;
78
+ return (h("div", { class: "totals" }, h("div", { class: "total-row" }, h("span", null, strings.subtotalRow), h("span", null, derived.subtotalLabel)), derived.totals.showFee && (h("div", { class: "total-row fee" }, h("span", null, derived.totals.feeLabel), h("span", null, derived.feeAmountLabel))), derived.showTaxRow && (h("div", { class: "total-row" }, h("span", null, strings.taxRow), h("span", null, derived.taxLabel))), derived.showDiscountRow && (h("div", { class: "total-row discount" }, h("span", null, strings.discountRow), h("span", null, "-", derived.discountLabel))), h("div", { class: "total-row total" }, h("span", null, strings.totalRow), h("span", null, derived.totalLabel))));
118
79
  }
119
80
  render() {
120
- if (this.paymentFailed) {
121
- return (h("div", { class: "confirmation" }, h("h1", { class: "title" }, "Payment not completed"), h("p", { class: "lead" }, "Your payment wasn't completed. Your cart is still saved, so you can try again.")));
81
+ const { status, state, strings, derived } = this.view;
82
+ if (status === 'payment-failed') {
83
+ return (h("div", { class: "confirmation" }, h("h1", { class: "title" }, strings.paymentFailedTitle), h("p", { class: "lead" }, strings.paymentFailedLead)));
122
84
  }
123
85
  // Nothing to show yet: no order in props, URL or from checkout.
124
- if (!this.resolvedOrderId && !this.order) {
86
+ if (status === 'idle') {
125
87
  return null;
126
88
  }
127
- if (this.loading) {
89
+ if (status === 'loading') {
128
90
  return (h("div", { class: "confirmation" }, h("div", { class: "skeleton-line short" }), h("div", { class: "skeleton-line long" }), h("div", { class: "skeleton-block" })));
129
91
  }
130
- const order = this.order;
131
- const orderId = order?.id || this.resolvedOrderId;
132
- return (h("div", { class: "confirmation" }, h("div", { class: "check", "aria-hidden": "true" }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M5 13l4 4L19 7" }))), h("h1", { class: "title" }, "You're going!"), h("p", { class: "lead" }, order?.orderNumber
133
- ? `Order ${order.orderNumber} is confirmed. Your tickets are on their way to your email.`
134
- : 'Your order is confirmed. Your tickets are on their way to your email.'), this.error && !order && (h("p", { class: "error" }, "We couldn't load your order details yet. ", this.error)), order && Array.isArray(order.items) && (h("div", { class: "summary" }, this.renderRows(order), h("ul", { class: "lines" }, ticketItems(order).map((item, i) => (h("li", { class: "line", key: i }, h("span", { class: "line-name" }, item.quantity, " x ", item.name), h("span", { class: "line-price" }, formatPrice(item.subtotal, order.currency)))))), this.renderTotals(order))), this.showTickets && order && (h("div", { class: "tickets" }, h("h2", { class: "subtitle" }, "Your tickets"), h("tl-order-tickets", { orderId: orderId, columns: this.columns }), h("p", { class: "hint" }, "Show these QR codes at the door for entry. Use Download PDF on a ticket to save it.")))));
92
+ return (h("div", { class: "confirmation" }, h("div", { class: "check", "aria-hidden": "true" }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M5 13l4 4L19 7" }))), h("h1", { class: "title" }, strings.title), h("p", { class: "lead" }, derived.leadText), derived.showError && (h("p", { class: "error" }, strings.errorPrefix, state.error)), derived.showSummary && (h("div", { class: "summary" }, this.renderRows(), h("ul", { class: "lines" }, derived.lines.map((line, i) => (h("li", { class: "line", key: i }, h("span", { class: "line-name" }, line.label), h("span", { class: "line-price" }, line.priceLabel))))), this.renderTotals())), derived.showTickets && (h("div", { class: "tickets" }, h("h2", { class: "subtitle" }, strings.ticketsTitle), h("tl-order-tickets", { orderId: derived.ticketsOrderId, columns: this.columns }), h("p", { class: "hint" }, strings.ticketsHint)))));
135
93
  }
136
94
  static get watchers() { return {
137
95
  "orderId": [{
138
96
  "watchOrderId": 0
97
+ }],
98
+ "customerEmail": [{
99
+ "syncProps": 0
100
+ }],
101
+ "showTickets": [{
102
+ "syncProps": 0
103
+ }],
104
+ "columns": [{
105
+ "syncProps": 0
139
106
  }]
140
107
  }; }
141
108
  };
@@ -1,104 +1,78 @@
1
- import { r as registerInstance, h } from './index-CNX8hZa8.js';
2
- import { w as whenSDK } from './sdk-connector-CqhvfyKV.js';
1
+ import { r as registerInstance, h } from './index-imfxp765.js';
2
+ import { w as whenSDK, g as getSDK } from './sdk-connector-CqhvfyKV.js';
3
+ import { c as createElement, i as orderTicketsCore } from './ticket-selector-BwChnHVC.js';
3
4
 
4
- const tlOrderTicketsCss = () => `:host{display:block;font-family:var(--tl-font-family, system-ui, sans-serif)}.grid{display:grid;gap:var(--tl-spacing-4, 1rem);grid-template-columns:1fr}@media (min-width: 480px){.grid.cols-2{grid-template-columns:repeat(2, 1fr)}.grid.cols-3{grid-template-columns:repeat(3, 1fr)}}.state{padding:var(--tl-spacing-6, 1.5rem);text-align:center;color:var(--tl-muted-foreground, #6b7280);font-size:0.875rem}.state.error{color:var(--tl-error, #dc2626)}.state.issuing{display:flex;align-items:center;justify-content:center;gap:var(--tl-spacing-2, 0.5rem)}.spinner{width:0.875rem;height:0.875rem;border:2px solid var(--tl-border, #e5e7eb);border-top-color:var(--tl-primary, #6366f1);border-radius:50%;animation:tl-spin 0.7s linear infinite;flex:none}@keyframes tl-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.spinner{animation:none;border-top-color:var(--tl-border, #e5e7eb)}}`;
5
+ const tlOrderTicketsCss = () => `:host{display:block;font-family:var(--tl-font-family)}.grid{display:grid;gap:var(--tl-spacing-4);grid-template-columns:1fr}@media (min-width: 480px){.grid.cols-2{grid-template-columns:repeat(2, 1fr)}.grid.cols-3{grid-template-columns:repeat(3, 1fr)}}.state{padding:var(--tl-spacing-6);text-align:center;color:var(--tl-muted-foreground);font-size:0.875rem}.state.error{color:var(--tl-error)}.state.issuing{display:flex;align-items:center;justify-content:center;gap:var(--tl-spacing-2)}.spinner{width:0.875rem;height:0.875rem;border:2px solid var(--tl-border);border-top-color:var(--tl-primary);border-radius:50%;animation:tl-spin 0.7s linear infinite;flex:none}@keyframes tl-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.spinner{animation:none;border-top-color:var(--tl-border)}}`;
5
6
 
6
7
  const TlOrderTickets = class {
7
8
  constructor(hostRef) {
8
9
  registerInstance(this, hostRef);
9
10
  /** Columns in the grid (default 2). */
10
11
  this.columns = 2;
11
- this.tickets = [];
12
- this.loading = true;
13
- this.error = null;
14
- /** The order loaded, but has no tickets yet and we are still waiting. */
15
- this.issuing = false;
16
- this.poll = 0;
12
+ this.element = createElement(orderTicketsCore, {
13
+ client: () => getSDK(),
14
+ clientReady: () => whenSDK(),
15
+ emit: (event, detail) => this.forward(event, detail),
16
+ });
17
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
18
+ this.view = this.element.view;
17
19
  }
20
+ /** A different order: forget this one and read again, from the top. */
18
21
  watchOrder() {
19
- this.reset();
20
- this.load();
22
+ this.syncProps();
23
+ this.element.actions.orderIdChanged();
21
24
  }
22
- async componentWillLoad() {
23
- await this.load();
25
+ syncProps() {
26
+ this.element.setProps({ orderId: this.orderId, columns: this.columns });
27
+ }
28
+ connectedCallback() {
29
+ this.syncProps();
30
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
31
+ this.view = view;
32
+ }));
33
+ this.view = this.element.view;
34
+ this.teardown = this.element.mount();
24
35
  }
36
+ /**
37
+ * Off screen: run the mount's teardown, which is what stops the polling.
38
+ * Not `destroy()`: Stencil calls this for a DOM move as well as a removal,
39
+ * and a destroyed core never mounts again.
40
+ */
25
41
  disconnectedCallback() {
26
- this.stopPolling();
42
+ this.teardown?.();
43
+ this.teardown = undefined;
44
+ this.unsubscribe?.();
45
+ this.unsubscribe = undefined;
27
46
  }
28
- reset() {
29
- this.stopPolling();
30
- this.poll = 0;
31
- this.tickets = [];
32
- this.issuing = false;
33
- }
34
- stopPolling() {
35
- if (this.timer !== undefined) {
36
- clearTimeout(this.timer);
37
- this.timer = undefined;
38
- }
39
- }
40
- async load() {
41
- try {
42
- this.error = null;
43
- const sdk = await whenSDK();
44
- const orders = sdk.getOrdersManager?.();
45
- if (!orders)
46
- throw new Error('Tickets are not available');
47
- const order = (await orders.get(this.orderId));
48
- this.tickets = order?.tickets ?? [];
49
- if (this.tickets.length > 0) {
50
- // Arrived. Stop asking.
51
- this.issuing = false;
52
- this.stopPolling();
53
- }
54
- else {
55
- const delay = TlOrderTickets.POLL_DELAYS_MS[this.poll];
56
- // `undefined` once the schedule runs out: give up waiting, and say so.
57
- this.issuing = delay !== undefined;
58
- if (delay !== undefined) {
59
- this.poll += 1;
60
- this.stopPolling();
61
- this.timer = setTimeout(() => this.load(), delay);
62
- }
63
- }
64
- }
65
- catch (err) {
66
- // A failed request is a different thing from an order with no tickets,
67
- // and stops the polling: retrying a 403 a dozen times helps nobody.
68
- this.stopPolling();
69
- this.issuing = false;
70
- this.error = err instanceof Error ? err.message : 'Failed to load tickets';
71
- }
72
- finally {
73
- this.loading = false;
74
- }
47
+ /** The core declares no events, so nothing ever reaches here. */
48
+ forward(_event, _detail) {
49
+ // Intentionally empty: tl-order-tickets announces nothing to its host.
75
50
  }
76
51
  render() {
77
- if (this.loading) {
78
- return h("div", { class: "state" }, "Loading tickets\u2026");
52
+ const { status, state, strings, derived } = this.view;
53
+ if (status === 'loading') {
54
+ return h("div", { class: "state" }, strings.loading);
55
+ }
56
+ if (status === 'error') {
57
+ return h("div", { class: "state error" }, state.error);
79
58
  }
80
- if (this.error) {
81
- return h("div", { class: "state error" }, this.error);
59
+ if (status === 'issuing') {
60
+ return (h("div", { class: "state issuing", role: "status", "aria-live": "polite" }, h("span", { class: "spinner", "aria-hidden": "true" }), strings.preparing));
82
61
  }
83
- if (this.tickets.length === 0) {
84
- return this.issuing ? (h("div", { class: "state issuing", role: "status", "aria-live": "polite" }, h("span", { class: "spinner", "aria-hidden": "true" }), "Preparing your tickets\u2026")) : (h("div", { class: "state", role: "status" }, "Your tickets are taking longer than usual. They will be emailed to you as soon as they are ready."));
62
+ if (status === 'delayed') {
63
+ return (h("div", { class: "state", role: "status" }, strings.delayed));
85
64
  }
86
- return (h("div", { class: `grid cols-${this.columns}` }, this.tickets.map((t) => (h("tl-ticket", { key: t.id, ticket: t, orderId: this.orderId })))));
65
+ return (h("div", { class: `grid cols-${derived.columns}` }, derived.tickets.map((t) => (h("tl-ticket", { key: t.id, ticket: t, orderId: derived.orderId })))));
87
66
  }
88
67
  static get watchers() { return {
89
68
  "orderId": [{
90
69
  "watchOrder": 0
70
+ }],
71
+ "columns": [{
72
+ "syncProps": 0
91
73
  }]
92
74
  }; }
93
75
  };
94
- /**
95
- * Gaps between polls, in milliseconds. Front-loaded because issuance
96
- * usually lands in the first few seconds, then widening so a genuinely
97
- * stuck order is not hammered. Sums to roughly one minute.
98
- */
99
- TlOrderTickets.POLL_DELAYS_MS = [
100
- 1000, 1500, 2000, 3000, 4000, 5000, 6000, 8000, 10000, 12000,
101
- ];
102
76
  TlOrderTickets.style = tlOrderTicketsCss();
103
77
 
104
78
  function getDefaultExportFromCjs (x) {
@@ -3176,7 +3150,7 @@ function requireBrowser () {
3176
3150
  var browserExports = requireBrowser();
3177
3151
  var QRCode = /*@__PURE__*/getDefaultExportFromCjs(browserExports);
3178
3152
 
3179
- const tlTicketCss = () => `:host{display:block;font-family:var(--tl-font-family, system-ui, sans-serif)}.ticket{border:1px solid var(--tl-border, #e5e7eb);border-radius:var(--tl-radius-lg, 0.75rem);background:var(--tl-card, #ffffff);padding:var(--tl-spacing-5, 1.25rem);text-align:center}.qr-wrap{position:relative;width:160px;height:160px;margin:0 auto}.qr{width:160px;height:160px}.qr svg{width:100%;height:100%;display:block}.qr.inactive{opacity:0.3}.stamp{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);padding:0.25rem 0.5rem;border-radius:var(--tl-radius-sm, 0.25rem);background:rgba(17, 24, 39, 0.85);color:#ffffff;font-size:0.7rem;font-weight:700;text-transform:uppercase;letter-spacing:0.05em}.name{margin:1rem 0 0;font-weight:600;color:var(--tl-foreground, #111827)}.meta{margin:0.125rem 0 0;font-size:0.875rem;color:var(--tl-muted-foreground, #6b7280)}.barcode{margin:0.25rem 0 0;font-family:ui-monospace, monospace;font-size:0.625rem;color:var(--tl-muted-foreground, #9ca3af);word-break:break-all}.download{margin-top:0.75rem;padding:0.375rem 0.75rem;border:1px solid var(--tl-border, #e5e7eb);border-radius:var(--tl-radius-md, 0.5rem);background:transparent;color:var(--tl-foreground, #111827);font-size:0.875rem;font-weight:500;cursor:pointer}.download:hover{background:var(--tl-muted, #f3f4f6)}.download:disabled{opacity:0.5;cursor:default}`;
3153
+ const tlTicketCss = () => `:host{display:block;font-family:var(--tl-font-family)}.ticket{border:1px solid var(--tl-border);border-radius:var(--tl-radius-lg);background:var(--tl-card);padding:var(--tl-spacing-5);text-align:center}.qr-wrap{position:relative;width:160px;height:160px;margin:0 auto}.qr{width:160px;height:160px}.qr svg{width:100%;height:100%;display:block}.qr.inactive{opacity:0.3}.stamp{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);padding:0.25rem 0.5rem;border-radius:var(--tl-radius-sm);background:rgba(17, 24, 39, 0.85);color:#ffffff;font-size:0.7rem;font-weight:700;text-transform:uppercase;letter-spacing:0.05em}.name{margin:1rem 0 0;font-weight:600;color:var(--tl-foreground)}.meta{margin:0.125rem 0 0;font-size:0.875rem;color:var(--tl-muted-foreground)}.barcode{margin:0.25rem 0 0;font-family:ui-monospace, monospace;font-size:0.625rem;color:var(--tl-muted-foreground);word-break:break-all}.download{margin-top:0.75rem;padding:0.375rem 0.75rem;border:1px solid var(--tl-border);border-radius:var(--tl-radius-md);background:transparent;color:var(--tl-foreground);font-size:0.875rem;font-weight:500;cursor:pointer}.download:hover{background:var(--tl-muted)}.download:disabled{opacity:0.5;cursor:default}`;
3180
3154
 
3181
3155
  const TlTicket = class {
3182
3156
  constructor(hostRef) {
@@ -1,8 +1,8 @@
1
- import { r as registerInstance, c as createEvent, h } from './index-CNX8hZa8.js';
2
- import { w as whenSDK } from './sdk-connector-CqhvfyKV.js';
3
- import { e as errorMessage, i as isAuthError } from './order-summary-BGekdjN9.js';
1
+ import { r as registerInstance, c as createEvent, h } from './index-imfxp765.js';
2
+ import { w as whenSDK, g as getSDK } from './sdk-connector-CqhvfyKV.js';
3
+ import { c as createElement, p as promoCodeCore } from './ticket-selector-BwChnHVC.js';
4
4
 
5
- const tlPromoCodeCss = () => `:host{display:block;font-family:var(--tl-font-family, system-ui, sans-serif);color:var(--tl-foreground, #111827)}.field{display:flex;gap:var(--tl-spacing-2, 0.5rem)}.input{flex:1;min-width:0;padding:var(--tl-spacing-3, 0.75rem);font:inherit;font-size:0.875rem;color:var(--tl-foreground, #111827);background:var(--tl-background, #ffffff);border:1px solid var(--tl-border, #e5e7eb);border-radius:var(--tl-radius-md, 0.5rem);text-transform:uppercase}.input::placeholder{text-transform:none;color:var(--tl-muted-foreground, #6b7280)}.input:focus{outline:2px solid var(--tl-primary, #6366f1);outline-offset:1px}.apply{padding:var(--tl-spacing-3, 0.75rem) var(--tl-spacing-4, 1rem);font:inherit;font-size:0.875rem;font-weight:500;color:var(--tl-primary-foreground, #ffffff);background:var(--tl-primary, #6366f1);border:none;border-radius:var(--tl-radius-md, 0.5rem);cursor:pointer;white-space:nowrap}.apply:hover:not(:disabled){opacity:0.9}.apply:disabled,.input:disabled{opacity:0.6;cursor:not-allowed}.error{margin:var(--tl-spacing-2, 0.5rem) 0 0;font-size:0.8125rem;color:var(--tl-error, #ef4444)}.applied{display:flex;align-items:center;justify-content:space-between;gap:var(--tl-spacing-3, 0.75rem);padding:var(--tl-spacing-3, 0.75rem);font-size:0.875rem;background:var(--tl-muted, #f9fafb);border:1px solid var(--tl-border, #e5e7eb);border-radius:var(--tl-radius-md, 0.5rem)}.applied-text{display:flex;align-items:center;gap:var(--tl-spacing-2, 0.5rem)}.icon{width:1rem;height:1rem;flex-shrink:0;color:var(--tl-success, #10b981)}.code{font-family:var(--tl-font-family-mono, ui-monospace, monospace)}.link{padding:0;font:inherit;font-size:0.8125rem;color:var(--tl-primary, #6366f1);background:none;border:none;cursor:pointer;white-space:nowrap}.link:hover{text-decoration:underline}`;
5
+ const tlPromoCodeCss = () => `:host{display:block;font-family:var(--tl-font-family);color:var(--tl-foreground)}.field{display:flex;gap:var(--tl-spacing-2)}.input{flex:1;min-width:0;padding:var(--tl-spacing-3);font:inherit;font-size:0.875rem;color:var(--tl-foreground);background:var(--tl-background);border:1px solid var(--tl-border);border-radius:var(--tl-radius-md);text-transform:uppercase}.input::placeholder{text-transform:none;color:var(--tl-muted-foreground)}.input:focus{outline:2px solid var(--tl-primary);outline-offset:1px}.apply{padding:var(--tl-spacing-3) var(--tl-spacing-4);font:inherit;font-size:0.875rem;font-weight:500;color:var(--tl-primary-foreground);background:var(--tl-primary);border:none;border-radius:var(--tl-radius-md);cursor:pointer;white-space:nowrap}.apply:hover:not(:disabled){opacity:0.9}.apply:disabled,.input:disabled{opacity:0.6;cursor:not-allowed}.error{margin:var(--tl-spacing-2) 0 0;font-size:0.8125rem;color:var(--tl-error)}.applied{display:flex;align-items:center;justify-content:space-between;gap:var(--tl-spacing-3);padding:var(--tl-spacing-3);font-size:0.875rem;background:var(--tl-muted);border:1px solid var(--tl-border);border-radius:var(--tl-radius-md)}.applied-text{display:flex;align-items:center;gap:var(--tl-spacing-2)}.icon{width:1rem;height:1rem;flex-shrink:0;color:var(--tl-success)}.code{font-family:var(--tl-font-family-mono)}.link{padding:0;font:inherit;font-size:0.8125rem;color:var(--tl-primary);background:none;border:none;cursor:pointer;white-space:nowrap}.link:hover{text-decoration:underline}`;
6
6
 
7
7
  const TlPromoCode = class {
8
8
  constructor(hostRef) {
@@ -13,60 +13,64 @@ const TlPromoCode = class {
13
13
  this.placeholder = 'Promo or presale code';
14
14
  /** Apply button label. */
15
15
  this.buttonLabel = 'Apply';
16
- this.code = '';
17
- this.applying = false;
18
- this.applied = null;
19
- this.error = null;
16
+ this.element = createElement(promoCodeCore, {
17
+ client: () => getSDK(),
18
+ clientReady: () => whenSDK(),
19
+ emit: (event, detail) => this.forward(event, detail),
20
+ });
21
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
22
+ this.view = this.element.view;
20
23
  this.handleInput = (e) => {
21
- this.code = e.target.value;
22
- this.error = null;
24
+ this.element.actions.setCode(e.target.value);
23
25
  };
24
26
  this.handleSubmit = (e) => {
25
27
  e.preventDefault();
26
- this.apply();
27
- };
28
- this.reset = () => {
29
- this.applied = null;
30
- this.error = null;
28
+ this.element.actions.apply();
31
29
  };
30
+ this.reset = () => this.element.actions.reset();
32
31
  }
33
- async apply() {
34
- const code = this.code.trim();
35
- if (!code) {
36
- this.error = 'Enter a code';
37
- return;
38
- }
39
- this.applying = true;
40
- this.error = null;
41
- try {
42
- const sdk = await whenSDK();
43
- const presale = sdk.getPresaleManager?.();
44
- if (!presale)
45
- throw new Error('Promo codes are not available');
46
- const result = await presale.redeem(code);
47
- // The SDK refreshed the cart on redeem; read the cached copy for the payload.
48
- const cart = await sdk.getCartManager?.()?.get().catch(() => null) ?? null;
49
- this.applied = { code: code.toUpperCase(), ...result, cart };
50
- this.code = '';
51
- this.tlPromoApplied.emit(this.applied);
52
- }
53
- catch (err) {
54
- console.error('[tl-promo-code] Redeem failed:', err);
55
- this.error = isAuthError(err)
56
- ? 'Sign in to use this code'
57
- : errorMessage(err, 'That code could not be applied');
58
- this.tlError.emit({ message: this.error });
59
- }
60
- finally {
61
- this.applying = false;
62
- }
32
+ syncProps() {
33
+ this.element.setProps({ placeholder: this.placeholder, buttonLabel: this.buttonLabel });
34
+ }
35
+ /**
36
+ * This core has nothing to do on mount, and the harness calls `mount()`
37
+ * anyway: a harness never asks what a core's lifecycle means (TKT-197).
38
+ */
39
+ connectedCallback() {
40
+ this.syncProps();
41
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
42
+ this.view = view;
43
+ }));
44
+ this.view = this.element.view;
45
+ this.teardown = this.element.mount();
46
+ }
47
+ disconnectedCallback() {
48
+ this.teardown?.();
49
+ this.teardown = undefined;
50
+ this.unsubscribe?.();
51
+ this.unsubscribe = undefined;
52
+ }
53
+ forward(event, detail) {
54
+ if (event === 'tlPromoApplied')
55
+ this.tlPromoApplied.emit(detail);
56
+ else if (event === 'tlError')
57
+ this.tlError.emit(detail);
63
58
  }
64
59
  render() {
65
- if (this.applied) {
66
- return (h("div", { class: "applied", role: "status" }, h("span", { class: "applied-text" }, h("svg", { class: "icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "aria-hidden": "true" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M5 13l4 4L19 7" })), "Code ", h("strong", { class: "code" }, this.applied.code), this.applied.alreadyHeld ? ' was already applied' : ' applied'), h("button", { type: "button", class: "link", onClick: this.reset }, "Use a different code")));
60
+ const { status, state, strings, derived } = this.view;
61
+ if (status === 'applied') {
62
+ return (h("div", { class: "applied", role: "status" }, h("span", { class: "applied-text" }, h("svg", { class: "icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "aria-hidden": "true" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M5 13l4 4L19 7" })), strings.codePrefix, h("strong", { class: "code" }, derived.appliedCode), derived.appliedSuffix), h("button", { type: "button", class: "link", onClick: this.reset }, strings.useDifferent)));
67
63
  }
68
- return (h("form", { class: "promo", onSubmit: this.handleSubmit }, h("div", { class: "field" }, h("input", { class: "input", type: "text", autocomplete: "off", autoCapitalize: "characters", spellcheck: false, "aria-label": this.placeholder, placeholder: this.placeholder, value: this.code, disabled: this.applying, onInput: this.handleInput }), h("button", { type: "submit", class: "apply", disabled: this.applying }, this.applying ? 'Applying…' : this.buttonLabel)), this.error && (h("p", { class: "error", role: "alert" }, this.error))));
64
+ return (h("form", { class: "promo", onSubmit: this.handleSubmit }, h("div", { class: "field" }, h("input", { class: "input", type: "text", autocomplete: "off", autoCapitalize: "characters", spellcheck: false, "aria-label": this.placeholder, placeholder: this.placeholder, value: state.code, disabled: derived.disabled, onInput: this.handleInput }), h("button", { type: "submit", class: "apply", disabled: derived.disabled }, derived.actionLabel)), state.error && (h("p", { class: "error", role: "alert" }, state.error))));
69
65
  }
66
+ static get watchers() { return {
67
+ "placeholder": [{
68
+ "syncProps": 0
69
+ }],
70
+ "buttonLabel": [{
71
+ "syncProps": 0
72
+ }]
73
+ }; }
70
74
  };
71
75
  TlPromoCode.style = tlPromoCodeCss();
72
76
 
@@ -1,7 +1,7 @@
1
- import { r as registerInstance, c as createEvent, h, a as getElement } from './index-CNX8hZa8.js';
1
+ import { r as registerInstance, c as createEvent, h, a as getElement } from './index-imfxp765.js';
2
2
  import { h as hasSDK, g as getSDK } from './sdk-connector-CqhvfyKV.js';
3
3
 
4
- const tlSeatMapCss = () => `:host{display:block;font-family:var(--tl-font-family, system-ui, -apple-system, sans-serif)}.seat-map-container{position:relative;overflow:hidden;border-radius:var(--tl-radius-md, 0.5rem);background:var(--tl-color-background, #ffffff)}.seat-map-iframe{width:100%;height:100%;border:none}.seat-map-iframe.hidden{display:none}.loading{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--tl-color-background, #ffffff);color:var(--tl-color-foreground, #0f172a);gap:1rem}.loading p{margin:0;font-size:0.875rem;color:var(--tl-color-muted-foreground, #64748b)}.spinner{width:40px;height:40px;border:3px solid var(--tl-color-border, #e2e8f0);border-top-color:var(--tl-color-primary, #e64b12);border-radius:50%;animation:tl-seat-map-spin 1s linear infinite}@keyframes tl-seat-map-spin{to{transform:rotate(360deg)}}.error{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--tl-color-background, #ffffff);color:var(--tl-color-error, #ef4444);padding:1.5rem;text-align:center}.error-title{margin:0 0 0.5rem;font-size:1.125rem;font-weight:600}.error-message{margin:0;font-size:0.875rem;opacity:0.9}`;
4
+ const tlSeatMapCss = () => `:host{display:block;font-family:var(--tl-font-family)}.seat-map-container{position:relative;overflow:hidden;border-radius:var(--tl-radius-md);background:var(--tl-color-background)}.seat-map-iframe{width:100%;height:100%;border:none}.seat-map-iframe.hidden{display:none}.loading{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--tl-color-background);color:var(--tl-color-foreground);gap:1rem}.loading p{margin:0;font-size:0.875rem;color:var(--tl-color-muted-foreground)}.spinner{width:40px;height:40px;border:3px solid var(--tl-color-border);border-top-color:var(--tl-color-primary);border-radius:50%;animation:tl-seat-map-spin 1s linear infinite}@keyframes tl-seat-map-spin{to{transform:rotate(360deg)}}.error{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--tl-color-background);color:var(--tl-color-error);padding:1.5rem;text-align:center}.error-title{margin:0 0 0.5rem;font-size:1.125rem;font-weight:600}.error-message{margin:0;font-size:0.875rem;opacity:0.9}`;
5
5
 
6
6
  const TlSeatMap = class {
7
7
  constructor(hostRef) {
@@ -125,10 +125,10 @@ const TlSeatMap = class {
125
125
  // Render
126
126
  // -------------------------------------------------------------------------
127
127
  render() {
128
- return (h("div", { key: 'c9beadc1792d7825ac3f99e6abc8b181e28fd1b6', class: "seat-map-container", style: {
128
+ return (h("div", { key: '1c6a23b0aa021c58fa62c106a92a2e970ca7f6d1', class: "seat-map-container", style: {
129
129
  width: this.width,
130
130
  height: this.height,
131
- } }, this.isLoading && (h("div", { key: 'ad792ea35f433a268c81722ce4e7d0e34780d337', class: "loading" }, h("div", { key: 'dd3e0657b16c2f265f59fbaa2ac39c559c2093b7', class: "spinner" }), h("p", { key: '2714e3073d0e5cddc50d530aa1cf46a7f73ba96f' }, "Loading seat map..."))), this.error && (h("div", { key: '3de9d73ab89a1ee963e73b4192d5c93e9667f11b', class: "error" }, h("p", { key: '0fea5c828b5b212ffdb45c9c7702d241e2b79cc4', class: "error-title" }, "Error"), h("p", { key: 'f2c090b982bb1ee6fb7d347d8860b4ce4798a891', class: "error-message" }, this.error))), h("iframe", { key: '8023219fdd467ec736f9627190690a6f73df3bb3', ref: (el) => (this.iframeRef = el), src: this.getIframeSrc(), class: {
131
+ } }, this.isLoading && (h("div", { key: '4a09ddbdbd38eb68675bb320cf33fd3ead5974a4', class: "loading" }, h("div", { key: 'c622ba1f996427f0624cfda2c782abd49ec72e19', class: "spinner" }), h("p", { key: '802f42554fc238a1ddc0f8a0f6a6b0de89bdfb60' }, "Loading seat map..."))), this.error && (h("div", { key: 'dde73e2430bad68c0a67306c0aa2b44810007cc5', class: "error" }, h("p", { key: '392d51bbc47ab34eab5670aa7a2faf800dabe638', class: "error-title" }, "Error"), h("p", { key: 'fcf6810f767f34bf3490c1af21320d262bdb961b', class: "error-message" }, this.error))), h("iframe", { key: '2333206fa6eb4fe6cdcbb1d30864d56d54583e1f', ref: (el) => (this.iframeRef = el), src: this.getIframeSrc(), class: {
132
132
  'seat-map-iframe': true,
133
133
  hidden: this.isLoading || !!this.error,
134
134
  }, title: "Ticketlayer Seat Map", allow: "fullscreen" })));
@@ -0,0 +1,14 @@
1
+ const webQuery = {
2
+ get(name) {
3
+ if (typeof window === 'undefined' || !window.location?.search)
4
+ return null;
5
+ try {
6
+ return new URLSearchParams(window.location.search).get(name);
7
+ }
8
+ catch {
9
+ return null;
10
+ }
11
+ },
12
+ };
13
+
14
+ export { webQuery as w };
@@ -1 +1 @@
1
- export{g as getSDK,h as hasSDK,o as onSDKEvent,s as setSDK,w as whenSDK}from"./p-CqhvfyKV.js";export{tl_buy_tickets_wrapper as TlBuyTicketsWrapper}from"./tl-buy-tickets-wrapper.entry.esm.js";export{tl_checkout_wrapper as TlCheckoutWrapper}from"./tl-checkout-wrapper.entry.esm.js";import"./p-CNX8hZa8.js";
1
+ export{g as getSDK,h as hasSDK,o as onSDKEvent,s as setSDK,w as whenSDK}from"./p-CqhvfyKV.js";export{tl_buy_tickets_wrapper as TlBuyTicketsWrapper}from"./tl-buy-tickets-wrapper.entry.esm.js";export{tl_checkout_wrapper as TlCheckoutWrapper}from"./tl-checkout-wrapper.entry.esm.js";import"./p-imfxp765.js";import"./p-BwChnHVC.js";
@@ -0,0 +1 @@
1
+ import{r as t,c as o,h as r}from"./p-imfxp765.js";import{w as e,g as i}from"./p-CqhvfyKV.js";import{c as a,d as s}from"./p-BwChnHVC.js";const n=class{constructor(r){t(this,r),this.tlCheckout=o(this,"tlCheckout"),this.tlItemRemoved=o(this,"tlItemRemoved"),this.tlCartUpdated=o(this,"tlCartUpdated"),this.tlError=o(this,"tlError"),this.showCheckoutButton=!0,this.checkoutLabel="Checkout",this.showRemoveButtons=!0,this.compact=!1,this.element=a(s,{client:()=>i(),clientReady:()=>e(),emit:(t,o)=>this.forward(t,o)}),this.view=this.element.view}syncProps(){this.element.setProps({showCheckoutButton:this.showCheckoutButton,checkoutLabel:this.checkoutLabel,showRemoveButtons:this.showRemoveButtons,compact:this.compact})}connectedCallback(){this.syncProps(),this.unsubscribe??(this.unsubscribe=this.element.subscribe((t=>{this.view=t}))),this.view=this.element.view,this.teardown=this.element.mount()}disconnectedCallback(){this.teardown?.(),this.teardown=void 0,this.unsubscribe?.(),this.unsubscribe=void 0}forward(t,o){"tlCheckout"===t?this.tlCheckout.emit(o):"tlItemRemoved"===t?this.tlItemRemoved.emit(o):"tlCartUpdated"===t?this.tlCartUpdated.emit(o):"tlError"===t&&this.tlError.emit(o)}renderSkeleton(){return r("div",{class:"skeleton-container"},r("div",{class:"skeleton-item"}),r("div",{class:"skeleton-item"}),r("div",{class:"skeleton-totals"}))}renderEmptyCart(){const t=this.view.strings;return r("div",{class:"empty-cart"},r("svg",{class:"empty-icon",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor"},r("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"1.5",d:"M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"})),r("p",{class:"empty-title"},t.emptyTitle),r("p",{class:"empty-message"},t.emptyMessage))}renderCartItem(t){return r("div",{class:"cart-item "+(t.removing?"removing":""),key:t.id},r("div",{class:"item-info"},r("p",{class:"item-name"},t.name),t.showEvent&&r("p",{class:"item-event"},t.eventName),t.showDate&&r("p",{class:"item-date"},t.dateLabel),r("p",{class:"item-quantity"},t.quantityLabel)),r("div",{class:"item-actions"},r("p",{class:"item-price"},t.priceLabel),this.showRemoveButtons&&r("button",{class:"remove-btn",onClick:()=>this.element.actions.removeItem(t.id),disabled:t.removing,"aria-label":this.view.strings.removeItem},t.removing?r("span",{class:"spinner"}):r("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor"},r("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M6 18L18 6M6 6l12 12"})))))}render(){const{status:t,strings:o,derived:e}=this.view;return"loading"===t?this.renderSkeleton():"empty"===t?this.renderEmptyCart():r("div",{class:"cart "+(this.compact?"compact":"")},r("div",{class:"cart-items"},e.rows.map((t=>this.renderCartItem(t)))),r("div",{class:"cart-totals"},e.showSubtotalRow&&r("div",{class:"total-row subtotal"},r("span",null,o.subtotalRow),r("span",null,e.subtotalLabel)),e.showFeeRow&&r("div",{class:"total-row fees"},r("span",null,e.totals.feeLabel),r("span",null,e.feeAmountLabel)),e.showDiscountRow&&r("div",{class:"total-row discount"},r("span",null,o.discountRow),r("span",null,"-",e.discountLabel)),r("div",{class:"total-row total"},r("span",null,o.totalRow),r("span",null,e.totalLabel))),this.showCheckoutButton&&r("button",{class:"checkout-btn",onClick:()=>this.element.actions.checkout(),disabled:e.checkoutDisabled},this.checkoutLabel))}static get watchers(){return{showCheckoutButton:[{syncProps:0}],checkoutLabel:[{syncProps:0}],showRemoveButtons:[{syncProps:0}],compact:[{syncProps:0}]}}};n.style=":host{display:block;font-family:var(--tl-font-family)}.skeleton-container{padding:var(--tl-spacing-4)}.skeleton-item{height:70px;background:var(--tl-color-muted);border-radius:var(--tl-radius-md);margin-bottom:var(--tl-spacing-3);animation:pulse 1.5s ease-in-out infinite}.skeleton-totals{height:100px;background:var(--tl-color-muted);border-radius:var(--tl-radius-md);animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}.empty-cart{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--tl-spacing-8);text-align:center}.empty-icon{width:48px;height:48px;color:var(--tl-color-muted-foreground);margin-bottom:var(--tl-spacing-4)}.empty-title{margin:0 0 var(--tl-spacing-2);font-size:16px;font-weight:600;color:var(--tl-color-foreground)}.empty-message{margin:0;font-size:14px;color:var(--tl-color-muted-foreground)}.cart{background:var(--tl-color-background)}.cart.compact .cart-items{margin-bottom:var(--tl-spacing-2)}.cart-items{margin-bottom:var(--tl-spacing-4)}.cart-item{display:flex;justify-content:space-between;align-items:flex-start;padding:var(--tl-spacing-3) 0;border-bottom:1px solid var(--tl-color-border);transition:opacity 0.2s}.cart-item:last-child{border-bottom:none}.cart-item.removing{opacity:0.5}.item-info{flex:1;min-width:0}.item-name{margin:0;font-weight:600;font-size:15px;color:var(--tl-color-foreground)}.item-event{margin:var(--tl-spacing-1) 0 0;font-size:14px;color:var(--tl-color-muted-foreground)}.item-date{margin:var(--tl-spacing-1) 0 0;font-size:13px;color:var(--tl-color-muted-foreground)}.item-quantity{margin:var(--tl-spacing-1) 0 0;font-size:13px;color:var(--tl-color-muted-foreground)}.item-actions{display:flex;align-items:center;gap:var(--tl-spacing-3);margin-left:var(--tl-spacing-4)}.item-price{margin:0;font-weight:600;font-size:15px;color:var(--tl-color-foreground);white-space:nowrap}.remove-btn{width:28px;height:28px;padding:0;border:none;background:transparent;border-radius:var(--tl-radius-sm);color:var(--tl-color-muted-foreground);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background-color 0.15s, color 0.15s}.remove-btn:hover:not(:disabled){background-color:var(--tl-color-destructive-bg);color:var(--tl-color-destructive)}.remove-btn:disabled{cursor:not-allowed;opacity:0.5}.remove-btn svg{width:16px;height:16px}.spinner{width:14px;height:14px;border:2px solid var(--tl-color-border);border-top-color:var(--tl-color-primary);border-radius:50%;animation:spin 0.6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.cart-totals{padding:var(--tl-spacing-3) 0;border-top:1px solid var(--tl-color-border)}.total-row{display:flex;justify-content:space-between;align-items:center;padding:var(--tl-spacing-1) 0}.total-row.subtotal,.total-row.fees,.total-row.discount{font-size:14px;color:var(--tl-color-muted-foreground)}.total-row.total{font-size:16px;font-weight:700;color:var(--tl-color-foreground);padding-top:var(--tl-spacing-2);border-top:1px solid var(--tl-color-border);margin-top:var(--tl-spacing-2)}.checkout-btn{width:100%;margin-top:var(--tl-spacing-4);padding:var(--tl-spacing-3) var(--tl-spacing-4);border-radius:var(--tl-radius-md);border:none;background-color:var(--tl-color-primary);color:var(--tl-color-primary-foreground);font-weight:600;font-size:16px;cursor:pointer;transition:opacity 0.15s, transform 0.1s}.checkout-btn:hover:not(:disabled){opacity:0.9}.checkout-btn:active:not(:disabled){transform:scale(0.98)}.checkout-btn:disabled{background-color:var(--tl-color-muted);color:var(--tl-color-muted-foreground);cursor:not-allowed}.cart.compact .item-info{flex-direction:row;align-items:center}.cart.compact .cart-totals{padding:var(--tl-spacing-2) 0}.cart.compact .total-row.total{margin-top:0;border-top:none;padding-top:0}";export{n as tl_cart}