@7365admin1/layer-common 4.35.1-staging.385 → 4.36.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/CHANGELOG.md +10 -4
- package/assets/css/primitives.css +0 -43
- package/assets/css/robot-hero.css +2 -2
- package/components/AccessCardAddForm.vue +4 -5
- package/components/AccessCardAssignToUnitForm.vue +2 -3
- package/components/AccessCardPreviewDialog.vue +3 -4
- package/components/AppSelect.vue +3 -77
- package/components/CameraForm.vue +6 -20
- package/components/DashboardMain.vue +60 -66
- package/components/Dialog/UpdateMoreAction.vue +1 -11
- package/components/DocumentForm.vue +112 -407
- package/components/DocumentManagement.vue +12 -213
- package/components/EntryPass/QrTemplatePreview.vue +25 -21
- package/components/EntryPassInformation.vue +24 -119
- package/components/Facility/BookingSetup.vue +0 -63
- package/components/HidAccessLogDashboard.vue +17 -74
- package/components/HidIntercomManagement.vue +14 -180
- package/components/HidQrCodeConfiguration.vue +195 -17
- package/components/HidReaderManagement.vue +0 -398
- package/components/HidReaderUserRoster.vue +3 -44
- package/components/HidUserEnrollment.vue +155 -845
- package/components/InvitationClientForm.vue +1 -19
- package/components/Layout/NavigationDrawer.vue +1 -4
- package/components/NotificationBell.vue +7 -59
- package/components/Robot/BuildingMap.vue +9 -16
- package/components/Robot/LiftQueue.vue +4 -9
- package/components/Robot/MapEditor.vue +8 -1
- package/components/RolePermissionFormCreate.vue +3 -4
- package/components/RolePermissionFormPreviewUpdate.vue +5 -12
- package/components/RolePermissionMain.vue +0 -1
- package/components/ServiceProviderInvitationPrompt.vue +155 -0
- package/components/ServiceProviderMain.vue +30 -178
- package/components/VisitorForm.vue +0 -12
- package/components/VisitorManagement.vue +10 -285
- package/components/VisitorSocketPopUp.vue +1 -2
- package/composables/useANPRSettings.ts +15 -45
- package/composables/useAccessManagement.ts +0 -5
- package/composables/useCleaningPermission.ts +0 -2
- package/composables/useCommonPermission.ts +0 -32
- package/composables/useDocument.ts +0 -7
- package/composables/useFacility.ts +0 -11
- package/composables/useHidAmico.ts +0 -159
- package/composables/useHidNavigation.ts +1 -25
- package/composables/usePeople.ts +0 -19
- package/composables/usePropertyPermission.ts +0 -19
- package/composables/useRobotBuilding.ts +3 -22
- package/composables/useRobots.ts +0 -13
- package/composables/useSecurityPermission.ts +0 -19
- package/composables/useServiceProvider.ts +0 -3
- package/composables/useServiceProviderInvitation.ts +3 -101
- package/composables/useSiteEntryPassSettings.ts +0 -26
- package/composables/useWebUsb.ts +11 -14
- package/package.json +2 -2
- package/pages/[org]/[site]/access-mgmt/administrator/index.vue +1 -1
- package/pages/[org]/[site]/access-mgmt/hid-cards/index.vue +1 -1
- package/pages/[org]/[site]/access-mgmt/hid-readers/index.vue +1 -1
- package/plugins/API.ts +0 -11
- package/types/document.d.ts +0 -14
- package/types/facility.d.ts +0 -4
- package/types/people.d.ts +1 -28
- package/utils/module-routes.ts +1 -8
- package/utils/nav-sections.ts +2 -8
- package/utils/notification-target.ts +8 -277
- package/utils/permission-labels.ts +2 -20
- package/components/ClientInvitations.vue +0 -291
- package/components/HidAccessPermissions.vue +0 -424
- package/components/HidProfileQrCode.vue +0 -332
- package/composables/useHidReaderSelection.ts +0 -50
- package/composables/useSiteCategory.ts +0 -45
- package/pages/[org]/[site]/access-mgmt/hid-qr-code/index.vue +0 -23
- package/utils/dashboard-widget-prefs.ts +0 -54
- package/utils/hid-permission-assignments.ts +0 -190
- package/utils/hid-reader-selection.ts +0 -52
- package/utils/occupancy-role.ts +0 -109
- package/utils/robot-fleet-core.ts +0 -57
- package/utils/sp-inviter-row.ts +0 -97
|
@@ -21,6 +21,33 @@
|
|
|
21
21
|
<div class="config-body">
|
|
22
22
|
<div class="section-label config-body__title">HID Configuration</div>
|
|
23
23
|
|
|
24
|
+
<div class="form-section identification-section">
|
|
25
|
+
<div class="section-label">Enable/disable reader identification method</div>
|
|
26
|
+
<p class="section-description">
|
|
27
|
+
Toggles multi-factor access methods, allowing users to authenticate via biometric, physical card, or credential inputs.
|
|
28
|
+
</p>
|
|
29
|
+
<div class="methods-grid">
|
|
30
|
+
<div
|
|
31
|
+
v-for="method in identificationMethodOptions"
|
|
32
|
+
:key="method.key"
|
|
33
|
+
class="method-control"
|
|
34
|
+
>
|
|
35
|
+
<span>{{ method.label }}</span>
|
|
36
|
+
<v-switch
|
|
37
|
+
v-model="form.identificationMethods[method.key]"
|
|
38
|
+
class="app-switch"
|
|
39
|
+
hide-details
|
|
40
|
+
density="compact"
|
|
41
|
+
:loading="savingSection === `method-${method.key}`"
|
|
42
|
+
:disabled="Boolean(savingSection) || !form.readerId"
|
|
43
|
+
@update:model-value="saveIdentificationMethod(method.key)"
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<v-divider />
|
|
50
|
+
|
|
24
51
|
<div class="form-section">
|
|
25
52
|
<div class="section-label">Printer Setup</div>
|
|
26
53
|
<div class="printer-grid">
|
|
@@ -197,11 +224,14 @@
|
|
|
197
224
|
|
|
198
225
|
|
|
199
226
|
<script setup lang="ts">
|
|
227
|
+
type HidIdentificationMethod = keyof NonNullable<THidQrCodePassConfig["identificationMethods"]>;
|
|
228
|
+
|
|
200
229
|
type HidQrConfig = Omit<
|
|
201
230
|
THidQrCodePassConfig,
|
|
202
231
|
"identificationMethods" | "onlineMode" | "updatedAt" | "qrGatePermissions"
|
|
203
232
|
> & {
|
|
204
233
|
onlineMode: boolean;
|
|
234
|
+
identificationMethods: Record<HidIdentificationMethod, boolean>;
|
|
205
235
|
qrGatePermissions: {
|
|
206
236
|
resident: string[];
|
|
207
237
|
propertyManagement: string[];
|
|
@@ -235,7 +265,11 @@ const runtimeConfig = useRuntimeConfig();
|
|
|
235
265
|
const isPermissionManager = computed(() => runtimeConfig.public.APP === "security_agency");
|
|
236
266
|
|
|
237
267
|
const { getSiteById, updateSitebyId } = useSiteSettings();
|
|
238
|
-
const {
|
|
268
|
+
const {
|
|
269
|
+
getReaders,
|
|
270
|
+
getReaderConfiguration,
|
|
271
|
+
setReaderConfiguration,
|
|
272
|
+
} = useHidAmico();
|
|
239
273
|
const {
|
|
240
274
|
requestDevice: requestPrinterDevice,
|
|
241
275
|
testConnection: testPrinterConnection,
|
|
@@ -250,6 +284,14 @@ const defaultConfig: HidQrConfig = {
|
|
|
250
284
|
propertyManagement: [],
|
|
251
285
|
},
|
|
252
286
|
qrFormat: "0",
|
|
287
|
+
identificationMethods: {
|
|
288
|
+
facial: true,
|
|
289
|
+
card: true,
|
|
290
|
+
qrCode: false,
|
|
291
|
+
idPassword: true,
|
|
292
|
+
pin: false,
|
|
293
|
+
bluetooth: false,
|
|
294
|
+
},
|
|
253
295
|
printer: {
|
|
254
296
|
vendorId: "",
|
|
255
297
|
productId: "",
|
|
@@ -279,6 +321,16 @@ const visitorQrGateOptions = computed(() => readers.value
|
|
|
279
321
|
title: String(reader.name || reader.deviceId || reader._id) + " - " + String(reader.portalName || "Portal"),
|
|
280
322
|
value: reader._id,
|
|
281
323
|
})));
|
|
324
|
+
const identificationMethodOptions: Array<{
|
|
325
|
+
key: HidIdentificationMethod;
|
|
326
|
+
label: string;
|
|
327
|
+
}> = [
|
|
328
|
+
{ key: "facial", label: "Facial" },
|
|
329
|
+
{ key: "card", label: "Card" },
|
|
330
|
+
{ key: "qrCode", label: "QR Code" },
|
|
331
|
+
{ key: "idPassword", label: "ID Password" },
|
|
332
|
+
{ key: "pin", label: "PIN" },
|
|
333
|
+
];
|
|
282
334
|
const siteName = computed(() => siteData.value?.name || "Site");
|
|
283
335
|
const siteLabel = computed(() => siteData.value?.code || siteData.value?.block || props.site);
|
|
284
336
|
const primaryQrGateReaderId = computed(() => (
|
|
@@ -319,6 +371,7 @@ async function loadConfig() {
|
|
|
319
371
|
const readerList = readerResponse as HidReaderListResponse;
|
|
320
372
|
readers.value = readerList.items ?? readerList.data?.items ?? readerList.data?.readers ?? [];
|
|
321
373
|
const config = siteData.value?.metadata?.hidQrCodePass || {};
|
|
374
|
+
const configuredMethods = config.identificationMethods;
|
|
322
375
|
Object.assign(form, {
|
|
323
376
|
enabled: Boolean(config.enabled),
|
|
324
377
|
// Visitor QR is reader-local. Do not restore a legacy Online Mode value
|
|
@@ -339,6 +392,14 @@ async function loadConfig() {
|
|
|
339
392
|
|| "",
|
|
340
393
|
),
|
|
341
394
|
qrFormat: "0",
|
|
395
|
+
identificationMethods: {
|
|
396
|
+
facial: configuredMethods?.facial ?? true,
|
|
397
|
+
card: configuredMethods?.card ?? true,
|
|
398
|
+
qrCode: configuredMethods?.qrCode ?? Boolean(config.enabled),
|
|
399
|
+
idPassword: configuredMethods?.idPassword ?? true,
|
|
400
|
+
pin: configuredMethods?.pin ?? false,
|
|
401
|
+
bluetooth: false,
|
|
402
|
+
},
|
|
342
403
|
printer: {
|
|
343
404
|
vendorId: String(config.printer?.vendorId || ""),
|
|
344
405
|
productId: String(config.printer?.productId || ""),
|
|
@@ -349,25 +410,57 @@ async function loadConfig() {
|
|
|
349
410
|
},
|
|
350
411
|
validityMinutes: config.validityMinutes ? Number(config.validityMinutes) : null,
|
|
351
412
|
});
|
|
413
|
+
|
|
414
|
+
await loadReaderConfiguration();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function isUnknownRecord(value: unknown): value is Record<string, unknown> {
|
|
418
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function getConfigurationData(response: unknown) {
|
|
422
|
+
if (!isUnknownRecord(response)) return {};
|
|
423
|
+
return isUnknownRecord(response.data) ? response.data : response;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function isEnabledConfiguration(value: unknown) {
|
|
427
|
+
return value === true || value === 1 || value === "1";
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function loadReaderConfiguration() {
|
|
431
|
+
if (!form.readerId) return;
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
const response = await getReaderConfiguration(form.readerId, {
|
|
435
|
+
identifier: [
|
|
436
|
+
"card_identification_enabled",
|
|
437
|
+
"pin_identification_enabled",
|
|
438
|
+
"multi_factor_authentication",
|
|
439
|
+
],
|
|
440
|
+
});
|
|
441
|
+
const configuration = getConfigurationData(response);
|
|
442
|
+
const identifier = isUnknownRecord(configuration.identifier) ? configuration.identifier : {};
|
|
443
|
+
|
|
444
|
+
if (identifier.pin_identification_enabled !== undefined) {
|
|
445
|
+
const pinEnabled = isEnabledConfiguration(identifier.pin_identification_enabled);
|
|
446
|
+
form.identificationMethods.pin = pinEnabled;
|
|
447
|
+
form.identificationMethods.idPassword = !pinEnabled;
|
|
448
|
+
}
|
|
449
|
+
if (identifier.card_identification_enabled !== undefined) {
|
|
450
|
+
form.identificationMethods.card = isEnabledConfiguration(
|
|
451
|
+
identifier.card_identification_enabled,
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
} catch (error: unknown) {
|
|
455
|
+
console.warn("Unable to load HID reader configuration:", errorConverter(error));
|
|
456
|
+
}
|
|
352
457
|
}
|
|
353
458
|
|
|
354
459
|
function buildPayload() {
|
|
355
|
-
/*
|
|
356
|
-
* `enabled` and `identificationMethods` are passed through from what was
|
|
357
|
-
* loaded, never recomputed.
|
|
358
|
-
*
|
|
359
|
-
* Which credentials a reader accepts now lives on the READER record and is
|
|
360
|
-
* set from HID Readers > Identification Methods, because the device is what
|
|
361
|
-
* holds them. The toggles that used to derive these two values are gone from
|
|
362
|
-
* this screen, so deriving them again from a form that no longer has them
|
|
363
|
-
* would erase the stored copy and flip `enabled` every time somebody saved
|
|
364
|
-
* the printer settings.
|
|
365
|
-
*/
|
|
366
|
-
const storedQrPass = siteData.value?.metadata?.hidQrCodePass || {};
|
|
367
460
|
const metadata = {
|
|
368
461
|
...(siteData.value?.metadata || {}),
|
|
369
462
|
hidQrCodePass: {
|
|
370
|
-
enabled:
|
|
463
|
+
enabled: form.identificationMethods.qrCode,
|
|
371
464
|
onlineMode: false,
|
|
372
465
|
// Kept only for old background/mobile flows. Gate permissions, not this
|
|
373
466
|
// compatibility value, decide where a new Visitor QR may be scanned.
|
|
@@ -378,9 +471,7 @@ function buildPayload() {
|
|
|
378
471
|
},
|
|
379
472
|
// Visitor QR passes always use Amico alphanumeric qrcodes.
|
|
380
473
|
qrFormat: "0",
|
|
381
|
-
...
|
|
382
|
-
? { identificationMethods: storedQrPass.identificationMethods }
|
|
383
|
-
: {}),
|
|
474
|
+
identificationMethods: { ...form.identificationMethods },
|
|
384
475
|
printer: {
|
|
385
476
|
vendorId: form.printer.vendorId.trim(),
|
|
386
477
|
productId: form.printer.productId.trim(),
|
|
@@ -416,6 +507,43 @@ async function saveSection(section: string) {
|
|
|
416
507
|
}
|
|
417
508
|
}
|
|
418
509
|
|
|
510
|
+
async function saveIdentificationMethod(method: HidIdentificationMethod) {
|
|
511
|
+
if (!props.site) return;
|
|
512
|
+
|
|
513
|
+
form.enabled = form.identificationMethods.qrCode;
|
|
514
|
+
|
|
515
|
+
savingSection.value = `method-${method}`;
|
|
516
|
+
try {
|
|
517
|
+
if (method === "card") {
|
|
518
|
+
await setReaderConfiguration(form.readerId, {
|
|
519
|
+
identifier: {
|
|
520
|
+
card_identification_enabled: form.identificationMethods.card ? "1" : "0",
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
} else if (method === "pin" || method === "idPassword") {
|
|
524
|
+
if (method === "pin" && form.identificationMethods.pin) {
|
|
525
|
+
form.identificationMethods.idPassword = false;
|
|
526
|
+
} else if (method === "idPassword" && form.identificationMethods.idPassword) {
|
|
527
|
+
form.identificationMethods.pin = false;
|
|
528
|
+
}
|
|
529
|
+
await setReaderConfiguration(form.readerId, {
|
|
530
|
+
identifier: {
|
|
531
|
+
pin_identification_enabled: form.identificationMethods.pin ? "1" : "0",
|
|
532
|
+
// Mode 1: PIN is an alternative credential, never a second factor.
|
|
533
|
+
multi_factor_authentication: "0",
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
await updateSitebyId(props.site, buildPayload());
|
|
538
|
+
showMessage(`${identificationMethodOptions.find((item) => item.key === method)?.label || "Identification method"} updated.`, "success");
|
|
539
|
+
} catch (error: unknown) {
|
|
540
|
+
await loadConfig();
|
|
541
|
+
showMessage(errorConverter(error), "error");
|
|
542
|
+
} finally {
|
|
543
|
+
savingSection.value = "";
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
419
547
|
async function scanPrinter() {
|
|
420
548
|
if (!("usb" in navigator)) {
|
|
421
549
|
showMessage("USB printer scan is not available in this browser.", "error");
|
|
@@ -523,6 +651,51 @@ function showMessage(text: string, color: string) {
|
|
|
523
651
|
padding: 16px 0;
|
|
524
652
|
}
|
|
525
653
|
|
|
654
|
+
.identification-section {
|
|
655
|
+
padding-bottom: 20px;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.methods-grid {
|
|
659
|
+
display: grid;
|
|
660
|
+
grid-template-columns: repeat(6, minmax(120px, 1fr));
|
|
661
|
+
gap: 18px;
|
|
662
|
+
margin-top: 16px;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
.method-control {
|
|
666
|
+
display: flex;
|
|
667
|
+
align-items: center;
|
|
668
|
+
justify-content: space-between;
|
|
669
|
+
min-width: 0;
|
|
670
|
+
min-height: 42px;
|
|
671
|
+
padding: 0 10px 0 14px;
|
|
672
|
+
border: 1px solid var(--border);
|
|
673
|
+
border-radius: var(--r-inner);
|
|
674
|
+
color: var(--text2);
|
|
675
|
+
font-family: var(--font-sans);
|
|
676
|
+
font-size: 12px;
|
|
677
|
+
font-weight: 700;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.method-control span {
|
|
681
|
+
overflow: hidden;
|
|
682
|
+
text-overflow: ellipsis;
|
|
683
|
+
white-space: nowrap;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.method-control--disabled {
|
|
687
|
+
background-color: var(--hover);
|
|
688
|
+
color: var(--muted);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.method-control :deep(.v-switch) {
|
|
692
|
+
flex: 0 0 auto;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.method-control :deep(.v-selection-control) {
|
|
696
|
+
min-height: 32px;
|
|
697
|
+
}
|
|
698
|
+
|
|
526
699
|
/* The three `.hid-config-switch` rules are gone: `.app-switch` in
|
|
527
700
|
primitives.css is the handoff's toggle, and it is shared with the other two
|
|
528
701
|
HID panels that have one. */
|
|
@@ -694,6 +867,10 @@ function showMessage(text: string, color: string) {
|
|
|
694
867
|
}
|
|
695
868
|
|
|
696
869
|
@media (max-width: 1100px) {
|
|
870
|
+
.methods-grid {
|
|
871
|
+
grid-template-columns: repeat(3, minmax(140px, 1fr));
|
|
872
|
+
}
|
|
873
|
+
|
|
697
874
|
.printer-grid,
|
|
698
875
|
.reader-grid,
|
|
699
876
|
.template-grid,
|
|
@@ -714,6 +891,7 @@ function showMessage(text: string, color: string) {
|
|
|
714
891
|
.reader-grid,
|
|
715
892
|
.template-grid,
|
|
716
893
|
.validity-grid,
|
|
894
|
+
.methods-grid,
|
|
717
895
|
.permission-location,
|
|
718
896
|
.permission-row,
|
|
719
897
|
.permission-actions {
|