@7365admin1/layer-common 1.10.6 → 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 +12 -0
- package/components/AccessCardQrTagging.vue +314 -34
- package/components/AccessCardQrTaggingPrintQr.vue +75 -0
- package/components/{AddSupplyForm.vue → AddEqupmentForm.vue} +5 -5
- package/components/AreaChecklistHistoryLogs.vue +9 -0
- package/components/BuildingForm.vue +36 -5
- package/components/BuildingManagement/buildings.vue +18 -9
- package/components/BuildingManagement/units.vue +13 -115
- package/components/BuildingUnitFormAdd.vue +42 -33
- package/components/BuildingUnitFormEdit.vue +334 -139
- package/components/CleaningScheduleMain.vue +60 -13
- package/components/Dialog/DeleteConfirmation.vue +2 -2
- package/components/Dialog/UpdateMoreAction.vue +2 -2
- package/components/EntryPassInformation.vue +443 -0
- 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/Input/InputPhoneNumberV2.vue +8 -0
- package/components/ManageChecklistMain.vue +400 -36
- package/components/ScheduleAreaMain.vue +56 -0
- package/components/TableHygiene.vue +47 -430
- package/components/UnitPersonCard.vue +123 -0
- package/components/VehicleAddSelection.vue +2 -2
- package/components/VehicleForm.vue +78 -19
- package/components/VehicleManagement.vue +164 -40
- package/components/VisitorForm.vue +95 -20
- package/components/VisitorFormSelection.vue +13 -2
- package/components/VisitorManagement.vue +83 -55
- package/composables/useAccessManagement.ts +52 -0
- 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/usePeople.ts +4 -3
- package/composables/useVehicle.ts +35 -2
- package/composables/useVisitor.ts +3 -3
- package/composables/useWorkOrder.ts +25 -3
- package/package.json +3 -2
- package/types/building.d.ts +1 -1
- package/types/cleaner-schedule.d.ts +1 -0
- package/types/{checkout-item.d.ts → equipment-item.d.ts} +3 -3
- package/types/{supply.d.ts → equipment.d.ts} +2 -2
- package/types/html2pdf.d.ts +19 -0
- package/types/people.d.ts +5 -2
- package/types/site.d.ts +8 -0
- package/types/vehicle.d.ts +4 -3
- package/types/visitor.d.ts +2 -1
- 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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<div class="w-100 d-flex justify-space-between ga-2">
|
|
13
13
|
<span class="text-subtitle-1 w-100 font-weight-bold mb-3">{{
|
|
14
14
|
formTitle
|
|
15
|
-
|
|
15
|
+
}}</span>
|
|
16
16
|
<span v-if="prop.type === 'contractor'" class="text-subtitle-2 font-weight-bold" style="text-wrap: nowrap">Step
|
|
17
17
|
<span class="text-primary-button">{{ contractorStep }}</span>/3</span>
|
|
18
18
|
</div>
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<v-list-item-title @click.stop="handleAddNewContractorType" class="d-flex align-center ga-1">
|
|
31
31
|
<span><v-icon icon="mdi-plus" /></span> Add "<strong>{{
|
|
32
32
|
contractorTypeInput
|
|
33
|
-
|
|
33
|
+
}}</strong>" as custom contractor type.
|
|
34
34
|
</v-list-item-title>
|
|
35
35
|
</v-list-item>
|
|
36
36
|
</template>
|
|
@@ -54,8 +54,9 @@
|
|
|
54
54
|
<v-col v-if="shouldShowField('nric')" cols="12">
|
|
55
55
|
<v-row>
|
|
56
56
|
<v-col cols="12">
|
|
57
|
-
<InputLabel class="text-capitalize" title="NRIC" required />
|
|
58
|
-
<InputNRICNumber v-model="visitor.nric" density="comfortable"
|
|
57
|
+
<InputLabel class="text-capitalize" title="NRIC" :required="requireNRIC" />
|
|
58
|
+
<InputNRICNumber v-model="visitor.nric" density="comfortable"
|
|
59
|
+
:rules="[requireNRIC ? requiredRule : () => true]" :key="currentAutofillSource + 'nric-key'"
|
|
59
60
|
@update:model-value="handleUpdateNRIC" :loading="fetchPersonByNRICPending" />
|
|
60
61
|
</v-col>
|
|
61
62
|
</v-row>
|
|
@@ -63,8 +64,9 @@
|
|
|
63
64
|
|
|
64
65
|
<v-col v-if="shouldShowField('contact')" cols="12">
|
|
65
66
|
<InputLabel class="text-capitalize" title="Phone Number" required />
|
|
66
|
-
<InputPhoneNumberV2 v-model="visitor.contact" :rules="[requiredRule]" density="comfortable"
|
|
67
|
-
:
|
|
67
|
+
<InputPhoneNumberV2 v-model="visitor.contact" :rules="[requiredRule]" density="comfortable"
|
|
68
|
+
:key="currentAutofillSource + 'phone-key'" :loading="fetchPersonByContactPending"
|
|
69
|
+
@update:model-value="handleUpdateContact" />
|
|
68
70
|
</v-col>
|
|
69
71
|
|
|
70
72
|
<v-col v-if="shouldShowField('deliveryType')" cols="12">
|
|
@@ -132,6 +134,7 @@
|
|
|
132
134
|
:loading="levelsStatus === 'pending'" @update:model-value="handleChangeLevel" :rules="[requiredRule]" />
|
|
133
135
|
</v-col>
|
|
134
136
|
|
|
137
|
+
|
|
135
138
|
<v-col v-if="shouldShowField('unit')" cols="12">
|
|
136
139
|
<InputLabel class="text-capitalize" title="Unit" required />
|
|
137
140
|
<v-select v-model="visitor.unit" :items="unitsArray" density="comfortable" item-title="title"
|
|
@@ -139,6 +142,11 @@
|
|
|
139
142
|
@update:model-value="handleUpdateUnit" />
|
|
140
143
|
</v-col>
|
|
141
144
|
|
|
145
|
+
<v-col v-if="shouldShowField('unit')" cols="12">
|
|
146
|
+
<InputLabel class="text-capitalize" title="Registered Unit Company Name" required />
|
|
147
|
+
<v-text-field v-model.trim="registeredUnitCompanyName" density="comfortable" :loading="buildingUnitDataPending" readonly class="no-pointer" />
|
|
148
|
+
</v-col>
|
|
149
|
+
|
|
142
150
|
<v-col v-if="shouldShowField('remarks')" cols="12">
|
|
143
151
|
<InputLabel class="text-capitalize" title="Remarks" />
|
|
144
152
|
<v-textarea v-model="visitor.remarks" density="comfortable" :rows="3" no-resize />
|
|
@@ -146,6 +154,10 @@
|
|
|
146
154
|
|
|
147
155
|
<v-col v-if="prop.type === 'contractor' && contractorStep === 2" cols="12">
|
|
148
156
|
<PassInformation />
|
|
157
|
+
<EntryPassInformation v-if="entryPassSettings?.data?.settings?.nfcPass" v-model="passType"
|
|
158
|
+
v-model:quantity="passQuantity" v-model:cards="passCards" :settings="entryPassSettings"
|
|
159
|
+
:loading="entryPassSettingsPending" @scan:barcode="$emit('scan:barcode')"
|
|
160
|
+
@scan:camera="$emit('scan:camera')" />
|
|
149
161
|
</v-col>
|
|
150
162
|
|
|
151
163
|
<v-col v-if="prop.type === 'contractor' && contractorStep === 3" cols="12">
|
|
@@ -178,9 +190,9 @@
|
|
|
178
190
|
prop.type === 'contractor' &&
|
|
179
191
|
contractorStep > 1
|
|
180
192
|
" tile block variant="text" class="text-none" size="48" @click="handleGoToPreviousPage" text="Back" />
|
|
181
|
-
<v-btn v-else-if="prop.mode === 'add'" tile block variant="text" class="text-none" size="48"
|
|
193
|
+
<v-btn v-else-if="prop.mode === 'add' || prop.mode === 'register'" tile block variant="text" class="text-none" size="48"
|
|
182
194
|
@click="backToSelection" text="Back to Selection" />
|
|
183
|
-
<v-btn v-else tile block variant="text" class="text-none" size="48" @click="close" text="Close" />
|
|
195
|
+
<v-btn v-else tile block variant="text" class="text-none" size="48" @click="emit('close:all')" text="Close" />
|
|
184
196
|
</v-col>
|
|
185
197
|
<v-col cols="6">
|
|
186
198
|
<v-btn v-if="prop.type === 'contractor'" tile block variant="flat" color="primary-button" class="text-none"
|
|
@@ -191,14 +203,18 @@
|
|
|
191
203
|
</v-row>
|
|
192
204
|
</v-toolbar>
|
|
193
205
|
|
|
194
|
-
<v-dialog v-model="dialog.vehicleNumberUsersList" v-if="vehicleNumberUserItems.length > 0" persistent
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
<v-dialog v-model="dialog.vehicleNumberUsersList" v-if="vehicleNumberUserItems.length > 0" persistent
|
|
207
|
+
max-width="600">
|
|
208
|
+
<SearchVehicleNumberUser :vehicle-number="visitor.plateNumber ?? ''"
|
|
209
|
+
:vehicle-number-users-list="vehicleNumberUserItems" @close="dialog.vehicleNumberUsersList = false"
|
|
210
|
+
@update:people="handleAutofillDataViaVehicleNumber" />
|
|
197
211
|
</v-dialog>
|
|
198
212
|
</v-card>
|
|
199
213
|
</template>
|
|
200
214
|
|
|
201
215
|
<script setup lang="ts">
|
|
216
|
+
import useBuildingUnit from '../composables/useBuildingUnit';
|
|
217
|
+
|
|
202
218
|
|
|
203
219
|
const prop = defineProps({
|
|
204
220
|
type: {
|
|
@@ -214,7 +230,7 @@ const prop = defineProps({
|
|
|
214
230
|
required: true,
|
|
215
231
|
},
|
|
216
232
|
mode: {
|
|
217
|
-
type: String as PropType<"add" | "edit">,
|
|
233
|
+
type: String as PropType<"add" | "edit" | "register">,
|
|
218
234
|
default: "add",
|
|
219
235
|
},
|
|
220
236
|
visitorData: {
|
|
@@ -230,7 +246,9 @@ const currentAutofillSource = ref<AutofillSource>(null);
|
|
|
230
246
|
const { requiredRule, debounce } = useUtils();
|
|
231
247
|
const { getSiteById, getSiteLevels, getSiteUnits } = useSiteSettings();
|
|
232
248
|
const { createVisitor, typeFieldMap, contractorTypes } = useVisitor();
|
|
249
|
+
const { getBySiteId: getEntryPassSettingsBySiteId } = useSiteEntryPassSettings();
|
|
233
250
|
const { findPersonByNRIC, findPersonByContact, searchCompanyList, findUsersByPlateNumber } = usePeople()
|
|
251
|
+
const { getById: getUnitDataById} = useBuildingUnit()
|
|
234
252
|
|
|
235
253
|
const emit = defineEmits([
|
|
236
254
|
"back",
|
|
@@ -259,6 +277,12 @@ const visitor = reactive<Partial<TVisitorPayload>>({
|
|
|
259
277
|
members: [],
|
|
260
278
|
});
|
|
261
279
|
|
|
280
|
+
const passType = ref("");
|
|
281
|
+
const passQuantity = ref<number | null>(null);
|
|
282
|
+
const passCards = ref<string[]>([]);
|
|
283
|
+
|
|
284
|
+
const registeredUnitCompanyName = ref('N/A')
|
|
285
|
+
|
|
262
286
|
const dialog = reactive({
|
|
263
287
|
vehicleNumberUsersList: false,
|
|
264
288
|
});
|
|
@@ -293,6 +317,10 @@ const shouldShowField = (fieldKey: keyof TVisitorPayload) => {
|
|
|
293
317
|
}
|
|
294
318
|
};
|
|
295
319
|
|
|
320
|
+
const requireNRIC = computed(() => {
|
|
321
|
+
return prop.type !== 'walk-in' && prop.type !== 'guest';
|
|
322
|
+
})
|
|
323
|
+
|
|
296
324
|
const companyNames = ref<string[]>([])
|
|
297
325
|
const companyAutofillDataArray = ref<{ companyName: string[], unit: string, block: number, level: string, unitName: string }[]>([])
|
|
298
326
|
|
|
@@ -411,7 +439,7 @@ watch(fetchPersonByContactReq, (obj) => {
|
|
|
411
439
|
visitor.company = companyNames.value?.[0]
|
|
412
440
|
}
|
|
413
441
|
visitor.block = visitor.block ?? obj.block ?? ""
|
|
414
|
-
visitor.level = visitor.level ??
|
|
442
|
+
visitor.level = visitor.level ?? obj.level ?? ""
|
|
415
443
|
visitor.unit = visitor.unit ?? obj.unit ?? ""
|
|
416
444
|
visitor.nric = visitor.nric ?? obj.nric ?? ""
|
|
417
445
|
|
|
@@ -475,15 +503,15 @@ const debounceSearchVehicleUsers = debounce(() => {
|
|
|
475
503
|
return;
|
|
476
504
|
}
|
|
477
505
|
fetchVehicleNumberUserRefresh();
|
|
478
|
-
}, 300);
|
|
506
|
+
}, 300);
|
|
479
507
|
|
|
480
508
|
watch(() => visitor.plateNumber, (newVal) => {
|
|
481
|
-
if(currentAutofillSource.value && currentAutofillSource.value !== "vehicleNumber") return;
|
|
509
|
+
if (currentAutofillSource.value && currentAutofillSource.value !== "vehicleNumber") return;
|
|
482
510
|
debounceSearchVehicleUsers();
|
|
483
511
|
});
|
|
484
512
|
|
|
485
513
|
|
|
486
|
-
function handleAutofillDataViaVehicleNumber(item: TPeople){
|
|
514
|
+
function handleAutofillDataViaVehicleNumber(item: TPeople) {
|
|
487
515
|
|
|
488
516
|
currentAutofillSource.value = "vehicleNumber";
|
|
489
517
|
|
|
@@ -504,6 +532,19 @@ function handleAutofillDataViaVehicleNumber(item: TPeople){
|
|
|
504
532
|
}
|
|
505
533
|
|
|
506
534
|
|
|
535
|
+
const {
|
|
536
|
+
data: entryPassSettings,
|
|
537
|
+
pending: entryPassSettingsPending,
|
|
538
|
+
refresh: refreshEntryPassSettings,
|
|
539
|
+
} = useLazyAsyncData(`fetch-entrypass-settings-${prop.site}`, () => {
|
|
540
|
+
if (contractorStep.value !== 2) return Promise.resolve(null);
|
|
541
|
+
return getEntryPassSettingsBySiteId(prop.site);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
watch(contractorStep, (step) => {
|
|
545
|
+
if (step === 2) refreshEntryPassSettings();
|
|
546
|
+
});
|
|
547
|
+
|
|
507
548
|
const {
|
|
508
549
|
data: siteData,
|
|
509
550
|
refresh: refreshSiteData,
|
|
@@ -601,6 +642,25 @@ watch(
|
|
|
601
642
|
)
|
|
602
643
|
|
|
603
644
|
|
|
645
|
+
const { data: buildingUnitData, refresh: refreshBuildingUnitData, pending: buildingUnitDataPending } = useLazyAsyncData(`building-unit-data-${visitor.unit}`, () => {
|
|
646
|
+
if (!visitor.unit) return Promise.resolve(null);
|
|
647
|
+
return getUnitDataById(visitor.unit);
|
|
648
|
+
})
|
|
649
|
+
|
|
650
|
+
// trigger refresh of building unit data when unit changes, to get the latest unit name in case there are changes in the backend
|
|
651
|
+
watch(() => visitor.unit, () => {
|
|
652
|
+
if (visitor.unit) {
|
|
653
|
+
refreshBuildingUnitData();
|
|
654
|
+
} else {
|
|
655
|
+
registeredUnitCompanyName.value = 'N/A';
|
|
656
|
+
}
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
watch(buildingUnitData, (newVal: any) => {
|
|
660
|
+
registeredUnitCompanyName.value = newVal?.data?.buildingUnit?.companyName || 'N/A';
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
|
|
604
664
|
function handleChangeBlock(value: any) {
|
|
605
665
|
visitor.level = "";
|
|
606
666
|
visitor.unit = "";
|
|
@@ -647,7 +707,7 @@ function close() {
|
|
|
647
707
|
function formatVisitorType(type: TVisitorType): string {
|
|
648
708
|
switch (type) {
|
|
649
709
|
case "guest":
|
|
650
|
-
return "
|
|
710
|
+
return "Drive-In";
|
|
651
711
|
case "contractor":
|
|
652
712
|
return "Contractor";
|
|
653
713
|
case "delivery":
|
|
@@ -696,16 +756,22 @@ async function submit() {
|
|
|
696
756
|
};
|
|
697
757
|
}
|
|
698
758
|
}
|
|
759
|
+
} else if (prop.mode === 'register') {
|
|
760
|
+
payload = {
|
|
761
|
+
...payload,
|
|
762
|
+
status: "registered",
|
|
763
|
+
}
|
|
764
|
+
}
|
|
699
765
|
|
|
700
|
-
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
if (prop.type === "contractor") {
|
|
701
769
|
// contractor type logic payload
|
|
702
770
|
payload = {
|
|
703
771
|
...payload,
|
|
704
772
|
members: visitor.members,
|
|
705
773
|
};
|
|
706
774
|
}
|
|
707
|
-
} else if (prop.mode === "edit") {
|
|
708
|
-
}
|
|
709
775
|
try {
|
|
710
776
|
const res = await createVisitor(payload);
|
|
711
777
|
if (res) {
|
|
@@ -734,10 +800,19 @@ watch(
|
|
|
734
800
|
onMounted(() => {
|
|
735
801
|
contractorStep.value = 1;
|
|
736
802
|
currentAutofillSource.value = null;
|
|
803
|
+
|
|
804
|
+
if(prop.mode === 'register' && prop.visitorData) {
|
|
805
|
+
console.log('Register mode, prefill visitor data', prop.visitorData?.plateNumber )
|
|
806
|
+
visitor.plateNumber = prop.visitorData?.plateNumber || ""
|
|
807
|
+
}
|
|
737
808
|
});
|
|
738
809
|
</script>
|
|
739
810
|
<style scoped>
|
|
740
811
|
.button-outline-class {
|
|
741
812
|
border: 1px solid rgba(var(--v-theme-primary));
|
|
742
813
|
}
|
|
814
|
+
|
|
815
|
+
.no-pointer {
|
|
816
|
+
pointer-events: none;
|
|
817
|
+
}
|
|
743
818
|
</style>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<v-toolbar>
|
|
4
4
|
<v-row no-gutters class="fill-height px-6" align="center">
|
|
5
5
|
<span class="font-weight-bold text-h5 text-capitalize">
|
|
6
|
-
|
|
6
|
+
{{ mode }} Visitor
|
|
7
7
|
</span>
|
|
8
8
|
</v-row>
|
|
9
9
|
</v-toolbar>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-5 my-5">
|
|
12
12
|
<span class="text-subtitle-2 w-100 font-weight-medium d-flex justify-center mb-3">Please Select Visitor Type</span>
|
|
13
13
|
<template v-for="item in selection" :key="item.value">
|
|
14
|
-
<v-btn color="primary-button" block variant="flat" rounded="md" size="48" :text="item.label" @click="select(item.value)" class="my-2 text-capitalize text-subtitle-2" />
|
|
14
|
+
<v-btn v-if="showSelection(item)" color="primary-button" block variant="flat" rounded="md" size="48" :text="item.label" @click="select(item.value)" class="my-2 text-capitalize text-subtitle-2" />
|
|
15
15
|
</template>
|
|
16
16
|
</v-card-text>
|
|
17
17
|
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
|
|
37
37
|
<script setup lang="ts">
|
|
38
38
|
const prop = defineProps({
|
|
39
|
+
mode: {
|
|
40
|
+
type: String as PropType<"add" | "edit" | "register">,
|
|
41
|
+
default: "add",
|
|
42
|
+
}
|
|
39
43
|
});
|
|
40
44
|
|
|
41
45
|
const emit = defineEmits(['cancel', 'select']);
|
|
@@ -50,4 +54,11 @@ function cancel() {
|
|
|
50
54
|
function select(value: string) {
|
|
51
55
|
emit("select", value);
|
|
52
56
|
}
|
|
57
|
+
|
|
58
|
+
function showSelection(item: any) {
|
|
59
|
+
if(item.value === 'walk-in' && prop.mode === 'register') {
|
|
60
|
+
return false ;
|
|
61
|
+
} return true;
|
|
62
|
+
|
|
63
|
+
}
|
|
53
64
|
</script>
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<TableMain :headers="headers" :items="items" :loading="getVisitorPending" :page="page" :pages="pages"
|
|
4
4
|
:extension-height="110" :offset="300" :pageRange="pageRange" :canCreate="canAddVisitor"
|
|
5
5
|
@refresh="getVisitorRefresh" @update:page="handleUpdatePage" createLabel="Add Visitor" show-header
|
|
6
|
-
@row-click="handleRowClick" @create="
|
|
6
|
+
@row-click="handleRowClick" @create="handleAddNew">
|
|
7
7
|
<template #extension>
|
|
8
8
|
<v-row no-gutters class="w-100 d-flex flex-column">
|
|
9
9
|
<v-tabs v-model="activeTab" color="primary" :height="40" @update:model-value="toRoute" class="w-100">
|
|
10
|
-
<v-tab v-for="tab in tabOptions" :value="tab.
|
|
10
|
+
<v-tab v-for="tab in tabOptions" :value="tab.value" :key="tab.value" class="text-capitalize">
|
|
11
11
|
{{ tab.name }}
|
|
12
12
|
</v-tab>
|
|
13
13
|
</v-tabs>
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
</v-checkbox>
|
|
23
23
|
<InputDateTimePicker v-model:utc="dateFrom" density="compact" hide-details />
|
|
24
24
|
<InputDateTimePicker v-model:utc="dateTo" density="compact" hide-details />
|
|
25
|
-
<v-select v-model="filterTypes" label="Filter by types" item-title="label"
|
|
26
|
-
:items="visitorSelection" density="compact" clearable multiple max-width="200"
|
|
25
|
+
<v-select v-if="activeTab !== 'guests'" v-model="filterTypes" label="Filter by types" item-title="label"
|
|
26
|
+
item-value="value" :items="visitorSelection" density="compact" clearable multiple max-width="200"
|
|
27
|
+
hide-details>
|
|
27
28
|
<template v-slot:selection="{ item, index }">
|
|
28
29
|
<div class="d-flex align-center text-caption text-nowrap">
|
|
29
30
|
<v-chip v-if="index === 0" color="error" :text="filterTypeSelectionLabel()" size="x-small"
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
<v-icon icon="mdi-user" size="15" />
|
|
50
51
|
<span v-if="item.type === 'contractor'" class="text-capitalize">{{
|
|
51
52
|
formatCamelCaseToWords(item.contractorType)
|
|
52
|
-
|
|
53
|
+
}}</span>
|
|
53
54
|
<span v-else class="text-capitalize">{{ formatType(item) }}</span>
|
|
54
55
|
</span>
|
|
55
56
|
<span class="d-flex align-center ga-2">
|
|
@@ -87,33 +88,39 @@
|
|
|
87
88
|
<v-icon icon="mdi-clock-time-four-outline" color="green" size="20" />
|
|
88
89
|
<span class="text-capitalize">{{
|
|
89
90
|
UTCToLocalTIme(item.checkIn) || "-"
|
|
90
|
-
|
|
91
|
+
}}</span>
|
|
91
92
|
</span>
|
|
92
93
|
<span class="d-flex align-center ga-2">
|
|
93
|
-
<v-icon icon="mdi-clock-time-eight-outline" color="red" size="20"
|
|
94
|
+
<v-icon icon="mdi-clock-time-eight-outline" color="red" size="20" />
|
|
94
95
|
<template v-if="item.checkOut">
|
|
95
96
|
<span class="text-capitalize">{{
|
|
96
97
|
UTCToLocalTIme(item.checkOut) || "_"
|
|
97
|
-
|
|
98
|
+
}}</span>
|
|
98
99
|
<span v-if="item?.manualCheckout">
|
|
99
100
|
<TooltipInfo text="Manual Checkout" density="compact" size="x-small" />
|
|
100
101
|
</span>
|
|
101
102
|
</template>
|
|
102
|
-
<span v-else>
|
|
103
|
+
<span v-else-if="!item?.checkOut && item?.status === 'registered'">
|
|
103
104
|
<v-btn size="x-small" class="text-capitalize" color="red" text="Checkout"
|
|
104
105
|
:loading="loading.checkingOut && item?._id === selectedVisitorId" @click.stop="handleCheckout(item._id)"
|
|
105
106
|
v-if="canCheckoutVisitor" />
|
|
106
107
|
</span>
|
|
107
108
|
</span>
|
|
108
109
|
</template>
|
|
110
|
+
|
|
111
|
+
<template v-slot:item.action="{ item }">
|
|
112
|
+
<v-btn v-if="activeTab === 'unregistered' && canAddVisitor" size="small" color="primary" text="Register"
|
|
113
|
+
@click.stop="handleRegistrationUnregisteredVisitor(item)" />
|
|
114
|
+
</template>
|
|
115
|
+
|
|
109
116
|
</TableMain>
|
|
110
117
|
|
|
111
|
-
|
|
112
|
-
<VisitorFormSelection @cancel="dialog.showSelection = false" @select="handleSelectVisitorType" />
|
|
118
|
+
<v-dialog v-model="dialog.showSelection" width="450" persistent>
|
|
119
|
+
<VisitorFormSelection :mode="mode" @cancel="dialog.showSelection = false" @select="handleSelectVisitorType" />
|
|
113
120
|
</v-dialog>
|
|
114
121
|
|
|
115
|
-
<v-dialog v-model="dialog.
|
|
116
|
-
<VisitorForm mode="
|
|
122
|
+
<v-dialog v-model="dialog.showForm" v-if="activeVisitorFormType" width="450" persistent>
|
|
123
|
+
<VisitorForm :mode="mode" :org="orgId" :site="siteId" :visitor-data="selectedVisitorDataObject" :type="activeVisitorFormType" @back="handleClickBack"
|
|
117
124
|
@done="handleVisitorFormDone" @done:more="handleVisitorFormCreateMore" @close:all="handleCloseAll" />
|
|
118
125
|
</v-dialog>
|
|
119
126
|
|
|
@@ -135,7 +142,7 @@
|
|
|
135
142
|
</span>
|
|
136
143
|
|
|
137
144
|
<span v-else-if="selectedVisitorObject[key]" class="d-flex ga-3 align-center"><strong>{{ label
|
|
138
|
-
|
|
145
|
+
}}:</strong>
|
|
139
146
|
{{ formatValues(key, selectedVisitorObject[key]) }}
|
|
140
147
|
<TooltipInfo v-if="key === 'checkOut'" text="Manual Checkout" density="compact" size="x-small" />
|
|
141
148
|
</span>
|
|
@@ -183,14 +190,6 @@ const props = defineProps({
|
|
|
183
190
|
},
|
|
184
191
|
});
|
|
185
192
|
|
|
186
|
-
const headers = [
|
|
187
|
-
{ title: "Name", value: "name" },
|
|
188
|
-
{ title: "Type/Company", value: "type-company" },
|
|
189
|
-
{ title: "Location", value: "location" },
|
|
190
|
-
{ title: "Contact/Vehicle No.", value: "contact-vehicleNumber" },
|
|
191
|
-
{ title: "Check In/Out", value: "checkin-out" },
|
|
192
|
-
];
|
|
193
|
-
|
|
194
193
|
const {
|
|
195
194
|
getVisitors,
|
|
196
195
|
visitorSelection,
|
|
@@ -225,6 +224,7 @@ const dateFrom = ref("");
|
|
|
225
224
|
const dateTo = ref("");
|
|
226
225
|
const filterTypes = ref<TVisitorType[]>([]);
|
|
227
226
|
const displayNotCheckedOut = ref<boolean>(false);
|
|
227
|
+
const mode = ref<"add" | "edit" | "register">("add");
|
|
228
228
|
|
|
229
229
|
const message = ref("");
|
|
230
230
|
const messageColor = ref("");
|
|
@@ -238,16 +238,31 @@ const loading = reactive({
|
|
|
238
238
|
|
|
239
239
|
const dialog = reactive({
|
|
240
240
|
showSelection: false,
|
|
241
|
-
|
|
241
|
+
showForm: false,
|
|
242
242
|
viewVisitor: false,
|
|
243
243
|
deleteConfirmation: false,
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
+
const headers = computed(() => [
|
|
247
|
+
{ title: "Name", value: "name" },
|
|
248
|
+
{ title: "Type/Company", value: "type-company" },
|
|
249
|
+
{ title: "Location", value: "location" },
|
|
250
|
+
{ title: "Contact/Vehicle No.", value: "contact-vehicleNumber" },
|
|
251
|
+
{ title: "Check In/Out", value: "checkin-out" },
|
|
252
|
+
...(activeTab.value === 'unregistered' ? [{ title: "Action", value: "action" }] : [])
|
|
253
|
+
])
|
|
254
|
+
|
|
255
|
+
|
|
246
256
|
const tabOptions = [
|
|
247
|
-
{ name: "Registered",
|
|
248
|
-
{ name: "Unregistered",
|
|
257
|
+
{ name: "Registered", value: "registered" },
|
|
258
|
+
{ name: "Unregistered", value: "unregistered" },
|
|
259
|
+
{ name: "Guests", value: "guests" },
|
|
249
260
|
];
|
|
250
261
|
|
|
262
|
+
const selectedVisitorDataObject = computed(() => {
|
|
263
|
+
return items.value.find((x: any) => x?._id === selectedVisitorId.value) || {};
|
|
264
|
+
});
|
|
265
|
+
|
|
251
266
|
const formatType = (item: any) =>
|
|
252
267
|
(item.deliveryType ? item.deliveryType + "-" : "") + item.type;
|
|
253
268
|
|
|
@@ -270,8 +285,9 @@ function filterTypeSelectionLabel() {
|
|
|
270
285
|
return `${length} selected ${length === 1 ? "type" : "types"}` as string;
|
|
271
286
|
}
|
|
272
287
|
|
|
273
|
-
function toRoute(
|
|
274
|
-
|
|
288
|
+
function toRoute(tab: any) {
|
|
289
|
+
|
|
290
|
+
const obj = tabOptions.find((x) => x.value === tab);
|
|
275
291
|
if (!obj) return;
|
|
276
292
|
page.value = 1
|
|
277
293
|
navigateTo({
|
|
@@ -279,9 +295,9 @@ function toRoute(status: any) {
|
|
|
279
295
|
params: {
|
|
280
296
|
org: orgId,
|
|
281
297
|
},
|
|
282
|
-
query: {
|
|
283
|
-
|
|
284
|
-
},
|
|
298
|
+
// query: {
|
|
299
|
+
// tab
|
|
300
|
+
// },
|
|
285
301
|
});
|
|
286
302
|
}
|
|
287
303
|
|
|
@@ -296,28 +312,30 @@ const {
|
|
|
296
312
|
// Optional delay (if needed)
|
|
297
313
|
await new Promise(resolve => setTimeout(resolve, 100))
|
|
298
314
|
|
|
299
|
-
|
|
315
|
+
const params: any = {
|
|
300
316
|
page: page.value,
|
|
301
317
|
site: siteId,
|
|
302
318
|
search: searchInput.value,
|
|
303
319
|
dateTo: dateTo.value,
|
|
304
320
|
dateFrom: dateFrom.value,
|
|
305
321
|
type: filterTypes.value.filter(Boolean).join(","),
|
|
306
|
-
status: activeTab.value as string,
|
|
307
322
|
checkedOut: displayNotCheckedOut.value ? false : undefined
|
|
308
|
-
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (activeTab.value !== "guests") {
|
|
326
|
+
params.status = activeTab.value
|
|
327
|
+
} else if (activeTab.value === "guests") {
|
|
328
|
+
params.type = "guest"
|
|
329
|
+
params.status = undefined
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return await getVisitors(params)
|
|
309
333
|
},
|
|
310
334
|
{
|
|
311
335
|
watch: [
|
|
312
336
|
page,
|
|
313
|
-
activeTab,
|
|
314
|
-
searchInput,
|
|
315
|
-
dateFrom,
|
|
316
|
-
dateTo,
|
|
317
|
-
filterTypes,
|
|
318
|
-
displayNotCheckedOut,
|
|
319
|
-
() => route.query
|
|
320
337
|
],
|
|
338
|
+
immediate: false
|
|
321
339
|
}
|
|
322
340
|
)
|
|
323
341
|
|
|
@@ -355,6 +373,12 @@ function formatValues(key: string, value: any) {
|
|
|
355
373
|
return value;
|
|
356
374
|
}
|
|
357
375
|
|
|
376
|
+
function handleAddNew() {
|
|
377
|
+
dialog.showSelection = true;
|
|
378
|
+
activeVisitorFormType.value = null;
|
|
379
|
+
mode.value = "add";
|
|
380
|
+
}
|
|
381
|
+
|
|
358
382
|
function handleRowClick(data: any) {
|
|
359
383
|
selectedVisitorId.value = data?.item?._id;
|
|
360
384
|
dialog.viewVisitor = true;
|
|
@@ -366,25 +390,25 @@ function handleUpdatePage(newPageNum: number) {
|
|
|
366
390
|
|
|
367
391
|
function handleSelectVisitorType(type: TVisitorType) {
|
|
368
392
|
dialog.showSelection = false;
|
|
369
|
-
dialog.
|
|
393
|
+
dialog.showForm = true;
|
|
370
394
|
activeVisitorFormType.value = type;
|
|
371
395
|
}
|
|
372
396
|
|
|
373
397
|
function handleClickBack() {
|
|
374
398
|
dialog.showSelection = true;
|
|
375
|
-
dialog.
|
|
399
|
+
dialog.showForm = false;
|
|
376
400
|
}
|
|
377
401
|
|
|
378
402
|
function handleVisitorFormDone() {
|
|
379
403
|
getVisitorRefresh();
|
|
380
404
|
dialog.showSelection = false;
|
|
381
|
-
dialog.
|
|
405
|
+
dialog.showForm = false;
|
|
382
406
|
}
|
|
383
407
|
|
|
384
408
|
function handleVisitorFormCreateMore() {
|
|
385
409
|
getVisitorRefresh();
|
|
386
410
|
dialog.showSelection = true;
|
|
387
|
-
dialog.
|
|
411
|
+
dialog.showForm = false;
|
|
388
412
|
}
|
|
389
413
|
|
|
390
414
|
function handleDeleteVisitor() {
|
|
@@ -400,14 +424,17 @@ function showMessage(msg: string, color: string) {
|
|
|
400
424
|
|
|
401
425
|
function handleCloseAll() {
|
|
402
426
|
dialog.showSelection = false;
|
|
403
|
-
dialog.
|
|
427
|
+
dialog.showForm = false;
|
|
404
428
|
}
|
|
405
429
|
|
|
406
|
-
function
|
|
407
|
-
|
|
408
|
-
|
|
430
|
+
function handleRegistrationUnregisteredVisitor(item: Partial<TVisitor>) {
|
|
431
|
+
const id = item?._id || null;
|
|
432
|
+
selectedVisitorId.value = id;
|
|
433
|
+
dialog.showSelection = true;
|
|
434
|
+
mode.value = "register";
|
|
409
435
|
}
|
|
410
436
|
|
|
437
|
+
|
|
411
438
|
async function handleProceedDeleteVisitor() {
|
|
412
439
|
try {
|
|
413
440
|
loading.deletingVisitor = true;
|
|
@@ -462,7 +489,8 @@ async function handleCheckout(userId: string) {
|
|
|
462
489
|
|
|
463
490
|
|
|
464
491
|
const updateRouteQuery = debounce(
|
|
465
|
-
(search: string, from: string, to: string, types: string[], status, checkedOut) => {
|
|
492
|
+
// (search: string, from: string, to: string, types: string[], status, checkedOut) => {
|
|
493
|
+
({ search, from, to, types, tab, checkedOut }: { search: string, from: string, to: string, types: string[], tab: string, checkedOut: boolean }) => {
|
|
466
494
|
router.replace({
|
|
467
495
|
query: {
|
|
468
496
|
...route.query,
|
|
@@ -470,7 +498,7 @@ const updateRouteQuery = debounce(
|
|
|
470
498
|
dateFrom: from || undefined,
|
|
471
499
|
dateTo: to || undefined,
|
|
472
500
|
type: types.filter(Boolean).join(",") || undefined,
|
|
473
|
-
|
|
501
|
+
tab: tab || undefined,
|
|
474
502
|
...(checkedOut === true ? { checkedOut: "true" } : { checkedOut: undefined })
|
|
475
503
|
},
|
|
476
504
|
});
|
|
@@ -480,9 +508,10 @@ const updateRouteQuery = debounce(
|
|
|
480
508
|
|
|
481
509
|
watch(
|
|
482
510
|
[searchInput, dateFrom, dateTo, filterTypes, activeTab, displayNotCheckedOut],
|
|
483
|
-
([search, from, to, types,
|
|
484
|
-
updateRouteQuery(search, from, to, types, status, checkedOut)
|
|
485
|
-
|
|
511
|
+
([search, from, to, types, tab, checkedOut]) => {
|
|
512
|
+
// updateRouteQuery(search, from, to, types, status, checkedOut)
|
|
513
|
+
updateRouteQuery({ search, from, to, types, tab, checkedOut })
|
|
514
|
+
getVisitorRefresh();
|
|
486
515
|
},
|
|
487
516
|
{ deep: true }
|
|
488
517
|
);
|
|
@@ -490,13 +519,12 @@ watch(
|
|
|
490
519
|
|
|
491
520
|
|
|
492
521
|
onMounted(() => {
|
|
493
|
-
activeTab.value = (route.query.
|
|
522
|
+
activeTab.value = (route.query.tab as string) || "unregistered"
|
|
494
523
|
searchInput.value = (route.query.search as string) || "";
|
|
495
524
|
dateFrom.value = (route.query.dateFrom as string) || "";
|
|
496
525
|
dateTo.value = (route.query.dateTo as string) || "";
|
|
497
526
|
filterTypes.value = ((route.query.type as string)?.split(",") || []).filter(Boolean) as TVisitorType[];
|
|
498
527
|
displayNotCheckedOut.value = (route.query.checkedOut as string) == 'true' || false
|
|
499
|
-
|
|
500
528
|
})
|
|
501
529
|
</script>
|
|
502
530
|
|