@7365admin1/layer-common 1.10.7 → 1.10.9
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/AccessCardAddForm.vue +1 -1
- package/components/AccessCardAssignToUnitForm.vue +1 -1
- package/components/AccessManagement.vue +1 -1
- package/components/{AddSupplyForm.vue → AddEqupmentForm.vue} +5 -5
- package/components/BuildingManagement/units.vue +2 -2
- package/components/BuildingUnitFormAdd.vue +4 -4
- package/components/BuildingUnitFormEdit.vue +114 -68
- package/components/Carousel.vue +474 -0
- package/components/DrawImage.vue +172 -0
- package/components/EntryPassInformation.vue +283 -29
- package/components/{CheckoutItemMain.vue → EquipmentItemMain.vue} +95 -87
- package/components/{SupplyManagement.vue → EquipmentManagement.vue} +3 -3
- package/components/Feedback/Form.vue +4 -4
- package/components/FeedbackMain.vue +748 -145
- package/components/FileInput.vue +289 -0
- package/components/Input/DateTimePicker.vue +17 -11
- package/components/ManageChecklistMain.vue +379 -41
- package/components/StockCard.vue +11 -7
- package/components/TableHygiene.vue +42 -452
- package/components/UnitPersonCard.vue +74 -14
- package/components/VisitorForm.vue +193 -52
- package/components/VisitorFormSelection.vue +13 -2
- package/components/VisitorManagement.vue +83 -55
- package/composables/useAccessManagement.ts +41 -18
- package/composables/useCleaningPermission.ts +7 -7
- package/composables/useDashboardData.ts +2 -2
- package/composables/useEquipment.ts +63 -0
- package/composables/{useCheckout.ts → useEquipmentItem.ts} +7 -7
- package/composables/{useCheckoutPermission.ts → useEquipmentItemPermission.ts} +13 -13
- package/composables/{useSupply.ts → useEquipmentManagement.ts} +1 -1
- package/composables/useEquipmentManagementPermission.ts +96 -0
- package/composables/{useSupplyPermission.ts → useEquipmentPermission.ts} +9 -9
- package/composables/useFeedback.ts +53 -21
- package/composables/useLocalAuth.ts +29 -1
- package/composables/useUploadFiles.ts +94 -0
- package/composables/useUtils.ts +152 -53
- package/composables/useVehicle.ts +21 -2
- package/composables/useVisitor.ts +9 -7
- package/composables/useWorkOrder.ts +25 -3
- package/package.json +2 -1
- package/types/building.d.ts +1 -1
- package/types/{checkout-item.d.ts → equipment-item.d.ts} +3 -3
- package/types/{supply.d.ts → equipment.d.ts} +2 -2
- package/types/feedback.d.ts +5 -2
- package/types/people.d.ts +3 -1
- package/types/user.d.ts +1 -0
- package/types/vehicle.d.ts +2 -0
- package/types/visitor.d.ts +2 -1
|
@@ -3,22 +3,26 @@
|
|
|
3
3
|
<v-row>
|
|
4
4
|
<v-col cols="12" md="4" class="mt-2">
|
|
5
5
|
<InputLabel class="font-weight-bold" title="Name" />
|
|
6
|
-
<v-text-field :model-value="person.name" density="compact" :readonly="readOnly"
|
|
6
|
+
<v-text-field :model-value="person.name" density="compact" :readonly="readOnly"
|
|
7
|
+
:class="{ 'no-pointer': readOnly }" />
|
|
7
8
|
</v-col>
|
|
8
9
|
|
|
9
10
|
<v-col cols="12" md="4" class="mt-2">
|
|
10
11
|
<InputLabel class="font-weight-bold" title="Email Address" />
|
|
11
|
-
<v-text-field :model-value="person.email" density="compact" :readonly="readOnly"
|
|
12
|
+
<v-text-field :model-value="person.email" density="compact" :readonly="readOnly"
|
|
13
|
+
:class="{ 'no-pointer': readOnly }" />
|
|
12
14
|
</v-col>
|
|
13
15
|
|
|
14
16
|
<v-col cols="12" md="4" class="mt-2">
|
|
15
17
|
<InputLabel class="font-weight-bold" title="NRIC" />
|
|
16
|
-
<v-text-field :model-value="person.nric" density="compact" :readonly="readOnly"
|
|
18
|
+
<v-text-field :model-value="person.nric" density="compact" :readonly="readOnly"
|
|
19
|
+
:class="{ 'no-pointer': readOnly }" />
|
|
17
20
|
</v-col>
|
|
18
21
|
|
|
19
22
|
<v-col cols="12" md="4" class="mt-2">
|
|
20
23
|
<InputLabel class="font-weight-bold" title="Mobile Number" />
|
|
21
|
-
<v-text-field :model-value="person.contact" density="compact" :readonly="readOnly"
|
|
24
|
+
<v-text-field :model-value="person.contact" density="compact" :readonly="readOnly"
|
|
25
|
+
:class="{ 'no-pointer': readOnly }" />
|
|
22
26
|
</v-col>
|
|
23
27
|
</v-row>
|
|
24
28
|
|
|
@@ -26,25 +30,50 @@
|
|
|
26
30
|
<v-col cols="12" md="4" class="mt-2">
|
|
27
31
|
<InputLabel class="font-weight-bold" title="Other Documents and Files" :viewMode="readOnly" />
|
|
28
32
|
|
|
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
|
+
<p v-if="!person.files?.length" class="text-blue-darken-2 font-weight-thin text-caption ml-2 mt-5">
|
|
33
34
|
**No documents to display**
|
|
34
35
|
</p>
|
|
36
|
+
<v-list v-else dense nav class="mt-2">
|
|
37
|
+
<v-list-item v-for="file in person.files" :key="file.id" class="text-blue-darken-2"
|
|
38
|
+
:href="getFileUrl(file.id)" target="_blank">
|
|
39
|
+
<v-list-item-content>
|
|
40
|
+
<v-list-item-title class="d-flex align-center">
|
|
41
|
+
<span>
|
|
42
|
+
<v-icon size="18" class="mr-1">mdi-file-document-outline</v-icon>
|
|
43
|
+
</span>
|
|
44
|
+
<span> {{ file.name }}</span>
|
|
45
|
+
</v-list-item-title>
|
|
46
|
+
</v-list-item-content>
|
|
47
|
+
</v-list-item>
|
|
48
|
+
</v-list>
|
|
35
49
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
50
|
+
</v-col>
|
|
51
|
+
|
|
52
|
+
<v-col cols="0" md="4"></v-col>
|
|
53
|
+
<v-col v-if="prop.showOwnerOption" cols="12" md="4">
|
|
54
|
+
<v-checkbox :model-value="person.isOwner" readonly hide-details density="compact" @click="handleClick">
|
|
55
|
+
<template #label>
|
|
56
|
+
<span class="font-weight-bold text-subtitle-2 ml-2">Is Owner?</span>
|
|
57
|
+
</template>
|
|
58
|
+
</v-checkbox>
|
|
59
|
+
|
|
60
|
+
<span class="d-flex text-caption text-gray-5 ml-7">As owner, you will be receiving emails and updates
|
|
61
|
+
related to this unit.</span>
|
|
41
62
|
</v-col>
|
|
42
63
|
</v-row>
|
|
64
|
+
|
|
65
|
+
<v-dialog v-model="dialog.showPrompt" persistent width="540">
|
|
66
|
+
<DialogReusablePrompt :loading="loadingOwnerProcessing"
|
|
67
|
+
:prompt-title="`Are you sure want to ${person.isOwner ? 'remove' : 'add'} (${person.name}) as owner ?`"
|
|
68
|
+
@proceed="handleApprove(person)" @close="dialog.showPrompt = false" />
|
|
69
|
+
</v-dialog>
|
|
43
70
|
</v-card-text>
|
|
44
71
|
</template>
|
|
45
72
|
|
|
46
73
|
<script setup lang="ts">
|
|
47
|
-
|
|
74
|
+
import useFile from '../composables/useFile';
|
|
75
|
+
|
|
76
|
+
const prop = defineProps({
|
|
48
77
|
person: {
|
|
49
78
|
type: Object as PropType<TPeople>,
|
|
50
79
|
required: true,
|
|
@@ -53,7 +82,38 @@ defineProps({
|
|
|
53
82
|
type: Boolean,
|
|
54
83
|
default: false,
|
|
55
84
|
},
|
|
85
|
+
showOwnerOption: {
|
|
86
|
+
type: Boolean,
|
|
87
|
+
default: false,
|
|
88
|
+
},
|
|
89
|
+
loadingOwnerProcessing: {
|
|
90
|
+
type: Boolean,
|
|
91
|
+
default: false,
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const { getFileUrl } = useFile()
|
|
96
|
+
|
|
97
|
+
const emit = defineEmits(['update:owner'])
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
const dialog = reactive({
|
|
101
|
+
showPrompt: false,
|
|
56
102
|
})
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
const handleApprove = (person: TPeople) => {
|
|
109
|
+
dialog.showPrompt = false;
|
|
110
|
+
emit('update:owner', { person, isOwner: !person.isOwner })
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const handleClick = () => {
|
|
114
|
+
dialog.showPrompt = true;
|
|
115
|
+
}
|
|
116
|
+
|
|
57
117
|
</script>
|
|
58
118
|
|
|
59
119
|
<style lang="scss" scoped>
|
|
@@ -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,12 @@
|
|
|
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"
|
|
148
|
+
:loading="buildingUnitDataPending" readonly class="no-pointer" />
|
|
149
|
+
</v-col>
|
|
150
|
+
|
|
142
151
|
<v-col v-if="shouldShowField('remarks')" cols="12">
|
|
143
152
|
<InputLabel class="text-capitalize" title="Remarks" />
|
|
144
153
|
<v-textarea v-model="visitor.remarks" density="comfortable" :rows="3" no-resize />
|
|
@@ -146,7 +155,16 @@
|
|
|
146
155
|
|
|
147
156
|
<v-col v-if="prop.type === 'contractor' && contractorStep === 2" cols="12">
|
|
148
157
|
<PassInformation />
|
|
149
|
-
<EntryPassInformation
|
|
158
|
+
<EntryPassInformation
|
|
159
|
+
v-if="entryPassSettings?.data?.settings?.nfcPass"
|
|
160
|
+
v-model="passType"
|
|
161
|
+
v-model:quantity="passQuantity"
|
|
162
|
+
v-model:cards="passCards"
|
|
163
|
+
:settings="entryPassSettings"
|
|
164
|
+
:loading="entryPassSettingsPending"
|
|
165
|
+
:site-id="prop.site"
|
|
166
|
+
:unit-id="visitor.unit || null"
|
|
167
|
+
/>
|
|
150
168
|
</v-col>
|
|
151
169
|
|
|
152
170
|
<v-col v-if="prop.type === 'contractor' && contractorStep === 3" cols="12">
|
|
@@ -179,9 +197,9 @@
|
|
|
179
197
|
prop.type === 'contractor' &&
|
|
180
198
|
contractorStep > 1
|
|
181
199
|
" tile block variant="text" class="text-none" size="48" @click="handleGoToPreviousPage" text="Back" />
|
|
182
|
-
<v-btn v-else-if="prop.mode === 'add'" tile block variant="text" class="text-none"
|
|
183
|
-
@click="backToSelection" text="Back to Selection" />
|
|
184
|
-
<v-btn v-else tile block variant="text" class="text-none" size="48" @click="close" text="Close" />
|
|
200
|
+
<v-btn v-else-if="prop.mode === 'add' || prop.mode === 'register'" tile block variant="text" class="text-none"
|
|
201
|
+
size="48" @click="backToSelection" text="Back to Selection" />
|
|
202
|
+
<v-btn v-else tile block variant="text" class="text-none" size="48" @click="emit('close:all')" text="Close" />
|
|
185
203
|
</v-col>
|
|
186
204
|
<v-col cols="6">
|
|
187
205
|
<v-btn v-if="prop.type === 'contractor'" tile block variant="flat" color="primary-button" class="text-none"
|
|
@@ -192,14 +210,56 @@
|
|
|
192
210
|
</v-row>
|
|
193
211
|
</v-toolbar>
|
|
194
212
|
|
|
195
|
-
<v-dialog v-model="dialog.vehicleNumberUsersList" v-if="vehicleNumberUserItems.length > 0" persistent
|
|
196
|
-
|
|
197
|
-
|
|
213
|
+
<v-dialog v-model="dialog.vehicleNumberUsersList" v-if="vehicleNumberUserItems.length > 0" persistent
|
|
214
|
+
max-width="600">
|
|
215
|
+
<SearchVehicleNumberUser :vehicle-number="visitor.plateNumber ?? ''"
|
|
216
|
+
:vehicle-number-users-list="vehicleNumberUserItems" @close="dialog.vehicleNumberUsersList = false"
|
|
217
|
+
@update:people="handleAutofillDataViaVehicleNumber" />
|
|
218
|
+
</v-dialog>
|
|
219
|
+
|
|
220
|
+
<v-dialog v-model="dialog.showNonCheckedOutDialog" max-width="700" persistent>
|
|
221
|
+
<v-card :loading="loading.checkingOut">
|
|
222
|
+
<v-toolbar>
|
|
223
|
+
<v-toolbar-title>
|
|
224
|
+
<v-row no-gutters class="d-flex align-center justify-space-between">
|
|
225
|
+
<span class="font-weight-bold">
|
|
226
|
+
You have an unchecked-out vehicle for: {{ visitor.plateNumber }}
|
|
227
|
+
</span>
|
|
228
|
+
</v-row>
|
|
229
|
+
</v-toolbar-title>
|
|
230
|
+
</v-toolbar>
|
|
231
|
+
|
|
232
|
+
<v-card-text>
|
|
233
|
+
|
|
234
|
+
<v-list lines="three">
|
|
235
|
+
<v-list-item v-if="matchingPlateNumberNonCheckedOutArr.length > 0"
|
|
236
|
+
v-for="v in matchingPlateNumberNonCheckedOutArr" :key="v._id" class="cursor-pointer">
|
|
237
|
+
<v-list-item-title>
|
|
238
|
+
{{ v.plateNumber }} - {{ v.name }}
|
|
239
|
+
</v-list-item-title>
|
|
240
|
+
|
|
241
|
+
<v-list-item-subtitle>
|
|
242
|
+
Checked In at : {{ UTCToLocalTIme(v.checkIn) }}
|
|
243
|
+
</v-list-item-subtitle>
|
|
244
|
+
|
|
245
|
+
<template #append>
|
|
246
|
+
<v-btn size="x-small" class="text-capitalize" color="red" text="Checkout"
|
|
247
|
+
:loading="loading.checkingOut && v?._id === prop.visitorData?._id"
|
|
248
|
+
@click.stop="handleCheckout(v._id)" />
|
|
249
|
+
</template>
|
|
250
|
+
|
|
251
|
+
</v-list-item>
|
|
252
|
+
</v-list>
|
|
253
|
+
|
|
254
|
+
</v-card-text>
|
|
255
|
+
</v-card>
|
|
198
256
|
</v-dialog>
|
|
199
257
|
</v-card>
|
|
200
258
|
</template>
|
|
201
259
|
|
|
202
260
|
<script setup lang="ts">
|
|
261
|
+
import useBuildingUnit from '../composables/useBuildingUnit';
|
|
262
|
+
|
|
203
263
|
|
|
204
264
|
const prop = defineProps({
|
|
205
265
|
type: {
|
|
@@ -215,7 +275,7 @@ const prop = defineProps({
|
|
|
215
275
|
required: true,
|
|
216
276
|
},
|
|
217
277
|
mode: {
|
|
218
|
-
type: String as PropType<"add" | "edit">,
|
|
278
|
+
type: String as PropType<"add" | "edit" | "register">,
|
|
219
279
|
default: "add",
|
|
220
280
|
},
|
|
221
281
|
visitorData: {
|
|
@@ -228,11 +288,12 @@ type AutofillSource = "nric" | "contact" | "vehicleNumber" | null;
|
|
|
228
288
|
const currentAutofillSource = ref<AutofillSource>(null);
|
|
229
289
|
|
|
230
290
|
|
|
231
|
-
const { requiredRule, debounce } = useUtils();
|
|
291
|
+
const { requiredRule, debounce, UTCToLocalTIme } = useUtils();
|
|
232
292
|
const { getSiteById, getSiteLevels, getSiteUnits } = useSiteSettings();
|
|
233
|
-
const { createVisitor, typeFieldMap, contractorTypes } = useVisitor();
|
|
293
|
+
const { createVisitor, typeFieldMap, contractorTypes, getVisitors, updateVisitor } = useVisitor();
|
|
234
294
|
const { getBySiteId: getEntryPassSettingsBySiteId } = useSiteEntryPassSettings();
|
|
235
295
|
const { findPersonByNRIC, findPersonByContact, searchCompanyList, findUsersByPlateNumber } = usePeople()
|
|
296
|
+
const { getById: getUnitDataById } = useBuildingUnit()
|
|
236
297
|
|
|
237
298
|
const emit = defineEmits([
|
|
238
299
|
"back",
|
|
@@ -262,13 +323,20 @@ const visitor = reactive<Partial<TVisitorPayload>>({
|
|
|
262
323
|
});
|
|
263
324
|
|
|
264
325
|
const passType = ref("");
|
|
265
|
-
const passQuantity = ref<number | null>(
|
|
326
|
+
const passQuantity = ref<number | null>(1);
|
|
266
327
|
const passCards = ref<string[]>([]);
|
|
267
328
|
|
|
329
|
+
const registeredUnitCompanyName = ref('N/A')
|
|
330
|
+
|
|
268
331
|
const dialog = reactive({
|
|
269
332
|
vehicleNumberUsersList: false,
|
|
333
|
+
showNonCheckedOutDialog: false,
|
|
270
334
|
});
|
|
271
335
|
|
|
336
|
+
const loading = reactive({
|
|
337
|
+
checkingOut: false,
|
|
338
|
+
})
|
|
339
|
+
|
|
272
340
|
const validForm = ref(false);
|
|
273
341
|
const formRef = ref<HTMLFormElement | null>(null);
|
|
274
342
|
const processing = ref(false);
|
|
@@ -288,6 +356,8 @@ const blocksArray = ref<TDefaultOptionObj[]>([]);
|
|
|
288
356
|
const levelsArray = ref<TDefaultOptionObj[]>([]);
|
|
289
357
|
const unitsArray = ref<TDefaultOptionObj[]>([]);
|
|
290
358
|
|
|
359
|
+
const matchingPlateNumberNonCheckedOutArr = ref<TVisitor[]>([])
|
|
360
|
+
|
|
291
361
|
|
|
292
362
|
const vehicleNumberUserItems = ref<TPeople[]>([])
|
|
293
363
|
|
|
@@ -299,6 +369,10 @@ const shouldShowField = (fieldKey: keyof TVisitorPayload) => {
|
|
|
299
369
|
}
|
|
300
370
|
};
|
|
301
371
|
|
|
372
|
+
const requireNRIC = computed(() => {
|
|
373
|
+
return prop.type !== 'walk-in' && prop.type !== 'guest';
|
|
374
|
+
})
|
|
375
|
+
|
|
302
376
|
const companyNames = ref<string[]>([])
|
|
303
377
|
const companyAutofillDataArray = ref<{ companyName: string[], unit: string, block: number, level: string, unitName: string }[]>([])
|
|
304
378
|
|
|
@@ -417,7 +491,7 @@ watch(fetchPersonByContactReq, (obj) => {
|
|
|
417
491
|
visitor.company = companyNames.value?.[0]
|
|
418
492
|
}
|
|
419
493
|
visitor.block = visitor.block ?? obj.block ?? ""
|
|
420
|
-
visitor.level = visitor.level ??
|
|
494
|
+
visitor.level = visitor.level ?? obj.level ?? ""
|
|
421
495
|
visitor.unit = visitor.unit ?? obj.unit ?? ""
|
|
422
496
|
visitor.nric = visitor.nric ?? obj.nric ?? ""
|
|
423
497
|
|
|
@@ -444,25 +518,70 @@ watch(fetchCompanyListReq, (arr) => {
|
|
|
444
518
|
}
|
|
445
519
|
})
|
|
446
520
|
const {
|
|
447
|
-
data:
|
|
448
|
-
refresh:
|
|
449
|
-
pending:
|
|
521
|
+
data: fetchVisitorListByVehicleNumberReq,
|
|
522
|
+
refresh: fetchVisitorListByVehicleNumberRefresh,
|
|
523
|
+
pending: fetchVisitorListByVehicleNumberPending,
|
|
450
524
|
|
|
451
|
-
} = useLazyAsyncData(`fetch-vehicle-number
|
|
525
|
+
} = useLazyAsyncData(`fetch-visitor-list-by-vehicle-number`, () => {
|
|
452
526
|
if (!visitor.plateNumber) return Promise.resolve(null)
|
|
453
|
-
return
|
|
527
|
+
return getVisitors({ page: 1, limit: 20, site: prop.site, plateNumber: visitor.plateNumber, checkedOut: false, status: "registered" })
|
|
454
528
|
})
|
|
455
529
|
|
|
456
|
-
watch(
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
530
|
+
watch(fetchVisitorListByVehicleNumberReq, (arr: any) => {
|
|
531
|
+
const itemsArray = arr?.items || []
|
|
532
|
+
const isValidArray = Array.isArray(itemsArray)
|
|
533
|
+
matchingPlateNumberNonCheckedOutArr.value = isValidArray ? itemsArray : []
|
|
534
|
+
if(matchingPlateNumberNonCheckedOutArr.value.length > 0) {
|
|
535
|
+
dialog.showNonCheckedOutDialog = true;
|
|
536
|
+
} else {
|
|
537
|
+
dialog.showNonCheckedOutDialog = false;
|
|
463
538
|
}
|
|
464
539
|
})
|
|
465
540
|
|
|
541
|
+
|
|
542
|
+
const debounceSearchVisitorsByPlateNumbers = debounce(() => {
|
|
543
|
+
if (!visitor.plateNumber) {
|
|
544
|
+
matchingPlateNumberNonCheckedOutArr.value = []
|
|
545
|
+
dialog.showNonCheckedOutDialog = false;
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
fetchVisitorListByVehicleNumberRefresh();
|
|
549
|
+
}, 300);
|
|
550
|
+
|
|
551
|
+
watch(() => visitor.plateNumber, (newVal) => {
|
|
552
|
+
debounceSearchVisitorsByPlateNumbers();
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
async function handleCheckout(visitorId: string) {
|
|
558
|
+
if (!visitorId) {
|
|
559
|
+
errorMessage.value = "Invalid visitor ID. Cannot proceed with checkout.";
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
try {
|
|
564
|
+
loading.checkingOut = true;
|
|
565
|
+
const res = await updateVisitor(visitorId as string, {
|
|
566
|
+
checkOut: new Date().toISOString(),
|
|
567
|
+
});
|
|
568
|
+
if (res) {
|
|
569
|
+
await fetchVisitorListByVehicleNumberRefresh();
|
|
570
|
+
|
|
571
|
+
}
|
|
572
|
+
} catch (error: any) {
|
|
573
|
+
const errorMessage = error?.response?._data?.message;
|
|
574
|
+
console.log("[ERROR]", error);
|
|
575
|
+
errorMessage.value =
|
|
576
|
+
errorMessage || "An error occurred while checking out the vehicle.";
|
|
577
|
+
} finally {
|
|
578
|
+
loading.checkingOut = false;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
|
|
466
585
|
const debounceFetchCompany = debounce(async () => fetchCompanyListRefresh(), 200)
|
|
467
586
|
|
|
468
587
|
watch(companyNameInput, async (val) => {
|
|
@@ -474,22 +593,10 @@ watch(companyNameInput, async (val) => {
|
|
|
474
593
|
})
|
|
475
594
|
|
|
476
595
|
|
|
477
|
-
const debounceSearchVehicleUsers = debounce(() => {
|
|
478
|
-
if (!visitor.plateNumber) {
|
|
479
|
-
vehicleNumberUserItems.value = [];
|
|
480
|
-
dialog.vehicleNumberUsersList = false;
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
fetchVehicleNumberUserRefresh();
|
|
484
|
-
}, 300);
|
|
485
596
|
|
|
486
|
-
watch(() => visitor.plateNumber, (newVal) => {
|
|
487
|
-
if(currentAutofillSource.value && currentAutofillSource.value !== "vehicleNumber") return;
|
|
488
|
-
debounceSearchVehicleUsers();
|
|
489
|
-
});
|
|
490
597
|
|
|
491
598
|
|
|
492
|
-
function handleAutofillDataViaVehicleNumber(item: TPeople){
|
|
599
|
+
function handleAutofillDataViaVehicleNumber(item: TPeople) {
|
|
493
600
|
|
|
494
601
|
currentAutofillSource.value = "vehicleNumber";
|
|
495
602
|
|
|
@@ -620,6 +727,25 @@ watch(
|
|
|
620
727
|
)
|
|
621
728
|
|
|
622
729
|
|
|
730
|
+
const { data: buildingUnitData, refresh: refreshBuildingUnitData, pending: buildingUnitDataPending } = useLazyAsyncData(`building-unit-data-${visitor.unit}`, () => {
|
|
731
|
+
if (!visitor.unit) return Promise.resolve(null);
|
|
732
|
+
return getUnitDataById(visitor.unit);
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
// trigger refresh of building unit data when unit changes, to get the latest unit name in case there are changes in the backend
|
|
736
|
+
watch(() => visitor.unit, () => {
|
|
737
|
+
if (visitor.unit) {
|
|
738
|
+
refreshBuildingUnitData();
|
|
739
|
+
} else {
|
|
740
|
+
registeredUnitCompanyName.value = 'N/A';
|
|
741
|
+
}
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
watch(buildingUnitData, (newVal: any) => {
|
|
745
|
+
registeredUnitCompanyName.value = newVal?.data?.buildingUnit?.companyName || 'N/A';
|
|
746
|
+
})
|
|
747
|
+
|
|
748
|
+
|
|
623
749
|
function handleChangeBlock(value: any) {
|
|
624
750
|
visitor.level = "";
|
|
625
751
|
visitor.unit = "";
|
|
@@ -666,7 +792,7 @@ function close() {
|
|
|
666
792
|
function formatVisitorType(type: TVisitorType): string {
|
|
667
793
|
switch (type) {
|
|
668
794
|
case "guest":
|
|
669
|
-
return "
|
|
795
|
+
return "Drive-In";
|
|
670
796
|
case "contractor":
|
|
671
797
|
return "Contractor";
|
|
672
798
|
case "delivery":
|
|
@@ -715,15 +841,21 @@ async function submit() {
|
|
|
715
841
|
};
|
|
716
842
|
}
|
|
717
843
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
...payload,
|
|
723
|
-
members: visitor.members,
|
|
724
|
-
};
|
|
844
|
+
} else if (prop.mode === 'register') {
|
|
845
|
+
payload = {
|
|
846
|
+
...payload,
|
|
847
|
+
status: "registered",
|
|
725
848
|
}
|
|
726
|
-
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
if (prop.type === "contractor") {
|
|
854
|
+
// contractor type logic payload
|
|
855
|
+
payload = {
|
|
856
|
+
...payload,
|
|
857
|
+
members: visitor.members,
|
|
858
|
+
};
|
|
727
859
|
}
|
|
728
860
|
try {
|
|
729
861
|
const res = await createVisitor(payload);
|
|
@@ -753,10 +885,19 @@ watch(
|
|
|
753
885
|
onMounted(() => {
|
|
754
886
|
contractorStep.value = 1;
|
|
755
887
|
currentAutofillSource.value = null;
|
|
888
|
+
|
|
889
|
+
if (prop.mode === 'register' && prop.visitorData) {
|
|
890
|
+
console.log('Register mode, prefill visitor data', prop.visitorData?.plateNumber)
|
|
891
|
+
visitor.plateNumber = prop.visitorData?.plateNumber || ""
|
|
892
|
+
}
|
|
756
893
|
});
|
|
757
894
|
</script>
|
|
758
895
|
<style scoped>
|
|
759
896
|
.button-outline-class {
|
|
760
897
|
border: 1px solid rgba(var(--v-theme-primary));
|
|
761
898
|
}
|
|
899
|
+
|
|
900
|
+
.no-pointer {
|
|
901
|
+
pointer-events: none;
|
|
902
|
+
}
|
|
762
903
|
</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>
|