@7365admin1/layer-common 1.9.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 (93) hide show
  1. package/CHANGELOG.md +12 -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 +8 -9
  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/IncidentReport/Authorities.vue +226 -0
  25. package/components/IncidentReport/IncidentInformation.vue +258 -0
  26. package/components/IncidentReport/affectedEntities.vue +167 -0
  27. package/components/InvitationMain.vue +19 -17
  28. package/components/ManageChecklistMain.vue +384 -0
  29. package/components/MemberMain.vue +48 -20
  30. package/components/MyAttendanceMain.vue +224 -0
  31. package/components/OnlineFormsConfiguration.vue +9 -2
  32. package/components/PasswordConfirmation.vue +95 -0
  33. package/components/PhotoUpload.vue +410 -0
  34. package/components/RolePermissionMain.vue +17 -15
  35. package/components/ScheduleAreaMain.vue +313 -0
  36. package/components/ScheduleTaskAreaFormDialog.vue +144 -0
  37. package/components/ScheduleTaskAreaUpdateMoreAction.vue +109 -0
  38. package/components/ScheduleTaskForm.vue +471 -0
  39. package/components/ScheduleTaskMain.vue +345 -0
  40. package/components/ScheduleTastTicketMain.vue +182 -0
  41. package/components/ServiceProviderMain.vue +27 -7
  42. package/components/StockCard.vue +191 -0
  43. package/components/SupplyManagementMain.vue +557 -0
  44. package/components/TableHygiene.vue +617 -0
  45. package/components/UnitMain.vue +451 -0
  46. package/components/VisitorManagement.vue +28 -15
  47. package/composables/useAccessManagement.ts +90 -0
  48. package/composables/useAreaPermission.ts +51 -0
  49. package/composables/useAreas.ts +99 -0
  50. package/composables/useAttendance.ts +89 -0
  51. package/composables/useAttendancePermission.ts +68 -0
  52. package/composables/useBuilding.ts +2 -2
  53. package/composables/useBuildingUnit.ts +2 -2
  54. package/composables/useCard.ts +2 -0
  55. package/composables/useCheckout.ts +61 -0
  56. package/composables/useCheckoutPermission.ts +80 -0
  57. package/composables/useCleaningPermission.ts +229 -0
  58. package/composables/useCleaningSchedulePermission.ts +58 -0
  59. package/composables/useCleaningSchedules.ts +233 -0
  60. package/composables/useCountry.ts +8 -0
  61. package/composables/useDOBEntries.ts +13 -0
  62. package/composables/useDashboardData.ts +2 -2
  63. package/composables/useDocument.ts +3 -2
  64. package/composables/useFeedback.ts +1 -1
  65. package/composables/useFile.ts +4 -6
  66. package/composables/useLocation.ts +78 -0
  67. package/composables/useOnlineForm.ts +16 -9
  68. package/composables/usePeople.ts +87 -72
  69. package/composables/useQR.ts +29 -0
  70. package/composables/useRole.ts +3 -2
  71. package/composables/useScheduleTask.ts +89 -0
  72. package/composables/useScheduleTaskArea.ts +85 -0
  73. package/composables/useScheduleTaskPermission.ts +68 -0
  74. package/composables/useSiteEntryPassSettings.ts +4 -15
  75. package/composables/useStock.ts +45 -0
  76. package/composables/useSupply.ts +63 -0
  77. package/composables/useSupplyPermission.ts +92 -0
  78. package/composables/useUnitPermission.ts +51 -0
  79. package/composables/useUnits.ts +82 -0
  80. package/composables/useWebUsb.ts +389 -0
  81. package/composables/useWorkOrder.ts +1 -1
  82. package/nuxt.config.ts +3 -0
  83. package/package.json +4 -1
  84. package/types/area.d.ts +22 -0
  85. package/types/attendance.d.ts +38 -0
  86. package/types/checkout-item.d.ts +27 -0
  87. package/types/cleaner-schedule.d.ts +54 -0
  88. package/types/location.d.ts +42 -0
  89. package/types/schedule-task.d.ts +18 -0
  90. package/types/stock.d.ts +16 -0
  91. package/types/supply.d.ts +11 -0
  92. package/types/verification.d.ts +1 -1
  93. package/utils/acm-crypto.ts +30 -0
@@ -0,0 +1,313 @@
1
+ <template>
2
+ <v-row no-gutters align="center" justify="center">
3
+ <v-col cols="12" lg="12">
4
+ <TableMain
5
+ :title="'Cleaner Checklist'"
6
+ :headers="headers"
7
+ :items="items"
8
+ :loading="loading"
9
+ :show-header="true"
10
+ v-model:page="page"
11
+ :pages="pages"
12
+ :pageRange="pageRange"
13
+ @update:items="items = $event"
14
+ @row-click="onRowClick"
15
+ @refresh="getScheduleAreasRefresh"
16
+ >
17
+ <template #item.type="{ item }">
18
+ <span class="text-capitalize">{{ item.type }}</span>
19
+ </template>
20
+
21
+ <template #extension>
22
+ <v-row no-gutters class="w-100 d-flex flex-column">
23
+ <v-card
24
+ class="w-100 px-3 d-flex align-center ga-5 py-2"
25
+ flat
26
+ :height="60"
27
+ >
28
+ <v-select
29
+ v-model="statusFilter"
30
+ :items="statusOptions"
31
+ density="compact"
32
+ hide-details
33
+ class="mx-2"
34
+ style="max-width: 160px"
35
+ item-title="title"
36
+ item-value="value"
37
+ label="Status"
38
+ />
39
+
40
+ <v-select
41
+ v-model="areaTypeFilter"
42
+ :items="typeOptions"
43
+ density="compact"
44
+ hide-details
45
+ class="mx-2"
46
+ style="max-width: 160px"
47
+ item-title="title"
48
+ item-value="value"
49
+ label="Type"
50
+ />
51
+ </v-card>
52
+ </v-row>
53
+ </template>
54
+ <template #actions>
55
+ <v-row class="w-100" align="center" no-gutters>
56
+ <v-col cols="auto">
57
+ <v-btn
58
+ variant="text"
59
+ color="primary"
60
+ class="text-none"
61
+ @click="back"
62
+ >
63
+ <v-icon left>mdi-arrow-left</v-icon>
64
+ Back
65
+ </v-btn>
66
+ </v-col>
67
+
68
+ <v-spacer />
69
+
70
+ <v-col cols="auto">
71
+ <v-btn
72
+ v-if="canViewHistory"
73
+ class="text-none mr-2"
74
+ size="large"
75
+ color="primary"
76
+ aria-label="History"
77
+ @click="openHistory"
78
+ >
79
+ History
80
+ </v-btn>
81
+ <v-btn
82
+ v-if="canGenerateChecklist"
83
+ class="text-none"
84
+ size="large"
85
+ color="primary"
86
+ aria-label="Generate"
87
+ @click="_generateChecklist"
88
+ :loading="submitting"
89
+ >
90
+ Generate List
91
+ </v-btn>
92
+ </v-col>
93
+ </v-row>
94
+ </template>
95
+ <template #item.acceptedBy="{ item }">
96
+ <div
97
+ v-if="
98
+ item.acceptedByName ||
99
+ (item.status && String(item.status).toLowerCase() === 'accepted')
100
+ "
101
+ >
102
+ <span class="text-capitalize" color="success" variant="tonal">
103
+ {{ item.acceptedByName || item.acceptedBy || "Accepted" }}
104
+ </span>
105
+ </div>
106
+ <div v-else>
107
+ <v-btn
108
+ v-if="canManageScheduleTasks"
109
+ variant="outlined"
110
+ size="medium"
111
+ class="text-capitalize px-4 py-2"
112
+ @click.stop="() => _acceptAreaChecklist(item._id)"
113
+ >
114
+ Accept
115
+ </v-btn>
116
+ </div>
117
+ </template>
118
+
119
+ <template #item.createdAt="{ value }">
120
+ {{
121
+ formatDate
122
+ ? formatDate(value)
123
+ : value
124
+ ? new Date(value).toLocaleString()
125
+ : ""
126
+ }}
127
+ </template>
128
+
129
+ <template #item.status="{ value }">
130
+ <v-chip
131
+ class="text-capitalize"
132
+ :color="getStatusColor(value)"
133
+ variant="flat"
134
+ pill
135
+ >
136
+ {{ value || "No Status" }}
137
+ </v-chip>
138
+ </template>
139
+ </TableMain></v-col
140
+ >
141
+ </v-row>
142
+
143
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
144
+ </template>
145
+
146
+ <script setup lang="ts">
147
+ import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
148
+ import useCleaningSchedules from "../composables/useCleaningSchedules";
149
+
150
+ const props = defineProps({
151
+ orgId: { type: String, required: true, default: "" },
152
+ site: { type: String, required: true, default: "" },
153
+ scheduleAreaId: { type: String, required: true, default: "" },
154
+ type: { type: String, required: true, default: "cleaner" },
155
+ });
156
+
157
+ const submitting = ref(false);
158
+ const message = ref("");
159
+ const messageSnackbar = ref(false);
160
+ const messageColor = ref("");
161
+ const areaName = ref<string>("");
162
+ const page = ref(1);
163
+ const pages = ref(0);
164
+ const pageRange = ref("-- - -- of --");
165
+
166
+ const headers = [
167
+ { title: "Area", value: "name" },
168
+ { title: "Status", value: "status" },
169
+ { title: "Type", value: "type" },
170
+ { title: "No. of Visits", value: "set" },
171
+ { title: "Created By", value: "createdByName" },
172
+ ];
173
+
174
+ const { formatDate, back } = useUtils();
175
+
176
+ const { canGenerateChecklist, canViewHistory, canManageScheduleTasks } =
177
+ useCleaningSchedulePermission();
178
+
179
+ const items = ref<Array<Record<string, any>>>([]);
180
+
181
+ const statusFilter = ref<TScheduleAreaStatus>("All");
182
+ const statusOptions = [
183
+ { title: "All", value: "All" },
184
+ { title: "Ready", value: "Ready" },
185
+ { title: "Ongoing", value: "Ongoing" },
186
+ { title: "Completed", value: "Completed" },
187
+ ];
188
+ const areaTypeFilter = ref<TAreaType>("all");
189
+ const typeOptions = [
190
+ { title: "All", value: "all" },
191
+ { title: "Common", value: "common" },
192
+ { title: "Toilet", value: "toilet" },
193
+ ];
194
+
195
+ const { getScheduleAreas, acceptAreaChecklist, generateChecklist } =
196
+ useCleaningSchedules();
197
+
198
+ const {
199
+ data: getScheduleAreasReq,
200
+ refresh: getScheduleAreasRefresh,
201
+ pending: loading,
202
+ } = await useLazyAsyncData(
203
+ "get-all-area-cleaner-checklists",
204
+ () =>
205
+ getScheduleAreas({
206
+ page: page.value,
207
+
208
+ scheduleAreaId: props.scheduleAreaId,
209
+ status: statusFilter.value === "All" ? undefined : statusFilter.value,
210
+ type: areaTypeFilter.value === "all" ? undefined : areaTypeFilter.value,
211
+ }),
212
+ {
213
+ watch: [
214
+ page,
215
+ () => props.orgId,
216
+ () => props.site,
217
+ () => props.scheduleAreaId,
218
+ statusFilter,
219
+ areaTypeFilter,
220
+ ],
221
+ }
222
+ );
223
+
224
+ watchEffect(() => {
225
+ if (getScheduleAreasReq.value) {
226
+ items.value = getScheduleAreasReq.value.items;
227
+ pages.value = getScheduleAreasReq.value.pages;
228
+ pageRange.value = getScheduleAreasReq.value.pageRange;
229
+ areaName.value = getScheduleAreasReq.value.areaName;
230
+ }
231
+ });
232
+
233
+ const getStatusColor = (status: unknown): string => {
234
+ if (!status) return "grey";
235
+
236
+ const normalized = String(status).toLowerCase();
237
+
238
+ switch (normalized) {
239
+ case "ready":
240
+ return "grey";
241
+ case "ongoing":
242
+ return "primary";
243
+ case "completed":
244
+ case "accepted":
245
+ return "success";
246
+ default:
247
+ return "secondary";
248
+ }
249
+ };
250
+
251
+ function showMessage(msg: string, color: string = "error") {
252
+ message.value = msg;
253
+ messageColor.value = color;
254
+ messageSnackbar.value = true;
255
+ }
256
+
257
+ function onRowClick(data: any) {
258
+ const item = data?.item;
259
+ const id = item?._id || item?.id || item?.areaId;
260
+ if (id) {
261
+ const checklistType =
262
+ props.type === "cleaner"
263
+ ? "cleaning-schedule"
264
+ : `${props.type}-checklist`;
265
+ const path = `/${props.orgId}/${props.site}/${checklistType}/${props.scheduleAreaId}/${id}`;
266
+ navigateTo(path);
267
+ } else {
268
+ console.warn("Row clicked but no id found to navigate:", item);
269
+ }
270
+ }
271
+
272
+ function openHistory() {
273
+ const path = `/${props.orgId}/${props.site}/cleaning-schedule/${props.scheduleAreaId}/history`;
274
+ navigateTo(path);
275
+ }
276
+
277
+ async function _acceptAreaChecklist(id?: string) {
278
+ if (!id) {
279
+ showMessage("No checklist id provided to accept.", "error");
280
+ return;
281
+ }
282
+
283
+ try {
284
+ submitting.value = true;
285
+ await acceptAreaChecklist(id);
286
+ showMessage("Area checklist accepted successfully.", "success");
287
+
288
+ if (typeof getScheduleAreasRefresh === "function") {
289
+ getScheduleAreasRefresh();
290
+ }
291
+ } catch (error: any) {
292
+ showMessage(error?.message || "Failed to accept area checklist.", "error");
293
+ } finally {
294
+ submitting.value = false;
295
+ }
296
+ }
297
+
298
+ async function _generateChecklist() {
299
+ try {
300
+ submitting.value = true;
301
+ await generateChecklist(props.site, props.scheduleAreaId);
302
+ showMessage("Checklist generated successfully.", "success");
303
+
304
+ if (typeof getScheduleAreasRefresh === "function") {
305
+ getScheduleAreasRefresh();
306
+ }
307
+ } catch (error: any) {
308
+ showMessage(error?.message || "Failed to generate checklist.", "error");
309
+ } finally {
310
+ submitting.value = false;
311
+ }
312
+ }
313
+ </script>
@@ -0,0 +1,144 @@
1
+ <template>
2
+ <v-dialog
3
+ :model-value="modelValue"
4
+ @update:model-value="(v) => emit('update:modelValue', v)"
5
+ max-width="450"
6
+ >
7
+ <v-card>
8
+ <v-toolbar>
9
+ <template v-if="$slots.header">
10
+ <slot
11
+ name="header"
12
+ :title="
13
+ mode === 'edit' ? `Edit ${entityType}` : `Add New ${entityType}`
14
+ "
15
+ :onClose="onClose"
16
+ />
17
+ </template>
18
+ <template v-else>
19
+ <v-row no-gutters class="fill-height px-6" align="center">
20
+ <span class="font-weight-bold text-h6 text-capitalize">{{
21
+ mode === "edit" ? `Edit ${entityType}` : `Add New ${entityType}`
22
+ }}</span>
23
+ </v-row>
24
+ </template>
25
+ </v-toolbar>
26
+
27
+ <v-card-text>
28
+ <v-form ref="formRef" v-model="valid">
29
+ <v-row>
30
+ <v-col cols="12">
31
+ <InputLabel for="name" :title="label" required />
32
+ <v-text-field v-model="nameValue" :rules="[requiredRule]" />
33
+ </v-col>
34
+ </v-row>
35
+ </v-form>
36
+ </v-card-text>
37
+
38
+ <v-toolbar class="pa-0" density="compact">
39
+ <v-row no-gutters>
40
+ <v-col cols="6" class="pa-0">
41
+ <v-btn
42
+ block
43
+ variant="text"
44
+ class="text-none"
45
+ size="large"
46
+ @click="close"
47
+ height="48"
48
+ >
49
+ Cancel
50
+ </v-btn>
51
+ </v-col>
52
+
53
+ <v-col cols="6" class="pa-0">
54
+ <v-btn
55
+ block
56
+ variant="flat"
57
+ color="black"
58
+ class="text-none font-weight-bold rounded-0"
59
+ height="48"
60
+ :loading="submitting"
61
+ @click="submit"
62
+ >
63
+ {{ mode === "edit" ? "Save" : "Create" }}
64
+ </v-btn>
65
+ </v-col>
66
+ </v-row>
67
+ </v-toolbar>
68
+ </v-card>
69
+ </v-dialog>
70
+ </template>
71
+
72
+ <script setup lang="ts">
73
+ const props = defineProps({
74
+ modelValue: { type: Boolean, required: true },
75
+ mode: { type: String as PropType<"add" | "edit">, default: "add" },
76
+ area: { type: Object as PropType<Record<string, any> | null>, default: null },
77
+ label: { type: String, default: "Name" },
78
+ nameModel: { type: String as PropType<string | null>, default: null },
79
+ entityType: { type: String, default: "Area" }, // values: "Area", "Unit"
80
+ });
81
+
82
+ const emit = defineEmits([
83
+ "update:modelValue",
84
+ "saved",
85
+ "update:nameModel",
86
+ ] as const);
87
+
88
+ const formRef = ref(null);
89
+ const valid = ref(false);
90
+ const submitting = ref(false);
91
+
92
+ const form = reactive({ name: "", description: "", status: "active" });
93
+
94
+ watchEffect(() => {
95
+ if (props.area) {
96
+ form.name = props.area.name || "";
97
+ form.description = props.area.description || "";
98
+ form.status = props.area.status || "active";
99
+ } else {
100
+ form.name = "";
101
+ form.description = "";
102
+ form.status = "active";
103
+ }
104
+ });
105
+
106
+ const { requiredRule } = useUtils();
107
+
108
+ const nameValue = computed({
109
+ get: () => {
110
+ return props.nameModel !== null ? props.nameModel : form.name;
111
+ },
112
+ set: (v: string) => {
113
+ if (props.nameModel !== null) {
114
+ emit("update:nameModel", v);
115
+ } else {
116
+ form.name = v;
117
+ }
118
+ },
119
+ });
120
+
121
+ function close() {
122
+ emit("update:modelValue", false);
123
+ }
124
+
125
+ const onClose = () => close();
126
+
127
+ async function submit() {
128
+ if (!valid.value) return;
129
+ submitting.value = true;
130
+ try {
131
+ await new Promise((r) => setTimeout(r, 500));
132
+ emit("saved", { ...form, _id: props.area?._id || Date.now().toString() });
133
+
134
+ form.name = "";
135
+ form.description = "";
136
+ form.status = "active";
137
+
138
+ emit("update:modelValue", false);
139
+ } catch (e) {
140
+ } finally {
141
+ submitting.value = false;
142
+ }
143
+ }
144
+ </script>
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <template v-if="$slots.header">
5
+ <slot name="header" :title="title" :onClose="onClose" />
6
+ </template>
7
+ <template v-else>
8
+ <v-row no-gutters class="fill-height px-6" align="center">
9
+ <span class="font-weight-bold text-h5 text-capitalize">{{
10
+ title
11
+ }}</span>
12
+ </v-row>
13
+ </template>
14
+ </v-toolbar>
15
+
16
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
17
+ <slot name="content" />
18
+ </v-card-text>
19
+
20
+ <v-toolbar class="pa-0" density="compact">
21
+ <v-row no-gutters>
22
+ <v-col cols="6" class="pa-0">
23
+ <v-btn
24
+ block
25
+ variant="text"
26
+ class="text-none"
27
+ size="large"
28
+ @click="emit('close')"
29
+ height="48"
30
+ >
31
+ Close
32
+ </v-btn>
33
+ </v-col>
34
+
35
+ <v-col cols="6" class="pa-0">
36
+ <v-menu>
37
+ <template #activator="{ props }">
38
+ <v-btn
39
+ block
40
+ variant="flat"
41
+ color="black"
42
+ class="text-none"
43
+ height="48"
44
+ v-bind="props"
45
+ tile
46
+ >
47
+ More actions
48
+ </v-btn>
49
+ </template>
50
+
51
+ <v-list class="pa-0">
52
+ <v-list-item v-if="canUpdate" @click="emit('edit')">
53
+ <v-list-item-title class="text-subtitle-2">
54
+ {{ editButtonLabel }}
55
+ </v-list-item-title>
56
+ </v-list-item>
57
+
58
+ <v-list-item
59
+ v-if="canDelete"
60
+ @click="emit('delete')"
61
+ class="text-red"
62
+ >
63
+ <v-list-item-title class="text-subtitle-2">
64
+ {{ deleteButtonLabel }}
65
+ </v-list-item-title>
66
+ </v-list-item>
67
+ </v-list>
68
+ </v-menu>
69
+ </v-col>
70
+ </v-row>
71
+ </v-toolbar>
72
+ </v-card>
73
+ </template>
74
+
75
+ <script setup lang="ts">
76
+ const prop = defineProps({
77
+ canUpdate: {
78
+ type: Boolean,
79
+ default: true,
80
+ },
81
+ canDelete: {
82
+ type: Boolean,
83
+ default: true,
84
+ },
85
+ editButtonLabel: {
86
+ type: String,
87
+ default: "Edit Area",
88
+ },
89
+ deleteButtonLabel: {
90
+ type: String,
91
+ default: "Delete Area",
92
+ },
93
+ title: {
94
+ type: String,
95
+ default: "Area Actions",
96
+ },
97
+ });
98
+
99
+ const emit = defineEmits(["close", "manage", "edit", "delete"]);
100
+ const {
101
+ canUpdate,
102
+ editButtonLabel,
103
+ deleteButtonLabel,
104
+ manageButtonLabel,
105
+ title,
106
+ } = prop;
107
+ </script>
108
+
109
+ <style scoped></style>