@7365admin1/layer-common 1.10.7 → 1.10.8
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/{AddSupplyForm.vue → AddEqupmentForm.vue} +5 -5
- package/components/BuildingManagement/units.vue +2 -2
- package/components/BuildingUnitFormAdd.vue +4 -4
- package/components/BuildingUnitFormEdit.vue +114 -68
- package/components/EntryPassInformation.vue +251 -23
- package/components/{CheckoutItemMain.vue → EquipmentItemMain.vue} +88 -85
- package/components/{SupplyManagement.vue → EquipmentManagement.vue} +3 -3
- package/components/Input/DateTimePicker.vue +17 -11
- package/components/ManageChecklistMain.vue +379 -41
- package/components/TableHygiene.vue +42 -452
- package/components/UnitPersonCard.vue +74 -14
- package/components/VisitorForm.vue +77 -21
- package/components/VisitorFormSelection.vue +13 -2
- package/components/VisitorManagement.vue +83 -55
- package/composables/useCleaningPermission.ts +7 -7
- package/composables/useDashboardData.ts +2 -2
- package/composables/{useSupply.ts → useEquipment.ts} +11 -11
- package/composables/{useCheckout.ts → useEquipmentItem.ts} +7 -7
- package/composables/{useCheckoutPermission.ts → useEquipmentItemPermission.ts} +13 -13
- package/composables/useEquipmentManagementPermission.ts +96 -0
- package/composables/{useSupplyPermission.ts → useEquipmentPermission.ts} +9 -9
- package/composables/useVehicle.ts +21 -2
- package/composables/useVisitor.ts +3 -3
- package/composables/useWorkOrder.ts +25 -3
- package/package.json +1 -1
- package/types/building.d.ts +1 -1
- package/types/{checkout-item.d.ts → equipment-item.d.ts} +3 -3
- package/types/{supply.d.ts → equipment.d.ts} +2 -2
- package/types/people.d.ts +3 -1
- package/types/vehicle.d.ts +2 -0
- package/types/visitor.d.ts +2 -1
|
@@ -17,11 +17,9 @@
|
|
|
17
17
|
</v-col>
|
|
18
18
|
</v-row>
|
|
19
19
|
<TableHygiene
|
|
20
|
-
ref="tableHygieneRef"
|
|
21
20
|
:title="'Cleaner Checklist'"
|
|
22
21
|
:headers="headers"
|
|
23
22
|
:items="items"
|
|
24
|
-
:selected="selectedItems"
|
|
25
23
|
:item-value="'unit'"
|
|
26
24
|
v-model:page="page"
|
|
27
25
|
:pages="pages"
|
|
@@ -29,13 +27,112 @@
|
|
|
29
27
|
:loading="loading"
|
|
30
28
|
:no-data-text="`No checklist found`"
|
|
31
29
|
:show-header="true"
|
|
32
|
-
:can-manage-schedule-tasks="!isScheduleClosed && canManageScheduleTasks"
|
|
33
|
-
:can-add-remarks="!isScheduleClosed && canAddRemarks"
|
|
34
30
|
@refresh="getUnitCleanerChecklistRefresh"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
>
|
|
32
|
+
<!-- Status icon driven by local activeActions state -->
|
|
33
|
+
<template #item-prepend="{ item, group }">
|
|
34
|
+
<v-icon
|
|
35
|
+
size="20"
|
|
36
|
+
:color="
|
|
37
|
+
activeActions[getKey(item, group.set)] === 'approve'
|
|
38
|
+
? 'success'
|
|
39
|
+
: activeActions[getKey(item, group.set)] === 'reject'
|
|
40
|
+
? 'error'
|
|
41
|
+
: 'grey-lighten-2'
|
|
42
|
+
"
|
|
43
|
+
>
|
|
44
|
+
{{
|
|
45
|
+
activeActions[getKey(item, group.set)] === "approve"
|
|
46
|
+
? "mdi-check-circle"
|
|
47
|
+
: activeActions[getKey(item, group.set)] === "reject"
|
|
48
|
+
? "mdi-close-circle"
|
|
49
|
+
: "mdi-circle-outline"
|
|
50
|
+
}}
|
|
51
|
+
</v-icon>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<!-- Item name with strike-through when approved -->
|
|
55
|
+
<template #item-content="{ item, group }">
|
|
56
|
+
<v-row no-gutters>
|
|
57
|
+
<v-col cols="12">
|
|
58
|
+
<span
|
|
59
|
+
class="text-body-2 font-weight-medium"
|
|
60
|
+
:class="
|
|
61
|
+
activeActions[getKey(item, group.set)] === 'approve'
|
|
62
|
+
? 'text-decoration-line-through text-medium-emphasis'
|
|
63
|
+
: ''
|
|
64
|
+
"
|
|
65
|
+
>
|
|
66
|
+
{{ item.name }}
|
|
67
|
+
</span>
|
|
68
|
+
</v-col>
|
|
69
|
+
<v-col v-if="item.timestamp" cols="12">
|
|
70
|
+
<v-row no-gutters align="center" class="mt-1">
|
|
71
|
+
<v-col
|
|
72
|
+
cols="auto"
|
|
73
|
+
class="d-flex align-center ga-1 text-caption text-medium-emphasis pa-0"
|
|
74
|
+
>
|
|
75
|
+
<v-icon size="11">mdi-clock-outline</v-icon>
|
|
76
|
+
{{ formatTimestamp(item.timestamp) }}
|
|
77
|
+
</v-col>
|
|
78
|
+
</v-row>
|
|
79
|
+
</v-col>
|
|
80
|
+
</v-row>
|
|
81
|
+
</template>
|
|
82
|
+
|
|
83
|
+
<!-- Approve / Reject action buttons -->
|
|
84
|
+
<template #item-append="{ item, group }">
|
|
85
|
+
<v-row
|
|
86
|
+
v-if="!isScheduleClosed && canManageScheduleTasks"
|
|
87
|
+
no-gutters
|
|
88
|
+
align="center"
|
|
89
|
+
>
|
|
90
|
+
<v-col cols="auto">
|
|
91
|
+
<v-btn
|
|
92
|
+
icon="mdi-close"
|
|
93
|
+
size="small"
|
|
94
|
+
:variant="
|
|
95
|
+
activeActions[getKey(item, group.set)] === 'reject'
|
|
96
|
+
? 'flat'
|
|
97
|
+
: 'text'
|
|
98
|
+
"
|
|
99
|
+
color="error"
|
|
100
|
+
@click.stop="handleItemActionClick(item, group.set, 'reject')"
|
|
101
|
+
/>
|
|
102
|
+
</v-col>
|
|
103
|
+
<v-col cols="auto">
|
|
104
|
+
<v-btn
|
|
105
|
+
icon="mdi-check"
|
|
106
|
+
size="small"
|
|
107
|
+
:variant="
|
|
108
|
+
activeActions[getKey(item, group.set)] === 'approve'
|
|
109
|
+
? 'flat'
|
|
110
|
+
: 'text'
|
|
111
|
+
"
|
|
112
|
+
color="success"
|
|
113
|
+
:loading="!!loadingActions[getKey(item, group.set)]"
|
|
114
|
+
:disabled="!!loadingActions[getKey(item, group.set)]"
|
|
115
|
+
@click.stop="handleItemActionClick(item, group.set, 'approve')"
|
|
116
|
+
/>
|
|
117
|
+
</v-col>
|
|
118
|
+
</v-row>
|
|
119
|
+
</template>
|
|
120
|
+
<template #group-header-append="{ group }">
|
|
121
|
+
<v-btn
|
|
122
|
+
v-if="group.attachments && group.attachments.length > 0"
|
|
123
|
+
size="x-small"
|
|
124
|
+
variant="tonal"
|
|
125
|
+
color="primary"
|
|
126
|
+
class="text-none"
|
|
127
|
+
prepend-icon="mdi-paperclip"
|
|
128
|
+
@click.stop="openAttachmentDialog(group.set, group.attachments)"
|
|
129
|
+
>
|
|
130
|
+
{{ group.attachments.length }} attachment{{
|
|
131
|
+
group.attachments.length > 1 ? "s" : ""
|
|
132
|
+
}}
|
|
133
|
+
</v-btn>
|
|
134
|
+
</template>
|
|
135
|
+
</TableHygiene>
|
|
39
136
|
</v-col>
|
|
40
137
|
</v-row>
|
|
41
138
|
|
|
@@ -99,12 +196,90 @@
|
|
|
99
196
|
</v-card>
|
|
100
197
|
</v-dialog>
|
|
101
198
|
|
|
199
|
+
<v-dialog v-model="showAttachmentDialog" max-width="700" scrollable>
|
|
200
|
+
<v-card>
|
|
201
|
+
<v-card-title class="d-flex align-center pa-4">
|
|
202
|
+
<span class="text-h6 font-weight-bold">
|
|
203
|
+
Set {{ attachmentDialogSetNum }} — Attachments
|
|
204
|
+
</span>
|
|
205
|
+
<v-spacer />
|
|
206
|
+
<v-btn
|
|
207
|
+
icon="mdi-close"
|
|
208
|
+
variant="text"
|
|
209
|
+
size="small"
|
|
210
|
+
@click="showAttachmentDialog = false"
|
|
211
|
+
/>
|
|
212
|
+
</v-card-title>
|
|
213
|
+
|
|
214
|
+
<v-divider />
|
|
215
|
+
|
|
216
|
+
<v-card-text class="pa-4">
|
|
217
|
+
<v-row>
|
|
218
|
+
<v-col
|
|
219
|
+
v-for="(id, index) in attachmentDialogIds"
|
|
220
|
+
:key="index"
|
|
221
|
+
cols="6"
|
|
222
|
+
sm="4"
|
|
223
|
+
>
|
|
224
|
+
<v-sheet
|
|
225
|
+
rounded="lg"
|
|
226
|
+
class="overflow-hidden"
|
|
227
|
+
style="aspect-ratio: 1"
|
|
228
|
+
>
|
|
229
|
+
<v-img
|
|
230
|
+
:src="getFileUrl(id)"
|
|
231
|
+
aspect-ratio="1"
|
|
232
|
+
cover
|
|
233
|
+
class="rounded-lg"
|
|
234
|
+
@click="openFullImage(getFileUrl(id))"
|
|
235
|
+
style="cursor: zoom-in"
|
|
236
|
+
>
|
|
237
|
+
<template v-slot:placeholder>
|
|
238
|
+
<v-row
|
|
239
|
+
class="fill-height ma-0"
|
|
240
|
+
align="center"
|
|
241
|
+
justify="center"
|
|
242
|
+
>
|
|
243
|
+
<v-progress-circular indeterminate color="grey-lighten-4" />
|
|
244
|
+
</v-row>
|
|
245
|
+
</template>
|
|
246
|
+
<template v-slot:error>
|
|
247
|
+
<v-row
|
|
248
|
+
class="fill-height ma-0"
|
|
249
|
+
align="center"
|
|
250
|
+
justify="center"
|
|
251
|
+
>
|
|
252
|
+
<v-icon icon="mdi-image-broken" size="40" color="grey" />
|
|
253
|
+
</v-row>
|
|
254
|
+
</template>
|
|
255
|
+
</v-img>
|
|
256
|
+
</v-sheet>
|
|
257
|
+
</v-col>
|
|
258
|
+
</v-row>
|
|
259
|
+
</v-card-text>
|
|
260
|
+
</v-card>
|
|
261
|
+
</v-dialog>
|
|
262
|
+
|
|
263
|
+
<v-dialog v-model="showLightbox" max-width="900">
|
|
264
|
+
<v-card>
|
|
265
|
+
<v-card-actions class="pa-2 justify-end">
|
|
266
|
+
<v-btn icon="mdi-close" variant="text" @click="showLightbox = false" />
|
|
267
|
+
</v-card-actions>
|
|
268
|
+
<v-card-text class="pa-2 pt-0">
|
|
269
|
+
<v-img :src="lightboxSrc" contain max-height="80vh" />
|
|
270
|
+
</v-card-text>
|
|
271
|
+
</v-card>
|
|
272
|
+
</v-dialog>
|
|
273
|
+
|
|
102
274
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
103
275
|
</template>
|
|
104
276
|
|
|
105
277
|
<script setup lang="ts">
|
|
106
278
|
import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
|
|
107
279
|
import useCleaningSchedules from "../composables/useCleaningSchedules";
|
|
280
|
+
import useCustomerSite from "../composables/useCustomerSite";
|
|
281
|
+
import useFile from "../composables/useFile";
|
|
282
|
+
import useUtils from "../composables/useUtils";
|
|
108
283
|
|
|
109
284
|
const props = defineProps({
|
|
110
285
|
orgId: { type: String, default: "" },
|
|
@@ -120,8 +295,13 @@ const { back } = useUtils();
|
|
|
120
295
|
const { canAddRemarks, canManageScheduleTasks } =
|
|
121
296
|
useCleaningSchedulePermission();
|
|
122
297
|
|
|
123
|
-
const selectedScheduleStatus = useState<string>(
|
|
124
|
-
|
|
298
|
+
const selectedScheduleStatus = useState<string>(
|
|
299
|
+
"selectedScheduleStatus",
|
|
300
|
+
() => ""
|
|
301
|
+
);
|
|
302
|
+
const isScheduleClosed = computed(
|
|
303
|
+
() => selectedScheduleStatus.value.toLowerCase() === "closed"
|
|
304
|
+
);
|
|
125
305
|
|
|
126
306
|
const page = ref<number>(1);
|
|
127
307
|
const pages = ref<number>(0);
|
|
@@ -135,10 +315,94 @@ const messageColor = ref<string>("");
|
|
|
135
315
|
const categories = ref<
|
|
136
316
|
Array<{ title: string; value: string; subtitle: string }>
|
|
137
317
|
>([]);
|
|
138
|
-
const tableHygieneRef = ref<any>(null);
|
|
139
|
-
|
|
140
318
|
const headers = [{ title: "Name", value: "name" }];
|
|
141
319
|
|
|
320
|
+
// ── Action state (approve / reject per item key) ───────────────────────────
|
|
321
|
+
const activeActions = reactive<Record<string, "approve" | "reject">>({});
|
|
322
|
+
const persistedActions = reactive<Record<string, "approve" | "reject">>({});
|
|
323
|
+
const loadingActions = reactive<Record<string, boolean>>({});
|
|
324
|
+
const completedSets = ref<Set<number>>(new Set());
|
|
325
|
+
const lastApprovedKey = ref<string | null>(null);
|
|
326
|
+
|
|
327
|
+
function getKey(item: any, set?: number): string {
|
|
328
|
+
return `${item.unit}_${set ?? ""}`;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isSetFullyApproved(setNumber: number): boolean {
|
|
332
|
+
const group = items.value.find((g: any) => g.set === setNumber);
|
|
333
|
+
if (!group) return false;
|
|
334
|
+
return (group.units || []).every(
|
|
335
|
+
(unit: any) => activeActions[getKey(unit, setNumber)] === "approve"
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function getNewApprovedItemsForSet(
|
|
340
|
+
setNumber: number
|
|
341
|
+
): Array<{ key: string; item: any; action: "approve" }> {
|
|
342
|
+
const group = items.value.find((g: any) => g.set === setNumber);
|
|
343
|
+
if (!group) return [];
|
|
344
|
+
return (group.units || [])
|
|
345
|
+
.filter((unit: any) => {
|
|
346
|
+
const key = getKey(unit, setNumber);
|
|
347
|
+
return activeActions[key] === "approve" && !(key in persistedActions);
|
|
348
|
+
})
|
|
349
|
+
.map((unit: any) => ({
|
|
350
|
+
key: getKey(unit, setNumber),
|
|
351
|
+
item: { ...unit, set: setNumber },
|
|
352
|
+
action: "approve" as const,
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function revertSetApprovals(setNumber: number): void {
|
|
357
|
+
const group = items.value.find((g: any) => g.set === setNumber);
|
|
358
|
+
if (group) {
|
|
359
|
+
(group.units || []).forEach((unit: any) => {
|
|
360
|
+
const key = getKey(unit, setNumber);
|
|
361
|
+
if (!(key in persistedActions)) {
|
|
362
|
+
delete activeActions[key];
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
completedSets.value.delete(setNumber);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function stopLoadingAction(key: string): void {
|
|
370
|
+
delete loadingActions[key];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ── Attachment dialog ──────────────────────────────────────────────────────
|
|
374
|
+
const showAttachmentDialog = ref(false);
|
|
375
|
+
const attachmentDialogSetNum = ref<number | undefined>(undefined);
|
|
376
|
+
const attachmentDialogIds = ref<string[]>([]);
|
|
377
|
+
const showLightbox = ref(false);
|
|
378
|
+
const lightboxSrc = ref("");
|
|
379
|
+
|
|
380
|
+
const { getFileUrl } = useFile();
|
|
381
|
+
|
|
382
|
+
function openAttachmentDialog(setNumber: number, attachments: string[]) {
|
|
383
|
+
attachmentDialogSetNum.value = setNumber;
|
|
384
|
+
attachmentDialogIds.value = attachments;
|
|
385
|
+
showAttachmentDialog.value = true;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function openFullImage(src: string) {
|
|
389
|
+
lightboxSrc.value = src;
|
|
390
|
+
showLightbox.value = true;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function formatTimestamp(ts: string): string {
|
|
394
|
+
if (!ts) return "";
|
|
395
|
+
const date = new Date(ts);
|
|
396
|
+
return date.toLocaleString("en-SG", {
|
|
397
|
+
day: "2-digit",
|
|
398
|
+
month: "short",
|
|
399
|
+
year: "numeric",
|
|
400
|
+
hour: "2-digit",
|
|
401
|
+
minute: "2-digit",
|
|
402
|
+
hour12: true,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
142
406
|
const { data: getCategoriesReq } = await useLazyAsyncData(
|
|
143
407
|
"get-categories-for-work-order",
|
|
144
408
|
() => getBySiteAsServiceProvider(props.site)
|
|
@@ -176,11 +440,12 @@ const {
|
|
|
176
440
|
);
|
|
177
441
|
|
|
178
442
|
watchEffect(() => {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
value:
|
|
443
|
+
const data = getCategoriesReq.value as any;
|
|
444
|
+
if (data) {
|
|
445
|
+
categories.value = (data.categories ?? data).map((cat: any) => ({
|
|
446
|
+
title: cat.name,
|
|
447
|
+
value: cat.id,
|
|
448
|
+
subtitle: cat.description || "",
|
|
184
449
|
}));
|
|
185
450
|
}
|
|
186
451
|
});
|
|
@@ -211,6 +476,35 @@ watchEffect(() => {
|
|
|
211
476
|
selectedItems.value = completed;
|
|
212
477
|
});
|
|
213
478
|
|
|
479
|
+
watch(
|
|
480
|
+
items,
|
|
481
|
+
(newItems) => {
|
|
482
|
+
if (!Array.isArray(newItems)) return;
|
|
483
|
+
|
|
484
|
+
Object.keys(persistedActions).forEach(
|
|
485
|
+
(key) => delete persistedActions[key]
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
newItems.forEach((group: any) => {
|
|
489
|
+
const set = group.set;
|
|
490
|
+
(group.units || []).forEach((unit: any) => {
|
|
491
|
+
const key = getKey(unit, set);
|
|
492
|
+
if ((unit as any).approve === true) {
|
|
493
|
+
activeActions[key] = "approve";
|
|
494
|
+
persistedActions[key] = "approve";
|
|
495
|
+
} else if ((unit as any).reject === true) {
|
|
496
|
+
activeActions[key] = "reject";
|
|
497
|
+
persistedActions[key] = "reject";
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
completedSets.value.clear();
|
|
503
|
+
Object.keys(loadingActions).forEach((k) => delete loadingActions[k]);
|
|
504
|
+
},
|
|
505
|
+
{ immediate: true }
|
|
506
|
+
);
|
|
507
|
+
|
|
214
508
|
function showMessage(msg: string, color: string = "error"): void {
|
|
215
509
|
message.value = msg;
|
|
216
510
|
messageColor.value = color;
|
|
@@ -231,9 +525,9 @@ function closeModal(): void {
|
|
|
231
525
|
showCompletionModal.value = false;
|
|
232
526
|
resetModalData();
|
|
233
527
|
|
|
234
|
-
//
|
|
235
|
-
if (setNumber !== undefined
|
|
236
|
-
|
|
528
|
+
// Revert optimistic approval state for this set
|
|
529
|
+
if (setNumber !== undefined) {
|
|
530
|
+
revertSetApprovals(setNumber);
|
|
237
531
|
}
|
|
238
532
|
}
|
|
239
533
|
|
|
@@ -252,7 +546,10 @@ function openCompletionDialog({
|
|
|
252
546
|
lastApprovedKey: string | null;
|
|
253
547
|
}): void {
|
|
254
548
|
if (isScheduleClosed.value) {
|
|
255
|
-
showMessage(
|
|
549
|
+
showMessage(
|
|
550
|
+
"This schedule is closed. No actions can be performed.",
|
|
551
|
+
"error"
|
|
552
|
+
);
|
|
256
553
|
return;
|
|
257
554
|
}
|
|
258
555
|
|
|
@@ -294,26 +591,73 @@ async function submitModal(): Promise<void> {
|
|
|
294
591
|
closeModal();
|
|
295
592
|
}
|
|
296
593
|
|
|
297
|
-
async function
|
|
298
|
-
item:
|
|
299
|
-
|
|
300
|
-
|
|
594
|
+
async function handleItemActionClick(
|
|
595
|
+
item: TUnitChecklistItem,
|
|
596
|
+
set: number | undefined,
|
|
597
|
+
action: "approve" | "reject"
|
|
598
|
+
): Promise<void> {
|
|
301
599
|
if (isScheduleClosed.value) {
|
|
302
|
-
showMessage(
|
|
600
|
+
showMessage(
|
|
601
|
+
"This schedule is closed. No actions can be performed.",
|
|
602
|
+
"error"
|
|
603
|
+
);
|
|
303
604
|
return;
|
|
304
605
|
}
|
|
305
606
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (!item?.unit || item?.set === undefined) {
|
|
607
|
+
if (!item?.unit || set === undefined) {
|
|
309
608
|
showMessage("Invalid unit or set", "error");
|
|
310
609
|
return;
|
|
311
610
|
}
|
|
312
611
|
|
|
612
|
+
const key = getKey(item, set);
|
|
613
|
+
const isPersisted = key in persistedActions;
|
|
614
|
+
|
|
615
|
+
// Toggle off optimistic state if clicking the same un-persisted action
|
|
616
|
+
if (activeActions[key] === action && !isPersisted) {
|
|
617
|
+
delete activeActions[key];
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
activeActions[key] = action;
|
|
622
|
+
|
|
623
|
+
if (action === "reject") {
|
|
624
|
+
await _callUpdateUnitChecklist(item, set, action);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Approve: check if this completes the entire set
|
|
629
|
+
lastApprovedKey.value = key;
|
|
630
|
+
|
|
631
|
+
if (isSetFullyApproved(set) && !completedSets.value.has(set)) {
|
|
632
|
+
const newApprovedItems = getNewApprovedItemsForSet(set);
|
|
633
|
+
if (newApprovedItems.length > 0) {
|
|
634
|
+
completedSets.value.add(set);
|
|
635
|
+
openCompletionDialog({
|
|
636
|
+
setNumber: set,
|
|
637
|
+
approvedItems: newApprovedItems,
|
|
638
|
+
lastApprovedKey: lastApprovedKey.value,
|
|
639
|
+
});
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Single item approve (does not complete the set yet)
|
|
645
|
+
if (!isPersisted) {
|
|
646
|
+
delete activeActions[key];
|
|
647
|
+
loadingActions[key] = true;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
await _callUpdateUnitChecklist(item, set, action);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
async function _callUpdateUnitChecklist(
|
|
654
|
+
item: TUnitChecklistItem,
|
|
655
|
+
set: number,
|
|
656
|
+
action: "approve" | "reject"
|
|
657
|
+
): Promise<void> {
|
|
313
658
|
const isApproved = selectedItems.value.some(
|
|
314
|
-
(s) => s.unit === item.unit && s.set ===
|
|
659
|
+
(s) => s.unit === item.unit && s.set === set
|
|
315
660
|
);
|
|
316
|
-
|
|
317
661
|
if (isApproved && action === "approve") {
|
|
318
662
|
showMessage("Unit already approved", "info");
|
|
319
663
|
return;
|
|
@@ -322,23 +666,17 @@ async function handleActionClick(data: {
|
|
|
322
666
|
submitting.value = true;
|
|
323
667
|
|
|
324
668
|
try {
|
|
325
|
-
const checklistId = props.scheduleAreaId;
|
|
326
|
-
const unitId = item.unit;
|
|
327
|
-
const setNumber = item.set;
|
|
328
|
-
|
|
329
669
|
const response = await updateUnitChecklist(
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
670
|
+
props.scheduleAreaId,
|
|
671
|
+
item.unit,
|
|
672
|
+
set,
|
|
333
673
|
action
|
|
334
674
|
);
|
|
335
|
-
|
|
336
675
|
showMessage(
|
|
337
676
|
response?.message ||
|
|
338
677
|
`Unit ${action === "approve" ? "approved" : "rejected"} successfully`,
|
|
339
678
|
"success"
|
|
340
679
|
);
|
|
341
|
-
|
|
342
680
|
await getUnitCleanerChecklistRefresh();
|
|
343
681
|
} catch (error: any) {
|
|
344
682
|
console.error("Error updating unit checklist:", error);
|
|
@@ -349,7 +687,7 @@ async function handleActionClick(data: {
|
|
|
349
687
|
} finally {
|
|
350
688
|
submitting.value = false;
|
|
351
689
|
if (action === "approve") {
|
|
352
|
-
|
|
690
|
+
stopLoadingAction(getKey(item, set));
|
|
353
691
|
}
|
|
354
692
|
}
|
|
355
693
|
}
|