@7365admin1/layer-common 4.0.3-staging.231 → 4.0.3-staging.233
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/CreateSubsciptionPlan.vue +28 -21
- package/components/HidAccessLogDashboard.vue +16 -77
- package/components/HidQrCodeConfiguration.vue +37 -38
- package/components/HidReaderManagement.vue +18 -284
- package/components/HidReaderUserRoster.vue +303 -60
- package/components/HidUserEnrollment.vue +26 -43
- package/components/SubscriptionPlanTable.vue +13 -8
- package/components/VisitorForm.vue +25 -10
- package/composables/useHidAmico.ts +1 -0
- package/composables/useHidNavigation.ts +0 -7
- package/package.json +1 -1
- package/components/HidUserMapping.vue +0 -583
- package/pages/[org]/[site]/access-mgmt/hid-user-mapping/index.vue +0 -23
|
@@ -311,9 +311,7 @@ type HidSyncUser = HidRecord & {
|
|
|
311
311
|
};
|
|
312
312
|
|
|
313
313
|
type HidSyncPayload = {
|
|
314
|
-
objects: Array<{ object: "users"; values: HidSyncUser[] }>;
|
|
315
314
|
users: HidSyncUser[];
|
|
316
|
-
unmappedCount: number;
|
|
317
315
|
stats: { newEnrolled: number; updatedInformation: number; facialData: number };
|
|
318
316
|
defaultMessage: string;
|
|
319
317
|
};
|
|
@@ -331,12 +329,10 @@ const {
|
|
|
331
329
|
updateReader,
|
|
332
330
|
deleteReader,
|
|
333
331
|
testReader,
|
|
334
|
-
syncReaderFacialData,
|
|
335
332
|
configureReaderIntegration,
|
|
336
333
|
setReaderMonitor,
|
|
337
334
|
setReaderOperatingMode,
|
|
338
|
-
|
|
339
|
-
runObjectOperation,
|
|
335
|
+
getReaderUsers,
|
|
340
336
|
} = useHidAmico();
|
|
341
337
|
|
|
342
338
|
const readers = ref<HidRecord[]>([]);
|
|
@@ -594,48 +590,11 @@ async function runConfirmedAction() {
|
|
|
594
590
|
|
|
595
591
|
actionStage.value = "loading";
|
|
596
592
|
const payload = syncPreviewPayload.value;
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
if (!payload.objects.length) {
|
|
600
|
-
const facialSync = await syncReaderFacialData(selectedReader.value._id);
|
|
601
|
-
const facialStats = toFacialSyncStats(facialSync);
|
|
602
|
-
if (unmappedCount) {
|
|
603
|
-
const message = [
|
|
604
|
-
syncMessage.value || `Found ${unmappedCount} HID user(s) that must be linked from the HID User Mapping screen.`,
|
|
605
|
-
facialStats.message,
|
|
606
|
-
].filter(Boolean).join(" ");
|
|
607
|
-
await applyReaderSyncState(selectedReader.value._id, "completed", message);
|
|
608
|
-
showResult(true, "Sync Review Required", message, {
|
|
609
|
-
readerName: selectedReader.value.name || "HID Reader",
|
|
610
|
-
stats: { ...syncPreviewStats.value, facialData: facialStats.syncedCount },
|
|
611
|
-
});
|
|
612
|
-
confirmDialog.value = false;
|
|
613
|
-
resetActionFlow();
|
|
614
|
-
await loadReaders();
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
const message = syncMessage.value || "No active HID users found to sync.";
|
|
619
|
-
await applyReaderSyncState(selectedReader.value._id, "failed", message);
|
|
620
|
-
showResult(false, "Sync Device Failed", message);
|
|
621
|
-
confirmDialog.value = false;
|
|
622
|
-
resetActionFlow();
|
|
623
|
-
await loadReaders();
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
const result = await syncHidUsers(selectedReader.value._id, payload.users);
|
|
627
|
-
const facialSync = await syncReaderFacialData(selectedReader.value._id);
|
|
628
|
-
const facialStats = toFacialSyncStats(facialSync);
|
|
629
|
-
const mappingMessage = unmappedCount
|
|
630
|
-
? `${unmappedCount} HID user(s) still need to be linked from the HID User Mapping screen.`
|
|
631
|
-
: "";
|
|
632
|
-
const message = [syncMessage.value, result.message, facialStats.message, mappingMessage]
|
|
633
|
-
.filter(Boolean)
|
|
634
|
-
.join(" ");
|
|
593
|
+
const message = syncMessage.value || `Read ${payload.users.length} HID user(s) directly from Amico.`;
|
|
635
594
|
await applyReaderSyncState(selectedReader.value._id, "completed", message);
|
|
636
595
|
showResult(true, "Sync Successful", message, {
|
|
637
596
|
readerName: selectedReader.value.name || "HID Reader",
|
|
638
|
-
stats:
|
|
597
|
+
stats: syncPreviewStats.value,
|
|
639
598
|
});
|
|
640
599
|
}
|
|
641
600
|
if (pendingAction.value === "delete") {
|
|
@@ -698,163 +657,31 @@ async function applyReaderSyncState(readerId: string, status: "completed" | "fai
|
|
|
698
657
|
}
|
|
699
658
|
|
|
700
659
|
async function buildSyncPayload(readerId: string) {
|
|
701
|
-
const
|
|
660
|
+
const readerResponse = await getReaderUsers(readerId, {
|
|
702
661
|
page: 1,
|
|
703
|
-
limit:
|
|
704
|
-
|
|
662
|
+
limit: 100,
|
|
663
|
+
includeVisitors: true,
|
|
705
664
|
});
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
limit: 500,
|
|
713
|
-
offset: 0,
|
|
714
|
-
});
|
|
715
|
-
const hidUsers = normalizeHidUsers(hidResponse);
|
|
716
|
-
return {
|
|
717
|
-
objects: [],
|
|
718
|
-
users: [],
|
|
719
|
-
unmappedCount: hidUsers.length,
|
|
720
|
-
stats: {
|
|
721
|
-
newEnrolled: 0,
|
|
722
|
-
updatedInformation: 0,
|
|
723
|
-
facialData: countFacialData(hidUsers),
|
|
724
|
-
},
|
|
725
|
-
defaultMessage: hidUsers.length
|
|
726
|
-
? `Found ${hidUsers.length} HID user(s) that must be linked from the HID User Mapping screen.`
|
|
727
|
-
: "",
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
const allLinkedIds = new Set(
|
|
732
|
-
identities
|
|
733
|
-
.map((identity) => Number(identity.hidUserId))
|
|
734
|
-
.filter((id: number) => Number.isInteger(id) && id > 0),
|
|
735
|
-
);
|
|
736
|
-
const users = identities
|
|
737
|
-
.filter((identity) => (
|
|
738
|
-
identity.type !== "visitor"
|
|
739
|
-
&& (identity.metadata as Record<string, unknown> | undefined)?.temporary !== true
|
|
740
|
-
))
|
|
741
|
-
.map(toHidUserObject)
|
|
665
|
+
const readerData = readerResponse?.data ?? readerResponse;
|
|
666
|
+
const directUsers = Array.isArray(readerResponse?.items)
|
|
667
|
+
? readerResponse.items
|
|
668
|
+
: Array.isArray(readerData?.items) ? readerData.items : [];
|
|
669
|
+
const users = directUsers
|
|
670
|
+
.map((user) => toHidUserObject(user as HidRecord))
|
|
742
671
|
.filter((user): user is HidSyncUser => user !== null);
|
|
743
|
-
const existingHidUsers = await getExistingHidUsers(readerId);
|
|
744
|
-
const existingIds = new Set(existingHidUsers.map((user) => Number(user.hidUserId)));
|
|
745
|
-
const hidUsersToImport = existingHidUsers.filter((user) => {
|
|
746
|
-
const hidUserId = Number(user.hidUserId);
|
|
747
|
-
return Number.isInteger(hidUserId) && hidUserId > 0 && !allLinkedIds.has(hidUserId);
|
|
748
|
-
});
|
|
749
|
-
const newEnrolled = users.filter((user) => !existingIds.has(Number(user.id))).length;
|
|
750
|
-
const updatedInformation = users.filter((user) => existingIds.has(Number(user.id))).length;
|
|
751
|
-
const totalUsersToSync = users.length;
|
|
752
672
|
|
|
753
673
|
return {
|
|
754
|
-
objects: users.length
|
|
755
|
-
? [
|
|
756
|
-
{
|
|
757
|
-
object: "users",
|
|
758
|
-
values: users,
|
|
759
|
-
},
|
|
760
|
-
]
|
|
761
|
-
: [],
|
|
762
674
|
users,
|
|
763
|
-
unmappedCount: hidUsersToImport.length,
|
|
764
675
|
stats: {
|
|
765
|
-
newEnrolled,
|
|
766
|
-
updatedInformation,
|
|
767
|
-
facialData: countFacialData([
|
|
676
|
+
newEnrolled: users.length,
|
|
677
|
+
updatedInformation: 0,
|
|
678
|
+
facialData: countFacialData(directUsers as HidRecord[]),
|
|
768
679
|
},
|
|
769
|
-
defaultMessage:
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
? `${hidUsersToImport.length} HID user(s) must be linked from the HID User Mapping screen.`
|
|
773
|
-
: "",
|
|
774
|
-
].filter(Boolean).join(" "),
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
async function syncHidUsers(readerId: string, users: HidSyncUser[]) {
|
|
779
|
-
const existingIds = await getExistingHidUserIds(readerId);
|
|
780
|
-
const usersToCreate = users.filter((user) => !existingIds.has(Number(user.id)));
|
|
781
|
-
const usersToModify = users.filter((user) => existingIds.has(Number(user.id)));
|
|
782
|
-
const results: HidRecord[] = [];
|
|
783
|
-
|
|
784
|
-
if (usersToModify.length) {
|
|
785
|
-
const modifyResults = [];
|
|
786
|
-
for (const user of usersToModify) {
|
|
787
|
-
const result = await runObjectOperation(readerId, {
|
|
788
|
-
operation: "modify",
|
|
789
|
-
object: "users",
|
|
790
|
-
where: {
|
|
791
|
-
users: {
|
|
792
|
-
id: user.id,
|
|
793
|
-
},
|
|
794
|
-
},
|
|
795
|
-
values: toHidUserValues(user),
|
|
796
|
-
});
|
|
797
|
-
modifyResults.push(result);
|
|
798
|
-
}
|
|
799
|
-
results.push({ operation: "modify", count: usersToModify.length, result: modifyResults });
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (usersToCreate.length) {
|
|
803
|
-
const result = await runObjectOperation(readerId, {
|
|
804
|
-
operation: "create",
|
|
805
|
-
object: "users",
|
|
806
|
-
values: usersToCreate.map(toHidUserCreateValues),
|
|
807
|
-
});
|
|
808
|
-
results.push({ operation: "create", count: usersToCreate.length, result });
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
for (const user of users) {
|
|
812
|
-
await syncHidUserRole(readerId, user.id, user._identityType === "admin");
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
return {
|
|
816
|
-
results,
|
|
817
|
-
message: `Synced ${users.length} HID user(s). ${usersToModify.length} updated, ${usersToCreate.length} created.`,
|
|
680
|
+
defaultMessage: users.length
|
|
681
|
+
? `Read ${users.length} HID user(s) directly from Amico.`
|
|
682
|
+
: "No HID users found on this reader.",
|
|
818
683
|
};
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
async function getExistingHidUserIds(readerId: string) {
|
|
822
|
-
const users = await getExistingHidUsers(readerId);
|
|
823
|
-
return new Set(users.map((user) => Number(user.hidUserId)));
|
|
824
|
-
}
|
|
825
684
|
|
|
826
|
-
async function getExistingHidUsers(readerId: string) {
|
|
827
|
-
const hidResponse = await runObjectOperation(readerId, {
|
|
828
|
-
operation: "load",
|
|
829
|
-
object: "users",
|
|
830
|
-
limit: 500,
|
|
831
|
-
offset: 0,
|
|
832
|
-
});
|
|
833
|
-
return normalizeHidUsers(hidResponse);
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
function normalizeHidUsers(response: HidRecord) {
|
|
837
|
-
const users =
|
|
838
|
-
response?.data?.users ||
|
|
839
|
-
response?.data?.data?.users ||
|
|
840
|
-
response?.users ||
|
|
841
|
-
[];
|
|
842
|
-
|
|
843
|
-
return Array.isArray(users)
|
|
844
|
-
? users
|
|
845
|
-
// Visitor QR passes are linked by their visitor transaction and must not
|
|
846
|
-
// be offered for manual HID user mapping or counted as unmapped users.
|
|
847
|
-
.filter((user) => Number(user?.user_type_id) !== 1)
|
|
848
|
-
.map((user) => ({
|
|
849
|
-
hidUserId: user.id,
|
|
850
|
-
registration: user.registration,
|
|
851
|
-
cardNo: user.card_value || user.cardNo,
|
|
852
|
-
name: user.name,
|
|
853
|
-
metadata: {
|
|
854
|
-
name: user.name,
|
|
855
|
-
},
|
|
856
|
-
}))
|
|
857
|
-
: [];
|
|
858
685
|
}
|
|
859
686
|
|
|
860
687
|
function toHidUserObject(identity: HidRecord): HidSyncUser | null {
|
|
@@ -872,89 +699,6 @@ function toHidUserObject(identity: HidRecord): HidSyncUser | null {
|
|
|
872
699
|
};
|
|
873
700
|
}
|
|
874
701
|
|
|
875
|
-
function toHidUserCreateValues(user: HidSyncUser) {
|
|
876
|
-
return {
|
|
877
|
-
id: user.id,
|
|
878
|
-
...toHidUserValues(user),
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
function toHidUserValues(user: HidSyncUser) {
|
|
883
|
-
return {
|
|
884
|
-
name: user.name,
|
|
885
|
-
registration: user.registration || "",
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
async function syncHidUserRole(readerId: string, hidUserId: unknown, isAdministrator: boolean) {
|
|
890
|
-
const userId = toHidNumericId(hidUserId);
|
|
891
|
-
if (!readerId || !userId) return;
|
|
892
|
-
|
|
893
|
-
const existingRole = await getHidUserRole(readerId, userId);
|
|
894
|
-
if (isAdministrator) {
|
|
895
|
-
if (existingRole) {
|
|
896
|
-
await runObjectOperation(readerId, {
|
|
897
|
-
operation: "modify",
|
|
898
|
-
object: "user_roles",
|
|
899
|
-
where: {
|
|
900
|
-
user_roles: {
|
|
901
|
-
user_id: userId,
|
|
902
|
-
},
|
|
903
|
-
},
|
|
904
|
-
values: {
|
|
905
|
-
role: 1,
|
|
906
|
-
},
|
|
907
|
-
});
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
await runObjectOperation(readerId, {
|
|
912
|
-
operation: "create",
|
|
913
|
-
object: "user_roles",
|
|
914
|
-
values: [
|
|
915
|
-
{
|
|
916
|
-
user_id: userId,
|
|
917
|
-
role: 1,
|
|
918
|
-
},
|
|
919
|
-
],
|
|
920
|
-
});
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
if (existingRole) {
|
|
925
|
-
await runObjectOperation(readerId, {
|
|
926
|
-
operation: "destroy",
|
|
927
|
-
object: "user_roles",
|
|
928
|
-
where: {
|
|
929
|
-
user_roles: {
|
|
930
|
-
user_id: userId,
|
|
931
|
-
},
|
|
932
|
-
},
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
async function getHidUserRole(readerId: string, userId: number) {
|
|
938
|
-
const response = await runObjectOperation(readerId, {
|
|
939
|
-
operation: "load",
|
|
940
|
-
object: "user_roles",
|
|
941
|
-
where: {
|
|
942
|
-
user_roles: {
|
|
943
|
-
user_id: userId,
|
|
944
|
-
},
|
|
945
|
-
},
|
|
946
|
-
limit: 1,
|
|
947
|
-
offset: 0,
|
|
948
|
-
});
|
|
949
|
-
const data = response?.data || response || {};
|
|
950
|
-
const roles =
|
|
951
|
-
data?.user_roles ||
|
|
952
|
-
data?.data?.user_roles ||
|
|
953
|
-
data?.result?.user_roles ||
|
|
954
|
-
[];
|
|
955
|
-
return Array.isArray(roles) ? roles[0] : undefined;
|
|
956
|
-
}
|
|
957
|
-
|
|
958
702
|
function toHidNumericId(value: unknown) {
|
|
959
703
|
const raw = String(value ?? "").trim();
|
|
960
704
|
if (!raw) return undefined;
|
|
@@ -983,16 +727,6 @@ function countFacialData(users: HidRecord[]) {
|
|
|
983
727
|
return users.filter((user) => user.face || user.photo || user.image || user.imageUrl || user.faceImage).length;
|
|
984
728
|
}
|
|
985
729
|
|
|
986
|
-
function toFacialSyncStats(response: unknown) {
|
|
987
|
-
const root = asHidRecord(response);
|
|
988
|
-
const data = asHidRecord(root.data ?? root);
|
|
989
|
-
const syncedCount = Number(data.syncedCount);
|
|
990
|
-
return {
|
|
991
|
-
syncedCount: Number.isSafeInteger(syncedCount) && syncedCount >= 0 ? syncedCount : 0,
|
|
992
|
-
message: String(data.message || "").trim(),
|
|
993
|
-
};
|
|
994
|
-
}
|
|
995
|
-
|
|
996
730
|
function getFriendlyHidError(error: unknown) {
|
|
997
731
|
const message = getHidErrorMessage(error);
|
|
998
732
|
const lowerMessage = message.toLowerCase();
|