@tickean/checkout-elements 0.2.0 → 0.2.22
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/index.d.mts +78 -3
- package/dist/index.d.ts +78 -3
- package/dist/index.js +3074 -214
- package/dist/index.mjs +3073 -214
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -41,101 +41,1136 @@ function formatMoney(amount, currency = "ARS", locale = "es-AR") {
|
|
|
41
41
|
// src/appearance.ts
|
|
42
42
|
var themeVars = {
|
|
43
43
|
default: {
|
|
44
|
-
"--tickean-font-family": "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif
|
|
44
|
+
"--tickean-font-family": '"Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
|
|
45
45
|
"--tickean-bg": "#ffffff",
|
|
46
|
-
"--tickean-
|
|
47
|
-
"--tickean-
|
|
48
|
-
"--tickean-
|
|
49
|
-
"--tickean-
|
|
46
|
+
"--tickean-surface": "#f0fdf4",
|
|
47
|
+
"--tickean-fg": "#052e16",
|
|
48
|
+
"--tickean-muted": "#64748b",
|
|
49
|
+
"--tickean-border": "#dcfce7",
|
|
50
|
+
"--tickean-primary": "#16a34a",
|
|
51
|
+
"--tickean-primary-hover": "#15803d",
|
|
50
52
|
"--tickean-primary-fg": "#ffffff",
|
|
51
|
-
"--tickean-danger": "#
|
|
53
|
+
"--tickean-danger": "#dc2626",
|
|
54
|
+
"--tickean-success": "#10b981",
|
|
55
|
+
"--tickean-pass": "#7c3aed",
|
|
56
|
+
"--tickean-pass-bg": "#f5f3ff",
|
|
57
|
+
"--tickean-pass-border": "#ddd6fe",
|
|
52
58
|
"--tickean-radius": "10px",
|
|
53
|
-
"--tickean-space": "
|
|
54
|
-
"--tickean-input-bg": "#ffffff"
|
|
59
|
+
"--tickean-space": "8px",
|
|
60
|
+
"--tickean-input-bg": "#ffffff",
|
|
61
|
+
"--tickean-shadow": "0 1px 2px rgba(15, 23, 42, 0.05)",
|
|
62
|
+
"--tickean-shadow-lg": "0 8px 20px rgba(15, 23, 42, 0.07)",
|
|
63
|
+
"--tickean-focus": "rgba(22, 163, 74, 0.22)"
|
|
55
64
|
},
|
|
56
65
|
flat: {
|
|
57
|
-
"--tickean-font-family": "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif
|
|
58
|
-
"--tickean-bg": "#
|
|
66
|
+
"--tickean-font-family": '"Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
|
|
67
|
+
"--tickean-bg": "#ffffff",
|
|
68
|
+
"--tickean-surface": "#f8fafc",
|
|
59
69
|
"--tickean-fg": "#0f172a",
|
|
60
70
|
"--tickean-muted": "#64748b",
|
|
61
|
-
"--tickean-border": "#
|
|
71
|
+
"--tickean-border": "#e2e8f0",
|
|
62
72
|
"--tickean-primary": "#0f172a",
|
|
73
|
+
"--tickean-primary-hover": "#1e293b",
|
|
63
74
|
"--tickean-primary-fg": "#ffffff",
|
|
64
75
|
"--tickean-danger": "#dc2626",
|
|
65
|
-
"--tickean-
|
|
66
|
-
"--tickean-
|
|
67
|
-
"--tickean-
|
|
76
|
+
"--tickean-success": "#0f766e",
|
|
77
|
+
"--tickean-radius": "10px",
|
|
78
|
+
"--tickean-space": "14px",
|
|
79
|
+
"--tickean-input-bg": "#ffffff",
|
|
80
|
+
"--tickean-shadow": "0 1px 2px rgba(15, 23, 42, 0.06)",
|
|
81
|
+
"--tickean-shadow-lg": "0 12px 28px rgba(15, 23, 42, 0.08)",
|
|
82
|
+
"--tickean-focus": "rgba(15, 23, 42, 0.18)"
|
|
68
83
|
},
|
|
69
84
|
night: {
|
|
70
|
-
"--tickean-font-family": "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif
|
|
85
|
+
"--tickean-font-family": '"Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
|
|
71
86
|
"--tickean-bg": "#0b1220",
|
|
87
|
+
"--tickean-surface": "#111827",
|
|
72
88
|
"--tickean-fg": "#e5e7eb",
|
|
73
89
|
"--tickean-muted": "#9ca3af",
|
|
74
90
|
"--tickean-border": "#1f2937",
|
|
75
|
-
"--tickean-primary": "#
|
|
91
|
+
"--tickean-primary": "#818cf8",
|
|
92
|
+
"--tickean-primary-hover": "#a5b4fc",
|
|
76
93
|
"--tickean-primary-fg": "#0b1220",
|
|
77
94
|
"--tickean-danger": "#f87171",
|
|
78
|
-
"--tickean-
|
|
79
|
-
"--tickean-
|
|
80
|
-
"--tickean-
|
|
95
|
+
"--tickean-success": "#2dd4bf",
|
|
96
|
+
"--tickean-radius": "12px",
|
|
97
|
+
"--tickean-space": "14px",
|
|
98
|
+
"--tickean-input-bg": "#0f172a",
|
|
99
|
+
"--tickean-shadow": "0 2px 8px rgba(0, 0, 0, 0.35)",
|
|
100
|
+
"--tickean-shadow-lg": "0 18px 40px rgba(0, 0, 0, 0.45)",
|
|
101
|
+
"--tickean-focus": "rgba(129, 140, 248, 0.35)"
|
|
81
102
|
}
|
|
82
103
|
};
|
|
83
104
|
var baseStyles = `
|
|
105
|
+
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");
|
|
106
|
+
|
|
84
107
|
:host {
|
|
85
108
|
display: block;
|
|
86
|
-
|
|
87
|
-
|
|
109
|
+
width: 100%;
|
|
110
|
+
max-width: none;
|
|
111
|
+
margin: 0;
|
|
112
|
+
color: var(--tickean-fg, #052e16);
|
|
113
|
+
font-family: var(--tickean-font-family, Inter, ui-sans-serif, system-ui, sans-serif);
|
|
88
114
|
box-sizing: border-box;
|
|
115
|
+
-webkit-font-smoothing: antialiased;
|
|
116
|
+
text-rendering: optimizeLegibility;
|
|
117
|
+
font-size: 15px;
|
|
118
|
+
}
|
|
119
|
+
/* Only the top-level shell caps width; nested elements fill the wizard. */
|
|
120
|
+
:host(tickean-checkout) {
|
|
121
|
+
max-width: min(100%, 560px);
|
|
122
|
+
margin: 0 auto;
|
|
89
123
|
}
|
|
90
124
|
*, *::before, *::after { box-sizing: border-box; }
|
|
125
|
+
|
|
126
|
+
@keyframes tickean-fade-up {
|
|
127
|
+
from { opacity: 0; transform: translateY(10px); }
|
|
128
|
+
to { opacity: 1; transform: translateY(0); }
|
|
129
|
+
}
|
|
130
|
+
@keyframes tickean-shimmer {
|
|
131
|
+
0% { background-position: 100% 0; }
|
|
132
|
+
100% { background-position: -100% 0; }
|
|
133
|
+
}
|
|
134
|
+
@keyframes tickean-pulse {
|
|
135
|
+
0%, 100% { opacity: 0.55; }
|
|
136
|
+
50% { opacity: 1; }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.reveal {
|
|
140
|
+
animation: tickean-fade-up 380ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
141
|
+
}
|
|
142
|
+
.reveal-1 { animation-delay: 30ms; }
|
|
143
|
+
.reveal-2 { animation-delay: 70ms; }
|
|
144
|
+
.reveal-3 { animation-delay: 110ms; }
|
|
145
|
+
.reveal-4 { animation-delay: 150ms; }
|
|
146
|
+
.reveal-5 { animation-delay: 190ms; }
|
|
147
|
+
.reveal-6 { animation-delay: 230ms; }
|
|
148
|
+
/* After first paint, hosts can set data-animated to avoid re-animating on cart updates */
|
|
149
|
+
:host([data-animated]) .reveal {
|
|
150
|
+
animation: none !important;
|
|
151
|
+
}
|
|
152
|
+
|
|
91
153
|
.wrap {
|
|
92
154
|
background: var(--tickean-bg, #fff);
|
|
93
|
-
border: 1px solid var(--tickean-border, #
|
|
155
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
94
156
|
border-radius: var(--tickean-radius, 10px);
|
|
95
|
-
padding:
|
|
157
|
+
padding: 10px;
|
|
158
|
+
box-shadow: var(--tickean-shadow);
|
|
96
159
|
}
|
|
97
|
-
.
|
|
98
|
-
|
|
160
|
+
.card {
|
|
161
|
+
background: var(--tickean-bg, #fff);
|
|
162
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
163
|
+
border-radius: calc(var(--tickean-radius, 10px) - 2px);
|
|
164
|
+
padding: var(--tickean-space, 8px);
|
|
165
|
+
box-shadow: var(--tickean-shadow);
|
|
166
|
+
transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
|
|
167
|
+
}
|
|
168
|
+
.card:hover {
|
|
169
|
+
border-color: color-mix(in srgb, var(--tickean-primary, #635bff) 35%, var(--tickean-border, #e3e8ee));
|
|
170
|
+
box-shadow: var(--tickean-shadow-lg);
|
|
171
|
+
}
|
|
172
|
+
.muted { color: var(--tickean-muted, #697386); font-size: 0.82rem; line-height: 1.35; }
|
|
173
|
+
.danger { color: var(--tickean-danger, #df1b41); font-size: 0.82rem; }
|
|
174
|
+
.success { color: var(--tickean-success, #0d9488); font-size: 0.82rem; }
|
|
99
175
|
.row { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
|
|
100
|
-
.stack { display: grid; gap:
|
|
176
|
+
.stack { display: grid; gap: 8px; }
|
|
177
|
+
|
|
178
|
+
.section-title {
|
|
179
|
+
margin: 0;
|
|
180
|
+
font-size: 0.7rem;
|
|
181
|
+
font-weight: 600;
|
|
182
|
+
letter-spacing: 0.04em;
|
|
183
|
+
text-transform: uppercase;
|
|
184
|
+
color: var(--tickean-muted, #697386);
|
|
185
|
+
}
|
|
186
|
+
|
|
101
187
|
.ticket {
|
|
102
|
-
border: 1px solid var(--tickean-border, #
|
|
103
|
-
border-radius: var(--tickean-radius, 10px);
|
|
104
|
-
padding:
|
|
188
|
+
border: 1px solid var(--tickean-border, #dcfce7);
|
|
189
|
+
border-radius: calc(var(--tickean-radius, 10px) - 2px);
|
|
190
|
+
padding: 8px 10px;
|
|
191
|
+
background: var(--tickean-bg, #fff);
|
|
192
|
+
box-shadow: none;
|
|
193
|
+
transition: border-color 160ms ease, box-shadow 160ms ease;
|
|
194
|
+
}
|
|
195
|
+
.ticket[data-selected="true"] {
|
|
196
|
+
border-color: var(--tickean-primary, #16a34a);
|
|
197
|
+
box-shadow: 0 0 0 3px var(--tickean-focus);
|
|
198
|
+
}
|
|
199
|
+
.ticket.pass {
|
|
200
|
+
border-color: var(--tickean-pass-border, #ddd6fe);
|
|
201
|
+
background: var(--tickean-pass-bg, #f5f3ff);
|
|
202
|
+
}
|
|
203
|
+
.ticket.pass[data-selected="true"] {
|
|
204
|
+
border-color: var(--tickean-pass, #7c3aed);
|
|
205
|
+
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.18);
|
|
206
|
+
}
|
|
207
|
+
.pass-badge {
|
|
208
|
+
display: inline-flex;
|
|
209
|
+
align-items: center;
|
|
210
|
+
padding: 2px 8px;
|
|
211
|
+
border-radius: 999px;
|
|
212
|
+
background: color-mix(in srgb, var(--tickean-pass, #7c3aed) 14%, transparent);
|
|
213
|
+
color: var(--tickean-pass, #7c3aed);
|
|
214
|
+
font-size: 0.7rem;
|
|
215
|
+
font-weight: 700;
|
|
216
|
+
letter-spacing: 0.02em;
|
|
217
|
+
text-transform: uppercase;
|
|
218
|
+
}
|
|
219
|
+
.promo-badge {
|
|
220
|
+
display: inline-flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
padding: 2px 7px;
|
|
223
|
+
border-radius: 999px;
|
|
224
|
+
background: color-mix(in srgb, var(--tickean-primary, #16a34a) 14%, transparent);
|
|
225
|
+
color: var(--tickean-primary, #16a34a);
|
|
226
|
+
font-size: 0.68rem;
|
|
227
|
+
font-weight: 700;
|
|
228
|
+
}
|
|
229
|
+
.promo-legend {
|
|
230
|
+
margin-top: 3px;
|
|
231
|
+
font-size: 0.72rem;
|
|
232
|
+
color: var(--tickean-primary, #16a34a);
|
|
233
|
+
font-weight: 550;
|
|
234
|
+
}
|
|
235
|
+
.stock-badge {
|
|
236
|
+
display: inline-flex;
|
|
237
|
+
align-items: center;
|
|
238
|
+
padding: 2px 7px;
|
|
239
|
+
border-radius: 999px;
|
|
240
|
+
font-size: 0.68rem;
|
|
241
|
+
font-weight: 700;
|
|
242
|
+
}
|
|
243
|
+
.stock-badge.low-stock {
|
|
244
|
+
background: #fffbeb;
|
|
245
|
+
color: #b45309;
|
|
246
|
+
border: 1px solid #fde68a;
|
|
247
|
+
}
|
|
248
|
+
.stock-badge.sold-out {
|
|
249
|
+
background: #fef2f2;
|
|
250
|
+
color: #b91c1c;
|
|
251
|
+
border: 1px solid #fecaca;
|
|
252
|
+
}
|
|
253
|
+
.stock-badge.coming-soon {
|
|
254
|
+
background: #f1f5f9;
|
|
255
|
+
color: #475569;
|
|
256
|
+
border: 1px solid #e2e8f0;
|
|
257
|
+
}
|
|
258
|
+
.ticket.sold-out,
|
|
259
|
+
.ticket.coming-soon {
|
|
260
|
+
opacity: 0.72;
|
|
261
|
+
background: var(--tickean-surface, #f8fafc);
|
|
262
|
+
}
|
|
263
|
+
.pay-methods {
|
|
264
|
+
display: grid;
|
|
265
|
+
gap: 8px;
|
|
266
|
+
}
|
|
267
|
+
.pay-method {
|
|
268
|
+
display: flex;
|
|
269
|
+
align-items: flex-start;
|
|
270
|
+
gap: 10px;
|
|
271
|
+
width: 100%;
|
|
272
|
+
text-align: left;
|
|
273
|
+
padding: 10px 12px;
|
|
274
|
+
border-radius: 10px;
|
|
275
|
+
border: 1.5px solid var(--tickean-border, #dcfce7);
|
|
276
|
+
background: var(--tickean-bg, #fff);
|
|
277
|
+
color: inherit;
|
|
278
|
+
box-shadow: none;
|
|
279
|
+
font-weight: 500;
|
|
280
|
+
}
|
|
281
|
+
.pay-method:hover:not(:disabled) {
|
|
282
|
+
transform: none;
|
|
283
|
+
background: var(--tickean-surface, #f0fdf4);
|
|
284
|
+
}
|
|
285
|
+
.pay-method:disabled {
|
|
286
|
+
opacity: 0.72;
|
|
287
|
+
cursor: not-allowed;
|
|
288
|
+
}
|
|
289
|
+
.pay-methods[data-locked="true"] {
|
|
290
|
+
pointer-events: none;
|
|
291
|
+
opacity: 0.85;
|
|
292
|
+
}
|
|
293
|
+
.pay-method[data-selected="true"] {
|
|
294
|
+
border-color: var(--tickean-primary, #16a34a);
|
|
295
|
+
box-shadow: 0 0 0 1px var(--tickean-primary, #16a34a);
|
|
296
|
+
}
|
|
297
|
+
.pay-method-radio {
|
|
298
|
+
width: 16px;
|
|
299
|
+
height: 16px;
|
|
300
|
+
margin-top: 2px;
|
|
301
|
+
border-radius: 999px;
|
|
302
|
+
border: 2px solid var(--tickean-border, #cbd5e1);
|
|
303
|
+
flex-shrink: 0;
|
|
304
|
+
}
|
|
305
|
+
.pay-method[data-selected="true"] .pay-method-radio {
|
|
306
|
+
border-color: var(--tickean-primary, #16a34a);
|
|
307
|
+
box-shadow: inset 0 0 0 3px var(--tickean-primary, #16a34a);
|
|
308
|
+
}
|
|
309
|
+
.pay-method-copy {
|
|
310
|
+
display: grid;
|
|
311
|
+
gap: 2px;
|
|
312
|
+
min-width: 0;
|
|
313
|
+
}
|
|
314
|
+
.pay-method-copy strong {
|
|
315
|
+
font-size: 0.88rem;
|
|
316
|
+
}
|
|
317
|
+
.pay-method-copy .muted {
|
|
318
|
+
font-size: 0.72rem;
|
|
319
|
+
line-height: 1.3;
|
|
320
|
+
}
|
|
321
|
+
.transfer-box {
|
|
322
|
+
padding: 12px;
|
|
323
|
+
border-radius: 12px;
|
|
324
|
+
border: 1px solid var(--tickean-border, #dcfce7);
|
|
325
|
+
background: var(--tickean-surface, #f0fdf4);
|
|
326
|
+
}
|
|
327
|
+
.transfer-box code {
|
|
328
|
+
font-size: 0.9rem;
|
|
329
|
+
font-weight: 700;
|
|
330
|
+
word-break: break-all;
|
|
331
|
+
color: var(--tickean-fg);
|
|
332
|
+
}
|
|
333
|
+
.transfer-amount {
|
|
334
|
+
display: flex;
|
|
335
|
+
flex-direction: column;
|
|
336
|
+
gap: 2px;
|
|
337
|
+
padding: 12px 12px 10px;
|
|
338
|
+
border-radius: 10px;
|
|
339
|
+
background: color-mix(in srgb, var(--tickean-brand, #16a34a) 10%, #fff);
|
|
340
|
+
border: 1px solid color-mix(in srgb, var(--tickean-brand, #16a34a) 28%, transparent);
|
|
341
|
+
text-align: center;
|
|
342
|
+
}
|
|
343
|
+
.transfer-amount-label {
|
|
344
|
+
font-size: 0.75rem;
|
|
345
|
+
font-weight: 600;
|
|
346
|
+
letter-spacing: 0.02em;
|
|
347
|
+
text-transform: uppercase;
|
|
348
|
+
color: var(--tickean-muted);
|
|
349
|
+
}
|
|
350
|
+
.transfer-amount-value {
|
|
351
|
+
font-size: 1.7rem;
|
|
352
|
+
line-height: 1.15;
|
|
353
|
+
font-weight: 800;
|
|
354
|
+
letter-spacing: -0.02em;
|
|
355
|
+
color: var(--tickean-brand, #16a34a);
|
|
356
|
+
}
|
|
357
|
+
.transfer-waiting {
|
|
358
|
+
display: flex;
|
|
359
|
+
align-items: center;
|
|
360
|
+
gap: 8px;
|
|
361
|
+
font-size: 0.84rem;
|
|
362
|
+
font-weight: 600;
|
|
363
|
+
color: var(--tickean-fg);
|
|
364
|
+
}
|
|
365
|
+
.transfer-waiting-dot {
|
|
366
|
+
width: 8px;
|
|
367
|
+
height: 8px;
|
|
368
|
+
border-radius: 999px;
|
|
369
|
+
background: var(--tickean-brand, #16a34a);
|
|
370
|
+
animation: tickean-pulse 1.2s ease-in-out infinite;
|
|
371
|
+
}
|
|
372
|
+
.copy-row {
|
|
373
|
+
display: flex;
|
|
374
|
+
align-items: center;
|
|
375
|
+
justify-content: space-between;
|
|
376
|
+
gap: 10px;
|
|
377
|
+
padding: 8px 0;
|
|
378
|
+
border-top: 1px solid color-mix(in srgb, var(--tickean-border) 80%, transparent);
|
|
379
|
+
}
|
|
380
|
+
.copy-row:first-of-type {
|
|
381
|
+
border-top: none;
|
|
382
|
+
padding-top: 0;
|
|
383
|
+
}
|
|
384
|
+
.copy-row-text {
|
|
385
|
+
display: flex;
|
|
386
|
+
flex-direction: column;
|
|
387
|
+
gap: 2px;
|
|
388
|
+
min-width: 0;
|
|
389
|
+
}
|
|
390
|
+
.copy-row-text .muted {
|
|
391
|
+
font-size: 0.72rem;
|
|
392
|
+
}
|
|
393
|
+
.copy-btn {
|
|
394
|
+
flex: 0 0 auto;
|
|
395
|
+
display: inline-flex;
|
|
396
|
+
align-items: center;
|
|
397
|
+
gap: 5px;
|
|
398
|
+
width: auto;
|
|
399
|
+
min-height: 34px;
|
|
400
|
+
padding: 6px 10px;
|
|
401
|
+
border-radius: 8px;
|
|
402
|
+
border: 1px solid var(--tickean-border);
|
|
403
|
+
background: #fff;
|
|
404
|
+
color: var(--tickean-fg);
|
|
405
|
+
font-size: 0.75rem;
|
|
406
|
+
font-weight: 650;
|
|
407
|
+
cursor: pointer;
|
|
408
|
+
}
|
|
409
|
+
.copy-btn:hover {
|
|
410
|
+
border-color: color-mix(in srgb, var(--tickean-brand, #16a34a) 45%, var(--tickean-border));
|
|
411
|
+
}
|
|
412
|
+
.pay-breakdown {
|
|
413
|
+
padding: 10px 12px;
|
|
414
|
+
border-radius: 10px;
|
|
415
|
+
border: 1px solid var(--tickean-border);
|
|
416
|
+
background: var(--tickean-bg, #fff);
|
|
417
|
+
}
|
|
418
|
+
.pay-breakdown-title {
|
|
419
|
+
font-size: 0.82rem;
|
|
420
|
+
}
|
|
421
|
+
.pay-breakdown-total {
|
|
422
|
+
margin-top: 2px;
|
|
423
|
+
padding-top: 8px;
|
|
424
|
+
border-top: 1px solid var(--tickean-border);
|
|
425
|
+
font-size: 0.92rem;
|
|
426
|
+
}
|
|
427
|
+
.pay-method-locked {
|
|
428
|
+
align-items: center;
|
|
429
|
+
justify-content: space-between;
|
|
430
|
+
gap: 10px;
|
|
431
|
+
}
|
|
432
|
+
.ghost-btn {
|
|
433
|
+
width: auto;
|
|
434
|
+
min-height: 34px;
|
|
435
|
+
padding: 6px 10px;
|
|
436
|
+
border-radius: 8px;
|
|
437
|
+
border: 1px solid var(--tickean-border);
|
|
438
|
+
background: transparent;
|
|
439
|
+
color: var(--tickean-fg);
|
|
440
|
+
font-size: 0.78rem;
|
|
441
|
+
font-weight: 650;
|
|
442
|
+
cursor: pointer;
|
|
443
|
+
}
|
|
444
|
+
.ghost-btn:hover {
|
|
445
|
+
background: color-mix(in srgb, var(--tickean-brand, #16a34a) 8%, transparent);
|
|
446
|
+
}
|
|
447
|
+
.terms-row {
|
|
448
|
+
display: flex;
|
|
449
|
+
align-items: flex-start;
|
|
450
|
+
gap: 8px;
|
|
451
|
+
font-size: 0.8rem;
|
|
452
|
+
line-height: 1.35;
|
|
453
|
+
}
|
|
454
|
+
.terms-row input {
|
|
455
|
+
width: auto;
|
|
456
|
+
margin-top: 2px;
|
|
457
|
+
}
|
|
458
|
+
.group-title {
|
|
459
|
+
margin: 4px 0 0;
|
|
460
|
+
font-size: 0.8rem;
|
|
461
|
+
font-weight: 650;
|
|
462
|
+
color: var(--tickean-fg);
|
|
463
|
+
}
|
|
464
|
+
.discount-inline {
|
|
465
|
+
margin-top: 2px;
|
|
466
|
+
width: 100%;
|
|
467
|
+
}
|
|
468
|
+
.discount-inline details {
|
|
469
|
+
width: 100%;
|
|
470
|
+
border: 1px solid var(--tickean-border);
|
|
471
|
+
border-radius: 10px;
|
|
472
|
+
padding: 8px 10px;
|
|
473
|
+
background: var(--tickean-bg);
|
|
474
|
+
}
|
|
475
|
+
.discount-inline summary {
|
|
476
|
+
cursor: pointer;
|
|
477
|
+
font-size: 0.82rem;
|
|
478
|
+
font-weight: 600;
|
|
479
|
+
color: var(--tickean-muted);
|
|
480
|
+
list-style: none;
|
|
481
|
+
}
|
|
482
|
+
.discount-inline summary::-webkit-details-marker { display: none; }
|
|
483
|
+
.promo-unlock {
|
|
484
|
+
width: 100%;
|
|
485
|
+
border: 1px solid var(--tickean-border);
|
|
486
|
+
border-radius: 12px;
|
|
487
|
+
padding: 12px;
|
|
488
|
+
background: color-mix(in srgb, var(--tickean-muted, #697386) 6%, var(--tickean-bg, #fff));
|
|
489
|
+
display: grid;
|
|
490
|
+
gap: 10px;
|
|
491
|
+
}
|
|
492
|
+
.promo-unlock-copy { display: grid; gap: 2px; }
|
|
493
|
+
.promo-unlock-title {
|
|
494
|
+
margin: 0;
|
|
495
|
+
font-size: 0.9rem;
|
|
496
|
+
font-weight: 650;
|
|
497
|
+
color: var(--tickean-fg);
|
|
498
|
+
}
|
|
499
|
+
.promo-unlock-hint {
|
|
500
|
+
margin: 0;
|
|
501
|
+
font-size: 0.78rem;
|
|
502
|
+
line-height: 1.35;
|
|
503
|
+
}
|
|
504
|
+
.promo-unlock-row {
|
|
505
|
+
display: flex;
|
|
506
|
+
gap: 8px;
|
|
507
|
+
align-items: stretch;
|
|
508
|
+
}
|
|
509
|
+
.promo-unlock-row input {
|
|
510
|
+
flex: 1;
|
|
511
|
+
min-width: 0;
|
|
512
|
+
text-transform: uppercase;
|
|
513
|
+
}
|
|
514
|
+
.promo-unlock-row button {
|
|
515
|
+
flex-shrink: 0;
|
|
516
|
+
}
|
|
517
|
+
.promo-unlock-msg {
|
|
518
|
+
margin: 0;
|
|
519
|
+
font-size: 0.78rem;
|
|
520
|
+
line-height: 1.35;
|
|
521
|
+
}
|
|
522
|
+
.promo-unlock-msg.success {
|
|
523
|
+
color: var(--tickean-success, #0d9488);
|
|
524
|
+
}
|
|
525
|
+
.promo-unlock-msg.danger {
|
|
526
|
+
color: var(--tickean-danger, #df1b41);
|
|
527
|
+
}
|
|
528
|
+
.sheet-backdrop {
|
|
529
|
+
position: fixed;
|
|
530
|
+
inset: 0;
|
|
531
|
+
background: rgba(15, 23, 42, 0.45);
|
|
532
|
+
display: grid;
|
|
533
|
+
place-items: end center;
|
|
534
|
+
z-index: 40;
|
|
535
|
+
padding: 0;
|
|
536
|
+
animation: tickean-fade-up 180ms ease both;
|
|
537
|
+
}
|
|
538
|
+
.sheet {
|
|
539
|
+
width: min(100%, 420px);
|
|
540
|
+
background: var(--tickean-bg, #fff);
|
|
541
|
+
border-radius: 14px 14px 0 0;
|
|
542
|
+
padding: 14px 14px calc(14px + env(safe-area-inset-bottom, 0px));
|
|
543
|
+
box-shadow: var(--tickean-shadow-lg);
|
|
544
|
+
display: grid;
|
|
545
|
+
gap: 10px;
|
|
546
|
+
animation: tickean-fade-up 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
547
|
+
}
|
|
548
|
+
.sheet h3 {
|
|
549
|
+
margin: 0;
|
|
550
|
+
font-size: 1rem;
|
|
551
|
+
letter-spacing: -0.02em;
|
|
105
552
|
}
|
|
106
|
-
|
|
553
|
+
.otp-digits {
|
|
554
|
+
display: grid;
|
|
555
|
+
grid-template-columns: repeat(6, 1fr);
|
|
556
|
+
gap: 5px;
|
|
557
|
+
}
|
|
558
|
+
.otp-digits input {
|
|
559
|
+
text-align: center;
|
|
560
|
+
font-weight: 700;
|
|
561
|
+
padding: 10px 0;
|
|
562
|
+
font-size: 16px;
|
|
563
|
+
}
|
|
564
|
+
.compact-aside .wrap {
|
|
565
|
+
padding: 8px 10px;
|
|
566
|
+
box-shadow: none;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
button, input, select {
|
|
107
570
|
font: inherit;
|
|
108
571
|
}
|
|
109
572
|
button {
|
|
110
|
-
border: 1px solid
|
|
111
|
-
background: var(--tickean-primary, #
|
|
573
|
+
border: 1px solid transparent;
|
|
574
|
+
background: var(--tickean-primary, #635bff);
|
|
112
575
|
color: var(--tickean-primary-fg, #fff);
|
|
113
|
-
border-radius:
|
|
114
|
-
padding:
|
|
576
|
+
border-radius: 999px;
|
|
577
|
+
padding: 10px 14px;
|
|
115
578
|
cursor: pointer;
|
|
579
|
+
font-weight: 600;
|
|
580
|
+
letter-spacing: 0.01em;
|
|
581
|
+
font-size: 0.92rem;
|
|
582
|
+
transition: background 150ms ease, transform 120ms ease, box-shadow 150ms ease, opacity 150ms ease;
|
|
583
|
+
box-shadow: 0 2px 8px color-mix(in srgb, var(--tickean-primary, #635bff) 22%, transparent);
|
|
584
|
+
}
|
|
585
|
+
button:hover:not(:disabled) {
|
|
586
|
+
background: var(--tickean-primary-hover, #5851ea);
|
|
587
|
+
transform: translateY(-1px);
|
|
116
588
|
}
|
|
589
|
+
button:active:not(:disabled) { transform: translateY(0); }
|
|
117
590
|
button.secondary {
|
|
118
591
|
background: transparent;
|
|
119
|
-
color: var(--tickean-fg, #
|
|
592
|
+
color: var(--tickean-fg, #0a2540);
|
|
593
|
+
border-color: var(--tickean-border, #e3e8ee);
|
|
594
|
+
box-shadow: none;
|
|
595
|
+
}
|
|
596
|
+
button.secondary:hover:not(:disabled) {
|
|
597
|
+
background: var(--tickean-surface, #f6f9fc);
|
|
598
|
+
border-color: color-mix(in srgb, var(--tickean-fg, #0a2540) 18%, var(--tickean-border, #e3e8ee));
|
|
599
|
+
}
|
|
600
|
+
button:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; transform: none; }
|
|
601
|
+
button.ghost {
|
|
602
|
+
background: transparent;
|
|
603
|
+
color: var(--tickean-muted, #697386);
|
|
604
|
+
border: none;
|
|
605
|
+
box-shadow: none;
|
|
606
|
+
padding: 8px 10px;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.field {
|
|
610
|
+
display: grid;
|
|
611
|
+
gap: 4px;
|
|
120
612
|
}
|
|
121
|
-
|
|
122
|
-
|
|
613
|
+
.field > span {
|
|
614
|
+
font-size: 0.76rem;
|
|
615
|
+
font-weight: 500;
|
|
616
|
+
color: var(--tickean-fg, #0a2540);
|
|
617
|
+
}
|
|
618
|
+
.field-hint {
|
|
619
|
+
font-size: 0.72rem;
|
|
620
|
+
color: var(--tickean-muted, #697386);
|
|
621
|
+
}
|
|
622
|
+
input, select {
|
|
123
623
|
width: 100%;
|
|
124
|
-
border: 1px solid var(--tickean-border, #
|
|
624
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
125
625
|
background: var(--tickean-input-bg, #fff);
|
|
126
|
-
color: var(--tickean-fg, #
|
|
127
|
-
border-radius:
|
|
128
|
-
padding:
|
|
626
|
+
color: var(--tickean-fg, #0a2540);
|
|
627
|
+
border-radius: 8px;
|
|
628
|
+
padding: 9px 10px;
|
|
629
|
+
transition: border-color 140ms ease, box-shadow 140ms ease;
|
|
630
|
+
outline: none;
|
|
631
|
+
font-size: 16px; /* prevent iOS zoom */
|
|
632
|
+
}
|
|
633
|
+
input:focus, select:focus {
|
|
634
|
+
border-color: var(--tickean-primary, #635bff);
|
|
635
|
+
box-shadow: 0 0 0 3px var(--tickean-focus);
|
|
636
|
+
}
|
|
637
|
+
.phone-row {
|
|
638
|
+
display: grid;
|
|
639
|
+
grid-template-columns: minmax(118px, 38%) 1fr;
|
|
640
|
+
gap: 8px;
|
|
641
|
+
}
|
|
642
|
+
.phone-country {
|
|
643
|
+
display: flex;
|
|
644
|
+
align-items: center;
|
|
645
|
+
gap: 6px;
|
|
646
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
647
|
+
border-radius: 10px;
|
|
648
|
+
background: var(--tickean-input-bg, #fff);
|
|
649
|
+
padding: 0 8px 0 10px;
|
|
650
|
+
transition: border-color 140ms ease, box-shadow 140ms ease;
|
|
651
|
+
}
|
|
652
|
+
.phone-country:focus-within {
|
|
653
|
+
border-color: var(--tickean-primary, #635bff);
|
|
654
|
+
box-shadow: 0 0 0 3px var(--tickean-focus);
|
|
655
|
+
}
|
|
656
|
+
.phone-country select {
|
|
657
|
+
border: none;
|
|
658
|
+
box-shadow: none;
|
|
659
|
+
padding: 10px 0;
|
|
660
|
+
background: transparent;
|
|
661
|
+
min-width: 0;
|
|
662
|
+
}
|
|
663
|
+
.phone-country select:focus { box-shadow: none; }
|
|
664
|
+
.qty {
|
|
665
|
+
display: inline-flex;
|
|
666
|
+
align-items: center;
|
|
667
|
+
gap: 2px;
|
|
668
|
+
background: var(--tickean-surface, #f6f9fc);
|
|
669
|
+
border-radius: 999px;
|
|
670
|
+
padding: 2px;
|
|
671
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
672
|
+
}
|
|
673
|
+
.qty button {
|
|
674
|
+
min-width: 32px;
|
|
675
|
+
height: 32px;
|
|
676
|
+
min-height: 32px;
|
|
677
|
+
padding: 0;
|
|
678
|
+
border-radius: 999px;
|
|
679
|
+
box-shadow: none;
|
|
680
|
+
background: var(--tickean-bg, #fff);
|
|
681
|
+
color: var(--tickean-fg, #0a2540);
|
|
682
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
683
|
+
}
|
|
684
|
+
.qty button:hover:not(:disabled) {
|
|
685
|
+
background: var(--tickean-surface, #f6f9fc);
|
|
686
|
+
transform: none;
|
|
687
|
+
}
|
|
688
|
+
.qty span {
|
|
689
|
+
min-width: 28px;
|
|
690
|
+
text-align: center;
|
|
691
|
+
font-weight: 600;
|
|
692
|
+
font-variant-numeric: tabular-nums;
|
|
693
|
+
}
|
|
694
|
+
.total[aria-live] {
|
|
695
|
+
font-weight: 700;
|
|
696
|
+
font-size: 1.05rem;
|
|
697
|
+
letter-spacing: -0.02em;
|
|
129
698
|
}
|
|
130
|
-
label { display: grid; gap: 4px; font-size: 0.85rem; }
|
|
131
|
-
.qty button { min-width: 36px; }
|
|
132
|
-
.total[aria-live] { font-weight: 700; font-size: 1.1rem; }
|
|
133
699
|
.provider-slot {
|
|
134
|
-
min-height:
|
|
135
|
-
border: 1px dashed var(--tickean-border, #
|
|
700
|
+
min-height: 56px;
|
|
701
|
+
border: 1px dashed var(--tickean-border, #e3e8ee);
|
|
702
|
+
border-radius: var(--tickean-radius, 12px);
|
|
703
|
+
padding: var(--tickean-space, 14px);
|
|
704
|
+
color: var(--tickean-muted, #697386);
|
|
705
|
+
background: var(--tickean-surface, #f6f9fc);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/* Stepped checkout shell */
|
|
709
|
+
.checkout-steps {
|
|
710
|
+
display: grid;
|
|
711
|
+
gap: 8px;
|
|
712
|
+
padding: 10px;
|
|
713
|
+
background:
|
|
714
|
+
radial-gradient(700px 220px at 8% -20%, color-mix(in srgb, var(--tickean-primary, #16a34a) 10%, transparent), transparent 55%),
|
|
715
|
+
var(--tickean-bg, #fff);
|
|
716
|
+
border: 1px solid var(--tickean-border, #dcfce7);
|
|
717
|
+
border-radius: calc(var(--tickean-radius, 10px) + 2px);
|
|
718
|
+
box-shadow: var(--tickean-shadow);
|
|
719
|
+
}
|
|
720
|
+
.checkout-steps.compact {
|
|
721
|
+
padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
|
|
722
|
+
}
|
|
723
|
+
.checkout-header h2 {
|
|
724
|
+
margin: 0;
|
|
725
|
+
font-size: 0.98rem;
|
|
726
|
+
line-height: 1.2;
|
|
727
|
+
letter-spacing: -0.02em;
|
|
728
|
+
font-weight: 700;
|
|
729
|
+
}
|
|
730
|
+
.checkout-header p {
|
|
731
|
+
margin: 2px 0 0;
|
|
732
|
+
}
|
|
733
|
+
.event-hero {
|
|
734
|
+
display: grid;
|
|
735
|
+
grid-template-columns: 48px minmax(0, 1fr);
|
|
736
|
+
gap: 8px;
|
|
737
|
+
align-items: start;
|
|
738
|
+
}
|
|
739
|
+
.event-flyer {
|
|
740
|
+
width: 48px;
|
|
741
|
+
height: 48px;
|
|
742
|
+
border-radius: 8px;
|
|
743
|
+
object-fit: cover;
|
|
744
|
+
border: 1px solid var(--tickean-border, #dcfce7);
|
|
745
|
+
background: var(--tickean-surface, #f0fdf4);
|
|
746
|
+
}
|
|
747
|
+
.event-flyer-fallback {
|
|
748
|
+
display: grid;
|
|
749
|
+
place-items: center;
|
|
750
|
+
color: var(--tickean-primary, #16a34a);
|
|
751
|
+
font-size: 1rem;
|
|
752
|
+
}
|
|
753
|
+
.event-hero-copy {
|
|
754
|
+
min-width: 0;
|
|
755
|
+
display: grid;
|
|
756
|
+
gap: 2px;
|
|
757
|
+
}
|
|
758
|
+
.event-dates {
|
|
759
|
+
margin: 0;
|
|
760
|
+
font-size: 0.78rem;
|
|
761
|
+
font-weight: 650;
|
|
762
|
+
color: var(--tickean-fg, #052e16);
|
|
763
|
+
letter-spacing: -0.01em;
|
|
764
|
+
}
|
|
765
|
+
.event-schedule {
|
|
766
|
+
margin: 2px 0 0;
|
|
767
|
+
}
|
|
768
|
+
.event-schedule summary {
|
|
769
|
+
cursor: pointer;
|
|
770
|
+
font-size: 0.72rem;
|
|
771
|
+
font-weight: 600;
|
|
772
|
+
color: var(--tickean-muted, #64748b);
|
|
773
|
+
list-style: none;
|
|
774
|
+
}
|
|
775
|
+
.event-schedule summary::-webkit-details-marker { display: none; }
|
|
776
|
+
.event-show-times {
|
|
777
|
+
list-style: none;
|
|
778
|
+
margin: 4px 0 0;
|
|
779
|
+
padding: 0;
|
|
780
|
+
display: grid;
|
|
781
|
+
gap: 1px;
|
|
782
|
+
}
|
|
783
|
+
.event-show-times li {
|
|
784
|
+
font-size: 0.72rem;
|
|
785
|
+
color: var(--tickean-muted, #64748b);
|
|
786
|
+
line-height: 1.3;
|
|
787
|
+
}
|
|
788
|
+
.event-place {
|
|
789
|
+
margin: 0;
|
|
790
|
+
font-size: 0.72rem;
|
|
791
|
+
}
|
|
792
|
+
.progress {
|
|
793
|
+
height: 3px;
|
|
794
|
+
border-radius: 999px;
|
|
795
|
+
background: var(--tickean-surface, #f6f9fc);
|
|
796
|
+
overflow: hidden;
|
|
797
|
+
border: none;
|
|
798
|
+
}
|
|
799
|
+
.progress > span {
|
|
800
|
+
display: block;
|
|
801
|
+
height: 100%;
|
|
802
|
+
width: var(--tickean-progress, 25%);
|
|
803
|
+
background: linear-gradient(90deg, var(--tickean-primary, #635bff), color-mix(in srgb, var(--tickean-primary, #635bff) 65%, #fff));
|
|
804
|
+
border-radius: inherit;
|
|
805
|
+
transition: width 380ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
806
|
+
}
|
|
807
|
+
.stepper {
|
|
808
|
+
display: grid;
|
|
809
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
810
|
+
gap: 4px;
|
|
811
|
+
list-style: none;
|
|
812
|
+
margin: 0;
|
|
813
|
+
padding: 0;
|
|
814
|
+
}
|
|
815
|
+
.stepper-item {
|
|
816
|
+
display: grid;
|
|
817
|
+
gap: 3px;
|
|
818
|
+
justify-items: center;
|
|
819
|
+
font-size: 0.65rem;
|
|
820
|
+
color: var(--tickean-muted, #697386);
|
|
821
|
+
font-weight: 500;
|
|
822
|
+
text-align: center;
|
|
823
|
+
}
|
|
824
|
+
.stepper-label {
|
|
825
|
+
white-space: nowrap;
|
|
826
|
+
overflow: hidden;
|
|
827
|
+
text-overflow: ellipsis;
|
|
828
|
+
max-width: 100%;
|
|
829
|
+
}
|
|
830
|
+
.stepper-dot {
|
|
831
|
+
width: 22px;
|
|
832
|
+
height: 22px;
|
|
833
|
+
border-radius: 999px;
|
|
834
|
+
display: grid;
|
|
835
|
+
place-items: center;
|
|
836
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
837
|
+
background: var(--tickean-input-bg, #fff);
|
|
838
|
+
font-weight: 650;
|
|
839
|
+
font-size: 0.68rem;
|
|
840
|
+
color: var(--tickean-muted, #697386);
|
|
841
|
+
transition: all 200ms ease;
|
|
842
|
+
}
|
|
843
|
+
.stepper-item[data-active="true"] {
|
|
844
|
+
color: var(--tickean-fg, #0a2540);
|
|
845
|
+
font-weight: 650;
|
|
846
|
+
}
|
|
847
|
+
.stepper-item[data-active="true"] .stepper-dot,
|
|
848
|
+
.stepper-item[data-done="true"] .stepper-dot {
|
|
849
|
+
background: var(--tickean-primary, #635bff);
|
|
850
|
+
border-color: var(--tickean-primary, #635bff);
|
|
851
|
+
color: var(--tickean-primary-fg, #fff);
|
|
852
|
+
}
|
|
853
|
+
.step-caption {
|
|
854
|
+
margin: 0;
|
|
855
|
+
font-size: 0.72rem;
|
|
856
|
+
}
|
|
857
|
+
.checkout-layout {
|
|
858
|
+
display: grid;
|
|
859
|
+
gap: 8px;
|
|
860
|
+
}
|
|
861
|
+
.summary-drawer {
|
|
862
|
+
border: 1px solid var(--tickean-border, #dcfce7);
|
|
863
|
+
border-radius: 10px;
|
|
864
|
+
background: var(--tickean-bg, #fff);
|
|
865
|
+
overflow: hidden;
|
|
866
|
+
}
|
|
867
|
+
.summary-drawer-toggle {
|
|
868
|
+
display: flex;
|
|
869
|
+
align-items: center;
|
|
870
|
+
justify-content: space-between;
|
|
871
|
+
gap: 8px;
|
|
872
|
+
padding: 8px 10px;
|
|
873
|
+
cursor: pointer;
|
|
874
|
+
list-style: none;
|
|
875
|
+
font-size: 0.8rem;
|
|
876
|
+
font-weight: 600;
|
|
877
|
+
}
|
|
878
|
+
.summary-drawer-toggle::-webkit-details-marker { display: none; }
|
|
879
|
+
.summary-drawer-toggle::after {
|
|
880
|
+
content: "";
|
|
881
|
+
width: 0.45em;
|
|
882
|
+
height: 0.45em;
|
|
883
|
+
border-right: 1.5px solid var(--tickean-muted);
|
|
884
|
+
border-bottom: 1.5px solid var(--tickean-muted);
|
|
885
|
+
transform: rotate(45deg);
|
|
886
|
+
transition: transform 0.15s ease;
|
|
887
|
+
margin-left: 4px;
|
|
888
|
+
flex: 0 0 auto;
|
|
889
|
+
}
|
|
890
|
+
.summary-drawer[open] .summary-drawer-toggle::after {
|
|
891
|
+
transform: rotate(225deg);
|
|
892
|
+
margin-top: 4px;
|
|
893
|
+
}
|
|
894
|
+
.summary-drawer-toggle strong {
|
|
895
|
+
font-variant-numeric: tabular-nums;
|
|
896
|
+
letter-spacing: -0.02em;
|
|
897
|
+
margin-left: auto;
|
|
898
|
+
}
|
|
899
|
+
.summary-drawer-body {
|
|
900
|
+
display: grid;
|
|
901
|
+
gap: 6px;
|
|
902
|
+
padding: 0 8px 8px;
|
|
903
|
+
border-top: 1px solid var(--tickean-border, #e3e8ee);
|
|
904
|
+
width: 100%;
|
|
905
|
+
}
|
|
906
|
+
.summary-drawer-body > * {
|
|
907
|
+
display: block;
|
|
908
|
+
width: 100%;
|
|
909
|
+
min-width: 0;
|
|
910
|
+
}
|
|
911
|
+
.summary-drawer:not([open]) .summary-drawer-body { display: none; }
|
|
912
|
+
.checkout-sticky {
|
|
913
|
+
display: grid;
|
|
914
|
+
gap: 0;
|
|
915
|
+
background: color-mix(in srgb, var(--tickean-bg, #fff) 92%, transparent);
|
|
916
|
+
backdrop-filter: blur(10px);
|
|
917
|
+
border-top: 1px solid var(--tickean-border, #dcfce7);
|
|
918
|
+
box-shadow: 0 -6px 18px rgba(15, 23, 42, 0.06);
|
|
919
|
+
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
920
|
+
}
|
|
921
|
+
.checkout-sticky[hidden] { display: none !important; }
|
|
922
|
+
.checkout-dock {
|
|
923
|
+
display: grid;
|
|
924
|
+
grid-template-columns: 1fr auto 1fr;
|
|
925
|
+
align-items: center;
|
|
926
|
+
gap: 8px;
|
|
927
|
+
padding: 8px 12px 4px;
|
|
928
|
+
}
|
|
929
|
+
.checkout-dock[hidden] { display: none !important; }
|
|
930
|
+
.checkout-sticky .powered {
|
|
931
|
+
width: 100%;
|
|
932
|
+
margin: 0;
|
|
933
|
+
padding: 2px 10px 8px;
|
|
934
|
+
}
|
|
935
|
+
.dock-side {
|
|
936
|
+
display: flex;
|
|
937
|
+
align-items: center;
|
|
938
|
+
min-width: 0;
|
|
939
|
+
}
|
|
940
|
+
.dock-side-start { justify-content: flex-start; }
|
|
941
|
+
.dock-side-end { justify-content: flex-end; }
|
|
942
|
+
.dock-total {
|
|
943
|
+
display: grid;
|
|
944
|
+
gap: 0;
|
|
945
|
+
min-width: 0;
|
|
946
|
+
line-height: 1.15;
|
|
947
|
+
text-align: center;
|
|
948
|
+
justify-items: center;
|
|
949
|
+
}
|
|
950
|
+
.dock-total .muted { font-size: 0.68rem; }
|
|
951
|
+
.dock-total strong {
|
|
952
|
+
font-size: 0.98rem;
|
|
953
|
+
font-variant-numeric: tabular-nums;
|
|
954
|
+
letter-spacing: -0.02em;
|
|
955
|
+
}
|
|
956
|
+
.dock-back {
|
|
957
|
+
padding: 9px 12px;
|
|
958
|
+
min-height: 40px;
|
|
959
|
+
flex-shrink: 0;
|
|
960
|
+
}
|
|
961
|
+
.dock-next {
|
|
962
|
+
padding: 9px 16px;
|
|
963
|
+
min-height: 40px;
|
|
964
|
+
min-width: 112px;
|
|
965
|
+
}
|
|
966
|
+
.step-panels { display: grid; gap: 8px; width: 100%; }
|
|
967
|
+
.step-panel[hidden] { display: none !important; }
|
|
968
|
+
.step-panel {
|
|
969
|
+
width: 100%;
|
|
970
|
+
animation: tickean-fade-up 280ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
971
|
+
}
|
|
972
|
+
.step-panel > * {
|
|
973
|
+
display: block;
|
|
974
|
+
width: 100%;
|
|
975
|
+
min-width: 0;
|
|
976
|
+
}
|
|
977
|
+
.checkout-aside,
|
|
978
|
+
.checkout-layout {
|
|
979
|
+
width: 100%;
|
|
980
|
+
}
|
|
981
|
+
.step-nav {
|
|
982
|
+
display: flex;
|
|
983
|
+
gap: 8px;
|
|
984
|
+
justify-content: space-between;
|
|
985
|
+
flex-wrap: wrap;
|
|
986
|
+
align-items: center;
|
|
987
|
+
margin-top: 2px;
|
|
988
|
+
}
|
|
989
|
+
.step-nav .spacer { flex: 1; }
|
|
990
|
+
.done-card {
|
|
991
|
+
text-align: center;
|
|
992
|
+
padding: 16px 12px;
|
|
993
|
+
background: var(--tickean-surface, #f6f9fc);
|
|
136
994
|
border-radius: var(--tickean-radius, 10px);
|
|
137
|
-
|
|
138
|
-
|
|
995
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
996
|
+
}
|
|
997
|
+
.done-card h3 {
|
|
998
|
+
margin: 0 0 6px;
|
|
999
|
+
font-size: 1.15rem;
|
|
1000
|
+
letter-spacing: -0.03em;
|
|
1001
|
+
}
|
|
1002
|
+
.done-icon {
|
|
1003
|
+
width: 40px;
|
|
1004
|
+
height: 40px;
|
|
1005
|
+
margin: 0 auto 10px;
|
|
1006
|
+
border-radius: 999px;
|
|
1007
|
+
display: grid;
|
|
1008
|
+
place-items: center;
|
|
1009
|
+
background: color-mix(in srgb, var(--tickean-success, #0d9488) 16%, transparent);
|
|
1010
|
+
color: var(--tickean-success, #0d9488);
|
|
1011
|
+
font-size: 1.15rem;
|
|
1012
|
+
font-weight: 700;
|
|
1013
|
+
}
|
|
1014
|
+
.done-actions {
|
|
1015
|
+
display: grid;
|
|
1016
|
+
gap: 8px;
|
|
1017
|
+
margin-top: 14px;
|
|
1018
|
+
}
|
|
1019
|
+
.done-primary {
|
|
1020
|
+
display: inline-flex;
|
|
1021
|
+
align-items: center;
|
|
1022
|
+
justify-content: center;
|
|
1023
|
+
min-height: 42px;
|
|
1024
|
+
padding: 10px 14px;
|
|
1025
|
+
border-radius: 999px;
|
|
1026
|
+
background: var(--tickean-primary, #16a34a);
|
|
1027
|
+
color: var(--tickean-primary-fg, #fff);
|
|
1028
|
+
text-decoration: none;
|
|
1029
|
+
font-weight: 650;
|
|
1030
|
+
font-size: 0.92rem;
|
|
1031
|
+
box-shadow: 0 2px 8px color-mix(in srgb, var(--tickean-primary, #16a34a) 22%, transparent);
|
|
1032
|
+
}
|
|
1033
|
+
.done-primary:hover {
|
|
1034
|
+
background: var(--tickean-primary-hover, #15803d);
|
|
1035
|
+
}
|
|
1036
|
+
.done-actions .secondary {
|
|
1037
|
+
width: 100%;
|
|
1038
|
+
}
|
|
1039
|
+
.powered {
|
|
1040
|
+
display: inline-flex;
|
|
1041
|
+
align-items: center;
|
|
1042
|
+
justify-content: center;
|
|
1043
|
+
gap: 5px;
|
|
1044
|
+
margin: 0 auto;
|
|
1045
|
+
padding: 4px 8px;
|
|
1046
|
+
color: var(--tickean-muted, #697386);
|
|
1047
|
+
text-decoration: none;
|
|
1048
|
+
font-size: 0.7rem;
|
|
1049
|
+
font-weight: 500;
|
|
1050
|
+
transition: color 140ms ease;
|
|
1051
|
+
}
|
|
1052
|
+
.powered:hover { color: var(--tickean-fg, #0a2540); }
|
|
1053
|
+
.powered strong {
|
|
1054
|
+
color: inherit;
|
|
1055
|
+
font-weight: 700;
|
|
1056
|
+
letter-spacing: -0.02em;
|
|
1057
|
+
}
|
|
1058
|
+
.powered-mark {
|
|
1059
|
+
width: 16px;
|
|
1060
|
+
height: 16px;
|
|
1061
|
+
border-radius: 5px;
|
|
1062
|
+
background: linear-gradient(135deg, #22c55e, #16a34a);
|
|
1063
|
+
box-shadow: 0 2px 6px rgba(22, 163, 74, 0.35);
|
|
1064
|
+
}
|
|
1065
|
+
.loading-shell {
|
|
1066
|
+
padding: calc(var(--tickean-space, 14px) * 1.5);
|
|
1067
|
+
border-radius: calc(var(--tickean-radius, 12px) + 4px);
|
|
1068
|
+
border: 1px solid var(--tickean-border, #e3e8ee);
|
|
1069
|
+
background: var(--tickean-bg, #fff);
|
|
1070
|
+
box-shadow: var(--tickean-shadow-lg);
|
|
1071
|
+
display: grid;
|
|
1072
|
+
gap: 14px;
|
|
1073
|
+
}
|
|
1074
|
+
.skeleton {
|
|
1075
|
+
height: 14px;
|
|
1076
|
+
border-radius: 8px;
|
|
1077
|
+
background: linear-gradient(
|
|
1078
|
+
90deg,
|
|
1079
|
+
var(--tickean-surface, #f6f9fc) 0%,
|
|
1080
|
+
color-mix(in srgb, var(--tickean-surface, #f6f9fc) 40%, #fff) 50%,
|
|
1081
|
+
var(--tickean-surface, #f6f9fc) 100%
|
|
1082
|
+
);
|
|
1083
|
+
background-size: 200% 100%;
|
|
1084
|
+
animation: tickean-shimmer 1.2s ease-in-out infinite;
|
|
1085
|
+
}
|
|
1086
|
+
.skeleton.lg { height: 22px; width: 55%; }
|
|
1087
|
+
.skeleton.md { height: 44px; }
|
|
1088
|
+
.skeleton.sm { width: 35%; }
|
|
1089
|
+
.loading-label {
|
|
1090
|
+
display: flex;
|
|
1091
|
+
align-items: center;
|
|
1092
|
+
gap: 8px;
|
|
1093
|
+
color: var(--tickean-muted, #697386);
|
|
1094
|
+
font-size: 0.9rem;
|
|
1095
|
+
animation: tickean-pulse 1.4s ease-in-out infinite;
|
|
1096
|
+
}
|
|
1097
|
+
.summary-line {
|
|
1098
|
+
display: flex;
|
|
1099
|
+
justify-content: space-between;
|
|
1100
|
+
gap: 8px;
|
|
1101
|
+
font-size: 0.82rem;
|
|
1102
|
+
}
|
|
1103
|
+
.badge {
|
|
1104
|
+
display: inline-flex;
|
|
1105
|
+
align-items: center;
|
|
1106
|
+
gap: 4px;
|
|
1107
|
+
padding: 2px 7px;
|
|
1108
|
+
border-radius: 999px;
|
|
1109
|
+
background: color-mix(in srgb, var(--tickean-success, #0d9488) 14%, transparent);
|
|
1110
|
+
color: var(--tickean-success, #0d9488);
|
|
1111
|
+
font-size: 0.7rem;
|
|
1112
|
+
font-weight: 600;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/* Mobile-first dock + density */
|
|
1116
|
+
@media (max-width: 640px) {
|
|
1117
|
+
:host {
|
|
1118
|
+
max-width: 100%;
|
|
1119
|
+
font-size: 14.5px;
|
|
1120
|
+
}
|
|
1121
|
+
.checkout-steps {
|
|
1122
|
+
border-radius: 0;
|
|
1123
|
+
border-left: none;
|
|
1124
|
+
border-right: none;
|
|
1125
|
+
box-shadow: none;
|
|
1126
|
+
padding: 8px 10px;
|
|
1127
|
+
}
|
|
1128
|
+
.checkout-steps.compact {
|
|
1129
|
+
padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
|
|
1130
|
+
}
|
|
1131
|
+
.step-caption,
|
|
1132
|
+
.stepper-label {
|
|
1133
|
+
display: none;
|
|
1134
|
+
}
|
|
1135
|
+
.stepper-item {
|
|
1136
|
+
justify-items: center;
|
|
1137
|
+
}
|
|
1138
|
+
.checkout-sticky {
|
|
1139
|
+
position: fixed;
|
|
1140
|
+
left: 0;
|
|
1141
|
+
right: 0;
|
|
1142
|
+
bottom: 0;
|
|
1143
|
+
z-index: 35;
|
|
1144
|
+
}
|
|
1145
|
+
.phone-row {
|
|
1146
|
+
grid-template-columns: minmax(104px, 34%) 1fr;
|
|
1147
|
+
gap: 6px;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
@media (min-width: 641px) {
|
|
1152
|
+
.checkout-steps.compact {
|
|
1153
|
+
padding-bottom: 10px;
|
|
1154
|
+
}
|
|
1155
|
+
.checkout-sticky {
|
|
1156
|
+
position: sticky;
|
|
1157
|
+
bottom: 0;
|
|
1158
|
+
border: 1px solid var(--tickean-border, #dcfce7);
|
|
1159
|
+
border-radius: 10px;
|
|
1160
|
+
margin-top: 2px;
|
|
1161
|
+
border-top: none;
|
|
1162
|
+
}
|
|
1163
|
+
.summary-drawer {
|
|
1164
|
+
border: 1px solid var(--tickean-border, #dcfce7);
|
|
1165
|
+
background: var(--tickean-bg, #fff);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1170
|
+
.reveal, .step-panel, .skeleton, .loading-label, .ticket, button {
|
|
1171
|
+
animation: none !important;
|
|
1172
|
+
transition: none !important;
|
|
1173
|
+
}
|
|
139
1174
|
}
|
|
140
1175
|
`;
|
|
141
1176
|
function applyAppearance(host, appearance) {
|
|
@@ -179,16 +1214,92 @@ var catalogs = {
|
|
|
179
1214
|
verifyOtp: "Verificar",
|
|
180
1215
|
total: "Total",
|
|
181
1216
|
pay: "Confirmar compra",
|
|
1217
|
+
creatingPayment: "Creando pago\u2026",
|
|
182
1218
|
transferInstructions: "Datos para transferir",
|
|
183
1219
|
alias: "Alias",
|
|
184
1220
|
cvu: "CVU",
|
|
185
1221
|
amount: "Monto",
|
|
186
1222
|
redirectPay: "Continuar al pago",
|
|
1223
|
+
redirectingPay: "Te estamos llevando a Mercado Pago\u2026",
|
|
187
1224
|
providerPlaceholder: "Complet\xE1 el pago con tu proveedor",
|
|
188
1225
|
completed: "\xA1Compra completada!",
|
|
189
1226
|
error: "Ocurri\xF3 un error",
|
|
190
1227
|
emptyCart: "Eleg\xED al menos una entrada",
|
|
191
|
-
quantity: "Cantidad"
|
|
1228
|
+
quantity: "Cantidad",
|
|
1229
|
+
stepTickets: "Entradas",
|
|
1230
|
+
stepBuyer: "Tus datos",
|
|
1231
|
+
stepPayment: "Pago",
|
|
1232
|
+
stepTransfer: "Transferir",
|
|
1233
|
+
stepDone: "Listo",
|
|
1234
|
+
next: "Continuar",
|
|
1235
|
+
back: "Volver",
|
|
1236
|
+
stepOf: "Paso {current} de {total}",
|
|
1237
|
+
doneSubtitle: "Te enviamos la confirmaci\xF3n por email y WhatsApp.",
|
|
1238
|
+
viewTickets: "Ver mis entradas",
|
|
1239
|
+
buyAgain: "Seguir comprando",
|
|
1240
|
+
country: "Pa\xEDs",
|
|
1241
|
+
verified: "Identidad verificada",
|
|
1242
|
+
sending: "Enviando\u2026",
|
|
1243
|
+
verifying: "Verificando\u2026",
|
|
1244
|
+
namePlaceholder: "Nombre y apellido",
|
|
1245
|
+
poweredBy: "Powered by",
|
|
1246
|
+
orderSummary: "Resumen",
|
|
1247
|
+
itemsCount: "{count} \xEDtem(s)",
|
|
1248
|
+
passes: "Pases",
|
|
1249
|
+
discountToggle: "\xBFTen\xE9s un c\xF3digo?",
|
|
1250
|
+
otpTitle: "Verific\xE1 tu tel\xE9fono",
|
|
1251
|
+
otpHint: "Te enviamos un c\xF3digo de 6 d\xEDgitos",
|
|
1252
|
+
resendOtp: "Reenviar c\xF3digo",
|
|
1253
|
+
close: "Cerrar",
|
|
1254
|
+
attendees: "Asistentes",
|
|
1255
|
+
attendeeN: "Asistente {n}",
|
|
1256
|
+
firstName: "Nombre",
|
|
1257
|
+
lastName: "Apellido",
|
|
1258
|
+
continueToOtp: "Enviar c\xF3digo",
|
|
1259
|
+
viewSummary: "Ver resumen",
|
|
1260
|
+
showTimes: "Horarios",
|
|
1261
|
+
soldOut: "Agotado",
|
|
1262
|
+
comingSoon: "Pr\xF3ximamente",
|
|
1263
|
+
lastTicket: "\xA1\xDAltima!",
|
|
1264
|
+
lastTickets: "\xA1\xDAltimas {count}!",
|
|
1265
|
+
unlocked: "Desbloqueada",
|
|
1266
|
+
promoCodeHint: "\xBFTen\xE9s un c\xF3digo? Ingresalo arriba para desbloquear entradas ocultas.",
|
|
1267
|
+
promoUnlockHint: "Ingresalo antes de elegir entradas para desbloquear opciones exclusivas.",
|
|
1268
|
+
discountCodeHint: "Ingresalo ac\xE1 para aplicar el descuento a tu compra.",
|
|
1269
|
+
promoUnlockPlaceholder: "Ej: ARTISTAS",
|
|
1270
|
+
promoUnlockSuccess: "C\xF3digo v\xE1lido. Se revelaron entradas exclusivas.",
|
|
1271
|
+
promoUnlockSuccessNamed: "C\xF3digo v\xE1lido. Desbloqueaste: {names}.",
|
|
1272
|
+
promoUnlockApplied: "C\xF3digo v\xE1lido. Se aplicar\xE1 al pagar.",
|
|
1273
|
+
promoNxM: "{label}: llev\xE1 {buy}, pag\xE1 {pay}",
|
|
1274
|
+
promoNth: "{label}: {percent}% off en la unidad {unit}",
|
|
1275
|
+
paymentMethods: "M\xE9todo de pago",
|
|
1276
|
+
choosePayment: "Eleg\xED un m\xE9todo",
|
|
1277
|
+
payTransfer: "Transferencia bancaria",
|
|
1278
|
+
payTransferDesc: "Transfer\xEDs desde tu banco o billetera. Las entradas se env\xEDan al confirmar el pago.",
|
|
1279
|
+
payTransferHint: "Al confirmar te mostramos alias/CVU para transferir el monto exacto.",
|
|
1280
|
+
payMercadoPago: "Mercado Pago",
|
|
1281
|
+
payMercadoPagoDesc: "Tarjeta, dinero en cuenta o saldo de Mercado Pago.",
|
|
1282
|
+
payCard: "Tarjeta",
|
|
1283
|
+
payCardDesc: "D\xE9bito o cr\xE9dito, inclusive internacionales.",
|
|
1284
|
+
payFree: "Gratis",
|
|
1285
|
+
payFreeDesc: "Sin cargo para este evento.",
|
|
1286
|
+
transferPendingHint: "Cuando acreditemos la transferencia te enviamos las entradas por email/WhatsApp.",
|
|
1287
|
+
transferExactAmount: "Monto exacto a transferir",
|
|
1288
|
+
transferWaiting: "Esperando confirmaci\xF3n de tu transferencia\u2026",
|
|
1289
|
+
copy: "Copiar",
|
|
1290
|
+
copied: "Copiado",
|
|
1291
|
+
changePaymentMethod: "Cambiar m\xE9todo",
|
|
1292
|
+
subtotal: "Subtotal",
|
|
1293
|
+
serviceCharge: "Cargo de servicio",
|
|
1294
|
+
promoDiscount: "Promoci\xF3n",
|
|
1295
|
+
accountHolder: "Titular",
|
|
1296
|
+
bank: "Banco",
|
|
1297
|
+
acceptTerms: "Acepto los t\xE9rminos",
|
|
1298
|
+
onboardHint: "No encontramos una cuenta con ese tel\xE9fono. Complet\xE1 tus datos para continuar.",
|
|
1299
|
+
onboardRequired: "Nombre y email son obligatorios",
|
|
1300
|
+
emailInvalid: "Email inv\xE1lido",
|
|
1301
|
+
changePhone: "Cambiar tel\xE9fono",
|
|
1302
|
+
welcomeBack: "Hola {name}, te enviamos un c\xF3digo para verificar."
|
|
192
1303
|
},
|
|
193
1304
|
"es-CL": {
|
|
194
1305
|
loading: "Cargando checkout\u2026",
|
|
@@ -203,16 +1314,92 @@ var catalogs = {
|
|
|
203
1314
|
verifyOtp: "Verificar",
|
|
204
1315
|
total: "Total",
|
|
205
1316
|
pay: "Confirmar compra",
|
|
1317
|
+
creatingPayment: "Creando pago\u2026",
|
|
206
1318
|
transferInstructions: "Datos para transferir",
|
|
207
1319
|
alias: "Alias",
|
|
208
1320
|
cvu: "CVU",
|
|
209
1321
|
amount: "Monto",
|
|
210
1322
|
redirectPay: "Continuar al pago",
|
|
1323
|
+
redirectingPay: "Te estamos llevando a Mercado Pago\u2026",
|
|
211
1324
|
providerPlaceholder: "Completa el pago con tu proveedor",
|
|
212
1325
|
completed: "\xA1Compra completada!",
|
|
213
1326
|
error: "Ocurri\xF3 un error",
|
|
214
1327
|
emptyCart: "Elige al menos una entrada",
|
|
215
|
-
quantity: "Cantidad"
|
|
1328
|
+
quantity: "Cantidad",
|
|
1329
|
+
stepTickets: "Entradas",
|
|
1330
|
+
stepBuyer: "Tus datos",
|
|
1331
|
+
stepPayment: "Pago",
|
|
1332
|
+
stepTransfer: "Transferir",
|
|
1333
|
+
stepDone: "Listo",
|
|
1334
|
+
next: "Continuar",
|
|
1335
|
+
back: "Volver",
|
|
1336
|
+
stepOf: "Paso {current} de {total}",
|
|
1337
|
+
doneSubtitle: "Te enviamos la confirmaci\xF3n por email y WhatsApp.",
|
|
1338
|
+
viewTickets: "Ver mis entradas",
|
|
1339
|
+
buyAgain: "Seguir comprando",
|
|
1340
|
+
country: "Pa\xEDs",
|
|
1341
|
+
verified: "Identidad verificada",
|
|
1342
|
+
sending: "Enviando\u2026",
|
|
1343
|
+
verifying: "Verificando\u2026",
|
|
1344
|
+
namePlaceholder: "Nombre y apellido",
|
|
1345
|
+
poweredBy: "Powered by",
|
|
1346
|
+
orderSummary: "Resumen",
|
|
1347
|
+
itemsCount: "{count} \xEDtem(s)",
|
|
1348
|
+
passes: "Pases",
|
|
1349
|
+
discountToggle: "\xBFTienes un c\xF3digo?",
|
|
1350
|
+
otpTitle: "Verifica tu tel\xE9fono",
|
|
1351
|
+
otpHint: "Te enviamos un c\xF3digo de 6 d\xEDgitos",
|
|
1352
|
+
resendOtp: "Reenviar c\xF3digo",
|
|
1353
|
+
close: "Cerrar",
|
|
1354
|
+
attendees: "Asistentes",
|
|
1355
|
+
attendeeN: "Asistente {n}",
|
|
1356
|
+
firstName: "Nombre",
|
|
1357
|
+
lastName: "Apellido",
|
|
1358
|
+
continueToOtp: "Enviar c\xF3digo",
|
|
1359
|
+
viewSummary: "Ver resumen",
|
|
1360
|
+
showTimes: "Horarios",
|
|
1361
|
+
soldOut: "Agotado",
|
|
1362
|
+
comingSoon: "Pr\xF3ximamente",
|
|
1363
|
+
lastTicket: "\xA1\xDAltima!",
|
|
1364
|
+
lastTickets: "\xA1\xDAltimas {count}!",
|
|
1365
|
+
unlocked: "Desbloqueada",
|
|
1366
|
+
promoCodeHint: "\xBFTienes un c\xF3digo? Ingr\xE9salo arriba para desbloquear entradas ocultas.",
|
|
1367
|
+
promoUnlockHint: "Ingr\xE9salo antes de elegir entradas para desbloquear opciones exclusivas.",
|
|
1368
|
+
discountCodeHint: "Ingr\xE9salo ac\xE1 para aplicar el descuento a tu compra.",
|
|
1369
|
+
promoUnlockPlaceholder: "Ej: ARTISTAS",
|
|
1370
|
+
promoUnlockSuccess: "C\xF3digo v\xE1lido. Se revelaron entradas exclusivas.",
|
|
1371
|
+
promoUnlockSuccessNamed: "C\xF3digo v\xE1lido. Desbloqueaste: {names}.",
|
|
1372
|
+
promoUnlockApplied: "C\xF3digo v\xE1lido. Se aplicar\xE1 al pagar.",
|
|
1373
|
+
promoNxM: "{label}: lleva {buy}, paga {pay}",
|
|
1374
|
+
promoNth: "{label}: {percent}% off en la unidad {unit}",
|
|
1375
|
+
paymentMethods: "M\xE9todo de pago",
|
|
1376
|
+
choosePayment: "Elige un m\xE9todo",
|
|
1377
|
+
payTransfer: "Transferencia bancaria",
|
|
1378
|
+
payTransferDesc: "Transfieres desde tu banco o billetera. Las entradas se env\xEDan al confirmar el pago.",
|
|
1379
|
+
payTransferHint: "Al confirmar te mostramos alias/CVU para transferir el monto exacto.",
|
|
1380
|
+
payMercadoPago: "Mercado Pago",
|
|
1381
|
+
payMercadoPagoDesc: "Tarjeta, dinero en cuenta o saldo de Mercado Pago.",
|
|
1382
|
+
payCard: "Tarjeta",
|
|
1383
|
+
payCardDesc: "D\xE9bito o cr\xE9dito, inclusive internacionales.",
|
|
1384
|
+
payFree: "Gratis",
|
|
1385
|
+
payFreeDesc: "Sin cargo para este evento.",
|
|
1386
|
+
transferPendingHint: "Cuando acreditemos la transferencia te enviamos las entradas por email/WhatsApp.",
|
|
1387
|
+
transferExactAmount: "Monto exacto a transferir",
|
|
1388
|
+
transferWaiting: "Esperando confirmaci\xF3n de tu transferencia\u2026",
|
|
1389
|
+
copy: "Copiar",
|
|
1390
|
+
copied: "Copiado",
|
|
1391
|
+
changePaymentMethod: "Cambiar m\xE9todo",
|
|
1392
|
+
subtotal: "Subtotal",
|
|
1393
|
+
serviceCharge: "Cargo de servicio",
|
|
1394
|
+
promoDiscount: "Promoci\xF3n",
|
|
1395
|
+
accountHolder: "Titular",
|
|
1396
|
+
bank: "Banco",
|
|
1397
|
+
acceptTerms: "Acepto los t\xE9rminos",
|
|
1398
|
+
onboardHint: "No encontramos una cuenta con ese tel\xE9fono. Completa tus datos para continuar.",
|
|
1399
|
+
onboardRequired: "Nombre y email son obligatorios",
|
|
1400
|
+
emailInvalid: "Email inv\xE1lido",
|
|
1401
|
+
changePhone: "Cambiar tel\xE9fono",
|
|
1402
|
+
welcomeBack: "Hola {name}, te enviamos un c\xF3digo para verificar."
|
|
216
1403
|
},
|
|
217
1404
|
en: {
|
|
218
1405
|
loading: "Loading checkout\u2026",
|
|
@@ -227,22 +1414,105 @@ var catalogs = {
|
|
|
227
1414
|
verifyOtp: "Verify",
|
|
228
1415
|
total: "Total",
|
|
229
1416
|
pay: "Confirm purchase",
|
|
1417
|
+
creatingPayment: "Creating payment\u2026",
|
|
230
1418
|
transferInstructions: "Transfer details",
|
|
231
1419
|
alias: "Alias",
|
|
232
1420
|
cvu: "CVU",
|
|
233
1421
|
amount: "Amount",
|
|
234
1422
|
redirectPay: "Continue to payment",
|
|
1423
|
+
redirectingPay: "Taking you to Mercado Pago\u2026",
|
|
235
1424
|
providerPlaceholder: "Complete payment with your provider",
|
|
236
1425
|
completed: "Purchase completed!",
|
|
237
1426
|
error: "Something went wrong",
|
|
238
1427
|
emptyCart: "Select at least one ticket",
|
|
239
|
-
quantity: "Quantity"
|
|
1428
|
+
quantity: "Quantity",
|
|
1429
|
+
stepTickets: "Tickets",
|
|
1430
|
+
stepBuyer: "Your details",
|
|
1431
|
+
stepPayment: "Payment",
|
|
1432
|
+
stepTransfer: "Transfer",
|
|
1433
|
+
stepDone: "Done",
|
|
1434
|
+
next: "Continue",
|
|
1435
|
+
back: "Back",
|
|
1436
|
+
stepOf: "Step {current} of {total}",
|
|
1437
|
+
doneSubtitle: "We sent your confirmation by email and WhatsApp.",
|
|
1438
|
+
viewTickets: "View my tickets",
|
|
1439
|
+
buyAgain: "Buy again",
|
|
1440
|
+
country: "Country",
|
|
1441
|
+
verified: "Identity verified",
|
|
1442
|
+
sending: "Sending\u2026",
|
|
1443
|
+
verifying: "Verifying\u2026",
|
|
1444
|
+
namePlaceholder: "Full name",
|
|
1445
|
+
poweredBy: "Powered by",
|
|
1446
|
+
orderSummary: "Summary",
|
|
1447
|
+
itemsCount: "{count} item(s)",
|
|
1448
|
+
passes: "Passes",
|
|
1449
|
+
discountToggle: "Have a code?",
|
|
1450
|
+
otpTitle: "Verify your phone",
|
|
1451
|
+
otpHint: "We sent a 6-digit code",
|
|
1452
|
+
resendOtp: "Resend code",
|
|
1453
|
+
close: "Close",
|
|
1454
|
+
attendees: "Attendees",
|
|
1455
|
+
attendeeN: "Attendee {n}",
|
|
1456
|
+
firstName: "First name",
|
|
1457
|
+
lastName: "Last name",
|
|
1458
|
+
continueToOtp: "Send code",
|
|
1459
|
+
viewSummary: "View summary",
|
|
1460
|
+
showTimes: "Show times",
|
|
1461
|
+
soldOut: "Sold out",
|
|
1462
|
+
comingSoon: "Coming soon",
|
|
1463
|
+
lastTicket: "Last one!",
|
|
1464
|
+
lastTickets: "Only {count} left!",
|
|
1465
|
+
unlocked: "Unlocked",
|
|
1466
|
+
promoCodeHint: "Have a code? Enter it above to unlock hidden tickets.",
|
|
1467
|
+
promoUnlockHint: "Enter it before choosing tickets to unlock exclusive options.",
|
|
1468
|
+
discountCodeHint: "Enter it here to apply the discount to your purchase.",
|
|
1469
|
+
promoUnlockPlaceholder: "E.g. ARTISTAS",
|
|
1470
|
+
promoUnlockSuccess: "Valid code. Exclusive tickets were revealed.",
|
|
1471
|
+
promoUnlockSuccessNamed: "Valid code. Unlocked: {names}.",
|
|
1472
|
+
promoUnlockApplied: "Valid code. It will apply at payment.",
|
|
1473
|
+
promoNxM: "{label}: buy {buy}, pay {pay}",
|
|
1474
|
+
promoNth: "{label}: {percent}% off on unit {unit}",
|
|
1475
|
+
paymentMethods: "Payment method",
|
|
1476
|
+
choosePayment: "Choose a method",
|
|
1477
|
+
payTransfer: "Bank transfer",
|
|
1478
|
+
payTransferDesc: "Pay from your bank or wallet. Tickets are sent once payment is confirmed.",
|
|
1479
|
+
payTransferHint: "After confirming we\u2019ll show alias/CVU for the exact amount.",
|
|
1480
|
+
payMercadoPago: "Mercado Pago",
|
|
1481
|
+
payMercadoPagoDesc: "Card, account money, or Mercado Pago balance.",
|
|
1482
|
+
payCard: "Card",
|
|
1483
|
+
payCardDesc: "Debit or credit, including international cards.",
|
|
1484
|
+
payFree: "Free",
|
|
1485
|
+
payFreeDesc: "No charge for this event.",
|
|
1486
|
+
transferPendingHint: "Once we confirm the transfer we\u2019ll send your tickets by email/WhatsApp.",
|
|
1487
|
+
transferExactAmount: "Exact amount to transfer",
|
|
1488
|
+
transferWaiting: "Waiting for your transfer confirmation\u2026",
|
|
1489
|
+
copy: "Copy",
|
|
1490
|
+
copied: "Copied",
|
|
1491
|
+
changePaymentMethod: "Change method",
|
|
1492
|
+
subtotal: "Subtotal",
|
|
1493
|
+
serviceCharge: "Service fee",
|
|
1494
|
+
promoDiscount: "Promotion",
|
|
1495
|
+
accountHolder: "Account holder",
|
|
1496
|
+
bank: "Bank",
|
|
1497
|
+
acceptTerms: "I accept the terms",
|
|
1498
|
+
onboardHint: "We couldn't find an account with that phone. Add your details to continue.",
|
|
1499
|
+
onboardRequired: "Name and email are required",
|
|
1500
|
+
emailInvalid: "Invalid email",
|
|
1501
|
+
changePhone: "Change phone",
|
|
1502
|
+
welcomeBack: "Hi {name}, we sent you a verification code."
|
|
240
1503
|
}
|
|
241
1504
|
};
|
|
242
1505
|
function t(locale, key) {
|
|
243
1506
|
const resolved = locale && catalogs[locale] ? locale : "es-AR";
|
|
244
1507
|
return catalogs[resolved][key];
|
|
245
1508
|
}
|
|
1509
|
+
function tFormat(locale, key, vars) {
|
|
1510
|
+
let out = t(locale, key);
|
|
1511
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
1512
|
+
out = out.replace(new RegExp(`\\{${k}\\}`, "g"), String(v));
|
|
1513
|
+
}
|
|
1514
|
+
return out;
|
|
1515
|
+
}
|
|
246
1516
|
function resolveLocale(locale) {
|
|
247
1517
|
if (locale && catalogs[locale]) return locale;
|
|
248
1518
|
return "es-AR";
|
|
@@ -332,85 +1602,499 @@ var TickeanElementBase = class extends NativeHTMLElement {
|
|
|
332
1602
|
}
|
|
333
1603
|
};
|
|
334
1604
|
|
|
335
|
-
// src/
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if (!state || state.loading) {
|
|
340
|
-
return `<div class="wrap muted">${t(this.elementLocale, "loading")}</div>`;
|
|
341
|
-
}
|
|
342
|
-
const options = (state.event?.shows || []).flatMap(
|
|
343
|
-
(show) => (show.showOptions || []).map((opt) => ({ ...opt, showTitle: show.title }))
|
|
344
|
-
);
|
|
345
|
-
return `
|
|
346
|
-
<div class="wrap stack" part="ticket-selector">
|
|
347
|
-
<strong>${t(this.elementLocale, "tickets")}</strong>
|
|
348
|
-
${options.map((opt) => {
|
|
349
|
-
const qty = state.cart.find((c) => c.showOptionId === opt.id)?.amount || 0;
|
|
350
|
-
return `
|
|
351
|
-
<div class="ticket" data-option-id="${opt.id}">
|
|
352
|
-
<div class="row">
|
|
353
|
-
<div>
|
|
354
|
-
<div><strong>${escapeHtml(opt.name || opt.id)}</strong></div>
|
|
355
|
-
<div class="muted">${escapeHtml(opt.showTitle || "")}</div>
|
|
356
|
-
</div>
|
|
357
|
-
<div>${this.money(Number(opt.price || 0))}</div>
|
|
358
|
-
</div>
|
|
359
|
-
<div class="row qty" style="margin-top:10px" role="group" aria-label="${t(this.elementLocale, "quantity")} ${escapeHtml(opt.name || "")}">
|
|
360
|
-
<button type="button" class="secondary" data-action="dec" aria-label="-">\u2212</button>
|
|
361
|
-
<span aria-live="polite">${qty}</span>
|
|
362
|
-
<button type="button" class="secondary" data-action="inc" aria-label="+">+</button>
|
|
363
|
-
</div>
|
|
364
|
-
</div>
|
|
365
|
-
`;
|
|
366
|
-
}).join("")}
|
|
367
|
-
</div>
|
|
368
|
-
`;
|
|
1605
|
+
// src/event-meta.ts
|
|
1606
|
+
function parseInstant(input) {
|
|
1607
|
+
if (input instanceof Date) {
|
|
1608
|
+
return Number.isNaN(input.getTime()) ? null : input;
|
|
369
1609
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
1610
|
+
const value = typeof input === "string" ? input.trim() : "";
|
|
1611
|
+
if (!value) return null;
|
|
1612
|
+
const parsed = new Date(value);
|
|
1613
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
1614
|
+
}
|
|
1615
|
+
function isValidTimeZone(tz) {
|
|
1616
|
+
if (!tz?.trim()) return false;
|
|
1617
|
+
try {
|
|
1618
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz }).format();
|
|
1619
|
+
return true;
|
|
1620
|
+
} catch {
|
|
1621
|
+
return false;
|
|
381
1622
|
}
|
|
382
|
-
};
|
|
383
|
-
function escapeHtml(value) {
|
|
384
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
385
1623
|
}
|
|
386
|
-
|
|
1624
|
+
function resolveEventTimeZone(location) {
|
|
1625
|
+
if (isValidTimeZone(location?.timeZone)) return location.timeZone.trim();
|
|
1626
|
+
const country = (location?.country || "").toLowerCase();
|
|
1627
|
+
if (country.includes("chile") || country === "cl") return "America/Santiago";
|
|
1628
|
+
if (country.includes("m\xE9xico") || country.includes("mexico") || country === "mx") {
|
|
1629
|
+
return "America/Mexico_City";
|
|
1630
|
+
}
|
|
1631
|
+
if (country.includes("united states") || country === "us") return "America/New_York";
|
|
1632
|
+
return "America/Argentina/Buenos_Aires";
|
|
1633
|
+
}
|
|
1634
|
+
function formatInZone(input, timeZone, locale, options) {
|
|
1635
|
+
const instant = parseInstant(input);
|
|
1636
|
+
if (!instant) return "";
|
|
1637
|
+
const tz = isValidTimeZone(timeZone) ? timeZone : "America/Argentina/Buenos_Aires";
|
|
1638
|
+
return new Intl.DateTimeFormat(locale, { ...options, timeZone: tz }).format(instant);
|
|
1639
|
+
}
|
|
1640
|
+
function formatEventDate(input, timeZone, locale = "es-AR", options = {
|
|
1641
|
+
weekday: "long",
|
|
1642
|
+
day: "numeric",
|
|
1643
|
+
month: "long",
|
|
1644
|
+
year: "numeric"
|
|
1645
|
+
}) {
|
|
1646
|
+
return formatInZone(input, timeZone, locale, options);
|
|
1647
|
+
}
|
|
1648
|
+
function formatEventTime(input, timeZone, locale = "es-AR") {
|
|
1649
|
+
return formatInZone(input, timeZone, locale, {
|
|
1650
|
+
hour: "2-digit",
|
|
1651
|
+
minute: "2-digit",
|
|
1652
|
+
hour12: false
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
function getCalendarDateParts(input, timeZone) {
|
|
1656
|
+
const instant = parseInstant(input);
|
|
1657
|
+
if (!instant) return null;
|
|
1658
|
+
const tz = isValidTimeZone(timeZone) ? timeZone : "America/Argentina/Buenos_Aires";
|
|
1659
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
1660
|
+
timeZone: tz,
|
|
1661
|
+
year: "numeric",
|
|
1662
|
+
month: "2-digit",
|
|
1663
|
+
day: "2-digit"
|
|
1664
|
+
}).formatToParts(instant);
|
|
1665
|
+
const values = Object.fromEntries(parts.map((p) => [p.type, p.value]));
|
|
1666
|
+
const year = Number(values.year);
|
|
1667
|
+
const month = Number(values.month);
|
|
1668
|
+
const day = Number(values.day);
|
|
1669
|
+
if (![year, month, day].every(Number.isInteger)) return null;
|
|
1670
|
+
const key = `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
1671
|
+
return { key, year, month, day };
|
|
1672
|
+
}
|
|
1673
|
+
function capitalize(label) {
|
|
1674
|
+
if (!label) return label;
|
|
1675
|
+
return label.charAt(0).toUpperCase() + label.slice(1);
|
|
1676
|
+
}
|
|
1677
|
+
function andWord(locale) {
|
|
1678
|
+
return locale.toLowerCase().startsWith("en") ? "and" : "y";
|
|
1679
|
+
}
|
|
1680
|
+
function joinWithAnd(items, word) {
|
|
1681
|
+
if (items.length <= 1) return items[0] || "";
|
|
1682
|
+
if (items.length === 2) return `${items[0]} ${word} ${items[1]}`;
|
|
1683
|
+
return `${items.slice(0, -1).join(", ")} ${word} ${items[items.length - 1]}`;
|
|
1684
|
+
}
|
|
1685
|
+
function monthName(year, month, locale) {
|
|
1686
|
+
const probe = new Date(Date.UTC(year, month - 1, 1, 12, 0, 0));
|
|
1687
|
+
return capitalize(
|
|
1688
|
+
new Intl.DateTimeFormat(locale, { month: "long", timeZone: "UTC" }).format(probe)
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
function formatMultiDayShowDates(shows, timeZone, locale = "es-AR") {
|
|
1692
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1693
|
+
for (const show of shows ?? []) {
|
|
1694
|
+
const parts = getCalendarDateParts(show.date, timeZone);
|
|
1695
|
+
if (!parts || byKey.has(parts.key)) continue;
|
|
1696
|
+
byKey.set(parts.key, parts);
|
|
1697
|
+
}
|
|
1698
|
+
const days = [...byKey.values()].sort((a, b) => a.key.localeCompare(b.key));
|
|
1699
|
+
if (days.length <= 1) return null;
|
|
1700
|
+
const word = andWord(locale);
|
|
1701
|
+
const isEs = word === "y";
|
|
1702
|
+
const sameMonthYear = days.every(
|
|
1703
|
+
(d) => d.month === days[0].month && d.year === days[0].year
|
|
1704
|
+
);
|
|
1705
|
+
if (sameMonthYear) {
|
|
1706
|
+
const dayList = joinWithAnd(
|
|
1707
|
+
days.map((d) => String(d.day)),
|
|
1708
|
+
word
|
|
1709
|
+
);
|
|
1710
|
+
const month = monthName(days[0].year, days[0].month, locale);
|
|
1711
|
+
return isEs ? `${dayList} de ${month} ${days[0].year}` : `${dayList} ${month} ${days[0].year}`;
|
|
1712
|
+
}
|
|
1713
|
+
return joinWithAnd(
|
|
1714
|
+
days.map(
|
|
1715
|
+
(d) => formatEventDate(
|
|
1716
|
+
`${d.key}T12:00:00Z`,
|
|
1717
|
+
"UTC",
|
|
1718
|
+
locale,
|
|
1719
|
+
{ day: "numeric", month: "short" }
|
|
1720
|
+
)
|
|
1721
|
+
),
|
|
1722
|
+
word
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
function formatShowTimeRange(show, timeZone, locale = "es-AR") {
|
|
1726
|
+
const start = formatEventTime(show?.date, timeZone, locale);
|
|
1727
|
+
if (!start) return "";
|
|
1728
|
+
const end = formatEventTime(show?.endDate, timeZone, locale);
|
|
1729
|
+
if (end && end !== start) return `${start}\u2013${end}`;
|
|
1730
|
+
return start;
|
|
1731
|
+
}
|
|
1732
|
+
function formatShowDayScheduleLabel(show, timeZone, locale = "es-AR", options) {
|
|
1733
|
+
const datePart = formatEventDate(show.date, timeZone, locale, {
|
|
1734
|
+
weekday: "short",
|
|
1735
|
+
day: "numeric",
|
|
1736
|
+
month: "short"
|
|
1737
|
+
});
|
|
1738
|
+
const timePart = formatShowTimeRange(show, timeZone, locale);
|
|
1739
|
+
const parts = [];
|
|
1740
|
+
if (options?.includeTitle && show.title?.trim()) parts.push(show.title.trim());
|
|
1741
|
+
if (datePart) parts.push(datePart);
|
|
1742
|
+
if (timePart) parts.push(timePart);
|
|
1743
|
+
return parts.join(" \xB7 ");
|
|
1744
|
+
}
|
|
1745
|
+
function getEventFlyerUrl(images) {
|
|
1746
|
+
const list = images || [];
|
|
1747
|
+
if (!list.length) return null;
|
|
1748
|
+
const main = list.find((img) => img.kind === "main") || list[0];
|
|
1749
|
+
return main?.mobileUrl || main?.desktopUrl || main?.url || null;
|
|
1750
|
+
}
|
|
1751
|
+
function buildEventScheduleSummary(shows, timeZone, locale = "es-AR") {
|
|
1752
|
+
const list = [...shows || []].sort((a, b) => {
|
|
1753
|
+
const da = parseInstant(a.date)?.getTime() ?? 0;
|
|
1754
|
+
const db = parseInstant(b.date)?.getTime() ?? 0;
|
|
1755
|
+
return da - db;
|
|
1756
|
+
});
|
|
1757
|
+
const multi = formatMultiDayShowDates(list, timeZone, locale);
|
|
1758
|
+
const earliest = list[0];
|
|
1759
|
+
const dateLabel = multi || formatEventDate(earliest?.date, timeZone, locale, {
|
|
1760
|
+
weekday: "long",
|
|
1761
|
+
day: "numeric",
|
|
1762
|
+
month: "long",
|
|
1763
|
+
year: "numeric"
|
|
1764
|
+
}) || "";
|
|
1765
|
+
const showLines = list.map(
|
|
1766
|
+
(show) => formatShowDayScheduleLabel(show, timeZone, locale, {
|
|
1767
|
+
includeTitle: list.length > 1
|
|
1768
|
+
})
|
|
1769
|
+
);
|
|
1770
|
+
if (!multi && list.length === 1) {
|
|
1771
|
+
const time = formatShowTimeRange(list[0], timeZone, locale);
|
|
1772
|
+
return {
|
|
1773
|
+
dateLabel: time ? `${dateLabel} \xB7 ${time}` : dateLabel,
|
|
1774
|
+
showLines: []
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
return { dateLabel, showLines };
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
// src/elements/ticket-selector.ts
|
|
1781
|
+
function isAbono(opt) {
|
|
1782
|
+
return Boolean(opt.accessScope && opt.accessScope !== "SINGLE_SHOW");
|
|
1783
|
+
}
|
|
1784
|
+
function availableStock(opt) {
|
|
1785
|
+
if (typeof opt.availableStock === "number") return Math.max(0, opt.availableStock);
|
|
1786
|
+
const stock = Number(opt.stock ?? 0);
|
|
1787
|
+
const sold = Number(opt.sold ?? 0);
|
|
1788
|
+
const reserved = Number(opt.reserved ?? 0);
|
|
1789
|
+
return Math.max(0, stock - sold - reserved);
|
|
1790
|
+
}
|
|
1791
|
+
function isSoldOut(opt) {
|
|
1792
|
+
if (opt.status === "SOLD_OUT" || opt.status === "INACTIVE") return true;
|
|
1793
|
+
return availableStock(opt) <= 0;
|
|
1794
|
+
}
|
|
1795
|
+
function isComingSoon(opt) {
|
|
1796
|
+
return opt.status === "COMING_SOON";
|
|
1797
|
+
}
|
|
1798
|
+
function activePromotion(opt) {
|
|
1799
|
+
const nxm = opt.promotionNxM;
|
|
1800
|
+
if (nxm?.enabled && nxm.buyQty && nxm.payQty) {
|
|
1801
|
+
return { kind: "nxm", data: nxm };
|
|
1802
|
+
}
|
|
1803
|
+
const qty = opt.quantityDiscount;
|
|
1804
|
+
if (qty?.enabled && typeof qty.percentOff === "number") {
|
|
1805
|
+
return { kind: "qty", data: qty };
|
|
1806
|
+
}
|
|
1807
|
+
return null;
|
|
1808
|
+
}
|
|
1809
|
+
function promoLegend(opt, locale) {
|
|
1810
|
+
const promo = activePromotion(opt);
|
|
1811
|
+
if (!promo) return null;
|
|
1812
|
+
if (promo.kind === "nxm") {
|
|
1813
|
+
const label2 = promo.data.label || "Promo";
|
|
1814
|
+
return tFormat(locale, "promoNxM", {
|
|
1815
|
+
label: label2,
|
|
1816
|
+
buy: promo.data.buyQty || 0,
|
|
1817
|
+
pay: promo.data.payQty || 0
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1820
|
+
const label = promo.data.label || "Promo";
|
|
1821
|
+
const unit = typeof promo.data.targetUnit === "number" && promo.data.targetUnit >= 2 ? promo.data.targetUnit : 2;
|
|
1822
|
+
return tFormat(locale, "promoNth", {
|
|
1823
|
+
label,
|
|
1824
|
+
unit,
|
|
1825
|
+
percent: promo.data.percentOff || 0
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
function promoBadge(opt) {
|
|
1829
|
+
const promo = activePromotion(opt);
|
|
1830
|
+
if (!promo) return null;
|
|
1831
|
+
if (promo.kind === "nxm") {
|
|
1832
|
+
return promo.data.label || `${promo.data.buyQty}x${promo.data.payQty}`;
|
|
1833
|
+
}
|
|
1834
|
+
return promo.data.label || `-${promo.data.percentOff}%`;
|
|
1835
|
+
}
|
|
1836
|
+
function passSubtitle(opt, showCount, locale) {
|
|
1837
|
+
if (!isAbono(opt)) return "";
|
|
1838
|
+
const covered = opt.accessScope === "ALL_EVENT_SHOWS" ? showCount : opt.coveredShowIds?.length || showCount;
|
|
1839
|
+
const coverage = covered <= 1 ? "1 d\xEDa" : locale.startsWith("en") ? `${covered} days` : `${covered} d\xEDas`;
|
|
1840
|
+
const issuance = opt.passIssuanceMode === "SINGLE_PASS" ? locale.startsWith("en") ? "1 QR for all days" : "1 QR para todos los d\xEDas" : locale.startsWith("en") ? "1 ticket per day" : "1 entrada por d\xEDa";
|
|
1841
|
+
return `${locale.startsWith("en") ? "Pass" : "Pase"} \xB7 ${coverage} \xB7 ${issuance}`;
|
|
1842
|
+
}
|
|
1843
|
+
function maxQty(opt) {
|
|
1844
|
+
const stock = availableStock(opt);
|
|
1845
|
+
const cap = opt.maxPerPurchase && opt.maxPerPurchase > 0 ? opt.maxPerPurchase : stock;
|
|
1846
|
+
return Math.max(0, Math.min(stock, cap || stock));
|
|
1847
|
+
}
|
|
1848
|
+
var TickeanTicketSelector = class extends TickeanElementBase {
|
|
1849
|
+
constructor() {
|
|
1850
|
+
super(...arguments);
|
|
1851
|
+
this.firstPaint = true;
|
|
1852
|
+
}
|
|
1853
|
+
renderBody() {
|
|
1854
|
+
const state = this.state;
|
|
1855
|
+
if (!state || state.loading) {
|
|
1856
|
+
return `<div class="wrap muted">${t(this.elementLocale, "loading")}</div>`;
|
|
1857
|
+
}
|
|
1858
|
+
const shows = state.event?.shows || [];
|
|
1859
|
+
const showCount = shows.length;
|
|
1860
|
+
const locale = this.elementLocale;
|
|
1861
|
+
const timeZone = resolveEventTimeZone(
|
|
1862
|
+
state.event?.location || {}
|
|
1863
|
+
);
|
|
1864
|
+
const allOptions = shows.flatMap(
|
|
1865
|
+
(show) => (show.showOptions || []).map((opt) => ({
|
|
1866
|
+
...opt,
|
|
1867
|
+
showId: show.id,
|
|
1868
|
+
showTitle: show.title,
|
|
1869
|
+
showDate: show.date,
|
|
1870
|
+
showEndDate: show.endDate
|
|
1871
|
+
}))
|
|
1872
|
+
);
|
|
1873
|
+
const abonoMap = /* @__PURE__ */ new Map();
|
|
1874
|
+
for (const opt of allOptions) {
|
|
1875
|
+
if (isAbono(opt) && !abonoMap.has(opt.id)) abonoMap.set(opt.id, opt);
|
|
1876
|
+
}
|
|
1877
|
+
const abonos = [...abonoMap.values()];
|
|
1878
|
+
const dayOptions = allOptions.filter((opt) => !isAbono(opt));
|
|
1879
|
+
const days = /* @__PURE__ */ new Map();
|
|
1880
|
+
for (const opt of dayOptions) {
|
|
1881
|
+
const key = opt.showId;
|
|
1882
|
+
if (!days.has(key)) days.set(key, []);
|
|
1883
|
+
days.get(key).push(opt);
|
|
1884
|
+
}
|
|
1885
|
+
const renderOption = (opt, pass = false) => {
|
|
1886
|
+
const qty = state.cart.find((c) => c.showOptionId === opt.id)?.amount || 0;
|
|
1887
|
+
const soldOut = isSoldOut(opt);
|
|
1888
|
+
const comingSoon = isComingSoon(opt);
|
|
1889
|
+
const remaining = availableStock(opt);
|
|
1890
|
+
const lowStock = !soldOut && !comingSoon && remaining > 0 && remaining <= 10;
|
|
1891
|
+
const max = maxQty(opt);
|
|
1892
|
+
const subtitle = pass ? passSubtitle(opt, showCount, locale) : formatShowTimeRange(
|
|
1893
|
+
{ date: opt.showDate, endDate: opt.showEndDate },
|
|
1894
|
+
timeZone,
|
|
1895
|
+
locale
|
|
1896
|
+
) || opt.showTitle || "";
|
|
1897
|
+
const legend = promoLegend(opt, locale);
|
|
1898
|
+
const badge = promoBadge(opt);
|
|
1899
|
+
const gated = opt.catalogVisibility === "PROMO_GATED";
|
|
1900
|
+
let stockBadge = "";
|
|
1901
|
+
if (soldOut) {
|
|
1902
|
+
stockBadge = `<span class="stock-badge sold-out">${escapeHtml(t(locale, "soldOut"))}</span>`;
|
|
1903
|
+
} else if (comingSoon) {
|
|
1904
|
+
stockBadge = `<span class="stock-badge coming-soon">${escapeHtml(t(locale, "comingSoon"))}</span>`;
|
|
1905
|
+
} else if (lowStock) {
|
|
1906
|
+
stockBadge = remaining === 1 ? `<span class="stock-badge low-stock">${escapeHtml(t(locale, "lastTicket"))}</span>` : `<span class="stock-badge low-stock">${escapeHtml(tFormat(locale, "lastTickets", { count: remaining }))}</span>`;
|
|
1907
|
+
}
|
|
1908
|
+
return `
|
|
1909
|
+
<div class="ticket ${pass ? "pass" : ""} ${soldOut ? "sold-out" : ""} ${comingSoon ? "coming-soon" : ""}"
|
|
1910
|
+
data-option-id="${opt.id}" data-selected="${qty > 0}" data-sold-out="${soldOut}" data-max="${max}">
|
|
1911
|
+
<div class="row">
|
|
1912
|
+
<div style="min-width:0">
|
|
1913
|
+
<div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
|
|
1914
|
+
<strong>${escapeHtml(opt.name || opt.id)}</strong>
|
|
1915
|
+
${pass ? `<span class="pass-badge">${locale.startsWith("en") ? "Pass" : "Pase"}</span>` : ""}
|
|
1916
|
+
${gated ? `<span class="pass-badge" style="color:var(--tickean-primary);background:color-mix(in srgb,var(--tickean-primary) 12%,transparent)">${escapeHtml(t(locale, "unlocked"))}</span>` : ""}
|
|
1917
|
+
${badge ? `<span class="promo-badge">${escapeHtml(badge)}</span>` : ""}
|
|
1918
|
+
${stockBadge}
|
|
1919
|
+
</div>
|
|
1920
|
+
<div class="muted" style="font-size:0.78rem;margin-top:2px">${escapeHtml(subtitle || "")}</div>
|
|
1921
|
+
${legend ? `<div class="promo-legend">${escapeHtml(legend)}</div>` : ""}
|
|
1922
|
+
</div>
|
|
1923
|
+
<div style="font-weight:650;font-variant-numeric:tabular-nums;white-space:nowrap">${this.money(Number(opt.price || 0))}</div>
|
|
1924
|
+
</div>
|
|
1925
|
+
<div class="row" style="margin-top:8px">
|
|
1926
|
+
<span class="muted" style="font-size:0.78rem">${soldOut ? escapeHtml(t(locale, "soldOut")) : t(locale, "quantity")}</span>
|
|
1927
|
+
${soldOut || comingSoon ? `<span class="muted" style="font-size:0.78rem">\u2014</span>` : `<div class="qty" role="group" aria-label="${t(locale, "quantity")} ${escapeHtml(opt.name || "")}">
|
|
1928
|
+
<button type="button" class="secondary" data-action="dec" aria-label="-" ${qty <= 0 ? "disabled" : ""}>\u2212</button>
|
|
1929
|
+
<span data-qty aria-live="polite">${qty}</span>
|
|
1930
|
+
<button type="button" class="secondary" data-action="inc" aria-label="+" ${qty >= max ? "disabled" : ""}>+</button>
|
|
1931
|
+
</div>`}
|
|
1932
|
+
</div>
|
|
1933
|
+
</div>
|
|
1934
|
+
`;
|
|
1935
|
+
};
|
|
1936
|
+
const dayBlocks = [...days.entries()].map(([showId, opts]) => {
|
|
1937
|
+
const show = shows.find((s) => s.id === showId);
|
|
1938
|
+
const schedule = show ? formatShowDayScheduleLabel(
|
|
1939
|
+
{
|
|
1940
|
+
date: show.date,
|
|
1941
|
+
endDate: show.endDate,
|
|
1942
|
+
title: show.title
|
|
1943
|
+
},
|
|
1944
|
+
timeZone,
|
|
1945
|
+
locale,
|
|
1946
|
+
{ includeTitle: Boolean(show.title) }
|
|
1947
|
+
) : "";
|
|
1948
|
+
const label = schedule || show?.title || t(locale, "tickets");
|
|
1949
|
+
return `
|
|
1950
|
+
<div class="stack" style="gap:8px">
|
|
1951
|
+
<p class="group-title">${escapeHtml(label)}</p>
|
|
1952
|
+
${opts.map((o) => renderOption(o, false)).join("")}
|
|
1953
|
+
</div>
|
|
1954
|
+
`;
|
|
1955
|
+
}).join("");
|
|
1956
|
+
return `
|
|
1957
|
+
<div class="stack" part="ticket-selector" style="gap:8px">
|
|
1958
|
+
<p class="section-title">${t(locale, "tickets")}</p>
|
|
1959
|
+
${abonos.length ? `<div class="stack" style="gap:8px">
|
|
1960
|
+
<p class="group-title" style="color:var(--tickean-pass)">${escapeHtml(t(locale, "passes"))}</p>
|
|
1961
|
+
${abonos.map((o) => renderOption(o, true)).join("")}
|
|
1962
|
+
</div>` : ""}
|
|
1963
|
+
${days.size > 1 ? dayBlocks : dayOptions.map((o) => renderOption(o, false)).join("")}
|
|
1964
|
+
</div>
|
|
1965
|
+
`;
|
|
1966
|
+
}
|
|
1967
|
+
afterRender() {
|
|
1968
|
+
if (this.firstPaint) {
|
|
1969
|
+
this.firstPaint = false;
|
|
1970
|
+
this.setAttribute("data-animated", "");
|
|
1971
|
+
}
|
|
1972
|
+
this.root.querySelectorAll("[data-option-id]").forEach((el) => {
|
|
1973
|
+
if (el.dataset.soldOut === "true") return;
|
|
1974
|
+
const optionId = el.getAttribute("data-option-id");
|
|
1975
|
+
const max = Number(el.dataset.max || 99);
|
|
1976
|
+
const qty = this.state?.cart.find((c) => c.showOptionId === optionId)?.amount || 0;
|
|
1977
|
+
el.querySelector('[data-action="dec"]')?.addEventListener("click", () => {
|
|
1978
|
+
this.controller?.setCartItem(optionId, Math.max(0, qty - 1));
|
|
1979
|
+
});
|
|
1980
|
+
el.querySelector('[data-action="inc"]')?.addEventListener("click", () => {
|
|
1981
|
+
this.controller?.setCartItem(optionId, Math.min(max, qty + 1));
|
|
1982
|
+
});
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1986
|
+
function escapeHtml(value) {
|
|
1987
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1988
|
+
}
|
|
1989
|
+
if (typeof customElements !== "undefined" && !customElements.get("tickean-ticket-selector")) {
|
|
387
1990
|
customElements.define("tickean-ticket-selector", TickeanTicketSelector);
|
|
388
1991
|
}
|
|
389
1992
|
|
|
390
1993
|
// src/elements/discount.ts
|
|
1994
|
+
function countUnlockedOptions(state) {
|
|
1995
|
+
if (!state?.event?.shows) return 0;
|
|
1996
|
+
let n = 0;
|
|
1997
|
+
for (const show of state.event.shows) {
|
|
1998
|
+
for (const opt of show.showOptions || []) {
|
|
1999
|
+
if (opt.catalogVisibility === "PROMO_GATED") n += 1;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
return n;
|
|
2003
|
+
}
|
|
2004
|
+
function shouldShowDiscountCode(params) {
|
|
2005
|
+
if (params.discountCode) return true;
|
|
2006
|
+
return params.discountsEnabled !== false;
|
|
2007
|
+
}
|
|
391
2008
|
var TickeanDiscount = class extends TickeanElementBase {
|
|
2009
|
+
constructor() {
|
|
2010
|
+
super(...arguments);
|
|
2011
|
+
this.lastSuccess = null;
|
|
2012
|
+
}
|
|
2013
|
+
static get observedAttributes() {
|
|
2014
|
+
return [...TickeanElementBase.observedAttributes, "mode"];
|
|
2015
|
+
}
|
|
2016
|
+
mode() {
|
|
2017
|
+
return this.getAttribute("mode") === "unlock" ? "unlock" : "discount";
|
|
2018
|
+
}
|
|
392
2019
|
renderBody() {
|
|
393
|
-
const
|
|
2020
|
+
const state = this.state;
|
|
2021
|
+
if (!state) return "";
|
|
2022
|
+
const mode = this.mode();
|
|
2023
|
+
const unlockedCount = countUnlockedOptions(state);
|
|
2024
|
+
const discountsEnabled = state.session?.capabilities?.discounts;
|
|
2025
|
+
const visible = mode === "unlock" ? Boolean(state.event?.hasPromoGatedShowOptions) || unlockedCount > 0 : shouldShowDiscountCode({
|
|
2026
|
+
discountsEnabled,
|
|
2027
|
+
discountCode: state.discountCode
|
|
2028
|
+
});
|
|
2029
|
+
if (!visible) return "";
|
|
2030
|
+
const code = state.discountCode || "";
|
|
2031
|
+
const locale = this.elementLocale;
|
|
2032
|
+
const quoting = Boolean(state.isQuoting);
|
|
2033
|
+
const error = state.error?.code === "discount_invalid" ? state.error.message : null;
|
|
2034
|
+
const title = t(locale, "discountToggle");
|
|
2035
|
+
const hint = mode === "unlock" ? t(locale, "promoUnlockHint") : t(locale, "discountCodeHint");
|
|
394
2036
|
return `
|
|
395
|
-
<div class="
|
|
396
|
-
<
|
|
397
|
-
|
|
398
|
-
<
|
|
399
|
-
</
|
|
400
|
-
<
|
|
401
|
-
|
|
2037
|
+
<div class="promo-unlock" part="discount" data-mode="${mode}">
|
|
2038
|
+
<div class="promo-unlock-copy">
|
|
2039
|
+
<p class="promo-unlock-title">${escapeHtml2(title)}</p>
|
|
2040
|
+
<p class="promo-unlock-hint muted">${escapeHtml2(hint)}</p>
|
|
2041
|
+
</div>
|
|
2042
|
+
<div class="promo-unlock-row">
|
|
2043
|
+
<input
|
|
2044
|
+
id="code"
|
|
2045
|
+
value="${escapeAttr(code)}"
|
|
2046
|
+
autocomplete="off"
|
|
2047
|
+
spellcheck="false"
|
|
2048
|
+
placeholder="${escapeAttr(t(locale, "promoUnlockPlaceholder"))}"
|
|
2049
|
+
aria-label="${escapeAttr(t(locale, "discount"))}"
|
|
2050
|
+
${quoting ? "disabled" : ""}
|
|
2051
|
+
/>
|
|
2052
|
+
<button type="button" class="secondary" id="apply" ${quoting ? "disabled" : ""}>
|
|
2053
|
+
${quoting ? escapeHtml2(t(locale, "verifying")) : escapeHtml2(t(locale, "apply"))}
|
|
2054
|
+
</button>
|
|
2055
|
+
</div>
|
|
2056
|
+
${error ? `<p class="promo-unlock-msg danger" role="alert">${escapeHtml2(error)}</p>` : this.lastSuccess ? `<p class="promo-unlock-msg success" role="status">${escapeHtml2(this.lastSuccess)}</p>` : ""}
|
|
402
2057
|
</div>
|
|
403
2058
|
`;
|
|
404
2059
|
}
|
|
405
2060
|
afterRender() {
|
|
406
2061
|
const input = this.root.querySelector("#code");
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
2062
|
+
input?.addEventListener("input", () => {
|
|
2063
|
+
if (input) input.value = input.value.toUpperCase();
|
|
2064
|
+
});
|
|
2065
|
+
input?.addEventListener("keydown", (ev) => {
|
|
2066
|
+
if (ev.key === "Enter") {
|
|
2067
|
+
ev.preventDefault();
|
|
2068
|
+
void this.applyCode();
|
|
412
2069
|
}
|
|
413
2070
|
});
|
|
2071
|
+
this.root.querySelector("#apply")?.addEventListener("click", () => {
|
|
2072
|
+
void this.applyCode();
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
async applyCode() {
|
|
2076
|
+
const input = this.root.querySelector("#code");
|
|
2077
|
+
const value = input?.value?.trim() || "";
|
|
2078
|
+
if (!value) return;
|
|
2079
|
+
this.lastSuccess = null;
|
|
2080
|
+
try {
|
|
2081
|
+
const quote = await this.controller?.applyDiscountCode(value);
|
|
2082
|
+
const unlocked = quote?.unlockedShowOptions || [];
|
|
2083
|
+
const names = unlocked.map((o) => o.name).filter((n) => Boolean(n));
|
|
2084
|
+
const locale = this.elementLocale;
|
|
2085
|
+
if (names.length > 0) {
|
|
2086
|
+
this.lastSuccess = tFormat(locale, "promoUnlockSuccessNamed", {
|
|
2087
|
+
names: names.join(", ")
|
|
2088
|
+
});
|
|
2089
|
+
} else if ((quote?.unlockedShowOptionIds || []).length > 0 || unlocked.length > 0) {
|
|
2090
|
+
this.lastSuccess = t(locale, "promoUnlockSuccess");
|
|
2091
|
+
} else {
|
|
2092
|
+
this.lastSuccess = t(locale, "promoUnlockApplied");
|
|
2093
|
+
}
|
|
2094
|
+
this.render();
|
|
2095
|
+
} catch {
|
|
2096
|
+
this.lastSuccess = null;
|
|
2097
|
+
}
|
|
414
2098
|
}
|
|
415
2099
|
};
|
|
416
2100
|
function escapeAttr(value) {
|
|
@@ -423,65 +2107,360 @@ if (typeof customElements !== "undefined" && !customElements.get("tickean-discou
|
|
|
423
2107
|
customElements.define("tickean-discount", TickeanDiscount);
|
|
424
2108
|
}
|
|
425
2109
|
|
|
2110
|
+
// src/phone-countries.ts
|
|
2111
|
+
var PHONE_COUNTRIES = [
|
|
2112
|
+
{ iso: "AR", name: "Argentina", dial: "54", flag: "\u{1F1E6}\u{1F1F7}" },
|
|
2113
|
+
{ iso: "CL", name: "Chile", dial: "56", flag: "\u{1F1E8}\u{1F1F1}" },
|
|
2114
|
+
{ iso: "UY", name: "Uruguay", dial: "598", flag: "\u{1F1FA}\u{1F1FE}" },
|
|
2115
|
+
{ iso: "PY", name: "Paraguay", dial: "595", flag: "\u{1F1F5}\u{1F1FE}" },
|
|
2116
|
+
{ iso: "BO", name: "Bolivia", dial: "591", flag: "\u{1F1E7}\u{1F1F4}" },
|
|
2117
|
+
{ iso: "BR", name: "Brasil", dial: "55", flag: "\u{1F1E7}\u{1F1F7}" },
|
|
2118
|
+
{ iso: "PE", name: "Per\xFA", dial: "51", flag: "\u{1F1F5}\u{1F1EA}" },
|
|
2119
|
+
{ iso: "CO", name: "Colombia", dial: "57", flag: "\u{1F1E8}\u{1F1F4}" },
|
|
2120
|
+
{ iso: "EC", name: "Ecuador", dial: "593", flag: "\u{1F1EA}\u{1F1E8}" },
|
|
2121
|
+
{ iso: "MX", name: "M\xE9xico", dial: "52", flag: "\u{1F1F2}\u{1F1FD}" },
|
|
2122
|
+
{ iso: "US", name: "United States", dial: "1", flag: "\u{1F1FA}\u{1F1F8}" },
|
|
2123
|
+
{ iso: "ES", name: "Espa\xF1a", dial: "34", flag: "\u{1F1EA}\u{1F1F8}" },
|
|
2124
|
+
{ iso: "GB", name: "United Kingdom", dial: "44", flag: "\u{1F1EC}\u{1F1E7}" }
|
|
2125
|
+
];
|
|
2126
|
+
var byIso = new Map(PHONE_COUNTRIES.map((c) => [c.iso, c]));
|
|
2127
|
+
function countryFromIso(iso) {
|
|
2128
|
+
const key = (iso || "").toUpperCase();
|
|
2129
|
+
return byIso.get(key) || PHONE_COUNTRIES[0];
|
|
2130
|
+
}
|
|
2131
|
+
function countryFromLocale(locale) {
|
|
2132
|
+
const l = (locale || "").toLowerCase();
|
|
2133
|
+
if (l.startsWith("es-cl")) return countryFromIso("CL");
|
|
2134
|
+
if (l.startsWith("es-mx")) return countryFromIso("MX");
|
|
2135
|
+
if (l.startsWith("pt") || l.startsWith("pt-br")) return countryFromIso("BR");
|
|
2136
|
+
if (l.startsWith("en-gb")) return countryFromIso("GB");
|
|
2137
|
+
if (l.startsWith("en")) return countryFromIso("US");
|
|
2138
|
+
if (l.startsWith("es")) return countryFromIso("AR");
|
|
2139
|
+
return PHONE_COUNTRIES[0];
|
|
2140
|
+
}
|
|
2141
|
+
function toE164(dial, national) {
|
|
2142
|
+
const digits = national.replace(/\D/g, "").replace(/^0+/, "");
|
|
2143
|
+
const d = dial.replace(/\D/g, "");
|
|
2144
|
+
return `+${d}${digits}`;
|
|
2145
|
+
}
|
|
2146
|
+
function nationalPlaceholder(iso) {
|
|
2147
|
+
switch (iso) {
|
|
2148
|
+
case "AR":
|
|
2149
|
+
return "11 2345 6789";
|
|
2150
|
+
case "CL":
|
|
2151
|
+
return "9 1234 5678";
|
|
2152
|
+
case "MX":
|
|
2153
|
+
return "55 1234 5678";
|
|
2154
|
+
case "US":
|
|
2155
|
+
return "(555) 123-4567";
|
|
2156
|
+
default:
|
|
2157
|
+
return "Phone number";
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
async function detectCountryIso(apiBaseUrl) {
|
|
2161
|
+
const base = (apiBaseUrl || "https://api.tickean.com").replace(/\/$/, "");
|
|
2162
|
+
try {
|
|
2163
|
+
const res = await fetch(`${base}/bff/marketplace/location/ip`, {
|
|
2164
|
+
credentials: "omit"
|
|
2165
|
+
});
|
|
2166
|
+
if (!res.ok) return null;
|
|
2167
|
+
const json = await res.json();
|
|
2168
|
+
if (json?.available && json.countryCode) {
|
|
2169
|
+
return String(json.countryCode).toUpperCase();
|
|
2170
|
+
}
|
|
2171
|
+
} catch {
|
|
2172
|
+
}
|
|
2173
|
+
return null;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
426
2176
|
// src/elements/buyer-verification.ts
|
|
427
2177
|
var TickeanBuyerVerification = class extends TickeanElementBase {
|
|
428
2178
|
constructor() {
|
|
429
2179
|
super(...arguments);
|
|
430
|
-
this.
|
|
2180
|
+
this.country = countryFromLocale("es-AR");
|
|
2181
|
+
this.national = "";
|
|
431
2182
|
this.name = "";
|
|
432
2183
|
this.email = "";
|
|
433
2184
|
this.code = "";
|
|
2185
|
+
this.geoResolved = false;
|
|
2186
|
+
this.countryTouched = false;
|
|
2187
|
+
this.step = "phone";
|
|
2188
|
+
this.knownBuyer = null;
|
|
2189
|
+
this.busy = null;
|
|
2190
|
+
this.firstPaint = true;
|
|
2191
|
+
this.formError = "";
|
|
2192
|
+
}
|
|
2193
|
+
connectedCallback() {
|
|
2194
|
+
super.connectedCallback();
|
|
2195
|
+
void this.resolveGeoCountry();
|
|
2196
|
+
}
|
|
2197
|
+
apiBaseUrl() {
|
|
2198
|
+
return this.getAttribute("api-base-url") || this.closest("tickean-checkout")?.getAttribute("api-base-url") || "https://api.tickean.com";
|
|
2199
|
+
}
|
|
2200
|
+
async resolveGeoCountry() {
|
|
2201
|
+
if (this.geoResolved || this.countryTouched) return;
|
|
2202
|
+
this.country = countryFromLocale(this.elementLocale);
|
|
2203
|
+
const iso = await detectCountryIso(this.apiBaseUrl());
|
|
2204
|
+
if (!this.countryTouched && iso) {
|
|
2205
|
+
this.country = countryFromIso(iso);
|
|
2206
|
+
this.geoResolved = true;
|
|
2207
|
+
this.render();
|
|
2208
|
+
} else {
|
|
2209
|
+
this.geoResolved = true;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
fullPhone() {
|
|
2213
|
+
return toE164(this.country.dial, this.national);
|
|
434
2214
|
}
|
|
435
2215
|
renderBody() {
|
|
436
2216
|
const verified = this.state?.buyerVerified;
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
<
|
|
444
|
-
${t(
|
|
445
|
-
<
|
|
446
|
-
</
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
<
|
|
452
|
-
<
|
|
453
|
-
|
|
454
|
-
|
|
2217
|
+
const locale = this.elementLocale;
|
|
2218
|
+
const options = PHONE_COUNTRIES.map(
|
|
2219
|
+
(c) => `<option value="${c.iso}" ${c.iso === this.country.iso ? "selected" : ""}>${c.flag} +${c.dial}</option>`
|
|
2220
|
+
).join("");
|
|
2221
|
+
if (verified) {
|
|
2222
|
+
return `
|
|
2223
|
+
<div class="wrap stack" part="buyer-verification">
|
|
2224
|
+
<div class="badge">\u2713 ${escapeHtml3(t(locale, "verified"))}</div>
|
|
2225
|
+
<div class="muted">${escapeHtml3(this.state?.buyer?.name || this.state?.buyer?.phone || this.fullPhone())}</div>
|
|
2226
|
+
</div>
|
|
2227
|
+
`;
|
|
2228
|
+
}
|
|
2229
|
+
const phoneBlock = `
|
|
2230
|
+
<label class="field">
|
|
2231
|
+
<span>${escapeHtml3(t(locale, "phone"))}</span>
|
|
2232
|
+
<div class="phone-row">
|
|
2233
|
+
<div class="phone-country">
|
|
2234
|
+
<select id="country" aria-label="${escapeAttr2(t(locale, "country"))}" ${this.step !== "phone" ? "disabled" : ""}>${options}</select>
|
|
2235
|
+
</div>
|
|
2236
|
+
<input id="phone" type="tel" inputmode="tel" autocomplete="tel-national"
|
|
2237
|
+
placeholder="${escapeAttr2(nationalPlaceholder(this.country.iso))}"
|
|
2238
|
+
value="${escapeAttr2(this.national)}" ${this.step !== "phone" ? "disabled" : ""} />
|
|
2239
|
+
</div>
|
|
2240
|
+
</label>`;
|
|
2241
|
+
const onboardBlock = this.step === "onboard" ? `
|
|
2242
|
+
<p class="muted" style="margin:0;font-size:0.8rem">${escapeHtml3(t(locale, "onboardHint"))}</p>
|
|
2243
|
+
<label class="field">
|
|
2244
|
+
<span>${escapeHtml3(t(locale, "name"))}</span>
|
|
2245
|
+
<input id="name" value="${escapeAttr2(this.name)}" autocomplete="name" placeholder="${escapeAttr2(t(locale, "namePlaceholder"))}" />
|
|
455
2246
|
</label>
|
|
456
|
-
<
|
|
457
|
-
|
|
2247
|
+
<label class="field">
|
|
2248
|
+
<span>${escapeHtml3(t(locale, "email"))}</span>
|
|
2249
|
+
<input id="email" type="email" value="${escapeAttr2(this.email)}" autocomplete="email" placeholder="name@email.com" />
|
|
2250
|
+
</label>` : "";
|
|
2251
|
+
const welcome = this.step === "otp" && this.knownBuyer?.name ? `<p class="muted" style="margin:0;font-size:0.8rem">${escapeHtml3(t(locale, "welcomeBack").replace("{name}", this.knownBuyer.name))}</p>` : "";
|
|
2252
|
+
const primaryLabel = this.busy === "lookup" || this.busy === "send" ? t(locale, "sending") : this.step === "onboard" ? t(locale, "continueToOtp") : t(locale, "continueToOtp");
|
|
2253
|
+
return `
|
|
2254
|
+
<div class="wrap stack" part="buyer-verification" style="gap:10px">
|
|
2255
|
+
<p class="section-title">${escapeHtml3(t(locale, "stepBuyer"))}</p>
|
|
2256
|
+
${phoneBlock}
|
|
2257
|
+
${onboardBlock}
|
|
2258
|
+
${welcome}
|
|
2259
|
+
${this.step === "phone" || this.step === "onboard" ? `<button type="button" id="continue" ${this.busy ? "disabled" : ""}>
|
|
2260
|
+
${escapeHtml3(primaryLabel)}
|
|
2261
|
+
</button>` : ""}
|
|
2262
|
+
${this.step !== "phone" ? `<button type="button" class="ghost" id="change-phone">${escapeHtml3(t(locale, "changePhone"))}</button>` : ""}
|
|
2263
|
+
${this.formError ? `<div class="danger" role="alert">${escapeHtml3(this.formError)}</div>` : ""}
|
|
2264
|
+
${this.step === "otp" ? `<div class="sheet-backdrop" data-sheet>
|
|
2265
|
+
<div class="sheet" role="dialog" aria-modal="true" aria-label="${escapeAttr2(t(locale, "otpTitle"))}">
|
|
2266
|
+
<div class="row">
|
|
2267
|
+
<h3>${escapeHtml3(t(locale, "otpTitle"))}</h3>
|
|
2268
|
+
<button type="button" class="ghost" data-close>${escapeHtml3(t(locale, "close"))}</button>
|
|
2269
|
+
</div>
|
|
2270
|
+
<p class="muted" style="margin:0">${escapeHtml3(t(locale, "otpHint"))}</p>
|
|
2271
|
+
<p class="muted" style="margin:0;font-weight:600">${escapeHtml3(this.fullPhone())}</p>
|
|
2272
|
+
<div class="otp-digits" id="otp-boxes">
|
|
2273
|
+
${[0, 1, 2, 3, 4, 5].map(
|
|
2274
|
+
(i) => `<input data-otp-i="${i}" inputmode="numeric" maxlength="1" autocomplete="${i === 0 ? "one-time-code" : "off"}" value="${escapeAttr2(this.code[i] || "")}" />`
|
|
2275
|
+
).join("")}
|
|
2276
|
+
</div>
|
|
2277
|
+
<button type="button" id="verify" ${this.busy === "verify" ? "disabled" : ""}>
|
|
2278
|
+
${escapeHtml3(this.busy === "verify" ? t(locale, "verifying") : t(locale, "verifyOtp"))}
|
|
2279
|
+
</button>
|
|
2280
|
+
<button type="button" class="secondary" id="resend" ${this.busy === "send" ? "disabled" : ""}>
|
|
2281
|
+
${escapeHtml3(t(locale, "resendOtp"))}
|
|
2282
|
+
</button>
|
|
2283
|
+
</div>
|
|
2284
|
+
</div>` : ""}
|
|
458
2285
|
</div>
|
|
459
2286
|
`;
|
|
460
2287
|
}
|
|
461
2288
|
afterRender() {
|
|
2289
|
+
if (this.firstPaint) {
|
|
2290
|
+
this.firstPaint = false;
|
|
2291
|
+
this.setAttribute("data-animated", "");
|
|
2292
|
+
}
|
|
2293
|
+
const country = this.root.querySelector("#country");
|
|
462
2294
|
const phone = this.root.querySelector("#phone");
|
|
463
2295
|
const name = this.root.querySelector("#name");
|
|
464
2296
|
const email = this.root.querySelector("#email");
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
this.
|
|
468
|
-
this.
|
|
469
|
-
|
|
470
|
-
|
|
2297
|
+
const syncProfile = () => {
|
|
2298
|
+
this.national = phone?.value || this.national;
|
|
2299
|
+
this.name = name?.value ?? this.name;
|
|
2300
|
+
this.email = email?.value ?? this.email;
|
|
2301
|
+
};
|
|
2302
|
+
country?.addEventListener("change", () => {
|
|
2303
|
+
this.countryTouched = true;
|
|
2304
|
+
this.country = countryFromIso(country.value);
|
|
2305
|
+
syncProfile();
|
|
2306
|
+
this.render();
|
|
2307
|
+
});
|
|
2308
|
+
phone?.addEventListener("input", syncProfile);
|
|
2309
|
+
name?.addEventListener("input", syncProfile);
|
|
2310
|
+
email?.addEventListener("input", syncProfile);
|
|
2311
|
+
const sendOtp = async () => {
|
|
2312
|
+
this.busy = "send";
|
|
2313
|
+
this.formError = "";
|
|
2314
|
+
this.render();
|
|
2315
|
+
try {
|
|
2316
|
+
await this.controller?.sendOtp(this.fullPhone());
|
|
2317
|
+
this.step = "otp";
|
|
2318
|
+
this.code = "";
|
|
2319
|
+
} catch (err) {
|
|
2320
|
+
this.formError = err?.message || t(this.elementLocale, "error");
|
|
2321
|
+
} finally {
|
|
2322
|
+
this.busy = null;
|
|
2323
|
+
this.render();
|
|
2324
|
+
}
|
|
2325
|
+
};
|
|
2326
|
+
const continueFlow = async () => {
|
|
2327
|
+
syncProfile();
|
|
2328
|
+
this.formError = "";
|
|
2329
|
+
if (!this.national.replace(/\D/g, "")) {
|
|
2330
|
+
this.formError = t(this.elementLocale, "phone");
|
|
2331
|
+
this.render();
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
if (this.step === "onboard") {
|
|
2335
|
+
if (!this.name.trim() || !this.email.trim()) {
|
|
2336
|
+
this.formError = t(this.elementLocale, "onboardRequired");
|
|
2337
|
+
this.render();
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
if (!/^\S+@\S+\.\S+$/.test(this.email.trim())) {
|
|
2341
|
+
this.formError = t(this.elementLocale, "emailInvalid");
|
|
2342
|
+
this.render();
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
await sendOtp();
|
|
2346
|
+
return;
|
|
2347
|
+
}
|
|
2348
|
+
this.busy = "lookup";
|
|
2349
|
+
this.render();
|
|
2350
|
+
try {
|
|
2351
|
+
const result = await this.controller?.lookupBuyer(this.fullPhone());
|
|
2352
|
+
if (result?.exists) {
|
|
2353
|
+
this.knownBuyer = {
|
|
2354
|
+
name: result.buyer?.name,
|
|
2355
|
+
email: result.buyer?.email
|
|
2356
|
+
};
|
|
2357
|
+
this.name = result.buyer?.name || "";
|
|
2358
|
+
this.email = result.buyer?.email || "";
|
|
2359
|
+
await sendOtp();
|
|
2360
|
+
} else {
|
|
2361
|
+
this.knownBuyer = null;
|
|
2362
|
+
this.step = "onboard";
|
|
2363
|
+
this.busy = null;
|
|
2364
|
+
this.render();
|
|
2365
|
+
}
|
|
2366
|
+
} catch (err) {
|
|
2367
|
+
const status = err?.status;
|
|
2368
|
+
const message = String(err?.message || "");
|
|
2369
|
+
if (status === 404 || /Cannot POST .*buyer\/lookup/i.test(message)) {
|
|
2370
|
+
this.knownBuyer = { name: void 0 };
|
|
2371
|
+
await sendOtp();
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
this.formError = message || t(this.elementLocale, "error");
|
|
2375
|
+
this.busy = null;
|
|
2376
|
+
this.render();
|
|
2377
|
+
}
|
|
471
2378
|
};
|
|
472
|
-
this.root.querySelector("#
|
|
473
|
-
|
|
474
|
-
|
|
2379
|
+
this.root.querySelector("#continue")?.addEventListener("click", () => {
|
|
2380
|
+
void continueFlow();
|
|
2381
|
+
});
|
|
2382
|
+
this.root.querySelector("#change-phone")?.addEventListener("click", () => {
|
|
2383
|
+
this.step = "phone";
|
|
2384
|
+
this.knownBuyer = null;
|
|
2385
|
+
this.code = "";
|
|
2386
|
+
this.formError = "";
|
|
2387
|
+
this.render();
|
|
2388
|
+
});
|
|
2389
|
+
this.root.querySelector("#resend")?.addEventListener("click", () => {
|
|
2390
|
+
void sendOtp();
|
|
2391
|
+
});
|
|
2392
|
+
this.root.querySelector("[data-close]")?.addEventListener("click", () => {
|
|
2393
|
+
this.step = this.knownBuyer ? "phone" : "onboard";
|
|
2394
|
+
this.render();
|
|
2395
|
+
});
|
|
2396
|
+
this.root.querySelector("[data-sheet]")?.addEventListener("click", (e) => {
|
|
2397
|
+
if (e.target.hasAttribute("data-sheet")) {
|
|
2398
|
+
this.step = this.knownBuyer ? "phone" : "onboard";
|
|
2399
|
+
this.render();
|
|
2400
|
+
}
|
|
475
2401
|
});
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
2402
|
+
const boxes = [
|
|
2403
|
+
...this.root.querySelectorAll("[data-otp-i]")
|
|
2404
|
+
];
|
|
2405
|
+
const readCode = () => boxes.map((b) => b.value.replace(/\D/g, "")).join("");
|
|
2406
|
+
const verify = async () => {
|
|
2407
|
+
this.code = readCode();
|
|
2408
|
+
if (this.code.length < 4) return;
|
|
2409
|
+
this.busy = "verify";
|
|
2410
|
+
this.formError = "";
|
|
2411
|
+
this.render();
|
|
2412
|
+
try {
|
|
2413
|
+
await this.controller?.verifyOtp({
|
|
2414
|
+
phone: this.fullPhone(),
|
|
2415
|
+
code: this.code,
|
|
2416
|
+
name: this.knownBuyer?.name ? void 0 : this.name || void 0,
|
|
2417
|
+
email: this.knownBuyer?.email ? void 0 : this.email || void 0
|
|
2418
|
+
});
|
|
2419
|
+
} catch (err) {
|
|
2420
|
+
const message = String(err?.message || "");
|
|
2421
|
+
if (/name and email are required/i.test(message)) {
|
|
2422
|
+
this.knownBuyer = null;
|
|
2423
|
+
this.step = "onboard";
|
|
2424
|
+
this.formError = t(this.elementLocale, "onboardRequired");
|
|
2425
|
+
} else {
|
|
2426
|
+
this.formError = message || t(this.elementLocale, "error");
|
|
2427
|
+
}
|
|
2428
|
+
} finally {
|
|
2429
|
+
this.busy = null;
|
|
2430
|
+
this.render();
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
boxes.forEach((box, i) => {
|
|
2434
|
+
box.addEventListener("input", () => {
|
|
2435
|
+
const v = box.value.replace(/\D/g, "").slice(-1);
|
|
2436
|
+
box.value = v;
|
|
2437
|
+
if (v && boxes[i + 1]) boxes[i + 1].focus();
|
|
2438
|
+
this.code = readCode();
|
|
2439
|
+
if (this.code.length === 6) void verify();
|
|
2440
|
+
});
|
|
2441
|
+
box.addEventListener("keydown", (e) => {
|
|
2442
|
+
if (e.key === "Backspace" && !box.value && boxes[i - 1]) {
|
|
2443
|
+
boxes[i - 1].focus();
|
|
2444
|
+
}
|
|
483
2445
|
});
|
|
2446
|
+
box.addEventListener("paste", (e) => {
|
|
2447
|
+
const text = e.clipboardData?.getData("text") || "";
|
|
2448
|
+
const digits = text.replace(/\D/g, "").slice(0, 6);
|
|
2449
|
+
if (!digits) return;
|
|
2450
|
+
e.preventDefault();
|
|
2451
|
+
digits.split("").forEach((d, idx) => {
|
|
2452
|
+
if (boxes[idx]) boxes[idx].value = d;
|
|
2453
|
+
});
|
|
2454
|
+
this.code = readCode();
|
|
2455
|
+
if (this.code.length === 6) void verify();
|
|
2456
|
+
});
|
|
2457
|
+
});
|
|
2458
|
+
this.root.querySelector("#verify")?.addEventListener("click", () => {
|
|
2459
|
+
void verify();
|
|
484
2460
|
});
|
|
2461
|
+
if (this.step === "otp" && boxes[0]) {
|
|
2462
|
+
boxes[0].focus();
|
|
2463
|
+
}
|
|
485
2464
|
}
|
|
486
2465
|
};
|
|
487
2466
|
function escapeAttr2(value) {
|
|
@@ -491,62 +2470,438 @@ function escapeHtml3(value) {
|
|
|
491
2470
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
492
2471
|
}
|
|
493
2472
|
if (typeof customElements !== "undefined" && !customElements.get("tickean-buyer-verification")) {
|
|
494
|
-
|
|
2473
|
+
customElements.define("tickean-buyer-verification", TickeanBuyerVerification);
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
// src/elements/attendees.ts
|
|
2477
|
+
var TickeanAttendees = class extends TickeanElementBase {
|
|
2478
|
+
constructor() {
|
|
2479
|
+
super(...arguments);
|
|
2480
|
+
this.slots = [];
|
|
2481
|
+
this.hydrated = false;
|
|
2482
|
+
this.termsAccepted = false;
|
|
2483
|
+
}
|
|
2484
|
+
optionName(id) {
|
|
2485
|
+
for (const show of this.state?.event?.shows || []) {
|
|
2486
|
+
for (const opt of show.showOptions || []) {
|
|
2487
|
+
if (opt.id === id) return opt.name || id;
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
return id;
|
|
2491
|
+
}
|
|
2492
|
+
rebuildSlots() {
|
|
2493
|
+
const cart = this.state?.cart || [];
|
|
2494
|
+
const prev = new Map(this.slots.map((s) => [s.key, s]));
|
|
2495
|
+
const next = [];
|
|
2496
|
+
for (const item of cart) {
|
|
2497
|
+
for (let i = 0; i < item.amount; i++) {
|
|
2498
|
+
const key = `${item.showOptionId}:${i}`;
|
|
2499
|
+
const existing = prev.get(key);
|
|
2500
|
+
next.push({
|
|
2501
|
+
key,
|
|
2502
|
+
showOptionId: item.showOptionId,
|
|
2503
|
+
label: this.optionName(item.showOptionId),
|
|
2504
|
+
firstName: existing?.firstName || "",
|
|
2505
|
+
lastName: existing?.lastName || "",
|
|
2506
|
+
email: existing?.email || "",
|
|
2507
|
+
phone: existing?.phone || ""
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
this.slots = next;
|
|
2512
|
+
this.emitAttendees();
|
|
2513
|
+
}
|
|
2514
|
+
emitAttendees() {
|
|
2515
|
+
const settings = this.state?.event?.registrationSettings;
|
|
2516
|
+
const attendees = this.slots.map((s) => ({
|
|
2517
|
+
showOptionId: s.showOptionId,
|
|
2518
|
+
firstName: s.firstName || void 0,
|
|
2519
|
+
lastName: s.lastName || void 0,
|
|
2520
|
+
email: settings?.requireAttendeeEmail ? s.email || void 0 : void 0,
|
|
2521
|
+
phone: settings?.requireAttendeePhone ? s.phone || void 0 : void 0
|
|
2522
|
+
}));
|
|
2523
|
+
this.dispatchEvent(
|
|
2524
|
+
new CustomEvent("attendees-change", {
|
|
2525
|
+
detail: { attendees, complete: this.isComplete() },
|
|
2526
|
+
bubbles: true,
|
|
2527
|
+
composed: true
|
|
2528
|
+
})
|
|
2529
|
+
);
|
|
2530
|
+
}
|
|
2531
|
+
isComplete() {
|
|
2532
|
+
const settings = this.state?.event?.registrationSettings;
|
|
2533
|
+
if (!this.slots.length) return false;
|
|
2534
|
+
if (settings?.terms?.required && !this.termsAccepted) return false;
|
|
2535
|
+
return this.slots.every((s) => {
|
|
2536
|
+
if (!s.firstName.trim() || !s.lastName.trim()) return false;
|
|
2537
|
+
if (settings?.requireAttendeeEmail && !s.email.trim()) return false;
|
|
2538
|
+
if (settings?.requireAttendeePhone && !s.phone.trim()) return false;
|
|
2539
|
+
return true;
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
getAttendees() {
|
|
2543
|
+
const termsRequired = Boolean(
|
|
2544
|
+
this.state?.event?.registrationSettings?.terms?.required
|
|
2545
|
+
);
|
|
2546
|
+
return this.slots.map((s) => ({
|
|
2547
|
+
showOptionId: s.showOptionId,
|
|
2548
|
+
firstName: s.firstName || void 0,
|
|
2549
|
+
lastName: s.lastName || void 0,
|
|
2550
|
+
email: s.email || void 0,
|
|
2551
|
+
phone: s.phone || void 0,
|
|
2552
|
+
termsAccepted: termsRequired ? this.termsAccepted : void 0
|
|
2553
|
+
}));
|
|
2554
|
+
}
|
|
2555
|
+
renderBody() {
|
|
2556
|
+
if (!this.hydrated || this.slots.length === 0) {
|
|
2557
|
+
this.rebuildSlots();
|
|
2558
|
+
this.hydrated = true;
|
|
2559
|
+
} else {
|
|
2560
|
+
const expected = (this.state?.cart || []).reduce((n, i) => n + i.amount, 0);
|
|
2561
|
+
if (expected !== this.slots.length) this.rebuildSlots();
|
|
2562
|
+
}
|
|
2563
|
+
const locale = this.elementLocale;
|
|
2564
|
+
const settings = this.state?.event?.registrationSettings;
|
|
2565
|
+
if (!this.state?.event?.collectAttendeeData) {
|
|
2566
|
+
return `<div class="muted">${escapeHtml4(t(locale, "attendees"))}</div>`;
|
|
2567
|
+
}
|
|
2568
|
+
return `
|
|
2569
|
+
<div class="stack" part="attendees" style="gap:10px">
|
|
2570
|
+
<p class="section-title">${escapeHtml4(t(locale, "attendees"))}</p>
|
|
2571
|
+
${this.slots.map(
|
|
2572
|
+
(slot, index) => `
|
|
2573
|
+
<div class="wrap stack" style="gap:8px;padding:10px 12px" data-slot="${escapeAttr3(slot.key)}">
|
|
2574
|
+
<div class="row">
|
|
2575
|
+
<strong style="font-size:0.9rem">${escapeHtml4(tFormat(locale, "attendeeN", { n: index + 1 }))}</strong>
|
|
2576
|
+
<span class="muted" style="font-size:0.78rem">${escapeHtml4(slot.label)}</span>
|
|
2577
|
+
</div>
|
|
2578
|
+
<div class="phone-row">
|
|
2579
|
+
<label class="field">
|
|
2580
|
+
<span>${escapeHtml4(t(locale, "firstName"))}</span>
|
|
2581
|
+
<input data-f="firstName" value="${escapeAttr3(slot.firstName)}" autocomplete="given-name" />
|
|
2582
|
+
</label>
|
|
2583
|
+
<label class="field">
|
|
2584
|
+
<span>${escapeHtml4(t(locale, "lastName"))}</span>
|
|
2585
|
+
<input data-f="lastName" value="${escapeAttr3(slot.lastName)}" autocomplete="family-name" />
|
|
2586
|
+
</label>
|
|
2587
|
+
</div>
|
|
2588
|
+
${settings?.requireAttendeeEmail ? `<label class="field"><span>${escapeHtml4(t(locale, "email"))}</span>
|
|
2589
|
+
<input data-f="email" type="email" value="${escapeAttr3(slot.email)}" autocomplete="email" /></label>` : ""}
|
|
2590
|
+
${settings?.requireAttendeePhone ? `<label class="field"><span>${escapeHtml4(t(locale, "phone"))}</span>
|
|
2591
|
+
<input data-f="phone" type="tel" value="${escapeAttr3(slot.phone)}" autocomplete="tel" /></label>` : ""}
|
|
2592
|
+
</div>
|
|
2593
|
+
`
|
|
2594
|
+
).join("")}
|
|
2595
|
+
${settings?.terms?.required ? `<label class="terms-row">
|
|
2596
|
+
<input type="checkbox" id="terms" ${this.termsAccepted ? "checked" : ""} />
|
|
2597
|
+
<span>
|
|
2598
|
+
${escapeHtml4(settings.terms.label || t(locale, "acceptTerms"))}
|
|
2599
|
+
${settings.terms.pdfUrl ? ` <a href="${escapeAttr3(settings.terms.pdfUrl)}" target="_blank" rel="noopener noreferrer">PDF</a>` : ""}
|
|
2600
|
+
</span>
|
|
2601
|
+
</label>` : ""}
|
|
2602
|
+
</div>
|
|
2603
|
+
`;
|
|
2604
|
+
}
|
|
2605
|
+
afterRender() {
|
|
2606
|
+
this.root.querySelectorAll("[data-slot]").forEach((el) => {
|
|
2607
|
+
const key = el.getAttribute("data-slot");
|
|
2608
|
+
const slot = this.slots.find((s) => s.key === key);
|
|
2609
|
+
if (!slot) return;
|
|
2610
|
+
el.querySelectorAll("[data-f]").forEach((input) => {
|
|
2611
|
+
input.addEventListener("input", () => {
|
|
2612
|
+
const field = input.getAttribute("data-f");
|
|
2613
|
+
if (field === "firstName" || field === "lastName" || field === "email" || field === "phone") {
|
|
2614
|
+
slot[field] = input.value;
|
|
2615
|
+
this.emitAttendees();
|
|
2616
|
+
}
|
|
2617
|
+
});
|
|
2618
|
+
});
|
|
2619
|
+
});
|
|
2620
|
+
this.root.querySelector("#terms")?.addEventListener("change", (e) => {
|
|
2621
|
+
this.termsAccepted = Boolean(e.target.checked);
|
|
2622
|
+
this.emitAttendees();
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
function escapeAttr3(value) {
|
|
2627
|
+
return value.replace(/"/g, """);
|
|
2628
|
+
}
|
|
2629
|
+
function escapeHtml4(value) {
|
|
2630
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2631
|
+
}
|
|
2632
|
+
if (typeof customElements !== "undefined" && !customElements.get("tickean-attendees")) {
|
|
2633
|
+
customElements.define("tickean-attendees", TickeanAttendees);
|
|
495
2634
|
}
|
|
496
2635
|
|
|
497
2636
|
// src/elements/payment.ts
|
|
2637
|
+
var METHOD_ORDER = ["TRANSFER", "MERCADO_PAGO", "CARD", "FREE"];
|
|
2638
|
+
var METHOD_META = {
|
|
2639
|
+
TRANSFER: { titleKey: "payTransfer", descKey: "payTransferDesc" },
|
|
2640
|
+
MERCADO_PAGO: { titleKey: "payMercadoPago", descKey: "payMercadoPagoDesc" },
|
|
2641
|
+
CARD: { titleKey: "payCard", descKey: "payCardDesc" },
|
|
2642
|
+
FREE: { titleKey: "payFree", descKey: "payFreeDesc" }
|
|
2643
|
+
};
|
|
498
2644
|
var TickeanPayment = class extends TickeanElementBase {
|
|
2645
|
+
constructor() {
|
|
2646
|
+
super(...arguments);
|
|
2647
|
+
this.busy = false;
|
|
2648
|
+
this.selectedMethod = null;
|
|
2649
|
+
this.copiedKey = null;
|
|
2650
|
+
this.copiedTimer = null;
|
|
2651
|
+
this.redirectedUrl = null;
|
|
2652
|
+
}
|
|
499
2653
|
static get observedAttributes() {
|
|
500
|
-
return [
|
|
2654
|
+
return [
|
|
2655
|
+
...TickeanElementBase.observedAttributes,
|
|
2656
|
+
"payment-method",
|
|
2657
|
+
"currency",
|
|
2658
|
+
"view"
|
|
2659
|
+
];
|
|
2660
|
+
}
|
|
2661
|
+
disconnectedCallback() {
|
|
2662
|
+
if (this.copiedTimer) clearTimeout(this.copiedTimer);
|
|
2663
|
+
super.disconnectedCallback();
|
|
2664
|
+
}
|
|
2665
|
+
view() {
|
|
2666
|
+
return this.getAttribute("view") === "transfer" ? "transfer" : "method";
|
|
2667
|
+
}
|
|
2668
|
+
methods() {
|
|
2669
|
+
const fromEvent = this.state?.event?.availablePaymentMethods || [];
|
|
2670
|
+
const attr = this.getAttribute("payment-method");
|
|
2671
|
+
if (fromEvent.length) {
|
|
2672
|
+
return [...fromEvent].sort(
|
|
2673
|
+
(a, b) => METHOD_ORDER.indexOf(a) - METHOD_ORDER.indexOf(b)
|
|
2674
|
+
);
|
|
2675
|
+
}
|
|
2676
|
+
return [attr || "TRANSFER"];
|
|
2677
|
+
}
|
|
2678
|
+
resolveMethod() {
|
|
2679
|
+
const methods = this.methods();
|
|
2680
|
+
const attr = this.getAttribute("payment-method");
|
|
2681
|
+
const purchaseMethod = this.state?.purchase?.paymentMethod;
|
|
2682
|
+
if (this.selectedMethod && methods.includes(this.selectedMethod)) {
|
|
2683
|
+
return this.selectedMethod;
|
|
2684
|
+
}
|
|
2685
|
+
if (purchaseMethod && methods.includes(purchaseMethod)) return purchaseMethod;
|
|
2686
|
+
if (attr && methods.includes(attr)) return attr;
|
|
2687
|
+
if (methods.length === 1) return methods[0];
|
|
2688
|
+
return this.selectedMethod || "";
|
|
501
2689
|
}
|
|
502
2690
|
renderBody() {
|
|
503
2691
|
const state = this.state;
|
|
504
|
-
const
|
|
505
|
-
const paymentMethod = this.getAttribute("payment-method") || state?.event?.availablePaymentMethods?.[0] || "TRANSFER";
|
|
2692
|
+
const locale = this.elementLocale;
|
|
506
2693
|
const currency = this.getAttribute("currency") || "ARS";
|
|
2694
|
+
const view = this.view();
|
|
507
2695
|
if (state?.phase === "completed") {
|
|
508
|
-
return `<div class="wrap" part="payment"><div role="status"
|
|
2696
|
+
return `<div class="wrap reveal" part="payment"><div class="badge" role="status">\u2713 ${t(locale, "completed")}</div></div>`;
|
|
2697
|
+
}
|
|
2698
|
+
if (view === "transfer") {
|
|
2699
|
+
return this.renderTransferView(locale, currency);
|
|
2700
|
+
}
|
|
2701
|
+
return this.renderMethodView(locale, currency);
|
|
2702
|
+
}
|
|
2703
|
+
redirectUrl() {
|
|
2704
|
+
const next = this.state?.nextAction;
|
|
2705
|
+
if (next?.type === "redirect" && next.url) return next.url;
|
|
2706
|
+
const payment = this.state?.payment;
|
|
2707
|
+
return payment?.redirectUrl || payment?.initPoint || null;
|
|
2708
|
+
}
|
|
2709
|
+
renderMethodView(locale, currency) {
|
|
2710
|
+
const state = this.state;
|
|
2711
|
+
const methods = this.methods();
|
|
2712
|
+
const paymentMethod = this.resolveMethod();
|
|
2713
|
+
const multi = methods.length > 1;
|
|
2714
|
+
const needsPick = multi && !paymentMethod;
|
|
2715
|
+
const hasPurchase = Boolean(state?.purchase);
|
|
2716
|
+
const redirectUrl = this.redirectUrl();
|
|
2717
|
+
const isRedirecting = Boolean(redirectUrl) && (state?.phase === "requires_action" || state?.nextAction?.type === "redirect");
|
|
2718
|
+
if (isRedirecting && redirectUrl) {
|
|
2719
|
+
return `
|
|
2720
|
+
<div class="stack" part="payment">
|
|
2721
|
+
<p class="section-title reveal reveal-1">${t(locale, "stepPayment")}</p>
|
|
2722
|
+
<div class="wrap stack reveal reveal-2" style="gap:12px;align-items:center;text-align:center">
|
|
2723
|
+
<div class="transfer-waiting" role="status">
|
|
2724
|
+
<span class="transfer-waiting-dot" aria-hidden="true"></span>
|
|
2725
|
+
<span>${escapeHtml5(t(locale, "redirectingPay"))}</span>
|
|
2726
|
+
</div>
|
|
2727
|
+
<a class="muted" id="redirect" href="${escapeAttr4(redirectUrl)}" target="_top" rel="noopener noreferrer" style="font-size:0.78rem">
|
|
2728
|
+
${escapeHtml5(t(locale, "redirectPay"))}
|
|
2729
|
+
</a>
|
|
2730
|
+
<div data-payment-method="${escapeAttr4(paymentMethod)}" data-currency="${escapeAttr4(currency)}" hidden></div>
|
|
2731
|
+
</div>
|
|
2732
|
+
</div>
|
|
2733
|
+
`;
|
|
509
2734
|
}
|
|
2735
|
+
const locked = this.busy || state?.phase === "purchasing";
|
|
2736
|
+
const canPay = Boolean(
|
|
2737
|
+
state?.cart.length && state?.buyerVerified && !locked && paymentMethod
|
|
2738
|
+
);
|
|
2739
|
+
const methodPicker = `
|
|
2740
|
+
<div class="pay-methods" role="radiogroup" aria-label="${escapeAttr4(t(locale, "paymentMethods"))}" data-locked="${locked}">
|
|
2741
|
+
${methods.map((method) => {
|
|
2742
|
+
const meta = METHOD_META[method] || METHOD_META.TRANSFER;
|
|
2743
|
+
const selected = method === paymentMethod;
|
|
2744
|
+
return `
|
|
2745
|
+
<button type="button" class="pay-method" role="radio" aria-checked="${selected}"
|
|
2746
|
+
data-method="${escapeAttr4(method)}" data-selected="${selected}" ${locked ? "disabled" : ""}>
|
|
2747
|
+
<span class="pay-method-radio" aria-hidden="true"></span>
|
|
2748
|
+
<span class="pay-method-copy">
|
|
2749
|
+
<strong>${escapeHtml5(t(locale, meta.titleKey))}</strong>
|
|
2750
|
+
<span class="muted">${escapeHtml5(t(locale, meta.descKey))}</span>
|
|
2751
|
+
</span>
|
|
2752
|
+
</button>`;
|
|
2753
|
+
}).join("")}
|
|
2754
|
+
</div>`;
|
|
2755
|
+
const prePayTransferHint = paymentMethod === "TRANSFER" ? `<p class="muted" style="margin:0;font-size:0.78rem">${escapeHtml5(t(locale, "payTransferHint"))}</p>` : "";
|
|
2756
|
+
const payLabel = locked ? t(locale, "creatingPayment") : needsPick ? t(locale, "choosePayment") : t(locale, "pay");
|
|
2757
|
+
return `
|
|
2758
|
+
<div class="stack" part="payment">
|
|
2759
|
+
<p class="section-title reveal reveal-1">${t(locale, "stepPayment")}</p>
|
|
2760
|
+
<div class="wrap stack reveal reveal-2" style="gap:12px">
|
|
2761
|
+
${methodPicker}
|
|
2762
|
+
${prePayTransferHint}
|
|
2763
|
+
<button type="button" id="pay" ${canPay ? "" : "disabled"}>
|
|
2764
|
+
${escapeHtml5(payLabel)}
|
|
2765
|
+
</button>
|
|
2766
|
+
<div data-payment-method="${escapeAttr4(paymentMethod)}" data-currency="${escapeAttr4(currency)}" hidden></div>
|
|
2767
|
+
</div>
|
|
2768
|
+
</div>
|
|
2769
|
+
`;
|
|
2770
|
+
}
|
|
2771
|
+
renderTransferView(locale, currency) {
|
|
2772
|
+
const nextAction = this.state?.nextAction || { type: "none" };
|
|
2773
|
+
const paymentMethod = this.resolveMethod();
|
|
2774
|
+
const methodTitle = t(
|
|
2775
|
+
locale,
|
|
2776
|
+
(METHOD_META[paymentMethod] || METHOD_META.TRANSFER).titleKey
|
|
2777
|
+
);
|
|
510
2778
|
return `
|
|
511
|
-
<div class="
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
2779
|
+
<div class="stack" part="payment">
|
|
2780
|
+
<p class="section-title reveal reveal-1">${t(locale, "stepTransfer")}</p>
|
|
2781
|
+
<div class="wrap stack reveal reveal-2" style="gap:12px">
|
|
2782
|
+
<div class="pay-method-locked row">
|
|
2783
|
+
<div>
|
|
2784
|
+
<div class="muted" style="font-size:0.72rem">${escapeHtml5(t(locale, "paymentMethods"))}</div>
|
|
2785
|
+
<strong style="font-size:0.9rem">${escapeHtml5(methodTitle)}</strong>
|
|
2786
|
+
</div>
|
|
2787
|
+
<button type="button" class="ghost-btn" id="change-method">${escapeHtml5(t(locale, "changePaymentMethod"))}</button>
|
|
2788
|
+
</div>
|
|
2789
|
+
${renderNextAction(nextAction, locale, (n) => this.money(n), this.copiedKey)}
|
|
2790
|
+
${nextAction.type === "display_instructions" ? `<div class="transfer-waiting" role="status">
|
|
2791
|
+
<span class="transfer-waiting-dot" aria-hidden="true"></span>
|
|
2792
|
+
<span>${escapeHtml5(t(locale, "transferWaiting"))}</span>
|
|
2793
|
+
</div>
|
|
2794
|
+
<p class="muted" style="margin:0;font-size:0.78rem">${escapeHtml5(t(locale, "transferPendingHint"))}</p>` : ""}
|
|
2795
|
+
<div data-payment-method="${escapeAttr4(paymentMethod)}" data-currency="${escapeAttr4(currency)}" hidden></div>
|
|
2796
|
+
</div>
|
|
515
2797
|
</div>
|
|
516
2798
|
`;
|
|
517
2799
|
}
|
|
518
2800
|
afterRender() {
|
|
519
|
-
const paymentMethod = this.getAttribute("payment-method") || this.state?.event?.availablePaymentMethods?.[0] || "TRANSFER";
|
|
520
2801
|
const currency = this.getAttribute("currency") || "ARS";
|
|
521
|
-
this.
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
2802
|
+
const view = this.view();
|
|
2803
|
+
this.root.querySelector("#change-method")?.addEventListener("click", () => {
|
|
2804
|
+
this.dispatchEvent(
|
|
2805
|
+
new CustomEvent("tickean-change-payment-method", {
|
|
2806
|
+
bubbles: true,
|
|
2807
|
+
composed: true
|
|
2808
|
+
})
|
|
2809
|
+
);
|
|
2810
|
+
});
|
|
2811
|
+
this.root.querySelectorAll("[data-method]").forEach((btn) => {
|
|
2812
|
+
btn.addEventListener("click", () => {
|
|
2813
|
+
if (btn.disabled || this.busy || this.state?.phase === "purchasing") return;
|
|
2814
|
+
this.selectedMethod = btn.getAttribute("data-method");
|
|
2815
|
+
this.render();
|
|
525
2816
|
});
|
|
526
2817
|
});
|
|
527
|
-
this.root.
|
|
528
|
-
|
|
529
|
-
|
|
2818
|
+
this.root.querySelectorAll("[data-copy]").forEach((btn) => {
|
|
2819
|
+
btn.addEventListener("click", async () => {
|
|
2820
|
+
const value = btn.getAttribute("data-copy") || "";
|
|
2821
|
+
const key = btn.getAttribute("data-copy-key") || value;
|
|
2822
|
+
const ok = await copyText(value);
|
|
2823
|
+
if (!ok) return;
|
|
2824
|
+
this.copiedKey = key;
|
|
2825
|
+
if (this.copiedTimer) clearTimeout(this.copiedTimer);
|
|
2826
|
+
this.copiedTimer = setTimeout(() => {
|
|
2827
|
+
this.copiedKey = null;
|
|
2828
|
+
this.render();
|
|
2829
|
+
}, 1600);
|
|
2830
|
+
this.render();
|
|
2831
|
+
});
|
|
530
2832
|
});
|
|
2833
|
+
if (view === "method") {
|
|
2834
|
+
this.root.querySelector("#pay")?.addEventListener("click", async () => {
|
|
2835
|
+
const paymentMethod = this.resolveMethod();
|
|
2836
|
+
if (!paymentMethod) return;
|
|
2837
|
+
this.busy = true;
|
|
2838
|
+
this.render();
|
|
2839
|
+
try {
|
|
2840
|
+
const root = this.getRootNode();
|
|
2841
|
+
const host = root.host || this.closest("tickean-checkout");
|
|
2842
|
+
const attendees = host?.getAttendees?.();
|
|
2843
|
+
await this.controller?.purchaseAndPay({
|
|
2844
|
+
paymentMethod,
|
|
2845
|
+
currency,
|
|
2846
|
+
attendees: attendees?.length ? attendees : void 0
|
|
2847
|
+
});
|
|
2848
|
+
} finally {
|
|
2849
|
+
this.busy = false;
|
|
2850
|
+
this.render();
|
|
2851
|
+
}
|
|
2852
|
+
});
|
|
2853
|
+
}
|
|
2854
|
+
const redirectUrl = this.redirectUrl();
|
|
2855
|
+
if (redirectUrl && this.redirectedUrl !== redirectUrl && (this.state?.phase === "requires_action" || this.state?.nextAction?.type === "redirect")) {
|
|
2856
|
+
this.redirectedUrl = redirectUrl;
|
|
2857
|
+
navigateTopLevel(redirectUrl);
|
|
2858
|
+
}
|
|
531
2859
|
}
|
|
532
2860
|
};
|
|
533
|
-
function
|
|
2861
|
+
function navigateTopLevel(url) {
|
|
2862
|
+
if (typeof window === "undefined" || !url) return;
|
|
2863
|
+
try {
|
|
2864
|
+
const topWin = window.top;
|
|
2865
|
+
if (topWin && topWin !== window) {
|
|
2866
|
+
topWin.location.assign(url);
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
} catch {
|
|
2870
|
+
}
|
|
2871
|
+
window.location.assign(url);
|
|
2872
|
+
}
|
|
2873
|
+
function renderNextAction(nextAction, locale, money, copiedKey) {
|
|
534
2874
|
switch (nextAction.type) {
|
|
535
2875
|
case "display_instructions": {
|
|
536
2876
|
const instructions = nextAction.paymentInstructions || {};
|
|
2877
|
+
const alias = instructions.alias ? String(instructions.alias) : "";
|
|
2878
|
+
const cvu = instructions.cvu ? String(instructions.cvu) : instructions.accountIdentifier ? String(instructions.accountIdentifier) : "";
|
|
2879
|
+
const amount = instructions.amount != null ? Number(instructions.amount) : null;
|
|
537
2880
|
return `
|
|
538
|
-
<div>
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
2881
|
+
<div class="transfer-box stack" style="gap:12px">
|
|
2882
|
+
${amount != null && Number.isFinite(amount) ? `<div class="transfer-amount">
|
|
2883
|
+
<span class="transfer-amount-label">${escapeHtml5(t(locale, "transferExactAmount"))}</span>
|
|
2884
|
+
<strong class="transfer-amount-value">${escapeHtml5(money(amount))}</strong>
|
|
2885
|
+
</div>` : ""}
|
|
2886
|
+
<div class="stack" style="gap:8px">
|
|
2887
|
+
<strong>${escapeHtml5(t(locale, "transferInstructions"))}</strong>
|
|
2888
|
+
${alias ? renderCopyRow(locale, "alias", alias, "alias", copiedKey) : ""}
|
|
2889
|
+
${cvu ? renderCopyRow(locale, "cvu", cvu, "cvu", copiedKey) : ""}
|
|
544
2890
|
</div>
|
|
545
2891
|
</div>
|
|
546
2892
|
`;
|
|
547
2893
|
}
|
|
548
|
-
case "redirect":
|
|
549
|
-
|
|
2894
|
+
case "redirect": {
|
|
2895
|
+
const url = nextAction.url || "";
|
|
2896
|
+
return `
|
|
2897
|
+
<div class="stack" style="gap:10px;align-items:center;text-align:center">
|
|
2898
|
+
<div class="transfer-waiting" role="status">
|
|
2899
|
+
<span class="transfer-waiting-dot" aria-hidden="true"></span>
|
|
2900
|
+
<span>${escapeHtml5(t(locale, "redirectingPay"))}</span>
|
|
2901
|
+
</div>
|
|
2902
|
+
${url ? `<a class="muted" id="redirect" href="${escapeAttr4(url)}" target="_top" rel="noopener noreferrer" style="font-size:0.78rem">${escapeHtml5(t(locale, "redirectPay"))}</a>` : ""}
|
|
2903
|
+
</div>`;
|
|
2904
|
+
}
|
|
550
2905
|
case "stripe_elements":
|
|
551
2906
|
return `<div class="provider-slot" data-provider="stripe" part="stripe-mount">${t(locale, "providerPlaceholder")} (Stripe)</div>`;
|
|
552
2907
|
case "airwallex_dropin":
|
|
@@ -556,14 +2911,60 @@ function renderNextAction(nextAction, locale, money) {
|
|
|
556
2911
|
case "fintoc_widget":
|
|
557
2912
|
return `<div class="provider-slot" data-provider="fintoc" part="fintoc-mount">${t(locale, "providerPlaceholder")} (Fintoc)</div>`;
|
|
558
2913
|
default:
|
|
559
|
-
return ""
|
|
2914
|
+
return `<p class="muted" style="margin:0">${escapeHtml5(t(locale, "transferWaiting"))}</p>`;
|
|
560
2915
|
}
|
|
561
2916
|
}
|
|
562
|
-
function
|
|
2917
|
+
function renderCopyRow(locale, labelKey, value, copyKey, copiedKey) {
|
|
2918
|
+
const copied = copiedKey === copyKey;
|
|
2919
|
+
return `
|
|
2920
|
+
<div class="copy-row">
|
|
2921
|
+
<div class="copy-row-text">
|
|
2922
|
+
<span class="muted">${escapeHtml5(t(locale, labelKey))}</span>
|
|
2923
|
+
<code>${escapeHtml5(value)}</code>
|
|
2924
|
+
</div>
|
|
2925
|
+
<button type="button" class="copy-btn" data-copy="${escapeAttr4(value)}" data-copy-key="${escapeAttr4(copyKey)}" aria-label="${escapeAttr4(
|
|
2926
|
+
copied ? t(locale, "copied") : t(locale, "copy")
|
|
2927
|
+
)}">
|
|
2928
|
+
${copied ? checkIcon() : copyIcon()}
|
|
2929
|
+
<span>${escapeHtml5(copied ? t(locale, "copied") : t(locale, "copy"))}</span>
|
|
2930
|
+
</button>
|
|
2931
|
+
</div>
|
|
2932
|
+
`;
|
|
2933
|
+
}
|
|
2934
|
+
function copyIcon() {
|
|
2935
|
+
return `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" stroke="currentColor" stroke-width="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10" stroke="currentColor" stroke-width="2"/></svg>`;
|
|
2936
|
+
}
|
|
2937
|
+
function checkIcon() {
|
|
2938
|
+
return `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M20 6 9 17l-5-5" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
2939
|
+
}
|
|
2940
|
+
async function copyText(value) {
|
|
2941
|
+
try {
|
|
2942
|
+
if (navigator?.clipboard?.writeText) {
|
|
2943
|
+
await navigator.clipboard.writeText(value);
|
|
2944
|
+
return true;
|
|
2945
|
+
}
|
|
2946
|
+
} catch {
|
|
2947
|
+
}
|
|
2948
|
+
try {
|
|
2949
|
+
const ta = document.createElement("textarea");
|
|
2950
|
+
ta.value = value;
|
|
2951
|
+
ta.setAttribute("readonly", "");
|
|
2952
|
+
ta.style.position = "fixed";
|
|
2953
|
+
ta.style.opacity = "0";
|
|
2954
|
+
document.body.appendChild(ta);
|
|
2955
|
+
ta.select();
|
|
2956
|
+
const ok = document.execCommand("copy");
|
|
2957
|
+
document.body.removeChild(ta);
|
|
2958
|
+
return ok;
|
|
2959
|
+
} catch {
|
|
2960
|
+
return false;
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
function escapeAttr4(value) {
|
|
563
2964
|
return value.replace(/"/g, """);
|
|
564
2965
|
}
|
|
565
|
-
function
|
|
566
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2966
|
+
function escapeHtml5(value) {
|
|
2967
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
567
2968
|
}
|
|
568
2969
|
if (typeof customElements !== "undefined" && !customElements.get("tickean-payment")) {
|
|
569
2970
|
customElements.define("tickean-payment", TickeanPayment);
|
|
@@ -571,34 +2972,70 @@ if (typeof customElements !== "undefined" && !customElements.get("tickean-paymen
|
|
|
571
2972
|
|
|
572
2973
|
// src/elements/order-summary.ts
|
|
573
2974
|
var TickeanOrderSummary = class extends TickeanElementBase {
|
|
2975
|
+
optionName(id) {
|
|
2976
|
+
for (const show of this.state?.event?.shows || []) {
|
|
2977
|
+
for (const opt of show.showOptions || []) {
|
|
2978
|
+
if (opt.id === id) return opt.name || id;
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
return id;
|
|
2982
|
+
}
|
|
574
2983
|
renderBody() {
|
|
575
|
-
const total = this.state?.quote?.totalPrice || 0;
|
|
2984
|
+
const total = Number(this.state?.quote?.totalPrice) || Number(this.state?.purchase?.totalPrice) || 0;
|
|
576
2985
|
const items = this.state?.cart || [];
|
|
2986
|
+
const locale = this.elementLocale;
|
|
2987
|
+
const breakdown = this.state?.quote?.pricingBreakdown || {};
|
|
2988
|
+
const promo = Number(breakdown.promotionDiscountTotal || 0);
|
|
2989
|
+
const code = Number(breakdown.codeDiscountTotal || 0);
|
|
2990
|
+
const service = Number(breakdown.serviceCharge || 0);
|
|
2991
|
+
const subtotal = Number(breakdown.subtotalBase) || Math.max(0, total - service + promo + code);
|
|
577
2992
|
return `
|
|
578
|
-
<div class="wrap stack" part="order-summary">
|
|
579
|
-
<
|
|
580
|
-
|
|
2993
|
+
<div class="wrap stack reveal reveal-2" part="order-summary">
|
|
2994
|
+
<p class="section-title">${t(locale, "orderSummary")}</p>
|
|
2995
|
+
<div class="summary-line">
|
|
2996
|
+
<span class="muted">${tFormat(locale, "itemsCount", { count: items.length })}</span>
|
|
2997
|
+
${this.state?.isQuoting ? `<span class="muted">${t(locale, "loading")}</span>` : ""}
|
|
2998
|
+
</div>
|
|
2999
|
+
${items.length === 0 ? `<div class="muted">${t(locale, "emptyCart")}</div>` : items.map(
|
|
3000
|
+
(item) => `<div class="summary-line"><span>${escapeHtml6(this.optionName(item.showOptionId))}</span><span class="muted">\xD7${item.amount}</span></div>`
|
|
3001
|
+
).join("")}
|
|
3002
|
+
${items.length && subtotal > 0 ? `<div class="summary-line"><span class="muted">${escapeHtml6(t(locale, "subtotal"))}</span><span>${escapeHtml6(this.money(subtotal))}</span></div>` : ""}
|
|
3003
|
+
${promo > 0 ? `<div class="summary-line"><span class="muted">${escapeHtml6(t(locale, "promoDiscount"))}</span><span>\u2212${escapeHtml6(this.money(promo))}</span></div>` : ""}
|
|
3004
|
+
${code > 0 ? `<div class="summary-line"><span class="muted">${escapeHtml6(t(locale, "discount"))}</span><span>\u2212${escapeHtml6(this.money(code))}</span></div>` : ""}
|
|
3005
|
+
${service > 0 ? `<div class="summary-line"><span class="muted">${escapeHtml6(t(locale, "serviceCharge"))}</span><span>${escapeHtml6(this.money(service))}</span></div>` : ""}
|
|
3006
|
+
<div class="row" style="margin-top:2px;padding-top:8px;border-top:1px solid var(--tickean-border)">
|
|
3007
|
+
<span style="font-weight:600">${t(locale, "total")}</span>
|
|
581
3008
|
<span class="total" aria-live="polite">${this.money(total)}</span>
|
|
582
3009
|
</div>
|
|
583
|
-
${items.length === 0 ? `<div class="muted">${t(this.elementLocale, "emptyCart")}</div>` : `<div class="muted">${items.length} item(s)</div>`}
|
|
584
|
-
${this.state?.isQuoting ? `<div class="muted">${t(this.elementLocale, "loading")}</div>` : ""}
|
|
585
3010
|
</div>
|
|
586
3011
|
`;
|
|
587
3012
|
}
|
|
588
3013
|
};
|
|
3014
|
+
function escapeHtml6(value) {
|
|
3015
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3016
|
+
}
|
|
589
3017
|
if (typeof customElements !== "undefined" && !customElements.get("tickean-order-summary")) {
|
|
590
|
-
|
|
3018
|
+
customElements.define("tickean-order-summary", TickeanOrderSummary);
|
|
591
3019
|
}
|
|
592
3020
|
|
|
593
3021
|
// src/elements/checkout.ts
|
|
594
3022
|
import {
|
|
595
3023
|
createCheckoutController
|
|
596
3024
|
} from "@tickean/checkout-js";
|
|
3025
|
+
var TICKEAN_HOME = "https://tickean.com";
|
|
3026
|
+
var TICKEAN_TICKETS_URL = "https://mi.tickean.com/tickets";
|
|
597
3027
|
var TickeanCheckout = class extends TickeanElementBase {
|
|
598
3028
|
constructor() {
|
|
599
3029
|
super(...arguments);
|
|
600
3030
|
this.ownedControllerId = null;
|
|
601
3031
|
this.ownedController = null;
|
|
3032
|
+
this.stepIndex = 0;
|
|
3033
|
+
this.shellMounted = false;
|
|
3034
|
+
this.shellLayout = null;
|
|
3035
|
+
this.attendees = [];
|
|
3036
|
+
this.attendeesComplete = false;
|
|
3037
|
+
/** After "Cambiar método", stay on payment until they confirm again. */
|
|
3038
|
+
this.suppressTransferAutoAdvance = false;
|
|
602
3039
|
}
|
|
603
3040
|
static get observedAttributes() {
|
|
604
3041
|
return [
|
|
@@ -609,81 +3046,501 @@ var TickeanCheckout = class extends TickeanElementBase {
|
|
|
609
3046
|
"return-url",
|
|
610
3047
|
"demo",
|
|
611
3048
|
"payment-method",
|
|
612
|
-
"currency"
|
|
3049
|
+
"currency",
|
|
3050
|
+
"layout"
|
|
613
3051
|
];
|
|
614
3052
|
}
|
|
3053
|
+
/** Used by payment element / hosts. */
|
|
3054
|
+
getAttendees() {
|
|
3055
|
+
const el = this.root.querySelector("tickean-attendees");
|
|
3056
|
+
if (el?.getAttendees) return el.getAttendees();
|
|
3057
|
+
return this.attendees;
|
|
3058
|
+
}
|
|
615
3059
|
connectedCallback() {
|
|
616
3060
|
this.ensureController();
|
|
617
3061
|
super.connectedCallback();
|
|
3062
|
+
this.addEventListener("attendees-change", ((e) => {
|
|
3063
|
+
this.attendees = e.detail?.attendees || [];
|
|
3064
|
+
this.attendeesComplete = Boolean(e.detail?.complete);
|
|
3065
|
+
this.syncStepsChrome();
|
|
3066
|
+
}));
|
|
3067
|
+
this.addEventListener("tickean-change-payment-method", (() => {
|
|
3068
|
+
this.suppressTransferAutoAdvance = true;
|
|
3069
|
+
this.controller?.changePaymentMethod?.();
|
|
3070
|
+
const paymentIdx = this.stepIds().indexOf("payment");
|
|
3071
|
+
if (paymentIdx >= 0) {
|
|
3072
|
+
this.stepIndex = paymentIdx;
|
|
3073
|
+
this.syncStepsChrome();
|
|
3074
|
+
}
|
|
3075
|
+
}));
|
|
618
3076
|
}
|
|
619
3077
|
disconnectedCallback() {
|
|
620
3078
|
super.disconnectedCallback();
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
this.ownedControllerId = null;
|
|
625
|
-
this.ownedController = null;
|
|
626
|
-
}
|
|
3079
|
+
this.teardownOwnedController();
|
|
3080
|
+
this.shellMounted = false;
|
|
3081
|
+
this.shellLayout = null;
|
|
627
3082
|
}
|
|
628
3083
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
629
3084
|
if (["publishable-key", "event-slug", "api-base-url", "return-url", "demo"].includes(
|
|
630
3085
|
name
|
|
631
3086
|
) && oldValue !== newValue) {
|
|
632
|
-
if (this.
|
|
633
|
-
this.
|
|
634
|
-
|
|
635
|
-
this.ownedControllerId = null;
|
|
636
|
-
this.ownedController = null;
|
|
3087
|
+
if (this.isConnected) {
|
|
3088
|
+
this.teardownOwnedController();
|
|
3089
|
+
this.ensureController();
|
|
637
3090
|
}
|
|
638
|
-
|
|
3091
|
+
}
|
|
3092
|
+
if (name === "layout" && oldValue !== newValue) {
|
|
3093
|
+
this.shellMounted = false;
|
|
3094
|
+
this.shellLayout = null;
|
|
3095
|
+
this.stepIndex = 0;
|
|
639
3096
|
}
|
|
640
3097
|
super.attributeChangedCallback(name, oldValue, newValue);
|
|
641
3098
|
}
|
|
3099
|
+
get layout() {
|
|
3100
|
+
const raw = (this.getAttribute("layout") || "steps").toLowerCase();
|
|
3101
|
+
return raw === "stacked" ? "stacked" : "steps";
|
|
3102
|
+
}
|
|
3103
|
+
needsAttendees() {
|
|
3104
|
+
return Boolean(this.state?.event?.collectAttendeeData);
|
|
3105
|
+
}
|
|
3106
|
+
needsTransferStep() {
|
|
3107
|
+
return this.state?.nextAction?.type === "display_instructions";
|
|
3108
|
+
}
|
|
3109
|
+
stepIds() {
|
|
3110
|
+
const ids = ["tickets", "buyer"];
|
|
3111
|
+
if (this.needsAttendees()) ids.push("attendees");
|
|
3112
|
+
ids.push("payment");
|
|
3113
|
+
if (this.needsTransferStep()) ids.push("transfer");
|
|
3114
|
+
ids.push("done");
|
|
3115
|
+
return ids;
|
|
3116
|
+
}
|
|
3117
|
+
currentStep() {
|
|
3118
|
+
return this.stepIds()[this.stepIndex] || "tickets";
|
|
3119
|
+
}
|
|
3120
|
+
teardownOwnedController() {
|
|
3121
|
+
this.ownedController?.dispose();
|
|
3122
|
+
if (this.ownedControllerId) unregisterController(this.ownedControllerId);
|
|
3123
|
+
this.ownedControllerId = null;
|
|
3124
|
+
this.ownedController = null;
|
|
3125
|
+
if (this.hasAttribute(controllerAttr())) this.removeAttribute(controllerAttr());
|
|
3126
|
+
}
|
|
642
3127
|
ensureController() {
|
|
643
|
-
if (this.
|
|
3128
|
+
if (this.ownedController) return;
|
|
644
3129
|
const eventSlug = this.getAttribute("event-slug");
|
|
645
3130
|
const publishableKey = this.getAttribute("publishable-key") || "";
|
|
646
3131
|
if (!eventSlug) return;
|
|
3132
|
+
if (this.getAttribute(controllerAttr())) this.removeAttribute(controllerAttr());
|
|
3133
|
+
const resumeCode = typeof window !== "undefined" ? new URLSearchParams(window.location.search).get("resume") || void 0 : void 0;
|
|
647
3134
|
const options = {
|
|
648
3135
|
publishableKey,
|
|
649
3136
|
eventSlug,
|
|
650
3137
|
apiBaseUrl: this.getAttribute("api-base-url") || void 0,
|
|
651
3138
|
returnUrl: this.getAttribute("return-url") || void 0,
|
|
3139
|
+
resumeCode: resumeCode || void 0,
|
|
652
3140
|
demo: this.hasAttribute("demo"),
|
|
653
3141
|
persistence: false
|
|
654
3142
|
};
|
|
655
3143
|
this.ownedController = createCheckoutController(options);
|
|
656
3144
|
this.ownedControllerId = registerController(this.ownedController);
|
|
657
3145
|
this.setAttribute(controllerAttr(), this.ownedControllerId);
|
|
3146
|
+
if (resumeCode && typeof window !== "undefined") {
|
|
3147
|
+
void this.ownedController.ready.then(() => {
|
|
3148
|
+
const snap = this.ownedController?.getSnapshot();
|
|
3149
|
+
if (!snap || snap.error) return;
|
|
3150
|
+
try {
|
|
3151
|
+
const url = new URL(window.location.href);
|
|
3152
|
+
if (url.searchParams.has("resume")) {
|
|
3153
|
+
url.searchParams.delete("resume");
|
|
3154
|
+
window.history.replaceState(
|
|
3155
|
+
{},
|
|
3156
|
+
"",
|
|
3157
|
+
`${url.pathname}${url.search}${url.hash}`
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
} catch {
|
|
3161
|
+
}
|
|
3162
|
+
const steps = this.stepIds();
|
|
3163
|
+
let target = "tickets";
|
|
3164
|
+
if (snap.phase === "completed") target = "done";
|
|
3165
|
+
else if (snap.nextAction?.type === "display_instructions" || snap.phase === "processing") {
|
|
3166
|
+
target = this.needsTransferStep() ? "transfer" : "payment";
|
|
3167
|
+
} else if (snap.purchase) target = "payment";
|
|
3168
|
+
else if (snap.buyerVerified) {
|
|
3169
|
+
target = this.needsAttendees() ? "attendees" : "payment";
|
|
3170
|
+
} else if ((snap.cart?.length || 0) > 0) target = "buyer";
|
|
3171
|
+
const idx = steps.indexOf(target);
|
|
3172
|
+
if (idx >= 0) {
|
|
3173
|
+
this.stepIndex = idx;
|
|
3174
|
+
this.syncStepsChrome();
|
|
3175
|
+
}
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
restartCheckout() {
|
|
3180
|
+
this.teardownOwnedController();
|
|
3181
|
+
this.stepIndex = 0;
|
|
3182
|
+
this.shellMounted = false;
|
|
3183
|
+
this.shellLayout = null;
|
|
3184
|
+
this.suppressTransferAutoAdvance = false;
|
|
3185
|
+
this.attendees = [];
|
|
3186
|
+
this.attendeesComplete = false;
|
|
3187
|
+
this.ensureController();
|
|
3188
|
+
this.bindController();
|
|
3189
|
+
this.render();
|
|
3190
|
+
}
|
|
3191
|
+
poweredByHtml(locale) {
|
|
3192
|
+
return `
|
|
3193
|
+
<a class="powered" href="${TICKEAN_HOME}" target="_blank" rel="noopener noreferrer">
|
|
3194
|
+
<span class="powered-mark" aria-hidden="true"></span>
|
|
3195
|
+
<span>${escapeHtml7(t(locale, "poweredBy"))} <strong>Tickean</strong></span>
|
|
3196
|
+
</a>
|
|
3197
|
+
`;
|
|
3198
|
+
}
|
|
3199
|
+
eventHeaderHtml() {
|
|
3200
|
+
const event = this.state?.event;
|
|
3201
|
+
if (!event) return "";
|
|
3202
|
+
const locale = this.elementLocale;
|
|
3203
|
+
const location = event.location || {};
|
|
3204
|
+
const timeZone = resolveEventTimeZone(location);
|
|
3205
|
+
const flyer = getEventFlyerUrl(event.images);
|
|
3206
|
+
const { dateLabel, showLines } = buildEventScheduleSummary(
|
|
3207
|
+
event.shows || [],
|
|
3208
|
+
timeZone,
|
|
3209
|
+
locale
|
|
3210
|
+
);
|
|
3211
|
+
const place = [location.venueName, location.city].filter(Boolean).join(" \xB7 ");
|
|
3212
|
+
return `
|
|
3213
|
+
<div class="checkout-header">
|
|
3214
|
+
<div class="event-hero">
|
|
3215
|
+
${flyer ? `<img class="event-flyer" src="${escapeAttr5(flyer)}" alt="${escapeAttr5(event.title || "")}" loading="lazy" />` : `<div class="event-flyer event-flyer-fallback" aria-hidden="true">\u2726</div>`}
|
|
3216
|
+
<div class="event-hero-copy">
|
|
3217
|
+
<h2>${escapeHtml7(event.title || "")}</h2>
|
|
3218
|
+
${dateLabel ? `<p class="event-dates">${escapeHtml7(dateLabel)}</p>` : ""}
|
|
3219
|
+
${place ? `<p class="muted event-place">${escapeHtml7(place)}</p>` : ""}
|
|
3220
|
+
${showLines.length ? `<details class="event-schedule">
|
|
3221
|
+
<summary>${escapeHtml7(t(locale, "showTimes"))}</summary>
|
|
3222
|
+
<ul class="event-show-times">${showLines.map((line) => `<li>${escapeHtml7(line)}</li>`).join("")}</ul>
|
|
3223
|
+
</details>` : ""}
|
|
3224
|
+
</div>
|
|
3225
|
+
</div>
|
|
3226
|
+
</div>
|
|
3227
|
+
`;
|
|
3228
|
+
}
|
|
3229
|
+
loadingShell(locale) {
|
|
3230
|
+
return `
|
|
3231
|
+
<div class="loading-shell" part="checkout-loading">
|
|
3232
|
+
<div class="loading-label">\u2726 ${escapeHtml7(t(locale, "loading"))}</div>
|
|
3233
|
+
<div class="skeleton lg"></div>
|
|
3234
|
+
<div class="skeleton md"></div>
|
|
3235
|
+
<div class="skeleton" style="width:42%"></div>
|
|
3236
|
+
${this.poweredByHtml(locale)}
|
|
3237
|
+
</div>
|
|
3238
|
+
`;
|
|
3239
|
+
}
|
|
3240
|
+
render() {
|
|
3241
|
+
const state = this.state;
|
|
3242
|
+
const layout = this.layout;
|
|
3243
|
+
const locale = this.elementLocale;
|
|
3244
|
+
if (!state || state.loading) {
|
|
3245
|
+
this.shellMounted = false;
|
|
3246
|
+
this.shellLayout = null;
|
|
3247
|
+
this.root.innerHTML = `<style>${this.styles()}</style>${this.loadingShell(locale)}`;
|
|
3248
|
+
return;
|
|
3249
|
+
}
|
|
3250
|
+
if (state.phase === "purchasing") {
|
|
3251
|
+
this.suppressTransferAutoAdvance = false;
|
|
3252
|
+
}
|
|
3253
|
+
if (layout === "steps" && state.phase === "completed") {
|
|
3254
|
+
const doneIndex = this.stepIds().indexOf("done");
|
|
3255
|
+
if (doneIndex >= 0) this.stepIndex = doneIndex;
|
|
3256
|
+
} else if (layout === "steps" && this.needsTransferStep() && state.phase !== "completed" && !this.suppressTransferAutoAdvance) {
|
|
3257
|
+
const transferIndex = this.stepIds().indexOf("transfer");
|
|
3258
|
+
if (transferIndex >= 0) this.stepIndex = transferIndex;
|
|
3259
|
+
}
|
|
3260
|
+
const hasTransferPanel = Boolean(
|
|
3261
|
+
this.root.querySelector('[data-panel="transfer"]')
|
|
3262
|
+
);
|
|
3263
|
+
if (layout === "steps" && this.shellMounted && this.needsTransferStep() !== hasTransferPanel) {
|
|
3264
|
+
this.shellMounted = false;
|
|
3265
|
+
}
|
|
3266
|
+
if (layout === "steps" && this.shellMounted && this.shellLayout === "steps" && this.root.querySelector("[data-tickean-shell='steps']")) {
|
|
3267
|
+
this.syncStepsChrome();
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
this.root.innerHTML = `<style>${this.styles()}</style>${this.renderBody()}`;
|
|
3271
|
+
this.shellMounted = true;
|
|
3272
|
+
this.shellLayout = layout;
|
|
3273
|
+
this.setAttribute("data-animated", "");
|
|
3274
|
+
this.afterRender();
|
|
658
3275
|
}
|
|
659
3276
|
renderBody() {
|
|
3277
|
+
return this.layout === "stacked" ? this.renderStacked() : this.renderSteps();
|
|
3278
|
+
}
|
|
3279
|
+
sharedChildAttrs(id, locale, appearance) {
|
|
3280
|
+
const apiBase = this.getAttribute("api-base-url") || "https://api.tickean.com";
|
|
3281
|
+
return `controller-id="${escapeAttr5(id)}" locale="${escapeAttr5(locale)}" appearance="${escapeAttr5(appearance)}" api-base-url="${escapeAttr5(apiBase)}"`;
|
|
3282
|
+
}
|
|
3283
|
+
renderStacked() {
|
|
660
3284
|
const id = this.getAttribute(controllerAttr()) || "";
|
|
661
3285
|
const locale = this.getAttribute("locale") || "es-AR";
|
|
662
3286
|
const appearance = this.getAttribute("appearance") || "default";
|
|
663
3287
|
const paymentMethod = this.getAttribute("payment-method") || "TRANSFER";
|
|
664
3288
|
const currency = this.getAttribute("currency") || "ARS";
|
|
665
3289
|
const state = this.state;
|
|
666
|
-
|
|
667
|
-
|
|
3290
|
+
const attrs = this.sharedChildAttrs(id, locale, appearance);
|
|
3291
|
+
return `
|
|
3292
|
+
<div class="stack checkout-steps" part="checkout" data-tickean-shell="stacked">
|
|
3293
|
+
${this.eventHeaderHtml()}
|
|
3294
|
+
<tickean-discount ${attrs} mode="unlock"></tickean-discount>
|
|
3295
|
+
<tickean-ticket-selector ${attrs}></tickean-ticket-selector>
|
|
3296
|
+
<tickean-discount ${attrs} mode="discount"></tickean-discount>
|
|
3297
|
+
<tickean-buyer-verification ${attrs}></tickean-buyer-verification>
|
|
3298
|
+
${this.needsAttendees() ? `<tickean-attendees ${attrs}></tickean-attendees>` : ""}
|
|
3299
|
+
<details class="summary-drawer">
|
|
3300
|
+
<summary class="summary-drawer-toggle">
|
|
3301
|
+
<span>${escapeHtml7(t(this.elementLocale, "viewSummary"))}</span>
|
|
3302
|
+
<strong>${escapeHtml7(this.money(Number(state.quote?.totalPrice || 0)))}</strong>
|
|
3303
|
+
</summary>
|
|
3304
|
+
<div class="summary-drawer-body">
|
|
3305
|
+
<tickean-order-summary ${attrs}></tickean-order-summary>
|
|
3306
|
+
</div>
|
|
3307
|
+
</details>
|
|
3308
|
+
<tickean-payment ${attrs} view="method" payment-method="${escapeAttr5(paymentMethod)}" currency="${escapeAttr5(currency)}"></tickean-payment>
|
|
3309
|
+
${this.needsTransferStep() ? `<tickean-payment ${attrs} view="transfer" payment-method="${escapeAttr5(paymentMethod)}" currency="${escapeAttr5(currency)}"></tickean-payment>` : ""}
|
|
3310
|
+
${this.poweredByHtml(this.elementLocale)}
|
|
3311
|
+
</div>
|
|
3312
|
+
`;
|
|
3313
|
+
}
|
|
3314
|
+
stepLabel(id, locale) {
|
|
3315
|
+
switch (id) {
|
|
3316
|
+
case "tickets":
|
|
3317
|
+
return t(locale, "stepTickets");
|
|
3318
|
+
case "buyer":
|
|
3319
|
+
return t(locale, "stepBuyer");
|
|
3320
|
+
case "attendees":
|
|
3321
|
+
return t(locale, "attendees");
|
|
3322
|
+
case "payment":
|
|
3323
|
+
return t(locale, "stepPayment");
|
|
3324
|
+
case "transfer":
|
|
3325
|
+
return t(locale, "stepTransfer");
|
|
3326
|
+
case "done":
|
|
3327
|
+
return t(locale, "stepDone");
|
|
668
3328
|
}
|
|
3329
|
+
}
|
|
3330
|
+
renderSteps() {
|
|
3331
|
+
const id = this.getAttribute(controllerAttr()) || "";
|
|
3332
|
+
const locale = this.elementLocale;
|
|
3333
|
+
const appearance = this.getAttribute("appearance") || "default";
|
|
3334
|
+
const paymentMethod = this.getAttribute("payment-method") || "TRANSFER";
|
|
3335
|
+
const currency = this.getAttribute("currency") || "ARS";
|
|
3336
|
+
const state = this.state;
|
|
3337
|
+
const attrs = this.sharedChildAttrs(id, locale, appearance);
|
|
3338
|
+
const steps = this.stepIds();
|
|
3339
|
+
const step = this.currentStep();
|
|
3340
|
+
const progress = Math.round((this.stepIndex + 1) / steps.length * 100);
|
|
3341
|
+
const stepper = steps.map((sid, index) => {
|
|
3342
|
+
const active = sid === step;
|
|
3343
|
+
const done = index < this.stepIndex;
|
|
3344
|
+
return `
|
|
3345
|
+
<li class="stepper-item" data-step-id="${sid}" data-active="${active}" data-done="${done}">
|
|
3346
|
+
<span class="stepper-dot" aria-hidden="true">${done ? "\u2713" : index + 1}</span>
|
|
3347
|
+
<span class="stepper-label">${escapeHtml7(this.stepLabel(sid, locale))}</span>
|
|
3348
|
+
</li>`;
|
|
3349
|
+
}).join("");
|
|
3350
|
+
const dockTotal = this.money(Number(state.quote?.totalPrice || 0));
|
|
669
3351
|
return `
|
|
670
|
-
<div class="
|
|
671
|
-
|
|
672
|
-
<
|
|
673
|
-
<
|
|
674
|
-
<
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
<
|
|
678
|
-
|
|
3352
|
+
<div class="checkout-steps compact" part="checkout" data-tickean-shell="steps" style="--tickean-progress:${progress}%">
|
|
3353
|
+
${this.eventHeaderHtml()}
|
|
3354
|
+
<div class="progress" aria-hidden="true"><span data-progress></span></div>
|
|
3355
|
+
<ol class="stepper" aria-label="Checkout" style="grid-template-columns:repeat(${steps.length},minmax(0,1fr))">${stepper}</ol>
|
|
3356
|
+
<p class="step-caption muted" data-step-caption>
|
|
3357
|
+
${escapeHtml7(tFormat(locale, "stepOf", { current: this.stepIndex + 1, total: steps.length }))}
|
|
3358
|
+
</p>
|
|
3359
|
+
<div class="checkout-layout">
|
|
3360
|
+
<aside class="checkout-aside compact-aside" ${step === "done" ? "hidden" : ""} data-aside>
|
|
3361
|
+
<details class="summary-drawer">
|
|
3362
|
+
<summary class="summary-drawer-toggle">
|
|
3363
|
+
<span>${escapeHtml7(t(locale, "viewSummary"))}</span>
|
|
3364
|
+
<strong data-dock-total>${escapeHtml7(dockTotal)}</strong>
|
|
3365
|
+
</summary>
|
|
3366
|
+
<div class="summary-drawer-body">
|
|
3367
|
+
<tickean-order-summary ${attrs}></tickean-order-summary>
|
|
3368
|
+
</div>
|
|
3369
|
+
</details>
|
|
3370
|
+
</aside>
|
|
3371
|
+
<div class="step-panels">
|
|
3372
|
+
<section class="step-panel stack" data-panel="tickets" ${step === "tickets" ? "" : "hidden"}>
|
|
3373
|
+
<tickean-discount ${attrs} mode="unlock"></tickean-discount>
|
|
3374
|
+
<tickean-ticket-selector ${attrs}></tickean-ticket-selector>
|
|
3375
|
+
</section>
|
|
3376
|
+
<section class="step-panel stack" data-panel="buyer" ${step === "buyer" ? "" : "hidden"}>
|
|
3377
|
+
<tickean-discount ${attrs} mode="discount"></tickean-discount>
|
|
3378
|
+
<tickean-buyer-verification ${attrs}></tickean-buyer-verification>
|
|
3379
|
+
</section>
|
|
3380
|
+
<section class="step-panel stack" data-panel="attendees" ${step === "attendees" ? "" : "hidden"}>
|
|
3381
|
+
<tickean-attendees ${attrs}></tickean-attendees>
|
|
3382
|
+
</section>
|
|
3383
|
+
<section class="step-panel stack" data-panel="payment" ${step === "payment" ? "" : "hidden"}>
|
|
3384
|
+
<tickean-payment ${attrs} view="method" payment-method="${escapeAttr5(paymentMethod)}" currency="${escapeAttr5(currency)}"></tickean-payment>
|
|
3385
|
+
</section>
|
|
3386
|
+
${this.needsTransferStep() ? `<section class="step-panel stack" data-panel="transfer" ${step === "transfer" ? "" : "hidden"}>
|
|
3387
|
+
<tickean-payment ${attrs} view="transfer" payment-method="${escapeAttr5(paymentMethod)}" currency="${escapeAttr5(currency)}"></tickean-payment>
|
|
3388
|
+
</section>` : ""}
|
|
3389
|
+
<section class="step-panel" data-panel="done" ${step === "done" ? "" : "hidden"}>
|
|
3390
|
+
<div class="done-card">
|
|
3391
|
+
<div class="done-icon" aria-hidden="true">\u2713</div>
|
|
3392
|
+
<h3>${escapeHtml7(t(locale, "completed"))}</h3>
|
|
3393
|
+
<p class="muted">${escapeHtml7(t(locale, "doneSubtitle"))}</p>
|
|
3394
|
+
<div data-done-summary class="muted"></div>
|
|
3395
|
+
<div class="done-actions">
|
|
3396
|
+
<a class="done-primary" href="${TICKEAN_TICKETS_URL}" target="_blank" rel="noopener noreferrer">
|
|
3397
|
+
${escapeHtml7(t(locale, "viewTickets"))}
|
|
3398
|
+
</a>
|
|
3399
|
+
<button type="button" class="secondary" data-action="buy-again">
|
|
3400
|
+
${escapeHtml7(t(locale, "buyAgain"))}
|
|
3401
|
+
</button>
|
|
3402
|
+
</div>
|
|
3403
|
+
</div>
|
|
3404
|
+
</section>
|
|
3405
|
+
<div class="danger" role="alert" data-error ${state.error ? "" : "hidden"}>
|
|
3406
|
+
${state.error ? escapeHtml7(state.error.message) : ""}
|
|
3407
|
+
</div>
|
|
3408
|
+
</div>
|
|
3409
|
+
</div>
|
|
3410
|
+
<div class="checkout-sticky" data-sticky ${step === "done" ? "hidden" : ""}>
|
|
3411
|
+
<div class="checkout-dock" data-step-nav ${step === "transfer" ? "hidden" : ""}>
|
|
3412
|
+
<div class="dock-side dock-side-start">
|
|
3413
|
+
<button type="button" class="secondary dock-back" data-action="back" ${this.stepIndex <= 0 ? "hidden" : ""}>
|
|
3414
|
+
${escapeHtml7(t(locale, "back"))}
|
|
3415
|
+
</button>
|
|
3416
|
+
</div>
|
|
3417
|
+
<div class="dock-total">
|
|
3418
|
+
<span class="muted">${escapeHtml7(t(locale, "total"))}</span>
|
|
3419
|
+
<strong data-dock-total>${escapeHtml7(dockTotal)}</strong>
|
|
3420
|
+
</div>
|
|
3421
|
+
<div class="dock-side dock-side-end">
|
|
3422
|
+
<button type="button" class="dock-next" data-action="next" ${step === "payment" || step === "transfer" || step === "done" ? "hidden" : ""} ${this.canGoNext() ? "" : "disabled"}>
|
|
3423
|
+
${escapeHtml7(t(locale, "next"))}
|
|
3424
|
+
</button>
|
|
3425
|
+
</div>
|
|
3426
|
+
</div>
|
|
3427
|
+
${this.poweredByHtml(locale)}
|
|
3428
|
+
</div>
|
|
679
3429
|
</div>
|
|
680
3430
|
`;
|
|
681
3431
|
}
|
|
3432
|
+
canGoNext() {
|
|
3433
|
+
const state = this.state;
|
|
3434
|
+
if (!state) return false;
|
|
3435
|
+
const step = this.currentStep();
|
|
3436
|
+
if (step === "tickets") return (state.cart?.length || 0) > 0;
|
|
3437
|
+
if (step === "buyer") return Boolean(state.buyerVerified);
|
|
3438
|
+
if (step === "attendees") return this.attendeesComplete || this.getAttendeesCompleteFromDom();
|
|
3439
|
+
return false;
|
|
3440
|
+
}
|
|
3441
|
+
getAttendeesCompleteFromDom() {
|
|
3442
|
+
const el = this.root.querySelector("tickean-attendees");
|
|
3443
|
+
return Boolean(el?.isComplete?.());
|
|
3444
|
+
}
|
|
3445
|
+
syncStepsChrome() {
|
|
3446
|
+
const locale = this.elementLocale;
|
|
3447
|
+
const steps = this.stepIds();
|
|
3448
|
+
if (this.stepIndex >= steps.length) this.stepIndex = steps.length - 1;
|
|
3449
|
+
const step = this.currentStep();
|
|
3450
|
+
const state = this.state;
|
|
3451
|
+
const progress = Math.round((this.stepIndex + 1) / steps.length * 100);
|
|
3452
|
+
const shell = this.root.querySelector("[data-tickean-shell='steps']");
|
|
3453
|
+
if (shell) shell.style.setProperty("--tickean-progress", `${progress}%`);
|
|
3454
|
+
this.root.querySelectorAll(".stepper-item").forEach((item) => {
|
|
3455
|
+
const sid = item.getAttribute("data-step-id");
|
|
3456
|
+
const index = steps.indexOf(sid);
|
|
3457
|
+
item.dataset.active = String(sid === step);
|
|
3458
|
+
item.dataset.done = String(index < this.stepIndex);
|
|
3459
|
+
const dot = item.querySelector(".stepper-dot");
|
|
3460
|
+
if (dot) dot.textContent = index < this.stepIndex ? "\u2713" : String(index + 1);
|
|
3461
|
+
});
|
|
3462
|
+
const caption = this.root.querySelector("[data-step-caption]");
|
|
3463
|
+
if (caption) {
|
|
3464
|
+
caption.textContent = tFormat(locale, "stepOf", {
|
|
3465
|
+
current: this.stepIndex + 1,
|
|
3466
|
+
total: steps.length
|
|
3467
|
+
});
|
|
3468
|
+
}
|
|
3469
|
+
this.root.querySelectorAll(".step-panel").forEach((panel) => {
|
|
3470
|
+
const id = panel.getAttribute("data-panel");
|
|
3471
|
+
panel.hidden = id !== step;
|
|
3472
|
+
});
|
|
3473
|
+
const sticky = this.root.querySelector("[data-sticky]");
|
|
3474
|
+
if (sticky) sticky.hidden = step === "done";
|
|
3475
|
+
const nav = this.root.querySelector("[data-step-nav]");
|
|
3476
|
+
if (nav) nav.hidden = step === "transfer";
|
|
3477
|
+
const aside = this.root.querySelector("[data-aside]");
|
|
3478
|
+
if (aside) aside.hidden = step === "done";
|
|
3479
|
+
const back = this.root.querySelector('[data-action="back"]');
|
|
3480
|
+
if (back) {
|
|
3481
|
+
back.hidden = this.stepIndex <= 0 || step === "done" || step === "transfer";
|
|
3482
|
+
back.disabled = state?.phase === "purchasing";
|
|
3483
|
+
}
|
|
3484
|
+
const next = this.root.querySelector('[data-action="next"]');
|
|
3485
|
+
if (next) {
|
|
3486
|
+
next.hidden = step === "payment" || step === "transfer" || step === "done";
|
|
3487
|
+
next.disabled = !this.canGoNext();
|
|
3488
|
+
}
|
|
3489
|
+
const drawer = this.root.querySelector("details.summary-drawer");
|
|
3490
|
+
if (drawer && !drawer.dataset.userToggled) {
|
|
3491
|
+
drawer.open = false;
|
|
3492
|
+
}
|
|
3493
|
+
const dockTotal = this.money(Number(state?.quote?.totalPrice || 0));
|
|
3494
|
+
this.root.querySelectorAll("[data-dock-total]").forEach((el) => {
|
|
3495
|
+
el.textContent = dockTotal;
|
|
3496
|
+
});
|
|
3497
|
+
const errorEl = this.root.querySelector("[data-error]");
|
|
3498
|
+
if (errorEl) {
|
|
3499
|
+
if (state?.error) {
|
|
3500
|
+
errorEl.hidden = false;
|
|
3501
|
+
errorEl.textContent = state.error.message;
|
|
3502
|
+
} else {
|
|
3503
|
+
errorEl.hidden = true;
|
|
3504
|
+
errorEl.textContent = "";
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
const doneSummary = this.root.querySelector("[data-done-summary]");
|
|
3508
|
+
if (doneSummary && step === "done") {
|
|
3509
|
+
const purchaseId = state?.purchase?.id;
|
|
3510
|
+
doneSummary.textContent = purchaseId ? `#${purchaseId}` : "";
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
afterRender() {
|
|
3514
|
+
this.root.querySelector('[data-action="buy-again"]')?.addEventListener("click", () => {
|
|
3515
|
+
this.restartCheckout();
|
|
3516
|
+
});
|
|
3517
|
+
if (this.layout !== "steps") return;
|
|
3518
|
+
this.root.querySelector("details.summary-drawer")?.addEventListener("toggle", (ev) => {
|
|
3519
|
+
const details = ev.currentTarget;
|
|
3520
|
+
details.dataset.userToggled = "1";
|
|
3521
|
+
});
|
|
3522
|
+
this.root.querySelector('[data-action="back"]')?.addEventListener("click", () => {
|
|
3523
|
+
if (this.state?.phase === "purchasing") return;
|
|
3524
|
+
if (this.stepIndex > 0) {
|
|
3525
|
+
this.stepIndex -= 1;
|
|
3526
|
+
this.syncStepsChrome();
|
|
3527
|
+
}
|
|
3528
|
+
});
|
|
3529
|
+
this.root.querySelector('[data-action="next"]')?.addEventListener("click", () => {
|
|
3530
|
+
if (!this.canGoNext()) return;
|
|
3531
|
+
const steps = this.stepIds();
|
|
3532
|
+
if (this.stepIndex < steps.length - 1) {
|
|
3533
|
+
this.stepIndex += 1;
|
|
3534
|
+
if (this.currentStep() === "done") this.stepIndex -= 1;
|
|
3535
|
+
this.syncStepsChrome();
|
|
3536
|
+
}
|
|
3537
|
+
});
|
|
3538
|
+
}
|
|
682
3539
|
};
|
|
683
|
-
function
|
|
3540
|
+
function escapeAttr5(value) {
|
|
684
3541
|
return value.replace(/"/g, """);
|
|
685
3542
|
}
|
|
686
|
-
function
|
|
3543
|
+
function escapeHtml7(value) {
|
|
687
3544
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
688
3545
|
}
|
|
689
3546
|
if (typeof customElements !== "undefined" && !customElements.get("tickean-checkout")) {
|
|
@@ -696,6 +3553,7 @@ var ELEMENT_TAGS = [
|
|
|
696
3553
|
"tickean-ticket-selector",
|
|
697
3554
|
"tickean-discount",
|
|
698
3555
|
"tickean-buyer-verification",
|
|
3556
|
+
"tickean-attendees",
|
|
699
3557
|
"tickean-payment",
|
|
700
3558
|
"tickean-order-summary"
|
|
701
3559
|
];
|
|
@@ -717,6 +3575,7 @@ function detachController(id) {
|
|
|
717
3575
|
}
|
|
718
3576
|
defineTickeanElements();
|
|
719
3577
|
export {
|
|
3578
|
+
TickeanAttendees,
|
|
720
3579
|
TickeanBuyerVerification,
|
|
721
3580
|
TickeanCheckout,
|
|
722
3581
|
TickeanDiscount,
|