@7365admin1/layer-common 1.11.17 → 1.11.19

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.
@@ -9,8 +9,8 @@
9
9
  <v-row no-gutters class="px-5 py-1 d-flex align-center justify-end ga-3">
10
10
  <v-text-field v-model="searchInput" density="compact" placeholder="Search" clearable max-width="300"
11
11
  append-inner-icon="mdi-magnify" hide-details />
12
- <v-select v-model="vehicleTypeFilter" density="compact" item-title="label" item-value="value"
13
- placeholder="Filter by Type" clearable max-width="200" hide-details :items="typeOptions" />
12
+ <!-- <v-select v-model="vehicleTypeFilter" density="compact" item-title="label" item-value="value"
13
+ placeholder="Filter by Type" clearable max-width="200" hide-details :items="typeOptions" /> -->
14
14
  </v-row>
15
15
  </template>
16
16
 
@@ -37,8 +37,8 @@
37
37
  </v-dialog>
38
38
 
39
39
  <v-dialog v-model="dialog.createVehicle" v-if="vehicleType" width="450" persistent>
40
- <VehicleForm :type="vehicleType" mode="add" :vehicle-data="selectedVehicleObject" @back="handleBackToSelection" @done="handleAddVehicleComplete"
41
- :org="org" :site="props.site" @close:all="handleCloseAll" />
40
+ <VehicleForm :type="vehicleType" mode="add" :vehicle-data="selectedVehicleObject" @back="handleBackToSelection"
41
+ @done="handleAddVehicleComplete" :org="org" :site="props.site" @close:all="handleCloseAll" />
42
42
  </v-dialog>
43
43
 
44
44
  <!-- <v-dialog v-model="dialog.updateVehicle" v-if="vehicleType" width="450" persistent>
@@ -65,10 +65,23 @@
65
65
  {{ formatVehicleStatus(value).label }}
66
66
  </v-chip>
67
67
  </template>
68
- <template #item.action="{ item }">
69
- <v-btn v-if="canDeleteVehicle && (item as TPlateNumber)?.status == 'active'" text="Delete" color="error" flat size="x-small" @click="handleDeleteVehicleAction(item as TPlateNumber)"/>
70
- <v-btn v-if="props.app === 'property_management_agency' && (item as TPlateNumber)?.status == 'pending'" text="Approve" color="success" flat size="x-small" @click="handleApproveVehicle(item as TPlateNumber)"/>
71
- <v-btn v-if=" (item as TPlateNumber)?.status == 'deleted'" text="Restore" color="orange" flat size="x-small" @click="handleRestoreVehicle(item as TPlateNumber)"/>
68
+ <template #item.action="{ item: plateNumberItem, value }">
69
+ <v-btn v-if="canDeleteVehicle && (plateNumberItem as TPlateNumber)?.status == 'active'"
70
+ text="Delete" color="error" flat size="x-small"
71
+ @click="handleDeleteVehicleAction(plateNumberItem as TPlateNumber)" />
72
+ <v-btn
73
+ v-if="props.app === 'property_management_agency' && (plateNumberItem as TPlateNumber)?.status == 'pending'"
74
+ text="Approve" color="success" flat size="x-small"
75
+ @click="handleApproveVehicle(plateNumberItem as TPlateNumber)" />
76
+ <v-btn v-if="(plateNumberItem as TPlateNumber)?.status == 'deleted'" text="Restore"
77
+ color="orange" flat size="x-small"
78
+ @click="handleRestoreVehicle(plateNumberItem as TPlateNumber)" />
79
+ <v-btn
80
+ v-if="((plateNumberItem as TPlateNumber)?.type === 'whitelist' || (plateNumberItem as TPlateNumber)?.type === 'blocklist') && plateNumberItem?.status == 'active'"
81
+ variant="outlined"
82
+ :text="(plateNumberItem as TPlateNumber)?.type === 'blocklist' ? 'Whitelist' : 'Blocklist'"
83
+ :color="(plateNumberItem as TPlateNumber)?.type === 'blocklist' ? 'primary' : 'red'" flat
84
+ size="x-small" class="ml-1" @click="handleUpdateType(plateNumberItem as TPlateNumber)" />
72
85
  </template>
73
86
  </v-data-table>
74
87
  <v-btn text="Add Vehicle Number" class="mt-6 text-capitalize" prepend-icon="mdi-plus"
@@ -81,7 +94,7 @@
81
94
  <v-col v-else-if="selectedVehicleObject[key]" cols="12">
82
95
  <span class="d-flex ga-3 align-center"><strong>{{ label }}:</strong> {{ formatValues(key,
83
96
  selectedVehicleObject[key])
84
- }}</span>
97
+ }}</span>
85
98
  </v-col>
86
99
  </template>
87
100
  </v-row>
@@ -89,16 +102,24 @@
89
102
  </DialogUpdateMoreAction>
90
103
  </v-dialog>
91
104
  <v-dialog v-model="dialog.deleteVehicle" persistent width="540">
92
- <DialogDeleteConfirmation :loading="deletingVehicle" :prompt-title="`Are you sure want to delete this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
105
+ <DialogDeleteConfirmation :loading="deletingVehicle"
106
+ :prompt-title="`Are you sure want to delete this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
93
107
  @delete="submitDelete" @close="closeDeleteDialog" />
94
108
  </v-dialog>
95
109
  <v-dialog v-model="dialog.approveVehicle" persistent width="540">
96
- <DialogReusablePrompt :loading="approvingVehicle" :prompt-title="`Are you sure want to approve this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
97
- @approve="submitApprove" @close="dialog.approveVehicle = false" />
110
+ <DialogReusablePrompt :loading="approvingVehicle"
111
+ :prompt-title="`Are you sure want to approve this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
112
+ @proceed="submitApprove" @close="dialog.approveVehicle = false" />
98
113
  </v-dialog>
99
114
  <v-dialog v-model="dialog.restoreVehicle" persistent width="540">
100
- <DialogReusablePrompt :loading="restoringVehicle" :prompt-title="`Are you sure want to restore this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
101
- @proceed="submitRestore" @close="dialog.restoreVehicle = false" />
115
+ <DialogReusablePrompt :loading="restoringVehicle"
116
+ :prompt-title="`Are you sure want to restore this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
117
+ @proceed="submitRestore" @close="dialog.restoreVehicle = false" />
118
+ </v-dialog>
119
+ <v-dialog v-model="dialog.updateVehicleType" persistent width="540">
120
+ <DialogReusablePrompt :loading="updatingType"
121
+ :prompt-title="`Are you sure want to update the type of this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
122
+ @proceed="submitUpdateType" @close="dialog.updateVehicleType = false" />
102
123
  </v-dialog>
103
124
  <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
104
125
  </v-row>
@@ -147,7 +168,7 @@ const plateHeaders = [
147
168
 
148
169
 
149
170
  const { formatCamelCaseToWords, formatDate, debounce } = useUtils();
150
- const { getVehicles, deleteVehicle, formatVehicleStatus, approveVehicle } = useVehicle();
171
+ const { getVehicles, deleteVehicle, formatVehicleStatus, approveVehicle, updateVehicle } = useVehicle();
151
172
 
152
173
  const items = ref<Array<Record<string, any>>>([]);
153
174
  const page = ref(1);
@@ -160,6 +181,7 @@ const loading = ref(false);
160
181
  const deletingVehicle = ref(false);
161
182
  const approvingVehicle = ref(false);
162
183
  const restoringVehicle = ref(false);
184
+ const updatingType = ref(false);
163
185
 
164
186
  const selectedVehicleId = ref<string | null>(null)
165
187
  const vehicleType = ref<TVehicleType | null>(null);
@@ -185,7 +207,8 @@ const dialog = reactive({
185
207
  deleteVehicle: false,
186
208
  showMoreActions: false,
187
209
  approveVehicle: false,
188
- restoreVehicle: false
210
+ restoreVehicle: false,
211
+ updateVehicleType: false
189
212
  });
190
213
 
191
214
 
@@ -280,19 +303,24 @@ function handleEditVehicleAction() {
280
303
  }
281
304
 
282
305
  function handleDeleteVehicleAction(item: TPlateNumber) {
283
- selectedPlateNumberObject.value = item || null;
306
+ selectedPlateNumberObject.value = item || null;
284
307
  dialog.deleteVehicle = true;
285
308
  }
286
309
 
287
310
  function handleApproveVehicle(item: TPlateNumber) {
288
- selectedPlateNumberObject.value = item || null;
311
+ selectedPlateNumberObject.value = item || null;
289
312
  dialog.approveVehicle = true;
290
313
  }
291
314
  function handleRestoreVehicle(item: TPlateNumber) {
292
- selectedPlateNumberObject.value = item || null;
315
+ selectedPlateNumberObject.value = item || null;
293
316
  dialog.restoreVehicle = true;
294
317
  }
295
318
 
319
+ function handleUpdateType(item: TPlateNumber) {
320
+ selectedPlateNumberObject.value = item || null;
321
+ dialog.updateVehicleType = true;
322
+ }
323
+
296
324
  function handleSelectVehicleStatus(value: TVehicleType) {
297
325
  vehicleType.value = value;
298
326
  dialog.showSelection = false;
@@ -341,6 +369,7 @@ async function submitDelete() {
341
369
 
342
370
  const plateNumberId = selectedPlateNumberObject.value?._id;
343
371
  const type = selectedPlateNumberObject.value?.type as TVehicleType;
372
+ const recNo = selectedPlateNumberObject.value?.recNo;
344
373
  if (!plateNumberId) {
345
374
  showMessage("Invalid plate number selected for deletion.", "error");
346
375
  return;
@@ -348,12 +377,11 @@ async function submitDelete() {
348
377
 
349
378
  try {
350
379
  deletingVehicle.value = true;
351
- const res = await deleteVehicle({ site: props.site, id: plateNumberId as string, recno: selectedVehicleObject.value.recno, type }
380
+ const res = await deleteVehicle({ site: props.site, recno: recNo, id: plateNumberId as string, type }
352
381
  );
353
382
  dialog.deleteVehicle = false;
354
- selectedVehicleId.value = null
355
383
  showMessage(res.message, "success");
356
- getVehiclesRefresh();
384
+ await getVehiclesRefresh();
357
385
  } catch (error: any) {
358
386
  console.error("Error deleting vehicle:", error);
359
387
  message.value = error.response._data.message;
@@ -366,24 +394,21 @@ async function submitDelete() {
366
394
 
367
395
  async function submitRestore() {
368
396
 
369
- console.log("Submitting restore for plate number ID:", selectedPlateNumberObject.value);
370
- const plateNumberId = selectedPlateNumberObject.value?._id;
371
-
397
+ const vehicleId = selectedPlateNumberObject.value?._id;
372
398
 
373
- if (!plateNumberId) {
374
- showMessage("Invalid plate number selected for restoration.", "error");
399
+ if (!vehicleId) {
400
+ showMessage("Invalid vehicle Id selected for restoration.", "error");
375
401
  return;
376
402
  }
377
403
 
378
404
  try {
379
- restoringVehicle.value = true;
405
+ restoringVehicle.value = true;
380
406
  // reactivate and restore will use the same endpoint, just with different payload
381
- const res = await approveVehicle({ site: props.site, org: props.org, id: plateNumberId as string }
407
+ const res = await approveVehicle({ site: props.site, org: props.org, id: vehicleId as string }
382
408
  );
383
409
  dialog.restoreVehicle = false;
384
- selectedVehicleId.value = null
385
410
  showMessage(res.message, "success");
386
- getVehiclesRefresh();
411
+ await getVehiclesRefresh();
387
412
  } catch (error: any) {
388
413
  console.error("Error restoring vehicle:", error);
389
414
  const errMessage = error?.response?._data?.message || "Failed to restore vehicle";
@@ -395,6 +420,55 @@ async function submitRestore() {
395
420
  }
396
421
 
397
422
 
423
+ async function submitUpdateType() {
424
+
425
+
426
+ const vehicleId = selectedPlateNumberObject.value?._id;
427
+ const type = selectedPlateNumberObject.value?.type as TVehicleType;
428
+ const recNo = selectedPlateNumberObject.value?.recNo;
429
+
430
+
431
+ if (!type) {
432
+ showMessage("Invalid vehicle type.", "error");
433
+ return;
434
+ }
435
+
436
+ // if(!recNo){
437
+ // showMessage("Invalid vehicle record number.", "error");
438
+ // return;
439
+ // }
440
+
441
+
442
+ const payload = {
443
+ site: props.site,
444
+ plateNumber: selectedPlateNumberObject.value?.plateNumber as string,
445
+ type: type === "whitelist" ? "blocklist" : "whitelist" as TVehicleType,
446
+ // recno: recNo
447
+ }
448
+
449
+
450
+ if (!vehicleId) {
451
+ showMessage("Invalid vehicle Id selected for updating type.", "error");
452
+ return;
453
+ }
454
+
455
+ try {
456
+ updatingType.value = true;
457
+ const res = await updateVehicle(vehicleId as string, payload);
458
+ dialog.updateVehicleType = false;
459
+ showMessage(res.message, "success");
460
+ await getVehiclesRefresh();
461
+ } catch (error: any) {
462
+ console.error("Error updating vehicle type:", error);
463
+ const errMessage = error?.response?._data?.message || "Failed to update vehicle type";
464
+ showMessage(errMessage, "error");
465
+ // message.value = error.response._data.message;
466
+ } finally {
467
+ updatingType.value = false;
468
+ }
469
+ }
470
+
471
+
398
472
  async function submitApprove() {
399
473
 
400
474
  const plateNumberId = selectedPlateNumberObject.value?._id;
@@ -404,14 +478,13 @@ async function submitApprove() {
404
478
  }
405
479
 
406
480
  try {
407
- approvingVehicle.value = true;
481
+ approvingVehicle.value = true;
408
482
  const res = await approveVehicle({ site: props.site, org: props.org, id: plateNumberId as string }
409
483
 
410
484
  );
411
485
  dialog.approveVehicle = false;
412
- selectedVehicleId.value = null
413
486
  showMessage(res.message, "success");
414
- getVehiclesRefresh();
487
+ await getVehiclesRefresh();
415
488
  } catch (error: any) {
416
489
  console.error("Error approving vehicle:", error);
417
490
  const errMessage = error?.response?._data?.message || "Failed to approve vehicle";
@@ -12,9 +12,9 @@
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>
16
- <span v-if="prop.type === 'contractor'" class="text-subtitle-2 font-weight-bold" style="text-wrap: nowrap">Step
17
- <span class="text-primary-button">{{ contractorStep }}</span>/3</span>
15
+ }}</span>
16
+ <span v-if="withStep2 || withStep3" class="text-subtitle-2 font-weight-bold" style="text-wrap: nowrap">Step
17
+ <span class="text-primary-button">{{ contractorStep }}</span>/{{ withStep3 ? 3 : withStep2 ? 2 : 1 }}</span>
18
18
  </div>
19
19
  <v-form ref="formRef" v-model="validForm" :disabled="processing" @click="errorMessage = ''">
20
20
  <v-row no-gutters class="pt-4">
@@ -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>
@@ -115,8 +115,7 @@
115
115
  <InputLabel class="text-capitalize" title="Delivery Company" />
116
116
  <v-combobox v-model="deliveryCompany" v-model:search="deliveryCompanyInput" ref="companyCombo"
117
117
  autocomplete="off" :hide-no-data="false" :items="deliveryCompanyList" item-value="value"
118
- :loading="siteDataPending" variant="outlined"
119
- density="comfortable" persistent-hint small-chips>
118
+ :loading="siteDataPending" variant="outlined" density="comfortable" persistent-hint small-chips>
120
119
  <template v-slot:no-data>
121
120
  <v-list-item>
122
121
  <v-list-item-title v-if="fetchCompanyListPending">
@@ -183,22 +182,17 @@
183
182
  <v-textarea v-model="visitor.remarks" density="comfortable" :rows="3" no-resize />
184
183
  </v-col>
185
184
 
186
- <v-col v-if="prop.type === 'contractor' && contractorStep === 2" cols="12">
187
- <PassInformation :site="prop.site" :type="prop.type" :contractor-type="visitor.contractorType" />
188
- <EntryPassInformation
189
- v-if="entryPassSettings?.data?.settings?.nfcPass"
190
- v-model="passType"
191
- v-model:quantity="passQuantity"
192
- v-model:cards="passCards"
193
- :settings="entryPassSettings"
194
- :loading="entryPassSettingsPending"
195
- :site-id="prop.site"
196
- :unit-id="visitor.unit || null"
197
- />
185
+ <v-col v-if="(withStep2 || withStep3) && contractorStep === 2" cols="12">
186
+ <PassInformation v-model:pass="visitor.visitorPass" v-model:keys="visitor.passKeys" :site="prop.site"
187
+ :type="prop.type" :contractor-type="visitor.contractorType" :hide-keys="!showShowKeysField" />
188
+ <EntryPassInformation v-if="entryPassSettings?.data?.settings?.nfcPass" v-model="passType"
189
+ v-model:quantity="passQuantity" v-model:cards="passCards" :settings="entryPassSettings"
190
+ :loading="entryPassSettingsPending" :site-id="prop.site" :unit-id="visitor.unit || null" />
198
191
  </v-col>
199
192
 
200
- <v-col v-if="prop.type === 'contractor' && contractorStep === 3" cols="12">
201
- <MemberInformation v-model="visitor.members" />
193
+ <v-col v-if="withStep3 && contractorStep === 3" cols="12">
194
+ <MemberInformation v-model="visitor.members" :type="prop.type" :contractor-type="visitor.contractorType"
195
+ :site="prop.site" :selected-visitor-pass="visitor.visitorPass" />
202
196
  </v-col>
203
197
 
204
198
  <v-col v-if="prop.mode === 'add'" cols="12" class="mt-2">
@@ -224,7 +218,7 @@
224
218
  <v-col cols="6">
225
219
  <v-btn v-if="
226
220
  prop.mode === 'add' &&
227
- prop.type === 'contractor' &&
221
+ (withStep2 || withStep3) &&
228
222
  contractorStep > 1
229
223
  " tile block variant="text" class="text-none" size="48" @click="handleGoToPreviousPage" text="Back" />
230
224
  <v-btn v-else-if="prop.mode === 'add' || prop.mode === 'register'" tile block variant="text" class="text-none"
@@ -232,10 +226,11 @@
232
226
  <v-btn v-else tile block variant="text" class="text-none" size="48" @click="emit('close:all')" text="Close" />
233
227
  </v-col>
234
228
  <v-col cols="6">
235
- <v-btn v-if="prop.type === 'contractor'" tile block variant="flat" color="primary-button" class="text-none"
236
- size="48" :disabled="processing" @click="handleNextPage" :text="contractorStep === 3 ? 'Submit' : 'Next'" />
229
+ <v-btn v-if="showNextButton" tile block variant="flat" color="primary-button" class="text-none" size="48"
230
+ :disabled="processing" @click="handleNextPage" :text="'Next'" />
237
231
  <v-btn v-else tile block variant="flat" color="black" class="text-none" size="48"
238
- :disabled="!validForm || processing" @click="submit" :text="prop.mode == 'add' ? 'Submit' : 'Update'" />
232
+ :disabled="!validForm || processing || membersFieldIncomplete" @click="submit"
233
+ :text="prop.mode == 'add' ? 'Submit' : 'Update'" />
239
234
  </v-col>
240
235
  </v-row>
241
236
  </v-toolbar>
@@ -353,6 +348,8 @@ const visitor = reactive<Partial<TVisitorPayload>>({
353
348
  remarks: "",
354
349
  attachments: [],
355
350
  members: [],
351
+ visitorPass: [],
352
+ passKeys: []
356
353
  });
357
354
 
358
355
  const passType = ref<string | null>(null);
@@ -400,12 +397,34 @@ const vehicleNumberUserItems = ref<TPeople[]>([])
400
397
 
401
398
 
402
399
  const shouldShowField = (fieldKey: keyof TVisitorPayload | 'delivery-company') => {
403
- if (prop.type !== "contractor" || contractorStep.value === 1) {
400
+ if ((!withStep2.value && !withStep3.value) || contractorStep.value === 1) {
404
401
  const visibleFields = typeFieldMap[prop.type];
405
402
  return visibleFields?.includes(fieldKey);
406
403
  }
407
404
  };
408
405
 
406
+ const withStep2 = computed(() => {
407
+ const step2Types = ["guest", "walk-in"];
408
+ return step2Types.includes(prop.type);
409
+ })
410
+
411
+ const withStep3 = computed(() => {
412
+ const step3Types = ["contractor"];
413
+ return step3Types.includes(prop.type);
414
+ })
415
+
416
+ const showNextButton = computed(() => {
417
+ if (withStep3.value) {
418
+ return contractorStep.value === 1 || contractorStep.value === 2
419
+ } else if (withStep2.value) {
420
+ return contractorStep.value === 1
421
+ } else return false
422
+ })
423
+
424
+ const showShowKeysField = computed(() => {
425
+ return prop.type === "contractor"
426
+ })
427
+
409
428
  const requireNRIC = computed(() => {
410
429
  return prop.type !== 'walk-in' && prop.type !== 'guest';
411
430
  })
@@ -415,8 +434,10 @@ const companyAutofillDataArray = ref<{ companyName: string[], unit: string, bloc
415
434
 
416
435
  const formTitle = computed(() => {
417
436
  const isContractorForm = prop.type === "contractor";
437
+ const isDriveInForm = prop.type === "guest";
438
+ const isWalkInForm = prop.type === "walk-in";
418
439
  const step = contractorStep.value;
419
- if (isContractorForm && step === 2) {
440
+ if ((isContractorForm || isDriveInForm || isWalkInForm) && step === 2) {
420
441
  return "Pass & Keys Information";
421
442
  } else if (isContractorForm && step === 3) {
422
443
  return "Members Information";
@@ -519,7 +540,7 @@ const {
519
540
  }
520
541
  );
521
542
 
522
- watch(fetchPersonByContactReq, (obj) => {
543
+ watch(fetchPersonByContactReq, (obj: Partial<TPerson>) => {
523
544
  if (obj) {
524
545
  currentAutofillSource.value = "contact";
525
546
  companyNames.value = obj.companyName ?? []
@@ -568,7 +589,7 @@ watch(fetchVisitorListByVehicleNumberReq, (arr: any) => {
568
589
  const itemsArray = arr?.items || []
569
590
  const isValidArray = Array.isArray(itemsArray)
570
591
  matchingPlateNumberNonCheckedOutArr.value = isValidArray ? itemsArray : []
571
- if(matchingPlateNumberNonCheckedOutArr.value.length > 0) {
592
+ if (matchingPlateNumberNonCheckedOutArr.value.length > 0) {
572
593
  dialog.showNonCheckedOutDialog = true;
573
594
  } else {
574
595
  dialog.showNonCheckedOutDialog = false;
@@ -603,7 +624,7 @@ async function handleCheckout(visitorId: string) {
603
624
  checkOut: new Date().toISOString(),
604
625
  });
605
626
  if (res) {
606
- await fetchVisitorListByVehicleNumberRefresh();
627
+ await fetchVisitorListByVehicleNumberRefresh();
607
628
 
608
629
  }
609
630
  } catch (error: any) {
@@ -876,6 +897,15 @@ async function submit() {
876
897
  };
877
898
  }
878
899
  }
900
+
901
+ const isGuest = prop.type === "guest";
902
+ const isWalkIn = prop.type === "walk-in";
903
+ const isContractor = prop.type === "contractor";
904
+ if (isGuest || isWalkIn || isContractor) {
905
+ payload.visitorPass = visitor.visitorPass;
906
+ payload.passKeys = visitor.passKeys;
907
+ }
908
+
879
909
  }
880
910
 
881
911
 
@@ -888,7 +918,7 @@ async function submit() {
888
918
  };
889
919
  }
890
920
 
891
- if(prop.type === "delivery"){
921
+ if (prop.type === "delivery") {
892
922
  payload = {
893
923
  ...payload,
894
924
  company: deliveryCompany.value
@@ -969,6 +999,18 @@ watch(
969
999
  }
970
1000
  );
971
1001
 
1002
+ const membersFieldIncomplete = computed(() => {
1003
+ if (!visitor.members || visitor.members.length === 0) return false;
1004
+
1005
+ return visitor.members.some((member) => {
1006
+ const hasName = !!member.name;
1007
+ const hasNric = !!member.nric;
1008
+ const hasContact = !!member.contact;
1009
+
1010
+ return !hasName || !hasNric || !hasContact;
1011
+ });
1012
+ });
1013
+
972
1014
  onMounted(() => {
973
1015
  contractorStep.value = 2;
974
1016
  currentAutofillSource.value = null;