@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.
- package/CHANGELOG.md +6 -0
- package/components/AcceptDialog.vue +44 -0
- package/components/AccessCardAddForm.vue +101 -13
- package/components/AccessManagement.vue +130 -47
- package/components/AddSupplyForm.vue +165 -0
- package/components/AreaChecklistHistoryLogs.vue +235 -0
- package/components/AreaChecklistHistoryMain.vue +176 -0
- package/components/AreaFormDialog.vue +266 -0
- package/components/AreaMain.vue +841 -0
- package/components/AttendanceCheckInOutDialog.vue +416 -0
- package/components/AttendanceDetailsDialog.vue +184 -0
- package/components/AttendanceMain.vue +155 -0
- package/components/AttendanceMapSearchDialog.vue +393 -0
- package/components/AttendanceSettingsDialog.vue +398 -0
- package/components/BuildingManagement/buildings.vue +5 -5
- package/components/BuildingManagement/units.vue +5 -5
- package/components/ChecklistItemRow.vue +54 -0
- package/components/CheckoutItemMain.vue +705 -0
- package/components/CleaningScheduleMain.vue +271 -0
- package/components/DocumentManagement.vue +4 -0
- package/components/EntryPass/QrTemplatePreview.vue +104 -0
- package/components/EntryPassMain.vue +252 -200
- package/components/HygieneUpdateMoreAction.vue +238 -0
- package/components/ManageChecklistMain.vue +384 -0
- package/components/MemberMain.vue +48 -20
- package/components/MyAttendanceMain.vue +224 -0
- package/components/OnlineFormsConfiguration.vue +9 -2
- package/components/PhotoUpload.vue +410 -0
- package/components/ScheduleAreaMain.vue +313 -0
- package/components/ScheduleTaskAreaFormDialog.vue +144 -0
- package/components/ScheduleTaskAreaUpdateMoreAction.vue +109 -0
- package/components/ScheduleTaskForm.vue +471 -0
- package/components/ScheduleTaskMain.vue +345 -0
- package/components/ScheduleTastTicketMain.vue +182 -0
- package/components/StockCard.vue +191 -0
- package/components/SupplyManagementMain.vue +557 -0
- package/components/TableHygiene.vue +617 -0
- package/components/UnitMain.vue +451 -0
- package/components/VisitorManagement.vue +28 -15
- package/composables/useAccessManagement.ts +90 -0
- package/composables/useAreaPermission.ts +51 -0
- package/composables/useAreas.ts +99 -0
- package/composables/useAttendance.ts +89 -0
- package/composables/useAttendancePermission.ts +68 -0
- package/composables/useBuilding.ts +2 -2
- package/composables/useBuildingUnit.ts +2 -2
- package/composables/useCard.ts +2 -0
- package/composables/useCheckout.ts +61 -0
- package/composables/useCheckoutPermission.ts +80 -0
- package/composables/useCleaningPermission.ts +229 -0
- package/composables/useCleaningSchedulePermission.ts +58 -0
- package/composables/useCleaningSchedules.ts +233 -0
- package/composables/useCountry.ts +8 -0
- package/composables/useDashboardData.ts +2 -2
- package/composables/useFeedback.ts +1 -1
- package/composables/useLocation.ts +78 -0
- package/composables/useOnlineForm.ts +16 -9
- package/composables/usePeople.ts +87 -72
- package/composables/useQR.ts +29 -0
- package/composables/useScheduleTask.ts +89 -0
- package/composables/useScheduleTaskArea.ts +85 -0
- package/composables/useScheduleTaskPermission.ts +68 -0
- package/composables/useSiteEntryPassSettings.ts +4 -15
- package/composables/useStock.ts +45 -0
- package/composables/useSupply.ts +63 -0
- package/composables/useSupplyPermission.ts +92 -0
- package/composables/useUnitPermission.ts +51 -0
- package/composables/useUnits.ts +82 -0
- package/composables/useWebUsb.ts +389 -0
- package/composables/useWorkOrder.ts +1 -1
- package/nuxt.config.ts +3 -0
- package/package.json +4 -1
- package/types/area.d.ts +22 -0
- package/types/attendance.d.ts +38 -0
- package/types/checkout-item.d.ts +27 -0
- package/types/cleaner-schedule.d.ts +54 -0
- package/types/location.d.ts +42 -0
- package/types/schedule-task.d.ts +18 -0
- package/types/stock.d.ts +16 -0
- package/types/supply.d.ts +11 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: [
|
|
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 ===
|
|
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 =
|
|
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
|
+
}
|