@7365admin1/layer-common 4.133.0 → 4.134.0
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 +11 -4
- package/components/AreaChecklistHistoryLogs.vue +5 -4
- package/components/CleaningScheduleMain.vue +109 -15
- package/components/FeedbackMain.vue +17 -1
- package/components/ManageChecklistMain.vue +16 -12
- package/components/ScheduleAreaMain.vue +56 -71
- package/components/ScheduleGuide.vue +132 -0
- package/components/ScheduleTaskForm.vue +520 -353
- package/components/ScheduleTaskMain.vue +211 -70
- package/components/TableHygiene.vue +2 -2
- package/composables/useCleaningSchedules.ts +9 -0
- package/composables/useFeedback.ts +4 -0
- package/composables/useScheduleTask.ts +32 -2
- package/package.json +1 -1
- package/types/cleaner-schedule.d.ts +1 -1
- package/utils/schedule-task.ts +281 -0
- package/utils/status.ts +6 -0
- package/composables/useScheduleTaskArea.ts +0 -85
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters align="center" justify="center">
|
|
3
3
|
<v-col cols="12">
|
|
4
|
+
<ScheduleGuide
|
|
5
|
+
:org-id="orgId"
|
|
6
|
+
:site="site"
|
|
7
|
+
:service-type="serviceType"
|
|
8
|
+
text="An extra visit adds one more round to your daily schedule at the time you choose, once or on a repeat. Staff on this site get a push notification when it is added."
|
|
9
|
+
/>
|
|
4
10
|
<TableMain
|
|
5
|
-
:title="'
|
|
11
|
+
:title="'Extra Visits (Scheduled)'"
|
|
6
12
|
:items="items"
|
|
7
13
|
:headers="headers"
|
|
8
14
|
:loading="loading"
|
|
@@ -10,13 +16,23 @@
|
|
|
10
16
|
v-model:page="page"
|
|
11
17
|
:pages="pages"
|
|
12
18
|
:pageRange="pageRange"
|
|
13
|
-
:no-data-text="'No schedule tasks found'"
|
|
14
19
|
@refresh="getTaskRefresh"
|
|
15
20
|
:canCreate="canCreateScheduleTask"
|
|
16
|
-
createLabel="
|
|
21
|
+
createLabel="Add Extra Visit"
|
|
17
22
|
@create="openFormDialog"
|
|
18
23
|
@row-click="onRowClick"
|
|
19
24
|
>
|
|
25
|
+
<template #no-data>
|
|
26
|
+
<div class="st-empty">
|
|
27
|
+
<strong>No extra visits yet.</strong>
|
|
28
|
+
<span>
|
|
29
|
+
An extra visit adds one more round to your daily
|
|
30
|
+
{{ scheduleName }} at a time you choose. Set up your Areas and
|
|
31
|
+
Units first, then add one.
|
|
32
|
+
</span>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
20
36
|
<template #actions>
|
|
21
37
|
<v-row no-gutters align="center" class="w-100">
|
|
22
38
|
<v-col cols="auto" v-if="canCreateScheduleTask">
|
|
@@ -25,7 +41,7 @@
|
|
|
25
41
|
variant="flat"
|
|
26
42
|
@click="openFormDialog"
|
|
27
43
|
>
|
|
28
|
-
|
|
44
|
+
Add Extra Visit
|
|
29
45
|
</v-btn>
|
|
30
46
|
</v-col>
|
|
31
47
|
|
|
@@ -66,13 +82,73 @@
|
|
|
66
82
|
<span v-else>N/A</span>
|
|
67
83
|
</template>
|
|
68
84
|
|
|
69
|
-
<template #item.
|
|
85
|
+
<template #item.when="{ item }">
|
|
86
|
+
<span class="st-when">{{ item.whenText }}</span>
|
|
87
|
+
</template>
|
|
88
|
+
|
|
89
|
+
<template #item.nextRun="{ item }">
|
|
90
|
+
{{ item.nextRunText || "-" }}
|
|
91
|
+
</template>
|
|
92
|
+
|
|
93
|
+
<template #item.lastRun="{ item }">
|
|
94
|
+
<StatusChip
|
|
95
|
+
v-if="item.lastRun && item.lastRunStatus === 'done'"
|
|
96
|
+
status="Done"
|
|
97
|
+
:label="item.lastRunText"
|
|
98
|
+
/>
|
|
99
|
+
<StatusChip
|
|
100
|
+
v-else-if="item.lastRun && item.lastRunStatus === 'missed'"
|
|
101
|
+
status="Missed"
|
|
102
|
+
:label="item.lastRunText"
|
|
103
|
+
/>
|
|
104
|
+
<span v-else>{{ item.lastRunText }}</span>
|
|
105
|
+
</template>
|
|
106
|
+
|
|
107
|
+
<template #item.status="{ item }">
|
|
70
108
|
<StatusChip
|
|
71
|
-
|
|
72
|
-
:
|
|
73
|
-
:label="value || 'N/A'"
|
|
109
|
+
:status="item.stateLabel"
|
|
110
|
+
:label="item.stateLabel"
|
|
74
111
|
/>
|
|
75
112
|
</template>
|
|
113
|
+
|
|
114
|
+
<template #item.actions="{ item }">
|
|
115
|
+
<div class="st-actions">
|
|
116
|
+
<v-btn
|
|
117
|
+
v-if="canUpdateScheduleTask"
|
|
118
|
+
size="small"
|
|
119
|
+
variant="text"
|
|
120
|
+
class="text-none"
|
|
121
|
+
prepend-icon="mdi-pencil-outline"
|
|
122
|
+
@click.stop="onEditRow(item)"
|
|
123
|
+
>
|
|
124
|
+
Edit
|
|
125
|
+
</v-btn>
|
|
126
|
+
<v-btn
|
|
127
|
+
v-if="canUpdateScheduleTask && item.stateKey !== 'ended'"
|
|
128
|
+
size="small"
|
|
129
|
+
variant="text"
|
|
130
|
+
class="text-none"
|
|
131
|
+
:prepend-icon="
|
|
132
|
+
item.stateKey === 'paused' ? 'mdi-play-outline' : 'mdi-pause'
|
|
133
|
+
"
|
|
134
|
+
:loading="pausingId === item._id"
|
|
135
|
+
@click.stop="togglePause(item)"
|
|
136
|
+
>
|
|
137
|
+
{{ item.stateKey === "paused" ? "Resume" : "Pause" }}
|
|
138
|
+
</v-btn>
|
|
139
|
+
<v-btn
|
|
140
|
+
v-if="canDeleteScheduleTask"
|
|
141
|
+
size="small"
|
|
142
|
+
variant="text"
|
|
143
|
+
color="error"
|
|
144
|
+
class="text-none"
|
|
145
|
+
prepend-icon="mdi-delete-outline"
|
|
146
|
+
@click.stop="onDeleteRow(item)"
|
|
147
|
+
>
|
|
148
|
+
Delete
|
|
149
|
+
</v-btn>
|
|
150
|
+
</div>
|
|
151
|
+
</template>
|
|
76
152
|
</TableMain>
|
|
77
153
|
|
|
78
154
|
<ScheduleTaskForm
|
|
@@ -86,14 +162,14 @@
|
|
|
86
162
|
|
|
87
163
|
<v-dialog v-model="dialogShowMoreActions" width="400" persistent>
|
|
88
164
|
<HygieneUpdateMoreAction
|
|
89
|
-
:title="selectedTask?.title || '
|
|
165
|
+
:title="selectedTask?.title || 'Extra Visit'"
|
|
90
166
|
:canUpdate="canUpdateScheduleTask"
|
|
91
167
|
@close="dialogShowMoreActions = false"
|
|
92
168
|
:canDelete="canDeleteScheduleTask"
|
|
93
|
-
:deleteButtonLabel="'Delete
|
|
169
|
+
:deleteButtonLabel="'Delete Extra Visit'"
|
|
94
170
|
@edit="onEditFromMoreAction"
|
|
95
171
|
@delete="onDeleteFromMoreAction"
|
|
96
|
-
:editButtonLabel="'Edit
|
|
172
|
+
:editButtonLabel="'Edit Extra Visit'"
|
|
97
173
|
>
|
|
98
174
|
<template #content>
|
|
99
175
|
<v-row no-gutters>
|
|
@@ -109,32 +185,28 @@
|
|
|
109
185
|
|
|
110
186
|
<v-row no-gutters class="mb-2">
|
|
111
187
|
<v-col cols="5" class="text-subtitle-2 font-weight-bold">
|
|
112
|
-
|
|
188
|
+
When:
|
|
113
189
|
</v-col>
|
|
114
190
|
<v-col cols="7" class="text-subtitle-2">
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
{{ formatDateChip(date) }}
|
|
126
|
-
</v-chip>
|
|
127
|
-
</div>
|
|
128
|
-
<div v-else>N/A</div>
|
|
191
|
+
{{ selectedWhen }}
|
|
192
|
+
</v-col>
|
|
193
|
+
</v-row>
|
|
194
|
+
|
|
195
|
+
<v-row no-gutters class="mb-2">
|
|
196
|
+
<v-col cols="5" class="text-subtitle-2 font-weight-bold">
|
|
197
|
+
Next run:
|
|
198
|
+
</v-col>
|
|
199
|
+
<v-col cols="7" class="text-subtitle-2">
|
|
200
|
+
{{ selectedNext || "-" }}
|
|
129
201
|
</v-col>
|
|
130
202
|
</v-row>
|
|
131
203
|
|
|
132
|
-
<v-row
|
|
204
|
+
<v-row no-gutters class="mb-2">
|
|
133
205
|
<v-col cols="5" class="text-subtitle-2 font-weight-bold">
|
|
134
|
-
|
|
206
|
+
Last run:
|
|
135
207
|
</v-col>
|
|
136
208
|
<v-col cols="7" class="text-subtitle-2">
|
|
137
|
-
{{ selectedTask
|
|
209
|
+
{{ lastRunLabel(selectedTask) }}
|
|
138
210
|
</v-col>
|
|
139
211
|
</v-row>
|
|
140
212
|
|
|
@@ -192,8 +264,8 @@
|
|
|
192
264
|
v-if="
|
|
193
265
|
loadingViewUnits &&
|
|
194
266
|
!(
|
|
195
|
-
area.value ||
|
|
196
|
-
|
|
267
|
+
(area.value || area._id) in
|
|
268
|
+
fetchedViewAreaUnits
|
|
197
269
|
)
|
|
198
270
|
"
|
|
199
271
|
size="14"
|
|
@@ -246,7 +318,8 @@
|
|
|
246
318
|
</v-chip>
|
|
247
319
|
</div>
|
|
248
320
|
<span v-else class="text-caption text-disabled"
|
|
249
|
-
>No units
|
|
321
|
+
>No units in this area, so it is skipped when
|
|
322
|
+
this visit runs.</span
|
|
250
323
|
>
|
|
251
324
|
</v-card-text>
|
|
252
325
|
</div>
|
|
@@ -266,15 +339,14 @@
|
|
|
266
339
|
</v-col>
|
|
267
340
|
</v-row>
|
|
268
341
|
|
|
269
|
-
<v-row
|
|
342
|
+
<v-row no-gutters class="mb-2">
|
|
270
343
|
<v-col cols="5" class="text-subtitle-2 font-weight-bold">
|
|
271
344
|
Status:
|
|
272
345
|
</v-col>
|
|
273
346
|
<v-col cols="7" class="text-subtitle-2">
|
|
274
347
|
<StatusChip
|
|
275
|
-
|
|
276
|
-
:
|
|
277
|
-
:label="selectedTask.status || 'N/A'"
|
|
348
|
+
:status="stateOf(selectedTask).label"
|
|
349
|
+
:label="stateOf(selectedTask).label"
|
|
278
350
|
/>
|
|
279
351
|
</v-col>
|
|
280
352
|
</v-row>
|
|
@@ -307,11 +379,12 @@
|
|
|
307
379
|
<v-dialog v-model="dialogDeleteTask" max-width="520">
|
|
308
380
|
<v-card class="screen-modal">
|
|
309
381
|
<v-card-title class="text-capitalize">
|
|
310
|
-
Delete {{ selectedTask?.title || "
|
|
382
|
+
Delete {{ selectedTask?.title || "Extra Visit" }}
|
|
311
383
|
</v-card-title>
|
|
312
384
|
|
|
313
385
|
<v-card-text class="screen-modal__body">
|
|
314
|
-
|
|
386
|
+
This stops all future runs of this extra visit. Visits that already
|
|
387
|
+
happened are kept. Delete it?
|
|
315
388
|
</v-card-text>
|
|
316
389
|
|
|
317
390
|
<div class="screen-modal__footer">
|
|
@@ -348,6 +421,13 @@
|
|
|
348
421
|
import useScheduleTask from "../composables/useScheduleTask";
|
|
349
422
|
import useUtils from "../composables/useUtils";
|
|
350
423
|
import useAreas from "../composables/useAreas";
|
|
424
|
+
import {
|
|
425
|
+
STATUS_LABELS,
|
|
426
|
+
describeWhen,
|
|
427
|
+
fmtMoment,
|
|
428
|
+
lastRunLabel,
|
|
429
|
+
scheduleNameFor,
|
|
430
|
+
} from "../utils/schedule-task";
|
|
351
431
|
|
|
352
432
|
const props = defineProps({
|
|
353
433
|
orgId: { type: String, required: true },
|
|
@@ -362,8 +442,12 @@ const props = defineProps({
|
|
|
362
442
|
});
|
|
363
443
|
|
|
364
444
|
const { formatDate, debounce } = useUtils();
|
|
365
|
-
const {
|
|
366
|
-
|
|
445
|
+
const {
|
|
446
|
+
getScheduleTasks,
|
|
447
|
+
getScheduleTaskById,
|
|
448
|
+
deleteScheduleTask,
|
|
449
|
+
setScheduleTaskPaused,
|
|
450
|
+
} = useScheduleTask();
|
|
367
451
|
const { getAreaById } = useAreas();
|
|
368
452
|
|
|
369
453
|
const canViewScheduleTasks = computed(() => props.canViewScheduleTasks);
|
|
@@ -374,6 +458,8 @@ const canViewScheduleTaskDetails = computed(
|
|
|
374
458
|
() => props.canViewScheduleTaskDetails
|
|
375
459
|
);
|
|
376
460
|
|
|
461
|
+
const scheduleName = scheduleNameFor(props.serviceType).title;
|
|
462
|
+
|
|
377
463
|
const page = ref(1);
|
|
378
464
|
const pages = ref(0);
|
|
379
465
|
const pageRange = ref("-- - -- of --");
|
|
@@ -433,41 +519,28 @@ const items = ref<any[]>([]);
|
|
|
433
519
|
const headers = [
|
|
434
520
|
{ title: "Title", value: "title" },
|
|
435
521
|
{ title: "Areas", value: "areas" },
|
|
436
|
-
{ title: "
|
|
522
|
+
{ title: "When", value: "when", sortable: false },
|
|
523
|
+
{ title: "Next run", value: "nextRun", sortable: false },
|
|
524
|
+
{ title: "Last run", value: "lastRun", sortable: false },
|
|
525
|
+
{ title: "Status", value: "status", sortable: false },
|
|
526
|
+
{ title: "", value: "actions", sortable: false, align: "end" as const },
|
|
437
527
|
];
|
|
438
528
|
|
|
529
|
+
// Active / Paused / Ended. An older server without `scheduleStatus` shows Active.
|
|
530
|
+
function stateOf(row: Record<string, any>) {
|
|
531
|
+
const key = row?.scheduleStatus || (row?.paused ? "paused" : "active");
|
|
532
|
+
return { key: key as string, label: STATUS_LABELS[key] || "Active" };
|
|
533
|
+
}
|
|
534
|
+
const selectedWhen = computed(() => describeWhen(selectedTask.value as any));
|
|
535
|
+
const selectedNext = computed(() => fmtMoment(selectedTask.value?.nextRun));
|
|
536
|
+
const pausingId = ref("");
|
|
537
|
+
|
|
439
538
|
const showMessage = (text: string, color: string = "success") => {
|
|
440
539
|
message.value = text;
|
|
441
540
|
messageColor.value = color;
|
|
442
541
|
messageSnackbar.value = true;
|
|
443
542
|
};
|
|
444
543
|
|
|
445
|
-
const formatDateChip = (dateStr: string) => {
|
|
446
|
-
const parts = dateStr?.split("-");
|
|
447
|
-
if (parts?.length === 3) return `${parts[2]}-${parts[1]}-${parts[0]}`;
|
|
448
|
-
return dateStr;
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
const mapShortToDay = (short: string) => {
|
|
452
|
-
const mapping: Record<string, string> = {
|
|
453
|
-
Mon: "Monday",
|
|
454
|
-
Tue: "Tuesday",
|
|
455
|
-
Wed: "Wednesday",
|
|
456
|
-
Thu: "Thursday",
|
|
457
|
-
Fri: "Friday",
|
|
458
|
-
Sat: "Saturday",
|
|
459
|
-
Sun: "Sunday",
|
|
460
|
-
};
|
|
461
|
-
return mapping[short] || short;
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
const formatTaskDays = (dayField: any) => {
|
|
465
|
-
if (!dayField) return "N/A";
|
|
466
|
-
if (Array.isArray(dayField))
|
|
467
|
-
return dayField.map((d) => mapShortToDay(d)).join(", ");
|
|
468
|
-
return String(dayField);
|
|
469
|
-
};
|
|
470
|
-
|
|
471
544
|
const {
|
|
472
545
|
data: getTaskReq,
|
|
473
546
|
refresh: getTaskRefresh,
|
|
@@ -488,7 +561,18 @@ const {
|
|
|
488
561
|
|
|
489
562
|
watchEffect(() => {
|
|
490
563
|
if (getTaskReq.value) {
|
|
491
|
-
|
|
564
|
+
const rows: any[] = getTaskReq.value.items || getTaskReq.value || [];
|
|
565
|
+
items.value = rows.map((row) => {
|
|
566
|
+
const st = stateOf(row);
|
|
567
|
+
return {
|
|
568
|
+
...row,
|
|
569
|
+
whenText: describeWhen(row),
|
|
570
|
+
nextRunText: fmtMoment(row.nextRun),
|
|
571
|
+
lastRunText: lastRunLabel(row),
|
|
572
|
+
stateKey: st.key,
|
|
573
|
+
stateLabel: st.label,
|
|
574
|
+
};
|
|
575
|
+
});
|
|
492
576
|
pages.value = getTaskReq.value.pages || 0;
|
|
493
577
|
pageRange.value = getTaskReq.value.pageRange || "-- - -- of --";
|
|
494
578
|
}
|
|
@@ -510,7 +594,41 @@ function openFormDialog() {
|
|
|
510
594
|
showForm.value = true;
|
|
511
595
|
}
|
|
512
596
|
|
|
597
|
+
function onEditRow(item: Record<string, any>) {
|
|
598
|
+
selectedTask.value = item;
|
|
599
|
+
formMode.value = "edit";
|
|
600
|
+
showForm.value = true;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function onDeleteRow(item: Record<string, any>) {
|
|
604
|
+
selectedTask.value = item;
|
|
605
|
+
dialogDeleteTask.value = true;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async function togglePause(item: Record<string, any>) {
|
|
609
|
+
const resuming = item.stateKey === "paused";
|
|
610
|
+
try {
|
|
611
|
+
pausingId.value = item._id;
|
|
612
|
+
await setScheduleTaskPaused(item._id, !resuming);
|
|
613
|
+
showMessage(
|
|
614
|
+
resuming
|
|
615
|
+
? "Resumed. It will run again from its next date."
|
|
616
|
+
: "Paused. It will not run until you resume it.",
|
|
617
|
+
"success",
|
|
618
|
+
);
|
|
619
|
+
await getTaskRefresh();
|
|
620
|
+
} catch (error: any) {
|
|
621
|
+
showMessage(
|
|
622
|
+
error?.data?.message || "Could not change this extra visit.",
|
|
623
|
+
"error",
|
|
624
|
+
);
|
|
625
|
+
} finally {
|
|
626
|
+
pausingId.value = "";
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
513
630
|
async function onRowClick(data: any) {
|
|
631
|
+
if (!canViewScheduleTaskDetails.value) return;
|
|
514
632
|
const taskId = data?.item?._id || data?._id || data?.item?.id || data?.id;
|
|
515
633
|
|
|
516
634
|
if (taskId) {
|
|
@@ -550,13 +668,36 @@ async function _deleteScheduleTask() {
|
|
|
550
668
|
|
|
551
669
|
const response = await deleteScheduleTask(id);
|
|
552
670
|
dialogDeleteTask.value = false;
|
|
553
|
-
showMessage("
|
|
671
|
+
showMessage("Extra visit deleted. Past visits are kept.", "success");
|
|
554
672
|
|
|
555
673
|
await getTaskRefresh();
|
|
556
674
|
} catch (error: any) {
|
|
557
|
-
showMessage(
|
|
675
|
+
showMessage(
|
|
676
|
+
error?.data?.message || "Could not delete this extra visit.",
|
|
677
|
+
"error",
|
|
678
|
+
);
|
|
558
679
|
} finally {
|
|
559
680
|
submitting.value = false;
|
|
560
681
|
}
|
|
561
682
|
}
|
|
562
683
|
</script>
|
|
684
|
+
|
|
685
|
+
<style scoped>
|
|
686
|
+
.st-empty {
|
|
687
|
+
display: flex;
|
|
688
|
+
flex-direction: column;
|
|
689
|
+
gap: 4px;
|
|
690
|
+
padding: 24px 16px;
|
|
691
|
+
font-family: var(--font-sans);
|
|
692
|
+
font-size: var(--fs-cell);
|
|
693
|
+
color: var(--text2);
|
|
694
|
+
}
|
|
695
|
+
.st-when {
|
|
696
|
+
white-space: normal;
|
|
697
|
+
}
|
|
698
|
+
.st-actions {
|
|
699
|
+
display: flex;
|
|
700
|
+
justify-content: flex-end;
|
|
701
|
+
flex-wrap: wrap;
|
|
702
|
+
}
|
|
703
|
+
</style>
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
class="text-none"
|
|
93
93
|
>
|
|
94
94
|
<v-icon size="12">mdi-calendar-clock</v-icon>
|
|
95
|
-
|
|
95
|
+
Extra visit
|
|
96
96
|
</StatusChip>
|
|
97
97
|
<slot name="group-header-chips" :group="group">
|
|
98
98
|
<StatusChip
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
class="text-none"
|
|
115
115
|
>
|
|
116
116
|
<v-icon size="12">mdi-progress-clock</v-icon>
|
|
117
|
-
|
|
117
|
+
In progress · {{ group.completedByName }}
|
|
118
118
|
</StatusChip>
|
|
119
119
|
</slot>
|
|
120
120
|
</v-col>
|
|
@@ -140,6 +140,14 @@ export default function useCleaningSchedules() {
|
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/** Today's day-row, created if missing (safe to repeat). Follow with generateChecklist(site, id). */
|
|
144
|
+
function createTodaysParent(site: string, serviceType: string) {
|
|
145
|
+
return useNuxtApp().$api<{ id: string }>(`/api/hygiene-parent-checklist`, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
body: { site, serviceType },
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
143
151
|
async function getAreaChecklistHistory({
|
|
144
152
|
parentChecklistId = "",
|
|
145
153
|
page = 1,
|
|
@@ -231,6 +239,7 @@ export default function useCleaningSchedules() {
|
|
|
231
239
|
updateAreaChecklistStatus,
|
|
232
240
|
uploadAreaChecklistAttachment,
|
|
233
241
|
generateChecklist,
|
|
242
|
+
createTodaysParent,
|
|
234
243
|
getAreaChecklistHistory,
|
|
235
244
|
getAreaChecklistHistoryById,
|
|
236
245
|
exportAreas,
|
|
@@ -73,6 +73,7 @@ export default function useFeedback() {
|
|
|
73
73
|
provider = "",
|
|
74
74
|
service = "",
|
|
75
75
|
userId = "",
|
|
76
|
+
category = "",
|
|
76
77
|
}: {
|
|
77
78
|
page?: number;
|
|
78
79
|
limit?: number;
|
|
@@ -85,6 +86,8 @@ export default function useFeedback() {
|
|
|
85
86
|
provider?: string | any;
|
|
86
87
|
service?: string;
|
|
87
88
|
userId?: string;
|
|
89
|
+
/** Optional: one category (the feedback subject) or a comma list. Empty = all. */
|
|
90
|
+
category?: string;
|
|
88
91
|
} = {}) {
|
|
89
92
|
try {
|
|
90
93
|
return useNuxtApp().$api<Record<string, any>>("/api/feedbacks2", {
|
|
@@ -100,6 +103,7 @@ export default function useFeedback() {
|
|
|
100
103
|
status,
|
|
101
104
|
provider,
|
|
102
105
|
service,
|
|
106
|
+
...(category && { category }),
|
|
103
107
|
...(currentUser.value?.type != "site" && {
|
|
104
108
|
userId,
|
|
105
109
|
}),
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
// Repeat fields (all optional: a payload without them behaves as a one-off on `dates`).
|
|
2
|
+
// "" / null clears an optional field on the server.
|
|
3
|
+
type TScheduleRepeatFields = {
|
|
4
|
+
repeat?: "none" | "daily" | "weekdays" | "weekly" | "monthly";
|
|
5
|
+
daysOfWeek?: number[] | null;
|
|
6
|
+
startDate?: string | null;
|
|
7
|
+
endDate?: string | null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const repeatBody = (p: TScheduleRepeatFields) => ({
|
|
11
|
+
repeat: p.repeat,
|
|
12
|
+
daysOfWeek: p.daysOfWeek,
|
|
13
|
+
startDate: p.startDate,
|
|
14
|
+
endDate: p.endDate,
|
|
15
|
+
});
|
|
16
|
+
|
|
1
17
|
export default function useScheduleTask() {
|
|
2
18
|
async function getScheduleTasks({
|
|
3
19
|
site = "",
|
|
@@ -38,7 +54,7 @@ export default function useScheduleTask() {
|
|
|
38
54
|
areas: { name: string; value: string }[];
|
|
39
55
|
site: string;
|
|
40
56
|
serviceType: string;
|
|
41
|
-
}) {
|
|
57
|
+
} & TScheduleRepeatFields) {
|
|
42
58
|
return useNuxtApp().$api<Record<string, any>>(
|
|
43
59
|
`/api/hygiene-schedule-tasks/site/${payload.site}`,
|
|
44
60
|
{
|
|
@@ -50,6 +66,7 @@ export default function useScheduleTask() {
|
|
|
50
66
|
description: payload.description,
|
|
51
67
|
areas: payload.areas,
|
|
52
68
|
serviceType: payload.serviceType,
|
|
69
|
+
...repeatBody(payload),
|
|
53
70
|
},
|
|
54
71
|
},
|
|
55
72
|
);
|
|
@@ -63,7 +80,7 @@ export default function useScheduleTask() {
|
|
|
63
80
|
dates: string[];
|
|
64
81
|
description?: string;
|
|
65
82
|
areas: { name: string; value: string }[];
|
|
66
|
-
},
|
|
83
|
+
} & TScheduleRepeatFields,
|
|
67
84
|
) {
|
|
68
85
|
return useNuxtApp().$api<Record<string, any>>(
|
|
69
86
|
`/api/hygiene-schedule-tasks/id/${id}`,
|
|
@@ -75,11 +92,23 @@ export default function useScheduleTask() {
|
|
|
75
92
|
dates: payload.dates,
|
|
76
93
|
description: payload.description,
|
|
77
94
|
areas: payload.areas,
|
|
95
|
+
...repeatBody(payload),
|
|
78
96
|
},
|
|
79
97
|
},
|
|
80
98
|
);
|
|
81
99
|
}
|
|
82
100
|
|
|
101
|
+
/** Pause / resume. The server stops the next runs while paused; nothing is deleted. */
|
|
102
|
+
function setScheduleTaskPaused(id: string, paused: boolean) {
|
|
103
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
104
|
+
`/api/hygiene-schedule-tasks/id/${id}`,
|
|
105
|
+
{
|
|
106
|
+
method: "PATCH",
|
|
107
|
+
body: { paused },
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
83
112
|
function deleteScheduleTask(id: string) {
|
|
84
113
|
return useNuxtApp().$api<Record<string, any>>(
|
|
85
114
|
`/api/hygiene-schedule-tasks/id/${id}`,
|
|
@@ -95,5 +124,6 @@ export default function useScheduleTask() {
|
|
|
95
124
|
updateScheduleTask,
|
|
96
125
|
getScheduleTaskById,
|
|
97
126
|
deleteScheduleTask,
|
|
127
|
+
setScheduleTaskPaused,
|
|
98
128
|
};
|
|
99
129
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@7365admin1/layer-common",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.134.0",
|
|
6
6
|
"author": "7365admin1",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
8
8
|
"//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
|
|
@@ -53,5 +53,5 @@ declare type TFlattenedUnitItem = TUnitChecklistItem & {
|
|
|
53
53
|
set: number;
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
declare type TScheduleAreaStatus = "All" | "Open" | "Ongoing" | "Completed";
|
|
56
|
+
declare type TScheduleAreaStatus = "All" | "Open" | "Ongoing" | "Completed" | "Closed";
|
|
57
57
|
declare type TAreaType = "all" | "common" | "toilet";
|