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.
- package/LICENSE +21 -0
- package/README.md +749 -0
- package/api.js +659 -0
- package/bison-operator-payments.js +1913 -0
- package/bison_logo.png +0 -0
- package/bison_logo_green.png +0 -0
- package/bison_logo_green.svg +103 -0
- package/bison_logo_white.svg +68 -0
- package/component.js +48 -0
- package/demo-payments.html +275 -0
- package/index.html +199 -0
- package/operator-bank-account.js +1193 -0
- package/operator-management.js +823 -0
- package/operator-onboarding.js +3750 -0
- package/operator-payment.js +2404 -0
- package/operator-underwriting.js +1473 -0
- package/package.json +14 -0
- package/test.js +3 -0
- package/test.mjs +3 -0
- package/theme.css +312 -0
- package/wio-bank-account.js +536 -0
- package/wio-onboarding.js +3981 -0
- package/wio-payment-linking.js +2054 -0
- package/wio-payment.js +579 -0
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OperatorBankAccount Web Component
|
|
3
|
+
*
|
|
4
|
+
* A simple web component that provides a button to add a bank account via Moov.
|
|
5
|
+
* Opens Moov's drop payment method directly when clicked.
|
|
6
|
+
*
|
|
7
|
+
* @author @kfajardo
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```html
|
|
12
|
+
* <script src="component.js"></script>
|
|
13
|
+
* <script src="operator-bank-account.js"></script>
|
|
14
|
+
*
|
|
15
|
+
* <operator-bank-account
|
|
16
|
+
* id="addBank"
|
|
17
|
+
* email="operator@example.com"
|
|
18
|
+
* operator-id="OP123456"
|
|
19
|
+
* client-id="CLIENT789"
|
|
20
|
+
* api-url="https://your-api.com"
|
|
21
|
+
* ></operator-bank-account>
|
|
22
|
+
*
|
|
23
|
+
* Provide at least one of: email, operator-id, or client-id.
|
|
24
|
+
*
|
|
25
|
+
* <script>
|
|
26
|
+
* const addBank = document.getElementById('addBank');
|
|
27
|
+
*
|
|
28
|
+
* // Success callback
|
|
29
|
+
* addBank.onSuccess = (result) => {
|
|
30
|
+
* console.log('Bank account added:', result);
|
|
31
|
+
* };
|
|
32
|
+
*
|
|
33
|
+
* // Fail callback
|
|
34
|
+
* addBank.onFail = (error) => {
|
|
35
|
+
* console.error('Failed to add bank account:', error);
|
|
36
|
+
* };
|
|
37
|
+
* </script>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
class OperatorBankAccount extends HTMLElement {
|
|
42
|
+
constructor() {
|
|
43
|
+
super();
|
|
44
|
+
this.attachShadow({ mode: "open" });
|
|
45
|
+
|
|
46
|
+
// API Configuration
|
|
47
|
+
this.apiBaseURL =
|
|
48
|
+
this.getAttribute("api-url") ||
|
|
49
|
+
"https://bison-jib-development.azurewebsites.net";
|
|
50
|
+
this.embeddableKey =
|
|
51
|
+
this.getAttribute("embeddable-key") ||
|
|
52
|
+
"R80WMkbNN8457RofiMYx03DL65P06IaVT30Q2emYJUBQwYCzRC";
|
|
53
|
+
|
|
54
|
+
// API will be initialized lazily when needed
|
|
55
|
+
this.api = null;
|
|
56
|
+
|
|
57
|
+
// Internal state
|
|
58
|
+
this._state = {
|
|
59
|
+
email: null,
|
|
60
|
+
operatorId: null,
|
|
61
|
+
clientId: null,
|
|
62
|
+
isLoading: true, // Loading by default for verification
|
|
63
|
+
moovAccountId: null,
|
|
64
|
+
moovToken: null,
|
|
65
|
+
error: null,
|
|
66
|
+
isVerified: false,
|
|
67
|
+
initializationError: false,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Callback functions
|
|
71
|
+
this._onSuccess = null;
|
|
72
|
+
this._onFail = null;
|
|
73
|
+
|
|
74
|
+
// Moov drop reference
|
|
75
|
+
this._moovRef = null;
|
|
76
|
+
|
|
77
|
+
// Render the component
|
|
78
|
+
this.render();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ==================== STATIC PROPERTIES ====================
|
|
82
|
+
|
|
83
|
+
static get observedAttributes() {
|
|
84
|
+
return ["email", "operator-id", "client-id", "api-url", "embeddable-key"];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ==================== PROPERTY GETTERS/SETTERS ====================
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get the email
|
|
91
|
+
* @returns {string|null}
|
|
92
|
+
*/
|
|
93
|
+
get email() {
|
|
94
|
+
return this._state.email;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set the email
|
|
99
|
+
* @param {string} value - Operator email address
|
|
100
|
+
*/
|
|
101
|
+
set email(value) {
|
|
102
|
+
console.log("OperatorBankAccount: Setting email to:", value);
|
|
103
|
+
|
|
104
|
+
const oldEmail = this._state.email;
|
|
105
|
+
|
|
106
|
+
// Update internal state
|
|
107
|
+
this._state.email = value;
|
|
108
|
+
|
|
109
|
+
// Update attribute only if different to prevent circular updates
|
|
110
|
+
const currentAttr = this.getAttribute("email");
|
|
111
|
+
if (currentAttr !== value) {
|
|
112
|
+
if (value) {
|
|
113
|
+
this.setAttribute("email", value);
|
|
114
|
+
} else {
|
|
115
|
+
this.removeAttribute("email");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Trigger verification if required fields are set and component is connected
|
|
120
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
121
|
+
if (!missingMessage && value && value !== oldEmail && this.isConnected) {
|
|
122
|
+
this.verifyAndInitialize();
|
|
123
|
+
} else if (missingMessage && this.isConnected) {
|
|
124
|
+
// Missing required fields, show error state
|
|
125
|
+
this._state.isLoading = false;
|
|
126
|
+
this._state.initializationError = true;
|
|
127
|
+
this._state.error = missingMessage;
|
|
128
|
+
this.updateButtonState();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get the operatorId
|
|
134
|
+
* @returns {string|null}
|
|
135
|
+
*/
|
|
136
|
+
get operatorId() {
|
|
137
|
+
return this._state.operatorId;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Set the operatorId
|
|
142
|
+
* @param {string} value - Operator ID
|
|
143
|
+
*/
|
|
144
|
+
set operatorId(value) {
|
|
145
|
+
console.log("OperatorBankAccount: Setting operatorId to:", value);
|
|
146
|
+
|
|
147
|
+
const oldOperatorId = this._state.operatorId;
|
|
148
|
+
|
|
149
|
+
// Update internal state
|
|
150
|
+
this._state.operatorId = value;
|
|
151
|
+
|
|
152
|
+
// Update attribute only if different to prevent circular updates
|
|
153
|
+
const currentAttr = this.getAttribute("operator-id");
|
|
154
|
+
if (currentAttr !== value) {
|
|
155
|
+
if (value) {
|
|
156
|
+
this.setAttribute("operator-id", value);
|
|
157
|
+
} else {
|
|
158
|
+
this.removeAttribute("operator-id");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Trigger verification if required fields are set and component is connected
|
|
163
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
164
|
+
if (
|
|
165
|
+
!missingMessage &&
|
|
166
|
+
value &&
|
|
167
|
+
value !== oldOperatorId &&
|
|
168
|
+
this.isConnected
|
|
169
|
+
) {
|
|
170
|
+
this.verifyAndInitialize();
|
|
171
|
+
} else if (missingMessage && this.isConnected) {
|
|
172
|
+
// Missing required fields, show error state
|
|
173
|
+
this._state.isLoading = false;
|
|
174
|
+
this._state.initializationError = true;
|
|
175
|
+
this._state.error = missingMessage;
|
|
176
|
+
this.updateButtonState();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get the clientId
|
|
182
|
+
* @returns {string|null}
|
|
183
|
+
*/
|
|
184
|
+
get clientId() {
|
|
185
|
+
return this._state.clientId;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Set the clientId
|
|
190
|
+
* @param {string} value - Client ID
|
|
191
|
+
*/
|
|
192
|
+
set clientId(value) {
|
|
193
|
+
console.log("OperatorBankAccount: Setting clientId to:", value);
|
|
194
|
+
|
|
195
|
+
const oldClientId = this._state.clientId;
|
|
196
|
+
|
|
197
|
+
// Update internal state
|
|
198
|
+
this._state.clientId = value;
|
|
199
|
+
|
|
200
|
+
// Update attribute only if different to prevent circular updates
|
|
201
|
+
const currentAttr = this.getAttribute("client-id");
|
|
202
|
+
if (currentAttr !== value) {
|
|
203
|
+
if (value) {
|
|
204
|
+
this.setAttribute("client-id", value);
|
|
205
|
+
} else {
|
|
206
|
+
this.removeAttribute("client-id");
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Trigger verification if required fields are set and component is connected
|
|
211
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
212
|
+
if (
|
|
213
|
+
!missingMessage &&
|
|
214
|
+
value &&
|
|
215
|
+
value !== oldClientId &&
|
|
216
|
+
this.isConnected
|
|
217
|
+
) {
|
|
218
|
+
this.verifyAndInitialize();
|
|
219
|
+
} else if (missingMessage && this.isConnected) {
|
|
220
|
+
// Missing required fields, show error state
|
|
221
|
+
this._state.isLoading = false;
|
|
222
|
+
this._state.initializationError = true;
|
|
223
|
+
this._state.error = missingMessage;
|
|
224
|
+
this.updateButtonState();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
getMissingOperatorInfoMessage() {
|
|
229
|
+
if (!this._state.email && !this._state.operatorId && !this._state.clientId) {
|
|
230
|
+
return "Email, operator ID, or client ID is required";
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get the moovAccountId
|
|
237
|
+
* @returns {string|null}
|
|
238
|
+
*/
|
|
239
|
+
get moovAccountId() {
|
|
240
|
+
return this._state.moovAccountId;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get/Set onSuccess callback
|
|
245
|
+
*/
|
|
246
|
+
get onSuccess() {
|
|
247
|
+
return this._onSuccess;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
set onSuccess(callback) {
|
|
251
|
+
if (typeof callback === "function" || callback === null) {
|
|
252
|
+
this._onSuccess = callback;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get/Set onFail callback
|
|
258
|
+
*/
|
|
259
|
+
get onFail() {
|
|
260
|
+
return this._onFail;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
set onFail(callback) {
|
|
264
|
+
if (typeof callback === "function" || callback === null) {
|
|
265
|
+
this._onFail = callback;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get/Set API URL
|
|
271
|
+
*/
|
|
272
|
+
get apiUrl() {
|
|
273
|
+
return this.apiBaseURL;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
set apiUrl(value) {
|
|
277
|
+
this.apiBaseURL = value;
|
|
278
|
+
if (this.api) {
|
|
279
|
+
this.api = new BisonJibPayAPI(this.apiBaseURL, this.embeddableKey);
|
|
280
|
+
}
|
|
281
|
+
// Update attribute
|
|
282
|
+
if (value) {
|
|
283
|
+
this.setAttribute("api-url", value);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ==================== LIFECYCLE METHODS ====================
|
|
288
|
+
|
|
289
|
+
connectedCallback() {
|
|
290
|
+
// Initialize email from attribute if present
|
|
291
|
+
const emailAttr = this.getAttribute("email");
|
|
292
|
+
if (emailAttr && !this._state.email) {
|
|
293
|
+
this._state.email = emailAttr;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Initialize operatorId from attribute if present
|
|
297
|
+
const operatorIdAttr = this.getAttribute("operator-id");
|
|
298
|
+
if (operatorIdAttr && !this._state.operatorId) {
|
|
299
|
+
this._state.operatorId = operatorIdAttr;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Initialize clientId from attribute if present
|
|
303
|
+
const clientIdAttr = this.getAttribute("client-id");
|
|
304
|
+
if (clientIdAttr && !this._state.clientId) {
|
|
305
|
+
this._state.clientId = clientIdAttr;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Load Moov SDK (preload for faster access later)
|
|
309
|
+
this.ensureMoovSDK();
|
|
310
|
+
|
|
311
|
+
this.setupEventListeners();
|
|
312
|
+
|
|
313
|
+
// Auto-verify if required fields are already set
|
|
314
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
315
|
+
if (!missingMessage) {
|
|
316
|
+
this.verifyAndInitialize();
|
|
317
|
+
} else {
|
|
318
|
+
// Missing required fields, show error state with tooltip
|
|
319
|
+
this._state.isLoading = false;
|
|
320
|
+
this._state.initializationError = true;
|
|
321
|
+
this._state.error = missingMessage;
|
|
322
|
+
this.updateButtonState();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
disconnectedCallback() {
|
|
327
|
+
this.removeEventListeners();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
331
|
+
if (oldValue === newValue) return;
|
|
332
|
+
|
|
333
|
+
switch (name) {
|
|
334
|
+
case "email":
|
|
335
|
+
console.log(
|
|
336
|
+
"OperatorBankAccount: attributeChangedCallback - email:",
|
|
337
|
+
newValue
|
|
338
|
+
);
|
|
339
|
+
this._state.email = newValue;
|
|
340
|
+
// Reset state when email changes
|
|
341
|
+
this._state.moovToken = null;
|
|
342
|
+
this._state.moovAccountId = null;
|
|
343
|
+
this._state.isVerified = false;
|
|
344
|
+
this._state.isLoading = true;
|
|
345
|
+
this._state.initializationError = false;
|
|
346
|
+
this.updateButtonState();
|
|
347
|
+
// Trigger verification if required fields are set
|
|
348
|
+
if (this.isConnected) {
|
|
349
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
350
|
+
if (!missingMessage) {
|
|
351
|
+
this.verifyAndInitialize();
|
|
352
|
+
} else {
|
|
353
|
+
this._state.isLoading = false;
|
|
354
|
+
this._state.initializationError = true;
|
|
355
|
+
this._state.error = missingMessage;
|
|
356
|
+
this.updateButtonState();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
break;
|
|
360
|
+
|
|
361
|
+
case "operator-id":
|
|
362
|
+
console.log(
|
|
363
|
+
"OperatorBankAccount: attributeChangedCallback - operator-id:",
|
|
364
|
+
newValue
|
|
365
|
+
);
|
|
366
|
+
this._state.operatorId = newValue;
|
|
367
|
+
// Reset state when operator ID changes
|
|
368
|
+
this._state.moovToken = null;
|
|
369
|
+
this._state.moovAccountId = null;
|
|
370
|
+
this._state.isVerified = false;
|
|
371
|
+
this._state.isLoading = true;
|
|
372
|
+
this._state.initializationError = false;
|
|
373
|
+
this.updateButtonState();
|
|
374
|
+
// Trigger verification if required fields are set
|
|
375
|
+
if (this.isConnected) {
|
|
376
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
377
|
+
if (!missingMessage) {
|
|
378
|
+
this.verifyAndInitialize();
|
|
379
|
+
} else {
|
|
380
|
+
this._state.isLoading = false;
|
|
381
|
+
this._state.initializationError = true;
|
|
382
|
+
this._state.error = missingMessage;
|
|
383
|
+
this.updateButtonState();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
|
|
388
|
+
case "client-id":
|
|
389
|
+
console.log(
|
|
390
|
+
"OperatorBankAccount: attributeChangedCallback - client-id:",
|
|
391
|
+
newValue
|
|
392
|
+
);
|
|
393
|
+
this._state.clientId = newValue;
|
|
394
|
+
// Reset state when client ID changes
|
|
395
|
+
this._state.moovToken = null;
|
|
396
|
+
this._state.moovAccountId = null;
|
|
397
|
+
this._state.isVerified = false;
|
|
398
|
+
this._state.isLoading = true;
|
|
399
|
+
this._state.initializationError = false;
|
|
400
|
+
this.updateButtonState();
|
|
401
|
+
// Trigger verification if required fields are set
|
|
402
|
+
if (this.isConnected) {
|
|
403
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
404
|
+
if (!missingMessage) {
|
|
405
|
+
this.verifyAndInitialize();
|
|
406
|
+
} else {
|
|
407
|
+
this._state.isLoading = false;
|
|
408
|
+
this._state.initializationError = true;
|
|
409
|
+
this._state.error = missingMessage;
|
|
410
|
+
this.updateButtonState();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
|
|
415
|
+
case "api-url":
|
|
416
|
+
this.apiBaseURL = newValue;
|
|
417
|
+
if (this.api) {
|
|
418
|
+
this.api = new BisonJibPayAPI(this.apiBaseURL, this.embeddableKey);
|
|
419
|
+
}
|
|
420
|
+
break;
|
|
421
|
+
|
|
422
|
+
case "embeddable-key":
|
|
423
|
+
this.embeddableKey = newValue;
|
|
424
|
+
if (this.api) {
|
|
425
|
+
this.api = new BisonJibPayAPI(this.apiBaseURL, this.embeddableKey);
|
|
426
|
+
}
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// ==================== MOOV SDK LOADING ====================
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Inject Moov Bison theme styles into document body
|
|
435
|
+
*/
|
|
436
|
+
injectMoovThemeStyles() {
|
|
437
|
+
if (document.getElementById("moov-bison-theme")) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const styleTag = document.createElement("style");
|
|
442
|
+
styleTag.id = "moov-bison-theme";
|
|
443
|
+
styleTag.textContent = `
|
|
444
|
+
:root {
|
|
445
|
+
--moov-color-background: var(--color-white, #fff);
|
|
446
|
+
--moov-color-background-secondary: var(--color-sidebar, #fafafa);
|
|
447
|
+
--moov-color-background-tertiary: var(--color-gray-100, #f3f4f6);
|
|
448
|
+
--moov-color-primary: var(--color-primary, #4c7b63);
|
|
449
|
+
--moov-color-secondary: var(--color-primary-hover, #436c57);
|
|
450
|
+
--moov-color-tertiary: var(--color-border, #e8e8e8);
|
|
451
|
+
--moov-color-info: var(--color-primary, #4c7b63);
|
|
452
|
+
--moov-color-warn: var(--color-warning, #f59e0b);
|
|
453
|
+
--moov-color-danger: var(--color-error, #dd524b);
|
|
454
|
+
--moov-color-success: var(--color-success, #22c55e);
|
|
455
|
+
--moov-color-low-contrast: var(--color-gray-400, #9ca3af);
|
|
456
|
+
--moov-color-medium-contrast: var(--color-gray-600, #4b5563);
|
|
457
|
+
--moov-color-high-contrast: var(--color-headline, #0f2a39);
|
|
458
|
+
--moov-color-graphic-1: var(--color-primary, #4c7b63);
|
|
459
|
+
--moov-color-graphic-2: var(--color-gray-500, #6b7280);
|
|
460
|
+
--moov-color-graphic-3: var(--color-warning, #f59e0b);
|
|
461
|
+
--moov-radius-small: var(--radius-lg, 0.5rem);
|
|
462
|
+
--moov-radius-large: var(--radius-xl, 0.75rem);
|
|
463
|
+
}
|
|
464
|
+
`;
|
|
465
|
+
document.body.appendChild(styleTag);
|
|
466
|
+
console.log("OperatorBankAccount: Bison theme styles injected");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Ensure Moov SDK is loaded
|
|
471
|
+
* @returns {Promise<void>}
|
|
472
|
+
*/
|
|
473
|
+
async ensureMoovSDK() {
|
|
474
|
+
if (window.Moov) {
|
|
475
|
+
console.log("OperatorBankAccount: Moov SDK already loaded");
|
|
476
|
+
return Promise.resolve();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const existingScript = document.querySelector('script[src*="moov.js"]');
|
|
480
|
+
if (existingScript) {
|
|
481
|
+
console.log(
|
|
482
|
+
"OperatorBankAccount: Moov SDK script found, waiting for load..."
|
|
483
|
+
);
|
|
484
|
+
return new Promise((resolve, reject) => {
|
|
485
|
+
existingScript.addEventListener("load", () => {
|
|
486
|
+
console.log(
|
|
487
|
+
"OperatorBankAccount: Moov SDK loaded from existing script"
|
|
488
|
+
);
|
|
489
|
+
resolve();
|
|
490
|
+
});
|
|
491
|
+
existingScript.addEventListener("error", () =>
|
|
492
|
+
reject(new Error("Failed to load Moov SDK"))
|
|
493
|
+
);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
console.log("OperatorBankAccount: Loading Moov SDK from CDN...");
|
|
498
|
+
return new Promise((resolve, reject) => {
|
|
499
|
+
const script = document.createElement("script");
|
|
500
|
+
script.src = "https://js.moov.io/v1";
|
|
501
|
+
script.async = true;
|
|
502
|
+
|
|
503
|
+
script.onload = () => {
|
|
504
|
+
console.log("OperatorBankAccount: Moov SDK loaded successfully");
|
|
505
|
+
resolve();
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
script.onerror = () => {
|
|
509
|
+
const error = new Error("Failed to load Moov SDK from CDN");
|
|
510
|
+
console.error("OperatorBankAccount:", error);
|
|
511
|
+
this._state.error = error.message;
|
|
512
|
+
this.triggerFail({ errorType: "sdk", error: error.message });
|
|
513
|
+
reject(error);
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
document.head.appendChild(script);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// ==================== EVENT HANDLING ====================
|
|
521
|
+
|
|
522
|
+
setupEventListeners() {
|
|
523
|
+
const button = this.shadowRoot.querySelector(".add-bank-btn");
|
|
524
|
+
|
|
525
|
+
if (button) {
|
|
526
|
+
button.addEventListener("click", this.handleButtonClick.bind(this));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
removeEventListeners() {
|
|
531
|
+
// Clean up modal close handlers
|
|
532
|
+
if (this._modalCloseHandlers) {
|
|
533
|
+
this._modalCloseHandlers.forEach((handler) => {
|
|
534
|
+
document.removeEventListener("click", handler, true);
|
|
535
|
+
});
|
|
536
|
+
this._modalCloseHandlers = [];
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Handle button click
|
|
542
|
+
*/
|
|
543
|
+
handleButtonClick() {
|
|
544
|
+
console.log("OperatorBankAccount: Button clicked");
|
|
545
|
+
|
|
546
|
+
// Validate required fields are set
|
|
547
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
548
|
+
if (missingMessage) {
|
|
549
|
+
console.warn(
|
|
550
|
+
"OperatorBankAccount: Cannot open - required fields missing"
|
|
551
|
+
);
|
|
552
|
+
this.triggerFail({
|
|
553
|
+
errorType: "validation",
|
|
554
|
+
error: missingMessage,
|
|
555
|
+
});
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Validate operator is verified
|
|
560
|
+
if (!this._state.isVerified) {
|
|
561
|
+
console.warn("OperatorBankAccount: Cannot open - operator not verified");
|
|
562
|
+
this.triggerFail({
|
|
563
|
+
errorType: "verification",
|
|
564
|
+
error: "Operator is not verified",
|
|
565
|
+
});
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Validate API is available (lazy initialization)
|
|
570
|
+
if (!this.ensureAPI()) {
|
|
571
|
+
console.warn("OperatorBankAccount: Cannot open - API is not available");
|
|
572
|
+
this.triggerFail({
|
|
573
|
+
errorType: "initialization",
|
|
574
|
+
error: "BisonJibPayAPI is not available",
|
|
575
|
+
});
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Open Moov drop
|
|
580
|
+
this.openMoovDrop();
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// ==================== VERIFICATION & INITIALIZATION ====================
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Ensure API is initialized (lazy initialization)
|
|
587
|
+
* @returns {boolean} True if API is available
|
|
588
|
+
*/
|
|
589
|
+
ensureAPI() {
|
|
590
|
+
if (this.api) {
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Try to create API if BisonJibPayAPI is now available
|
|
595
|
+
if (typeof BisonJibPayAPI !== "undefined") {
|
|
596
|
+
this.api = new BisonJibPayAPI(this.apiBaseURL, this.embeddableKey);
|
|
597
|
+
console.log("OperatorBankAccount: API initialized lazily");
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
console.error(
|
|
602
|
+
"OperatorBankAccount: BisonJibPayAPI is not available. Please ensure component.js is loaded."
|
|
603
|
+
);
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Verify operator and initialize Moov token
|
|
609
|
+
*/
|
|
610
|
+
async verifyAndInitialize() {
|
|
611
|
+
console.log(
|
|
612
|
+
"🔍 OperatorBankAccount: verifyAndInitialize() called with email:",
|
|
613
|
+
this._state.email
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
const missingMessage = this.getMissingOperatorInfoMessage();
|
|
617
|
+
if (missingMessage) {
|
|
618
|
+
console.warn(
|
|
619
|
+
"❌ OperatorBankAccount: Required fields are missing for verification"
|
|
620
|
+
);
|
|
621
|
+
this._state.isLoading = false;
|
|
622
|
+
this._state.initializationError = true;
|
|
623
|
+
this._state.error = missingMessage;
|
|
624
|
+
this.updateButtonState();
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
console.log("🔧 OperatorBankAccount: Checking if API is available...");
|
|
629
|
+
// Ensure API is available (lazy initialization)
|
|
630
|
+
if (!this.ensureAPI()) {
|
|
631
|
+
console.error(
|
|
632
|
+
"❌ OperatorBankAccount: ensureAPI() returned false - BisonJibPayAPI is not available"
|
|
633
|
+
);
|
|
634
|
+
console.error("❌ This is why verifyOperator is NOT being called!");
|
|
635
|
+
console.error(
|
|
636
|
+
"💡 Solution: Load component.js with type='module' or use api.js directly"
|
|
637
|
+
);
|
|
638
|
+
this._state.isLoading = false;
|
|
639
|
+
this._state.initializationError = true;
|
|
640
|
+
this.updateButtonState();
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
console.log(
|
|
645
|
+
"✅ OperatorBankAccount: API is available, proceeding with verification..."
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
this._state.isLoading = true;
|
|
650
|
+
this._state.error = null;
|
|
651
|
+
this._state.initializationError = false;
|
|
652
|
+
this.updateButtonState();
|
|
653
|
+
|
|
654
|
+
console.log(
|
|
655
|
+
"OperatorBankAccount: Verifying operator:",
|
|
656
|
+
this._state.email
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
// Step 1: Verify operator exists
|
|
660
|
+
const verifyResult = await this.api.verifyOperator(
|
|
661
|
+
this._state.email || undefined,
|
|
662
|
+
this._state.operatorId || undefined,
|
|
663
|
+
this._state.clientId || undefined
|
|
664
|
+
);
|
|
665
|
+
|
|
666
|
+
if (!verifyResult.success) {
|
|
667
|
+
throw new Error(verifyResult.message || "Operator verification failed");
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
console.log("OperatorBankAccount: Operator verified successfully");
|
|
671
|
+
|
|
672
|
+
// Step 2: Get account to retrieve moovAccountId
|
|
673
|
+
// Priority: operatorId > clientId > email
|
|
674
|
+
let accountResult;
|
|
675
|
+
if (this._state.operatorId) {
|
|
676
|
+
accountResult = await this.api.getAccountByOperatorId(this._state.operatorId);
|
|
677
|
+
} else if (this._state.clientId) {
|
|
678
|
+
accountResult = await this.api.getAccountByClientId(this._state.clientId);
|
|
679
|
+
} else {
|
|
680
|
+
accountResult = await this.api.getAccountByEmail(this._state.email);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (!accountResult.data?.moovAccountId) {
|
|
684
|
+
throw new Error("Operator does not have a Moov account");
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
this._state.moovAccountId = accountResult.data.moovAccountId;
|
|
688
|
+
console.log(
|
|
689
|
+
"OperatorBankAccount: moovAccountId:",
|
|
690
|
+
this._state.moovAccountId
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
// Step 3: Generate Moov token
|
|
694
|
+
await this.generateMoovToken();
|
|
695
|
+
|
|
696
|
+
// Mark as verified
|
|
697
|
+
this._state.isVerified = true;
|
|
698
|
+
this._state.isLoading = false;
|
|
699
|
+
this.updateButtonState();
|
|
700
|
+
|
|
701
|
+
// Dispatch ready event
|
|
702
|
+
this.dispatchEvent(
|
|
703
|
+
new CustomEvent("operator-bank-account-ready", {
|
|
704
|
+
detail: {
|
|
705
|
+
email: this._state.email,
|
|
706
|
+
operatorId: this._state.operatorId,
|
|
707
|
+
clientId: this._state.clientId,
|
|
708
|
+
moovAccountId: this._state.moovAccountId,
|
|
709
|
+
},
|
|
710
|
+
bubbles: true,
|
|
711
|
+
composed: true,
|
|
712
|
+
})
|
|
713
|
+
);
|
|
714
|
+
} catch (error) {
|
|
715
|
+
console.error("OperatorBankAccount: Verification failed:", error);
|
|
716
|
+
|
|
717
|
+
this._state.isLoading = false;
|
|
718
|
+
this._state.isVerified = false;
|
|
719
|
+
this._state.initializationError = true;
|
|
720
|
+
this._state.error =
|
|
721
|
+
error.data?.message || error.message || "Verification failed";
|
|
722
|
+
this.updateButtonState();
|
|
723
|
+
|
|
724
|
+
// Dispatch error event
|
|
725
|
+
this.dispatchEvent(
|
|
726
|
+
new CustomEvent("operator-bank-account-error", {
|
|
727
|
+
detail: {
|
|
728
|
+
error: this._state.error,
|
|
729
|
+
type: "verification",
|
|
730
|
+
originalError: error,
|
|
731
|
+
},
|
|
732
|
+
bubbles: true,
|
|
733
|
+
composed: true,
|
|
734
|
+
})
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Generate Moov access token
|
|
741
|
+
* @returns {Promise<boolean>}
|
|
742
|
+
*/
|
|
743
|
+
async generateMoovToken() {
|
|
744
|
+
try {
|
|
745
|
+
console.log("OperatorBankAccount: Generating Moov token...");
|
|
746
|
+
|
|
747
|
+
const tokenResult = await this.api.generateMoovToken(
|
|
748
|
+
this._state.email,
|
|
749
|
+
this._state.moovAccountId,
|
|
750
|
+
this._state.operatorId,
|
|
751
|
+
this._state.clientId
|
|
752
|
+
);
|
|
753
|
+
|
|
754
|
+
if (!tokenResult || !tokenResult.data?.accessToken) {
|
|
755
|
+
throw new Error("Failed to generate Moov token");
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
this._state.moovToken = tokenResult.data.accessToken;
|
|
759
|
+
|
|
760
|
+
if (tokenResult.data?.accountID) {
|
|
761
|
+
this._state.moovAccountId = tokenResult.data.accountID;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
console.log("OperatorBankAccount: Moov token generated successfully");
|
|
765
|
+
return true;
|
|
766
|
+
} catch (error) {
|
|
767
|
+
console.error("OperatorBankAccount: Token generation failed:", error);
|
|
768
|
+
this._state.error = error.message || "Failed to generate Moov token";
|
|
769
|
+
throw error;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Reset and regenerate token (called on Moov drop close)
|
|
775
|
+
*/
|
|
776
|
+
async resetAndRefreshToken() {
|
|
777
|
+
console.log("OperatorBankAccount: Resetting token and accountID...");
|
|
778
|
+
|
|
779
|
+
// Clear current token
|
|
780
|
+
this._state.moovToken = null;
|
|
781
|
+
|
|
782
|
+
// Regenerate token
|
|
783
|
+
try {
|
|
784
|
+
await this.generateMoovToken();
|
|
785
|
+
console.log("OperatorBankAccount: Token refreshed successfully");
|
|
786
|
+
} catch (error) {
|
|
787
|
+
console.error("OperatorBankAccount: Failed to refresh token:", error);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// ==================== MOOV DROP ====================
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Open Moov Drop
|
|
795
|
+
*/
|
|
796
|
+
async openMoovDrop() {
|
|
797
|
+
console.log("OperatorBankAccount: Opening Moov drop...");
|
|
798
|
+
|
|
799
|
+
// Ensure Moov SDK is loaded
|
|
800
|
+
if (!window.Moov) {
|
|
801
|
+
console.log("OperatorBankAccount: Moov SDK not loaded yet, waiting...");
|
|
802
|
+
try {
|
|
803
|
+
await this.ensureMoovSDK();
|
|
804
|
+
} catch (error) {
|
|
805
|
+
console.error("OperatorBankAccount: Failed to load Moov SDK:", error);
|
|
806
|
+
this.triggerFail({ errorType: "sdk", error: error.message });
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Generate token if not available
|
|
812
|
+
if (!this._state.moovToken) {
|
|
813
|
+
console.log("OperatorBankAccount: Generating Moov token on demand...");
|
|
814
|
+
try {
|
|
815
|
+
await this.generateMoovToken();
|
|
816
|
+
} catch (error) {
|
|
817
|
+
console.error("OperatorBankAccount: Failed to generate token:", error);
|
|
818
|
+
this.triggerFail({ errorType: "token", error: error.message });
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Inject Bison theme styles
|
|
824
|
+
this.injectMoovThemeStyles();
|
|
825
|
+
|
|
826
|
+
// Remove any existing moov-payment-methods element
|
|
827
|
+
const existingMoovDrop = document.getElementById(
|
|
828
|
+
"operator-bank-account-moov-drop"
|
|
829
|
+
);
|
|
830
|
+
if (existingMoovDrop) {
|
|
831
|
+
existingMoovDrop.remove();
|
|
832
|
+
console.log("OperatorBankAccount: Removed existing Moov drop element");
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Create fresh moov-payment-methods element
|
|
836
|
+
const moovDrop = document.createElement("moov-payment-methods");
|
|
837
|
+
moovDrop.id = "operator-bank-account-moov-drop";
|
|
838
|
+
document.body.appendChild(moovDrop);
|
|
839
|
+
|
|
840
|
+
// Configure the Moov drop
|
|
841
|
+
moovDrop.token = this._state.moovToken;
|
|
842
|
+
moovDrop.accountID = this._state.moovAccountId;
|
|
843
|
+
moovDrop.microDeposits = false;
|
|
844
|
+
moovDrop.paymentMethodTypes = ["bankAccount"];
|
|
845
|
+
|
|
846
|
+
// Set up callbacks
|
|
847
|
+
moovDrop.onResourceCreated = async (result) => {
|
|
848
|
+
console.log("OperatorBankAccount: Payment method created:", result);
|
|
849
|
+
|
|
850
|
+
// Trigger success callback
|
|
851
|
+
this.triggerSuccess(result);
|
|
852
|
+
|
|
853
|
+
// Dispatch success event
|
|
854
|
+
this.dispatchEvent(
|
|
855
|
+
new CustomEvent("bank-account-added", {
|
|
856
|
+
detail: result,
|
|
857
|
+
bubbles: true,
|
|
858
|
+
composed: true,
|
|
859
|
+
})
|
|
860
|
+
);
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
moovDrop.onError = ({ errorType, error }) => {
|
|
864
|
+
console.error("OperatorBankAccount: Moov error:", errorType, error);
|
|
865
|
+
|
|
866
|
+
// Trigger fail callback
|
|
867
|
+
this.triggerFail({ errorType, error });
|
|
868
|
+
|
|
869
|
+
// Dispatch error event
|
|
870
|
+
this.dispatchEvent(
|
|
871
|
+
new CustomEvent("bank-account-error", {
|
|
872
|
+
detail: { errorType, error },
|
|
873
|
+
bubbles: true,
|
|
874
|
+
composed: true,
|
|
875
|
+
})
|
|
876
|
+
);
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
// Close handler - reset token and accountID
|
|
880
|
+
moovDrop.onClose = async () => {
|
|
881
|
+
console.log("OperatorBankAccount: Moov UI closed");
|
|
882
|
+
moovDrop.open = false;
|
|
883
|
+
|
|
884
|
+
// Reset and refresh token on every close
|
|
885
|
+
await this.resetAndRefreshToken();
|
|
886
|
+
|
|
887
|
+
// Update moov drop with new token if it exists
|
|
888
|
+
if (this._state.moovToken && moovDrop) {
|
|
889
|
+
moovDrop.token = this._state.moovToken;
|
|
890
|
+
moovDrop.accountID = this._state.moovAccountId;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// Dispatch close event
|
|
894
|
+
this.dispatchEvent(
|
|
895
|
+
new CustomEvent("moov-drop-close", {
|
|
896
|
+
bubbles: true,
|
|
897
|
+
composed: true,
|
|
898
|
+
})
|
|
899
|
+
);
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
// Cancel handler
|
|
903
|
+
moovDrop.onCancel = async () => {
|
|
904
|
+
console.log("OperatorBankAccount: Moov UI cancelled");
|
|
905
|
+
moovDrop.open = false;
|
|
906
|
+
|
|
907
|
+
// Reset and refresh token on every close
|
|
908
|
+
await this.resetAndRefreshToken();
|
|
909
|
+
|
|
910
|
+
// Update moov drop with new token if it exists
|
|
911
|
+
if (this._state.moovToken && moovDrop) {
|
|
912
|
+
moovDrop.token = this._state.moovToken;
|
|
913
|
+
moovDrop.accountID = this._state.moovAccountId;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// Dispatch close event
|
|
917
|
+
this.dispatchEvent(
|
|
918
|
+
new CustomEvent("moov-drop-close", {
|
|
919
|
+
bubbles: true,
|
|
920
|
+
composed: true,
|
|
921
|
+
})
|
|
922
|
+
);
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
// Handle modal close button clicks
|
|
926
|
+
const handleModalClose = async (e) => {
|
|
927
|
+
const target = e.target;
|
|
928
|
+
const modalCloseElement =
|
|
929
|
+
target.closest && target.closest('[data-testid="modalClose"]');
|
|
930
|
+
|
|
931
|
+
if (modalCloseElement) {
|
|
932
|
+
console.log("OperatorBankAccount: Modal close button clicked");
|
|
933
|
+
this.closeMoovDrop();
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
|
|
937
|
+
document.addEventListener("click", handleModalClose, true);
|
|
938
|
+
|
|
939
|
+
if (!this._modalCloseHandlers) {
|
|
940
|
+
this._modalCloseHandlers = [];
|
|
941
|
+
}
|
|
942
|
+
this._modalCloseHandlers.push(handleModalClose);
|
|
943
|
+
|
|
944
|
+
// Open the Moov drop
|
|
945
|
+
console.log("OperatorBankAccount: Setting moovDrop.open = true");
|
|
946
|
+
moovDrop.open = true;
|
|
947
|
+
|
|
948
|
+
// Store reference
|
|
949
|
+
this._moovRef = moovDrop;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* Close the Moov UI
|
|
954
|
+
*/
|
|
955
|
+
async closeMoovDrop() {
|
|
956
|
+
if (this._moovRef && this._moovRef.open) {
|
|
957
|
+
console.log("OperatorBankAccount: Closing Moov UI");
|
|
958
|
+
this._moovRef.open = false;
|
|
959
|
+
|
|
960
|
+
// Reset and refresh token
|
|
961
|
+
await this.resetAndRefreshToken();
|
|
962
|
+
|
|
963
|
+
this._moovRef = null;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// ==================== CALLBACKS ====================
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* Trigger success callback
|
|
971
|
+
*/
|
|
972
|
+
triggerSuccess(result) {
|
|
973
|
+
if (typeof this._onSuccess === "function") {
|
|
974
|
+
this._onSuccess(result);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Trigger fail callback
|
|
980
|
+
*/
|
|
981
|
+
triggerFail(error) {
|
|
982
|
+
if (typeof this._onFail === "function") {
|
|
983
|
+
this._onFail(error);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// ==================== UI UPDATES ====================
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Update button state based on verification status
|
|
991
|
+
*/
|
|
992
|
+
updateButtonState() {
|
|
993
|
+
const button = this.shadowRoot.querySelector(".add-bank-btn");
|
|
994
|
+
const wrapper = this.shadowRoot.querySelector(".btn-wrapper");
|
|
995
|
+
const tooltip = this.shadowRoot.querySelector(".tooltip");
|
|
996
|
+
|
|
997
|
+
if (!button) return;
|
|
998
|
+
|
|
999
|
+
// Handle loading state
|
|
1000
|
+
if (this._state.isLoading) {
|
|
1001
|
+
button.classList.add("loading");
|
|
1002
|
+
button.classList.remove("error");
|
|
1003
|
+
button.disabled = true;
|
|
1004
|
+
if (wrapper) wrapper.classList.remove("has-error");
|
|
1005
|
+
}
|
|
1006
|
+
// Handle error state (not verified)
|
|
1007
|
+
else if (this._state.initializationError || !this._state.isVerified) {
|
|
1008
|
+
button.classList.remove("loading");
|
|
1009
|
+
button.classList.add("error");
|
|
1010
|
+
button.disabled = true;
|
|
1011
|
+
if (wrapper) wrapper.classList.add("has-error");
|
|
1012
|
+
|
|
1013
|
+
// Update tooltip text based on error
|
|
1014
|
+
if (tooltip && this._state.error) {
|
|
1015
|
+
tooltip.textContent = this._state.error;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
// Handle normal state (verified)
|
|
1019
|
+
else {
|
|
1020
|
+
button.classList.remove("loading", "error");
|
|
1021
|
+
button.disabled = false;
|
|
1022
|
+
if (wrapper) wrapper.classList.remove("has-error");
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// ==================== RENDERING ====================
|
|
1027
|
+
|
|
1028
|
+
render() {
|
|
1029
|
+
this.shadowRoot.innerHTML = `
|
|
1030
|
+
<style>
|
|
1031
|
+
:host {
|
|
1032
|
+
display: inline-block;
|
|
1033
|
+
font-family: var(--font-sans, 'Inter', system-ui, sans-serif);
|
|
1034
|
+
color: var(--color-secondary, #5f6e78);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
.btn-wrapper {
|
|
1038
|
+
position: relative;
|
|
1039
|
+
display: inline-block;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
.add-bank-btn {
|
|
1043
|
+
padding: 12px 24px;
|
|
1044
|
+
background: var(--color-primary, #4c7b63);
|
|
1045
|
+
color: var(--color-white, #fff);
|
|
1046
|
+
border: none;
|
|
1047
|
+
border-radius: var(--radius-xl, 0.75rem);
|
|
1048
|
+
font-size: var(--text-sm, 0.875rem);
|
|
1049
|
+
font-weight: var(--font-weight-medium, 500);
|
|
1050
|
+
cursor: pointer;
|
|
1051
|
+
transition: all var(--duration-normal, 200ms) var(--ease-in-out, cubic-bezier(0.4, 0, 0.2, 1));
|
|
1052
|
+
display: inline-flex;
|
|
1053
|
+
align-items: center;
|
|
1054
|
+
gap: 8px;
|
|
1055
|
+
height: 40px;
|
|
1056
|
+
box-sizing: border-box;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
.add-bank-btn:hover:not(.error):not(.loading) {
|
|
1060
|
+
background: var(--color-primary-hover, #436c57);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
.add-bank-btn:active:not(.error):not(.loading) {
|
|
1064
|
+
background: var(--color-primary-active, #3d624f);
|
|
1065
|
+
transform: translateY(0);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
.add-bank-btn.error {
|
|
1069
|
+
background: var(--color-gray-400, #9ca3af);
|
|
1070
|
+
cursor: not-allowed;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
.add-bank-btn.loading {
|
|
1074
|
+
background: var(--color-primary-soft, #678f7a);
|
|
1075
|
+
cursor: wait;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
.add-bank-btn .bank-icon {
|
|
1079
|
+
width: 18px;
|
|
1080
|
+
height: 18px;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
.add-bank-btn .loading-spinner {
|
|
1084
|
+
display: none;
|
|
1085
|
+
width: 16px;
|
|
1086
|
+
height: 16px;
|
|
1087
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
1088
|
+
border-top-color: var(--color-white, #fff);
|
|
1089
|
+
border-radius: 50%;
|
|
1090
|
+
animation: spin 0.8s linear infinite;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
.add-bank-btn.loading .loading-spinner {
|
|
1094
|
+
display: inline-block;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.add-bank-btn.loading .bank-icon {
|
|
1098
|
+
display: none;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
.add-bank-btn .error-icon {
|
|
1102
|
+
display: none;
|
|
1103
|
+
width: 16px;
|
|
1104
|
+
height: 16px;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.add-bank-btn.error .error-icon {
|
|
1108
|
+
display: inline-block;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.add-bank-btn.error .bank-icon {
|
|
1112
|
+
display: none;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
@keyframes spin {
|
|
1116
|
+
to {
|
|
1117
|
+
transform: rotate(360deg);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
.tooltip {
|
|
1122
|
+
visibility: hidden;
|
|
1123
|
+
opacity: 0;
|
|
1124
|
+
position: absolute;
|
|
1125
|
+
bottom: 100%;
|
|
1126
|
+
left: 50%;
|
|
1127
|
+
transform: translateX(-50%);
|
|
1128
|
+
background: var(--color-gray-700, #374151);
|
|
1129
|
+
color: var(--color-white, #fff);
|
|
1130
|
+
padding: 8px 12px;
|
|
1131
|
+
border-radius: var(--radius-lg, 0.5rem);
|
|
1132
|
+
font-size: 13px;
|
|
1133
|
+
white-space: nowrap;
|
|
1134
|
+
margin-bottom: 8px;
|
|
1135
|
+
transition: opacity var(--duration-normal, 200ms) var(--ease-in-out, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
1136
|
+
visibility var(--duration-normal, 200ms) var(--ease-in-out, cubic-bezier(0.4, 0, 0.2, 1));
|
|
1137
|
+
z-index: 10002;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
.tooltip::after {
|
|
1141
|
+
content: '';
|
|
1142
|
+
position: absolute;
|
|
1143
|
+
top: 100%;
|
|
1144
|
+
left: 50%;
|
|
1145
|
+
transform: translateX(-50%);
|
|
1146
|
+
border: 6px solid transparent;
|
|
1147
|
+
border-top-color: var(--color-gray-700, #374151);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
.btn-wrapper:hover .tooltip {
|
|
1151
|
+
visibility: visible;
|
|
1152
|
+
opacity: 1;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
.btn-wrapper:not(.has-error) .tooltip {
|
|
1156
|
+
display: none;
|
|
1157
|
+
}
|
|
1158
|
+
</style>
|
|
1159
|
+
|
|
1160
|
+
<div class="btn-wrapper">
|
|
1161
|
+
<span class="tooltip">Email, operator ID, or client ID is required</span>
|
|
1162
|
+
<button class="add-bank-btn error">
|
|
1163
|
+
<span class="loading-spinner"></span>
|
|
1164
|
+
<svg class="error-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1165
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
1166
|
+
<line x1="12" y1="8" x2="12" y2="12"></line>
|
|
1167
|
+
<line x1="12" y1="16" x2="12.01" y2="16"></line>
|
|
1168
|
+
</svg>
|
|
1169
|
+
<svg class="bank-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1170
|
+
<rect x="3" y="10" width="18" height="11" rx="2" ry="2"></rect>
|
|
1171
|
+
<path d="M12 3L2 10h20L12 3z"></path>
|
|
1172
|
+
</svg>
|
|
1173
|
+
Add Bank Account
|
|
1174
|
+
</button>
|
|
1175
|
+
</div>
|
|
1176
|
+
`;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Register the custom element only if it hasn't been registered yet
|
|
1181
|
+
if (!customElements.get("operator-bank-account")) {
|
|
1182
|
+
customElements.define("operator-bank-account", OperatorBankAccount);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// Export for module usage
|
|
1186
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
1187
|
+
module.exports = { OperatorBankAccount };
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// Make available globally for script tag usage
|
|
1191
|
+
if (typeof window !== "undefined") {
|
|
1192
|
+
window.OperatorBankAccount = OperatorBankAccount;
|
|
1193
|
+
}
|