@7365admin1/layer-common 3.0.31-staging.17 → 3.0.31-staging.18

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.
@@ -18,64 +18,27 @@
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">
21
- <v-col v-if="shouldShowField('contractorType')" cols="12">
22
- <InputLabel class="text-capitalize" title="Contractor Type" required />
23
- <v-combobox v-model="contractorTypeObj" v-model:search="contractorTypeInput" ref="contractorTypeCombo"
24
- autocomplete="off" :hide-no-data="false" @update:focused="handleFocusedContractorType"
25
- :items="contractorTypes" :rules="[requiredRule]" item-value="value"
26
- @update:model-value="handleSelectContractorType" variant="outlined" density="comfortable" persistent-hint
27
- small-chips>
28
- <template v-slot:no-data>
29
- <v-list-item>
30
- <v-list-item-title @click.stop="handleAddNewContractorType" class="d-flex align-center ga-1">
31
- <span><v-icon icon="mdi-plus" /></span> Add "<strong>{{
32
- contractorTypeInput
33
- }}</strong>" as custom contractor type.
34
- </v-list-item-title>
35
- </v-list-item>
36
- </template>
37
- </v-combobox>
38
- </v-col>
39
-
40
21
  <v-col v-if="shouldShowField('attachments')" class="w-100">
41
22
  <InputLabel class="text-capitalize" title="Attach Image" />
42
23
  <InputFileV2 v-model="visitor.attachments" multiple :max-length="3" />
43
24
  </v-col>
44
25
 
45
- <v-col v-if="shouldShowField('name')" cols="12">
46
- <v-row>
47
- <v-col cols="12">
48
- <InputLabel class="text-capitalize" title="Full Name" required />
49
- <v-text-field v-model.trim="visitor.name" density="comfortable" :rules="[requiredRule]" />
50
- </v-col>
51
- </v-row>
52
- </v-col>
53
-
54
26
  <v-col v-if="shouldShowField('nric')" cols="12">
55
27
  <v-row>
56
28
  <v-col cols="12">
57
29
  <InputLabel class="text-capitalize" title="NRIC" :required="requireNRIC" />
58
30
  <v-menu v-model="nricSearchMenu" location="bottom" offset-y :close-on-content-click="false">
59
31
  <template #activator="{ props }">
60
- <InputNRICNumber v-bind="props" v-model="visitor.nric" density="comfortable" @focus="isNricFieldFocused = true" @blur="isNricFieldFocused = false"
32
+ <InputNRICNumber v-bind="props" v-model="visitor.nric" density="comfortable"
33
+ @focus="isNricFieldFocused = true" @blur="isNricFieldFocused = false"
61
34
  :rules="[requireNRIC ? requiredRule : () => true]" :key="currentAutofillSource + 'nric-key'"
62
35
  @update:model-value="handleUpdateNRIC" :loading="fetchPersonByNRICPending" />
63
36
  </template>
64
37
 
65
- <v-list v-if="nricSearchResults.length" class="pa-1">
38
+ <v-list v-if="nricSearchResults.length" class="pa-1 phone-search-results">
66
39
  <v-list-item v-for="(item, index) in nricSearchResults" :key="item._id || index"
67
40
  @click="selectNricSearchResult(item)" class="cursor-pointer nric-search-item">
68
- <div class="d-flex align-center justify-space-between ga-4 w-100">
69
- <span class="text-subtitle-1">{{ item.name || 'Unknown' }}</span>
70
- <span class="text-subtitle-1 text-no-wrap">NRIC: {{ item.nric || 'N/A' }}</span>
71
- </div>
72
- <template v-if="item?.contacts && item?.visitorPlateNumbers">
73
- <div v-if="item.contacts?.length < 2 && item.visitorPlateNumbers?.length < 2"
74
- class="d-flex align-center justify-space-between ga-4 w-100">
75
- <span class="text-subtitle-1">{{ item.contacts?.[0] || 'N/A' }}</span>
76
- <span class="text-subtitle-1 text-no-wrap">{{ item.visitorPlateNumbers?.[0] || 'N/A' }}</span>
77
- </div>
78
- </template>
41
+ <VisitorSearchResultSummary :item="item" />
79
42
  </v-list-item>
80
43
  </v-list>
81
44
  </v-menu>
@@ -84,24 +47,25 @@
84
47
  </v-col>
85
48
 
86
49
  <v-col v-if="shouldShowField('contact')" cols="12">
87
- <InputLabel class="text-capitalize" title="Phone Number" required />
88
- <v-menu v-model="phoneSearchMenu" location="bottom" offset-y :close-on-content-click="false">
89
- <template #activator="{ props }">
90
- <InputPhoneNumberV2 v-bind="props" v-model="visitor.contact" :rules="[requiredRule]" @focus="isPhoneFieldFocused = true" @blur="isPhoneFieldFocused = false"
91
- density="comfortable" :key="currentAutofillSource + 'phone-key'"
92
- @update:model-value="handleUpdatePhoneNumber" />
93
- </template>
50
+ <v-row>
51
+ <v-col cols="12">
52
+ <InputLabel class="text-capitalize" title="Phone Number" required />
53
+ <v-menu v-model="phoneSearchMenu" location="bottom" offset-y :close-on-content-click="false">
54
+ <template #activator="{ props }">
55
+ <InputPhoneNumberV2 v-bind="props" v-model="visitor.contact" :rules="[requiredRule]"
56
+ @focus="isPhoneFieldFocused = true" @blur="isPhoneFieldFocused = false" density="comfortable"
57
+ :key="currentAutofillSource + 'phone-key'" @update:model-value="handleUpdatePhoneNumber" />
58
+ </template>
94
59
 
95
- <v-list v-if="phoneSearchResults.length" class="pa-1">
96
- <v-list-item v-for="(item, index) in phoneSearchResults" :key="item._id || index"
97
- @click="selectPhoneNumberSearchResult(item)" class="cursor-pointer nric-search-item">
98
- <div class="d-flex align-center justify-space-between ga-4 w-100">
99
- <span class="text-subtitle-1">{{ item.name || 'Unknown' }}</span>
100
- <span class="text-subtitle-1 text-no-wrap">NRIC: {{ item.nric || 'N/A' }}</span>
101
- </div>
102
- </v-list-item>
103
- </v-list>
104
- </v-menu>
60
+ <v-list v-if="phoneSearchResults.length" class="pa-1 phone-search-results">
61
+ <v-list-item v-for="(item, index) in phoneSearchResults" :key="item._id || index"
62
+ @click="selectPhoneNumberSearchResult(item)" class="cursor-pointer nric-search-item">
63
+ <VisitorSearchResultSummary :item="item" />
64
+ </v-list-item>
65
+ </v-list>
66
+ </v-menu>
67
+ </v-col>
68
+ </v-row>
105
69
  </v-col>
106
70
 
107
71
 
@@ -113,17 +77,14 @@
113
77
  <v-menu v-model="vehicleSearchMenu" location="bottom" offset-y :close-on-content-click="false">
114
78
  <template #activator="{ props }">
115
79
  <InputVehicleNumber v-bind="props" v-model="visitor.plateNumber" density="comfortable"
116
- @update:model-value="handleUpdateVehicleNumber" @focus="isPlateNumberFieldFocused = true" @blur="isPlateNumberFieldFocused = false" />
80
+ @update:model-value="handleUpdateVehicleNumber" @focus="isPlateNumberFieldFocused = true"
81
+ @blur="isPlateNumberFieldFocused = false" />
117
82
  </template>
118
83
 
119
- <v-list v-if="vehicleSearchResults.length" class="pa-1">
84
+ <v-list v-if="vehicleSearchResults.length" class="pa-1 phone-search-results">
120
85
  <v-list-item v-for="(item, index) in vehicleSearchResults" :key="item._id || index"
121
- @click="selectPlateNumberSearchResult(item)"
122
- class="cursor-pointer nric-search-item">
123
- <div class="d-flex align-center justify-space-between ga-4 w-100">
124
- <span class="text-subtitle-1">{{ item.name || 'Unknown' }}</span>
125
- <span class="text-subtitle-1 text-no-wrap">NRIC: {{ item.nric || 'N/A' }}</span>
126
- </div>
86
+ @click="selectPlateNumberSearchResult(item)" class="cursor-pointer nric-search-item">
87
+ <VisitorSearchResultSummary :item="item" />
127
88
  </v-list-item>
128
89
  </v-list>
129
90
  </v-menu>
@@ -131,6 +92,35 @@
131
92
  </v-row>
132
93
  </v-col>
133
94
 
95
+ <v-col v-if="shouldShowField('contractorType')" cols="12">
96
+ <InputLabel class="text-capitalize" title="Contractor Type" required />
97
+ <v-combobox v-model="contractorTypeObj" v-model:search="contractorTypeInput" ref="contractorTypeCombo"
98
+ autocomplete="off" :hide-no-data="false" @update:focused="handleFocusedContractorType"
99
+ :items="contractorTypes" :rules="[requiredRule]" item-value="value"
100
+ @update:model-value="handleSelectContractorType" variant="outlined" density="comfortable" persistent-hint
101
+ small-chips>
102
+ <template v-slot:no-data>
103
+ <v-list-item>
104
+ <v-list-item-title @click.stop="handleAddNewContractorType" class="d-flex align-center ga-1">
105
+ <span><v-icon icon="mdi-plus" /></span> Add "<strong>{{
106
+ contractorTypeInput
107
+ }}</strong>" as custom contractor type.
108
+ </v-list-item-title>
109
+ </v-list-item>
110
+ </template>
111
+ </v-combobox>
112
+ </v-col>
113
+
114
+
115
+ <v-col v-if="shouldShowField('name')" cols="12">
116
+ <v-row>
117
+ <v-col cols="12">
118
+ <InputLabel class="text-capitalize" title="Full Name" required />
119
+ <v-text-field v-model.trim="visitor.name" density="comfortable" :rules="[requiredRule]" />
120
+ </v-col>
121
+ </v-row>
122
+ </v-col>
123
+
134
124
  <v-col v-if="shouldShowField('deliveryType')" cols="12">
135
125
  <v-row>
136
126
  <v-col cols="12">
@@ -321,7 +311,7 @@
321
311
  <span class="text-no-wrap">NRIC: {{ selectedNricSearchResult.nric || "N/A" }}</span>
322
312
  </div>
323
313
 
324
- <template v-if="selectedNricContactOptions.length > 1">
314
+ <template v-if="selectedNricContactOptions.length >= 1">
325
315
  <h3 class="nric-options-section-title font-weight-bold mb-2">Contact</h3>
326
316
  <v-radio-group v-model="selectedNricContact" hide-details color="success" class="nric-option-group">
327
317
  <v-radio v-for="contact in selectedNricContactOptions" :key="contact" :label="contact" :value="contact"
@@ -329,7 +319,7 @@
329
319
  </v-radio-group>
330
320
  </template>
331
321
 
332
- <template v-if="selectedNricPlateOptions.length > 1">
322
+ <template v-if="selectedNricPlateOptions.length >= 1">
333
323
  <h3 class="nric-options-section-title font-weight-bold mb-2 mt-5">Vehicle</h3>
334
324
  <v-radio-group v-model="selectedNricPlate" hide-details color="success" class="nric-option-group">
335
325
  <v-radio v-for="plate in selectedNricPlateOptions" :key="plate" :label="plate" :value="plate"
@@ -499,7 +489,6 @@ import useVisitor from "../composables/useVisitor";
499
489
  import useSiteEntryPassSettings from "../composables/useSiteEntryPassSettings";
500
490
  import useAccessManagement from "../composables/useAccessManagement";
501
491
  import usePeople from "../composables/usePeople";
502
- import { mockPhoneNumberSearchResults, mockVehicleNumberSearchResults } from "../utils/mock-people-response";
503
492
 
504
493
  const prop = defineProps({
505
494
  type: {
@@ -556,8 +545,8 @@ const {
556
545
  findPersonByNRIC,
557
546
  findPersonByNRICMultipleResult,
558
547
  findPersonByPhoneNumberMultipleResult,
548
+ findPersonByPlateNumberMultipleResult,
559
549
  searchCompanyList,
560
- findUsersByPlateNumber,
561
550
  } = usePeople();
562
551
  const { getById: getUnitDataById } = useBuildingUnit();
563
552
 
@@ -767,10 +756,18 @@ const selectedNricPlate = ref("");
767
756
  type SearchPerson = Omit<Partial<TPeople>, "contact"> & {
768
757
  contact?: string | string[];
769
758
  contacts?: string | string[];
770
- visitorPlateNumbers?: string | string[];
759
+ plateNumbers?: string | string[];
771
760
  plateNumber?: string | string[];
772
761
  };
773
762
 
763
+ function getPeopleSearchItems(res: any): Partial<TPeople>[] {
764
+ if (Array.isArray(res?.items)) return res.items;
765
+ if (Array.isArray(res?.data)) return res.data;
766
+ if (Array.isArray(res?.data?.items)) return res.data.items;
767
+ if (res?.data) return [res.data];
768
+ if (res && !res?.message) return [res];
769
+ return [];
770
+ }
774
771
 
775
772
  const formTitle = computed(() => {
776
773
  const isContractorForm = prop.type === "contractor";
@@ -843,12 +840,13 @@ const {
843
840
  } = useLazyAsyncData(`fetch-person`, () => {
844
841
  const NRIC = visitor.nric;
845
842
  if (!NRIC || NRIC.length < 1) return Promise.resolve(null);
846
- return findPersonByNRICMultipleResult(NRIC, prop.site);
843
+
844
+ const type = prop.type === 'walk-in' ? 'guest' : prop.type
845
+ return findPersonByNRICMultipleResult(NRIC, prop.site, type);
847
846
  });
848
847
 
849
848
  watch(fetchPersonByNRICReq, (obj) => {
850
- const items = obj?.items ?? [];
851
- nricSearchResults.value = Array.isArray(items) ? items : [];
849
+ nricSearchResults.value = getPeopleSearchItems(obj);
852
850
  nricSearchMenu.value = nricSearchResults.value.length > 0;
853
851
 
854
852
  // Mock data for testing NRIC autofill options.
@@ -860,7 +858,7 @@ watch(fetchPersonByNRICReq, (obj) => {
860
858
  // name: "James Smith",
861
859
  // nric: "1323232499",
862
860
  // contacts: ["653939394", "81234567"],
863
- // visitorPlateNumbers: ["34566", "SMA1234A"],
861
+ // plateNumbers: ["34566", "SMA1234A"],
864
862
  // companyName: ["Mock Company Pte Ltd"],
865
863
  // } as Partial<SearchPerson>,
866
864
  // {
@@ -868,7 +866,7 @@ watch(fetchPersonByNRICReq, (obj) => {
868
866
  // name: "Jane Tan",
869
867
  // nric: "S1234567A",
870
868
  // contacts: ["91234567"],
871
- // visitorPlateNumbers: ["SGB5678B"],
869
+ // plateNumbers: ["SGB5678B"],
872
870
  // companyName: ["Single Option Company"],
873
871
  // } as Partial<SearchPerson>,
874
872
  // ];
@@ -921,7 +919,7 @@ function getPersonContactOptions(item: Partial<TPeople>) {
921
919
 
922
920
  function getPersonPlateOptions(item: Partial<TPeople>) {
923
921
  const person = item as SearchPerson;
924
- const visitorPlateNumbers = toUniqueStringArray(person.visitorPlateNumbers);
922
+ const visitorPlateNumbers = toUniqueStringArray(person.plateNumbers);
925
923
  if (visitorPlateNumbers.length) return visitorPlateNumbers;
926
924
 
927
925
  const plateNumbers = toUniqueStringArray(person.plateNumber);
@@ -932,6 +930,28 @@ function getPersonPlateOptions(item: Partial<TPeople>) {
932
930
  return toUniqueStringArray([...plateNumbers, ...plates]);
933
931
  }
934
932
 
933
+ function findMatchingContactOption(options: string[], value?: string | null) {
934
+ const normalizedValue = value?.trim();
935
+ if (!normalizedValue) return "";
936
+
937
+ return (
938
+ options.find((option) => option.trim() === normalizedValue) ||
939
+ options.find((option) => option.replace(/\s/g, "") === normalizedValue.replace(/\s/g, "")) ||
940
+ ""
941
+ );
942
+ }
943
+
944
+ function normalizePlateOption(value?: string | null) {
945
+ return (value || "").replace(/[^A-Z0-9]/gi, "").toUpperCase();
946
+ }
947
+
948
+ function findMatchingPlateOption(options: string[], value?: string | null) {
949
+ const normalizedValue = normalizePlateOption(value);
950
+ if (!normalizedValue) return "";
951
+
952
+ return options.find((option) => normalizePlateOption(option) === normalizedValue) || "";
953
+ }
954
+
935
955
  function applyNricSearchResult(
936
956
  item: Partial<TPeople>,
937
957
  options: { contacts?: string; plateNumber?: string; source?: Exclude<AutofillSource, null> } = {}
@@ -978,22 +998,26 @@ function selectPlateNumberSearchResult(item: Partial<TPeople>) {
978
998
  function selectAutofillSearchResult(item: Partial<TPeople>, source: Exclude<AutofillSource, null>) {
979
999
  const contactOptions = getPersonContactOptions(item);
980
1000
  const plateOptions = getPersonPlateOptions(item);
1001
+ const selectedContact =
1002
+ findMatchingContactOption(contactOptions, visitor.contact) || contactOptions[0] || "";
1003
+ const selectedPlate =
1004
+ findMatchingPlateOption(plateOptions, visitor.plateNumber) || plateOptions[0] || "";
981
1005
 
982
- if (source === "nric" && (contactOptions.length > 1 || plateOptions.length > 1)) {
1006
+ if (contactOptions.length > 1 || plateOptions.length > 1) {
983
1007
  nricSearchMenu.value = false;
984
1008
  phoneSearchMenu.value = false;
985
1009
  vehicleSearchMenu.value = false;
986
1010
  currentAutofillSource.value = source;
987
1011
  selectedNricSearchResult.value = item;
988
- selectedNricContact.value = contactOptions[0] || "";
989
- selectedNricPlate.value = plateOptions[0] || "";
1012
+ selectedNricContact.value = selectedContact;
1013
+ selectedNricPlate.value = selectedPlate;
990
1014
  dialog.nricAutofillOptions = true;
991
1015
  return;
992
1016
  }
993
1017
 
994
1018
  applyNricSearchResult(item, {
995
- contacts: contactOptions[0],
996
- plateNumber: plateOptions[0],
1019
+ contacts: selectedContact,
1020
+ plateNumber: selectedPlate,
997
1021
  source,
998
1022
  });
999
1023
  }
@@ -1362,7 +1386,8 @@ const debounceFetchNRIC = debounce(fetchPersonByNRICRefresh, 500);
1362
1386
 
1363
1387
  function handleUpdateNRIC() {
1364
1388
  if (skipNricFetch.value) return;
1365
- if(!isNricFieldFocused.value) return;
1389
+ if (!isNricFieldFocused.value) return;
1390
+ if (visitor.nric && visitor.nric?.length < 4) return;
1366
1391
  debounceFetchNRIC();
1367
1392
  }
1368
1393
 
@@ -1374,35 +1399,59 @@ const {
1374
1399
  } = useLazyAsyncData(`fetch-person-phone-number`, () => {
1375
1400
  const contact = visitor.contact;
1376
1401
  if (!contact || contact.length < 4) return Promise.resolve(null);
1377
- return findPersonByPhoneNumberMultipleResult(contact, prop.site);
1402
+ const type = prop.type === 'walk-in' ? 'guest' : prop.type
1403
+ return findPersonByPhoneNumberMultipleResult(contact, prop.site, type);
1404
+ });
1405
+
1406
+ watch(fetchPersonByPhoneNumber, (obj) => {
1407
+ phoneSearchResults.value = getPeopleSearchItems(obj);
1408
+ phoneSearchMenu.value = phoneSearchResults.value.length > 0;
1378
1409
  });
1379
1410
 
1411
+ const debounceFetchPhoneNumber = debounce(fetchPersonByPhoneNumberRefresh, 500);
1412
+
1380
1413
  function handleUpdatePhoneNumber() {
1381
- if(!isPhoneFieldFocused.value) return;
1414
+ if (!isPhoneFieldFocused.value) return;
1382
1415
 
1383
1416
  const search = visitor.contact?.trim() || "";
1384
- if (search.length < 1) {
1417
+ if (search.length < 4) {
1385
1418
  phoneSearchResults.value = [];
1386
1419
  phoneSearchMenu.value = false;
1387
1420
  return;
1388
1421
  }
1389
1422
 
1390
- phoneSearchResults.value = mockPhoneNumberSearchResults
1391
- phoneSearchMenu.value = phoneSearchResults.value.length > 0;
1423
+ debounceFetchPhoneNumber();
1392
1424
  }
1393
1425
 
1426
+ const {
1427
+ data: fetchPersonByPlateNumber,
1428
+ refresh: fetchPersonByPlateNumberRefresh,
1429
+ pending: fetchPersonByPlateNumberPending,
1430
+ } = useLazyAsyncData(`fetch-person-plate-number`, () => {
1431
+ const plateNumber = visitor.plateNumber?.trim()?.toUpperCase() || "";
1432
+ if (plateNumber.length < 4) return Promise.resolve(null);
1433
+ const type = prop.type === 'walk-in' ? 'guest' : prop.type
1434
+ return findPersonByPlateNumberMultipleResult(plateNumber, prop.site, type);
1435
+ });
1436
+
1437
+ watch(fetchPersonByPlateNumber, (obj) => {
1438
+ vehicleSearchResults.value = getPeopleSearchItems(obj);
1439
+ vehicleSearchMenu.value = vehicleSearchResults.value.length > 0;
1440
+ });
1441
+
1442
+ const debounceFetchPlateNumber = debounce(fetchPersonByPlateNumberRefresh, 500);
1443
+
1394
1444
  function handleUpdateVehicleNumber() {
1395
- if(!isPlateNumberFieldFocused.value) return;
1445
+ if (!isPlateNumberFieldFocused.value) return;
1396
1446
 
1397
1447
  const search = visitor.plateNumber?.trim()?.toUpperCase() || "";
1398
- if (search.length < 1) {
1448
+ if (search.length < 4) {
1399
1449
  vehicleSearchResults.value = [];
1400
1450
  vehicleSearchMenu.value = false;
1401
1451
  return;
1402
1452
  }
1403
1453
 
1404
- vehicleSearchResults.value = mockVehicleNumberSearchResults
1405
- vehicleSearchMenu.value = vehicleSearchResults.value.length > 0;
1454
+ debounceFetchPlateNumber();
1406
1455
  }
1407
1456
 
1408
1457
  function backToSelection() {
@@ -2009,6 +2058,11 @@ onMounted(() => {
2009
2058
  min-height: 72px;
2010
2059
  }
2011
2060
 
2061
+ .phone-search-results {
2062
+ max-height: 400px;
2063
+ overflow-y: auto;
2064
+ }
2065
+
2012
2066
  .nric-options-card {
2013
2067
  border-radius: 16px !important;
2014
2068
  }
@@ -0,0 +1,86 @@
1
+ <template>
2
+ <div class="w-100 py-2">
3
+ <div class="d-flex align-center mb-3 justify-space-between ga-4 w-100">
4
+ <span class="text-subtitle-1" style="min-width: 50%">
5
+ <div>Name:</div>
6
+ <div>{{ displayName }}</div>
7
+ </span>
8
+ <span class="text-subtitle-1" style="min-width: 50%">
9
+ <div>NRIC:</div>
10
+ <div>{{ item?.nric || "N/A" }}</div>
11
+ </span>
12
+ </div>
13
+
14
+ <div
15
+ v-if="contacts.length < 2 && plateNumbers.length < 2"
16
+ class="d-flex align-center justify-space-between ga-4 w-100"
17
+ >
18
+ <div class="text-subtitle-1" style="min-width: 50%">
19
+ <div>Contact:</div>
20
+ <div>{{ contacts[0] || "N/A" }}</div>
21
+ </div>
22
+ <div class="text-subtitle-1" style="min-width: 50%">
23
+ <div>Plate:</div>
24
+ <div class="text-uppercase">{{ plateNumbers[0] || "N/A" }}</div>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </template>
29
+
30
+ <script setup lang="ts">
31
+ const props = defineProps<{
32
+ item: any;
33
+ }>();
34
+
35
+ const displayName = computed(() => {
36
+ const name =
37
+ props.item?.name ||
38
+ [props.item?.firstName, props.item?.lastName].filter(Boolean).join(" ");
39
+
40
+ return name || "Unknown";
41
+ });
42
+
43
+ const contacts = computed(() => {
44
+ const values = [
45
+ props.item?.contact,
46
+ props.item?.phoneNumber,
47
+ ...(Array.isArray(props.item?.contacts) ? props.item.contacts : []),
48
+ ...(Array.isArray(props.item?.contactNumbers)
49
+ ? props.item.contactNumbers
50
+ : []),
51
+ ...(Array.isArray(props.item?.phoneNumbers) ? props.item.phoneNumbers : []),
52
+ ];
53
+
54
+ return Array.from(
55
+ new Set(
56
+ values
57
+ .map((contact: any) =>
58
+ typeof contact === "string"
59
+ ? contact
60
+ : contact?.contact || contact?.phoneNumber || contact?.number
61
+ )
62
+ .filter(Boolean)
63
+ )
64
+ );
65
+ });
66
+
67
+ const plateNumbers = computed(() => {
68
+ const values = [
69
+ props.item?.plateNumber,
70
+ ...(Array.isArray(props.item?.plates) ? props.item.plates : []),
71
+ ...(Array.isArray(props.item?.plateNumbers) ? props.item.plateNumbers : []),
72
+ ];
73
+
74
+ return Array.from(
75
+ new Set(
76
+ values
77
+ .map((plate: any) =>
78
+ typeof plate === "string"
79
+ ? plate
80
+ : plate?.plateNumber || plate?.number || plate?.value
81
+ )
82
+ .filter(Boolean)
83
+ )
84
+ );
85
+ });
86
+ </script>
@@ -37,43 +37,50 @@ export default function () {
37
37
  });
38
38
  }
39
39
 
40
- async function findPersonByNRICMultipleResult(nric: string, site: string) {
41
- return await $fetch<Record<any, any>>(`/api/people/all-nric`, {
40
+ async function findPersonByNRICMultipleResult(nric: string, site?: string, type?:string) {
41
+ return await $fetch<Record<any, any>>(`/api/people`, {
42
42
  method: "GET",
43
- query: { nric, site },
43
+ query: site ? { nric, type, site } : { nric, type },
44
44
  });
45
45
  }
46
46
 
47
- async function findPersonByPhoneNumberMultipleResult(contact: string, site: string) {
48
- return await $fetch<Record<any, any>>(`/api/people/contact`, {
47
+ async function findPersonByPhoneNumberMultipleResult(
48
+ contact: string,
49
+ site?: string,
50
+ type?:string
51
+ ) {
52
+ return await $fetch<Record<any, any>>(`/api/people`, {
49
53
  method: "GET",
50
- query: { contact, site },
54
+ query: site ? { contact, site, type } : { contact, type },
51
55
  });
52
56
  }
53
- async function findPersonByPlateNumberMultipleResult(plateNumber: string, site: string) {
54
- return await $fetch<Record<any, any>>(`/api/people/plate-number`, {
57
+
58
+ async function findPersonByPlateNumberMultipleResult(
59
+ plateNumber: string,
60
+ site?: string,
61
+ type?:string
62
+ ) {
63
+ return await $fetch<Record<any, any>>(`/api/people`, {
55
64
  method: "GET",
56
- query: { plateNumber, site },
65
+ query: site ? { plateNumber, site, type } : { plateNumber, type },
57
66
  });
58
67
  }
59
68
 
60
69
  async function findPersonByContact(
61
70
  contact: string
62
71
  ): Promise<null | Partial<TPeople>> {
63
- return await $fetch<Record<string, any>>(`/api/people/contact/${contact}`, {
72
+ return await $fetch<Record<string, any>>(`/api/people`, {
64
73
  method: "GET",
74
+ query: { contact },
65
75
  });
66
76
  }
67
77
  async function findUsersByPlateNumber(
68
78
  plateNumber: string
69
79
  ): Promise<null | Partial<TPeople>> {
70
- return await $fetch<Record<string, any>>(
71
- `/api/people/plateNumber/${plateNumber}`,
72
- {
73
- method: "GET",
74
- query: { limit: 20 },
75
- }
76
- );
80
+ return await $fetch<Record<string, any>>(`/api/people`, {
81
+ method: "GET",
82
+ query: { plateNumber },
83
+ });
77
84
  }
78
85
 
79
86
  async function searchCompanyList(
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.0.31-staging.17",
5
+ "version": "3.0.31-staging.18",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
package/types/people.d.ts CHANGED
@@ -11,7 +11,7 @@ declare type TPeople = {
11
11
  nric?: string;
12
12
  contact?: string;
13
13
  contacts?: string[] //used in autofill only in VMS
14
- visitorPlateNumbers?: string [] //used in autofill only in VMS
14
+ plateNumbers?: string [] //used in autofill only in VMS
15
15
  remarks?: string;
16
16
  start?: string;
17
17
  end?: string;