@7365admin1/layer-common 1.8.4 → 1.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.8.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 21990e5: Update Version for new changes
8
+
9
+ ## 1.8.5
10
+
11
+ ### Patch Changes
12
+
13
+ - 8db0ed5: Update on components and config
14
+
3
15
  ## 1.8.4
4
16
 
5
17
  ### Patch Changes
@@ -28,7 +28,7 @@
28
28
  </v-toolbar>
29
29
 
30
30
  <v-data-table :headers="headers" :items="items" item-value="_id" items-per-page="20" fixed-header
31
- hide-default-footer hide-default-header @click:row="tableRowClickHandler"
31
+ hide-default-footer @click:row="tableRowClickHandler"
32
32
  style="max-height: calc(100vh - (200px))">
33
33
  <template #item.createdAt="{ value }">
34
34
  {{ new Date(value).toLocaleDateString() }}
@@ -333,3 +333,8 @@ function setBuilding({
333
333
  }
334
334
  }
335
335
  </script>
336
+ <style scoped>
337
+ .v-data-table :deep(thead th) {
338
+ font-weight: bold !important;
339
+ }
340
+ </style>
@@ -28,7 +28,7 @@
28
28
  </v-toolbar>
29
29
 
30
30
  <v-data-table :headers="props.headers" :items="items" item-value="_id" items-per-page="20" fixed-header
31
- hide-default-footer hide-default-header @click:row="tableRowClickHandler"
31
+ hide-default-footer @click:row="tableRowClickHandler"
32
32
  style="max-height: calc(100vh - (200px))">
33
33
  <template #item.block="{ value }">
34
34
  {{ value ? `blk ${value}` : "" }}
@@ -348,3 +348,9 @@ function setBuilding({
348
348
  }
349
349
  }
350
350
  </script>
351
+
352
+ <style scoped>
353
+ .v-data-table :deep(thead th) {
354
+ font-weight: bold !important;
355
+ }
356
+ </style>
@@ -11,6 +11,7 @@
11
11
 
12
12
  <template #append>
13
13
  <v-btn
14
+ v-if="!prop.readOnly"
14
15
  variant="flat"
15
16
  color="primary"
16
17
  class="text-none"
@@ -144,7 +145,7 @@
144
145
 
145
146
  <v-toolbar class="pa-0" density="compact">
146
147
  <v-row no-gutters>
147
- <v-col cols="6" class="pa-0">
148
+ <v-col :cols="prop.readOnly ? 12 : 6" class="pa-0">
148
149
  <v-btn
149
150
  block
150
151
  variant="text"
@@ -157,7 +158,7 @@
157
158
  </v-btn>
158
159
  </v-col>
159
160
 
160
- <v-col cols="6" class="pa-0">
161
+ <v-col v-if="!prop.readOnly" cols="6" class="pa-0">
161
162
  <v-menu>
162
163
  <template #activator="{ props }">
163
164
  <v-btn
@@ -176,13 +177,13 @@
176
177
  <v-list class="pa-0">
177
178
  <v-list-item @click="openDialogEdit()">
178
179
  <v-list-item-title class="text-subtitle-2">
179
- Edit Building
180
+ Edit
180
181
  </v-list-item-title>
181
182
  </v-list-item>
182
183
 
183
184
  <v-list-item @click="openDialogDelete()" class="text-red">
184
185
  <v-list-item-title class="text-subtitle-2">
185
- Delete Building
186
+ Delete
186
187
  </v-list-item-title>
187
188
  </v-list-item>
188
189
  </v-list>
@@ -268,6 +269,10 @@ const prop = defineProps({
268
269
  { text: "Actions", value: "action", sortable: false },
269
270
  ],
270
271
  },
272
+ readOnly: {
273
+ type: Boolean,
274
+ default: false,
275
+ },
271
276
  });
272
277
 
273
278
  const items = ref<Array<TCamera>>([]);
@@ -310,7 +310,7 @@ const _feedback = ref({
310
310
 
311
311
  const selected = ref<string[]>([]);
312
312
 
313
- const { getColorStatus } = useUtils();
313
+ const { getColorStatus, debounce } = useUtils();
314
314
 
315
315
  const headers: Array<Record<string, any>> = [
316
316
  { title: "Name", value: "createdByName", align: "start" },
@@ -325,6 +325,10 @@ const erroredImages = ref<string[]>([]);
325
325
  const route = useRoute();
326
326
  const { customers } = useCustomer();
327
327
 
328
+ const dialogPreview = ref(false);
329
+ const selectedFeedback = ref<TFeedback | null>(null);
330
+ const searchText = ref("");
331
+
328
332
  const {
329
333
  getFeedbacks: _getFeedbacks,
330
334
  deleteFeedback,
@@ -347,6 +351,7 @@ const {
347
351
  page: page.value,
348
352
  site: route.params.site as string,
349
353
  category: props.category,
354
+ search: searchText.value || "",
350
355
  })
351
356
  );
352
357
 
@@ -575,14 +580,19 @@ const categories = computed(() =>
575
580
  );
576
581
 
577
582
  function tableRowClickHandler(_: any, data: any) {
578
- console.log(data.item);
583
+ // console.log(data.item);
579
584
  selectedFeedback.value = data.item;
580
585
  dialogPreview.value = true;
581
586
  }
582
587
 
583
- const dialogPreview = ref(false);
584
- const selectedFeedback = ref<TFeedback | null>(null);
585
- const searchText = ref("");
586
-
587
588
  getServiceProviderCategories();
589
+
590
+ const debounceSearch = debounce(getAllReqRefresh, 500);
591
+
592
+ watch(
593
+ () => searchText.value,
594
+ () => {
595
+ debounceSearch();
596
+ }
597
+ );
588
598
  </script>
@@ -1,71 +1,83 @@
1
1
  <template>
2
- <v-row no-gutters class="mb-5">
3
- <v-col cols="12" class="d-flex ga-2">
4
- <v-select v-model="selectedCode" :variant="variant" :items="countries" item-title="code" item-value="code"
5
- hide-details class="px-0" :density="density" style="max-width: 95px" :rules="[...props.rules]"
6
- :readonly="props.readOnly" @update:model-value="handleUpdateCountry">
7
- <template v-slot:item="{ props: itemProps, item }">
8
- <v-list-item v-bind="itemProps" :title="item.raw.name" :subtitle="item.raw.dial_code" width="300" />
9
- </template>
10
- </v-select>
11
- <v-mask-input v-model="input" :mask="currentMask" :rules="[...props.rules, validatePhone]" ref="maskRef" :key="`mask-key-${maskKey}`"
12
- :loading="loading" :readonly="props.readOnly" :variant="variant" hint="Enter a valid phone number"
13
- hide-details persistent-hint return-masked-value :prefix="phonePrefix || '###'" persistent-placeholder
14
- :density="density" :placeholder="placeholder || currentMask"></v-mask-input>
15
- </v-col>
16
- <span class="text-error text-caption w-100" v-if="errorMessage && !hideDetails">{{ errorMessage }}</span>
17
- </v-row>
18
-
2
+ <v-row no-gutters class="mb-5">
3
+ <v-col cols="12" class="d-flex ga-2">
4
+ <v-select
5
+ v-model="selectedCode"
6
+ :variant="variant"
7
+ :items="countries"
8
+ item-title="code"
9
+ item-value="code"
10
+ hide-details
11
+ class="px-0"
12
+ :density="density"
13
+ style="max-width: 95px"
14
+ :rules="[...props.rules]"
15
+ :readonly="props.readOnly"
16
+ @update:model-value="handleUpdateCountry"
17
+ >
18
+ <template v-slot:item="{ props: itemProps, item }">
19
+ <v-list-item
20
+ v-bind="itemProps"
21
+ :title="item.raw.name"
22
+ :subtitle="item.raw.dial_code"
23
+ />
24
+ </template>
25
+ </v-select>
26
+
27
+ <v-mask-input
28
+ v-model="input"
29
+ :mask="currentMask"
30
+ :rules="[...props.rules, validatePhone]"
31
+ ref="maskRef"
32
+ :key="`mask-key-${maskKey}`"
33
+ :loading="loading"
34
+ :readonly="props.readOnly"
35
+ :variant="variant"
36
+ hint="Enter a valid phone number"
37
+ hide-details
38
+ persistent-hint
39
+ return-masked-value
40
+ :prefix="phonePrefix || ''"
41
+ persistent-placeholder
42
+ :density="density"
43
+ :placeholder="placeholder || currentMask"
44
+ />
45
+ </v-col>
46
+ <span class="text-error text-caption w-100" v-if="errorMessage && !hideDetails">
47
+ {{ errorMessage }}
48
+ </span>
49
+ </v-row>
19
50
  </template>
20
51
 
21
52
  <script setup lang="ts">
22
- import { ref, computed, type PropType } from 'vue'
23
- import type { ValidationRule } from 'vuetify/lib/types.mjs'
53
+ import { ref, computed, watch, onMounted, type PropType } from 'vue'
24
54
  //@ts-ignore
25
55
  import phoneMasks from '~/utils/phoneMasks'
56
+ import type { ValidationRule } from 'vuetify/lib/types.mjs'
26
57
 
27
58
  const props = defineProps({
28
- rules: {
29
- type: Array as PropType<ValidationRule[]>,
30
- default: []
31
- },
32
- variant: {
33
- type: String as PropType<any>,
34
- default: "outlined"
35
- },
36
- density: {
37
- type: String as PropType<Density>,
38
- default: "default"
39
- },
40
- placeholder: {
41
- type: String,
42
- },
43
- hideDetails: {
44
- type: Boolean,
45
- default: false
46
- },
47
- loading: {
48
- type: Boolean,
49
- default: false
50
- },
51
- readOnly: {
52
- type: Boolean,
53
- default: false
54
- }
59
+ modelValue: { type: String as PropType<string>, default: '' },
60
+ rules: { type: Array as PropType<ValidationRule[]>, default: () => [] },
61
+ variant: { type: String as PropType<any>, default: 'outlined' },
62
+ density: { type: String as PropType<'default' | 'comfortable' | 'compact'>, default: 'default' },
63
+ placeholder: { type: String },
64
+ hideDetails: { type: Boolean, default: false },
65
+ loading: { type: Boolean, default: false },
66
+ readOnly: { type: Boolean, default: false },
55
67
  })
56
68
 
57
- type Density = 'default' | 'comfortable' | 'compact';
69
+ const emit = defineEmits(['update:modelValue'])
58
70
 
59
- type TPhoneMask =
60
- {
61
- name: string
62
- flag: string
63
- code: string;
64
- dial_code: string;
65
- regex: string;
66
- }
71
+ type TPhoneMask = {
72
+ name: string
73
+ flag: string
74
+ code: string
75
+ dial_code: string
76
+ regex: string
77
+ }
67
78
 
68
- const phone = defineModel({ default: '' })
79
+ // Main reactive values
80
+ const phone = ref(props.modelValue)
69
81
  const input = ref('')
70
82
  const selectedCode = ref('SG')
71
83
  const countries = phoneMasks
@@ -73,92 +85,82 @@ const errorMessage = ref('')
73
85
  const maskRef = ref()
74
86
  const maskKey = ref(0)
75
87
 
88
+
76
89
  const currentMask = computed(() => {
77
- const country = phoneMasks.find((c: TPhoneMask) => c.code === selectedCode.value)
78
- if (!country) return '############'
79
- return generateMaskFromRegex(country.regex)
90
+ const country = countries.find((c: TPhoneMask) => c.code === selectedCode.value)
91
+ if (!country) return '############'
92
+ return generateMaskFromRegex(country.regex)
80
93
  })
81
94
 
82
95
 
83
-
84
- const validatePhone = (): boolean | string => {
85
- if(props.readOnly) return true;
86
- const value = phone.value
87
- if (!value) {
88
- errorMessage.value = ''
89
- return true;
90
- }
91
- const country = phoneMasks.find((c: any) => c.code === selectedCode.value)
92
- if (!country) {
93
- errorMessage.value = ''
94
- return true
95
- }
96
-
97
- const regex = new RegExp(country.regex)
98
- const isValid = regex.test(value)
99
-
100
- errorMessage.value = isValid ? '' : `Invalid ${country.name} phone number`
101
- return isValid
102
- }
103
-
104
-
96
+ const phonePrefix = computed(() => {
97
+ const country = countries.find((c: TPhoneMask) => c.code === selectedCode.value)
98
+ return country?.dial_code || ''
99
+ })
105
100
 
106
101
  function generateMaskFromRegex(regex: string): string {
107
- let pattern = regex.replace(/^\^|\$$/g, '');
108
-
109
- pattern = pattern.replace(/\(\?:\+?\d+\)\?/g, '');
110
- pattern = pattern.replace(/\+?\d{1,4}/, '');
111
-
112
- pattern = pattern.replace(/\\d\{(\d+)\}/g, (_, count) => '#'.repeat(Number(count)));
102
+ let pattern = regex.replace(/^\^|\$$/g, '')
103
+ pattern = pattern.replace(/\(\?:\+?\d+\)\?/g, '')
104
+ pattern = pattern.replace(/\+?\d{1,4}/, '')
105
+ pattern = pattern.replace(/\\d\{(\d+)\}/g, (_, count) => '#'.repeat(Number(count)))
106
+ pattern = pattern.replace(/\\d/g, '#')
107
+ pattern = pattern.replace(/\\/g, '')
108
+ pattern = pattern.replace(/\(\?:/g, '')
109
+ return pattern.trim()
110
+ }
113
111
 
114
- pattern = pattern.replace(/\\d/g, '#');
112
+ const validatePhone = (): boolean | string => {
113
+ if (props.readOnly) return true
114
+ if (!phone.value) return true
115
115
 
116
- pattern = pattern.replace(/\\/g, '');
117
- pattern = pattern.replace(/\(\?:/g, '');
118
- pattern = pattern.trim();
116
+ const country = countries.find((c: TPhoneMask) => c.code === selectedCode.value)
117
+ if (!country) return true
119
118
 
120
- return pattern;
119
+ const regex = new RegExp(country.regex)
120
+ const isValid = regex.test(phone.value)
121
+ errorMessage.value = isValid ? '' : `Invalid ${country.name} phone number`
122
+ return isValid
121
123
  }
122
124
 
123
- const phonePrefix = computed(() => {
124
- const country = phoneMasks.find((c: TPhoneMask) => c.code === selectedCode.value)
125
- return country?.dial_code || ''
126
- })
127
-
128
-
129
125
  function handleUpdateCountry() {
130
- phone.value = ''
126
+ const prefix = phonePrefix.value
127
+ if (phone.value?.startsWith(prefix)) {
128
+ input.value = phone.value.slice(prefix.length)
129
+ } else {
130
+ input.value = ''
131
+ }
131
132
  }
132
133
 
133
- const emit = defineEmits(['update:modelValue'])
134
-
135
- watch(phone, (newVal) => {
136
- emit('update:modelValue', newVal)
137
- })
138
134
 
139
135
  watch(input, (newInput) => {
140
- const prefix = phonePrefix.value
136
+ const prefix = phonePrefix.value
137
+ if (!newInput) phone.value = ''
138
+ else phone.value = prefix + newInput
139
+ })
141
140
 
142
- if (!newInput) {
143
- return
144
- }
145
141
 
146
- phone.value = prefix + newInput
142
+ watch(phone, (newVal) => {
143
+ const prefix = phonePrefix.value
144
+ if (!newVal) input.value = ''
145
+ else input.value = newVal.startsWith(prefix) ? newVal.slice(prefix.length) : newVal
146
+ emit('update:modelValue', newVal)
147
147
  })
148
148
 
149
149
 
150
- onMounted(() => {
151
- if (!phone.value) return
150
+ watch(selectedCode, () => {
151
+ const prefix = phonePrefix.value
152
+ if (phone.value?.startsWith(prefix)) input.value = phone.value.slice(prefix.length)
153
+ else input.value = phone.value || ''
154
+ })
152
155
 
153
- const found = phoneMasks.find((c: any) => phone.value?.startsWith(c?.dial_code))
154
- if (found) {
155
- selectedCode.value = found.code
156
- }
157
156
 
158
- input.value = phone.value.replace(found?.dial_code || '', '')
157
+ onMounted(() => {
158
+ if (phone.value) {
159
+ const found = countries.find((c: TPhoneMask) => phone.value.startsWith(c.dial_code))
160
+ if (found) selectedCode.value = found.code
161
+ const prefix = phonePrefix.value
162
+ input.value = phone.value.startsWith(prefix) ? phone.value.slice(prefix.length) : phone.value
159
163
  maskKey.value++
164
+ }
160
165
  })
161
-
162
-
163
-
164
166
  </script>
@@ -4,11 +4,11 @@
4
4
  <v-row>
5
5
  <v-col cols="6">
6
6
  <InputLabel class="text-capitalize font-weight-bold" :title="title" :required="required" />
7
- <v-text-field v-model.number="count" type="number" density="comfortable" :rules="rules" />
7
+ <v-text-field v-model.number="count" type="number" density="comfortable" :rules="rules" :readonly="readOnly" />
8
8
  </v-col>
9
9
 
10
10
  <v-col cols="6">
11
- <v-btn color="primary" class="text-none mt-6" size="large" variant="flat"
11
+ <v-btn v-if="!readOnly" color="primary" class="text-none mt-6" size="large" variant="flat"
12
12
  :disabled="!valid || disabled" :loading="loading || updating" text="Save" @click="handleSave" />
13
13
  </v-col>
14
14
  </v-row>
@@ -34,6 +34,7 @@ const props = defineProps({
34
34
  siteId: { type: String, required: true },
35
35
  existingBlockNumber: { type: Number, default: 0 },
36
36
  existingGuardPostsNumber: { type: Number, default: 0 },
37
+ readOnly: { type: Boolean, default: false },
37
38
  });
38
39
 
39
40
  const { updateSite, setSiteGuardPosts } = useSiteSettings()
@@ -7,7 +7,7 @@
7
7
  </v-toolbar>
8
8
 
9
9
  <v-card-text style="max-height: 100vh; overflow-y: auto">
10
- <v-data-table :items="prop.vehicleNumberUsersList" hide-default-footer :headers="headers" density="comfortable" style="max-height: calc(100vh - (200px))"
10
+ <v-data-table :items="prop.vehicleNumberUsersList" hide-default-footer :headers="headers" density="comfortable"
11
11
  item-key="_id ">
12
12
  <template #item.name="{ item }">
13
13
  <span class="d-flex align-center ga-2">
@@ -81,6 +81,7 @@ function handleSelectUser(item: TPeople){
81
81
  function handleUpdateUserDetails(){
82
82
  confirmAction.value = false;
83
83
  emit('update:people', selectedPeople.value)
84
+ emit('close')
84
85
  }
85
86
 
86
87
  onMounted(() => {
@@ -62,7 +62,7 @@
62
62
 
63
63
  <v-col v-if="shouldShowField('contact')" cols="12">
64
64
  <InputLabel class="text-capitalize" title="Phone Number" required />
65
- <InputPhoneNumberV2 v-model="visitor.contact" :rules="[requiredRule]" density="comfortable"
65
+ <InputPhoneNumberV2 v-model="visitor.contact" :rules="[requiredRule]" density="comfortable" :key="currentAutofillSource + 'phone-key'"
66
66
  :loading="fetchPersonByContactPending" @update:model-value="handleUpdateContact" />
67
67
  </v-col>
68
68
 
@@ -134,8 +134,8 @@
134
134
  <v-col v-if="shouldShowField('unit')" cols="12">
135
135
  <InputLabel class="text-capitalize" title="Unit" required />
136
136
  <v-select v-model="visitor.unit" :items="unitsArray" density="comfortable" item-title="title"
137
- item-value="value" :disabled="!visitor.level"
138
- :loading="unitsStatus === 'pending'" :rules="[requiredRule]" @update:model-value="handleUpdateUnit" />
137
+ item-value="value" :disabled="!visitor.level" :loading="unitsStatus === 'pending'" :rules="[requiredRule]"
138
+ @update:model-value="handleUpdateUnit" />
139
139
  </v-col>
140
140
 
141
141
  <v-col v-if="shouldShowField('remarks')" cols="12">
@@ -189,6 +189,11 @@
189
189
  </v-col>
190
190
  </v-row>
191
191
  </v-toolbar>
192
+
193
+ <v-dialog v-model="dialog.vehicleNumberUsersList" v-if="vehicleNumberUserItems.length > 0" persistent max-width="600">
194
+ <SearchVehicleNumberUser :vehicle-number="visitor.plateNumber ?? ''" :vehicle-number-users-list="vehicleNumberUserItems"
195
+ @close="dialog.vehicleNumberUsersList = false" @update:people="handleAutofillDataViaVehicleNumber" />
196
+ </v-dialog>
192
197
  </v-card>
193
198
  </template>
194
199
 
@@ -217,10 +222,14 @@ const prop = defineProps({
217
222
  },
218
223
  });
219
224
 
225
+ type AutofillSource = "nric" | "contact" | "vehicleNumber" | null;
226
+ const currentAutofillSource = ref<AutofillSource>(null);
227
+
228
+
220
229
  const { requiredRule, debounce } = useUtils();
221
230
  const { getSiteById, getSiteLevels, getSiteUnits } = useSiteSettings();
222
231
  const { createVisitor, typeFieldMap, contractorTypes } = useVisitor();
223
- const { findPersonByNRIC, findPersonByContact, searchCompanyList } = usePeople()
232
+ const { findPersonByNRIC, findPersonByContact, searchCompanyList, findUsersByPlateNumber } = usePeople()
224
233
 
225
234
  const emit = defineEmits([
226
235
  "back",
@@ -248,6 +257,10 @@ const visitor = reactive<Partial<TVisitorPayload>>({
248
257
  members: [],
249
258
  });
250
259
 
260
+ const dialog = reactive({
261
+ vehicleNumberUsersList: false,
262
+ });
263
+
251
264
  const validForm = ref(false);
252
265
  const formRef = ref<HTMLFormElement | null>(null);
253
266
  const processing = ref(false);
@@ -267,6 +280,10 @@ const blocksArray = ref<TDefaultOptionObj[]>([]);
267
280
  const levelsArray = ref<TDefaultOptionObj[]>([]);
268
281
  const unitsArray = ref<TDefaultOptionObj[]>([]);
269
282
 
283
+
284
+ const vehicleNumberUserItems = ref<TPeople[]>([])
285
+
286
+
270
287
  const shouldShowField = (fieldKey: keyof TVisitorPayload) => {
271
288
  if (prop.type !== "contractor" || contractorStep.value === 1) {
272
289
  const visibleFields = typeFieldMap[prop.type];
@@ -355,16 +372,20 @@ const {
355
372
 
356
373
  watch(fetchPersonByNRICReq, (obj) => {
357
374
  if (obj) {
375
+ currentAutofillSource.value = "nric";
358
376
  companyNames.value = obj.companyName ?? []
359
377
  visitor.name = obj.name
360
378
  visitor.contact = obj.contact
361
379
  if (!visitor.company) {
362
380
  visitor.company = companyNames.value?.[0]
363
381
  }
364
- visitor.plateNumber = obj.plateNumber ?? ""
365
382
  visitor.block = obj.block ?? ""
366
383
  visitor.level = obj.level ?? ""
367
384
  visitor.unit = obj.unit ?? ""
385
+
386
+ setTimeout(() => {
387
+ currentAutofillSource.value = null;
388
+ }, 1000);
368
389
  }
369
390
  })
370
391
 
@@ -381,16 +402,20 @@ const {
381
402
 
382
403
  watch(fetchPersonByContactReq, (obj) => {
383
404
  if (obj) {
405
+ currentAutofillSource.value = "contact";
384
406
  companyNames.value = obj.companyName ?? []
385
- visitor.name = obj.name
407
+ visitor.name = visitor.name ?? obj.name
386
408
  if (!visitor.company) {
387
409
  visitor.company = companyNames.value?.[0]
388
410
  }
389
- visitor.plateNumber = obj.plateNumber ?? ""
390
- visitor.block = obj.block ?? ""
391
- visitor.level = obj.level ?? ""
392
- visitor.unit = obj.unit ?? ""
393
- visitor.nric = obj.nric ?? ""
411
+ visitor.block = visitor.block ?? obj.block ?? ""
412
+ visitor.level = visitor.level ?? obj.level ?? ""
413
+ visitor.unit = visitor.unit ?? obj.unit ?? ""
414
+ visitor.nric = visitor.nric ?? obj.nric ?? ""
415
+
416
+ setTimeout(() => {
417
+ currentAutofillSource.value = null;
418
+ }, 1000);
394
419
  }
395
420
  })
396
421
 
@@ -410,6 +435,25 @@ watch(fetchCompanyListReq, (arr) => {
410
435
  companyNames.value = arr?.flatMap(x => x?.companyName)
411
436
  }
412
437
  })
438
+ const {
439
+ data: fetchVehicleNumberUserReq,
440
+ refresh: fetchVehicleNumberUserRefresh,
441
+ pending: fetchVehicleNumberUserPending,
442
+
443
+ } = useLazyAsyncData(`fetch-vehicle-number-user-list`, () => {
444
+ if (!visitor.plateNumber) return Promise.resolve(null)
445
+ return findUsersByPlateNumber(visitor.plateNumber)
446
+ })
447
+
448
+ watch(fetchVehicleNumberUserReq, (arr) => {
449
+ const arrayData = arr?.data
450
+ if (Array.isArray(arrayData)) {
451
+ vehicleNumberUserItems.value = arrayData;
452
+ if (arrayData.length > 0) {
453
+ dialog.vehicleNumberUsersList = true
454
+ }
455
+ }
456
+ })
413
457
 
414
458
  const debounceFetchCompany = debounce(async () => fetchCompanyListRefresh(), 200)
415
459
 
@@ -422,6 +466,41 @@ watch(companyNameInput, async (val) => {
422
466
  })
423
467
 
424
468
 
469
+ const debounceSearchVehicleUsers = debounce(() => {
470
+ if (!visitor.plateNumber) {
471
+ vehicleNumberUserItems.value = [];
472
+ dialog.vehicleNumberUsersList = false;
473
+ return;
474
+ }
475
+ fetchVehicleNumberUserRefresh();
476
+ }, 300);
477
+
478
+ watch(() => visitor.plateNumber, (newVal) => {
479
+ if(currentAutofillSource.value && currentAutofillSource.value !== "vehicleNumber") return;
480
+ debounceSearchVehicleUsers();
481
+ });
482
+
483
+
484
+ function handleAutofillDataViaVehicleNumber(item: TPeople){
485
+
486
+ currentAutofillSource.value = "vehicleNumber";
487
+
488
+ visitor.name = item.name
489
+ visitor.nric = item.nric
490
+ visitor.contact = item.contact
491
+ companyNames.value = item.companyName ?? []
492
+ if (!visitor.company) {
493
+ visitor.company = companyNames.value?.[0]
494
+ }
495
+ visitor.block = item.block ?? ""
496
+ visitor.level = item.level ?? ""
497
+ visitor.unit = item.unit ?? ""
498
+
499
+ setTimeout(() => {
500
+ currentAutofillSource.value = null;
501
+ }, 1000);
502
+ }
503
+
425
504
 
426
505
  const {
427
506
  data: siteData,
@@ -538,10 +617,12 @@ const debounceFetchNRIC = debounce(fetchPersonByNRICRefresh, 500)
538
617
  const debounceFetchContact = debounce(fetchPersonByContactRefresh, 500)
539
618
 
540
619
  function handleUpdateNRIC() {
620
+ if (currentAutofillSource.value && currentAutofillSource.value !== "nric") return;
541
621
  debounceFetchNRIC()
542
622
  }
543
623
 
544
624
  function handleUpdateContact() {
625
+ if (currentAutofillSource.value && currentAutofillSource.value !== "contact") return;
545
626
  debounceFetchContact()
546
627
  }
547
628
 
@@ -650,6 +731,7 @@ watch(
650
731
 
651
732
  onMounted(() => {
652
733
  contractorStep.value = 1;
734
+ currentAutofillSource.value = null;
653
735
  });
654
736
  </script>
655
737
  <style scoped>
@@ -145,10 +145,6 @@
145
145
  </VehicleUpdateMoreAction>
146
146
  </v-dialog>
147
147
 
148
- <v-dialog v-model="dialog.vehicleNumberUsersList" persistent max-width="600">
149
- <SearchVehicleNumberUser :vehicle-number="'123123'" @close="dialog.vehicleNumberUsersList = false" @update:people="handleUpdateAutofillDetails"/>
150
- </v-dialog>
151
-
152
148
  <v-dialog v-model="dialog.deleteConfirmation" width="450" persistent>
153
149
  <CardDeleteConfirmation prompt-title="Are you sure want to delete this visitor?"
154
150
  :loading="loading.deletingVisitor" @close="dialog.deleteConfirmation = false"
@@ -245,7 +241,6 @@ const dialog = reactive({
245
241
  addVisitor: false,
246
242
  viewVisitor: false,
247
243
  deleteConfirmation: false,
248
- vehicleNumberUsersList: false
249
244
  });
250
245
 
251
246
  const tabOptions = [
@@ -91,7 +91,12 @@
91
91
  <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
92
92
 
93
93
  <!-- Preview Dialog -->
94
- <v-dialog v-model="dialogPreview" width="450" persistent v-if="canViewWorkOrders">
94
+ <v-dialog
95
+ v-model="dialogPreview"
96
+ width="450"
97
+ persistent
98
+ v-if="canViewWorkOrders"
99
+ >
95
100
  <v-card width="100%">
96
101
  <v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
97
102
  <v-row no-gutters v-if="selectedWorkOrder" class="mb-4">
@@ -139,12 +144,18 @@
139
144
  </template>
140
145
 
141
146
  <v-list class="pa-0">
142
- <v-list-item @click="onViewWorkOrder(selectedWorkOrder)" v-if="canViewWorkOrderDetails">
147
+ <v-list-item
148
+ @click="onViewWorkOrder(selectedWorkOrder)"
149
+ v-if="canViewWorkOrderDetails"
150
+ >
143
151
  <v-list-item-title class="text-subtitle-2">
144
152
  View
145
153
  </v-list-item-title>
146
154
  </v-list-item>
147
- <v-list-item @click="editWorkOrder(selectedWorkOrder)" v-if="canUpdateWorkOrder">
155
+ <v-list-item
156
+ @click="editWorkOrder(selectedWorkOrder)"
157
+ v-if="canUpdateWorkOrder"
158
+ >
148
159
  <v-list-item-title class="text-subtitle-2">
149
160
  Edit
150
161
  </v-list-item-title>
@@ -224,7 +235,7 @@ const message = ref("");
224
235
  const messageColor = ref("");
225
236
  const messageSnackbar = ref(false);
226
237
 
227
- const { getColorStatus, formatDate } = useUtils();
238
+ const { getColorStatus, formatDate, debounce } = useUtils();
228
239
 
229
240
  const _workOrder = ref<TWorkOrderCreate>({
230
241
  attachments: [],
@@ -283,6 +294,7 @@ const {
283
294
  page: page.value,
284
295
  site: route.params.site as string,
285
296
  category: props.category,
297
+ search: searchText.value || "",
286
298
  })
287
299
  );
288
300
 
@@ -486,4 +498,13 @@ function tableRowClickHandler(_: any, data: any) {
486
498
  const dialogPreview = ref(false);
487
499
  const selectedWorkOrder = ref<TWorkOrder | null>(null);
488
500
  const searchText = ref("");
501
+
502
+ const debounceSearch = debounce(getAllReqRefresh, 500);
503
+
504
+ watch(
505
+ () => searchText.value,
506
+ () => {
507
+ debounceSearch();
508
+ }
509
+ );
489
510
  </script>
@@ -30,6 +30,12 @@ export default function(){
30
30
  method: "GET",
31
31
  });
32
32
  }
33
+ async function findUsersByPlateNumber(plateNumber: string): Promise<null | Partial<TPeople>> {
34
+ return await $fetch<Record<string, any>>(`/api/people/plateNumber/${plateNumber}`, {
35
+ method: "GET",
36
+ query: { limit: 20 }
37
+ });
38
+ }
33
39
 
34
40
  async function searchCompanyList(company: string): Promise<null | Partial<TPeople>> {
35
41
  return await $fetch<Record<string, any>>('/api/people/company', {
@@ -76,6 +82,7 @@ export default function(){
76
82
  findPersonByNRIC,
77
83
  findPersonByContact,
78
84
  getPeopleByUnit,
79
- searchCompanyList
85
+ searchCompanyList,
86
+ findUsersByPlateNumber
80
87
  }
81
88
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.8.4",
5
+ "version": "1.8.6",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
package/tsconfig.json CHANGED
@@ -1,15 +1,3 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "Node",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "types": ["vite/client", "nuxt"],
10
- "jsx": "preserve",
11
- "resolveJsonModule": true,
12
- "allowSyntheticDefaultImports": true
13
- },
14
- "include": ["./**/*.ts", "./**/*.vue"]
15
- }
2
+ "extends": "./.playground/.nuxt/tsconfig.json"
3
+ }