@7365admin1/layer-common 1.11.20 → 1.11.22
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 +12 -0
- package/components/AddPassKeyToVisitor.vue +117 -56
- package/components/AreaMain.vue +10 -8
- 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/UnitMain.vue +10 -8
- package/components/VisitorForm.vue +1 -1
- package/components/VisitorManagement.vue +598 -229
- package/composables/useOrg.ts +16 -0
- package/composables/useVisitor.ts +12 -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,220 @@
|
|
|
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="!item.checkOut && (item?.status === 'registered' || item?.status === 'unregistered') && canCheckoutVisitor">
|
|
173
|
+
<span class="d-flex align-center ga-2" v-bind="menuProps" style="cursor:pointer">
|
|
174
|
+
<v-icon icon="mdi-clock-time-eight-outline" color="red" size="20" />
|
|
175
|
+
<v-btn size="x-small" class="text-capitalize" color="red" text="Checkout"
|
|
176
|
+
:loading="loading.checkingOut && item?._id === selectedVisitorId" @click.stop="handleCheckout(item._id)" />
|
|
177
|
+
</span>
|
|
178
|
+
</span>
|
|
179
|
+
<span v-else class="d-flex align-center ga-2">
|
|
101
180
|
<v-icon icon="mdi-clock-time-eight-outline" color="red" size="20" />
|
|
102
181
|
<template v-if="item.checkOut">
|
|
103
182
|
<span class="text-capitalize">{{
|
|
104
183
|
UTCToLocalTIme(item.checkOut) || "-"
|
|
105
184
|
}}</span>
|
|
106
185
|
<span>
|
|
107
|
-
<v-icon
|
|
108
|
-
|
|
186
|
+
<v-icon
|
|
187
|
+
v-if="item?.snapshotExitImage"
|
|
188
|
+
size="17"
|
|
189
|
+
icon="mdi-image"
|
|
190
|
+
@click.stop="handleViewImage(item.snapshotExitImage)"
|
|
191
|
+
/>
|
|
109
192
|
</span>
|
|
110
193
|
<span v-if="item?.manualCheckout">
|
|
111
|
-
<TooltipInfo
|
|
194
|
+
<TooltipInfo
|
|
195
|
+
text="Manual Checkout"
|
|
196
|
+
density="compact"
|
|
197
|
+
size="x-small"
|
|
198
|
+
/>
|
|
112
199
|
</span>
|
|
113
200
|
</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
201
|
</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
202
|
|
|
203
|
+
<div
|
|
204
|
+
v-if="showAddPassKeyButton(item)"
|
|
205
|
+
class="d-flex flex-wrap ga-1 mt-1"
|
|
206
|
+
v-bind="menuProps"
|
|
207
|
+
style="cursor: pointer"
|
|
208
|
+
>
|
|
209
|
+
<v-chip
|
|
210
|
+
size="x-small"
|
|
211
|
+
variant="tonal"
|
|
212
|
+
prepend-icon="mdi-key"
|
|
213
|
+
@click.stop="handleOpenAddPassKey(item)"
|
|
214
|
+
>Add Pass/Key</v-chip
|
|
215
|
+
>
|
|
137
216
|
</div>
|
|
217
|
+
|
|
218
|
+
<v-menu :close-on-content-click="true">
|
|
219
|
+
<template v-slot:activator="{ props: menuProps }">
|
|
220
|
+
<div
|
|
221
|
+
v-if="
|
|
222
|
+
(item.visitorPass?.length ?? 0) > 0 ||
|
|
223
|
+
(item.passKeys?.length ?? 0) > 0
|
|
224
|
+
"
|
|
225
|
+
class="d-flex flex-wrap ga-1 mt-1"
|
|
226
|
+
v-bind="menuProps"
|
|
227
|
+
style="cursor: pointer"
|
|
228
|
+
>
|
|
229
|
+
<v-chip
|
|
230
|
+
v-for="pass in item.visitorPass"
|
|
231
|
+
:key="(pass as any)._id ?? (pass as any).keyId"
|
|
232
|
+
prepend-icon="mdi-card-bulleted-outline"
|
|
233
|
+
size="x-small"
|
|
234
|
+
variant="tonal"
|
|
235
|
+
color="blue"
|
|
236
|
+
>
|
|
237
|
+
{{ (pass as any)?.prefixAndName }}
|
|
238
|
+
</v-chip>
|
|
239
|
+
<v-chip
|
|
240
|
+
v-for="key in item.passKeys"
|
|
241
|
+
:key="(key as any)._id ?? (key as any).keyId"
|
|
242
|
+
prepend-icon="mdi-key"
|
|
243
|
+
size="x-small"
|
|
244
|
+
variant="tonal"
|
|
245
|
+
color="orange"
|
|
246
|
+
>
|
|
247
|
+
{{ (key as any)?.prefixAndName }}
|
|
248
|
+
</v-chip>
|
|
249
|
+
</div>
|
|
250
|
+
</template>
|
|
251
|
+
<v-list density="compact">
|
|
252
|
+
<v-list-item v-if="!item?.checkOut" prepend-icon="mdi-logout" title="Checkout" @click.stop="handleCheckout(item._id)" />
|
|
253
|
+
<v-list-item v-if="(item.visitorPass?.length ?? 0) > 0 || (item.passKeys?.length ?? 0) > 0"
|
|
254
|
+
prepend-icon="mdi-card-bulleted-outline" :title="item.checkOut ? 'View Pass/Key' :'Edit Pass/Key'"
|
|
255
|
+
@click.stop="handleOpenEditPassKey(item)" />
|
|
256
|
+
<v-list-item v-if="showAddPassKeyButton(item)" prepend-icon="mdi-plus" title="Add Pass/Key"
|
|
257
|
+
@click.stop="handleOpenAddPassKey(item)" />
|
|
258
|
+
</v-list>
|
|
259
|
+
</v-menu>
|
|
138
260
|
</v-row>
|
|
139
261
|
</template>
|
|
140
262
|
|
|
141
263
|
<template v-slot:item.action="{ item }">
|
|
142
|
-
<v-btn
|
|
143
|
-
|
|
264
|
+
<v-btn
|
|
265
|
+
v-if="activeTab === 'unregistered' && canAddVisitor"
|
|
266
|
+
size="small"
|
|
267
|
+
color="primary"
|
|
268
|
+
text="Register"
|
|
269
|
+
@click.stop="handleRegistrationUnregisteredVisitor(item)"
|
|
270
|
+
/>
|
|
144
271
|
</template>
|
|
145
272
|
|
|
146
273
|
<template v-slot:item.checkInRemarks="{ item }">
|
|
147
274
|
<span>
|
|
148
|
-
<v-btn
|
|
149
|
-
|
|
150
|
-
|
|
275
|
+
<v-btn
|
|
276
|
+
variant="text"
|
|
277
|
+
color="blue-darken-2"
|
|
278
|
+
density="compact"
|
|
279
|
+
size="small"
|
|
280
|
+
@click.stop="handleOpenRemarks(item, 'checkIn')"
|
|
281
|
+
>{{ item.checkInRemarks ? "View Remarks" : "Add Remarks" }}</v-btn
|
|
282
|
+
>
|
|
151
283
|
</span>
|
|
152
284
|
</template>
|
|
153
285
|
<template v-slot:item.checkOutRemarks="{ item }">
|
|
154
286
|
<span>
|
|
155
|
-
<v-btn
|
|
156
|
-
|
|
157
|
-
|
|
287
|
+
<v-btn
|
|
288
|
+
variant="text"
|
|
289
|
+
color="blue-darken-2"
|
|
290
|
+
density="compact"
|
|
291
|
+
size="small"
|
|
292
|
+
@click.stop="handleOpenRemarks(item, 'checkOut')"
|
|
293
|
+
>{{ item.checkOutRemarks ? "View Remarks" : "Add Remarks" }}</v-btn
|
|
294
|
+
>
|
|
158
295
|
</span>
|
|
159
296
|
</template>
|
|
160
|
-
|
|
161
297
|
</TableMain>
|
|
162
298
|
|
|
163
299
|
<v-dialog v-model="dialog.showSelection" width="450" persistent>
|
|
164
|
-
<VisitorFormSelection
|
|
300
|
+
<VisitorFormSelection
|
|
301
|
+
:mode="mode"
|
|
302
|
+
@cancel="dialog.showSelection = false"
|
|
303
|
+
@select="handleSelectVisitorType"
|
|
304
|
+
/>
|
|
165
305
|
</v-dialog>
|
|
166
306
|
|
|
167
|
-
<v-dialog
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
307
|
+
<v-dialog
|
|
308
|
+
v-model="dialog.showForm"
|
|
309
|
+
v-if="activeVisitorFormType"
|
|
310
|
+
width="450"
|
|
311
|
+
persistent
|
|
312
|
+
>
|
|
313
|
+
<VisitorForm
|
|
314
|
+
:mode="mode"
|
|
315
|
+
:org="orgId"
|
|
316
|
+
:site="siteId"
|
|
317
|
+
:visitor-data="selectedVisitorDataObject"
|
|
318
|
+
:type="activeVisitorFormType"
|
|
319
|
+
@back="handleClickBack"
|
|
320
|
+
@done="handleVisitorFormDone"
|
|
321
|
+
@done:more="handleVisitorFormCreateMore"
|
|
322
|
+
@close:all="handleCloseAll"
|
|
323
|
+
/>
|
|
171
324
|
</v-dialog>
|
|
172
325
|
|
|
173
326
|
<v-dialog v-model="dialog.viewVisitor" width="450" persistent>
|
|
174
|
-
<VehicleUpdateMoreAction
|
|
175
|
-
|
|
176
|
-
|
|
327
|
+
<VehicleUpdateMoreAction
|
|
328
|
+
title="Preview"
|
|
329
|
+
:can-update="false"
|
|
330
|
+
:can-delete="false"
|
|
331
|
+
@close="dialog.viewVisitor = false"
|
|
332
|
+
edit-button-label="Edit Visitor"
|
|
333
|
+
>
|
|
177
334
|
<template v-slot:content>
|
|
178
335
|
<v-row no-gutters class="mb-4">
|
|
179
336
|
<v-col v-for="(label, key) in formattedFields" :key="key" cols="12">
|
|
180
|
-
<span
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
337
|
+
<span
|
|
338
|
+
v-if="
|
|
339
|
+
key === 'checkOut' &&
|
|
340
|
+
!selectedVisitorObject[key] &&
|
|
341
|
+
canCheckoutVisitor
|
|
342
|
+
"
|
|
343
|
+
class="d-flex align-center"
|
|
344
|
+
>
|
|
185
345
|
<strong>{{ label }}:</strong>
|
|
186
|
-
<v-btn
|
|
187
|
-
|
|
346
|
+
<v-btn
|
|
347
|
+
size="x-small"
|
|
348
|
+
class="ml-3 text-capitalize"
|
|
349
|
+
color="red"
|
|
350
|
+
text="Checkout"
|
|
351
|
+
:disabled="loading.checkingOut"
|
|
352
|
+
@click="handleCheckout(selectedVisitorId as string)"
|
|
353
|
+
/>
|
|
188
354
|
</span>
|
|
189
355
|
|
|
190
|
-
<span
|
|
191
|
-
|
|
356
|
+
<span
|
|
357
|
+
v-else-if="selectedVisitorObject[key]"
|
|
358
|
+
class="d-flex ga-3 align-center"
|
|
359
|
+
><strong>{{ label }}:</strong>
|
|
192
360
|
{{ formatValues(key, selectedVisitorObject[key]) }}
|
|
193
|
-
<TooltipInfo
|
|
361
|
+
<TooltipInfo
|
|
362
|
+
v-if="key === 'checkOut'"
|
|
363
|
+
text="Manual Checkout"
|
|
364
|
+
density="compact"
|
|
365
|
+
size="x-small"
|
|
366
|
+
/>
|
|
194
367
|
</span>
|
|
195
368
|
</v-col>
|
|
196
369
|
</v-row>
|
|
@@ -198,17 +371,15 @@
|
|
|
198
371
|
</VehicleUpdateMoreAction>
|
|
199
372
|
</v-dialog>
|
|
200
373
|
|
|
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
374
|
<v-dialog v-model="dialog.snapshotImage" max-width="700">
|
|
208
375
|
<v-card>
|
|
209
376
|
<v-card-title class="d-flex justify-space-between align-center">
|
|
210
377
|
<span>Snapshot</span>
|
|
211
|
-
<v-btn
|
|
378
|
+
<v-btn
|
|
379
|
+
icon="mdi-close"
|
|
380
|
+
variant="text"
|
|
381
|
+
@click="dialog.snapshotImage = false"
|
|
382
|
+
/>
|
|
212
383
|
</v-card-title>
|
|
213
384
|
<v-card-text class="pa-2 d-flex justify-center">
|
|
214
385
|
<v-img :src="snapshotImageUrl" max-height="600" contain />
|
|
@@ -218,23 +389,51 @@
|
|
|
218
389
|
|
|
219
390
|
<v-dialog v-model="dialog.remarks" max-width="450" persistent>
|
|
220
391
|
<v-card>
|
|
221
|
-
<v-card-title
|
|
222
|
-
|
|
223
|
-
|
|
392
|
+
<v-card-title
|
|
393
|
+
class="d-flex justify-space-between align-center pt-4 px-4"
|
|
394
|
+
>
|
|
395
|
+
<span>{{
|
|
396
|
+
remarksType === "checkIn" ? "Check-In Remarks" : "Check-Out Remarks"
|
|
397
|
+
}}</span>
|
|
398
|
+
<v-btn
|
|
399
|
+
icon="mdi-close"
|
|
400
|
+
variant="text"
|
|
401
|
+
@click="dialog.remarks = false"
|
|
402
|
+
/>
|
|
224
403
|
</v-card-title>
|
|
225
404
|
<v-card-text class="px-4 pb-2">
|
|
226
|
-
<v-textarea
|
|
227
|
-
|
|
405
|
+
<v-textarea
|
|
406
|
+
v-model="remarksInput"
|
|
407
|
+
label="Remarks"
|
|
408
|
+
rows="4"
|
|
409
|
+
auto-grow
|
|
410
|
+
variant="outlined"
|
|
411
|
+
:readonly="remarksViewOnly"
|
|
412
|
+
/>
|
|
228
413
|
</v-card-text>
|
|
229
414
|
<v-toolbar class="pa-0" density="compact">
|
|
230
415
|
<v-row no-gutters>
|
|
231
416
|
<v-col cols="6">
|
|
232
|
-
<v-btn
|
|
233
|
-
|
|
417
|
+
<v-btn
|
|
418
|
+
variant="text"
|
|
419
|
+
block
|
|
420
|
+
:disabled="loading.savingRemarks"
|
|
421
|
+
@click="dialog.remarks = false"
|
|
422
|
+
>Cancel</v-btn
|
|
423
|
+
>
|
|
234
424
|
</v-col>
|
|
235
425
|
<v-col cols="6">
|
|
236
|
-
<v-btn
|
|
237
|
-
|
|
426
|
+
<v-btn
|
|
427
|
+
color="primary"
|
|
428
|
+
variant="flat"
|
|
429
|
+
height="48"
|
|
430
|
+
rounded="0"
|
|
431
|
+
block
|
|
432
|
+
:loading="loading.savingRemarks"
|
|
433
|
+
:disabled="!remarksInput.trim()"
|
|
434
|
+
@click="handleSaveRemarks"
|
|
435
|
+
>Save</v-btn
|
|
436
|
+
>
|
|
238
437
|
</v-col>
|
|
239
438
|
</v-row>
|
|
240
439
|
</v-toolbar>
|
|
@@ -244,54 +443,134 @@
|
|
|
244
443
|
<v-dialog v-model="dialog.returnPassesKeys" max-width="450" persistent>
|
|
245
444
|
<v-card>
|
|
246
445
|
<v-toolbar density="compact" color="">
|
|
247
|
-
<v-row
|
|
446
|
+
<v-row
|
|
447
|
+
no-gutters
|
|
448
|
+
class="d-flex fill-height justify-space-between align-center px-4"
|
|
449
|
+
>
|
|
248
450
|
<span class="font-weight-bold">Return Passes & Keys</span>
|
|
249
|
-
<v-btn
|
|
451
|
+
<v-btn
|
|
452
|
+
icon="mdi-close"
|
|
453
|
+
variant="text"
|
|
454
|
+
@click="dialog.returnPassesKeys = false"
|
|
455
|
+
/>
|
|
250
456
|
</v-row>
|
|
251
457
|
</v-toolbar>
|
|
252
458
|
<v-card-text class="px-4 pb-2">
|
|
253
|
-
<p class="text-body-2 mb-3">
|
|
459
|
+
<p class="text-body-2 mb-3">
|
|
460
|
+
Please ensure all passes and keys are returned before checking out.
|
|
461
|
+
</p>
|
|
254
462
|
<div v-if="passReturnStatuses.length > 0" class="mb-2">
|
|
255
463
|
<p class="text-caption text-medium-emphasis mb-1">Passes</p>
|
|
256
|
-
<v-row
|
|
257
|
-
|
|
258
|
-
|
|
464
|
+
<v-row
|
|
465
|
+
no-gutters
|
|
466
|
+
v-for="pass in passReturnStatuses"
|
|
467
|
+
:key="(pass as any)._id ?? (pass as any).keyId"
|
|
468
|
+
class="d-flex flex-wrap justify-space-between align-center ga-5 mb-2"
|
|
469
|
+
>
|
|
470
|
+
<v-chip
|
|
471
|
+
prepend-icon="mdi-card-bulleted-outline"
|
|
472
|
+
size="small"
|
|
473
|
+
variant="tonal"
|
|
474
|
+
color="blue"
|
|
475
|
+
>
|
|
259
476
|
{{ (pass as any)?.prefixAndName }}
|
|
260
477
|
</v-chip>
|
|
261
|
-
<v-select
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
density="compact"
|
|
478
|
+
<v-select
|
|
479
|
+
hide-details
|
|
480
|
+
max-width="200px"
|
|
481
|
+
density="compact"
|
|
482
|
+
:items="passStatusOptions"
|
|
483
|
+
item-title="label"
|
|
484
|
+
item-value="value"
|
|
485
|
+
v-model="pass.status"
|
|
486
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
487
|
+
></v-select>
|
|
488
|
+
<v-textarea
|
|
489
|
+
v-if="pass.status === 'Lost' || pass.status === 'Damaged'"
|
|
490
|
+
no-resize
|
|
491
|
+
rows="3"
|
|
492
|
+
class="w-100"
|
|
493
|
+
density="compact"
|
|
494
|
+
v-model="pass.remarks"
|
|
495
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
496
|
+
></v-textarea>
|
|
265
497
|
</v-row>
|
|
266
498
|
</div>
|
|
267
|
-
<div v-if="
|
|
499
|
+
<div v-if="keyReturnStatuses.length > 0" class="mb-2">
|
|
268
500
|
<p class="text-caption text-medium-emphasis mb-1">Keys</p>
|
|
269
|
-
<v-row
|
|
270
|
-
|
|
271
|
-
|
|
501
|
+
<v-row
|
|
502
|
+
no-gutters
|
|
503
|
+
v-for="key in keyReturnStatuses"
|
|
504
|
+
:key="(key as any)._id ?? (key as any).keyId"
|
|
505
|
+
class="d-flex flex-wrap justify-space-between align-center ga-5 mb-2"
|
|
506
|
+
>
|
|
507
|
+
<v-chip
|
|
508
|
+
prepend-icon="mdi-key"
|
|
509
|
+
size="small"
|
|
510
|
+
variant="tonal"
|
|
511
|
+
color="orange"
|
|
512
|
+
>
|
|
272
513
|
{{ (key as any)?.prefixAndName }}
|
|
273
514
|
</v-chip>
|
|
274
|
-
<v-select
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
density="compact"
|
|
515
|
+
<v-select
|
|
516
|
+
hide-details
|
|
517
|
+
max-width="200px"
|
|
518
|
+
density="compact"
|
|
519
|
+
:items="passStatusOptions"
|
|
520
|
+
item-title="label"
|
|
521
|
+
item-value="value"
|
|
522
|
+
v-model="key.status"
|
|
523
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
524
|
+
></v-select>
|
|
525
|
+
<v-textarea
|
|
526
|
+
v-if="key.status === 'Lost' || key.status === 'Damaged'"
|
|
527
|
+
no-resize
|
|
528
|
+
rows="3"
|
|
529
|
+
class="w-100"
|
|
530
|
+
density="compact"
|
|
531
|
+
v-model="key.remarks"
|
|
532
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
533
|
+
></v-textarea>
|
|
278
534
|
</v-row>
|
|
279
535
|
</div>
|
|
280
536
|
|
|
281
537
|
<v-row no-gutters class="my-5">
|
|
282
|
-
<v-btn
|
|
283
|
-
|
|
538
|
+
<v-btn
|
|
539
|
+
variant="flat"
|
|
540
|
+
color="blue"
|
|
541
|
+
density="comfortable"
|
|
542
|
+
class="text-capitalize"
|
|
543
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
544
|
+
:loading="loading.updatingPassKeys"
|
|
545
|
+
@click.stop="handleUpdatePassKeys"
|
|
546
|
+
>Update Pass/Keys</v-btn
|
|
547
|
+
>
|
|
284
548
|
</v-row>
|
|
285
549
|
</v-card-text>
|
|
286
550
|
<v-toolbar class="pa-0" density="compact">
|
|
287
551
|
<v-row no-gutters>
|
|
288
552
|
<v-col cols="6">
|
|
289
|
-
<v-btn
|
|
290
|
-
|
|
553
|
+
<v-btn
|
|
554
|
+
variant="text"
|
|
555
|
+
block
|
|
556
|
+
@click="dialog.returnPassesKeys = false"
|
|
557
|
+
>Close</v-btn
|
|
558
|
+
>
|
|
291
559
|
</v-col>
|
|
292
560
|
<v-col cols="6">
|
|
293
|
-
<v-btn
|
|
294
|
-
|
|
561
|
+
<v-btn
|
|
562
|
+
color="red"
|
|
563
|
+
variant="flat"
|
|
564
|
+
height="48"
|
|
565
|
+
rounded="0"
|
|
566
|
+
block
|
|
567
|
+
:loading="loading.checkingOut"
|
|
568
|
+
:disabled="
|
|
569
|
+
!canConfirmCheckout || selectedVisitorObject.checkOut
|
|
570
|
+
"
|
|
571
|
+
@click="proceedCheckout"
|
|
572
|
+
>Confirm Checkout</v-btn
|
|
573
|
+
>
|
|
295
574
|
</v-col>
|
|
296
575
|
</v-row>
|
|
297
576
|
</v-toolbar>
|
|
@@ -299,10 +578,40 @@
|
|
|
299
578
|
</v-dialog>
|
|
300
579
|
|
|
301
580
|
<v-dialog v-model="dialog.addPassKey" width="450" persistent>
|
|
302
|
-
<AddPassKeyToVisitor
|
|
303
|
-
|
|
581
|
+
<AddPassKeyToVisitor
|
|
582
|
+
mode="add"
|
|
583
|
+
:visitor="selectedVisitorDataObject"
|
|
584
|
+
:site="siteId"
|
|
585
|
+
@close="dialog.addPassKey = false"
|
|
586
|
+
@done="
|
|
587
|
+
() => {
|
|
588
|
+
dialog.addPassKey = false;
|
|
589
|
+
getVisitorRefresh();
|
|
590
|
+
}
|
|
591
|
+
"
|
|
592
|
+
/>
|
|
593
|
+
</v-dialog>
|
|
594
|
+
|
|
595
|
+
<v-dialog v-model="dialog.editPassKey" width="450" persistent>
|
|
596
|
+
<AddPassKeyToVisitor
|
|
597
|
+
mode="edit"
|
|
598
|
+
:visitor="selectedVisitorDataObject"
|
|
599
|
+
:site="siteId"
|
|
600
|
+
@close="dialog.editPassKey = false"
|
|
601
|
+
@done="
|
|
602
|
+
() => {
|
|
603
|
+
dialog.editPassKey = false;
|
|
604
|
+
getVisitorRefresh();
|
|
605
|
+
}
|
|
606
|
+
"
|
|
607
|
+
/>
|
|
304
608
|
</v-dialog>
|
|
305
609
|
|
|
610
|
+
<ScanVisitorQRCode
|
|
611
|
+
:dialog="dialog.scanVisitorQRCode"
|
|
612
|
+
@close-dialog="dialog.scanVisitorQRCode = false"
|
|
613
|
+
/>
|
|
614
|
+
|
|
306
615
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
307
616
|
</v-row>
|
|
308
617
|
</template>
|
|
@@ -317,15 +626,15 @@ const props = defineProps({
|
|
|
317
626
|
type: Boolean,
|
|
318
627
|
default: true,
|
|
319
628
|
},
|
|
320
|
-
|
|
629
|
+
canScanVisitorQRCode: {
|
|
321
630
|
type: Boolean,
|
|
322
631
|
default: true,
|
|
323
632
|
},
|
|
324
|
-
|
|
633
|
+
canViewVisitor: {
|
|
325
634
|
type: Boolean,
|
|
326
635
|
default: true,
|
|
327
636
|
},
|
|
328
|
-
|
|
637
|
+
canUpdateVisitor: {
|
|
329
638
|
type: Boolean,
|
|
330
639
|
default: true,
|
|
331
640
|
},
|
|
@@ -335,21 +644,16 @@ const props = defineProps({
|
|
|
335
644
|
},
|
|
336
645
|
});
|
|
337
646
|
|
|
338
|
-
const {
|
|
339
|
-
|
|
340
|
-
visitorSelection,
|
|
341
|
-
typeFieldMap,
|
|
342
|
-
deleteVisitor,
|
|
343
|
-
updateVisitor,
|
|
344
|
-
} = useVisitor();
|
|
647
|
+
const { getVisitors, visitorSelection, typeFieldMap, updateVisitor } =
|
|
648
|
+
useVisitor();
|
|
345
649
|
const { debounce, formatCamelCaseToWords, formatDate, UTCToLocalTIme } =
|
|
346
650
|
useUtils();
|
|
347
651
|
const { formatLocation } = useSecurityUtils();
|
|
348
652
|
const { getFileUrlAnpr } = useFile();
|
|
349
653
|
// const { status: visitorStatus, search } = useRoute().query as { status: string, search: string};
|
|
350
654
|
|
|
351
|
-
const route = useRoute()
|
|
352
|
-
const router = useRouter()
|
|
655
|
+
const route = useRoute();
|
|
656
|
+
const router = useRouter();
|
|
353
657
|
const { org: orgId, site: siteId } = route.params as {
|
|
354
658
|
org: string;
|
|
355
659
|
site: string;
|
|
@@ -377,7 +681,6 @@ const messageColor = ref("");
|
|
|
377
681
|
const messageSnackbar = ref(false);
|
|
378
682
|
|
|
379
683
|
const loading = reactive({
|
|
380
|
-
deletingVisitor: false,
|
|
381
684
|
fetchingVisitors: false,
|
|
382
685
|
checkingOut: false,
|
|
383
686
|
savingRemarks: false,
|
|
@@ -388,15 +691,16 @@ const dialog = reactive({
|
|
|
388
691
|
showSelection: false,
|
|
389
692
|
showForm: false,
|
|
390
693
|
viewVisitor: false,
|
|
391
|
-
deleteConfirmation: false,
|
|
392
694
|
snapshotImage: false,
|
|
393
695
|
remarks: false,
|
|
394
696
|
returnPassesKeys: false,
|
|
395
|
-
addPassKey: false
|
|
697
|
+
addPassKey: false,
|
|
698
|
+
editPassKey: false,
|
|
699
|
+
scanVisitorQRCode: false,
|
|
396
700
|
});
|
|
397
701
|
|
|
398
702
|
const snapshotImageUrl = ref("");
|
|
399
|
-
const remarksType = ref<
|
|
703
|
+
const remarksType = ref<"checkIn" | "checkOut">("checkIn");
|
|
400
704
|
const remarksInput = ref("");
|
|
401
705
|
const remarksViewOnly = ref(false);
|
|
402
706
|
|
|
@@ -406,10 +710,16 @@ const headers = computed(() => [
|
|
|
406
710
|
{ title: "Location", value: "location" },
|
|
407
711
|
{ title: "Contact/Vehicle No.", value: "contact-vehicleNumber" },
|
|
408
712
|
{ title: "Check In/Out", value: "checkin-out" },
|
|
409
|
-
...(activeTab.value === "resident-transactions"
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
713
|
+
...(activeTab.value === "resident-transactions"
|
|
714
|
+
? [
|
|
715
|
+
{ title: "Check-in Remarks", value: "checkInRemarks" },
|
|
716
|
+
{ title: "Check-out Remarks", value: "checkOutRemarks" },
|
|
717
|
+
]
|
|
718
|
+
: []),
|
|
719
|
+
...(activeTab.value === "unregistered"
|
|
720
|
+
? [{ title: "Action", value: "action" }]
|
|
721
|
+
: []),
|
|
722
|
+
]);
|
|
413
723
|
|
|
414
724
|
const tabOptions = [
|
|
415
725
|
{ name: "Registered", value: "registered" },
|
|
@@ -452,7 +762,6 @@ const formattedFields = {
|
|
|
452
762
|
checkOut: "Check Out",
|
|
453
763
|
snapshotExitImage: "Exit Image",
|
|
454
764
|
remarks: "Remarks",
|
|
455
|
-
|
|
456
765
|
} as const;
|
|
457
766
|
|
|
458
767
|
function filterTypeSelectionLabel() {
|
|
@@ -465,14 +774,14 @@ const passStatusOptions = [
|
|
|
465
774
|
{ label: "Not Returned", value: "In Use" },
|
|
466
775
|
{ label: "Damaged", value: "Damaged" },
|
|
467
776
|
{ label: "Lost", value: "Lost" },
|
|
468
|
-
]
|
|
777
|
+
];
|
|
469
778
|
|
|
470
779
|
function toRoute(tab: any) {
|
|
471
|
-
items.value = []
|
|
780
|
+
items.value = [];
|
|
472
781
|
|
|
473
782
|
const obj = tabOptions.find((x) => x.value === tab);
|
|
474
783
|
if (!obj) return;
|
|
475
|
-
page.value = 1
|
|
784
|
+
page.value = 1;
|
|
476
785
|
navigateTo({
|
|
477
786
|
name: routeName,
|
|
478
787
|
params: {
|
|
@@ -483,54 +792,51 @@ function toRoute(tab: any) {
|
|
|
483
792
|
// },
|
|
484
793
|
});
|
|
485
794
|
}
|
|
486
|
-
|
|
487
795
|
const {
|
|
488
796
|
data: getVisitorReq,
|
|
489
797
|
refresh: getVisitorRefresh,
|
|
490
798
|
pending: getVisitorPending,
|
|
491
799
|
} = await useLazyAsyncData(
|
|
492
|
-
|
|
800
|
+
"get-all-visitors",
|
|
493
801
|
async () => {
|
|
494
|
-
|
|
495
|
-
// Optional delay (if needed)
|
|
496
|
-
await new Promise(resolve => setTimeout(resolve, 100))
|
|
497
|
-
|
|
498
802
|
const params: any = {
|
|
499
803
|
page: page.value,
|
|
500
804
|
site: siteId,
|
|
501
805
|
search: searchInput.value,
|
|
502
806
|
dateTo: normalizeDateOnly(dateTo.value),
|
|
503
807
|
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
|
-
// }
|
|
808
|
+
type:
|
|
809
|
+
filterTypes.value.length === 0 && activeTab.value === "registered"
|
|
810
|
+
? "contractor,delivery,walk-in,pick-up,drop-off,guest"
|
|
811
|
+
: filterTypes.value.filter(Boolean).join(","),
|
|
812
|
+
checkedOut: displayNotCheckedOut.value ? false : undefined,
|
|
813
|
+
};
|
|
514
814
|
|
|
515
815
|
if (activeTab.value === "guests") {
|
|
516
|
-
params.type = "guest"
|
|
517
|
-
params.status = "pending"
|
|
816
|
+
params.type = "guest";
|
|
817
|
+
params.status = "pending";
|
|
518
818
|
} else if (activeTab.value === "resident-transactions") {
|
|
519
|
-
params.status = "registered"
|
|
520
|
-
params.type = "resident,tenant"
|
|
819
|
+
params.status = "registered";
|
|
820
|
+
params.type = "resident,tenant";
|
|
521
821
|
} else {
|
|
522
|
-
params.status = activeTab.value
|
|
822
|
+
params.status = activeTab.value;
|
|
523
823
|
}
|
|
524
824
|
|
|
525
|
-
return await getVisitors(params)
|
|
825
|
+
return await getVisitors(params);
|
|
526
826
|
},
|
|
527
827
|
{
|
|
528
828
|
watch: [
|
|
529
829
|
page,
|
|
830
|
+
searchInput,
|
|
831
|
+
dateFrom,
|
|
832
|
+
dateTo,
|
|
833
|
+
filterTypes,
|
|
834
|
+
activeTab,
|
|
835
|
+
displayNotCheckedOut,
|
|
530
836
|
],
|
|
531
|
-
immediate:
|
|
837
|
+
immediate: true,
|
|
532
838
|
}
|
|
533
|
-
)
|
|
839
|
+
);
|
|
534
840
|
|
|
535
841
|
watch(getVisitorReq, (newData: any) => {
|
|
536
842
|
if (newData) {
|
|
@@ -542,11 +848,16 @@ watch(getVisitorReq, (newData: any) => {
|
|
|
542
848
|
});
|
|
543
849
|
|
|
544
850
|
const selectedVisitorObject = computed(() => {
|
|
545
|
-
|
|
546
851
|
const obj = items.value.find((x: any) => x?._id === selectedVisitorId.value);
|
|
547
852
|
if (!obj) return {};
|
|
548
853
|
const type = obj?.type as TVisitorType | undefined;
|
|
549
|
-
let includedKeys: string[] = [
|
|
854
|
+
let includedKeys: string[] = [
|
|
855
|
+
"checkIn",
|
|
856
|
+
"checkOut",
|
|
857
|
+
"plateNumber",
|
|
858
|
+
"snapshotEntryImage",
|
|
859
|
+
"snapshotExitImage",
|
|
860
|
+
];
|
|
550
861
|
includedKeys.unshift(...(typeFieldMap[type] ?? []));
|
|
551
862
|
return Object.fromEntries(
|
|
552
863
|
Object.entries(obj).filter(([key]) => includedKeys.includes(key))
|
|
@@ -567,8 +878,6 @@ function formatValues(key: string, value: any) {
|
|
|
567
878
|
return value;
|
|
568
879
|
}
|
|
569
880
|
|
|
570
|
-
|
|
571
|
-
|
|
572
881
|
function handleAddNew() {
|
|
573
882
|
dialog.showSelection = true;
|
|
574
883
|
activeVisitorFormType.value = null;
|
|
@@ -588,9 +897,17 @@ function showAddPassKeyButton(item: any) {
|
|
|
588
897
|
const hasPasses = (item?.visitorPass?.length ?? 0) > 0;
|
|
589
898
|
const hasKeys = (item?.passKeys?.length ?? 0) > 0;
|
|
590
899
|
|
|
591
|
-
const isTypeWithPassKey = ["contractor", "guest", "walk-in"].includes(
|
|
900
|
+
const isTypeWithPassKey = ["contractor", "guest", "walk-in"].includes(
|
|
901
|
+
item?.type
|
|
902
|
+
);
|
|
592
903
|
|
|
593
|
-
return
|
|
904
|
+
return (
|
|
905
|
+
!hasPasses &&
|
|
906
|
+
!hasKeys &&
|
|
907
|
+
isTypeWithPassKey &&
|
|
908
|
+
item?.status === "registered" &&
|
|
909
|
+
!item?.checkOut
|
|
910
|
+
);
|
|
594
911
|
}
|
|
595
912
|
|
|
596
913
|
function handleSelectVisitorType(type: TVisitorType) {
|
|
@@ -616,11 +933,6 @@ function handleVisitorFormCreateMore() {
|
|
|
616
933
|
dialog.showForm = false;
|
|
617
934
|
}
|
|
618
935
|
|
|
619
|
-
function handleDeleteVisitor() {
|
|
620
|
-
dialog.deleteConfirmation = true;
|
|
621
|
-
dialog.viewVisitor = false;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
936
|
function showMessage(msg: string, color: string) {
|
|
625
937
|
message.value = msg;
|
|
626
938
|
messageColor.value = color;
|
|
@@ -650,10 +962,19 @@ function handleOpenAddPassKey(item: any) {
|
|
|
650
962
|
dialog.addPassKey = true;
|
|
651
963
|
}
|
|
652
964
|
|
|
653
|
-
function
|
|
965
|
+
function handleOpenEditPassKey(item: any) {
|
|
966
|
+
selectedVisitorId.value = item?._id;
|
|
967
|
+
if (selectedVisitorDataObject.value.checkOut) {
|
|
968
|
+
dialog.returnPassesKeys = true;
|
|
969
|
+
handleCheckout(selectedVisitorId.value as string);
|
|
970
|
+
} else dialog.editPassKey = true;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
function handleOpenRemarks(item: any, type: "checkIn" | "checkOut") {
|
|
654
974
|
selectedVisitorId.value = item?._id;
|
|
655
975
|
remarksType.value = type;
|
|
656
|
-
const existingRemarks =
|
|
976
|
+
const existingRemarks =
|
|
977
|
+
type === "checkIn" ? item?.checkInRemarks : item?.checkOutRemarks;
|
|
657
978
|
remarksInput.value = existingRemarks || "";
|
|
658
979
|
remarksViewOnly.value = !!existingRemarks;
|
|
659
980
|
dialog.remarks = true;
|
|
@@ -663,16 +984,20 @@ async function handleSaveRemarks() {
|
|
|
663
984
|
if (!remarksInput.value.trim() || !selectedVisitorId.value) return;
|
|
664
985
|
try {
|
|
665
986
|
loading.savingRemarks = true;
|
|
666
|
-
const payload =
|
|
667
|
-
|
|
668
|
-
|
|
987
|
+
const payload =
|
|
988
|
+
remarksType.value === "checkIn"
|
|
989
|
+
? { checkInRemarks: remarksInput.value.trim() }
|
|
990
|
+
: { checkOutRemarks: remarksInput.value.trim() };
|
|
669
991
|
await updateVisitor(selectedVisitorId.value, payload);
|
|
670
992
|
showMessage("Remarks saved successfully!", "info");
|
|
671
993
|
await getVisitorRefresh();
|
|
672
994
|
dialog.remarks = false;
|
|
673
995
|
} catch (error: any) {
|
|
674
996
|
const errorMessage = error?.response?._data?.message;
|
|
675
|
-
showMessage(
|
|
997
|
+
showMessage(
|
|
998
|
+
errorMessage || "Something went wrong. Please try again later.",
|
|
999
|
+
"error"
|
|
1000
|
+
);
|
|
676
1001
|
} finally {
|
|
677
1002
|
loading.savingRemarks = false;
|
|
678
1003
|
}
|
|
@@ -684,11 +1009,19 @@ async function handleUpdatePassKeys() {
|
|
|
684
1009
|
loading.updatingPassKeys = true;
|
|
685
1010
|
const payload: any = {};
|
|
686
1011
|
if (passReturnStatuses.value.length > 0) {
|
|
687
|
-
const passReturnStatusesPayload = passReturnStatuses.value.map(p => ({
|
|
1012
|
+
const passReturnStatusesPayload = passReturnStatuses.value.map((p) => ({
|
|
1013
|
+
keyId: p.keyId,
|
|
1014
|
+
status: p.status,
|
|
1015
|
+
remarks: p.remarks,
|
|
1016
|
+
}));
|
|
688
1017
|
payload.visitorPass = passReturnStatusesPayload;
|
|
689
1018
|
}
|
|
690
1019
|
if (keyReturnStatuses.value.length > 0) {
|
|
691
|
-
const keyReturnStatusesPayload = keyReturnStatuses.value.map(k => ({
|
|
1020
|
+
const keyReturnStatusesPayload = keyReturnStatuses.value.map((k) => ({
|
|
1021
|
+
keyId: k.keyId,
|
|
1022
|
+
status: k.status,
|
|
1023
|
+
remarks: k.remarks,
|
|
1024
|
+
}));
|
|
692
1025
|
payload.passKeys = keyReturnStatusesPayload;
|
|
693
1026
|
}
|
|
694
1027
|
await updateVisitor(selectedVisitorId.value, payload);
|
|
@@ -696,50 +1029,35 @@ async function handleUpdatePassKeys() {
|
|
|
696
1029
|
await getVisitorRefresh();
|
|
697
1030
|
} catch (error: any) {
|
|
698
1031
|
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
1032
|
showMessage(
|
|
720
1033
|
errorMessage || "Something went wrong. Please try again later.",
|
|
721
1034
|
"error"
|
|
722
1035
|
);
|
|
723
1036
|
} finally {
|
|
724
|
-
loading.
|
|
1037
|
+
loading.updatingPassKeys = false;
|
|
725
1038
|
}
|
|
726
1039
|
}
|
|
727
1040
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
1041
|
+
const passReturnStatuses = ref<
|
|
1042
|
+
{ keyId: string; status: string; prefixAndName: string; remarks: string }[]
|
|
1043
|
+
>([]);
|
|
1044
|
+
const keyReturnStatuses = ref<
|
|
1045
|
+
{ keyId: string; status: string; prefixAndName: string; remarks: string }[]
|
|
1046
|
+
>([]);
|
|
731
1047
|
|
|
732
1048
|
const canConfirmCheckout = computed(() => {
|
|
733
1049
|
const allEntries = [...passReturnStatuses.value, ...keyReturnStatuses.value];
|
|
734
1050
|
return allEntries.every((entry) => {
|
|
735
|
-
if (!entry.status || entry.status ===
|
|
736
|
-
if (
|
|
1051
|
+
if (!entry.status || entry.status === "In Use") return false;
|
|
1052
|
+
if (
|
|
1053
|
+
(entry.status === "Lost" || entry.status === "Damaged") &&
|
|
1054
|
+
!entry.remarks.trim()
|
|
1055
|
+
)
|
|
1056
|
+
return false;
|
|
737
1057
|
return true;
|
|
738
1058
|
});
|
|
739
1059
|
});
|
|
740
1060
|
|
|
741
|
-
|
|
742
|
-
|
|
743
1061
|
function handleCheckout(userId: string) {
|
|
744
1062
|
if (!userId) {
|
|
745
1063
|
showMessage("Invalid userId", "error");
|
|
@@ -753,8 +1071,22 @@ function handleCheckout(userId: string) {
|
|
|
753
1071
|
|
|
754
1072
|
if (hasPasses || hasKeys) {
|
|
755
1073
|
const visitor = items.value.find((x: any) => x?._id === userId);
|
|
756
|
-
passReturnStatuses.value = ((visitor?.visitorPass as any[]) || []).map(
|
|
757
|
-
|
|
1074
|
+
passReturnStatuses.value = ((visitor?.visitorPass as any[]) || []).map(
|
|
1075
|
+
(p: any) => ({
|
|
1076
|
+
keyId: p.keyId,
|
|
1077
|
+
status: p.status ?? "In Use",
|
|
1078
|
+
remarks: "",
|
|
1079
|
+
prefixAndName: p.prefixAndName,
|
|
1080
|
+
})
|
|
1081
|
+
);
|
|
1082
|
+
keyReturnStatuses.value = ((visitor?.passKeys as any[]) || []).map(
|
|
1083
|
+
(k: any) => ({
|
|
1084
|
+
keyId: k.keyId,
|
|
1085
|
+
status: k.status ?? "In Use",
|
|
1086
|
+
remarks: "",
|
|
1087
|
+
prefixAndName: k.prefixAndName,
|
|
1088
|
+
})
|
|
1089
|
+
);
|
|
758
1090
|
dialog.returnPassesKeys = true;
|
|
759
1091
|
return;
|
|
760
1092
|
}
|
|
@@ -769,13 +1101,25 @@ async function proceedCheckout() {
|
|
|
769
1101
|
try {
|
|
770
1102
|
loading.checkingOut = true;
|
|
771
1103
|
|
|
772
|
-
const passReturnStatusesPayload = passReturnStatuses.value.map(p => ({
|
|
773
|
-
|
|
1104
|
+
const passReturnStatusesPayload = passReturnStatuses.value.map((p) => ({
|
|
1105
|
+
keyId: p.keyId,
|
|
1106
|
+
status: p.status,
|
|
1107
|
+
remarks: p.remarks,
|
|
1108
|
+
}));
|
|
1109
|
+
const keyReturnStatusesPayload = keyReturnStatuses.value.map((k) => ({
|
|
1110
|
+
keyId: k.keyId,
|
|
1111
|
+
status: k.status,
|
|
1112
|
+
remarks: k.remarks,
|
|
1113
|
+
}));
|
|
774
1114
|
|
|
775
1115
|
const res = await updateVisitor(userId as string, {
|
|
776
1116
|
checkOut: new Date().toISOString(),
|
|
777
|
-
...(passReturnStatuses.value.length > 0
|
|
778
|
-
|
|
1117
|
+
...(passReturnStatuses.value.length > 0
|
|
1118
|
+
? { visitorPass: passReturnStatusesPayload }
|
|
1119
|
+
: {}),
|
|
1120
|
+
...(keyReturnStatuses.value.length > 0
|
|
1121
|
+
? { passKeys: keyReturnStatusesPayload }
|
|
1122
|
+
: {}),
|
|
779
1123
|
});
|
|
780
1124
|
if (res) {
|
|
781
1125
|
showMessage("Visitor successfully checked-out!", "info");
|
|
@@ -795,11 +1139,23 @@ async function proceedCheckout() {
|
|
|
795
1139
|
}
|
|
796
1140
|
}
|
|
797
1141
|
|
|
798
|
-
|
|
799
|
-
|
|
800
1142
|
const updateRouteQuery = debounce(
|
|
801
1143
|
// (search: string, from: string, to: string, types: string[], status, checkedOut) => {
|
|
802
|
-
({
|
|
1144
|
+
({
|
|
1145
|
+
search,
|
|
1146
|
+
from,
|
|
1147
|
+
to,
|
|
1148
|
+
types,
|
|
1149
|
+
tab,
|
|
1150
|
+
checkedOut,
|
|
1151
|
+
}: {
|
|
1152
|
+
search: string;
|
|
1153
|
+
from: string;
|
|
1154
|
+
to: string;
|
|
1155
|
+
types: string[];
|
|
1156
|
+
tab: string;
|
|
1157
|
+
checkedOut: boolean;
|
|
1158
|
+
}) => {
|
|
803
1159
|
router.replace({
|
|
804
1160
|
query: {
|
|
805
1161
|
...route.query,
|
|
@@ -808,7 +1164,9 @@ const updateRouteQuery = debounce(
|
|
|
808
1164
|
dateTo: to || undefined,
|
|
809
1165
|
type: types.filter(Boolean).join(",") || undefined,
|
|
810
1166
|
tab: tab || undefined,
|
|
811
|
-
...(checkedOut === true
|
|
1167
|
+
...(checkedOut === true
|
|
1168
|
+
? { checkedOut: "true" }
|
|
1169
|
+
: { checkedOut: undefined }),
|
|
812
1170
|
},
|
|
813
1171
|
});
|
|
814
1172
|
},
|
|
@@ -824,22 +1182,33 @@ watch(
|
|
|
824
1182
|
dateTo.value = normalizedTo;
|
|
825
1183
|
|
|
826
1184
|
// updateRouteQuery(search, from, to, types, status, checkedOut)
|
|
827
|
-
updateRouteQuery({
|
|
828
|
-
|
|
1185
|
+
updateRouteQuery({
|
|
1186
|
+
search,
|
|
1187
|
+
from: normalizedFrom,
|
|
1188
|
+
to: normalizedTo,
|
|
1189
|
+
types,
|
|
1190
|
+
tab,
|
|
1191
|
+
checkedOut,
|
|
1192
|
+
});
|
|
1193
|
+
// getVisitorRefresh();
|
|
829
1194
|
},
|
|
830
1195
|
{ deep: true }
|
|
831
1196
|
);
|
|
832
|
-
|
|
833
|
-
|
|
1197
|
+
watch([searchInput, dateFrom, dateTo, filterTypes, activeTab], () => {
|
|
1198
|
+
page.value = 1;
|
|
1199
|
+
});
|
|
834
1200
|
|
|
835
1201
|
onMounted(() => {
|
|
836
|
-
activeTab.value = (route.query.tab as string) || "unregistered"
|
|
1202
|
+
activeTab.value = (route.query.tab as string) || "unregistered";
|
|
837
1203
|
searchInput.value = (route.query.search as string) || "";
|
|
838
1204
|
dateFrom.value = normalizeDateOnly((route.query.dateFrom as string) || "");
|
|
839
1205
|
dateTo.value = normalizeDateOnly((route.query.dateTo as string) || "");
|
|
840
|
-
filterTypes.value = ((route.query.type as string)?.split(",") || []).filter(
|
|
841
|
-
|
|
842
|
-
|
|
1206
|
+
filterTypes.value = ((route.query.type as string)?.split(",") || []).filter(
|
|
1207
|
+
Boolean
|
|
1208
|
+
) as TVisitorType[];
|
|
1209
|
+
displayNotCheckedOut.value =
|
|
1210
|
+
(route.query.checkedOut as string) == "true" || false;
|
|
1211
|
+
});
|
|
843
1212
|
</script>
|
|
844
1213
|
|
|
845
1214
|
<style scoped></style>
|