@7365admin1/layer-common 1.10.0 → 1.10.2

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 (86) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/AcceptDialog.vue +44 -0
  3. package/components/AccessCard/AvailableStats.vue +55 -0
  4. package/components/AccessCardAddForm.vue +284 -19
  5. package/components/AccessCardAssignToUnitForm.vue +440 -0
  6. package/components/AccessManagement.vue +218 -85
  7. package/components/AddSupplyForm.vue +165 -0
  8. package/components/AreaChecklistHistoryLogs.vue +235 -0
  9. package/components/AreaChecklistHistoryMain.vue +176 -0
  10. package/components/AreaFormDialog.vue +266 -0
  11. package/components/AreaMain.vue +863 -0
  12. package/components/AttendanceCheckInOutDialog.vue +416 -0
  13. package/components/AttendanceDetailsDialog.vue +184 -0
  14. package/components/AttendanceMain.vue +155 -0
  15. package/components/AttendanceMapSearchDialog.vue +393 -0
  16. package/components/AttendanceSettingsDialog.vue +398 -0
  17. package/components/BuildingManagement/buildings.vue +5 -5
  18. package/components/BuildingManagement/units.vue +5 -5
  19. package/components/BulletinBoardManagement.vue +322 -0
  20. package/components/ChecklistItemRow.vue +54 -0
  21. package/components/CheckoutItemMain.vue +705 -0
  22. package/components/CleaningScheduleMain.vue +271 -0
  23. package/components/DocumentManagement.vue +4 -0
  24. package/components/EntryPass/QrTemplatePreview.vue +104 -0
  25. package/components/EntryPassMain.vue +252 -200
  26. package/components/HygieneUpdateMoreAction.vue +238 -0
  27. package/components/ManageChecklistMain.vue +384 -0
  28. package/components/MemberMain.vue +48 -20
  29. package/components/MyAttendanceMain.vue +224 -0
  30. package/components/OnlineFormsConfiguration.vue +9 -2
  31. package/components/PhotoUpload.vue +410 -0
  32. package/components/ScheduleAreaMain.vue +313 -0
  33. package/components/ScheduleTaskAreaFormDialog.vue +144 -0
  34. package/components/ScheduleTaskAreaUpdateMoreAction.vue +109 -0
  35. package/components/ScheduleTaskForm.vue +471 -0
  36. package/components/ScheduleTaskMain.vue +345 -0
  37. package/components/ScheduleTastTicketMain.vue +182 -0
  38. package/components/SignaturePad.vue +17 -5
  39. package/components/StockCard.vue +191 -0
  40. package/components/SupplyManagementMain.vue +557 -0
  41. package/components/TableHygiene.vue +617 -0
  42. package/components/UnitMain.vue +451 -0
  43. package/components/VisitorManagement.vue +28 -15
  44. package/composables/useAccessManagement.ts +163 -0
  45. package/composables/useAreaPermission.ts +51 -0
  46. package/composables/useAreas.ts +99 -0
  47. package/composables/useAttendance.ts +89 -0
  48. package/composables/useAttendancePermission.ts +68 -0
  49. package/composables/useBuilding.ts +2 -2
  50. package/composables/useBuildingUnit.ts +2 -2
  51. package/composables/useBulletin.ts +82 -0
  52. package/composables/useCard.ts +2 -0
  53. package/composables/useCheckout.ts +61 -0
  54. package/composables/useCheckoutPermission.ts +80 -0
  55. package/composables/useCleaningPermission.ts +229 -0
  56. package/composables/useCleaningSchedulePermission.ts +58 -0
  57. package/composables/useCleaningSchedules.ts +233 -0
  58. package/composables/useCountry.ts +8 -0
  59. package/composables/useDashboardData.ts +2 -2
  60. package/composables/useFeedback.ts +1 -1
  61. package/composables/useLocation.ts +78 -0
  62. package/composables/useOnlineForm.ts +16 -9
  63. package/composables/usePeople.ts +87 -72
  64. package/composables/useQR.ts +29 -0
  65. package/composables/useScheduleTask.ts +89 -0
  66. package/composables/useScheduleTaskArea.ts +85 -0
  67. package/composables/useScheduleTaskPermission.ts +68 -0
  68. package/composables/useSiteEntryPassSettings.ts +4 -15
  69. package/composables/useStock.ts +45 -0
  70. package/composables/useSupply.ts +63 -0
  71. package/composables/useSupplyPermission.ts +92 -0
  72. package/composables/useUnitPermission.ts +51 -0
  73. package/composables/useUnits.ts +82 -0
  74. package/composables/useWebUsb.ts +389 -0
  75. package/composables/useWorkOrder.ts +1 -1
  76. package/nuxt.config.ts +3 -0
  77. package/package.json +4 -1
  78. package/types/area.d.ts +22 -0
  79. package/types/attendance.d.ts +38 -0
  80. package/types/checkout-item.d.ts +27 -0
  81. package/types/cleaner-schedule.d.ts +54 -0
  82. package/types/location.d.ts +42 -0
  83. package/types/schedule-task.d.ts +18 -0
  84. package/types/stock.d.ts +16 -0
  85. package/types/supply.d.ts +11 -0
  86. package/utils/acm-crypto.ts +30 -0
@@ -0,0 +1,238 @@
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
+ :disabled="disabled"
45
+ v-bind="props"
46
+ tile
47
+ >
48
+ More actions
49
+ </v-btn>
50
+ </template>
51
+
52
+ <v-list class="pa-0 rounded-0">
53
+ <v-list-item
54
+ v-if="showQrButton && qrButtonLabel"
55
+ @click="emit('showqr')"
56
+ >
57
+ <v-list-item-title class="text-subtitle-2">
58
+ {{ qrButtonLabel }}
59
+ </v-list-item-title>
60
+ </v-list-item>
61
+
62
+ <v-list-item
63
+ v-if="showViewStock && viewButtonLabel"
64
+ @click="emit('viewStock')"
65
+ >
66
+ <v-list-item-title class="text-subtitle-2">
67
+ {{ viewButtonLabel }}
68
+ </v-list-item-title>
69
+ </v-list-item>
70
+
71
+ <v-list-item
72
+ v-if="showAddStock && addStockButtonLabel"
73
+ @click="emit('addstock')"
74
+ >
75
+ <v-list-item-title class="text-subtitle-2">
76
+ {{ addStockButtonLabel }}
77
+ </v-list-item-title>
78
+ </v-list-item>
79
+
80
+ <v-list-item
81
+ v-if="showApproveRequest && appproveRequestItemButtonLabel"
82
+ @click="emit('approveRequest')"
83
+ >
84
+ <v-list-item-title class="text-subtitle-2">
85
+ {{ appproveRequestItemButtonLabel }}
86
+ </v-list-item-title>
87
+ </v-list-item>
88
+
89
+ <v-list-item
90
+ v-if="showDisapproveRequest && disapproveRequestItemButtonLabel"
91
+ @click="emit('disapproveRequest')"
92
+ >
93
+ <v-list-item-title class="text-subtitle-2">
94
+ {{ disapproveRequestItemButtonLabel }}
95
+ </v-list-item-title>
96
+ </v-list-item>
97
+
98
+ <v-list-item
99
+ v-if="showRequestItem && requestItemButtonLabel"
100
+ @click="emit('requestItem')"
101
+ >
102
+ <v-list-item-title class="text-subtitle-2">
103
+ {{ requestItemButtonLabel }}
104
+ </v-list-item-title>
105
+ </v-list-item>
106
+
107
+ <v-list-item v-if="canUpdate" @click="emit('edit')">
108
+ <v-list-item-title class="text-subtitle-2">
109
+ {{ editButtonLabel }}
110
+ </v-list-item-title>
111
+ </v-list-item>
112
+
113
+ <v-list-item
114
+ v-if="canDelete"
115
+ @click="emit('delete')"
116
+ class="text-red"
117
+ >
118
+ <v-list-item-title class="text-subtitle-2">
119
+ {{ deleteButtonLabel }}
120
+ </v-list-item-title>
121
+ </v-list-item>
122
+ </v-list>
123
+ </v-menu>
124
+ </v-col>
125
+ </v-row>
126
+ </v-toolbar>
127
+ </v-card>
128
+ </template>
129
+
130
+ <script setup lang="ts">
131
+ const prop = defineProps({
132
+ canUpdate: {
133
+ type: Boolean,
134
+ default: true,
135
+ },
136
+ canDelete: {
137
+ type: Boolean,
138
+ default: true,
139
+ },
140
+ editButtonLabel: {
141
+ type: String,
142
+ default: "Edit Area",
143
+ },
144
+ deleteButtonLabel: {
145
+ type: String,
146
+ default: "Delete Area",
147
+ },
148
+
149
+ qrButtonLabel: {
150
+ type: String,
151
+ default: "Show QR",
152
+ },
153
+ showQrButton: {
154
+ type: Boolean,
155
+ default: false,
156
+ },
157
+ addStockButtonLabel: {
158
+ type: String,
159
+ default: "Add Stock",
160
+ },
161
+ viewButtonLabel: {
162
+ type: String,
163
+ default: "View Stock",
164
+ },
165
+ showAddStock: {
166
+ type: Boolean,
167
+ default: false,
168
+ },
169
+ showViewStock: {
170
+ type: Boolean,
171
+ default: false,
172
+ },
173
+ requestItemButtonLabel: {
174
+ type: String,
175
+ default: "Request Item",
176
+ },
177
+
178
+ appproveRequestItemButtonLabel: {
179
+ type: String,
180
+ default: "Approve Request",
181
+ },
182
+
183
+ showApproveRequest: {
184
+ type: Boolean,
185
+ default: false,
186
+ },
187
+ showDisapproveRequest: {
188
+ type: Boolean,
189
+ default: false,
190
+ },
191
+ disapproveRequestItemButtonLabel: {
192
+ type: String,
193
+ default: "Disapprove Request",
194
+ },
195
+ showRequestItem: {
196
+ type: Boolean,
197
+ default: false,
198
+ },
199
+ disabled: {
200
+ type: Boolean,
201
+ default: false,
202
+ },
203
+ title: {
204
+ type: String,
205
+ default: "Actions",
206
+ },
207
+ });
208
+
209
+ const emit = defineEmits([
210
+ "close",
211
+ "edit",
212
+ "delete",
213
+ "showqr",
214
+ "addstock",
215
+ "viewStock",
216
+ "requestItem",
217
+ "approveRequest",
218
+ "disapproveRequest",
219
+ ]);
220
+ const {
221
+ canUpdate,
222
+ editButtonLabel,
223
+ deleteButtonLabel,
224
+
225
+ qrButtonLabel,
226
+ showQrButton,
227
+ addStockButtonLabel,
228
+ viewButtonLabel,
229
+ showAddStock,
230
+ showViewStock,
231
+ title,
232
+ requestItemButtonLabel,
233
+ showRequestItem,
234
+ disabled,
235
+ } = prop;
236
+ </script>
237
+
238
+ <style scoped></style>
@@ -0,0 +1,384 @@
1
+ <template>
2
+ <v-row no-gutters align="center" justify="center">
3
+ <v-col cols="12" lg="12">
4
+ <v-row no-gutters class="mb-4">
5
+ <v-col cols="auto">
6
+ <v-btn variant="text" color="primary" class="text-none" @click="back">
7
+ <v-icon left>mdi-arrow-left</v-icon>
8
+ Back
9
+ </v-btn>
10
+ </v-col>
11
+ </v-row>
12
+ <TableHygiene
13
+ ref="tableHygieneRef"
14
+ :title="'Cleaner Checklist'"
15
+ :headers="headers"
16
+ :items="items"
17
+ :selected="selectedItems"
18
+ :item-value="'unit'"
19
+ v-model:page="page"
20
+ :pages="pages"
21
+ :pageRange="pageRange"
22
+ :loading="loading"
23
+ :no-data-text="`No checklist found`"
24
+ :show-header="true"
25
+ :can-manage-schedule-tasks="canManageScheduleTasks"
26
+ :can-add-remarks="canAddRemarks"
27
+ @refresh="getUnitCleanerChecklistRefresh"
28
+ @update:selected="selectedItems = $event"
29
+ @action-click="handleActionClick"
30
+ @request-completion-dialog="openCompletionDialog"
31
+ />
32
+ </v-col>
33
+ </v-row>
34
+
35
+ <v-dialog v-model="showCompletionModal" max-width="600" persistent>
36
+ <v-card>
37
+ <v-card-title class="text-h5 pa-4">
38
+ {{
39
+ modalData.currentSet !== undefined
40
+ ? `Set ${modalData.currentSet} Completed`
41
+ : "All Items Checked"
42
+ }}
43
+ </v-card-title>
44
+
45
+ <v-divider />
46
+
47
+ <v-card-text class="pa-4">
48
+ <v-row>
49
+ <v-col cols="12">
50
+ <InputLabel
51
+ title="Remarks (Optional)"
52
+ :required="false"
53
+ class="mb-2"
54
+ />
55
+ <v-textarea
56
+ v-model="modalData.remark"
57
+ placeholder="Enter your remarks here"
58
+ rows="4"
59
+ variant="outlined"
60
+ density="comfortable"
61
+ hide-details
62
+ />
63
+ </v-col>
64
+
65
+ <v-col cols="12">
66
+ <v-divider class="mb-4" />
67
+ <PhotoUpload
68
+ title="Take/Upload Photo"
69
+ :required="false"
70
+ v-model="modalData.photos"
71
+ v-model:photo-ids="modalData.photoIds"
72
+ @error="handlePhotoError"
73
+ />
74
+ </v-col>
75
+ </v-row>
76
+ </v-card-text>
77
+
78
+ <v-divider />
79
+
80
+ <v-card-actions class="pa-4">
81
+ <v-spacer />
82
+ <v-btn color="grey" variant="text" @click="closeModal"> Cancel </v-btn>
83
+ <v-btn
84
+ color="primary"
85
+ variant="flat"
86
+ @click="submitModal"
87
+ :disabled="!isModalValid"
88
+ >
89
+ Submit
90
+ </v-btn>
91
+ </v-card-actions>
92
+ </v-card>
93
+ </v-dialog>
94
+
95
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
96
+ </template>
97
+
98
+ <script setup lang="ts">
99
+ import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
100
+ import useCleaningSchedules from "../composables/useCleaningSchedules";
101
+
102
+ const props = defineProps({
103
+ orgId: { type: String, default: "" },
104
+ site: { type: String, default: "" },
105
+ cleanerChecklistId: { type: String, default: "" },
106
+ scheduleAreaId: { type: String, default: "" },
107
+ type: { type: String, default: "cleaner" },
108
+ });
109
+
110
+ const { getUnitCleanerChecklist, updateUnitChecklist } = useCleaningSchedules();
111
+ const { getBySiteAsServiceProvider } = useCustomerSite();
112
+ const { back } = useUtils();
113
+ const { canAddRemarks, canManageScheduleTasks } =
114
+ useCleaningSchedulePermission();
115
+
116
+ const page = ref<number>(1);
117
+ const pages = ref<number>(0);
118
+ const pageRange = ref<string>("-- - -- of --");
119
+ const items = ref<TChecklistSet[]>([]);
120
+ const selectedItems = ref<Array<{ unit: string; set: number }>>([]);
121
+ const submitting = ref<boolean>(false);
122
+ const message = ref<string>("");
123
+ const messageSnackbar = ref<boolean>(false);
124
+ const messageColor = ref<string>("");
125
+ const categories = ref<
126
+ Array<{ title: string; value: string; subtitle: string }>
127
+ >([]);
128
+ const tableHygieneRef = ref<any>(null);
129
+
130
+ const headers = [{ title: "Name", value: "name" }];
131
+
132
+ const { data: getCategoriesReq } = await useLazyAsyncData(
133
+ "get-categories-for-work-order",
134
+ () => getBySiteAsServiceProvider(props.site)
135
+ );
136
+
137
+ // Modal state management
138
+ const showCompletionModal = ref<boolean>(false);
139
+ const modalData = reactive({
140
+ remark: "",
141
+ photos: [] as string[],
142
+ photoIds: [] as string[],
143
+ currentSet: undefined as number | undefined,
144
+ approvedItems: [] as Array<{ key: string; item: any; action: "approve" }>,
145
+ lastApprovedKey: null as string | null,
146
+ });
147
+
148
+ const isModalValid = computed(() => {
149
+ return modalData.remark?.trim().length > 0;
150
+ });
151
+
152
+ const {
153
+ data: getUnitCleanerChecklistReq,
154
+ refresh: getUnitCleanerChecklistRefresh,
155
+ pending: loading,
156
+ } = await useLazyAsyncData(
157
+ `get-all-unit-cleaner-checklist`,
158
+ () =>
159
+ getUnitCleanerChecklist({
160
+ page: page.value,
161
+ scheduleAreaId: props.scheduleAreaId,
162
+ }),
163
+ {
164
+ watch: [() => page.value, () => props.scheduleAreaId],
165
+ }
166
+ );
167
+
168
+ watchEffect(() => {
169
+ if (getCategoriesReq.value) {
170
+ categories.value = getCategoriesReq.value.map((i: any) => ({
171
+ title: i.nature.replace(/_/g, " "),
172
+ subtitle: i.title,
173
+ value: i._id.org,
174
+ }));
175
+ }
176
+ });
177
+
178
+ watchEffect(() => {
179
+ if (getUnitCleanerChecklistReq.value) {
180
+ items.value = getUnitCleanerChecklistReq.value.items;
181
+ page.value = getUnitCleanerChecklistReq.value.page;
182
+ pages.value = getUnitCleanerChecklistReq.value.pages;
183
+ pageRange.value = getUnitCleanerChecklistReq.value.pageRange;
184
+ }
185
+ });
186
+
187
+ watchEffect(() => {
188
+ const completed: Array<{ unit: string; set: number }> = [];
189
+
190
+ items.value.forEach((checklistSet: TChecklistSet) => {
191
+ checklistSet.units?.forEach((unit: TUnitChecklistItem) => {
192
+ if (
193
+ unit.status?.toLowerCase() === "completed" ||
194
+ (unit as any).approve === true
195
+ ) {
196
+ completed.push({ unit: unit.unit, set: checklistSet.set });
197
+ }
198
+ });
199
+ });
200
+
201
+ selectedItems.value = completed;
202
+ });
203
+
204
+ function showMessage(msg: string, color: string = "error"): void {
205
+ message.value = msg;
206
+ messageColor.value = color;
207
+ messageSnackbar.value = true;
208
+ }
209
+
210
+ function resetModalData(): void {
211
+ modalData.remark = "";
212
+ modalData.photos = [];
213
+ modalData.photoIds = [];
214
+ modalData.currentSet = undefined;
215
+ modalData.approvedItems = [];
216
+ modalData.lastApprovedKey = null;
217
+ }
218
+
219
+ function closeModal(): void {
220
+ const setNumber = modalData.currentSet;
221
+ showCompletionModal.value = false;
222
+ resetModalData();
223
+
224
+ // Notify TableHygiene to revert approval states
225
+ if (setNumber !== undefined && tableHygieneRef.value) {
226
+ tableHygieneRef.value.revertSetApprovals(setNumber);
227
+ }
228
+ }
229
+
230
+ function handlePhotoError(errorMessage: string): void {
231
+ console.error("Photo upload error:", errorMessage);
232
+ showMessage(errorMessage, "error");
233
+ }
234
+
235
+ function openCompletionDialog({
236
+ setNumber,
237
+ approvedItems,
238
+ lastApprovedKey,
239
+ }: {
240
+ setNumber: number | undefined;
241
+ approvedItems: Array<{ key: string; item: any; action: "approve" }>;
242
+ lastApprovedKey: string | null;
243
+ }): void {
244
+ // If user does not need to add remarks, perform approvals immediately
245
+ if (!canAddRemarks.value) {
246
+ // call update directly without opening modal
247
+ _updateUnitChecklist({
248
+ remark: "",
249
+ photos: [],
250
+ photoIds: [],
251
+ approvedItems,
252
+ lastApprovedKey,
253
+ setNumber,
254
+ }).catch((err) => {
255
+ console.error("Error auto-approving set:", err);
256
+ showMessage(err?.message || "Failed to approve set", "error");
257
+ });
258
+ return;
259
+ }
260
+
261
+ modalData.currentSet = setNumber;
262
+ modalData.approvedItems = approvedItems;
263
+ modalData.lastApprovedKey = lastApprovedKey;
264
+ showCompletionModal.value = true;
265
+ }
266
+
267
+ async function submitModal(): Promise<void> {
268
+ if (!isModalValid.value) return;
269
+
270
+ await _updateUnitChecklist({
271
+ remark: modalData.remark,
272
+ photos: modalData.photos,
273
+ photoIds: modalData.photoIds,
274
+ approvedItems: modalData.approvedItems,
275
+ lastApprovedKey: modalData.lastApprovedKey,
276
+ setNumber: modalData.currentSet,
277
+ });
278
+
279
+ closeModal();
280
+ }
281
+
282
+ async function handleActionClick(data: {
283
+ item: TFlattenedUnitItem;
284
+ action: "approve" | "reject";
285
+ }): Promise<void> {
286
+ const { item, action } = data;
287
+
288
+ if (!item?.unit || item?.set === undefined) {
289
+ showMessage("Invalid unit or set", "error");
290
+ return;
291
+ }
292
+
293
+ const isApproved = selectedItems.value.some(
294
+ (s) => s.unit === item.unit && s.set === item.set
295
+ );
296
+
297
+ if (isApproved && action === "approve") {
298
+ showMessage("Unit already approved", "info");
299
+ return;
300
+ }
301
+
302
+ submitting.value = true;
303
+
304
+ try {
305
+ const checklistId = props.scheduleAreaId;
306
+ const unitId = item.unit;
307
+ const setNumber = item.set;
308
+
309
+ const response = await updateUnitChecklist(
310
+ checklistId,
311
+ unitId,
312
+ setNumber,
313
+ action
314
+ );
315
+
316
+ showMessage(
317
+ response?.message ||
318
+ `Unit ${action === "approve" ? "approved" : "rejected"} successfully`,
319
+ "success"
320
+ );
321
+
322
+ await getUnitCleanerChecklistRefresh();
323
+ } catch (error: any) {
324
+ console.error("Error updating unit checklist:", error);
325
+ showMessage(error?.message || "Failed to update checklist", "error");
326
+ } finally {
327
+ submitting.value = false;
328
+ }
329
+ }
330
+
331
+ async function _updateUnitChecklist({
332
+ remark,
333
+ photoIds,
334
+ approvedItems,
335
+ setNumber,
336
+ }: {
337
+ remark: string;
338
+ photos: string[];
339
+ photoIds: string[];
340
+ approvedItems: Array<{ key: string; item: any; action: "approve" }>;
341
+ lastApprovedKey: string | null;
342
+ setNumber: number | undefined;
343
+ }) {
344
+ submitting.value = true;
345
+
346
+ try {
347
+ if (!approvedItems || approvedItems.length === 0) {
348
+ throw new Error("No items to approve");
349
+ }
350
+
351
+ if (!remark?.trim()) {
352
+ throw new Error("Remark is required");
353
+ }
354
+
355
+ const checklistId = props.scheduleAreaId;
356
+
357
+ for (let i = 0; i < approvedItems.length; i++) {
358
+ const { item } = approvedItems[i];
359
+ const unitId = item.unit;
360
+ const setNum = item.set;
361
+ const isLastItem = i === approvedItems.length - 1;
362
+
363
+ await updateUnitChecklist(
364
+ checklistId,
365
+ unitId,
366
+ setNum,
367
+ "approve",
368
+ isLastItem ? remark : undefined,
369
+ isLastItem ? photoIds : undefined
370
+ );
371
+ }
372
+
373
+ showMessage(`Set ${setNumber} approved successfully`, "success");
374
+
375
+ // Refresh data
376
+ await getUnitCleanerChecklistRefresh();
377
+ } catch (error: any) {
378
+ console.error("Error approving set:", error);
379
+ showMessage(error?.message || "Failed to approve set", "error");
380
+ } finally {
381
+ submitting.value = false;
382
+ }
383
+ }
384
+ </script>
@@ -41,20 +41,18 @@
41
41
  </template>
42
42
 
43
43
  <template #extension>
44
- <v-tabs>
44
+ <v-tabs
45
+ v-model="selectedStatus"
46
+ color="primary"
47
+ :height="40"
48
+ @update:model-value="toRoute"
49
+ class="w-100"
50
+ >
45
51
  <v-tab
46
- v-for="tab in [
47
- { name: 'Active', status: 'active' },
48
- { name: 'Suspended', status: 'suspended' },
49
- ]"
52
+ v-for="tab in tabOptions"
53
+ :value="tab.status"
50
54
  :key="tab.status"
51
- :to="{
52
- name: props.route,
53
- params: setRouteParams({
54
- status: tab.status,
55
- orgId: props.orgId,
56
- }),
57
- }"
55
+ class="text-capitalize"
58
56
  >
59
57
  {{ tab.name }}
60
58
  </v-tab>
@@ -331,18 +329,48 @@ const {
331
329
  const { headerSearch } = useLocal();
332
330
  const { replaceMatch, setRouteParams, requiredRule } = useUtils();
333
331
 
332
+ const selectedStatus = ref(props.status);
333
+ const route = useRoute();
334
+ const orgId = route.params.org as string;
335
+ const routeName = (useRoute().name as string) ?? "";
336
+
337
+ const tabOptions = [
338
+ { name: "Active", status: "active" },
339
+ { name: "Suspended", status: "suspended" },
340
+ ];
341
+
342
+ function toRoute(status: any) {
343
+ const obj = tabOptions.find((x) => x.status === status);
344
+ if (!obj) return;
345
+ page.value = 1;
346
+ navigateTo({
347
+ name: routeName,
348
+ params: {
349
+ org: orgId,
350
+ },
351
+ query: {
352
+ status: obj.status,
353
+ },
354
+ });
355
+ }
356
+
334
357
  const {
335
358
  data: getAllReq,
336
359
  refresh: getAll,
337
360
  status: getAllReqStatus,
338
- } = useLazyAsyncData("get-all-members", () =>
339
- _getAll({
340
- status: props.status,
341
- org: props.orgId,
342
- search: headerSearch.value,
343
- page: page.value,
344
- type: props.type,
345
- })
361
+ } = useLazyAsyncData(
362
+ "get-all-members",
363
+ () =>
364
+ _getAll({
365
+ status: selectedStatus.value,
366
+ org: orgId,
367
+ search: headerSearch.value,
368
+ page: page.value,
369
+ type: props.type,
370
+ }),
371
+ {
372
+ watch: [page, () => route.query],
373
+ }
346
374
  );
347
375
 
348
376
  const loading = computed(() => getAllReqStatus.value === "pending");