@wral/report-it 0.1.2 → 0.1.4
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/README.md +40 -1
- package/dist/define.mjs +1 -1
- package/dist/define.standalone.js +1290 -1111
- package/dist/index.mjs +94 -80
- package/dist/{wral-code-input-CC2ZCxcs.js → wral-code-input-DCemMj53.js} +650 -471
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { css as
|
|
2
|
-
import { getAuthContext as
|
|
3
|
-
const
|
|
1
|
+
import { css as f, html as a, nothing as c, LitElement as T } from "lit";
|
|
2
|
+
import { getAuthContext as Q, createStorage as G, verifyCode as J, extractTokens as D, saveTokens as z, decodeJwtPayload as B, refreshTokenSimple as X, sendCode as Z, createUserPool as ee, signUpIfNeeded as te, createCognitoUser as re, getAuthHeaders as se, STORAGE_KEYS as I, isAuthenticatedSimple as ie } from "@wral/sdk-auth-audience";
|
|
3
|
+
const ne = f`
|
|
4
4
|
:host {
|
|
5
5
|
display: block;
|
|
6
6
|
box-sizing: border-box;
|
|
@@ -24,7 +24,7 @@ const Z = p`
|
|
|
24
24
|
position: absolute; width: 1px; height: 1px; overflow: hidden;
|
|
25
25
|
clip: rect(0 0 0 0); white-space: nowrap;
|
|
26
26
|
}
|
|
27
|
-
`,
|
|
27
|
+
`, ae = f`
|
|
28
28
|
@keyframes pop-in {
|
|
29
29
|
0% { opacity: 0; transform: translateY(16px) scale(0.95); }
|
|
30
30
|
100% { opacity: 1; transform: translateY(0) scale(1); }
|
|
@@ -55,7 +55,7 @@ const Z = p`
|
|
|
55
55
|
animation: none !important;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
`,
|
|
58
|
+
`, oe = f`
|
|
59
59
|
.msg { max-width: 85%; padding: 12px 16px; border-radius: 14px; font-size: 15px; }
|
|
60
60
|
.msg.bot {
|
|
61
61
|
background: var(--wral-semantic-surface-default, #FFFFFF);
|
|
@@ -77,7 +77,7 @@ const Z = p`
|
|
|
77
77
|
.msg .sub { display: block; font-size: 13px; color: var(--wral-semantic-fg-muted, #64748B); margin-top: 4px; }
|
|
78
78
|
.msg.user .sub { color: rgba(255, 255, 255, 0.75); }
|
|
79
79
|
.msg.error { border-left: 3px solid var(--wral-base-red-on-light, #D1232A); }
|
|
80
|
-
`,
|
|
80
|
+
`, le = f`
|
|
81
81
|
.options { display: flex; flex-wrap: wrap; gap: 8px; align-self: flex-start; max-width: 92%; padding: 2px 0 6px; }
|
|
82
82
|
.chip {
|
|
83
83
|
font-family: var(--wral-base-font-ui, 'IBM Plex Sans Condensed', sans-serif);
|
|
@@ -91,7 +91,7 @@ const Z = p`
|
|
|
91
91
|
.chip:hover { background: var(--wral-base-secondary-1, #2594E3); color: #FFFFFF; }
|
|
92
92
|
.chip:focus-visible { outline: 2px solid var(--wral-base-secondary-1, #2594E3); outline-offset: 2px; }
|
|
93
93
|
.chip:active { transform: scale(0.97); }
|
|
94
|
-
`,
|
|
94
|
+
`, de = f`
|
|
95
95
|
.form-card {
|
|
96
96
|
align-self: stretch;
|
|
97
97
|
background: var(--wral-semantic-surface-default, #FFFFFF);
|
|
@@ -156,7 +156,7 @@ const Z = p`
|
|
|
156
156
|
.btn-ghost:hover { border-color: var(--wral-base-secondary-1, #2594E3); }
|
|
157
157
|
.btn-ghost:active { transform: scale(0.97); }
|
|
158
158
|
.btn-ghost:focus-visible { outline: 2px solid var(--wral-base-secondary-1, #2594E3); outline-offset: 2px; }
|
|
159
|
-
`,
|
|
159
|
+
`, ce = f`
|
|
160
160
|
.login-card { padding: 28px 26px; }
|
|
161
161
|
.login-card h2 {
|
|
162
162
|
font-family: var(--wral-base-font-display, 'IBM Plex Sans Condensed', sans-serif);
|
|
@@ -202,13 +202,48 @@ const Z = p`
|
|
|
202
202
|
.login-why ul { margin: 0; padding-left: 20px; font-size: 14px; color: var(--color-gray-6, #565454); }
|
|
203
203
|
.login-why li { margin-bottom: 4px; line-height: 1.5; }
|
|
204
204
|
.login-why a { color: var(--color-secondary, #2594E3); font-weight: 600; text-decoration: underline; }
|
|
205
|
-
.login-
|
|
206
|
-
border-top: 1px solid var(--color-gray-2, #E7E5E4); margin: 16px
|
|
207
|
-
font-size: 14px; color: var(--color-gray-6, #565454); line-height: 1.5;
|
|
205
|
+
.login-alt {
|
|
206
|
+
border-top: 1px solid var(--color-gray-2, #E7E5E4); margin-top: 16px; padding-top: 12px;
|
|
208
207
|
}
|
|
209
|
-
.login-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
.login-alt > summary {
|
|
209
|
+
display: flex; align-items: center; gap: 7px;
|
|
210
|
+
font-size: 14px; font-weight: 600;
|
|
211
|
+
color: var(--color-gray-5, #6C6C6C);
|
|
212
|
+
padding: 3px 0; cursor: pointer; list-style: none;
|
|
213
|
+
}
|
|
214
|
+
.login-alt > summary::-webkit-details-marker { display: none; }
|
|
215
|
+
.login-alt > summary::before {
|
|
216
|
+
content: ''; flex-shrink: 0; padding: 2.5px;
|
|
217
|
+
border: solid currentColor; border-width: 0 1.5px 1.5px 0;
|
|
218
|
+
transform: rotate(-45deg); transition: transform 150ms;
|
|
219
|
+
}
|
|
220
|
+
.login-alt[open] > summary::before { transform: rotate(45deg); }
|
|
221
|
+
.login-alt > summary:hover { color: var(--color-gray-8, #263238); }
|
|
222
|
+
.login-alt > summary:focus-visible { outline: 2px solid var(--color-secondary, #2594E3); outline-offset: 2px; }
|
|
223
|
+
.login-alt .alt-body {
|
|
224
|
+
font-size: 13.5px; color: var(--color-gray-6, #565454);
|
|
225
|
+
line-height: 1.55; padding: 6px 0 2px;
|
|
226
|
+
}
|
|
227
|
+
.login-alt .alt-row { margin: 0 0 10px; }
|
|
228
|
+
.login-alt .alt-row:last-child { margin-bottom: 0; }
|
|
229
|
+
.login-alt a, .login-alt .alt-link {
|
|
230
|
+
color: var(--color-primary-dark, #00144A); font-weight: 600;
|
|
231
|
+
text-decoration: underline;
|
|
232
|
+
}
|
|
233
|
+
.login-alt a { text-decoration: none; white-space: nowrap; }
|
|
234
|
+
.login-alt a:hover { text-decoration: underline; }
|
|
235
|
+
.login-alt .alt-link {
|
|
236
|
+
background: none; border: none; padding: 0; cursor: pointer;
|
|
237
|
+
font-family: inherit; font-size: inherit;
|
|
238
|
+
}
|
|
239
|
+
.login-alt .alt-link:focus-visible { outline: 2px solid var(--color-secondary, #2594E3); outline-offset: 2px; }
|
|
240
|
+
.anon-costs { margin: 0 0 18px; padding: 0; list-style: none; font-size: 14px; color: var(--color-gray-6, #565454); }
|
|
241
|
+
.anon-costs li { display: flex; gap: 9px; align-items: flex-start; margin-bottom: 9px; line-height: 1.5; }
|
|
242
|
+
.anon-costs li:last-child { margin-bottom: 0; }
|
|
243
|
+
.anon-costs .x { flex-shrink: 0; font-weight: 700; color: var(--wral-base-red-on-light, #D1232A); }
|
|
244
|
+
.anon-actions { display: flex; flex-direction: column; gap: 10px; }
|
|
245
|
+
.anon-actions .btn-ghost { width: 100%; }
|
|
246
|
+
`, ue = f`
|
|
212
247
|
.review-list { margin: 0; padding: 0; list-style: none; font-size: 14px; }
|
|
213
248
|
.review-list li {
|
|
214
249
|
display: grid; grid-template-columns: 130px 1fr; gap: 10px;
|
|
@@ -240,7 +275,7 @@ const Z = p`
|
|
|
240
275
|
color: var(--wral-base-primary-1, #001D68);
|
|
241
276
|
}
|
|
242
277
|
@media (max-width: 540px) { .review-list li { grid-template-columns: 1fr; gap: 2px; } }
|
|
243
|
-
`,
|
|
278
|
+
`, pe = f`
|
|
244
279
|
.upload-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: 13.5px; }
|
|
245
280
|
.upload-row .name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
246
281
|
.upload-row .state { font-size: 12px; color: var(--wral-semantic-fg-subtle, #64748B); }
|
|
@@ -256,7 +291,7 @@ const Z = p`
|
|
|
256
291
|
border-radius: 999px; padding: 3px 12px; cursor: pointer;
|
|
257
292
|
}
|
|
258
293
|
.retry-chip:focus-visible { outline: 2px solid var(--wral-base-secondary-1, #2594E3); outline-offset: 2px; }
|
|
259
|
-
`,
|
|
294
|
+
`, he = f`
|
|
260
295
|
.confirm-card {
|
|
261
296
|
align-self: stretch;
|
|
262
297
|
background: var(--wral-base-primary-1, #001D68);
|
|
@@ -306,7 +341,7 @@ const Z = p`
|
|
|
306
341
|
.c-again:hover { background: rgba(255, 255, 255, 0.12); }
|
|
307
342
|
.c-again:focus-visible { outline: 2px solid var(--wral-base-secondary-2, #50A9E8); outline-offset: 2px; }
|
|
308
343
|
.c-again:active { transform: scale(0.97); }
|
|
309
|
-
`,
|
|
344
|
+
`, fe = f`
|
|
310
345
|
.journey {
|
|
311
346
|
align-self: stretch;
|
|
312
347
|
background: var(--wral-semantic-surface-default, #FFFFFF);
|
|
@@ -347,7 +382,7 @@ const Z = p`
|
|
|
347
382
|
color: var(--wral-base-primary-1, #001D68); margin-bottom: 2px;
|
|
348
383
|
}
|
|
349
384
|
.j-body .j-d { font-size: 13.5px; color: var(--wral-semantic-fg-muted, #475569); line-height: 1.55; }
|
|
350
|
-
`,
|
|
385
|
+
`, ge = f`
|
|
351
386
|
.bottom-bar {
|
|
352
387
|
position: sticky; bottom: 0; z-index: 20;
|
|
353
388
|
background: var(--wral-semantic-surface-default, #FFFFFF);
|
|
@@ -360,118 +395,145 @@ const Z = p`
|
|
|
360
395
|
}
|
|
361
396
|
.bottom-bar .note { margin-left: auto; font-size: 12px; color: var(--wral-semantic-fg-subtle, #64748B); }
|
|
362
397
|
@media (max-width: 540px) { .bottom-bar .note { display: none; } }
|
|
363
|
-
`,
|
|
398
|
+
`, me = [ne, ae, oe, le, de, ce, ue, pe, he, fe, ge];
|
|
364
399
|
function h(r, e, { noAnim: t = !1, error: s = !1 } = {}) {
|
|
365
|
-
return
|
|
400
|
+
return a`
|
|
366
401
|
<div class="msg bot ${t ? "no-anim" : ""} ${s ? "error" : ""}">
|
|
367
402
|
<span class="lead">${r}</span>
|
|
368
|
-
${e ?
|
|
403
|
+
${e ? a`<span class="sub">${e}</span>` : c}
|
|
369
404
|
</div>
|
|
370
405
|
`;
|
|
371
406
|
}
|
|
372
|
-
function
|
|
373
|
-
return
|
|
407
|
+
function $(r, { noAnim: e = !1 } = {}) {
|
|
408
|
+
return a`<div class="msg user ${e ? "no-anim" : ""}">${r}</div>`;
|
|
374
409
|
}
|
|
375
|
-
function
|
|
376
|
-
return
|
|
410
|
+
function E() {
|
|
411
|
+
return a`<div class="msg bot typing"><span></span><span></span><span></span></div>`;
|
|
377
412
|
}
|
|
378
|
-
function
|
|
379
|
-
return r ?
|
|
413
|
+
function be(r) {
|
|
414
|
+
return r ? a`<span class="flag-pill ${r}">${r === "urgent" ? "Urgent" : "Moderate"}</span>` : c;
|
|
380
415
|
}
|
|
381
|
-
function
|
|
382
|
-
return
|
|
416
|
+
function ye() {
|
|
417
|
+
return a`
|
|
383
418
|
<svg viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" stroke-width="3"
|
|
384
419
|
stroke-linecap="round" stroke-linejoin="round" width="20" height="20"
|
|
385
420
|
aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
|
|
386
421
|
`;
|
|
387
422
|
}
|
|
388
|
-
|
|
423
|
+
const we = 15e3;
|
|
424
|
+
class x extends Error {
|
|
389
425
|
constructor(e, t) {
|
|
390
426
|
super(`api-forms request failed with status ${e}`), this.status = e, this.body = t;
|
|
391
427
|
}
|
|
392
428
|
}
|
|
393
|
-
|
|
429
|
+
const P = Object.freeze({ enabled: !1, humanCheck: null });
|
|
430
|
+
function xe({
|
|
394
431
|
baseUrl: r,
|
|
395
432
|
fetchFn: e = (...i) => fetch(...i),
|
|
396
433
|
xhrFactory: t = () => new XMLHttpRequest(),
|
|
397
434
|
getAuthHeaders: s
|
|
398
435
|
}) {
|
|
399
436
|
const i = String(r || "").replace(/\/+$/, "");
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
437
|
+
let n = P, o = null;
|
|
438
|
+
async function l(d, { method: u = "GET", body: g, auth: y = !1, timeoutMs: p } = {}) {
|
|
439
|
+
const k = { Accept: "application/json" };
|
|
440
|
+
y && Object.assign(k, o ? { Authorization: `Bearer ${o}` } : await s()), g !== void 0 && (k["Content-Type"] = "application/json");
|
|
441
|
+
let w;
|
|
404
442
|
try {
|
|
405
|
-
|
|
406
|
-
method:
|
|
407
|
-
headers:
|
|
408
|
-
...
|
|
443
|
+
w = await e(`${i}${d}`, {
|
|
444
|
+
method: u,
|
|
445
|
+
headers: k,
|
|
446
|
+
...p ? { signal: AbortSignal.timeout(p) } : {},
|
|
447
|
+
...g !== void 0 ? { body: JSON.stringify(g) } : {}
|
|
409
448
|
});
|
|
410
|
-
} catch (
|
|
411
|
-
throw Object.assign(new
|
|
449
|
+
} catch (_) {
|
|
450
|
+
throw Object.assign(new x(0, null), { cause: _ });
|
|
412
451
|
}
|
|
413
|
-
let
|
|
452
|
+
let m = null;
|
|
414
453
|
try {
|
|
415
|
-
|
|
454
|
+
m = await w.json();
|
|
416
455
|
} catch {
|
|
417
456
|
}
|
|
418
|
-
if (!
|
|
419
|
-
throw new w
|
|
420
|
-
return
|
|
457
|
+
if (!w.ok)
|
|
458
|
+
throw new x(w.status, m);
|
|
459
|
+
return m;
|
|
421
460
|
}
|
|
422
461
|
return {
|
|
423
462
|
/** @returns {Promise<Array>} published form documents */
|
|
424
463
|
listForms: async () => {
|
|
425
|
-
const
|
|
426
|
-
return
|
|
464
|
+
const d = await l("/v1/forms", { timeoutMs: we });
|
|
465
|
+
return n = d?.anonymous?.enabled === !0 ? { enabled: !0, humanCheck: d.anonymous.humanCheck || null } : P, (d?.forms || []).filter((u) => u?.formId && u?.start && Array.isArray(u?.nodes));
|
|
466
|
+
},
|
|
467
|
+
/**
|
|
468
|
+
* What the server advertised on the last listForms.
|
|
469
|
+
* @returns {{enabled: boolean, humanCheck: {provider, siteKey}|null}}
|
|
470
|
+
*/
|
|
471
|
+
anonymousCapability: () => n,
|
|
472
|
+
/**
|
|
473
|
+
* Mint a submission-scoped anonymous session; subsequent auth-bearing
|
|
474
|
+
* calls present it until endAnonSession(). Unauthenticated by design.
|
|
475
|
+
* @returns {Promise<{expiresIn: number}>}
|
|
476
|
+
*/
|
|
477
|
+
createAnonSession: async ({ formId: d, requestId: u, humanToken: g }) => {
|
|
478
|
+
const y = await l("/v1/anon/sessions", {
|
|
479
|
+
method: "POST",
|
|
480
|
+
body: { formId: d, requestId: u, ...g ? { humanToken: g } : {} }
|
|
481
|
+
});
|
|
482
|
+
if (o = y?.token || null, !o)
|
|
483
|
+
throw new x(502, { message: "The anonymous session had no token" });
|
|
484
|
+
return { expiresIn: y.expiresIn };
|
|
485
|
+
},
|
|
486
|
+
/** Drop the anonymous session so identity headers apply again. */
|
|
487
|
+
endAnonSession: () => {
|
|
488
|
+
o = null;
|
|
427
489
|
},
|
|
428
490
|
/** @returns {Promise<Array<{assetId,url,method,expires,assetUrl}>>} */
|
|
429
|
-
createUploads: async (
|
|
430
|
-
const { uploads:
|
|
431
|
-
return
|
|
491
|
+
createUploads: async (d) => {
|
|
492
|
+
const { uploads: u } = await l("/v1/uploads", { method: "POST", body: d, auth: !0 });
|
|
493
|
+
return u || [];
|
|
432
494
|
},
|
|
433
495
|
/**
|
|
434
496
|
* PUT one file to its presigned URL. XHR (not fetch) so upload
|
|
435
497
|
* progress is observable. Resolves on 2xx; rejects with
|
|
436
498
|
* ApiError(status) otherwise. Returns { promise, abort }.
|
|
437
499
|
*/
|
|
438
|
-
uploadFile: ({ url:
|
|
500
|
+
uploadFile: ({ url: d, file: u, contentType: g, onProgress: y = () => {
|
|
439
501
|
} }) => {
|
|
440
|
-
const
|
|
441
|
-
return { promise: new Promise((
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}),
|
|
445
|
-
|
|
446
|
-
},
|
|
447
|
-
}), abort: () =>
|
|
502
|
+
const p = t();
|
|
503
|
+
return { promise: new Promise((w, m) => {
|
|
504
|
+
p.open("PUT", d), p.setRequestHeader("Content-Type", g || u.type), p.upload && (p.upload.onprogress = (_) => {
|
|
505
|
+
y({ loaded: _.loaded, total: _.total || u.size });
|
|
506
|
+
}), p.onload = () => {
|
|
507
|
+
p.status >= 200 && p.status < 300 ? w({ status: p.status }) : m(new x(p.status, null));
|
|
508
|
+
}, p.onerror = () => m(new x(0, null)), p.onabort = () => m(new x(0, { aborted: !0 })), p.send(u);
|
|
509
|
+
}), abort: () => p.abort() };
|
|
448
510
|
},
|
|
449
511
|
/** @returns {Promise<{requestId,reference,confirmation,routing}>} */
|
|
450
|
-
putRequest: async (
|
|
512
|
+
putRequest: async (d, u) => await l(`/v1/requests/${d}`, { method: "PUT", body: u, auth: !0 })
|
|
451
513
|
};
|
|
452
514
|
}
|
|
453
|
-
function
|
|
454
|
-
const { config: r, storage: e } =
|
|
515
|
+
function ve() {
|
|
516
|
+
const { config: r, storage: e } = Q();
|
|
455
517
|
let t = null;
|
|
456
518
|
return {
|
|
457
|
-
isAuthenticated: () =>
|
|
458
|
-
signedInEmail: () => e.get(
|
|
459
|
-
getAuthHeaders: () =>
|
|
519
|
+
isAuthenticated: () => ie(),
|
|
520
|
+
signedInEmail: () => e.get(I.userEmail) || null,
|
|
521
|
+
getAuthHeaders: () => se(r, e),
|
|
460
522
|
/**
|
|
461
523
|
* Email step. Keeps the challenge user in memory for verifyCode.
|
|
462
524
|
* @returns {{challengeRequired?: boolean, authenticated?: boolean}}
|
|
463
525
|
*/
|
|
464
526
|
sendCode: async (s) => {
|
|
465
|
-
const i = await
|
|
527
|
+
const i = await Z(
|
|
466
528
|
r,
|
|
467
529
|
s,
|
|
468
530
|
e,
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
531
|
+
ee,
|
|
532
|
+
te,
|
|
533
|
+
re,
|
|
534
|
+
D,
|
|
535
|
+
z,
|
|
536
|
+
B
|
|
475
537
|
);
|
|
476
538
|
return t = i.cognitoUser || null, {
|
|
477
539
|
challengeRequired: !!i.challengeRequired,
|
|
@@ -486,114 +548,114 @@ function fe() {
|
|
|
486
548
|
if (!t)
|
|
487
549
|
throw new Error("Please enter your email again to get a new code.");
|
|
488
550
|
try {
|
|
489
|
-
await
|
|
551
|
+
await J(
|
|
490
552
|
t,
|
|
491
553
|
s,
|
|
492
554
|
e,
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
555
|
+
D,
|
|
556
|
+
z,
|
|
557
|
+
B
|
|
496
558
|
);
|
|
497
559
|
} catch {
|
|
498
560
|
throw new Error("Invalid or expired code. Please request a new one and try again.");
|
|
499
561
|
}
|
|
500
|
-
return await
|
|
562
|
+
return await X(), { success: !0 };
|
|
501
563
|
},
|
|
502
564
|
/** Local sign-out only ("use a different email"). */
|
|
503
565
|
signOut: () => {
|
|
504
|
-
t = null,
|
|
566
|
+
t = null, G().clear();
|
|
505
567
|
},
|
|
506
568
|
/**
|
|
507
569
|
* Detect a login completed in ANOTHER tab (the storage event
|
|
508
570
|
* never fires in the tab that wrote). Returns an unsubscribe.
|
|
509
571
|
*/
|
|
510
572
|
onCrossTabLogin: (s) => {
|
|
511
|
-
const i = (
|
|
512
|
-
(
|
|
573
|
+
const i = (n) => {
|
|
574
|
+
(n.key === I.accessToken || n.key === I.idToken) && s();
|
|
513
575
|
};
|
|
514
576
|
return window.addEventListener("storage", i), () => window.removeEventListener("storage", i);
|
|
515
577
|
}
|
|
516
578
|
};
|
|
517
579
|
}
|
|
518
|
-
function
|
|
519
|
-
const e =
|
|
520
|
-
return { ...
|
|
580
|
+
function ke({ baseUrl: r }) {
|
|
581
|
+
const e = ve();
|
|
582
|
+
return { ...xe({ baseUrl: r, getAuthHeaders: e.getAuthHeaders }), ...e };
|
|
521
583
|
}
|
|
522
|
-
function
|
|
584
|
+
function b(r, e) {
|
|
523
585
|
return (r?.nodes || []).find((t) => t.id === e);
|
|
524
586
|
}
|
|
525
|
-
function
|
|
587
|
+
function V(r, e = []) {
|
|
526
588
|
const t = [];
|
|
527
589
|
let s = r?.start;
|
|
528
|
-
for (const
|
|
529
|
-
const
|
|
530
|
-
if (!
|
|
531
|
-
return { nodeId: null, node: null, flags: t, error: { step:
|
|
532
|
-
if (
|
|
533
|
-
return { nodeId: null, node: null, flags: t, error: { step:
|
|
534
|
-
if (
|
|
535
|
-
return { nodeId: null, node: null, flags: t, error: { step:
|
|
536
|
-
if (
|
|
537
|
-
const
|
|
538
|
-
if (!
|
|
539
|
-
return { nodeId: null, node: null, flags: t, error: { step:
|
|
540
|
-
t.push(...
|
|
590
|
+
for (const n of e) {
|
|
591
|
+
const o = b(r, s);
|
|
592
|
+
if (!o)
|
|
593
|
+
return { nodeId: null, node: null, flags: t, error: { step: n, reason: `unknown node: ${s}` } };
|
|
594
|
+
if (o.type === "terminal" || o.type === "redirect")
|
|
595
|
+
return { nodeId: null, node: null, flags: t, error: { step: n, reason: `steps continue past ${o.type}` } };
|
|
596
|
+
if (n.nodeId !== s || n.kind !== o.type)
|
|
597
|
+
return { nodeId: null, node: null, flags: t, error: { step: n, reason: `step does not match node ${s}` } };
|
|
598
|
+
if (o.type === "choice") {
|
|
599
|
+
const l = (o.options || []).find((d) => d.id === n.optionId);
|
|
600
|
+
if (!l)
|
|
601
|
+
return { nodeId: null, node: null, flags: t, error: { step: n, reason: `unknown option: ${n.optionId}` } };
|
|
602
|
+
t.push(...l.flags || []), s = l.next;
|
|
541
603
|
continue;
|
|
542
604
|
}
|
|
543
|
-
s =
|
|
605
|
+
s = o.next;
|
|
544
606
|
}
|
|
545
|
-
const i =
|
|
546
|
-
return i ? { nodeId: s, node: i, flags:
|
|
607
|
+
const i = b(r, s);
|
|
608
|
+
return i ? { nodeId: s, node: i, flags: H(t), error: null } : { nodeId: null, node: null, flags: t, error: { step: null, reason: `unknown node: ${s}` } };
|
|
547
609
|
}
|
|
548
|
-
function
|
|
610
|
+
function _e(r, e = []) {
|
|
549
611
|
const t = [];
|
|
550
612
|
for (const s of e) {
|
|
551
613
|
if (s.kind !== "collect") continue;
|
|
552
|
-
const i =
|
|
614
|
+
const i = b(r, s.nodeId);
|
|
553
615
|
t.push(...i?.fields || []);
|
|
554
616
|
}
|
|
555
617
|
return t;
|
|
556
618
|
}
|
|
557
|
-
function
|
|
619
|
+
function $e(r = []) {
|
|
558
620
|
const e = {};
|
|
559
621
|
for (const t of r)
|
|
560
622
|
t.kind === "collect" && Object.assign(e, t.values || {});
|
|
561
623
|
return e;
|
|
562
624
|
}
|
|
563
|
-
function
|
|
625
|
+
function Fe(r = []) {
|
|
564
626
|
return r.filter((e) => e.kind === "choice").map((e) => `${e.nodeId}:${e.optionId}`);
|
|
565
627
|
}
|
|
566
|
-
function
|
|
628
|
+
function Y(r, e = []) {
|
|
567
629
|
const t = [];
|
|
568
630
|
for (const s of e) {
|
|
569
631
|
if (s.kind !== "choice") continue;
|
|
570
|
-
const
|
|
571
|
-
t.push(...
|
|
632
|
+
const n = (b(r, s.nodeId)?.options || []).find((o) => o.id === s.optionId);
|
|
633
|
+
t.push(...n?.flags || []);
|
|
572
634
|
}
|
|
573
|
-
return
|
|
635
|
+
return H(t);
|
|
574
636
|
}
|
|
575
|
-
function
|
|
637
|
+
function Ie(r = []) {
|
|
576
638
|
return r.includes("urgent") ? "urgent" : r.includes("moderate") ? "moderate" : null;
|
|
577
639
|
}
|
|
578
|
-
function
|
|
640
|
+
function H(r) {
|
|
579
641
|
return [...new Set(r)];
|
|
580
642
|
}
|
|
581
|
-
const
|
|
643
|
+
const Ee = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?(\.\d+)?(Z|[+-]\d{2}:\d{2})?$/, M = {
|
|
582
644
|
text: 500,
|
|
583
645
|
textarea: 5e3,
|
|
584
646
|
url: 2048
|
|
585
647
|
};
|
|
586
|
-
function
|
|
587
|
-
if (e == null || e === "" || r.kind === "location" &&
|
|
648
|
+
function Se(r, e) {
|
|
649
|
+
if (e == null || e === "" || r.kind === "location" && Ae(e))
|
|
588
650
|
return r.required ? `Please fill in: ${r.label.toLowerCase()}.` : null;
|
|
589
651
|
switch (r.kind) {
|
|
590
652
|
case "text":
|
|
591
653
|
case "textarea": {
|
|
592
|
-
const s = r.maxLength ||
|
|
654
|
+
const s = r.maxLength || M[r.kind];
|
|
593
655
|
return typeof e != "string" ? `Please enter text for ${r.label.toLowerCase()}.` : e.length > s ? `Please keep ${r.label.toLowerCase()} under ${s} characters.` : null;
|
|
594
656
|
}
|
|
595
657
|
case "url": {
|
|
596
|
-
if (typeof e != "string" || e.length >
|
|
658
|
+
if (typeof e != "string" || e.length > M.url)
|
|
597
659
|
return "Please enter a valid link.";
|
|
598
660
|
try {
|
|
599
661
|
return new URL(e), null;
|
|
@@ -602,85 +664,85 @@ function ve(r, e) {
|
|
|
602
664
|
}
|
|
603
665
|
}
|
|
604
666
|
case "datetime":
|
|
605
|
-
return
|
|
667
|
+
return Ee.test(String(e)) ? null : "Please pick a date and time.";
|
|
606
668
|
case "select":
|
|
607
669
|
return (r.options || []).map((i) => i.id).includes(e) ? null : `Please choose one of the options for ${r.label.toLowerCase()}.`;
|
|
608
670
|
case "consent":
|
|
609
671
|
return e === !0 ? null : "Please confirm the rights statement so we can use what you send.";
|
|
610
672
|
case "location":
|
|
611
|
-
return
|
|
673
|
+
return Ce(r, e);
|
|
612
674
|
case "media":
|
|
613
675
|
return null;
|
|
614
676
|
default:
|
|
615
677
|
return `Unsupported field: ${r.kind}`;
|
|
616
678
|
}
|
|
617
679
|
}
|
|
618
|
-
function
|
|
680
|
+
function Ae(r) {
|
|
619
681
|
return r == null ? !0 : typeof r != "object" ? !1 : !r.county && !r.where && !(Array.isArray(r.gps) && r.gps.length);
|
|
620
682
|
}
|
|
621
|
-
function
|
|
683
|
+
function Ce(r, e) {
|
|
622
684
|
return typeof e != "object" || Array.isArray(e) ? "Please tell us where this was." : e.county !== void 0 && (typeof e.county != "string" || e.county.length > 100) ? "Please choose a county." : e.where !== void 0 && (typeof e.where != "string" || e.where.length > 500) ? "Please shorten the location description." : e.gps !== void 0 && !(Array.isArray(e.gps) && e.gps.length === 2 && e.gps.every((s) => typeof s == "number" && Number.isFinite(s))) ? "Location coordinates look wrong; try re-sharing your location." : r.required && !e.county && !e.where ? `Please fill in: ${r.label.toLowerCase()}.` : null;
|
|
623
685
|
}
|
|
624
|
-
function
|
|
686
|
+
function O(r, e = {}) {
|
|
625
687
|
const t = {};
|
|
626
688
|
for (const s of r) {
|
|
627
689
|
if (s.kind === "media") continue;
|
|
628
|
-
const i =
|
|
690
|
+
const i = Se(s, e[s.key]);
|
|
629
691
|
i && (t[s.key] = i);
|
|
630
692
|
}
|
|
631
693
|
return { ok: Object.keys(t).length === 0, errors: t };
|
|
632
694
|
}
|
|
633
|
-
function
|
|
695
|
+
function C(r = [], e = {}, { required: t = !1 } = {}) {
|
|
634
696
|
const {
|
|
635
697
|
maxFiles: s = 5,
|
|
636
698
|
maxFileBytes: i = 512 * 1024 * 1024,
|
|
637
|
-
allowedTypes:
|
|
638
|
-
} = e,
|
|
639
|
-
return t && r.length === 0 &&
|
|
699
|
+
allowedTypes: n = []
|
|
700
|
+
} = e, o = [];
|
|
701
|
+
return t && r.length === 0 && o.push({ reason: "required", message: "Please attach at least one photo or video." }), r.length > s && o.push({
|
|
640
702
|
reason: "count",
|
|
641
703
|
message: `This form accepts at most ${s} ${s === 1 ? "file" : "files"}.`
|
|
642
|
-
}), r.forEach((
|
|
643
|
-
|
|
704
|
+
}), r.forEach((l, d) => {
|
|
705
|
+
n.length && !n.includes(l.type) && o.push({
|
|
644
706
|
index: d,
|
|
645
707
|
reason: "type",
|
|
646
|
-
message: `${
|
|
647
|
-
}),
|
|
708
|
+
message: `${l.name || "A file"} is a type this form does not accept.`
|
|
709
|
+
}), l.size > i && o.push({
|
|
648
710
|
index: d,
|
|
649
711
|
reason: "size",
|
|
650
|
-
message: `${
|
|
712
|
+
message: `${l.name || "A file"} is larger than this form allows.`
|
|
651
713
|
});
|
|
652
|
-
}), { ok:
|
|
714
|
+
}), { ok: o.length === 0, errors: o };
|
|
653
715
|
}
|
|
654
|
-
function
|
|
716
|
+
function U({ formId: r, requestId: e, files: t }) {
|
|
655
717
|
return {
|
|
656
718
|
formId: r,
|
|
657
719
|
requestId: e,
|
|
658
720
|
files: t.map((s) => ({ contentType: s.type, size: s.size }))
|
|
659
721
|
};
|
|
660
722
|
}
|
|
661
|
-
function
|
|
662
|
-
const s =
|
|
663
|
-
for (const
|
|
664
|
-
if (
|
|
665
|
-
const
|
|
666
|
-
if (!(
|
|
667
|
-
if (
|
|
723
|
+
function Te({ form: r, steps: e, media: t = [] }) {
|
|
724
|
+
const s = _e(r, e), i = $e(e), n = {};
|
|
725
|
+
for (const o of s) {
|
|
726
|
+
if (o.kind === "media") continue;
|
|
727
|
+
const l = i[o.key];
|
|
728
|
+
if (!(l == null || l === "") && !(o.kind === "consent" && l !== !0)) {
|
|
729
|
+
if (o.kind === "location") {
|
|
668
730
|
const d = {};
|
|
669
|
-
|
|
731
|
+
l.county && (d.county = l.county), l.where && (d.where = l.where), Array.isArray(l.gps) && l.gps.length === 2 && (d.gps = l.gps), Object.keys(d).length && (n[o.key] = d);
|
|
670
732
|
continue;
|
|
671
733
|
}
|
|
672
|
-
|
|
734
|
+
n[o.key] = l;
|
|
673
735
|
}
|
|
674
736
|
}
|
|
675
737
|
return {
|
|
676
738
|
formId: r.formId,
|
|
677
739
|
flowVersion: r.flowVersion,
|
|
678
|
-
path:
|
|
679
|
-
answers:
|
|
680
|
-
media: t.map(({ assetId:
|
|
740
|
+
path: Fe(e),
|
|
741
|
+
answers: n,
|
|
742
|
+
media: t.map(({ assetId: o, type: l }) => ({ assetId: o, type: l }))
|
|
681
743
|
};
|
|
682
744
|
}
|
|
683
|
-
function
|
|
745
|
+
function q(r, e) {
|
|
684
746
|
if (r === 429)
|
|
685
747
|
return {
|
|
686
748
|
kind: "throttled",
|
|
@@ -718,15 +780,15 @@ function Fe(r, e) {
|
|
|
718
780
|
message: "Something went wrong on our end; please try again."
|
|
719
781
|
};
|
|
720
782
|
}
|
|
721
|
-
const
|
|
722
|
-
function
|
|
783
|
+
const Re = 34, De = 44;
|
|
784
|
+
function L(r, e) {
|
|
723
785
|
const t = String(r);
|
|
724
786
|
return t.length > e ? `${t.slice(0, e)}…` : t;
|
|
725
787
|
}
|
|
726
|
-
function
|
|
788
|
+
function ze(r) {
|
|
727
789
|
return !r || typeof r != "object" ? "" : [r.county, r.where].filter(Boolean).join(", ");
|
|
728
790
|
}
|
|
729
|
-
function
|
|
791
|
+
function Be(r, e = {}, t = []) {
|
|
730
792
|
const s = [];
|
|
731
793
|
for (const i of r) {
|
|
732
794
|
if (i.kind === "consent") continue;
|
|
@@ -734,21 +796,21 @@ function Se(r, e = {}, t = []) {
|
|
|
734
796
|
t.length && s.push(`${t.length} ${t.length === 1 ? "file" : "files"}`);
|
|
735
797
|
continue;
|
|
736
798
|
}
|
|
737
|
-
const
|
|
738
|
-
|
|
799
|
+
const n = i.kind === "location" ? ze(e[i.key]) : e[i.key];
|
|
800
|
+
n == null || n === "" || (i.kind === "textarea" ? s.push(`"${L(n, De)}"`) : s.push(L(n, Re)));
|
|
739
801
|
}
|
|
740
802
|
return s.join(" · ") || "Details provided";
|
|
741
803
|
}
|
|
742
|
-
function
|
|
804
|
+
function Pe(r, e = [], t = null) {
|
|
743
805
|
const s = [];
|
|
744
|
-
for (const
|
|
745
|
-
const
|
|
746
|
-
|
|
806
|
+
for (const n of e) {
|
|
807
|
+
const o = b(r, n.nodeId);
|
|
808
|
+
n.kind === "choice" && o && s.push({ k: (o.prompt || "").replace(/\?$/, ""), v: n.label || n.optionId }), n.kind === "collect" && s.push({ k: "Details", v: n.summary || "Details provided" }), n.kind === "gate" && (n.anonymous ? s.push({ k: "Contact", v: "Anonymous — we will have no way to reach you" }) : (n.email || t) && s.push({ k: "Verified email", v: n.email || t }));
|
|
747
809
|
}
|
|
748
|
-
const i =
|
|
810
|
+
const i = Ie(Y(r, e));
|
|
749
811
|
return i && s.push({ k: "Priority", v: i === "urgent" ? "Urgent" : "Moderate", pill: i }), s;
|
|
750
812
|
}
|
|
751
|
-
function
|
|
813
|
+
function Me(r, e = []) {
|
|
752
814
|
const t = r?.confirmation || {};
|
|
753
815
|
return {
|
|
754
816
|
title: t.title || "Report received.",
|
|
@@ -756,12 +818,12 @@ function Re(r, e = []) {
|
|
|
756
818
|
urgentNote: e.includes("urgent") ? t.urgentNote : void 0
|
|
757
819
|
};
|
|
758
820
|
}
|
|
759
|
-
function
|
|
821
|
+
function Oe(r, e) {
|
|
760
822
|
const t = e === "failed" ? " The notification is queued for a delivery retry." : "";
|
|
761
823
|
return r?.startsWith("studio+email:") ? `Sent to the Studio review queue; the desk is emailed.${t}` : r?.startsWith("studio:") ? "Sent to the Studio review queue." : r?.startsWith("jsm:") ? `A ticket is opened for tech support with your details as fields, not prose.${t}` : r?.startsWith("email:") ? `Emailed to the owning desk with a link to your report.${t}` : "Routed to the right team.";
|
|
762
824
|
}
|
|
763
|
-
function
|
|
764
|
-
const
|
|
825
|
+
function Ue({ routing: r = {}, flags: e = [], reference: t = "", anonymous: s = !1 }) {
|
|
826
|
+
const i = e.includes("urgent");
|
|
765
827
|
return [
|
|
766
828
|
{
|
|
767
829
|
title: "Received",
|
|
@@ -769,12 +831,12 @@ function Te({ routing: r = {}, flags: e = [], reference: t = "" }) {
|
|
|
769
831
|
},
|
|
770
832
|
{
|
|
771
833
|
title: "Routed",
|
|
772
|
-
body:
|
|
773
|
-
pill:
|
|
834
|
+
body: Oe(r.destination, r.state),
|
|
835
|
+
pill: i ? "Urgent · pinned first" : void 0
|
|
774
836
|
},
|
|
775
837
|
{
|
|
776
838
|
title: "Reviewed",
|
|
777
|
-
body: "An editor reviews it in the Report It queue: accept and promote with your credit, reject, or re-route."
|
|
839
|
+
body: s ? "An editor reviews it in the Report It queue: accept and promote it uncredited, reject, or re-route." : "An editor reviews it in the Report It queue: accept and promote with your credit, reject, or re-route."
|
|
778
840
|
},
|
|
779
841
|
{
|
|
780
842
|
title: "Monitored",
|
|
@@ -786,30 +848,30 @@ function Te({ routing: r = {}, flags: e = [], reference: t = "" }) {
|
|
|
786
848
|
}
|
|
787
849
|
];
|
|
788
850
|
}
|
|
789
|
-
const
|
|
790
|
-
function
|
|
851
|
+
const R = "wral-report-it:draft:v1", K = 1;
|
|
852
|
+
function qe(r, e) {
|
|
791
853
|
try {
|
|
792
|
-
r.setItem(
|
|
854
|
+
r.setItem(R, JSON.stringify({ ...e, v: K, savedAt: Date.now() }));
|
|
793
855
|
} catch {
|
|
794
856
|
}
|
|
795
857
|
}
|
|
796
|
-
function
|
|
858
|
+
function Le(r) {
|
|
797
859
|
try {
|
|
798
|
-
const e = r.getItem(
|
|
860
|
+
const e = r.getItem(R);
|
|
799
861
|
if (!e) return null;
|
|
800
862
|
const t = JSON.parse(e);
|
|
801
|
-
return t?.v !==
|
|
863
|
+
return t?.v !== K ? null : t;
|
|
802
864
|
} catch {
|
|
803
865
|
return null;
|
|
804
866
|
}
|
|
805
867
|
}
|
|
806
|
-
function
|
|
868
|
+
function j(r) {
|
|
807
869
|
try {
|
|
808
|
-
r.removeItem(
|
|
870
|
+
r.removeItem(R);
|
|
809
871
|
} catch {
|
|
810
872
|
}
|
|
811
873
|
}
|
|
812
|
-
function
|
|
874
|
+
function je({ apiOrigin: r, form: e, steps: t, requestId: s = null, mediaMeta: i = {} }) {
|
|
813
875
|
return {
|
|
814
876
|
apiOrigin: r,
|
|
815
877
|
formId: e.formId,
|
|
@@ -819,26 +881,26 @@ function Be({ apiOrigin: r, form: e, steps: t, requestId: s = null, mediaMeta: i
|
|
|
819
881
|
media: i
|
|
820
882
|
};
|
|
821
883
|
}
|
|
822
|
-
function
|
|
884
|
+
function Ne(r, e, { apiOrigin: t } = {}) {
|
|
823
885
|
if (!r) return { status: "discarded" };
|
|
824
886
|
if (t && r.apiOrigin && r.apiOrigin !== t)
|
|
825
887
|
return { status: "discarded" };
|
|
826
|
-
const s = (e || []).find((
|
|
888
|
+
const s = (e || []).find((o) => o.formId === r.formId);
|
|
827
889
|
if (!s) return { status: "discarded" };
|
|
828
|
-
const { error: i } =
|
|
890
|
+
const { error: i } = V(s, r.steps || []);
|
|
829
891
|
if (i) return { status: "discarded" };
|
|
830
|
-
const
|
|
831
|
-
for (const [
|
|
832
|
-
|
|
892
|
+
const n = {};
|
|
893
|
+
for (const [o, l] of Object.entries(r.media || {}))
|
|
894
|
+
l?.length && (n[o] = l.map((d) => ({ ...d, lost: !0 })));
|
|
833
895
|
return {
|
|
834
896
|
status: r.flowVersion === s.flowVersion ? "ok" : "review-required",
|
|
835
897
|
form: s,
|
|
836
898
|
steps: r.steps || [],
|
|
837
899
|
requestId: r.requestId || null,
|
|
838
|
-
lostMedia:
|
|
900
|
+
lostMedia: n
|
|
839
901
|
};
|
|
840
902
|
}
|
|
841
|
-
function
|
|
903
|
+
function F(r = []) {
|
|
842
904
|
return {
|
|
843
905
|
stage: "idle",
|
|
844
906
|
resumeStage: null,
|
|
@@ -858,7 +920,7 @@ function k(r = []) {
|
|
|
858
920
|
error: null
|
|
859
921
|
};
|
|
860
922
|
}
|
|
861
|
-
function
|
|
923
|
+
function We(r, e) {
|
|
862
924
|
switch (e.type) {
|
|
863
925
|
case "START":
|
|
864
926
|
return { ...r, stage: r.files.length ? "minting" : "requesting", error: null };
|
|
@@ -879,29 +941,29 @@ function Me(r, e) {
|
|
|
879
941
|
return { ...r, stage: "uploading", files: t };
|
|
880
942
|
}
|
|
881
943
|
case "FILE_PROGRESS":
|
|
882
|
-
return
|
|
944
|
+
return v(r, e.i, (t) => ({
|
|
883
945
|
...t,
|
|
884
946
|
progress: e.total ? e.loaded / e.total : 0
|
|
885
947
|
}));
|
|
886
948
|
case "FILE_DONE": {
|
|
887
|
-
const t =
|
|
888
|
-
return
|
|
949
|
+
const t = v(r, e.i, (s) => ({ ...s, status: "done", progress: 1 }));
|
|
950
|
+
return Ve(t);
|
|
889
951
|
}
|
|
890
952
|
case "FILE_FAIL":
|
|
891
|
-
return { ...
|
|
953
|
+
return { ...v(r, e.i, (s) => ({
|
|
892
954
|
...s,
|
|
893
955
|
status: "failed",
|
|
894
956
|
error: e.error || "upload failed"
|
|
895
957
|
})), stage: "uploading-error" };
|
|
896
958
|
case "RETRY_FILE":
|
|
897
|
-
return { ...
|
|
959
|
+
return { ...v(r, e.i, (s) => ({
|
|
898
960
|
...s,
|
|
899
961
|
status: "uploading",
|
|
900
962
|
progress: 0,
|
|
901
963
|
error: null
|
|
902
964
|
})), stage: "uploading" };
|
|
903
965
|
case "REMINT_FILE":
|
|
904
|
-
return { ...
|
|
966
|
+
return { ...v(r, e.i, (s) => ({
|
|
905
967
|
...s,
|
|
906
968
|
assetId: e.upload.assetId,
|
|
907
969
|
url: e.upload.url,
|
|
@@ -917,23 +979,23 @@ function Me(r, e) {
|
|
|
917
979
|
case "REQUEST_FAIL":
|
|
918
980
|
return { ...r, stage: "requesting-error", error: e.error };
|
|
919
981
|
case "RESET":
|
|
920
|
-
return
|
|
982
|
+
return F([]);
|
|
921
983
|
default:
|
|
922
984
|
return r;
|
|
923
985
|
}
|
|
924
986
|
}
|
|
925
|
-
function
|
|
926
|
-
const s = r.files.map((i,
|
|
987
|
+
function v(r, e, t) {
|
|
988
|
+
const s = r.files.map((i, n) => n === e ? t(i) : i);
|
|
927
989
|
return { ...r, files: s };
|
|
928
990
|
}
|
|
929
|
-
function
|
|
991
|
+
function Ve(r) {
|
|
930
992
|
const e = r.files.every((s) => s.status === "done"), t = r.files.some((s) => s.status === "failed");
|
|
931
993
|
return e ? { ...r, stage: "requesting" } : t ? { ...r, stage: "uploading-error" } : r;
|
|
932
994
|
}
|
|
933
|
-
function
|
|
995
|
+
function N(r) {
|
|
934
996
|
return r.files.map((e, t) => ({ ...e, i: t })).filter((e) => e.status === "failed");
|
|
935
997
|
}
|
|
936
|
-
const
|
|
998
|
+
const Ye = [
|
|
937
999
|
"Wake",
|
|
938
1000
|
"Durham",
|
|
939
1001
|
"Orange",
|
|
@@ -950,14 +1012,18 @@ const je = [
|
|
|
950
1012
|
"Moore",
|
|
951
1013
|
"Sampson",
|
|
952
1014
|
"Other / outside the area"
|
|
953
|
-
],
|
|
954
|
-
|
|
1015
|
+
], W = "(919) 821-8600", He = "https://www.wral.com/my-wral/", Ke = Object.freeze([
|
|
1016
|
+
"We cannot come back to you for the detail that often turns a tip into a story.",
|
|
1017
|
+
"We cannot credit you if we use your photos or video.",
|
|
1018
|
+
"If you lose your reference number, we cannot look it up for you."
|
|
1019
|
+
]);
|
|
1020
|
+
function Qe(r) {
|
|
955
1021
|
const e = String(r).replace(/\D/g, "");
|
|
956
1022
|
return `tel:+1${e.length === 11 && e.startsWith("1") ? e.slice(1) : e}`;
|
|
957
1023
|
}
|
|
958
|
-
const
|
|
959
|
-
class
|
|
960
|
-
static styles =
|
|
1024
|
+
const Ge = "https://api.wral.com/forms", Je = 480, Xe = 2, S = "Welcome to Report It. What would you like to send us?", A = "A few quick questions route your report to the right team.", Ze = 2500;
|
|
1025
|
+
class at extends T {
|
|
1026
|
+
static styles = me;
|
|
961
1027
|
static properties = {
|
|
962
1028
|
api: { type: String },
|
|
963
1029
|
_phase: { state: !0 },
|
|
@@ -965,6 +1031,7 @@ class Xe extends I {
|
|
|
965
1031
|
_form: { state: !0 },
|
|
966
1032
|
_steps: { state: !0 },
|
|
967
1033
|
_typing: { state: !0 },
|
|
1034
|
+
_loadingSlow: { state: !0 },
|
|
968
1035
|
_cardValues: { state: !0 },
|
|
969
1036
|
_fieldErrors: { state: !0 },
|
|
970
1037
|
_gate: { state: !0 },
|
|
@@ -973,41 +1040,43 @@ class Xe extends I {
|
|
|
973
1040
|
_notice: { state: !0 },
|
|
974
1041
|
_result: { state: !0 }
|
|
975
1042
|
};
|
|
976
|
-
#
|
|
1043
|
+
#l = null;
|
|
977
1044
|
#a = null;
|
|
978
1045
|
#u = null;
|
|
979
1046
|
#t = null;
|
|
980
1047
|
#r = {};
|
|
981
1048
|
#s = {};
|
|
982
1049
|
#i = /* @__PURE__ */ new Map();
|
|
983
|
-
#
|
|
984
|
-
#
|
|
985
|
-
#
|
|
1050
|
+
#y = null;
|
|
1051
|
+
#p = null;
|
|
1052
|
+
#w = !1;
|
|
1053
|
+
#d = null;
|
|
1054
|
+
#I = 0;
|
|
986
1055
|
constructor() {
|
|
987
|
-
super(), this.api =
|
|
1056
|
+
super(), this.api = Ge, this._phase = "loading", this._forms = [], this._form = null, this._steps = [], this._typing = !1, this._loadingSlow = !1, this._cardValues = {}, this._fieldErrors = {}, this._gate = { stage: null, email: "", error: "", loading: !1 }, this._submit = F([]), this._banner = null, this._notice = null, this._result = null;
|
|
988
1057
|
}
|
|
989
1058
|
get client() {
|
|
990
|
-
return this.#
|
|
1059
|
+
return this.#l ? this.#l : ((!this.#a || this.#u !== this.api) && (this.#a = ke({ baseUrl: this.api }), this.#u = this.api), this.#a);
|
|
991
1060
|
}
|
|
992
1061
|
set client(e) {
|
|
993
|
-
this.#
|
|
1062
|
+
this.#l = e || null, this.isConnected && this.#x();
|
|
994
1063
|
}
|
|
995
1064
|
connectedCallback() {
|
|
996
|
-
super.connectedCallback(), this.#
|
|
1065
|
+
super.connectedCallback(), this.#x();
|
|
997
1066
|
}
|
|
998
1067
|
disconnectedCallback() {
|
|
999
|
-
super.disconnectedCallback(), clearTimeout(this.#
|
|
1068
|
+
super.disconnectedCallback(), clearTimeout(this.#y), clearTimeout(this.#p), this.#d?.();
|
|
1000
1069
|
for (const { abort: e } of this.#i.values()) e?.();
|
|
1001
1070
|
this.#i.clear();
|
|
1002
1071
|
}
|
|
1003
|
-
get #
|
|
1072
|
+
get #z() {
|
|
1004
1073
|
try {
|
|
1005
1074
|
return matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1006
1075
|
} catch {
|
|
1007
1076
|
return !0;
|
|
1008
1077
|
}
|
|
1009
1078
|
}
|
|
1010
|
-
get #
|
|
1079
|
+
get #h() {
|
|
1011
1080
|
try {
|
|
1012
1081
|
return sessionStorage;
|
|
1013
1082
|
} catch {
|
|
@@ -1016,28 +1085,49 @@ class Xe extends I {
|
|
|
1016
1085
|
} };
|
|
1017
1086
|
}
|
|
1018
1087
|
}
|
|
1088
|
+
/*
|
|
1089
|
+
* The anonymous route is offered only where it can actually complete.
|
|
1090
|
+
* The server is the authority: GET /v1/forms says whether anonymous
|
|
1091
|
+
* submission is enabled on this deployment, and a form whose desk needs
|
|
1092
|
+
* a way to reach the viewer opts out with allowAnonymous: false.
|
|
1093
|
+
*/
|
|
1094
|
+
get #B() {
|
|
1095
|
+
return this.client.anonymousCapability?.().enabled === !0 && this._form?.allowAnonymous !== !1;
|
|
1096
|
+
}
|
|
1097
|
+
/* Derived from the gate step, so a reload restores the choice. */
|
|
1098
|
+
get #f() {
|
|
1099
|
+
return this._steps.some((e) => e.kind === "gate" && e.anonymous === !0);
|
|
1100
|
+
}
|
|
1019
1101
|
// ── Loading + draft restore ─────────────────────────────────────
|
|
1020
|
-
async #
|
|
1021
|
-
|
|
1102
|
+
async #x() {
|
|
1103
|
+
const e = ++this.#I, t = () => e === this.#I;
|
|
1104
|
+
this._phase = "loading", this._banner = null, this._loadingSlow = !1, clearTimeout(this.#p), this.#p = setTimeout(() => {
|
|
1105
|
+
this._loadingSlow = !0;
|
|
1106
|
+
}, Ze);
|
|
1107
|
+
let s;
|
|
1022
1108
|
try {
|
|
1023
|
-
|
|
1109
|
+
s = await this.client.listForms();
|
|
1024
1110
|
} catch {
|
|
1025
|
-
this._phase = "load-error";
|
|
1111
|
+
t() && (this._phase = "load-error");
|
|
1026
1112
|
return;
|
|
1113
|
+
} finally {
|
|
1114
|
+
t() && (clearTimeout(this.#p), this._loadingSlow = !1);
|
|
1027
1115
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1116
|
+
if (!t()) return;
|
|
1117
|
+
this._forms = s;
|
|
1118
|
+
const i = Ne(Le(this.#h), this._forms, { apiOrigin: this.api });
|
|
1119
|
+
if (i.status === "ok" || i.status === "review-required") {
|
|
1120
|
+
this._form = i.form, this._steps = i.steps, this.#t = i.requestId, this.#s = i.lostMedia || {}, i.status === "review-required" ? this._notice = "This form changed since your last visit — please review your answers." : Object.keys(this.#s).length && (this._notice = "Welcome back — your answers were saved, but attachments need to be re-attached."), this.#o({ animate: !1 });
|
|
1031
1121
|
return;
|
|
1032
1122
|
}
|
|
1033
1123
|
this._phase = "pick";
|
|
1034
1124
|
}
|
|
1035
|
-
#
|
|
1125
|
+
#c() {
|
|
1036
1126
|
if (!this._form) return;
|
|
1037
1127
|
const e = {};
|
|
1038
1128
|
for (const [t, s] of Object.entries(this.#r))
|
|
1039
|
-
s.length && (e[t] = s.map(({ name: i, type:
|
|
1040
|
-
|
|
1129
|
+
s.length && (e[t] = s.map(({ name: i, type: n, size: o }) => ({ name: i, type: n, size: o })));
|
|
1130
|
+
qe(this.#h, je({
|
|
1041
1131
|
apiOrigin: this.api,
|
|
1042
1132
|
form: this._form,
|
|
1043
1133
|
steps: this._steps,
|
|
@@ -1046,77 +1136,77 @@ class Xe extends I {
|
|
|
1046
1136
|
}));
|
|
1047
1137
|
}
|
|
1048
1138
|
// ── Walk mechanics ──────────────────────────────────────────────
|
|
1049
|
-
#
|
|
1050
|
-
this._phase = "walk", this._cardValues = {}, this._fieldErrors = {}, this._gate = { stage: null, email: "", error: "", loading: !1 }, e && !this.#
|
|
1139
|
+
#o({ animate: e = !0 } = {}) {
|
|
1140
|
+
this._phase = "walk", this._cardValues = {}, this._fieldErrors = {}, this._gate = { stage: null, email: "", error: "", loading: !1 }, e && !this.#z ? (this._typing = !0, clearTimeout(this.#y), this.#y = setTimeout(() => {
|
|
1051
1141
|
this._typing = !1;
|
|
1052
|
-
},
|
|
1142
|
+
}, Je)) : this._typing = !1;
|
|
1053
1143
|
}
|
|
1054
|
-
get #
|
|
1055
|
-
return this._form ?
|
|
1144
|
+
get #v() {
|
|
1145
|
+
return this._form ? V(this._form, this._steps) : { node: null };
|
|
1056
1146
|
}
|
|
1057
|
-
#
|
|
1058
|
-
this._form = e, this._steps = [], this.#t = null, this.#r = {}, this.#s = {}, this._notice = null, this.#
|
|
1147
|
+
#P(e) {
|
|
1148
|
+
this._form = e, this._steps = [], this.#t = null, this.#r = {}, this.#s = {}, this._notice = null, this.#c(), this.#o();
|
|
1059
1149
|
}
|
|
1060
|
-
#
|
|
1061
|
-
this._steps = [...this._steps, e], this.#
|
|
1150
|
+
#g(e) {
|
|
1151
|
+
this._steps = [...this._steps, e], this.#c(), this.#o();
|
|
1062
1152
|
}
|
|
1063
|
-
#
|
|
1153
|
+
#M() {
|
|
1064
1154
|
if (this._phase === "done" || this._phase === "submitting") return;
|
|
1065
1155
|
if (this._phase === "review") {
|
|
1066
|
-
this.#
|
|
1156
|
+
this.#o({ animate: !1 });
|
|
1067
1157
|
return;
|
|
1068
1158
|
}
|
|
1069
1159
|
if (!this._steps.length) {
|
|
1070
|
-
this.#
|
|
1160
|
+
this.#k();
|
|
1071
1161
|
return;
|
|
1072
1162
|
}
|
|
1073
1163
|
const e = this._steps.at(-1);
|
|
1074
|
-
this._steps = this._steps.slice(0, -1), e.kind === "collect" && (this._cardValues = { ...e.values }), this.#
|
|
1164
|
+
this._steps = this._steps.slice(0, -1), e.kind === "collect" && (this._cardValues = { ...e.values }), this.#c(), this.#o({ animate: !1 });
|
|
1075
1165
|
}
|
|
1076
|
-
#
|
|
1077
|
-
this._form = null, this._steps = [], this.#t = null, this.#r = {}, this.#s = {}, this._result = null, this._banner = null, this._notice = null, this._submit =
|
|
1166
|
+
#k() {
|
|
1167
|
+
this.client.endAnonSession?.(), this._form = null, this._steps = [], this.#t = null, this.#r = {}, this.#s = {}, this._result = null, this._banner = null, this._notice = null, this._submit = F([]), j(this.#h), this._phase = "pick";
|
|
1078
1168
|
}
|
|
1079
1169
|
// ── Collect card ────────────────────────────────────────────────
|
|
1080
|
-
#
|
|
1170
|
+
#_(e, t) {
|
|
1081
1171
|
this._cardValues = { ...this._cardValues, [e]: t };
|
|
1082
1172
|
}
|
|
1083
|
-
#
|
|
1084
|
-
const t = e.fields || [], { ok: s, errors: i } =
|
|
1085
|
-
let
|
|
1086
|
-
if (
|
|
1087
|
-
const d = this.#r[e.id] || [],
|
|
1088
|
-
required: !!
|
|
1173
|
+
#O(e) {
|
|
1174
|
+
const t = e.fields || [], { ok: s, errors: i } = O(t, this._cardValues), n = t.find((d) => d.kind === "media");
|
|
1175
|
+
let o = !0;
|
|
1176
|
+
if (n) {
|
|
1177
|
+
const d = this.#r[e.id] || [], u = C(d, this._form.budget, {
|
|
1178
|
+
required: !!n.required
|
|
1089
1179
|
});
|
|
1090
|
-
|
|
1180
|
+
u.ok ? d.length && (this.#s = { ...this.#s, [e.id]: [] }) : (o = !1, i[n.key] = u.errors[0].message);
|
|
1091
1181
|
}
|
|
1092
|
-
if (!s || !
|
|
1182
|
+
if (!s || !o) {
|
|
1093
1183
|
this._fieldErrors = i;
|
|
1094
1184
|
return;
|
|
1095
1185
|
}
|
|
1096
|
-
const
|
|
1097
|
-
this.#
|
|
1186
|
+
const l = this.#r[e.id] || [];
|
|
1187
|
+
this.#g({
|
|
1098
1188
|
nodeId: e.id,
|
|
1099
1189
|
kind: "collect",
|
|
1100
1190
|
values: { ...this._cardValues },
|
|
1101
|
-
summary:
|
|
1191
|
+
summary: Be(t, this._cardValues, l)
|
|
1102
1192
|
});
|
|
1103
1193
|
}
|
|
1104
1194
|
// ── Gate ────────────────────────────────────────────────────────
|
|
1105
|
-
async
|
|
1106
|
-
if (!(this._gate.stage || this.#
|
|
1107
|
-
this.#
|
|
1195
|
+
async #E() {
|
|
1196
|
+
if (!(this._gate.stage || this.#w)) {
|
|
1197
|
+
this.#w = !0;
|
|
1108
1198
|
try {
|
|
1109
1199
|
const e = await this.client.isAuthenticated();
|
|
1110
1200
|
this._gate = e ? { stage: "signedin", email: this.client.signedInEmail?.() || "", error: "", loading: !1 } : { stage: "email", email: "", error: "", loading: !1 };
|
|
1111
1201
|
} finally {
|
|
1112
|
-
this.#
|
|
1202
|
+
this.#w = !1;
|
|
1113
1203
|
}
|
|
1114
|
-
this._gate.stage === "email" && this.client.onCrossTabLogin && !this.#
|
|
1115
|
-
this.#
|
|
1204
|
+
this._gate.stage === "email" && this.client.onCrossTabLogin && !this.#d && (this.#d = this.client.onCrossTabLogin(() => {
|
|
1205
|
+
this.#d?.(), this.#d = null, this._gate = { stage: null, email: "", error: "", loading: !1 }, this.#E();
|
|
1116
1206
|
}));
|
|
1117
1207
|
}
|
|
1118
1208
|
}
|
|
1119
|
-
async
|
|
1209
|
+
async #$() {
|
|
1120
1210
|
const e = this._gate.email.trim();
|
|
1121
1211
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)) {
|
|
1122
1212
|
this._gate = { ...this._gate, error: "Please enter a valid email address." };
|
|
@@ -1134,10 +1224,10 @@ class Xe extends I {
|
|
|
1134
1224
|
};
|
|
1135
1225
|
}
|
|
1136
1226
|
}
|
|
1137
|
-
async #
|
|
1227
|
+
async #S(e) {
|
|
1138
1228
|
this._gate = { ...this._gate, error: "", loading: !0 };
|
|
1139
1229
|
try {
|
|
1140
|
-
await this.client.verifyCode(e), this.#
|
|
1230
|
+
await this.client.verifyCode(e), this.#A(this._gate.email);
|
|
1141
1231
|
} catch (t) {
|
|
1142
1232
|
this._gate = {
|
|
1143
1233
|
...this._gate,
|
|
@@ -1146,19 +1236,53 @@ class Xe extends I {
|
|
|
1146
1236
|
};
|
|
1147
1237
|
}
|
|
1148
1238
|
}
|
|
1149
|
-
#
|
|
1239
|
+
#A(e) {
|
|
1150
1240
|
if (this._gate = { stage: null, email: "", error: "", loading: !1 }, this._submit.stage === "auth") {
|
|
1151
|
-
this.#e({ type: "AUTH_OK" }), this._phase = "submitting", this.#
|
|
1241
|
+
this.#e({ type: "AUTH_OK" }), this._phase = "submitting", this.#n();
|
|
1152
1242
|
return;
|
|
1153
1243
|
}
|
|
1154
|
-
const { node: t } = this.#
|
|
1155
|
-
t?.type === "gate" && this.#
|
|
1244
|
+
const { node: t } = this.#v;
|
|
1245
|
+
t?.type === "gate" && this.#g({ nodeId: t.id, kind: "gate", email: e });
|
|
1156
1246
|
}
|
|
1157
|
-
#
|
|
1247
|
+
#U() {
|
|
1158
1248
|
this.client.signOut?.(), this._gate = { stage: "email", email: "", error: "", loading: !1 };
|
|
1159
1249
|
}
|
|
1250
|
+
// ── Anonymous route ─────────────────────────────────────────────
|
|
1251
|
+
#q() {
|
|
1252
|
+
this._gate = { ...this._gate, stage: "anon-confirm", error: "" };
|
|
1253
|
+
}
|
|
1254
|
+
#L() {
|
|
1255
|
+
this._gate = { ...this._gate, stage: "email", error: "" };
|
|
1256
|
+
}
|
|
1257
|
+
/*
|
|
1258
|
+
* Records the choice only. The session is minted at submit, against
|
|
1259
|
+
* the requestId, so an abandoned form leaves nothing behind.
|
|
1260
|
+
*/
|
|
1261
|
+
#j() {
|
|
1262
|
+
const { node: e } = this.#v;
|
|
1263
|
+
e?.type === "gate" && (this._gate = { stage: null, email: "", error: "", loading: !1 }, this.#g({ nodeId: e.id, kind: "gate", anonymous: !0 }));
|
|
1264
|
+
}
|
|
1265
|
+
async #C() {
|
|
1266
|
+
try {
|
|
1267
|
+
return await this.client.createAnonSession({
|
|
1268
|
+
formId: this._form.formId,
|
|
1269
|
+
requestId: this.#t
|
|
1270
|
+
}), !0;
|
|
1271
|
+
} catch (e) {
|
|
1272
|
+
return this._banner = q(e?.status ?? 0, e?.body ?? null), !1;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
/* An anonymous session outliving the upload run re-mints in place; there
|
|
1276
|
+
* is no identity to send back to the sign-in card. */
|
|
1277
|
+
async #N() {
|
|
1278
|
+
if (await this.#C()) {
|
|
1279
|
+
this.#e({ type: "AUTH_OK" }), this._phase = "submitting", this.#n();
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
this.#e({ type: "REQUEST_FAIL", error: this._banner });
|
|
1283
|
+
}
|
|
1160
1284
|
// ── Submit pipeline ─────────────────────────────────────────────
|
|
1161
|
-
get #
|
|
1285
|
+
get #m() {
|
|
1162
1286
|
const e = [];
|
|
1163
1287
|
for (const t of this._steps)
|
|
1164
1288
|
if (t.kind === "collect")
|
|
@@ -1167,117 +1291,129 @@ class Xe extends I {
|
|
|
1167
1291
|
return e;
|
|
1168
1292
|
}
|
|
1169
1293
|
#e(e) {
|
|
1170
|
-
this._submit =
|
|
1294
|
+
this._submit = We(this._submit, e);
|
|
1171
1295
|
}
|
|
1172
|
-
async #
|
|
1296
|
+
async #W() {
|
|
1173
1297
|
this._banner = null;
|
|
1174
1298
|
for (let e = 0; e < this._steps.length; e++) {
|
|
1175
1299
|
const t = this._steps[e];
|
|
1176
1300
|
if (t.kind !== "collect") continue;
|
|
1177
|
-
const s =
|
|
1178
|
-
let
|
|
1179
|
-
if (
|
|
1180
|
-
const d = this.#r[t.nodeId] || [],
|
|
1181
|
-
required: !!
|
|
1301
|
+
const s = b(this._form, t.nodeId), { ok: i, errors: n } = O(s?.fields || [], t.values), o = (s?.fields || []).find((d) => d.kind === "media");
|
|
1302
|
+
let l = !0;
|
|
1303
|
+
if (o) {
|
|
1304
|
+
const d = this.#r[t.nodeId] || [], u = C(d, this._form.budget, {
|
|
1305
|
+
required: !!o.required
|
|
1182
1306
|
});
|
|
1183
|
-
|
|
1307
|
+
u.ok || (l = !1, n[o.key] = u.errors[0].message);
|
|
1184
1308
|
}
|
|
1185
|
-
if (!i || !
|
|
1186
|
-
this._steps = this._steps.slice(0, e), this._cardValues = { ...t.values }, this._fieldErrors =
|
|
1309
|
+
if (!i || !l) {
|
|
1310
|
+
this._steps = this._steps.slice(0, e), this._cardValues = { ...t.values }, this._fieldErrors = n, this.#c(), this.#o({ animate: !1 });
|
|
1187
1311
|
return;
|
|
1188
1312
|
}
|
|
1189
1313
|
}
|
|
1190
|
-
if (this.#t = this.#t || crypto.randomUUID(), this.#
|
|
1314
|
+
if (this.#t = this.#t || crypto.randomUUID(), this.#c(), this.#f) {
|
|
1315
|
+
if (!await this.#C()) return;
|
|
1316
|
+
} else
|
|
1317
|
+
this.client.endAnonSession?.();
|
|
1318
|
+
if (this._submit = F(this.#m), this._phase = "submitting", this.#f) {
|
|
1319
|
+
this.#e({ type: "START" }), this.#n();
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
if (!await this.client.isAuthenticated()) {
|
|
1191
1323
|
this.#e({ type: "START" }), this.#e({ type: "AUTH_REQUIRED" }), this._gate = { stage: "email", email: "", error: "", loading: !1 };
|
|
1192
1324
|
return;
|
|
1193
1325
|
}
|
|
1194
|
-
this.#e({ type: "START" }), this.#
|
|
1326
|
+
this.#e({ type: "START" }), this.#n();
|
|
1195
1327
|
}
|
|
1196
|
-
async #
|
|
1328
|
+
async #n() {
|
|
1197
1329
|
const e = this._submit.stage;
|
|
1198
|
-
if (e === "minting") return this.#
|
|
1199
|
-
if (e === "uploading") return this.#
|
|
1200
|
-
if (e === "requesting") return this.#
|
|
1330
|
+
if (e === "minting") return this.#V();
|
|
1331
|
+
if (e === "uploading") return this.#T();
|
|
1332
|
+
if (e === "requesting") return this.#K();
|
|
1201
1333
|
}
|
|
1202
|
-
async #
|
|
1334
|
+
async #V() {
|
|
1203
1335
|
try {
|
|
1204
|
-
const e = await this.client.createUploads(
|
|
1336
|
+
const e = await this.client.createUploads(U({
|
|
1205
1337
|
formId: this._form.formId,
|
|
1206
1338
|
requestId: this.#t,
|
|
1207
|
-
files: this.#
|
|
1339
|
+
files: this.#m
|
|
1208
1340
|
}));
|
|
1209
|
-
this.#e({ type: "MINTED", uploads: e }), this.#
|
|
1341
|
+
this.#e({ type: "MINTED", uploads: e }), this.#n();
|
|
1210
1342
|
} catch (e) {
|
|
1211
|
-
this.#
|
|
1343
|
+
this.#F(e);
|
|
1212
1344
|
}
|
|
1213
1345
|
}
|
|
1214
|
-
#
|
|
1346
|
+
#T() {
|
|
1215
1347
|
const e = this._submit.files, t = this.#i.size;
|
|
1216
1348
|
let s = 0;
|
|
1217
|
-
for (let i = 0; i < e.length && t + s <
|
|
1218
|
-
e[i].status !== "uploading" || this.#i.has(i) || (s += 1, this.#
|
|
1349
|
+
for (let i = 0; i < e.length && t + s < Xe; i++)
|
|
1350
|
+
e[i].status !== "uploading" || this.#i.has(i) || (s += 1, this.#Y(i));
|
|
1219
1351
|
}
|
|
1220
|
-
#
|
|
1221
|
-
const t = this._submit.files[e], s = this.#
|
|
1352
|
+
#Y(e) {
|
|
1353
|
+
const t = this._submit.files[e], s = this.#m[e], { promise: i, abort: n } = this.client.uploadFile({
|
|
1222
1354
|
url: t.url,
|
|
1223
1355
|
file: s,
|
|
1224
1356
|
contentType: t.type,
|
|
1225
|
-
onProgress: ({ loaded:
|
|
1226
|
-
this.#e({ type: "FILE_PROGRESS", i: e, loaded:
|
|
1357
|
+
onProgress: ({ loaded: o, total: l }) => {
|
|
1358
|
+
this.#e({ type: "FILE_PROGRESS", i: e, loaded: o, total: l });
|
|
1227
1359
|
}
|
|
1228
1360
|
});
|
|
1229
|
-
this.#i.set(e, { abort:
|
|
1230
|
-
this.#i.delete(e), this.#e({ type: "FILE_DONE", i: e }), this.#
|
|
1231
|
-
}).catch((
|
|
1361
|
+
this.#i.set(e, { abort: n }), i.then(() => {
|
|
1362
|
+
this.#i.delete(e), this.#e({ type: "FILE_DONE", i: e }), this.#n();
|
|
1363
|
+
}).catch((o) => {
|
|
1232
1364
|
this.#i.delete(e), this.#e({
|
|
1233
1365
|
type: "FILE_FAIL",
|
|
1234
1366
|
i: e,
|
|
1235
|
-
error:
|
|
1367
|
+
error: o?.status === 403 ? "expired" : "failed"
|
|
1236
1368
|
});
|
|
1237
1369
|
});
|
|
1238
1370
|
}
|
|
1239
|
-
async #
|
|
1371
|
+
async #H(e) {
|
|
1240
1372
|
if (this._submit.files[e].error === "expired")
|
|
1241
1373
|
try {
|
|
1242
|
-
const [s] = await this.client.createUploads(
|
|
1374
|
+
const [s] = await this.client.createUploads(U({
|
|
1243
1375
|
formId: this._form.formId,
|
|
1244
1376
|
requestId: this.#t,
|
|
1245
|
-
files: [this.#
|
|
1377
|
+
files: [this.#m[e]]
|
|
1246
1378
|
}));
|
|
1247
1379
|
this.#e({ type: "REMINT_FILE", i: e, upload: s });
|
|
1248
1380
|
} catch (s) {
|
|
1249
|
-
this.#
|
|
1381
|
+
this.#F(s);
|
|
1250
1382
|
return;
|
|
1251
1383
|
}
|
|
1252
1384
|
else
|
|
1253
1385
|
this.#e({ type: "RETRY_FILE", i: e });
|
|
1254
|
-
this.#
|
|
1386
|
+
this.#T();
|
|
1255
1387
|
}
|
|
1256
|
-
async #
|
|
1388
|
+
async #K() {
|
|
1257
1389
|
try {
|
|
1258
|
-
const e = this._submit.files.map(({ assetId: s, type: i }) => ({ assetId: s, type: i })), t = await this.client.putRequest(this.#t,
|
|
1390
|
+
const e = this._submit.files.map(({ assetId: s, type: i }) => ({ assetId: s, type: i })), t = await this.client.putRequest(this.#t, Te({
|
|
1259
1391
|
form: this._form,
|
|
1260
1392
|
steps: this._steps,
|
|
1261
1393
|
media: e
|
|
1262
1394
|
}));
|
|
1263
|
-
this.#e({ type: "REQUEST_OK", result: t }), this._result = t,
|
|
1395
|
+
this.#e({ type: "REQUEST_OK", result: t }), this._result = t, j(this.#h), this.client.endAnonSession?.(), this._phase = "done";
|
|
1264
1396
|
} catch (e) {
|
|
1265
|
-
this.#
|
|
1397
|
+
this.#F(e);
|
|
1266
1398
|
}
|
|
1267
1399
|
}
|
|
1268
|
-
#
|
|
1269
|
-
const t =
|
|
1400
|
+
#F(e) {
|
|
1401
|
+
const t = q(e?.status ?? 0, e?.body ?? null);
|
|
1270
1402
|
if (t.kind === "auth") {
|
|
1271
|
-
this.#e({ type: "AUTH_REQUIRED" }), this
|
|
1403
|
+
if (this.#e({ type: "AUTH_REQUIRED" }), this.#f) {
|
|
1404
|
+
this.#N();
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
this._gate = { stage: "email", email: "", error: "", loading: !1 };
|
|
1272
1408
|
return;
|
|
1273
1409
|
}
|
|
1274
1410
|
if (t.kind === "stale") {
|
|
1275
|
-
this._banner = t, this.#e({ type: "REQUEST_FAIL", error: t }), this.#
|
|
1411
|
+
this._banner = t, this.#e({ type: "REQUEST_FAIL", error: t }), this.#Q();
|
|
1276
1412
|
return;
|
|
1277
1413
|
}
|
|
1278
1414
|
this._banner = t, this.#e({ type: "REQUEST_FAIL", error: t });
|
|
1279
1415
|
}
|
|
1280
|
-
async #
|
|
1416
|
+
async #Q() {
|
|
1281
1417
|
try {
|
|
1282
1418
|
this._forms = await this.client.listForms();
|
|
1283
1419
|
const e = this._forms.find((t) => t.formId === this._form.formId);
|
|
@@ -1285,86 +1421,84 @@ class Xe extends I {
|
|
|
1285
1421
|
} catch {
|
|
1286
1422
|
}
|
|
1287
1423
|
}
|
|
1288
|
-
#
|
|
1289
|
-
this._banner = null, this.#e({ type: "REQUEST_START" }), this.#
|
|
1424
|
+
#G() {
|
|
1425
|
+
this._banner = null, this.#e({ type: "REQUEST_START" }), this.#n();
|
|
1290
1426
|
}
|
|
1291
1427
|
// ── Render ──────────────────────────────────────────────────────
|
|
1292
1428
|
render() {
|
|
1293
|
-
return
|
|
1429
|
+
return a`
|
|
1294
1430
|
<div class="chat" aria-live="polite">
|
|
1295
|
-
${this.#
|
|
1431
|
+
${this.#J()}
|
|
1296
1432
|
</div>
|
|
1297
|
-
${this.#
|
|
1433
|
+
${this.#ce()}
|
|
1298
1434
|
`;
|
|
1299
1435
|
}
|
|
1300
|
-
#
|
|
1436
|
+
#J() {
|
|
1301
1437
|
switch (this._phase) {
|
|
1302
1438
|
case "loading":
|
|
1303
|
-
return
|
|
1439
|
+
return a`${h(S, this._loadingSlow ? "Loading the form list…" : A)}
|
|
1440
|
+
${E()}`;
|
|
1304
1441
|
case "load-error":
|
|
1305
|
-
return
|
|
1442
|
+
return a`
|
|
1306
1443
|
${h("We could not load the forms.", "Check your connection and try again.", { error: !0 })}
|
|
1307
1444
|
<div class="options">
|
|
1308
|
-
<button type="button" class="chip" @click=${() => this.#
|
|
1445
|
+
<button type="button" class="chip" @click=${() => this.#x()}>Try again</button>
|
|
1309
1446
|
</div>
|
|
1310
1447
|
`;
|
|
1311
1448
|
case "pick":
|
|
1312
|
-
return this.#
|
|
1449
|
+
return this.#X();
|
|
1313
1450
|
case "walk":
|
|
1314
|
-
return
|
|
1451
|
+
return a`${this.#b()}${this._typing ? E() : this.#Z()}`;
|
|
1315
1452
|
case "review":
|
|
1316
|
-
return
|
|
1453
|
+
return a`${this.#b()}${this.#D()}`;
|
|
1317
1454
|
case "submitting":
|
|
1318
|
-
return
|
|
1455
|
+
return a`${this.#b()}${this.#le()}`;
|
|
1319
1456
|
case "done":
|
|
1320
|
-
return
|
|
1457
|
+
return a`${this.#b()}${this.#de()}`;
|
|
1321
1458
|
default:
|
|
1322
1459
|
return c;
|
|
1323
1460
|
}
|
|
1324
1461
|
}
|
|
1325
|
-
#
|
|
1462
|
+
#X() {
|
|
1326
1463
|
const e = this._forms;
|
|
1327
|
-
return
|
|
1328
|
-
${h(
|
|
1329
|
-
"Welcome to Report It. What would you like to send us?",
|
|
1330
|
-
"A few quick questions route your report to the right team."
|
|
1331
|
-
)}
|
|
1464
|
+
return a`
|
|
1465
|
+
${h(S, A, { noAnim: !0 })}
|
|
1332
1466
|
<div class="options" role="group" aria-label="What would you like to send us?">
|
|
1333
|
-
${e.map((t, s) =>
|
|
1467
|
+
${e.map((t, s) => a`
|
|
1334
1468
|
<button type="button" class="chip" style="animation-delay: ${60 + s * 50}ms"
|
|
1335
|
-
@click=${() => this.#
|
|
1469
|
+
@click=${() => this.#P(t)}>${t.title}</button>
|
|
1336
1470
|
`)}
|
|
1337
1471
|
</div>
|
|
1338
1472
|
`;
|
|
1339
1473
|
}
|
|
1340
|
-
#
|
|
1474
|
+
#b() {
|
|
1341
1475
|
const e = [];
|
|
1342
|
-
this._form && (e.push(h(
|
|
1343
|
-
"Welcome to Report It. What would you like to send us?",
|
|
1344
|
-
"A few quick questions route your report to the right team.",
|
|
1345
|
-
{ noAnim: !0 }
|
|
1346
|
-
)), e.push(v(this._form.title, { noAnim: !0 }))), this._notice && e.push(h(this._notice, "", { noAnim: !0 }));
|
|
1476
|
+
this._form && (e.push(h(S, A, { noAnim: !0 })), e.push($(this._form.title, { noAnim: !0 }))), this._notice && e.push(h(this._notice, "", { noAnim: !0 }));
|
|
1347
1477
|
for (const t of this._steps) {
|
|
1348
|
-
const s =
|
|
1349
|
-
s?.prompt && e.push(h(s.prompt, s.help, { noAnim: !0 })), s?.type === "gate" && e.push(h(
|
|
1478
|
+
const s = b(this._form, t.nodeId);
|
|
1479
|
+
s?.prompt && e.push(h(s.prompt, s.help, { noAnim: !0 })), s?.type === "gate" && e.push(t.anonymous ? h(
|
|
1480
|
+
"Last step: how can we reach you?",
|
|
1481
|
+
"You chose to send this anonymously.",
|
|
1482
|
+
{ noAnim: !0 }
|
|
1483
|
+
) : h(
|
|
1350
1484
|
"Last step: verify your email so we can follow up.",
|
|
1351
1485
|
"One address, one code. No password needed.",
|
|
1352
1486
|
{ noAnim: !0 }
|
|
1353
|
-
)), t.kind === "choice" && e.push(
|
|
1487
|
+
)), t.kind === "choice" && e.push($(t.label || t.optionId, { noAnim: !0 })), t.kind === "collect" && e.push($(t.summary || "Details provided", { noAnim: !0 })), t.kind === "gate" && e.push($(t.anonymous ? "Send anonymously" : t.email, { noAnim: !0 }));
|
|
1354
1488
|
}
|
|
1355
1489
|
return e;
|
|
1356
1490
|
}
|
|
1357
|
-
#
|
|
1358
|
-
const { node: e, error: t } = this.#
|
|
1359
|
-
return t || !e ? h("Something went sideways with this form.", "Please start over.", { error: !0 }) : e.type === "choice" ? this.#
|
|
1491
|
+
#Z() {
|
|
1492
|
+
const { node: e, error: t } = this.#v;
|
|
1493
|
+
return t || !e ? h("Something went sideways with this form.", "Please start over.", { error: !0 }) : e.type === "choice" ? this.#ee(e) : e.type === "collect" ? this.#te(e) : e.type === "gate" ? this.#R(e) : e.type === "redirect" ? this.#oe(e) : e.type === "terminal" ? this.#D() : c;
|
|
1360
1494
|
}
|
|
1361
|
-
#
|
|
1362
|
-
return
|
|
1495
|
+
#ee(e) {
|
|
1496
|
+
return a`
|
|
1363
1497
|
${h(e.prompt, e.help)}
|
|
1364
1498
|
<div class="options" role="group" aria-label=${e.prompt || "Choose one"}>
|
|
1365
|
-
${(e.options || []).map((t, s) =>
|
|
1499
|
+
${(e.options || []).map((t, s) => a`
|
|
1366
1500
|
<button type="button" class="chip" style="animation-delay: ${60 + s * 50}ms"
|
|
1367
|
-
@click=${() => this.#
|
|
1501
|
+
@click=${() => this.#g({
|
|
1368
1502
|
nodeId: e.id,
|
|
1369
1503
|
kind: "choice",
|
|
1370
1504
|
optionId: t.id,
|
|
@@ -1374,61 +1508,61 @@ class Xe extends I {
|
|
|
1374
1508
|
</div>
|
|
1375
1509
|
`;
|
|
1376
1510
|
}
|
|
1377
|
-
#
|
|
1511
|
+
#te(e) {
|
|
1378
1512
|
const t = e.fields || [];
|
|
1379
|
-
return
|
|
1513
|
+
return a`
|
|
1380
1514
|
${h(e.prompt, e.help)}
|
|
1381
1515
|
<div class="form-card">
|
|
1382
|
-
${t.map((s) => this.#
|
|
1383
|
-
<button type="button" class="btn-primary" @click=${() => this.#
|
|
1516
|
+
${t.map((s) => this.#re(e, s))}
|
|
1517
|
+
<button type="button" class="btn-primary" @click=${() => this.#O(e)}>
|
|
1384
1518
|
Continue
|
|
1385
1519
|
</button>
|
|
1386
1520
|
</div>
|
|
1387
1521
|
`;
|
|
1388
1522
|
}
|
|
1389
|
-
#
|
|
1390
|
-
const s = this._cardValues[t.key], i = this._fieldErrors[t.key],
|
|
1391
|
-
<label for="f-${t.key}">${t.label}${
|
|
1523
|
+
#re(e, t) {
|
|
1524
|
+
const s = this._cardValues[t.key], i = this._fieldErrors[t.key], n = !t.required && t.kind !== "consent" ? a` <span class="opt">optional</span>` : c, o = t.kind === "consent" ? c : a`
|
|
1525
|
+
<label for="f-${t.key}">${t.label}${n}</label>
|
|
1392
1526
|
`;
|
|
1393
|
-
return
|
|
1527
|
+
return a`
|
|
1394
1528
|
<div class="field">
|
|
1395
|
-
${
|
|
1396
|
-
${this.#
|
|
1397
|
-
${i ?
|
|
1529
|
+
${o}
|
|
1530
|
+
${this.#se(e, t, s)}
|
|
1531
|
+
${i ? a`<div class="err" role="alert">${i}</div>` : c}
|
|
1398
1532
|
</div>
|
|
1399
1533
|
`;
|
|
1400
1534
|
}
|
|
1401
|
-
#
|
|
1402
|
-
const i = (
|
|
1535
|
+
#se(e, t, s) {
|
|
1536
|
+
const i = (n) => (o) => this.#_(n, o.target.value);
|
|
1403
1537
|
switch (t.kind) {
|
|
1404
1538
|
case "text":
|
|
1405
|
-
return
|
|
1539
|
+
return a`<input type="text" id="f-${t.key}" .value=${s || ""}
|
|
1406
1540
|
placeholder=${t.placeholder || ""} @input=${i(t.key)}>`;
|
|
1407
1541
|
case "url":
|
|
1408
|
-
return
|
|
1542
|
+
return a`<input type="url" id="f-${t.key}" .value=${s || ""}
|
|
1409
1543
|
placeholder=${t.placeholder || ""} @input=${i(t.key)}>`;
|
|
1410
1544
|
case "textarea":
|
|
1411
|
-
return
|
|
1545
|
+
return a`<textarea id="f-${t.key}" .value=${s || ""}
|
|
1412
1546
|
placeholder=${t.placeholder || ""} @input=${i(t.key)}></textarea>`;
|
|
1413
1547
|
case "datetime":
|
|
1414
|
-
return
|
|
1548
|
+
return a`<input type="datetime-local" id="f-${t.key}" .value=${s || ""}
|
|
1415
1549
|
@input=${i(t.key)}>`;
|
|
1416
1550
|
case "select":
|
|
1417
|
-
return
|
|
1551
|
+
return a`
|
|
1418
1552
|
<select id="f-${t.key}" @change=${i(t.key)}>
|
|
1419
1553
|
<option value="">Choose one</option>
|
|
1420
|
-
${(t.options || []).map((
|
|
1421
|
-
<option value=${
|
|
1554
|
+
${(t.options || []).map((n) => a`
|
|
1555
|
+
<option value=${n.id} ?selected=${s === n.id}>${n.label}</option>
|
|
1422
1556
|
`)}
|
|
1423
1557
|
</select>
|
|
1424
1558
|
`;
|
|
1425
1559
|
case "location":
|
|
1426
|
-
return this.#
|
|
1560
|
+
return this.#ie(t, s || {});
|
|
1427
1561
|
case "consent":
|
|
1428
|
-
return
|
|
1562
|
+
return a`
|
|
1429
1563
|
<div class="consent-row">
|
|
1430
1564
|
<input type="checkbox" id="f-${t.key}" .checked=${s === !0}
|
|
1431
|
-
@change=${(
|
|
1565
|
+
@change=${(n) => this.#_(t.key, n.target.checked)}>
|
|
1432
1566
|
<label for="f-${t.key}" style="all: unset; cursor: pointer;">
|
|
1433
1567
|
I took or own this material and give WRAL permission to use it
|
|
1434
1568
|
on air and online with credit to me.
|
|
@@ -1436,56 +1570,56 @@ class Xe extends I {
|
|
|
1436
1570
|
</div>
|
|
1437
1571
|
`;
|
|
1438
1572
|
case "media":
|
|
1439
|
-
return
|
|
1573
|
+
return a`
|
|
1440
1574
|
<wral-report-media
|
|
1441
1575
|
.files=${this.#r[e.id] || []}
|
|
1442
1576
|
.lostMeta=${this.#s[e.id] || []}
|
|
1443
1577
|
.budget=${this._form.budget || {}}
|
|
1444
1578
|
?required=${!!t.required}
|
|
1445
1579
|
label=${t.label}
|
|
1446
|
-
@files-changed=${(
|
|
1447
|
-
this.#r = { ...this.#r, [e.id]:
|
|
1580
|
+
@files-changed=${(n) => {
|
|
1581
|
+
this.#r = { ...this.#r, [e.id]: n.detail.files }, this.requestUpdate();
|
|
1448
1582
|
}}></wral-report-media>
|
|
1449
1583
|
`;
|
|
1450
1584
|
default:
|
|
1451
1585
|
return c;
|
|
1452
1586
|
}
|
|
1453
1587
|
}
|
|
1454
|
-
#
|
|
1455
|
-
const s = e.counties ||
|
|
1456
|
-
return
|
|
1588
|
+
#ie(e, t) {
|
|
1589
|
+
const s = e.counties || Ye, i = (n) => this.#_(e.key, { ...t, ...n });
|
|
1590
|
+
return a`
|
|
1457
1591
|
<select id="f-${e.key}" aria-label="County"
|
|
1458
|
-
@change=${(
|
|
1592
|
+
@change=${(n) => i({ county: n.target.value || void 0 })}>
|
|
1459
1593
|
<option value="">Choose a county</option>
|
|
1460
|
-
${s.map((
|
|
1461
|
-
<option value=${
|
|
1594
|
+
${s.map((n) => a`
|
|
1595
|
+
<option value=${n} ?selected=${t.county === n}>${n}</option>
|
|
1462
1596
|
`)}
|
|
1463
1597
|
</select>
|
|
1464
1598
|
<input type="text" style="margin-top: 8px;"
|
|
1465
1599
|
aria-label="Nearest town or crossroads"
|
|
1466
1600
|
placeholder="Nearest town or crossroads"
|
|
1467
1601
|
.value=${t.where || ""}
|
|
1468
|
-
@input=${(
|
|
1602
|
+
@input=${(n) => i({ where: n.target.value || void 0 })}>
|
|
1469
1603
|
`;
|
|
1470
1604
|
}
|
|
1471
|
-
#
|
|
1472
|
-
this
|
|
1605
|
+
#R(e) {
|
|
1606
|
+
this.#E();
|
|
1473
1607
|
const t = this._gate, s = h(
|
|
1474
1608
|
"Last step: verify your email so we can follow up.",
|
|
1475
1609
|
"One address, one code. No password needed."
|
|
1476
1610
|
);
|
|
1477
|
-
return t.stage ? t.stage === "signedin" ?
|
|
1611
|
+
return t.stage ? t.stage === "signedin" ? a`
|
|
1478
1612
|
${s}
|
|
1479
1613
|
<div class="form-card">
|
|
1480
1614
|
<p style="margin: 0 0 12px; font-size: 14.5px;">
|
|
1481
1615
|
You are signed in as <strong>${t.email || "your My WRAL account"}</strong> on this device.
|
|
1482
1616
|
</p>
|
|
1483
1617
|
<button type="button" class="btn-primary"
|
|
1484
|
-
@click=${() => this.#
|
|
1618
|
+
@click=${() => this.#A(t.email)}>Continue</button>
|
|
1485
1619
|
<button type="button" class="btn-ghost"
|
|
1486
|
-
@click=${() => this.#
|
|
1620
|
+
@click=${() => this.#U()}>Use a different email</button>
|
|
1487
1621
|
</div>
|
|
1488
|
-
` : t.stage === "email" ?
|
|
1622
|
+
` : t.stage === "email" ? a`
|
|
1489
1623
|
${s}
|
|
1490
1624
|
<div class="form-card login-card">
|
|
1491
1625
|
<h2>Sign in or create account</h2>
|
|
@@ -1500,12 +1634,12 @@ class Xe extends I {
|
|
|
1500
1634
|
this._gate = { ...t, email: i.target.value };
|
|
1501
1635
|
}}
|
|
1502
1636
|
@keydown=${(i) => {
|
|
1503
|
-
i.key === "Enter" && this
|
|
1637
|
+
i.key === "Enter" && this.#$();
|
|
1504
1638
|
}}>
|
|
1505
1639
|
</div>
|
|
1506
|
-
${t.error ?
|
|
1640
|
+
${t.error ? a`<div class="err" role="alert">${t.error}</div>` : c}
|
|
1507
1641
|
<button type="button" class="login-btn" ?disabled=${t.loading}
|
|
1508
|
-
@click=${() => this
|
|
1642
|
+
@click=${() => this.#$()}>
|
|
1509
1643
|
${t.loading ? "Sending…" : "Continue with email"}
|
|
1510
1644
|
</button>
|
|
1511
1645
|
<div class="login-why">
|
|
@@ -1514,15 +1648,12 @@ class Xe extends I {
|
|
|
1514
1648
|
<li>We can follow up on your report</li>
|
|
1515
1649
|
<li>Your contact information fills in automatically next time</li>
|
|
1516
1650
|
<li>You are credited by name when we use your photos or video</li>
|
|
1517
|
-
<li><a href=${
|
|
1651
|
+
<li><a href=${He} target="_blank" rel="noopener">All other My WRAL account benefits</a></li>
|
|
1518
1652
|
</ul>
|
|
1519
1653
|
</div>
|
|
1520
|
-
|
|
1521
|
-
Want to remain anonymous? Call us at
|
|
1522
|
-
<a href=${Oe(M)}>${M}</a>.
|
|
1523
|
-
</p>
|
|
1654
|
+
${this._submit.stage === "auth" ? c : this.#ne()}
|
|
1524
1655
|
</div>
|
|
1525
|
-
` :
|
|
1656
|
+
` : t.stage === "anon-confirm" ? this.#ae(s) : a`
|
|
1526
1657
|
${s}
|
|
1527
1658
|
<div class="form-card login-card">
|
|
1528
1659
|
<h2>Enter verification code</h2>
|
|
@@ -1536,27 +1667,71 @@ class Xe extends I {
|
|
|
1536
1667
|
<div class="field">
|
|
1537
1668
|
<label>Verification code</label>
|
|
1538
1669
|
<wral-code-input
|
|
1539
|
-
@code-complete=${(i) => this.#
|
|
1670
|
+
@code-complete=${(i) => this.#S(i.detail?.code ?? i.detail?.value ?? i.target.value)}>
|
|
1540
1671
|
</wral-code-input>
|
|
1541
1672
|
</div>
|
|
1542
|
-
${t.error ?
|
|
1673
|
+
${t.error ? a`<div class="err" role="alert">${t.error}</div>` : c}
|
|
1543
1674
|
<button type="button" class="login-btn" ?disabled=${t.loading}
|
|
1544
1675
|
@click=${() => {
|
|
1545
1676
|
const i = this.renderRoot.querySelector("wral-code-input");
|
|
1546
|
-
this.#
|
|
1677
|
+
this.#S(i?.value || "");
|
|
1547
1678
|
}}>
|
|
1548
1679
|
${t.loading ? "Checking…" : "Sign in"}
|
|
1549
1680
|
</button>
|
|
1550
1681
|
<p class="login-help">
|
|
1551
1682
|
Didn’t receive a code? Check your spam folder or
|
|
1552
|
-
<button type="button" @click=${() => this
|
|
1683
|
+
<button type="button" @click=${() => this.#$()}>resend a new code</button>.
|
|
1684
|
+
</p>
|
|
1685
|
+
</div>
|
|
1686
|
+
` : a`${s}${E()}`;
|
|
1687
|
+
}
|
|
1688
|
+
/* Collapsed by default: findable for whoever needs it, never the
|
|
1689
|
+
* first thing a viewer reaches for. */
|
|
1690
|
+
#ne() {
|
|
1691
|
+
return a`
|
|
1692
|
+
<details class="login-alt">
|
|
1693
|
+
<summary>Other ways to send this</summary>
|
|
1694
|
+
<div class="alt-body">
|
|
1695
|
+
<p class="alt-row">
|
|
1696
|
+
Call the newsroom desk at
|
|
1697
|
+
<a href=${Qe(W)}>${W}</a>.
|
|
1698
|
+
</p>
|
|
1699
|
+
${this.#B ? a`
|
|
1700
|
+
<p class="alt-row">
|
|
1701
|
+
<button type="button" class="alt-link"
|
|
1702
|
+
@click=${() => this.#q()}>Send this anonymously</button>
|
|
1703
|
+
— no follow-up, and no credit if we use your photos or video.
|
|
1704
|
+
</p>
|
|
1705
|
+
` : c}
|
|
1706
|
+
</div>
|
|
1707
|
+
</details>
|
|
1708
|
+
`;
|
|
1709
|
+
}
|
|
1710
|
+
#ae(e) {
|
|
1711
|
+
return a`
|
|
1712
|
+
${e}
|
|
1713
|
+
<div class="form-card login-card">
|
|
1714
|
+
<h2>Before you send anonymously</h2>
|
|
1715
|
+
<p class="login-sub">
|
|
1716
|
+
You can send this report without an account. Here is what it costs you.
|
|
1553
1717
|
</p>
|
|
1718
|
+
<ul class="anon-costs">
|
|
1719
|
+
${Ke.map((t) => a`
|
|
1720
|
+
<li><span class="x" aria-hidden="true">✕</span><span>${t}</span></li>
|
|
1721
|
+
`)}
|
|
1722
|
+
</ul>
|
|
1723
|
+
<div class="anon-actions">
|
|
1724
|
+
<button type="button" class="login-btn"
|
|
1725
|
+
@click=${() => this.#L()}>Sign in instead</button>
|
|
1726
|
+
<button type="button" class="btn-ghost"
|
|
1727
|
+
@click=${() => this.#j()}>Continue anonymously</button>
|
|
1728
|
+
</div>
|
|
1554
1729
|
</div>
|
|
1555
|
-
|
|
1730
|
+
`;
|
|
1556
1731
|
}
|
|
1557
|
-
#
|
|
1732
|
+
#oe(e) {
|
|
1558
1733
|
const t = e.redirect || {};
|
|
1559
|
-
return
|
|
1734
|
+
return a`
|
|
1560
1735
|
<div class="form-card">
|
|
1561
1736
|
<p style="margin: 0 0 6px; font-weight: 600; font-size: 16px;
|
|
1562
1737
|
color: var(--wral-base-primary-1, #001D68);
|
|
@@ -1573,30 +1748,33 @@ class Xe extends I {
|
|
|
1573
1748
|
</div>
|
|
1574
1749
|
`;
|
|
1575
1750
|
}
|
|
1576
|
-
#
|
|
1577
|
-
const e =
|
|
1578
|
-
return
|
|
1751
|
+
#D() {
|
|
1752
|
+
const e = Pe(this._form, this._steps);
|
|
1753
|
+
return a`
|
|
1579
1754
|
${h("Here is what you are sending.", "Check it over, then submit.")}
|
|
1580
1755
|
<div class="form-card">
|
|
1581
1756
|
<ul class="review-list">
|
|
1582
|
-
${e.map((t) =>
|
|
1757
|
+
${e.map((t) => a`
|
|
1583
1758
|
<li>
|
|
1584
1759
|
<span class="k">${t.k}</span>
|
|
1585
|
-
<span>${t.pill ?
|
|
1760
|
+
<span>${t.pill ? be(t.pill) : t.v}</span>
|
|
1586
1761
|
</li>
|
|
1587
1762
|
`)}
|
|
1588
1763
|
</ul>
|
|
1764
|
+
${this._banner ? a`
|
|
1765
|
+
<div class="err" role="alert" style="margin-top: 12px;">${this._banner.message}</div>
|
|
1766
|
+
` : c}
|
|
1589
1767
|
<div style="margin-top: 16px;">
|
|
1590
|
-
<button type="button" class="btn-primary" @click=${() => this.#
|
|
1768
|
+
<button type="button" class="btn-primary" @click=${() => this.#W()}>
|
|
1591
1769
|
Submit report
|
|
1592
1770
|
</button>
|
|
1593
1771
|
</div>
|
|
1594
1772
|
</div>
|
|
1595
1773
|
`;
|
|
1596
1774
|
}
|
|
1597
|
-
#
|
|
1775
|
+
#le() {
|
|
1598
1776
|
const e = this._submit;
|
|
1599
|
-
if (e.stage === "auth") return this.#
|
|
1777
|
+
if (e.stage === "auth") return this.#R({ id: null, type: "gate" });
|
|
1600
1778
|
const t = {
|
|
1601
1779
|
minting: "Preparing your uploads…",
|
|
1602
1780
|
uploading: "Uploading your files…",
|
|
@@ -1604,10 +1782,10 @@ class Xe extends I {
|
|
|
1604
1782
|
requesting: "Sending your report…",
|
|
1605
1783
|
"requesting-error": this._banner?.message || "We could not send your report."
|
|
1606
1784
|
}[e.stage] || "Working…";
|
|
1607
|
-
return
|
|
1785
|
+
return a`
|
|
1608
1786
|
${h("Submitting your report.", t)}
|
|
1609
1787
|
<div class="form-card">
|
|
1610
|
-
${e.files.map((s, i) =>
|
|
1788
|
+
${e.files.map((s, i) => a`
|
|
1611
1789
|
<div class="upload-row">
|
|
1612
1790
|
<span class="name">${s.name}</span>
|
|
1613
1791
|
<div class="upload-bar" role="progressbar"
|
|
@@ -1618,62 +1796,63 @@ class Xe extends I {
|
|
|
1618
1796
|
<span class="state ${s.status === "failed" ? "failed" : ""}">
|
|
1619
1797
|
${s.status === "done" ? "Done" : s.status === "failed" ? "Failed" : "Uploading"}
|
|
1620
1798
|
</span>
|
|
1621
|
-
${s.status === "failed" ?
|
|
1622
|
-
<button type="button" class="retry-chip" @click=${() => this.#
|
|
1799
|
+
${s.status === "failed" ? a`
|
|
1800
|
+
<button type="button" class="retry-chip" @click=${() => this.#H(i)}>
|
|
1623
1801
|
Retry
|
|
1624
1802
|
</button>
|
|
1625
1803
|
` : c}
|
|
1626
1804
|
</div>
|
|
1627
1805
|
`)}
|
|
1628
|
-
${e.stage === "requesting-error" ?
|
|
1806
|
+
${e.stage === "requesting-error" ? a`
|
|
1629
1807
|
<div class="err" role="alert">${this._banner?.message || ""}</div>
|
|
1630
|
-
${this._banner?.retryAfter ?
|
|
1808
|
+
${this._banner?.retryAfter ? a`
|
|
1631
1809
|
<div class="file-note">You can try again in about
|
|
1632
1810
|
${Math.ceil(this._banner.retryAfter / 60)} minutes.</div>
|
|
1633
1811
|
` : c}
|
|
1634
1812
|
<button type="button" class="btn-primary" style="margin-top: 8px;"
|
|
1635
|
-
@click=${() => this.#
|
|
1813
|
+
@click=${() => this.#G()}>Try again</button>
|
|
1636
1814
|
` : c}
|
|
1637
|
-
${
|
|
1815
|
+
${N(e).length && e.stage === "uploading-error" ? a`
|
|
1638
1816
|
<div class="err" role="alert">Retry the failed
|
|
1639
|
-
${
|
|
1817
|
+
${N(e).length === 1 ? "file" : "files"} to continue.</div>
|
|
1640
1818
|
` : c}
|
|
1641
1819
|
</div>
|
|
1642
1820
|
`;
|
|
1643
1821
|
}
|
|
1644
|
-
#
|
|
1645
|
-
const e =
|
|
1822
|
+
#de() {
|
|
1823
|
+
const e = Y(this._form, this._steps), t = Me(this._result, e), s = Ue({
|
|
1646
1824
|
routing: this._result?.routing || {},
|
|
1647
1825
|
flags: e,
|
|
1648
|
-
reference: this._result?.reference || ""
|
|
1826
|
+
reference: this._result?.reference || "",
|
|
1827
|
+
anonymous: this.#f
|
|
1649
1828
|
});
|
|
1650
|
-
return
|
|
1829
|
+
return a`
|
|
1651
1830
|
<div class="confirm-card">
|
|
1652
1831
|
<div class="c-head">
|
|
1653
|
-
<div class="c-check">${
|
|
1832
|
+
<div class="c-check">${ye()}</div>
|
|
1654
1833
|
<div>
|
|
1655
1834
|
<div class="eyebrow">Report received</div>
|
|
1656
1835
|
<h2>${t.title}</h2>
|
|
1657
1836
|
</div>
|
|
1658
1837
|
</div>
|
|
1659
1838
|
<p>${t.body}</p>
|
|
1660
|
-
${t.urgentNote ?
|
|
1839
|
+
${t.urgentNote ? a`<p>${t.urgentNote}</p>` : c}
|
|
1661
1840
|
<div class="refs">
|
|
1662
1841
|
<span class="ref"><span class="rk">Reference</span>${this._result?.reference || ""}</span>
|
|
1663
1842
|
</div>
|
|
1664
|
-
<button type="button" class="c-again" @click=${() => this.#
|
|
1843
|
+
<button type="button" class="c-again" @click=${() => this.#k()}>
|
|
1665
1844
|
Start a new report
|
|
1666
1845
|
</button>
|
|
1667
1846
|
</div>
|
|
1668
1847
|
<div class="journey">
|
|
1669
1848
|
<div class="j-eyebrow">Behind the scenes</div>
|
|
1670
1849
|
<div class="j-title">What happens to your report now</div>
|
|
1671
|
-
${s.map((i,
|
|
1850
|
+
${s.map((i, n) => a`
|
|
1672
1851
|
<div class="j-step">
|
|
1673
|
-
<div class="j-dot">${
|
|
1852
|
+
<div class="j-dot">${n + 1}</div>
|
|
1674
1853
|
<div class="j-body">
|
|
1675
1854
|
<div class="j-t">${i.title}
|
|
1676
|
-
${i.pill ?
|
|
1855
|
+
${i.pill ? a` <span class="flag-pill urgent">${i.pill}</span>` : c}
|
|
1677
1856
|
</div>
|
|
1678
1857
|
<div class="j-d">${i.body}</div>
|
|
1679
1858
|
</div>
|
|
@@ -1682,15 +1861,15 @@ class Xe extends I {
|
|
|
1682
1861
|
</div>
|
|
1683
1862
|
`;
|
|
1684
1863
|
}
|
|
1685
|
-
#
|
|
1864
|
+
#ce() {
|
|
1686
1865
|
const e = this._phase === "submitting" || this._phase === "done" || this._phase === "loading" || this._phase === "load-error";
|
|
1687
|
-
return
|
|
1866
|
+
return a`
|
|
1688
1867
|
<div class="bottom-bar">
|
|
1689
1868
|
<div class="inner">
|
|
1690
1869
|
<button type="button" class="btn-ghost" ?disabled=${e || this._phase === "pick"}
|
|
1691
|
-
@click=${() => this.#
|
|
1870
|
+
@click=${() => this.#M()}>← Back</button>
|
|
1692
1871
|
<button type="button" class="btn-ghost" ?disabled=${this._phase === "submitting"}
|
|
1693
|
-
@click=${() => this.#
|
|
1872
|
+
@click=${() => this.#k()}>Start over</button>
|
|
1694
1873
|
<span class="note">Your report goes to a person on the right desk.</span>
|
|
1695
1874
|
</div>
|
|
1696
1875
|
</div>
|
|
@@ -1700,7 +1879,7 @@ class Xe extends I {
|
|
|
1700
1879
|
this.renderRoot?.querySelector(".chat")?.lastElementChild?.scrollIntoView?.({ block: "nearest" });
|
|
1701
1880
|
}
|
|
1702
1881
|
}
|
|
1703
|
-
class
|
|
1882
|
+
class ot extends T {
|
|
1704
1883
|
static properties = {
|
|
1705
1884
|
files: { attribute: !1 },
|
|
1706
1885
|
lostMeta: { attribute: !1 },
|
|
@@ -1708,7 +1887,7 @@ class Ze extends I {
|
|
|
1708
1887
|
required: { type: Boolean },
|
|
1709
1888
|
label: { type: String }
|
|
1710
1889
|
};
|
|
1711
|
-
static styles =
|
|
1890
|
+
static styles = f`
|
|
1712
1891
|
:host { display: block; }
|
|
1713
1892
|
input[type=file] { font-family: inherit; font-size: 14px; }
|
|
1714
1893
|
.file-list { margin: 8px 0 0; padding: 0; list-style: none; font-size: 13.5px; }
|
|
@@ -1730,7 +1909,7 @@ class Ze extends I {
|
|
|
1730
1909
|
constructor() {
|
|
1731
1910
|
super(), this.files = [], this.lostMeta = [], this.budget = {}, this.required = !1, this.label = "Photos or video";
|
|
1732
1911
|
}
|
|
1733
|
-
get #
|
|
1912
|
+
get #l() {
|
|
1734
1913
|
return (this.budget?.allowedTypes || []).join(",");
|
|
1735
1914
|
}
|
|
1736
1915
|
#a(e) {
|
|
@@ -1749,18 +1928,18 @@ class Ze extends I {
|
|
|
1749
1928
|
t.splice(e, 1), this.#a(t);
|
|
1750
1929
|
}
|
|
1751
1930
|
render() {
|
|
1752
|
-
const e = this.files || [], { errors: t } =
|
|
1753
|
-
return
|
|
1931
|
+
const e = this.files || [], { errors: t } = C(e, this.budget, { required: !1 });
|
|
1932
|
+
return a`
|
|
1754
1933
|
<input type="file" multiple
|
|
1755
|
-
accept=${this.#
|
|
1934
|
+
accept=${this.#l || c}
|
|
1756
1935
|
aria-label=${this.label}
|
|
1757
1936
|
@change=${this.#u}>
|
|
1758
|
-
${e.length ?
|
|
1937
|
+
${e.length ? a`
|
|
1759
1938
|
<ul class="file-list">
|
|
1760
|
-
${e.map((s, i) =>
|
|
1939
|
+
${e.map((s, i) => a`
|
|
1761
1940
|
<li>
|
|
1762
1941
|
<span>${s.name}</span>
|
|
1763
|
-
<span class="size">${
|
|
1942
|
+
<span class="size">${et(s.size)}</span>
|
|
1764
1943
|
<button type="button" class="remove"
|
|
1765
1944
|
aria-label="Remove ${s.name}"
|
|
1766
1945
|
@click=${() => this.#t(i)}>✕</button>
|
|
@@ -1768,50 +1947,50 @@ class Ze extends I {
|
|
|
1768
1947
|
`)}
|
|
1769
1948
|
</ul>
|
|
1770
1949
|
` : c}
|
|
1771
|
-
${(this.lostMeta || []).length ?
|
|
1950
|
+
${(this.lostMeta || []).length ? a`
|
|
1772
1951
|
<div class="lost">
|
|
1773
1952
|
Please re-attach your ${this.lostMeta.length === 1 ? "file" : "files"}
|
|
1774
1953
|
(${this.lostMeta.map((s) => s.name).join(", ")}) —
|
|
1775
1954
|
attachments can't be saved between visits.
|
|
1776
1955
|
</div>
|
|
1777
1956
|
` : c}
|
|
1778
|
-
${t.map((s) =>
|
|
1957
|
+
${t.map((s) => a`<div class="err">${s.message}</div>`)}
|
|
1779
1958
|
`;
|
|
1780
1959
|
}
|
|
1781
1960
|
}
|
|
1782
|
-
function
|
|
1961
|
+
function et(r) {
|
|
1783
1962
|
return Number.isFinite(r) ? r >= 1024 * 1024 ? `${(r / (1024 * 1024)).toFixed(1)} MB` : r >= 1024 ? `${Math.round(r / 1024)} KB` : `${r} B` : "";
|
|
1784
1963
|
}
|
|
1785
|
-
const
|
|
1786
|
-
const i = e.target.value.replace(/\D/g, "").slice(-1),
|
|
1787
|
-
|
|
1788
|
-
const
|
|
1789
|
-
return s("code-change", { value:
|
|
1790
|
-
},
|
|
1964
|
+
const tt = (r, e, t, s) => {
|
|
1965
|
+
const i = e.target.value.replace(/\D/g, "").slice(-1), n = [...t];
|
|
1966
|
+
n[r] = i;
|
|
1967
|
+
const o = n.join(""), l = n.every((d) => d !== "");
|
|
1968
|
+
return s("code-change", { value: o, complete: l }), i && l && s("code-complete", { value: o }), n;
|
|
1969
|
+
}, rt = (r, e, t, s) => {
|
|
1791
1970
|
if (e.key === "Backspace") {
|
|
1792
1971
|
const i = [...t];
|
|
1793
1972
|
!i[r] && r > 0 ? i[r - 1] = "" : i[r] && (i[r] = "");
|
|
1794
|
-
const
|
|
1795
|
-
return s("code-change", { value:
|
|
1973
|
+
const n = i.join("");
|
|
1974
|
+
return s("code-change", { value: n, complete: !1 }), i;
|
|
1796
1975
|
}
|
|
1797
1976
|
return t;
|
|
1798
|
-
},
|
|
1977
|
+
}, st = (r, e, t) => {
|
|
1799
1978
|
r.preventDefault();
|
|
1800
1979
|
const s = r.clipboardData.getData("text").replace(/\D/g, "").slice(0, 6);
|
|
1801
1980
|
if (s.length === 0) return e;
|
|
1802
1981
|
const i = [...e];
|
|
1803
|
-
for (let
|
|
1804
|
-
i[
|
|
1805
|
-
const
|
|
1806
|
-
return t("code-change", { value:
|
|
1982
|
+
for (let l = 0; l < s.length && l < 6; l++)
|
|
1983
|
+
i[l] = s[l];
|
|
1984
|
+
const n = i.join(""), o = i.every((l) => l !== "");
|
|
1985
|
+
return t("code-change", { value: n, complete: o }), s.length === 6 && t("code-complete", { value: n }), i;
|
|
1807
1986
|
};
|
|
1808
|
-
class
|
|
1987
|
+
class lt extends T {
|
|
1809
1988
|
static formAssociated = !0;
|
|
1810
1989
|
static properties = {
|
|
1811
1990
|
_values: { type: Array, state: !0 },
|
|
1812
1991
|
disabled: { type: Boolean, reflect: !0 }
|
|
1813
1992
|
};
|
|
1814
|
-
static styles =
|
|
1993
|
+
static styles = f`
|
|
1815
1994
|
:host {
|
|
1816
1995
|
display: block;
|
|
1817
1996
|
width: 100%;
|
|
@@ -1910,7 +2089,7 @@ class et extends I {
|
|
|
1910
2089
|
this.getInputs()[e]?.focus();
|
|
1911
2090
|
}
|
|
1912
2091
|
inputHandler(e, t) {
|
|
1913
|
-
this._values =
|
|
2092
|
+
this._values = tt(
|
|
1914
2093
|
e,
|
|
1915
2094
|
t,
|
|
1916
2095
|
this._values,
|
|
@@ -1920,7 +2099,7 @@ class et extends I {
|
|
|
1920
2099
|
keydownHandler(e, t) {
|
|
1921
2100
|
if (t.key === "Backspace") {
|
|
1922
2101
|
const s = !this._values[e];
|
|
1923
|
-
this._values =
|
|
2102
|
+
this._values = rt(
|
|
1924
2103
|
e,
|
|
1925
2104
|
t,
|
|
1926
2105
|
this._values,
|
|
@@ -1929,12 +2108,12 @@ class et extends I {
|
|
|
1929
2108
|
}
|
|
1930
2109
|
}
|
|
1931
2110
|
pasteHandler(e) {
|
|
1932
|
-
this._values =
|
|
2111
|
+
this._values = st(e, this._values, this.dispatchCustomEvent.bind(this));
|
|
1933
2112
|
}
|
|
1934
2113
|
render() {
|
|
1935
|
-
return
|
|
2114
|
+
return a`
|
|
1936
2115
|
<div class="code-inputs">
|
|
1937
|
-
${[0, 1, 2, 3, 4, 5].map((e) =>
|
|
2116
|
+
${[0, 1, 2, 3, 4, 5].map((e) => a`
|
|
1938
2117
|
<input
|
|
1939
2118
|
type = "text"
|
|
1940
2119
|
inputmode = "numeric"
|
|
@@ -1954,10 +2133,10 @@ class et extends I {
|
|
|
1954
2133
|
}
|
|
1955
2134
|
}
|
|
1956
2135
|
export {
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
2136
|
+
lt as W,
|
|
2137
|
+
ot as a,
|
|
2138
|
+
at as b,
|
|
2139
|
+
ve as c,
|
|
2140
|
+
xe as d,
|
|
2141
|
+
ke as e
|
|
1963
2142
|
};
|