@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
|
@@ -32,47 +32,87 @@
|
|
|
32
32
|
</v-combobox>
|
|
33
33
|
</v-col>
|
|
34
34
|
|
|
35
|
+
<v-col v-if="shouldShowField('nric')" cols="12">
|
|
36
|
+
<InputLabel class="text-capitalize" title="NRIC" required />
|
|
37
|
+
<InputNRICNumber v-model="vehicle.nric" density="comfortable" :rules="[requiredRule]" />
|
|
38
|
+
</v-col>
|
|
39
|
+
|
|
35
40
|
<v-col v-if="shouldShowField('name')" cols="12">
|
|
36
41
|
<v-row>
|
|
37
42
|
<v-col cols="12">
|
|
38
43
|
<InputLabel class="text-capitalize" title="Full Name" required />
|
|
39
|
-
<v-text-field v-model.trim="vehicle.name" density="comfortable" :rules="[requiredRule]" />
|
|
44
|
+
<v-text-field v-model.trim="vehicle.name" density="comfortable" :rules="[requiredRule]" disabled />
|
|
40
45
|
</v-col>
|
|
41
46
|
</v-row>
|
|
42
47
|
</v-col>
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
<InputLabel class="text-capitalize" title="NRIC" required />
|
|
46
|
-
<InputNRICNumber v-model="vehicle.nric" density="comfortable" :rules="[requiredRule]" />
|
|
47
|
-
</v-col>
|
|
49
|
+
|
|
48
50
|
|
|
49
51
|
<v-col v-if="shouldShowField('phone')" cols="12">
|
|
50
52
|
<InputLabel class="text-capitalize" title="Phone Number" required />
|
|
51
|
-
<InputPhoneNumberV2 v-model="vehicle.phoneNumber" density="comfortable" :rules="[requiredRule]" />
|
|
53
|
+
<InputPhoneNumberV2 v-model="vehicle.phoneNumber" density="comfortable" :rules="[requiredRule]" :disabled="disablePrefilledInputs" />
|
|
52
54
|
</v-col>
|
|
53
55
|
|
|
54
56
|
<v-col v-if="shouldShowField('block')" cols="12">
|
|
55
57
|
<InputLabel class="text-capitalize" title="Block" required />
|
|
56
58
|
<v-select v-model="vehicle.block" :items="blocksArray" item-value="value" item-title="title"
|
|
57
|
-
@update:model-value="handleChangeBlock" density="comfortable" :rules="[requiredRule]" />
|
|
59
|
+
@update:model-value="handleChangeBlock" density="comfortable" :rules="[requiredRule]" :disabled="disablePrefilledInputs" />
|
|
58
60
|
</v-col>
|
|
59
61
|
|
|
60
62
|
<v-col v-if="shouldShowField('level')" cols="12">
|
|
61
63
|
<InputLabel class="text-capitalize" title="Level" required />
|
|
62
|
-
<v-select v-model="vehicle.level" :items="levelsArray" density="comfortable" :disabled="!vehicle.block"
|
|
64
|
+
<v-select v-model="vehicle.level" :items="levelsArray" density="comfortable" :disabled="!vehicle.block || disablePrefilledInputs"
|
|
63
65
|
@update:model-value="handleChangeLevel" :rules="[requiredRule]" />
|
|
64
66
|
</v-col>
|
|
65
67
|
|
|
66
68
|
<v-col v-if="shouldShowField('unit')" cols="12">
|
|
67
69
|
<InputLabel class="text-capitalize" title="Unit" required />
|
|
68
|
-
<v-select v-model="vehicle.unit" :items="unitsArray" density="comfortable" :disabled="!vehicle.level"
|
|
70
|
+
<v-select v-model="vehicle.unit" :items="unitsArray" density="comfortable" :disabled="!vehicle.level || disablePrefilledInputs"
|
|
69
71
|
:rules="[requiredRule]" />
|
|
70
72
|
</v-col>
|
|
71
73
|
|
|
72
74
|
<v-col v-if="shouldShowField('plateNumber')" cols="12">
|
|
73
|
-
<InputLabel class="text-capitalize" title="Vehicle
|
|
75
|
+
<InputLabel class="text-capitalize" title="Vehicle Numbers" required />
|
|
74
76
|
<!-- <v-text-field v-model="vehicle.plateNumber" density="comfortable" :rules="[requiredRule]" /> -->
|
|
75
|
-
|
|
77
|
+
<template v-for="plate in vehicle.plates" :key="plate.plateNumber">
|
|
78
|
+
<v-text-field v-model="plate.plateNumber" density="comfortable" :rules="[requiredRule]" class="mb-2" readonly>
|
|
79
|
+
<template #append-inner>
|
|
80
|
+
<v-chip size="small" class="ma-0" :color="formatVehicleStatus(plate?.status)?.color as string">{{ formatVehicleStatus(plate?.status)?.label }}</v-chip>
|
|
81
|
+
</template>
|
|
82
|
+
</v-text-field>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<template v-for="(plate, index) in newPlateNumbers" :key="index">
|
|
86
|
+
<v-row class="" :class="`${index === newPlateNumbers.length - 1 ? 'mb-1' : 'mb-5'}`" no-gutters>
|
|
87
|
+
<v-col :cols="newPlateNumbers.length > 1 ? '11' : '12'">
|
|
88
|
+
<InputVehicleNumber
|
|
89
|
+
v-model="newPlateNumbers[index]"
|
|
90
|
+
density="comfortable"
|
|
91
|
+
placeholder="New Vehicle Number"
|
|
92
|
+
:rules="[requiredRule]"
|
|
93
|
+
/>
|
|
94
|
+
</v-col>
|
|
95
|
+
|
|
96
|
+
<v-col :cols="newPlateNumbers.length > 1 ? '1' : '0'" class="d-flex align-start">
|
|
97
|
+
<v-btn
|
|
98
|
+
icon="mdi-delete-outline"
|
|
99
|
+
variant="text"
|
|
100
|
+
color="error"
|
|
101
|
+
v-if="newPlateNumbers.length > 1"
|
|
102
|
+
@click="removePlate(index)"
|
|
103
|
+
/>
|
|
104
|
+
</v-col>
|
|
105
|
+
</v-row>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
<v-btn
|
|
109
|
+
prepend-icon="mdi-plus"
|
|
110
|
+
variant="outlined"
|
|
111
|
+
class="text-none"
|
|
112
|
+
@click="addPlate"
|
|
113
|
+
>
|
|
114
|
+
Add Plate
|
|
115
|
+
</v-btn>
|
|
76
116
|
</v-col>
|
|
77
117
|
|
|
78
118
|
<v-col v-if="shouldShowField('remarks')" cols="12">
|
|
@@ -149,6 +189,59 @@
|
|
|
149
189
|
</v-col>
|
|
150
190
|
</v-row>
|
|
151
191
|
</v-toolbar>
|
|
192
|
+
|
|
193
|
+
<v-dialog v-model="showMatchingPeopleDialog" max-width="700">
|
|
194
|
+
<v-card :loading="checkingNRIC">
|
|
195
|
+
<v-toolbar>
|
|
196
|
+
<v-toolbar-title>
|
|
197
|
+
<v-row no-gutters class="d-flex align-center justify-space-between">
|
|
198
|
+
<span class="font-weight-bold">
|
|
199
|
+
Matching Records for NRIC: {{ vehicle.nric }}
|
|
200
|
+
</span>
|
|
201
|
+
<span>
|
|
202
|
+
<v-btn icon="mdi-close" variant="text" @click="showMatchingPeopleDialog = false" />
|
|
203
|
+
</span>
|
|
204
|
+
</v-row>
|
|
205
|
+
</v-toolbar-title>
|
|
206
|
+
</v-toolbar>
|
|
207
|
+
|
|
208
|
+
<v-card-text>
|
|
209
|
+
|
|
210
|
+
<v-list lines="three">
|
|
211
|
+
<v-list-item v-if="matchingPeople.length > 0 || checkingNRIC" v-for="v in matchingPeople" :key="v._id" class="cursor-pointer">
|
|
212
|
+
<v-list-item-title>
|
|
213
|
+
{{ v.name }}
|
|
214
|
+
</v-list-item-title>
|
|
215
|
+
|
|
216
|
+
<v-list-item-subtitle>
|
|
217
|
+
Block {{ v.block }} - {{ v.level }} - {{ v.unitName }}
|
|
218
|
+
</v-list-item-subtitle>
|
|
219
|
+
|
|
220
|
+
<div class="mt-1">
|
|
221
|
+
<v-chip v-for="p in v.plates" :key="p?.plateNumber" size="small" class="mr-1">
|
|
222
|
+
{{ p?.plateNumber }}
|
|
223
|
+
</v-chip>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<template #append>
|
|
227
|
+
<v-btn variant="flat" color="primary" @click="selectNRICRecord(v)">Select</v-btn>
|
|
228
|
+
</template>
|
|
229
|
+
|
|
230
|
+
</v-list-item>
|
|
231
|
+
<v-empty-state v-else
|
|
232
|
+
icon="mdi-account-off"
|
|
233
|
+
title="No matching records found"
|
|
234
|
+
subtitle="The NRIC you entered does not match any existing records.">
|
|
235
|
+
<template #actions>
|
|
236
|
+
<v-btn variant="flat" color="primary" @click="showMatchingPeopleDialog = false">Close</v-btn>
|
|
237
|
+
</template>
|
|
238
|
+
</v-empty-state>
|
|
239
|
+
</v-list>
|
|
240
|
+
|
|
241
|
+
</v-card-text>
|
|
242
|
+
</v-card>
|
|
243
|
+
</v-dialog>
|
|
244
|
+
|
|
152
245
|
</v-card>
|
|
153
246
|
</template>
|
|
154
247
|
|
|
@@ -158,7 +251,7 @@
|
|
|
158
251
|
|
|
159
252
|
const prop = defineProps({
|
|
160
253
|
type: {
|
|
161
|
-
type: String as PropType<TVehicleType>,
|
|
254
|
+
type: String as PropType<TVehicleType | 'seasonpass'>,
|
|
162
255
|
required: true
|
|
163
256
|
},
|
|
164
257
|
org: {
|
|
@@ -180,14 +273,15 @@ const prop = defineProps({
|
|
|
180
273
|
});
|
|
181
274
|
|
|
182
275
|
const { requiredRule, formatDateISO8601, debounce } = useUtils();
|
|
183
|
-
const { addVehicle, getCustomSeasonPassTypes, updateVehicle,
|
|
276
|
+
const { addVehicle, getCustomSeasonPassTypes, updateVehicle, formatVehicleStatus } = useVehicle();
|
|
184
277
|
const { getSiteById, getSiteLevels, getSiteUnits } = useSiteSettings();
|
|
278
|
+
const { findPersonByNRICMultipleResult } = usePeople();
|
|
185
279
|
|
|
186
280
|
const emit = defineEmits(['back', 'select', 'done', 'error', 'close', 'close:all']);
|
|
187
281
|
|
|
188
282
|
|
|
189
|
-
const vehicle = reactive<
|
|
190
|
-
|
|
283
|
+
const vehicle = reactive<Omit<ExceptPartial<TVehicle>, 'type'> & { type: TVehicleType | 'seasonpass' }>({
|
|
284
|
+
plates: [],
|
|
191
285
|
type: prop.type,
|
|
192
286
|
category: "resident",
|
|
193
287
|
name: '',
|
|
@@ -205,15 +299,16 @@ const vehicle = reactive<Partial<TVehicle>>({
|
|
|
205
299
|
_id: '',
|
|
206
300
|
});
|
|
207
301
|
|
|
208
|
-
|
|
302
|
+
const newPlateNumbers = ref<string[]>(['']);
|
|
303
|
+
const disablePrefilledInputs = ref(true);
|
|
209
304
|
|
|
210
305
|
const blocksArray = ref<TDefaultOptionObj[]>([]);
|
|
211
306
|
const levelsArray = ref<TDefaultOptionObj[]>([]);
|
|
212
307
|
const unitsArray = ref<TDefaultOptionObj[]>([]);
|
|
213
308
|
const seasonPassTypeArray = ref<{ title: string, value: string }[]>([]);
|
|
214
309
|
|
|
215
|
-
const
|
|
216
|
-
const
|
|
310
|
+
const matchingPeople = ref<Partial<TPeople>[]>([]);
|
|
311
|
+
const showMatchingPeopleDialog = ref(false);
|
|
217
312
|
const checkingNRIC = ref(false);
|
|
218
313
|
|
|
219
314
|
const defaultSeasonPassTypeArray = computed(() => {
|
|
@@ -231,7 +326,7 @@ const defaultSeasonPassTypeArray = computed(() => {
|
|
|
231
326
|
});
|
|
232
327
|
|
|
233
328
|
|
|
234
|
-
const typeFieldMap: Record<TVehicleType, string[]> = {
|
|
329
|
+
const typeFieldMap: Record<TVehicleType | 'seasonpass', string[]> = {
|
|
235
330
|
seasonpass: ['seasonPassType', 'name', 'phone', 'plateNumber', 'block', 'level', 'unit', 'start', 'end'],
|
|
236
331
|
blocklist: ['name', 'nric', 'phone', 'plateNumber', 'remarks'],
|
|
237
332
|
whitelist: ['name', 'nric', 'phone', 'plateNumber', 'block', 'level', 'unit']
|
|
@@ -256,7 +351,7 @@ const errorMessage = ref('');
|
|
|
256
351
|
|
|
257
352
|
|
|
258
353
|
// fetch existing vehicle data if in edit mode
|
|
259
|
-
if (prop.
|
|
354
|
+
if (prop.vehicleData) {
|
|
260
355
|
const existingVehicleData = JSON.parse(JSON.stringify(prop.vehicleData || {}));
|
|
261
356
|
Object.assign(vehicle, existingVehicleData);
|
|
262
357
|
}
|
|
@@ -370,7 +465,14 @@ function handleChangeLevel(value: any) {
|
|
|
370
465
|
refreshUnitsData();
|
|
371
466
|
}
|
|
372
467
|
|
|
468
|
+
function addPlate() {
|
|
469
|
+
newPlateNumbers.value.push('');
|
|
470
|
+
}
|
|
471
|
+
|
|
373
472
|
|
|
473
|
+
function removePlate(index: number) {
|
|
474
|
+
newPlateNumbers.value.splice(index, 1);
|
|
475
|
+
}
|
|
374
476
|
|
|
375
477
|
function back() {
|
|
376
478
|
emit("back");
|
|
@@ -386,12 +488,13 @@ function close() {
|
|
|
386
488
|
selectedSubscriptionDuration.value = null;
|
|
387
489
|
}
|
|
388
490
|
|
|
389
|
-
function formatVehicleType(type: TVehicleType): string {
|
|
491
|
+
function formatVehicleType(type: TVehicleType | 'seasonpass'): string {
|
|
390
492
|
switch (type) {
|
|
391
493
|
case 'whitelist':
|
|
392
494
|
return 'Whitelist';
|
|
393
495
|
case 'blocklist':
|
|
394
496
|
return 'Blocklist';
|
|
497
|
+
|
|
395
498
|
case 'seasonpass':
|
|
396
499
|
return 'Season Pass';
|
|
397
500
|
default:
|
|
@@ -417,15 +520,16 @@ async function submit() {
|
|
|
417
520
|
const { plateNumber, type, category, name, phoneNumber, block, level, unit, nric, remarks, seasonPassType, start, end, site, org } = vehicle
|
|
418
521
|
|
|
419
522
|
let payload: Partial<TVehiclePayload> = {
|
|
420
|
-
plateNumber,
|
|
523
|
+
plateNumber: newPlateNumbers.value,
|
|
421
524
|
name,
|
|
422
525
|
phoneNumber,
|
|
526
|
+
type: type === 'seasonpass' ? 'whitelist' : type, // season pass will be created as whitelist type in backend with additional seasonPassType field,
|
|
527
|
+
nric
|
|
423
528
|
};
|
|
424
529
|
|
|
425
530
|
if (prop.mode === 'add') {
|
|
426
531
|
payload = {
|
|
427
532
|
...payload,
|
|
428
|
-
type,
|
|
429
533
|
category,
|
|
430
534
|
site,
|
|
431
535
|
org
|
|
@@ -452,7 +556,6 @@ async function submit() {
|
|
|
452
556
|
} else if (vehicle.type === 'blocklist') {
|
|
453
557
|
payload = {
|
|
454
558
|
...payload,
|
|
455
|
-
nric,
|
|
456
559
|
remarks,
|
|
457
560
|
};
|
|
458
561
|
}
|
|
@@ -541,23 +644,41 @@ watch([() => vehicle.end, () => vehicle.start], () => {
|
|
|
541
644
|
});
|
|
542
645
|
|
|
543
646
|
|
|
647
|
+
function selectNRICRecord(record: TPeople) {
|
|
648
|
+
|
|
649
|
+
vehicle.name = record.name;
|
|
650
|
+
vehicle.phoneNumber = record.contact;
|
|
651
|
+
vehicle.block = Number(record.block);
|
|
652
|
+
vehicle.level = record.level;
|
|
653
|
+
vehicle.unit = record.unit;
|
|
654
|
+
|
|
655
|
+
vehicle.plates = record.plates || [];
|
|
656
|
+
|
|
657
|
+
disablePrefilledInputs.value = true;
|
|
658
|
+
showMatchingPeopleDialog.value = false;
|
|
659
|
+
|
|
660
|
+
refreshLevelsData();
|
|
661
|
+
refreshUnitsData();
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
|
|
544
665
|
async function checkNRIC() {
|
|
545
666
|
if (!vehicle.nric || vehicle.nric.length < 5) return;
|
|
546
667
|
|
|
547
668
|
checkingNRIC.value = true;
|
|
669
|
+
showMatchingPeopleDialog.value = true;
|
|
548
670
|
try {
|
|
549
|
-
const res = await
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
671
|
+
const res = await findPersonByNRICMultipleResult(vehicle.nric, prop.site) as { items: TPeople[] } | null;
|
|
672
|
+
|
|
673
|
+
if (res?.items && res.items.length > 0) {
|
|
674
|
+
matchingPeople.value = res.items || []
|
|
553
675
|
} else {
|
|
554
|
-
|
|
555
|
-
matchingVehicles.value = [];
|
|
676
|
+
matchingPeople.value = [];
|
|
556
677
|
}
|
|
678
|
+
|
|
679
|
+
|
|
557
680
|
} catch (error) {
|
|
558
|
-
console.error(
|
|
559
|
-
showVehicleMatchDialog.value = false;
|
|
560
|
-
matchingVehicles.value = [];
|
|
681
|
+
console.error("NRIC search failed:", error);
|
|
561
682
|
} finally {
|
|
562
683
|
checkingNRIC.value = false;
|
|
563
684
|
}
|
|
@@ -568,26 +689,27 @@ const debounceedCheckNRIC = debounce(checkNRIC, 500);
|
|
|
568
689
|
watch(
|
|
569
690
|
() => vehicle.nric,
|
|
570
691
|
async (newNRIC) => {
|
|
571
|
-
|
|
692
|
+
resetVehicleDetails();
|
|
693
|
+
matchingPeople.value = [];
|
|
694
|
+
if (!newNRIC || newNRIC.length < 3) return;
|
|
572
695
|
|
|
573
696
|
debounceedCheckNRIC();
|
|
574
697
|
}
|
|
575
698
|
);
|
|
576
699
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
errorMessage.value = '';
|
|
587
|
-
showVehicleMatchDialog.value = false;
|
|
588
|
-
}
|
|
700
|
+
|
|
701
|
+
const resetVehicleDetails = () => {
|
|
702
|
+
vehicle.name = '';
|
|
703
|
+
vehicle.phoneNumber = '';
|
|
704
|
+
vehicle.block = '';
|
|
705
|
+
vehicle.level = '';
|
|
706
|
+
vehicle.unit = '';
|
|
707
|
+
vehicle.plates = [];
|
|
708
|
+
disablePrefilledInputs.value = false;
|
|
589
709
|
}
|
|
590
|
-
|
|
710
|
+
|
|
711
|
+
watch()
|
|
712
|
+
|
|
591
713
|
|
|
592
714
|
|
|
593
715
|
|