@7365admin1/layer-common 1.10.0 → 1.10.1

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 (81) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/components/AcceptDialog.vue +44 -0
  3. package/components/AccessCardAddForm.vue +101 -13
  4. package/components/AccessManagement.vue +130 -47
  5. package/components/AddSupplyForm.vue +165 -0
  6. package/components/AreaChecklistHistoryLogs.vue +235 -0
  7. package/components/AreaChecklistHistoryMain.vue +176 -0
  8. package/components/AreaFormDialog.vue +266 -0
  9. package/components/AreaMain.vue +841 -0
  10. package/components/AttendanceCheckInOutDialog.vue +416 -0
  11. package/components/AttendanceDetailsDialog.vue +184 -0
  12. package/components/AttendanceMain.vue +155 -0
  13. package/components/AttendanceMapSearchDialog.vue +393 -0
  14. package/components/AttendanceSettingsDialog.vue +398 -0
  15. package/components/BuildingManagement/buildings.vue +5 -5
  16. package/components/BuildingManagement/units.vue +5 -5
  17. package/components/ChecklistItemRow.vue +54 -0
  18. package/components/CheckoutItemMain.vue +705 -0
  19. package/components/CleaningScheduleMain.vue +271 -0
  20. package/components/DocumentManagement.vue +4 -0
  21. package/components/EntryPass/QrTemplatePreview.vue +104 -0
  22. package/components/EntryPassMain.vue +252 -200
  23. package/components/HygieneUpdateMoreAction.vue +238 -0
  24. package/components/ManageChecklistMain.vue +384 -0
  25. package/components/MemberMain.vue +48 -20
  26. package/components/MyAttendanceMain.vue +224 -0
  27. package/components/OnlineFormsConfiguration.vue +9 -2
  28. package/components/PhotoUpload.vue +410 -0
  29. package/components/ScheduleAreaMain.vue +313 -0
  30. package/components/ScheduleTaskAreaFormDialog.vue +144 -0
  31. package/components/ScheduleTaskAreaUpdateMoreAction.vue +109 -0
  32. package/components/ScheduleTaskForm.vue +471 -0
  33. package/components/ScheduleTaskMain.vue +345 -0
  34. package/components/ScheduleTastTicketMain.vue +182 -0
  35. package/components/StockCard.vue +191 -0
  36. package/components/SupplyManagementMain.vue +557 -0
  37. package/components/TableHygiene.vue +617 -0
  38. package/components/UnitMain.vue +451 -0
  39. package/components/VisitorManagement.vue +28 -15
  40. package/composables/useAccessManagement.ts +90 -0
  41. package/composables/useAreaPermission.ts +51 -0
  42. package/composables/useAreas.ts +99 -0
  43. package/composables/useAttendance.ts +89 -0
  44. package/composables/useAttendancePermission.ts +68 -0
  45. package/composables/useBuilding.ts +2 -2
  46. package/composables/useBuildingUnit.ts +2 -2
  47. package/composables/useCard.ts +2 -0
  48. package/composables/useCheckout.ts +61 -0
  49. package/composables/useCheckoutPermission.ts +80 -0
  50. package/composables/useCleaningPermission.ts +229 -0
  51. package/composables/useCleaningSchedulePermission.ts +58 -0
  52. package/composables/useCleaningSchedules.ts +233 -0
  53. package/composables/useCountry.ts +8 -0
  54. package/composables/useDashboardData.ts +2 -2
  55. package/composables/useFeedback.ts +1 -1
  56. package/composables/useLocation.ts +78 -0
  57. package/composables/useOnlineForm.ts +16 -9
  58. package/composables/usePeople.ts +87 -72
  59. package/composables/useQR.ts +29 -0
  60. package/composables/useScheduleTask.ts +89 -0
  61. package/composables/useScheduleTaskArea.ts +85 -0
  62. package/composables/useScheduleTaskPermission.ts +68 -0
  63. package/composables/useSiteEntryPassSettings.ts +4 -15
  64. package/composables/useStock.ts +45 -0
  65. package/composables/useSupply.ts +63 -0
  66. package/composables/useSupplyPermission.ts +92 -0
  67. package/composables/useUnitPermission.ts +51 -0
  68. package/composables/useUnits.ts +82 -0
  69. package/composables/useWebUsb.ts +389 -0
  70. package/composables/useWorkOrder.ts +1 -1
  71. package/nuxt.config.ts +3 -0
  72. package/package.json +4 -1
  73. package/types/area.d.ts +22 -0
  74. package/types/attendance.d.ts +38 -0
  75. package/types/checkout-item.d.ts +27 -0
  76. package/types/cleaner-schedule.d.ts +54 -0
  77. package/types/location.d.ts +42 -0
  78. package/types/schedule-task.d.ts +18 -0
  79. package/types/stock.d.ts +16 -0
  80. package/types/supply.d.ts +11 -0
  81. package/utils/acm-crypto.ts +30 -0
@@ -0,0 +1,451 @@
1
+ <template>
2
+ <v-row no-gutters align="center" justify="center">
3
+ <v-col cols="12" lg="12">
4
+ <TableMain
5
+ :title="`${siteName} Units`"
6
+ :items="items"
7
+ :headers="headers"
8
+ :loading="loading"
9
+ :show-header="true"
10
+ v-model:page="page"
11
+ :pages="pages"
12
+ :pageRange="pageRange"
13
+ :no-data-text="`No units found for ${siteName}.`"
14
+ @refresh="getUnitsRefresh"
15
+ :canCreate="canCreateUnit"
16
+ createLabel="Add Unit"
17
+ @create="onCreateUnit"
18
+ @row-click="handleRowClick"
19
+ >
20
+ <template #actions>
21
+ <v-row no-gutters align="center" class="w-100">
22
+ <v-col cols="auto">
23
+ <div class="d-flex align-center">
24
+ <!-- Primary action -->
25
+ <v-btn
26
+ v-if="canCreateUnit"
27
+ class="text-none mr-3"
28
+ rounded="pill"
29
+ variant="tonal"
30
+ size="large"
31
+ @click="onCreateUnit()"
32
+ >
33
+ Add Unit
34
+ </v-btn>
35
+
36
+ <v-tooltip v-if="canCreateUnit" location="top">
37
+ <template #activator="{ props: t }">
38
+ <v-btn
39
+ v-bind="t"
40
+ class="ma-1"
41
+ variant="tonal"
42
+ size="large"
43
+ rounded="pill"
44
+ href="/files/unit-sample-data.xlsx"
45
+ target="_blank"
46
+ download
47
+ >
48
+ <v-icon>mdi-download</v-icon>
49
+ </v-btn>
50
+ </template>
51
+ <span>Download Template</span>
52
+ </v-tooltip>
53
+
54
+ <v-tooltip v-if="canImportUnit" location="top">
55
+ <template #activator="{ props: t }">
56
+ <v-btn
57
+ v-bind="t"
58
+ class="ma-1"
59
+ variant="tonal"
60
+ size="large"
61
+ rounded="pill"
62
+ @click="onImportData"
63
+ >
64
+ <v-icon>mdi-upload</v-icon>
65
+ </v-btn>
66
+ </template>
67
+ <span>Import Data</span>
68
+ </v-tooltip>
69
+
70
+ <v-tooltip v-if="canViewUnits" location="top">
71
+ <template #activator="{ props: t }">
72
+ <v-btn
73
+ v-bind="t"
74
+ class="ma-1"
75
+ variant="tonal"
76
+ size="large"
77
+ rounded="pill"
78
+ @click="handleDownloadExcel"
79
+ :loading="isDownloading"
80
+ >
81
+ <v-icon>mdi-file-excel</v-icon>
82
+ </v-btn>
83
+ </template>
84
+ <span>Download Excel</span>
85
+ </v-tooltip>
86
+ </div>
87
+ </v-col>
88
+
89
+ <v-spacer />
90
+
91
+ <v-col cols="auto">
92
+ <v-text-field
93
+ v-model="searchInput"
94
+ density="compact"
95
+ placeholder="Search"
96
+ clearable
97
+ width="300"
98
+ append-inner-icon="mdi-magnify"
99
+ hide-details
100
+ />
101
+ </v-col>
102
+ </v-row>
103
+ </template>
104
+
105
+ <template #item.block="{ value }">
106
+ {{ value ? `blk ${value}` : "" }}
107
+ </template>
108
+ </TableMain>
109
+ </v-col>
110
+ </v-row>
111
+
112
+ <AreaFormDialog
113
+ v-model="dialogShowForm"
114
+ v-model:name="areaName"
115
+ :mode="dialogMode"
116
+ :areaData="editingUnit"
117
+ label="Unit Name"
118
+ entityType="Unit"
119
+ :showOptional="false"
120
+ :showType="false"
121
+ @saved="_createUnit"
122
+ />
123
+
124
+ <v-dialog v-model="dialogShowMoreActions" width="400" persistent>
125
+ <HygieneUpdateMoreAction
126
+ :title="selectedUnit?.name || 'Unit Actions'"
127
+ :canUpdate="canUpdateUnit"
128
+ :canDelete="canDeleteUnit"
129
+ :manageButtonLabel="''"
130
+ :editButtonLabel="'Edit Unit'"
131
+ :deleteButtonLabel="'Delete Unit'"
132
+ @close="dialogShowMoreActions = false"
133
+ @edit="onEditFromMoreAction"
134
+ @delete="onDeleteFromMoreAction"
135
+ >
136
+ <template #content>
137
+ <v-row no-gutters>
138
+ <v-col cols="12" class="my-2">
139
+ <span class="text-subtitle-2"
140
+ >Select an action to perform on this unit.</span
141
+ >
142
+ </v-col>
143
+
144
+ <v-col cols="12" class="my-2">
145
+ <span class="text-subtitle-2 text-error">{{ message }}</span>
146
+ </v-col>
147
+ </v-row>
148
+ </template>
149
+ </HygieneUpdateMoreAction>
150
+ </v-dialog>
151
+
152
+ <v-dialog v-model="dialogDeleteUnit" max-width="450">
153
+ <v-card>
154
+ <v-toolbar>
155
+ <v-row no-gutters class="fill-height px-6" align="center">
156
+ <span class="font-weight-bold text-h6 text-capitalize">
157
+ Delete {{ selectedUnit?.name || "Unit" }}
158
+ </span>
159
+ </v-row>
160
+ </v-toolbar>
161
+ <v-card-text>
162
+ <span class="text-subtitle-2"
163
+ >Are you sure you want to delete this unit?</span
164
+ >
165
+ </v-card-text>
166
+
167
+ <v-toolbar class="pa-0" density="compact">
168
+ <v-row no-gutters>
169
+ <v-col cols="6" class="pa-0">
170
+ <v-btn
171
+ block
172
+ variant="text"
173
+ class="text-none"
174
+ size="large"
175
+ @click="dialogDeleteUnit = false"
176
+ height="48"
177
+ >
178
+ Cancel
179
+ </v-btn>
180
+ </v-col>
181
+
182
+ <v-col cols="6" class="pa-0">
183
+ <v-btn
184
+ block
185
+ variant="flat"
186
+ color="black"
187
+ class="text-none font-weight-bold"
188
+ height="48"
189
+ :loading="submitting"
190
+ @click="_deleteUnit"
191
+ >
192
+ Delete
193
+ </v-btn>
194
+ </v-col>
195
+ </v-row>
196
+ </v-toolbar>
197
+ </v-card>
198
+ </v-dialog>
199
+
200
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
201
+
202
+ <input
203
+ ref="fileInput"
204
+ type="file"
205
+ accept=".csv,.xlsx,.xls"
206
+ style="display: none"
207
+ @change="onFileSelected"
208
+ />
209
+ </template>
210
+
211
+ <script setup lang="ts">
212
+ import { useUnitPermission } from "../composables/useUnitPermission";
213
+ import useUnits from "../composables/useUnits";
214
+
215
+ const props = defineProps({
216
+ orgId: { type: String, default: "" },
217
+ site: { type: String, default: "" },
218
+ });
219
+
220
+ const items = ref<Array<Record<string, any>>>([]);
221
+ const siteName = ref<string>("");
222
+ const submitting = ref(false);
223
+ const isDownloading = ref(false);
224
+
225
+ const headers = [{ title: "UNIT", value: "name" }];
226
+
227
+ const { debounce } = useUtils();
228
+ const {
229
+ getUnits,
230
+ createUnit,
231
+ updateUnit,
232
+ deleteUnit,
233
+ uploadUnits,
234
+ downloadUnits,
235
+ } = useUnits();
236
+
237
+ const searchInput = ref("");
238
+ const startDate = ref("");
239
+ const endDate = ref("");
240
+ const page = ref(1);
241
+ const pages = ref(0);
242
+ const pageRange = ref("-- - -- of --");
243
+
244
+ const {
245
+ data: getUnitsReq,
246
+ refresh: getUnitsRefresh,
247
+ pending: loading,
248
+ } = await useLazyAsyncData(
249
+ "get-all-units",
250
+ () =>
251
+ getUnits({
252
+ page: page.value,
253
+ search: searchInput.value,
254
+ site: props.site,
255
+ }),
256
+ {
257
+ watch: [page, () => props.site],
258
+ }
259
+ );
260
+
261
+ watchEffect(() => {
262
+ if (getUnitsReq.value) {
263
+ items.value = getUnitsReq.value.items;
264
+ pages.value = getUnitsReq.value.pages;
265
+ pageRange.value = getUnitsReq.value.pageRange;
266
+ }
267
+ });
268
+
269
+ const debounceSearch = debounce(getUnitsRefresh, 500);
270
+
271
+ watch(
272
+ [searchInput, endDate, startDate],
273
+ ([]) => {
274
+ debounceSearch();
275
+ },
276
+ { immediate: false, deep: true }
277
+ );
278
+
279
+ const selectedUnit = ref<Record<string, any> | null>(null);
280
+ const message = ref("");
281
+ const messageSnackbar = ref(false);
282
+ const messageColor = ref("");
283
+ const fileInput = ref<HTMLInputElement | null>(null);
284
+
285
+ const dialogShowMoreActions = ref(false);
286
+ const dialogShowForm = ref(false);
287
+ const dialogDeleteUnit = ref(false);
288
+ const dialogMode = ref<"add" | "edit">("add");
289
+
290
+ const editingUnit = ref<Record<string, any>>({});
291
+ const areaName = ref("");
292
+
293
+ function showMessage(msg: string, color: string = "error") {
294
+ message.value = msg;
295
+ messageColor.value = color;
296
+ messageSnackbar.value = true;
297
+ }
298
+
299
+ const {
300
+ canCreateUnit,
301
+ canUpdateUnit,
302
+ canDeleteUnit,
303
+ canViewUnits,
304
+ canImportUnit,
305
+ } = useUnitPermission();
306
+
307
+ async function handleDownloadExcel() {
308
+ try {
309
+ isDownloading.value = true;
310
+
311
+ if (!props.site || !/^[0-9a-fA-F]{24}$/.test(props.site)) {
312
+ showMessage("Invalid site id for export.", "error");
313
+ isDownloading.value = false;
314
+ return;
315
+ }
316
+
317
+ const excelBuffer = await downloadUnits(props.site);
318
+
319
+ if (!excelBuffer) {
320
+ showMessage("Failed to download units.", "error");
321
+ return;
322
+ }
323
+
324
+ const blob = new Blob([excelBuffer], {
325
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
326
+ });
327
+ const url = URL.createObjectURL(blob);
328
+ const a = document.createElement("a");
329
+
330
+ const date = new Date();
331
+ const formattedDate = `${String(date.getMonth() + 1).padStart(
332
+ 2,
333
+ "0"
334
+ )}-${String(date.getDate()).padStart(2, "0")}-${date.getFullYear()}`;
335
+
336
+ a.href = url;
337
+ a.download = `units-${siteName.value || "export"}-${formattedDate}.xlsx`;
338
+ document.body.appendChild(a);
339
+ a.click();
340
+ document.body.removeChild(a);
341
+ URL.revokeObjectURL(url);
342
+
343
+ showMessage("Units exported successfully.", "success");
344
+ } catch (error: any) {
345
+ showMessage(error?.message || "Failed to download units.", "error");
346
+ } finally {
347
+ isDownloading.value = false;
348
+ }
349
+ }
350
+
351
+ function onImportData() {
352
+ fileInput.value?.click();
353
+ }
354
+
355
+ async function onFileSelected(event: Event) {
356
+ const target = event.target as HTMLInputElement;
357
+ const file = target.files?.[0];
358
+
359
+ if (!file) return;
360
+
361
+ try {
362
+ submitting.value = true;
363
+
364
+ const response = await uploadUnits(file, props.site);
365
+ showMessage(response?.message, "success");
366
+
367
+ await getUnitsRefresh();
368
+ } catch (err: any) {
369
+ showMessage(err?.data?.message, "error");
370
+ } finally {
371
+ submitting.value = false;
372
+ target.value = "";
373
+ }
374
+ }
375
+
376
+ function onCreateUnit() {
377
+ dialogMode.value = "add";
378
+ editingUnit.value = {};
379
+ dialogShowForm.value = true;
380
+ }
381
+
382
+ async function _createUnit(name: string) {
383
+ try {
384
+ submitting.value = true;
385
+ let response;
386
+ if (dialogMode.value === "edit" && editingUnit.value) {
387
+ const id =
388
+ (editingUnit.value as any)._id || (editingUnit.value as any).id;
389
+ if (!id) throw new Error("Invalid unit id for update");
390
+ response = await updateUnit(id, name);
391
+ } else {
392
+ response = await createUnit(name, props.site);
393
+ }
394
+ showMessage(response?.message, "success");
395
+ dialogShowForm.value = false;
396
+ editingUnit.value = {};
397
+
398
+ await getUnitsRefresh();
399
+ } catch (err: any) {
400
+ showMessage(err?.message, "error");
401
+ } finally {
402
+ submitting.value = false;
403
+ }
404
+ }
405
+
406
+ function editUnit(unit: Record<string, any> | null) {
407
+ if (!unit) return;
408
+ dialogMode.value = "edit";
409
+ editingUnit.value = unit;
410
+ dialogShowForm.value = true;
411
+ }
412
+
413
+ function handleRowClick(data: any) {
414
+ selectedUnit.value = data?.item;
415
+ dialogShowMoreActions.value = true;
416
+ message.value = "";
417
+ }
418
+
419
+ const onEditFromMoreAction = () => {
420
+ dialogShowMoreActions.value = false;
421
+ editUnit(selectedUnit.value);
422
+ };
423
+
424
+ const onDeleteFromMoreAction = () => {
425
+ dialogShowMoreActions.value = false;
426
+ dialogDeleteUnit.value = true;
427
+ };
428
+
429
+ async function _deleteUnit() {
430
+ if (!selectedUnit.value) return;
431
+
432
+ try {
433
+ loading.value = true;
434
+ const id =
435
+ (selectedUnit.value as any)._id || (selectedUnit.value as any).id;
436
+ if (!id) throw new Error("Invalid unit id");
437
+
438
+ const response = await deleteUnit(id);
439
+ dialogDeleteUnit.value = false;
440
+ showMessage(response?.message, "success");
441
+
442
+ await getUnitsRefresh();
443
+ } catch (err: any) {
444
+ const errorMessage = err?.data?.message || err?.message;
445
+ dialogDeleteUnit.value = false;
446
+ showMessage(errorMessage, "error");
447
+ } finally {
448
+ loading.value = false;
449
+ }
450
+ }
451
+ </script>
@@ -49,7 +49,7 @@
49
49
  <v-icon icon="mdi-user" size="15" />
50
50
  <span v-if="item.type === 'contractor'" class="text-capitalize">{{
51
51
  formatCamelCaseToWords(item.contractorType)
52
- }}</span>
52
+ }}</span>
53
53
  <span v-else class="text-capitalize">{{ formatType(item) }}</span>
54
54
  </span>
55
55
  <span class="d-flex align-center ga-2">
@@ -87,14 +87,14 @@
87
87
  <v-icon icon="mdi-clock-time-four-outline" color="green" size="20" />
88
88
  <span class="text-capitalize">{{
89
89
  UTCToLocalTIme(item.checkIn) || "-"
90
- }}</span>
90
+ }}</span>
91
91
  </span>
92
92
  <span class="d-flex align-center ga-2">
93
93
  <v-icon icon="mdi-clock-time-eight-outline" color="red" size="20" v-if="item.checkOut" />
94
94
  <template v-if="item.checkOut">
95
95
  <span class="text-capitalize">{{
96
96
  UTCToLocalTIme(item.checkOut) || "_"
97
- }}</span>
97
+ }}</span>
98
98
  <span v-if="item?.manualCheckout">
99
99
  <TooltipInfo text="Manual Checkout" density="compact" size="x-small" />
100
100
  </span>
@@ -135,7 +135,7 @@
135
135
  </span>
136
136
 
137
137
  <span v-else-if="selectedVisitorObject[key]" class="d-flex ga-3 align-center"><strong>{{ label
138
- }}:</strong>
138
+ }}:</strong>
139
139
  {{ formatValues(key, selectedVisitorObject[key]) }}
140
140
  <TooltipInfo v-if="key === 'checkOut'" text="Manual Checkout" density="compact" size="x-small" />
141
141
  </span>
@@ -290,10 +290,13 @@ const {
290
290
  refresh: getVisitorRefresh,
291
291
  pending: getVisitorPending,
292
292
  } = await useLazyAsyncData(
293
- `get-all-visitors-${activeTab.value}-${page.value}-${siteId}`
294
- ,
295
- () =>
296
- getVisitors({
293
+ `get-all-visitors-${activeTab.value}-${page.value}-${siteId}`,
294
+ async () => {
295
+
296
+ // Optional delay (if needed)
297
+ await new Promise(resolve => setTimeout(resolve, 100))
298
+
299
+ return await getVisitors({
297
300
  page: page.value,
298
301
  site: siteId,
299
302
  search: searchInput.value,
@@ -302,11 +305,21 @@ const {
302
305
  type: filterTypes.value.filter(Boolean).join(","),
303
306
  status: activeTab.value as string,
304
307
  checkedOut: displayNotCheckedOut.value ? false : undefined
305
- }),
308
+ })
309
+ },
306
310
  {
307
- watch: [page, () => route.query],
311
+ watch: [
312
+ page,
313
+ activeTab,
314
+ searchInput,
315
+ dateFrom,
316
+ dateTo,
317
+ filterTypes,
318
+ displayNotCheckedOut,
319
+ () => route.query
320
+ ],
308
321
  }
309
- );
322
+ )
310
323
 
311
324
  watch(getVisitorReq, (newData: any) => {
312
325
  if (newData) {
@@ -388,9 +401,9 @@ function showMessage(msg: string, color: string) {
388
401
  function handleCloseAll() {
389
402
  dialog.showSelection = false;
390
403
  dialog.addVisitor = false;
391
- }
404
+ }
392
405
 
393
- function handleUpdateAutofillDetails(people: TPeople){
406
+ function handleUpdateAutofillDetails(people: TPeople) {
394
407
  dialog.vehicleNumberUsersList = false
395
408
  console.log('people', people)
396
409
  }
@@ -458,7 +471,7 @@ const updateRouteQuery = debounce(
458
471
  dateTo: to || undefined,
459
472
  type: types.filter(Boolean).join(",") || undefined,
460
473
  status: status || undefined,
461
- ...(checkedOut === false ? { checkedOut: "false" } : { checkedOut: undefined })
474
+ ...(checkedOut === true ? { checkedOut: "true" } : { checkedOut: undefined })
462
475
  },
463
476
  });
464
477
  },
@@ -482,7 +495,7 @@ onMounted(() => {
482
495
  dateFrom.value = (route.query.dateFrom as string) || "";
483
496
  dateTo.value = (route.query.dateTo as string) || "";
484
497
  filterTypes.value = ((route.query.type as string)?.split(",") || []).filter(Boolean) as TVisitorType[];
485
- displayNotCheckedOut.value = !!(route.query.checkedOut as string) || false
498
+ displayNotCheckedOut.value = (route.query.checkedOut as string) == 'true' || false
486
499
 
487
500
  })
488
501
  </script>
@@ -0,0 +1,90 @@
1
+ export default function useAccessManagement() {
2
+ function getDoorAccessLevels(acmUrl?: string) {
3
+ return useNuxtApp().$api<Record<string, any>>(
4
+ `/api/access-management/door-access-levels`,
5
+ {
6
+ method: "GET",
7
+ query: {
8
+ ...(acmUrl ? { acm_url: acmUrl } : {}),
9
+ },
10
+ }
11
+ );
12
+ }
13
+
14
+ function getLiftAccessLevels(acmUrl?: string) {
15
+ return useNuxtApp().$api<Record<string, any>>(
16
+ `/api/access-management/lift-access-levels`,
17
+ {
18
+ method: "GET",
19
+ query: {
20
+ ...(acmUrl ? { acm_url: acmUrl } : {}),
21
+ },
22
+ }
23
+ );
24
+ }
25
+
26
+ function getAccessGroups(acmUrl?: string) {
27
+ return useNuxtApp().$api<Record<string, any>>(
28
+ `/api/access-management/access-groups`,
29
+ {
30
+ method: "GET",
31
+ query: {
32
+ ...(acmUrl ? { acm_url: acmUrl } : {}),
33
+ },
34
+ }
35
+ );
36
+ }
37
+
38
+ function addPhysicalCard(payload: Record<string, any>) {
39
+ return useNuxtApp().$api<Record<string, any>>(
40
+ `/api/access-management/physical-card`,
41
+ {
42
+ method: "POST",
43
+ body: payload,
44
+ }
45
+ );
46
+ }
47
+
48
+ function addNonPhysicalCard(payload: Record<string, any>) {
49
+ return useNuxtApp().$api<Record<string, any>>(
50
+ `/api/access-management/non-physical-card`,
51
+ {
52
+ method: "POST",
53
+ body: payload,
54
+ }
55
+ );
56
+ }
57
+
58
+ function getUserTypeAccessCards(params: {
59
+ page?: number;
60
+ limit?: number;
61
+ search?: string;
62
+ organization?: string;
63
+ userType?: string;
64
+ site?: string;
65
+ } = {}) {
66
+ return useNuxtApp().$api<Record<string, any>>(
67
+ `/api/access-management/user-type-access-cards`,
68
+ {
69
+ method: "GET",
70
+ query: {
71
+ ...(params.page ? { page: params.page } : {}),
72
+ ...(params.limit ? { limit: params.limit } : {}),
73
+ ...(params.search ? { search: params.search } : {}),
74
+ ...(params.organization ? { organization: params.organization } : {}),
75
+ ...(params.userType ? { userType: params.userType } : {}),
76
+ ...(params.site ? { site: params.site } : {}),
77
+ },
78
+ }
79
+ );
80
+ }
81
+
82
+ return {
83
+ getDoorAccessLevels,
84
+ getLiftAccessLevels,
85
+ getAccessGroups,
86
+ addPhysicalCard,
87
+ addNonPhysicalCard,
88
+ getUserTypeAccessCards,
89
+ };
90
+ }
@@ -0,0 +1,51 @@
1
+ export function useAreaPermission() {
2
+ const { hasPermission } = usePermission();
3
+ const { permissions } = useCleaningPermission();
4
+
5
+ const { userAppRole } = useLocalSetup();
6
+
7
+ const canViewAreas = computed(() => {
8
+ if (!userAppRole.value) return true;
9
+ if (userAppRole.value.permissions.includes("*")) return true;
10
+ return hasPermission(userAppRole.value, permissions, "area-mgmt", "see-all-areas");
11
+ });
12
+
13
+ const canCreateArea = computed(() => {
14
+ if (!userAppRole.value) return true;
15
+ if (userAppRole.value.permissions.includes("*")) return true;
16
+ return hasPermission(userAppRole.value, permissions, "area-mgmt", "add-area");
17
+ });
18
+
19
+ const canUpdateArea = computed(() => {
20
+ if (!userAppRole.value) return true;
21
+ if (userAppRole.value.permissions.includes("*")) return true;
22
+ return hasPermission(userAppRole.value, permissions, "area-mgmt", "update-area");
23
+ });
24
+
25
+ const canViewAreaDetails = computed(() => {
26
+ if (!userAppRole.value) return true;
27
+ if (userAppRole.value.permissions.includes("*")) return true;
28
+ return hasPermission(userAppRole.value, permissions, "area-mgmt", "see-area-details");
29
+ });
30
+
31
+ const canImportArea = computed(() => {
32
+ if (!userAppRole.value) return true;
33
+ if (userAppRole.value.permissions.includes("*")) return true;
34
+ return hasPermission(userAppRole.value, permissions, "area-mgmt", "import-areas");
35
+ });
36
+
37
+ const canDeleteArea = computed(() => {
38
+ if (!userAppRole.value) return true;
39
+ if (userAppRole.value.permissions.includes("*")) return true;
40
+ return hasPermission(userAppRole.value, permissions, "area-mgmt", "delete-area");
41
+ });
42
+
43
+ return {
44
+ canViewAreas,
45
+ canCreateArea,
46
+ canUpdateArea,
47
+ canViewAreaDetails,
48
+ canImportArea,
49
+ canDeleteArea,
50
+ };
51
+ }