@ticketlayer/elements 0.2.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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/dist/cjs/index-DfzcrE9-.js +3598 -0
  4. package/dist/cjs/index.cjs.js +16 -0
  5. package/dist/cjs/loader.cjs.js +12 -0
  6. package/dist/cjs/order-summary-ZMkJ5bez.js +116 -0
  7. package/dist/cjs/sdk-connector-ClDF3013.js +108 -0
  8. package/dist/cjs/ticketlayer-elements.cjs.js +24 -0
  9. package/dist/cjs/tl-buy-modal.cjs.entry.js +127 -0
  10. package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +131 -0
  11. package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +58 -0
  12. package/dist/cjs/tl-cart-badge.cjs.entry.js +109 -0
  13. package/dist/cjs/tl-cart-drawer.cjs.entry.js +67 -0
  14. package/dist/cjs/tl-cart.cjs.entry.js +148 -0
  15. package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +57 -0
  16. package/dist/cjs/tl-event-card.cjs.entry.js +140 -0
  17. package/dist/cjs/tl-event-list.cjs.entry.js +80 -0
  18. package/dist/cjs/tl-login.cjs.entry.js +184 -0
  19. package/dist/cjs/tl-my-orders.cjs.entry.js +123 -0
  20. package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +754 -0
  21. package/dist/cjs/tl-order-confirmation.cjs.entry.js +146 -0
  22. package/dist/cjs/tl-order-tickets_2.cjs.entry.js +3198 -0
  23. package/dist/cjs/tl-promo-code.cjs.entry.js +75 -0
  24. package/dist/cjs/tl-seat-map.cjs.entry.js +147 -0
  25. package/dist/collection/collection-manifest.json +29 -0
  26. package/dist/collection/components/tl-buy-modal/tl-buy-modal.css +264 -0
  27. package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +249 -0
  28. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +120 -0
  29. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +412 -0
  30. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.css +23 -0
  31. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +192 -0
  32. package/dist/collection/components/tl-cart/tl-cart.css +251 -0
  33. package/dist/collection/components/tl-cart/tl-cart.js +314 -0
  34. package/dist/collection/components/tl-cart-badge/tl-cart-badge.css +138 -0
  35. package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +217 -0
  36. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.css +98 -0
  37. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +185 -0
  38. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.css +23 -0
  39. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +148 -0
  40. package/dist/collection/components/tl-event-card/event-card-adapter.js +23 -0
  41. package/dist/collection/components/tl-event-card/tl-event-card.css +175 -0
  42. package/dist/collection/components/tl-event-card/tl-event-card.js +205 -0
  43. package/dist/collection/components/tl-event-list/tl-event-list.css +134 -0
  44. package/dist/collection/components/tl-event-list/tl-event-list.js +171 -0
  45. package/dist/collection/components/tl-login/tl-login.css +133 -0
  46. package/dist/collection/components/tl-login/tl-login.js +309 -0
  47. package/dist/collection/components/tl-my-orders/tl-my-orders.css +204 -0
  48. package/dist/collection/components/tl-my-orders/tl-my-orders.js +221 -0
  49. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.css +658 -0
  50. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +707 -0
  51. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.css +176 -0
  52. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +298 -0
  53. package/dist/collection/components/tl-order-tickets/tl-order-tickets.css +30 -0
  54. package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +122 -0
  55. package/dist/collection/components/tl-promo-code/tl-promo-code.css +106 -0
  56. package/dist/collection/components/tl-promo-code/tl-promo-code.js +181 -0
  57. package/dist/collection/components/tl-seat-map/tl-seat-map.css +85 -0
  58. package/dist/collection/components/tl-seat-map/tl-seat-map.js +366 -0
  59. package/dist/collection/components/tl-ticket/tl-ticket.css +90 -0
  60. package/dist/collection/components/tl-ticket/tl-ticket.js +129 -0
  61. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.css +205 -0
  62. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +332 -0
  63. package/dist/collection/index.js +10 -0
  64. package/dist/collection/utils/order-summary.js +101 -0
  65. package/dist/collection/utils/sdk-connector.js +100 -0
  66. package/dist/collection/utils/testing/mock-sdk.js +95 -0
  67. package/dist/components/index.d.ts +35 -0
  68. package/dist/components/index.js +1 -0
  69. package/dist/components/order-summary.js +1 -0
  70. package/dist/components/sdk-connector.js +1 -0
  71. package/dist/components/tl-buy-modal.d.ts +11 -0
  72. package/dist/components/tl-buy-modal.js +1 -0
  73. package/dist/components/tl-buy-tickets-button.d.ts +11 -0
  74. package/dist/components/tl-buy-tickets-button.js +1 -0
  75. package/dist/components/tl-buy-tickets-wrapper.d.ts +11 -0
  76. package/dist/components/tl-buy-tickets-wrapper.js +1 -0
  77. package/dist/components/tl-buy-tickets-wrapper2.js +1 -0
  78. package/dist/components/tl-cart-badge.d.ts +11 -0
  79. package/dist/components/tl-cart-badge.js +1 -0
  80. package/dist/components/tl-cart-drawer.d.ts +11 -0
  81. package/dist/components/tl-cart-drawer.js +1 -0
  82. package/dist/components/tl-cart.d.ts +11 -0
  83. package/dist/components/tl-cart.js +1 -0
  84. package/dist/components/tl-cart2.js +1 -0
  85. package/dist/components/tl-checkout-wrapper.d.ts +11 -0
  86. package/dist/components/tl-checkout-wrapper.js +1 -0
  87. package/dist/components/tl-checkout-wrapper2.js +1 -0
  88. package/dist/components/tl-event-card.d.ts +11 -0
  89. package/dist/components/tl-event-card.js +1 -0
  90. package/dist/components/tl-event-card2.js +1 -0
  91. package/dist/components/tl-event-list.d.ts +11 -0
  92. package/dist/components/tl-event-list.js +1 -0
  93. package/dist/components/tl-login.d.ts +11 -0
  94. package/dist/components/tl-login.js +1 -0
  95. package/dist/components/tl-login2.js +1 -0
  96. package/dist/components/tl-my-orders.d.ts +11 -0
  97. package/dist/components/tl-my-orders.js +1 -0
  98. package/dist/components/tl-occurrence-selector.d.ts +11 -0
  99. package/dist/components/tl-occurrence-selector.js +1 -0
  100. package/dist/components/tl-occurrence-selector2.js +1 -0
  101. package/dist/components/tl-order-confirmation.d.ts +11 -0
  102. package/dist/components/tl-order-confirmation.js +1 -0
  103. package/dist/components/tl-order-tickets.d.ts +11 -0
  104. package/dist/components/tl-order-tickets.js +1 -0
  105. package/dist/components/tl-order-tickets2.js +1 -0
  106. package/dist/components/tl-promo-code.d.ts +11 -0
  107. package/dist/components/tl-promo-code.js +1 -0
  108. package/dist/components/tl-seat-map.d.ts +11 -0
  109. package/dist/components/tl-seat-map.js +1 -0
  110. package/dist/components/tl-ticket-selector.d.ts +11 -0
  111. package/dist/components/tl-ticket-selector.js +1 -0
  112. package/dist/components/tl-ticket-selector2.js +1 -0
  113. package/dist/components/tl-ticket.d.ts +11 -0
  114. package/dist/components/tl-ticket.js +1 -0
  115. package/dist/components/tl-ticket2.js +1 -0
  116. package/dist/elements.docs.d.ts +454 -0
  117. package/dist/elements.docs.json +3008 -0
  118. package/dist/esm/index-Ba-RZTFI.js +3588 -0
  119. package/dist/esm/index.js +4 -0
  120. package/dist/esm/loader.js +10 -0
  121. package/dist/esm/order-summary-BGekdjN9.js +103 -0
  122. package/dist/esm/sdk-connector-CqhvfyKV.js +102 -0
  123. package/dist/esm/ticketlayer-elements.js +20 -0
  124. package/dist/esm/tl-buy-modal.entry.js +125 -0
  125. package/dist/esm/tl-buy-tickets-button.entry.js +129 -0
  126. package/dist/esm/tl-buy-tickets-wrapper.entry.js +56 -0
  127. package/dist/esm/tl-cart-badge.entry.js +107 -0
  128. package/dist/esm/tl-cart-drawer.entry.js +65 -0
  129. package/dist/esm/tl-cart.entry.js +146 -0
  130. package/dist/esm/tl-checkout-wrapper.entry.js +55 -0
  131. package/dist/esm/tl-event-card.entry.js +138 -0
  132. package/dist/esm/tl-event-list.entry.js +78 -0
  133. package/dist/esm/tl-login.entry.js +182 -0
  134. package/dist/esm/tl-my-orders.entry.js +121 -0
  135. package/dist/esm/tl-occurrence-selector_2.entry.js +751 -0
  136. package/dist/esm/tl-order-confirmation.entry.js +144 -0
  137. package/dist/esm/tl-order-tickets_2.entry.js +3195 -0
  138. package/dist/esm/tl-promo-code.entry.js +73 -0
  139. package/dist/esm/tl-seat-map.entry.js +145 -0
  140. package/dist/index.cjs.js +1 -0
  141. package/dist/index.js +1 -0
  142. package/dist/ticketlayer-elements/index.esm.js +1 -0
  143. package/dist/ticketlayer-elements/p-0291df4c.entry.js +1 -0
  144. package/dist/ticketlayer-elements/p-10f01a1f.entry.js +1 -0
  145. package/dist/ticketlayer-elements/p-11273f8f.entry.js +1 -0
  146. package/dist/ticketlayer-elements/p-340ab4f5.entry.js +1 -0
  147. package/dist/ticketlayer-elements/p-41d22d7c.entry.js +1 -0
  148. package/dist/ticketlayer-elements/p-82752e3b.entry.js +1 -0
  149. package/dist/ticketlayer-elements/p-976770f3.entry.js +1 -0
  150. package/dist/ticketlayer-elements/p-9a903ff6.entry.js +1 -0
  151. package/dist/ticketlayer-elements/p-9c9072c8.entry.js +1 -0
  152. package/dist/ticketlayer-elements/p-9d8163d6.entry.js +1 -0
  153. package/dist/ticketlayer-elements/p-BGekdjN9.js +1 -0
  154. package/dist/ticketlayer-elements/p-Ba-RZTFI.js +2 -0
  155. package/dist/ticketlayer-elements/p-CqhvfyKV.js +1 -0
  156. package/dist/ticketlayer-elements/p-a5c90b68.entry.js +1 -0
  157. package/dist/ticketlayer-elements/p-b6d479ad.entry.js +1 -0
  158. package/dist/ticketlayer-elements/p-dd43f015.entry.js +1 -0
  159. package/dist/ticketlayer-elements/p-eb640760.entry.js +1 -0
  160. package/dist/ticketlayer-elements/p-ee3aa227.entry.js +1 -0
  161. package/dist/ticketlayer-elements/p-fb9444c4.entry.js +1 -0
  162. package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -0
  163. package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -0
  164. package/dist/types/components/tl-buy-modal/tl-buy-modal.d.ts +54 -0
  165. package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +61 -0
  166. package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +52 -0
  167. package/dist/types/components/tl-cart/tl-cart.d.ts +80 -0
  168. package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +36 -0
  169. package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +35 -0
  170. package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +39 -0
  171. package/dist/types/components/tl-event-card/event-card-adapter.d.ts +23 -0
  172. package/dist/types/components/tl-event-card/tl-event-card.d.ts +55 -0
  173. package/dist/types/components/tl-event-list/tl-event-list.d.ts +30 -0
  174. package/dist/types/components/tl-login/tl-login.d.ts +59 -0
  175. package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +39 -0
  176. package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +208 -0
  177. package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +54 -0
  178. package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +21 -0
  179. package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +39 -0
  180. package/dist/types/components/tl-seat-map/tl-seat-map.d.ts +68 -0
  181. package/dist/types/components/tl-ticket/tl-ticket.d.ts +28 -0
  182. package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +78 -0
  183. package/dist/types/components.d.ts +1629 -0
  184. package/dist/types/index.d.ts +14 -0
  185. package/dist/types/stencil-public-runtime.d.ts +1839 -0
  186. package/dist/types/utils/order-summary.d.ts +67 -0
  187. package/dist/types/utils/sdk-connector.d.ts +113 -0
  188. package/dist/types/utils/testing/mock-sdk.d.ts +36 -0
  189. package/elements.catalogue.json +968 -0
  190. package/loader/cdn.js +1 -0
  191. package/loader/index.cjs.js +1 -0
  192. package/loader/index.d.ts +24 -0
  193. package/loader/index.es2017.js +1 -0
  194. package/loader/index.js +2 -0
  195. package/package.json +101 -0
@@ -0,0 +1,176 @@
1
+ :host {
2
+ display: block;
3
+ font-family: var(--tl-font-family, system-ui, sans-serif);
4
+ color: var(--tl-foreground, #111827);
5
+ }
6
+
7
+ .confirmation {
8
+ text-align: center;
9
+ }
10
+
11
+ .check {
12
+ width: 4rem;
13
+ height: 4rem;
14
+ margin: 0 auto var(--tl-spacing-4, 1rem);
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ border-radius: 9999px;
19
+ background: var(--tl-success, #10b981);
20
+ color: var(--tl-success-foreground, #ffffff);
21
+ }
22
+
23
+ .check svg {
24
+ width: 2rem;
25
+ height: 2rem;
26
+ }
27
+
28
+ .title {
29
+ margin: 0 0 var(--tl-spacing-2, 0.5rem);
30
+ font-size: 1.875rem;
31
+ font-weight: 700;
32
+ line-height: 1.2;
33
+ }
34
+
35
+ .lead {
36
+ margin: 0 0 var(--tl-spacing-6, 1.5rem);
37
+ color: var(--tl-muted-foreground, #6b7280);
38
+ }
39
+
40
+ .error {
41
+ margin: 0 0 var(--tl-spacing-6, 1.5rem);
42
+ font-size: 0.875rem;
43
+ color: var(--tl-error, #ef4444);
44
+ }
45
+
46
+ .summary {
47
+ text-align: left;
48
+ padding: var(--tl-spacing-6, 1.5rem);
49
+ margin-bottom: var(--tl-spacing-6, 1.5rem);
50
+ background: var(--tl-muted, #f9fafb);
51
+ border: 1px solid var(--tl-border, #e5e7eb);
52
+ border-radius: var(--tl-radius-lg, 0.75rem);
53
+ }
54
+
55
+ .rows {
56
+ display: grid;
57
+ gap: var(--tl-spacing-2, 0.5rem);
58
+ margin: 0 0 var(--tl-spacing-4, 1rem);
59
+ padding: 0 0 var(--tl-spacing-4, 1rem);
60
+ border-bottom: 1px solid var(--tl-border, #e5e7eb);
61
+ }
62
+
63
+ .row {
64
+ display: flex;
65
+ justify-content: space-between;
66
+ gap: var(--tl-spacing-4, 1rem);
67
+ font-size: 0.875rem;
68
+ }
69
+
70
+ .row dt {
71
+ color: var(--tl-muted-foreground, #6b7280);
72
+ }
73
+
74
+ .row dd {
75
+ margin: 0;
76
+ text-align: right;
77
+ overflow-wrap: anywhere;
78
+ }
79
+
80
+ .reference {
81
+ font-family: var(--tl-font-family-mono, ui-monospace, monospace);
82
+ font-weight: 600;
83
+ }
84
+
85
+ .lines {
86
+ list-style: none;
87
+ margin: 0;
88
+ padding: 0;
89
+ display: grid;
90
+ gap: var(--tl-spacing-2, 0.5rem);
91
+ }
92
+
93
+ .line {
94
+ display: flex;
95
+ justify-content: space-between;
96
+ gap: var(--tl-spacing-4, 1rem);
97
+ font-size: 0.875rem;
98
+ }
99
+
100
+ .totals {
101
+ margin-top: var(--tl-spacing-3, 0.75rem);
102
+ padding-top: var(--tl-spacing-3, 0.75rem);
103
+ border-top: 1px solid var(--tl-border, #e5e7eb);
104
+ display: grid;
105
+ gap: var(--tl-spacing-1, 0.25rem);
106
+ font-size: 0.875rem;
107
+ }
108
+
109
+ .total-row {
110
+ display: flex;
111
+ justify-content: space-between;
112
+ color: var(--tl-muted-foreground, #6b7280);
113
+ }
114
+
115
+ .total-row.discount {
116
+ color: var(--tl-success, #10b981);
117
+ }
118
+
119
+ .total-row.total {
120
+ margin-top: var(--tl-spacing-1, 0.25rem);
121
+ font-size: 1rem;
122
+ font-weight: 600;
123
+ color: var(--tl-foreground, #111827);
124
+ }
125
+
126
+ .tickets {
127
+ text-align: left;
128
+ }
129
+
130
+ .subtitle {
131
+ margin: 0 0 var(--tl-spacing-4, 1rem);
132
+ font-size: 1.125rem;
133
+ font-weight: 600;
134
+ }
135
+
136
+ .hint {
137
+ margin: var(--tl-spacing-3, 0.75rem) 0 0;
138
+ font-size: 0.75rem;
139
+ color: var(--tl-muted-foreground, #6b7280);
140
+ }
141
+
142
+ /* Skeleton */
143
+ .skeleton-line,
144
+ .skeleton-block {
145
+ background: var(--tl-secondary, #f3f4f6);
146
+ border-radius: var(--tl-radius-md, 0.5rem);
147
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
148
+ margin: 0 auto var(--tl-spacing-3, 0.75rem);
149
+ }
150
+
151
+ .skeleton-line {
152
+ height: 1rem;
153
+ }
154
+
155
+ .skeleton-line.short {
156
+ width: 40%;
157
+ height: 1.75rem;
158
+ }
159
+
160
+ .skeleton-line.long {
161
+ width: 70%;
162
+ }
163
+
164
+ .skeleton-block {
165
+ height: 8rem;
166
+ }
167
+
168
+ @keyframes pulse {
169
+ 0%,
170
+ 100% {
171
+ opacity: 1;
172
+ }
173
+ 50% {
174
+ opacity: 0.5;
175
+ }
176
+ }
@@ -0,0 +1,298 @@
1
+ import { h } from "@stencil/core";
2
+ import { onSDKEvent, whenSDK } from "../../utils/sdk-connector";
3
+ import { errorMessage, formatDateTime, formatPrice, orderCustomerEmail, orderEventName, resolveOccurrence, ticketItems, } from "../../utils/order-summary";
4
+ /**
5
+ * tl-order-confirmation - the "thank you" page block: order reference,
6
+ * customer email, event and date, ticket lines, totals, then the tickets
7
+ * themselves (tl-order-tickets, each with a Download PDF link).
8
+ *
9
+ * Finds its order three ways, in priority order:
10
+ * 1. the `order-id` prop
11
+ * 2. the URL (`?orderId=`), the contract the hosted box office confirmation
12
+ * page uses; `?redirect_status=` from a Stripe return is honoured too
13
+ * 3. the SDK's `checkout:completed` event, for a page that mounts this before
14
+ * the customer pays
15
+ *
16
+ * Usage: <tl-order-confirmation order-id="ord_xxx"></tl-order-confirmation>
17
+ *
18
+ * @event tlOrderLoaded - the order was loaded
19
+ * @event tlError - the order could not be loaded
20
+ */
21
+ export class TlOrderConfirmation {
22
+ constructor() {
23
+ /** Mount tl-order-tickets under the summary (default true). */
24
+ this.showTickets = true;
25
+ /** Columns for the ticket grid. */
26
+ this.columns = 2;
27
+ this.order = null;
28
+ this.resolvedOrderId = null;
29
+ this.loading = false;
30
+ this.error = null;
31
+ this.paymentFailed = false;
32
+ this.occurrence = null;
33
+ this.signedInCustomer = null;
34
+ }
35
+ watchOrderId(next) {
36
+ this.resolvedOrderId = next || this.orderIdFromUrl();
37
+ this.load();
38
+ }
39
+ async componentWillLoad() {
40
+ const params = this.urlParams();
41
+ // Stripe returns with ?redirect_status=; anything but succeeded is not paid.
42
+ const redirect = params?.get('redirect_status');
43
+ this.paymentFailed = !!redirect && redirect !== 'succeeded';
44
+ this.resolvedOrderId = this.orderId || this.orderIdFromUrl();
45
+ if (this.resolvedOrderId && !this.paymentFailed) {
46
+ await this.load();
47
+ }
48
+ }
49
+ componentDidLoad() {
50
+ // A page that mounts before checkout: pick the order up when the SDK completes it.
51
+ this.unsubscribe = onSDKEvent('checkout:completed', (payload) => {
52
+ const order = payload;
53
+ if (!order?.id)
54
+ return;
55
+ this.paymentFailed = false;
56
+ this.order = order;
57
+ this.resolvedOrderId = order.id;
58
+ this.tlOrderLoaded.emit({ order });
59
+ // Re-read for tickets, which are issued after the order is created.
60
+ this.load();
61
+ });
62
+ }
63
+ disconnectedCallback() {
64
+ this.unsubscribe?.();
65
+ }
66
+ urlParams() {
67
+ if (typeof window === 'undefined' || !window.location?.search)
68
+ return null;
69
+ return new URLSearchParams(window.location.search);
70
+ }
71
+ orderIdFromUrl() {
72
+ return this.urlParams()?.get('orderId') || null;
73
+ }
74
+ async load() {
75
+ const id = this.resolvedOrderId;
76
+ if (!id)
77
+ return;
78
+ try {
79
+ this.loading = !this.order;
80
+ this.error = null;
81
+ const sdk = await whenSDK();
82
+ const orders = sdk.getOrdersManager?.();
83
+ if (!orders)
84
+ throw new Error('Orders are not available');
85
+ const order = (await orders.get(id));
86
+ if (!order)
87
+ throw new Error('Order not found');
88
+ this.order = order;
89
+ this.signedInCustomer = sdk.getAuthManager?.()?.customer ?? null;
90
+ this.tlOrderLoaded.emit({ order });
91
+ this.resolveEvent(sdk, order);
92
+ }
93
+ catch (err) {
94
+ console.error('[tl-order-confirmation] Failed to load order:', err);
95
+ this.error = errorMessage(err, 'Failed to load your order');
96
+ this.tlError.emit({ message: this.error });
97
+ }
98
+ finally {
99
+ this.loading = false;
100
+ }
101
+ }
102
+ // The order view has no event name or date; look them up from the first
103
+ // ticket's occurrence. Non-blocking and best effort.
104
+ async resolveEvent(sdk, order) {
105
+ const occurrenceId = order.tickets?.find((t) => t.eventOccurrenceId)?.eventOccurrenceId;
106
+ if (!occurrenceId)
107
+ return;
108
+ try {
109
+ const resolved = await resolveOccurrence(sdk, occurrenceId);
110
+ if (resolved && this.order?.id === order.id)
111
+ this.occurrence = resolved;
112
+ }
113
+ catch {
114
+ // Leave the event line at what the order gives us.
115
+ }
116
+ }
117
+ renderRows(order) {
118
+ const email = this.customerEmail || orderCustomerEmail(order, this.signedInCustomer);
119
+ const eventName = this.occurrence?.eventName || orderEventName(order);
120
+ const startsAt = this.occurrence?.startsAt;
121
+ 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))))));
122
+ }
123
+ renderTotals(order) {
124
+ const c = order.currency || 'GBP';
125
+ const fees = Number(order.totalFees) || 0;
126
+ const tax = Number(order.totalTax) || 0;
127
+ const discounts = Number(order.totalDiscounts) || 0;
128
+ 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)))));
129
+ }
130
+ render() {
131
+ if (this.paymentFailed) {
132
+ 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.")));
133
+ }
134
+ // Nothing to show yet: no order in props, URL or from checkout.
135
+ if (!this.resolvedOrderId && !this.order) {
136
+ return null;
137
+ }
138
+ if (this.loading) {
139
+ return (h("div", { class: "confirmation" }, h("div", { class: "skeleton-line short" }), h("div", { class: "skeleton-line long" }), h("div", { class: "skeleton-block" })));
140
+ }
141
+ const order = this.order;
142
+ const orderId = order?.id || this.resolvedOrderId;
143
+ 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
144
+ ? `Order ${order.orderNumber} is confirmed. Your tickets are on their way to your email.`
145
+ : '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.")))));
146
+ }
147
+ static get is() { return "tl-order-confirmation"; }
148
+ static get encapsulation() { return "shadow"; }
149
+ static get originalStyleUrls() {
150
+ return {
151
+ "$": ["tl-order-confirmation.css"]
152
+ };
153
+ }
154
+ static get styleUrls() {
155
+ return {
156
+ "$": ["tl-order-confirmation.css"]
157
+ };
158
+ }
159
+ static get properties() {
160
+ return {
161
+ "orderId": {
162
+ "type": "string",
163
+ "mutable": false,
164
+ "complexType": {
165
+ "original": "string",
166
+ "resolved": "string | undefined",
167
+ "references": {}
168
+ },
169
+ "required": false,
170
+ "optional": true,
171
+ "docs": {
172
+ "tags": [],
173
+ "text": "The order to show. Falls back to `?orderId=` in the page URL."
174
+ },
175
+ "getter": false,
176
+ "setter": false,
177
+ "reflect": false,
178
+ "attribute": "order-id"
179
+ },
180
+ "customerEmail": {
181
+ "type": "string",
182
+ "mutable": false,
183
+ "complexType": {
184
+ "original": "string",
185
+ "resolved": "string | undefined",
186
+ "references": {}
187
+ },
188
+ "required": false,
189
+ "optional": true,
190
+ "docs": {
191
+ "tags": [],
192
+ "text": "Email to show when the order view carries none (a host that collected it at checkout)."
193
+ },
194
+ "getter": false,
195
+ "setter": false,
196
+ "reflect": false,
197
+ "attribute": "customer-email"
198
+ },
199
+ "showTickets": {
200
+ "type": "boolean",
201
+ "mutable": false,
202
+ "complexType": {
203
+ "original": "boolean",
204
+ "resolved": "boolean",
205
+ "references": {}
206
+ },
207
+ "required": false,
208
+ "optional": false,
209
+ "docs": {
210
+ "tags": [],
211
+ "text": "Mount tl-order-tickets under the summary (default true)."
212
+ },
213
+ "getter": false,
214
+ "setter": false,
215
+ "reflect": false,
216
+ "attribute": "show-tickets",
217
+ "defaultValue": "true"
218
+ },
219
+ "columns": {
220
+ "type": "number",
221
+ "mutable": false,
222
+ "complexType": {
223
+ "original": "number",
224
+ "resolved": "number",
225
+ "references": {}
226
+ },
227
+ "required": false,
228
+ "optional": false,
229
+ "docs": {
230
+ "tags": [],
231
+ "text": "Columns for the ticket grid."
232
+ },
233
+ "getter": false,
234
+ "setter": false,
235
+ "reflect": false,
236
+ "attribute": "columns",
237
+ "defaultValue": "2"
238
+ }
239
+ };
240
+ }
241
+ static get states() {
242
+ return {
243
+ "order": {},
244
+ "resolvedOrderId": {},
245
+ "loading": {},
246
+ "error": {},
247
+ "paymentFailed": {},
248
+ "occurrence": {},
249
+ "signedInCustomer": {}
250
+ };
251
+ }
252
+ static get events() {
253
+ return [{
254
+ "method": "tlOrderLoaded",
255
+ "name": "tlOrderLoaded",
256
+ "bubbles": true,
257
+ "cancelable": true,
258
+ "composed": true,
259
+ "docs": {
260
+ "tags": [],
261
+ "text": ""
262
+ },
263
+ "complexType": {
264
+ "original": "{ order: TlOrderData }",
265
+ "resolved": "{ order: TlOrderData; }",
266
+ "references": {
267
+ "TlOrderData": {
268
+ "location": "import",
269
+ "path": "../../utils/order-summary",
270
+ "id": "src/utils/order-summary.ts::TlOrderData",
271
+ "referenceLocation": "TlOrderData"
272
+ }
273
+ }
274
+ }
275
+ }, {
276
+ "method": "tlError",
277
+ "name": "tlError",
278
+ "bubbles": true,
279
+ "cancelable": true,
280
+ "composed": true,
281
+ "docs": {
282
+ "tags": [],
283
+ "text": ""
284
+ },
285
+ "complexType": {
286
+ "original": "{ message: string }",
287
+ "resolved": "{ message: string; }",
288
+ "references": {}
289
+ }
290
+ }];
291
+ }
292
+ static get watchers() {
293
+ return [{
294
+ "propName": "orderId",
295
+ "methodName": "watchOrderId"
296
+ }];
297
+ }
298
+ }
@@ -0,0 +1,30 @@
1
+ :host {
2
+ display: block;
3
+ font-family: var(--tl-font-family, system-ui, sans-serif);
4
+ }
5
+
6
+ .grid {
7
+ display: grid;
8
+ gap: var(--tl-spacing-4, 1rem);
9
+ grid-template-columns: 1fr;
10
+ }
11
+
12
+ @media (min-width: 480px) {
13
+ .grid.cols-2 {
14
+ grid-template-columns: repeat(2, 1fr);
15
+ }
16
+ .grid.cols-3 {
17
+ grid-template-columns: repeat(3, 1fr);
18
+ }
19
+ }
20
+
21
+ .state {
22
+ padding: var(--tl-spacing-6, 1.5rem);
23
+ text-align: center;
24
+ color: var(--tl-muted-foreground, #6b7280);
25
+ font-size: 0.875rem;
26
+ }
27
+
28
+ .state.error {
29
+ color: var(--tl-error, #dc2626);
30
+ }
@@ -0,0 +1,122 @@
1
+ import { h } from "@stencil/core";
2
+ import { whenSDK } from "../../utils/sdk-connector";
3
+ /**
4
+ * tl-order-tickets - renders all the tickets for an order as scannable QR
5
+ * cards with PDF download. Fetches the order through the SDK (order-access
6
+ * gated by the session), so it works on a confirmation page or in an account.
7
+ *
8
+ * Usage: <tl-order-tickets order-id="ord_xxx"></tl-order-tickets>
9
+ */
10
+ export class TlOrderTickets {
11
+ constructor() {
12
+ /** Columns in the grid (default 2). */
13
+ this.columns = 2;
14
+ this.tickets = [];
15
+ this.loading = true;
16
+ this.error = null;
17
+ }
18
+ watchOrder() {
19
+ this.load();
20
+ }
21
+ async componentWillLoad() {
22
+ await this.load();
23
+ }
24
+ async load() {
25
+ try {
26
+ this.loading = true;
27
+ this.error = null;
28
+ const sdk = await whenSDK();
29
+ const orders = sdk.getOrdersManager?.();
30
+ if (!orders)
31
+ throw new Error('Tickets are not available');
32
+ const order = (await orders.get(this.orderId));
33
+ this.tickets = order?.tickets ?? [];
34
+ }
35
+ catch (err) {
36
+ this.error = err instanceof Error ? err.message : 'Failed to load tickets';
37
+ }
38
+ finally {
39
+ this.loading = false;
40
+ }
41
+ }
42
+ render() {
43
+ if (this.loading) {
44
+ return h("div", { class: "state" }, "Loading tickets\u2026");
45
+ }
46
+ if (this.error) {
47
+ return h("div", { class: "state error" }, this.error);
48
+ }
49
+ if (this.tickets.length === 0) {
50
+ return h("div", { class: "state" }, "No tickets for this order yet.");
51
+ }
52
+ return (h("div", { class: `grid cols-${this.columns}` }, this.tickets.map((t) => (h("tl-ticket", { key: t.id, ticket: t, orderId: this.orderId })))));
53
+ }
54
+ static get is() { return "tl-order-tickets"; }
55
+ static get encapsulation() { return "shadow"; }
56
+ static get originalStyleUrls() {
57
+ return {
58
+ "$": ["tl-order-tickets.css"]
59
+ };
60
+ }
61
+ static get styleUrls() {
62
+ return {
63
+ "$": ["tl-order-tickets.css"]
64
+ };
65
+ }
66
+ static get properties() {
67
+ return {
68
+ "orderId": {
69
+ "type": "string",
70
+ "mutable": false,
71
+ "complexType": {
72
+ "original": "string",
73
+ "resolved": "string",
74
+ "references": {}
75
+ },
76
+ "required": true,
77
+ "optional": false,
78
+ "docs": {
79
+ "tags": [],
80
+ "text": "The order whose tickets to show."
81
+ },
82
+ "getter": false,
83
+ "setter": false,
84
+ "reflect": false,
85
+ "attribute": "order-id"
86
+ },
87
+ "columns": {
88
+ "type": "number",
89
+ "mutable": false,
90
+ "complexType": {
91
+ "original": "number",
92
+ "resolved": "number",
93
+ "references": {}
94
+ },
95
+ "required": false,
96
+ "optional": false,
97
+ "docs": {
98
+ "tags": [],
99
+ "text": "Columns in the grid (default 2)."
100
+ },
101
+ "getter": false,
102
+ "setter": false,
103
+ "reflect": false,
104
+ "attribute": "columns",
105
+ "defaultValue": "2"
106
+ }
107
+ };
108
+ }
109
+ static get states() {
110
+ return {
111
+ "tickets": {},
112
+ "loading": {},
113
+ "error": {}
114
+ };
115
+ }
116
+ static get watchers() {
117
+ return [{
118
+ "propName": "orderId",
119
+ "methodName": "watchOrder"
120
+ }];
121
+ }
122
+ }