@7365admin1/layer-common 1.10.5 → 1.10.7

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 (44) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/AccessCardDetailsDialog.vue +144 -0
  3. package/components/AccessCardPreviewDialog.vue +7 -2
  4. package/components/AccessCardQrTagging.vue +314 -34
  5. package/components/AccessCardQrTaggingPrintQr.vue +75 -0
  6. package/components/AccessManagement.vue +5 -1
  7. package/components/AreaChecklistHistoryLogs.vue +9 -0
  8. package/components/BuildingForm.vue +36 -5
  9. package/components/BuildingManagement/buildings.vue +18 -9
  10. package/components/BuildingManagement/units.vue +12 -114
  11. package/components/BuildingUnitFormAdd.vue +39 -30
  12. package/components/BuildingUnitFormEdit.vue +265 -116
  13. package/components/CleaningScheduleMain.vue +60 -13
  14. package/components/Dialog/DeleteConfirmation.vue +2 -2
  15. package/components/Dialog/UpdateMoreAction.vue +2 -2
  16. package/components/EntryPassInformation.vue +215 -0
  17. package/components/Input/InputPhoneNumberV2.vue +11 -0
  18. package/components/ManageChecklistMain.vue +29 -3
  19. package/components/PlateNumberDisplay.vue +9 -1
  20. package/components/ScheduleAreaMain.vue +56 -0
  21. package/components/TableHygiene.vue +265 -113
  22. package/components/UnitPersonCard.vue +63 -0
  23. package/components/VehicleAddSelection.vue +2 -2
  24. package/components/VehicleForm.vue +169 -47
  25. package/components/VehicleManagement.vue +169 -43
  26. package/components/VisitorForm.vue +19 -0
  27. package/components/VisitorManagement.vue +1 -1
  28. package/components/WorkOrder/Main.vue +2 -1
  29. package/composables/useAccessManagement.ts +63 -0
  30. package/composables/useFeedback.ts +2 -2
  31. package/composables/usePeople.ts +14 -3
  32. package/composables/useVehicle.ts +15 -1
  33. package/composables/useWorkOrder.ts +2 -2
  34. package/package.json +3 -2
  35. package/types/cleaner-schedule.d.ts +1 -0
  36. package/types/html2pdf.d.ts +19 -0
  37. package/types/people.d.ts +4 -0
  38. package/types/site.d.ts +8 -0
  39. package/types/vehicle.d.ts +3 -4
  40. package/.playground/app.vue +0 -41
  41. package/.playground/eslint.config.mjs +0 -6
  42. package/.playground/nuxt.config.ts +0 -22
  43. package/.playground/pages/feedback.vue +0 -30
  44. package/components/AccessCardHistoryDialog.vue +0 -133
@@ -2,31 +2,31 @@
2
2
  <v-row no-gutters>
3
3
  <TableMain :headers="headers" :items="items" :loading="loading || getVehiclesPending" :page="page" :pages="pages"
4
4
  :pageRange="pageRange" :extension-height="70" :canCreate="canCreateVehicle" @refresh="getVehiclesRefresh"
5
- createLabel="Add Vehicle" show-header @row-click="handleRowClick" @create="dialog.showSelection = true"
5
+ createLabel="Add Vehicle" show-header @row-click="handleRowClick" @create="handleAddVehicleClick"
6
6
  @update:page="handleUpdatePage">
7
7
 
8
8
  <template #extension>
9
9
  <v-row no-gutters class="px-5 py-1 d-flex align-center justify-end ga-3">
10
10
  <v-text-field v-model="searchInput" density="compact" placeholder="Search" clearable max-width="300"
11
11
  append-inner-icon="mdi-magnify" hide-details />
12
- <v-select v-model="vehicleType" density="compact" item-title="label" item-value="value"
12
+ <v-select v-model="vehicleTypeFilter" density="compact" item-title="label" item-value="value"
13
13
  placeholder="Filter by Type" clearable max-width="200" hide-details :items="typeOptions" />
14
14
  </v-row>
15
15
  </template>
16
16
 
17
17
  <template #item.block="{ value }">
18
- {{ value ? `blk ${value}` : "" }}
18
+ {{ value ? `Blk ${value}` : "" }}
19
19
  </template>
20
- <template #item.status="{ value }">
20
+ <!-- <template #item.status="{ value }">
21
21
  <v-chip :color="formatVehicleStatus(value).color" size="x-small" dark>
22
22
  {{ formatVehicleStatus(value).label }}
23
23
  </v-chip>
24
24
 
25
25
 
26
- </template>
26
+ </template> -->
27
27
 
28
- <template #item.plateNumbers="{ value, item }">
29
- <PlateNumberDisplay :plate-numbers="value" :default-value="item.plateNumber" />
28
+ <template #item.plates="{ value, item }">
29
+ <PlateNumberDisplay :plate-numbers="value" :default-value="item.plateNumber" />
30
30
  </template>
31
31
  </TableMain>
32
32
 
@@ -37,33 +37,51 @@
37
37
  </v-dialog>
38
38
 
39
39
  <v-dialog v-model="dialog.createVehicle" v-if="vehicleType" width="450" persistent>
40
- <VehicleForm :type="vehicleType" mode="add" @back="handleBackToSelection" @done="handleAddVehicleComplete"
40
+ <VehicleForm :type="vehicleType" mode="add" :vehicle-data="selectedVehicleObject" @back="handleBackToSelection" @done="handleAddVehicleComplete"
41
41
  :org="org" :site="props.site" @close:all="handleCloseAll" />
42
42
  </v-dialog>
43
43
 
44
- <v-dialog v-model="dialog.updateVehicle" v-if="vehicleType" width="450" persistent>
44
+ <!-- <v-dialog v-model="dialog.updateVehicle" v-if="vehicleType" width="450" persistent>
45
45
  <VehicleForm :type="vehicleType" mode="edit" :vehicle-data="selectedVehicleObject"
46
46
  @back="dialog.updateVehicle = false" @close="dialog.updateVehicle = false" @done="handleUpdateVehicleComplete"
47
47
  :org="org" :site="site" @close:all="handleCloseAll" />
48
- </v-dialog>
48
+ </v-dialog> -->
49
49
 
50
- <v-dialog v-if="canViewVehicleDetails" v-model="dialog.showMoreActions" width="450" persistent>
51
- <DialogUpdateMoreAction title="Preview" :can-update="canUpdateVehicle" :can-delete="canDeleteVehicle"
52
- @close="dialog.showMoreActions = false" edit-button-label="Edit Vehicle" delete-button-label="Delete Vehicle"
53
- @delete="handleDeleteVehicleAction" @edit="handleEditVehicleAction">
50
+ <v-dialog v-if="canViewVehicleDetails" v-model="dialog.showMoreActions" width="600" persistent>
51
+ <DialogUpdateMoreAction title="Preview" :can-update="false" :can-delete="false"
52
+ @close="dialog.showMoreActions = false">
54
53
  <template v-slot:content>
55
54
  <v-row no-gutters class="ga-1 mb-5">
56
-
55
+
57
56
  <template v-for="(label, key) in formattedFields" :key="key">
58
- <v-col v-if="key === 'plateNumbers'" class="d-flex ga-2">
57
+ <v-col v-if="key === 'plates'" class="d-flex flex-column ga-2">
59
58
  <span class="d-flex ga-3 align-center"><strong>{{ label }}:</strong></span>
60
- <PlateNumberDisplay :plate-numbers="selectedVehicleObject[key]" :default-value="selectedVehicleObject.plateNumber" />
59
+ <!-- <PlateNumberDisplay :plate-numbers="selectedVehicleObject[key]" show-all :default-value="selectedVehicleObject.plateNumber" /> -->
60
+ <v-col cols="12">
61
+ <v-card flat outlined class="pa-2" border="sm black">
62
+ <v-data-table :items="selectedVehicleObject[key]" :headers="plateHeaders" hide-default-footer>
63
+ <template #item.status="{ value }">
64
+ <v-chip :color="formatVehicleStatus(value).color" size="x-small" dark>
65
+ {{ formatVehicleStatus(value).label }}
66
+ </v-chip>
67
+ </template>
68
+ <template #item.action="{ item }">
69
+ <v-btn v-if="canDeleteVehicle && (item as TPlateNumber)?.status == 'active'" text="Delete" color="error" flat size="x-small" @click="handleDeleteVehicleAction(item as TPlateNumber)"/>
70
+ <v-btn v-if="props.app === 'property_management_agency' && (item as TPlateNumber)?.status == 'pending'" text="Approve" color="success" flat size="x-small" @click="handleApproveVehicle(item as TPlateNumber)"/>
71
+ <v-btn v-if=" (item as TPlateNumber)?.status == 'deleted'" text="Restore" color="orange" flat size="x-small" @click="handleRestoreVehicle(item as TPlateNumber)"/>
72
+ </template>
73
+ </v-data-table>
74
+ <v-btn text="Add Vehicle Number" class="mt-6 text-capitalize" prepend-icon="mdi-plus"
75
+ @click="addNewPlateNumbers" color="primary" />
76
+ </v-card>
77
+ </v-col>å
78
+
61
79
  </v-col>
62
80
 
63
81
  <v-col v-else-if="selectedVehicleObject[key]" cols="12">
64
82
  <span class="d-flex ga-3 align-center"><strong>{{ label }}:</strong> {{ formatValues(key,
65
83
  selectedVehicleObject[key])
66
- }}</span>
84
+ }}</span>
67
85
  </v-col>
68
86
  </template>
69
87
  </v-row>
@@ -71,13 +89,22 @@
71
89
  </DialogUpdateMoreAction>
72
90
  </v-dialog>
73
91
  <v-dialog v-model="dialog.deleteVehicle" persistent width="540">
74
- <DialogDeleteConfirmation :message="message" prompt-title="Are you sure want to delete this vehicle?"
92
+ <DialogDeleteConfirmation :message="message" :loading="deletingVehicle" :prompt-title="`Are you sure want to delete this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
75
93
  @delete="submitDelete" @close="closeDeleteDialog" />
76
94
  </v-dialog>
95
+ <v-dialog v-model="dialog.approveVehicle" persistent width="540">
96
+ <DialogReusablePrompt :message="message" :loading="approvingVehicle" :prompt-title="`Are you sure want to approve this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
97
+ @approve="submitApprove" @close="dialog.approveVehicle = false" />
98
+ </v-dialog>
99
+ <v-dialog v-model="dialog.restoreVehicle" persistent width="540">
100
+ <DialogReusablePrompt :message="message" :loading="restoringVehicle" :prompt-title="`Are you sure want to restore this vehicle - ${selectedPlateNumberObject?.plateNumber}?`"
101
+ @approve="submitRestore" @close="dialog.restoreVehicle = false" />
102
+ </v-dialog>
77
103
  </v-row>
78
104
  </template>
79
105
 
80
106
  <script lang="ts" setup>
107
+ import useUtils from '../composables/useUtils';
81
108
  import useVehicle from '../composables/useVehicle';
82
109
 
83
110
  definePageMeta({
@@ -93,25 +120,33 @@ const props = defineProps({
93
120
  canViewVehicleDetails: { type: Boolean, default: true },
94
121
  site: { type: String, required: true },
95
122
  org: { type: String, required: true },
123
+ app: { type: String, required: true },
96
124
  });
97
125
 
98
126
 
99
127
  const headers = [
100
- { title: "Name", value: "name" },
101
- // { title: "Building", value: "buildingName" },
102
- { title: "Vehicle Numbers", value: "plateNumbers" },
103
- { title: "NRIC", value: "nric" },
104
- { title: "Block", value: "block" },
105
- { title: "Floor", value: "level" },
106
- { title: "Category", value: "category" },
107
- { title: "Type", value: "type" },
108
- { title: "Status", value: "status" },
109
- ]
128
+ { title: "Name", value: "name" },
129
+ { title: "Vehicle Numbers", value: "plates" },
130
+ { title: "NRIC", value: "nric" },
131
+ { title: "Block", value: "block" },
132
+ { title: "Floor", value: "level" },
133
+ { title: "Unit", value: "unitName" },
134
+ { title: "Category", value: "category" },
135
+ // { title: "Type", value: "type" },
136
+ // { title: "Status", value: "status" },
137
+ ]
138
+
139
+ const plateHeaders = [
140
+ { title: "Plate Number", value: "plateNumber" },
141
+ { title: "Type", value: "type" },
142
+ { title: "Status", value: "status" },
143
+ { title: "", value: "action" },
144
+ ]
110
145
 
111
146
 
112
147
 
113
148
  const { formatCamelCaseToWords, formatDate, debounce } = useUtils();
114
- const { getVehicles, deleteVehicle, formatVehicleStatus } = useVehicle();
149
+ const { getVehicles, deleteVehicle, formatVehicleStatus, approveVehicle } = useVehicle();
115
150
 
116
151
  const items = ref<Array<Record<string, any>>>([]);
117
152
  const page = ref(1);
@@ -121,8 +156,15 @@ const pageRange = ref("-- - -- of --");
121
156
  const searchInput = ref("")
122
157
 
123
158
  const loading = ref(false);
159
+ const deletingVehicle = ref(false);
160
+ const approvingVehicle = ref(false);
161
+ const restoringVehicle = ref(false);
162
+
124
163
  const selectedVehicleId = ref<string | null>(null)
125
164
  const vehicleType = ref<TVehicleType | null>(null);
165
+ const vehicleTypeFilter = ref<TVehicleType | null>(null);
166
+
167
+ const selectedPlateNumberObject = ref<TPlateNumber | null>(null);
126
168
 
127
169
  const message = ref("");
128
170
  const messageColor = ref("");
@@ -141,17 +183,18 @@ const dialog = reactive({
141
183
  showSelection: false,
142
184
  deleteVehicle: false,
143
185
  showMoreActions: false,
186
+ approveVehicle: false,
187
+ restoreVehicle: false
144
188
  });
145
189
 
190
+
146
191
  const typeOptions = [
147
192
  { label: "Whitelist", value: "whitelist" },
148
193
  { label: "Blocklist", value: "blocklist" },
149
- { label: "Season Pass", value: "seasonPass" },
150
194
  ]
151
195
 
152
196
  const formattedFields: Partial<Record<keyof TVehicle, string>> = {
153
197
  name: "Name",
154
- plateNumbers: "Vehicle Numbers",
155
198
  phoneNumber: "Phone Number",
156
199
  nric: "NRIC",
157
200
  block: "Block",
@@ -162,6 +205,7 @@ const formattedFields: Partial<Record<keyof TVehicle, string>> = {
162
205
  category: "Category",
163
206
  type: "Type",
164
207
  remarks: "Remarks",
208
+ plates: "Vehicle Numbers",
165
209
  }
166
210
 
167
211
  function formatValues(key: string, value: any) {
@@ -201,7 +245,7 @@ const { data: getVehiclesReq, refresh: getVehiclesRefresh, pending: getVehiclesP
201
245
  getVehicles({
202
246
  page: page.value,
203
247
  search: searchInput.value,
204
- type: vehicleType.value ?? "",
248
+ type: vehicleTypeFilter.value ?? "",
205
249
  }),
206
250
  {
207
251
  watch: [page],
@@ -210,9 +254,9 @@ const { data: getVehiclesReq, refresh: getVehiclesRefresh, pending: getVehiclesP
210
254
 
211
255
 
212
256
  watch(getVehiclesReq, (newData: any) => {
213
- items.value = newData?.items || [];
214
- pages.value = newData?.pages || 0;
215
- pageRange.value = newData?.pageRange || "-- - -- of --";
257
+ items.value = newData?.items || [];
258
+ pages.value = newData?.pages || 0;
259
+ pageRange.value = newData?.pageRange || "-- - -- of --";
216
260
  })
217
261
 
218
262
  function handleRowClick(data: any) {
@@ -221,17 +265,33 @@ function handleRowClick(data: any) {
221
265
  message.value = "";
222
266
  }
223
267
 
268
+ function addNewPlateNumbers() {
269
+ if (!selectedVehicleId.value) return;
270
+ dialog.showSelection = true;
271
+ }
272
+
224
273
  function handleEditVehicleAction() {
225
274
  vehicleType.value = selectedVehicleObject.value?.type || null;
275
+ console.log("Selected Vehicle Object: ", selectedVehicleObject.value);
226
276
  dialog.showSelection = false;
277
+ dialog.updateVehicle = false;
227
278
  dialog.updateVehicle = true;
228
279
  }
229
280
 
230
- function handleDeleteVehicleAction() {
231
- dialog.showMoreActions = false;
281
+ function handleDeleteVehicleAction(item: TPlateNumber) {
282
+ selectedPlateNumberObject.value = item || null;
232
283
  dialog.deleteVehicle = true;
233
284
  }
234
285
 
286
+ function handleApproveVehicle(item: TPlateNumber) {
287
+ selectedPlateNumberObject.value = item || null;
288
+ dialog.approveVehicle = true;
289
+ }
290
+ function handleRestoreVehicle(item: TPlateNumber) {
291
+ selectedPlateNumberObject.value = item || null;
292
+ dialog.restoreVehicle = true;
293
+ }
294
+
235
295
  function handleSelectVehicleStatus(value: TVehicleType) {
236
296
  vehicleType.value = value;
237
297
  dialog.showSelection = false;
@@ -259,10 +319,16 @@ function handleUpdateVehicleComplete() {
259
319
  getVehiclesRefresh();
260
320
  }
261
321
 
322
+
262
323
  function handleUpdatePage(newPageNum: number) {
263
324
  page.value = newPageNum;
264
325
  }
265
326
 
327
+ function handleAddVehicleClick() {
328
+ dialog.showSelection = true;
329
+ selectedVehicleId.value = null;
330
+ }
331
+
266
332
  const formatPlateNumbers = (value: any) => {
267
333
  if (!value || value.length === 0) return "";
268
334
 
@@ -271,25 +337,85 @@ const formatPlateNumbers = (value: any) => {
271
337
  };
272
338
 
273
339
  async function submitDelete() {
274
- bypass.value = false;
340
+
341
+ const plateNumberId = selectedPlateNumberObject.value?._id;
342
+ const type = selectedPlateNumberObject.value?.type as TVehicleType;
343
+ if (!plateNumberId) {
344
+ showMessage("Invalid plate number selected for deletion.", "error");
345
+ return;
346
+ }
275
347
 
276
348
  try {
277
- const res = await deleteVehicle({ site: props.site, id: selectedVehicleId.value as string, recno: selectedVehicleObject.value.recno, type: selectedVehicleObject.value.type }
349
+ deletingVehicle.value = true;
350
+ const res = await deleteVehicle({ site: props.site, id: plateNumberId as string, recno: selectedVehicleObject.value.recno, type }
278
351
  );
279
352
  dialog.deleteVehicle = false;
280
- dialog.showMoreActions = false;
281
353
  selectedVehicleId.value = null
282
354
  showMessage(res.message, "success");
283
355
  getVehiclesRefresh();
284
356
  } catch (error: any) {
285
357
  console.error("Error deleting vehicle:", error);
286
358
  message.value = error.response._data.message;
287
- bypass.value = true;
359
+ } finally {
360
+ deletingVehicle.value = false;
361
+ }
362
+ }
363
+
364
+
365
+
366
+ async function submitRestore() {
367
+
368
+ const plateNumberId = selectedPlateNumberObject.value?._id;
369
+ if (!plateNumberId) {
370
+ showMessage("Invalid plate number selected for restoration.", "error");
371
+ return;
372
+ }
373
+
374
+ try {
375
+ restoringVehicle.value = true;
376
+ // reactivate and restore will use the same endpoint, just with different payload
377
+ const res = await approveVehicle({ site: props.site, org: props.org, id: plateNumberId as string }
378
+ );
379
+ dialog.restoreVehicle = false;
380
+ selectedVehicleId.value = null
381
+ showMessage(res.message, "success");
382
+ getVehiclesRefresh();
383
+ } catch (error: any) {
384
+ console.error("Error restoring vehicle:", error);
385
+ message.value = error.response._data.message;
386
+ } finally {
387
+ restoringVehicle.value = false;
388
+ }
389
+ }
390
+
391
+
392
+ async function submitApprove() {
393
+
394
+ const plateNumberId = selectedPlateNumberObject.value?._id;
395
+ if (!plateNumberId) {
396
+ showMessage("Invalid plate number selected for approval.", "error");
397
+ return;
398
+ }
399
+
400
+ try {
401
+ approvingVehicle.value = true;
402
+ const res = await approveVehicle({ site: props.site, org: props.org, id: plateNumberId as string }
403
+
404
+ );
405
+ dialog.approveVehicle = false;
406
+ selectedVehicleId.value = null
407
+ showMessage(res.message, "success");
408
+ getVehiclesRefresh();
409
+ } catch (error: any) {
410
+ console.error("Error approving vehicle:", error);
411
+ message.value = error.response._data.message;
412
+ } finally {
413
+ approvingVehicle.value = false;
288
414
  }
289
415
  }
290
416
 
291
417
  const debouncedSearch = debounce(getVehiclesRefresh, 500);
292
- watch([searchInput, vehicleType], () => {
418
+ watch([searchInput, vehicleTypeFilter], () => {
293
419
  page.value = 1;
294
420
  debouncedSearch()
295
421
  })
@@ -146,6 +146,7 @@
146
146
 
147
147
  <v-col v-if="prop.type === 'contractor' && contractorStep === 2" cols="12">
148
148
  <PassInformation />
149
+ <EntryPassInformation v-if="entryPassSettings?.data?.settings?.nfcPass" v-model="passType" v-model:quantity="passQuantity" v-model:cards="passCards" :settings="entryPassSettings" :loading="entryPassSettingsPending" @scan:barcode="$emit('scan:barcode')" @scan:camera="$emit('scan:camera')" />
149
150
  </v-col>
150
151
 
151
152
  <v-col v-if="prop.type === 'contractor' && contractorStep === 3" cols="12">
@@ -230,6 +231,7 @@ const currentAutofillSource = ref<AutofillSource>(null);
230
231
  const { requiredRule, debounce } = useUtils();
231
232
  const { getSiteById, getSiteLevels, getSiteUnits } = useSiteSettings();
232
233
  const { createVisitor, typeFieldMap, contractorTypes } = useVisitor();
234
+ const { getBySiteId: getEntryPassSettingsBySiteId } = useSiteEntryPassSettings();
233
235
  const { findPersonByNRIC, findPersonByContact, searchCompanyList, findUsersByPlateNumber } = usePeople()
234
236
 
235
237
  const emit = defineEmits([
@@ -259,6 +261,10 @@ const visitor = reactive<Partial<TVisitorPayload>>({
259
261
  members: [],
260
262
  });
261
263
 
264
+ const passType = ref("");
265
+ const passQuantity = ref<number | null>(null);
266
+ const passCards = ref<string[]>([]);
267
+
262
268
  const dialog = reactive({
263
269
  vehicleNumberUsersList: false,
264
270
  });
@@ -504,6 +510,19 @@ function handleAutofillDataViaVehicleNumber(item: TPeople){
504
510
  }
505
511
 
506
512
 
513
+ const {
514
+ data: entryPassSettings,
515
+ pending: entryPassSettingsPending,
516
+ refresh: refreshEntryPassSettings,
517
+ } = useLazyAsyncData(`fetch-entrypass-settings-${prop.site}`, () => {
518
+ if (contractorStep.value !== 2) return Promise.resolve(null);
519
+ return getEntryPassSettingsBySiteId(prop.site);
520
+ });
521
+
522
+ watch(contractorStep, (step) => {
523
+ if (step === 2) refreshEntryPassSettings();
524
+ });
525
+
507
526
  const {
508
527
  data: siteData,
509
528
  refresh: refreshSiteData,
@@ -108,7 +108,7 @@
108
108
  </template>
109
109
  </TableMain>
110
110
 
111
- <v-dialog v-model="dialog.showSelection" width="450" persistent>
111
+ <v-dialog v-model="dialog.showSelection" width="450" persistent>
112
112
  <VisitorFormSelection @cancel="dialog.showSelection = false" @select="handleSelectVisitorType" />
113
113
  </v-dialog>
114
114
 
@@ -437,7 +437,8 @@ async function handleFileAdded(file: File) {
437
437
  const res = await addFile(file);
438
438
  const uploadedId = res?.id;
439
439
  if (uploadedId) {
440
- const url = `${API_DO_STORAGE_ENDPOINT}/${uploadedId}`;
440
+ // const url = `${API_DO_STORAGE_ENDPOINT}/${uploadedId}`;
441
+ const url = `${uploadedId}`;
441
442
  _workOrder.value.attachments = _workOrder.value.attachments ?? [];
442
443
  _workOrder.value.attachments.push(url);
443
444
  }
@@ -162,6 +162,65 @@ export default function useAccessManagement() {
162
162
  );
163
163
  }
164
164
 
165
+ function getCardDetails(params: { siteId: string; cardId: string }) {
166
+ return useNuxtApp().$api<{ message: string; data: Record<string, any> }>(
167
+ `/api/access-management/card-details`,
168
+ {
169
+ method: "GET",
170
+ query: { siteId: params.siteId, cardId: params.cardId },
171
+ }
172
+ );
173
+ }
174
+
175
+ function saveVisitorAccessCardQrTag(
176
+ siteId: string,
177
+ payloads: Array<{
178
+ _id: string;
179
+ cardNo: string;
180
+ qrTag: string;
181
+ qrTagCardNo: string;
182
+ }>
183
+ ) {
184
+ return useNuxtApp().$api<Record<string, any>>(
185
+ `/api/access-management/qr-tag/${siteId}`,
186
+ {
187
+ method: "POST",
188
+ body: payloads,
189
+ }
190
+ );
191
+ }
192
+
193
+ function getAllVisitorAccessCardsQrTags(site: string) {
194
+ return useNuxtApp().$api<Record<string, any>>(
195
+ `/api/access-management/qr-tag`,
196
+ {
197
+ method: "GET",
198
+ query: { site },
199
+ }
200
+ );
201
+ }
202
+
203
+ function getVisitorAccessCards(params: {
204
+ page?: number;
205
+ limit?: number;
206
+ site?: string;
207
+ search?: string;
208
+ } = {}) {
209
+ return useNuxtApp().$api<Record<string, any>>(
210
+ `/api/access-management/visitor-access-cards`,
211
+ {
212
+ method: "GET",
213
+ query: {
214
+ type: "Visitor/Resident",
215
+ ...(params.page ? { page: params.page } : {}),
216
+ ...(params.limit ? { limit: params.limit } : {}),
217
+ ...(params.site ? { site: params.site } : {}),
218
+ ...(params.search ? { search: params.search } : {}),
219
+ },
220
+ }
221
+ );
222
+ }
223
+
165
224
  return {
166
225
  getDoorAccessLevels,
167
226
  getLiftAccessLevels,
@@ -175,5 +234,9 @@ export default function useAccessManagement() {
175
234
  getAvailableAccessCards,
176
235
  deleteCard,
177
236
  getCardHistory,
237
+ getCardDetails,
238
+ getVisitorAccessCards,
239
+ saveVisitorAccessCardQrTag,
240
+ getAllVisitorAccessCardsQrTags,
178
241
  };
179
242
  }
@@ -75,9 +75,9 @@ export default function useFeedback() {
75
75
  }
76
76
 
77
77
  function deleteFeedback(id: string) {
78
- return useNuxtApp().$api<Record<string, any>>(`/api/feedbacks/deleted/feedback`, {
78
+ return useNuxtApp().$api<Record<string, any>>(`/api/feedbacks/deleted/feedback/${id}`, {
79
79
  method: "PUT",
80
- query: { id },
80
+ // query: { id },
81
81
  });
82
82
  }
83
83
 
@@ -10,10 +10,11 @@ export default function () {
10
10
  dateFrom = "",
11
11
  type = "",
12
12
  displayNoCheckOut = false,
13
+ status = "",
13
14
  } = {}) {
14
15
  return await useNuxtApp().$api<Record<string, any>>("/api/people", {
15
16
  method: "GET",
16
- query: { page, limit, sort, search, org, site, dateTo, dateFrom, type },
17
+ query: { page, limit, sort, search, org, site, dateTo, dateFrom, type, status },
17
18
  });
18
19
  }
19
20
 
@@ -25,6 +26,15 @@ export default function () {
25
26
  });
26
27
  }
27
28
 
29
+ async function findPersonByNRICMultipleResult(
30
+ nric: string, site: string
31
+ ){
32
+ return await $fetch<Record<any, any>>(`/api/people/all-nric`, {
33
+ method: "GET",
34
+ query: { nric, site }
35
+ });
36
+ }
37
+
28
38
  async function findPersonByContact(
29
39
  contact: string
30
40
  ): Promise<null | Partial<TPeople>> {
@@ -78,13 +88,13 @@ export default function () {
78
88
 
79
89
  async function getPeopleByUnit(
80
90
  _id: string,
81
- { status = "active", type = "resident" } = {}
91
+ { status = "active", type = "" } = {}
82
92
  ) {
83
93
  return await useNuxtApp().$api<Record<string, any>>(
84
94
  `/api/people/unit/${_id}`,
85
95
  {
86
96
  method: "GET",
87
- query: { status, type },
97
+ query: { status },
88
98
  }
89
99
  );
90
100
  }
@@ -95,6 +105,7 @@ export default function () {
95
105
  updateById,
96
106
  deleteById,
97
107
  findPersonByNRIC,
108
+ findPersonByNRICMultipleResult,
98
109
  findPersonByContact,
99
110
  getPeopleByUnit,
100
111
  searchCompanyList,
@@ -64,6 +64,19 @@ export default function useVehicle() {
64
64
  );
65
65
  }
66
66
 
67
+ async function approveVehicle({ site, org, id }: { site: string, org: string, id: string}){
68
+ return await useNuxtApp().$api<Record<string, any>>(
69
+ `/api/vehicles/approve/ ${id}`,
70
+ {
71
+ method: "PUT",
72
+ body: {
73
+ site,
74
+ org,
75
+ },
76
+ }
77
+ );
78
+ }
79
+
67
80
  async function getVehicleByNRIC(search: string) {
68
81
  return await useNuxtApp().$api<Record<string, any>>(
69
82
  `/api/vehicles/nric`,
@@ -109,6 +122,7 @@ export default function useVehicle() {
109
122
  updateVehicle,
110
123
  deleteVehicle,
111
124
  getVehicleByNRIC,
112
- formatVehicleStatus
125
+ formatVehicleStatus,
126
+ approveVehicle
113
127
  };
114
128
  }
@@ -72,9 +72,9 @@ export default function useWorkOrder() {
72
72
  }
73
73
 
74
74
  function deleteWorkOrder(id: string) {
75
- return useNuxtApp().$api<Record<string, any>>(`/api/work-orders/deleted/work-order`, {
75
+ return useNuxtApp().$api<Record<string, any>>(`/api/work-orders/deleted/work-order/${id}`, {
76
76
  method: "PUT",
77
- query: { id },
77
+ // query: { id },
78
78
  });
79
79
  }
80
80
 
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.10.5",
5
+ "version": "1.10.7",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
@@ -30,11 +30,12 @@
30
30
  "dependencies": {
31
31
  "@ckeditor/ckeditor5-vue": "^7.3.0",
32
32
  "@iconify/vue": "^5.0.0",
33
- "qrcode.vue": "^3.4.1",
34
33
  "@mdi/font": "^7.4.47",
35
34
  "@types/qrcode": "^1.5.6",
36
35
  "ckeditor5": "^47.2.0",
36
+ "html2pdf.js": "^0.10.2",
37
37
  "qrcode": "^1.5.4",
38
+ "qrcode.vue": "^3.4.1",
38
39
  "sass": "^1.80.6",
39
40
  "vue3-signature": "^0.2.4",
40
41
  "zod": "^3.24.2"
@@ -44,6 +44,7 @@ declare type TChecklistSet = {
44
44
  remarks?: string;
45
45
  attachments?: string[];
46
46
  completedByName?: string;
47
+ isScheduleTask?: boolean;
47
48
  };
48
49
 
49
50
  declare type TFlattenedUnitItem = TUnitChecklistItem & {