@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.
Files changed (49) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/AccessCardAddForm.vue +1 -1
  3. package/components/AccessCardAssignToUnitForm.vue +1 -1
  4. package/components/AccessManagement.vue +1 -1
  5. package/components/{AddSupplyForm.vue → AddEqupmentForm.vue} +5 -5
  6. package/components/BuildingManagement/units.vue +2 -2
  7. package/components/BuildingUnitFormAdd.vue +4 -4
  8. package/components/BuildingUnitFormEdit.vue +114 -68
  9. package/components/Carousel.vue +474 -0
  10. package/components/DrawImage.vue +172 -0
  11. package/components/EntryPassInformation.vue +283 -29
  12. package/components/{CheckoutItemMain.vue → EquipmentItemMain.vue} +95 -87
  13. package/components/{SupplyManagement.vue → EquipmentManagement.vue} +3 -3
  14. package/components/Feedback/Form.vue +4 -4
  15. package/components/FeedbackMain.vue +748 -145
  16. package/components/FileInput.vue +289 -0
  17. package/components/Input/DateTimePicker.vue +17 -11
  18. package/components/ManageChecklistMain.vue +379 -41
  19. package/components/StockCard.vue +11 -7
  20. package/components/TableHygiene.vue +42 -452
  21. package/components/UnitPersonCard.vue +74 -14
  22. package/components/VisitorForm.vue +193 -52
  23. package/components/VisitorFormSelection.vue +13 -2
  24. package/components/VisitorManagement.vue +83 -55
  25. package/composables/useAccessManagement.ts +41 -18
  26. package/composables/useCleaningPermission.ts +7 -7
  27. package/composables/useDashboardData.ts +2 -2
  28. package/composables/useEquipment.ts +63 -0
  29. package/composables/{useCheckout.ts → useEquipmentItem.ts} +7 -7
  30. package/composables/{useCheckoutPermission.ts → useEquipmentItemPermission.ts} +13 -13
  31. package/composables/{useSupply.ts → useEquipmentManagement.ts} +1 -1
  32. package/composables/useEquipmentManagementPermission.ts +96 -0
  33. package/composables/{useSupplyPermission.ts → useEquipmentPermission.ts} +9 -9
  34. package/composables/useFeedback.ts +53 -21
  35. package/composables/useLocalAuth.ts +29 -1
  36. package/composables/useUploadFiles.ts +94 -0
  37. package/composables/useUtils.ts +152 -53
  38. package/composables/useVehicle.ts +21 -2
  39. package/composables/useVisitor.ts +9 -7
  40. package/composables/useWorkOrder.ts +25 -3
  41. package/package.json +2 -1
  42. package/types/building.d.ts +1 -1
  43. package/types/{checkout-item.d.ts → equipment-item.d.ts} +3 -3
  44. package/types/{supply.d.ts → equipment.d.ts} +2 -2
  45. package/types/feedback.d.ts +5 -2
  46. package/types/people.d.ts +3 -1
  47. package/types/user.d.ts +1 -0
  48. package/types/vehicle.d.ts +2 -0
  49. 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" :class="{'no-pointer': 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" :class="{'no-pointer': 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" :class="{'no-pointer': 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" :class="{'no-pointer': 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
- <InputFileV2
37
- v-if="person.files?.length"
38
- :files="person.files"
39
- :viewMode="true"
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
- defineProps({
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
- }}</span>
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
- }}</strong>" as custom contractor type.
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" :rules="[requiredRule]"
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" :key="currentAutofillSource + 'phone-key'"
67
- :loading="fetchPersonByContactPending" @update:model-value="handleUpdateContact" />
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 v-if="entryPassSettings?.data?.settings?.nfcPass" v-model="passType" v-model:quantity="passQuantity" v-model:cards="passCards" :settings="entryPassSettings" :loading="entryPassSettingsPending" @scan:barcode="$emit('scan:barcode')" @scan:camera="$emit('scan:camera')" />
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" size="48"
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 max-width="600">
196
- <SearchVehicleNumberUser :vehicle-number="visitor.plateNumber ?? ''" :vehicle-number-users-list="vehicleNumberUserItems"
197
- @close="dialog.vehicleNumberUsersList = false" @update:people="handleAutofillDataViaVehicleNumber" />
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>(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 ?? obj.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: fetchVehicleNumberUserReq,
448
- refresh: fetchVehicleNumberUserRefresh,
449
- pending: fetchVehicleNumberUserPending,
521
+ data: fetchVisitorListByVehicleNumberReq,
522
+ refresh: fetchVisitorListByVehicleNumberRefresh,
523
+ pending: fetchVisitorListByVehicleNumberPending,
450
524
 
451
- } = useLazyAsyncData(`fetch-vehicle-number-user-list`, () => {
525
+ } = useLazyAsyncData(`fetch-visitor-list-by-vehicle-number`, () => {
452
526
  if (!visitor.plateNumber) return Promise.resolve(null)
453
- return findUsersByPlateNumber(visitor.plateNumber)
527
+ return getVisitors({ page: 1, limit: 20, site: prop.site, plateNumber: visitor.plateNumber, checkedOut: false, status: "registered" })
454
528
  })
455
529
 
456
- watch(fetchVehicleNumberUserReq, (arr) => {
457
- const arrayData = arr?.data
458
- if (Array.isArray(arrayData)) {
459
- vehicleNumberUserItems.value = arrayData;
460
- if (arrayData.length > 0) {
461
- dialog.vehicleNumberUsersList = true
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 "Guest";
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
- if (prop.type === "contractor") {
720
- // contractor type logic payload
721
- payload = {
722
- ...payload,
723
- members: visitor.members,
724
- };
844
+ } else if (prop.mode === 'register') {
845
+ payload = {
846
+ ...payload,
847
+ status: "registered",
725
848
  }
726
- } else if (prop.mode === "edit") {
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
- Add Visitor
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>