@7365admin1/layer-common 1.10.5 → 1.10.7
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 +12 -0
- package/components/AccessCardDetailsDialog.vue +144 -0
- package/components/AccessCardPreviewDialog.vue +7 -2
- package/components/AccessCardQrTagging.vue +314 -34
- package/components/AccessCardQrTaggingPrintQr.vue +75 -0
- package/components/AccessManagement.vue +5 -1
- package/components/AreaChecklistHistoryLogs.vue +9 -0
- package/components/BuildingForm.vue +36 -5
- package/components/BuildingManagement/buildings.vue +18 -9
- package/components/BuildingManagement/units.vue +12 -114
- package/components/BuildingUnitFormAdd.vue +39 -30
- package/components/BuildingUnitFormEdit.vue +265 -116
- package/components/CleaningScheduleMain.vue +60 -13
- package/components/Dialog/DeleteConfirmation.vue +2 -2
- package/components/Dialog/UpdateMoreAction.vue +2 -2
- package/components/EntryPassInformation.vue +215 -0
- package/components/Input/InputPhoneNumberV2.vue +11 -0
- package/components/ManageChecklistMain.vue +29 -3
- package/components/PlateNumberDisplay.vue +9 -1
- package/components/ScheduleAreaMain.vue +56 -0
- package/components/TableHygiene.vue +265 -113
- package/components/UnitPersonCard.vue +63 -0
- package/components/VehicleAddSelection.vue +2 -2
- package/components/VehicleForm.vue +169 -47
- package/components/VehicleManagement.vue +169 -43
- package/components/VisitorForm.vue +19 -0
- package/components/VisitorManagement.vue +1 -1
- package/components/WorkOrder/Main.vue +2 -1
- package/composables/useAccessManagement.ts +63 -0
- package/composables/useFeedback.ts +2 -2
- package/composables/usePeople.ts +14 -3
- package/composables/useVehicle.ts +15 -1
- package/composables/useWorkOrder.ts +2 -2
- package/package.json +3 -2
- package/types/cleaner-schedule.d.ts +1 -0
- package/types/html2pdf.d.ts +19 -0
- package/types/people.d.ts +4 -0
- package/types/site.d.ts +8 -0
- package/types/vehicle.d.ts +3 -4
- package/.playground/app.vue +0 -41
- package/.playground/eslint.config.mjs +0 -6
- package/.playground/nuxt.config.ts +0 -22
- package/.playground/pages/feedback.vue +0 -30
- package/components/AccessCardHistoryDialog.vue +0 -133
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
<!-- Top Actions -->
|
|
4
3
|
<v-col cols="12" class="mb-2" v-if="canCreate || $slots.actions">
|
|
5
4
|
<v-row no-gutters>
|
|
6
5
|
<slot name="actions">
|
|
@@ -18,7 +17,6 @@
|
|
|
18
17
|
</v-row>
|
|
19
18
|
</v-col>
|
|
20
19
|
|
|
21
|
-
<!-- List Card -->
|
|
22
20
|
<v-col cols="12">
|
|
23
21
|
<v-card
|
|
24
22
|
width="100%"
|
|
@@ -27,7 +25,6 @@
|
|
|
27
25
|
rounded="lg"
|
|
28
26
|
:loading="loading"
|
|
29
27
|
>
|
|
30
|
-
<!-- Toolbar -->
|
|
31
28
|
<v-toolbar
|
|
32
29
|
density="compact"
|
|
33
30
|
color="grey-lighten-4"
|
|
@@ -40,7 +37,10 @@
|
|
|
40
37
|
<slot name="prepend-additional" />
|
|
41
38
|
</template>
|
|
42
39
|
|
|
43
|
-
<v-toolbar-title
|
|
40
|
+
<v-toolbar-title
|
|
41
|
+
v-if="title"
|
|
42
|
+
class="text-subtitle-1 font-weight-medium"
|
|
43
|
+
>
|
|
44
44
|
{{ title }}
|
|
45
45
|
</v-toolbar-title>
|
|
46
46
|
|
|
@@ -64,126 +64,236 @@
|
|
|
64
64
|
|
|
65
65
|
<v-divider />
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
<v-sheet
|
|
68
|
+
:max-height="`calc(100vh - (${offset}px))`"
|
|
69
|
+
class="overflow-y-auto"
|
|
70
70
|
>
|
|
71
|
-
<v-
|
|
71
|
+
<v-row
|
|
72
72
|
v-if="groupedItems.length === 0"
|
|
73
|
-
|
|
73
|
+
no-gutters
|
|
74
|
+
justify="center"
|
|
75
|
+
class="py-10"
|
|
74
76
|
>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
<v-col
|
|
78
|
+
cols="auto"
|
|
79
|
+
class="text-center text-medium-emphasis text-body-2"
|
|
80
|
+
>
|
|
81
|
+
{{ noDataText }}
|
|
82
|
+
</v-col>
|
|
83
|
+
</v-row>
|
|
77
84
|
|
|
78
85
|
<template
|
|
79
86
|
v-for="(group, groupIndex) in groupedItems"
|
|
80
87
|
:key="`group-${groupIndex}`"
|
|
81
88
|
>
|
|
82
|
-
<
|
|
83
|
-
v-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
89
|
+
<v-sheet color="grey-lighten-4" border="b t">
|
|
90
|
+
<v-row no-gutters align="center" class="px-4 py-2">
|
|
91
|
+
<v-col cols="auto" class="d-flex align-center ga-2">
|
|
92
|
+
<span
|
|
93
|
+
class="text-caption font-weight-bold text-medium-emphasis text-uppercase"
|
|
94
|
+
>
|
|
95
|
+
Set {{ group.set }}
|
|
96
|
+
</span>
|
|
97
|
+
<v-chip
|
|
98
|
+
v-if="group.isScheduleTask"
|
|
99
|
+
size="x-small"
|
|
100
|
+
color="info"
|
|
101
|
+
variant="tonal"
|
|
102
|
+
prepend-icon="mdi-calendar-clock"
|
|
103
|
+
class="text-none"
|
|
104
|
+
>
|
|
105
|
+
Schedule Task
|
|
106
|
+
</v-chip>
|
|
107
|
+
<v-chip
|
|
108
|
+
v-if="group.completedByName && isGroupComplete(group)"
|
|
109
|
+
size="x-small"
|
|
110
|
+
color="success"
|
|
111
|
+
variant="tonal"
|
|
112
|
+
prepend-icon="mdi-check-circle-outline"
|
|
113
|
+
class="text-none"
|
|
114
|
+
>
|
|
115
|
+
Completed · {{ group.completedByName }}
|
|
116
|
+
</v-chip>
|
|
117
|
+
<v-chip
|
|
118
|
+
v-else-if="
|
|
119
|
+
group.completedByName && isGroupInProgress(group)
|
|
120
|
+
"
|
|
121
|
+
size="x-small"
|
|
122
|
+
color="warning"
|
|
123
|
+
variant="tonal"
|
|
124
|
+
prepend-icon="mdi-progress-clock"
|
|
125
|
+
class="text-none"
|
|
126
|
+
>
|
|
127
|
+
Ongoing · {{ group.completedByName }}
|
|
128
|
+
</v-chip>
|
|
129
|
+
</v-col>
|
|
130
|
+
<v-spacer />
|
|
131
|
+
<v-col cols="auto">
|
|
132
|
+
<v-btn
|
|
133
|
+
v-if="group.attachments && group.attachments.length > 0"
|
|
134
|
+
size="x-small"
|
|
135
|
+
variant="tonal"
|
|
136
|
+
color="primary"
|
|
137
|
+
class="text-none"
|
|
138
|
+
prepend-icon="mdi-paperclip"
|
|
139
|
+
@click.stop="
|
|
140
|
+
openAttachmentDialog(group.set, group.attachments)
|
|
141
|
+
"
|
|
142
|
+
>
|
|
143
|
+
{{ group.attachments.length }} attachment{{
|
|
144
|
+
group.attachments.length > 1 ? "s" : ""
|
|
145
|
+
}}
|
|
146
|
+
</v-btn>
|
|
147
|
+
</v-col>
|
|
148
|
+
</v-row>
|
|
149
|
+
</v-sheet>
|
|
150
|
+
|
|
151
|
+
<v-sheet
|
|
109
152
|
v-for="item in group.items"
|
|
110
153
|
:key="item[itemValue]"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
isItemSelected(item, group.set)
|
|
114
|
-
? ['bg-grey-lighten-4', 'rounded']
|
|
115
|
-
: []
|
|
154
|
+
:color="
|
|
155
|
+
isItemSelected(item, group.set) ? 'grey-lighten-4' : 'white'
|
|
116
156
|
"
|
|
157
|
+
border="b"
|
|
117
158
|
>
|
|
118
|
-
<
|
|
119
|
-
<v-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
<
|
|
140
|
-
name="list-item
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
<v-btn
|
|
161
|
-
icon="mdi-check"
|
|
162
|
-
size="small"
|
|
163
|
-
:variant="
|
|
164
|
-
activeActions[getKey(item, group.set)] === 'approve'
|
|
165
|
-
? 'flat'
|
|
166
|
-
: 'text'
|
|
167
|
-
"
|
|
168
|
-
color="success"
|
|
169
|
-
@click.stop="
|
|
170
|
-
handleActionClick(item, group.set, 'approve')
|
|
159
|
+
<v-row no-gutters align="center" class="px-4 py-2">
|
|
160
|
+
<v-col cols="auto" class="mr-3">
|
|
161
|
+
<v-icon
|
|
162
|
+
size="20"
|
|
163
|
+
:color="
|
|
164
|
+
activeActions[getKey(item, group.set)] === 'approve'
|
|
165
|
+
? 'success'
|
|
166
|
+
: activeActions[getKey(item, group.set)] === 'reject'
|
|
167
|
+
? 'error'
|
|
168
|
+
: 'grey-lighten-2'
|
|
169
|
+
"
|
|
170
|
+
>
|
|
171
|
+
{{
|
|
172
|
+
activeActions[getKey(item, group.set)] === "approve"
|
|
173
|
+
? "mdi-check-circle"
|
|
174
|
+
: activeActions[getKey(item, group.set)] === "reject"
|
|
175
|
+
? "mdi-close-circle"
|
|
176
|
+
: "mdi-circle-outline"
|
|
177
|
+
}}
|
|
178
|
+
</v-icon>
|
|
179
|
+
</v-col>
|
|
180
|
+
<v-col>
|
|
181
|
+
<slot name="list-item" :item="item">
|
|
182
|
+
<v-row no-gutters align-center>
|
|
183
|
+
<v-col cols="12">
|
|
184
|
+
<span
|
|
185
|
+
class="text-body-2 font-weight-medium"
|
|
186
|
+
:class="
|
|
187
|
+
activeActions[getKey(item, group.set)] === 'approve'
|
|
188
|
+
? 'text-decoration-line-through text-medium-emphasis'
|
|
189
|
+
: ''
|
|
190
|
+
"
|
|
191
|
+
>
|
|
192
|
+
{{ getItemValue(item, headers[0].value) }}
|
|
193
|
+
</span>
|
|
194
|
+
</v-col>
|
|
195
|
+
<v-col
|
|
196
|
+
v-if="
|
|
197
|
+
item.timestamp ||
|
|
198
|
+
(headers[1] &&
|
|
199
|
+
getItemValue(item, headers[1].value)) ||
|
|
200
|
+
(headers[2] && getItemValue(item, headers[2].value))
|
|
171
201
|
"
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
202
|
+
cols="12"
|
|
203
|
+
>
|
|
204
|
+
<v-row
|
|
205
|
+
no-gutters
|
|
206
|
+
align="center"
|
|
207
|
+
class="ga-3 flex-wrap mt-1"
|
|
208
|
+
>
|
|
209
|
+
<v-col
|
|
210
|
+
v-if="item.timestamp"
|
|
211
|
+
cols="auto"
|
|
212
|
+
class="d-flex align-center ga-1 text-caption text-medium-emphasis pa-0"
|
|
213
|
+
>
|
|
214
|
+
<v-icon size="11">mdi-clock-outline</v-icon>
|
|
215
|
+
{{ formatTimestamp(item.timestamp) }}
|
|
216
|
+
</v-col>
|
|
217
|
+
<v-col
|
|
218
|
+
v-if="
|
|
219
|
+
headers[1] && getItemValue(item, headers[1].value)
|
|
220
|
+
"
|
|
221
|
+
cols="auto"
|
|
222
|
+
class="text-caption text-medium-emphasis pa-0"
|
|
223
|
+
>
|
|
224
|
+
{{ getItemValue(item, headers[1].value) }}
|
|
225
|
+
</v-col>
|
|
226
|
+
<v-col
|
|
227
|
+
v-if="
|
|
228
|
+
headers[2] && getItemValue(item, headers[2].value)
|
|
229
|
+
"
|
|
230
|
+
cols="auto"
|
|
231
|
+
class="text-caption text-medium-emphasis pa-0"
|
|
232
|
+
>
|
|
233
|
+
{{ getItemValue(item, headers[2].value) }}
|
|
234
|
+
</v-col>
|
|
235
|
+
</v-row>
|
|
236
|
+
</v-col>
|
|
237
|
+
</v-row>
|
|
238
|
+
</slot>
|
|
239
|
+
</v-col>
|
|
240
|
+
|
|
241
|
+
<v-col cols="auto">
|
|
242
|
+
<slot
|
|
243
|
+
name="list-item-append"
|
|
244
|
+
:item="item"
|
|
245
|
+
:isSelected="isItemSelected(item, group.set)"
|
|
246
|
+
>
|
|
247
|
+
<v-row
|
|
248
|
+
v-if="canManageScheduleTasks"
|
|
249
|
+
no-gutters
|
|
250
|
+
align="center"
|
|
251
|
+
>
|
|
252
|
+
<v-col cols="auto">
|
|
253
|
+
<v-btn
|
|
254
|
+
icon="mdi-close"
|
|
255
|
+
size="small"
|
|
256
|
+
:variant="
|
|
257
|
+
activeActions[getKey(item, group.set)] === 'reject'
|
|
258
|
+
? 'flat'
|
|
259
|
+
: 'text'
|
|
260
|
+
"
|
|
261
|
+
color="error"
|
|
262
|
+
@click.stop="
|
|
263
|
+
handleActionClick(item, group.set, 'reject')
|
|
264
|
+
"
|
|
265
|
+
/>
|
|
266
|
+
</v-col>
|
|
267
|
+
<v-col cols="auto">
|
|
268
|
+
<v-btn
|
|
269
|
+
icon="mdi-check"
|
|
270
|
+
size="small"
|
|
271
|
+
:variant="
|
|
272
|
+
activeActions[getKey(item, group.set)] === 'approve'
|
|
273
|
+
? 'flat'
|
|
274
|
+
: 'text'
|
|
275
|
+
"
|
|
276
|
+
color="success"
|
|
277
|
+
:loading="!!loadingActions[getKey(item, group.set)]"
|
|
278
|
+
:disabled="!!loadingActions[getKey(item, group.set)]"
|
|
279
|
+
@click.stop="
|
|
280
|
+
handleActionClick(item, group.set, 'approve')
|
|
281
|
+
"
|
|
282
|
+
/>
|
|
283
|
+
</v-col>
|
|
284
|
+
</v-row>
|
|
285
|
+
</slot>
|
|
286
|
+
</v-col>
|
|
287
|
+
</v-row>
|
|
288
|
+
</v-sheet>
|
|
178
289
|
</template>
|
|
179
|
-
</v-
|
|
290
|
+
</v-sheet>
|
|
180
291
|
|
|
181
292
|
<slot name="footer" />
|
|
182
293
|
</v-card>
|
|
183
294
|
</v-col>
|
|
184
295
|
</v-row>
|
|
185
296
|
|
|
186
|
-
<!-- Attachment Preview Dialog -->
|
|
187
297
|
<v-dialog v-model="showAttachmentDialog" max-width="700" scrollable>
|
|
188
298
|
<v-card>
|
|
189
299
|
<v-card-title class="d-flex align-center pa-4">
|
|
@@ -248,7 +358,6 @@
|
|
|
248
358
|
</v-card>
|
|
249
359
|
</v-dialog>
|
|
250
360
|
|
|
251
|
-
<!-- Full Image Lightbox -->
|
|
252
361
|
<v-dialog v-model="showLightbox" max-width="900">
|
|
253
362
|
<v-card>
|
|
254
363
|
<v-card-actions class="pa-2 justify-end">
|
|
@@ -351,12 +460,14 @@ const emits = defineEmits([
|
|
|
351
460
|
|
|
352
461
|
defineExpose({
|
|
353
462
|
revertSetApprovals,
|
|
463
|
+
stopLoadingAction,
|
|
354
464
|
});
|
|
355
465
|
|
|
356
466
|
const internalPage = ref(props.page);
|
|
357
467
|
const selected = shallowRef<any[]>(props.selected);
|
|
358
468
|
const activeActions = reactive<Record<string, "approve" | "reject">>({});
|
|
359
469
|
const persistedActions = reactive<Record<string, "approve" | "reject">>({});
|
|
470
|
+
const loadingActions = reactive<Record<string, boolean>>({});
|
|
360
471
|
const completedSets = ref<Set<number>>(new Set());
|
|
361
472
|
const itemOrderMap = new Map<string, number>();
|
|
362
473
|
|
|
@@ -400,6 +511,7 @@ const groupedItems = computed(() => {
|
|
|
400
511
|
|
|
401
512
|
return {
|
|
402
513
|
set: item.set,
|
|
514
|
+
isScheduleTask: item.isScheduleTask ?? false,
|
|
403
515
|
completedByName: item.completedByName ?? null,
|
|
404
516
|
attachments:
|
|
405
517
|
(item.attachment as string[] | undefined) ??
|
|
@@ -431,6 +543,34 @@ const allItemsApproved = computed(() => {
|
|
|
431
543
|
);
|
|
432
544
|
});
|
|
433
545
|
|
|
546
|
+
function formatTimestamp(ts: string): string {
|
|
547
|
+
if (!ts) return "";
|
|
548
|
+
const date = new Date(ts);
|
|
549
|
+
return date.toLocaleString("en-SG", {
|
|
550
|
+
day: "2-digit",
|
|
551
|
+
month: "short",
|
|
552
|
+
year: "numeric",
|
|
553
|
+
hour: "2-digit",
|
|
554
|
+
minute: "2-digit",
|
|
555
|
+
hour12: true,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function isGroupComplete(group: { items: any[] }): boolean {
|
|
560
|
+
return (
|
|
561
|
+
group.items.length > 0 &&
|
|
562
|
+
group.items.every((item: any) => item.approve === true)
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function isGroupInProgress(group: { items: any[] }): boolean {
|
|
567
|
+
return (
|
|
568
|
+
group.items.some(
|
|
569
|
+
(item: any) => item.approve === true || item.reject === true
|
|
570
|
+
) && !isGroupComplete(group)
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
434
574
|
function isSetFullyApproved(setNumber: number): boolean {
|
|
435
575
|
const group = groupedItems.value.find((g) => g.set === setNumber);
|
|
436
576
|
if (!group) return false;
|
|
@@ -442,7 +582,7 @@ function isSetFullyApproved(setNumber: number): boolean {
|
|
|
442
582
|
}
|
|
443
583
|
|
|
444
584
|
function getNewApprovedItemsForSet(
|
|
445
|
-
setNumber: number
|
|
585
|
+
setNumber: number
|
|
446
586
|
): Array<{ key: string; item: any; action: "approve" }> {
|
|
447
587
|
const group = groupedItems.value.find((g) => g.set === setNumber);
|
|
448
588
|
if (!group) return [];
|
|
@@ -472,14 +612,14 @@ watch(
|
|
|
472
612
|
internalPage.value = val;
|
|
473
613
|
|
|
474
614
|
itemOrderMap.clear();
|
|
475
|
-
}
|
|
615
|
+
}
|
|
476
616
|
);
|
|
477
617
|
|
|
478
618
|
watch(
|
|
479
619
|
() => props.selected,
|
|
480
620
|
(val) => {
|
|
481
621
|
selected.value = val;
|
|
482
|
-
}
|
|
622
|
+
}
|
|
483
623
|
);
|
|
484
624
|
|
|
485
625
|
watch(selected, (val) => {
|
|
@@ -492,7 +632,7 @@ watch(
|
|
|
492
632
|
if (!items || !Array.isArray(items)) return;
|
|
493
633
|
|
|
494
634
|
Object.keys(persistedActions).forEach(
|
|
495
|
-
(key) => delete persistedActions[key]
|
|
635
|
+
(key) => delete persistedActions[key]
|
|
496
636
|
);
|
|
497
637
|
|
|
498
638
|
items.forEach((group: any) => {
|
|
@@ -511,7 +651,7 @@ watch(
|
|
|
511
651
|
});
|
|
512
652
|
});
|
|
513
653
|
},
|
|
514
|
-
{ immediate: true }
|
|
654
|
+
{ immediate: true }
|
|
515
655
|
);
|
|
516
656
|
|
|
517
657
|
function getKey(item: any, set?: number): string {
|
|
@@ -530,7 +670,7 @@ function isItemSelected(item: any, set?: number): boolean {
|
|
|
530
670
|
|
|
531
671
|
if (typeof selected.value[0] === "object" && "unit" in selected.value[0]) {
|
|
532
672
|
return selected.value.some(
|
|
533
|
-
(s: any) => s.unit === item[props.itemValue] && s.set === set
|
|
673
|
+
(s: any) => s.unit === item[props.itemValue] && s.set === set
|
|
534
674
|
);
|
|
535
675
|
}
|
|
536
676
|
|
|
@@ -540,7 +680,7 @@ function isItemSelected(item: any, set?: number): boolean {
|
|
|
540
680
|
function handleActionClick(
|
|
541
681
|
item: any,
|
|
542
682
|
set: number | undefined,
|
|
543
|
-
action: "approve" | "reject"
|
|
683
|
+
action: "approve" | "reject"
|
|
544
684
|
): void {
|
|
545
685
|
const key = getKey(item, set);
|
|
546
686
|
|
|
@@ -584,6 +724,13 @@ function handleActionClick(
|
|
|
584
724
|
return;
|
|
585
725
|
}
|
|
586
726
|
}
|
|
727
|
+
|
|
728
|
+
// Non-completing approve: undo optimistic check and show loading instead
|
|
729
|
+
if (!isPersisted) {
|
|
730
|
+
delete activeActions[key];
|
|
731
|
+
loadingActions[key] = true;
|
|
732
|
+
}
|
|
733
|
+
|
|
587
734
|
console.debug("TableHygiene: emitting action-click (approve immediate)", {
|
|
588
735
|
item: { ...item, set },
|
|
589
736
|
action,
|
|
@@ -592,6 +739,10 @@ function handleActionClick(
|
|
|
592
739
|
}
|
|
593
740
|
}
|
|
594
741
|
|
|
742
|
+
function stopLoadingAction(key: string): void {
|
|
743
|
+
delete loadingActions[key];
|
|
744
|
+
}
|
|
745
|
+
|
|
595
746
|
function revertSetApprovals(setNumber: number): void {
|
|
596
747
|
const group = groupedItems.value.find((g) => g.set === setNumber);
|
|
597
748
|
if (group) {
|
|
@@ -611,7 +762,8 @@ watch(
|
|
|
611
762
|
() => props.items,
|
|
612
763
|
() => {
|
|
613
764
|
completedSets.value.clear();
|
|
765
|
+
Object.keys(loadingActions).forEach((k) => delete loadingActions[k]);
|
|
614
766
|
},
|
|
615
|
-
{ deep: true }
|
|
767
|
+
{ deep: true }
|
|
616
768
|
);
|
|
617
769
|
</script>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card-text>
|
|
3
|
+
<v-row>
|
|
4
|
+
<v-col cols="12" md="4" class="mt-2">
|
|
5
|
+
<InputLabel class="font-weight-bold" title="Name" />
|
|
6
|
+
<v-text-field :model-value="person.name" density="compact" :readonly="readOnly" :class="{'no-pointer': readOnly}"/>
|
|
7
|
+
</v-col>
|
|
8
|
+
|
|
9
|
+
<v-col cols="12" md="4" class="mt-2">
|
|
10
|
+
<InputLabel class="font-weight-bold" title="Email Address" />
|
|
11
|
+
<v-text-field :model-value="person.email" density="compact" :readonly="readOnly" :class="{'no-pointer': readOnly}"/>
|
|
12
|
+
</v-col>
|
|
13
|
+
|
|
14
|
+
<v-col cols="12" md="4" class="mt-2">
|
|
15
|
+
<InputLabel class="font-weight-bold" title="NRIC" />
|
|
16
|
+
<v-text-field :model-value="person.nric" density="compact" :readonly="readOnly" :class="{'no-pointer': readOnly}"/>
|
|
17
|
+
</v-col>
|
|
18
|
+
|
|
19
|
+
<v-col cols="12" md="4" class="mt-2">
|
|
20
|
+
<InputLabel class="font-weight-bold" title="Mobile Number" />
|
|
21
|
+
<v-text-field :model-value="person.contact" density="compact" :readonly="readOnly" :class="{'no-pointer': readOnly}"/>
|
|
22
|
+
</v-col>
|
|
23
|
+
</v-row>
|
|
24
|
+
|
|
25
|
+
<v-row>
|
|
26
|
+
<v-col cols="12" md="4" class="mt-2">
|
|
27
|
+
<InputLabel class="font-weight-bold" title="Other Documents and Files" :viewMode="readOnly" />
|
|
28
|
+
|
|
29
|
+
<p
|
|
30
|
+
v-if="!person.files?.length"
|
|
31
|
+
class="text-blue-darken-2 font-weight-thin text-caption ml-2 mt-5"
|
|
32
|
+
>
|
|
33
|
+
**No documents to display**
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
<InputFileV2
|
|
37
|
+
v-if="person.files?.length"
|
|
38
|
+
:files="person.files"
|
|
39
|
+
:viewMode="true"
|
|
40
|
+
/>
|
|
41
|
+
</v-col>
|
|
42
|
+
</v-row>
|
|
43
|
+
</v-card-text>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
defineProps({
|
|
48
|
+
person: {
|
|
49
|
+
type: Object as PropType<TPeople>,
|
|
50
|
+
required: true,
|
|
51
|
+
},
|
|
52
|
+
readOnly: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: false,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<style lang="scss" scoped>
|
|
60
|
+
.no-pointer {
|
|
61
|
+
pointer-events: none;
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
@@ -40,7 +40,7 @@ const prop = defineProps({
|
|
|
40
40
|
|
|
41
41
|
const emit = defineEmits(['cancel', 'select']);
|
|
42
42
|
|
|
43
|
-
const selection = computed<{label: string, value: TVehicleType}[]>(() => {
|
|
43
|
+
const selection = computed<{label: string, value: TVehicleType | 'seasonpass'}[]>(() => {
|
|
44
44
|
return [
|
|
45
45
|
{ label: "Whitelist", value: "whitelist" },
|
|
46
46
|
{ label: "Season Pass", value: "seasonpass" },
|
|
@@ -52,7 +52,7 @@ function cancel() {
|
|
|
52
52
|
emit("cancel");
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function select(value:
|
|
55
|
+
function select(value: TVehicleType | 'seasonpass') {
|
|
56
56
|
emit("select", value);
|
|
57
57
|
}
|
|
58
58
|
</script>
|