@7365admin1/layer-common 1.10.6 → 1.10.8

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