bison-web-components 1.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,823 @@
1
+ /**
2
+ * OperatorManagement Web Component
3
+ *
4
+ * A hybrid web component that dynamically shows either operator-onboarding or
5
+ * operator-underwriting based on whether the operator email exists in the system.
6
+ *
7
+ * @author @kfajardo
8
+ * @version 1.0.0
9
+ *
10
+ * @requires BisonJibPayAPI - Must be loaded before this component (from api.js)
11
+ * @requires OperatorOnboarding - Must be loaded (from operator-onboarding.js)
12
+ * @requires OperatorUnderwriting - Must be loaded (from operator-underwriting.js)
13
+ *
14
+ * @example
15
+ * ```html
16
+ * <script type="module" src="component.js"></script>
17
+ *
18
+ * <operator-management
19
+ * operator-email="operator@example.com"
20
+ * api-base-url="https://api.example.com"
21
+ * embeddable-key="your-key">
22
+ * </operator-management>
23
+ *
24
+ * <script>
25
+ * const management = document.querySelector('operator-management');
26
+ * management.addEventListener('management-mode-determined', (e) => {
27
+ * console.log('Mode:', e.detail.mode); // 'onboarding' or 'underwriting'
28
+ * });
29
+ * </script>
30
+ * ```
31
+ */
32
+
33
+ class OperatorManagement extends HTMLElement {
34
+ constructor() {
35
+ super();
36
+ this.attachShadow({ mode: "open" });
37
+
38
+ // API Configuration
39
+ this.apiBaseURL =
40
+ this.getAttribute("api-base-url") ||
41
+ "https://bison-jib-development.azurewebsites.net";
42
+ this.embeddableKey =
43
+ this.getAttribute("embeddable-key") ||
44
+ "R80WMkbNN8457RofiMYx03DL65P06IaVT30Q2emYJUBQwYCzRC";
45
+
46
+ // Check if BisonJibPayAPI is available
47
+ if (typeof BisonJibPayAPI === "undefined") {
48
+ console.error(
49
+ "OperatorManagement: BisonJibPayAPI is not available. Please ensure api.js is loaded before operator-management.js"
50
+ );
51
+ this.api = null;
52
+ } else {
53
+ this.api = new BisonJibPayAPI(this.apiBaseURL, this.embeddableKey);
54
+ }
55
+
56
+ // Internal state
57
+ this._state = {
58
+ operatorEmail: null,
59
+ moovAccountId: null,
60
+ isLoading: false,
61
+ isError: false,
62
+ mode: null, // 'onboarding' | 'underwriting' | null
63
+ error: null,
64
+ };
65
+
66
+ // Data to pre-populate the onboarding form
67
+ this._onboardingData = null;
68
+
69
+ // Callbacks for onboarding component
70
+ this._onboardingSuccess = null;
71
+ this._onboardingError = null;
72
+ this._onboardingSubmit = null;
73
+ this._onboardingConfirm = null;
74
+
75
+ // Render the component
76
+ this.render();
77
+ }
78
+
79
+ // ==================== STATIC PROPERTIES ====================
80
+
81
+ static get observedAttributes() {
82
+ return ["operator-email", "api-base-url", "embeddable-key"];
83
+ }
84
+
85
+ // ==================== PROPERTY GETTERS/SETTERS ====================
86
+
87
+ /**
88
+ * Get the operator email
89
+ * @returns {string|null}
90
+ */
91
+ get operatorEmail() {
92
+ return this._state.operatorEmail;
93
+ }
94
+
95
+ /**
96
+ * Set the operator email
97
+ * @param {string} value - Operator email address
98
+ */
99
+ set operatorEmail(value) {
100
+ console.log("OperatorManagement: Setting operator email to:", value);
101
+
102
+ const oldEmail = this._state.operatorEmail;
103
+
104
+ // Update internal state
105
+ this._state.operatorEmail = value;
106
+
107
+ // Update attribute only if different to prevent circular updates
108
+ const currentAttr = this.getAttribute("operator-email");
109
+ if (currentAttr !== value) {
110
+ if (value) {
111
+ this.setAttribute("operator-email", value);
112
+ } else {
113
+ this.removeAttribute("operator-email");
114
+ }
115
+ }
116
+
117
+ // Trigger check if email changed and component is connected
118
+ if (value && value !== oldEmail && this.isConnected) {
119
+ this.checkOperatorStatus();
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Get the moov account ID (if exists)
125
+ * @returns {string|null}
126
+ */
127
+ get moovAccountId() {
128
+ return this._state.moovAccountId;
129
+ }
130
+
131
+ /**
132
+ * Get the current mode
133
+ * @returns {string|null} 'onboarding' | 'underwriting' | null
134
+ */
135
+ get mode() {
136
+ return this._state.mode;
137
+ }
138
+
139
+ /**
140
+ * Check if currently loading
141
+ * @returns {boolean}
142
+ */
143
+ get isLoading() {
144
+ return this._state.isLoading;
145
+ }
146
+
147
+ /**
148
+ * Get the onboarding pre-population data
149
+ * @returns {Object|null}
150
+ */
151
+ get onboardingData() {
152
+ return this._onboardingData;
153
+ }
154
+
155
+ /**
156
+ * Set data to pre-populate the onboarding form
157
+ * @param {Object} data - Data object matching operator-onboarding's onLoad format
158
+ * @example
159
+ * management.onboardingData = {
160
+ * businessDetails: {
161
+ * businessName: "Acme Corp",
162
+ * businessEmail: "contact@acme.com",
163
+ * // ... other business fields
164
+ * },
165
+ * representatives: [...],
166
+ * bankDetails: {...},
167
+ * underwriting: {...}
168
+ * };
169
+ */
170
+ set onboardingData(data) {
171
+ if (data && typeof data === "object") {
172
+ this._onboardingData = data;
173
+
174
+ // If onboarding component already exists, apply data immediately
175
+ const onboarding = this.shadowRoot.querySelector("operator-onboarding");
176
+ if (onboarding) {
177
+ onboarding.onLoad = data;
178
+ }
179
+ } else {
180
+ this._onboardingData = null;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Get the onboarding success callback
186
+ * @returns {Function|null}
187
+ */
188
+ get onboardingSuccess() {
189
+ return this._onboardingSuccess;
190
+ }
191
+
192
+ /**
193
+ * Set callback for successful onboarding submission
194
+ * @param {Function} callback - Called with submission data on success
195
+ */
196
+ set onboardingSuccess(callback) {
197
+ if (typeof callback === "function" || callback === null) {
198
+ this._onboardingSuccess = callback;
199
+
200
+ // If onboarding component already exists, apply callback immediately
201
+ const onboarding = this.shadowRoot.querySelector("operator-onboarding");
202
+ if (onboarding) {
203
+ onboarding.onSuccess = callback;
204
+ }
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Get the onboarding error callback
210
+ * @returns {Function|null}
211
+ */
212
+ get onboardingError() {
213
+ return this._onboardingError;
214
+ }
215
+
216
+ /**
217
+ * Set callback for onboarding errors
218
+ * @param {Function} callback - Called with error data on failure
219
+ */
220
+ set onboardingError(callback) {
221
+ if (typeof callback === "function" || callback === null) {
222
+ this._onboardingError = callback;
223
+
224
+ // If onboarding component already exists, apply callback immediately
225
+ const onboarding = this.shadowRoot.querySelector("operator-onboarding");
226
+ if (onboarding) {
227
+ onboarding.onError = callback;
228
+ }
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Get the onboarding submit callback
234
+ * @returns {Function|null}
235
+ */
236
+ get onboardingSubmit() {
237
+ return this._onboardingSubmit;
238
+ }
239
+
240
+ /**
241
+ * Set callback for pre-submission handling
242
+ * @param {Function} callback - Called before form submission
243
+ */
244
+ set onboardingSubmit(callback) {
245
+ if (typeof callback === "function" || callback === null) {
246
+ this._onboardingSubmit = callback;
247
+
248
+ // If onboarding component already exists, apply callback immediately
249
+ const onboarding = this.shadowRoot.querySelector("operator-onboarding");
250
+ if (onboarding) {
251
+ onboarding.onSubmit = callback;
252
+ }
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Get the onboarding confirm callback
258
+ * @returns {Function|null}
259
+ */
260
+ get onboardingConfirm() {
261
+ return this._onboardingConfirm;
262
+ }
263
+
264
+ /**
265
+ * Set callback for when user clicks Done on success screen
266
+ * @param {Function} callback - Called when user confirms successful onboarding
267
+ */
268
+ set onboardingConfirm(callback) {
269
+ if (typeof callback === "function" || callback === null) {
270
+ this._onboardingConfirm = callback;
271
+
272
+ // If onboarding component already exists, apply callback immediately
273
+ const onboarding = this.shadowRoot.querySelector("operator-onboarding");
274
+ if (onboarding) {
275
+ onboarding.onConfirm = callback;
276
+ }
277
+ }
278
+ }
279
+
280
+ // ==================== LIFECYCLE METHODS ====================
281
+
282
+ connectedCallback() {
283
+ // Initialize email from attribute if present
284
+ const emailAttr = this.getAttribute("operator-email");
285
+ if (emailAttr && !this._state.operatorEmail) {
286
+ this._state.operatorEmail = emailAttr;
287
+ // Trigger API call when email is set
288
+ this.checkOperatorStatus();
289
+ }
290
+ }
291
+
292
+ disconnectedCallback() {
293
+ // Cleanup if needed
294
+ }
295
+
296
+ attributeChangedCallback(name, oldValue, newValue) {
297
+ if (oldValue === newValue) return;
298
+
299
+ switch (name) {
300
+ case "operator-email":
301
+ console.log(
302
+ "OperatorManagement: attributeChangedCallback - operator-email:",
303
+ newValue
304
+ );
305
+ this._state.operatorEmail = newValue;
306
+ // Reset state when email changes
307
+ this._state.moovAccountId = null;
308
+ this._state.mode = null;
309
+ this._state.isError = false;
310
+ this._state.error = null;
311
+ // Trigger API call
312
+ if (newValue && this.isConnected) {
313
+ this.checkOperatorStatus();
314
+ } else if (!newValue) {
315
+ this.render();
316
+ }
317
+ break;
318
+
319
+ case "api-base-url":
320
+ this.apiBaseURL = newValue;
321
+ if (typeof BisonJibPayAPI !== "undefined") {
322
+ this.api = new BisonJibPayAPI(this.apiBaseURL, this.embeddableKey);
323
+ }
324
+ break;
325
+
326
+ case "embeddable-key":
327
+ this.embeddableKey = newValue;
328
+ if (typeof BisonJibPayAPI !== "undefined") {
329
+ this.api = new BisonJibPayAPI(this.apiBaseURL, this.embeddableKey);
330
+ }
331
+ break;
332
+ }
333
+ }
334
+
335
+ // ==================== API INTEGRATION ====================
336
+
337
+ /**
338
+ * Check operator status by calling getAccountByEmail API
339
+ * Determines whether to show onboarding or underwriting
340
+ */
341
+ async checkOperatorStatus() {
342
+ // Validate email is set
343
+ if (!this._state.operatorEmail) {
344
+ console.warn("OperatorManagement: Email is required");
345
+ return;
346
+ }
347
+
348
+ // Validate API is available
349
+ if (!this.api) {
350
+ console.error(
351
+ "OperatorManagement: BisonJibPayAPI is not available. Please ensure api.js is loaded first."
352
+ );
353
+ this._state.isError = true;
354
+ this._state.error = "API not available";
355
+ this.render();
356
+ return;
357
+ }
358
+
359
+ // Set loading state
360
+ this._state.isLoading = true;
361
+ this._state.isError = false;
362
+ this._state.error = null;
363
+ this._state.mode = null;
364
+ this.render();
365
+
366
+ try {
367
+ console.log(
368
+ "OperatorManagement: Checking account status for:",
369
+ this._state.operatorEmail
370
+ );
371
+
372
+ const response = await this.api.getAccountByEmail(
373
+ this._state.operatorEmail
374
+ );
375
+
376
+ // Success: Account exists - show underwriting (keep loading state)
377
+ this._state.moovAccountId =
378
+ response.data?.moovAccountId || response.moovAccountId;
379
+ // Keep isLoading = true, will be set to false when underwriting-ready event fires
380
+ this._state.mode = "underwriting";
381
+
382
+ console.log(
383
+ "OperatorManagement: Account exists, showing underwriting. MoovAccountId:",
384
+ this._state.moovAccountId
385
+ );
386
+
387
+ // Emit mode determined event
388
+ this.dispatchEvent(
389
+ new CustomEvent("management-mode-determined", {
390
+ detail: {
391
+ mode: "underwriting",
392
+ moovAccountId: this._state.moovAccountId,
393
+ operatorEmail: this._state.operatorEmail,
394
+ },
395
+ bubbles: true,
396
+ composed: true,
397
+ })
398
+ );
399
+ } catch (error) {
400
+ // Failure: Account doesn't exist - show onboarding (stop loading immediately)
401
+ this._state.isLoading = false;
402
+ this._state.moovAccountId = null;
403
+ this._state.mode = "onboarding";
404
+
405
+ console.log(
406
+ "OperatorManagement: Account doesn't exist, showing onboarding"
407
+ );
408
+
409
+ // Emit mode determined event
410
+ this.dispatchEvent(
411
+ new CustomEvent("management-mode-determined", {
412
+ detail: {
413
+ mode: "onboarding",
414
+ operatorEmail: this._state.operatorEmail,
415
+ error: error.data?.message || error.message,
416
+ },
417
+ bubbles: true,
418
+ composed: true,
419
+ })
420
+ );
421
+ }
422
+
423
+ this.render();
424
+ }
425
+
426
+ // ==================== RENDERING ====================
427
+
428
+ /**
429
+ * Render the component (Shadow DOM)
430
+ */
431
+ render() {
432
+ this.shadowRoot.innerHTML = `
433
+ <style>
434
+ :host {
435
+ display: block;
436
+ font-family: var(--font-sans, 'Inter', system-ui, sans-serif);
437
+ color: var(--color-secondary, #5f6e78);
438
+ }
439
+
440
+ .management-container {
441
+ width: 100%;
442
+ }
443
+
444
+ .loading-button-container {
445
+ display: inline-block;
446
+ }
447
+
448
+ .unified-loading-button {
449
+ padding: 12px 24px;
450
+ background: var(--color-primary-soft, #678f7a);
451
+ color: var(--color-white, #fff);
452
+ border: none;
453
+ border-radius: var(--radius-xl, 0.75rem);
454
+ font-size: var(--text-sm, 0.875rem);
455
+ font-weight: var(--font-weight-medium, 500);
456
+ cursor: wait;
457
+ transition: all var(--duration-normal, 200ms) var(--ease-in-out, cubic-bezier(0.4, 0, 0.2, 1));
458
+ display: inline-flex;
459
+ align-items: center;
460
+ gap: 8px;
461
+ height: 40px;
462
+ box-sizing: border-box;
463
+ }
464
+
465
+ .unified-loading-button:disabled {
466
+ cursor: not-allowed;
467
+ }
468
+
469
+ .button-spinner {
470
+ width: 16px;
471
+ height: 16px;
472
+ border: 2px solid rgba(255, 255, 255, 0.3);
473
+ border-top-color: var(--color-white, #fff);
474
+ border-radius: 50%;
475
+ animation: spin 0.8s linear infinite;
476
+ box-sizing: border-box;
477
+ }
478
+
479
+ @keyframes spin {
480
+ to {
481
+ transform: rotate(360deg);
482
+ }
483
+ }
484
+
485
+ .empty-state {
486
+ display: flex;
487
+ flex-direction: column;
488
+ align-items: center;
489
+ justify-content: center;
490
+ padding: 48px 24px;
491
+ text-align: center;
492
+ background: var(--color-gray-50, #f9fafb);
493
+ border: 1px dashed var(--color-gray-200, #d1d5db);
494
+ border-radius: var(--radius-xl, 0.75rem);
495
+ }
496
+
497
+ .empty-icon {
498
+ width: 64px;
499
+ height: 64px;
500
+ color: var(--color-gray-400, #9ca3af);
501
+ margin-bottom: 16px;
502
+ }
503
+
504
+ .empty-text {
505
+ font-size: 16px;
506
+ color: var(--color-gray-500, #6b7280);
507
+ margin: 0;
508
+ }
509
+
510
+ .empty-subtext {
511
+ font-size: 14px;
512
+ color: var(--color-gray-400, #9ca3af);
513
+ margin-top: 8px;
514
+ }
515
+
516
+ .error-container {
517
+ display: flex;
518
+ flex-direction: column;
519
+ align-items: center;
520
+ justify-content: center;
521
+ padding: 48px 24px;
522
+ text-align: center;
523
+ background: var(--color-error-light, #fae5e4);
524
+ border: 1px solid var(--color-error-muted, #eea9a5);
525
+ border-radius: var(--radius-xl, 0.75rem);
526
+ }
527
+
528
+ .error-icon {
529
+ width: 64px;
530
+ height: 64px;
531
+ color: var(--color-error, #dd524b);
532
+ margin-bottom: 16px;
533
+ }
534
+
535
+ .error-text {
536
+ font-size: 16px;
537
+ color: var(--color-error-dark, #903531);
538
+ margin: 0;
539
+ }
540
+
541
+ .error-subtext {
542
+ font-size: 14px;
543
+ color: var(--color-error, #dd524b);
544
+ margin-top: 8px;
545
+ }
546
+ </style>
547
+
548
+ <div class="management-container">
549
+ ${this.renderContent()}
550
+ </div>
551
+ `;
552
+
553
+ console.log("OperatorManagement: render() called", {
554
+ mode: this._state.mode,
555
+ isLoading: this._state.isLoading,
556
+ operatorEmail: this._state.operatorEmail,
557
+ });
558
+
559
+ // Setup event forwarding after render, with a delay to ensure child components are ready
560
+ setTimeout(() => this.setupEventForwarding(), 0);
561
+ }
562
+
563
+ /**
564
+ * Render the appropriate content based on state
565
+ */
566
+ renderContent() {
567
+ // No email provided
568
+ if (!this._state.operatorEmail) {
569
+ return `
570
+ <div class="empty-state">
571
+ <svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
572
+ <path d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
573
+ </svg>
574
+ <p class="empty-text">No operator email provided</p>
575
+ <p class="empty-subtext">Set the operator-email attribute to get started</p>
576
+ </div>
577
+ `;
578
+ }
579
+
580
+ // Error state (API not available)
581
+ if (this._state.isError) {
582
+ return `
583
+ <div class="error-container">
584
+ <svg class="error-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
585
+ <circle cx="12" cy="12" r="10"></circle>
586
+ <line x1="12" y1="8" x2="12" y2="12"></line>
587
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
588
+ </svg>
589
+ <p class="error-text">Unable to check operator status</p>
590
+ <p class="error-subtext">${this._state.error || "Please try again later"
591
+ }</p>
592
+ </div>
593
+ `;
594
+ }
595
+
596
+ // Show appropriate component based on mode
597
+ if (this._state.mode === "onboarding") {
598
+ return `
599
+ <operator-onboarding
600
+ api-base-url="${this.apiBaseURL}"
601
+ embeddable-key="${this.embeddableKey}">
602
+ </operator-onboarding>
603
+ `;
604
+ }
605
+
606
+ if (this._state.mode === "underwriting") {
607
+ // Show loading button overlay while underwriting is initializing
608
+ const loadingOverlay = this._state.isLoading
609
+ ? `
610
+ <div class="loading-button-container">
611
+ <button class="unified-loading-button" disabled>
612
+ <span class="button-spinner"></span>
613
+ Checking operator status...
614
+ </button>
615
+ </div>
616
+ `
617
+ : "";
618
+
619
+ return `
620
+ ${loadingOverlay}
621
+ <operator-underwriting
622
+ operator-email="${this._state.operatorEmail}"
623
+ api-base-url="${this.apiBaseURL}"
624
+ embeddable-key="${this.embeddableKey}"
625
+ style="${this._state.isLoading
626
+ ? "visibility: hidden; position: absolute;"
627
+ : ""
628
+ }">
629
+ </operator-underwriting>
630
+ `;
631
+ }
632
+
633
+ // Loading state - show unified loading button (only if no mode determined yet)
634
+ if (this._state.isLoading) {
635
+ return `
636
+ <div class="loading-button-container">
637
+ <button class="unified-loading-button" disabled>
638
+ <span class="button-spinner"></span>
639
+ Checking operator status...
640
+ </button>
641
+ </div>
642
+ `;
643
+ }
644
+
645
+ // Default empty state (shouldn't reach here normally)
646
+ return `
647
+ <div class="empty-state">
648
+ <p class="empty-text">Initializing...</p>
649
+ </div>
650
+ `;
651
+ }
652
+
653
+ /**
654
+ * Setup event forwarding from child components
655
+ */
656
+ setupEventForwarding() {
657
+ // Forward onboarding events
658
+ const onboarding = this.shadowRoot.querySelector("operator-onboarding");
659
+
660
+ console.log("OperatorManagement: setupEventForwarding called", {
661
+ onboardingFound: !!onboarding,
662
+ hasSuccessCallback: !!this._onboardingSuccess,
663
+ hasErrorCallback: !!this._onboardingError,
664
+ hasSubmitCallback: !!this._onboardingSubmit,
665
+ hasOnboardingData: !!this._onboardingData,
666
+ });
667
+
668
+ if (onboarding) {
669
+ // Pre-populate form data if onboardingData was set
670
+ if (this._onboardingData) {
671
+ onboarding.onLoad = this._onboardingData;
672
+ }
673
+
674
+ // Apply callbacks if they were set
675
+ if (this._onboardingSuccess) {
676
+ console.log("OperatorManagement: Applying onSuccess callback to onboarding");
677
+ onboarding.onSuccess = this._onboardingSuccess;
678
+ }
679
+ if (this._onboardingError) {
680
+ onboarding.onError = this._onboardingError;
681
+ }
682
+ if (this._onboardingSubmit) {
683
+ onboarding.onSubmit = this._onboardingSubmit;
684
+ }
685
+ if (this._onboardingConfirm) {
686
+ onboarding.onConfirm = this._onboardingConfirm;
687
+ }
688
+
689
+ onboarding.addEventListener("formComplete", (e) => {
690
+ this.dispatchEvent(
691
+ new CustomEvent("onboarding-complete", {
692
+ detail: {
693
+ ...e.detail,
694
+ operatorEmail: this._state.operatorEmail,
695
+ },
696
+ bubbles: true,
697
+ composed: true,
698
+ })
699
+ );
700
+ // Note: checkOperatorStatus is now triggered when user clicks "Done" button
701
+ });
702
+
703
+ // Listen for confirm button click to re-check status
704
+ onboarding.addEventListener("onboardingConfirmed", (e) => {
705
+ this.dispatchEvent(
706
+ new CustomEvent("onboarding-confirmed", {
707
+ detail: {
708
+ ...e.detail,
709
+ operatorEmail: this._state.operatorEmail,
710
+ },
711
+ bubbles: true,
712
+ composed: true,
713
+ })
714
+ );
715
+
716
+ // After user confirms, re-check status to potentially switch to underwriting
717
+ console.log(
718
+ "OperatorManagement: Onboarding confirmed, re-checking status..."
719
+ );
720
+ // Give the backend time to process
721
+ setTimeout(() => this.checkOperatorStatus(), 1000);
722
+ });
723
+
724
+ onboarding.addEventListener("submissionFailed", (e) => {
725
+ this.dispatchEvent(
726
+ new CustomEvent("onboarding-failed", {
727
+ detail: e.detail,
728
+ bubbles: true,
729
+ composed: true,
730
+ })
731
+ );
732
+ });
733
+ }
734
+
735
+ // Forward underwriting events
736
+ const underwriting = this.shadowRoot.querySelector("operator-underwriting");
737
+ if (underwriting) {
738
+ underwriting.addEventListener("underwriting-ready", (e) => {
739
+ // Hide the unified loading button when underwriting is ready
740
+ this._state.isLoading = false;
741
+
742
+ // Just hide the loading overlay and show the underwriting component
743
+ const loadingOverlay = this.shadowRoot.querySelector(
744
+ ".loading-button-container"
745
+ );
746
+ if (loadingOverlay) {
747
+ loadingOverlay.style.display = "none";
748
+ }
749
+ underwriting.style.visibility = "visible";
750
+ underwriting.style.position = "static";
751
+
752
+ this.dispatchEvent(
753
+ new CustomEvent("underwriting-ready", {
754
+ detail: e.detail,
755
+ bubbles: true,
756
+ composed: true,
757
+ })
758
+ );
759
+ });
760
+
761
+ underwriting.addEventListener("underwriting-error", (e) => {
762
+ // Hide the unified loading button on error too
763
+ this._state.isLoading = false;
764
+
765
+ // Just hide the loading overlay and show the underwriting component
766
+ const loadingOverlay = this.shadowRoot.querySelector(
767
+ ".loading-button-container"
768
+ );
769
+ if (loadingOverlay) {
770
+ loadingOverlay.style.display = "none";
771
+ }
772
+ underwriting.style.visibility = "visible";
773
+ underwriting.style.position = "static";
774
+
775
+ this.dispatchEvent(
776
+ new CustomEvent("underwriting-error", {
777
+ detail: e.detail,
778
+ bubbles: true,
779
+ composed: true,
780
+ })
781
+ );
782
+ });
783
+
784
+ underwriting.addEventListener("underwriting-modal-open", (e) => {
785
+ this.dispatchEvent(
786
+ new CustomEvent("underwriting-modal-open", {
787
+ detail: e.detail,
788
+ bubbles: true,
789
+ composed: true,
790
+ })
791
+ );
792
+ });
793
+
794
+ underwriting.addEventListener("underwriting-modal-close", (e) => {
795
+ this.dispatchEvent(
796
+ new CustomEvent("underwriting-modal-close", {
797
+ detail: e.detail,
798
+ bubbles: true,
799
+ composed: true,
800
+ })
801
+ );
802
+ });
803
+ }
804
+ }
805
+ }
806
+
807
+ // Register the custom element only if it hasn't been registered yet
808
+ if (!customElements.get("operator-management")) {
809
+ customElements.define("operator-management", OperatorManagement);
810
+ }
811
+
812
+ // Export for module usage
813
+ if (typeof module !== "undefined" && module.exports) {
814
+ module.exports = { OperatorManagement };
815
+ }
816
+
817
+ // Make available globally for script tag usage
818
+ if (typeof window !== "undefined") {
819
+ window.OperatorManagement = OperatorManagement;
820
+ }
821
+
822
+ // Export for ES6 modules
823
+ export { OperatorManagement };