@ticketlayer/elements-core 0.1.0 → 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 (62) hide show
  1. package/README.md +141 -4
  2. package/dist/cjs/cores/buy-tickets-button.js +213 -0
  3. package/dist/cjs/cores/buy-tickets-wrapper.js +106 -0
  4. package/dist/cjs/cores/cart-badge.js +150 -0
  5. package/dist/cjs/cores/cart-drawer.js +122 -0
  6. package/dist/cjs/cores/cart.js +247 -0
  7. package/dist/cjs/cores/checkout-wrapper.js +75 -0
  8. package/dist/cjs/cores/event-card.js +209 -0
  9. package/dist/cjs/cores/event-list.js +109 -0
  10. package/dist/cjs/cores/index.js +64 -1
  11. package/dist/cjs/cores/login.js +309 -0
  12. package/dist/cjs/cores/my-orders.js +221 -0
  13. package/dist/cjs/cores/occurrence-selector.js +528 -0
  14. package/dist/cjs/cores/order-confirmation.js +301 -0
  15. package/dist/cjs/cores/order-tickets.js +172 -0
  16. package/dist/cjs/cores/promo-code.js +136 -0
  17. package/dist/cjs/cores/ticket-selector.js +30 -0
  18. package/dist/cjs/define.js +76 -0
  19. package/dist/cjs/index.js +17 -1
  20. package/dist/cjs/manifest.js +5 -0
  21. package/dist/cjs/orders.js +169 -0
  22. package/dist/esm/client.d.ts +106 -0
  23. package/dist/esm/cores/buy-tickets-button.d.ts +55 -0
  24. package/dist/esm/cores/buy-tickets-button.js +210 -0
  25. package/dist/esm/cores/buy-tickets-wrapper.d.ts +27 -0
  26. package/dist/esm/cores/buy-tickets-wrapper.js +103 -0
  27. package/dist/esm/cores/cart-badge.d.ts +48 -0
  28. package/dist/esm/cores/cart-badge.js +147 -0
  29. package/dist/esm/cores/cart-drawer.d.ts +44 -0
  30. package/dist/esm/cores/cart-drawer.js +119 -0
  31. package/dist/esm/cores/cart.d.ts +105 -0
  32. package/dist/esm/cores/cart.js +244 -0
  33. package/dist/esm/cores/checkout-wrapper.d.ts +22 -0
  34. package/dist/esm/cores/checkout-wrapper.js +72 -0
  35. package/dist/esm/cores/event-card.d.ts +80 -0
  36. package/dist/esm/cores/event-card.js +205 -0
  37. package/dist/esm/cores/event-list.d.ts +35 -0
  38. package/dist/esm/cores/event-list.js +106 -0
  39. package/dist/esm/cores/index.d.ts +28 -0
  40. package/dist/esm/cores/index.js +42 -0
  41. package/dist/esm/cores/login.d.ts +125 -0
  42. package/dist/esm/cores/login.js +306 -0
  43. package/dist/esm/cores/my-orders.d.ts +63 -0
  44. package/dist/esm/cores/my-orders.js +218 -0
  45. package/dist/esm/cores/occurrence-selector.d.ts +170 -0
  46. package/dist/esm/cores/occurrence-selector.js +525 -0
  47. package/dist/esm/cores/order-confirmation.d.ts +99 -0
  48. package/dist/esm/cores/order-confirmation.js +298 -0
  49. package/dist/esm/cores/order-tickets.d.ts +46 -0
  50. package/dist/esm/cores/order-tickets.js +169 -0
  51. package/dist/esm/cores/promo-code.d.ts +56 -0
  52. package/dist/esm/cores/promo-code.js +132 -0
  53. package/dist/esm/cores/ticket-selector.js +30 -0
  54. package/dist/esm/define.d.ts +146 -0
  55. package/dist/esm/define.js +75 -0
  56. package/dist/esm/index.d.ts +5 -3
  57. package/dist/esm/index.js +2 -1
  58. package/dist/esm/manifest.d.ts +12 -0
  59. package/dist/esm/manifest.js +5 -0
  60. package/dist/esm/orders.d.ts +110 -0
  61. package/dist/esm/orders.js +154 -0
  62. package/package.json +2 -2
@@ -0,0 +1,154 @@
1
+ /**
2
+ * The order shape, and what every harness has to work out from it.
3
+ *
4
+ * The sales order view carries line items, totals and tickets but no event
5
+ * name, no occurrence date and often no customer email, so these derive what
6
+ * they can from the order and resolve the rest through the client. They lived
7
+ * in `packages/elements/src/utils/order-summary.ts`, where only the web could
8
+ * reach them; they moved here with the split of `tl-my-orders` and
9
+ * `tl-order-confirmation` (TKT-69) so the same rules answer on every runtime.
10
+ * That module re-exports them, so every existing import still resolves and
11
+ * `tl-order-tickets` and `tl-ticket`, which are not split yet, read the same
12
+ * helpers they always did.
13
+ *
14
+ * `en-GB` is written out in the date and money helpers rather than taken from
15
+ * the instance's locale. That is what the components did and this is a
16
+ * behaviour-identical move; `ctx.formatMoney` is the locale-aware formatter and
17
+ * these are deliberately not it, because an order's money arrives as a minor
18
+ * unit integer OR a decimal string and `formatMoney` takes only the first.
19
+ */
20
+ import { expressesFee, minorUnits, FEE_LABEL_FALLBACK } from './money.js';
21
+ /** Money arrives as minor-unit integers or decimal strings ("50.00"). */
22
+ export function formatPrice(value, currency = 'GBP') {
23
+ if (value === null || value === undefined || value === '')
24
+ return '';
25
+ const fmt = new Intl.NumberFormat('en-GB', { style: 'currency', currency });
26
+ if (typeof value === 'number')
27
+ return fmt.format(value / 100);
28
+ const n = Number(value);
29
+ return Number.isNaN(n) ? String(value) : fmt.format(n);
30
+ }
31
+ export function formatDateTime(iso) {
32
+ return new Date(iso).toLocaleDateString('en-GB', {
33
+ weekday: 'short',
34
+ day: 'numeric',
35
+ month: 'short',
36
+ year: 'numeric',
37
+ hour: '2-digit',
38
+ minute: '2-digit',
39
+ });
40
+ }
41
+ export function formatDate(iso) {
42
+ return new Date(iso).toLocaleDateString('en-GB', {
43
+ day: 'numeric',
44
+ month: 'short',
45
+ year: 'numeric',
46
+ });
47
+ }
48
+ /** Line items that are tickets (fees, taxes and discounts have their own type). */
49
+ export function ticketItems(order) {
50
+ return (order.items ?? []).filter((i) => !i.type || i.type === 'ticket');
51
+ }
52
+ export function ticketCount(order) {
53
+ const fromItems = ticketItems(order).reduce((sum, i) => sum + (Number(i.quantity) || 0), 0);
54
+ return fromItems || order.tickets?.length || 0;
55
+ }
56
+ /** Event name as the order view exposes it: the first ticket line's name. */
57
+ export function orderEventName(order) {
58
+ return order.eventName || ticketItems(order)[0]?.name || order.items?.[0]?.name || null;
59
+ }
60
+ export function orderCustomerEmail(order, customer) {
61
+ const fromOrder = order?.customerEmail || order?.customer?.email;
62
+ if (fromOrder)
63
+ return fromOrder;
64
+ const c = customer;
65
+ return c?.email || null;
66
+ }
67
+ export function statusLabel(status) {
68
+ if (!status)
69
+ return '';
70
+ return status.replace(/_/g, ' ').replace(/^\w/, (m) => m.toUpperCase());
71
+ }
72
+ export function statusTone(status) {
73
+ switch (status) {
74
+ case 'confirmed':
75
+ return 'success';
76
+ case 'completed':
77
+ case 'fulfilled':
78
+ return 'info';
79
+ case 'pending':
80
+ case 'processing':
81
+ return 'warning';
82
+ case 'cancelled':
83
+ case 'expired':
84
+ case 'failed':
85
+ return 'error';
86
+ default:
87
+ return 'muted';
88
+ }
89
+ }
90
+ export function errorMessage(err, fallback) {
91
+ return err instanceof Error && err.message ? err.message : fallback;
92
+ }
93
+ /**
94
+ * Find the event and start time for an occurrence id. The order view only
95
+ * carries the occurrence id, so this walks the channel's listings (cached by
96
+ * live-api) until one owns the occurrence. Best effort: null when not found,
97
+ * and null on a client whose events manager cannot list or fetch, which is an
98
+ * older connector rather than a failure.
99
+ */
100
+ export async function resolveOccurrence(client, occurrenceId, maxEvents = 12) {
101
+ const events = client.getEventsManager();
102
+ if (!events?.list || !events.get)
103
+ return null;
104
+ const listing = await events.list({ limit: maxEvents });
105
+ const items = (listing?.items ?? []);
106
+ const candidates = items.slice(0, maxEvents);
107
+ for (const candidate of candidates) {
108
+ const detail = (await events.get(candidate.id).catch(() => null));
109
+ const occ = detail?.occurrences?.find((o) => o.id === occurrenceId);
110
+ if (occ) {
111
+ return {
112
+ eventId: detail?.id ?? candidate.id,
113
+ eventName: detail?.name ?? candidate.name ?? '',
114
+ startsAt: occ.startsAt ?? null,
115
+ };
116
+ }
117
+ }
118
+ return null;
119
+ }
120
+ export function orderTotals(order) {
121
+ const o = order || {};
122
+ const items = Array.isArray(o.items) ? o.items : [];
123
+ const feeLines = items.filter((i) => i.type === 'fee');
124
+ const discountLines = items.filter((i) => i.type === 'discount');
125
+ const tickets = ticketItems(o);
126
+ const feeFromLines = feeLines.length
127
+ ? feeLines.reduce((sum, i) => sum + (minorUnits(i.subtotal) ?? 0), 0)
128
+ : null;
129
+ const fees = feeFromLines ?? minorUnits(o.totalFees) ?? 0;
130
+ const declaredSubtotal = minorUnits(o.subtotal);
131
+ const subtotal = tickets.length
132
+ ? tickets.reduce((sum, i) => sum + (minorUnits(i.subtotal) ?? 0), 0)
133
+ : (declaredSubtotal ?? 0) - fees;
134
+ const discountFromLines = discountLines.length
135
+ ? discountLines.reduce((sum, i) => sum + Math.abs(minorUnits(i.subtotal) ?? 0), 0)
136
+ : null;
137
+ const discount = minorUnits(o.totalDiscounts) ?? discountFromLines ?? 0;
138
+ const tax = minorUnits(o.totalTax) ?? 0;
139
+ const named = feeLines.some((i) => typeof i.name === 'string' && !!i.name);
140
+ return {
141
+ currency: o.currency || 'GBP',
142
+ subtotal,
143
+ fees,
144
+ feeLabel: feeLines.map((i) => i.name).find((n) => !!n) || FEE_LABEL_FALLBACK,
145
+ showFee: expressesFee(feeFromLines ?? minorUnits(o.totalFees), named),
146
+ tax,
147
+ discount,
148
+ total: minorUnits(o.total) ?? subtotal + fees + tax - discount,
149
+ };
150
+ }
151
+ /** The rows add up to the total the order reports. Asserted in the specs. */
152
+ export function totalsAgree(totals) {
153
+ return totals.subtotal + totals.fees + totals.tax - totals.discount === totals.total;
154
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticketlayer/elements-core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "The headless cores the Ticketlayer Elements render: state, actions, derived values, status, strings, tokens and events, with no runtime in them",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -44,7 +44,7 @@
44
44
  "registry": "https://registry.npmjs.org/"
45
45
  },
46
46
  "dependencies": {
47
- "@ticketlayer/theme": "^0.1.0"
47
+ "@ticketlayer/theme": "^0.2.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "typescript": "^5.3.3"