@7365admin1/layer-common 3.0.24 → 3.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/components/AccessCardAddForm.vue +2 -2
  3. package/components/AccessCardAssignToUnitForm.vue +2 -2
  4. package/components/AccessCardDetailsDialog.vue +31 -1
  5. package/components/AccessManagement.vue +16 -6
  6. package/components/AddPassKeyToVisitor.vue +346 -225
  7. package/components/BuildingUnitFormEdit.vue +1 -1
  8. package/components/DashboardMain.vue +124 -60
  9. package/components/DocumentManagement.vue +294 -1
  10. package/components/HidIntercomManagement.vue +186 -0
  11. package/components/HidReaderManagement.vue +21 -1
  12. package/components/Nfc/NFCPatrolReportMain.vue +1 -0
  13. package/components/Nfc/PatrolReport/PatrolReportTab.vue +2 -1
  14. package/components/Nfc/PatrolReport/ReportFilters.vue +3 -1
  15. package/components/RolePermissionMain.vue +34 -5
  16. package/components/SiteSettings.vue +3 -1
  17. package/components/VisitorForm.vue +174 -398
  18. package/components/VisitorManagement.vue +302 -679
  19. package/components/VisitorSocketPopUp.vue +29 -1
  20. package/composables/useANPRSettings.ts +57 -8
  21. package/composables/useAccessManagement.ts +9 -1
  22. package/composables/useCleaningPermission.ts +2 -2
  23. package/composables/useCommonPermission.ts +2 -39
  24. package/composables/useDocument.ts +21 -0
  25. package/composables/useLocalAuth.ts +3 -1
  26. package/composables/useNFCPatrolLog.ts +35 -0
  27. package/composables/useNFCPatrolReport.ts +37 -4
  28. package/composables/useNFCPatrolReportFilters.ts +28 -5
  29. package/package.json +1 -1
  30. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -0
  31. package/pages/[org]/[site]/access-mgmt/hid-readers/index.vue +2 -0
  32. package/pages/[org]/[site]/access-mgmt/hid-users/index.vue +2 -0
  33. package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +2 -0
  34. package/services/nfcPatrolReport.ts +5 -1
  35. package/types/nfc-patrol-report.ts +7 -1
  36. package/types/visitor.d.ts +1 -0
@@ -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
- async function getANPRSettingBySite(data: any) {
3
- return await useNuxtApp().$api<Record<string, any>>(
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: data,
26
+ body: payload,
8
27
  }
9
28
  );
29
+
30
+ if (response) {
31
+ anprSettings.value = response;
32
+ }
33
+
34
+ return response;
10
35
  }
11
- async function createUpdateANPRSettings(
12
- data: any
13
- ) {
14
- return await useNuxtApp().$api<Record<string, any>>(
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
- visitorId: string;
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`,
@@ -6,7 +6,7 @@ export default function useCleaningPermission() {
6
6
  workOrderPermissions,
7
7
  invitationPermissions,
8
8
  bulletinBoardPermissions,
9
- dashboardPermissions,
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
- dashboard: dashboardPermissions,
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
- const dashboardPermissions: Record<string, TPermission> = {
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
- dashboardPermissions
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
  }
@@ -41,7 +41,9 @@ export default function useLocalAuth() {
41
41
  );
42
42
 
43
43
  watchEffect(() => {
44
- currentUser.value = getCurrentUserReq.value as TUser | null;
44
+ if (getCurrentUserReq.value) {
45
+ currentUser.value = getCurrentUserReq.value as TUser;
46
+ }
45
47
  });
46
48
 
47
49
  watchEffect(() => {
@@ -0,0 +1,35 @@
1
+ interface NFCPatrolLogQuery {
2
+ page?: number;
3
+ limit?: number;
4
+ site: string;
5
+ date: string;
6
+ routeId: string;
7
+ routeStartTime: string;
8
+ }
9
+
10
+ export default function useNFCPatrolLog() {
11
+ function getAll<T = Record<string, any>>({
12
+ page = 1,
13
+ limit = 10,
14
+ site,
15
+ date,
16
+ routeId,
17
+ routeStartTime,
18
+ }: NFCPatrolLogQuery) {
19
+ return useNuxtApp().$api<T>("/api/nfc-patrol-log", {
20
+ method: "GET",
21
+ query: {
22
+ page,
23
+ limit,
24
+ site,
25
+ date,
26
+ "route._id": routeId,
27
+ "route.startTime": routeStartTime,
28
+ },
29
+ });
30
+ }
31
+
32
+ return {
33
+ getAll,
34
+ };
35
+ }
@@ -3,25 +3,58 @@ import type {
3
3
  NFCPatrolReport,
4
4
  NFCPatrolReportFilters,
5
5
  } from "../types/nfc-patrol-report";
6
- import { useNFCPatrolReportService } from "../services/nfcPatrolReport";
7
6
 
8
- export function useNFCPatrolReport(filters: NFCPatrolReportFilters) {
9
- const reportService = useNFCPatrolReportService();
7
+ interface NFCPatrolLogResponse {
8
+ data?: NFCPatrolLogResponse | NFCPatrolReport;
9
+ item?: NFCPatrolReport;
10
+ items?: NFCPatrolReport[];
11
+ nfcPatrolLog?: NFCPatrolReport;
12
+ }
13
+
14
+ export function useNFCPatrolReport(
15
+ filters: NFCPatrolReportFilters,
16
+ site: string,
17
+ ) {
18
+ const { getAll: getPatrolLogs } = useNFCPatrolLog();
10
19
  const report = ref<NFCPatrolReport | null>(null);
11
20
  const loading = ref(false);
12
21
  const selectedActivityRemarks = ref("");
13
22
  const dialogRemarks = ref(false);
14
23
 
15
24
  async function fetchReport() {
25
+ if (!site || !filters.date || !filters.route || !filters.timeRange) {
26
+ report.value = null;
27
+ return;
28
+ }
29
+
16
30
  loading.value = true;
17
31
 
18
32
  try {
19
- report.value = await reportService.getPatrolReport(filters);
33
+ const response = await getPatrolLogs<NFCPatrolLogResponse>({
34
+ page: 1,
35
+ limit: 10,
36
+ site,
37
+ date: filters.date,
38
+ routeId: filters.route,
39
+ routeStartTime: filters.timeRange.split(" - ")[0],
40
+ });
41
+
42
+ report.value = extractReport(response);
20
43
  } finally {
21
44
  loading.value = false;
22
45
  }
23
46
  }
24
47
 
48
+ function extractReport(response: NFCPatrolLogResponse): NFCPatrolReport | null {
49
+ const payload = response.data ?? response;
50
+
51
+ if ("location" in payload && "activities" in payload) {
52
+ return payload;
53
+ }
54
+
55
+ return payload.nfcPatrolLog ?? payload.item ?? payload.items?.[0] ?? null;
56
+ }
57
+
25
58
  function openRemarksDialog(activity: NFCPatrolActivity) {
26
59
  selectedActivityRemarks.value = activity.remarks || "No remarks provided";
27
60
  dialogRemarks.value = true;
@@ -2,14 +2,13 @@ import type {
2
2
  NFCPatrolReportFilterOptions,
3
3
  NFCPatrolReportFilters,
4
4
  } from "../types/nfc-patrol-report";
5
- import { useNFCPatrolReportService } from "../services/nfcPatrolReport";
6
5
 
7
6
  export function useNFCPatrolReportFilters(site: string) {
8
- const reportService = useNFCPatrolReportService();
7
+ const { getAll: getPatrolRoutes } = useNFCPatrolRoute();
9
8
 
10
9
  const filters = reactive<NFCPatrolReportFilters>({
11
- route: "WH01 Patrol Route",
12
- timeRange: "15:00 - 17:00",
10
+ route: "",
11
+ timeRange: "",
13
12
  date: "2025-11-27",
14
13
  });
15
14
 
@@ -24,12 +23,36 @@ export function useNFCPatrolReportFilters(site: string) {
24
23
  loading.value = true;
25
24
 
26
25
  try {
27
- options.value = await reportService.getFilterOptions(site);
26
+ const response = await getPatrolRoutes({ site, page: 1, limit: 100 });
27
+ const routes = (response.items ?? []).map((route: Record<string, any>) => ({
28
+ title: route.name,
29
+ value: route._id,
30
+ startTimes: route.startTimes ?? [],
31
+ }));
32
+
33
+ options.value.routes = routes;
34
+
35
+ if (!routes.some((route) => route.value === filters.route)) {
36
+ filters.route = routes[0]?.value ?? "";
37
+ }
38
+
39
+ updateStartTimes();
28
40
  } finally {
29
41
  loading.value = false;
30
42
  }
31
43
  }
32
44
 
45
+ function updateStartTimes() {
46
+ const route = options.value.routes.find(({ value }) => value === filters.route);
47
+ options.value.timeRanges = route?.startTimes ?? [];
48
+
49
+ if (!options.value.timeRanges.includes(filters.timeRange)) {
50
+ filters.timeRange = options.value.timeRanges[0] ?? "";
51
+ }
52
+ }
53
+
54
+ watch(() => filters.route, updateStartTimes);
55
+
33
56
  return {
34
57
  filters,
35
58
  options,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.0.24",
5
+ "version": "3.0.26",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
@@ -13,6 +13,8 @@
13
13
  <script setup lang="ts">
14
14
  definePageMeta({
15
15
  layout: "default",
16
+ middleware: ["01-auth", "02-org"],
17
+ memberOnly: true,
16
18
  });
17
19
 
18
20
  const route = useRoute();
@@ -13,6 +13,8 @@
13
13
  <script setup lang="ts">
14
14
  definePageMeta({
15
15
  layout: "default",
16
+ middleware: ["01-auth", "02-org"],
17
+ memberOnly: true,
16
18
  });
17
19
 
18
20
  const route = useRoute();
@@ -13,6 +13,8 @@
13
13
  <script setup lang="ts">
14
14
  definePageMeta({
15
15
  layout: "default",
16
+ middleware: ["01-auth", "02-org"],
17
+ memberOnly: true,
16
18
  });
17
19
 
18
20
  const route = useRoute();
@@ -13,6 +13,8 @@
13
13
  <script setup lang="ts">
14
14
  definePageMeta({
15
15
  layout: "default",
16
+ middleware: ["01-auth", "02-org"],
17
+ memberOnly: true,
16
18
  });
17
19
 
18
20
  const route = useRoute();
@@ -7,7 +7,11 @@ import type {
7
7
  } from "../types/nfc-patrol-report";
8
8
 
9
9
  const filterOptions: NFCPatrolReportFilterOptions = {
10
- routes: ["WH01 Patrol Route", "WH02 Patrol Route", "WH03 Patrol Route"],
10
+ routes: [
11
+ { title: "WH01 Patrol Route", value: "WH01 Patrol Route", startTimes: [] },
12
+ { title: "WH02 Patrol Route", value: "WH02 Patrol Route", startTimes: [] },
13
+ { title: "WH03 Patrol Route", value: "WH03 Patrol Route", startTimes: [] },
14
+ ],
11
15
  timeRanges: ["15:00 - 17:00", "08:00 - 10:00", "13:00 - 15:00", "18:00 - 20:00"],
12
16
  };
13
17
 
@@ -13,8 +13,14 @@ export interface NFCPatrolReportExportRequest {
13
13
  filters: NFCPatrolReportFilters;
14
14
  }
15
15
 
16
+ export interface NFCPatrolReportRouteOption {
17
+ title: string;
18
+ value: string;
19
+ startTimes: string[];
20
+ }
21
+
16
22
  export interface NFCPatrolReportFilterOptions {
17
- routes: string[];
23
+ routes: NFCPatrolReportRouteOption[];
18
24
  timeRanges: string[];
19
25
  }
20
26
 
@@ -71,6 +71,7 @@ declare type TVisitorPayload = Pick<
71
71
  | "createdBy"
72
72
  > & {
73
73
  members?: TMemberInfo[];
74
+ updatedBy?: string;
74
75
  };
75
76
 
76
77
  declare type TDefaultOptionObj = {