@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.
- package/dist/cjs/{index-I9CoYv3k.js → index-DanPUX0s.js} +1 -1
- package/dist/cjs/index.cjs.js +2 -1
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/ticket-selector-BYVL13k_.js +3623 -0
- package/dist/cjs/ticketlayer-elements.cjs.js +2 -2
- package/dist/cjs/tl-buy-modal.cjs.entry.js +3 -3
- package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +89 -74
- package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +61 -30
- package/dist/cjs/tl-cart-badge.cjs.entry.js +74 -61
- package/dist/cjs/tl-cart-drawer.cjs.entry.js +51 -23
- package/dist/cjs/tl-cart.cjs.entry.js +71 -103
- package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +51 -29
- package/dist/cjs/tl-event-card.cjs.entry.js +43 -105
- package/dist/cjs/tl-event-list.cjs.entry.js +72 -35
- package/dist/cjs/tl-login.cjs.entry.js +74 -141
- package/dist/cjs/tl-my-orders.cjs.entry.js +52 -92
- package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +153 -921
- package/dist/cjs/tl-order-confirmation.cjs.entry.js +71 -107
- package/dist/cjs/tl-order-tickets_2.cjs.entry.js +48 -74
- package/dist/cjs/tl-promo-code.cjs.entry.js +50 -47
- package/dist/cjs/tl-seat-map.cjs.entry.js +3 -3
- package/dist/cjs/web-query-Ce73jB4k.js +16 -0
- package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +2 -2
- package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +37 -0
- package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +105 -79
- package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +73 -30
- package/dist/collection/components/tl-cart/tl-cart.js +82 -107
- package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +62 -62
- package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +56 -24
- package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +61 -29
- package/dist/collection/components/tl-event-card/event-card-adapter.js +11 -23
- package/dist/collection/components/tl-event-card/tl-event-card.js +51 -84
- package/dist/collection/components/tl-event-list/tl-event-list.js +82 -38
- package/dist/collection/components/tl-login/tl-login.js +83 -147
- package/dist/collection/components/tl-my-orders/tl-my-orders.js +58 -96
- package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +137 -516
- package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +81 -115
- package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +57 -92
- package/dist/collection/components/tl-promo-code/tl-promo-code.js +55 -50
- package/dist/collection/components/tl-seat-map/tl-seat-map.js +2 -2
- package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +34 -20
- package/dist/collection/utils/order-summary.js +15 -135
- package/dist/collection/utils/testing/deep-text.js +29 -0
- package/dist/collection/utils/testing/fixtures.js +290 -0
- package/dist/collection/utils/testing/mock-sdk.js +13 -2
- package/dist/collection/utils/web-query.js +12 -0
- package/dist/collection/utils/web-storage.js +22 -0
- package/dist/components/ticket-selector.js +1 -0
- package/dist/components/tl-buy-modal.js +1 -1
- package/dist/components/tl-buy-tickets-button.js +1 -1
- package/dist/components/tl-buy-tickets-wrapper2.js +1 -1
- package/dist/components/tl-cart-badge.js +1 -1
- package/dist/components/tl-cart-drawer.js +1 -1
- package/dist/components/tl-cart2.js +1 -1
- package/dist/components/tl-checkout-wrapper2.js +1 -1
- package/dist/components/tl-event-card2.js +1 -1
- package/dist/components/tl-event-list.js +1 -1
- package/dist/components/tl-login2.js +1 -1
- package/dist/components/tl-my-orders.js +1 -1
- package/dist/components/tl-occurrence-selector2.js +1 -1
- package/dist/components/tl-order-confirmation.js +1 -1
- package/dist/components/tl-order-tickets2.js +1 -1
- package/dist/components/tl-promo-code.js +1 -1
- package/dist/components/tl-seat-map.js +1 -1
- package/dist/components/tl-ticket-selector2.js +1 -1
- package/dist/components/web-query.js +1 -0
- package/dist/elements.docs.json +34 -34
- package/dist/esm/{index-OoYV1faY.js → index-imfxp765.js} +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/ticket-selector-BwChnHVC.js +3606 -0
- package/dist/esm/ticketlayer-elements.js +3 -3
- package/dist/esm/tl-buy-modal.entry.js +3 -3
- package/dist/esm/tl-buy-tickets-button.entry.js +90 -75
- package/dist/esm/tl-buy-tickets-wrapper.entry.js +62 -31
- package/dist/esm/tl-cart-badge.entry.js +75 -62
- package/dist/esm/tl-cart-drawer.entry.js +52 -24
- package/dist/esm/tl-cart.entry.js +72 -104
- package/dist/esm/tl-checkout-wrapper.entry.js +52 -30
- package/dist/esm/tl-event-card.entry.js +44 -106
- package/dist/esm/tl-event-list.entry.js +73 -36
- package/dist/esm/tl-login.entry.js +75 -142
- package/dist/esm/tl-my-orders.entry.js +53 -93
- package/dist/esm/tl-occurrence-selector_2.entry.js +152 -920
- package/dist/esm/tl-order-confirmation.entry.js +72 -108
- package/dist/esm/tl-order-tickets_2.entry.js +49 -75
- package/dist/esm/tl-promo-code.entry.js +51 -48
- package/dist/esm/tl-seat-map.entry.js +3 -3
- package/dist/esm/web-query-Bdbb2lk5.js +14 -0
- package/dist/ticketlayer-elements/index.esm.js +1 -1
- package/dist/ticketlayer-elements/p-45923d2c.entry.js +1 -0
- package/dist/ticketlayer-elements/p-492adc77.entry.js +1 -0
- package/dist/ticketlayer-elements/p-4cfc9e53.entry.js +1 -0
- package/dist/ticketlayer-elements/{p-ac264adc.entry.js → p-5135630e.entry.js} +1 -1
- package/dist/ticketlayer-elements/p-5bbce3b5.entry.js +1 -0
- package/dist/ticketlayer-elements/p-7d547721.entry.js +1 -0
- package/dist/ticketlayer-elements/p-7fe7b6ba.entry.js +1 -0
- package/dist/ticketlayer-elements/p-8122f0c1.entry.js +1 -0
- package/dist/ticketlayer-elements/p-98d40869.entry.js +1 -0
- package/dist/ticketlayer-elements/p-Bdbb2lk5.js +1 -0
- package/dist/ticketlayer-elements/p-BwChnHVC.js +1 -0
- package/dist/ticketlayer-elements/p-abec290a.entry.js +1 -0
- package/dist/ticketlayer-elements/p-ac79a3f6.entry.js +1 -0
- package/dist/ticketlayer-elements/p-d01f4429.entry.js +1 -0
- package/dist/ticketlayer-elements/p-d7ffaf46.entry.js +1 -0
- package/dist/ticketlayer-elements/p-e7170f3c.entry.js +1 -0
- package/dist/ticketlayer-elements/p-e787e862.entry.js +1 -0
- package/dist/ticketlayer-elements/p-f1ff931f.entry.js +1 -0
- package/dist/ticketlayer-elements/p-imfxp765.js +3 -0
- package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -1
- package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -1
- package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +40 -8
- package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +20 -1
- package/dist/types/components/tl-cart/tl-cart.d.ts +40 -42
- package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +22 -7
- package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +28 -5
- package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +18 -1
- package/dist/types/components/tl-event-card/event-card-adapter.d.ts +9 -20
- package/dist/types/components/tl-event-card/tl-event-card.d.ts +25 -28
- package/dist/types/components/tl-event-list/tl-event-list.d.ts +46 -6
- package/dist/types/components/tl-login/tl-login.d.ts +68 -22
- package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +30 -12
- package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +73 -161
- package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +48 -15
- package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +29 -31
- package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +28 -10
- package/dist/types/components/tl-ticket/tl-ticket.d.ts +2 -11
- package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +22 -4
- package/dist/types/components.d.ts +270 -62
- package/dist/types/utils/order-summary.d.ts +14 -97
- package/dist/types/utils/testing/deep-text.d.ts +13 -0
- package/dist/types/utils/testing/fixtures.d.ts +225 -0
- package/dist/types/utils/testing/mock-sdk.d.ts +14 -1
- package/dist/types/utils/web-query.d.ts +18 -0
- package/dist/types/utils/web-storage.d.ts +16 -0
- package/elements.catalogue.json +602 -50
- package/package.json +8 -6
- package/dist/cjs/money-BtmM2gt-.js +0 -118
- package/dist/cjs/order-summary-X3TlZxc2.js +0 -150
- package/dist/components/money.js +0 -1
- package/dist/components/order-summary.js +0 -1
- package/dist/esm/money-4ig0-WB6.js +0 -112
- package/dist/esm/order-summary-DiDtvfZM.js +0 -136
- package/dist/ticketlayer-elements/p-0814420f.entry.js +0 -1
- package/dist/ticketlayer-elements/p-0acabc26.entry.js +0 -1
- package/dist/ticketlayer-elements/p-109a3feb.entry.js +0 -1
- package/dist/ticketlayer-elements/p-1a4ba71c.entry.js +0 -1
- package/dist/ticketlayer-elements/p-318965c1.entry.js +0 -1
- package/dist/ticketlayer-elements/p-347c7fd1.entry.js +0 -1
- package/dist/ticketlayer-elements/p-3649c26d.entry.js +0 -1
- package/dist/ticketlayer-elements/p-40260cc9.entry.js +0 -1
- package/dist/ticketlayer-elements/p-4ig0-WB6.js +0 -1
- package/dist/ticketlayer-elements/p-55fd2f9f.entry.js +0 -1
- package/dist/ticketlayer-elements/p-62ee659b.entry.js +0 -1
- package/dist/ticketlayer-elements/p-7d4ecc2b.entry.js +0 -1
- package/dist/ticketlayer-elements/p-7f0abf95.entry.js +0 -1
- package/dist/ticketlayer-elements/p-BXpVGW51.js +0 -1
- package/dist/ticketlayer-elements/p-OoYV1faY.js +0 -3
- package/dist/ticketlayer-elements/p-a3c1b26c.entry.js +0 -1
- package/dist/ticketlayer-elements/p-aeb4c6fb.entry.js +0 -1
- package/dist/ticketlayer-elements/p-ba62541f.entry.js +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { h } from "@stencil/core";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createElement, loginCore, } from "@ticketlayer/elements-core";
|
|
3
|
+
import { getSDK, whenSDK } from "../../utils/sdk-connector";
|
|
4
|
+
import { webQuery } from "../../utils/web-query";
|
|
4
5
|
/**
|
|
5
6
|
* tl-login - magic-link sign-in. The customer enters an email, we ask the SDK
|
|
6
7
|
* to send a link (POST /sales/customer-auth/magic-link), then either the link
|
|
@@ -11,6 +12,13 @@ import { errorMessage } from "../../utils/order-summary";
|
|
|
11
12
|
* States: enter email -> check your inbox (resend, enter code) -> signed in
|
|
12
13
|
* (email + sign out).
|
|
13
14
|
*
|
|
15
|
+
* The web harness for `loginCore`: it renders and it forwards. The phases, the
|
|
16
|
+
* exchange, the auth subscriptions and every string are in
|
|
17
|
+
* `@ticketlayer/elements-core` (AGENTS.md, TKT-11). What is left here is the
|
|
18
|
+
* Stencil surface and the two genuinely web things: the client the page
|
|
19
|
+
* published on `window.Ticketlayer`, and `window.location.search`, which is
|
|
20
|
+
* where this runtime keeps the `?code=` the core asks for.
|
|
21
|
+
*
|
|
14
22
|
* Usage: <tl-login></tl-login>
|
|
15
23
|
*
|
|
16
24
|
* @event tlAuthChanged - signed in or out ({ customer, authenticated })
|
|
@@ -23,167 +31,92 @@ export class TlLogin {
|
|
|
23
31
|
this.heading = 'Sign in';
|
|
24
32
|
/** Offer a "enter the code" path alongside the emailed link (default true). */
|
|
25
33
|
this.allowCodeEntry = true;
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
this.unsubscribes = [];
|
|
35
|
-
// A magic-link code is single-use; never exchange the URL code twice.
|
|
36
|
-
this.urlCodeUsed = false;
|
|
34
|
+
this.element = createElement(loginCore, {
|
|
35
|
+
client: () => getSDK(),
|
|
36
|
+
clientReady: () => whenSDK(),
|
|
37
|
+
query: webQuery,
|
|
38
|
+
emit: (event, detail) => this.forward(event, detail),
|
|
39
|
+
});
|
|
40
|
+
/** The core's whole view. Assigning it is what makes Stencil re-render. */
|
|
41
|
+
this.view = this.element.view;
|
|
37
42
|
this.handleEmailInput = (e) => {
|
|
38
|
-
this.
|
|
39
|
-
this.error = null;
|
|
43
|
+
this.element.actions.setEmail(e.target.value);
|
|
40
44
|
};
|
|
41
45
|
this.handleCodeInput = (e) => {
|
|
42
|
-
this.
|
|
43
|
-
this.error = null;
|
|
46
|
+
this.element.actions.setCode(e.target.value);
|
|
44
47
|
};
|
|
45
48
|
this.handleEmailSubmit = (e) => {
|
|
46
49
|
e.preventDefault();
|
|
47
|
-
this.requestLink();
|
|
50
|
+
this.element.actions.requestLink();
|
|
48
51
|
};
|
|
49
52
|
this.handleCodeSubmit = (e) => {
|
|
50
53
|
e.preventDefault();
|
|
51
|
-
this.
|
|
54
|
+
this.element.actions.submitCode();
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (!sdk) {
|
|
57
|
-
this.error = 'Sign-in is not available right now';
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
this.auth = sdk.getAuthManager?.() ?? null;
|
|
61
|
-
if (!this.auth) {
|
|
62
|
-
this.error = 'Sign-in is not available with this SDK';
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
this.syncFromSDK();
|
|
66
|
-
// The session hydrates after the SDK publishes; follow both signals.
|
|
67
|
-
this.unsubscribes.push(sdk.on('auth:changed', () => this.syncFromSDK()));
|
|
68
|
-
this.unsubscribes.push(sdk.on('session:changed', () => this.syncFromSDK()));
|
|
69
|
-
const urlCode = this.codeFromUrl();
|
|
70
|
-
if (urlCode && !this.auth.authenticated) {
|
|
71
|
-
await this.exchange(urlCode);
|
|
72
|
-
}
|
|
57
|
+
syncProps() {
|
|
58
|
+
this.element.setProps({ heading: this.heading, allowCodeEntry: this.allowCodeEntry });
|
|
73
59
|
}
|
|
74
|
-
|
|
75
|
-
this.
|
|
76
|
-
this.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const code = new URLSearchParams(window.location.search).get('code');
|
|
82
|
-
if (code)
|
|
83
|
-
this.urlCodeUsed = true;
|
|
84
|
-
return code;
|
|
60
|
+
connectedCallback() {
|
|
61
|
+
this.syncProps();
|
|
62
|
+
this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
|
|
63
|
+
this.view = view;
|
|
64
|
+
}));
|
|
65
|
+
this.view = this.element.view;
|
|
66
|
+
this.teardown = this.element.mount();
|
|
85
67
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const email = this.email.trim();
|
|
100
|
-
if (!email) {
|
|
101
|
-
this.error = 'Enter your email address';
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
if (!this.auth)
|
|
105
|
-
return;
|
|
106
|
-
this.busy = true;
|
|
107
|
-
this.error = null;
|
|
108
|
-
this.notice = null;
|
|
109
|
-
try {
|
|
110
|
-
// Always reports success; account existence is never revealed.
|
|
111
|
-
await this.auth.requestMagicLink({ email });
|
|
112
|
-
this.phase = 'sent';
|
|
113
|
-
this.notice = resend ? 'Sent again. Check your inbox.' : null;
|
|
114
|
-
this.tlMagicLinkSent.emit({ email });
|
|
115
|
-
}
|
|
116
|
-
catch (err) {
|
|
117
|
-
console.error('[tl-login] Magic-link request failed:', err);
|
|
118
|
-
this.error = errorMessage(err, 'Could not send your sign-in link');
|
|
119
|
-
this.tlError.emit({ message: this.error });
|
|
120
|
-
}
|
|
121
|
-
finally {
|
|
122
|
-
this.busy = false;
|
|
123
|
-
}
|
|
68
|
+
/**
|
|
69
|
+
* Nothing is painted until this page's client is up.
|
|
70
|
+
*
|
|
71
|
+
* A rendering decision, and the harness's to make: Stencil waits on this
|
|
72
|
+
* promise before the first render, and without it the form would flash
|
|
73
|
+
* disabled for a microtask on every page. The core is already at work by
|
|
74
|
+
* now - `connectedCallback` mounted it - so this waits on the same
|
|
75
|
+
* `whenSDK()` it is waiting on rather than deciding anything of its own, and
|
|
76
|
+
* a page that never starts Live renders the core's "not available" state
|
|
77
|
+
* instead of hanging.
|
|
78
|
+
*/
|
|
79
|
+
componentWillLoad() {
|
|
80
|
+
return whenSDK().catch(() => undefined);
|
|
124
81
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.
|
|
134
|
-
this.
|
|
135
|
-
this.phase = 'verifying';
|
|
136
|
-
try {
|
|
137
|
-
const res = (await this.auth.exchange(code));
|
|
138
|
-
this.customer = this.auth.customer ?? res?.customer ?? null;
|
|
139
|
-
this.phase = 'signed-in';
|
|
140
|
-
this.code = '';
|
|
141
|
-
this.tlAuthChanged.emit({ customer: this.customer, authenticated: true });
|
|
142
|
-
}
|
|
143
|
-
catch (err) {
|
|
144
|
-
console.error('[tl-login] Code exchange failed:', err);
|
|
145
|
-
this.phase = previous === 'verifying' || previous === 'email' ? 'email' : 'code';
|
|
146
|
-
this.error = 'That code did not work. Check it and try again, or request a new link.';
|
|
147
|
-
this.tlError.emit({ message: this.error });
|
|
148
|
-
}
|
|
149
|
-
finally {
|
|
150
|
-
this.busy = false;
|
|
151
|
-
}
|
|
82
|
+
/**
|
|
83
|
+
* Off screen: run the mount's teardown, which drops both auth
|
|
84
|
+
* subscriptions. Not `destroy()`: Stencil calls this for a DOM move as well
|
|
85
|
+
* as a removal, and a destroyed core never mounts again.
|
|
86
|
+
*/
|
|
87
|
+
disconnectedCallback() {
|
|
88
|
+
this.teardown?.();
|
|
89
|
+
this.teardown = undefined;
|
|
90
|
+
this.unsubscribe?.();
|
|
91
|
+
this.unsubscribe = undefined;
|
|
152
92
|
}
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
catch (err) {
|
|
161
|
-
console.error('[tl-login] Sign out failed:', err);
|
|
162
|
-
}
|
|
163
|
-
finally {
|
|
164
|
-
this.busy = false;
|
|
165
|
-
this.customer = null;
|
|
166
|
-
this.phase = 'email';
|
|
167
|
-
this.tlAuthChanged.emit({ customer: null, authenticated: false });
|
|
168
|
-
}
|
|
93
|
+
forward(event, detail) {
|
|
94
|
+
if (event === 'tlAuthChanged')
|
|
95
|
+
this.tlAuthChanged.emit(detail);
|
|
96
|
+
else if (event === 'tlMagicLinkSent')
|
|
97
|
+
this.tlMagicLinkSent.emit(detail);
|
|
98
|
+
else if (event === 'tlError')
|
|
99
|
+
this.tlError.emit(detail);
|
|
169
100
|
}
|
|
170
101
|
renderError() {
|
|
171
|
-
|
|
102
|
+
const error = this.view.state.error;
|
|
103
|
+
return error ? (h("p", { class: "error", role: "alert" }, error)) : null;
|
|
172
104
|
}
|
|
173
105
|
render() {
|
|
174
|
-
|
|
175
|
-
|
|
106
|
+
const { status, state, strings, derived } = this.view;
|
|
107
|
+
if (status === 'signed-in') {
|
|
108
|
+
return (h("div", { class: "login signed-in" }, h("p", { class: "signed-in-text" }, strings.signedInPrefix, h("strong", null, derived.signedInEmail)), h("button", { type: "button", class: "secondary", disabled: state.busy, onClick: () => this.element.actions.signOut() }, strings.signOut)));
|
|
176
109
|
}
|
|
177
|
-
if (
|
|
178
|
-
return (h("div", { class: "login" }, h("h2", { class: "title" },
|
|
110
|
+
if (status === 'verifying') {
|
|
111
|
+
return (h("div", { class: "login" }, h("h2", { class: "title" }, strings.verifyingTitle), h("p", { class: "lead" }, strings.verifyingLead)));
|
|
179
112
|
}
|
|
180
|
-
if (
|
|
181
|
-
return (h("div", { class: "login" }, h("h2", { class: "title" },
|
|
113
|
+
if (status === 'sent') {
|
|
114
|
+
return (h("div", { class: "login" }, h("h2", { class: "title" }, strings.sentTitle), h("p", { class: "lead" }, strings.sentLeadBefore, h("strong", null, state.email), strings.sentLeadAfter), state.notice && h("p", { class: "notice" }, state.notice), this.renderError(), h("div", { class: "actions" }, h("button", { type: "button", class: "secondary", disabled: state.busy, onClick: () => this.element.actions.requestLink(true) }, derived.resendLabel), derived.showCodeEntry && (h("button", { type: "button", class: "link", onClick: () => this.element.actions.goTo('code') }, strings.enterCodeInstead)), h("button", { type: "button", class: "link", onClick: () => this.element.actions.goTo('email') }, strings.differentEmail))));
|
|
182
115
|
}
|
|
183
|
-
if (
|
|
184
|
-
return (h("form", { class: "login", onSubmit: this.handleCodeSubmit }, h("h2", { class: "title" },
|
|
116
|
+
if (status === 'code') {
|
|
117
|
+
return (h("form", { class: "login", onSubmit: this.handleCodeSubmit }, h("h2", { class: "title" }, strings.codeTitle), h("p", { class: "lead" }, strings.codeLeadBefore, h("strong", null, state.email), strings.codeLeadAfter), h("label", { class: "label", htmlFor: "tl-login-code" }, strings.codeLabel), h("input", { id: "tl-login-code", class: "input code-input", type: "text", inputmode: "numeric", autocomplete: "one-time-code", placeholder: strings.codePlaceholder, value: state.code, disabled: state.busy, onInput: this.handleCodeInput }), this.renderError(), h("button", { type: "submit", class: "primary", disabled: state.busy }, derived.codeSubmitLabel), h("div", { class: "actions" }, h("button", { type: "button", class: "link", onClick: () => this.element.actions.requestLink(true) }, strings.resendLink), h("button", { type: "button", class: "link", onClick: () => this.element.actions.goTo('sent') }, strings.back))));
|
|
185
118
|
}
|
|
186
|
-
return (h("form", { class: "login", onSubmit: this.handleEmailSubmit }, h("h2", { class: "title" }, this.heading), h("p", { class: "lead" },
|
|
119
|
+
return (h("form", { class: "login", onSubmit: this.handleEmailSubmit }, h("h2", { class: "title" }, this.heading), h("p", { class: "lead" }, strings.emailLead), h("label", { class: "label", htmlFor: "tl-login-email" }, strings.emailLabel), h("input", { id: "tl-login-email", class: "input", type: "email", autocomplete: "email", placeholder: strings.emailPlaceholder, required: true, value: state.email, disabled: derived.inputsDisabled, onInput: this.handleEmailInput }), this.renderError(), h("button", { type: "submit", class: "primary", disabled: derived.inputsDisabled }, derived.emailSubmitLabel)));
|
|
187
120
|
}
|
|
188
121
|
static get is() { return "tl-login"; }
|
|
189
122
|
static get encapsulation() { return "shadow"; }
|
|
@@ -243,13 +176,7 @@ export class TlLogin {
|
|
|
243
176
|
}
|
|
244
177
|
static get states() {
|
|
245
178
|
return {
|
|
246
|
-
"
|
|
247
|
-
"email": {},
|
|
248
|
-
"code": {},
|
|
249
|
-
"busy": {},
|
|
250
|
-
"error": {},
|
|
251
|
-
"notice": {},
|
|
252
|
-
"customer": {}
|
|
179
|
+
"view": {}
|
|
253
180
|
};
|
|
254
181
|
}
|
|
255
182
|
static get events() {
|
|
@@ -306,4 +233,13 @@ export class TlLogin {
|
|
|
306
233
|
}
|
|
307
234
|
}];
|
|
308
235
|
}
|
|
236
|
+
static get watchers() {
|
|
237
|
+
return [{
|
|
238
|
+
"propName": "heading",
|
|
239
|
+
"methodName": "syncProps"
|
|
240
|
+
}, {
|
|
241
|
+
"propName": "allowCodeEntry",
|
|
242
|
+
"methodName": "syncProps"
|
|
243
|
+
}];
|
|
244
|
+
}
|
|
309
245
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { h } from "@stencil/core";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createElement, myOrdersCore, } from "@ticketlayer/elements-core";
|
|
3
|
+
import { getSDK, whenSDK } from "../../utils/sdk-connector";
|
|
4
4
|
/**
|
|
5
5
|
* tl-my-orders - the signed-in customer's orders (GET /sales/my-orders through
|
|
6
6
|
* the SDK): event, date, ticket count, total and status per row. Clicking a
|
|
7
7
|
* row expands it to show the order's tickets (tl-order-tickets). Shows
|
|
8
8
|
* tl-login inline when nobody is signed in.
|
|
9
9
|
*
|
|
10
|
+
* The web harness for `myOrdersCore`: it renders and it forwards. Who is
|
|
11
|
+
* signed in, the two auth subscriptions that keep that current, the listing
|
|
12
|
+
* and every string are in `@ticketlayer/elements-core` (AGENTS.md, TKT-11).
|
|
13
|
+
*
|
|
10
14
|
* Usage: <tl-my-orders></tl-my-orders>
|
|
11
15
|
*
|
|
12
16
|
* @event tlOrderSelect - a row was expanded ({ orderId })
|
|
@@ -18,107 +22,60 @@ export class TlMyOrders {
|
|
|
18
22
|
this.emptyMessage = "When you buy tickets they'll show up here, ready to view and download.";
|
|
19
23
|
/** Columns for the expanded ticket grid. */
|
|
20
24
|
this.columns = 2;
|
|
21
|
-
this.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.
|
|
25
|
+
this.element = createElement(myOrdersCore, {
|
|
26
|
+
client: () => getSDK(),
|
|
27
|
+
clientReady: () => whenSDK(),
|
|
28
|
+
emit: (event, detail) => this.forward(event, detail),
|
|
29
|
+
});
|
|
30
|
+
/** The core's whole view. Assigning it is what makes Stencil re-render. */
|
|
31
|
+
this.view = this.element.view;
|
|
28
32
|
}
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
if (!this.sdk) {
|
|
32
|
-
this.loading = false;
|
|
33
|
-
this.error = 'Orders are not available right now';
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
// Older SDKs have no auth accessor: try the list and let a 401 decide.
|
|
37
|
-
const auth = this.sdk.getAuthManager?.();
|
|
38
|
-
this.authenticated = auth ? auth.authenticated : true;
|
|
39
|
-
// The session hydrates after the SDK publishes; follow both signals.
|
|
40
|
-
this.unsubscribes.push(this.sdk.on('auth:changed', () => this.syncAuth()));
|
|
41
|
-
this.unsubscribes.push(this.sdk.on('session:changed', () => this.syncAuth()));
|
|
42
|
-
if (this.authenticated) {
|
|
43
|
-
await this.load();
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
this.loading = false;
|
|
47
|
-
}
|
|
33
|
+
syncProps() {
|
|
34
|
+
this.element.setProps({ emptyMessage: this.emptyMessage, columns: this.columns });
|
|
48
35
|
}
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
36
|
+
connectedCallback() {
|
|
37
|
+
this.syncProps();
|
|
38
|
+
this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
|
|
39
|
+
this.view = view;
|
|
40
|
+
}));
|
|
41
|
+
this.view = this.element.view;
|
|
42
|
+
this.teardown = this.element.mount();
|
|
52
43
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.orders = [];
|
|
64
|
-
this.expandedId = null;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
async load() {
|
|
68
|
-
if (!this.sdk)
|
|
69
|
-
return;
|
|
70
|
-
try {
|
|
71
|
-
this.loading = true;
|
|
72
|
-
this.error = null;
|
|
73
|
-
const orders = this.sdk.getOrdersManager?.();
|
|
74
|
-
if (!orders)
|
|
75
|
-
throw new Error('Orders are not available');
|
|
76
|
-
const list = (await orders.mine());
|
|
77
|
-
this.orders = list ?? [];
|
|
78
|
-
this.authenticated = true;
|
|
79
|
-
}
|
|
80
|
-
catch (err) {
|
|
81
|
-
if (isAuthError(err)) {
|
|
82
|
-
// Not signed in: fall through to the inline login rather than an error.
|
|
83
|
-
this.authenticated = false;
|
|
84
|
-
this.orders = [];
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
console.error('[tl-my-orders] Failed to load orders:', err);
|
|
88
|
-
this.error = errorMessage(err, 'Failed to load your orders');
|
|
89
|
-
this.tlError.emit({ message: this.error });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
finally {
|
|
93
|
-
this.loading = false;
|
|
94
|
-
}
|
|
44
|
+
/**
|
|
45
|
+
* Off screen: run the mount's teardown, which drops both auth
|
|
46
|
+
* subscriptions. Not `destroy()`: Stencil calls this for a DOM move as well
|
|
47
|
+
* as a removal, and a destroyed core never mounts again.
|
|
48
|
+
*/
|
|
49
|
+
disconnectedCallback() {
|
|
50
|
+
this.teardown?.();
|
|
51
|
+
this.teardown = undefined;
|
|
52
|
+
this.unsubscribe?.();
|
|
53
|
+
this.unsubscribe = undefined;
|
|
95
54
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (
|
|
100
|
-
this.
|
|
55
|
+
forward(event, detail) {
|
|
56
|
+
if (event === 'tlOrderSelect')
|
|
57
|
+
this.tlOrderSelect.emit(detail);
|
|
58
|
+
else if (event === 'tlError')
|
|
59
|
+
this.tlError.emit(detail);
|
|
101
60
|
}
|
|
102
|
-
renderRow(
|
|
103
|
-
|
|
104
|
-
const count = ticketCount(order);
|
|
105
|
-
const tone = statusTone(order.status);
|
|
106
|
-
return (h("li", { class: { order: true, expanded }, key: order.id }, h("button", { type: "button", class: "order-row", "aria-expanded": expanded ? 'true' : 'false', onClick: () => this.toggle(order.id) }, h("span", { class: "order-main" }, h("span", { class: "order-number" }, "Order ", order.orderNumber), h("span", { class: "order-event" }, orderEventName(order) ?? 'Order'), h("span", { class: "order-meta" }, order.createdAt && h("span", null, formatDate(order.createdAt)), h("span", null, count, " ticket", count === 1 ? '' : 's'))), h("span", { class: "order-side" }, h("span", { class: "order-total" }, formatPrice(order.total, order.currency)), h("span", { class: `status status-${tone}` }, statusLabel(order.status)), h("svg", { class: "chevron", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "aria-hidden": "true" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M9 18l6-6-6-6" })))), expanded && (h("div", { class: "order-tickets" }, h("tl-order-tickets", { orderId: order.id, columns: this.columns })))));
|
|
61
|
+
renderRow(row) {
|
|
62
|
+
return (h("li", { class: { order: true, expanded: row.expanded }, key: row.id }, h("button", { type: "button", class: "order-row", "aria-expanded": row.expanded ? 'true' : 'false', onClick: () => this.element.actions.toggle(row.id) }, h("span", { class: "order-main" }, h("span", { class: "order-number" }, row.numberLabel), h("span", { class: "order-event" }, row.eventName), h("span", { class: "order-meta" }, row.dateLabel && h("span", null, row.dateLabel), h("span", null, row.countLabel))), h("span", { class: "order-side" }, h("span", { class: "order-total" }, row.totalLabel), h("span", { class: `status status-${row.statusTone}` }, row.statusLabel), h("svg", { class: "chevron", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "aria-hidden": "true" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M9 18l6-6-6-6" })))), row.expanded && (h("div", { class: "order-tickets" }, h("tl-order-tickets", { orderId: row.id, columns: this.columns })))));
|
|
107
63
|
}
|
|
108
64
|
render() {
|
|
109
|
-
|
|
65
|
+
const { status, strings, state, derived } = this.view;
|
|
66
|
+
if (status === 'loading') {
|
|
110
67
|
return (h("div", { class: "orders" }, [0, 1, 2].map((i) => (h("div", { class: "skeleton", key: i })))));
|
|
111
68
|
}
|
|
112
|
-
if (
|
|
113
|
-
return (h("div", { class: "orders" }, h("div", { class: "signin" }, h("p", { class: "signin-title" },
|
|
69
|
+
if (status === 'signed-out') {
|
|
70
|
+
return (h("div", { class: "orders" }, h("div", { class: "signin" }, h("p", { class: "signin-title" }, strings.signInTitle), h("p", { class: "signin-text" }, strings.signInText)), h("tl-login", null)));
|
|
114
71
|
}
|
|
115
|
-
if (
|
|
116
|
-
return (h("div", { class: "orders" }, h("div", { class: "state error" }, h("p", { class: "state-title" },
|
|
72
|
+
if (status === 'error') {
|
|
73
|
+
return (h("div", { class: "orders" }, h("div", { class: "state error" }, h("p", { class: "state-title" }, strings.errorTitle), h("p", { class: "state-text" }, state.error))));
|
|
117
74
|
}
|
|
118
|
-
if (
|
|
119
|
-
return (h("div", { class: "orders" }, h("div", { class: "state empty" }, h("svg", { class: "empty-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "aria-hidden": "true" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", d: "M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z" }), h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", d: "M13 5v2M13 17v2M13 11v2" })), h("p", { class: "state-title" },
|
|
75
|
+
if (status === 'empty') {
|
|
76
|
+
return (h("div", { class: "orders" }, h("div", { class: "state empty" }, h("svg", { class: "empty-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "aria-hidden": "true" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", d: "M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z" }), h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", d: "M13 5v2M13 17v2M13 11v2" })), h("p", { class: "state-title" }, strings.emptyTitle), h("p", { class: "state-text" }, this.emptyMessage))));
|
|
120
77
|
}
|
|
121
|
-
return (h("div", { class: "orders" }, h("ul", { class: "order-list" },
|
|
78
|
+
return (h("div", { class: "orders" }, h("ul", { class: "order-list" }, derived.rows.map((row) => this.renderRow(row)))));
|
|
122
79
|
}
|
|
123
80
|
static get is() { return "tl-my-orders"; }
|
|
124
81
|
static get encapsulation() { return "shadow"; }
|
|
@@ -178,11 +135,7 @@ export class TlMyOrders {
|
|
|
178
135
|
}
|
|
179
136
|
static get states() {
|
|
180
137
|
return {
|
|
181
|
-
"
|
|
182
|
-
"authenticated": {},
|
|
183
|
-
"loading": {},
|
|
184
|
-
"error": {},
|
|
185
|
-
"expandedId": {}
|
|
138
|
+
"view": {}
|
|
186
139
|
};
|
|
187
140
|
}
|
|
188
141
|
static get events() {
|
|
@@ -218,4 +171,13 @@ export class TlMyOrders {
|
|
|
218
171
|
}
|
|
219
172
|
}];
|
|
220
173
|
}
|
|
174
|
+
static get watchers() {
|
|
175
|
+
return [{
|
|
176
|
+
"propName": "emptyMessage",
|
|
177
|
+
"methodName": "syncProps"
|
|
178
|
+
}, {
|
|
179
|
+
"propName": "columns",
|
|
180
|
+
"methodName": "syncProps"
|
|
181
|
+
}];
|
|
182
|
+
}
|
|
221
183
|
}
|