@7365admin1/layer-common 4.96.0 → 4.97.1-staging.500

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +10 -4
  2. package/assets/css/primitives.css +57 -0
  3. package/assets/css/screens.css +8 -4
  4. package/components/AccessCardDetailsDialog.vue +59 -6
  5. package/components/AccessCardPreviewDialog.vue +371 -53
  6. package/components/AppSelect.vue +143 -4
  7. package/components/BuildingUnitFormEdit.vue +6 -0
  8. package/components/BuildingUnitManagement.vue +298 -0
  9. package/components/Dialog/UpdateMoreAction.vue +11 -1
  10. package/components/DocumentForm.vue +407 -112
  11. package/components/DocumentManagement.vue +213 -12
  12. package/components/Facility/BookingSetup.vue +63 -0
  13. package/components/HidAccessLogDashboard.vue +74 -17
  14. package/components/HidAccessPermissions.vue +424 -0
  15. package/components/HidIntercomManagement.vue +183 -14
  16. package/components/HidProfileQrCode.vue +332 -0
  17. package/components/HidQrCodeConfiguration.vue +17 -195
  18. package/components/HidReaderManagement.vue +398 -0
  19. package/components/HidReaderUserRoster.vue +44 -3
  20. package/components/HidUserEnrollment.vue +2239 -276
  21. package/components/InvitationClientForm.vue +19 -1
  22. package/components/Nfc/NFCPatrolRouteForm.vue +109 -58
  23. package/components/Nfc/NFCPatrolRouteMain.vue +3 -18
  24. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +0 -8
  25. package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +1 -1
  26. package/components/ServiceProviderMain.vue +48 -0
  27. package/components/SiteProviderChange.vue +371 -0
  28. package/components/TableMain.vue +22 -7
  29. package/components/VehicleQrStickerDialog.vue +64 -4
  30. package/components/VisitorForm.vue +12 -0
  31. package/components/VisitorManagement.vue +267 -5
  32. package/composables/useAccessManagement.ts +17 -1
  33. package/composables/useCustomerSite.ts +29 -0
  34. package/composables/useDocument.ts +7 -0
  35. package/composables/useFacility.ts +11 -0
  36. package/composables/useHidAmico.ts +165 -0
  37. package/composables/useHidNavigation.ts +25 -1
  38. package/composables/useHidReaderSelection.ts +50 -0
  39. package/composables/useMember.ts +12 -0
  40. package/composables/useNFCPatrolReport.ts +36 -32
  41. package/composables/useNFCPatrolReportFilters.ts +21 -3
  42. package/composables/useNFCPatrolRoute.ts +10 -3
  43. package/composables/usePeople.ts +19 -0
  44. package/composables/useServiceProvider.ts +45 -2
  45. package/composables/useSiteCategory.ts +45 -0
  46. package/package.json +1 -1
  47. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +1 -1
  48. package/pages/[org]/[site]/access-mgmt/hid-cards/index.vue +1 -1
  49. package/pages/[org]/[site]/access-mgmt/hid-qr-code/index.vue +23 -0
  50. package/pages/[org]/[site]/access-mgmt/hid-readers/index.vue +1 -1
  51. package/types/document.d.ts +14 -0
  52. package/types/facility.d.ts +4 -0
  53. package/types/member.d.ts +4 -0
  54. package/types/nfc-patrol-report.ts +0 -2
  55. package/types/people.d.ts +28 -1
  56. package/types/service-provider.d.ts +5 -0
  57. package/types/site.d.ts +7 -1
  58. package/utils/hid-enrolment-subject.ts +344 -0
  59. package/utils/hid-permission-assignments.ts +198 -0
  60. package/utils/hid-reader-selection.ts +52 -0
  61. package/utils/nfc-patrol-time.ts +99 -0
  62. package/utils/occupancy-role.ts +109 -0
  63. package/utils/service-type.ts +30 -0
  64. package/utils/vehicle-qr-design.ts +304 -20
  65. package/utils/vehicle-qr-sticker.ts +126 -20
@@ -21,33 +21,6 @@
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
-
51
24
  <div class="form-section">
52
25
  <div class="section-label">Printer Setup</div>
53
26
  <div class="printer-grid">
@@ -224,14 +197,11 @@
224
197
 
225
198
 
226
199
  <script setup lang="ts">
227
- type HidIdentificationMethod = keyof NonNullable<THidQrCodePassConfig["identificationMethods"]>;
228
-
229
200
  type HidQrConfig = Omit<
230
201
  THidQrCodePassConfig,
231
202
  "identificationMethods" | "onlineMode" | "updatedAt" | "qrGatePermissions"
232
203
  > & {
233
204
  onlineMode: boolean;
234
- identificationMethods: Record<HidIdentificationMethod, boolean>;
235
205
  qrGatePermissions: {
236
206
  resident: string[];
237
207
  propertyManagement: string[];
@@ -265,11 +235,7 @@ const runtimeConfig = useRuntimeConfig();
265
235
  const isPermissionManager = computed(() => runtimeConfig.public.APP === "security_agency");
266
236
 
267
237
  const { getSiteById, updateSitebyId } = useSiteSettings();
268
- const {
269
- getReaders,
270
- getReaderConfiguration,
271
- setReaderConfiguration,
272
- } = useHidAmico();
238
+ const { getReaders } = useHidAmico();
273
239
  const {
274
240
  requestDevice: requestPrinterDevice,
275
241
  testConnection: testPrinterConnection,
@@ -284,14 +250,6 @@ const defaultConfig: HidQrConfig = {
284
250
  propertyManagement: [],
285
251
  },
286
252
  qrFormat: "0",
287
- identificationMethods: {
288
- facial: true,
289
- card: true,
290
- qrCode: false,
291
- idPassword: true,
292
- pin: false,
293
- bluetooth: false,
294
- },
295
253
  printer: {
296
254
  vendorId: "",
297
255
  productId: "",
@@ -321,16 +279,6 @@ const visitorQrGateOptions = computed(() => readers.value
321
279
  title: String(reader.name || reader.deviceId || reader._id) + " - " + String(reader.portalName || "Portal"),
322
280
  value: reader._id,
323
281
  })));
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
- ];
334
282
  const siteName = computed(() => siteData.value?.name || "Site");
335
283
  const siteLabel = computed(() => siteData.value?.code || siteData.value?.block || props.site);
336
284
  const primaryQrGateReaderId = computed(() => (
@@ -371,7 +319,6 @@ async function loadConfig() {
371
319
  const readerList = readerResponse as HidReaderListResponse;
372
320
  readers.value = readerList.items ?? readerList.data?.items ?? readerList.data?.readers ?? [];
373
321
  const config = siteData.value?.metadata?.hidQrCodePass || {};
374
- const configuredMethods = config.identificationMethods;
375
322
  Object.assign(form, {
376
323
  enabled: Boolean(config.enabled),
377
324
  // Visitor QR is reader-local. Do not restore a legacy Online Mode value
@@ -392,14 +339,6 @@ async function loadConfig() {
392
339
  || "",
393
340
  ),
394
341
  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
- },
403
342
  printer: {
404
343
  vendorId: String(config.printer?.vendorId || ""),
405
344
  productId: String(config.printer?.productId || ""),
@@ -410,57 +349,25 @@ async function loadConfig() {
410
349
  },
411
350
  validityMinutes: config.validityMinutes ? Number(config.validityMinutes) : null,
412
351
  });
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
- }
457
352
  }
458
353
 
459
354
  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 || {};
460
367
  const metadata = {
461
368
  ...(siteData.value?.metadata || {}),
462
369
  hidQrCodePass: {
463
- enabled: form.identificationMethods.qrCode,
370
+ enabled: Boolean(storedQrPass.enabled),
464
371
  onlineMode: false,
465
372
  // Kept only for old background/mobile flows. Gate permissions, not this
466
373
  // compatibility value, decide where a new Visitor QR may be scanned.
@@ -471,7 +378,9 @@ function buildPayload() {
471
378
  },
472
379
  // Visitor QR passes always use Amico alphanumeric qrcodes.
473
380
  qrFormat: "0",
474
- identificationMethods: { ...form.identificationMethods },
381
+ ...(storedQrPass.identificationMethods
382
+ ? { identificationMethods: storedQrPass.identificationMethods }
383
+ : {}),
475
384
  printer: {
476
385
  vendorId: form.printer.vendorId.trim(),
477
386
  productId: form.printer.productId.trim(),
@@ -507,43 +416,6 @@ async function saveSection(section: string) {
507
416
  }
508
417
  }
509
418
 
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
-
547
419
  async function scanPrinter() {
548
420
  if (!("usb" in navigator)) {
549
421
  showMessage("USB printer scan is not available in this browser.", "error");
@@ -651,51 +523,6 @@ function showMessage(text: string, color: string) {
651
523
  padding: 16px 0;
652
524
  }
653
525
 
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
-
699
526
  /* The three `.hid-config-switch` rules are gone: `.app-switch` in
700
527
  primitives.css is the handoff's toggle, and it is shared with the other two
701
528
  HID panels that have one. */
@@ -867,10 +694,6 @@ function showMessage(text: string, color: string) {
867
694
  }
868
695
 
869
696
  @media (max-width: 1100px) {
870
- .methods-grid {
871
- grid-template-columns: repeat(3, minmax(140px, 1fr));
872
- }
873
-
874
697
  .printer-grid,
875
698
  .reader-grid,
876
699
  .template-grid,
@@ -891,7 +714,6 @@ function showMessage(text: string, color: string) {
891
714
  .reader-grid,
892
715
  .template-grid,
893
716
  .validity-grid,
894
- .methods-grid,
895
717
  .permission-location,
896
718
  .permission-row,
897
719
  .permission-actions {