@7365admin1/layer-common 3.2.8-staging.205 → 3.2.8-staging.207
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/components/HidAccessLogDashboard.vue +176 -246
- package/components/HidIntercomManagement.vue +51 -18
- package/components/HidQrCodeConfiguration.vue +2 -3
- package/components/HidReaderForm.vue +176 -10
- package/components/HidReaderManagement.vue +211 -96
- package/components/HidServiceSettingsPanel.vue +21 -6
- package/components/HidUserEnrollment.vue +371 -198
- package/components/Layout/NavigationDrawer.vue +19 -2
- package/composables/useHidAmico.ts +113 -28
- package/composables/useHidNavigation.ts +4 -2
- package/composables/useOptionalServices.ts +26 -16
- package/package.json +1 -1
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
item-value="key"
|
|
32
32
|
show-header
|
|
33
33
|
@refresh="loadDashboard"
|
|
34
|
-
@update:page="
|
|
34
|
+
@update:page="goToPage"
|
|
35
35
|
>
|
|
36
36
|
<template #actions>
|
|
37
37
|
<AppButton icon="mdi-download" @click="openExportDialog">Export</AppButton>
|
|
@@ -62,20 +62,27 @@
|
|
|
62
62
|
v-model="selectedReaderId"
|
|
63
63
|
class="reader-select"
|
|
64
64
|
:items="readerOptions"
|
|
65
|
-
@update:model-value="
|
|
65
|
+
@update:model-value="reloadDashboardFromFirstPage"
|
|
66
|
+
/>
|
|
67
|
+
<AppField
|
|
68
|
+
v-model="search"
|
|
69
|
+
search
|
|
70
|
+
placeholder="Search"
|
|
71
|
+
@keyup.enter="reloadDashboardFromFirstPage"
|
|
66
72
|
/>
|
|
67
|
-
<AppField v-model="search" search placeholder="Search" />
|
|
68
73
|
<AppSelect
|
|
69
74
|
v-model="status"
|
|
70
75
|
:items="statusOptions"
|
|
71
76
|
placeholder="Status"
|
|
72
77
|
clearable
|
|
78
|
+
@update:model-value="reloadDashboardFromFirstPage"
|
|
73
79
|
/>
|
|
74
80
|
<AppSelect
|
|
75
81
|
v-model="accessMethod"
|
|
76
82
|
:items="accessMethodFilterOptions"
|
|
77
83
|
placeholder="Access Method"
|
|
78
84
|
clearable
|
|
85
|
+
@update:model-value="reloadDashboardFromFirstPage"
|
|
79
86
|
/>
|
|
80
87
|
</div>
|
|
81
88
|
</template>
|
|
@@ -320,16 +327,57 @@ type PdfAccessRow = {
|
|
|
320
327
|
accessDateTime: string;
|
|
321
328
|
};
|
|
322
329
|
|
|
323
|
-
|
|
330
|
+
type HidAccessRecord = {
|
|
331
|
+
[key: string]: unknown;
|
|
332
|
+
_id?: string | number;
|
|
333
|
+
id?: string | number;
|
|
334
|
+
name?: string;
|
|
335
|
+
location?: string;
|
|
336
|
+
metadata?: HidAccessRecord;
|
|
337
|
+
payload?: HidAccessRecord;
|
|
338
|
+
identity?: HidAccessRecord;
|
|
339
|
+
identityLookup?: HidAccessRecord;
|
|
340
|
+
rawAccessLog?: HidAccessRecord;
|
|
341
|
+
reader?: HidAccessRecord;
|
|
342
|
+
authorization?: HidAccessRecord;
|
|
343
|
+
data?: HidAccessRecord;
|
|
344
|
+
result?: HidAccessRecord;
|
|
345
|
+
values?: HidAccessRecord;
|
|
346
|
+
access_logs?: HidAccessRecord[];
|
|
347
|
+
accessLogs?: HidAccessRecord[];
|
|
348
|
+
object_changes?: unknown[];
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
type JsPdf = {
|
|
352
|
+
internal: { pageSize: { getWidth(): number; getHeight(): number } };
|
|
353
|
+
setFillColor(red: number, green: number, blue: number): void;
|
|
354
|
+
setDrawColor(red: number, green: number, blue: number): void;
|
|
355
|
+
setTextColor(red: number, green: number, blue: number): void;
|
|
356
|
+
setFont(name: string, style: string): void;
|
|
357
|
+
setFontSize(size: number): void;
|
|
358
|
+
rect(x: number, y: number, width: number, height: number, style?: string): void;
|
|
359
|
+
text(value: string | string[], x: number, y: number): void;
|
|
360
|
+
addPage(): void;
|
|
361
|
+
splitTextToSize(value: string, width: number): string[];
|
|
362
|
+
save(filename: string): void;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const {
|
|
366
|
+
getReaders,
|
|
367
|
+
getIdentities,
|
|
368
|
+
getLogs,
|
|
369
|
+
getUserImage,
|
|
370
|
+
getReaderUsers,
|
|
371
|
+
getReaderAccessLogs,
|
|
372
|
+
} = useHidAmico();
|
|
324
373
|
const { getVisitors } = useVisitor();
|
|
325
374
|
|
|
326
|
-
const readers = ref<
|
|
327
|
-
const identities = ref<
|
|
328
|
-
const allIdentities = ref<
|
|
329
|
-
const visitorRecords = ref<Record<string,
|
|
330
|
-
const logs = ref<
|
|
331
|
-
const liveAccessLogs = ref<
|
|
332
|
-
const readerConfiguration = ref<Record<string, any>>({});
|
|
375
|
+
const readers = ref<HidAccessRecord[]>([]);
|
|
376
|
+
const identities = ref<HidAccessRecord[]>([]);
|
|
377
|
+
const allIdentities = ref<HidAccessRecord[]>([]);
|
|
378
|
+
const visitorRecords = ref<Record<string, HidAccessRecord>>({});
|
|
379
|
+
const logs = ref<HidAccessRecord[]>([]);
|
|
380
|
+
const liveAccessLogs = ref<HidAccessRecord[]>([]);
|
|
333
381
|
const administratorUserIds = ref<Set<number>>(new Set());
|
|
334
382
|
const userImages = ref<Record<string, string>>({});
|
|
335
383
|
const selectedReaderId = ref("");
|
|
@@ -339,6 +387,8 @@ const status = ref("");
|
|
|
339
387
|
const accessMethod = ref("");
|
|
340
388
|
const page = ref(1);
|
|
341
389
|
const limit = 10;
|
|
390
|
+
const serverPages = ref(1);
|
|
391
|
+
const serverPageRange = ref("");
|
|
342
392
|
const loading = ref(false);
|
|
343
393
|
const loadSequence = ref(0);
|
|
344
394
|
const exportDialog = ref(false);
|
|
@@ -490,22 +540,26 @@ const logHeaders = [
|
|
|
490
540
|
{ title: "", value: "action-table", sortable: false },
|
|
491
541
|
];
|
|
492
542
|
|
|
493
|
-
const pages = computed(() => Math.max(1,
|
|
543
|
+
const pages = computed(() => Math.max(1, serverPages.value));
|
|
494
544
|
|
|
495
545
|
const paginatedRows = computed(() => {
|
|
496
|
-
|
|
497
|
-
const start = (currentPage - 1) * limit;
|
|
498
|
-
return filteredRows.value.slice(start, start + limit);
|
|
546
|
+
return filteredRows.value;
|
|
499
547
|
});
|
|
500
548
|
|
|
501
549
|
const pageRange = computed(() => {
|
|
502
|
-
|
|
503
|
-
const currentPage = Math.min(page.value, pages.value);
|
|
504
|
-
const start = (currentPage - 1) * limit + 1;
|
|
505
|
-
const end = Math.min(currentPage * limit, filteredRows.value.length);
|
|
506
|
-
return `${start}-${end} of ${filteredRows.value.length}`;
|
|
550
|
+
return serverPageRange.value || (filteredRows.value.length ? `1-${filteredRows.value.length}` : "0-0 of 0");
|
|
507
551
|
});
|
|
508
552
|
|
|
553
|
+
function goToPage(nextPage: number) {
|
|
554
|
+
page.value = nextPage;
|
|
555
|
+
loadDashboard();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function reloadDashboardFromFirstPage() {
|
|
559
|
+
page.value = 1;
|
|
560
|
+
loadDashboard();
|
|
561
|
+
}
|
|
562
|
+
|
|
509
563
|
const exportRowsData = computed(() =>
|
|
510
564
|
getExportSourceRows().filter((row) => {
|
|
511
565
|
const selectedUsers = exportForm.users.filter((user) => user !== "all");
|
|
@@ -525,10 +579,6 @@ watch(
|
|
|
525
579
|
{ immediate: true },
|
|
526
580
|
);
|
|
527
581
|
|
|
528
|
-
watch([search, status, accessMethod, activeTab], () => {
|
|
529
|
-
page.value = 1;
|
|
530
|
-
});
|
|
531
|
-
|
|
532
582
|
watch(pages, () => {
|
|
533
583
|
if (page.value > pages.value) page.value = pages.value;
|
|
534
584
|
});
|
|
@@ -559,26 +609,47 @@ async function loadDashboard() {
|
|
|
559
609
|
resetDashboardData();
|
|
560
610
|
try {
|
|
561
611
|
const isVisitorTab = activeTab.value === "visitor";
|
|
562
|
-
const [identityResponse, logResponse,
|
|
612
|
+
const [identityResponse, logResponse, hidAccessLogResponse, visitorResponse, readerUserResponse] = await Promise.all([
|
|
563
613
|
loadDashboardSource(
|
|
564
614
|
getIdentities(selectedReaderId.value, {
|
|
565
|
-
page:
|
|
566
|
-
limit:
|
|
615
|
+
page: page.value,
|
|
616
|
+
limit: 100,
|
|
567
617
|
}),
|
|
568
618
|
null,
|
|
569
619
|
"identities",
|
|
570
620
|
),
|
|
571
621
|
loadDashboardSource(
|
|
572
622
|
getLogs(selectedReaderId.value, {
|
|
573
|
-
page:
|
|
574
|
-
limit
|
|
623
|
+
page: page.value,
|
|
624
|
+
limit,
|
|
575
625
|
}),
|
|
576
626
|
null,
|
|
577
627
|
"persisted access logs",
|
|
578
628
|
),
|
|
579
|
-
loadDashboardSource(
|
|
580
|
-
|
|
581
|
-
|
|
629
|
+
loadDashboardSource(
|
|
630
|
+
getReaderAccessLogs(selectedReaderId.value, {
|
|
631
|
+
page: page.value,
|
|
632
|
+
limit,
|
|
633
|
+
search: search.value.trim(),
|
|
634
|
+
status: status.value === "Authorized"
|
|
635
|
+
? "authorized"
|
|
636
|
+
: status.value === "Not Authorized"
|
|
637
|
+
? "not_authorized"
|
|
638
|
+
: status.value === "Unknown" ? "unknown" : "",
|
|
639
|
+
method: accessMethod.value === "Facial"
|
|
640
|
+
? "facial"
|
|
641
|
+
: accessMethod.value === "QR Code"
|
|
642
|
+
? "qr_code"
|
|
643
|
+
: accessMethod.value === "ID and Password"
|
|
644
|
+
? "id_password"
|
|
645
|
+
: accessMethod.value === "PIN"
|
|
646
|
+
? "pin"
|
|
647
|
+
: accessMethod.value === "Card" ? "card" : "",
|
|
648
|
+
tab: activeTab.value,
|
|
649
|
+
}),
|
|
650
|
+
{ items: [], pages: 1, pageRange: "0-0 of 0" },
|
|
651
|
+
"live access logs",
|
|
652
|
+
),
|
|
582
653
|
isVisitorTab && props.org
|
|
583
654
|
? loadDashboardSource(
|
|
584
655
|
getVisitors({
|
|
@@ -592,25 +663,47 @@ async function loadDashboard() {
|
|
|
592
663
|
"visitor records",
|
|
593
664
|
)
|
|
594
665
|
: Promise.resolve(null),
|
|
595
|
-
loadDashboardSource(
|
|
666
|
+
loadDashboardSource(
|
|
667
|
+
getReaderUsers(selectedReaderId.value, { page: page.value, limit: 100 }),
|
|
668
|
+
{ items: [] },
|
|
669
|
+
"reader users",
|
|
670
|
+
),
|
|
596
671
|
]);
|
|
597
672
|
|
|
598
673
|
if (sequence !== loadSequence.value) return;
|
|
599
674
|
|
|
600
675
|
const loadedIdentities = identityResponse?.items ?? identityResponse?.data?.items ?? identityResponse?.data?.identities ?? [];
|
|
601
|
-
const
|
|
676
|
+
const liveItems = hidAccessLogResponse?.items ?? hidAccessLogResponse?.data?.items ?? [];
|
|
677
|
+
const readerUsers = readerUserResponse?.items ?? readerUserResponse?.data?.items ?? [];
|
|
678
|
+
const embeddedIdentities = liveItems
|
|
679
|
+
.map((item: Record<string, unknown>) => item.identity)
|
|
680
|
+
.filter(isUnknownRecord);
|
|
681
|
+
const mergedIdentities = mergeReaderUsersWithIdentities(
|
|
682
|
+
readerUsers,
|
|
683
|
+
[...loadedIdentities, ...embeddedIdentities],
|
|
684
|
+
);
|
|
602
685
|
const loadedVisitors = visitorResponse?.items ?? visitorResponse?.data?.items ?? visitorResponse?.data?.visitors ?? [];
|
|
603
686
|
visitorRecords.value = Object.fromEntries(
|
|
604
|
-
loadedVisitors.map((visitor:
|
|
687
|
+
loadedVisitors.map((visitor: HidAccessRecord) => [String(visitor._id), visitor]),
|
|
688
|
+
);
|
|
689
|
+
administratorUserIds.value = new Set(
|
|
690
|
+
[...readerUsers, ...liveItems]
|
|
691
|
+
.filter((item: Record<string, unknown>) => item.isAdministrator === true)
|
|
692
|
+
.map((item: Record<string, unknown>) => toHidNumericId(item.hidUserId ?? item.user_id))
|
|
693
|
+
.filter((userId: number | undefined): userId is number => Boolean(userId)),
|
|
605
694
|
);
|
|
606
|
-
administratorUserIds.value = getAdministratorRoleUserIds(roleResponse);
|
|
607
695
|
allIdentities.value = mergedIdentities;
|
|
608
696
|
identities.value = mergedIdentities.filter(
|
|
609
697
|
(identity: Record<string, unknown>) => classifyIdentity(identity) === activeTab.value,
|
|
610
698
|
);
|
|
611
|
-
logs.value =
|
|
612
|
-
|
|
613
|
-
|
|
699
|
+
logs.value = liveItems.length
|
|
700
|
+
? []
|
|
701
|
+
: logResponse?.items ?? logResponse?.data?.items ?? logResponse?.data?.logs ?? [];
|
|
702
|
+
liveAccessLogs.value = liveItems;
|
|
703
|
+
serverPages.value = Number(hidAccessLogResponse?.pages ?? hidAccessLogResponse?.data?.pages ?? 1);
|
|
704
|
+
serverPageRange.value = String(
|
|
705
|
+
hidAccessLogResponse?.pageRange ?? hidAccessLogResponse?.data?.pageRange ?? "",
|
|
706
|
+
);
|
|
614
707
|
await loadUserImages(identities.value);
|
|
615
708
|
} finally {
|
|
616
709
|
if (sequence === loadSequence.value) {
|
|
@@ -628,7 +721,7 @@ async function loadDashboardSource<T>(request: Promise<T>, fallback: T, label: s
|
|
|
628
721
|
}
|
|
629
722
|
}
|
|
630
723
|
|
|
631
|
-
async function loadUserImages(items:
|
|
724
|
+
async function loadUserImages(items: HidAccessRecord[]) {
|
|
632
725
|
if (!selectedReaderId.value) return;
|
|
633
726
|
|
|
634
727
|
await Promise.all(items.map(async (identity) => {
|
|
@@ -648,7 +741,7 @@ async function loadUserImages(items: Record<string, any>[]) {
|
|
|
648
741
|
}));
|
|
649
742
|
}
|
|
650
743
|
|
|
651
|
-
function hasHidImageHint(identity:
|
|
744
|
+
function hasHidImageHint(identity: HidAccessRecord) {
|
|
652
745
|
return Boolean(
|
|
653
746
|
identity?.metadata?.imageTimestamp ||
|
|
654
747
|
identity?.metadata?.facialData ||
|
|
@@ -660,56 +753,6 @@ function hasHidImageHint(identity: Record<string, any>) {
|
|
|
660
753
|
);
|
|
661
754
|
}
|
|
662
755
|
|
|
663
|
-
async function loadLiveAccessLogs(readerId: string) {
|
|
664
|
-
const allLogs: Record<string, any>[] = [];
|
|
665
|
-
const limit = 500;
|
|
666
|
-
const maxPages = 20;
|
|
667
|
-
|
|
668
|
-
for (let pageIndex = 0; pageIndex < maxPages; pageIndex += 1) {
|
|
669
|
-
try {
|
|
670
|
-
const response = await runObjectOperation(readerId, {
|
|
671
|
-
operation: "load",
|
|
672
|
-
object: "access_logs",
|
|
673
|
-
where: {
|
|
674
|
-
access_logs: {},
|
|
675
|
-
},
|
|
676
|
-
limit,
|
|
677
|
-
offset: pageIndex * limit,
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
const pageLogs = normalizeHidAccessLogs(response);
|
|
681
|
-
allLogs.push(...pageLogs);
|
|
682
|
-
|
|
683
|
-
if (pageLogs.length < limit) break;
|
|
684
|
-
} catch (error) {
|
|
685
|
-
console.warn("Unable to load HID live access logs", error);
|
|
686
|
-
break;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
return dedupeAccessLogs(allLogs).sort((a, b) => {
|
|
691
|
-
const aTime = parseDate(getAccessLogTime(a, {}))?.getTime() || 0;
|
|
692
|
-
const bTime = parseDate(getAccessLogTime(b, {}))?.getTime() || 0;
|
|
693
|
-
return bTime - aTime;
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
async function loadReaderConfiguration(readerId: string) {
|
|
698
|
-
try {
|
|
699
|
-
return await getReaderConfiguration(readerId, {
|
|
700
|
-
access_logs: [],
|
|
701
|
-
event_types: [],
|
|
702
|
-
log_types: [],
|
|
703
|
-
identification_rules: [],
|
|
704
|
-
portals: [],
|
|
705
|
-
users: [],
|
|
706
|
-
});
|
|
707
|
-
} catch (error) {
|
|
708
|
-
console.warn("Unable to load HID configuration", error);
|
|
709
|
-
return {};
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
|
|
713
756
|
async function onTabChange(tab: AccessTab) {
|
|
714
757
|
if (tab) {
|
|
715
758
|
activeTab.value = tab;
|
|
@@ -720,48 +763,8 @@ async function onTabChange(tab: AccessTab) {
|
|
|
720
763
|
loadDashboard();
|
|
721
764
|
}
|
|
722
765
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
const response = await runObjectOperation(readerId, {
|
|
726
|
-
operation: "load",
|
|
727
|
-
object: "users",
|
|
728
|
-
limit: 500,
|
|
729
|
-
offset: 0,
|
|
730
|
-
});
|
|
731
|
-
return normalizeHidUsers(response);
|
|
732
|
-
} catch (error) {
|
|
733
|
-
console.warn("Unable to load HID users for access log images", error);
|
|
734
|
-
return [];
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
function normalizeHidUsers(response: Record<string, any>) {
|
|
739
|
-
const items =
|
|
740
|
-
response?.data?.users ||
|
|
741
|
-
response?.data?.data?.users ||
|
|
742
|
-
response?.users ||
|
|
743
|
-
[];
|
|
744
|
-
|
|
745
|
-
return Array.isArray(items)
|
|
746
|
-
? items.map((user) => ({
|
|
747
|
-
hidUserId: String(user.id || ""),
|
|
748
|
-
rawHidUserId: user.id,
|
|
749
|
-
registration: user.registration,
|
|
750
|
-
cardNo: user.card_value || user.cardNo || "",
|
|
751
|
-
name: user.name,
|
|
752
|
-
hidUserTypeId: user.user_type_id,
|
|
753
|
-
metadata: {
|
|
754
|
-
name: user.name,
|
|
755
|
-
hidUserTypeId: user.user_type_id,
|
|
756
|
-
imageTimestamp: user.image_timestamp ? String(user.image_timestamp) : "",
|
|
757
|
-
hidReaderSource: true,
|
|
758
|
-
},
|
|
759
|
-
}))
|
|
760
|
-
: [];
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
function mergeReaderUsersWithIdentities(readerUsers: Record<string, any>[], identities: Record<string, any>[]) {
|
|
764
|
-
const identityById = new Map<string, Record<string, any>>();
|
|
766
|
+
function mergeReaderUsersWithIdentities(readerUsers: HidAccessRecord[], identities: HidAccessRecord[]) {
|
|
767
|
+
const identityById = new Map<string, HidAccessRecord>();
|
|
765
768
|
identities.forEach((identity) => {
|
|
766
769
|
const id = toHidNumericId(identity.hidUserId);
|
|
767
770
|
if (id) identityById.set(String(id), identity);
|
|
@@ -799,8 +802,9 @@ function resetDashboardData() {
|
|
|
799
802
|
visitorRecords.value = {};
|
|
800
803
|
logs.value = [];
|
|
801
804
|
liveAccessLogs.value = [];
|
|
802
|
-
readerConfiguration.value = {};
|
|
803
805
|
administratorUserIds.value = new Set();
|
|
806
|
+
serverPages.value = 1;
|
|
807
|
+
serverPageRange.value = "";
|
|
804
808
|
}
|
|
805
809
|
|
|
806
810
|
function isVisibleForActiveTab(row: AccessRow) {
|
|
@@ -823,35 +827,6 @@ function classifyIdentity(identity?: Record<string, unknown>, row?: AccessRow):
|
|
|
823
827
|
return "resident";
|
|
824
828
|
}
|
|
825
829
|
|
|
826
|
-
function loadAdministratorRoles(readerId: string) {
|
|
827
|
-
return runObjectOperation(readerId, {
|
|
828
|
-
operation: "load",
|
|
829
|
-
object: "user_roles",
|
|
830
|
-
where: {
|
|
831
|
-
user_roles: {
|
|
832
|
-
role: 1,
|
|
833
|
-
},
|
|
834
|
-
},
|
|
835
|
-
limit: 500,
|
|
836
|
-
offset: 0,
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
function getAdministratorRoleUserIds(response: Record<string, any> | null) {
|
|
841
|
-
const data = response?.data || response || {};
|
|
842
|
-
const roles =
|
|
843
|
-
data?.user_roles ||
|
|
844
|
-
data?.data?.user_roles ||
|
|
845
|
-
data?.result?.user_roles ||
|
|
846
|
-
[];
|
|
847
|
-
|
|
848
|
-
return new Set(
|
|
849
|
-
(Array.isArray(roles) ? roles : [])
|
|
850
|
-
.map((role) => toHidNumericId(role.user_id))
|
|
851
|
-
.filter((userId): userId is number => Boolean(userId)),
|
|
852
|
-
);
|
|
853
|
-
}
|
|
854
|
-
|
|
855
830
|
function toHidNumericId(value: unknown) {
|
|
856
831
|
const raw = String(value ?? "").trim();
|
|
857
832
|
if (!raw) return undefined;
|
|
@@ -862,7 +837,7 @@ function toHidNumericId(value: unknown) {
|
|
|
862
837
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
863
838
|
}
|
|
864
839
|
|
|
865
|
-
function getIdentityKey(identity:
|
|
840
|
+
function getIdentityKey(identity: HidAccessRecord) {
|
|
866
841
|
return [
|
|
867
842
|
identity.hidUserId,
|
|
868
843
|
identity.registration,
|
|
@@ -875,7 +850,7 @@ function findIdentityByKey(identityKey: string) {
|
|
|
875
850
|
return allIdentities.value.find((identity) => getIdentityKey(identity) === identityKey);
|
|
876
851
|
}
|
|
877
852
|
|
|
878
|
-
function getLogIdentityKey(log:
|
|
853
|
+
function getLogIdentityKey(log: HidAccessRecord) {
|
|
879
854
|
const identity = log.payload?.identity || {};
|
|
880
855
|
const lookup = log.payload?.identityLookup || {};
|
|
881
856
|
const rawLog = log.payload?.rawAccessLog || log;
|
|
@@ -886,20 +861,20 @@ function getLogIdentityKey(log: Record<string, any>) {
|
|
|
886
861
|
].filter(Boolean).join("|");
|
|
887
862
|
}
|
|
888
863
|
|
|
889
|
-
function getName(identity:
|
|
864
|
+
function getName(identity: HidAccessRecord) {
|
|
890
865
|
return getVisitorRecord(identity)?.name || identity.metadata?.name || identity.name || "N/A";
|
|
891
866
|
}
|
|
892
867
|
|
|
893
|
-
function getFacialData(identity:
|
|
868
|
+
function getFacialData(identity: HidAccessRecord) {
|
|
894
869
|
return identity.metadata?.facialData || identity.metadata?.imageTimestamp || (identity.metadata?.photo ? identity.hidUserId : "N/A");
|
|
895
870
|
}
|
|
896
871
|
|
|
897
|
-
function getUserImageSrc(identity:
|
|
872
|
+
function getUserImageSrc(identity: HidAccessRecord) {
|
|
898
873
|
const hidUserId = toHidNumericId(identity?.hidUserId);
|
|
899
874
|
return identity?.metadata?.photo || (hidUserId ? userImages.value[String(hidUserId)] : "") || "";
|
|
900
875
|
}
|
|
901
876
|
|
|
902
|
-
function getUnit(identity:
|
|
877
|
+
function getUnit(identity: HidAccessRecord) {
|
|
903
878
|
const visitor = getVisitorRecord(identity);
|
|
904
879
|
if (visitor) {
|
|
905
880
|
return visitor.unitName || [visitor.block, visitor.level, visitor.unit].filter(Boolean).join("/") || "N/A";
|
|
@@ -908,17 +883,22 @@ function getUnit(identity: Record<string, any>) {
|
|
|
908
883
|
return identity.metadata?.unitLabel || [identity.metadata?.block, identity.metadata?.level, identity.metadata?.unit].filter(Boolean).join("/") || "N/A";
|
|
909
884
|
}
|
|
910
885
|
|
|
911
|
-
function getVisitorRecord(identity:
|
|
886
|
+
function getVisitorRecord(identity: HidAccessRecord) {
|
|
912
887
|
const visitorId = identity?.visitor;
|
|
913
888
|
return visitorId ? visitorRecords.value[String(visitorId)] : undefined;
|
|
914
889
|
}
|
|
915
890
|
|
|
916
|
-
function getAccessMethod(identity:
|
|
891
|
+
function getAccessMethod(identity: HidAccessRecord, log?: HidAccessRecord) {
|
|
917
892
|
const payload = log?.payload?.rawAccessLog || log?.payload || log || {};
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
893
|
+
const normalizedMethodLabels: Record<string, string> = {
|
|
894
|
+
facial: "Facial",
|
|
895
|
+
qr_code: "QR Code",
|
|
896
|
+
id_password: "ID and Password",
|
|
897
|
+
pin: "PIN",
|
|
898
|
+
card: "Card",
|
|
899
|
+
};
|
|
900
|
+
const normalizedMethod = String(payload.normalizedMethod || "");
|
|
901
|
+
if (normalizedMethodLabels[normalizedMethod]) return normalizedMethodLabels[normalizedMethod];
|
|
922
902
|
const rawIdentification = String(payload.identification || payload.identification_type || payload.identificationType || "").toLowerCase();
|
|
923
903
|
if (rawIdentification.includes("facial") || rawIdentification.includes("face")) return "Facial";
|
|
924
904
|
if (payload.qrcode_value || payload.qrCode || payload.qrcode) return "QR Code";
|
|
@@ -1122,60 +1102,7 @@ function sortAccessRowsByDateDesc(a: AccessRow, b: AccessRow) {
|
|
|
1122
1102
|
return bTime - aTime;
|
|
1123
1103
|
}
|
|
1124
1104
|
|
|
1125
|
-
function
|
|
1126
|
-
const seen = new Set<string>();
|
|
1127
|
-
return items.filter((item) => {
|
|
1128
|
-
const key = [
|
|
1129
|
-
item.id,
|
|
1130
|
-
item.time,
|
|
1131
|
-
item.user_id ?? item.userId ?? item.hidUserId,
|
|
1132
|
-
item.card_value ?? item.cardNo,
|
|
1133
|
-
].join("|");
|
|
1134
|
-
|
|
1135
|
-
if (seen.has(key)) return false;
|
|
1136
|
-
seen.add(key);
|
|
1137
|
-
return true;
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
function normalizeHidAccessLogs(response: Record<string, any>) {
|
|
1142
|
-
const data = response?.data || response || {};
|
|
1143
|
-
const accessLogs =
|
|
1144
|
-
data.access_logs ||
|
|
1145
|
-
data.data?.access_logs ||
|
|
1146
|
-
data.accessLogs ||
|
|
1147
|
-
data.data?.accessLogs ||
|
|
1148
|
-
data.result?.access_logs ||
|
|
1149
|
-
data.result?.data?.access_logs;
|
|
1150
|
-
|
|
1151
|
-
return Array.isArray(accessLogs) ? accessLogs : [];
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
function normalizeReaderConfiguration(response: Record<string, any>) {
|
|
1155
|
-
const data = response?.data || response || {};
|
|
1156
|
-
return data?.data || data?.result || data;
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
function getConfigurationLabel(section: string, id: unknown) {
|
|
1160
|
-
if (id === undefined || id === null || id === "" || Number(id) === -1) return "";
|
|
1161
|
-
const entries = readerConfiguration.value?.[section];
|
|
1162
|
-
if (!entries) return "";
|
|
1163
|
-
|
|
1164
|
-
const items = Array.isArray(entries)
|
|
1165
|
-
? entries
|
|
1166
|
-
: typeof entries === "object"
|
|
1167
|
-
? Object.values(entries)
|
|
1168
|
-
: [];
|
|
1169
|
-
|
|
1170
|
-
const match = items.find((item: any) => {
|
|
1171
|
-
if (!item || typeof item !== "object") return false;
|
|
1172
|
-
return String(item.id ?? item.value ?? item.key ?? item.code) === String(id);
|
|
1173
|
-
}) as Record<string, any> | undefined;
|
|
1174
|
-
|
|
1175
|
-
return match?.name || match?.label || match?.description || match?.title || "";
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
function getRawAccessLogs(event: Record<string, any>) {
|
|
1105
|
+
function getRawAccessLogs(event: HidAccessRecord): HidAccessRecord[] {
|
|
1179
1106
|
const payload = event.payload || {};
|
|
1180
1107
|
const result = payload.result || payload.data || {};
|
|
1181
1108
|
const accessLogs =
|
|
@@ -1199,7 +1126,7 @@ function getRawAccessLogs(event: Record<string, any>) {
|
|
|
1199
1126
|
: [];
|
|
1200
1127
|
}
|
|
1201
1128
|
|
|
1202
|
-
function mapAccessLogToRow(rawLog:
|
|
1129
|
+
function mapAccessLogToRow(rawLog: HidAccessRecord, event: HidAccessRecord, index: string): AccessRow {
|
|
1203
1130
|
const identity = findIdentityForAccessLog(rawLog, event);
|
|
1204
1131
|
const eventIdentity = event.payload?.identity || event.payload?.identityLookup || {};
|
|
1205
1132
|
const rawAccessTime = getAccessLogTime(rawLog, event);
|
|
@@ -1225,8 +1152,8 @@ function mapAccessLogToRow(rawLog: Record<string, any>, event: Record<string, an
|
|
|
1225
1152
|
}
|
|
1226
1153
|
|
|
1227
1154
|
function getAuthorizationStatus(
|
|
1228
|
-
rawLog:
|
|
1229
|
-
event:
|
|
1155
|
+
rawLog: HidAccessRecord,
|
|
1156
|
+
event: HidAccessRecord,
|
|
1230
1157
|
): Pick<AccessRow, "status" | "authorizationReason"> {
|
|
1231
1158
|
const authorization = event.payload?.authorization || rawLog.authorization;
|
|
1232
1159
|
if (typeof authorization?.granted === "boolean") {
|
|
@@ -1248,9 +1175,7 @@ function getAuthorizationStatus(
|
|
|
1248
1175
|
return { status: "Not Authorized", authorizationReason: "Access denied by HID authorization." };
|
|
1249
1176
|
}
|
|
1250
1177
|
|
|
1251
|
-
const eventLabel = String(
|
|
1252
|
-
getConfigurationLabel("event_types", eventCode) || rawLog.event_name || rawLog.eventName || "",
|
|
1253
|
-
).toLowerCase();
|
|
1178
|
+
const eventLabel = String(rawLog.event_name || rawLog.eventName || "").toLowerCase();
|
|
1254
1179
|
if (/not authorized|unauthori[sz]ed|access denied|denied|rejected/.test(eventLabel)) {
|
|
1255
1180
|
return { status: "Not Authorized", authorizationReason: eventLabel };
|
|
1256
1181
|
}
|
|
@@ -1264,7 +1189,10 @@ function getAuthorizationStatus(
|
|
|
1264
1189
|
};
|
|
1265
1190
|
}
|
|
1266
1191
|
|
|
1267
|
-
function findIdentityForAccessLog(rawLog:
|
|
1192
|
+
function findIdentityForAccessLog(rawLog: HidAccessRecord, event: HidAccessRecord) {
|
|
1193
|
+
if (rawLog.identity && typeof rawLog.identity === "object") {
|
|
1194
|
+
return rawLog.identity;
|
|
1195
|
+
}
|
|
1268
1196
|
const eventIdentityId = event.payload?.identity?._id;
|
|
1269
1197
|
if (eventIdentityId) {
|
|
1270
1198
|
const byId = allIdentities.value.find((identity) => String(identity._id) === String(eventIdentityId));
|
|
@@ -1291,7 +1219,7 @@ function findIdentityForAccessLog(rawLog: Record<string, any>, event: Record<str
|
|
|
1291
1219
|
});
|
|
1292
1220
|
}
|
|
1293
1221
|
|
|
1294
|
-
function getLatestAccessLogForIdentity(identity:
|
|
1222
|
+
function getLatestAccessLogForIdentity(identity: HidAccessRecord) {
|
|
1295
1223
|
const accessLogs = [
|
|
1296
1224
|
...liveAccessLogs.value,
|
|
1297
1225
|
...logs.value.flatMap((event) => getRawAccessLogs(event)),
|
|
@@ -1306,10 +1234,12 @@ function getLatestAccessLogForIdentity(identity: Record<string, any>) {
|
|
|
1306
1234
|
})[0];
|
|
1307
1235
|
}
|
|
1308
1236
|
|
|
1237
|
+
function isUnknownRecord(value: unknown): value is Record<string, unknown> {
|
|
1238
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1309
1241
|
function toUnknownRecord(value: unknown): Record<string, unknown> {
|
|
1310
|
-
return
|
|
1311
|
-
? value as Record<string, unknown>
|
|
1312
|
-
: {};
|
|
1242
|
+
return isUnknownRecord(value) ? value : {};
|
|
1313
1243
|
}
|
|
1314
1244
|
|
|
1315
1245
|
function accessLogMatchesIdentity(log: unknown, identity: unknown) {
|
|
@@ -1341,7 +1271,7 @@ function accessLogMatchesIdentity(log: unknown, identity: unknown) {
|
|
|
1341
1271
|
);
|
|
1342
1272
|
}
|
|
1343
1273
|
|
|
1344
|
-
function getAccessLogIdentityKey(rawLog:
|
|
1274
|
+
function getAccessLogIdentityKey(rawLog: HidAccessRecord) {
|
|
1345
1275
|
return [
|
|
1346
1276
|
rawLog.user_id ?? rawLog.userId ?? rawLog.hidUserId,
|
|
1347
1277
|
rawLog.registration,
|
|
@@ -1349,11 +1279,11 @@ function getAccessLogIdentityKey(rawLog: Record<string, any>) {
|
|
|
1349
1279
|
].filter(Boolean).join("|");
|
|
1350
1280
|
}
|
|
1351
1281
|
|
|
1352
|
-
function getAccessLogTime(rawLog:
|
|
1282
|
+
function getAccessLogTime(rawLog: HidAccessRecord, event: HidAccessRecord) {
|
|
1353
1283
|
return rawLog.time || rawLog.createdAt || rawLog.accessedAt || event.createdAt;
|
|
1354
1284
|
}
|
|
1355
1285
|
|
|
1356
|
-
function getRawLogName(rawLog:
|
|
1286
|
+
function getRawLogName(rawLog: HidAccessRecord, event: HidAccessRecord) {
|
|
1357
1287
|
return rawLog.name || event.payload?.identity?.name || event.payload?.name || "Unmapped";
|
|
1358
1288
|
}
|
|
1359
1289
|
|
|
@@ -1366,12 +1296,12 @@ function getExportSourceRows() {
|
|
|
1366
1296
|
return exportScope.value === "history" ? selectedHistoryRows.value : filteredRows.value;
|
|
1367
1297
|
}
|
|
1368
1298
|
|
|
1369
|
-
function getLogAccessDateValue(log:
|
|
1299
|
+
function getLogAccessDateValue(log: HidAccessRecord) {
|
|
1370
1300
|
const payload = log.payload || {};
|
|
1371
1301
|
return log.createdAt || payload.createdAt || payload.accessedAt || payload.time || payload.eventTime;
|
|
1372
1302
|
}
|
|
1373
1303
|
|
|
1374
|
-
function getLogReaderLocation(log:
|
|
1304
|
+
function getLogReaderLocation(log: HidAccessRecord) {
|
|
1375
1305
|
return log.payload?.reader?.location || log.payload?.readerLocation || selectedReader.value?.location || "N/A";
|
|
1376
1306
|
}
|
|
1377
1307
|
|
|
@@ -1392,7 +1322,7 @@ async function exportRowsAsPdf() {
|
|
|
1392
1322
|
pdf.save(`hid-access-log-${activeTab.value}.pdf`);
|
|
1393
1323
|
}
|
|
1394
1324
|
|
|
1395
|
-
function drawAccessLogPdf(pdf:
|
|
1325
|
+
function drawAccessLogPdf(pdf: JsPdf, rows: PdfAccessRow[]) {
|
|
1396
1326
|
const margin = 12;
|
|
1397
1327
|
const pageWidth = pdf.internal.pageSize.getWidth();
|
|
1398
1328
|
const pageHeight = pdf.internal.pageSize.getHeight();
|