bison-web-components 3.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,555 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Bison Operator Onboarding - Demo</title>
7
+ <link
8
+ rel="stylesheet"
9
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
10
+ />
11
+ <style>
12
+ * {
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ margin: 0;
18
+ min-height: 100vh;
19
+ font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
20
+ background: linear-gradient(180deg, #eff4f1 0%, #f6f8fa 55%, #ffffff 100%);
21
+ color: #0f2a39;
22
+ }
23
+
24
+ .page {
25
+ width: min(1100px, 100%);
26
+ margin: 0 auto;
27
+ padding: 28px 16px 32px;
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: 14px;
31
+ }
32
+
33
+ .header {
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: 8px;
37
+ }
38
+
39
+ .title {
40
+ margin: 0;
41
+ font-size: 1.25rem;
42
+ line-height: 1.2;
43
+ font-weight: 700;
44
+ }
45
+
46
+ .subtitle {
47
+ margin: 0;
48
+ color: #5f6e78;
49
+ font-size: 0.9rem;
50
+ }
51
+
52
+ .toolbar {
53
+ display: flex;
54
+ flex-wrap: wrap;
55
+ gap: 8px;
56
+ }
57
+
58
+ .config-panel {
59
+ display: grid;
60
+ grid-template-columns: 1fr 220px auto;
61
+ gap: 8px;
62
+ align-items: end;
63
+ }
64
+
65
+ .field {
66
+ display: grid;
67
+ gap: 4px;
68
+ }
69
+
70
+ .field label {
71
+ font-size: 0.72rem;
72
+ font-weight: 600;
73
+ color: #3f4f58;
74
+ }
75
+
76
+ .field input {
77
+ border: 1px solid #d8e3dc;
78
+ border-radius: 8px;
79
+ padding: 8px 10px;
80
+ font-size: 0.82rem;
81
+ color: #0f2a39;
82
+ background: #fff;
83
+ }
84
+
85
+ .field input:focus {
86
+ outline: 0;
87
+ border-color: #4c7b63;
88
+ box-shadow: 0 0 0 3px rgba(76, 123, 99, 0.12);
89
+ }
90
+
91
+ @media (max-width: 760px) {
92
+ .config-panel {
93
+ grid-template-columns: 1fr;
94
+ }
95
+ }
96
+
97
+ button {
98
+ border: 1px solid #d8e3dc;
99
+ background: #ffffff;
100
+ color: #0f2a39;
101
+ border-radius: 8px;
102
+ padding: 8px 12px;
103
+ font-size: 0.8rem;
104
+ font-weight: 600;
105
+ cursor: pointer;
106
+ }
107
+
108
+ button:hover {
109
+ border-color: #4c7b63;
110
+ }
111
+
112
+ .primary {
113
+ background: #4c7b63;
114
+ border-color: #4c7b63;
115
+ color: #fff;
116
+ }
117
+
118
+ .component-shell {
119
+ width: 100%;
120
+ background: #fff;
121
+ border: 1px solid #e8e8e8;
122
+ border-radius: 12px;
123
+ padding: 18px;
124
+ overflow: auto;
125
+ }
126
+
127
+ .log {
128
+ margin: 0;
129
+ background: #111827;
130
+ color: #d1d5db;
131
+ border-radius: 10px;
132
+ padding: 12px 14px;
133
+ font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
134
+ max-height: 150px;
135
+ overflow: auto;
136
+ white-space: pre-wrap;
137
+ }
138
+
139
+ .note {
140
+ margin: 0;
141
+ color: #5f6e78;
142
+ font-size: 0.8rem;
143
+ }
144
+
145
+ .dev-prefill-panel {
146
+ position: fixed;
147
+ right: 16px;
148
+ bottom: 16px;
149
+ z-index: 1000;
150
+ width: min(260px, calc(100vw - 24px));
151
+ border: 1px solid #d8e3dc;
152
+ border-radius: 12px;
153
+ background: rgba(15, 42, 57, 0.95);
154
+ color: #fff;
155
+ box-shadow: 0 12px 30px rgba(15, 42, 57, 0.25);
156
+ padding: 12px;
157
+ }
158
+
159
+ .dev-prefill-label {
160
+ margin: 0 0 8px;
161
+ font-size: 0.7rem;
162
+ font-weight: 700;
163
+ letter-spacing: 0.06em;
164
+ text-transform: uppercase;
165
+ color: #b9d0c1;
166
+ }
167
+
168
+ .dev-prefill-buttons {
169
+ display: grid;
170
+ gap: 6px;
171
+ }
172
+
173
+ .dev-prefill-btn {
174
+ width: 100%;
175
+ border-radius: 7px;
176
+ border: 1px solid #365244;
177
+ background: #1f3a2f;
178
+ color: #f4fff8;
179
+ text-align: left;
180
+ font-size: 0.76rem;
181
+ font-weight: 600;
182
+ padding: 7px 9px;
183
+ }
184
+
185
+ .dev-prefill-btn:hover {
186
+ border-color: #7ab191;
187
+ }
188
+ </style>
189
+ </head>
190
+ <body>
191
+ <main class="page">
192
+ <header class="header">
193
+ <h1 class="title">Bison Operator Onboarding Demo</h1>
194
+ <p class="subtitle">
195
+ Interactive sandbox for <code>bison-operator-onboarding.js</code>.
196
+ </p>
197
+ </header>
198
+
199
+ <section class="toolbar">
200
+ <button id="seed-signup" type="button" class="primary">Seed Signup Data</button>
201
+ <button id="clear-onboarding" type="button">Clear Onboarding Data</button>
202
+ <button id="show-welcome" type="button">Show Welcome Modal Again</button>
203
+ <button id="reload-component" type="button">Reload Component</button>
204
+ </section>
205
+
206
+ <section class="config-panel">
207
+ <div class="field">
208
+ <label for="embeddable-key-input">Embeddable Key</label>
209
+ <input id="embeddable-key-input" type="text" autocomplete="off" />
210
+ </div>
211
+ <div class="field">
212
+ <label for="op-org-id-input">Operator Org ID (opOrgId)</label>
213
+ <input id="op-org-id-input" type="text" autocomplete="off" />
214
+ </div>
215
+ <button id="apply-config" type="button" class="primary">Apply API Config</button>
216
+ </section>
217
+
218
+ <p class="note">
219
+ Serve this folder over HTTP (example: <code>npx serve .</code>) then open this file in the browser.
220
+ </p>
221
+
222
+ <section class="component-shell" id="component-shell">
223
+ <bison-operator-onboarding></bison-operator-onboarding>
224
+ </section>
225
+
226
+ <pre class="log" id="log"></pre>
227
+ </main>
228
+
229
+ <aside id="dev-prefill-panel" class="dev-prefill-panel" hidden>
230
+ <p class="dev-prefill-label">Dev only - Verification prefill</p>
231
+ <div class="dev-prefill-buttons">
232
+ <button type="button" class="dev-prefill-btn" data-prefill-section="business">
233
+ Fill Business Profile
234
+ </button>
235
+ <button type="button" class="dev-prefill-btn" data-prefill-section="officer">
236
+ Fill Control Officer
237
+ </button>
238
+ <button type="button" class="dev-prefill-btn" data-prefill-section="owners">
239
+ Fill Beneficial Owners
240
+ </button>
241
+ <button type="button" class="dev-prefill-btn" data-prefill-section="volume">
242
+ Fill Processing Volume
243
+ </button>
244
+ <button type="button" class="dev-prefill-btn" data-prefill-section="bank">
245
+ Fill Bank Account
246
+ </button>
247
+ </div>
248
+ </aside>
249
+
250
+ <script type="module" src="./bison-operator-onboarding.js"></script>
251
+ <script type="module">
252
+ const STORAGE_KEY = "bison_operator_onboarding";
253
+ const WELCOME_SEEN_PREFIX = "bison_operator_welcome_seen_";
254
+ const WELCOME_METHODS_PREFIX = "bison_operator_welcome_methods_";
255
+ const DEMO_CONFIG_KEY = "bison_operator_onboarding_demo_config";
256
+ const shell = document.getElementById("component-shell");
257
+ const logEl = document.getElementById("log");
258
+ const devPrefillPanel = document.getElementById("dev-prefill-panel");
259
+ const embeddableKeyInput = document.getElementById("embeddable-key-input");
260
+ const opOrgIdInput = document.getElementById("op-org-id-input");
261
+ const applyConfigBtn = document.getElementById("apply-config");
262
+ let logoObserver = null;
263
+ const DEV_PREFILLS = {
264
+ business: {
265
+ legalName: "High Plains Resources, LLC",
266
+ dba: "High Plains Midstream",
267
+ businessType: "llc",
268
+ ein: "12-3456789",
269
+ address: "450 Energy Park Blvd",
270
+ city: "Houston",
271
+ state: "TX",
272
+ zip: "77002",
273
+ phone: "(713) 555-0188",
274
+ website: "https://highplains.example",
275
+ industry: "oil_gas_extraction",
276
+ },
277
+ officer: {
278
+ firstName: "Jordan",
279
+ lastName: "Maddox",
280
+ email: "jordan.maddox@highplains.example",
281
+ phone: "(346) 555-0102",
282
+ address: "1200 Main St",
283
+ city: "Houston",
284
+ state: "TX",
285
+ zip: "77010",
286
+ dob: "09/15/1985",
287
+ ssn: "321-54-6789",
288
+ jobTitle: "Chief Financial Officer",
289
+ },
290
+ owners: {
291
+ owners: [
292
+ {
293
+ id: "demo-owner-1",
294
+ firstName: "Casey",
295
+ lastName: "Delgado",
296
+ email: "casey.delgado@highplains.example",
297
+ phone: "(713) 555-0154",
298
+ address: "780 River Oaks Dr",
299
+ city: "Houston",
300
+ state: "TX",
301
+ zip: "77019",
302
+ dob: "02/21/1981",
303
+ ssn: "456-78-9012",
304
+ jobTitle: "Managing Member",
305
+ ownershipPercent: 62,
306
+ },
307
+ ],
308
+ noOwnersAbove25: false,
309
+ },
310
+ volume: {
311
+ monthlyTransactionCount: "140",
312
+ monthlyDollarVolume: "$245000",
313
+ avgTransactionSize: "$1750",
314
+ },
315
+ bank: {
316
+ routingNumber: "111000025",
317
+ accountNumber: "000987654321",
318
+ accountType: "checking",
319
+ connectedViaPlaid: false,
320
+ },
321
+ };
322
+
323
+ function log(message) {
324
+ const stamp = new Date().toLocaleTimeString();
325
+ logEl.textContent += `[${stamp}] ${message}\n`;
326
+ logEl.scrollTop = logEl.scrollHeight;
327
+ }
328
+
329
+ function clearKeysByPrefix(prefix) {
330
+ const keys = [];
331
+ for (let i = 0; i < localStorage.length; i += 1) {
332
+ const key = localStorage.key(i);
333
+ if (key && key.startsWith(prefix)) keys.push(key);
334
+ }
335
+ keys.forEach((key) => localStorage.removeItem(key));
336
+ return keys.length;
337
+ }
338
+
339
+ function remountComponent() {
340
+ shell.innerHTML = "<bison-operator-onboarding></bison-operator-onboarding>";
341
+ applyConfigToHost(getOnboardingHost(), getDemoConfig());
342
+ log("Component remounted");
343
+ patchWelcomeLogoSource();
344
+ }
345
+
346
+ function getOnboardingHost() {
347
+ return shell.querySelector("bison-operator-onboarding");
348
+ }
349
+
350
+ function getDemoConfig() {
351
+ return {
352
+ embeddableKey: String(embeddableKeyInput?.value || "").trim(),
353
+ opOrgId: String(opOrgIdInput?.value || "").trim(),
354
+ };
355
+ }
356
+
357
+ function saveDemoConfig(config) {
358
+ try {
359
+ localStorage.setItem(DEMO_CONFIG_KEY, JSON.stringify(config));
360
+ } catch (_err) {
361
+ // ignore storage errors in demo
362
+ }
363
+ }
364
+
365
+ function loadDemoConfig() {
366
+ try {
367
+ const raw = localStorage.getItem(DEMO_CONFIG_KEY);
368
+ if (!raw) return;
369
+ const parsed = JSON.parse(raw);
370
+ if (!parsed || typeof parsed !== "object") return;
371
+ if (typeof parsed.embeddableKey === "string") embeddableKeyInput.value = parsed.embeddableKey;
372
+ if (typeof parsed.opOrgId === "string") opOrgIdInput.value = parsed.opOrgId;
373
+ } catch (_err) {
374
+ // ignore storage errors in demo
375
+ }
376
+ }
377
+
378
+ function applyConfigToHost(host, config) {
379
+ if (!host || !config) return;
380
+
381
+ if (config.embeddableKey) host.setAttribute("x-embeddable-key", config.embeddableKey);
382
+ else host.removeAttribute("x-embeddable-key");
383
+
384
+ if (config.opOrgId) host.setAttribute("op-org-id", config.opOrgId);
385
+ else host.removeAttribute("op-org-id");
386
+ }
387
+
388
+ function isVerificationSectionOpen(host, sectionKey) {
389
+ return Boolean(
390
+ host &&
391
+ host.state &&
392
+ host.state.activeTab === "verification" &&
393
+ host.state.openSection === sectionKey,
394
+ );
395
+ }
396
+
397
+ function resetFormMeta(host, sectionKey) {
398
+ if (!host?.state?.ui) return;
399
+ if (sectionKey === "owners") {
400
+ host.state.ui.ownerEditor = {
401
+ mode: null,
402
+ editingId: "",
403
+ form: {
404
+ id: "",
405
+ firstName: "",
406
+ lastName: "",
407
+ email: "",
408
+ phone: "",
409
+ address: "",
410
+ city: "",
411
+ state: "",
412
+ zip: "",
413
+ dob: "",
414
+ ssn: "",
415
+ jobTitle: "",
416
+ ownershipPercent: 0,
417
+ },
418
+ errors: {},
419
+ touched: {},
420
+ submitAttempted: false,
421
+ isSaving: false,
422
+ };
423
+ return;
424
+ }
425
+ const meta = host.state.ui[sectionKey];
426
+ if (!meta) return;
427
+ meta.errors = {};
428
+ meta.touched = {};
429
+ meta.submitAttempted = false;
430
+ meta.isSaving = false;
431
+ }
432
+
433
+ function prefillSection(sectionKey) {
434
+ const host = getOnboardingHost();
435
+ if (!host?.state) {
436
+ log("Dev prefill skipped: onboarding component is not ready yet.");
437
+ return;
438
+ }
439
+
440
+ if (!isVerificationSectionOpen(host, sectionKey)) {
441
+ log(
442
+ `Dev prefill skipped: "${sectionKey}" must be open in the Verification tab (not collapsed).`,
443
+ );
444
+ return;
445
+ }
446
+
447
+ const fixture = DEV_PREFILLS[sectionKey];
448
+ if (!fixture) {
449
+ log(`Dev prefill skipped: no fixture found for "${sectionKey}".`);
450
+ return;
451
+ }
452
+
453
+ if (sectionKey === "owners") {
454
+ host.state.data.owners = {
455
+ owners: fixture.owners.map((owner) => ({ ...owner })),
456
+ noOwnersAbove25: fixture.noOwnersAbove25,
457
+ };
458
+ } else if (host.state.data[sectionKey]) {
459
+ host.state.data[sectionKey] = { ...host.state.data[sectionKey], ...fixture };
460
+ } else {
461
+ log(`Dev prefill skipped: section "${sectionKey}" is unsupported.`);
462
+ return;
463
+ }
464
+
465
+ resetFormMeta(host, sectionKey);
466
+ host.persist?.();
467
+ host.render?.();
468
+ log(`Dev prefill applied for "${sectionKey}" (open section only).`);
469
+ }
470
+
471
+ const shouldShowDevPrefill =
472
+ location.hostname === "" ||
473
+ location.hostname === "localhost" ||
474
+ location.hostname === "127.0.0.1" ||
475
+ location.hostname === "[::1]" ||
476
+ new URLSearchParams(location.search).get("devtools") === "1";
477
+
478
+ if (shouldShowDevPrefill) {
479
+ devPrefillPanel.hidden = false;
480
+ devPrefillPanel.addEventListener("click", (event) => {
481
+ const target = event.target;
482
+ if (!(target instanceof Element)) return;
483
+ const trigger = target.closest("[data-prefill-section]");
484
+ if (!(trigger instanceof HTMLButtonElement)) return;
485
+ const sectionKey = trigger.getAttribute("data-prefill-section");
486
+ if (!sectionKey) return;
487
+ prefillSection(sectionKey);
488
+ });
489
+ log("Dev prefill panel enabled.");
490
+ }
491
+
492
+ function patchWelcomeLogoSource() {
493
+ const host = shell.querySelector("bison-operator-onboarding");
494
+ if (!host || !host.shadowRoot) return;
495
+ if (logoObserver) logoObserver.disconnect();
496
+ const applyPatch = () => {
497
+ const logo = host.shadowRoot.querySelector('img.welcome-logo[src="/bison-logo.svg"]');
498
+ if (logo) logo.setAttribute("src", "./bison_logo_green.svg");
499
+ };
500
+ applyPatch();
501
+ logoObserver = new MutationObserver(applyPatch);
502
+ logoObserver.observe(host.shadowRoot, { childList: true, subtree: true });
503
+ }
504
+
505
+ document.getElementById("seed-signup").addEventListener("click", () => {
506
+ localStorage.setItem(
507
+ "jibpay_signup_data",
508
+ JSON.stringify({
509
+ fullName: "Avery Kimball",
510
+ email: "avery.kimball@outsidehire.demo",
511
+ }),
512
+ );
513
+ log("Saved jibpay_signup_data with demo name/email");
514
+ remountComponent();
515
+ });
516
+
517
+ document.getElementById("clear-onboarding").addEventListener("click", () => {
518
+ localStorage.removeItem(STORAGE_KEY);
519
+ const seenCount = clearKeysByPrefix(WELCOME_SEEN_PREFIX);
520
+ const methodsCount = clearKeysByPrefix(WELCOME_METHODS_PREFIX);
521
+ log(
522
+ `Cleared onboarding state (${STORAGE_KEY}) and ${seenCount + methodsCount} welcome key(s)`,
523
+ );
524
+ remountComponent();
525
+ });
526
+
527
+ document.getElementById("show-welcome").addEventListener("click", () => {
528
+ const seenCount = clearKeysByPrefix(WELCOME_SEEN_PREFIX);
529
+ log(`Cleared ${seenCount} welcome seen key(s)`);
530
+ remountComponent();
531
+ });
532
+
533
+ document.getElementById("reload-component").addEventListener("click", () => {
534
+ remountComponent();
535
+ });
536
+
537
+ applyConfigBtn.addEventListener("click", () => {
538
+ const config = getDemoConfig();
539
+ saveDemoConfig(config);
540
+ applyConfigToHost(getOnboardingHost(), config);
541
+ log(
542
+ `Applied API config (key: ${config.embeddableKey ? "set" : "missing"}, opOrgId: ${config.opOrgId || "missing"})`,
543
+ );
544
+ });
545
+
546
+ loadDemoConfig();
547
+ applyConfigToHost(getOnboardingHost(), getDemoConfig());
548
+
549
+ customElements.whenDefined("bison-operator-onboarding").then(() => {
550
+ log("Custom element defined");
551
+ patchWelcomeLogoSource();
552
+ });
553
+ </script>
554
+ </body>
555
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bison-web-components",
3
- "version": "3.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "Bison JIB's suite of web components",
5
5
  "main": "component.js",
6
6
  "type": "module",