@7365admin1/layer-common 1.10.3 → 1.10.5

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 (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/AccessCardDeleteDialog.vue +109 -0
  3. package/components/AccessCardHistoryDialog.vue +133 -0
  4. package/components/AccessCardPreviewDialog.vue +308 -0
  5. package/components/AccessCardQrTagging.vue +183 -0
  6. package/components/AccessCardReplaceForm.vue +179 -0
  7. package/components/AccessManagement.vue +61 -251
  8. package/components/AreaChecklistHistoryLogs.vue +1 -1
  9. package/components/BuildingManagement/units.vue +33 -2
  10. package/components/BuildingUnitFormAdd.vue +45 -99
  11. package/components/BuildingUnitFormEdit.vue +59 -148
  12. package/components/BulletinBoardManagement.vue +6 -1
  13. package/components/BulletinBoardView.vue +2 -2
  14. package/components/Button/Close.vue +3 -1
  15. package/components/CleaningScheduleMain.vue +20 -9
  16. package/components/IncidentReport/IncidentInformation.vue +45 -6
  17. package/components/IncidentReport/affectedEntities.vue +29 -0
  18. package/components/PeopleForm.vue +1 -1
  19. package/components/PlateNumberDisplay.vue +45 -0
  20. package/components/ScheduleAreaMain.vue +5 -2
  21. package/components/ScheduleTaskForm.vue +59 -114
  22. package/components/ScheduleTaskMain.vue +19 -15
  23. package/components/VehicleAddSelection.vue +58 -0
  24. package/components/VehicleForm.vue +600 -0
  25. package/components/VehicleManagement.vue +298 -0
  26. package/components/VisitorForm.vue +1 -1
  27. package/composables/useAccessManagement.ts +16 -0
  28. package/composables/useBulletin.ts +11 -9
  29. package/composables/useCard.ts +14 -0
  30. package/composables/useScheduleTask.ts +4 -8
  31. package/composables/useVehicle.ts +114 -0
  32. package/package.json +1 -1
  33. package/types/bulletin-board.d.ts +1 -1
  34. package/types/checkout-item.d.ts +1 -0
  35. package/types/cleaner-schedule.d.ts +1 -1
  36. package/types/people.d.ts +1 -1
  37. package/types/vehicle.d.ts +43 -0
@@ -12,10 +12,7 @@
12
12
  <v-row>
13
13
  <v-col cols="12" class="mt-2">
14
14
  <v-row no-gutters>
15
- <InputLabel
16
- class="text-capitalize font-weight-bold"
17
- title="Building"
18
- />
15
+ <InputLabel class="text-capitalize font-weight-bold" title="Building" />
19
16
  <v-col cols="12">
20
17
  {{ buildingUnit.buildingName }}
21
18
  </v-col>
@@ -28,10 +25,7 @@
28
25
  <v-row>
29
26
  <v-col cols="12" class="mt-2">
30
27
  <v-row no-gutters>
31
- <InputLabel
32
- class="text-capitalize font-weight-bold"
33
- title="Block"
34
- />
28
+ <InputLabel class="text-capitalize font-weight-bold" title="Block" />
35
29
  <v-col cols="12">
36
30
  {{ buildingUnit.block }}
37
31
  </v-col>
@@ -42,162 +36,96 @@
42
36
 
43
37
  <v-col cols="12">
44
38
  <v-row>
45
- <v-col cols="12" class="mt-2">
39
+ <v-col cols="12" md="6" class="mt-2">
46
40
  <v-row no-gutters>
47
- <InputLabel
48
- class="text-capitalize font-weight-bold"
49
- title="Unit Name"
50
- />
41
+ <InputLabel class="text-capitalize font-weight-bold" title="Unit Name" />
51
42
  <v-col cols="12">
52
- <v-text-field
53
- v-model="buildingUnit.name"
54
- density="comfortable"
55
- :rules="[requiredRule]"
56
- ></v-text-field>
43
+ <v-text-field v-model="buildingUnit.name" density="comfortable"
44
+ :rules="[requiredRule]"></v-text-field>
57
45
  </v-col>
58
46
  </v-row>
59
47
  </v-col>
60
- </v-row>
61
- </v-col>
62
48
 
63
- <v-col cols="12">
64
- <v-row>
65
- <v-col cols="12" class="mt-2">
49
+ <v-col cols="12" md="6" class="mt-2">
66
50
  <v-row no-gutters>
67
- <InputLabel
68
- class="text-capitalize font-weight-bold"
69
- title="Owner"
70
- />
51
+ <InputLabel class="text-capitalize font-weight-bold" title="Owner" />
71
52
  <v-col cols="12">
72
- <v-autocomplete
73
- v-model="buildingUnit.ownerName"
74
- :items="peopleItems"
75
- item-title="name"
76
- return-object
77
- density="comfortable">
78
- </v-autocomplete>
53
+ <v-autocomplete v-model="buildingUnit.ownerName" v-model:search="ownerNameInput"
54
+ :items="peopleItems" item-title="name" return-object density="comfortable">
55
+ </v-autocomplete>
79
56
  </v-col>
80
57
  </v-row>
81
58
  </v-col>
82
59
  </v-row>
83
60
  </v-col>
84
61
 
62
+
85
63
  <v-col cols="12">
86
64
  <v-row>
87
- <v-col cols="12" class="mt-2">
65
+ <v-col cols="12" md="6" class="mt-2">
88
66
  <v-row no-gutters>
89
- <InputLabel
90
- class="text-capitalize"
91
- title="Company Name"
92
- required
93
- />
67
+ <InputLabel class="text-capitalize" title="Company Name" required />
94
68
  <v-col cols="12">
95
- <v-text-field
96
- v-model="buildingUnit.companyName"
97
- density="comfortable"
98
- :rules="[requiredRule]"
99
- ></v-text-field>
69
+ <v-text-field v-model="buildingUnit.companyName" density="comfortable"
70
+ :rules="[requiredRule]"></v-text-field>
100
71
  </v-col>
101
72
  </v-row>
102
73
  </v-col>
103
- </v-row>
104
- </v-col>
105
74
 
106
- <v-col cols="12">
107
- <v-row>
108
- <v-col cols="12" class="mt-2">
75
+ <v-col cols="12" md="6" class="mt-2">
109
76
  <v-row no-gutters>
110
- <InputLabel
111
- class="text-capitalize"
112
- title="Company Registration Number"
113
- />
77
+ <InputLabel class="text-capitalize" title="Company Registration Number" />
114
78
  <v-col cols="12">
115
- <v-text-field
116
- v-model="buildingUnit.companyRegistrationNumber"
117
- density="comfortable"
118
- ></v-text-field>
79
+ <v-text-field v-model="buildingUnit.companyRegistrationNumber" density="comfortable"></v-text-field>
119
80
  </v-col>
120
81
  </v-row>
121
82
  </v-col>
122
83
  </v-row>
123
84
  </v-col>
124
85
 
86
+
125
87
  <v-col cols="12">
126
88
  <v-row>
127
- <v-col cols="12" class="mt-2">
89
+ <v-col cols="12" md="6" class="mt-2">
128
90
  <v-row no-gutters>
129
- <InputLabel
130
- class="text-capitalize"
131
- title="Lease Start"
132
- required
133
- />
91
+ <InputLabel class="text-capitalize" title="Lease Start" required />
134
92
  <v-col cols="12">
135
- <InputDateTimePicker
136
- ref="startDateRef"
137
- v-model="buildingUnit.leaseStart"
138
- :rules="[requiredRule]"
139
- />
93
+ <InputDateTimePicker ref="startDateRef" v-model="buildingUnit.leaseStart" :rules="[requiredRule]" />
140
94
  </v-col>
141
95
  </v-row>
142
96
  </v-col>
143
- </v-row>
144
- </v-col>
145
97
 
146
- <v-col cols="12">
147
- <v-row>
148
- <v-col cols="12" class="mt-2">
98
+ <v-col cols="12" md="6" class="mt-2">
149
99
  <v-row no-gutters>
150
- <InputLabel
151
- class="text-capitalize"
152
- title="Lease End"
153
- required
154
- />
100
+ <InputLabel class="text-capitalize" title="Lease End" required />
155
101
  <v-col cols="12">
156
- <InputDateTimePicker
157
- ref="endDateRef"
158
- v-model="buildingUnit.leaseEnd"
159
- :rules="[requiredRule]"
160
- />
102
+ <InputDateTimePicker ref="endDateRef" v-model="buildingUnit.leaseEnd" :rules="[requiredRule]" />
161
103
  </v-col>
162
104
  </v-row>
163
105
  </v-col>
106
+
164
107
  </v-row>
165
108
  </v-col>
166
109
 
167
- <v-col cols="12" class="mt-2">
110
+
111
+ <v-col cols="12" md="6" class="mt-2">
168
112
  <v-row no-gutters>
169
- <InputLabel
170
- class="text-capitalize font-weight-bold"
171
- title="Category"
172
- required
173
- />
113
+ <InputLabel class="text-capitalize font-weight-bold" title="Category" required />
174
114
  <v-col cols="12">
175
- <v-autocomplete
176
- v-model="buildingUnit.category"
177
- :items="unitCategories"
178
- density="comfortable"
179
- :rules="[requiredRule]"
180
- ></v-autocomplete>
115
+ <v-autocomplete v-model="buildingUnit.category" :items="unitCategories" density="comfortable"
116
+ :rules="[requiredRule]"></v-autocomplete>
181
117
  </v-col>
182
118
  </v-row>
183
119
  </v-col>
184
120
 
185
121
  <v-col cols="12">
186
122
  <v-row>
187
- <v-col cols="6" class="mt-2">
123
+ <v-col cols="6" md="6" class="mt-2">
188
124
  <v-row no-gutters>
189
- <InputLabel
190
- class="text-capitalize font-weight-bold"
191
- title="Level"
192
- required
193
- />
125
+ <InputLabel class="text-capitalize font-weight-bold" title="Level" required />
194
126
  <v-col cols="12">
195
- <v-select
196
- v-model="buildingUnit.level"
197
- :items="buildingLevels"
198
- density="comfortable"
199
- :rules="[requiredRule]"
200
- ></v-select>
127
+ <v-select v-model="buildingUnit.level" :items="buildingLevels" density="comfortable"
128
+ :rules="[requiredRule]"></v-select>
201
129
  </v-col>
202
130
  </v-row>
203
131
  </v-col>
@@ -205,25 +133,15 @@
205
133
  </v-col>
206
134
 
207
135
  <v-col cols="12" class="mt-5">
208
- <InputLabel
209
- class="text-capitalize"
210
- title="Upload Files (Lease of Contract, Cert. of Occupancy)"
211
- />
212
- <InputFileV2
213
- v-model="buildingUnit.buildingUnitFiles"
214
- :multiple="false"
215
- :max-length="10"
216
- title="Upload PDF Files"
217
- accept="application/pdf"
218
- />
136
+ <InputLabel class="text-capitalize" title="Upload Files (Lease of Contract, Cert. of Occupancy)" />
137
+ <InputFileV2 v-model="buildingUnit.buildingUnitFiles" :multiple="false" :max-length="10"
138
+ title="Upload PDF Files" accept="application/pdf" />
219
139
  </v-col>
220
140
 
221
141
  <v-col cols="12" class="my-2">
222
142
  <v-row no-gutters>
223
143
  <v-col cols="12" class="text-center">
224
- <span
225
- class="text-none text-subtitle-2 font-weight-medium text-error"
226
- >
144
+ <span class="text-none text-subtitle-2 font-weight-medium text-error">
227
145
  {{ message }}
228
146
  </span>
229
147
  </v-col>
@@ -236,30 +154,14 @@
236
154
  <v-toolbar density="compact">
237
155
  <v-row no-gutters>
238
156
  <v-col cols="6">
239
- <v-btn
240
- tile
241
- block
242
- variant="text"
243
- class="text-none"
244
- size="48"
245
- @click="cancel"
246
- >
157
+ <v-btn tile block variant="text" class="text-none" size="48" @click="cancel">
247
158
  Cancel
248
159
  </v-btn>
249
160
  </v-col>
250
161
 
251
162
  <v-col cols="6">
252
- <v-btn
253
- tile
254
- block
255
- variant="flat"
256
- color="black"
257
- class="text-none"
258
- size="48"
259
- :disabled="!validForm || disable"
260
- @click="submit"
261
- :loading="disable"
262
- >
163
+ <v-btn tile block variant="flat" color="black" class="text-none" size="48" :disabled="!validForm || disable"
164
+ @click="submit" :loading="disable">
263
165
  Submit
264
166
  </v-btn>
265
167
  </v-col>
@@ -320,10 +222,13 @@ buildingUnit.value = JSON.parse(JSON.stringify(prop.roomFacility));
320
222
 
321
223
  const emit = defineEmits(["cancel", "success", "success:create-more"]);
322
224
 
225
+ const ownerNameInput = ref("");
226
+
323
227
  const validForm = ref(false);
324
228
 
325
229
  const { getAll } = useBuilding();
326
- const { getPeopleByUnit } = usePeople();
230
+ const { getAll: getAllPeople } = usePeople();
231
+ const { debounce } = useUtils();
327
232
 
328
233
  const buildings = ref<Record<string, any>[]>([]);
329
234
  const peopleItems = ref<Array<Record<string, any>>>([]);
@@ -343,15 +248,13 @@ watchEffect(() => {
343
248
  }
344
249
  });
345
250
 
346
- const { data: getUnitPeople } = useLazyAsyncData(
251
+ const { data: getUnitPeople, refresh: getUnitPeopleRefresh } = useLazyAsyncData(
347
252
  "get-unit-people",
348
- async () => getPeopleByUnit(prop.roomFacility._id as string)
253
+ async () => getAllPeople({ limit: 10, site: prop.site, type: "tenant", search: ownerNameInput.value })
349
254
  );
350
255
 
351
256
  watch(getUnitPeople, (newData: any) => {
352
- if (newData) {
353
- peopleItems.value = newData ?? [];
354
- }
257
+ peopleItems.value = newData ?? [];
355
258
  });
356
259
 
357
260
  buildingUnit.value.site = prop.site;
@@ -394,7 +297,7 @@ const hasChanges = computed(() => {
394
297
  async function submit() {
395
298
  disable.value = true;
396
299
  try {
397
-
300
+
398
301
  if (buildingUnit.value.ownerName) {
399
302
  buildingUnit.value.owner = (buildingUnit.value.ownerName as any)._id
400
303
  buildingUnit.value.ownerName = (buildingUnit.value.ownerName as any).name
@@ -426,4 +329,12 @@ function cancel() {
426
329
  message.value = "";
427
330
  emit("cancel");
428
331
  }
332
+
333
+ const debounceSearchOwner = debounce(() => {
334
+ getUnitPeopleRefresh();
335
+ }, 500);
336
+
337
+ watch(ownerNameInput, (newVal) => {
338
+ debounceSearchOwner();
339
+ }, { immediate: false });
429
340
  </script>
@@ -83,6 +83,11 @@ const props = defineProps({
83
83
  }, canDeleteBulletinBoard: {
84
84
  type: Boolean,
85
85
  default: true
86
+ },
87
+ recipients: {
88
+ type: String,
89
+ default: "",
90
+ required: false
86
91
  }
87
92
  })
88
93
 
@@ -158,7 +163,7 @@ const {
158
163
  pending: getAnnouncementPending,
159
164
  } = await useLazyAsyncData(
160
165
  `get-all-announcements-${page.value}`,
161
- () => getAll({ page: page.value, site: siteId, status: status.value }),
166
+ () => getAll({ page: page.value, site: siteId, status: status.value, recipients: props.recipients }),
162
167
  {
163
168
  watch: [page, () => route.query],
164
169
  }
@@ -54,13 +54,13 @@
54
54
 
55
55
  <v-toolbar class="pa-0" density="compact">
56
56
  <v-row no-gutters>
57
- <v-col cols="6" class="pa-0">
57
+ <v-col :col="(!canUpdate && !canDelete) ? 12 : 6" class="pa-0">
58
58
  <v-btn block variant="text" height="48" class="text-none" @click="emit('close')">
59
59
  Close
60
60
  </v-btn>
61
61
  </v-col>
62
62
 
63
- <v-col cols="6" class="pa-0">
63
+ <v-col v-if="canUpdate || canDelete" cols="6" class="pa-0">
64
64
  <v-menu contained>
65
65
  <template #activator="{ props }">
66
66
  <v-btn block variant="flat" color="black" height="48" class="text-none" tile v-bind="props">
@@ -1,10 +1,12 @@
1
1
  <template>
2
- <v-btn text="Close" class="bg-grey-lighten-2 text-capitalize" :height="prop.height" @click="emit('click')" />
2
+ <v-btn v-if="!prop.iconOnly" text="Close" class="bg-grey-lighten-2 text-capitalize" :height="prop.height" @click="emit('click')" />
3
+ <v-btn v-else icon="mdi-close" size="small" class="bg-grey-lighten-2 text-capitalize" :height="prop.height" @click="emit('click')" />
3
4
  </template>
4
5
 
5
6
  <script setup lang="ts">
6
7
  const prop = defineProps<{
7
8
  height?: string | number;
9
+ iconOnly?: boolean;
8
10
  }>();
9
11
 
10
12
  const emit = defineEmits<{
@@ -57,10 +57,9 @@
57
57
  }}
58
58
  </template>
59
59
  <template #item.closeIn="{ item }">
60
- <!-- <v-chip class="text-capitalize">{{ value || "No Status" }}</v-chip> -->
61
- <v-chip class="text-capitalize" variant="flat" color="primary">{{
62
- remainingTime[item._id] || "No Status"
63
- }}</v-chip>
60
+ <v-chip class="text-capitalize" variant="flat" color="primary" pill>
61
+ {{ remainingTime[item._id] || "No Status" }}
62
+ </v-chip>
64
63
  </template>
65
64
  <template #item.status="{ value }">
66
65
  <v-chip
@@ -112,7 +111,7 @@ const endDate = ref("");
112
111
  const status = ref<TScheduleAreaStatus>("All");
113
112
  const statusOptions = [
114
113
  { title: "All", value: "all" },
115
- { title: "Ready", value: "ready" },
114
+ { title: "Open", value: "open" },
116
115
  { title: "Ongoing", value: "ongoing" },
117
116
  { title: "Completed", value: "completed" },
118
117
  ] as const;
@@ -129,7 +128,8 @@ const headers = [
129
128
  { title: "Completion Date", value: "completionDate" },
130
129
  { title: "", value: "download" },
131
130
  ];
132
- const remainingTime = ref({} as any);
131
+ const remainingTime = ref({} as Record<string, string>);
132
+ const remainingSeconds = ref({} as Record<string, number>);
133
133
  const downloadingId = ref<string | null>(null);
134
134
 
135
135
  const { getCleaningSchedules, downloadChecklistPdf } = useCleaningSchedules();
@@ -141,18 +141,29 @@ const getStatusColor = (status: unknown): string => {
141
141
  const normalized = String(status).toLowerCase();
142
142
 
143
143
  switch (normalized) {
144
- case "ready":
144
+ case "open":
145
145
  return "grey";
146
146
  case "ongoing":
147
147
  return "primary";
148
148
  case "completed":
149
149
  case "accepted":
150
150
  return "success";
151
+ case "closed":
152
+ case "rejected":
153
+ return "error";
151
154
  default:
152
155
  return "secondary";
153
156
  }
154
157
  };
155
158
 
159
+ const getCloseInColor = (id: string): string => {
160
+ const secs = remainingSeconds.value[id];
161
+ if (secs === undefined) return "primary";
162
+ if (secs <= 0) return "error";
163
+ if (secs < 3 * 3600) return "warning";
164
+ return "primary";
165
+ };
166
+
156
167
  const {
157
168
  canDownloadSchedule,
158
169
  canViewSchedules,
@@ -205,11 +216,11 @@ const formatTime = (seconds: number) => {
205
216
  };
206
217
 
207
218
  const updateRemainingTime = () => {
208
- console.log(items.value);
209
219
  items.value.forEach((item) => {
210
220
  const itemId = item._id as string;
211
221
  const _time = calculateRemainingTime(item.date as string);
212
- remainingTime.value[itemId] = _time < 0 ? "00h 00m" : formatTime(_time);
222
+ remainingSeconds.value[itemId] = _time;
223
+ remainingTime.value[itemId] = _time <= 0 ? "00h 00m" : formatTime(_time);
213
224
  });
214
225
  };
215
226
 
@@ -63,7 +63,7 @@
63
63
  <p class="my-1 text-h6">
64
64
  {{
65
65
  toLocalDate(
66
- incidentInformation?.incidentTypeAndTime?.dateOfIncident,
66
+ incidentInformation?.incidentTypeAndTime?.dateOfIncident
67
67
  ) || "NA"
68
68
  }}
69
69
  </p>
@@ -137,9 +137,24 @@
137
137
  </p>
138
138
  </v-col>
139
139
  <v-col cols="12" sm="4" md="3">
140
- <InputLabel class="text-capitalize" title="NRIC/WP Number" />
140
+ <div>
141
+ <InputLabel class="text-capitalize mr-1" title="NRIC/WP Number" />
142
+ <v-icon
143
+ v-if="incidentInformation?.complaintInfo?.nric"
144
+ size="small"
145
+ color="blue"
146
+ class="cursor-pointer"
147
+ @click="showNRICComplainant = !showNRICComplainant"
148
+ >
149
+ {{ showNRICComplainant ? "mdi-eye-off" : "mdi-eye" }}
150
+ </v-icon>
151
+ </div>
141
152
  <p class="my-1 text-h6 text-capitalize">
142
- {{ incidentInformation?.complaintInfo?.nric || "NA" }}
153
+ {{
154
+ showNRICComplainant
155
+ ? incidentInformation?.complaintInfo?.nric || "NA"
156
+ : maskNRIC(incidentInformation?.complaintInfo?.nric)
157
+ }}
143
158
  </p>
144
159
  </v-col>
145
160
  <v-col cols="12" sm="4" md="3">
@@ -164,9 +179,24 @@
164
179
  </p>
165
180
  </v-col>
166
181
  <v-col cols="12" sm="4" md="3">
167
- <InputLabel class="text-capitalize" title="NRIC/WP Number" />
182
+ <div>
183
+ <InputLabel class="text-capitalize" title="NRIC/WP Number" />
184
+ <v-icon
185
+ v-if="incidentInformation?.recipientOfComplaint?.nric"
186
+ size="small"
187
+ color="blue"
188
+ class="cursor-pointer"
189
+ @click="showNRICRecipient = !showNRICRecipient"
190
+ >
191
+ {{ showNRICRecipient ? "mdi-eye-off" : "mdi-eye" }}
192
+ </v-icon>
193
+ </div>
168
194
  <p class="my-1 text-h6 text-capitalize">
169
- {{ incidentInformation?.recipientOfComplaint?.nric || "NA" }}
195
+ {{
196
+ showNRICRecipient
197
+ ? incidentInformation?.recipientOfComplaint?.nric || "NA"
198
+ : maskNRIC(incidentInformation?.recipientOfComplaint?.nric)
199
+ }}
170
200
  </p>
171
201
  </v-col>
172
202
  <v-col cols="12" sm="4" md="3">
@@ -234,7 +264,10 @@ const { getSiteById } = useSiteSettings();
234
264
  // state
235
265
  const siteName = ref("");
236
266
 
237
- const siteId = computed(() => props.incidentInformation?.siteInfo?.site );
267
+ const siteId = computed(() => props.incidentInformation?.siteInfo?.site);
268
+
269
+ const showNRICComplainant = ref(false);
270
+ const showNRICRecipient = ref(false);
238
271
 
239
272
  watch(
240
273
  siteId,
@@ -255,4 +288,10 @@ function toLocalDate(utcString: string) {
255
288
  day: "2-digit",
256
289
  });
257
290
  }
291
+
292
+ const maskNRIC = (value: any) => {
293
+ if (!value) return "NA";
294
+
295
+ return value.replace(/.(?=.{3})/g, "*");
296
+ };
258
297
  </script>
@@ -76,6 +76,26 @@
76
76
  style="max-height: calc(100vh - (200px))"
77
77
  class="border mt-5"
78
78
  >
79
+ <template #header.nric>
80
+ <div class="d-flex align-center">
81
+ NRIC
82
+ <v-icon
83
+ class="cursor-pointer ml-1"
84
+ size="18"
85
+ color="blue"
86
+ @click="isShowNRIC = !isShowNRIC"
87
+ >
88
+ {{ isShowNRIC ? "mdi-eye-off-outline" : "mdi-eye-outline" }}
89
+ </v-icon>
90
+ </div>
91
+ </template>
92
+ <template #item.nric="{ value }">
93
+ <tr>
94
+ {{
95
+ maskedNric(value)
96
+ }}
97
+ </tr>
98
+ </template>
79
99
  </v-data-table>
80
100
  </v-col>
81
101
 
@@ -142,6 +162,7 @@ const injuredTableHeader = [
142
162
  value: "contact",
143
163
  },
144
164
  ];
165
+
145
166
  const damagePropertyTableHeader = [
146
167
  {
147
168
  title: "Description",
@@ -164,4 +185,12 @@ const damagePropertyTableHeader = [
164
185
  value: "action",
165
186
  },
166
187
  ];
188
+
189
+ const isShowNRIC = ref(false);
190
+
191
+ const maskedNric = (nric: string) => {
192
+ if (!nric) return "";
193
+ if (isShowNRIC.value) return nric;
194
+ return nric[0] + "*".repeat(nric.length - 5) + nric.slice(-4);
195
+ };
167
196
  </script>
@@ -33,7 +33,7 @@
33
33
  <v-col cols="12">
34
34
  <v-row>
35
35
  <v-col cols="12">
36
- <InputLabel class="text-capitalize" title="NRIC/Passport/ID No." />
36
+ <InputLabel class="text-capitalize" title="NRIC" />
37
37
  <InputNRICNumber v-model.trim="form.nric" density="comfortable" />
38
38
  </v-col>
39
39
  </v-row>
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <div class="d-flex align-center ga-2 flex-wrap">
3
+ <span v-if="formatted">
4
+ {{ formatted }}
5
+ </span>
6
+
7
+ <v-chip
8
+ v-if="extraCount > 0"
9
+ density="comfortable"
10
+ size="small"
11
+ >
12
+ + {{ extraCount }}
13
+ </v-chip>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ const props = defineProps({
19
+ plateNumbers: {
20
+ type: Array as PropType<string[]>,
21
+ default: () => []
22
+ },
23
+ defaultValue: {
24
+ type: String,
25
+ default: ""
26
+ }
27
+ })
28
+
29
+ const formatted = computed(() => {
30
+ if (!props.plateNumbers?.length) return props.defaultValue || ""
31
+
32
+ const firstTwo = props.plateNumbers
33
+ .slice(0, 2)
34
+ .map((v: any) => v?.plateNumber || "")
35
+
36
+ return firstTwo.join(", ")
37
+ })
38
+
39
+ const extraCount = computed(() => {
40
+ if (!props.plateNumbers) return 0
41
+ return props.plateNumbers.length > 2
42
+ ? props.plateNumbers.length - 2
43
+ : 0
44
+ })
45
+ </script>