@7365admin1/layer-common 3.2.1-staging.62 → 3.2.1-staging.64

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.
@@ -50,7 +50,13 @@
50
50
 
51
51
  <v-card flat border class="table-card">
52
52
  <div class="table-refresh">
53
- <v-btn icon="mdi-refresh" variant="text" density="comfortable" :loading="loading" @click="loadUsers" />
53
+ <v-btn
54
+ icon="mdi-refresh"
55
+ variant="text"
56
+ density="comfortable"
57
+ :loading="loading"
58
+ @click="loadUsers"
59
+ />
54
60
  <div class="page-count">{{ pageRange }}</div>
55
61
  </div>
56
62
 
@@ -66,10 +72,18 @@
66
72
  </tr>
67
73
  </thead>
68
74
  <tbody>
69
- <tr v-for="user in users" :key="user._rowKey || user._id || user.hidUserId">
75
+ <tr
76
+ v-for="user in users"
77
+ :key="user._rowKey || user._id || user.hidUserId"
78
+ >
70
79
  <td>{{ getName(user) }}</td>
71
80
  <td>
72
- <v-avatar v-if="getUserImageSrc(user)" size="34" rounded="lg" class="hid-face-avatar">
81
+ <v-avatar
82
+ v-if="getUserImageSrc(user)"
83
+ size="34"
84
+ rounded="lg"
85
+ class="hid-face-avatar"
86
+ >
73
87
  <v-img :src="getUserImageSrc(user)" cover />
74
88
  </v-avatar>
75
89
  <span v-else>{{ getFacialData(user) }}</span>
@@ -88,13 +102,22 @@
88
102
  <td class="text-right">
89
103
  <v-menu>
90
104
  <template #activator="{ props: menuProps }">
91
- <v-btn v-bind="menuProps" icon="mdi-dots-vertical" variant="text" density="comfortable" />
105
+ <v-btn
106
+ v-bind="menuProps"
107
+ icon="mdi-dots-vertical"
108
+ variant="text"
109
+ density="comfortable"
110
+ />
92
111
  </template>
93
112
 
94
113
  <v-list density="compact" min-width="150">
95
114
  <v-list-item title="View" @click="openView(user)" />
96
115
  <v-list-item title="Edit" @click="openEdit(user)" />
97
- <v-list-item title="Delete" class="text-error" @click="openDelete(user)" />
116
+ <v-list-item
117
+ title="Delete"
118
+ class="text-error"
119
+ @click="openDelete(user)"
120
+ />
98
121
  </v-list>
99
122
  </v-menu>
100
123
  </td>
@@ -109,8 +132,26 @@
109
132
 
110
133
  <div class="table-footer">
111
134
  <span>{{ pageRange }}</span>
112
- <v-btn icon="mdi-chevron-left" variant="text" density="comfortable" :disabled="page <= 1" @click="page--; loadUsers()" />
113
- <v-btn icon="mdi-chevron-right" variant="text" density="comfortable" :disabled="page >= pages" @click="page++; loadUsers()" />
135
+ <v-btn
136
+ icon="mdi-chevron-left"
137
+ variant="text"
138
+ density="comfortable"
139
+ :disabled="page <= 1"
140
+ @click="
141
+ page--;
142
+ loadUsers();
143
+ "
144
+ />
145
+ <v-btn
146
+ icon="mdi-chevron-right"
147
+ variant="text"
148
+ density="comfortable"
149
+ :disabled="page >= pages"
150
+ @click="
151
+ page++;
152
+ loadUsers();
153
+ "
154
+ />
114
155
  </div>
115
156
  </v-card>
116
157
 
@@ -125,7 +166,12 @@
125
166
  <v-menu>
126
167
  <template #activator="{ props: menuProps }">
127
168
  <button v-bind="menuProps" class="photo-button" type="button">
128
- <v-img v-if="form.photoPreview" :src="form.photoPreview" cover class="photo-preview" />
169
+ <v-img
170
+ v-if="form.photoPreview"
171
+ :src="form.photoPreview"
172
+ cover
173
+ class="photo-preview"
174
+ />
129
175
  <span v-if="form.photoPreview" class="photo-edit-icon">
130
176
  <v-icon icon="mdi-pencil" size="16" />
131
177
  </span>
@@ -137,7 +183,10 @@
137
183
  </template>
138
184
  <v-list density="compact" min-width="190">
139
185
  <v-list-item title="Browse" @click="triggerPhotoInput" />
140
- <v-list-item title="Take Photo using Camera" @click="triggerCameraInput" />
186
+ <v-list-item
187
+ title="Take Photo using Camera"
188
+ @click="triggerCameraInput"
189
+ />
141
190
  <v-list-item
142
191
  v-if="form.photoPreview"
143
192
  title="Remove Photo"
@@ -146,12 +195,10 @@
146
195
  />
147
196
  </v-list>
148
197
  </v-menu>
149
- <input ref="photoInput" type="file" accept="image/*" class="hidden-input" @change="onPhotoChange" />
150
198
  <input
151
- ref="cameraInput"
199
+ ref="photoInput"
152
200
  type="file"
153
201
  accept="image/*"
154
- capture="user"
155
202
  class="hidden-input"
156
203
  @change="onPhotoChange"
157
204
  />
@@ -217,31 +264,61 @@
217
264
  </v-card-text>
218
265
 
219
266
  <v-card-actions class="pa-0">
220
- <v-btn class="text-none action-cancel" variant="flat" height="44" @click="formDialog = false">Cancel</v-btn>
221
- <v-btn class="text-none action-submit" variant="flat" height="44" :loading="saving" @click="saveUser">
267
+ <v-btn
268
+ class="text-none action-cancel"
269
+ variant="flat"
270
+ height="44"
271
+ @click="formDialog = false"
272
+ >Cancel</v-btn
273
+ >
274
+ <v-btn
275
+ class="text-none action-submit"
276
+ variant="flat"
277
+ height="44"
278
+ :loading="saving"
279
+ @click="saveUser"
280
+ >
222
281
  Save
223
282
  </v-btn>
224
283
  </v-card-actions>
225
284
  </v-card>
226
285
  </v-dialog>
227
286
 
287
+ <HidCameraCaptureDialog v-model="cameraDialog" @capture="onCameraCapture" />
288
+
228
289
  <v-dialog v-model="viewDialog" max-width="430">
229
290
  <v-card rounded="lg">
230
291
  <v-card-title class="small-title">User Information</v-card-title>
231
292
  <v-card-text>
232
293
  <div class="detail-grid">
233
294
  <span>Name</span><strong>{{ getName(selectedUser) }}</strong>
234
- <span>User ID</span><strong>{{ selectedUser ? formatHidUid(selectedUser.hidUserId) : "N/A" }}</strong>
235
- <span>Registration No.</span><strong>{{ selectedUser?.registration || "N/A" }}</strong>
236
- <span>Last access time</span><strong>{{ formatDate(selectedUser?.metadata?.lastAccessAt) }}</strong>
237
- <span>Status</span><strong>{{ selectedUser ? getMappingStatus(selectedUser) : "N/A" }}</strong>
238
- <span>Panic Pin</span><strong>{{ selectedUser?.cardNo || "N/A" }}</strong>
239
- <span>Private Password</span><strong>{{ selectedUser?.metadata?.pinPassword ? "********" : "N/A" }}</strong>
295
+ <span>User ID</span
296
+ ><strong>{{
297
+ selectedUser ? formatHidUid(selectedUser.hidUserId) : "N/A"
298
+ }}</strong>
299
+ <span>Registration No.</span
300
+ ><strong>{{ selectedUser?.registration || "N/A" }}</strong>
301
+ <span>Last access time</span
302
+ ><strong>{{
303
+ formatDate(selectedUser?.metadata?.lastAccessAt)
304
+ }}</strong>
305
+ <span>Status</span
306
+ ><strong>{{
307
+ selectedUser ? getMappingStatus(selectedUser) : "N/A"
308
+ }}</strong>
309
+ <span>Panic Pin</span
310
+ ><strong>{{ selectedUser?.cardNo || "N/A" }}</strong>
311
+ <span>Private Password</span
312
+ ><strong>{{
313
+ selectedUser?.metadata?.pinPassword ? "********" : "N/A"
314
+ }}</strong>
240
315
  </div>
241
316
  </v-card-text>
242
317
  <v-card-actions>
243
318
  <v-spacer />
244
- <v-btn class="text-none" variant="text" @click="viewDialog = false">Close</v-btn>
319
+ <v-btn class="text-none" variant="text" @click="viewDialog = false"
320
+ >Close</v-btn
321
+ >
245
322
  </v-card-actions>
246
323
  </v-card>
247
324
  </v-dialog>
@@ -252,12 +329,26 @@
252
329
  <v-card-text class="text-body-2">
253
330
  Are you sure you want to permanently delete this HID user?
254
331
  <div class="text-caption text-medium-emphasis mt-2">
255
- This action will remove the user from the HID reader and mark the identity as deleted.
332
+ This action will remove the user from the HID reader and mark the
333
+ identity as deleted.
256
334
  </div>
257
335
  </v-card-text>
258
336
  <v-card-actions class="pa-0">
259
- <v-btn class="text-none action-cancel" variant="flat" height="44" @click="deleteDialog = false">Cancel</v-btn>
260
- <v-btn class="text-none action-submit" variant="flat" height="44" :loading="deleting" @click="deleteUser">Delete</v-btn>
337
+ <v-btn
338
+ class="text-none action-cancel"
339
+ variant="flat"
340
+ height="44"
341
+ @click="deleteDialog = false"
342
+ >Cancel</v-btn
343
+ >
344
+ <v-btn
345
+ class="text-none action-submit"
346
+ variant="flat"
347
+ height="44"
348
+ :loading="deleting"
349
+ @click="deleteUser"
350
+ >Delete</v-btn
351
+ >
261
352
  </v-card-actions>
262
353
  </v-card>
263
354
  </v-dialog>
@@ -312,7 +403,7 @@ const deleteDialog = ref(false);
312
403
  const selectedUser = ref<Record<string, any> | null>(null);
313
404
  const showPassword = ref(false);
314
405
  const photoInput = ref<HTMLInputElement | null>(null);
315
- const cameraInput = ref<HTMLInputElement | null>(null);
406
+ const cameraDialog = ref(false);
316
407
  const selectedPhotoFile = ref<File | null>(null);
317
408
  const removePhotoRequested = ref(false);
318
409
  const snackbar = reactive({
@@ -340,7 +431,7 @@ const readerOptions = computed(() =>
340
431
  readers.value.map((reader) => ({
341
432
  title: reader.name || reader.deviceId || reader._id,
342
433
  value: reader._id,
343
- })),
434
+ }))
344
435
  );
345
436
 
346
437
  const pageRange = computed(() => {
@@ -354,7 +445,7 @@ const pageRange = computed(() => {
354
445
  watch(
355
446
  () => props.site,
356
447
  () => init(),
357
- { immediate: true },
448
+ { immediate: true }
358
449
  );
359
450
 
360
451
  async function init() {
@@ -365,7 +456,8 @@ async function init() {
365
456
  async function loadReaders() {
366
457
  if (!props.site) return;
367
458
  const response = await getReaders({ site: props.site, page: 1, limit: 100 });
368
- readers.value = response?.items ?? response?.data?.items ?? response?.data?.readers ?? [];
459
+ readers.value =
460
+ response?.items ?? response?.data?.items ?? response?.data?.readers ?? [];
369
461
  if (!selectedReaderId.value && readers.value.length) {
370
462
  selectedReaderId.value = readers.value[0]._id;
371
463
  }
@@ -394,10 +486,16 @@ async function loadUsers() {
394
486
  limit: 500,
395
487
  offset: 0,
396
488
  }),
397
- isAdministratorIdentity() ? loadAdministratorRoles(selectedReaderId.value) : Promise.resolve(null),
489
+ isAdministratorIdentity()
490
+ ? loadAdministratorRoles(selectedReaderId.value)
491
+ : Promise.resolve(null),
398
492
  ]);
399
493
 
400
- const identities = identityResponse?.items ?? identityResponse?.data?.items ?? identityResponse?.data?.identities ?? [];
494
+ const identities =
495
+ identityResponse?.items ??
496
+ identityResponse?.data?.items ??
497
+ identityResponse?.data?.identities ??
498
+ [];
401
499
  const adminRoleIds = getAdministratorRoleUserIds(roleResponse);
402
500
  const readerUsers = normalizeHidUsers(readerResponse).filter((user) => {
403
501
  if (Number(user.userTypeId) === 1) return false;
@@ -429,21 +527,23 @@ async function loadUsers() {
429
527
  async function loadUserImages(items: Record<string, any>[]) {
430
528
  if (!selectedReaderId.value) return;
431
529
 
432
- await Promise.all(items.map(async (user) => {
433
- const hidUserId = toHidNumericId(user.hidUserId);
434
- const key = String(hidUserId || "");
435
- if (!hidUserId || userImages.value[key] || user?.metadata?.photo) return;
436
-
437
- try {
438
- const response = await getUserImage(selectedReaderId.value, hidUserId);
439
- const image = response?.data || response;
440
- userImages.value[key] = image?.base64
441
- ? `data:${image.contentType || "image/jpeg"};base64,${image.base64}`
442
- : "";
443
- } catch {
444
- userImages.value[key] = "";
445
- }
446
- }));
530
+ await Promise.all(
531
+ items.map(async (user) => {
532
+ const hidUserId = toHidNumericId(user.hidUserId);
533
+ const key = String(hidUserId || "");
534
+ if (!hidUserId || userImages.value[key] || user?.metadata?.photo) return;
535
+
536
+ try {
537
+ const response = await getUserImage(selectedReaderId.value, hidUserId);
538
+ const image = response?.data || response;
539
+ userImages.value[key] = image?.base64
540
+ ? `data:${image.contentType || "image/jpeg"};base64,${image.base64}`
541
+ : "";
542
+ } catch {
543
+ userImages.value[key] = "";
544
+ }
545
+ })
546
+ );
447
547
  }
448
548
 
449
549
  function resetForm() {
@@ -512,7 +612,28 @@ function triggerPhotoInput() {
512
612
  }
513
613
 
514
614
  function triggerCameraInput() {
515
- cameraInput.value?.click();
615
+ cameraDialog.value = true;
616
+ }
617
+
618
+ function onCameraCapture(dataUrl: string) {
619
+ const [header, encodedData] = dataUrl.split(",", 2);
620
+ if (!header || !encodedData) {
621
+ showToast("Unable to capture the facial image.", "error");
622
+ return;
623
+ }
624
+
625
+ const mimeType = header.match(/^data:([^;]+);base64$/)?.[1] || "image/jpeg";
626
+ const decodedData = atob(encodedData);
627
+ const bytes = new Uint8Array(decodedData.length);
628
+ for (let index = 0; index < decodedData.length; index += 1) {
629
+ bytes[index] = decodedData.charCodeAt(index);
630
+ }
631
+
632
+ selectedPhotoFile.value = new File([bytes], `hid-facial-${Date.now()}.jpg`, {
633
+ type: mimeType,
634
+ });
635
+ form.photoPreview = dataUrl;
636
+ removePhotoRequested.value = false;
516
637
  }
517
638
 
518
639
  function removePhoto() {
@@ -545,7 +666,8 @@ function onPhotoChange(event: Event) {
545
666
  }
546
667
 
547
668
  async function saveUser() {
548
- const readerId = selectedReaderId.value || form.reader || readers.value[0]?._id;
669
+ const readerId =
670
+ selectedReaderId.value || form.reader || readers.value[0]?._id;
549
671
  if (!readerId || !form.name || !form.hidUserId || !form.registration) {
550
672
  showToast("Please complete the required HID user fields.", "error");
551
673
  return;
@@ -567,7 +689,13 @@ async function saveUser() {
567
689
  hidUserId: String(hidUser.id),
568
690
  registration: form.registration,
569
691
  cardNo: form.cardNo,
570
- type: props.identityType as "resident" | "staff" | "contractor" | "visitor" | "admin" | "unknown",
692
+ type: props.identityType as
693
+ | "resident"
694
+ | "staff"
695
+ | "contractor"
696
+ | "visitor"
697
+ | "admin"
698
+ | "unknown",
571
699
  status: "active" as const,
572
700
  metadata: {
573
701
  name: form.name,
@@ -604,7 +732,11 @@ async function saveUser() {
604
732
 
605
733
  formDialog.value = false;
606
734
  await loadUsers();
607
- showToast(selectedUser.value?._id ? "HID user updated on reader." : "HID user enrolled on reader.");
735
+ showToast(
736
+ selectedUser.value?._id
737
+ ? "HID user updated on reader."
738
+ : "HID user enrolled on reader."
739
+ );
608
740
  } catch (error: any) {
609
741
  console.error("Unable to save HID user:", error);
610
742
  showToast(getHidErrorMessage(error), "error");
@@ -619,13 +751,21 @@ type FacialSyncResult = {
619
751
  scores?: Record<string, unknown>;
620
752
  };
621
753
 
622
- async function syncFacialImage(readerId: string, hidUserId: number): Promise<FacialSyncResult> {
754
+ async function syncFacialImage(
755
+ readerId: string,
756
+ hidUserId: number
757
+ ): Promise<FacialSyncResult> {
623
758
  if (selectedPhotoFile.value) {
624
759
  const timestamp = Math.floor(Date.now() / 1000);
625
- const response = await setUserImage(readerId, hidUserId, selectedPhotoFile.value, {
626
- timestamp,
627
- match: true,
628
- });
760
+ const response = await setUserImage(
761
+ readerId,
762
+ hidUserId,
763
+ selectedPhotoFile.value,
764
+ {
765
+ timestamp,
766
+ match: true,
767
+ }
768
+ );
629
769
  const result = response?.data;
630
770
  userImages.value[String(hidUserId)] = form.photoPreview;
631
771
  return { action: "enrolled", timestamp, scores: result?.scores };
@@ -640,7 +780,10 @@ async function syncFacialImage(readerId: string, hidUserId: number): Promise<Fac
640
780
  return { action: "none" };
641
781
  }
642
782
 
643
- function applyFacialResult(metadata: Record<string, unknown>, result: FacialSyncResult) {
783
+ function applyFacialResult(
784
+ metadata: Record<string, unknown>,
785
+ result: FacialSyncResult
786
+ ) {
644
787
  if (result.action === "enrolled") {
645
788
  metadata.facialData = form.hidUserId;
646
789
  metadata.imageTimestamp = String(result.timestamp || "");
@@ -658,7 +801,11 @@ async function deleteUser() {
658
801
 
659
802
  deleting.value = true;
660
803
  try {
661
- await syncHidUserRole(selectedReaderId.value, toHidNumericId(selectedUser.value.hidUserId), false);
804
+ await syncHidUserRole(
805
+ selectedReaderId.value,
806
+ toHidNumericId(selectedUser.value.hidUserId),
807
+ false
808
+ );
662
809
  await deleteHidUserFromReader(selectedReaderId.value, selectedUser.value);
663
810
  if (selectedUser.value._id) {
664
811
  await deleteIdentity(selectedUser.value._id);
@@ -674,8 +821,14 @@ async function deleteUser() {
674
821
  }
675
822
  }
676
823
 
677
- async function saveIdentityOnReader(readerId: string, payload: Record<string, any>) {
678
- const existingIdentity = await findExistingIdentityOnReader(readerId, payload);
824
+ async function saveIdentityOnReader(
825
+ readerId: string,
826
+ payload: Record<string, any>
827
+ ) {
828
+ const existingIdentity = await findExistingIdentityOnReader(
829
+ readerId,
830
+ payload
831
+ );
679
832
  if (existingIdentity?._id) {
680
833
  await updateIdentity(existingIdentity._id, payload);
681
834
  return;
@@ -687,31 +840,47 @@ async function saveIdentityOnReader(readerId: string, payload: Record<string, an
687
840
  const message = getHidErrorMessage(error);
688
841
  if (!message.toLowerCase().includes("already exists")) throw error;
689
842
 
690
- const duplicatedIdentity = await findExistingIdentityOnReader(readerId, payload);
843
+ const duplicatedIdentity = await findExistingIdentityOnReader(
844
+ readerId,
845
+ payload
846
+ );
691
847
  if (!duplicatedIdentity?._id) throw error;
692
848
  await updateIdentity(duplicatedIdentity._id, payload);
693
849
  }
694
850
  }
695
851
 
696
- async function findExistingIdentityOnReader(readerId: string, payload: Record<string, any>) {
852
+ async function findExistingIdentityOnReader(
853
+ readerId: string,
854
+ payload: Record<string, any>
855
+ ) {
697
856
  const response = await getIdentities(readerId, {
698
857
  page: 1,
699
858
  limit: 500,
700
859
  search: payload.hidUserId || payload.registration || payload.cardNo || "",
701
860
  });
702
- const identities = response?.items ?? response?.data?.items ?? response?.data?.identities ?? [];
861
+ const identities =
862
+ response?.items ??
863
+ response?.data?.items ??
864
+ response?.data?.identities ??
865
+ [];
703
866
  const hidUserId = String(payload.hidUserId || "");
704
867
  const registration = String(payload.registration || "");
705
868
  const cardNo = String(payload.cardNo || "");
706
869
 
707
- return identities.find((identity: Record<string, any>) =>
708
- String(identity.hidUserId || "") === hidUserId ||
709
- Boolean(registration && String(identity.registration || "") === registration) ||
710
- Boolean(cardNo && String(identity.cardNo || "") === cardNo),
870
+ return identities.find(
871
+ (identity: Record<string, any>) =>
872
+ String(identity.hidUserId || "") === hidUserId ||
873
+ Boolean(
874
+ registration && String(identity.registration || "") === registration
875
+ ) ||
876
+ Boolean(cardNo && String(identity.cardNo || "") === cardNo)
711
877
  );
712
878
  }
713
879
 
714
- async function createHidUserOnReader(readerId: string, user: Record<string, any>) {
880
+ async function createHidUserOnReader(
881
+ readerId: string,
882
+ user: Record<string, any>
883
+ ) {
715
884
  const existing = await getHidUserFromReader(readerId, user.id);
716
885
  if (existing) {
717
886
  await updateHidUserOnReader(readerId, user);
@@ -741,7 +910,10 @@ async function getHidUserFromReader(readerId: string, id: unknown) {
741
910
  return normalizeHidUsers(response)[0];
742
911
  }
743
912
 
744
- async function updateHidUserOnReader(readerId: string, user: Record<string, any>) {
913
+ async function updateHidUserOnReader(
914
+ readerId: string,
915
+ user: Record<string, any>
916
+ ) {
745
917
  const { id, ...values } = user;
746
918
  await runObjectOperation(readerId, {
747
919
  operation: "modify",
@@ -755,7 +927,10 @@ async function updateHidUserOnReader(readerId: string, user: Record<string, any>
755
927
  });
756
928
  }
757
929
 
758
- async function deleteHidUserFromReader(readerId: string, user: Record<string, any>) {
930
+ async function deleteHidUserFromReader(
931
+ readerId: string,
932
+ user: Record<string, any>
933
+ ) {
759
934
  const id = toHidNumericId(user.hidUserId);
760
935
  if (!readerId || !id) return;
761
936
 
@@ -770,7 +945,11 @@ async function deleteHidUserFromReader(readerId: string, user: Record<string, an
770
945
  });
771
946
  }
772
947
 
773
- async function syncHidUserRole(readerId: string, hidUserId: unknown, isAdministrator: boolean) {
948
+ async function syncHidUserRole(
949
+ readerId: string,
950
+ hidUserId: unknown,
951
+ isAdministrator: boolean
952
+ ) {
774
953
  const userId = toHidNumericId(hidUserId);
775
954
  if (!readerId || !userId) return;
776
955
 
@@ -864,14 +1043,17 @@ function getAdministratorRoleUserIds(response: Record<string, any> | null) {
864
1043
  return new Set(
865
1044
  (Array.isArray(roles) ? roles : [])
866
1045
  .map((role) => toHidNumericId(role.user_id))
867
- .filter((userId): userId is number => Boolean(userId)),
1046
+ .filter((userId): userId is number => Boolean(userId))
868
1047
  );
869
1048
  }
870
1049
 
871
1050
  function toHidUserObject(identity: Record<string, any>) {
872
1051
  return {
873
1052
  id: toHidNumericId(identity.hidUserId),
874
- name: identity.metadata?.name || identity.name || `User ${identity.hidUserId || ""}`.trim(),
1053
+ name:
1054
+ identity.metadata?.name ||
1055
+ identity.name ||
1056
+ `User ${identity.hidUserId || ""}`.trim(),
875
1057
  registration: identity.registration || "",
876
1058
  };
877
1059
  }
@@ -900,23 +1082,40 @@ function getName(user?: Record<string, any> | null) {
900
1082
  }
901
1083
 
902
1084
  function getUnit(user?: Record<string, any> | null) {
903
- return user?.metadata?.unitLabel || [user?.metadata?.block, user?.metadata?.level, user?.metadata?.unit].filter(Boolean).join("/") || "N/A";
1085
+ return (
1086
+ user?.metadata?.unitLabel ||
1087
+ [user?.metadata?.block, user?.metadata?.level, user?.metadata?.unit]
1088
+ .filter(Boolean)
1089
+ .join("/") ||
1090
+ "N/A"
1091
+ );
904
1092
  }
905
1093
 
906
1094
  function getFacialData(user: Record<string, any>) {
907
- return user?.metadata?.facialData || user?.metadata?.imageTimestamp || (user?.metadata?.photo ? user.hidUserId : "N/A");
1095
+ return (
1096
+ user?.metadata?.facialData ||
1097
+ user?.metadata?.imageTimestamp ||
1098
+ (user?.metadata?.photo ? user.hidUserId : "N/A")
1099
+ );
908
1100
  }
909
1101
 
910
1102
  function getUserImageSrc(user: Record<string, any>) {
911
1103
  const hidUserId = toHidNumericId(user?.hidUserId);
912
- return user?.metadata?.photo || (hidUserId ? userImages.value[String(hidUserId)] : "") || "";
1104
+ return (
1105
+ user?.metadata?.photo ||
1106
+ (hidUserId ? userImages.value[String(hidUserId)] : "") ||
1107
+ ""
1108
+ );
913
1109
  }
914
1110
 
915
1111
  async function getNextUid() {
916
1112
  const readerIds = await getExistingHidReaderIds();
917
1113
  const maxNumber = users.value.reduce((max, user) => {
918
1114
  const match = String(user.hidUserId ?? "").match(/^UID(\d+)$/i);
919
- return Math.max(max, match ? Number(match[1]) : toHidNumericId(user.hidUserId) || 0);
1115
+ return Math.max(
1116
+ max,
1117
+ match ? Number(match[1]) : toHidNumericId(user.hidUserId) || 0
1118
+ );
920
1119
  }, 0);
921
1120
  const nextNumber = Math.max(maxNumber, ...readerIds, 0) + 1;
922
1121
  return `UID${String(nextNumber).padStart(6, "0")}`;
@@ -930,10 +1129,15 @@ async function getNextRegistration() {
930
1129
  const numericRegistrations = registrations
931
1130
  .map((registration) => {
932
1131
  const match = registration.match(/^(\d+)$/);
933
- return match ? { value: Number(match[1]), width: match[1].length } : undefined;
1132
+ return match
1133
+ ? { value: Number(match[1]), width: match[1].length }
1134
+ : undefined;
934
1135
  })
935
1136
  .filter((item): item is { value: number; width: number } => Boolean(item));
936
- const maxRegistration = numericRegistrations.reduce((max, item) => Math.max(max, item.value), 0);
1137
+ const maxRegistration = numericRegistrations.reduce(
1138
+ (max, item) => Math.max(max, item.value),
1139
+ 0
1140
+ );
937
1141
  const width = Math.max(6, ...numericRegistrations.map((item) => item.width));
938
1142
 
939
1143
  return String(maxRegistration + 1).padStart(width, "0");
@@ -980,7 +1184,9 @@ function normalizeHidUsers(response: Record<string, any>) {
980
1184
  name: user.name,
981
1185
  metadata: {
982
1186
  name: user.name,
983
- imageTimestamp: user.image_timestamp ? String(user.image_timestamp) : "",
1187
+ imageTimestamp: user.image_timestamp
1188
+ ? String(user.image_timestamp)
1189
+ : "",
984
1190
  lastAccessAt: fromUnixSeconds(user.last_access),
985
1191
  hidReaderSource: true,
986
1192
  },
@@ -988,7 +1194,10 @@ function normalizeHidUsers(response: Record<string, any>) {
988
1194
  : [];
989
1195
  }
990
1196
 
991
- function mergeReaderUsersWithIdentities(readerUsers: Record<string, any>[], identities: Record<string, any>[]) {
1197
+ function mergeReaderUsersWithIdentities(
1198
+ readerUsers: Record<string, any>[],
1199
+ identities: Record<string, any>[]
1200
+ ) {
992
1201
  const identityById = new Map<string, Record<string, any>>();
993
1202
  identities.forEach((identity) => {
994
1203
  const id = toHidNumericId(identity.hidUserId);
@@ -996,7 +1205,9 @@ function mergeReaderUsersWithIdentities(readerUsers: Record<string, any>[], iden
996
1205
  });
997
1206
 
998
1207
  const merged = readerUsers.map((readerUser) => {
999
- const identity = identityById.get(String(toHidNumericId(readerUser.hidUserId)));
1208
+ const identity = identityById.get(
1209
+ String(toHidNumericId(readerUser.hidUserId))
1210
+ );
1000
1211
  if (!identity) return readerUser;
1001
1212
 
1002
1213
  return {
@@ -1011,8 +1222,11 @@ function mergeReaderUsersWithIdentities(readerUsers: Record<string, any>[], iden
1011
1222
  metadata: {
1012
1223
  ...readerUser.metadata,
1013
1224
  ...identity.metadata,
1014
- imageTimestamp: identity.metadata?.imageTimestamp || readerUser.metadata?.imageTimestamp,
1015
- lastAccessAt: identity.metadata?.lastAccessAt || readerUser.metadata?.lastAccessAt,
1225
+ imageTimestamp:
1226
+ identity.metadata?.imageTimestamp ||
1227
+ readerUser.metadata?.imageTimestamp,
1228
+ lastAccessAt:
1229
+ identity.metadata?.lastAccessAt || readerUser.metadata?.lastAccessAt,
1016
1230
  },
1017
1231
  };
1018
1232
  });
@@ -1023,13 +1237,16 @@ function mergeReaderUsersWithIdentities(readerUsers: Record<string, any>[], iden
1023
1237
  function filterUsers(items: Record<string, any>[]) {
1024
1238
  const searchText = search.value.trim().toLowerCase();
1025
1239
  return items.filter((item) => {
1026
- const matchesSearch = !searchText || [
1027
- getName(item),
1028
- item.hidUserId,
1029
- item.registration,
1030
- item.cardNo,
1031
- ].some((value) => String(value || "").toLowerCase().includes(searchText));
1032
- const matchesStatus = status.value === "Status" || getMappingStatus(item) === status.value;
1240
+ const matchesSearch =
1241
+ !searchText ||
1242
+ [getName(item), item.hidUserId, item.registration, item.cardNo].some(
1243
+ (value) =>
1244
+ String(value || "")
1245
+ .toLowerCase()
1246
+ .includes(searchText)
1247
+ );
1248
+ const matchesStatus =
1249
+ status.value === "Status" || getMappingStatus(item) === status.value;
1033
1250
  return matchesSearch && matchesStatus;
1034
1251
  });
1035
1252
  }
@@ -1089,7 +1306,7 @@ function getHidErrorMessage(error: any) {
1089
1306
  data?.statusMessage ||
1090
1307
  error?.statusMessage ||
1091
1308
  error?.message ||
1092
- "Unable to save HID user.",
1309
+ "Unable to save HID user."
1093
1310
  );
1094
1311
  }
1095
1312
  </script>