@nodii/onboarding 0.1.0-dev.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/dist/chunk-AZE3NPSY.js +186 -0
- package/dist/chunk-AZE3NPSY.js.map +1 -0
- package/dist/{chunk-BQCTDU5V.js → chunk-BMQINH6O.js} +89 -15
- package/dist/chunk-BMQINH6O.js.map +1 -0
- package/dist/{chunk-34BQX2KR.js → chunk-F75ZJERM.js} +41 -6
- package/dist/chunk-F75ZJERM.js.map +1 -0
- package/dist/chunk-FIRSA7EH.js +3 -0
- package/dist/{chunk-ZDY2I6KY.js.map → chunk-FIRSA7EH.js.map} +1 -1
- package/dist/{chunk-SWMIIUXR.js → chunk-IPAKPLIF.js} +590 -92
- package/dist/chunk-IPAKPLIF.js.map +1 -0
- package/dist/chunk-JFDQZ3AC.js +3 -0
- package/dist/chunk-JFDQZ3AC.js.map +1 -0
- package/dist/{chunk-4SWOVHZ2.js → chunk-KQTS47FC.js} +129 -17
- package/dist/chunk-KQTS47FC.js.map +1 -0
- package/dist/chunk-LVJM4CTG.js +306 -0
- package/dist/chunk-LVJM4CTG.js.map +1 -0
- package/dist/chunk-SEKNVHD3.js +59 -0
- package/dist/chunk-SEKNVHD3.js.map +1 -0
- package/dist/chunk-SIMLMQPT.js +37 -0
- package/dist/chunk-SIMLMQPT.js.map +1 -0
- package/dist/chunk-U56GQITG.js +79 -0
- package/dist/chunk-U56GQITG.js.map +1 -0
- package/dist/{chunk-AFCJLUPK.js → chunk-U6AXKGYQ.js} +14 -5
- package/dist/chunk-U6AXKGYQ.js.map +1 -0
- package/dist/{chunk-AW7W3HAC.js → chunk-XJTJPBJB.js} +40 -3
- package/dist/chunk-XJTJPBJB.js.map +1 -0
- package/dist/client-CqE2dF9A.d.ts +86 -0
- package/dist/consent/index.d.ts +59 -0
- package/dist/consent/index.js +7 -0
- package/dist/consent/index.js.map +1 -0
- package/dist/controller-BQb62D0N.d.ts +177 -0
- package/dist/dynamic/index.d.ts +57 -6
- package/dist/dynamic/index.js +4 -2
- package/dist/errors-vJkye4a0.d.ts +110 -0
- package/dist/form/index.d.ts +2 -2
- package/dist/form/index.js +2 -2
- package/dist/index-Dqfa890U.d.ts +83 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.js +12 -8
- package/dist/{model-IEyDI54w.d.ts → model-ESLSEbi0.d.ts} +199 -46
- package/dist/model-mnjAEtKe.d.ts +253 -0
- package/dist/render-DC7P2BGQ.js +5 -0
- package/dist/render-DC7P2BGQ.js.map +1 -0
- package/dist/schema-BrXb9gnh.d.ts +267 -0
- package/dist/{signup-client-Bl6eUrxR.d.ts → signup-client-BL5DfarJ.d.ts} +152 -19
- package/dist/spec/index.d.ts +10 -81
- package/dist/spec/index.js +3 -2
- package/dist/submit/index.d.ts +7 -71
- package/dist/submit/index.js +3 -3
- package/dist/viewer/index.d.ts +4 -0
- package/dist/viewer/index.js +5 -0
- package/dist/viewer/index.js.map +1 -0
- package/dist/widget/index.d.ts +50 -7
- package/dist/widget/index.js +9 -7
- package/package.json +21 -2
- package/dist/chunk-34BQX2KR.js.map +0 -1
- package/dist/chunk-4SWOVHZ2.js.map +0 -1
- package/dist/chunk-AFCJLUPK.js.map +0 -1
- package/dist/chunk-AW7W3HAC.js.map +0 -1
- package/dist/chunk-BIFANV7M.js +0 -22
- package/dist/chunk-BIFANV7M.js.map +0 -1
- package/dist/chunk-BQCTDU5V.js.map +0 -1
- package/dist/chunk-SWMIIUXR.js.map +0 -1
- package/dist/chunk-ZDY2I6KY.js +0 -3
- package/dist/schema-TfyBU6JW.d.ts +0 -107
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createContactFormState } from './chunk-
|
|
2
|
-
import { defaultSpecCache, fetchOnboardingSpec, diffSpecFields } from './chunk-
|
|
3
|
-
import { createSignupStepper, optionValue } from './chunk-
|
|
4
|
-
import { computePayloadFingerprint, generateIdempotencyKey, submitLead, computeSignupFingerprint, submitSignup } from './chunk-
|
|
5
|
-
import { OnboardingBotChallengeError, OnboardingRateLimitError, OnboardingSpecStaleError } from './chunk-
|
|
1
|
+
import { createContactFormState } from './chunk-U6AXKGYQ.js';
|
|
2
|
+
import { defaultSpecCache, fetchOnboardingSpec, diffSpecFields } from './chunk-F75ZJERM.js';
|
|
3
|
+
import { createSignupStepper, optionValue } from './chunk-KQTS47FC.js';
|
|
4
|
+
import { computePayloadFingerprint, generateIdempotencyKey, submitLead, computeSignupFingerprint, submitSignup } from './chunk-BMQINH6O.js';
|
|
5
|
+
import { OnboardingBotChallengeError, OnboardingRateLimitError, OnboardingSpecStaleError, OnboardingConsentStaleError, OnboardingConsentIncompleteError } from './chunk-XJTJPBJB.js';
|
|
6
6
|
import { loadRecaptcha, executeRecaptcha } from './chunk-7NS7ENJV.js';
|
|
7
7
|
|
|
8
8
|
// src/widget/dom-utils.ts
|
|
@@ -243,6 +243,193 @@ var DEFAULT_WIDGET_CSS = `
|
|
|
243
243
|
text-align: center;
|
|
244
244
|
color: var(--_label);
|
|
245
245
|
}
|
|
246
|
+
|
|
247
|
+
/* \u2500\u2500 D543 ConsentPack step (full-width, generous spacing) \u2500\u2500 */
|
|
248
|
+
|
|
249
|
+
.nodii-onboarding-consent-stage {
|
|
250
|
+
/* Full-bleed within the widget host \u2014 consent cards may span the page. */
|
|
251
|
+
width: 100%;
|
|
252
|
+
display: flex;
|
|
253
|
+
flex-direction: column;
|
|
254
|
+
gap: calc(var(--_spacing) * 2);
|
|
255
|
+
margin: calc(var(--_spacing) * 1.5) 0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.nodii-onboarding-consent-intro {
|
|
259
|
+
font-size: 0.9375rem;
|
|
260
|
+
color: var(--_label);
|
|
261
|
+
margin: 0 0 calc(var(--_spacing) * 0.5) 0;
|
|
262
|
+
line-height: 1.6;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.nodii-onboarding-consent-card {
|
|
266
|
+
width: 100%;
|
|
267
|
+
border: 1px solid var(--_border);
|
|
268
|
+
border-radius: var(--_radius);
|
|
269
|
+
background: var(--_input-bg);
|
|
270
|
+
padding: calc(var(--_spacing) * 1.5);
|
|
271
|
+
display: flex;
|
|
272
|
+
flex-direction: column;
|
|
273
|
+
gap: var(--_spacing);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.nodii-onboarding-consent-card.is-stale {
|
|
277
|
+
border-color: #fcd34d;
|
|
278
|
+
background: #fffbeb;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.nodii-onboarding-consent-card.has-error {
|
|
282
|
+
border-color: var(--_error);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.nodii-onboarding-consent-title {
|
|
286
|
+
font-size: 1.125rem;
|
|
287
|
+
font-weight: 600;
|
|
288
|
+
color: var(--_label);
|
|
289
|
+
margin: 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.nodii-onboarding-consent-stale-tag {
|
|
293
|
+
font-size: 0.8125rem;
|
|
294
|
+
font-weight: 500;
|
|
295
|
+
color: #92400e;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.nodii-onboarding-consent-gate {
|
|
299
|
+
width: 100%;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.nodii-onboarding-consent-scrollbox {
|
|
303
|
+
width: 100%;
|
|
304
|
+
height: 360px;
|
|
305
|
+
overflow-y: auto;
|
|
306
|
+
border: 1px solid var(--_border);
|
|
307
|
+
border-radius: var(--_radius);
|
|
308
|
+
background: #fff;
|
|
309
|
+
position: relative;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.nodii-onboarding-consent-scrollbox:focus {
|
|
313
|
+
outline: none;
|
|
314
|
+
border-color: var(--_primary);
|
|
315
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--_primary) 20%, transparent);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* Inline, same-origin document content (no iframe). Rendered by the default
|
|
319
|
+
viewer: sanitized HTML/markdown, escaped plain text, or PDF.js canvases. */
|
|
320
|
+
.nodii-onboarding-consent-doc-body {
|
|
321
|
+
padding: var(--_spacing);
|
|
322
|
+
font-size: 0.9375rem;
|
|
323
|
+
line-height: 1.7;
|
|
324
|
+
color: var(--_label);
|
|
325
|
+
word-break: break-word;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.nodii-onboarding-consent-doc-body:not(.nodii-consent-pdf) {
|
|
329
|
+
white-space: pre-wrap;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.nodii-onboarding-consent-doc-loading {
|
|
333
|
+
padding: var(--_spacing);
|
|
334
|
+
color: #6b7280;
|
|
335
|
+
font-size: 0.875rem;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.nodii-consent-pdf {
|
|
339
|
+
display: flex;
|
|
340
|
+
flex-direction: column;
|
|
341
|
+
gap: var(--_spacing);
|
|
342
|
+
white-space: normal;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.nodii-consent-pdf-page {
|
|
346
|
+
max-width: 100%;
|
|
347
|
+
height: auto;
|
|
348
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.nodii-onboarding-consent-note {
|
|
352
|
+
font-size: 0.8125rem;
|
|
353
|
+
color: #6b7280;
|
|
354
|
+
margin: calc(var(--_spacing) * 0.5) 0 0 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* A gated doc that could not be rendered inline: the gate stays unsatisfiable
|
|
358
|
+
(accept disabled); we surface a clear error + Retry. NO click-to-satisfy. */
|
|
359
|
+
.nodii-onboarding-consent-unrenderable {
|
|
360
|
+
display: flex;
|
|
361
|
+
flex-direction: column;
|
|
362
|
+
gap: calc(var(--_spacing) * 0.5);
|
|
363
|
+
padding: var(--_spacing);
|
|
364
|
+
border: 1px solid color-mix(in srgb, var(--_error) 30%, transparent);
|
|
365
|
+
border-radius: var(--_radius);
|
|
366
|
+
background: color-mix(in srgb, var(--_error) 6%, transparent);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.nodii-onboarding-consent-retry {
|
|
370
|
+
align-self: flex-start;
|
|
371
|
+
padding: calc(var(--_spacing) * 0.5) var(--_spacing);
|
|
372
|
+
background: transparent;
|
|
373
|
+
color: var(--_primary);
|
|
374
|
+
border: 1px solid var(--_primary);
|
|
375
|
+
border-radius: var(--_radius);
|
|
376
|
+
font-family: var(--_font);
|
|
377
|
+
font-size: 0.875rem;
|
|
378
|
+
cursor: pointer;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.nodii-onboarding-consent-open-row,
|
|
382
|
+
.nodii-onboarding-consent-preview-row {
|
|
383
|
+
display: flex;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.nodii-onboarding-consent-open {
|
|
387
|
+
display: inline-flex;
|
|
388
|
+
align-items: center;
|
|
389
|
+
padding: calc(var(--_spacing) * 0.625) var(--_spacing);
|
|
390
|
+
border: 1px solid var(--_primary);
|
|
391
|
+
border-radius: var(--_radius);
|
|
392
|
+
color: var(--_primary);
|
|
393
|
+
text-decoration: none;
|
|
394
|
+
font-size: 0.9375rem;
|
|
395
|
+
font-weight: 500;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.nodii-onboarding-consent-open:hover {
|
|
399
|
+
background: color-mix(in srgb, var(--_primary) 8%, transparent);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.nodii-onboarding-consent-preview {
|
|
403
|
+
font-size: 0.875rem;
|
|
404
|
+
color: var(--_primary);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.nodii-onboarding-consent-accept-row {
|
|
408
|
+
display: flex;
|
|
409
|
+
align-items: flex-start;
|
|
410
|
+
gap: calc(var(--_spacing) * 0.5);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.nodii-onboarding-consent-accept {
|
|
414
|
+
margin-top: 3px;
|
|
415
|
+
width: 1.125rem;
|
|
416
|
+
height: 1.125rem;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.nodii-onboarding-consent-accept:disabled {
|
|
420
|
+
cursor: not-allowed;
|
|
421
|
+
opacity: 0.5;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.nodii-onboarding-consent-accept-label {
|
|
425
|
+
font-size: 0.9375rem;
|
|
426
|
+
color: var(--_label);
|
|
427
|
+
line-height: 1.5;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.nodii-onboarding-consent-required {
|
|
431
|
+
color: var(--_error);
|
|
432
|
+
}
|
|
246
433
|
`;
|
|
247
434
|
function injectWidgetStyles() {
|
|
248
435
|
if (typeof document === "undefined") return;
|
|
@@ -295,13 +482,13 @@ function setFieldError(container, fieldId, message) {
|
|
|
295
482
|
if (inputEl) inputEl.classList.add("has-error");
|
|
296
483
|
}
|
|
297
484
|
function clearFieldErrors(container) {
|
|
298
|
-
for (const
|
|
485
|
+
for (const el4 of container.querySelectorAll(
|
|
299
486
|
".nodii-onboarding-field-error"
|
|
300
487
|
)) {
|
|
301
|
-
|
|
488
|
+
el4.textContent = "";
|
|
302
489
|
}
|
|
303
|
-
for (const
|
|
304
|
-
|
|
490
|
+
for (const el4 of container.querySelectorAll(".has-error")) {
|
|
491
|
+
el4.classList.remove("has-error");
|
|
305
492
|
}
|
|
306
493
|
}
|
|
307
494
|
function applyErrors(container, errors) {
|
|
@@ -313,8 +500,10 @@ function applyErrors(container, errors) {
|
|
|
313
500
|
function mountContactForm(host, options) {
|
|
314
501
|
injectWidgetStyles();
|
|
315
502
|
const formControl = createContactFormState();
|
|
316
|
-
|
|
317
|
-
|
|
503
|
+
if (options.siteKey) {
|
|
504
|
+
loadRecaptcha(options.siteKey).catch(() => {
|
|
505
|
+
});
|
|
506
|
+
}
|
|
318
507
|
const wrapper = el("div", { class: "nodii-onboarding-widget" });
|
|
319
508
|
const nameInput = el("input", {
|
|
320
509
|
type: "text",
|
|
@@ -475,14 +664,18 @@ function mountContactForm(host, options) {
|
|
|
475
664
|
lastFingerprint = fingerprint;
|
|
476
665
|
}
|
|
477
666
|
let firstToken;
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
667
|
+
if (options.siteKey) {
|
|
668
|
+
try {
|
|
669
|
+
firstToken = await executeRecaptcha(options.siteKey, "contact_submit", {
|
|
670
|
+
timeoutMs: options.timeoutMs
|
|
671
|
+
});
|
|
672
|
+
} catch (recaptchaErr) {
|
|
673
|
+
formControl.setError(
|
|
674
|
+
"Could not load bot protection. Please try again."
|
|
675
|
+
);
|
|
676
|
+
options.onError?.(recaptchaErr);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
486
679
|
}
|
|
487
680
|
let usedFirst = false;
|
|
488
681
|
const tokenProvider = async () => {
|
|
@@ -497,7 +690,7 @@ function mountContactForm(host, options) {
|
|
|
497
690
|
try {
|
|
498
691
|
await submitLead(values, {
|
|
499
692
|
baseUrl: options.baseUrl,
|
|
500
|
-
botToken: tokenProvider,
|
|
693
|
+
...options.siteKey ? { botToken: tokenProvider } : {},
|
|
501
694
|
idempotencyKey,
|
|
502
695
|
fetch: options.fetch,
|
|
503
696
|
timeoutMs: options.timeoutMs
|
|
@@ -543,13 +736,243 @@ function mountContactForm(host, options) {
|
|
|
543
736
|
};
|
|
544
737
|
}
|
|
545
738
|
|
|
739
|
+
// src/widget/consent-step.ts
|
|
740
|
+
var CONSENT_ID_PREFIX = "nodii-consent-doc-";
|
|
741
|
+
function el2(tag, attrs = {}, children = []) {
|
|
742
|
+
const element = document.createElement(tag);
|
|
743
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
744
|
+
element.setAttribute(key, value);
|
|
745
|
+
}
|
|
746
|
+
for (const child of children) {
|
|
747
|
+
if (typeof child === "string") {
|
|
748
|
+
element.appendChild(document.createTextNode(child));
|
|
749
|
+
} else {
|
|
750
|
+
element.appendChild(child);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return element;
|
|
754
|
+
}
|
|
755
|
+
function consentDocElementId(key) {
|
|
756
|
+
return `${CONSENT_ID_PREFIX}${key.replace(/[^A-Za-z0-9_-]/g, "-")}`;
|
|
757
|
+
}
|
|
758
|
+
function buildAcceptRow(control, doc) {
|
|
759
|
+
const id = consentDocElementId(doc.key);
|
|
760
|
+
const checkbox = el2("input", {
|
|
761
|
+
type: "checkbox",
|
|
762
|
+
id,
|
|
763
|
+
class: "nodii-onboarding-consent-accept"
|
|
764
|
+
});
|
|
765
|
+
checkbox.checked = doc.accepted;
|
|
766
|
+
checkbox.disabled = !doc.gateSatisfied;
|
|
767
|
+
checkbox.addEventListener("change", () => {
|
|
768
|
+
control.setAccepted(doc.key, checkbox.checked);
|
|
769
|
+
checkbox.checked = control.getState().documents.find((d) => d.key === doc.key)?.accepted ?? false;
|
|
770
|
+
});
|
|
771
|
+
const labelText = doc.acceptanceLevel === "checkbox" ? `I accept the ${doc.doc.title}` : `I have reviewed and accept the ${doc.doc.title}`;
|
|
772
|
+
const label = el2(
|
|
773
|
+
"label",
|
|
774
|
+
{ for: id, class: "nodii-onboarding-consent-accept-label" },
|
|
775
|
+
[labelText]
|
|
776
|
+
);
|
|
777
|
+
if (doc.required) {
|
|
778
|
+
label.appendChild(
|
|
779
|
+
el2("span", { class: "nodii-onboarding-consent-required" }, [" *"])
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
const row = el2("div", { class: "nodii-onboarding-consent-accept-row" }, [
|
|
783
|
+
checkbox,
|
|
784
|
+
label
|
|
785
|
+
]);
|
|
786
|
+
const errorEl = el2("div", {
|
|
787
|
+
class: "nodii-onboarding-field-error nodii-onboarding-consent-error",
|
|
788
|
+
id: `${id}-error`,
|
|
789
|
+
role: "alert",
|
|
790
|
+
"aria-live": "polite"
|
|
791
|
+
});
|
|
792
|
+
return { row, errorEl };
|
|
793
|
+
}
|
|
794
|
+
function buildOpenAffordance(control, doc) {
|
|
795
|
+
const id = consentDocElementId(doc.key);
|
|
796
|
+
const openLink = el2(
|
|
797
|
+
"a",
|
|
798
|
+
{
|
|
799
|
+
class: "nodii-onboarding-consent-open",
|
|
800
|
+
id: `${id}-open`,
|
|
801
|
+
href: doc.doc.downloadUrl,
|
|
802
|
+
target: "_blank",
|
|
803
|
+
rel: "noopener noreferrer"
|
|
804
|
+
},
|
|
805
|
+
[`Open ${doc.doc.title}`]
|
|
806
|
+
);
|
|
807
|
+
const markOpened = () => control.markOpened(doc.key);
|
|
808
|
+
openLink.addEventListener("click", markOpened);
|
|
809
|
+
openLink.addEventListener("keydown", (event) => {
|
|
810
|
+
if (event.key === "Enter") markOpened();
|
|
811
|
+
});
|
|
812
|
+
return el2("div", { class: "nodii-onboarding-consent-open-row" }, [openLink]);
|
|
813
|
+
}
|
|
814
|
+
function buildGateAffordance(control, doc, opts) {
|
|
815
|
+
const id = consentDocElementId(doc.key);
|
|
816
|
+
if (doc.acceptanceLevel === "open_then_accept") {
|
|
817
|
+
return buildOpenAffordance(control, doc);
|
|
818
|
+
}
|
|
819
|
+
if (doc.acceptanceLevel === "checkbox") {
|
|
820
|
+
return el2("div", { class: "nodii-onboarding-consent-preview-row" }, [
|
|
821
|
+
el2(
|
|
822
|
+
"a",
|
|
823
|
+
{
|
|
824
|
+
class: "nodii-onboarding-consent-preview",
|
|
825
|
+
id: `${id}-preview`,
|
|
826
|
+
href: doc.doc.downloadUrl,
|
|
827
|
+
target: "_blank",
|
|
828
|
+
rel: "noopener noreferrer"
|
|
829
|
+
},
|
|
830
|
+
[`View ${doc.doc.title}`]
|
|
831
|
+
)
|
|
832
|
+
]);
|
|
833
|
+
}
|
|
834
|
+
const container = el2("div", {
|
|
835
|
+
class: "nodii-onboarding-consent-gate",
|
|
836
|
+
id: `${id}-gate`
|
|
837
|
+
});
|
|
838
|
+
container.appendChild(
|
|
839
|
+
el2("div", { class: "nodii-onboarding-consent-doc-loading" }, [
|
|
840
|
+
"Loading document\u2026"
|
|
841
|
+
])
|
|
842
|
+
);
|
|
843
|
+
const importViewer = opts.importViewer ?? (() => import('./render-DC7P2BGQ.js'));
|
|
844
|
+
const loadViewer = () => {
|
|
845
|
+
void (async () => {
|
|
846
|
+
try {
|
|
847
|
+
const { renderConsentDocument } = await importViewer();
|
|
848
|
+
await renderConsentDocument(container, doc.doc, control, {
|
|
849
|
+
consentKey: doc.key,
|
|
850
|
+
fetch: opts.fetch,
|
|
851
|
+
timeoutMs: opts.timeoutMs
|
|
852
|
+
});
|
|
853
|
+
} catch {
|
|
854
|
+
while (container.firstChild) {
|
|
855
|
+
container.removeChild(container.firstChild);
|
|
856
|
+
}
|
|
857
|
+
const wrap = el2("div", {
|
|
858
|
+
class: "nodii-onboarding-consent-unrenderable",
|
|
859
|
+
role: "alert"
|
|
860
|
+
});
|
|
861
|
+
wrap.appendChild(
|
|
862
|
+
el2(
|
|
863
|
+
"p",
|
|
864
|
+
{
|
|
865
|
+
// DISTINCT id (NOT `${id}-error`): buildAcceptRow already owns
|
|
866
|
+
// `${id}-error`, and updateConsentStep's querySelector would match
|
|
867
|
+
// THIS paragraph first and blank it on the next state change,
|
|
868
|
+
// silently wiping the compliance message. A unique id keeps this
|
|
869
|
+
// load-failure error persistent (P1b).
|
|
870
|
+
class: "nodii-onboarding-field-error nodii-onboarding-consent-error",
|
|
871
|
+
id: `${id}-load-error`
|
|
872
|
+
},
|
|
873
|
+
[
|
|
874
|
+
`\u201C${doc.doc.title}\u201D could not be loaded, so it cannot be accepted yet. Please retry.`
|
|
875
|
+
]
|
|
876
|
+
)
|
|
877
|
+
);
|
|
878
|
+
const retry = el2(
|
|
879
|
+
"button",
|
|
880
|
+
{ type: "button", class: "nodii-onboarding-consent-retry" },
|
|
881
|
+
["Retry loading document"]
|
|
882
|
+
);
|
|
883
|
+
retry.addEventListener("click", loadViewer);
|
|
884
|
+
wrap.appendChild(retry);
|
|
885
|
+
wrap.appendChild(
|
|
886
|
+
el2(
|
|
887
|
+
"a",
|
|
888
|
+
{
|
|
889
|
+
class: "nodii-onboarding-consent-open",
|
|
890
|
+
href: doc.doc.downloadUrl,
|
|
891
|
+
target: "_blank",
|
|
892
|
+
rel: "noopener noreferrer"
|
|
893
|
+
},
|
|
894
|
+
[`Open ${doc.doc.title} in a new tab`]
|
|
895
|
+
)
|
|
896
|
+
);
|
|
897
|
+
container.appendChild(wrap);
|
|
898
|
+
}
|
|
899
|
+
})();
|
|
900
|
+
};
|
|
901
|
+
loadViewer();
|
|
902
|
+
return container;
|
|
903
|
+
}
|
|
904
|
+
function buildConsentCard(control, doc, stale, opts) {
|
|
905
|
+
const card = el2("div", {
|
|
906
|
+
class: `nodii-onboarding-consent-card${stale ? " is-stale" : ""}`,
|
|
907
|
+
"data-consent-key": doc.key
|
|
908
|
+
});
|
|
909
|
+
card.appendChild(
|
|
910
|
+
el2("h4", { class: "nodii-onboarding-consent-title" }, [doc.doc.title])
|
|
911
|
+
);
|
|
912
|
+
if (stale) {
|
|
913
|
+
card.appendChild(
|
|
914
|
+
el2(
|
|
915
|
+
"div",
|
|
916
|
+
{ class: "nodii-onboarding-consent-stale-tag", role: "status" },
|
|
917
|
+
["Updated \u2014 please review and re-accept."]
|
|
918
|
+
)
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
card.appendChild(buildGateAffordance(control, doc, opts));
|
|
922
|
+
const { row, errorEl } = buildAcceptRow(control, doc);
|
|
923
|
+
card.appendChild(row);
|
|
924
|
+
card.appendChild(errorEl);
|
|
925
|
+
return card;
|
|
926
|
+
}
|
|
927
|
+
function renderConsentStep(host, control, state, staleKeys = [], opts = {}) {
|
|
928
|
+
while (host.firstChild) host.removeChild(host.firstChild);
|
|
929
|
+
const staleSet = new Set(staleKeys);
|
|
930
|
+
host.appendChild(
|
|
931
|
+
el2("h3", { class: "nodii-onboarding-stage-title" }, [
|
|
932
|
+
"Review & accept documents"
|
|
933
|
+
])
|
|
934
|
+
);
|
|
935
|
+
host.appendChild(
|
|
936
|
+
el2("p", { class: "nodii-onboarding-consent-intro" }, [
|
|
937
|
+
"Please review each document below and accept to continue."
|
|
938
|
+
])
|
|
939
|
+
);
|
|
940
|
+
for (const doc of state.documents) {
|
|
941
|
+
host.appendChild(
|
|
942
|
+
buildConsentCard(control, doc, staleSet.has(doc.key), opts)
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
function updateConsentStep(host, state, consentErrors) {
|
|
947
|
+
const errorByKey = new Map(consentErrors.map((e) => [e.path, e.message]));
|
|
948
|
+
for (const doc of state.documents) {
|
|
949
|
+
const id = consentDocElementId(doc.key);
|
|
950
|
+
const checkbox = host.querySelector(
|
|
951
|
+
idSelector(id)
|
|
952
|
+
);
|
|
953
|
+
if (checkbox) {
|
|
954
|
+
checkbox.disabled = !doc.gateSatisfied;
|
|
955
|
+
checkbox.checked = doc.accepted;
|
|
956
|
+
}
|
|
957
|
+
const errorNode = host.querySelector(idSelector(`${id}-error`));
|
|
958
|
+
if (errorNode) errorNode.textContent = errorByKey.get(doc.key) ?? "";
|
|
959
|
+
const card = host.querySelector(
|
|
960
|
+
`[data-consent-key="${cssEscape(doc.key)}"]`
|
|
961
|
+
);
|
|
962
|
+
if (card) {
|
|
963
|
+
if (errorByKey.has(doc.key)) card.classList.add("has-error");
|
|
964
|
+
else card.classList.remove("has-error");
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
546
969
|
// src/widget/signup-form.ts
|
|
547
970
|
var STAGE_TITLES = {
|
|
548
971
|
lead: "About you",
|
|
549
972
|
prospect: "Your business",
|
|
550
973
|
activation: "Choose your services"
|
|
551
974
|
};
|
|
552
|
-
function
|
|
975
|
+
function el3(tag, attrs = {}, children = []) {
|
|
553
976
|
const element = document.createElement(tag);
|
|
554
977
|
for (const [key, value] of Object.entries(attrs)) {
|
|
555
978
|
element.setAttribute(key, value);
|
|
@@ -574,7 +997,7 @@ function buildFieldInput(field, currentValue, onChange) {
|
|
|
574
997
|
const id = `nodii-f-${field.key}`;
|
|
575
998
|
switch (field.type) {
|
|
576
999
|
case "textarea": {
|
|
577
|
-
const input =
|
|
1000
|
+
const input = el3("textarea", {
|
|
578
1001
|
id,
|
|
579
1002
|
class: "nodii-onboarding-textarea",
|
|
580
1003
|
rows: "4"
|
|
@@ -585,7 +1008,7 @@ function buildFieldInput(field, currentValue, onChange) {
|
|
|
585
1008
|
}
|
|
586
1009
|
case "checkbox":
|
|
587
1010
|
case "boolean": {
|
|
588
|
-
const input =
|
|
1011
|
+
const input = el3("input", {
|
|
589
1012
|
type: "checkbox",
|
|
590
1013
|
id
|
|
591
1014
|
});
|
|
@@ -595,14 +1018,14 @@ function buildFieldInput(field, currentValue, onChange) {
|
|
|
595
1018
|
}
|
|
596
1019
|
case "select":
|
|
597
1020
|
case "radio": {
|
|
598
|
-
const select =
|
|
1021
|
+
const select = el3("select", {
|
|
599
1022
|
id,
|
|
600
1023
|
class: "nodii-onboarding-input"
|
|
601
1024
|
});
|
|
602
|
-
select.appendChild(
|
|
1025
|
+
select.appendChild(el3("option", { value: "" }, ["\u2014 select \u2014"]));
|
|
603
1026
|
for (const opt of field.options ?? []) {
|
|
604
1027
|
const value = optionValue(opt);
|
|
605
|
-
const optionEl =
|
|
1028
|
+
const optionEl = el3("option", { value }, [optionLabel(opt)]);
|
|
606
1029
|
select.appendChild(optionEl);
|
|
607
1030
|
}
|
|
608
1031
|
if (typeof currentValue === "string") select.value = currentValue;
|
|
@@ -610,7 +1033,7 @@ function buildFieldInput(field, currentValue, onChange) {
|
|
|
610
1033
|
return select;
|
|
611
1034
|
}
|
|
612
1035
|
case "multiselect": {
|
|
613
|
-
const select =
|
|
1036
|
+
const select = el3("select", {
|
|
614
1037
|
id,
|
|
615
1038
|
class: "nodii-onboarding-input",
|
|
616
1039
|
multiple: "multiple"
|
|
@@ -618,7 +1041,7 @@ function buildFieldInput(field, currentValue, onChange) {
|
|
|
618
1041
|
const current = Array.isArray(currentValue) ? currentValue.map(String) : [];
|
|
619
1042
|
for (const opt of field.options ?? []) {
|
|
620
1043
|
const value = optionValue(opt);
|
|
621
|
-
const optionEl =
|
|
1044
|
+
const optionEl = el3("option", { value }, [
|
|
622
1045
|
optionLabel(opt)
|
|
623
1046
|
]);
|
|
624
1047
|
if (current.includes(value)) optionEl.selected = true;
|
|
@@ -635,7 +1058,7 @@ function buildFieldInput(field, currentValue, onChange) {
|
|
|
635
1058
|
}
|
|
636
1059
|
default: {
|
|
637
1060
|
const typeAttr = field.type === "email" || field.type === "tel" || field.type === "number" || field.type === "date" ? field.type : "text";
|
|
638
|
-
const input =
|
|
1061
|
+
const input = el3("input", {
|
|
639
1062
|
type: typeAttr,
|
|
640
1063
|
id,
|
|
641
1064
|
class: "nodii-onboarding-input"
|
|
@@ -649,7 +1072,7 @@ function buildFieldInput(field, currentValue, onChange) {
|
|
|
649
1072
|
}
|
|
650
1073
|
function fieldRow(field, input, idOverride) {
|
|
651
1074
|
const id = idOverride ?? `nodii-f-${field.key}`;
|
|
652
|
-
const labelEl =
|
|
1075
|
+
const labelEl = el3(
|
|
653
1076
|
"label",
|
|
654
1077
|
{
|
|
655
1078
|
for: id,
|
|
@@ -657,13 +1080,13 @@ function fieldRow(field, input, idOverride) {
|
|
|
657
1080
|
},
|
|
658
1081
|
[field.label]
|
|
659
1082
|
);
|
|
660
|
-
const errorEl =
|
|
1083
|
+
const errorEl = el3("div", {
|
|
661
1084
|
class: "nodii-onboarding-field-error",
|
|
662
1085
|
id: `${id}-error`,
|
|
663
1086
|
role: "alert",
|
|
664
1087
|
"aria-live": "polite"
|
|
665
1088
|
});
|
|
666
|
-
return
|
|
1089
|
+
return el3("div", { class: "nodii-onboarding-field" }, [
|
|
667
1090
|
labelEl,
|
|
668
1091
|
input,
|
|
669
1092
|
errorEl
|
|
@@ -671,8 +1094,8 @@ function fieldRow(field, input, idOverride) {
|
|
|
671
1094
|
}
|
|
672
1095
|
function mountSignupForm(host, options) {
|
|
673
1096
|
injectWidgetStyles();
|
|
674
|
-
const wrapper =
|
|
675
|
-
const loadingEl =
|
|
1097
|
+
const wrapper = el3("div", { class: "nodii-onboarding-widget" });
|
|
1098
|
+
const loadingEl = el3("div", { class: "nodii-onboarding-loading" }, [
|
|
676
1099
|
"Loading signup form\u2026"
|
|
677
1100
|
]);
|
|
678
1101
|
wrapper.appendChild(loadingEl);
|
|
@@ -686,27 +1109,28 @@ function mountSignupForm(host, options) {
|
|
|
686
1109
|
let lastFingerprint = null;
|
|
687
1110
|
let renderedStepIndex = -1;
|
|
688
1111
|
let renderedStale = false;
|
|
1112
|
+
let renderedConsentStale = false;
|
|
689
1113
|
let currentSpecVersion = null;
|
|
690
1114
|
let staleDiffSummary = "";
|
|
691
|
-
const stepArea =
|
|
692
|
-
const staleBanner =
|
|
1115
|
+
const stepArea = el3("div", {});
|
|
1116
|
+
const staleBanner = el3("div", {
|
|
693
1117
|
class: "nodii-onboarding-stale-banner",
|
|
694
1118
|
role: "alert",
|
|
695
1119
|
style: "display:none"
|
|
696
1120
|
});
|
|
697
|
-
const serverErrorEl =
|
|
1121
|
+
const serverErrorEl = el3("div", {
|
|
698
1122
|
class: "nodii-onboarding-server-error",
|
|
699
1123
|
role: "alert",
|
|
700
1124
|
"aria-live": "assertive",
|
|
701
1125
|
style: "display:none"
|
|
702
1126
|
});
|
|
703
|
-
const successEl =
|
|
1127
|
+
const successEl = el3("div", {
|
|
704
1128
|
class: "nodii-onboarding-success",
|
|
705
1129
|
role: "status",
|
|
706
1130
|
style: "display:none"
|
|
707
1131
|
});
|
|
708
|
-
const stepsIndicator =
|
|
709
|
-
const form =
|
|
1132
|
+
const stepsIndicator = el3("div", { class: "nodii-onboarding-steps" });
|
|
1133
|
+
const form = el3("form", { novalidate: "true", "aria-label": "Sign up" });
|
|
710
1134
|
function setFieldErrorsInPlace(container, errors, idPrefix) {
|
|
711
1135
|
for (const errEl of container.querySelectorAll(
|
|
712
1136
|
".nodii-onboarding-field-error"
|
|
@@ -726,7 +1150,7 @@ function mountSignupForm(host, options) {
|
|
|
726
1150
|
}
|
|
727
1151
|
function accountIdOf(path) {
|
|
728
1152
|
if (path === "email") return "nodii-account-email";
|
|
729
|
-
if (path === "
|
|
1153
|
+
if (path === "displayName") return "nodii-account-display-name";
|
|
730
1154
|
if (path === "tosAccepted") return "nodii-consent-tos";
|
|
731
1155
|
if (path === "privacyAccepted") return "nodii-consent-privacy";
|
|
732
1156
|
return `nodii-account-${path}`;
|
|
@@ -737,15 +1161,15 @@ function mountSignupForm(host, options) {
|
|
|
737
1161
|
}
|
|
738
1162
|
for (let i = 0; i < state.stepCount; i++) {
|
|
739
1163
|
stepsIndicator.appendChild(
|
|
740
|
-
|
|
1164
|
+
el3("div", {
|
|
741
1165
|
class: `nodii-onboarding-step-dot${i <= state.stepIndex ? " active" : ""}`
|
|
742
1166
|
})
|
|
743
1167
|
);
|
|
744
1168
|
}
|
|
745
1169
|
}
|
|
746
1170
|
function renderAccountSection(control, state) {
|
|
747
|
-
const section =
|
|
748
|
-
const emailInput =
|
|
1171
|
+
const section = el3("div", { class: "nodii-onboarding-account-section" });
|
|
1172
|
+
const emailInput = el3("input", {
|
|
749
1173
|
type: "email",
|
|
750
1174
|
id: "nodii-account-email",
|
|
751
1175
|
class: "nodii-onboarding-input",
|
|
@@ -768,32 +1192,54 @@ function mountSignupForm(host, options) {
|
|
|
768
1192
|
// label for + error id match the real input id
|
|
769
1193
|
)
|
|
770
1194
|
);
|
|
771
|
-
const
|
|
772
|
-
type: "
|
|
773
|
-
id: "nodii-account-
|
|
1195
|
+
const displayNameInput = el3("input", {
|
|
1196
|
+
type: "text",
|
|
1197
|
+
id: "nodii-account-display-name",
|
|
774
1198
|
class: "nodii-onboarding-input",
|
|
775
|
-
autocomplete: "
|
|
776
|
-
|
|
777
|
-
maxlength: "256"
|
|
1199
|
+
autocomplete: "name",
|
|
1200
|
+
maxlength: "200"
|
|
778
1201
|
});
|
|
779
|
-
|
|
780
|
-
|
|
1202
|
+
displayNameInput.value = state.credentials.displayName;
|
|
1203
|
+
displayNameInput.addEventListener(
|
|
781
1204
|
"input",
|
|
782
|
-
() => control.setCredentials({
|
|
1205
|
+
() => control.setCredentials({ displayName: displayNameInput.value })
|
|
783
1206
|
);
|
|
784
1207
|
section.appendChild(
|
|
785
1208
|
fieldRow(
|
|
786
1209
|
{
|
|
787
|
-
key: "account-
|
|
788
|
-
label: "
|
|
1210
|
+
key: "account-display-name",
|
|
1211
|
+
label: "Your name",
|
|
789
1212
|
required: true
|
|
790
1213
|
},
|
|
791
|
-
|
|
792
|
-
"nodii-account-
|
|
793
|
-
// label for + error id match the real input id
|
|
1214
|
+
displayNameInput,
|
|
1215
|
+
"nodii-account-display-name"
|
|
794
1216
|
)
|
|
795
1217
|
);
|
|
796
|
-
const
|
|
1218
|
+
for (const [key, label, autocomplete] of [
|
|
1219
|
+
["organisationName", "Organisation name", "organization"],
|
|
1220
|
+
["subdomain", "Workspace subdomain", "off"]
|
|
1221
|
+
]) {
|
|
1222
|
+
const input = el3("input", {
|
|
1223
|
+
type: "text",
|
|
1224
|
+
id: `nodii-account-${key}`,
|
|
1225
|
+
class: "nodii-onboarding-input",
|
|
1226
|
+
autocomplete,
|
|
1227
|
+
maxlength: key === "subdomain" ? "63" : "200"
|
|
1228
|
+
});
|
|
1229
|
+
input.value = state.credentials[key];
|
|
1230
|
+
input.addEventListener(
|
|
1231
|
+
"input",
|
|
1232
|
+
() => control.setCredentials({ [key]: input.value })
|
|
1233
|
+
);
|
|
1234
|
+
section.appendChild(
|
|
1235
|
+
fieldRow(
|
|
1236
|
+
{ key: `account-${key}`, label, required: true },
|
|
1237
|
+
input,
|
|
1238
|
+
`nodii-account-${key}`
|
|
1239
|
+
)
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
const tosCheckbox = el3("input", {
|
|
797
1243
|
type: "checkbox",
|
|
798
1244
|
id: "nodii-consent-tos"
|
|
799
1245
|
});
|
|
@@ -805,7 +1251,7 @@ function mountSignupForm(host, options) {
|
|
|
805
1251
|
const tosLabelChildren = ["I accept the "];
|
|
806
1252
|
if (options.tosUrl) {
|
|
807
1253
|
tosLabelChildren.push(
|
|
808
|
-
|
|
1254
|
+
el3(
|
|
809
1255
|
"a",
|
|
810
1256
|
{
|
|
811
1257
|
href: options.tosUrl,
|
|
@@ -819,19 +1265,19 @@ function mountSignupForm(host, options) {
|
|
|
819
1265
|
tosLabelChildren.push("Terms of Service");
|
|
820
1266
|
}
|
|
821
1267
|
section.appendChild(
|
|
822
|
-
|
|
1268
|
+
el3("div", { class: "nodii-onboarding-consent-row" }, [
|
|
823
1269
|
tosCheckbox,
|
|
824
|
-
|
|
1270
|
+
el3("label", { for: "nodii-consent-tos" }, tosLabelChildren)
|
|
825
1271
|
])
|
|
826
1272
|
);
|
|
827
1273
|
section.appendChild(
|
|
828
|
-
|
|
1274
|
+
el3("div", {
|
|
829
1275
|
class: "nodii-onboarding-field-error",
|
|
830
1276
|
id: "nodii-consent-tos-error",
|
|
831
1277
|
role: "alert"
|
|
832
1278
|
})
|
|
833
1279
|
);
|
|
834
|
-
const privacyCheckbox =
|
|
1280
|
+
const privacyCheckbox = el3("input", {
|
|
835
1281
|
type: "checkbox",
|
|
836
1282
|
id: "nodii-consent-privacy"
|
|
837
1283
|
});
|
|
@@ -843,7 +1289,7 @@ function mountSignupForm(host, options) {
|
|
|
843
1289
|
const privacyLabelChildren = ["I accept the "];
|
|
844
1290
|
if (options.privacyUrl) {
|
|
845
1291
|
privacyLabelChildren.push(
|
|
846
|
-
|
|
1292
|
+
el3(
|
|
847
1293
|
"a",
|
|
848
1294
|
{
|
|
849
1295
|
href: options.privacyUrl,
|
|
@@ -857,13 +1303,13 @@ function mountSignupForm(host, options) {
|
|
|
857
1303
|
privacyLabelChildren.push("Privacy Policy");
|
|
858
1304
|
}
|
|
859
1305
|
section.appendChild(
|
|
860
|
-
|
|
1306
|
+
el3("div", { class: "nodii-onboarding-consent-row" }, [
|
|
861
1307
|
privacyCheckbox,
|
|
862
|
-
|
|
1308
|
+
el3("label", { for: "nodii-consent-privacy" }, privacyLabelChildren)
|
|
863
1309
|
])
|
|
864
1310
|
);
|
|
865
1311
|
section.appendChild(
|
|
866
|
-
|
|
1312
|
+
el3("div", {
|
|
867
1313
|
class: "nodii-onboarding-field-error",
|
|
868
1314
|
id: "nodii-consent-privacy-error",
|
|
869
1315
|
role: "alert"
|
|
@@ -876,7 +1322,7 @@ function mountSignupForm(host, options) {
|
|
|
876
1322
|
const step = state.currentStep;
|
|
877
1323
|
if (step) {
|
|
878
1324
|
stepArea.appendChild(
|
|
879
|
-
|
|
1325
|
+
el3("h3", { class: "nodii-onboarding-stage-title" }, [
|
|
880
1326
|
STAGE_TITLES[step.stage] ?? step.stage
|
|
881
1327
|
])
|
|
882
1328
|
);
|
|
@@ -891,11 +1337,24 @@ function mountSignupForm(host, options) {
|
|
|
891
1337
|
}
|
|
892
1338
|
const isLast = control.isLastStep();
|
|
893
1339
|
if (isLast) {
|
|
1340
|
+
if (!control.consent.isEmpty()) {
|
|
1341
|
+
const consentStage = el3("div", {
|
|
1342
|
+
class: "nodii-onboarding-consent-stage"
|
|
1343
|
+
});
|
|
1344
|
+
renderConsentStep(
|
|
1345
|
+
consentStage,
|
|
1346
|
+
control.consent,
|
|
1347
|
+
state.consentState,
|
|
1348
|
+
state.staleConsentKeys,
|
|
1349
|
+
{ fetch: options.fetch, timeoutMs: options.timeoutMs }
|
|
1350
|
+
);
|
|
1351
|
+
stepArea.appendChild(consentStage);
|
|
1352
|
+
}
|
|
894
1353
|
stepArea.appendChild(renderAccountSection(control, state));
|
|
895
1354
|
}
|
|
896
|
-
const nav =
|
|
1355
|
+
const nav = el3("div", { class: "nodii-onboarding-nav" });
|
|
897
1356
|
if (!control.isFirstStep()) {
|
|
898
|
-
const backBtn =
|
|
1357
|
+
const backBtn = el3(
|
|
899
1358
|
"button",
|
|
900
1359
|
{ type: "button", class: "nodii-onboarding-back-btn" },
|
|
901
1360
|
["Back"]
|
|
@@ -903,7 +1362,7 @@ function mountSignupForm(host, options) {
|
|
|
903
1362
|
backBtn.addEventListener("click", () => control.back());
|
|
904
1363
|
nav.appendChild(backBtn);
|
|
905
1364
|
}
|
|
906
|
-
const forwardBtn =
|
|
1365
|
+
const forwardBtn = el3(
|
|
907
1366
|
"button",
|
|
908
1367
|
{
|
|
909
1368
|
type: isLast ? "submit" : "button",
|
|
@@ -940,12 +1399,27 @@ function mountSignupForm(host, options) {
|
|
|
940
1399
|
} else {
|
|
941
1400
|
staleDiffSummary = "";
|
|
942
1401
|
}
|
|
943
|
-
siteKey = options.siteKey ?? fresh.botChallenge
|
|
1402
|
+
siteKey = options.siteKey ?? fresh.botChallenge?.siteKey ?? "";
|
|
944
1403
|
currentSpecVersion = fresh.specVersion;
|
|
945
1404
|
control.applyNewSpec(fresh);
|
|
946
1405
|
idempotencyKey = null;
|
|
947
1406
|
lastFingerprint = null;
|
|
948
1407
|
}
|
|
1408
|
+
async function handleConsentStale(control) {
|
|
1409
|
+
const fresh = await fetchOnboardingSpec({
|
|
1410
|
+
baseUrl: options.baseUrl,
|
|
1411
|
+
onboarder: options.onboarder,
|
|
1412
|
+
fetch: options.fetch,
|
|
1413
|
+
timeoutMs: options.timeoutMs,
|
|
1414
|
+
cache
|
|
1415
|
+
});
|
|
1416
|
+
if (unmounted) return;
|
|
1417
|
+
siteKey = options.siteKey ?? fresh.botChallenge?.siteKey ?? "";
|
|
1418
|
+
currentSpecVersion = fresh.specVersion;
|
|
1419
|
+
control.applyNewConsentPack(fresh);
|
|
1420
|
+
idempotencyKey = null;
|
|
1421
|
+
lastFingerprint = null;
|
|
1422
|
+
}
|
|
949
1423
|
async function handleSubmit(control) {
|
|
950
1424
|
if (!control.validateAll()) return;
|
|
951
1425
|
control.acknowledgeSpecStale();
|
|
@@ -957,14 +1431,16 @@ function mountSignupForm(host, options) {
|
|
|
957
1431
|
lastFingerprint = fingerprint;
|
|
958
1432
|
}
|
|
959
1433
|
let firstToken;
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1434
|
+
if (siteKey) {
|
|
1435
|
+
try {
|
|
1436
|
+
firstToken = await executeRecaptcha(siteKey, "signup_submit", {
|
|
1437
|
+
timeoutMs: options.timeoutMs
|
|
1438
|
+
});
|
|
1439
|
+
} catch (recaptchaErr) {
|
|
1440
|
+
control.setError("Could not load bot protection. Please try again.");
|
|
1441
|
+
options.onError?.(recaptchaErr);
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
968
1444
|
}
|
|
969
1445
|
let usedFirst = false;
|
|
970
1446
|
const tokenProvider = async () => {
|
|
@@ -979,7 +1455,7 @@ function mountSignupForm(host, options) {
|
|
|
979
1455
|
try {
|
|
980
1456
|
const result = await submitSignup(body, {
|
|
981
1457
|
baseUrl: options.baseUrl,
|
|
982
|
-
botToken: tokenProvider,
|
|
1458
|
+
...siteKey ? { botToken: tokenProvider } : {},
|
|
983
1459
|
idempotencyKey,
|
|
984
1460
|
fetch: options.fetch,
|
|
985
1461
|
timeoutMs: options.timeoutMs
|
|
@@ -1000,6 +1476,21 @@ function mountSignupForm(host, options) {
|
|
|
1000
1476
|
return;
|
|
1001
1477
|
}
|
|
1002
1478
|
options.onError?.(err);
|
|
1479
|
+
} else if (err instanceof OnboardingConsentStaleError) {
|
|
1480
|
+
try {
|
|
1481
|
+
await handleConsentStale(control);
|
|
1482
|
+
} catch (refetchErr) {
|
|
1483
|
+
control.setError(
|
|
1484
|
+
"The legal documents changed and could not be reloaded. Please refresh the page."
|
|
1485
|
+
);
|
|
1486
|
+
options.onError?.(refetchErr);
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
options.onError?.(err);
|
|
1490
|
+
} else if (err instanceof OnboardingConsentIncompleteError) {
|
|
1491
|
+
control.validateAll();
|
|
1492
|
+
control.setError("Please accept all required documents to continue.");
|
|
1493
|
+
options.onError?.(err);
|
|
1003
1494
|
} else if (err instanceof OnboardingBotChallengeError) {
|
|
1004
1495
|
control.setError(
|
|
1005
1496
|
"Bot protection check failed. Please refresh the page and try again."
|
|
@@ -1017,14 +1508,15 @@ function mountSignupForm(host, options) {
|
|
|
1017
1508
|
}
|
|
1018
1509
|
}
|
|
1019
1510
|
function onStateChange(control, state) {
|
|
1020
|
-
if (state.stepIndex !== renderedStepIndex || state.specStale !== renderedStale) {
|
|
1511
|
+
if (state.stepIndex !== renderedStepIndex || state.specStale !== renderedStale || state.consentStale !== renderedConsentStale) {
|
|
1021
1512
|
renderedStepIndex = state.stepIndex;
|
|
1022
1513
|
renderedStale = state.specStale;
|
|
1514
|
+
renderedConsentStale = state.consentStale;
|
|
1023
1515
|
renderStep(control, state);
|
|
1024
1516
|
renderStepsIndicator(state);
|
|
1025
1517
|
}
|
|
1026
|
-
staleBanner.style.display = state.specStale ? "" : "none";
|
|
1027
|
-
staleBanner.textContent = state.specStale ? `This form was just updated by the provider${staleDiffSummary}. Please review your answers below and submit again.` : "";
|
|
1518
|
+
staleBanner.style.display = state.specStale || state.consentStale ? "" : "none";
|
|
1519
|
+
staleBanner.textContent = state.specStale ? `This form was just updated by the provider${staleDiffSummary}. Please review your answers below and submit again.` : state.consentStale ? "One or more legal documents were updated. Please review and re-accept the updated documents below, then submit again." : "";
|
|
1028
1520
|
setFieldErrorsInPlace(
|
|
1029
1521
|
stepArea,
|
|
1030
1522
|
state.errors,
|
|
@@ -1037,6 +1529,12 @@ function mountSignupForm(host, options) {
|
|
|
1037
1529
|
const inputNode = stepArea.querySelector(idSelector(id));
|
|
1038
1530
|
if (inputNode) inputNode.classList.add("has-error");
|
|
1039
1531
|
}
|
|
1532
|
+
const consentStage = stepArea.querySelector(
|
|
1533
|
+
".nodii-onboarding-consent-stage"
|
|
1534
|
+
);
|
|
1535
|
+
if (consentStage) {
|
|
1536
|
+
updateConsentStep(consentStage, state.consentState, state.consentErrors);
|
|
1537
|
+
}
|
|
1040
1538
|
if (state.serverError) {
|
|
1041
1539
|
serverErrorEl.textContent = state.serverError;
|
|
1042
1540
|
serverErrorEl.style.display = "";
|
|
@@ -1080,7 +1578,7 @@ function mountSignupForm(host, options) {
|
|
|
1080
1578
|
return;
|
|
1081
1579
|
}
|
|
1082
1580
|
if (unmounted) return;
|
|
1083
|
-
siteKey = options.siteKey ?? spec.botChallenge
|
|
1581
|
+
siteKey = options.siteKey ?? spec.botChallenge?.siteKey ?? "";
|
|
1084
1582
|
currentSpecVersion = spec.specVersion;
|
|
1085
1583
|
const control = createSignupStepper(spec);
|
|
1086
1584
|
stepper = control;
|
|
@@ -1144,12 +1642,12 @@ var OnboardingWidget = {
|
|
|
1144
1642
|
* });
|
|
1145
1643
|
* ```
|
|
1146
1644
|
*/
|
|
1147
|
-
mount(
|
|
1645
|
+
mount(el4, options) {
|
|
1148
1646
|
if (options.form === "contact") {
|
|
1149
|
-
return mountContactForm(
|
|
1647
|
+
return mountContactForm(el4, options);
|
|
1150
1648
|
}
|
|
1151
1649
|
if (options.form === "signup") {
|
|
1152
|
-
return mountSignupForm(
|
|
1650
|
+
return mountSignupForm(el4, options);
|
|
1153
1651
|
}
|
|
1154
1652
|
throw new Error(
|
|
1155
1653
|
`OnboardingWidget.mount: unknown form "${options.form}". Supported: "contact", "signup".`
|
|
@@ -1157,6 +1655,6 @@ var OnboardingWidget = {
|
|
|
1157
1655
|
}
|
|
1158
1656
|
};
|
|
1159
1657
|
|
|
1160
|
-
export { DEFAULT_WIDGET_CSS, OnboardingWidget, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm };
|
|
1161
|
-
//# sourceMappingURL=chunk-
|
|
1162
|
-
//# sourceMappingURL=chunk-
|
|
1658
|
+
export { DEFAULT_WIDGET_CSS, OnboardingWidget, consentDocElementId, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm, renderConsentStep, updateConsentStep };
|
|
1659
|
+
//# sourceMappingURL=chunk-IPAKPLIF.js.map
|
|
1660
|
+
//# sourceMappingURL=chunk-IPAKPLIF.js.map
|