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