@7365admin1/layer-common 1.11.18 → 1.11.19

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,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.11.19
4
+
5
+ ### Patch Changes
6
+
7
+ - e340731: Update Layer-common
8
+
3
9
  ## 1.11.18
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,207 @@
1
+ <template>
2
+ <v-card width="100%" :loading="processing">
3
+ <v-toolbar>
4
+ <v-row no-gutters class="fill-height px-6 d-flex justify-space-between align-center" align="center">
5
+ <span class="font-weight-bold text-subtitle-1">Assign Pass & Keys</span>
6
+ <ButtonClose @click="emit('close')" />
7
+ </v-row>
8
+ </v-toolbar>
9
+
10
+ <v-card-text>
11
+ <v-row no-gutters class="ga-1">
12
+ <v-col cols="12">
13
+ <span class="text-subtitle-2 text-medium-emphasis">
14
+ Name: {{ prop.visitor.name }}
15
+ </span>
16
+ </v-col>
17
+
18
+ <v-col cols="12" class="mt-3">
19
+ <InputLabel title="Pass" />
20
+ <v-autocomplete
21
+ v-model="selectedPass"
22
+ v-model:search="passInput"
23
+ :hide-no-data="false"
24
+ :items="passItems"
25
+ item-title="prefixAndName"
26
+ item-value="_id"
27
+ variant="outlined"
28
+ hide-details
29
+ density="compact"
30
+ small-chips
31
+ :loading="fetchPassesPending"
32
+ >
33
+ <template v-slot:chip="{ props: chipProps, item }">
34
+ <v-chip v-if="selectedPass" v-bind="chipProps" prepend-icon="mdi-card-bulleted-outline"
35
+ :text="item.raw?.prefixAndName" />
36
+ </template>
37
+ <template v-slot:no-data>
38
+ <v-list-item density="compact">
39
+ <v-list-item-title>No available passes</v-list-item-title>
40
+ </v-list-item>
41
+ </template>
42
+ </v-autocomplete>
43
+ </v-col>
44
+
45
+ <v-col v-if="showKeys" cols="12" class="mt-3">
46
+ <InputLabel title="Keys" />
47
+ <v-autocomplete
48
+ v-model="selectedKeys"
49
+ v-model:search="keyInput"
50
+ :hide-no-data="false"
51
+ :items="keyItems"
52
+ item-title="prefixAndName"
53
+ item-value="_id"
54
+ multiple
55
+ variant="outlined"
56
+ hide-details
57
+ density="compact"
58
+ small-chips
59
+ :loading="fetchKeysPending"
60
+ >
61
+ <template v-slot:chip="{ props: chipProps, item }">
62
+ <v-chip v-if="selectedKeys.length > 0" v-bind="chipProps" prepend-icon="mdi-key"
63
+ :text="item.raw?.prefixAndName" />
64
+ </template>
65
+ <template v-slot:no-data>
66
+ <v-list-item density="compact">
67
+ <v-list-item-title>No available keys</v-list-item-title>
68
+ </v-list-item>
69
+ </template>
70
+ </v-autocomplete>
71
+ </v-col>
72
+ </v-row>
73
+
74
+ <v-row v-if="errorMessage" no-gutters class="mt-2">
75
+ <p class="text-error text-subtitle-2 w-100 text-center">{{ errorMessage }}</p>
76
+ </v-row>
77
+ </v-card-text>
78
+
79
+ <v-toolbar density="compact">
80
+ <v-row no-gutters>
81
+ <v-col cols="6">
82
+ <v-btn tile block variant="text" class="text-none" size="48" text="Cancel" @click="emit('close')" />
83
+ </v-col>
84
+ <v-col cols="6">
85
+ <v-btn
86
+ tile block variant="flat" color="black" class="text-none" size="48"
87
+ text="Assign"
88
+ :disabled="!selectedPass && selectedKeys.length === 0"
89
+ :loading="processing"
90
+ @click="handleSubmit"
91
+ />
92
+ </v-col>
93
+ </v-row>
94
+ </v-toolbar>
95
+ </v-card>
96
+ </template>
97
+
98
+ <script setup lang="ts">
99
+ import type { PropType } from 'vue'
100
+ import usePassKey from '../composables/usePassKey'
101
+ import useVisitor from '../composables/useVisitor'
102
+
103
+ const prop = defineProps({
104
+ visitor: {
105
+ type: Object as PropType<TVisitor>,
106
+ required: true,
107
+ },
108
+ site: {
109
+ type: String,
110
+ required: true,
111
+ },
112
+ type: {
113
+ type: String as PropType<TVisitorType>,
114
+ required: true,
115
+ },
116
+ contractorType: {
117
+ type: String,
118
+ default: '',
119
+ },
120
+ })
121
+
122
+ const emit = defineEmits<{
123
+ (e: 'done'): void
124
+ (e: 'close'): void
125
+ }>()
126
+
127
+ const { getPassKeysByPageSearch } = usePassKey()
128
+ const { updateVisitor } = useVisitor()
129
+
130
+ const processing = ref(false)
131
+ const errorMessage = ref('')
132
+
133
+ const selectedPass = ref<string>('')
134
+ const selectedKeys = ref<string[]>([])
135
+ const passInput = ref('')
136
+ const keyInput = ref('')
137
+ const passItems = ref<TPassKey[]>([])
138
+ const keyItems = ref<TPassKey[]>([])
139
+
140
+ const showKeys = computed(() => prop.visitor.type === 'contractor')
141
+
142
+ const passTypesComputed = computed(() => {
143
+ if (prop.type === 'contractor') {
144
+ return prop.contractorType === 'property-agent' ? ['agent-pass'] : ['contractor-pass']
145
+ }
146
+ return ['visitor-pass']
147
+ })
148
+
149
+ const { data: passesData, pending: fetchPassesPending } = await useLazyAsyncData(
150
+ `add-pass-key-visitor-passes-${prop.visitor._id}`,
151
+ () => getPassKeysByPageSearch({
152
+ search: passInput.value,
153
+ page: 1,
154
+ limit: 500,
155
+ passTypes: passTypesComputed.value,
156
+ sites: [prop.site],
157
+ statuses: ['Available'],
158
+ })
159
+ )
160
+
161
+ const { data: keysData, pending: fetchKeysPending } = await useLazyAsyncData(
162
+ `add-pass-key-visitor-keys-${prop.visitor._id}`,
163
+ () => getPassKeysByPageSearch({
164
+ search: keyInput.value,
165
+ page: 1,
166
+ limit: 500,
167
+ passTypes: ['pass-key'],
168
+ sites: [prop.site],
169
+ statuses: ['Available'],
170
+ })
171
+ )
172
+
173
+ watch(passesData, (data: any) => {
174
+ passItems.value = Array.isArray(data?.items) ? data.items : []
175
+ })
176
+
177
+ watch(keysData, (data: any) => {
178
+ keyItems.value = Array.isArray(data?.items) ? data.items : []
179
+ })
180
+
181
+ async function handleSubmit() {
182
+ if (!prop.visitor._id) return
183
+ errorMessage.value = ''
184
+ processing.value = true
185
+
186
+ try {
187
+ const payload: Partial<TVisitorPayload> = {}
188
+
189
+ if (selectedPass.value) {
190
+ payload.visitorPass = [{ keyId: selectedPass.value, status: "In Use" }]
191
+ }
192
+
193
+ if (selectedKeys.value.length > 0) {
194
+ payload.passKeys = selectedKeys.value.map(keyId => ({ keyId, status: "In Use" }))
195
+ }
196
+
197
+ await updateVisitor(prop.visitor._id, payload)
198
+ emit('done')
199
+ } catch (error: any) {
200
+ errorMessage.value = error?.data?.message || 'Failed to assign pass & keys. Please try again.'
201
+ } finally {
202
+ processing.value = false
203
+ }
204
+ }
205
+ </script>
206
+
207
+ <style scoped></style>
@@ -227,6 +227,17 @@
227
227
 
228
228
  </v-row>
229
229
  </v-col>
230
+
231
+ <v-col cols="12" class="mt-2">
232
+ <InputLabel class="text-capitalize d-block mb-1" title="Unit Documents" />
233
+ <InputFileV2
234
+ v-model="buildingUnit.buildingUnitFiles"
235
+ :multiple="true"
236
+ accept="*/*"
237
+ title="Upload documents"
238
+ :height="104"
239
+ />
240
+ </v-col>
230
241
  </v-row>
231
242
  </template>
232
243
  </v-card-text>
@@ -331,6 +342,35 @@ const buildingUnit = ref({
331
342
 
332
343
  buildingUnit.value = JSON.parse(JSON.stringify(prop.roomFacility));
333
344
 
345
+ // Normalize buildingUnitFiles: extract IDs for InputFileV2, keep names in a separate map
346
+ const buildingUnitFilesNames = ref<Record<string, string>>({});
347
+ const rawFiles = buildingUnit.value.buildingUnitFiles as any[];
348
+ if (rawFiles?.length && typeof rawFiles[0] === 'object') {
349
+ rawFiles.forEach((f: { id: string; name: string }) => {
350
+ buildingUnitFilesNames.value[f.id] = f.name ?? "";
351
+ });
352
+ buildingUnit.value.buildingUnitFiles = rawFiles.map((f: { id: string }) => f.id);
353
+ }
354
+
355
+ const { getFileById } = useFile();
356
+
357
+ watch(
358
+ () => buildingUnit.value.buildingUnitFiles as string[],
359
+ async (ids) => {
360
+ for (const id of ids) {
361
+ if (!buildingUnitFilesNames.value[id]) {
362
+ try {
363
+ const meta = await getFileById(id) as any;
364
+ buildingUnitFilesNames.value[id] = meta?.data?.name ?? meta?.name ?? "";
365
+ } catch {
366
+ buildingUnitFilesNames.value[id] = "";
367
+ }
368
+ }
369
+ }
370
+ },
371
+ { deep: true }
372
+ );
373
+
334
374
  const emit = defineEmits(["cancel", "success", "success:create-more", "delete-unit"]);
335
375
 
336
376
 
@@ -462,7 +502,7 @@ async function submit() {
462
502
  name: buildingUnit.value.name,
463
503
  level: buildingUnit.value.level,
464
504
  // category: buildingUnit.value.category,
465
- buildingUnitFiles: buildingUnit.value.buildingUnitFiles || [],
505
+ buildingUnitFiles: (buildingUnit.value.buildingUnitFiles as string[] || []).map((id) => ({ id, name: buildingUnitFilesNames.value[id] ?? "" })),
466
506
  companyName: buildingUnit.value.companyName,
467
507
  companyRegistrationNumber: buildingUnit.value.companyRegistrationNumber || "",
468
508
  leaseStart: buildingUnit.value.leaseStart,
@@ -77,7 +77,7 @@ const props = defineProps({
77
77
  }
78
78
  })
79
79
 
80
- const { addFile, deleteFile, getFileUrl, urlToFile } = useFile()
80
+ const { addFile, deleteFile, getFileUrl, getFileById, urlToFile } = useFile()
81
81
 
82
82
  const showImageCarousel = ref(false)
83
83
  const activeImageId = ref("")
@@ -187,8 +187,9 @@ async function downloadFile(id: string, filename: string) {
187
187
  const result: { file: File; id: string }[] = []
188
188
  for (const id of ids) {
189
189
  try {
190
- const url = await getFileUrl(id)
191
- const name = decodeURIComponent(url.split('/').pop() || `file_${id}`)
190
+ const meta = await getFileById(id) as any
191
+ const name: string = meta?.data?.name || meta?.name || `file_${id}`
192
+ const url = getFileUrl(id)
192
193
  const file = await urlToFile(url, name)
193
194
  result.push({ file, id })
194
195
  } catch (err) {
@@ -19,7 +19,7 @@
19
19
  </v-col>
20
20
 
21
21
  <v-col cols="12">
22
- <v-autocomplete v-model="memberForm.visitorPass" v-model:search="passInput"
22
+ <v-autocomplete v-model="selectedPass" v-model:search="passInput"
23
23
  :hide-no-data="false" class="mt-3" :items="passItemsFilteredFinal"
24
24
  item-title="prefixAndName" item-value="_id" label="Pass (optional)" variant="outlined"
25
25
  hide-details density="compact" persistent-hint small-chips>
@@ -35,7 +35,7 @@
35
35
  </template>
36
36
 
37
37
  <template v-slot:chip="{ props, item }">
38
- <v-chip v-if="memberForm.visitorPass" v-bind="props"
38
+ <v-chip v-if="selectedPass" v-bind="props"
39
39
  prepend-icon="mdi-card-bulleted-outline"
40
40
  :text="item.raw?.prefixAndName"></v-chip>
41
41
  </template>
@@ -116,6 +116,8 @@ const errorMessage = ref('')
116
116
  const passInput = ref('')
117
117
  const passItems = ref<TPassKey[]>([])
118
118
 
119
+ const selectedPass = ref<string>('')
120
+
119
121
  const members = defineModel<TMemberInfo[]>({ required: true, default: [] })
120
122
  const committedMembers = ref<TMemberInfo[]>([])
121
123
 
@@ -171,6 +173,10 @@ const membersDisplayed = computed(() => {
171
173
  })
172
174
  })
173
175
 
176
+ watch(selectedPass, (newVal) => {
177
+ memberForm.visitorPass = [{ keyId: newVal }]
178
+ })
179
+
174
180
 
175
181
 
176
182
  function handleClearForm() {
@@ -43,7 +43,8 @@
43
43
  </span>
44
44
  <span class="text-capitalize">{{ item?.name }}</span>
45
45
  </span>
46
- <span class="text-grey text-caption" v-if="item?.members?.length > 0">( +{{ item?.members?.length }} members)</span>
46
+ <span class="text-grey text-caption" v-if="item?.members?.length > 0">( +{{ item?.members?.length }}
47
+ members)</span>
47
48
  </template>
48
49
 
49
50
  <template v-slot:item.type-company="{ item }">
@@ -51,7 +52,7 @@
51
52
  <v-icon icon="mdi-user" size="15" />
52
53
  <span v-if="item.type === 'contractor'" class="text-capitalize">{{
53
54
  formatCamelCaseToWords(item.contractorType)
54
- }}</span>
55
+ }}</span>
55
56
  <span v-else class="text-capitalize">{{ formatType(item) }}</span>
56
57
  </span>
57
58
  <span class="d-flex align-center ga-2">
@@ -90,7 +91,7 @@
90
91
  <v-icon icon="mdi-clock-time-four-outline" color="green" size="20" />
91
92
  <span class="text-capitalize">{{
92
93
  UTCToLocalTIme(item.checkIn) || "-"
93
- }}</span>
94
+ }}</span>
94
95
  <span>
95
96
  <v-icon v-if="item?.snapshotEntryImage" size="17" icon="mdi-image"
96
97
  @click.stop="handleViewImage(item.snapshotEntryImage)" />
@@ -101,7 +102,7 @@
101
102
  <template v-if="item.checkOut">
102
103
  <span class="text-capitalize">{{
103
104
  UTCToLocalTIme(item.checkOut) || "-"
104
- }}</span>
105
+ }}</span>
105
106
  <span>
106
107
  <v-icon v-if="item?.snapshotExitImage" size="17" icon="mdi-image"
107
108
  @click.stop="handleViewImage(item.snapshotExitImage)" />
@@ -127,6 +128,13 @@
127
128
  {{ (key as any)?.prefixAndName }}
128
129
  </v-chip>
129
130
  </div>
131
+ <div v-if="showAddPassKeyButton(item)" class="d-flex flex-wrap ga-1 mt-1 mt-2"
132
+ @click.stop="handleCheckout(item._id)">
133
+ <v-chip size="x-small" variant="tonal" prepend-icon="mdi-key" @click.stop="handleOpenAddPassKey(item)">
134
+ Add Pass/Key
135
+ </v-chip>
136
+
137
+ </div>
130
138
  </v-row>
131
139
  </template>
132
140
 
@@ -180,7 +188,7 @@
180
188
  </span>
181
189
 
182
190
  <span v-else-if="selectedVisitorObject[key]" class="d-flex ga-3 align-center"><strong>{{ label
183
- }}:</strong>
191
+ }}:</strong>
184
192
  {{ formatValues(key, selectedVisitorObject[key]) }}
185
193
  <TooltipInfo v-if="key === 'checkOut'" text="Manual Checkout" density="compact" size="x-small" />
186
194
  </span>
@@ -251,9 +259,9 @@
251
259
  {{ (pass as any)?.prefixAndName }}
252
260
  </v-chip>
253
261
  <v-select hide-details max-width="200px" density="compact" :items="passStatusOptions" item-title="label"
254
- item-value="value" v-model="pass.status"></v-select>
262
+ item-value="value" v-model="pass.status" :disabled="selectedVisitorObject.checkOut"></v-select>
255
263
  <v-textarea v-if="pass.status === 'Lost' || pass.status === 'Damaged'" no-resize rows="3" class="w-100"
256
- density="compact" v-model="pass.remarks"></v-textarea>
264
+ density="compact" v-model="pass.remarks" :disabled="selectedVisitorObject.checkOut"></v-textarea>
257
265
  </v-row>
258
266
  </div>
259
267
  <div v-if="(keyReturnStatuses.length > 0)" class="mb-2">
@@ -264,32 +272,37 @@
264
272
  {{ (key as any)?.prefixAndName }}
265
273
  </v-chip>
266
274
  <v-select hide-details max-width="200px" density="compact" :items="passStatusOptions" item-title="label"
267
- item-value="value" v-model="key.status">
268
- </v-select>
275
+ item-value="value" v-model="key.status" :disabled="selectedVisitorObject.checkOut"></v-select>
269
276
  <v-textarea v-if="key.status === 'Lost' || key.status === 'Damaged'" no-resize rows="3" class="w-100"
270
- density="compact" v-model="key.remarks"></v-textarea>
277
+ density="compact" v-model="key.remarks" :disabled="selectedVisitorObject.checkOut"></v-textarea>
271
278
  </v-row>
272
279
  </div>
273
280
 
274
281
  <v-row no-gutters class="my-5">
275
- <v-btn variant="flat" color="blue" density="comfortable" class="text-capitalize" :loading="loading.updatingPassKeys" @click.stop="handleUpdatePassKeys">Update Pass/Keys</v-btn>
282
+ <v-btn variant="flat" color="blue" density="comfortable" class="text-capitalize" :disabled="selectedVisitorObject.checkOut"
283
+ :loading="loading.updatingPassKeys" @click.stop="handleUpdatePassKeys" >Update Pass/Keys</v-btn>
276
284
  </v-row>
277
285
  </v-card-text>
278
286
  <v-toolbar class="pa-0" density="compact">
279
287
  <v-row no-gutters>
280
288
  <v-col cols="6">
281
- <v-btn variant="text" block :disabled="loading.checkingOut"
289
+ <v-btn variant="text" block
282
290
  @click="dialog.returnPassesKeys = false">Close</v-btn>
283
291
  </v-col>
284
292
  <v-col cols="6">
285
293
  <v-btn color="red" variant="flat" height="48" rounded="0" block :loading="loading.checkingOut"
286
- :disabled="!canConfirmCheckout" @click="proceedCheckout">Confirm Checkout</v-btn>
294
+ :disabled="!canConfirmCheckout || selectedVisitorObject.checkOut" @click="proceedCheckout">Confirm Checkout</v-btn>
287
295
  </v-col>
288
296
  </v-row>
289
297
  </v-toolbar>
290
298
  </v-card>
291
299
  </v-dialog>
292
300
 
301
+ <v-dialog v-model="dialog.addPassKey" width="450" persistent>
302
+ <AddPassKeyToVisitor :visitor="selectedVisitorDataObject" :site="siteId" @close="dialog.addPassKey = false"
303
+ @update="getVisitorRefresh" />
304
+ </v-dialog>
305
+
293
306
  <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
294
307
  </v-row>
295
308
  </template>
@@ -379,6 +392,7 @@ const dialog = reactive({
379
392
  snapshotImage: false,
380
393
  remarks: false,
381
394
  returnPassesKeys: false,
395
+ addPassKey: false
382
396
  });
383
397
 
384
398
  const snapshotImageUrl = ref("");
@@ -507,8 +521,8 @@ const {
507
521
 
508
522
  watch(getVisitorReq, (newData: any) => {
509
523
  if (newData) {
510
- // items.value = newData.items ?? [];
511
- items.value = [{ _id: "testid", name: "John Doe", type: "Contractor", company: "ABC Corp", location: "Block A, Level 1, Unit 101", contact: "12345678", plateNumber: "SGX1234A", checkIn: new Date().toISOString(), checkOut: null, status: "registered", visitorPass: [{ _id: "1", keyId: "pass1", prefixAndName: "VIP Pass" }, { _id: "2", keyId: "pass2", prefixAndName: "VIP Pass 2" }], passKeys: [{ _id: "1", keyId: "key2", prefixAndName: "Master Key" }] }];
524
+ items.value = newData.items ?? [];
525
+ // items.value = [{ _id: "testid", name: "John Doe", type: "Contractor", company: "ABC Corp", location: "Block A, Level 1, Unit 101", contact: "12345678", plateNumber: "SGX1234A", checkIn: new Date().toISOString(), checkOut: null, status: "registered", visitorPass: [{ _id: "1", keyId: "pass1", prefixAndName: "VIP Pass" }, { _id: "2", keyId: "pass2", prefixAndName: "VIP Pass 2" }], passKeys: [{ _id: "1", keyId: "key2", prefixAndName: "Master Key" }] }];
512
526
  pages.value = newData.pages ?? 0;
513
527
  pageRange.value = newData?.pageRange ?? "-- - -- of --";
514
528
  }
@@ -540,6 +554,8 @@ function formatValues(key: string, value: any) {
540
554
  return value;
541
555
  }
542
556
 
557
+
558
+
543
559
  function handleAddNew() {
544
560
  dialog.showSelection = true;
545
561
  activeVisitorFormType.value = null;
@@ -555,6 +571,15 @@ function handleUpdatePage(newPageNum: number) {
555
571
  page.value = newPageNum;
556
572
  }
557
573
 
574
+ function showAddPassKeyButton(item: any) {
575
+ const hasPasses = (item?.visitorPass?.length ?? 0) > 0;
576
+ const hasKeys = (item?.passKeys?.length ?? 0) > 0;
577
+
578
+ const isTypeWithPassKey = ["contractor", "guest", "walk-in"].includes(item?.type);
579
+
580
+ return !hasPasses && !hasKeys && isTypeWithPassKey && item?.status === "registered" && !item?.checkOut;
581
+ }
582
+
558
583
  function handleSelectVisitorType(type: TVisitorType) {
559
584
  dialog.showSelection = false;
560
585
  dialog.showForm = true;
@@ -607,6 +632,11 @@ function handleViewImage(imageId: string) {
607
632
  dialog.snapshotImage = true;
608
633
  }
609
634
 
635
+ function handleOpenAddPassKey(item: any) {
636
+ selectedVisitorId.value = item?._id;
637
+ dialog.addPassKey = true;
638
+ }
639
+
610
640
  function handleOpenRemarks(item: any, type: 'checkIn' | 'checkOut') {
611
641
  selectedVisitorId.value = item?._id;
612
642
  remarksType.value = type;
@@ -635,16 +665,18 @@ async function handleSaveRemarks() {
635
665
  }
636
666
  }
637
667
 
638
- async function handleUpdatePassKeys(){
668
+ async function handleUpdatePassKeys() {
639
669
  if (!selectedVisitorId.value) return;
640
670
  try {
641
671
  loading.updatingPassKeys = true;
642
672
  const payload: any = {};
643
673
  if (passReturnStatuses.value.length > 0) {
644
- payload.visitorPass = passReturnStatuses.value;
674
+ const passReturnStatusesPayload = passReturnStatuses.value.map(p => ({ keyId: p.keyId, status: p.status, remarks: p.remarks }));
675
+ payload.visitorPass = passReturnStatusesPayload;
645
676
  }
646
677
  if (keyReturnStatuses.value.length > 0) {
647
- payload.passKeys = keyReturnStatuses.value;
678
+ const keyReturnStatusesPayload = keyReturnStatuses.value.map(k => ({ keyId: k.keyId, status: k.status, remarks: k.remarks }));
679
+ payload.passKeys = keyReturnStatusesPayload;
648
680
  }
649
681
  await updateVisitor(selectedVisitorId.value, payload);
650
682
  showMessage("Pass/Key statuses updated successfully!", "info");
@@ -723,10 +755,14 @@ async function proceedCheckout() {
723
755
 
724
756
  try {
725
757
  loading.checkingOut = true;
758
+
759
+ const passReturnStatusesPayload = passReturnStatuses.value.map(p => ({ keyId: p.keyId, status: p.status, remarks: p.remarks }));
760
+ const keyReturnStatusesPayload = keyReturnStatuses.value.map(k => ({ keyId: k.keyId, status: k.status, remarks: k.remarks }));
761
+
726
762
  const res = await updateVisitor(userId as string, {
727
763
  checkOut: new Date().toISOString(),
728
- ...(passReturnStatuses.value.length > 0 ? { visitorPass: passReturnStatuses.value } : {}),
729
- ...(keyReturnStatuses.value.length > 0 ? { passKeys: keyReturnStatuses.value } : {}),
764
+ ...(passReturnStatuses.value.length > 0 ? { visitorPass: passReturnStatusesPayload } : {}),
765
+ ...(keyReturnStatuses.value.length > 0 ? { passKeys: keyReturnStatusesPayload } : {}),
730
766
  });
731
767
  if (res) {
732
768
  showMessage("Visitor successfully checked-out!", "info");
@@ -98,6 +98,19 @@ export default function () {
98
98
  });
99
99
  }
100
100
 
101
+ async function updateSiteInformation(
102
+ siteId: string,
103
+ payload: { bgImage: string; description: string; docs: { id: string; name: string }[] }
104
+ ) {
105
+ return await useNuxtApp().$api<Record<string, any>>(
106
+ `/api/sites/information/id/${siteId}`,
107
+ {
108
+ method: "PATCH",
109
+ body: payload,
110
+ }
111
+ );
112
+ }
113
+
101
114
  async function setSiteGuardPosts(siteId: string, value: number) {
102
115
  return await useNuxtApp().$api<Record<string, any>>(
103
116
  `/api/sites/guard-post/id/${siteId}`,
@@ -138,6 +151,7 @@ export default function () {
138
151
  updateSiteCamera,
139
152
  deleteSiteCameraById,
140
153
  updateSitebyId,
154
+ updateSiteInformation,
141
155
  getOvernightParkingAvailability,
142
156
  updateOvernightParkingAvailability
143
157
  };
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.11.18",
5
+ "version": "1.11.19",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {