@7365admin1/layer-common 1.11.20 → 1.11.21
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 +6 -0
- package/components/AddPassKeyToVisitor.vue +114 -53
- package/components/BulletinBoardView.vue +158 -16
- package/components/OvernightParkingAvailability.vue +286 -151
- package/components/ScanVisitorQRCode.vue +157 -0
- package/components/SiteSettings.vue +303 -243
- package/components/TableMain.vue +72 -21
- package/components/VisitorManagement.vue +633 -229
- package/composables/useOrg.ts +16 -0
- package/package.json +1 -1
- package/plugins/html5-qrcode.client.js +8 -0
- package/types/overnight-parking.d.ts +3 -2
|
@@ -1,34 +1,95 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
<TableMain
|
|
4
|
-
:
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
<TableMain
|
|
4
|
+
:headers="headers"
|
|
5
|
+
:items="items"
|
|
6
|
+
:loading="getVisitorPending"
|
|
7
|
+
:page="page"
|
|
8
|
+
:pages="pages"
|
|
9
|
+
:extension-height="110"
|
|
10
|
+
:offset="300"
|
|
11
|
+
:pageRange="pageRange"
|
|
12
|
+
:canCreate="canAddVisitor"
|
|
13
|
+
:canScanVisitorQRCode="canScanVisitorQRCode"
|
|
14
|
+
@refresh="getVisitorRefresh"
|
|
15
|
+
@update:page="handleUpdatePage"
|
|
16
|
+
createLabel="Add Visitor"
|
|
17
|
+
show-header
|
|
18
|
+
@row-click="handleRowClick"
|
|
19
|
+
@create="handleAddNew"
|
|
20
|
+
@scan="dialog.scanVisitorQRCode = true"
|
|
21
|
+
>
|
|
7
22
|
<template #extension>
|
|
8
23
|
<v-row no-gutters class="w-100 d-flex flex-column">
|
|
9
|
-
<v-tabs
|
|
10
|
-
|
|
24
|
+
<v-tabs
|
|
25
|
+
v-model="activeTab"
|
|
26
|
+
color="primary"
|
|
27
|
+
:height="40"
|
|
28
|
+
@update:model-value="toRoute"
|
|
29
|
+
class="w-100"
|
|
30
|
+
>
|
|
31
|
+
<v-tab
|
|
32
|
+
v-for="tab in tabOptions"
|
|
33
|
+
:value="tab.value"
|
|
34
|
+
:key="tab.value"
|
|
35
|
+
class="text-capitalize"
|
|
36
|
+
>
|
|
11
37
|
{{ tab.name }}
|
|
12
38
|
</v-tab>
|
|
13
39
|
</v-tabs>
|
|
14
40
|
|
|
15
|
-
<v-card
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
41
|
+
<v-card
|
|
42
|
+
class="w-100 px-3 d-flex align-center ga-5 py-2"
|
|
43
|
+
flat
|
|
44
|
+
:height="60"
|
|
45
|
+
>
|
|
46
|
+
<v-text-field
|
|
47
|
+
v-model="searchInput"
|
|
48
|
+
density="compact"
|
|
49
|
+
placeholder="Search"
|
|
50
|
+
clearable
|
|
51
|
+
max-width="300"
|
|
52
|
+
append-inner-icon="mdi-magnify"
|
|
53
|
+
hide-details
|
|
54
|
+
/>
|
|
55
|
+
<v-checkbox
|
|
56
|
+
v-model="displayNotCheckedOut"
|
|
57
|
+
class="text-subtitle-2"
|
|
58
|
+
hide-details
|
|
59
|
+
>
|
|
19
60
|
<template #label>
|
|
20
61
|
<span class="text-caption">Not Checked Out</span>
|
|
21
62
|
</template>
|
|
22
63
|
</v-checkbox>
|
|
23
|
-
<InputDatePicker
|
|
64
|
+
<InputDatePicker
|
|
65
|
+
v-model="dateFrom"
|
|
66
|
+
density="compact"
|
|
67
|
+
hide-details
|
|
68
|
+
/>
|
|
24
69
|
<InputDatePicker v-model="dateTo" density="compact" hide-details />
|
|
25
|
-
<v-select
|
|
26
|
-
|
|
27
|
-
|
|
70
|
+
<v-select
|
|
71
|
+
v-if="activeTab == 'registered'"
|
|
72
|
+
v-model="filterTypes"
|
|
73
|
+
label="Filter by types"
|
|
74
|
+
item-title="label"
|
|
75
|
+
item-value="value"
|
|
76
|
+
:items="visitorSelection"
|
|
77
|
+
density="compact"
|
|
78
|
+
clearable
|
|
79
|
+
multiple
|
|
80
|
+
max-width="200"
|
|
81
|
+
hide-details
|
|
82
|
+
>
|
|
28
83
|
<template v-slot:selection="{ item, index }">
|
|
29
84
|
<div class="d-flex align-center text-caption text-nowrap">
|
|
30
|
-
<v-chip
|
|
31
|
-
|
|
85
|
+
<v-chip
|
|
86
|
+
v-if="index === 0"
|
|
87
|
+
color="error"
|
|
88
|
+
:text="filterTypeSelectionLabel()"
|
|
89
|
+
size="x-small"
|
|
90
|
+
variant="tonal"
|
|
91
|
+
class="ml-2"
|
|
92
|
+
/>
|
|
32
93
|
</div>
|
|
33
94
|
</template>
|
|
34
95
|
</v-select>
|
|
@@ -43,8 +104,9 @@
|
|
|
43
104
|
</span>
|
|
44
105
|
<span class="text-capitalize">{{ item?.name }}</span>
|
|
45
106
|
</span>
|
|
46
|
-
<span class="text-grey text-caption" v-if="item?.members?.length > 0"
|
|
47
|
-
members)</span
|
|
107
|
+
<span class="text-grey text-caption" v-if="item?.members?.length > 0"
|
|
108
|
+
>( +{{ item?.members?.length }} members)</span
|
|
109
|
+
>
|
|
48
110
|
</template>
|
|
49
111
|
|
|
50
112
|
<template v-slot:item.type-company="{ item }">
|
|
@@ -52,7 +114,7 @@
|
|
|
52
114
|
<v-icon icon="mdi-user" size="15" />
|
|
53
115
|
<span v-if="item.type === 'contractor'" class="text-capitalize">{{
|
|
54
116
|
formatCamelCaseToWords(item.contractorType)
|
|
55
|
-
|
|
117
|
+
}}</span>
|
|
56
118
|
<span v-else class="text-capitalize">{{ formatType(item) }}</span>
|
|
57
119
|
</span>
|
|
58
120
|
<span class="d-flex align-center ga-2">
|
|
@@ -88,109 +150,255 @@
|
|
|
88
150
|
<template v-slot:item.checkin-out="{ item }">
|
|
89
151
|
<v-row no-gutters class="d-flex flex-column py-2">
|
|
90
152
|
<span class="d-flex align-center ga-2">
|
|
91
|
-
<v-icon
|
|
153
|
+
<v-icon
|
|
154
|
+
icon="mdi-clock-time-four-outline"
|
|
155
|
+
color="green"
|
|
156
|
+
size="20"
|
|
157
|
+
/>
|
|
92
158
|
<span class="text-capitalize">{{
|
|
93
159
|
UTCToLocalTIme(item.checkIn) || "-"
|
|
94
160
|
}}</span>
|
|
95
161
|
<span>
|
|
96
|
-
<v-icon
|
|
97
|
-
|
|
162
|
+
<v-icon
|
|
163
|
+
v-if="item?.snapshotEntryImage"
|
|
164
|
+
size="17"
|
|
165
|
+
icon="mdi-image"
|
|
166
|
+
@click.stop="handleViewImage(item.snapshotEntryImage)"
|
|
167
|
+
/>
|
|
98
168
|
</span>
|
|
99
169
|
</span>
|
|
100
|
-
|
|
170
|
+
|
|
171
|
+
<span
|
|
172
|
+
v-if="
|
|
173
|
+
!item.checkOut &&
|
|
174
|
+
(item?.status === 'registered' ||
|
|
175
|
+
item?.status === 'unregistered') &&
|
|
176
|
+
canCheckoutVisitor
|
|
177
|
+
"
|
|
178
|
+
>
|
|
179
|
+
<span
|
|
180
|
+
class="d-flex align-center ga-2"
|
|
181
|
+
v-bind="menuProps"
|
|
182
|
+
style="cursor: pointer"
|
|
183
|
+
>
|
|
184
|
+
<v-icon
|
|
185
|
+
icon="mdi-clock-time-eight-outline"
|
|
186
|
+
color="red"
|
|
187
|
+
size="20"
|
|
188
|
+
/>
|
|
189
|
+
<v-btn
|
|
190
|
+
size="x-small"
|
|
191
|
+
class="text-capitalize"
|
|
192
|
+
color="red"
|
|
193
|
+
text="Checkout"
|
|
194
|
+
:loading="
|
|
195
|
+
loading.checkingOut && item?._id === selectedVisitorId
|
|
196
|
+
"
|
|
197
|
+
/>
|
|
198
|
+
</span>
|
|
199
|
+
</span>
|
|
200
|
+
<span v-else class="d-flex align-center ga-2">
|
|
101
201
|
<v-icon icon="mdi-clock-time-eight-outline" color="red" size="20" />
|
|
102
202
|
<template v-if="item.checkOut">
|
|
103
203
|
<span class="text-capitalize">{{
|
|
104
204
|
UTCToLocalTIme(item.checkOut) || "-"
|
|
105
205
|
}}</span>
|
|
106
206
|
<span>
|
|
107
|
-
<v-icon
|
|
108
|
-
|
|
207
|
+
<v-icon
|
|
208
|
+
v-if="item?.snapshotExitImage"
|
|
209
|
+
size="17"
|
|
210
|
+
icon="mdi-image"
|
|
211
|
+
@click.stop="handleViewImage(item.snapshotExitImage)"
|
|
212
|
+
/>
|
|
109
213
|
</span>
|
|
110
214
|
<span v-if="item?.manualCheckout">
|
|
111
|
-
<TooltipInfo
|
|
215
|
+
<TooltipInfo
|
|
216
|
+
text="Manual Checkout"
|
|
217
|
+
density="compact"
|
|
218
|
+
size="x-small"
|
|
219
|
+
/>
|
|
112
220
|
</span>
|
|
113
221
|
</template>
|
|
114
|
-
<span v-else-if="!item?.checkOut && (item?.status === 'registered' || item?.status === 'unregistered')">
|
|
115
|
-
<v-btn size="x-small" class="text-capitalize" color="red" text="Checkout"
|
|
116
|
-
:loading="loading.checkingOut && item?._id === selectedVisitorId" @click.stop="handleCheckout(item._id)"
|
|
117
|
-
v-if="canCheckoutVisitor" />
|
|
118
|
-
</span>
|
|
119
222
|
</span>
|
|
120
|
-
<div v-if="(item.visitorPass?.length ?? 0) > 0 || (item.passKeys?.length ?? 0) > 0"
|
|
121
|
-
class="d-flex flex-wrap ga-1 mt-1" @click.stop="handleCheckout(item._id)">
|
|
122
|
-
<v-chip v-for="pass in item.visitorPass" :key="(pass as any)._id ?? (pass as any).keyId"
|
|
123
|
-
prepend-icon="mdi-card-bulleted-outline" size="x-small" variant="tonal" color="blue">
|
|
124
|
-
{{ (pass as any)?.prefixAndName }}
|
|
125
|
-
</v-chip>
|
|
126
|
-
<v-chip v-for="key in item.passKeys" :key="(key as any)._id ?? (key as any).keyId" prepend-icon="mdi-key"
|
|
127
|
-
size="x-small" variant="tonal" color="orange">
|
|
128
|
-
{{ (key as any)?.prefixAndName }}
|
|
129
|
-
</v-chip>
|
|
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
223
|
|
|
224
|
+
<div
|
|
225
|
+
v-if="showAddPassKeyButton(item)"
|
|
226
|
+
class="d-flex flex-wrap ga-1 mt-1"
|
|
227
|
+
v-bind="menuProps"
|
|
228
|
+
style="cursor: pointer"
|
|
229
|
+
>
|
|
230
|
+
<v-chip
|
|
231
|
+
size="x-small"
|
|
232
|
+
variant="tonal"
|
|
233
|
+
prepend-icon="mdi-key"
|
|
234
|
+
@click.stop="handleOpenAddPassKey(item)"
|
|
235
|
+
>Add Pass/Key</v-chip
|
|
236
|
+
>
|
|
137
237
|
</div>
|
|
238
|
+
|
|
239
|
+
<v-menu :close-on-content-click="true">
|
|
240
|
+
<template v-slot:activator="{ props: menuProps }">
|
|
241
|
+
<div
|
|
242
|
+
v-if="
|
|
243
|
+
(item.visitorPass?.length ?? 0) > 0 ||
|
|
244
|
+
(item.passKeys?.length ?? 0) > 0
|
|
245
|
+
"
|
|
246
|
+
class="d-flex flex-wrap ga-1 mt-1"
|
|
247
|
+
v-bind="menuProps"
|
|
248
|
+
style="cursor: pointer"
|
|
249
|
+
>
|
|
250
|
+
<v-chip
|
|
251
|
+
v-for="pass in item.visitorPass"
|
|
252
|
+
:key="(pass as any)._id ?? (pass as any).keyId"
|
|
253
|
+
prepend-icon="mdi-card-bulleted-outline"
|
|
254
|
+
size="x-small"
|
|
255
|
+
variant="tonal"
|
|
256
|
+
color="blue"
|
|
257
|
+
>
|
|
258
|
+
{{ (pass as any)?.prefixAndName }}
|
|
259
|
+
</v-chip>
|
|
260
|
+
<v-chip
|
|
261
|
+
v-for="key in item.passKeys"
|
|
262
|
+
:key="(key as any)._id ?? (key as any).keyId"
|
|
263
|
+
prepend-icon="mdi-key"
|
|
264
|
+
size="x-small"
|
|
265
|
+
variant="tonal"
|
|
266
|
+
color="orange"
|
|
267
|
+
>
|
|
268
|
+
{{ (key as any)?.prefixAndName }}
|
|
269
|
+
</v-chip>
|
|
270
|
+
</div>
|
|
271
|
+
</template>
|
|
272
|
+
<v-list density="compact">
|
|
273
|
+
<v-list-item
|
|
274
|
+
prepend-icon="mdi-logout"
|
|
275
|
+
title="Checkout"
|
|
276
|
+
@click.stop="handleCheckout(item._id)"
|
|
277
|
+
/>
|
|
278
|
+
<v-list-item
|
|
279
|
+
v-if="
|
|
280
|
+
(item.visitorPass?.length ?? 0) > 0 ||
|
|
281
|
+
(item.passKeys?.length ?? 0) > 0
|
|
282
|
+
"
|
|
283
|
+
prepend-icon="mdi-card-bulleted-outline"
|
|
284
|
+
:title="item.checkOut ? 'View Pass/Key' : 'Edit Pass/Key'"
|
|
285
|
+
@click.stop="handleOpenEditPassKey(item)"
|
|
286
|
+
/>
|
|
287
|
+
<v-list-item
|
|
288
|
+
v-if="showAddPassKeyButton(item)"
|
|
289
|
+
prepend-icon="mdi-plus"
|
|
290
|
+
title="Add Pass/Key"
|
|
291
|
+
@click.stop="handleOpenAddPassKey(item)"
|
|
292
|
+
/>
|
|
293
|
+
</v-list>
|
|
294
|
+
</v-menu>
|
|
138
295
|
</v-row>
|
|
139
296
|
</template>
|
|
140
297
|
|
|
141
298
|
<template v-slot:item.action="{ item }">
|
|
142
|
-
<v-btn
|
|
143
|
-
|
|
299
|
+
<v-btn
|
|
300
|
+
v-if="activeTab === 'unregistered' && canAddVisitor"
|
|
301
|
+
size="small"
|
|
302
|
+
color="primary"
|
|
303
|
+
text="Register"
|
|
304
|
+
@click.stop="handleRegistrationUnregisteredVisitor(item)"
|
|
305
|
+
/>
|
|
144
306
|
</template>
|
|
145
307
|
|
|
146
308
|
<template v-slot:item.checkInRemarks="{ item }">
|
|
147
309
|
<span>
|
|
148
|
-
<v-btn
|
|
149
|
-
|
|
150
|
-
|
|
310
|
+
<v-btn
|
|
311
|
+
variant="text"
|
|
312
|
+
color="blue-darken-2"
|
|
313
|
+
density="compact"
|
|
314
|
+
size="small"
|
|
315
|
+
@click.stop="handleOpenRemarks(item, 'checkIn')"
|
|
316
|
+
>{{ item.checkInRemarks ? "View Remarks" : "Add Remarks" }}</v-btn
|
|
317
|
+
>
|
|
151
318
|
</span>
|
|
152
319
|
</template>
|
|
153
320
|
<template v-slot:item.checkOutRemarks="{ item }">
|
|
154
321
|
<span>
|
|
155
|
-
<v-btn
|
|
156
|
-
|
|
157
|
-
|
|
322
|
+
<v-btn
|
|
323
|
+
variant="text"
|
|
324
|
+
color="blue-darken-2"
|
|
325
|
+
density="compact"
|
|
326
|
+
size="small"
|
|
327
|
+
@click.stop="handleOpenRemarks(item, 'checkOut')"
|
|
328
|
+
>{{ item.checkOutRemarks ? "View Remarks" : "Add Remarks" }}</v-btn
|
|
329
|
+
>
|
|
158
330
|
</span>
|
|
159
331
|
</template>
|
|
160
|
-
|
|
161
332
|
</TableMain>
|
|
162
333
|
|
|
163
334
|
<v-dialog v-model="dialog.showSelection" width="450" persistent>
|
|
164
|
-
<VisitorFormSelection
|
|
335
|
+
<VisitorFormSelection
|
|
336
|
+
:mode="mode"
|
|
337
|
+
@cancel="dialog.showSelection = false"
|
|
338
|
+
@select="handleSelectVisitorType"
|
|
339
|
+
/>
|
|
165
340
|
</v-dialog>
|
|
166
341
|
|
|
167
|
-
<v-dialog
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
342
|
+
<v-dialog
|
|
343
|
+
v-model="dialog.showForm"
|
|
344
|
+
v-if="activeVisitorFormType"
|
|
345
|
+
width="450"
|
|
346
|
+
persistent
|
|
347
|
+
>
|
|
348
|
+
<VisitorForm
|
|
349
|
+
:mode="mode"
|
|
350
|
+
:org="orgId"
|
|
351
|
+
:site="siteId"
|
|
352
|
+
:visitor-data="selectedVisitorDataObject"
|
|
353
|
+
:type="activeVisitorFormType"
|
|
354
|
+
@back="handleClickBack"
|
|
355
|
+
@done="handleVisitorFormDone"
|
|
356
|
+
@done:more="handleVisitorFormCreateMore"
|
|
357
|
+
@close:all="handleCloseAll"
|
|
358
|
+
/>
|
|
171
359
|
</v-dialog>
|
|
172
360
|
|
|
173
361
|
<v-dialog v-model="dialog.viewVisitor" width="450" persistent>
|
|
174
|
-
<VehicleUpdateMoreAction
|
|
175
|
-
|
|
176
|
-
|
|
362
|
+
<VehicleUpdateMoreAction
|
|
363
|
+
title="Preview"
|
|
364
|
+
:can-update="false"
|
|
365
|
+
:can-delete="false"
|
|
366
|
+
@close="dialog.viewVisitor = false"
|
|
367
|
+
edit-button-label="Edit Visitor"
|
|
368
|
+
>
|
|
177
369
|
<template v-slot:content>
|
|
178
370
|
<v-row no-gutters class="mb-4">
|
|
179
371
|
<v-col v-for="(label, key) in formattedFields" :key="key" cols="12">
|
|
180
|
-
<span
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
372
|
+
<span
|
|
373
|
+
v-if="
|
|
374
|
+
key === 'checkOut' &&
|
|
375
|
+
!selectedVisitorObject[key] &&
|
|
376
|
+
canCheckoutVisitor
|
|
377
|
+
"
|
|
378
|
+
class="d-flex align-center"
|
|
379
|
+
>
|
|
185
380
|
<strong>{{ label }}:</strong>
|
|
186
|
-
<v-btn
|
|
187
|
-
|
|
381
|
+
<v-btn
|
|
382
|
+
size="x-small"
|
|
383
|
+
class="ml-3 text-capitalize"
|
|
384
|
+
color="red"
|
|
385
|
+
text="Checkout"
|
|
386
|
+
:disabled="loading.checkingOut"
|
|
387
|
+
@click="handleCheckout(selectedVisitorId as string)"
|
|
388
|
+
/>
|
|
188
389
|
</span>
|
|
189
390
|
|
|
190
|
-
<span
|
|
191
|
-
|
|
391
|
+
<span
|
|
392
|
+
v-else-if="selectedVisitorObject[key]"
|
|
393
|
+
class="d-flex ga-3 align-center"
|
|
394
|
+
><strong>{{ label }}:</strong>
|
|
192
395
|
{{ formatValues(key, selectedVisitorObject[key]) }}
|
|
193
|
-
<TooltipInfo
|
|
396
|
+
<TooltipInfo
|
|
397
|
+
v-if="key === 'checkOut'"
|
|
398
|
+
text="Manual Checkout"
|
|
399
|
+
density="compact"
|
|
400
|
+
size="x-small"
|
|
401
|
+
/>
|
|
194
402
|
</span>
|
|
195
403
|
</v-col>
|
|
196
404
|
</v-row>
|
|
@@ -198,17 +406,15 @@
|
|
|
198
406
|
</VehicleUpdateMoreAction>
|
|
199
407
|
</v-dialog>
|
|
200
408
|
|
|
201
|
-
<v-dialog v-model="dialog.deleteConfirmation" width="450" persistent>
|
|
202
|
-
<CardDeleteConfirmation prompt-title="Are you sure want to delete this visitor?"
|
|
203
|
-
:loading="loading.deletingVisitor" @close="dialog.deleteConfirmation = false"
|
|
204
|
-
@delete="handleProceedDeleteVisitor" />
|
|
205
|
-
</v-dialog>
|
|
206
|
-
|
|
207
409
|
<v-dialog v-model="dialog.snapshotImage" max-width="700">
|
|
208
410
|
<v-card>
|
|
209
411
|
<v-card-title class="d-flex justify-space-between align-center">
|
|
210
412
|
<span>Snapshot</span>
|
|
211
|
-
<v-btn
|
|
413
|
+
<v-btn
|
|
414
|
+
icon="mdi-close"
|
|
415
|
+
variant="text"
|
|
416
|
+
@click="dialog.snapshotImage = false"
|
|
417
|
+
/>
|
|
212
418
|
</v-card-title>
|
|
213
419
|
<v-card-text class="pa-2 d-flex justify-center">
|
|
214
420
|
<v-img :src="snapshotImageUrl" max-height="600" contain />
|
|
@@ -218,23 +424,51 @@
|
|
|
218
424
|
|
|
219
425
|
<v-dialog v-model="dialog.remarks" max-width="450" persistent>
|
|
220
426
|
<v-card>
|
|
221
|
-
<v-card-title
|
|
222
|
-
|
|
223
|
-
|
|
427
|
+
<v-card-title
|
|
428
|
+
class="d-flex justify-space-between align-center pt-4 px-4"
|
|
429
|
+
>
|
|
430
|
+
<span>{{
|
|
431
|
+
remarksType === "checkIn" ? "Check-In Remarks" : "Check-Out Remarks"
|
|
432
|
+
}}</span>
|
|
433
|
+
<v-btn
|
|
434
|
+
icon="mdi-close"
|
|
435
|
+
variant="text"
|
|
436
|
+
@click="dialog.remarks = false"
|
|
437
|
+
/>
|
|
224
438
|
</v-card-title>
|
|
225
439
|
<v-card-text class="px-4 pb-2">
|
|
226
|
-
<v-textarea
|
|
227
|
-
|
|
440
|
+
<v-textarea
|
|
441
|
+
v-model="remarksInput"
|
|
442
|
+
label="Remarks"
|
|
443
|
+
rows="4"
|
|
444
|
+
auto-grow
|
|
445
|
+
variant="outlined"
|
|
446
|
+
:readonly="remarksViewOnly"
|
|
447
|
+
/>
|
|
228
448
|
</v-card-text>
|
|
229
449
|
<v-toolbar class="pa-0" density="compact">
|
|
230
450
|
<v-row no-gutters>
|
|
231
451
|
<v-col cols="6">
|
|
232
|
-
<v-btn
|
|
233
|
-
|
|
452
|
+
<v-btn
|
|
453
|
+
variant="text"
|
|
454
|
+
block
|
|
455
|
+
:disabled="loading.savingRemarks"
|
|
456
|
+
@click="dialog.remarks = false"
|
|
457
|
+
>Cancel</v-btn
|
|
458
|
+
>
|
|
234
459
|
</v-col>
|
|
235
460
|
<v-col cols="6">
|
|
236
|
-
<v-btn
|
|
237
|
-
|
|
461
|
+
<v-btn
|
|
462
|
+
color="primary"
|
|
463
|
+
variant="flat"
|
|
464
|
+
height="48"
|
|
465
|
+
rounded="0"
|
|
466
|
+
block
|
|
467
|
+
:loading="loading.savingRemarks"
|
|
468
|
+
:disabled="!remarksInput.trim()"
|
|
469
|
+
@click="handleSaveRemarks"
|
|
470
|
+
>Save</v-btn
|
|
471
|
+
>
|
|
238
472
|
</v-col>
|
|
239
473
|
</v-row>
|
|
240
474
|
</v-toolbar>
|
|
@@ -244,54 +478,134 @@
|
|
|
244
478
|
<v-dialog v-model="dialog.returnPassesKeys" max-width="450" persistent>
|
|
245
479
|
<v-card>
|
|
246
480
|
<v-toolbar density="compact" color="">
|
|
247
|
-
<v-row
|
|
481
|
+
<v-row
|
|
482
|
+
no-gutters
|
|
483
|
+
class="d-flex fill-height justify-space-between align-center px-4"
|
|
484
|
+
>
|
|
248
485
|
<span class="font-weight-bold">Return Passes & Keys</span>
|
|
249
|
-
<v-btn
|
|
486
|
+
<v-btn
|
|
487
|
+
icon="mdi-close"
|
|
488
|
+
variant="text"
|
|
489
|
+
@click="dialog.returnPassesKeys = false"
|
|
490
|
+
/>
|
|
250
491
|
</v-row>
|
|
251
492
|
</v-toolbar>
|
|
252
493
|
<v-card-text class="px-4 pb-2">
|
|
253
|
-
<p class="text-body-2 mb-3">
|
|
494
|
+
<p class="text-body-2 mb-3">
|
|
495
|
+
Please ensure all passes and keys are returned before checking out.
|
|
496
|
+
</p>
|
|
254
497
|
<div v-if="passReturnStatuses.length > 0" class="mb-2">
|
|
255
498
|
<p class="text-caption text-medium-emphasis mb-1">Passes</p>
|
|
256
|
-
<v-row
|
|
257
|
-
|
|
258
|
-
|
|
499
|
+
<v-row
|
|
500
|
+
no-gutters
|
|
501
|
+
v-for="pass in passReturnStatuses"
|
|
502
|
+
:key="(pass as any)._id ?? (pass as any).keyId"
|
|
503
|
+
class="d-flex flex-wrap justify-space-between align-center ga-5 mb-2"
|
|
504
|
+
>
|
|
505
|
+
<v-chip
|
|
506
|
+
prepend-icon="mdi-card-bulleted-outline"
|
|
507
|
+
size="small"
|
|
508
|
+
variant="tonal"
|
|
509
|
+
color="blue"
|
|
510
|
+
>
|
|
259
511
|
{{ (pass as any)?.prefixAndName }}
|
|
260
512
|
</v-chip>
|
|
261
|
-
<v-select
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
density="compact"
|
|
513
|
+
<v-select
|
|
514
|
+
hide-details
|
|
515
|
+
max-width="200px"
|
|
516
|
+
density="compact"
|
|
517
|
+
:items="passStatusOptions"
|
|
518
|
+
item-title="label"
|
|
519
|
+
item-value="value"
|
|
520
|
+
v-model="pass.status"
|
|
521
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
522
|
+
></v-select>
|
|
523
|
+
<v-textarea
|
|
524
|
+
v-if="pass.status === 'Lost' || pass.status === 'Damaged'"
|
|
525
|
+
no-resize
|
|
526
|
+
rows="3"
|
|
527
|
+
class="w-100"
|
|
528
|
+
density="compact"
|
|
529
|
+
v-model="pass.remarks"
|
|
530
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
531
|
+
></v-textarea>
|
|
265
532
|
</v-row>
|
|
266
533
|
</div>
|
|
267
|
-
<div v-if="
|
|
534
|
+
<div v-if="keyReturnStatuses.length > 0" class="mb-2">
|
|
268
535
|
<p class="text-caption text-medium-emphasis mb-1">Keys</p>
|
|
269
|
-
<v-row
|
|
270
|
-
|
|
271
|
-
|
|
536
|
+
<v-row
|
|
537
|
+
no-gutters
|
|
538
|
+
v-for="key in keyReturnStatuses"
|
|
539
|
+
:key="(key as any)._id ?? (key as any).keyId"
|
|
540
|
+
class="d-flex flex-wrap justify-space-between align-center ga-5 mb-2"
|
|
541
|
+
>
|
|
542
|
+
<v-chip
|
|
543
|
+
prepend-icon="mdi-key"
|
|
544
|
+
size="small"
|
|
545
|
+
variant="tonal"
|
|
546
|
+
color="orange"
|
|
547
|
+
>
|
|
272
548
|
{{ (key as any)?.prefixAndName }}
|
|
273
549
|
</v-chip>
|
|
274
|
-
<v-select
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
density="compact"
|
|
550
|
+
<v-select
|
|
551
|
+
hide-details
|
|
552
|
+
max-width="200px"
|
|
553
|
+
density="compact"
|
|
554
|
+
:items="passStatusOptions"
|
|
555
|
+
item-title="label"
|
|
556
|
+
item-value="value"
|
|
557
|
+
v-model="key.status"
|
|
558
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
559
|
+
></v-select>
|
|
560
|
+
<v-textarea
|
|
561
|
+
v-if="key.status === 'Lost' || key.status === 'Damaged'"
|
|
562
|
+
no-resize
|
|
563
|
+
rows="3"
|
|
564
|
+
class="w-100"
|
|
565
|
+
density="compact"
|
|
566
|
+
v-model="key.remarks"
|
|
567
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
568
|
+
></v-textarea>
|
|
278
569
|
</v-row>
|
|
279
570
|
</div>
|
|
280
571
|
|
|
281
572
|
<v-row no-gutters class="my-5">
|
|
282
|
-
<v-btn
|
|
283
|
-
|
|
573
|
+
<v-btn
|
|
574
|
+
variant="flat"
|
|
575
|
+
color="blue"
|
|
576
|
+
density="comfortable"
|
|
577
|
+
class="text-capitalize"
|
|
578
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
579
|
+
:loading="loading.updatingPassKeys"
|
|
580
|
+
@click.stop="handleUpdatePassKeys"
|
|
581
|
+
>Update Pass/Keys</v-btn
|
|
582
|
+
>
|
|
284
583
|
</v-row>
|
|
285
584
|
</v-card-text>
|
|
286
585
|
<v-toolbar class="pa-0" density="compact">
|
|
287
586
|
<v-row no-gutters>
|
|
288
587
|
<v-col cols="6">
|
|
289
|
-
<v-btn
|
|
290
|
-
|
|
588
|
+
<v-btn
|
|
589
|
+
variant="text"
|
|
590
|
+
block
|
|
591
|
+
@click="dialog.returnPassesKeys = false"
|
|
592
|
+
>Close</v-btn
|
|
593
|
+
>
|
|
291
594
|
</v-col>
|
|
292
595
|
<v-col cols="6">
|
|
293
|
-
<v-btn
|
|
294
|
-
|
|
596
|
+
<v-btn
|
|
597
|
+
color="red"
|
|
598
|
+
variant="flat"
|
|
599
|
+
height="48"
|
|
600
|
+
rounded="0"
|
|
601
|
+
block
|
|
602
|
+
:loading="loading.checkingOut"
|
|
603
|
+
:disabled="
|
|
604
|
+
!canConfirmCheckout || selectedVisitorObject.checkOut
|
|
605
|
+
"
|
|
606
|
+
@click="proceedCheckout"
|
|
607
|
+
>Confirm Checkout</v-btn
|
|
608
|
+
>
|
|
295
609
|
</v-col>
|
|
296
610
|
</v-row>
|
|
297
611
|
</v-toolbar>
|
|
@@ -299,10 +613,40 @@
|
|
|
299
613
|
</v-dialog>
|
|
300
614
|
|
|
301
615
|
<v-dialog v-model="dialog.addPassKey" width="450" persistent>
|
|
302
|
-
<AddPassKeyToVisitor
|
|
303
|
-
|
|
616
|
+
<AddPassKeyToVisitor
|
|
617
|
+
mode="add"
|
|
618
|
+
:visitor="selectedVisitorDataObject"
|
|
619
|
+
:site="siteId"
|
|
620
|
+
@close="dialog.addPassKey = false"
|
|
621
|
+
@done="
|
|
622
|
+
() => {
|
|
623
|
+
dialog.addPassKey = false;
|
|
624
|
+
getVisitorRefresh();
|
|
625
|
+
}
|
|
626
|
+
"
|
|
627
|
+
/>
|
|
304
628
|
</v-dialog>
|
|
305
629
|
|
|
630
|
+
<v-dialog v-model="dialog.editPassKey" width="450" persistent>
|
|
631
|
+
<AddPassKeyToVisitor
|
|
632
|
+
mode="edit"
|
|
633
|
+
:visitor="selectedVisitorDataObject"
|
|
634
|
+
:site="siteId"
|
|
635
|
+
@close="dialog.editPassKey = false"
|
|
636
|
+
@done="
|
|
637
|
+
() => {
|
|
638
|
+
dialog.editPassKey = false;
|
|
639
|
+
getVisitorRefresh();
|
|
640
|
+
}
|
|
641
|
+
"
|
|
642
|
+
/>
|
|
643
|
+
</v-dialog>
|
|
644
|
+
|
|
645
|
+
<ScanVisitorQRCode
|
|
646
|
+
:dialog="dialog.scanVisitorQRCode"
|
|
647
|
+
@close-dialog="dialog.scanVisitorQRCode = false"
|
|
648
|
+
/>
|
|
649
|
+
|
|
306
650
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
307
651
|
</v-row>
|
|
308
652
|
</template>
|
|
@@ -317,15 +661,15 @@ const props = defineProps({
|
|
|
317
661
|
type: Boolean,
|
|
318
662
|
default: true,
|
|
319
663
|
},
|
|
320
|
-
|
|
664
|
+
canScanVisitorQRCode: {
|
|
321
665
|
type: Boolean,
|
|
322
666
|
default: true,
|
|
323
667
|
},
|
|
324
|
-
|
|
668
|
+
canViewVisitor: {
|
|
325
669
|
type: Boolean,
|
|
326
670
|
default: true,
|
|
327
671
|
},
|
|
328
|
-
|
|
672
|
+
canUpdateVisitor: {
|
|
329
673
|
type: Boolean,
|
|
330
674
|
default: true,
|
|
331
675
|
},
|
|
@@ -335,21 +679,16 @@ const props = defineProps({
|
|
|
335
679
|
},
|
|
336
680
|
});
|
|
337
681
|
|
|
338
|
-
const {
|
|
339
|
-
|
|
340
|
-
visitorSelection,
|
|
341
|
-
typeFieldMap,
|
|
342
|
-
deleteVisitor,
|
|
343
|
-
updateVisitor,
|
|
344
|
-
} = useVisitor();
|
|
682
|
+
const { getVisitors, visitorSelection, typeFieldMap, updateVisitor } =
|
|
683
|
+
useVisitor();
|
|
345
684
|
const { debounce, formatCamelCaseToWords, formatDate, UTCToLocalTIme } =
|
|
346
685
|
useUtils();
|
|
347
686
|
const { formatLocation } = useSecurityUtils();
|
|
348
687
|
const { getFileUrlAnpr } = useFile();
|
|
349
688
|
// const { status: visitorStatus, search } = useRoute().query as { status: string, search: string};
|
|
350
689
|
|
|
351
|
-
const route = useRoute()
|
|
352
|
-
const router = useRouter()
|
|
690
|
+
const route = useRoute();
|
|
691
|
+
const router = useRouter();
|
|
353
692
|
const { org: orgId, site: siteId } = route.params as {
|
|
354
693
|
org: string;
|
|
355
694
|
site: string;
|
|
@@ -377,7 +716,6 @@ const messageColor = ref("");
|
|
|
377
716
|
const messageSnackbar = ref(false);
|
|
378
717
|
|
|
379
718
|
const loading = reactive({
|
|
380
|
-
deletingVisitor: false,
|
|
381
719
|
fetchingVisitors: false,
|
|
382
720
|
checkingOut: false,
|
|
383
721
|
savingRemarks: false,
|
|
@@ -388,15 +726,16 @@ const dialog = reactive({
|
|
|
388
726
|
showSelection: false,
|
|
389
727
|
showForm: false,
|
|
390
728
|
viewVisitor: false,
|
|
391
|
-
deleteConfirmation: false,
|
|
392
729
|
snapshotImage: false,
|
|
393
730
|
remarks: false,
|
|
394
731
|
returnPassesKeys: false,
|
|
395
|
-
addPassKey: false
|
|
732
|
+
addPassKey: false,
|
|
733
|
+
editPassKey: false,
|
|
734
|
+
scanVisitorQRCode: false,
|
|
396
735
|
});
|
|
397
736
|
|
|
398
737
|
const snapshotImageUrl = ref("");
|
|
399
|
-
const remarksType = ref<
|
|
738
|
+
const remarksType = ref<"checkIn" | "checkOut">("checkIn");
|
|
400
739
|
const remarksInput = ref("");
|
|
401
740
|
const remarksViewOnly = ref(false);
|
|
402
741
|
|
|
@@ -406,10 +745,16 @@ const headers = computed(() => [
|
|
|
406
745
|
{ title: "Location", value: "location" },
|
|
407
746
|
{ title: "Contact/Vehicle No.", value: "contact-vehicleNumber" },
|
|
408
747
|
{ title: "Check In/Out", value: "checkin-out" },
|
|
409
|
-
...(activeTab.value === "resident-transactions"
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
748
|
+
...(activeTab.value === "resident-transactions"
|
|
749
|
+
? [
|
|
750
|
+
{ title: "Check-in Remarks", value: "checkInRemarks" },
|
|
751
|
+
{ title: "Check-out Remarks", value: "checkOutRemarks" },
|
|
752
|
+
]
|
|
753
|
+
: []),
|
|
754
|
+
...(activeTab.value === "unregistered"
|
|
755
|
+
? [{ title: "Action", value: "action" }]
|
|
756
|
+
: []),
|
|
757
|
+
]);
|
|
413
758
|
|
|
414
759
|
const tabOptions = [
|
|
415
760
|
{ name: "Registered", value: "registered" },
|
|
@@ -452,7 +797,6 @@ const formattedFields = {
|
|
|
452
797
|
checkOut: "Check Out",
|
|
453
798
|
snapshotExitImage: "Exit Image",
|
|
454
799
|
remarks: "Remarks",
|
|
455
|
-
|
|
456
800
|
} as const;
|
|
457
801
|
|
|
458
802
|
function filterTypeSelectionLabel() {
|
|
@@ -465,14 +809,14 @@ const passStatusOptions = [
|
|
|
465
809
|
{ label: "Not Returned", value: "In Use" },
|
|
466
810
|
{ label: "Damaged", value: "Damaged" },
|
|
467
811
|
{ label: "Lost", value: "Lost" },
|
|
468
|
-
]
|
|
812
|
+
];
|
|
469
813
|
|
|
470
814
|
function toRoute(tab: any) {
|
|
471
|
-
items.value = []
|
|
815
|
+
items.value = [];
|
|
472
816
|
|
|
473
817
|
const obj = tabOptions.find((x) => x.value === tab);
|
|
474
818
|
if (!obj) return;
|
|
475
|
-
page.value = 1
|
|
819
|
+
page.value = 1;
|
|
476
820
|
navigateTo({
|
|
477
821
|
name: routeName,
|
|
478
822
|
params: {
|
|
@@ -483,54 +827,51 @@ function toRoute(tab: any) {
|
|
|
483
827
|
// },
|
|
484
828
|
});
|
|
485
829
|
}
|
|
486
|
-
|
|
487
830
|
const {
|
|
488
831
|
data: getVisitorReq,
|
|
489
832
|
refresh: getVisitorRefresh,
|
|
490
833
|
pending: getVisitorPending,
|
|
491
834
|
} = await useLazyAsyncData(
|
|
492
|
-
|
|
835
|
+
"get-all-visitors",
|
|
493
836
|
async () => {
|
|
494
|
-
|
|
495
|
-
// Optional delay (if needed)
|
|
496
|
-
await new Promise(resolve => setTimeout(resolve, 100))
|
|
497
|
-
|
|
498
837
|
const params: any = {
|
|
499
838
|
page: page.value,
|
|
500
839
|
site: siteId,
|
|
501
840
|
search: searchInput.value,
|
|
502
841
|
dateTo: normalizeDateOnly(dateTo.value),
|
|
503
842
|
dateFrom: normalizeDateOnly(dateFrom.value),
|
|
504
|
-
type:
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
// } else if (activeTab.value === "guests") {
|
|
511
|
-
// params.type = "guest"
|
|
512
|
-
// params.status = "pending"
|
|
513
|
-
// }
|
|
843
|
+
type:
|
|
844
|
+
filterTypes.value.length === 0 && activeTab.value === "registered"
|
|
845
|
+
? "contractor,delivery,walk-in,pick-up,drop-off,guest"
|
|
846
|
+
: filterTypes.value.filter(Boolean).join(","),
|
|
847
|
+
checkedOut: displayNotCheckedOut.value ? false : undefined,
|
|
848
|
+
};
|
|
514
849
|
|
|
515
850
|
if (activeTab.value === "guests") {
|
|
516
|
-
params.type = "guest"
|
|
517
|
-
params.status = "pending"
|
|
851
|
+
params.type = "guest";
|
|
852
|
+
params.status = "pending";
|
|
518
853
|
} else if (activeTab.value === "resident-transactions") {
|
|
519
|
-
params.status = "registered"
|
|
520
|
-
params.type = "resident,tenant"
|
|
854
|
+
params.status = "registered";
|
|
855
|
+
params.type = "resident,tenant";
|
|
521
856
|
} else {
|
|
522
|
-
params.status = activeTab.value
|
|
857
|
+
params.status = activeTab.value;
|
|
523
858
|
}
|
|
524
859
|
|
|
525
|
-
return await getVisitors(params)
|
|
860
|
+
return await getVisitors(params);
|
|
526
861
|
},
|
|
527
862
|
{
|
|
528
863
|
watch: [
|
|
529
864
|
page,
|
|
865
|
+
searchInput,
|
|
866
|
+
dateFrom,
|
|
867
|
+
dateTo,
|
|
868
|
+
filterTypes,
|
|
869
|
+
activeTab,
|
|
870
|
+
displayNotCheckedOut,
|
|
530
871
|
],
|
|
531
|
-
immediate:
|
|
872
|
+
immediate: true,
|
|
532
873
|
}
|
|
533
|
-
)
|
|
874
|
+
);
|
|
534
875
|
|
|
535
876
|
watch(getVisitorReq, (newData: any) => {
|
|
536
877
|
if (newData) {
|
|
@@ -542,11 +883,16 @@ watch(getVisitorReq, (newData: any) => {
|
|
|
542
883
|
});
|
|
543
884
|
|
|
544
885
|
const selectedVisitorObject = computed(() => {
|
|
545
|
-
|
|
546
886
|
const obj = items.value.find((x: any) => x?._id === selectedVisitorId.value);
|
|
547
887
|
if (!obj) return {};
|
|
548
888
|
const type = obj?.type as TVisitorType | undefined;
|
|
549
|
-
let includedKeys: string[] = [
|
|
889
|
+
let includedKeys: string[] = [
|
|
890
|
+
"checkIn",
|
|
891
|
+
"checkOut",
|
|
892
|
+
"plateNumber",
|
|
893
|
+
"snapshotEntryImage",
|
|
894
|
+
"snapshotExitImage",
|
|
895
|
+
];
|
|
550
896
|
includedKeys.unshift(...(typeFieldMap[type] ?? []));
|
|
551
897
|
return Object.fromEntries(
|
|
552
898
|
Object.entries(obj).filter(([key]) => includedKeys.includes(key))
|
|
@@ -567,8 +913,6 @@ function formatValues(key: string, value: any) {
|
|
|
567
913
|
return value;
|
|
568
914
|
}
|
|
569
915
|
|
|
570
|
-
|
|
571
|
-
|
|
572
916
|
function handleAddNew() {
|
|
573
917
|
dialog.showSelection = true;
|
|
574
918
|
activeVisitorFormType.value = null;
|
|
@@ -588,9 +932,17 @@ function showAddPassKeyButton(item: any) {
|
|
|
588
932
|
const hasPasses = (item?.visitorPass?.length ?? 0) > 0;
|
|
589
933
|
const hasKeys = (item?.passKeys?.length ?? 0) > 0;
|
|
590
934
|
|
|
591
|
-
const isTypeWithPassKey = ["contractor", "guest", "walk-in"].includes(
|
|
935
|
+
const isTypeWithPassKey = ["contractor", "guest", "walk-in"].includes(
|
|
936
|
+
item?.type
|
|
937
|
+
);
|
|
592
938
|
|
|
593
|
-
return
|
|
939
|
+
return (
|
|
940
|
+
!hasPasses &&
|
|
941
|
+
!hasKeys &&
|
|
942
|
+
isTypeWithPassKey &&
|
|
943
|
+
item?.status === "registered" &&
|
|
944
|
+
!item?.checkOut
|
|
945
|
+
);
|
|
594
946
|
}
|
|
595
947
|
|
|
596
948
|
function handleSelectVisitorType(type: TVisitorType) {
|
|
@@ -616,11 +968,6 @@ function handleVisitorFormCreateMore() {
|
|
|
616
968
|
dialog.showForm = false;
|
|
617
969
|
}
|
|
618
970
|
|
|
619
|
-
function handleDeleteVisitor() {
|
|
620
|
-
dialog.deleteConfirmation = true;
|
|
621
|
-
dialog.viewVisitor = false;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
971
|
function showMessage(msg: string, color: string) {
|
|
625
972
|
message.value = msg;
|
|
626
973
|
messageColor.value = color;
|
|
@@ -650,10 +997,19 @@ function handleOpenAddPassKey(item: any) {
|
|
|
650
997
|
dialog.addPassKey = true;
|
|
651
998
|
}
|
|
652
999
|
|
|
653
|
-
function
|
|
1000
|
+
function handleOpenEditPassKey(item: any) {
|
|
1001
|
+
selectedVisitorId.value = item?._id;
|
|
1002
|
+
if (selectedVisitorDataObject.value.checkOut) {
|
|
1003
|
+
dialog.returnPassesKeys = true;
|
|
1004
|
+
handleCheckout(selectedVisitorId.value as string);
|
|
1005
|
+
} else dialog.editPassKey = true;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
function handleOpenRemarks(item: any, type: "checkIn" | "checkOut") {
|
|
654
1009
|
selectedVisitorId.value = item?._id;
|
|
655
1010
|
remarksType.value = type;
|
|
656
|
-
const existingRemarks =
|
|
1011
|
+
const existingRemarks =
|
|
1012
|
+
type === "checkIn" ? item?.checkInRemarks : item?.checkOutRemarks;
|
|
657
1013
|
remarksInput.value = existingRemarks || "";
|
|
658
1014
|
remarksViewOnly.value = !!existingRemarks;
|
|
659
1015
|
dialog.remarks = true;
|
|
@@ -663,16 +1019,20 @@ async function handleSaveRemarks() {
|
|
|
663
1019
|
if (!remarksInput.value.trim() || !selectedVisitorId.value) return;
|
|
664
1020
|
try {
|
|
665
1021
|
loading.savingRemarks = true;
|
|
666
|
-
const payload =
|
|
667
|
-
|
|
668
|
-
|
|
1022
|
+
const payload =
|
|
1023
|
+
remarksType.value === "checkIn"
|
|
1024
|
+
? { checkInRemarks: remarksInput.value.trim() }
|
|
1025
|
+
: { checkOutRemarks: remarksInput.value.trim() };
|
|
669
1026
|
await updateVisitor(selectedVisitorId.value, payload);
|
|
670
1027
|
showMessage("Remarks saved successfully!", "info");
|
|
671
1028
|
await getVisitorRefresh();
|
|
672
1029
|
dialog.remarks = false;
|
|
673
1030
|
} catch (error: any) {
|
|
674
1031
|
const errorMessage = error?.response?._data?.message;
|
|
675
|
-
showMessage(
|
|
1032
|
+
showMessage(
|
|
1033
|
+
errorMessage || "Something went wrong. Please try again later.",
|
|
1034
|
+
"error"
|
|
1035
|
+
);
|
|
676
1036
|
} finally {
|
|
677
1037
|
loading.savingRemarks = false;
|
|
678
1038
|
}
|
|
@@ -684,11 +1044,19 @@ async function handleUpdatePassKeys() {
|
|
|
684
1044
|
loading.updatingPassKeys = true;
|
|
685
1045
|
const payload: any = {};
|
|
686
1046
|
if (passReturnStatuses.value.length > 0) {
|
|
687
|
-
const passReturnStatusesPayload = passReturnStatuses.value.map(p => ({
|
|
1047
|
+
const passReturnStatusesPayload = passReturnStatuses.value.map((p) => ({
|
|
1048
|
+
keyId: p.keyId,
|
|
1049
|
+
status: p.status,
|
|
1050
|
+
remarks: p.remarks,
|
|
1051
|
+
}));
|
|
688
1052
|
payload.visitorPass = passReturnStatusesPayload;
|
|
689
1053
|
}
|
|
690
1054
|
if (keyReturnStatuses.value.length > 0) {
|
|
691
|
-
const keyReturnStatusesPayload = keyReturnStatuses.value.map(k => ({
|
|
1055
|
+
const keyReturnStatusesPayload = keyReturnStatuses.value.map((k) => ({
|
|
1056
|
+
keyId: k.keyId,
|
|
1057
|
+
status: k.status,
|
|
1058
|
+
remarks: k.remarks,
|
|
1059
|
+
}));
|
|
692
1060
|
payload.passKeys = keyReturnStatusesPayload;
|
|
693
1061
|
}
|
|
694
1062
|
await updateVisitor(selectedVisitorId.value, payload);
|
|
@@ -696,50 +1064,35 @@ async function handleUpdatePassKeys() {
|
|
|
696
1064
|
await getVisitorRefresh();
|
|
697
1065
|
} catch (error: any) {
|
|
698
1066
|
const errorMessage = error?.response?._data?.message;
|
|
699
|
-
showMessage(errorMessage || "Something went wrong. Please try again later.", "error");
|
|
700
|
-
} finally {
|
|
701
|
-
loading.updatingPassKeys = false;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
async function handleProceedDeleteVisitor() {
|
|
707
|
-
try {
|
|
708
|
-
loading.deletingVisitor = true;
|
|
709
|
-
const userId = selectedVisitorId.value;
|
|
710
|
-
const res = await deleteVisitor(userId as string);
|
|
711
|
-
if (res) {
|
|
712
|
-
showMessage("Visitor successfully deleted!", "info");
|
|
713
|
-
await getVisitorRefresh();
|
|
714
|
-
dialog.deleteConfirmation = false;
|
|
715
|
-
}
|
|
716
|
-
} catch (error: any) {
|
|
717
|
-
const errorMessage = error?.response?._data?.message;
|
|
718
|
-
console.log("[ERROR]", error);
|
|
719
1067
|
showMessage(
|
|
720
1068
|
errorMessage || "Something went wrong. Please try again later.",
|
|
721
1069
|
"error"
|
|
722
1070
|
);
|
|
723
1071
|
} finally {
|
|
724
|
-
loading.
|
|
1072
|
+
loading.updatingPassKeys = false;
|
|
725
1073
|
}
|
|
726
1074
|
}
|
|
727
1075
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
1076
|
+
const passReturnStatuses = ref<
|
|
1077
|
+
{ keyId: string; status: string; prefixAndName: string; remarks: string }[]
|
|
1078
|
+
>([]);
|
|
1079
|
+
const keyReturnStatuses = ref<
|
|
1080
|
+
{ keyId: string; status: string; prefixAndName: string; remarks: string }[]
|
|
1081
|
+
>([]);
|
|
731
1082
|
|
|
732
1083
|
const canConfirmCheckout = computed(() => {
|
|
733
1084
|
const allEntries = [...passReturnStatuses.value, ...keyReturnStatuses.value];
|
|
734
1085
|
return allEntries.every((entry) => {
|
|
735
|
-
if (!entry.status || entry.status ===
|
|
736
|
-
if (
|
|
1086
|
+
if (!entry.status || entry.status === "In Use") return false;
|
|
1087
|
+
if (
|
|
1088
|
+
(entry.status === "Lost" || entry.status === "Damaged") &&
|
|
1089
|
+
!entry.remarks.trim()
|
|
1090
|
+
)
|
|
1091
|
+
return false;
|
|
737
1092
|
return true;
|
|
738
1093
|
});
|
|
739
1094
|
});
|
|
740
1095
|
|
|
741
|
-
|
|
742
|
-
|
|
743
1096
|
function handleCheckout(userId: string) {
|
|
744
1097
|
if (!userId) {
|
|
745
1098
|
showMessage("Invalid userId", "error");
|
|
@@ -753,8 +1106,22 @@ function handleCheckout(userId: string) {
|
|
|
753
1106
|
|
|
754
1107
|
if (hasPasses || hasKeys) {
|
|
755
1108
|
const visitor = items.value.find((x: any) => x?._id === userId);
|
|
756
|
-
passReturnStatuses.value = ((visitor?.visitorPass as any[]) || []).map(
|
|
757
|
-
|
|
1109
|
+
passReturnStatuses.value = ((visitor?.visitorPass as any[]) || []).map(
|
|
1110
|
+
(p: any) => ({
|
|
1111
|
+
keyId: p.keyId,
|
|
1112
|
+
status: p.status ?? "In Use",
|
|
1113
|
+
remarks: "",
|
|
1114
|
+
prefixAndName: p.prefixAndName,
|
|
1115
|
+
})
|
|
1116
|
+
);
|
|
1117
|
+
keyReturnStatuses.value = ((visitor?.passKeys as any[]) || []).map(
|
|
1118
|
+
(k: any) => ({
|
|
1119
|
+
keyId: k.keyId,
|
|
1120
|
+
status: k.status ?? "In Use",
|
|
1121
|
+
remarks: "",
|
|
1122
|
+
prefixAndName: k.prefixAndName,
|
|
1123
|
+
})
|
|
1124
|
+
);
|
|
758
1125
|
dialog.returnPassesKeys = true;
|
|
759
1126
|
return;
|
|
760
1127
|
}
|
|
@@ -769,13 +1136,25 @@ async function proceedCheckout() {
|
|
|
769
1136
|
try {
|
|
770
1137
|
loading.checkingOut = true;
|
|
771
1138
|
|
|
772
|
-
const passReturnStatusesPayload = passReturnStatuses.value.map(p => ({
|
|
773
|
-
|
|
1139
|
+
const passReturnStatusesPayload = passReturnStatuses.value.map((p) => ({
|
|
1140
|
+
keyId: p.keyId,
|
|
1141
|
+
status: p.status,
|
|
1142
|
+
remarks: p.remarks,
|
|
1143
|
+
}));
|
|
1144
|
+
const keyReturnStatusesPayload = keyReturnStatuses.value.map((k) => ({
|
|
1145
|
+
keyId: k.keyId,
|
|
1146
|
+
status: k.status,
|
|
1147
|
+
remarks: k.remarks,
|
|
1148
|
+
}));
|
|
774
1149
|
|
|
775
1150
|
const res = await updateVisitor(userId as string, {
|
|
776
1151
|
checkOut: new Date().toISOString(),
|
|
777
|
-
...(passReturnStatuses.value.length > 0
|
|
778
|
-
|
|
1152
|
+
...(passReturnStatuses.value.length > 0
|
|
1153
|
+
? { visitorPass: passReturnStatusesPayload }
|
|
1154
|
+
: {}),
|
|
1155
|
+
...(keyReturnStatuses.value.length > 0
|
|
1156
|
+
? { passKeys: keyReturnStatusesPayload }
|
|
1157
|
+
: {}),
|
|
779
1158
|
});
|
|
780
1159
|
if (res) {
|
|
781
1160
|
showMessage("Visitor successfully checked-out!", "info");
|
|
@@ -795,11 +1174,23 @@ async function proceedCheckout() {
|
|
|
795
1174
|
}
|
|
796
1175
|
}
|
|
797
1176
|
|
|
798
|
-
|
|
799
|
-
|
|
800
1177
|
const updateRouteQuery = debounce(
|
|
801
1178
|
// (search: string, from: string, to: string, types: string[], status, checkedOut) => {
|
|
802
|
-
({
|
|
1179
|
+
({
|
|
1180
|
+
search,
|
|
1181
|
+
from,
|
|
1182
|
+
to,
|
|
1183
|
+
types,
|
|
1184
|
+
tab,
|
|
1185
|
+
checkedOut,
|
|
1186
|
+
}: {
|
|
1187
|
+
search: string;
|
|
1188
|
+
from: string;
|
|
1189
|
+
to: string;
|
|
1190
|
+
types: string[];
|
|
1191
|
+
tab: string;
|
|
1192
|
+
checkedOut: boolean;
|
|
1193
|
+
}) => {
|
|
803
1194
|
router.replace({
|
|
804
1195
|
query: {
|
|
805
1196
|
...route.query,
|
|
@@ -808,7 +1199,9 @@ const updateRouteQuery = debounce(
|
|
|
808
1199
|
dateTo: to || undefined,
|
|
809
1200
|
type: types.filter(Boolean).join(",") || undefined,
|
|
810
1201
|
tab: tab || undefined,
|
|
811
|
-
...(checkedOut === true
|
|
1202
|
+
...(checkedOut === true
|
|
1203
|
+
? { checkedOut: "true" }
|
|
1204
|
+
: { checkedOut: undefined }),
|
|
812
1205
|
},
|
|
813
1206
|
});
|
|
814
1207
|
},
|
|
@@ -824,22 +1217,33 @@ watch(
|
|
|
824
1217
|
dateTo.value = normalizedTo;
|
|
825
1218
|
|
|
826
1219
|
// updateRouteQuery(search, from, to, types, status, checkedOut)
|
|
827
|
-
updateRouteQuery({
|
|
828
|
-
|
|
1220
|
+
updateRouteQuery({
|
|
1221
|
+
search,
|
|
1222
|
+
from: normalizedFrom,
|
|
1223
|
+
to: normalizedTo,
|
|
1224
|
+
types,
|
|
1225
|
+
tab,
|
|
1226
|
+
checkedOut,
|
|
1227
|
+
});
|
|
1228
|
+
// getVisitorRefresh();
|
|
829
1229
|
},
|
|
830
1230
|
{ deep: true }
|
|
831
1231
|
);
|
|
832
|
-
|
|
833
|
-
|
|
1232
|
+
watch([searchInput, dateFrom, dateTo, filterTypes, activeTab], () => {
|
|
1233
|
+
page.value = 1;
|
|
1234
|
+
});
|
|
834
1235
|
|
|
835
1236
|
onMounted(() => {
|
|
836
|
-
activeTab.value = (route.query.tab as string) || "unregistered"
|
|
1237
|
+
activeTab.value = (route.query.tab as string) || "unregistered";
|
|
837
1238
|
searchInput.value = (route.query.search as string) || "";
|
|
838
1239
|
dateFrom.value = normalizeDateOnly((route.query.dateFrom as string) || "");
|
|
839
1240
|
dateTo.value = normalizeDateOnly((route.query.dateTo as string) || "");
|
|
840
|
-
filterTypes.value = ((route.query.type as string)?.split(",") || []).filter(
|
|
841
|
-
|
|
842
|
-
|
|
1241
|
+
filterTypes.value = ((route.query.type as string)?.split(",") || []).filter(
|
|
1242
|
+
Boolean
|
|
1243
|
+
) as TVisitorType[];
|
|
1244
|
+
displayNotCheckedOut.value =
|
|
1245
|
+
(route.query.checkedOut as string) == "true" || false;
|
|
1246
|
+
});
|
|
843
1247
|
</script>
|
|
844
1248
|
|
|
845
1249
|
<style scoped></style>
|