@7365admin1/layer-common 3.0.25 → 3.0.27
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 +14 -0
- package/components/AccessCardDetailsDialog.vue +129 -3
- package/components/AccessCardPreviewDialog.vue +31 -3
- package/components/AccessManagement.vue +10 -1
- package/components/AddPassKeyToVisitor.vue +346 -225
- package/components/DashboardMain.vue +95 -59
- package/components/DocumentManagement.vue +71 -5
- package/components/EntryPassInformation.vue +55 -17
- package/components/HidIntercomManagement.vue +186 -0
- package/components/HidReaderForm.vue +10 -16
- package/components/HidReaderManagement.vue +21 -1
- package/components/MemberInformation.vue +59 -1
- package/components/RolePermissionMain.vue +14 -8
- package/components/SiteSettings.vue +3 -1
- package/components/VisitorForm.vue +35 -7
- package/components/VisitorManagement.vue +233 -39
- package/components/VisitorSocketPopUp.vue +29 -1
- package/composables/useANPRSettings.ts +57 -8
- package/composables/useAccessManagement.ts +17 -1
- package/composables/useCleaningPermission.ts +2 -2
- package/composables/useCommonPermission.ts +2 -39
- package/composables/useDocument.ts +21 -0
- package/composables/useLocalAuth.ts +3 -1
- package/package.json +1 -1
- package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/hid-readers/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/hid-users/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +2 -0
|
@@ -151,8 +151,15 @@
|
|
|
151
151
|
>
|
|
152
152
|
</template>
|
|
153
153
|
|
|
154
|
-
<template v-slot:item.email="{
|
|
155
|
-
|
|
154
|
+
<template v-slot:item.email-vehicleNumber="{ item }">
|
|
155
|
+
<span class="d-flex align-center ga-2">
|
|
156
|
+
<v-icon icon="mdi-email" size="15" />
|
|
157
|
+
<span class="text-capitalize">{{ item?.email || "N/A" }}</span>
|
|
158
|
+
</span>
|
|
159
|
+
<span class="d-flex align-center ga-2">
|
|
160
|
+
<v-icon icon="mdi-car-back" size="15" />
|
|
161
|
+
<span class="text-capitalize">{{ item?.plateNumber || "N/A" }}</span>
|
|
162
|
+
</span>
|
|
156
163
|
</template>
|
|
157
164
|
|
|
158
165
|
<template v-slot:item.invited-by="{ item }">
|
|
@@ -353,6 +360,54 @@
|
|
|
353
360
|
/>
|
|
354
361
|
</v-list>
|
|
355
362
|
</v-menu>
|
|
363
|
+
|
|
364
|
+
<!-- NFC Access Cards -->
|
|
365
|
+
<v-menu
|
|
366
|
+
v-if="item.accessCards?.type === 'NFC' && item.accessCards?.cardNo"
|
|
367
|
+
:close-on-content-click="false"
|
|
368
|
+
>
|
|
369
|
+
<template #activator="{ props: nfcMenuProps }">
|
|
370
|
+
<div
|
|
371
|
+
class="d-flex align-center ga-1 mt-1"
|
|
372
|
+
style="cursor: pointer"
|
|
373
|
+
v-bind="nfcMenuProps"
|
|
374
|
+
@click.stop
|
|
375
|
+
>
|
|
376
|
+
<v-icon
|
|
377
|
+
size="15"
|
|
378
|
+
icon="mdi-credit-card-outline"
|
|
379
|
+
color="purple"
|
|
380
|
+
/>
|
|
381
|
+
<v-chip size="x-small" variant="tonal" color="purple">
|
|
382
|
+
{{ item.accessCards.cardNo }}
|
|
383
|
+
<template v-if="(item.cards?.flat().length ?? 0) > 1">
|
|
384
|
+
(+{{ item.cards.flat().length - 1 }})
|
|
385
|
+
</template>
|
|
386
|
+
</v-chip>
|
|
387
|
+
</div>
|
|
388
|
+
</template>
|
|
389
|
+
<v-list density="compact" min-width="200">
|
|
390
|
+
<v-list-subheader>NFC Cards</v-list-subheader>
|
|
391
|
+
<v-list-item
|
|
392
|
+
prepend-icon="mdi-credit-card-outline"
|
|
393
|
+
:title="item.accessCards.cardNo"
|
|
394
|
+
/>
|
|
395
|
+
<template v-for="(cardGroup, gi) in item.cards" :key="gi">
|
|
396
|
+
<v-list-item
|
|
397
|
+
v-for="card in cardGroup.filter((c: any) => c._id !== item.accessCards._id)"
|
|
398
|
+
:key="card._id"
|
|
399
|
+
prepend-icon="mdi-credit-card-outline"
|
|
400
|
+
:title="card._id"
|
|
401
|
+
/>
|
|
402
|
+
</template>
|
|
403
|
+
</v-list>
|
|
404
|
+
</v-menu>
|
|
405
|
+
|
|
406
|
+
<!-- QRCODE identifier -->
|
|
407
|
+
<div v-if="item.accessCards?.type === 'QRCODE' && item.accessCards?.cardNo" class="d-flex align-center ga-1 mt-1">
|
|
408
|
+
<v-icon size="15" icon="mdi-qrcode" color="teal" />
|
|
409
|
+
<v-chip size="x-small" variant="tonal" color="teal">QR · ({{ item.cards?.flat().length ?? 1 }})</v-chip>
|
|
410
|
+
</div>
|
|
356
411
|
</v-row>
|
|
357
412
|
</template>
|
|
358
413
|
|
|
@@ -852,6 +907,46 @@
|
|
|
852
907
|
</v-card>
|
|
853
908
|
</v-dialog>
|
|
854
909
|
|
|
910
|
+
<v-dialog v-model="dialog.returnNfcCard" max-width="450" persistent>
|
|
911
|
+
<v-card>
|
|
912
|
+
<v-toolbar density="compact" color="">
|
|
913
|
+
<v-row no-gutters class="d-flex fill-height justify-space-between align-center px-4">
|
|
914
|
+
<span class="font-weight-bold">Return NFC Card</span>
|
|
915
|
+
<v-btn icon="mdi-close" variant="text" @click="dialog.returnNfcCard = false" />
|
|
916
|
+
</v-row>
|
|
917
|
+
</v-toolbar>
|
|
918
|
+
<v-card-text class="px-4 pb-2">
|
|
919
|
+
<p class="text-body-2 mb-3">Please indicate the status of the NFC card before checking out.</p>
|
|
920
|
+
<div>
|
|
921
|
+
<InputLabel class="text-capitalize" title="Full Name" />
|
|
922
|
+
<v-text-field v-model="selectedVisitorObject.name" density="comfortable" readonly />
|
|
923
|
+
</div>
|
|
924
|
+
<div>
|
|
925
|
+
<InputLabel class="text-capitalize" title="Location" />
|
|
926
|
+
<v-text-field v-model="selectedVisitorObject.location" density="comfortable" readonly />
|
|
927
|
+
</div>
|
|
928
|
+
<div v-for="(card, idx) in nfcCardReturnStatuses" :key="card.cardId" class="mb-4">
|
|
929
|
+
<div class="d-flex align-center ga-2 mb-2">
|
|
930
|
+
<v-icon size="18" icon="mdi-credit-card-outline" color="purple" />
|
|
931
|
+
<v-chip size="small" variant="tonal" color="purple">{{ card.cardNo }}</v-chip>
|
|
932
|
+
</div>
|
|
933
|
+
<v-select v-model="nfcCardReturnStatuses[idx].status" :items="nfcPassStatusOptions" item-title="label" item-value="value" density="comfortable" hide-details />
|
|
934
|
+
<v-textarea v-if="card.status === 'Lost' || card.status === 'Damage'" v-model="nfcCardReturnStatuses[idx].remarks" label="Remarks (required)" no-resize rows="3" class="mt-2" density="compact" />
|
|
935
|
+
</div>
|
|
936
|
+
</v-card-text>
|
|
937
|
+
<v-toolbar class="pa-0" density="compact">
|
|
938
|
+
<v-row no-gutters>
|
|
939
|
+
<v-col cols="6">
|
|
940
|
+
<v-btn variant="text" block @click="dialog.returnNfcCard = false">Close</v-btn>
|
|
941
|
+
</v-col>
|
|
942
|
+
<v-col cols="6">
|
|
943
|
+
<v-btn color="red" variant="flat" height="48" rounded="0" block :loading="loading.checkingOut" :disabled="!canConfirmNfcCheckout" @click="handleNfcCheckout">Confirm Checkout</v-btn>
|
|
944
|
+
</v-col>
|
|
945
|
+
</v-row>
|
|
946
|
+
</v-toolbar>
|
|
947
|
+
</v-card>
|
|
948
|
+
</v-dialog>
|
|
949
|
+
|
|
855
950
|
<v-dialog v-model="dialog.addPassKey" width="450" persistent>
|
|
856
951
|
<AddPassKeyToVisitor
|
|
857
952
|
mode="add"
|
|
@@ -1039,6 +1134,16 @@ const props = defineProps({
|
|
|
1039
1134
|
const { authenticate, currentUser } = useLocalAuth();
|
|
1040
1135
|
authenticate();
|
|
1041
1136
|
|
|
1137
|
+
const route = useRoute();
|
|
1138
|
+
const router = useRouter();
|
|
1139
|
+
const { org: orgId, site: siteId } = route.params as {
|
|
1140
|
+
org: string;
|
|
1141
|
+
site: string;
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
const { enableUnregistered } = useANPRSettings(siteId);
|
|
1145
|
+
const enableUnregisteredTab = computed(() => enableUnregistered.value);
|
|
1146
|
+
|
|
1042
1147
|
const {
|
|
1043
1148
|
getVisitors,
|
|
1044
1149
|
visitorSelection,
|
|
@@ -1046,6 +1151,7 @@ const {
|
|
|
1046
1151
|
updateVisitor,
|
|
1047
1152
|
visitorDataFromScannedQRCodeCheckIn,
|
|
1048
1153
|
} = useVisitor();
|
|
1154
|
+
const { visitorCheckout } = useAccessManagement();
|
|
1049
1155
|
const {
|
|
1050
1156
|
debounce,
|
|
1051
1157
|
formatCamelCaseToWords,
|
|
@@ -1058,12 +1164,6 @@ const { formatLocation } = useSecurityUtils();
|
|
|
1058
1164
|
const { getFileUrlAnpr } = useFile();
|
|
1059
1165
|
// const { status: visitorStatus, search } = useRoute().query as { status: string, search: string};
|
|
1060
1166
|
|
|
1061
|
-
const route = useRoute();
|
|
1062
|
-
const router = useRouter();
|
|
1063
|
-
const { org: orgId, site: siteId } = route.params as {
|
|
1064
|
-
org: string;
|
|
1065
|
-
site: string;
|
|
1066
|
-
};
|
|
1067
1167
|
const routeName = route.name;
|
|
1068
1168
|
|
|
1069
1169
|
const items = ref<Array<Record<string, any>>>([]);
|
|
@@ -1112,6 +1212,7 @@ const dialog = reactive({
|
|
|
1112
1212
|
scanVisitorQRCode: false,
|
|
1113
1213
|
showVisitorDataFromScannedQRCode: false,
|
|
1114
1214
|
approveRejectOvernightParkingRequestDialog: false,
|
|
1215
|
+
returnNfcCard: false,
|
|
1115
1216
|
});
|
|
1116
1217
|
|
|
1117
1218
|
const snapshotImageUrl = ref("");
|
|
@@ -1126,7 +1227,7 @@ const headers = computed(() => {
|
|
|
1126
1227
|
if (tab === "overnight-parking") {
|
|
1127
1228
|
return [
|
|
1128
1229
|
{ title: "Name", value: "name" },
|
|
1129
|
-
{ title: "Email", value: "email" },
|
|
1230
|
+
{ title: "Email/Vehicle No.", value: "email-vehicleNumber" },
|
|
1130
1231
|
{ title: "Invited By", value: "invited-by" },
|
|
1131
1232
|
{ title: "Arrival Date/Time", value: "arrival-date-time" },
|
|
1132
1233
|
{ title: "Check In/Out", value: "checkin-out" },
|
|
@@ -1167,13 +1268,19 @@ const headers = computed(() => {
|
|
|
1167
1268
|
return list;
|
|
1168
1269
|
});
|
|
1169
1270
|
|
|
1170
|
-
const tabOptions =
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1271
|
+
const tabOptions = computed(() => {
|
|
1272
|
+
const baseTabs = [
|
|
1273
|
+
{ name: "Registered", value: "registered" },
|
|
1274
|
+
...(enableUnregisteredTab.value
|
|
1275
|
+
? [{ name: "Unregistered", value: "unregistered" }]
|
|
1276
|
+
: []),
|
|
1277
|
+
{ name: "Guests", value: "guests" },
|
|
1278
|
+
{ name: "Resident Transactions", value: "resident-transactions" },
|
|
1279
|
+
{ name: "Overnight Parking", value: "overnight-parking" },
|
|
1280
|
+
];
|
|
1281
|
+
|
|
1282
|
+
return baseTabs;
|
|
1283
|
+
});
|
|
1177
1284
|
|
|
1178
1285
|
function getOvernightParkingRequestStatusColor(status: string) {
|
|
1179
1286
|
switch (status) {
|
|
@@ -1319,10 +1426,17 @@ const passStatusOptions = [
|
|
|
1319
1426
|
{ label: "Lost", value: "Lost" },
|
|
1320
1427
|
];
|
|
1321
1428
|
|
|
1429
|
+
const nfcPassStatusOptions = [
|
|
1430
|
+
{ label: "Returned", value: "Returned" },
|
|
1431
|
+
{ label: "Not Returned", value: "In Use" },
|
|
1432
|
+
{ label: "Damaged", value: "Damage" },
|
|
1433
|
+
{ label: "Lost", value: "Lost" },
|
|
1434
|
+
];
|
|
1435
|
+
|
|
1322
1436
|
function toRoute(tab: any) {
|
|
1323
1437
|
items.value = [];
|
|
1324
1438
|
|
|
1325
|
-
const obj = tabOptions.find((x) => x.value === tab);
|
|
1439
|
+
const obj = tabOptions.value.find((x) => x.value === tab);
|
|
1326
1440
|
if (!obj) return;
|
|
1327
1441
|
page.value = 1;
|
|
1328
1442
|
getVisitorRefresh();
|
|
@@ -1605,7 +1719,7 @@ function handleRegistrationUnregisteredVisitor(item: Partial<TVisitor>) {
|
|
|
1605
1719
|
dialog.showSelection = true;
|
|
1606
1720
|
mode.value = "register";
|
|
1607
1721
|
|
|
1608
|
-
if(!selectedVisitorDataObject.value){
|
|
1722
|
+
if (!selectedVisitorDataObject.value) {
|
|
1609
1723
|
getVisitorRefresh();
|
|
1610
1724
|
}
|
|
1611
1725
|
}
|
|
@@ -1703,6 +1817,9 @@ const passReturnStatuses = ref<
|
|
|
1703
1817
|
const keyReturnStatuses = ref<
|
|
1704
1818
|
{ keyId: string; status: string; prefixAndName: string; remarks: string }[]
|
|
1705
1819
|
>([]);
|
|
1820
|
+
const nfcCardReturnStatuses = ref<
|
|
1821
|
+
{ cardId: string; cardNo: string; status: string; remarks: string }[]
|
|
1822
|
+
>([]);
|
|
1706
1823
|
|
|
1707
1824
|
const canConfirmCheckout = computed(() => {
|
|
1708
1825
|
const allEntries = [...passReturnStatuses.value, ...keyReturnStatuses.value];
|
|
@@ -1717,6 +1834,15 @@ const canConfirmCheckout = computed(() => {
|
|
|
1717
1834
|
});
|
|
1718
1835
|
});
|
|
1719
1836
|
|
|
1837
|
+
const canConfirmNfcCheckout = computed(() => {
|
|
1838
|
+
if (nfcCardReturnStatuses.value.length === 0) return false;
|
|
1839
|
+
return nfcCardReturnStatuses.value.every(({ status, remarks }) => {
|
|
1840
|
+
if (!status || status === "In Use") return false;
|
|
1841
|
+
if ((status === "Lost" || status === "Damage") && !remarks.trim()) return false;
|
|
1842
|
+
return true;
|
|
1843
|
+
});
|
|
1844
|
+
});
|
|
1845
|
+
|
|
1720
1846
|
function handleCheckin(user: Record<string, any>) {
|
|
1721
1847
|
visitorDataFromScannedQRCode.value = user;
|
|
1722
1848
|
dialog.showVisitorDataFromScannedQRCode = true;
|
|
@@ -1755,6 +1881,22 @@ function handleCheckout(userId: string) {
|
|
|
1755
1881
|
return;
|
|
1756
1882
|
}
|
|
1757
1883
|
|
|
1884
|
+
const hasNfc = visitor?.accessCards?.type === "NFC" && !!visitor?.accessCards?.cardNo;
|
|
1885
|
+
if (hasNfc) {
|
|
1886
|
+
const allCards: any[] = visitor.cards?.flat() ?? [];
|
|
1887
|
+
nfcCardReturnStatuses.value = allCards.map((card: any) => ({
|
|
1888
|
+
cardId: card._id,
|
|
1889
|
+
cardNo: card._id === visitor.accessCards._id ? visitor.accessCards.cardNo : card._id,
|
|
1890
|
+
status: "In Use",
|
|
1891
|
+
remarks: "",
|
|
1892
|
+
}));
|
|
1893
|
+
if (nfcCardReturnStatuses.value.length === 0) {
|
|
1894
|
+
nfcCardReturnStatuses.value = [{ cardId: visitor.accessCards._id, cardNo: visitor.accessCards.cardNo, status: "In Use", remarks: "" }];
|
|
1895
|
+
}
|
|
1896
|
+
dialog.returnNfcCard = true;
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1758
1900
|
proceedCheckout();
|
|
1759
1901
|
}
|
|
1760
1902
|
|
|
@@ -1803,6 +1945,34 @@ async function proceedCheckout() {
|
|
|
1803
1945
|
}
|
|
1804
1946
|
}
|
|
1805
1947
|
|
|
1948
|
+
async function handleNfcCheckout() {
|
|
1949
|
+
const userId = selectedVisitorId.value;
|
|
1950
|
+
if (!userId) return;
|
|
1951
|
+
try {
|
|
1952
|
+
loading.checkingOut = true;
|
|
1953
|
+
const res = await visitorCheckout({
|
|
1954
|
+
cards: nfcCardReturnStatuses.value.map(({ cardId, status, remarks }) => ({
|
|
1955
|
+
cardId,
|
|
1956
|
+
userId,
|
|
1957
|
+
status,
|
|
1958
|
+
remarks: remarks ?? "",
|
|
1959
|
+
})),
|
|
1960
|
+
});
|
|
1961
|
+
if (res) {
|
|
1962
|
+
await updateVisitor(userId as string, { checkOut: new Date().toISOString() });
|
|
1963
|
+
showMessage("Visitor successfully checked-out!", "info");
|
|
1964
|
+
await getVisitorRefresh();
|
|
1965
|
+
dialog.returnNfcCard = false;
|
|
1966
|
+
dialog.viewVisitor = false;
|
|
1967
|
+
}
|
|
1968
|
+
} catch (error: any) {
|
|
1969
|
+
const errorMessage = error?.response?._data?.message;
|
|
1970
|
+
showMessage(errorMessage || "Something went wrong. Please try again later.", "error");
|
|
1971
|
+
} finally {
|
|
1972
|
+
loading.checkingOut = false;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1806
1976
|
function buildReportParams(): any {
|
|
1807
1977
|
const params: any = {
|
|
1808
1978
|
site: siteId,
|
|
@@ -2021,7 +2191,12 @@ watch([searchInput, dateFrom, dateTo, filterTypes, activeTab], () => {
|
|
|
2021
2191
|
});
|
|
2022
2192
|
|
|
2023
2193
|
onMounted(() => {
|
|
2024
|
-
|
|
2194
|
+
const requestedTab = (route.query.tab as string) || "registered";
|
|
2195
|
+
activeTab.value = enableUnregisteredTab.value
|
|
2196
|
+
? requestedTab
|
|
2197
|
+
: requestedTab === "unregistered"
|
|
2198
|
+
? "registered"
|
|
2199
|
+
: requestedTab;
|
|
2025
2200
|
searchInput.value = (route.query.search as string) || "";
|
|
2026
2201
|
if (route.query.dateFrom) {
|
|
2027
2202
|
dateFrom.value = normalizeDateOnly(route.query.dateFrom as string);
|
|
@@ -2042,29 +2217,48 @@ onMounted(() => {
|
|
|
2042
2217
|
const { socketUnregisteredVisitorTrigger, visitorSocketData } =
|
|
2043
2218
|
useVisitorSocket();
|
|
2044
2219
|
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2220
|
+
watch(
|
|
2221
|
+
enableUnregisteredTab,
|
|
2222
|
+
(enabled) => {
|
|
2223
|
+
if (!enabled && activeTab.value === "unregistered") {
|
|
2224
|
+
activeTab.value = "registered";
|
|
2050
2225
|
}
|
|
2226
|
+
},
|
|
2227
|
+
{ immediate: true }
|
|
2228
|
+
);
|
|
2051
2229
|
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2230
|
+
watchEffect(async () => {
|
|
2231
|
+
if (
|
|
2232
|
+
visitorSocketData.value &&
|
|
2233
|
+
visitorSocketData.value.reload &&
|
|
2234
|
+
activeTab.value === "unregistered"
|
|
2235
|
+
) {
|
|
2236
|
+
await getVisitorRefresh();
|
|
2237
|
+
visitorSocketData.value = null;
|
|
2238
|
+
socketUnregisteredVisitorTrigger.value = false;
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
if (
|
|
2242
|
+
visitorSocketData.value &&
|
|
2243
|
+
visitorSocketData.value.plateNumber &&
|
|
2244
|
+
socketUnregisteredVisitorTrigger.value &&
|
|
2245
|
+
activeTab.value === "unregistered" &&
|
|
2246
|
+
dialog.showSelection === false
|
|
2247
|
+
) {
|
|
2248
|
+
// await getVisitorRefresh();
|
|
2249
|
+
// visitorSocketData.value = null;
|
|
2250
|
+
// socketUnregisteredVisitorTrigger.value = false;
|
|
2251
|
+
setTimeout(async () => {
|
|
2252
|
+
const { _id } = visitorSocketData.value;
|
|
2253
|
+
await handleRegistrationUnregisteredVisitor({ _id });
|
|
2254
|
+
socketUnregisteredVisitorTrigger.value = false;
|
|
2067
2255
|
|
|
2256
|
+
setTimeout(() => {
|
|
2257
|
+
visitorSocketData.value = null;
|
|
2258
|
+
}, 1000);
|
|
2259
|
+
}, 1000);
|
|
2260
|
+
}
|
|
2261
|
+
});
|
|
2068
2262
|
</script>
|
|
2069
2263
|
|
|
2070
2264
|
<style scoped></style>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
</section> -->
|
|
33
33
|
|
|
34
34
|
<v-snackbar
|
|
35
|
-
v-if="!isUnregisteredVisitorRoute"
|
|
35
|
+
v-if="!isUnregisteredVisitorRoute && enableUnregisteredSnackbar && canViewVisitor"
|
|
36
36
|
v-model="snackBarMessage.modal"
|
|
37
37
|
:color="snackBarMessage.color"
|
|
38
38
|
:timeout="snackBarMessage.timeout"
|
|
@@ -94,12 +94,20 @@ const props = defineProps({
|
|
|
94
94
|
type: String,
|
|
95
95
|
default: "",
|
|
96
96
|
},
|
|
97
|
+
canViewVisitor: {
|
|
98
|
+
type: Boolean,
|
|
99
|
+
default: false,
|
|
100
|
+
},
|
|
97
101
|
});
|
|
98
102
|
|
|
99
103
|
const config = useRuntimeConfig();
|
|
100
104
|
const publicConfig = config.public as Record<string, string | undefined>;
|
|
101
105
|
const route = useRoute();
|
|
102
106
|
const { tab } = useVisitor();
|
|
107
|
+
const { enableUnregistered, ensureANPRSettings } = useANPRSettings(
|
|
108
|
+
props.siteId
|
|
109
|
+
);
|
|
110
|
+
const enableUnregisteredSnackbar = computed(() => enableUnregistered.value);
|
|
103
111
|
const { socketUnregisteredVisitorTrigger, visitorSocketData } =
|
|
104
112
|
useVisitorSocket();
|
|
105
113
|
|
|
@@ -124,6 +132,16 @@ const permanentMessageDialog = reactive({
|
|
|
124
132
|
text: "",
|
|
125
133
|
});
|
|
126
134
|
|
|
135
|
+
watch(
|
|
136
|
+
() => props.siteId,
|
|
137
|
+
async (siteId) => {
|
|
138
|
+
if (siteId && !route.path.includes("/settings")) {
|
|
139
|
+
await ensureANPRSettings();
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{ immediate: true }
|
|
143
|
+
);
|
|
144
|
+
|
|
127
145
|
const path = computed(() => route.path);
|
|
128
146
|
const isVisitorManagementPath = (path: string) =>
|
|
129
147
|
path.includes("/visitor-management");
|
|
@@ -166,6 +184,10 @@ const showTransientMessage = (
|
|
|
166
184
|
color: string,
|
|
167
185
|
isUnregistered = false
|
|
168
186
|
) => {
|
|
187
|
+
if (!enableUnregisteredSnackbar.value) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
169
191
|
snackBarMessage.text = text;
|
|
170
192
|
snackBarMessage.color = color;
|
|
171
193
|
snackBarMessage.modal = true;
|
|
@@ -283,6 +305,12 @@ watch(
|
|
|
283
305
|
{ immediate: true }
|
|
284
306
|
);
|
|
285
307
|
|
|
308
|
+
watch(enableUnregisteredSnackbar, (enabled) => {
|
|
309
|
+
if (!enabled) {
|
|
310
|
+
snackBarMessage.modal = false;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
286
314
|
onUnmounted(() => {
|
|
287
315
|
disconnectSocket();
|
|
288
316
|
});
|
|
@@ -1,26 +1,75 @@
|
|
|
1
|
-
export default function () {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export default function (siteId?: string) {
|
|
2
|
+
const stateKey = computed(() =>
|
|
3
|
+
siteId ? `anpr-settings-${siteId}` : "anpr-settings"
|
|
4
|
+
);
|
|
5
|
+
|
|
6
|
+
const anprSettings = useState<Record<string, any> | null>(
|
|
7
|
+
stateKey.value,
|
|
8
|
+
() => null
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const enableUnregistered = computed(
|
|
12
|
+
() => anprSettings.value?.ANPRSwitches?.enableUnregistered === true
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
async function fetchANPRSettings(data?: any) {
|
|
16
|
+
const payload = data ?? (siteId ? { site: siteId, userType: "site" } : undefined);
|
|
17
|
+
|
|
18
|
+
if (!payload) {
|
|
19
|
+
return anprSettings.value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const response = await useNuxtApp().$api<Record<string, any>>(
|
|
4
23
|
`/api/anpr-settings/get/v1`,
|
|
5
24
|
{
|
|
6
25
|
method: "POST",
|
|
7
|
-
body:
|
|
26
|
+
body: payload,
|
|
8
27
|
}
|
|
9
28
|
);
|
|
29
|
+
|
|
30
|
+
if (response) {
|
|
31
|
+
anprSettings.value = response;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return response;
|
|
10
35
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
36
|
+
|
|
37
|
+
async function ensureANPRSettings(force = false) {
|
|
38
|
+
if (!siteId) {
|
|
39
|
+
return anprSettings.value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!force && anprSettings.value) {
|
|
43
|
+
return anprSettings.value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return await fetchANPRSettings({ site: siteId, userType: "site" });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function getANPRSettingBySite(data: any) {
|
|
50
|
+
return await fetchANPRSettings(data);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function createUpdateANPRSettings(data: any) {
|
|
54
|
+
const response = await useNuxtApp().$api<Record<string, any>>(
|
|
15
55
|
`/api/anpr-settings/v1`,
|
|
16
56
|
{
|
|
17
57
|
method: "POST",
|
|
18
58
|
body: data,
|
|
19
59
|
}
|
|
20
60
|
);
|
|
61
|
+
|
|
62
|
+
if (response) {
|
|
63
|
+
anprSettings.value = response;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return response;
|
|
21
67
|
}
|
|
22
68
|
|
|
23
69
|
return {
|
|
70
|
+
anprSettings,
|
|
71
|
+
enableUnregistered,
|
|
72
|
+
ensureANPRSettings,
|
|
24
73
|
getANPRSettingBySite,
|
|
25
74
|
createUpdateANPRSettings,
|
|
26
75
|
};
|
|
@@ -268,7 +268,15 @@ export default function useAccessManagement() {
|
|
|
268
268
|
type: string;
|
|
269
269
|
nfcCards: { _id: string; cardNo: string }[];
|
|
270
270
|
acm_url: string;
|
|
271
|
-
|
|
271
|
+
user: {
|
|
272
|
+
visitorId: string;
|
|
273
|
+
name?: string;
|
|
274
|
+
nric?: string;
|
|
275
|
+
contact?: string;
|
|
276
|
+
company?: string;
|
|
277
|
+
type?: string;
|
|
278
|
+
plateNumber?: string;
|
|
279
|
+
};
|
|
272
280
|
}) {
|
|
273
281
|
return useNuxtApp().$api<Record<string, any>>(
|
|
274
282
|
`/api/access-management/visitor`,
|
|
@@ -313,6 +321,13 @@ export default function useAccessManagement() {
|
|
|
313
321
|
);
|
|
314
322
|
}
|
|
315
323
|
|
|
324
|
+
function visitorCheckout(payload: { cards: { cardId: string; userId: string; status: string; remarks: string }[] }) {
|
|
325
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
326
|
+
`/api/access-management/visitor-checkout`,
|
|
327
|
+
{ method: "PATCH", body: payload }
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
316
331
|
return {
|
|
317
332
|
getResidents,
|
|
318
333
|
assignUser,
|
|
@@ -337,5 +352,6 @@ export default function useAccessManagement() {
|
|
|
337
352
|
createVisitorPass,
|
|
338
353
|
signQr,
|
|
339
354
|
getBlockLevelUnitList,
|
|
355
|
+
visitorCheckout,
|
|
340
356
|
};
|
|
341
357
|
}
|
|
@@ -6,7 +6,7 @@ export default function useCleaningPermission() {
|
|
|
6
6
|
workOrderPermissions,
|
|
7
7
|
invitationPermissions,
|
|
8
8
|
bulletinBoardPermissions,
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
} = useCommonPermissions();
|
|
11
11
|
const permissions: TPermissions = {
|
|
12
12
|
members: memberPermissions,
|
|
@@ -15,7 +15,7 @@ export default function useCleaningPermission() {
|
|
|
15
15
|
roles: rolePermissions,
|
|
16
16
|
invitations: invitationPermissions,
|
|
17
17
|
"bulletin-board": bulletinBoardPermissions,
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
inventory: {
|
|
20
20
|
"view-inventory": {
|
|
21
21
|
check: true,
|
|
@@ -279,44 +279,7 @@ export function useCommonPermissions() {
|
|
|
279
279
|
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
|
|
283
|
-
"view-activity": {
|
|
284
|
-
check: true,
|
|
285
|
-
description: "Allows the user to view the activity chart widget.",
|
|
286
|
-
},
|
|
287
|
-
"view-task-schedule": {
|
|
288
|
-
check: true,
|
|
289
|
-
description: "Allows the user to view the task schedule widget.",
|
|
290
|
-
},
|
|
291
|
-
"view-attendance-widget": {
|
|
292
|
-
check: true,
|
|
293
|
-
description: "Allows the user to view the attendance/staff status widget.",
|
|
294
|
-
},
|
|
295
|
-
"view-feedbacks-widget": {
|
|
296
|
-
check: true,
|
|
297
|
-
description: "Allows the user to view the feedbacks widget.",
|
|
298
|
-
},
|
|
299
|
-
"view-upcoming-events": {
|
|
300
|
-
check: true,
|
|
301
|
-
description: "Allows the user to view the upcoming events widget.",
|
|
302
|
-
},
|
|
303
|
-
"view-today-reminders": {
|
|
304
|
-
check: true,
|
|
305
|
-
description: "Allows the user to view the today's reminders widget.",
|
|
306
|
-
},
|
|
307
|
-
"view-today-attentions": {
|
|
308
|
-
check: true,
|
|
309
|
-
description: "Allows the user to view the today's attentions widget.",
|
|
310
|
-
},
|
|
311
|
-
"view-work-order-status": {
|
|
312
|
-
check: true,
|
|
313
|
-
description: "Allows the user to view the work order status widget.",
|
|
314
|
-
},
|
|
315
|
-
"view-active-patrol": {
|
|
316
|
-
check: true,
|
|
317
|
-
description: "Allows the user to view the active patrol widget.",
|
|
318
|
-
},
|
|
319
|
-
}
|
|
282
|
+
|
|
320
283
|
|
|
321
284
|
return {
|
|
322
285
|
invitationPermissions,
|
|
@@ -329,6 +292,6 @@ export function useCommonPermissions() {
|
|
|
329
292
|
buildingUnitManagementPermissions,
|
|
330
293
|
bulletinBoardPermissions,
|
|
331
294
|
siteSettingsPermissions,
|
|
332
|
-
|
|
295
|
+
|
|
333
296
|
};
|
|
334
297
|
}
|
|
@@ -76,6 +76,25 @@ export default function useDocument() {
|
|
|
76
76
|
return add({ ...value, type: "folder" });
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
function updateFolderById(id: string, value: any) {
|
|
80
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
81
|
+
`/api/documents/folder/${id}`,
|
|
82
|
+
{
|
|
83
|
+
method: "PUT",
|
|
84
|
+
body: value,
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function deleteFolderById(id: string) {
|
|
90
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
91
|
+
`/api/documents/folder/${id}`,
|
|
92
|
+
{
|
|
93
|
+
method: "DELETE",
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
79
98
|
return {
|
|
80
99
|
getAll,
|
|
81
100
|
getById,
|
|
@@ -85,5 +104,7 @@ export default function useDocument() {
|
|
|
85
104
|
getDocumentsBySiteId,
|
|
86
105
|
create,
|
|
87
106
|
createFolder,
|
|
107
|
+
updateFolderById,
|
|
108
|
+
deleteFolderById,
|
|
88
109
|
};
|
|
89
110
|
}
|