verant_id_cloud_scan 1.4.4-beta.0 → 1.4.4-beta.2
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/Api.js +106 -122
- package/CloudScan.js +96 -7
- package/DateUtils.js +57 -0
- package/FieldMapping.js +72 -0
- package/LocalScannerApi.js +7 -0
- package/VerificationModal.js +499 -0
- package/dmv-modal.css +512 -0
- package/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
import { dmvCheck } from './Api.js';
|
|
2
|
+
import { FIELD_MAPPING, getFieldValue as getFieldValueFromMapping, FIELD_LABELS } from './FieldMapping.js';
|
|
3
|
+
import { formatDateToDisplay } from './DateUtils.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* VerificationModal - DMV Verification Modal Controller
|
|
7
|
+
*
|
|
8
|
+
* Creates and manages a modal dialog that prompts users to verify their ID
|
|
9
|
+
* against DMV records. Handles confirmation, loading, results, and error states.
|
|
10
|
+
*
|
|
11
|
+
* @class VerificationModal
|
|
12
|
+
*/
|
|
13
|
+
export class VerificationModal {
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new VerificationModal instance
|
|
16
|
+
* @param {string} clientId - Client ID for DMV verification
|
|
17
|
+
* @param {string} scannerType - Type of scanner used (e.g., 'DigitalCheck', 'VerantId6S')
|
|
18
|
+
* @param {Object} licenseData - Barcode data from scanned license
|
|
19
|
+
* @param {Object} options - Configuration options
|
|
20
|
+
* @param {string} options.primaryColor - Theme color (default: '#0074CB')
|
|
21
|
+
* @param {HTMLElement} options.container - Mount point for modal (default: document.body)
|
|
22
|
+
*/
|
|
23
|
+
constructor(clientId, scannerType, licenseData, options = {}) {
|
|
24
|
+
this.clientId = clientId;
|
|
25
|
+
this.scannerType = scannerType;
|
|
26
|
+
this.licenseData = licenseData;
|
|
27
|
+
this.primaryColor = options.primaryColor || '#0074CB';
|
|
28
|
+
this.container = options.container || document.body;
|
|
29
|
+
this.modalRoot = null;
|
|
30
|
+
this.resolve = null;
|
|
31
|
+
this.reject = null;
|
|
32
|
+
|
|
33
|
+
// Automatically inject CSS if not already present
|
|
34
|
+
this.injectCSS();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Injects the modal CSS into the document if not already present
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
injectCSS() {
|
|
42
|
+
// Check if CSS is already injected
|
|
43
|
+
if (document.getElementById('verant-dmv-modal-styles')) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Try to load external CSS file first
|
|
48
|
+
const cssLink = document.createElement('link');
|
|
49
|
+
cssLink.id = 'verant-dmv-modal-styles';
|
|
50
|
+
cssLink.rel = 'stylesheet';
|
|
51
|
+
|
|
52
|
+
// Try to determine the correct path to the CSS file
|
|
53
|
+
// First, check if it's in the same directory as this script
|
|
54
|
+
const scriptPath = new URL(import.meta.url).pathname;
|
|
55
|
+
const scriptDir = scriptPath.substring(0, scriptPath.lastIndexOf('/'));
|
|
56
|
+
cssLink.href = scriptDir + '/dmv-modal.css';
|
|
57
|
+
|
|
58
|
+
document.head.appendChild(cssLink);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Opens the modal and returns a Promise that resolves when user completes/cancels
|
|
63
|
+
* @returns {Promise<Object>} Result object with status and optional DMV data
|
|
64
|
+
*/
|
|
65
|
+
async open() {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
this.resolve = resolve;
|
|
68
|
+
this.reject = reject;
|
|
69
|
+
this.render();
|
|
70
|
+
this.showConfirmation();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Renders the modal structure and attaches event listeners
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
render() {
|
|
79
|
+
// Create overlay + modal structure
|
|
80
|
+
this.modalRoot = document.createElement('div');
|
|
81
|
+
this.modalRoot.className = 'verant-dmv-overlay';
|
|
82
|
+
this.modalRoot.innerHTML = `
|
|
83
|
+
<div class="verant-dmv-modal" role="dialog" aria-modal="true" aria-labelledby="dmv-modal-title">
|
|
84
|
+
<div class="verant-dmv-header" style="background-color: ${this.primaryColor}">
|
|
85
|
+
<h3 id="dmv-modal-title">DMV Verification</h3>
|
|
86
|
+
<button class="verant-dmv-close" aria-label="Close">×</button>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="verant-dmv-body">
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
// Close on X button
|
|
94
|
+
this.modalRoot.querySelector('.verant-dmv-close').addEventListener('click', () => this.cancel());
|
|
95
|
+
|
|
96
|
+
// Close on overlay click
|
|
97
|
+
this.modalRoot.addEventListener('click', (e) => {
|
|
98
|
+
if (e.target === this.modalRoot) this.cancel();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Keyboard: ESC to close
|
|
102
|
+
this.handleKeydown = (e) => {
|
|
103
|
+
if (e.key === 'Escape') this.cancel();
|
|
104
|
+
};
|
|
105
|
+
document.addEventListener('keydown', this.handleKeydown);
|
|
106
|
+
|
|
107
|
+
this.container.appendChild(this.modalRoot);
|
|
108
|
+
|
|
109
|
+
// Focus trap - focus first button
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
const firstButton = this.modalRoot.querySelector('button:not(.verant-dmv-close)');
|
|
112
|
+
if (firstButton) firstButton.focus();
|
|
113
|
+
}, 100);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Shows the confirmation screen asking user if they want to verify
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
showConfirmation() {
|
|
121
|
+
const body = this.modalRoot.querySelector('.verant-dmv-body');
|
|
122
|
+
|
|
123
|
+
// Extract all driver data fields using shared field mapping
|
|
124
|
+
const firstName = getFieldValueFromMapping(this.licenseData, 'first_name') || '';
|
|
125
|
+
const lastName = getFieldValueFromMapping(this.licenseData, 'last_name') || '';
|
|
126
|
+
const licenseNumber = getFieldValueFromMapping(this.licenseData, 'license_number') || '';
|
|
127
|
+
const state = getFieldValueFromMapping(this.licenseData, 'state') || '';
|
|
128
|
+
const birthDate = formatDateToDisplay(getFieldValueFromMapping(this.licenseData, 'birth_date') || '');
|
|
129
|
+
const issueDate = formatDateToDisplay(getFieldValueFromMapping(this.licenseData, 'issue_date') || '');
|
|
130
|
+
const expirationDate = formatDateToDisplay(getFieldValueFromMapping(this.licenseData, 'expiration_date') || '');
|
|
131
|
+
const address = getFieldValueFromMapping(this.licenseData, 'address_line_1') || '';
|
|
132
|
+
const city = getFieldValueFromMapping(this.licenseData, 'city') || '';
|
|
133
|
+
const postalCode = getFieldValueFromMapping(this.licenseData, 'postal_code') || '';
|
|
134
|
+
const sex = getFieldValueFromMapping(this.licenseData, 'sex_code') || '';
|
|
135
|
+
const eyeColor = getFieldValueFromMapping(this.licenseData, 'eye_color') || '';
|
|
136
|
+
const height = getFieldValueFromMapping(this.licenseData, 'height') || '';
|
|
137
|
+
const weight = getFieldValueFromMapping(this.licenseData, 'weight') || '';
|
|
138
|
+
const documentCategory = getFieldValueFromMapping(this.licenseData, 'document_category_code') || '';
|
|
139
|
+
|
|
140
|
+
// Build the data display in two columns with new styling
|
|
141
|
+
let dataHTML = '<div class="verant-dmv-results-container">';
|
|
142
|
+
dataHTML += '<div class="verant-dmv-results-grid">';
|
|
143
|
+
|
|
144
|
+
// Left column
|
|
145
|
+
dataHTML += '<ul class="verant-dmv-fields-column">';
|
|
146
|
+
if (firstName) {
|
|
147
|
+
dataHTML += `
|
|
148
|
+
<li class="verant-dmv-field-item">
|
|
149
|
+
<div class="verant-dmv-field-label">First Name</div>
|
|
150
|
+
<div class="verant-dmv-field-value">${firstName}</div>
|
|
151
|
+
</li>`;
|
|
152
|
+
}
|
|
153
|
+
if (lastName) {
|
|
154
|
+
dataHTML += `
|
|
155
|
+
<li class="verant-dmv-field-item">
|
|
156
|
+
<div class="verant-dmv-field-label">Last Name</div>
|
|
157
|
+
<div class="verant-dmv-field-value">${lastName}</div>
|
|
158
|
+
</li>`;
|
|
159
|
+
}
|
|
160
|
+
if (licenseNumber) {
|
|
161
|
+
dataHTML += `
|
|
162
|
+
<li class="verant-dmv-field-item">
|
|
163
|
+
<div class="verant-dmv-field-label">License Number</div>
|
|
164
|
+
<div class="verant-dmv-field-value">${licenseNumber}</div>
|
|
165
|
+
</li>`;
|
|
166
|
+
}
|
|
167
|
+
if (state) {
|
|
168
|
+
dataHTML += `
|
|
169
|
+
<li class="verant-dmv-field-item">
|
|
170
|
+
<div class="verant-dmv-field-label">State</div>
|
|
171
|
+
<div class="verant-dmv-field-value">${state}</div>
|
|
172
|
+
</li>`;
|
|
173
|
+
}
|
|
174
|
+
if (birthDate) {
|
|
175
|
+
dataHTML += `
|
|
176
|
+
<li class="verant-dmv-field-item">
|
|
177
|
+
<div class="verant-dmv-field-label">Date of Birth</div>
|
|
178
|
+
<div class="verant-dmv-field-value">${birthDate}</div>
|
|
179
|
+
</li>`;
|
|
180
|
+
}
|
|
181
|
+
if (sex) {
|
|
182
|
+
dataHTML += `
|
|
183
|
+
<li class="verant-dmv-field-item">
|
|
184
|
+
<div class="verant-dmv-field-label">Sex</div>
|
|
185
|
+
<div class="verant-dmv-field-value">${sex}</div>
|
|
186
|
+
</li>`;
|
|
187
|
+
}
|
|
188
|
+
if (eyeColor) {
|
|
189
|
+
dataHTML += `
|
|
190
|
+
<li class="verant-dmv-field-item">
|
|
191
|
+
<div class="verant-dmv-field-label">Eye Color</div>
|
|
192
|
+
<div class="verant-dmv-field-value">${eyeColor}</div>
|
|
193
|
+
</li>`;
|
|
194
|
+
}
|
|
195
|
+
dataHTML += '</ul>';
|
|
196
|
+
|
|
197
|
+
// Right column
|
|
198
|
+
dataHTML += '<ul class="verant-dmv-fields-column">';
|
|
199
|
+
if (weight) {
|
|
200
|
+
dataHTML += `
|
|
201
|
+
<li class="verant-dmv-field-item">
|
|
202
|
+
<div class="verant-dmv-field-label">Weight</div>
|
|
203
|
+
<div class="verant-dmv-field-value">${weight}</div>
|
|
204
|
+
</li>`;
|
|
205
|
+
}
|
|
206
|
+
if (issueDate) {
|
|
207
|
+
dataHTML += `
|
|
208
|
+
<li class="verant-dmv-field-item">
|
|
209
|
+
<div class="verant-dmv-field-label">Issue Date</div>
|
|
210
|
+
<div class="verant-dmv-field-value">${issueDate}</div>
|
|
211
|
+
</li>`;
|
|
212
|
+
}
|
|
213
|
+
if (expirationDate) {
|
|
214
|
+
dataHTML += `
|
|
215
|
+
<li class="verant-dmv-field-item">
|
|
216
|
+
<div class="verant-dmv-field-label">Expiration Date</div>
|
|
217
|
+
<div class="verant-dmv-field-value">${expirationDate}</div>
|
|
218
|
+
</li>`;
|
|
219
|
+
}
|
|
220
|
+
if (address) {
|
|
221
|
+
dataHTML += `
|
|
222
|
+
<li class="verant-dmv-field-item">
|
|
223
|
+
<div class="verant-dmv-field-label">Address</div>
|
|
224
|
+
<div class="verant-dmv-field-value">${address}</div>
|
|
225
|
+
</li>`;
|
|
226
|
+
}
|
|
227
|
+
if (city) {
|
|
228
|
+
dataHTML += `
|
|
229
|
+
<li class="verant-dmv-field-item">
|
|
230
|
+
<div class="verant-dmv-field-label">City</div>
|
|
231
|
+
<div class="verant-dmv-field-value">${city}</div>
|
|
232
|
+
</li>`;
|
|
233
|
+
}
|
|
234
|
+
if (postalCode) {
|
|
235
|
+
dataHTML += `
|
|
236
|
+
<li class="verant-dmv-field-item">
|
|
237
|
+
<div class="verant-dmv-field-label">Postal Code</div>
|
|
238
|
+
<div class="verant-dmv-field-value">${postalCode}</div>
|
|
239
|
+
</li>`;
|
|
240
|
+
}
|
|
241
|
+
if (documentCategory) {
|
|
242
|
+
dataHTML += `
|
|
243
|
+
<li class="verant-dmv-field-item">
|
|
244
|
+
<div class="verant-dmv-field-label">Document Type</div>
|
|
245
|
+
<div class="verant-dmv-field-value">${documentCategory}</div>
|
|
246
|
+
</li>`;
|
|
247
|
+
}
|
|
248
|
+
if (height) {
|
|
249
|
+
dataHTML += `
|
|
250
|
+
<li class="verant-dmv-field-item">
|
|
251
|
+
<div class="verant-dmv-field-label">Height</div>
|
|
252
|
+
<div class="verant-dmv-field-value">${height}</div>
|
|
253
|
+
</li>`;
|
|
254
|
+
}
|
|
255
|
+
dataHTML += '</ul>';
|
|
256
|
+
|
|
257
|
+
dataHTML += '</div>'; // close grid
|
|
258
|
+
dataHTML += '</div>'; // close container
|
|
259
|
+
|
|
260
|
+
body.innerHTML = `
|
|
261
|
+
<p class="verant-dmv-question">Would you like to perform a DMV Verification for this customer?</p>
|
|
262
|
+
<div class="verant-dmv-info-box">
|
|
263
|
+
${dataHTML}
|
|
264
|
+
</div>
|
|
265
|
+
<div class="verant-dmv-actions">
|
|
266
|
+
<button class="verant-dmv-btn-secondary" id="cancelBtn">Skip</button>
|
|
267
|
+
<button class="verant-dmv-btn-primary" id="verifyBtn" style="background-color: ${this.primaryColor}">Yes, Verify</button>
|
|
268
|
+
</div>
|
|
269
|
+
`;
|
|
270
|
+
|
|
271
|
+
body.querySelector('#cancelBtn').addEventListener('click', () => this.cancel());
|
|
272
|
+
body.querySelector('#verifyBtn').addEventListener('click', () => this.verify());
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Shows mock DMV results for preview/testing purposes
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
showMockResults() {
|
|
280
|
+
// Create mock DMV data with mixed match/mismatch results
|
|
281
|
+
const mockDmvData = {
|
|
282
|
+
match_status: true,
|
|
283
|
+
field_matches: [
|
|
284
|
+
{ first_name: 'true' },
|
|
285
|
+
{ last_name: 'true' },
|
|
286
|
+
{ license_number: 'true' },
|
|
287
|
+
{ birth_date: 'true' },
|
|
288
|
+
{ address_line_1: 'false' },
|
|
289
|
+
{ expiration_date: 'true' },
|
|
290
|
+
{ issue_date: 'true' },
|
|
291
|
+
{ eye_color: 'true' },
|
|
292
|
+
{ sex_code: 'true' },
|
|
293
|
+
{ height: 'false' },
|
|
294
|
+
{ weight: 'true' },
|
|
295
|
+
{ city: 'true' },
|
|
296
|
+
{ state: 'true' },
|
|
297
|
+
{ postal_code: 'false' }
|
|
298
|
+
]
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
this.showResults(mockDmvData);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Initiates DMV verification process
|
|
306
|
+
* @private
|
|
307
|
+
*/
|
|
308
|
+
async verify() {
|
|
309
|
+
this.showLoading();
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const response = await dmvCheck(this.clientId, this.scannerType, this.licenseData);
|
|
313
|
+
|
|
314
|
+
if (response && response.data) {
|
|
315
|
+
this.showResults(response.data);
|
|
316
|
+
} else if (response && response.status === 'error') {
|
|
317
|
+
this.showError(response.message || 'DMV verification service returned an error.');
|
|
318
|
+
} else {
|
|
319
|
+
this.showError('DMV verification service returned an invalid response.');
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error('DMV check error:', error);
|
|
323
|
+
this.showError(error.message || 'Unable to connect to DMV verification service.');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Shows loading spinner while DMV check is in progress
|
|
329
|
+
* @private
|
|
330
|
+
*/
|
|
331
|
+
showLoading() {
|
|
332
|
+
const body = this.modalRoot.querySelector('.verant-dmv-body');
|
|
333
|
+
body.innerHTML = `
|
|
334
|
+
<div class="verant-dmv-loading">
|
|
335
|
+
<div class="verant-dmv-spinner" style="border-top-color: ${this.primaryColor}"></div>
|
|
336
|
+
<p>Verification in progress...</p>
|
|
337
|
+
</div>
|
|
338
|
+
`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Shows DMV verification results
|
|
343
|
+
* @private
|
|
344
|
+
* @param {Object} dmvData - DMV API response data
|
|
345
|
+
*/
|
|
346
|
+
showResults(dmvData) {
|
|
347
|
+
const body = this.modalRoot.querySelector('.verant-dmv-body');
|
|
348
|
+
|
|
349
|
+
const matchStatus = dmvData.match_status === true || dmvData.match_status === 'true';
|
|
350
|
+
|
|
351
|
+
// Build field list in two columns
|
|
352
|
+
let fieldsHTML = '';
|
|
353
|
+
if (dmvData.field_matches && Array.isArray(dmvData.field_matches)) {
|
|
354
|
+
fieldsHTML += '<div class="verant-dmv-results-container">';
|
|
355
|
+
fieldsHTML += '<div class="verant-dmv-results-grid">';
|
|
356
|
+
|
|
357
|
+
// Split fields into two columns
|
|
358
|
+
const halfLength = Math.ceil(dmvData.field_matches.length / 2);
|
|
359
|
+
|
|
360
|
+
// Left column
|
|
361
|
+
fieldsHTML += '<ul class="verant-dmv-fields-column">';
|
|
362
|
+
dmvData.field_matches.slice(0, halfLength).forEach(field => {
|
|
363
|
+
const fieldName = Object.keys(field)[0];
|
|
364
|
+
const matches = field[fieldName] === 'true' || field[fieldName] === true;
|
|
365
|
+
const icon = matches ? '✓' : '✗';
|
|
366
|
+
const label = this.formatFieldLabel(fieldName);
|
|
367
|
+
const value = this.getFieldValue(fieldName);
|
|
368
|
+
const className = matches ? 'match' : 'mismatch';
|
|
369
|
+
fieldsHTML += `
|
|
370
|
+
<li class="verant-dmv-field-item">
|
|
371
|
+
<div class="verant-dmv-field-header">
|
|
372
|
+
<span class="verant-dmv-field-label">${label}</span>
|
|
373
|
+
<span class="verant-dmv-field-icon ${className}">${icon}</span>
|
|
374
|
+
</div>
|
|
375
|
+
<div class="verant-dmv-field-value">${value}</div>
|
|
376
|
+
</li>`;
|
|
377
|
+
});
|
|
378
|
+
fieldsHTML += '</ul>';
|
|
379
|
+
|
|
380
|
+
// Right column
|
|
381
|
+
fieldsHTML += '<ul class="verant-dmv-fields-column">';
|
|
382
|
+
dmvData.field_matches.slice(halfLength).forEach(field => {
|
|
383
|
+
const fieldName = Object.keys(field)[0];
|
|
384
|
+
const matches = field[fieldName] === 'true' || field[fieldName] === true;
|
|
385
|
+
const icon = matches ? '✓' : '✗';
|
|
386
|
+
const label = this.formatFieldLabel(fieldName);
|
|
387
|
+
const value = this.getFieldValue(fieldName);
|
|
388
|
+
const className = matches ? 'match' : 'mismatch';
|
|
389
|
+
fieldsHTML += `
|
|
390
|
+
<li class="verant-dmv-field-item">
|
|
391
|
+
<div class="verant-dmv-field-header">
|
|
392
|
+
<span class="verant-dmv-field-label">${label}</span>
|
|
393
|
+
<span class="verant-dmv-field-icon ${className}">${icon}</span>
|
|
394
|
+
</div>
|
|
395
|
+
<div class="verant-dmv-field-value">${value}</div>
|
|
396
|
+
</li>`;
|
|
397
|
+
});
|
|
398
|
+
fieldsHTML += '</ul>';
|
|
399
|
+
|
|
400
|
+
fieldsHTML += '</div>'; // close grid
|
|
401
|
+
fieldsHTML += '</div>'; // close container
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
body.innerHTML = `
|
|
405
|
+
<div class="verant-dmv-info-box">
|
|
406
|
+
${fieldsHTML}
|
|
407
|
+
</div>
|
|
408
|
+
<div class="verant-dmv-actions">
|
|
409
|
+
<button class="verant-dmv-btn-primary" id="doneBtn" style="background-color: ${this.primaryColor}">Done</button>
|
|
410
|
+
</div>
|
|
411
|
+
`;
|
|
412
|
+
|
|
413
|
+
body.querySelector('#doneBtn').addEventListener('click', () => {
|
|
414
|
+
this.close({
|
|
415
|
+
status: 'verified',
|
|
416
|
+
matchStatus: matchStatus,
|
|
417
|
+
fieldMatches: dmvData.field_matches,
|
|
418
|
+
rawResponse: dmvData
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Shows error message
|
|
425
|
+
* @private
|
|
426
|
+
* @param {string} message - Error message to display
|
|
427
|
+
*/
|
|
428
|
+
showError(message) {
|
|
429
|
+
const body = this.modalRoot.querySelector('.verant-dmv-body');
|
|
430
|
+
body.innerHTML = `
|
|
431
|
+
<div class="verant-dmv-result error">
|
|
432
|
+
<h3>✗ Verification Failed</h3>
|
|
433
|
+
<p>${message}</p>
|
|
434
|
+
</div>
|
|
435
|
+
<div class="verant-dmv-actions">
|
|
436
|
+
<button class="verant-dmv-btn-primary" id="closeBtn" style="background-color: ${this.primaryColor}">Close</button>
|
|
437
|
+
</div>
|
|
438
|
+
`;
|
|
439
|
+
|
|
440
|
+
body.querySelector('#closeBtn').addEventListener('click', () => {
|
|
441
|
+
this.close({ status: 'error', error: message });
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Cancels verification (user clicked Skip or closed modal)
|
|
447
|
+
* @private
|
|
448
|
+
*/
|
|
449
|
+
cancel() {
|
|
450
|
+
this.close({ status: 'skipped' });
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Closes the modal and resolves the promise
|
|
455
|
+
* @private
|
|
456
|
+
* @param {Object} result - Result object to return to caller
|
|
457
|
+
*/
|
|
458
|
+
close(result) {
|
|
459
|
+
document.removeEventListener('keydown', this.handleKeydown);
|
|
460
|
+
if (this.modalRoot) {
|
|
461
|
+
this.modalRoot.remove();
|
|
462
|
+
this.modalRoot = null;
|
|
463
|
+
}
|
|
464
|
+
this.resolve(result);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Gets the value of a field from license data
|
|
469
|
+
* @private
|
|
470
|
+
* @param {string} fieldName - Field name from API response (e.g., 'first_name', 'last_name')
|
|
471
|
+
* @returns {string} Field value from license data
|
|
472
|
+
*/
|
|
473
|
+
getFieldValue(fieldName) {
|
|
474
|
+
const value = getFieldValueFromMapping(this.licenseData, fieldName);
|
|
475
|
+
|
|
476
|
+
if (value === undefined || value === null || value === '') {
|
|
477
|
+
return 'N/A';
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
let displayValue = String(value).trim();
|
|
481
|
+
|
|
482
|
+
// Format date fields to MM/DD/YYYY
|
|
483
|
+
if (fieldName === 'birth_date' || fieldName === 'expiration_date' || fieldName === 'issue_date') {
|
|
484
|
+
displayValue = formatDateToDisplay(displayValue);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return displayValue;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Formats field names for display
|
|
492
|
+
* @private
|
|
493
|
+
* @param {string} fieldName - Field name from API response
|
|
494
|
+
* @returns {string} Formatted label
|
|
495
|
+
*/
|
|
496
|
+
formatFieldLabel(fieldName) {
|
|
497
|
+
return FIELD_LABELS[fieldName] || fieldName.replace(/_/g, ' ');
|
|
498
|
+
}
|
|
499
|
+
}
|