@ticketlayer/elements 0.4.0 → 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 (161) hide show
  1. package/dist/cjs/{index-I9CoYv3k.js → index-DanPUX0s.js} +1 -1
  2. package/dist/cjs/index.cjs.js +2 -1
  3. package/dist/cjs/loader.cjs.js +2 -2
  4. package/dist/cjs/ticket-selector-BYVL13k_.js +3623 -0
  5. package/dist/cjs/ticketlayer-elements.cjs.js +2 -2
  6. package/dist/cjs/tl-buy-modal.cjs.entry.js +3 -3
  7. package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +89 -74
  8. package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +61 -30
  9. package/dist/cjs/tl-cart-badge.cjs.entry.js +74 -61
  10. package/dist/cjs/tl-cart-drawer.cjs.entry.js +51 -23
  11. package/dist/cjs/tl-cart.cjs.entry.js +71 -103
  12. package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +51 -29
  13. package/dist/cjs/tl-event-card.cjs.entry.js +43 -105
  14. package/dist/cjs/tl-event-list.cjs.entry.js +72 -35
  15. package/dist/cjs/tl-login.cjs.entry.js +74 -141
  16. package/dist/cjs/tl-my-orders.cjs.entry.js +52 -92
  17. package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +153 -921
  18. package/dist/cjs/tl-order-confirmation.cjs.entry.js +71 -107
  19. package/dist/cjs/tl-order-tickets_2.cjs.entry.js +48 -74
  20. package/dist/cjs/tl-promo-code.cjs.entry.js +50 -47
  21. package/dist/cjs/tl-seat-map.cjs.entry.js +3 -3
  22. package/dist/cjs/web-query-Ce73jB4k.js +16 -0
  23. package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +2 -2
  24. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +37 -0
  25. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +105 -79
  26. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +73 -30
  27. package/dist/collection/components/tl-cart/tl-cart.js +82 -107
  28. package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +62 -62
  29. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +56 -24
  30. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +61 -29
  31. package/dist/collection/components/tl-event-card/event-card-adapter.js +11 -23
  32. package/dist/collection/components/tl-event-card/tl-event-card.js +51 -84
  33. package/dist/collection/components/tl-event-list/tl-event-list.js +82 -38
  34. package/dist/collection/components/tl-login/tl-login.js +83 -147
  35. package/dist/collection/components/tl-my-orders/tl-my-orders.js +58 -96
  36. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +137 -516
  37. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +81 -115
  38. package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +57 -92
  39. package/dist/collection/components/tl-promo-code/tl-promo-code.js +55 -50
  40. package/dist/collection/components/tl-seat-map/tl-seat-map.js +2 -2
  41. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +34 -20
  42. package/dist/collection/utils/order-summary.js +15 -135
  43. package/dist/collection/utils/testing/deep-text.js +29 -0
  44. package/dist/collection/utils/testing/fixtures.js +290 -0
  45. package/dist/collection/utils/testing/mock-sdk.js +13 -2
  46. package/dist/collection/utils/web-query.js +12 -0
  47. package/dist/collection/utils/web-storage.js +22 -0
  48. package/dist/components/ticket-selector.js +1 -0
  49. package/dist/components/tl-buy-modal.js +1 -1
  50. package/dist/components/tl-buy-tickets-button.js +1 -1
  51. package/dist/components/tl-buy-tickets-wrapper2.js +1 -1
  52. package/dist/components/tl-cart-badge.js +1 -1
  53. package/dist/components/tl-cart-drawer.js +1 -1
  54. package/dist/components/tl-cart2.js +1 -1
  55. package/dist/components/tl-checkout-wrapper2.js +1 -1
  56. package/dist/components/tl-event-card2.js +1 -1
  57. package/dist/components/tl-event-list.js +1 -1
  58. package/dist/components/tl-login2.js +1 -1
  59. package/dist/components/tl-my-orders.js +1 -1
  60. package/dist/components/tl-occurrence-selector2.js +1 -1
  61. package/dist/components/tl-order-confirmation.js +1 -1
  62. package/dist/components/tl-order-tickets2.js +1 -1
  63. package/dist/components/tl-promo-code.js +1 -1
  64. package/dist/components/tl-seat-map.js +1 -1
  65. package/dist/components/tl-ticket-selector2.js +1 -1
  66. package/dist/components/web-query.js +1 -0
  67. package/dist/elements.docs.json +34 -34
  68. package/dist/esm/{index-OoYV1faY.js → index-imfxp765.js} +1 -1
  69. package/dist/esm/index.js +2 -1
  70. package/dist/esm/loader.js +3 -3
  71. package/dist/esm/ticket-selector-BwChnHVC.js +3606 -0
  72. package/dist/esm/ticketlayer-elements.js +3 -3
  73. package/dist/esm/tl-buy-modal.entry.js +3 -3
  74. package/dist/esm/tl-buy-tickets-button.entry.js +90 -75
  75. package/dist/esm/tl-buy-tickets-wrapper.entry.js +62 -31
  76. package/dist/esm/tl-cart-badge.entry.js +75 -62
  77. package/dist/esm/tl-cart-drawer.entry.js +52 -24
  78. package/dist/esm/tl-cart.entry.js +72 -104
  79. package/dist/esm/tl-checkout-wrapper.entry.js +52 -30
  80. package/dist/esm/tl-event-card.entry.js +44 -106
  81. package/dist/esm/tl-event-list.entry.js +73 -36
  82. package/dist/esm/tl-login.entry.js +75 -142
  83. package/dist/esm/tl-my-orders.entry.js +53 -93
  84. package/dist/esm/tl-occurrence-selector_2.entry.js +152 -920
  85. package/dist/esm/tl-order-confirmation.entry.js +72 -108
  86. package/dist/esm/tl-order-tickets_2.entry.js +49 -75
  87. package/dist/esm/tl-promo-code.entry.js +51 -48
  88. package/dist/esm/tl-seat-map.entry.js +3 -3
  89. package/dist/esm/web-query-Bdbb2lk5.js +14 -0
  90. package/dist/ticketlayer-elements/index.esm.js +1 -1
  91. package/dist/ticketlayer-elements/p-45923d2c.entry.js +1 -0
  92. package/dist/ticketlayer-elements/p-492adc77.entry.js +1 -0
  93. package/dist/ticketlayer-elements/p-4cfc9e53.entry.js +1 -0
  94. package/dist/ticketlayer-elements/{p-ac264adc.entry.js → p-5135630e.entry.js} +1 -1
  95. package/dist/ticketlayer-elements/p-5bbce3b5.entry.js +1 -0
  96. package/dist/ticketlayer-elements/p-7d547721.entry.js +1 -0
  97. package/dist/ticketlayer-elements/p-7fe7b6ba.entry.js +1 -0
  98. package/dist/ticketlayer-elements/p-8122f0c1.entry.js +1 -0
  99. package/dist/ticketlayer-elements/p-98d40869.entry.js +1 -0
  100. package/dist/ticketlayer-elements/p-Bdbb2lk5.js +1 -0
  101. package/dist/ticketlayer-elements/p-BwChnHVC.js +1 -0
  102. package/dist/ticketlayer-elements/p-abec290a.entry.js +1 -0
  103. package/dist/ticketlayer-elements/p-ac79a3f6.entry.js +1 -0
  104. package/dist/ticketlayer-elements/p-d01f4429.entry.js +1 -0
  105. package/dist/ticketlayer-elements/p-d7ffaf46.entry.js +1 -0
  106. package/dist/ticketlayer-elements/p-e7170f3c.entry.js +1 -0
  107. package/dist/ticketlayer-elements/p-e787e862.entry.js +1 -0
  108. package/dist/ticketlayer-elements/p-f1ff931f.entry.js +1 -0
  109. package/dist/ticketlayer-elements/p-imfxp765.js +3 -0
  110. package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -1
  111. package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -1
  112. package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +40 -8
  113. package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +20 -1
  114. package/dist/types/components/tl-cart/tl-cart.d.ts +40 -42
  115. package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +22 -7
  116. package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +28 -5
  117. package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +18 -1
  118. package/dist/types/components/tl-event-card/event-card-adapter.d.ts +9 -20
  119. package/dist/types/components/tl-event-card/tl-event-card.d.ts +25 -28
  120. package/dist/types/components/tl-event-list/tl-event-list.d.ts +46 -6
  121. package/dist/types/components/tl-login/tl-login.d.ts +68 -22
  122. package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +30 -12
  123. package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +73 -161
  124. package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +48 -15
  125. package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +29 -31
  126. package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +28 -10
  127. package/dist/types/components/tl-ticket/tl-ticket.d.ts +2 -11
  128. package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +22 -4
  129. package/dist/types/components.d.ts +270 -62
  130. package/dist/types/utils/order-summary.d.ts +14 -97
  131. package/dist/types/utils/testing/deep-text.d.ts +13 -0
  132. package/dist/types/utils/testing/fixtures.d.ts +225 -0
  133. package/dist/types/utils/testing/mock-sdk.d.ts +14 -1
  134. package/dist/types/utils/web-query.d.ts +18 -0
  135. package/dist/types/utils/web-storage.d.ts +16 -0
  136. package/elements.catalogue.json +602 -50
  137. package/package.json +8 -6
  138. package/dist/cjs/money-BtmM2gt-.js +0 -118
  139. package/dist/cjs/order-summary-X3TlZxc2.js +0 -150
  140. package/dist/components/money.js +0 -1
  141. package/dist/components/order-summary.js +0 -1
  142. package/dist/esm/money-4ig0-WB6.js +0 -112
  143. package/dist/esm/order-summary-DiDtvfZM.js +0 -136
  144. package/dist/ticketlayer-elements/p-0814420f.entry.js +0 -1
  145. package/dist/ticketlayer-elements/p-0acabc26.entry.js +0 -1
  146. package/dist/ticketlayer-elements/p-109a3feb.entry.js +0 -1
  147. package/dist/ticketlayer-elements/p-1a4ba71c.entry.js +0 -1
  148. package/dist/ticketlayer-elements/p-318965c1.entry.js +0 -1
  149. package/dist/ticketlayer-elements/p-347c7fd1.entry.js +0 -1
  150. package/dist/ticketlayer-elements/p-3649c26d.entry.js +0 -1
  151. package/dist/ticketlayer-elements/p-40260cc9.entry.js +0 -1
  152. package/dist/ticketlayer-elements/p-4ig0-WB6.js +0 -1
  153. package/dist/ticketlayer-elements/p-55fd2f9f.entry.js +0 -1
  154. package/dist/ticketlayer-elements/p-62ee659b.entry.js +0 -1
  155. package/dist/ticketlayer-elements/p-7d4ecc2b.entry.js +0 -1
  156. package/dist/ticketlayer-elements/p-7f0abf95.entry.js +0 -1
  157. package/dist/ticketlayer-elements/p-BXpVGW51.js +0 -1
  158. package/dist/ticketlayer-elements/p-OoYV1faY.js +0 -3
  159. package/dist/ticketlayer-elements/p-a3c1b26c.entry.js +0 -1
  160. package/dist/ticketlayer-elements/p-aeb4c6fb.entry.js +0 -1
  161. package/dist/ticketlayer-elements/p-ba62541f.entry.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticketlayer/elements",
3
- "version": "0.4.0",
3
+ "version": "1.0.0",
4
4
  "description": "Ticketlayer UI components built with Stencil.js",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.js",
@@ -42,10 +42,10 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "qrcode": "^1.5.4",
45
- "@ticketlayer/elements-core": "^0.1.0"
45
+ "@ticketlayer/elements-core": "^0.2.0"
46
46
  },
47
47
  "peerDependencies": {
48
- "@ticketlayer/live": ">=0.1.0 <1.0.0"
48
+ "@ticketlayer/live": ">=1.0.0"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "@ticketlayer/live": {
@@ -76,8 +76,8 @@
76
76
  "react-dom": "^18.3.1",
77
77
  "storybook": "^10.1.11",
78
78
  "typescript": "^5.3.3",
79
- "@ticketlayer/live": "^0.4.0",
80
- "@ticketlayer/theme": "^0.1.0"
79
+ "@ticketlayer/live": "^1.0.0",
80
+ "@ticketlayer/theme": "^0.2.0"
81
81
  },
82
82
  "scripts": {
83
83
  "build": "node scripts/write-global-css.mjs && stencil build && node scripts/patch-react-generated.js",
@@ -93,8 +93,10 @@
93
93
  "build-storybook": "storybook build",
94
94
  "build:catalogue": "node scripts/build-catalogue.mjs",
95
95
  "build:catalogue:version": "node scripts/build-catalogue.mjs --version-only",
96
+ "build:stories": "node scripts/build-stories.mjs",
96
97
  "test:catalogue": "node scripts/build-catalogue.mjs --check",
98
+ "test:stories": "node scripts/build-stories.mjs --check",
97
99
  "typecheck": "tsc -p tsconfig.json --noEmit",
98
- "lint": "node scripts/write-global-css.mjs --check && node scripts/check-token-usage.mjs && node scripts/check-core-tokens.mjs"
100
+ "lint": "node scripts/write-global-css.mjs --check && node scripts/check-token-usage.mjs && node scripts/check-core-tokens.mjs && node scripts/build-stories.mjs --check"
99
101
  }
100
102
  }
@@ -1,118 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * The money rows a cart-shaped view renders: subtotal, the booking fee, a
5
- * discount and the total.
6
- *
7
- * Pure: no Stencil, no DOM, no SDK. A core derives these figures and a harness
8
- * renders what it returns, so the rule for what is shown can be unit tested on
9
- * its own (the platform plan's headless-core split, AGENTS.md). It lived in
10
- * `packages/elements/src/utils/cart-totals.ts` until the core split (TKT-68)
11
- * and is re-exported from there, so every existing import still resolves.
12
- *
13
- * The client (`@ticketlayer/live`) already derives these figures from the
14
- * sales-surface response and publishes `subtotal`, `fees`, `discount`,
15
- * `total`, `hasFees` and `feeLabel` on the cart it hands us. Cores never
16
- * import the client, so the same rule is stated here for the case where a
17
- * host binds an older connector, or a bare sales cart: read what is there,
18
- * and never invent money that is not.
19
- *
20
- * Money is integer minor units (2500 = 25.00) everywhere on this platform.
21
- * Nothing here divides or multiplies money; the only division is the one
22
- * `formatPrice` does to render it.
23
- */
24
- /** Shown when the response names no fee of its own. */
25
- const FEE_LABEL_FALLBACK = 'Booking fee';
26
- /**
27
- * Minor units, or null when the value is not minor units. Integers and
28
- * all-digit strings only: a decimal is not minor units, and treating it as
29
- * money here would put floating point into a total.
30
- */
31
- function minorUnits(value) {
32
- if (typeof value === 'number')
33
- return Number.isInteger(value) ? value : null;
34
- if (typeof value === 'string' && /^-?\d+$/.test(value))
35
- return Number(value);
36
- return null;
37
- }
38
- /**
39
- * THE RULE: a fee of zero is not the same as no fee.
40
- *
41
- * Render the fee row when the response EXPRESSES a fee - an amount above
42
- * zero, or a fee the surface has NAMED (a `type: 'fee'` line or a fee label)
43
- * even where it computes to zero. A response with no fee figure at all, or a
44
- * bare zero nothing names, expresses no fee: a channel with no fee profile
45
- * renders no row, and we never show a "Booking fee 0.00" the surface did not
46
- * put there.
47
- */
48
- function expressesFee(fees, named) {
49
- if (fees === null)
50
- return named;
51
- return fees > 0 || named;
52
- }
53
- const lineType = (line) => line.type || 'ticket';
54
- function cartTotals(cart) {
55
- const c = cart || {};
56
- const lines = Array.isArray(c.items) ? c.items : [];
57
- const feeLines = lines.filter((l) => lineType(l) === 'fee');
58
- const discountLines = lines.filter((l) => lineType(l) === 'discount');
59
- const subtotal = minorUnits(c.itemsSubtotal) ??
60
- minorUnits(c.subtotal) ??
61
- lines
62
- .filter((l) => !['fee', 'tax', 'discount'].includes(lineType(l)))
63
- .reduce((sum, l) => sum + (minorUnits(l.subtotal) ?? 0), 0);
64
- const feeFromLines = feeLines.length
65
- ? feeLines.reduce((sum, l) => sum + (minorUnits(l.subtotal) ?? 0), 0)
66
- : null;
67
- const fees = minorUnits(c.totalFees) ?? minorUnits(c.fees) ?? feeFromLines;
68
- const discountFromLines = discountLines.length
69
- ? discountLines.reduce((sum, l) => sum + Math.abs(minorUnits(l.subtotal) ?? 0), 0)
70
- : null;
71
- const discount = minorUnits(c.totalDiscounts) ?? minorUnits(c.discount) ?? discountFromLines;
72
- const tax = minorUnits(c.totalTax) ?? 0;
73
- const named = (typeof c.feeLabel === 'string' && !!c.feeLabel) ||
74
- feeLines.some((l) => typeof l.name === 'string' && !!l.name);
75
- const label = (typeof c.feeLabel === 'string' && c.feeLabel) ||
76
- feeLines.map((l) => l.name).find((n) => typeof n === 'string' && !!n) ||
77
- FEE_LABEL_FALLBACK;
78
- return {
79
- currency: c.currency || 'GBP',
80
- subtotal,
81
- fees: fees ?? 0,
82
- discount: discount ?? 0,
83
- // The server's total is what the buyer is charged, so it wins over any sum
84
- // we could do here. We only add up when the response carries no total.
85
- total: minorUnits(c.total) ?? subtotal + (fees ?? 0) + tax - (discount ?? 0),
86
- // The client already applied the rule; honour its answer when it sent one.
87
- showFee: typeof c.hasFees === 'boolean' ? c.hasFees : expressesFee(fees, named),
88
- feeLabel: label,
89
- };
90
- }
91
- /**
92
- * The totals for a selection that is not yet a cart (tl-ticket-selector).
93
- *
94
- * The buyer-facing surface quotes no fee before the cart exists - there is no
95
- * quote or preview operation, and the channel's fee profile is not exposed to
96
- * a publishable key - so `quote` is null on every call today and no fee row is
97
- * rendered. It is a parameter rather than an assumption so that the row lights
98
- * up from data the moment the surface quotes one, and so the rule stays
99
- * testable now.
100
- */
101
- function selectionTotals(lines, quote = null) {
102
- const subtotal = (lines || []).reduce((sum, l) => sum + (minorUnits(l.price) ?? 0) * (Math.max(0, Math.trunc(l.quantity || 0)) || 0), 0);
103
- const fees = quote ? minorUnits(quote.amount) : null;
104
- const named = !!quote?.label;
105
- return {
106
- subtotal,
107
- fees: fees ?? 0,
108
- total: subtotal + (fees ?? 0),
109
- showFee: quote ? expressesFee(fees, named) : false,
110
- feeLabel: quote?.label || FEE_LABEL_FALLBACK,
111
- };
112
- }
113
-
114
- exports.FEE_LABEL_FALLBACK = FEE_LABEL_FALLBACK;
115
- exports.cartTotals = cartTotals;
116
- exports.expressesFee = expressesFee;
117
- exports.minorUnits = minorUnits;
118
- exports.selectionTotals = selectionTotals;
@@ -1,150 +0,0 @@
1
- 'use strict';
2
-
3
- var money = require('./money-BtmM2gt-.js');
4
-
5
- /** Money arrives as minor-unit integers or decimal strings ("50.00"). */
6
- function formatPrice(value, currency = 'GBP') {
7
- if (value === null || value === undefined || value === '')
8
- return '';
9
- const fmt = new Intl.NumberFormat('en-GB', { style: 'currency', currency });
10
- if (typeof value === 'number')
11
- return fmt.format(value / 100);
12
- const n = Number(value);
13
- return Number.isNaN(n) ? String(value) : fmt.format(n);
14
- }
15
- function formatDateTime(iso) {
16
- return new Date(iso).toLocaleDateString('en-GB', {
17
- weekday: 'short',
18
- day: 'numeric',
19
- month: 'short',
20
- year: 'numeric',
21
- hour: '2-digit',
22
- minute: '2-digit',
23
- });
24
- }
25
- function formatDate(iso) {
26
- return new Date(iso).toLocaleDateString('en-GB', {
27
- day: 'numeric',
28
- month: 'short',
29
- year: 'numeric',
30
- });
31
- }
32
- /** Line items that are tickets (fees, taxes and discounts have their own type). */
33
- function ticketItems(order) {
34
- return (order.items ?? []).filter((i) => !i.type || i.type === 'ticket');
35
- }
36
- function ticketCount(order) {
37
- const fromItems = ticketItems(order).reduce((sum, i) => sum + (Number(i.quantity) || 0), 0);
38
- return fromItems || order.tickets?.length || 0;
39
- }
40
- /** Event name as the order view exposes it: the first ticket line's name. */
41
- function orderEventName(order) {
42
- return order.eventName || ticketItems(order)[0]?.name || order.items?.[0]?.name || null;
43
- }
44
- function orderCustomerEmail(order, customer) {
45
- const fromOrder = order?.customerEmail || order?.customer?.email;
46
- if (fromOrder)
47
- return fromOrder;
48
- const c = customer;
49
- return c?.email || null;
50
- }
51
- function statusLabel(status) {
52
- if (!status)
53
- return '';
54
- return status.replace(/_/g, ' ').replace(/^\w/, (m) => m.toUpperCase());
55
- }
56
- function statusTone(status) {
57
- switch (status) {
58
- case 'confirmed':
59
- return 'success';
60
- case 'completed':
61
- case 'fulfilled':
62
- return 'info';
63
- case 'pending':
64
- case 'processing':
65
- return 'warning';
66
- case 'cancelled':
67
- case 'expired':
68
- case 'failed':
69
- return 'error';
70
- default:
71
- return 'muted';
72
- }
73
- }
74
- function errorMessage(err, fallback) {
75
- return err instanceof Error && err.message ? err.message : fallback;
76
- }
77
- /** True for the "not signed in" family of API errors (401/403, NO_SESSION, NOT_AUTHENTICATED). */
78
- function isAuthError(err) {
79
- const e = err;
80
- if (!e || typeof e !== 'object')
81
- return false;
82
- return e.status === 401 || e.status === 403 || e.code === 'NO_SESSION' || e.code === 'NOT_AUTHENTICATED';
83
- }
84
- /**
85
- * Find the event and start time for an occurrence id. The order view only
86
- * carries the occurrence id, so this walks the channel's listings (cached by
87
- * live-api) until one owns the occurrence. Best effort: null when not found.
88
- */
89
- async function resolveOccurrence(sdk, occurrenceId, maxEvents = 12) {
90
- const events = sdk.getEventsManager();
91
- const listing = await events.list({ limit: maxEvents });
92
- const candidates = listing.items.slice(0, maxEvents);
93
- for (const candidate of candidates) {
94
- const detail = (await events.get(candidate.id).catch(() => null));
95
- const occ = detail?.occurrences?.find((o) => o.id === occurrenceId);
96
- if (occ) {
97
- return {
98
- eventId: detail?.id ?? candidate.id,
99
- eventName: detail?.name ?? candidate.name ?? '',
100
- startsAt: occ.startsAt ?? null,
101
- };
102
- }
103
- }
104
- return null;
105
- }
106
- function orderTotals(order) {
107
- const o = order || {};
108
- const items = Array.isArray(o.items) ? o.items : [];
109
- const feeLines = items.filter((i) => i.type === 'fee');
110
- const discountLines = items.filter((i) => i.type === 'discount');
111
- const tickets = ticketItems(o);
112
- const feeFromLines = feeLines.length
113
- ? feeLines.reduce((sum, i) => sum + (money.minorUnits(i.subtotal) ?? 0), 0)
114
- : null;
115
- const fees = feeFromLines ?? money.minorUnits(o.totalFees) ?? 0;
116
- const declaredSubtotal = money.minorUnits(o.subtotal);
117
- const subtotal = tickets.length
118
- ? tickets.reduce((sum, i) => sum + (money.minorUnits(i.subtotal) ?? 0), 0)
119
- : (declaredSubtotal ?? 0) - fees;
120
- const discountFromLines = discountLines.length
121
- ? discountLines.reduce((sum, i) => sum + Math.abs(money.minorUnits(i.subtotal) ?? 0), 0)
122
- : null;
123
- const discount = money.minorUnits(o.totalDiscounts) ?? discountFromLines ?? 0;
124
- const tax = money.minorUnits(o.totalTax) ?? 0;
125
- const named = feeLines.some((i) => typeof i.name === 'string' && !!i.name);
126
- return {
127
- currency: o.currency || 'GBP',
128
- subtotal,
129
- fees,
130
- feeLabel: feeLines.map((i) => i.name).find((n) => !!n) || money.FEE_LABEL_FALLBACK,
131
- showFee: money.expressesFee(feeFromLines ?? money.minorUnits(o.totalFees), named),
132
- tax,
133
- discount,
134
- total: money.minorUnits(o.total) ?? subtotal + fees + tax - discount,
135
- };
136
- }
137
-
138
- exports.errorMessage = errorMessage;
139
- exports.formatDate = formatDate;
140
- exports.formatDateTime = formatDateTime;
141
- exports.formatPrice = formatPrice;
142
- exports.isAuthError = isAuthError;
143
- exports.orderCustomerEmail = orderCustomerEmail;
144
- exports.orderEventName = orderEventName;
145
- exports.orderTotals = orderTotals;
146
- exports.resolveOccurrence = resolveOccurrence;
147
- exports.statusLabel = statusLabel;
148
- exports.statusTone = statusTone;
149
- exports.ticketCount = ticketCount;
150
- exports.ticketItems = ticketItems;
@@ -1 +0,0 @@
1
- const t="Booking fee";function e(t){return"number"==typeof t?Number.isInteger(t)?t:null:"string"==typeof t&&/^-?\d+$/.test(t)?Number(t):null}function n(t,e){return null===t?e:t>0||e}const o=t=>t.type||"ticket";function s(s){const u=s||{},l=Array.isArray(u.items)?u.items:[],r=l.filter((t=>"fee"===o(t))),f=l.filter((t=>"discount"===o(t))),a=e(u.itemsSubtotal)??e(u.subtotal)??l.filter((t=>!["fee","tax","discount"].includes(o(t)))).reduce(((t,n)=>t+(e(n.subtotal)??0)),0),c=r.length?r.reduce(((t,n)=>t+(e(n.subtotal)??0)),0):null,i=e(u.totalFees)??e(u.fees)??c,y=f.length?f.reduce(((t,n)=>t+Math.abs(e(n.subtotal)??0)),0):null,b=e(u.totalDiscounts)??e(u.discount)??y,p=e(u.totalTax)??0,g="string"==typeof u.feeLabel&&!!u.feeLabel||r.some((t=>"string"==typeof t.name&&!!t.name)),h="string"==typeof u.feeLabel&&u.feeLabel||r.map((t=>t.name)).find((t=>"string"==typeof t&&!!t))||t;return{currency:u.currency||"GBP",subtotal:a,fees:i??0,discount:b??0,total:e(u.total)??a+(i??0)+p-(b??0),showFee:"boolean"==typeof u.hasFees?u.hasFees:n(i,g),feeLabel:h}}function u(o,s=null){const u=(o||[]).reduce(((t,n)=>t+(e(n.price)??0)*(Math.max(0,Math.trunc(n.quantity||0))||0)),0),l=s?e(s.amount):null;return{subtotal:u,fees:l??0,total:u+(l??0),showFee:!!s&&n(l,!!s?.label),feeLabel:s?.label||t}}export{t as F,s as c,n as e,e as m,u as s}
@@ -1 +0,0 @@
1
- import{m as n,e,F as t}from"./money.js";function r(n,e="GBP"){if(null==n||""===n)return"";const t=new Intl.NumberFormat("en-GB",{style:"currency",currency:e});if("number"==typeof n)return t.format(n/100);const r=Number(n);return Number.isNaN(r)?String(n):t.format(r)}function u(n){return new Date(n).toLocaleDateString("en-GB",{weekday:"short",day:"numeric",month:"short",year:"numeric",hour:"2-digit",minute:"2-digit"})}function c(n){return new Date(n).toLocaleDateString("en-GB",{day:"numeric",month:"short",year:"numeric"})}function s(n){return(n.items??[]).filter((n=>!n.type||"ticket"===n.type))}function a(n){return s(n).reduce(((n,e)=>n+(Number(e.quantity)||0)),0)||n.tickets?.length||0}function o(n){return n.eventName||s(n)[0]?.name||n.items?.[0]?.name||null}function i(n,e){const t=n?.customerEmail||n?.customer?.email;if(t)return t;const r=e;return r?.email||null}function f(n){return n?n.replace(/_/g," ").replace(/^\w/,(n=>n.toUpperCase())):""}function l(n){switch(n){case"confirmed":return"success";case"completed":case"fulfilled":return"info";case"pending":case"processing":return"warning";case"cancelled":case"expired":case"failed":return"error";default:return"muted"}}function m(n,e){return n instanceof Error&&n.message?n.message:e}function d(n){return!(!n||"object"!=typeof n||401!==n.status&&403!==n.status&&"NO_SESSION"!==n.code&&"NOT_AUTHENTICATED"!==n.code)}async function y(n,e,t=12){const r=n.getEventsManager(),u=(await r.list({limit:t})).items.slice(0,t);for(const n of u){const t=await r.get(n.id).catch((()=>null)),u=t?.occurrences?.find((n=>n.id===e));if(u)return{eventId:t?.id??n.id,eventName:t?.name??n.name??"",startsAt:u.startsAt??null}}return null}function h(r){const u=r||{},c=Array.isArray(u.items)?u.items:[],a=c.filter((n=>"fee"===n.type)),o=c.filter((n=>"discount"===n.type)),i=s(u),f=a.length?a.reduce(((e,t)=>e+(n(t.subtotal)??0)),0):null,l=f??n(u.totalFees)??0,m=n(u.subtotal),d=i.length?i.reduce(((e,t)=>e+(n(t.subtotal)??0)),0):(m??0)-l,y=o.length?o.reduce(((e,t)=>e+Math.abs(n(t.subtotal)??0)),0):null,h=n(u.totalDiscounts)??y??0,w=n(u.totalTax)??0,g=a.some((n=>"string"==typeof n.name&&!!n.name));return{currency:u.currency||"GBP",subtotal:d,fees:l,feeLabel:a.map((n=>n.name)).find((n=>!!n))||t,showFee:e(f??n(u.totalFees),g),tax:w,discount:h,total:n(u.total)??d+l+w-h}}export{r as a,f as b,i as c,u as d,m as e,c as f,h as g,s as h,d as i,o,y as r,l as s,a as t}
@@ -1,112 +0,0 @@
1
- /**
2
- * The money rows a cart-shaped view renders: subtotal, the booking fee, a
3
- * discount and the total.
4
- *
5
- * Pure: no Stencil, no DOM, no SDK. A core derives these figures and a harness
6
- * renders what it returns, so the rule for what is shown can be unit tested on
7
- * its own (the platform plan's headless-core split, AGENTS.md). It lived in
8
- * `packages/elements/src/utils/cart-totals.ts` until the core split (TKT-68)
9
- * and is re-exported from there, so every existing import still resolves.
10
- *
11
- * The client (`@ticketlayer/live`) already derives these figures from the
12
- * sales-surface response and publishes `subtotal`, `fees`, `discount`,
13
- * `total`, `hasFees` and `feeLabel` on the cart it hands us. Cores never
14
- * import the client, so the same rule is stated here for the case where a
15
- * host binds an older connector, or a bare sales cart: read what is there,
16
- * and never invent money that is not.
17
- *
18
- * Money is integer minor units (2500 = 25.00) everywhere on this platform.
19
- * Nothing here divides or multiplies money; the only division is the one
20
- * `formatPrice` does to render it.
21
- */
22
- /** Shown when the response names no fee of its own. */
23
- const FEE_LABEL_FALLBACK = 'Booking fee';
24
- /**
25
- * Minor units, or null when the value is not minor units. Integers and
26
- * all-digit strings only: a decimal is not minor units, and treating it as
27
- * money here would put floating point into a total.
28
- */
29
- function minorUnits(value) {
30
- if (typeof value === 'number')
31
- return Number.isInteger(value) ? value : null;
32
- if (typeof value === 'string' && /^-?\d+$/.test(value))
33
- return Number(value);
34
- return null;
35
- }
36
- /**
37
- * THE RULE: a fee of zero is not the same as no fee.
38
- *
39
- * Render the fee row when the response EXPRESSES a fee - an amount above
40
- * zero, or a fee the surface has NAMED (a `type: 'fee'` line or a fee label)
41
- * even where it computes to zero. A response with no fee figure at all, or a
42
- * bare zero nothing names, expresses no fee: a channel with no fee profile
43
- * renders no row, and we never show a "Booking fee 0.00" the surface did not
44
- * put there.
45
- */
46
- function expressesFee(fees, named) {
47
- if (fees === null)
48
- return named;
49
- return fees > 0 || named;
50
- }
51
- const lineType = (line) => line.type || 'ticket';
52
- function cartTotals(cart) {
53
- const c = cart || {};
54
- const lines = Array.isArray(c.items) ? c.items : [];
55
- const feeLines = lines.filter((l) => lineType(l) === 'fee');
56
- const discountLines = lines.filter((l) => lineType(l) === 'discount');
57
- const subtotal = minorUnits(c.itemsSubtotal) ??
58
- minorUnits(c.subtotal) ??
59
- lines
60
- .filter((l) => !['fee', 'tax', 'discount'].includes(lineType(l)))
61
- .reduce((sum, l) => sum + (minorUnits(l.subtotal) ?? 0), 0);
62
- const feeFromLines = feeLines.length
63
- ? feeLines.reduce((sum, l) => sum + (minorUnits(l.subtotal) ?? 0), 0)
64
- : null;
65
- const fees = minorUnits(c.totalFees) ?? minorUnits(c.fees) ?? feeFromLines;
66
- const discountFromLines = discountLines.length
67
- ? discountLines.reduce((sum, l) => sum + Math.abs(minorUnits(l.subtotal) ?? 0), 0)
68
- : null;
69
- const discount = minorUnits(c.totalDiscounts) ?? minorUnits(c.discount) ?? discountFromLines;
70
- const tax = minorUnits(c.totalTax) ?? 0;
71
- const named = (typeof c.feeLabel === 'string' && !!c.feeLabel) ||
72
- feeLines.some((l) => typeof l.name === 'string' && !!l.name);
73
- const label = (typeof c.feeLabel === 'string' && c.feeLabel) ||
74
- feeLines.map((l) => l.name).find((n) => typeof n === 'string' && !!n) ||
75
- FEE_LABEL_FALLBACK;
76
- return {
77
- currency: c.currency || 'GBP',
78
- subtotal,
79
- fees: fees ?? 0,
80
- discount: discount ?? 0,
81
- // The server's total is what the buyer is charged, so it wins over any sum
82
- // we could do here. We only add up when the response carries no total.
83
- total: minorUnits(c.total) ?? subtotal + (fees ?? 0) + tax - (discount ?? 0),
84
- // The client already applied the rule; honour its answer when it sent one.
85
- showFee: typeof c.hasFees === 'boolean' ? c.hasFees : expressesFee(fees, named),
86
- feeLabel: label,
87
- };
88
- }
89
- /**
90
- * The totals for a selection that is not yet a cart (tl-ticket-selector).
91
- *
92
- * The buyer-facing surface quotes no fee before the cart exists - there is no
93
- * quote or preview operation, and the channel's fee profile is not exposed to
94
- * a publishable key - so `quote` is null on every call today and no fee row is
95
- * rendered. It is a parameter rather than an assumption so that the row lights
96
- * up from data the moment the surface quotes one, and so the rule stays
97
- * testable now.
98
- */
99
- function selectionTotals(lines, quote = null) {
100
- const subtotal = (lines || []).reduce((sum, l) => sum + (minorUnits(l.price) ?? 0) * (Math.max(0, Math.trunc(l.quantity || 0)) || 0), 0);
101
- const fees = quote ? minorUnits(quote.amount) : null;
102
- const named = !!quote?.label;
103
- return {
104
- subtotal,
105
- fees: fees ?? 0,
106
- total: subtotal + (fees ?? 0),
107
- showFee: quote ? expressesFee(fees, named) : false,
108
- feeLabel: quote?.label || FEE_LABEL_FALLBACK,
109
- };
110
- }
111
-
112
- export { FEE_LABEL_FALLBACK as F, cartTotals as c, expressesFee as e, minorUnits as m, selectionTotals as s };
@@ -1,136 +0,0 @@
1
- import { m as minorUnits, e as expressesFee, F as FEE_LABEL_FALLBACK } from './money-4ig0-WB6.js';
2
-
3
- /** Money arrives as minor-unit integers or decimal strings ("50.00"). */
4
- function formatPrice(value, currency = 'GBP') {
5
- if (value === null || value === undefined || value === '')
6
- return '';
7
- const fmt = new Intl.NumberFormat('en-GB', { style: 'currency', currency });
8
- if (typeof value === 'number')
9
- return fmt.format(value / 100);
10
- const n = Number(value);
11
- return Number.isNaN(n) ? String(value) : fmt.format(n);
12
- }
13
- function formatDateTime(iso) {
14
- return new Date(iso).toLocaleDateString('en-GB', {
15
- weekday: 'short',
16
- day: 'numeric',
17
- month: 'short',
18
- year: 'numeric',
19
- hour: '2-digit',
20
- minute: '2-digit',
21
- });
22
- }
23
- function formatDate(iso) {
24
- return new Date(iso).toLocaleDateString('en-GB', {
25
- day: 'numeric',
26
- month: 'short',
27
- year: 'numeric',
28
- });
29
- }
30
- /** Line items that are tickets (fees, taxes and discounts have their own type). */
31
- function ticketItems(order) {
32
- return (order.items ?? []).filter((i) => !i.type || i.type === 'ticket');
33
- }
34
- function ticketCount(order) {
35
- const fromItems = ticketItems(order).reduce((sum, i) => sum + (Number(i.quantity) || 0), 0);
36
- return fromItems || order.tickets?.length || 0;
37
- }
38
- /** Event name as the order view exposes it: the first ticket line's name. */
39
- function orderEventName(order) {
40
- return order.eventName || ticketItems(order)[0]?.name || order.items?.[0]?.name || null;
41
- }
42
- function orderCustomerEmail(order, customer) {
43
- const fromOrder = order?.customerEmail || order?.customer?.email;
44
- if (fromOrder)
45
- return fromOrder;
46
- const c = customer;
47
- return c?.email || null;
48
- }
49
- function statusLabel(status) {
50
- if (!status)
51
- return '';
52
- return status.replace(/_/g, ' ').replace(/^\w/, (m) => m.toUpperCase());
53
- }
54
- function statusTone(status) {
55
- switch (status) {
56
- case 'confirmed':
57
- return 'success';
58
- case 'completed':
59
- case 'fulfilled':
60
- return 'info';
61
- case 'pending':
62
- case 'processing':
63
- return 'warning';
64
- case 'cancelled':
65
- case 'expired':
66
- case 'failed':
67
- return 'error';
68
- default:
69
- return 'muted';
70
- }
71
- }
72
- function errorMessage(err, fallback) {
73
- return err instanceof Error && err.message ? err.message : fallback;
74
- }
75
- /** True for the "not signed in" family of API errors (401/403, NO_SESSION, NOT_AUTHENTICATED). */
76
- function isAuthError(err) {
77
- const e = err;
78
- if (!e || typeof e !== 'object')
79
- return false;
80
- return e.status === 401 || e.status === 403 || e.code === 'NO_SESSION' || e.code === 'NOT_AUTHENTICATED';
81
- }
82
- /**
83
- * Find the event and start time for an occurrence id. The order view only
84
- * carries the occurrence id, so this walks the channel's listings (cached by
85
- * live-api) until one owns the occurrence. Best effort: null when not found.
86
- */
87
- async function resolveOccurrence(sdk, occurrenceId, maxEvents = 12) {
88
- const events = sdk.getEventsManager();
89
- const listing = await events.list({ limit: maxEvents });
90
- const candidates = listing.items.slice(0, maxEvents);
91
- for (const candidate of candidates) {
92
- const detail = (await events.get(candidate.id).catch(() => null));
93
- const occ = detail?.occurrences?.find((o) => o.id === occurrenceId);
94
- if (occ) {
95
- return {
96
- eventId: detail?.id ?? candidate.id,
97
- eventName: detail?.name ?? candidate.name ?? '',
98
- startsAt: occ.startsAt ?? null,
99
- };
100
- }
101
- }
102
- return null;
103
- }
104
- function orderTotals(order) {
105
- const o = order || {};
106
- const items = Array.isArray(o.items) ? o.items : [];
107
- const feeLines = items.filter((i) => i.type === 'fee');
108
- const discountLines = items.filter((i) => i.type === 'discount');
109
- const tickets = ticketItems(o);
110
- const feeFromLines = feeLines.length
111
- ? feeLines.reduce((sum, i) => sum + (minorUnits(i.subtotal) ?? 0), 0)
112
- : null;
113
- const fees = feeFromLines ?? minorUnits(o.totalFees) ?? 0;
114
- const declaredSubtotal = minorUnits(o.subtotal);
115
- const subtotal = tickets.length
116
- ? tickets.reduce((sum, i) => sum + (minorUnits(i.subtotal) ?? 0), 0)
117
- : (declaredSubtotal ?? 0) - fees;
118
- const discountFromLines = discountLines.length
119
- ? discountLines.reduce((sum, i) => sum + Math.abs(minorUnits(i.subtotal) ?? 0), 0)
120
- : null;
121
- const discount = minorUnits(o.totalDiscounts) ?? discountFromLines ?? 0;
122
- const tax = minorUnits(o.totalTax) ?? 0;
123
- const named = feeLines.some((i) => typeof i.name === 'string' && !!i.name);
124
- return {
125
- currency: o.currency || 'GBP',
126
- subtotal,
127
- fees,
128
- feeLabel: feeLines.map((i) => i.name).find((n) => !!n) || FEE_LABEL_FALLBACK,
129
- showFee: expressesFee(feeFromLines ?? minorUnits(o.totalFees), named),
130
- tax,
131
- discount,
132
- total: minorUnits(o.total) ?? subtotal + fees + tax - discount,
133
- };
134
- }
135
-
136
- export { formatPrice as a, statusLabel as b, formatDateTime as c, orderCustomerEmail as d, errorMessage as e, formatDate as f, orderTotals as g, ticketItems as h, isAuthError as i, orderEventName as o, resolveOccurrence as r, statusTone as s, ticketCount as t };
@@ -1 +0,0 @@
1
- import{r as i,c as t,h as s}from"./p-OoYV1faY.js";import{w as n}from"./p-CqhvfyKV.js";import{e}from"./p-BXpVGW51.js";import"./p-4ig0-WB6.js";const l=class{constructor(s){i(this,s),this.tlAuthChanged=t(this,"tlAuthChanged"),this.tlMagicLinkSent=t(this,"tlMagicLinkSent"),this.tlError=t(this,"tlError"),this.heading="Sign in",this.allowCodeEntry=!0,this.phase="email",this.email="",this.code="",this.busy=!1,this.error=null,this.notice=null,this.customer=null,this.auth=null,this.unsubscribes=[],this.urlCodeUsed=!1,this.handleEmailInput=i=>{this.email=i.target.value,this.error=null},this.handleCodeInput=i=>{this.code=i.target.value,this.error=null},this.handleEmailSubmit=i=>{i.preventDefault(),this.requestLink()},this.handleCodeSubmit=i=>{i.preventDefault(),this.exchange(this.code.trim())}}async componentWillLoad(){const i=await n().catch((()=>null));if(!i)return void(this.error="Sign-in is not available right now");if(this.auth=i.getAuthManager?.()??null,!this.auth)return void(this.error="Sign-in is not available with this SDK");this.syncFromSDK(),this.unsubscribes.push(i.on("auth:changed",(()=>this.syncFromSDK()))),this.unsubscribes.push(i.on("session:changed",(()=>this.syncFromSDK())));const t=this.codeFromUrl();t&&!this.auth.authenticated&&await this.exchange(t)}disconnectedCallback(){this.unsubscribes.forEach((i=>i())),this.unsubscribes=[]}codeFromUrl(){if("undefined"==typeof window||!window.location?.search||this.urlCodeUsed)return null;const i=new URLSearchParams(window.location.search).get("code");return i&&(this.urlCodeUsed=!0),i}syncFromSDK(){this.auth&&(this.auth.authenticated?(this.customer=this.auth.customer??null,this.phase="signed-in"):"signed-in"===this.phase&&(this.customer=null,this.phase="email"))}async requestLink(i=!1){const t=this.email.trim();if(t){if(this.auth){this.busy=!0,this.error=null,this.notice=null;try{await this.auth.requestMagicLink({email:t}),this.phase="sent",this.notice=i?"Sent again. Check your inbox.":null,this.tlMagicLinkSent.emit({email:t})}catch(i){console.error("[tl-login] Magic-link request failed:",i),this.error=e(i,"Could not send your sign-in link"),this.tlError.emit({message:this.error})}finally{this.busy=!1}}}else this.error="Enter your email address"}async exchange(i){if(!i)return void(this.error="Enter the code from your email");if(!this.auth)return;const t=this.phase;this.busy=!0,this.error=null,this.phase="verifying";try{const t=await this.auth.exchange(i);this.customer=this.auth.customer??t?.customer??null,this.phase="signed-in",this.code="",this.tlAuthChanged.emit({customer:this.customer,authenticated:!0})}catch(i){console.error("[tl-login] Code exchange failed:",i),this.phase="verifying"===t||"email"===t?"email":"code",this.error="That code did not work. Check it and try again, or request a new link.",this.tlError.emit({message:this.error})}finally{this.busy=!1}}async signOut(){if(this.auth){this.busy=!0;try{await this.auth.logout()}catch(i){console.error("[tl-login] Sign out failed:",i)}finally{this.busy=!1,this.customer=null,this.phase="email",this.tlAuthChanged.emit({customer:null,authenticated:!1})}}}renderError(){return this.error?s("p",{class:"error",role:"alert"},this.error):null}render(){return"signed-in"===this.phase?s("div",{class:"login signed-in"},s("p",{class:"signed-in-text"},"Signed in as ",s("strong",null,this.customer?.email??"your account")),s("button",{type:"button",class:"secondary",disabled:this.busy,onClick:()=>this.signOut()},"Sign out")):"verifying"===this.phase?s("div",{class:"login"},s("h2",{class:"title"},"Signing you in…"),s("p",{class:"lead"},"Just a moment.")):"sent"===this.phase?s("div",{class:"login"},s("h2",{class:"title"},"Check your inbox"),s("p",{class:"lead"},"We've sent a sign-in link to ",s("strong",null,this.email),". Open it on this device to continue."),this.notice&&s("p",{class:"notice"},this.notice),this.renderError(),s("div",{class:"actions"},s("button",{type:"button",class:"secondary",disabled:this.busy,onClick:()=>this.requestLink(!0)},this.busy?"Sending…":"Resend link"),this.allowCodeEntry&&s("button",{type:"button",class:"link",onClick:()=>this.phase="code"},"Enter the code instead"),s("button",{type:"button",class:"link",onClick:()=>this.phase="email"},"Use a different email"))):"code"===this.phase?s("form",{class:"login",onSubmit:this.handleCodeSubmit},s("h2",{class:"title"},"Enter your code"),s("p",{class:"lead"},"Type the 6-digit code from the email we sent to ",s("strong",null,this.email),"."),s("label",{class:"label",htmlFor:"tl-login-code"},"Code"),s("input",{id:"tl-login-code",class:"input code-input",type:"text",inputmode:"numeric",autocomplete:"one-time-code",placeholder:"123456",value:this.code,disabled:this.busy,onInput:this.handleCodeInput}),this.renderError(),s("button",{type:"submit",class:"primary",disabled:this.busy},this.busy?"Checking…":"Sign in"),s("div",{class:"actions"},s("button",{type:"button",class:"link",onClick:()=>this.requestLink(!0)},"Resend link"),s("button",{type:"button",class:"link",onClick:()=>this.phase="sent"},"Back"))):s("form",{class:"login",onSubmit:this.handleEmailSubmit},s("h2",{class:"title"},this.heading),s("p",{class:"lead"},"Enter your email and we'll send you a secure sign-in link. No password needed."),s("label",{class:"label",htmlFor:"tl-login-email"},"Email address"),s("input",{id:"tl-login-email",class:"input",type:"email",autocomplete:"email",placeholder:"you@example.com",required:!0,value:this.email,disabled:this.busy||!this.auth,onInput:this.handleEmailInput}),this.renderError(),s("button",{type:"submit",class:"primary",disabled:this.busy||!this.auth},this.busy?"Sending link…":"Email me a sign-in link"))}};l.style=":host{display:block;font-family:var(--tl-font-family);color:var(--tl-foreground)}.login{display:flex;flex-direction:column;gap:var(--tl-spacing-3);padding:var(--tl-spacing-6);background:var(--tl-background);border:1px solid var(--tl-border);border-radius:var(--tl-radius-lg)}.title{margin:0;font-size:1.25rem;font-weight:600}.lead{margin:0;font-size:0.875rem;color:var(--tl-muted-foreground)}.label{font-size:0.875rem;font-weight:500}.input{padding:var(--tl-spacing-3);font:inherit;font-size:0.9375rem;color:var(--tl-foreground);background:var(--tl-background);border:1px solid var(--tl-border);border-radius:var(--tl-radius-md)}.input:focus{outline:2px solid var(--tl-primary);outline-offset:1px}.code-input{font-family:var(--tl-font-family-mono);font-size:1.25rem;letter-spacing:0.25em;text-align:center}.primary,.secondary{padding:var(--tl-spacing-3) var(--tl-spacing-4);font:inherit;font-size:0.9375rem;font-weight:600;border-radius:var(--tl-radius-md);cursor:pointer}.primary{color:var(--tl-primary-foreground);background:var(--tl-primary);border:none}.primary:hover:not(:disabled){opacity:0.9}.secondary{color:var(--tl-foreground);background:var(--tl-secondary);border:1px solid var(--tl-border)}.primary:disabled,.secondary:disabled,.input:disabled{opacity:0.6;cursor:not-allowed}.link{padding:0;font:inherit;font-size:0.875rem;color:var(--tl-primary);background:none;border:none;cursor:pointer}.link:hover{text-decoration:underline}.actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--tl-spacing-4)}.error{margin:0;font-size:0.8125rem;color:var(--tl-error)}.notice{margin:0;font-size:0.8125rem;color:var(--tl-success)}.signed-in{flex-direction:row;align-items:center;justify-content:space-between;flex-wrap:wrap}.signed-in-text{margin:0;font-size:0.875rem;overflow-wrap:anywhere}";export{l as tl_login}
@@ -1 +0,0 @@
1
- import{r as t,c as r,h as s}from"./p-OoYV1faY.js";import{o as e,w as i}from"./p-CqhvfyKV.js";import{e as a,r as o,o as l,c as n,d,g as c,a as h,h as u}from"./p-BXpVGW51.js";import"./p-4ig0-WB6.js";const p=class{constructor(s){t(this,s),this.tlOrderLoaded=r(this,"tlOrderLoaded"),this.tlError=r(this,"tlError"),this.showTickets=!0,this.columns=2,this.order=null,this.resolvedOrderId=null,this.loading=!1,this.error=null,this.paymentFailed=!1,this.occurrence=null,this.signedInCustomer=null}watchOrderId(t){this.resolvedOrderId=t||this.orderIdFromUrl(),this.load()}async componentWillLoad(){const t=this.urlParams(),r=t?.get("redirect_status");this.paymentFailed=!!r&&"succeeded"!==r,this.resolvedOrderId=this.orderId||this.orderIdFromUrl(),this.resolvedOrderId&&!this.paymentFailed&&await this.load()}componentDidLoad(){this.unsubscribe=e("checkout:completed",(t=>{const r=t;r?.id&&(this.paymentFailed=!1,this.order=r,this.resolvedOrderId=r.id,this.tlOrderLoaded.emit({order:r}),this.load())}))}disconnectedCallback(){this.unsubscribe?.()}urlParams(){return"undefined"!=typeof window&&window.location?.search?new URLSearchParams(window.location.search):null}orderIdFromUrl(){return this.urlParams()?.get("orderId")||null}async load(){const t=this.resolvedOrderId;if(t)try{this.loading=!this.order,this.error=null;const r=await i(),s=r.getOrdersManager?.();if(!s)throw new Error("Orders are not available");const e=await s.get(t);if(!e)throw new Error("Order not found");this.order=e,this.signedInCustomer=r.getAuthManager?.()?.customer??null,this.tlOrderLoaded.emit({order:e}),this.resolveEvent(r,e)}catch(t){console.error("[tl-order-confirmation] Failed to load order:",t),this.error=a(t,"Failed to load your order"),this.tlError.emit({message:this.error})}finally{this.loading=!1}}async resolveEvent(t,r){const s=r.tickets?.find((t=>t.eventOccurrenceId))?.eventOccurrenceId;if(s)try{const e=await o(t,s);e&&this.order?.id===r.id&&(this.occurrence=e)}catch{}}renderRows(t){const r=this.customerEmail||d(t,this.signedInCustomer),e=this.occurrence?.eventName||l(t),i=this.occurrence?.startsAt;return s("dl",{class:"rows"},s("div",{class:"row"},s("dt",null,"Order reference"),s("dd",{class:"reference"},t.orderNumber)),r&&s("div",{class:"row"},s("dt",null,"Email"),s("dd",null,r)),e&&s("div",{class:"row"},s("dt",null,"Event"),s("dd",null,e)),i&&s("div",{class:"row"},s("dt",null,"Date"),s("dd",null,n(i))))}renderTotals(t){const r=c(t),e=r.currency;return s("div",{class:"totals"},s("div",{class:"total-row"},s("span",null,"Subtotal"),s("span",null,h(r.subtotal,e))),r.showFee&&s("div",{class:"total-row fee"},s("span",null,r.feeLabel),s("span",null,h(r.fees,e))),r.tax>0&&s("div",{class:"total-row"},s("span",null,"Tax"),s("span",null,h(r.tax,e))),r.discount>0&&s("div",{class:"total-row discount"},s("span",null,"Discount"),s("span",null,"-",h(r.discount,e))),s("div",{class:"total-row total"},s("span",null,"Total"),s("span",null,h(r.total,e))))}render(){if(this.paymentFailed)return s("div",{class:"confirmation"},s("h1",{class:"title"},"Payment not completed"),s("p",{class:"lead"},"Your payment wasn't completed. Your cart is still saved, so you can try again."));if(!this.resolvedOrderId&&!this.order)return null;if(this.loading)return s("div",{class:"confirmation"},s("div",{class:"skeleton-line short"}),s("div",{class:"skeleton-line long"}),s("div",{class:"skeleton-block"}));const t=this.order,r=t?.id||this.resolvedOrderId;return s("div",{class:"confirmation"},s("div",{class:"check","aria-hidden":"true"},s("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor"},s("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M5 13l4 4L19 7"}))),s("h1",{class:"title"},"You're going!"),s("p",{class:"lead"},t?.orderNumber?`Order ${t.orderNumber} is confirmed. Your tickets are on their way to your email.`:"Your order is confirmed. Your tickets are on their way to your email."),this.error&&!t&&s("p",{class:"error"},"We couldn't load your order details yet. ",this.error),t&&Array.isArray(t.items)&&s("div",{class:"summary"},this.renderRows(t),s("ul",{class:"lines"},u(t).map(((r,e)=>s("li",{class:"line",key:e},s("span",{class:"line-name"},r.quantity," x ",r.name),s("span",{class:"line-price"},h(r.subtotal,t.currency)))))),this.renderTotals(t)),this.showTickets&&t&&s("div",{class:"tickets"},s("h2",{class:"subtitle"},"Your tickets"),s("tl-order-tickets",{orderId:r,columns:this.columns}),s("p",{class:"hint"},"Show these QR codes at the door for entry. Use Download PDF on a ticket to save it.")))}static get watchers(){return{orderId:[{watchOrderId:0}]}}};p.style=":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}}";export{p as tl_order_confirmation}
@@ -1 +0,0 @@
1
- import{r as e,c as t,h as a,a as s}from"./p-OoYV1faY.js";import{h as i,g as r}from"./p-CqhvfyKV.js";const o=class{constructor(a){e(this,a),this.tlReady=t(this,"tlReady"),this.tlLoad=t(this,"tlLoad"),this.tlError=t(this,"tlError"),this.tlSeatAdded=t(this,"tlSeatAdded"),this.tlSeatRemoved=t(this,"tlSeatRemoved"),this.tlCartChange=t(this,"tlCartChange"),this.hboUrl="https://checkout.ticketlayer.com",this.apiUrl="https://api.ticketlayer.com",this.height="600px",this.width="100%",this.isReady=!1,this.isLoading=!0,this.error=null,this.seatsInCart=[],this.postToIframe=(e,t={})=>{this.iframeRef?.contentWindow&&this.iframeRef.contentWindow.postMessage({type:e,...t},"*")},this.handleMessage=e=>{if(e.data&&e.data.type)switch(e.data.type){case"seatmap:ready":this.isReady=!0,this.isLoading=!1,this.tlReady.emit(),this.postToIframe("seatmap:set-cart-seats",{seatIds:this.seatsInCart});break;case"seatmap:loaded":this.tlLoad.emit({seatMapId:e.data.seatMapId});break;case"seatmap:error":this.error=e.data.error,this.isLoading=!1,this.tlError.emit({error:e.data.error});break;case"seatmap:add-seat":this.handleAddSeat(e.data.seatId,e.data.ticketTypeId);break;case"seatmap:remove-seat":this.handleRemoveSeat(e.data.seatId)}},this.handleAddSeat=async(e,t)=>{if(!i())return console.warn("[tl-seat-map] SDK not available, cannot add seat"),void this.postToIframe("seatmap:add-seat-failed",{seatId:e,error:"SDK not available"});try{const a=r();await a.getCartManager().addSeat(e,t,this.occurrenceId),this.postToIframe("seatmap:add-seat-confirmed",{seatId:e}),this.seatsInCart=[...this.seatsInCart,e],this.tlSeatAdded.emit({seatId:e,ticketTypeId:t}),this.tlCartChange.emit({seatIds:this.seatsInCart})}catch(t){console.error("[tl-seat-map] Failed to add seat:",t),this.postToIframe("seatmap:add-seat-failed",{seatId:e,error:"Failed to add seat"})}},this.handleRemoveSeat=async e=>{if(!i())return console.warn("[tl-seat-map] SDK not available, cannot remove seat"),void this.postToIframe("seatmap:remove-seat-failed",{seatId:e,error:"SDK not available"});try{const t=r();await t.getCartManager().removeSeat(e,this.occurrenceId),this.postToIframe("seatmap:remove-seat-confirmed",{seatId:e}),this.seatsInCart=this.seatsInCart.filter((t=>t!==e)),this.tlSeatRemoved.emit({seatId:e}),this.tlCartChange.emit({seatIds:this.seatsInCart})}catch(t){console.error("[tl-seat-map] Failed to remove seat:",t),this.postToIframe("seatmap:remove-seat-failed",{seatId:e,error:"Failed to remove seat"})}}}componentWillLoad(){window.addEventListener("message",this.handleMessage)}disconnectedCallback(){window.removeEventListener("message",this.handleMessage)}watchOccurrenceId(){this.isReady=!1,this.isLoading=!0,this.error=null}getIframeSrc(){return`${this.hboUrl}/embed/seats?occurrenceId=${encodeURIComponent(this.occurrenceId)}&apiUrl=${encodeURIComponent(this.apiUrl)}`}render(){return a("div",{key:"c9beadc1792d7825ac3f99e6abc8b181e28fd1b6",class:"seat-map-container",style:{width:this.width,height:this.height}},this.isLoading&&a("div",{key:"ad792ea35f433a268c81722ce4e7d0e34780d337",class:"loading"},a("div",{key:"dd3e0657b16c2f265f59fbaa2ac39c559c2093b7",class:"spinner"}),a("p",{key:"2714e3073d0e5cddc50d530aa1cf46a7f73ba96f"},"Loading seat map...")),this.error&&a("div",{key:"3de9d73ab89a1ee963e73b4192d5c93e9667f11b",class:"error"},a("p",{key:"0fea5c828b5b212ffdb45c9c7702d241e2b79cc4",class:"error-title"},"Error"),a("p",{key:"f2c090b982bb1ee6fb7d347d8860b4ce4798a891",class:"error-message"},this.error)),a("iframe",{key:"8023219fdd467ec736f9627190690a6f73df3bb3",ref:e=>this.iframeRef=e,src:this.getIframeSrc(),class:{"seat-map-iframe":!0,hidden:this.isLoading||!!this.error},title:"Ticketlayer Seat Map",allow:"fullscreen"}))}get el(){return s(this)}static get watchers(){return{occurrenceId:[{watchOccurrenceId:0}]}}};o.style=":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}";export{o as tl_seat_map}
@@ -1 +0,0 @@
1
- import{r as t,c as e,h as r,H as s,a as i}from"./p-OoYV1faY.js";import{h as o,g as a}from"./p-CqhvfyKV.js";const l=class{constructor(r){t(this,r),this.tlComplete=e(this,"tl-complete"),this.tlError=e(this,"tl-error"),this.tlClose=e(this,"tl-close"),this.disabled=!1,this.handleClick=()=>{if(!this.disabled)return o()?void a().checkout.openModal({onComplete:t=>{this.tlComplete.emit(t)},onError:t=>{this.tlError.emit({message:t.message})},onClose:()=>{this.tlClose.emit()}}):(console.error("[tl-checkout-wrapper] SDK not initialized. Call setSDK(ticketlayer) first."),void this.tlError.emit({message:"SDK not initialized"}))},this.handleKeyDown=t=>{"Enter"!==t.key&&" "!==t.key||(t.preventDefault(),this.handleClick())}}render(){return r(s,{key:"ebaae5dd6710ffc5353ece4327012df0332fdbb7",class:{"tl-checkout-wrapper":!0,"tl-checkout-wrapper--disabled":this.disabled},onClick:this.handleClick,onKeyDown:this.handleKeyDown,role:"button",tabindex:this.disabled?-1:0,"aria-disabled":this.disabled?"true":null},r("slot",{key:"0118d9921df5af1ae2376fd4135bac487aac7ebd"}))}get el(){return i(this)}};l.style="tl-checkout-wrapper,.tl-checkout-wrapper{display:inline-block;cursor:pointer}tl-checkout-wrapper:focus,.tl-checkout-wrapper:focus{outline:2px solid var(--tl-ring);outline-offset:2px;border-radius:var(--tl-radius-sm)}tl-checkout-wrapper[disabled],tl-checkout-wrapper.tl-checkout-wrapper--disabled,.tl-checkout-wrapper--disabled{cursor:not-allowed;opacity:0.5}";export{l as tl_checkout_wrapper}
@@ -1 +0,0 @@
1
- import{r as e,c as t,h as s}from"./p-OoYV1faY.js";import{w as i}from"./p-CqhvfyKV.js";const r=class{constructor(s){e(this,s),this.tlEventClick=t(this,"tlEventClick"),this.limit=12,this.columns=3,this.events=[],this.loading=!0,this.error=null,this.handleEventClick=e=>{console.log("[tl-event-list] Event clicked:",e.detail.eventId),this.tlEventClick.emit(e.detail)}}watchFiltersChange(){this.loadEvents()}async componentWillLoad(){await this.loadEvents()}async loadEvents(){try{console.log("[tl-event-list] Loading events via SDK...",{limit:this.limit,category:this.category}),this.loading=!0,this.error=null;const e=(await i()).getEventsManager(),t=await e.list({limit:this.limit,category:this.category});console.log("[tl-event-list] Events loaded:",t.items.length),this.events=t.items}catch(e){console.error("[tl-event-list] Failed to load events:",e),this.error=e instanceof Error?e.message:"Failed to load events"}finally{this.loading=!1}}render(){const e=`event-grid cols-${Math.min(Math.max(this.columns,1),4)}`;return this.loading?s("div",{class:e},Array.from({length:6}).map(((e,t)=>s("div",{class:"skeleton-card",key:t},s("div",{class:"skeleton-image"}),s("div",{class:"skeleton-content"},s("div",{class:"skeleton-line short"}),s("div",{class:"skeleton-line medium"}),s("div",{class:"skeleton-line long"})))))):this.error?s("div",{class:"error-state"},s("p",{class:"error-title"},"Error loading events"),s("p",{class:"error-message"},this.error)):0===this.events.length?s("div",{class:"empty-state"},s("svg",{class:"empty-icon",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor"},s("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"1.5",d:"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"})),s("p",{class:"empty-title"},"No events available"),s("p",{class:"empty-message"},"Check back later for upcoming events.")):s("div",{class:e},this.events.map((e=>s("tl-event-card",{key:e.id,event:e,onTlClick:this.handleEventClick}))))}static get watchers(){return{limit:[{watchFiltersChange:0}],category:[{watchFiltersChange:0}]}}};r.style=":host{display:block;font-family:var(--tl-font-family)}.event-grid{display:grid;gap:var(--tl-spacing-6)}.cols-1{grid-template-columns:1fr}.cols-2{grid-template-columns:repeat(2, 1fr)}.cols-3{grid-template-columns:repeat(3, 1fr)}.cols-4{grid-template-columns:repeat(4, 1fr)}@media (max-width: 1024px){.cols-4,.cols-3{grid-template-columns:repeat(2, 1fr)}}@media (max-width: 640px){.cols-4,.cols-3,.cols-2{grid-template-columns:1fr}}.skeleton-card{background:var(--tl-secondary);border-radius:var(--tl-radius-lg);overflow:hidden}.skeleton-image{aspect-ratio:16 / 9;background:var(--tl-muted);animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}.skeleton-content{padding:var(--tl-spacing-4);display:flex;flex-direction:column;gap:0.5rem}.skeleton-line{height:1rem;background:var(--tl-muted);border-radius:var(--tl-radius-sm);animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}.skeleton-line.short{width:4rem;height:0.75rem}.skeleton-line.medium{width:75%}.skeleton-line.long{width:50%}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}.error-state{background:rgba(239, 68, 68, 0.1);border:1px solid var(--tl-error);border-radius:var(--tl-radius-lg);padding:var(--tl-spacing-4)}.error-title{margin:0;font-weight:500;color:var(--tl-error)}.error-message{margin:0.25rem 0 0;font-size:0.875rem;color:var(--tl-error)}.empty-state{text-align:center;padding:var(--tl-spacing-8) var(--tl-spacing-4)}.empty-icon{width:4rem;height:4rem;margin:0 auto 1rem;color:var(--tl-muted-foreground)}.empty-title{margin:0;font-size:1.25rem;font-weight:600;color:var(--tl-foreground)}.empty-message{margin:0.5rem 0 0;color:var(--tl-muted-foreground)}";export{r as tl_event_list}