@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,345 @@
1
+ <template>
2
+ <v-row no-gutters align="center" justify="center">
3
+ <v-col cols="12">
4
+ <TableMain
5
+ :title="'Schedule Tasks'"
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 schedule tasks found'"
14
+ @refresh="getTaskRefresh"
15
+ :canCreate="canCreateScheduleTask"
16
+ createLabel="Create Schedule Task"
17
+ @create="openFormDialog"
18
+ @row-click="onRowClick"
19
+ >
20
+ <template #actions>
21
+ <v-row no-gutters align="center" class="w-100">
22
+ <v-col cols="auto" v-if="canCreateScheduleTask">
23
+ <v-btn
24
+ class="text-none"
25
+ rounded="pill"
26
+ variant="tonal"
27
+ size="large"
28
+ @click="openFormDialog"
29
+ >
30
+ Create Schedule Task
31
+ </v-btn>
32
+ </v-col>
33
+
34
+ <v-spacer />
35
+
36
+ <v-col cols="auto">
37
+ <v-text-field
38
+ v-model="searchInput"
39
+ density="compact"
40
+ placeholder="Search"
41
+ clearable
42
+ width="300"
43
+ append-inner-icon="mdi-magnify"
44
+ hide-details
45
+ />
46
+ </v-col>
47
+ </v-row>
48
+ </template>
49
+
50
+ <!-- frequency removed: using explicit date range in details -->
51
+
52
+ <template #item.areas="{ value }">
53
+ <div v-if="value && value.length > 0">
54
+ <v-chip
55
+ v-for="(area, idx) in value.slice(0, 2)"
56
+ :key="idx"
57
+ size="small"
58
+ class="mr-1 mb-1"
59
+ >
60
+ {{ area.name }}
61
+ </v-chip>
62
+ <v-chip v-if="value.length > 2" size="small" class="mb-1">
63
+ +{{ value.length - 2 }} more
64
+ </v-chip>
65
+ </div>
66
+ <span v-else>N/A</span>
67
+ </template>
68
+
69
+ <template #item.status="{ value }">
70
+ <v-chip
71
+ :color="value === 'active' ? 'success' : 'default'"
72
+ class="text-capitalize"
73
+ >
74
+ {{ value || "N/A" }}
75
+ </v-chip>
76
+ </template>
77
+ </TableMain>
78
+
79
+ <ScheduleTaskForm
80
+ v-model:show="showForm"
81
+ :mode="formMode"
82
+ :taskData="selectedTask"
83
+ :site="props.site"
84
+ @refresh="getTaskRefresh"
85
+ />
86
+
87
+ <v-dialog v-model="dialogShowMoreActions" width="400" persistent>
88
+ <HygieneUpdateMoreAction
89
+ :title="selectedTask?.title || 'Schedule Task Actions'"
90
+ :canUpdate="canUpdateScheduleTask"
91
+ @close="dialogShowMoreActions = false"
92
+ :canDelete="false"
93
+ @edit="onEditFromMoreAction"
94
+ :editButtonLabel="'Edit Schedule Task'"
95
+ >
96
+ <template #content>
97
+ <v-row no-gutters>
98
+ <v-col v-if="selectedTask" cols="12" class="mb-2">
99
+ <v-row no-gutters class="mb-2">
100
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
101
+ Title:
102
+ </v-col>
103
+ <v-col cols="7" class="text-subtitle-2">
104
+ {{ selectedTask.title || "N/A" }}
105
+ </v-col>
106
+ </v-row>
107
+
108
+ <v-row no-gutters class="mb-2">
109
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
110
+ Date Range:
111
+ </v-col>
112
+ <v-col cols="7" class="text-subtitle-2">
113
+ <div v-if="selectedTask.startDate || selectedTask.endDate">
114
+ {{
115
+ selectedTask.startDate
116
+ ? formatDate(selectedTask.startDate)
117
+ : "N/A"
118
+ }}
119
+ -
120
+ {{
121
+ selectedTask.endDate
122
+ ? formatDate(selectedTask.endDate)
123
+ : "N/A"
124
+ }}
125
+ </div>
126
+ <div v-else>N/A</div>
127
+ </v-col>
128
+ </v-row>
129
+
130
+ <v-row v-if="selectedTask.time" no-gutters class="mb-2">
131
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
132
+ Time:
133
+ </v-col>
134
+ <v-col cols="7" class="text-subtitle-2">
135
+ {{ selectedTask.time || "N/A" }}
136
+ </v-col>
137
+ </v-row>
138
+
139
+ <v-row
140
+ v-if="selectedTask.areas && selectedTask.areas.length > 0"
141
+ no-gutters
142
+ class="mb-2"
143
+ >
144
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
145
+ Areas:
146
+ </v-col>
147
+ <v-col cols="7" class="text-subtitle-2">
148
+ <div
149
+ v-for="(area, idx) in selectedTask.areas"
150
+ :key="idx"
151
+ class="mb-1"
152
+ >
153
+ {{ area.name || area }}
154
+ </div>
155
+ </v-col>
156
+ </v-row>
157
+
158
+ <v-row v-if="selectedTask.description" no-gutters class="mb-2">
159
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
160
+ Description:
161
+ </v-col>
162
+ <v-col cols="7" class="text-subtitle-2">
163
+ {{ selectedTask.description || "N/A" }}
164
+ </v-col>
165
+ </v-row>
166
+
167
+ <v-row v-if="selectedTask.status" no-gutters class="mb-2">
168
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
169
+ Status:
170
+ </v-col>
171
+ <v-col cols="7" class="text-subtitle-2">
172
+ <v-chip
173
+ :color="
174
+ selectedTask.status === 'active' ? 'success' : 'default'
175
+ "
176
+ size="small"
177
+ class="text-capitalize"
178
+ >
179
+ {{ selectedTask.status || "N/A" }}
180
+ </v-chip>
181
+ </v-col>
182
+ </v-row>
183
+
184
+ <v-row v-if="selectedTask.createdAt" no-gutters class="mb-2">
185
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
186
+ Created At:
187
+ </v-col>
188
+ <v-col cols="7" class="text-subtitle-2">
189
+ {{ formatDate(selectedTask.createdAt) || "N/A" }}
190
+ </v-col>
191
+ </v-row>
192
+ </v-col>
193
+
194
+ <v-col v-if="message" cols="12" class="my-2">
195
+ <span class="text-subtitle-2 text-error">{{ message }}</span>
196
+ </v-col>
197
+ </v-row>
198
+ </template>
199
+ </HygieneUpdateMoreAction>
200
+ </v-dialog>
201
+
202
+ <Snackbar
203
+ v-model="messageSnackbar"
204
+ :text="message"
205
+ :color="messageColor"
206
+ />
207
+ </v-col>
208
+ </v-row>
209
+ </template>
210
+
211
+ <script lang="ts" setup>
212
+ import useScheduleTask from "../composables/useScheduleTask";
213
+ import useScheduleTaskPermission from "../composables/useScheduleTaskPermission";
214
+
215
+ const props = defineProps({
216
+ orgId: { type: String, required: true },
217
+ site: { type: String, required: true },
218
+ type: { type: String, default: "toilet" },
219
+ });
220
+
221
+ const { formatDate, debounce } = useUtils();
222
+ const { getScheduleTasks, getScheduleTaskById } = useScheduleTask();
223
+ const {
224
+ canViewScheduleTasks,
225
+ canCreateScheduleTask,
226
+ canUpdateScheduleTask,
227
+ canDeleteScheduleTask,
228
+ canViewScheduleTaskDetails,
229
+ } = useScheduleTaskPermission();
230
+
231
+ const page = ref(1);
232
+ const pages = ref(0);
233
+ const pageRange = ref("-- - -- of --");
234
+
235
+ const searchInput = ref("");
236
+ const showForm = ref(false);
237
+ const selectedTask = ref<Record<string, any>>({});
238
+ const formMode = ref<"add" | "edit">("add");
239
+ const submitting = ref(false);
240
+ const dialogShowMoreActions = ref(false);
241
+ const dialogDeleteTask = ref(false);
242
+
243
+ const message = ref("");
244
+ const messageSnackbar = ref(false);
245
+ const messageColor = ref("success");
246
+
247
+ const items = ref<any[]>([]);
248
+
249
+ const headers = [
250
+ { title: "Title", value: "title" },
251
+ { title: "Areas", value: "areas" },
252
+ { title: "Status", value: "status" },
253
+ ];
254
+
255
+ const showMessage = (text: string, color: string = "success") => {
256
+ message.value = text;
257
+ messageColor.value = color;
258
+ messageSnackbar.value = true;
259
+ };
260
+
261
+ const mapShortToDay = (short: string) => {
262
+ const mapping: Record<string, string> = {
263
+ Mon: "Monday",
264
+ Tue: "Tuesday",
265
+ Wed: "Wednesday",
266
+ Thu: "Thursday",
267
+ Fri: "Friday",
268
+ Sat: "Saturday",
269
+ Sun: "Sunday",
270
+ };
271
+ return mapping[short] || short;
272
+ };
273
+
274
+ const formatTaskDays = (dayField: any) => {
275
+ if (!dayField) return "N/A";
276
+ if (Array.isArray(dayField))
277
+ return dayField.map((d) => mapShortToDay(d)).join(", ");
278
+ return String(dayField);
279
+ };
280
+
281
+ const {
282
+ data: getTaskReq,
283
+ refresh: getTaskRefresh,
284
+ pending: loading,
285
+ } = await useLazyAsyncData(
286
+ `get-schedule-tasks-${props.site}`,
287
+ () =>
288
+ getScheduleTasks({
289
+ page: page.value,
290
+ site: props.site,
291
+ search: searchInput.value,
292
+ }),
293
+ {
294
+ watch: [page, () => props.site],
295
+ }
296
+ );
297
+
298
+ watchEffect(() => {
299
+ if (getTaskReq.value) {
300
+ items.value = getTaskReq.value.items || getTaskReq.value || [];
301
+ pages.value = getTaskReq.value.pages || 0;
302
+ pageRange.value = getTaskReq.value.pageRange || "-- - -- of --";
303
+ }
304
+ });
305
+
306
+ const debounceSearch = debounce(getTaskRefresh, 500);
307
+
308
+ watch(
309
+ [searchInput],
310
+ () => {
311
+ debounceSearch();
312
+ },
313
+ { immediate: false, deep: true }
314
+ );
315
+
316
+ function openFormDialog() {
317
+ formMode.value = "add";
318
+ selectedTask.value = {};
319
+ showForm.value = true;
320
+ }
321
+
322
+ async function onRowClick(data: any) {
323
+ const taskId = data?.item?._id || data?._id || data?.item?.id || data?.id;
324
+
325
+ if (taskId) {
326
+ try {
327
+ const fullTaskData = await getScheduleTaskById(taskId);
328
+ selectedTask.value = fullTaskData || data?.item || data;
329
+ } catch (error) {
330
+ selectedTask.value = data?.item || data;
331
+ }
332
+ } else {
333
+ selectedTask.value = data?.item || data;
334
+ }
335
+
336
+ dialogShowMoreActions.value = true;
337
+ message.value = "";
338
+ }
339
+
340
+ const onEditFromMoreAction = async () => {
341
+ dialogShowMoreActions.value = false;
342
+ formMode.value = "edit";
343
+ showForm.value = true;
344
+ };
345
+ </script>
@@ -0,0 +1,182 @@
1
+ <template>
2
+ <v-row no-gutters align="center" justify="center">
3
+ <v-col cols="12" lg="10">
4
+ <TableMain
5
+ :title="'Schedule Tasks'"
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 Schedule Tasks found'"
14
+ @row-click="onRowClick"
15
+ >
16
+ <template #item.task="{ item }">
17
+ {{ item.task || "N/A" }}
18
+ </template>
19
+
20
+ <template #item.createdAt="{ value }">
21
+ {{
22
+ formatDate
23
+ ? formatDate(value)
24
+ : value
25
+ ? new Date(value).toLocaleDateString()
26
+ : ""
27
+ }}
28
+ </template>
29
+
30
+ <template #item.area="{ value }">
31
+ {{ value || "N/A" }}
32
+ </template>
33
+
34
+ <template #item.createdBy="{ item }">
35
+ <v-avatar
36
+ size="32"
37
+ class="mr-2"
38
+ :color="getAvatarColor(item.createdBy)"
39
+ >
40
+ <span class="white--text">{{ getInitials(item.createdBy) }}</span>
41
+ </v-avatar>
42
+ {{ item.createdBy || "N/A" }}
43
+ </template>
44
+
45
+ <template #item.cleaners="{ value }">
46
+ {{ value || "N/A" }}
47
+ </template>
48
+
49
+ <template #item.status="{ value }">
50
+ <v-chip
51
+ :color="getStatusColor(value)"
52
+ class="font-weight-bold"
53
+ style="background: #ffe6c2; color: #ff9800"
54
+ >
55
+ {{ value || "To-Do" }}
56
+ </v-chip>
57
+ </template>
58
+ <!-- Completion Date/Time -->
59
+ <template #item.completionDate="{ value }">
60
+ {{ value ? formatDate(value) : "N/A" }}
61
+ </template>
62
+ <!-- Action (Accept button) -->
63
+ <template #item.action="{ item }">
64
+ <v-btn outlined class="accept-btn" @click.stop="acceptTask(item)">
65
+ Accept
66
+ </v-btn>
67
+ </template>
68
+ </TableMain>
69
+
70
+ <!-- Accept Confirmation Dialog -->
71
+ <AcceptDialog
72
+ :model-value="showAcceptDialog"
73
+ title="Accept Scheduled Task"
74
+ message="Are you sure you want to Accept this Scheduled Task?"
75
+ @confirm="confirmAccept"
76
+ @cancel="showAcceptDialog = false"
77
+ />
78
+
79
+ <!-- ScheduleTaskForm Dialog -->
80
+ <ScheduleTaskForm v-model:show="showForm" />
81
+ </v-col>
82
+ </v-row>
83
+ </template>
84
+
85
+ <script lang="ts" setup>
86
+ const props = defineProps({
87
+ orgId: { type: String, required: true },
88
+ site: { type: String, required: true },
89
+ type: { type: String, required: true },
90
+ });
91
+ const loading = ref(false);
92
+ const page = ref(1);
93
+ const pages = ref(1);
94
+ const pageRange = ref("1-3 of 3");
95
+ const showForm = ref(false);
96
+
97
+ const showAcceptDialog = ref(false);
98
+ const selectedTask = ref(null);
99
+
100
+ const items = ref([
101
+ {
102
+ _id: "1",
103
+ task: "Clean Windows",
104
+ createdAt: "2025-10-28T10:00:00Z",
105
+ area: "",
106
+ createdBy: "Demo Smith",
107
+ cleaners: "N/A",
108
+ status: "To-Do",
109
+ completionDate: "2025-10-28T10:00:00Z",
110
+ },
111
+ {
112
+ _id: "2",
113
+ task: "Clean The 3",
114
+ createdAt: "2025-10-28T10:00:00Z",
115
+ area: "",
116
+ createdBy: "Hygiene SP",
117
+ cleaners: "N/A",
118
+ status: "To-Do",
119
+ completionDate: "2025-10-28T10:00:00Z",
120
+ },
121
+ ]);
122
+
123
+ const headers = [
124
+ { title: "Task", value: "task" },
125
+ { title: "Date Created", value: "createdAt" },
126
+ { title: "Area", value: "area" },
127
+ { title: "Created By", value: "createdBy" },
128
+ { title: "Cleaners", value: "cleaners" },
129
+ { title: "Status", value: "status" },
130
+ { title: "Completion Date/Time", value: "completionDate" },
131
+ { title: "Action", value: "action" },
132
+ ];
133
+
134
+ const { formatDate } = useUtils();
135
+
136
+ function getInitials(name: string) {
137
+ if (!name) return "";
138
+ return name
139
+ .split(" ")
140
+ .map((n) => n[0])
141
+ .join("")
142
+ .toUpperCase();
143
+ }
144
+ function getAvatarColor(name: string) {
145
+ if (name === "Demo Smith") return "purple";
146
+ if (name === "Hygiene SP") return "blue";
147
+ return "grey";
148
+ }
149
+ function getStatusColor(status: string) {
150
+ if (status === "To-Do") return "orange";
151
+ if (status === "Completed") return "green";
152
+ return "grey";
153
+ }
154
+ function acceptTask(item: any) {
155
+ selectedTask.value = item;
156
+ showAcceptDialog.value = true;
157
+ }
158
+ function confirmAccept() {
159
+ // Handle accept logic here (e.g., update status, API call)
160
+ showAcceptDialog.value = false;
161
+ }
162
+ function onRowClick(data: any) {
163
+ const item = data?.item ?? data;
164
+ const id = item?._id || item?.id;
165
+ if (id) {
166
+ const path = `/${props.orgId}/${props.site}/schedule-tasks-ticket/${id}`;
167
+ navigateTo(path);
168
+ } else {
169
+ console.warn("Row clicked but no id found to navigate:", item);
170
+ }
171
+ }
172
+ </script>
173
+
174
+ <style scoped>
175
+ .accept-btn {
176
+ border: 1px solid #333;
177
+ background: #fff;
178
+ border-radius: 4px;
179
+ cursor: pointer;
180
+ min-width: 80px;
181
+ }
182
+ </style>