@iservice365/layer-common 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/components/CameraForm.vue +264 -0
- package/components/CameraMain.vue +352 -0
- package/components/Card/DeleteConfirmation.vue +51 -0
- package/components/Card/MemberInfoSummary.vue +44 -0
- package/components/Chat/Information.vue +28 -11
- package/components/Dialog/DeleteConfirmation.vue +51 -0
- package/components/Dialog/UpdateMoreAction.vue +99 -0
- package/components/Feedback/Form.vue +17 -3
- package/components/FeedbackDetail.vue +0 -11
- package/components/FeedbackMain.vue +21 -11
- package/components/Input/DateTimePicker.vue +10 -5
- package/components/Input/File.vue +1 -1
- package/components/Input/FileV2.vue +106 -63
- package/components/Input/InputPhoneNumberV2.vue +114 -0
- package/components/Input/NRICNumber.vue +41 -0
- package/components/Input/PhoneNumber.vue +1 -0
- package/components/Input/VehicleNumber.vue +49 -0
- package/components/NumberSettingField.vue +107 -0
- package/components/PeopleForm.vue +452 -0
- package/components/TableMain.vue +2 -1
- package/components/VehicleUpdateMoreAction.vue +84 -0
- package/components/VisitorForm.vue +712 -0
- package/components/VisitorFormSelection.vue +53 -0
- package/components/VisitorManagement.vue +569 -0
- package/components/WorkOrder/Create.vue +87 -49
- package/components/WorkOrder/Main.vue +17 -12
- package/composables/useBuilding.ts +250 -0
- package/composables/useBuildingUnit.ts +116 -0
- package/composables/useFeedback.ts +3 -3
- package/composables/useFile.ts +7 -9
- package/composables/useLocal.ts +67 -0
- package/composables/usePeople.ts +48 -0
- package/composables/useSecurityUtils.ts +18 -0
- package/composables/useSiteSettings.ts +111 -0
- package/composables/useUtils.ts +30 -1
- package/composables/useVisitor.ts +80 -0
- package/composables/useWorkOrder.ts +3 -3
- package/package.json +1 -1
- package/plugins/vuetify.ts +6 -1
- package/types/building.d.ts +19 -0
- package/types/camera.d.ts +31 -0
- package/types/people.d.ts +22 -0
- package/types/select.d.ts +4 -0
- package/types/site.d.ts +10 -7
- package/types/visitor.d.ts +42 -0
- package/utils/phoneMasks.ts +1703 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card width="100%" :loading="processing">
|
|
3
|
+
<v-toolbar>
|
|
4
|
+
<v-row no-gutters class="fill-height px-6" align="center">
|
|
5
|
+
<span class="font-weight-bold text-h5 text-capitalize">
|
|
6
|
+
{{ prop.mode }} {{ formatVisitorType(type) }}
|
|
7
|
+
</span>
|
|
8
|
+
</v-row>
|
|
9
|
+
</v-toolbar>
|
|
10
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-5 my-3">
|
|
11
|
+
<div class="w-100 d-flex justify-space-between ga-2">
|
|
12
|
+
<span class="text-subtitle-1 w-100 font-weight-bold mb-3">{{
|
|
13
|
+
formTitle
|
|
14
|
+
}}</span>
|
|
15
|
+
<span
|
|
16
|
+
v-if="prop.type === 'contractor'"
|
|
17
|
+
class="text-subtitle-2 font-weight-bold"
|
|
18
|
+
style="text-wrap: nowrap"
|
|
19
|
+
>Step <span class="text-primary-button">{{ contractorStep }}</span
|
|
20
|
+
>/3</span
|
|
21
|
+
>
|
|
22
|
+
</div>
|
|
23
|
+
<v-form
|
|
24
|
+
ref="formRef"
|
|
25
|
+
v-model="validForm"
|
|
26
|
+
:disabled="processing"
|
|
27
|
+
@click="errorMessage = ''"
|
|
28
|
+
>
|
|
29
|
+
<v-row no-gutters class="pt-4">
|
|
30
|
+
<v-col v-if="shouldShowField('contractorType')" cols="12">
|
|
31
|
+
<InputLabel
|
|
32
|
+
class="text-capitalize"
|
|
33
|
+
title="Contractor Type"
|
|
34
|
+
required
|
|
35
|
+
/>
|
|
36
|
+
<v-combobox
|
|
37
|
+
v-model="contractorTypeObj"
|
|
38
|
+
v-model:search="contractorTypeInput"
|
|
39
|
+
:hide-no-data="false"
|
|
40
|
+
@update:focused="handleFocusedContractorType"
|
|
41
|
+
:items="contractorTypes"
|
|
42
|
+
:rules="[requiredRule]"
|
|
43
|
+
item-value="value"
|
|
44
|
+
@update:model-value="handleSelectContractorType"
|
|
45
|
+
variant="outlined"
|
|
46
|
+
density="comfortable"
|
|
47
|
+
persistent-hint
|
|
48
|
+
small-chips
|
|
49
|
+
>
|
|
50
|
+
<template v-slot:no-data>
|
|
51
|
+
<v-list-item>
|
|
52
|
+
<v-list-item-title>
|
|
53
|
+
No results matching "<strong>{{
|
|
54
|
+
contractorTypeInput
|
|
55
|
+
}}</strong
|
|
56
|
+
>". This value will be added as new option.
|
|
57
|
+
</v-list-item-title>
|
|
58
|
+
</v-list-item>
|
|
59
|
+
</template>
|
|
60
|
+
</v-combobox>
|
|
61
|
+
</v-col>
|
|
62
|
+
|
|
63
|
+
<v-col v-if="shouldShowField('attachments')" class="w-100">
|
|
64
|
+
<InputLabel class="text-capitalize" title="Attach Image" />
|
|
65
|
+
<InputFileV2 v-model="visitor.attachments" multiple :max-length="3" />
|
|
66
|
+
</v-col>
|
|
67
|
+
|
|
68
|
+
<v-col v-if="shouldShowField('name')" cols="12">
|
|
69
|
+
<v-row>
|
|
70
|
+
<v-col cols="12">
|
|
71
|
+
<InputLabel
|
|
72
|
+
class="text-capitalize"
|
|
73
|
+
title="Full Name"
|
|
74
|
+
required
|
|
75
|
+
/>
|
|
76
|
+
<v-text-field
|
|
77
|
+
v-model.trim="visitor.name"
|
|
78
|
+
density="comfortable"
|
|
79
|
+
:rules="[requiredRule]"
|
|
80
|
+
/>
|
|
81
|
+
</v-col>
|
|
82
|
+
</v-row>
|
|
83
|
+
</v-col>
|
|
84
|
+
|
|
85
|
+
<v-col v-if="shouldShowField('deliveryType')" cols="12">
|
|
86
|
+
<v-row>
|
|
87
|
+
<v-col cols="12">
|
|
88
|
+
<v-radio-group
|
|
89
|
+
v-model="visitor.deliveryType"
|
|
90
|
+
inline
|
|
91
|
+
color="primary"
|
|
92
|
+
:rules="[requiredRule]"
|
|
93
|
+
>
|
|
94
|
+
<v-radio label="Food" value="Food"></v-radio>
|
|
95
|
+
<v-radio label="Parcel" value="Parcel"></v-radio>
|
|
96
|
+
</v-radio-group>
|
|
97
|
+
</v-col>
|
|
98
|
+
</v-row>
|
|
99
|
+
</v-col>
|
|
100
|
+
|
|
101
|
+
<template v-if="shouldShowField('company')">
|
|
102
|
+
<v-col v-if="prop.type === 'delivery'" cols="12">
|
|
103
|
+
<InputLabel
|
|
104
|
+
class="text-capitalize"
|
|
105
|
+
title="Company Name"
|
|
106
|
+
required
|
|
107
|
+
/>
|
|
108
|
+
<v-combobox
|
|
109
|
+
v-model="companyNameObj"
|
|
110
|
+
v-model:search="companyNameInput"
|
|
111
|
+
:hide-no-data="false"
|
|
112
|
+
@update:focused="handleFocusedCompanyName"
|
|
113
|
+
:items="companyNames"
|
|
114
|
+
:rules="[requiredRule]"
|
|
115
|
+
item-value="value"
|
|
116
|
+
@update:model-value="handleSelectCompanyName"
|
|
117
|
+
variant="outlined"
|
|
118
|
+
density="comfortable"
|
|
119
|
+
persistent-hint
|
|
120
|
+
small-chips
|
|
121
|
+
>
|
|
122
|
+
<template v-slot:no-data>
|
|
123
|
+
<v-list-item>
|
|
124
|
+
<v-list-item-title>
|
|
125
|
+
No results matching "<strong>{{
|
|
126
|
+
contractorTypeInput
|
|
127
|
+
}}</strong
|
|
128
|
+
>". This value will be added as new option.
|
|
129
|
+
</v-list-item-title>
|
|
130
|
+
</v-list-item>
|
|
131
|
+
</template>
|
|
132
|
+
</v-combobox>
|
|
133
|
+
</v-col>
|
|
134
|
+
|
|
135
|
+
<v-col v-else cols="12">
|
|
136
|
+
<v-row>
|
|
137
|
+
<v-col cols="12">
|
|
138
|
+
<InputLabel
|
|
139
|
+
class="text-capitalize"
|
|
140
|
+
title="Company Name"
|
|
141
|
+
required
|
|
142
|
+
/>
|
|
143
|
+
<v-text-field
|
|
144
|
+
v-model.trim="visitor.company"
|
|
145
|
+
density="comfortable"
|
|
146
|
+
:rules="[requiredRule]"
|
|
147
|
+
/>
|
|
148
|
+
</v-col>
|
|
149
|
+
</v-row>
|
|
150
|
+
</v-col>
|
|
151
|
+
</template>
|
|
152
|
+
|
|
153
|
+
<v-col v-if="shouldShowField('nric')" cols="12">
|
|
154
|
+
<v-row>
|
|
155
|
+
<v-col cols="12">
|
|
156
|
+
<InputLabel
|
|
157
|
+
class="text-capitalize"
|
|
158
|
+
title="NRIC/Passport/ID No."
|
|
159
|
+
required
|
|
160
|
+
/>
|
|
161
|
+
<InputNRICNumber
|
|
162
|
+
v-model.trim="visitor.nric"
|
|
163
|
+
density="comfortable"
|
|
164
|
+
:rules="[requiredRule]"
|
|
165
|
+
/>
|
|
166
|
+
</v-col>
|
|
167
|
+
</v-row>
|
|
168
|
+
</v-col>
|
|
169
|
+
|
|
170
|
+
<v-col v-if="shouldShowField('contact')" cols="12">
|
|
171
|
+
<InputLabel class="text-capitalize" title="Phone Number" required />
|
|
172
|
+
<InputPhoneNumberV2 v-model="visitor.contact" :rules="[requiredRule]" density="comfortable"/>
|
|
173
|
+
</v-col>
|
|
174
|
+
|
|
175
|
+
<v-col v-if="shouldShowField('plateNumber')" cols="12">
|
|
176
|
+
<v-row>
|
|
177
|
+
<v-col cols="12">
|
|
178
|
+
<InputLabel
|
|
179
|
+
class="text-capitalize"
|
|
180
|
+
title="Vehicle Number"
|
|
181
|
+
required
|
|
182
|
+
/>
|
|
183
|
+
<!-- <v-text-field v-model.trim.uppercase="visitor.plateNumber" density="comfortable" /> -->
|
|
184
|
+
<InputVehicleNumber
|
|
185
|
+
v-model.trim="visitor.plateNumber"
|
|
186
|
+
density="comfortable"
|
|
187
|
+
:rules="[requiredRule]"
|
|
188
|
+
/>
|
|
189
|
+
</v-col>
|
|
190
|
+
</v-row>
|
|
191
|
+
</v-col>
|
|
192
|
+
|
|
193
|
+
<v-col v-if="shouldShowField('block')" cols="12">
|
|
194
|
+
<InputLabel class="text-capitalize" title="Block" required />
|
|
195
|
+
<v-select
|
|
196
|
+
v-model="visitor.block"
|
|
197
|
+
:items="blocksArray"
|
|
198
|
+
item-value="value"
|
|
199
|
+
item-title="title"
|
|
200
|
+
:loading="blockStatus === 'pending'"
|
|
201
|
+
@update:model-value="handleChangeBlock"
|
|
202
|
+
density="comfortable"
|
|
203
|
+
:rules="[requiredRule]"
|
|
204
|
+
/>
|
|
205
|
+
</v-col>
|
|
206
|
+
|
|
207
|
+
<v-col v-if="shouldShowField('level')" cols="12">
|
|
208
|
+
<InputLabel class="text-capitalize" title="Level" required />
|
|
209
|
+
<v-select
|
|
210
|
+
v-model="visitor.level"
|
|
211
|
+
:items="levelsArray"
|
|
212
|
+
density="comfortable"
|
|
213
|
+
:disabled="!visitor.block"
|
|
214
|
+
:loading="levelsStatus === 'pending'"
|
|
215
|
+
@update:model-value="handleChangeLevel"
|
|
216
|
+
:rules="[requiredRule]"
|
|
217
|
+
/>
|
|
218
|
+
</v-col>
|
|
219
|
+
|
|
220
|
+
<v-col v-if="shouldShowField('unit')" cols="12">
|
|
221
|
+
<InputLabel class="text-capitalize" title="Unit" required />
|
|
222
|
+
<v-select
|
|
223
|
+
v-model="visitor.unit"
|
|
224
|
+
:items="unitsArray"
|
|
225
|
+
density="comfortable"
|
|
226
|
+
:disabled="!visitor.level"
|
|
227
|
+
:loading="unitsStatus === 'pending'"
|
|
228
|
+
:rules="[requiredRule]"
|
|
229
|
+
@update:model-value="handleUpdateUnit"
|
|
230
|
+
/>
|
|
231
|
+
</v-col>
|
|
232
|
+
|
|
233
|
+
<v-col v-if="shouldShowField('remarks')" cols="12">
|
|
234
|
+
<InputLabel class="text-capitalize" title="Remarks" />
|
|
235
|
+
<v-textarea
|
|
236
|
+
v-model="visitor.remarks"
|
|
237
|
+
density="comfortable"
|
|
238
|
+
:rows="3"
|
|
239
|
+
no-resize
|
|
240
|
+
/>
|
|
241
|
+
</v-col>
|
|
242
|
+
|
|
243
|
+
<v-col
|
|
244
|
+
v-if="prop.type === 'contractor' && contractorStep === 2"
|
|
245
|
+
cols="12"
|
|
246
|
+
>
|
|
247
|
+
<PassInformation />
|
|
248
|
+
</v-col>
|
|
249
|
+
|
|
250
|
+
<v-col
|
|
251
|
+
v-if="prop.type === 'contractor' && contractorStep === 3"
|
|
252
|
+
cols="12"
|
|
253
|
+
>
|
|
254
|
+
<MemberInformation v-model="visitor.members" />
|
|
255
|
+
</v-col>
|
|
256
|
+
|
|
257
|
+
<v-col v-if="prop.mode === 'add'" cols="12" class="mt-2">
|
|
258
|
+
<v-checkbox v-model="createMore" density="comfortable" hide-details>
|
|
259
|
+
<template #label>
|
|
260
|
+
<span class="text-subtitle-2 font-weight-bold">
|
|
261
|
+
Create more
|
|
262
|
+
</span>
|
|
263
|
+
</template>
|
|
264
|
+
</v-checkbox>
|
|
265
|
+
</v-col>
|
|
266
|
+
</v-row>
|
|
267
|
+
</v-form>
|
|
268
|
+
</v-card-text>
|
|
269
|
+
|
|
270
|
+
<v-row no-gutters class="w-100" v-if="errorMessage">
|
|
271
|
+
<p class="text-error w-100 text-center text-subtitle-2">
|
|
272
|
+
{{ errorMessage }}
|
|
273
|
+
</p>
|
|
274
|
+
</v-row>
|
|
275
|
+
<v-toolbar density="compact">
|
|
276
|
+
<v-row no-gutters>
|
|
277
|
+
<v-col cols="6">
|
|
278
|
+
<v-btn
|
|
279
|
+
v-if="
|
|
280
|
+
prop.mode === 'add' &&
|
|
281
|
+
prop.type === 'contractor' &&
|
|
282
|
+
contractorStep > 1
|
|
283
|
+
"
|
|
284
|
+
tile
|
|
285
|
+
block
|
|
286
|
+
variant="text"
|
|
287
|
+
class="text-none"
|
|
288
|
+
size="48"
|
|
289
|
+
@click="handleGoToPreviousPage"
|
|
290
|
+
text="Back"
|
|
291
|
+
/>
|
|
292
|
+
<v-btn
|
|
293
|
+
v-else-if="prop.mode === 'add'"
|
|
294
|
+
tile
|
|
295
|
+
block
|
|
296
|
+
variant="text"
|
|
297
|
+
class="text-none"
|
|
298
|
+
size="48"
|
|
299
|
+
@click="backToSelection"
|
|
300
|
+
text="Back to Selection"
|
|
301
|
+
/>
|
|
302
|
+
<v-btn
|
|
303
|
+
v-else
|
|
304
|
+
tile
|
|
305
|
+
block
|
|
306
|
+
variant="text"
|
|
307
|
+
class="text-none"
|
|
308
|
+
size="48"
|
|
309
|
+
@click="close"
|
|
310
|
+
text="Close"
|
|
311
|
+
/>
|
|
312
|
+
</v-col>
|
|
313
|
+
<v-col cols="6">
|
|
314
|
+
<v-btn
|
|
315
|
+
v-if="prop.type === 'contractor'"
|
|
316
|
+
tile
|
|
317
|
+
block
|
|
318
|
+
variant="flat"
|
|
319
|
+
color="primary-button"
|
|
320
|
+
class="text-none"
|
|
321
|
+
size="48"
|
|
322
|
+
:disabled="processing"
|
|
323
|
+
@click="handleNextPage"
|
|
324
|
+
:text="contractorStep === 3 ? 'Submit' : 'Next'"
|
|
325
|
+
/>
|
|
326
|
+
<v-btn
|
|
327
|
+
v-else
|
|
328
|
+
tile
|
|
329
|
+
block
|
|
330
|
+
variant="flat"
|
|
331
|
+
color="black"
|
|
332
|
+
class="text-none"
|
|
333
|
+
size="48"
|
|
334
|
+
:disabled="!validForm || processing"
|
|
335
|
+
@click="submit"
|
|
336
|
+
:text="prop.mode == 'add' ? 'Submit' : 'Update'"
|
|
337
|
+
/>
|
|
338
|
+
</v-col>
|
|
339
|
+
</v-row>
|
|
340
|
+
</v-toolbar>
|
|
341
|
+
</v-card>
|
|
342
|
+
</template>
|
|
343
|
+
|
|
344
|
+
<script setup lang="ts">
|
|
345
|
+
|
|
346
|
+
const prop = defineProps({
|
|
347
|
+
type: {
|
|
348
|
+
type: String as PropType<TVisitorType>,
|
|
349
|
+
required: true,
|
|
350
|
+
},
|
|
351
|
+
org: {
|
|
352
|
+
type: String,
|
|
353
|
+
required: true,
|
|
354
|
+
},
|
|
355
|
+
site: {
|
|
356
|
+
type: String,
|
|
357
|
+
required: true,
|
|
358
|
+
},
|
|
359
|
+
mode: {
|
|
360
|
+
type: String as PropType<"add" | "edit">,
|
|
361
|
+
default: "add",
|
|
362
|
+
},
|
|
363
|
+
visitorData: {
|
|
364
|
+
type: Object as PropType<Partial<TVisitor> | null>,
|
|
365
|
+
default: null,
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const { requiredRule, formatDateISO8601 } = useUtils();
|
|
370
|
+
const { getSiteById, getSiteLevels, getSiteUnits } = useSiteSettings();
|
|
371
|
+
const { createVisitor, typeFieldMap, contractorTypes } = useVisitor();
|
|
372
|
+
|
|
373
|
+
const emit = defineEmits([
|
|
374
|
+
"back",
|
|
375
|
+
"select",
|
|
376
|
+
"done",
|
|
377
|
+
"done:more",
|
|
378
|
+
"error",
|
|
379
|
+
"close",
|
|
380
|
+
]);
|
|
381
|
+
|
|
382
|
+
const visitor = reactive<Partial<TVisitorPayload>>({
|
|
383
|
+
contractorType: "",
|
|
384
|
+
name: "",
|
|
385
|
+
deliveryType: "",
|
|
386
|
+
company: "",
|
|
387
|
+
nric: "",
|
|
388
|
+
contact: "",
|
|
389
|
+
plateNumber: "",
|
|
390
|
+
block: "",
|
|
391
|
+
level: "",
|
|
392
|
+
unit: "",
|
|
393
|
+
unitName: "",
|
|
394
|
+
remarks: "",
|
|
395
|
+
attachments: [],
|
|
396
|
+
members: [],
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const validForm = ref(false);
|
|
400
|
+
const formRef = ref<HTMLFormElement | null>(null);
|
|
401
|
+
const processing = ref(false);
|
|
402
|
+
const message = ref("");
|
|
403
|
+
const errorMessage = ref("");
|
|
404
|
+
const createMore = ref(false);
|
|
405
|
+
|
|
406
|
+
const contractorTypeInput = ref("");
|
|
407
|
+
const contractorTypeObj = ref<TDefaultOptionObj | null>(null);
|
|
408
|
+
const contractorStep = ref(1);
|
|
409
|
+
|
|
410
|
+
const companyNameInput = ref("");
|
|
411
|
+
const companyNameObj = ref<TDefaultOptionObj | null>(null);
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
const blocksArray = ref<TDefaultOptionObj[]>([]);
|
|
415
|
+
const levelsArray = ref<TDefaultOptionObj[]>([]);
|
|
416
|
+
const unitsArray = ref<TDefaultOptionObj[]>([]);
|
|
417
|
+
|
|
418
|
+
const shouldShowField = (fieldKey: keyof TVisitorPayload) => {
|
|
419
|
+
if (prop.type !== "contractor" || contractorStep.value === 1) {
|
|
420
|
+
const visibleFields = typeFieldMap[prop.type];
|
|
421
|
+
return visibleFields?.includes(fieldKey);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const companyNames = computed(() => {
|
|
426
|
+
const arr: string[] = [
|
|
427
|
+
"Foodpanda",
|
|
428
|
+
"GrabFood",
|
|
429
|
+
"Deliveroo",
|
|
430
|
+
"Lazada",
|
|
431
|
+
"Amazon",
|
|
432
|
+
"RedMart",
|
|
433
|
+
"J&T",
|
|
434
|
+
"NTUC FairPrice",
|
|
435
|
+
"NinjaVan",
|
|
436
|
+
"Lalamove",
|
|
437
|
+
"SingPost",
|
|
438
|
+
"EasyParcel",
|
|
439
|
+
"DHL",
|
|
440
|
+
"FedEx",
|
|
441
|
+
"Oddle",
|
|
442
|
+
"UParcel",
|
|
443
|
+
"UPS",
|
|
444
|
+
"Park N Parcel",
|
|
445
|
+
"ParkXL",
|
|
446
|
+
"Airpak Express",
|
|
447
|
+
"Pandago",
|
|
448
|
+
"Qdelivery",
|
|
449
|
+
"iXpress Logistics",
|
|
450
|
+
"Blue Dart",
|
|
451
|
+
"GogoX",
|
|
452
|
+
"Others (Type in remarks)",
|
|
453
|
+
];
|
|
454
|
+
return arr.map((x) => ({
|
|
455
|
+
title: x,
|
|
456
|
+
value: x,
|
|
457
|
+
}));
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const formTitle = computed(() => {
|
|
461
|
+
const isContractorForm = prop.type === "contractor";
|
|
462
|
+
const step = contractorStep.value;
|
|
463
|
+
if (isContractorForm && step === 2) {
|
|
464
|
+
return "Pass & Keys Information";
|
|
465
|
+
} else if (isContractorForm && step === 3) {
|
|
466
|
+
return "Members Information";
|
|
467
|
+
} else return "General Information";
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
function handleSelectContractorType(obj: { title: string; value: string }) {
|
|
471
|
+
visitor.contractorType = obj.value ?? "";
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function handleFocusedContractorType() {
|
|
475
|
+
const obj = contractorTypeObj.value;
|
|
476
|
+
const matched = contractorTypes.some(
|
|
477
|
+
(x) => x.value === obj?.value && x.title === obj?.title
|
|
478
|
+
);
|
|
479
|
+
if (!matched) {
|
|
480
|
+
contractorTypeObj.value = null;
|
|
481
|
+
contractorTypeInput.value = "";
|
|
482
|
+
visitor.contractorType = "";
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function handleSelectCompanyName(obj: { title: string; value: string }) {
|
|
487
|
+
visitor.company = obj.value ?? "";
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function handleFocusedCompanyName() {
|
|
491
|
+
const obj = companyNameObj.value;
|
|
492
|
+
const matched = companyNames.value.some(
|
|
493
|
+
(x) => x.value === obj?.value && x.title === obj?.title
|
|
494
|
+
);
|
|
495
|
+
if (!matched) {
|
|
496
|
+
companyNameObj.value = null;
|
|
497
|
+
companyNameInput.value = "";
|
|
498
|
+
visitor.company = "";
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const {
|
|
503
|
+
data: siteData,
|
|
504
|
+
refresh: refreshSiteData,
|
|
505
|
+
status: blockStatus,
|
|
506
|
+
} = useLazyAsyncData(`fetch-site-data-${prop.site}`, () =>
|
|
507
|
+
getSiteById(prop.site)
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
const {
|
|
511
|
+
data: levelsData,
|
|
512
|
+
refresh: refreshLevelsData,
|
|
513
|
+
status: levelsStatus,
|
|
514
|
+
} = useLazyAsyncData(
|
|
515
|
+
`fetch-levels-data-${prop.site}-${visitor.block}`,
|
|
516
|
+
async () => {
|
|
517
|
+
if (!visitor.block) return Promise.resolve(null);
|
|
518
|
+
return await getSiteLevels(prop.site, { block: Number(visitor.block) });
|
|
519
|
+
}
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
const {
|
|
523
|
+
data: unitsData,
|
|
524
|
+
refresh: refreshUnitsData,
|
|
525
|
+
status: unitsStatus,
|
|
526
|
+
} = useLazyAsyncData(
|
|
527
|
+
`fetch-units-data-${prop.site}-${visitor.level}`,
|
|
528
|
+
async () => {
|
|
529
|
+
if (!visitor.level) return Promise.resolve(null);
|
|
530
|
+
return await getSiteUnits(prop.site, Number(visitor.block), visitor.level);
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
watch(
|
|
535
|
+
siteData,
|
|
536
|
+
(newVal) => {
|
|
537
|
+
const siteDataValue = newVal as any;
|
|
538
|
+
if (siteDataValue) {
|
|
539
|
+
const numberOfBlocks = siteDataValue.metadata?.block || 0;
|
|
540
|
+
for (let i = 1; i <= numberOfBlocks; i++) {
|
|
541
|
+
blocksArray.value.push({
|
|
542
|
+
title: `Block ${i}`,
|
|
543
|
+
value: i,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
} else {
|
|
547
|
+
blocksArray.value = [];
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
{ immediate: true }
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
watch(
|
|
554
|
+
levelsData,
|
|
555
|
+
(newVal: any) => {
|
|
556
|
+
if (newVal) {
|
|
557
|
+
const arr = newVal.levels || [];
|
|
558
|
+
levelsArray.value = arr?.map((level: any) => ({
|
|
559
|
+
title: level,
|
|
560
|
+
value: level,
|
|
561
|
+
}));
|
|
562
|
+
} else {
|
|
563
|
+
levelsArray.value = [];
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
{ immediate: true }
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
watch(
|
|
570
|
+
unitsData,
|
|
571
|
+
(newVal: any) => {
|
|
572
|
+
if (newVal && Array.isArray(newVal)) {
|
|
573
|
+
const arr = newVal || [];
|
|
574
|
+
unitsArray.value = arr?.map((unit: any) => ({
|
|
575
|
+
title: unit?.name,
|
|
576
|
+
value: unit?._id,
|
|
577
|
+
}));
|
|
578
|
+
} else {
|
|
579
|
+
unitsArray.value = [];
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
{ immediate: true }
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
function handleChangeBlock(value: any) {
|
|
587
|
+
visitor.level = "";
|
|
588
|
+
visitor.unit = "";
|
|
589
|
+
refreshLevelsData();
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function handleChangeLevel(value: any) {
|
|
593
|
+
visitor.unit = "";
|
|
594
|
+
refreshUnitsData();
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function handleUpdateUnit(value: any) {
|
|
598
|
+
const selectedUnit = unitsArray.value?.find((x: any) => x.value === value);
|
|
599
|
+
visitor.unitName = selectedUnit?.title || "";
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function backToSelection() {
|
|
603
|
+
emit("back");
|
|
604
|
+
message.value = "";
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function handleGoToPreviousPage() {
|
|
608
|
+
if (contractorStep.value > 1) {
|
|
609
|
+
contractorStep.value--;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function close() {
|
|
614
|
+
emit("close");
|
|
615
|
+
message.value = "";
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function formatVisitorType(type: TVisitorType): string {
|
|
619
|
+
switch (type) {
|
|
620
|
+
case "contractor":
|
|
621
|
+
return "Contractor";
|
|
622
|
+
case "delivery":
|
|
623
|
+
return "Delivery";
|
|
624
|
+
case "walk-in":
|
|
625
|
+
return "Walk-In";
|
|
626
|
+
case "pick-up":
|
|
627
|
+
return "Pick-Up";
|
|
628
|
+
case "drop-off":
|
|
629
|
+
return "Drop-Off";
|
|
630
|
+
default:
|
|
631
|
+
return "";
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
async function handleNextPage() {
|
|
636
|
+
errorMessage.value = "";
|
|
637
|
+
formRef.value!.validate();
|
|
638
|
+
if (!validForm.value) {
|
|
639
|
+
errorMessage.value = "Please complete all required fields *";
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
const step = contractorStep.value;
|
|
643
|
+
if (step < 3) contractorStep.value++;
|
|
644
|
+
else await submit();
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async function submit() {
|
|
648
|
+
formRef.value!.validate();
|
|
649
|
+
errorMessage.value = "";
|
|
650
|
+
processing.value = true;
|
|
651
|
+
|
|
652
|
+
let payload: Partial<TVisitorPayload> = {
|
|
653
|
+
type: prop.type,
|
|
654
|
+
org: prop.org,
|
|
655
|
+
site: prop.site,
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
if (prop.mode === "add") {
|
|
659
|
+
const allowedFields = typeFieldMap[prop.type];
|
|
660
|
+
for (const key of allowedFields as (keyof TVisitorPayload)[]) {
|
|
661
|
+
if (visitor[key] !== undefined) {
|
|
662
|
+
payload = {
|
|
663
|
+
...payload,
|
|
664
|
+
[key]: visitor[key],
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (prop.type === "contractor") {
|
|
670
|
+
// contractor type logic payload
|
|
671
|
+
payload = {
|
|
672
|
+
...payload,
|
|
673
|
+
members: visitor.members,
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
} else if (prop.mode === "edit") {
|
|
677
|
+
}
|
|
678
|
+
try {
|
|
679
|
+
const res = await createVisitor(payload);
|
|
680
|
+
if (res) {
|
|
681
|
+
if (createMore.value) {
|
|
682
|
+
emit("done:more");
|
|
683
|
+
} else emit("done");
|
|
684
|
+
}
|
|
685
|
+
} catch (error: any) {
|
|
686
|
+
const err = error?.data?.message;
|
|
687
|
+
errorMessage.value =
|
|
688
|
+
err ||
|
|
689
|
+
`Failed to ${
|
|
690
|
+
prop.mode === "add" ? "add" : "update"
|
|
691
|
+
} visitor. Please try again.`;
|
|
692
|
+
} finally {
|
|
693
|
+
processing.value = false;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
watch(
|
|
698
|
+
() => visitor.plateNumber,
|
|
699
|
+
(newVal) => {
|
|
700
|
+
visitor.plateNumber = newVal?.toUpperCase();
|
|
701
|
+
}
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
onMounted(() => {
|
|
705
|
+
contractorStep.value = 1;
|
|
706
|
+
});
|
|
707
|
+
</script>
|
|
708
|
+
<style scoped>
|
|
709
|
+
.button-outline-class {
|
|
710
|
+
border: 1px solid rgba(var(--v-theme-primary));
|
|
711
|
+
}
|
|
712
|
+
</style>
|