@7365admin1/layer-common 3.0.25 → 3.0.26
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 +7 -0
- package/components/AccessCardDetailsDialog.vue +31 -1
- package/components/AddPassKeyToVisitor.vue +346 -225
- package/components/DashboardMain.vue +95 -59
- package/components/DocumentManagement.vue +71 -5
- package/components/HidIntercomManagement.vue +186 -0
- package/components/HidReaderManagement.vue +21 -1
- package/components/RolePermissionMain.vue +14 -8
- package/components/SiteSettings.vue +3 -1
- package/components/VisitorForm.vue +13 -1
- package/components/VisitorManagement.vue +302 -679
- package/components/VisitorSocketPopUp.vue +29 -1
- package/composables/useANPRSettings.ts +57 -8
- package/composables/useAccessManagement.ts +9 -1
- package/composables/useCleaningPermission.ts +2 -2
- package/composables/useCommonPermission.ts +2 -39
- package/composables/useDocument.ts +21 -0
- package/composables/useLocalAuth.ts +3 -1
- package/package.json +1 -1
- package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/hid-readers/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/hid-users/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +2 -0
|
@@ -1,137 +1,51 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
<TableMain
|
|
4
|
-
:
|
|
5
|
-
:
|
|
6
|
-
:loading="getVisitorPending"
|
|
7
|
-
:page="page"
|
|
8
|
-
:pages="pages"
|
|
9
|
-
:extension-height="110"
|
|
10
|
-
:offset="300"
|
|
11
|
-
:pageRange="pageRange"
|
|
12
|
-
@refresh="getVisitorRefresh"
|
|
13
|
-
@update:page="handleUpdatePage"
|
|
14
|
-
show-header
|
|
15
|
-
@row-click="handleRowClick"
|
|
16
|
-
>
|
|
3
|
+
<TableMain :headers="headers" :items="items" :loading="getVisitorPending" :page="page" :pages="pages"
|
|
4
|
+
:extension-height="110" :offset="300" :pageRange="pageRange" @refresh="getVisitorRefresh"
|
|
5
|
+
@update:page="handleUpdatePage" show-header @row-click="handleRowClick">
|
|
17
6
|
<template #actions>
|
|
18
7
|
<v-row no-gutters>
|
|
19
|
-
<v-btn
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
variant="tonal"
|
|
24
|
-
size="large"
|
|
25
|
-
@click="handleAddNew"
|
|
26
|
-
text="Add Visitor"
|
|
27
|
-
/>
|
|
28
|
-
<v-btn
|
|
29
|
-
v-if="canScanVisitorQRCode"
|
|
30
|
-
text="Scan QR Code"
|
|
31
|
-
class="text-none ml-2"
|
|
32
|
-
rounded="pill"
|
|
33
|
-
variant="tonal"
|
|
34
|
-
size="large"
|
|
35
|
-
@click="dialog.scanVisitorQRCode = true"
|
|
36
|
-
/>
|
|
8
|
+
<v-btn v-if="canAddVisitor" class="text-none" rounded="pill" variant="tonal" size="large"
|
|
9
|
+
@click="handleAddNew" text="Add Visitor" />
|
|
10
|
+
<v-btn v-if="canScanVisitorQRCode" text="Scan QR Code" class="text-none ml-2" rounded="pill" variant="tonal"
|
|
11
|
+
size="large" @click="dialog.scanVisitorQRCode = true" />
|
|
37
12
|
<v-menu>
|
|
38
13
|
<template #activator="{ props }">
|
|
39
|
-
<v-btn
|
|
40
|
-
|
|
41
|
-
text="Download Report"
|
|
42
|
-
class="text-none ml-2"
|
|
43
|
-
prepend-icon="mdi-download"
|
|
44
|
-
rounded="pill"
|
|
45
|
-
variant="tonal"
|
|
46
|
-
size="large"
|
|
47
|
-
/>
|
|
14
|
+
<v-btn v-bind="props" text="Download Report" class="text-none ml-2" prepend-icon="mdi-download"
|
|
15
|
+
rounded="pill" variant="tonal" size="large" />
|
|
48
16
|
</template>
|
|
49
17
|
<v-list>
|
|
50
|
-
<v-list-item
|
|
51
|
-
|
|
52
|
-
title="Download PDF Report"
|
|
53
|
-
@click="handleDownloadPDFReport"
|
|
54
|
-
/>
|
|
55
|
-
<v-list-item
|
|
56
|
-
prepend-icon="mdi-file-csv"
|
|
57
|
-
title="Download CSV Report"
|
|
58
|
-
@click="handleDownloadCSVReport"
|
|
59
|
-
/>
|
|
18
|
+
<v-list-item prepend-icon="mdi-file-pdf" title="Download PDF Report" @click="handleDownloadPDFReport" />
|
|
19
|
+
<v-list-item prepend-icon="mdi-file-csv" title="Download CSV Report" @click="handleDownloadCSVReport" />
|
|
60
20
|
</v-list>
|
|
61
21
|
</v-menu>
|
|
62
22
|
</v-row>
|
|
63
23
|
</template>
|
|
64
24
|
<template #extension>
|
|
65
25
|
<v-row no-gutters class="w-100 d-flex flex-column">
|
|
66
|
-
<v-tabs
|
|
67
|
-
v-
|
|
68
|
-
color="primary"
|
|
69
|
-
:height="40"
|
|
70
|
-
@update:model-value="toRoute"
|
|
71
|
-
class="w-100"
|
|
72
|
-
>
|
|
73
|
-
<v-tab
|
|
74
|
-
v-for="tab in tabOptions"
|
|
75
|
-
:value="tab.value"
|
|
76
|
-
:key="tab.value"
|
|
77
|
-
class="text-capitalize"
|
|
78
|
-
>
|
|
26
|
+
<v-tabs v-model="activeTab" color="primary" :height="40" @update:model-value="toRoute" class="w-100">
|
|
27
|
+
<v-tab v-for="tab in tabOptions" :value="tab.value" :key="tab.value" class="text-capitalize">
|
|
79
28
|
{{ tab.name }}
|
|
80
29
|
</v-tab>
|
|
81
30
|
</v-tabs>
|
|
82
31
|
|
|
83
|
-
<v-card
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
>
|
|
88
|
-
<v-text-field
|
|
89
|
-
v-model="searchInput"
|
|
90
|
-
density="compact"
|
|
91
|
-
placeholder="Search"
|
|
92
|
-
clearable
|
|
93
|
-
max-width="300"
|
|
94
|
-
append-inner-icon="mdi-magnify"
|
|
95
|
-
hide-details
|
|
96
|
-
/>
|
|
97
|
-
<v-checkbox
|
|
98
|
-
v-model="displayNotCheckedOut"
|
|
99
|
-
class="text-subtitle-2"
|
|
100
|
-
hide-details
|
|
101
|
-
>
|
|
32
|
+
<v-card class="w-100 px-3 d-flex align-center ga-5 py-2" flat :height="60">
|
|
33
|
+
<v-text-field v-model="searchInput" density="compact" placeholder="Search" clearable max-width="300"
|
|
34
|
+
append-inner-icon="mdi-magnify" hide-details />
|
|
35
|
+
<v-checkbox v-model="displayNotCheckedOut" class="text-subtitle-2" hide-details>
|
|
102
36
|
<template #label>
|
|
103
37
|
<span class="text-caption">Not Checked Out</span>
|
|
104
38
|
</template>
|
|
105
39
|
</v-checkbox>
|
|
106
|
-
<InputDatePicker
|
|
107
|
-
v-model="dateFrom"
|
|
108
|
-
density="compact"
|
|
109
|
-
hide-details
|
|
110
|
-
/>
|
|
40
|
+
<InputDatePicker v-model="dateFrom" density="compact" hide-details />
|
|
111
41
|
<InputDatePicker v-model="dateTo" density="compact" hide-details />
|
|
112
|
-
<v-select
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
label="Filter by types"
|
|
116
|
-
item-title="label"
|
|
117
|
-
item-value="value"
|
|
118
|
-
:items="visitorSelection"
|
|
119
|
-
density="compact"
|
|
120
|
-
clearable
|
|
121
|
-
multiple
|
|
122
|
-
max-width="200"
|
|
123
|
-
hide-details
|
|
124
|
-
>
|
|
42
|
+
<v-select v-if="activeTab == 'registered'" v-model="filterTypes" label="Filter by types" item-title="label"
|
|
43
|
+
item-value="value" :items="visitorSelection" density="compact" clearable multiple max-width="200"
|
|
44
|
+
hide-details>
|
|
125
45
|
<template v-slot:selection="{ item, index }">
|
|
126
46
|
<div class="d-flex align-center text-caption text-nowrap">
|
|
127
|
-
<v-chip
|
|
128
|
-
|
|
129
|
-
color="error"
|
|
130
|
-
:text="filterTypeSelectionLabel()"
|
|
131
|
-
size="x-small"
|
|
132
|
-
variant="tonal"
|
|
133
|
-
class="ml-2"
|
|
134
|
-
/>
|
|
47
|
+
<v-chip v-if="index === 0" color="error" :text="filterTypeSelectionLabel()" size="x-small"
|
|
48
|
+
variant="tonal" class="ml-2" />
|
|
135
49
|
</div>
|
|
136
50
|
</template>
|
|
137
51
|
</v-select>
|
|
@@ -146,9 +60,8 @@
|
|
|
146
60
|
</span>
|
|
147
61
|
<span class="text-capitalize">{{ item?.name }}</span>
|
|
148
62
|
</span>
|
|
149
|
-
<span class="text-grey text-caption" v-if="item?.members?.length > 0"
|
|
150
|
-
|
|
151
|
-
>
|
|
63
|
+
<span class="text-grey text-caption" v-if="item?.members?.length > 0">( +{{ item?.members?.length }}
|
|
64
|
+
members)</span>
|
|
152
65
|
</template>
|
|
153
66
|
|
|
154
67
|
<template v-slot:item.email="{ value }">
|
|
@@ -157,12 +70,7 @@
|
|
|
157
70
|
|
|
158
71
|
<template v-slot:item.invited-by="{ item }">
|
|
159
72
|
<span class="d-flex align-center ga-2">
|
|
160
|
-
<AvatarMain
|
|
161
|
-
v-if="item?.inviterName"
|
|
162
|
-
:name="item?.inviterName"
|
|
163
|
-
:size="20"
|
|
164
|
-
:id="item?._id"
|
|
165
|
-
/>
|
|
73
|
+
<AvatarMain v-if="item?.inviterName" :name="item?.inviterName" :size="20" :id="item?._id" />
|
|
166
74
|
<span class="text-capitalize">{{ item?.inviterName ?? "N/A" }}</span>
|
|
167
75
|
</span>
|
|
168
76
|
</template>
|
|
@@ -172,7 +80,7 @@
|
|
|
172
80
|
<v-icon icon="mdi-user" size="15" />
|
|
173
81
|
<span v-if="item.type === 'contractor'" class="text-capitalize">{{
|
|
174
82
|
formatCamelCaseToWords(item.contractorType)
|
|
175
|
-
|
|
83
|
+
}}</span>
|
|
176
84
|
<span v-else class="text-capitalize">{{ formatType(item) }}</span>
|
|
177
85
|
</span>
|
|
178
86
|
<span class="d-flex align-center ga-2">
|
|
@@ -208,52 +116,26 @@
|
|
|
208
116
|
<template v-slot:item.checkin-out="{ item }">
|
|
209
117
|
<v-row no-gutters class="d-flex flex-column py-2">
|
|
210
118
|
<span class="d-flex align-center ga-2">
|
|
211
|
-
<v-icon
|
|
212
|
-
icon="mdi-clock-time-four-outline"
|
|
213
|
-
color="green"
|
|
214
|
-
size="20"
|
|
215
|
-
/>
|
|
119
|
+
<v-icon icon="mdi-clock-time-four-outline" color="green" size="20" />
|
|
216
120
|
<span class="text-capitalize">{{
|
|
217
121
|
UTCToLocalTIme(item.checkIn) || "-"
|
|
218
|
-
|
|
122
|
+
}}</span>
|
|
219
123
|
<span>
|
|
220
|
-
<v-icon
|
|
221
|
-
|
|
222
|
-
size="17"
|
|
223
|
-
icon="mdi-image"
|
|
224
|
-
@click.stop="handleViewImage(item.snapshotEntryImage)"
|
|
225
|
-
/>
|
|
124
|
+
<v-icon v-if="item?.snapshotEntryImage" size="17" icon="mdi-image"
|
|
125
|
+
@click.stop="handleViewImage(item.snapshotEntryImage)" />
|
|
226
126
|
</span>
|
|
227
127
|
</span>
|
|
228
128
|
|
|
229
|
-
<span
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
"
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
v-bind="menuProps"
|
|
240
|
-
style="cursor: pointer"
|
|
241
|
-
>
|
|
242
|
-
<v-icon
|
|
243
|
-
icon="mdi-clock-time-eight-outline"
|
|
244
|
-
color="red"
|
|
245
|
-
size="20"
|
|
246
|
-
/>
|
|
247
|
-
<v-btn
|
|
248
|
-
size="x-small"
|
|
249
|
-
class="text-capitalize"
|
|
250
|
-
color="red"
|
|
251
|
-
text="Checkout"
|
|
252
|
-
:loading="
|
|
253
|
-
loading.checkingOut && item?._id === selectedVisitorId
|
|
254
|
-
"
|
|
255
|
-
@click.stop="handleCheckout(item._id)"
|
|
256
|
-
/>
|
|
129
|
+
<span v-if="
|
|
130
|
+
!item.checkOut &&
|
|
131
|
+
(item?.status === 'registered' ||
|
|
132
|
+
item?.status === 'unregistered') &&
|
|
133
|
+
canCheckoutVisitor
|
|
134
|
+
">
|
|
135
|
+
<span class="d-flex align-center ga-2" v-bind="menuProps" style="cursor: pointer">
|
|
136
|
+
<v-icon icon="mdi-clock-time-eight-outline" color="red" size="20" />
|
|
137
|
+
<v-btn size="x-small" class="text-capitalize" color="red" text="Checkout" :loading="loading.checkingOut && item?._id === selectedVisitorId
|
|
138
|
+
" @click.stop="handleCheckout(item._id)" />
|
|
257
139
|
</span>
|
|
258
140
|
</span>
|
|
259
141
|
<span v-else class="d-flex align-center ga-2">
|
|
@@ -261,96 +143,88 @@
|
|
|
261
143
|
<template v-if="item.checkOut">
|
|
262
144
|
<span class="text-capitalize">{{
|
|
263
145
|
UTCToLocalTIme(item.checkOut)
|
|
264
|
-
|
|
146
|
+
}}</span>
|
|
265
147
|
<span>
|
|
266
|
-
<v-icon
|
|
267
|
-
|
|
268
|
-
size="17"
|
|
269
|
-
icon="mdi-image"
|
|
270
|
-
@click.stop="handleViewImage(item.snapshotExitImage)"
|
|
271
|
-
/>
|
|
148
|
+
<v-icon v-if="item?.snapshotExitImage" size="17" icon="mdi-image"
|
|
149
|
+
@click.stop="handleViewImage(item.snapshotExitImage)" />
|
|
272
150
|
</span>
|
|
273
151
|
<span v-if="item?.manualCheckout">
|
|
274
|
-
<TooltipInfo
|
|
275
|
-
text="Manual Checkout"
|
|
276
|
-
density="compact"
|
|
277
|
-
size="x-small"
|
|
278
|
-
/>
|
|
152
|
+
<TooltipInfo text="Manual Checkout" density="compact" size="x-small" />
|
|
279
153
|
</span>
|
|
280
154
|
</template>
|
|
281
155
|
<template v-else> <span class="mt-1"> N/A </span> </template>
|
|
282
156
|
</span>
|
|
283
157
|
|
|
284
|
-
<div
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
style="cursor: pointer"
|
|
289
|
-
>
|
|
290
|
-
<v-chip
|
|
291
|
-
size="x-small"
|
|
292
|
-
variant="tonal"
|
|
293
|
-
prepend-icon="mdi-key"
|
|
294
|
-
@click.stop="handleOpenAddPassKey(item)"
|
|
295
|
-
>Add Pass/Key</v-chip
|
|
296
|
-
>
|
|
158
|
+
<div v-if="showAddPassKeyButton(item)" class="d-flex flex-wrap ga-1 mt-1" v-bind="menuProps"
|
|
159
|
+
style="cursor: pointer">
|
|
160
|
+
<v-chip size="x-small" variant="tonal" prepend-icon="mdi-key" @click.stop="handleOpenAddPassKey(item)">Add
|
|
161
|
+
Pass/Key</v-chip>
|
|
297
162
|
</div>
|
|
298
163
|
|
|
299
164
|
<v-menu :close-on-content-click="true">
|
|
300
165
|
<template v-slot:activator="{ props: menuProps }">
|
|
301
|
-
<div
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
"
|
|
306
|
-
|
|
307
|
-
v-bind="menuProps"
|
|
308
|
-
style="cursor: pointer"
|
|
309
|
-
>
|
|
310
|
-
<v-chip
|
|
311
|
-
v-for="pass in item.visitorPass"
|
|
312
|
-
:key="(pass as any)._id ?? (pass as any).keyId"
|
|
313
|
-
prepend-icon="mdi-card-bulleted-outline"
|
|
314
|
-
size="x-small"
|
|
315
|
-
variant="tonal"
|
|
316
|
-
color="blue"
|
|
317
|
-
>
|
|
166
|
+
<div v-if="
|
|
167
|
+
(item.visitorPass?.length ?? 0) > 0 ||
|
|
168
|
+
(item.passKeys?.length ?? 0) > 0
|
|
169
|
+
" class="d-flex flex-wrap ga-1 mt-1" v-bind="menuProps" style="cursor: pointer">
|
|
170
|
+
<v-chip v-for="pass in item.visitorPass" :key="(pass as any)._id ?? (pass as any).keyId"
|
|
171
|
+
prepend-icon="mdi-card-bulleted-outline" size="x-small" variant="tonal" color="blue">
|
|
318
172
|
{{ (pass as any)?.prefixAndName }}
|
|
319
173
|
</v-chip>
|
|
320
|
-
<v-chip
|
|
321
|
-
|
|
322
|
-
:key="(key as any)._id ?? (key as any).keyId"
|
|
323
|
-
prepend-icon="mdi-key"
|
|
324
|
-
size="x-small"
|
|
325
|
-
variant="tonal"
|
|
326
|
-
color="orange"
|
|
327
|
-
>
|
|
174
|
+
<v-chip v-for="key in item.passKeys" :key="(key as any)._id ?? (key as any).keyId"
|
|
175
|
+
prepend-icon="mdi-key" size="x-small" variant="tonal" color="orange">
|
|
328
176
|
{{ (key as any)?.prefixAndName }}
|
|
329
177
|
</v-chip>
|
|
330
178
|
</div>
|
|
331
179
|
</template>
|
|
332
180
|
<v-list density="compact">
|
|
181
|
+
<v-list-item v-if="!item?.checkOut" prepend-icon="mdi-logout" title="Checkout"
|
|
182
|
+
@click.stop="handleCheckout(item._id)" />
|
|
183
|
+
<v-list-item v-if="
|
|
184
|
+
(item.visitorPass?.length ?? 0) > 0 ||
|
|
185
|
+
(item.passKeys?.length ?? 0) > 0
|
|
186
|
+
" prepend-icon="mdi-card-bulleted-outline" :title="item.checkOut ? 'View Pass/Key' : 'Edit Pass/Key'"
|
|
187
|
+
@click.stop="handleOpenEditPassKey(item)" />
|
|
188
|
+
<v-list-item v-if="showAddPassKeyButton(item)" prepend-icon="mdi-plus" title="Add Pass/Key"
|
|
189
|
+
@click.stop="handleOpenAddPassKey(item)" />
|
|
190
|
+
</v-list>
|
|
191
|
+
</v-menu>
|
|
192
|
+
|
|
193
|
+
<!-- NFC Access Cards -->
|
|
194
|
+
<v-menu
|
|
195
|
+
v-if="item.accessCards?.type === 'NFC' && item.accessCards?.cardNo"
|
|
196
|
+
:close-on-content-click="false"
|
|
197
|
+
>
|
|
198
|
+
<template #activator="{ props: nfcMenuProps }">
|
|
199
|
+
<div
|
|
200
|
+
class="d-flex align-center ga-1 mt-1"
|
|
201
|
+
style="cursor: pointer"
|
|
202
|
+
v-bind="nfcMenuProps"
|
|
203
|
+
@click.stop
|
|
204
|
+
>
|
|
205
|
+
<v-icon size="15" icon="mdi-credit-card-outline" color="purple" />
|
|
206
|
+
<v-chip size="x-small" variant="tonal" color="purple">
|
|
207
|
+
{{ item.accessCards.cardNo }}
|
|
208
|
+
<template v-if="(item.cards?.flat().length ?? 0) > 1">
|
|
209
|
+
(+{{ item.cards.flat().length - 1 }})
|
|
210
|
+
</template>
|
|
211
|
+
</v-chip>
|
|
212
|
+
</div>
|
|
213
|
+
</template>
|
|
214
|
+
<v-list density="compact" min-width="200">
|
|
215
|
+
<v-list-subheader>NFC Cards</v-list-subheader>
|
|
333
216
|
<v-list-item
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
title="Checkout"
|
|
337
|
-
@click.stop="handleCheckout(item._id)"
|
|
338
|
-
/>
|
|
339
|
-
<v-list-item
|
|
340
|
-
v-if="
|
|
341
|
-
(item.visitorPass?.length ?? 0) > 0 ||
|
|
342
|
-
(item.passKeys?.length ?? 0) > 0
|
|
343
|
-
"
|
|
344
|
-
prepend-icon="mdi-card-bulleted-outline"
|
|
345
|
-
:title="item.checkOut ? 'View Pass/Key' : 'Edit Pass/Key'"
|
|
346
|
-
@click.stop="handleOpenEditPassKey(item)"
|
|
347
|
-
/>
|
|
348
|
-
<v-list-item
|
|
349
|
-
v-if="showAddPassKeyButton(item)"
|
|
350
|
-
prepend-icon="mdi-plus"
|
|
351
|
-
title="Add Pass/Key"
|
|
352
|
-
@click.stop="handleOpenAddPassKey(item)"
|
|
217
|
+
prepend-icon="mdi-credit-card-outline"
|
|
218
|
+
:title="item.accessCards.cardNo"
|
|
353
219
|
/>
|
|
220
|
+
<template v-for="(cardGroup, gi) in item.cards" :key="gi">
|
|
221
|
+
<v-list-item
|
|
222
|
+
v-for="card in cardGroup.filter((c: any) => c._id !== item.accessCards._id)"
|
|
223
|
+
:key="card._id"
|
|
224
|
+
prepend-icon="mdi-credit-card-outline"
|
|
225
|
+
:title="card._id"
|
|
226
|
+
/>
|
|
227
|
+
</template>
|
|
354
228
|
</v-list>
|
|
355
229
|
</v-menu>
|
|
356
230
|
</v-row>
|
|
@@ -369,44 +243,27 @@
|
|
|
369
243
|
|
|
370
244
|
<span v-if="!item.checkIn && canUpdateVisitor">
|
|
371
245
|
<span class="d-flex align-center ga-2 cursor-pointer">
|
|
372
|
-
<v-icon
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
size="20"
|
|
376
|
-
/>
|
|
377
|
-
<v-btn
|
|
378
|
-
size="x-small"
|
|
379
|
-
class="text-capitalize"
|
|
380
|
-
color="success"
|
|
381
|
-
text="Checkin"
|
|
382
|
-
@click.stop="handleCheckin(item)"
|
|
383
|
-
/>
|
|
246
|
+
<v-icon icon="mdi-clock-time-eight-outline" color="success" size="20" />
|
|
247
|
+
<v-btn size="x-small" class="text-capitalize" color="success" text="Checkin"
|
|
248
|
+
@click.stop="handleCheckin(item)" />
|
|
384
249
|
</span>
|
|
385
250
|
</span>
|
|
386
251
|
</v-row>
|
|
387
252
|
</template>
|
|
388
253
|
|
|
389
254
|
<template v-slot:item.status="{ item }">
|
|
390
|
-
<v-card
|
|
391
|
-
v-if="item?.overnightParking?.status"
|
|
392
|
-
size="small"
|
|
255
|
+
<v-card v-if="item?.overnightParking?.status" size="small"
|
|
393
256
|
class="rounded-xl text-capitalize text-center elevation-0 py-1 px-2"
|
|
394
257
|
:color="getOvernightParkingRequestStatusColor(item?.overnightParking?.status as string)"
|
|
395
|
-
style="max-width: 150px; margin: auto"
|
|
396
|
-
>
|
|
258
|
+
style="max-width: 150px; margin: auto">
|
|
397
259
|
{{ item?.overnightParking?.status }}
|
|
398
260
|
</v-card>
|
|
399
261
|
<span v-else> N/A </span>
|
|
400
262
|
</template>
|
|
401
263
|
|
|
402
264
|
<template v-slot:item.action="{ item }">
|
|
403
|
-
<v-btn
|
|
404
|
-
|
|
405
|
-
size="small"
|
|
406
|
-
color="primary"
|
|
407
|
-
text="Register"
|
|
408
|
-
@click.stop="handleRegistrationUnregisteredVisitor(item)"
|
|
409
|
-
/>
|
|
265
|
+
<v-btn v-if="activeTab === 'unregistered' && canAddVisitor" size="small" color="primary" text="Register"
|
|
266
|
+
@click.stop="handleRegistrationUnregisteredVisitor(item)" />
|
|
410
267
|
<v-menu v-else-if="activeTab === 'overnight-parking'">
|
|
411
268
|
<template #activator="{ props }">
|
|
412
269
|
<v-avatar v-bind="props" class="rounded-xl border-md">
|
|
@@ -415,20 +272,17 @@
|
|
|
415
272
|
</template>
|
|
416
273
|
|
|
417
274
|
<v-card>
|
|
418
|
-
<v-list-item
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
"
|
|
425
|
-
@click.stop="
|
|
275
|
+
<v-list-item v-if="
|
|
276
|
+
!overNightParkingListActions[0].disabled ||
|
|
277
|
+
!['approved', 'rejected'].includes(
|
|
278
|
+
item?.overnightParking?.status
|
|
279
|
+
)
|
|
280
|
+
" @click.stop="
|
|
426
281
|
openApproveRejectOverNightParkingRequestDialog(
|
|
427
282
|
item,
|
|
428
283
|
overNightParkingListActions[0].status
|
|
429
284
|
)
|
|
430
|
-
|
|
431
|
-
>
|
|
285
|
+
">
|
|
432
286
|
<template #title>
|
|
433
287
|
<span class="text-caption">
|
|
434
288
|
{{ overNightParkingListActions[0].text }}
|
|
@@ -436,36 +290,30 @@
|
|
|
436
290
|
</template>
|
|
437
291
|
</v-list-item>
|
|
438
292
|
<v-divider />
|
|
439
|
-
<v-list-item
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
"
|
|
446
|
-
@click="
|
|
293
|
+
<v-list-item v-if="
|
|
294
|
+
!overNightParkingListActions[1].disabled ||
|
|
295
|
+
!['approved', 'rejected'].includes(
|
|
296
|
+
item?.overnightParking?.status
|
|
297
|
+
)
|
|
298
|
+
" @click="
|
|
447
299
|
openApproveRejectOverNightParkingRequestDialog(
|
|
448
300
|
item,
|
|
449
301
|
overNightParkingListActions[1].status
|
|
450
302
|
)
|
|
451
|
-
|
|
452
|
-
>
|
|
303
|
+
">
|
|
453
304
|
<template #title>
|
|
454
305
|
<span class="text-caption">
|
|
455
306
|
{{ overNightParkingListActions[1].text }}
|
|
456
307
|
</span>
|
|
457
308
|
</template>
|
|
458
309
|
</v-list-item>
|
|
459
|
-
<v-list-item
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
)
|
|
467
|
-
"
|
|
468
|
-
>
|
|
310
|
+
<v-list-item v-if="item?.overnightParking?.remarks" @click="
|
|
311
|
+
openApproveRejectOverNightParkingRequestDialog(
|
|
312
|
+
item,
|
|
313
|
+
overNightParkingListActions[2].status,
|
|
314
|
+
item?.overnightParking?.remarks
|
|
315
|
+
)
|
|
316
|
+
">
|
|
469
317
|
<template #title>
|
|
470
318
|
<span class="text-caption">
|
|
471
319
|
{{ overNightParkingListActions[2].text }}
|
|
@@ -479,147 +327,78 @@
|
|
|
479
327
|
|
|
480
328
|
<template v-slot:item.checkInRemarks="{ item }">
|
|
481
329
|
<span>
|
|
482
|
-
<v-btn
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
density="compact"
|
|
486
|
-
size="small"
|
|
487
|
-
@click.stop="handleOpenRemarks(item, 'checkIn')"
|
|
488
|
-
>{{ item.checkInRemarks ? "View Remarks" : "Add Remarks" }}</v-btn
|
|
489
|
-
>
|
|
330
|
+
<v-btn variant="text" color="blue-darken-2" density="compact" size="small"
|
|
331
|
+
@click.stop="handleOpenRemarks(item, 'checkIn')">{{ item.checkInRemarks ? "View Remarks" : "Add Remarks"
|
|
332
|
+
}}</v-btn>
|
|
490
333
|
</span>
|
|
491
334
|
</template>
|
|
492
335
|
<template v-slot:item.checkOutRemarks="{ item }">
|
|
493
336
|
<span>
|
|
494
|
-
<v-btn
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
density="compact"
|
|
498
|
-
size="small"
|
|
499
|
-
@click.stop="handleOpenRemarks(item, 'checkOut')"
|
|
500
|
-
>{{ item.checkOutRemarks ? "View Remarks" : "Add Remarks" }}</v-btn
|
|
501
|
-
>
|
|
337
|
+
<v-btn variant="text" color="blue-darken-2" density="compact" size="small"
|
|
338
|
+
@click.stop="handleOpenRemarks(item, 'checkOut')">{{ item.checkOutRemarks ? "View Remarks" : "Add Remarks"
|
|
339
|
+
}}</v-btn>
|
|
502
340
|
</span>
|
|
503
341
|
</template>
|
|
504
342
|
</TableMain>
|
|
505
343
|
|
|
506
344
|
<v-dialog v-model="dialog.showSelection" width="450" persistent>
|
|
507
|
-
<VisitorFormSelection
|
|
508
|
-
:mode="mode"
|
|
509
|
-
@cancel="dialog.showSelection = false"
|
|
510
|
-
@select="handleSelectVisitorType"
|
|
511
|
-
/>
|
|
345
|
+
<VisitorFormSelection :mode="mode" @cancel="dialog.showSelection = false" @select="handleSelectVisitorType" />
|
|
512
346
|
</v-dialog>
|
|
513
347
|
|
|
514
|
-
<v-dialog
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
persistent
|
|
519
|
-
>
|
|
520
|
-
<VisitorForm
|
|
521
|
-
:mode="mode"
|
|
522
|
-
:org="orgId"
|
|
523
|
-
:site="siteId"
|
|
524
|
-
:visitor-data="selectedVisitorDataObject"
|
|
525
|
-
:current-user="currentUser._id"
|
|
526
|
-
:type="activeVisitorFormType"
|
|
527
|
-
@back="handleClickBack"
|
|
528
|
-
@done="handleVisitorFormDone"
|
|
529
|
-
@done:more="handleVisitorFormCreateMore"
|
|
530
|
-
@close:all="handleCloseAll"
|
|
531
|
-
/>
|
|
348
|
+
<v-dialog v-model="dialog.showForm" v-if="activeVisitorFormType" width="450" persistent>
|
|
349
|
+
<VisitorForm :mode="mode" :org="orgId" :site="siteId" :visitor-data="selectedVisitorDataObject"
|
|
350
|
+
:current-user="currentUser._id" :type="activeVisitorFormType" @back="handleClickBack"
|
|
351
|
+
@done="handleVisitorFormDone" @done:more="handleVisitorFormCreateMore" @close:all="handleCloseAll" />
|
|
532
352
|
</v-dialog>
|
|
533
353
|
|
|
534
354
|
<v-dialog v-model="dialog.viewVisitor" width="450" persistent>
|
|
535
|
-
<VehicleUpdateMoreAction
|
|
536
|
-
|
|
537
|
-
:can-update="false"
|
|
538
|
-
:can-delete="false"
|
|
539
|
-
@close="dialog.viewVisitor = false"
|
|
540
|
-
edit-button-label="Edit Visitor"
|
|
541
|
-
>
|
|
355
|
+
<VehicleUpdateMoreAction title="Preview" :can-update="false" :can-delete="false"
|
|
356
|
+
@close="dialog.viewVisitor = false" edit-button-label="Edit Visitor">
|
|
542
357
|
<template v-slot:content>
|
|
543
358
|
<v-row no-gutters class="mb-4">
|
|
544
359
|
<v-col v-for="(label, key) in formattedFields" :key="key" cols="12">
|
|
545
|
-
<span
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
"
|
|
551
|
-
class="d-flex align-center"
|
|
552
|
-
>
|
|
360
|
+
<span v-if="
|
|
361
|
+
key === 'checkIn' &&
|
|
362
|
+
!selectedVisitorObject[key] &&
|
|
363
|
+
canUpdateVisitor
|
|
364
|
+
" class="d-flex align-center">
|
|
553
365
|
<strong>{{ label }}:</strong>
|
|
554
|
-
<v-btn
|
|
555
|
-
size="x-small"
|
|
556
|
-
class="ml-3 text-capitalize"
|
|
557
|
-
color="success"
|
|
558
|
-
text="Checkin"
|
|
366
|
+
<v-btn size="x-small" class="ml-3 text-capitalize" color="success" text="Checkin"
|
|
559
367
|
:disabled="isVisitorDataFromScannedQRCodeCheckingInOut"
|
|
560
368
|
@click="handleCheckin(selectedVisitorDataObject)"
|
|
561
|
-
:loading="isVisitorDataFromScannedQRCodeCheckingInOut"
|
|
562
|
-
/>
|
|
369
|
+
:loading="isVisitorDataFromScannedQRCodeCheckingInOut" />
|
|
563
370
|
</span>
|
|
564
|
-
<span
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
"
|
|
571
|
-
class="d-flex align-center"
|
|
572
|
-
>
|
|
371
|
+
<span v-if="
|
|
372
|
+
key === 'checkOut' &&
|
|
373
|
+
selectedVisitorObject['checkIn'] &&
|
|
374
|
+
!selectedVisitorObject[key] &&
|
|
375
|
+
canCheckoutVisitor
|
|
376
|
+
" class="d-flex align-center">
|
|
573
377
|
<strong>{{ label }}:</strong>
|
|
574
|
-
<v-btn
|
|
575
|
-
|
|
576
|
-
class="ml-3 text-capitalize"
|
|
577
|
-
color="red"
|
|
578
|
-
text="Checkout"
|
|
579
|
-
:disabled="loading.checkingOut"
|
|
580
|
-
@click="handleCheckout(selectedVisitorId as string)"
|
|
581
|
-
/>
|
|
378
|
+
<v-btn size="x-small" class="ml-3 text-capitalize" color="red" text="Checkout"
|
|
379
|
+
:disabled="loading.checkingOut" @click="handleCheckout(selectedVisitorId as string)" />
|
|
582
380
|
</span>
|
|
583
381
|
|
|
584
|
-
<span
|
|
585
|
-
v-else-if="key === 'nric' && selectedVisitorObject['nric']"
|
|
586
|
-
class="d-flex align-center"
|
|
587
|
-
>
|
|
382
|
+
<span v-else-if="key === 'nric' && selectedVisitorObject['nric']" class="d-flex align-center">
|
|
588
383
|
<strong>{{ label }}:</strong>
|
|
589
384
|
<span v-tooltip:top="selectedVisitorObject[key]" class="ml-2">{{
|
|
590
385
|
maskNRIC(selectedVisitorObject[key])
|
|
591
|
-
|
|
386
|
+
}}</span>
|
|
592
387
|
</span>
|
|
593
|
-
<span
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
"
|
|
598
|
-
class="d-flex flex-column"
|
|
599
|
-
>
|
|
388
|
+
<span v-else-if="
|
|
389
|
+
key === 'attachments' &&
|
|
390
|
+
selectedVisitorObject?.[key]?.length > 0
|
|
391
|
+
" class="d-flex flex-column">
|
|
600
392
|
<strong class="w-full">{{ label }}:</strong>
|
|
601
393
|
<div class="d-flex flex-wrap ga-2">
|
|
602
|
-
<AttachmentsViewer
|
|
603
|
-
|
|
604
|
-
mappedAttachments(selectedVisitorObject.attachments)
|
|
605
|
-
"
|
|
606
|
-
title=""
|
|
607
|
-
show-file-type-title
|
|
608
|
-
/>
|
|
394
|
+
<AttachmentsViewer :files="mappedAttachments(selectedVisitorObject.attachments)
|
|
395
|
+
" title="" show-file-type-title />
|
|
609
396
|
</div>
|
|
610
397
|
</span>
|
|
611
398
|
|
|
612
|
-
<span
|
|
613
|
-
v-else-if="selectedVisitorObject[key]"
|
|
614
|
-
class="d-flex ga-3 align-start"
|
|
615
|
-
><strong>{{ label }}:</strong>
|
|
399
|
+
<span v-else-if="selectedVisitorObject[key]" class="d-flex ga-3 align-start"><strong>{{ label }}:</strong>
|
|
616
400
|
{{ formatValues(key, selectedVisitorObject[key]) }}
|
|
617
|
-
<TooltipInfo
|
|
618
|
-
v-if="key === 'checkOut'"
|
|
619
|
-
text="Manual Checkout"
|
|
620
|
-
density="compact"
|
|
621
|
-
size="x-small"
|
|
622
|
-
/>
|
|
401
|
+
<TooltipInfo v-if="key === 'checkOut'" text="Manual Checkout" density="compact" size="x-small" />
|
|
623
402
|
</span>
|
|
624
403
|
</v-col>
|
|
625
404
|
</v-row>
|
|
@@ -631,11 +410,7 @@
|
|
|
631
410
|
<v-card>
|
|
632
411
|
<v-card-title class="d-flex justify-space-between align-center">
|
|
633
412
|
<span>Snapshot</span>
|
|
634
|
-
<v-btn
|
|
635
|
-
icon="mdi-close"
|
|
636
|
-
variant="text"
|
|
637
|
-
@click="dialog.snapshotImage = false"
|
|
638
|
-
/>
|
|
413
|
+
<v-btn icon="mdi-close" variant="text" @click="dialog.snapshotImage = false" />
|
|
639
414
|
</v-card-title>
|
|
640
415
|
<v-card-text class="pa-2 d-flex justify-center">
|
|
641
416
|
<v-img :src="snapshotImageUrl" max-height="600" contain />
|
|
@@ -645,51 +420,25 @@
|
|
|
645
420
|
|
|
646
421
|
<v-dialog v-model="dialog.remarks" max-width="450" persistent>
|
|
647
422
|
<v-card>
|
|
648
|
-
<v-card-title
|
|
649
|
-
class="d-flex justify-space-between align-center pt-4 px-4"
|
|
650
|
-
>
|
|
423
|
+
<v-card-title class="d-flex justify-space-between align-center pt-4 px-4">
|
|
651
424
|
<span>{{
|
|
652
425
|
remarksType === "checkIn" ? "Check-In Remarks" : "Check-Out Remarks"
|
|
653
|
-
|
|
654
|
-
<v-btn
|
|
655
|
-
icon="mdi-close"
|
|
656
|
-
variant="text"
|
|
657
|
-
@click="dialog.remarks = false"
|
|
658
|
-
/>
|
|
426
|
+
}}</span>
|
|
427
|
+
<v-btn icon="mdi-close" variant="text" @click="dialog.remarks = false" />
|
|
659
428
|
</v-card-title>
|
|
660
429
|
<v-card-text class="px-4 pb-2">
|
|
661
|
-
<v-textarea
|
|
662
|
-
|
|
663
|
-
label="Remarks"
|
|
664
|
-
rows="4"
|
|
665
|
-
auto-grow
|
|
666
|
-
variant="outlined"
|
|
667
|
-
:readonly="remarksViewOnly"
|
|
668
|
-
/>
|
|
430
|
+
<v-textarea v-model="remarksInput" label="Remarks" rows="4" auto-grow variant="outlined"
|
|
431
|
+
:readonly="remarksViewOnly" />
|
|
669
432
|
</v-card-text>
|
|
670
433
|
<v-toolbar class="pa-0" density="compact">
|
|
671
434
|
<v-row no-gutters>
|
|
672
435
|
<v-col cols="6">
|
|
673
|
-
<v-btn
|
|
674
|
-
|
|
675
|
-
block
|
|
676
|
-
:disabled="loading.savingRemarks"
|
|
677
|
-
@click="dialog.remarks = false"
|
|
678
|
-
>Cancel</v-btn
|
|
679
|
-
>
|
|
436
|
+
<v-btn variant="text" block :disabled="loading.savingRemarks"
|
|
437
|
+
@click="dialog.remarks = false">Cancel</v-btn>
|
|
680
438
|
</v-col>
|
|
681
439
|
<v-col cols="6">
|
|
682
|
-
<v-btn
|
|
683
|
-
|
|
684
|
-
variant="flat"
|
|
685
|
-
height="48"
|
|
686
|
-
rounded="0"
|
|
687
|
-
block
|
|
688
|
-
:loading="loading.savingRemarks"
|
|
689
|
-
:disabled="!remarksInput.trim()"
|
|
690
|
-
@click="handleSaveRemarks"
|
|
691
|
-
>Save</v-btn
|
|
692
|
-
>
|
|
440
|
+
<v-btn color="primary" variant="flat" height="48" rounded="0" block :loading="loading.savingRemarks"
|
|
441
|
+
:disabled="!remarksInput.trim()" @click="handleSaveRemarks">Save</v-btn>
|
|
693
442
|
</v-col>
|
|
694
443
|
</v-row>
|
|
695
444
|
</v-toolbar>
|
|
@@ -699,16 +448,9 @@
|
|
|
699
448
|
<v-dialog v-model="dialog.returnPassesKeys" max-width="450" persistent>
|
|
700
449
|
<v-card>
|
|
701
450
|
<v-toolbar density="compact" color="">
|
|
702
|
-
<v-row
|
|
703
|
-
no-gutters
|
|
704
|
-
class="d-flex fill-height justify-space-between align-center px-4"
|
|
705
|
-
>
|
|
451
|
+
<v-row no-gutters class="d-flex fill-height justify-space-between align-center px-4">
|
|
706
452
|
<span class="font-weight-bold">Return Passes & Keys</span>
|
|
707
|
-
<v-btn
|
|
708
|
-
icon="mdi-close"
|
|
709
|
-
variant="text"
|
|
710
|
-
@click="dialog.returnPassesKeys = false"
|
|
711
|
-
/>
|
|
453
|
+
<v-btn icon="mdi-close" variant="text" @click="dialog.returnPassesKeys = false" />
|
|
712
454
|
</v-row>
|
|
713
455
|
</v-toolbar>
|
|
714
456
|
<v-card-text class="px-4 pb-2">
|
|
@@ -718,134 +460,55 @@
|
|
|
718
460
|
|
|
719
461
|
<div>
|
|
720
462
|
<InputLabel class="text-capitalize" title="Full Name" />
|
|
721
|
-
<v-text-field
|
|
722
|
-
v-model="selectedVisitorObject.name"
|
|
723
|
-
density="comfortable"
|
|
724
|
-
readonly
|
|
725
|
-
/>
|
|
463
|
+
<v-text-field v-model="selectedVisitorObject.name" density="comfortable" readonly />
|
|
726
464
|
</div>
|
|
727
465
|
|
|
728
466
|
<div>
|
|
729
467
|
<InputLabel class="text-capitalize" title="Location" />
|
|
730
|
-
<v-text-field
|
|
731
|
-
v-model="selectedVisitorObject.location"
|
|
732
|
-
density="comfortable"
|
|
733
|
-
readonly
|
|
734
|
-
/>
|
|
468
|
+
<v-text-field v-model="selectedVisitorObject.location" density="comfortable" readonly />
|
|
735
469
|
</div>
|
|
736
470
|
|
|
737
471
|
<div v-if="passReturnStatuses.length > 0" class="mb-2">
|
|
738
472
|
<p class="text-caption text-medium-emphasis mb-1">Passes</p>
|
|
739
|
-
<v-row
|
|
740
|
-
|
|
741
|
-
v-
|
|
742
|
-
:key="(pass as any)._id ?? (pass as any).keyId"
|
|
743
|
-
class="d-flex flex-wrap justify-space-between align-center ga-5 mb-2"
|
|
744
|
-
>
|
|
745
|
-
<v-chip
|
|
746
|
-
prepend-icon="mdi-card-bulleted-outline"
|
|
747
|
-
size="small"
|
|
748
|
-
variant="tonal"
|
|
749
|
-
color="blue"
|
|
750
|
-
>
|
|
473
|
+
<v-row no-gutters v-for="pass in passReturnStatuses" :key="(pass as any)._id ?? (pass as any).keyId"
|
|
474
|
+
class="d-flex flex-wrap justify-space-between align-center ga-5 mb-2">
|
|
475
|
+
<v-chip prepend-icon="mdi-card-bulleted-outline" size="small" variant="tonal" color="blue">
|
|
751
476
|
{{ (pass as any)?.prefixAndName }}
|
|
752
477
|
</v-chip>
|
|
753
|
-
<v-select
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
density="compact"
|
|
757
|
-
:items="passStatusOptions"
|
|
758
|
-
item-title="label"
|
|
759
|
-
item-value="value"
|
|
760
|
-
v-model="pass.status"
|
|
761
|
-
:disabled="selectedVisitorObject.checkOut"
|
|
762
|
-
></v-select>
|
|
763
|
-
<v-textarea
|
|
764
|
-
v-if="pass.status === 'Lost' || pass.status === 'Damaged'"
|
|
765
|
-
no-resize
|
|
766
|
-
rows="3"
|
|
767
|
-
class="w-100"
|
|
768
|
-
density="compact"
|
|
769
|
-
v-model="pass.remarks"
|
|
770
|
-
:disabled="selectedVisitorObject.checkOut"
|
|
771
|
-
></v-textarea>
|
|
478
|
+
<v-select hide-details max-width="200px" density="compact" :items="passStatusOptions" item-title="label"
|
|
479
|
+
item-value="value" v-model="pass.status" :disabled="selectedVisitorObject.checkOut"></v-select>
|
|
480
|
+
<v-textarea v-if="pass.status === 'Lost' || pass.status === 'Damaged'" no-resize rows="3" class="w-100"
|
|
481
|
+
density="compact" v-model="pass.remarks" :disabled="selectedVisitorObject.checkOut"></v-textarea>
|
|
772
482
|
</v-row>
|
|
773
483
|
</div>
|
|
774
484
|
<div v-if="keyReturnStatuses.length > 0" class="mb-2">
|
|
775
485
|
<p class="text-caption text-medium-emphasis mb-1">Keys</p>
|
|
776
|
-
<v-row
|
|
777
|
-
|
|
778
|
-
v-
|
|
779
|
-
:key="(key as any)._id ?? (key as any).keyId"
|
|
780
|
-
class="d-flex flex-wrap justify-space-between align-center ga-5 mb-2"
|
|
781
|
-
>
|
|
782
|
-
<v-chip
|
|
783
|
-
prepend-icon="mdi-key"
|
|
784
|
-
size="small"
|
|
785
|
-
variant="tonal"
|
|
786
|
-
color="orange"
|
|
787
|
-
>
|
|
486
|
+
<v-row no-gutters v-for="key in keyReturnStatuses" :key="(key as any)._id ?? (key as any).keyId"
|
|
487
|
+
class="d-flex flex-wrap justify-space-between align-center ga-5 mb-2">
|
|
488
|
+
<v-chip prepend-icon="mdi-key" size="small" variant="tonal" color="orange">
|
|
788
489
|
{{ (key as any)?.prefixAndName }}
|
|
789
490
|
</v-chip>
|
|
790
|
-
<v-select
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
density="compact"
|
|
794
|
-
:items="passStatusOptions"
|
|
795
|
-
item-title="label"
|
|
796
|
-
item-value="value"
|
|
797
|
-
v-model="key.status"
|
|
798
|
-
:disabled="selectedVisitorObject.checkOut"
|
|
799
|
-
></v-select>
|
|
800
|
-
<v-textarea
|
|
801
|
-
v-if="key.status === 'Lost' || key.status === 'Damaged'"
|
|
802
|
-
no-resize
|
|
803
|
-
rows="3"
|
|
804
|
-
class="w-100"
|
|
805
|
-
density="compact"
|
|
806
|
-
v-model="key.remarks"
|
|
807
|
-
:disabled="selectedVisitorObject.checkOut"
|
|
808
|
-
></v-textarea>
|
|
491
|
+
<v-select hide-details max-width="200px" density="compact" :items="passStatusOptions" item-title="label"
|
|
492
|
+
item-value="value" v-model="key.status" :disabled="selectedVisitorObject.checkOut"></v-select>
|
|
493
|
+
<v-textarea v-if="key.status === 'Lost' || key.status === 'Damaged'" no-resize rows="3" class="w-100"
|
|
494
|
+
density="compact" v-model="key.remarks" :disabled="selectedVisitorObject.checkOut"></v-textarea>
|
|
809
495
|
</v-row>
|
|
810
496
|
</div>
|
|
811
497
|
|
|
812
498
|
<v-row no-gutters class="my-5">
|
|
813
|
-
<v-btn
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
density="comfortable"
|
|
817
|
-
class="text-capitalize"
|
|
818
|
-
:disabled="selectedVisitorObject.checkOut"
|
|
819
|
-
:loading="loading.updatingPassKeys"
|
|
820
|
-
@click.stop="handleUpdatePassKeys"
|
|
821
|
-
>Update Pass/Keys</v-btn
|
|
822
|
-
>
|
|
499
|
+
<v-btn variant="flat" color="blue" density="comfortable" class="text-capitalize"
|
|
500
|
+
:disabled="selectedVisitorObject.checkOut" :loading="loading.updatingPassKeys"
|
|
501
|
+
@click.stop="handleUpdatePassKeys">Update Pass/Keys</v-btn>
|
|
823
502
|
</v-row>
|
|
824
503
|
</v-card-text>
|
|
825
504
|
<v-toolbar class="pa-0" density="compact">
|
|
826
505
|
<v-row no-gutters>
|
|
827
506
|
<v-col cols="6">
|
|
828
|
-
<v-btn
|
|
829
|
-
variant="text"
|
|
830
|
-
block
|
|
831
|
-
@click="dialog.returnPassesKeys = false"
|
|
832
|
-
>Close</v-btn
|
|
833
|
-
>
|
|
507
|
+
<v-btn variant="text" block @click="dialog.returnPassesKeys = false">Close</v-btn>
|
|
834
508
|
</v-col>
|
|
835
509
|
<v-col cols="6">
|
|
836
|
-
<v-btn
|
|
837
|
-
|
|
838
|
-
variant="flat"
|
|
839
|
-
height="48"
|
|
840
|
-
rounded="0"
|
|
841
|
-
block
|
|
842
|
-
:loading="loading.checkingOut"
|
|
843
|
-
:disabled="
|
|
844
|
-
!canConfirmCheckout || selectedVisitorObject.checkOut
|
|
845
|
-
"
|
|
846
|
-
@click="proceedCheckout"
|
|
847
|
-
>Confirm Checkout</v-btn
|
|
848
|
-
>
|
|
510
|
+
<v-btn color="red" variant="flat" height="48" rounded="0" block :loading="loading.checkingOut" :disabled="!canConfirmCheckout || selectedVisitorObject.checkOut
|
|
511
|
+
" @click="proceedCheckout">Confirm Checkout</v-btn>
|
|
849
512
|
</v-col>
|
|
850
513
|
</v-row>
|
|
851
514
|
</v-toolbar>
|
|
@@ -853,87 +516,50 @@
|
|
|
853
516
|
</v-dialog>
|
|
854
517
|
|
|
855
518
|
<v-dialog v-model="dialog.addPassKey" width="450" persistent>
|
|
856
|
-
<AddPassKeyToVisitor
|
|
857
|
-
|
|
858
|
-
:visitor="selectedVisitorDataObject"
|
|
859
|
-
:site="siteId"
|
|
860
|
-
@close="dialog.addPassKey = false"
|
|
861
|
-
@done="
|
|
519
|
+
<AddPassKeyToVisitor mode="add" :visitor="selectedVisitorDataObject" :site="siteId"
|
|
520
|
+
@close="dialog.addPassKey = false" @done="
|
|
862
521
|
() => {
|
|
863
522
|
dialog.addPassKey = false;
|
|
864
523
|
getVisitorRefresh();
|
|
865
524
|
}
|
|
866
|
-
"
|
|
867
|
-
/>
|
|
525
|
+
" />
|
|
868
526
|
</v-dialog>
|
|
869
527
|
|
|
870
528
|
<v-dialog v-model="dialog.editPassKey" width="450" persistent>
|
|
871
|
-
<AddPassKeyToVisitor
|
|
872
|
-
|
|
873
|
-
:visitor="selectedVisitorDataObject"
|
|
874
|
-
:site="siteId"
|
|
875
|
-
@close="dialog.editPassKey = false"
|
|
876
|
-
@done="
|
|
529
|
+
<AddPassKeyToVisitor mode="edit" :visitor="selectedVisitorDataObject" :site="siteId"
|
|
530
|
+
@close="dialog.editPassKey = false" @done="
|
|
877
531
|
() => {
|
|
878
532
|
dialog.editPassKey = false;
|
|
879
533
|
getVisitorRefresh();
|
|
880
534
|
}
|
|
881
|
-
"
|
|
882
|
-
/>
|
|
535
|
+
" />
|
|
883
536
|
</v-dialog>
|
|
884
537
|
|
|
885
|
-
<ScanVisitorQRCode
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
"
|
|
891
|
-
@close-dialog="dialog.scanVisitorQRCode = false"
|
|
892
|
-
/>
|
|
893
|
-
|
|
894
|
-
<v-dialog
|
|
895
|
-
v-model="dialog.showVisitorDataFromScannedQRCode"
|
|
896
|
-
width="450"
|
|
897
|
-
persistent
|
|
898
|
-
>
|
|
899
|
-
<VisitorDataFromScannedQRCodeForm
|
|
900
|
-
:site="siteId"
|
|
901
|
-
:visitor-data="visitorDataFromScannedQRCode"
|
|
538
|
+
<ScanVisitorQRCode :dialog="dialog.scanVisitorQRCode" :site="siteId" @open-visitor-data-from-scanned-qr-code-dialog="
|
|
539
|
+
handleShowVisitorDataFromScannedQRCodeDialog
|
|
540
|
+
" @close-dialog="dialog.scanVisitorQRCode = false" />
|
|
541
|
+
|
|
542
|
+
<v-dialog v-model="dialog.showVisitorDataFromScannedQRCode" width="450" persistent>
|
|
543
|
+
<VisitorDataFromScannedQRCodeForm :site="siteId" :visitor-data="visitorDataFromScannedQRCode"
|
|
902
544
|
@check-in-out="handleVisitorDataFromScannedQRCodeCheckInOut"
|
|
903
545
|
:processing="isVisitorDataFromScannedQRCodeCheckingInOut"
|
|
904
|
-
@close="handleCloseVisitorDataFromScannedQRCodeDialog"
|
|
905
|
-
/>
|
|
546
|
+
@close="handleCloseVisitorDataFromScannedQRCodeDialog" />
|
|
906
547
|
</v-dialog>
|
|
907
548
|
|
|
908
|
-
<v-dialog
|
|
909
|
-
|
|
910
|
-
transition="dialog-bottom-transition"
|
|
911
|
-
persistent
|
|
912
|
-
width="60vh"
|
|
913
|
-
>
|
|
549
|
+
<v-dialog v-model="dialog.approveRejectOvernightParkingRequestDialog" transition="dialog-bottom-transition"
|
|
550
|
+
persistent width="60vh">
|
|
914
551
|
<v-card>
|
|
915
552
|
<v-toolbar density="compact">
|
|
916
|
-
<v-row
|
|
917
|
-
no-gutters
|
|
918
|
-
class="d-flex fill-height justify-space-between align-center px-4"
|
|
919
|
-
>
|
|
553
|
+
<v-row no-gutters class="d-flex fill-height justify-space-between align-center px-4">
|
|
920
554
|
<span class="font-weight-bold"> Overnight Parking Request </span>
|
|
921
|
-
<v-btn
|
|
922
|
-
icon="mdi-close"
|
|
923
|
-
variant="text"
|
|
924
|
-
@click="dialog.approveRejectOvernightParkingRequestDialog = false"
|
|
925
|
-
/>
|
|
555
|
+
<v-btn icon="mdi-close" variant="text" @click="dialog.approveRejectOvernightParkingRequestDialog = false" />
|
|
926
556
|
</v-row>
|
|
927
557
|
</v-toolbar>
|
|
928
558
|
<v-card-text class="px-4 pb-6">
|
|
929
559
|
<v-row no-gutters justify="center" align-content="center">
|
|
930
|
-
<v-col
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
"
|
|
934
|
-
cols="12"
|
|
935
|
-
class="text-h6 text-center"
|
|
936
|
-
>
|
|
560
|
+
<v-col v-if="
|
|
561
|
+
approveRejectOvernightParkingRequestDialog.status !== 'remarks'
|
|
562
|
+
" cols="12" class="text-h6 text-center">
|
|
937
563
|
{{
|
|
938
564
|
`Are you sure you want to ${approveRejectOvernightParkingRequestDialog.text} the visitor's overnight
|
|
939
565
|
parking
|
|
@@ -942,58 +568,28 @@
|
|
|
942
568
|
</v-col>
|
|
943
569
|
|
|
944
570
|
<v-col cols="12" md="8" class="mt-4">
|
|
945
|
-
<v-textarea
|
|
946
|
-
|
|
947
|
-
label="Remarks"
|
|
948
|
-
placeholder="Enter remarks"
|
|
949
|
-
outlined
|
|
950
|
-
rows="3"
|
|
951
|
-
clearable
|
|
952
|
-
:hide-details="
|
|
953
|
-
approveRejectOvernightParkingRequestDialog.status ===
|
|
571
|
+
<v-textarea v-model="overNightParkingRequestApproveRejectRemarks" label="Remarks"
|
|
572
|
+
placeholder="Enter remarks" outlined rows="3" clearable :hide-details="approveRejectOvernightParkingRequestDialog.status ===
|
|
954
573
|
'remarks'
|
|
955
|
-
|
|
956
|
-
:readonly="
|
|
957
|
-
approveRejectOvernightParkingRequestDialog.status ===
|
|
574
|
+
" :readonly="approveRejectOvernightParkingRequestDialog.status ===
|
|
958
575
|
'remarks'
|
|
959
|
-
|
|
960
|
-
/>
|
|
576
|
+
" />
|
|
961
577
|
</v-col>
|
|
962
578
|
</v-row>
|
|
963
579
|
</v-card-text>
|
|
964
|
-
<v-toolbar
|
|
965
|
-
|
|
966
|
-
class="pa-0"
|
|
967
|
-
density="compact"
|
|
968
|
-
>
|
|
580
|
+
<v-toolbar v-if="approveRejectOvernightParkingRequestDialog.status !== 'remarks'" class="pa-0"
|
|
581
|
+
density="compact">
|
|
969
582
|
<v-row no-gutters>
|
|
970
583
|
<v-col cols="6">
|
|
971
|
-
<v-btn
|
|
972
|
-
text="No"
|
|
973
|
-
color="grey-lighten-3"
|
|
974
|
-
variant="flat"
|
|
975
|
-
height="48"
|
|
976
|
-
tile
|
|
977
|
-
block
|
|
584
|
+
<v-btn text="No" color="grey-lighten-3" variant="flat" height="48" tile block
|
|
978
585
|
:disabled="isApprovingRejectingOvernightParkingRequest"
|
|
979
|
-
@click="closeApproveRejectOvernightParkingDialog"
|
|
980
|
-
/>
|
|
586
|
+
@click="closeApproveRejectOvernightParkingDialog" />
|
|
981
587
|
</v-col>
|
|
982
588
|
<v-col cols="6">
|
|
983
|
-
<v-btn
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
height="48"
|
|
988
|
-
tile
|
|
989
|
-
block
|
|
990
|
-
:disabled="
|
|
991
|
-
!overNightParkingRequestApproveRejectRemarks ||
|
|
992
|
-
isApprovingRejectingOvernightParkingRequest
|
|
993
|
-
"
|
|
994
|
-
@click="updateOvernightParkingRequestStatus"
|
|
995
|
-
:loading="isApprovingRejectingOvernightParkingRequest"
|
|
996
|
-
/>
|
|
589
|
+
<v-btn text="Yes" color="success" variant="flat" height="48" tile block :disabled="!overNightParkingRequestApproveRejectRemarks ||
|
|
590
|
+
isApprovingRejectingOvernightParkingRequest
|
|
591
|
+
" @click="updateOvernightParkingRequestStatus"
|
|
592
|
+
:loading="isApprovingRejectingOvernightParkingRequest" />
|
|
997
593
|
</v-col>
|
|
998
594
|
</v-row>
|
|
999
595
|
</v-toolbar>
|
|
@@ -1039,6 +635,18 @@ const props = defineProps({
|
|
|
1039
635
|
const { authenticate, currentUser } = useLocalAuth();
|
|
1040
636
|
authenticate();
|
|
1041
637
|
|
|
638
|
+
const route = useRoute();
|
|
639
|
+
const router = useRouter();
|
|
640
|
+
const { org: orgId, site: siteId } = route.params as {
|
|
641
|
+
org: string;
|
|
642
|
+
site: string;
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
const { enableUnregistered } = useANPRSettings(siteId);
|
|
646
|
+
const enableUnregisteredTab = computed(() => enableUnregistered.value);
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
|
|
1042
650
|
const {
|
|
1043
651
|
getVisitors,
|
|
1044
652
|
visitorSelection,
|
|
@@ -1058,12 +666,6 @@ const { formatLocation } = useSecurityUtils();
|
|
|
1058
666
|
const { getFileUrlAnpr } = useFile();
|
|
1059
667
|
// const { status: visitorStatus, search } = useRoute().query as { status: string, search: string};
|
|
1060
668
|
|
|
1061
|
-
const route = useRoute();
|
|
1062
|
-
const router = useRouter();
|
|
1063
|
-
const { org: orgId, site: siteId } = route.params as {
|
|
1064
|
-
org: string;
|
|
1065
|
-
site: string;
|
|
1066
|
-
};
|
|
1067
669
|
const routeName = route.name;
|
|
1068
670
|
|
|
1069
671
|
const items = ref<Array<Record<string, any>>>([]);
|
|
@@ -1167,13 +769,21 @@ const headers = computed(() => {
|
|
|
1167
769
|
return list;
|
|
1168
770
|
});
|
|
1169
771
|
|
|
1170
|
-
const tabOptions =
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
772
|
+
const tabOptions = computed(() => {
|
|
773
|
+
const baseTabs = [
|
|
774
|
+
{ name: "Registered", value: "registered" },
|
|
775
|
+
...(enableUnregisteredTab.value
|
|
776
|
+
? [{ name: "Unregistered", value: "unregistered" }]
|
|
777
|
+
: []
|
|
778
|
+
),
|
|
779
|
+
{ name: "Guests", value: "guests" },
|
|
780
|
+
{ name: "Resident Transactions", value: "resident-transactions" },
|
|
781
|
+
{ name: "Overnight Parking", value: "overnight-parking" },
|
|
782
|
+
];
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
return baseTabs;
|
|
786
|
+
});
|
|
1177
787
|
|
|
1178
788
|
function getOvernightParkingRequestStatusColor(status: string) {
|
|
1179
789
|
switch (status) {
|
|
@@ -1322,7 +932,7 @@ const passStatusOptions = [
|
|
|
1322
932
|
function toRoute(tab: any) {
|
|
1323
933
|
items.value = [];
|
|
1324
934
|
|
|
1325
|
-
const obj = tabOptions.find((x) => x.value === tab);
|
|
935
|
+
const obj = tabOptions.value.find((x) => x.value === tab);
|
|
1326
936
|
if (!obj) return;
|
|
1327
937
|
page.value = 1;
|
|
1328
938
|
getVisitorRefresh();
|
|
@@ -1605,7 +1215,7 @@ function handleRegistrationUnregisteredVisitor(item: Partial<TVisitor>) {
|
|
|
1605
1215
|
dialog.showSelection = true;
|
|
1606
1216
|
mode.value = "register";
|
|
1607
1217
|
|
|
1608
|
-
if(!selectedVisitorDataObject.value){
|
|
1218
|
+
if (!selectedVisitorDataObject.value) {
|
|
1609
1219
|
getVisitorRefresh();
|
|
1610
1220
|
}
|
|
1611
1221
|
}
|
|
@@ -1856,12 +1466,10 @@ async function handleDownloadPDFReport() {
|
|
|
1856
1466
|
if (displayNotCheckedOut.value) queryParams.append("checkedOut", "true");
|
|
1857
1467
|
|
|
1858
1468
|
const queryString = queryParams.toString();
|
|
1859
|
-
const url = `/${route.params.org}/${siteId}/visitor-management/preview${
|
|
1860
|
-
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
new Date().toISOString().split("T")[0]
|
|
1864
|
-
}`;
|
|
1469
|
+
const url = `/${route.params.org}/${siteId}/visitor-management/preview${queryString ? "?" + queryString : ""
|
|
1470
|
+
}`;
|
|
1471
|
+
const title = `Visitor-Report-${siteId}-${new Date().toISOString().split("T")[0]
|
|
1472
|
+
}`;
|
|
1865
1473
|
|
|
1866
1474
|
await downloadPDF({ url, title, orientation: "P", appKey: "SECURITY" });
|
|
1867
1475
|
showMessage("PDF report downloaded successfully!", "success");
|
|
@@ -2021,7 +1629,12 @@ watch([searchInput, dateFrom, dateTo, filterTypes, activeTab], () => {
|
|
|
2021
1629
|
});
|
|
2022
1630
|
|
|
2023
1631
|
onMounted(() => {
|
|
2024
|
-
|
|
1632
|
+
const requestedTab = (route.query.tab as string) || "registered";
|
|
1633
|
+
activeTab.value = enableUnregisteredTab.value
|
|
1634
|
+
? requestedTab
|
|
1635
|
+
: requestedTab === "unregistered"
|
|
1636
|
+
? "registered"
|
|
1637
|
+
: requestedTab;
|
|
2025
1638
|
searchInput.value = (route.query.search as string) || "";
|
|
2026
1639
|
if (route.query.dateFrom) {
|
|
2027
1640
|
dateFrom.value = normalizeDateOnly(route.query.dateFrom as string);
|
|
@@ -2042,28 +1655,38 @@ onMounted(() => {
|
|
|
2042
1655
|
const { socketUnregisteredVisitorTrigger, visitorSocketData } =
|
|
2043
1656
|
useVisitorSocket();
|
|
2044
1657
|
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
1658
|
+
watch(
|
|
1659
|
+
enableUnregisteredTab,
|
|
1660
|
+
(enabled) => {
|
|
1661
|
+
if (!enabled && activeTab.value === "unregistered") {
|
|
1662
|
+
activeTab.value = "registered";
|
|
2050
1663
|
}
|
|
1664
|
+
},
|
|
1665
|
+
{ immediate: true }
|
|
1666
|
+
);
|
|
2051
1667
|
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
1668
|
+
watchEffect(async () => {
|
|
1669
|
+
if (visitorSocketData.value && visitorSocketData.value.reload && activeTab.value === "unregistered") {
|
|
1670
|
+
await getVisitorRefresh();
|
|
1671
|
+
visitorSocketData.value = null;
|
|
1672
|
+
socketUnregisteredVisitorTrigger.value = false;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
if (visitorSocketData.value && visitorSocketData.value.plateNumber && socketUnregisteredVisitorTrigger.value && activeTab.value === "unregistered" && dialog.showSelection === false) {
|
|
1676
|
+
// await getVisitorRefresh();
|
|
1677
|
+
// visitorSocketData.value = null;
|
|
1678
|
+
// socketUnregisteredVisitorTrigger.value = false;
|
|
1679
|
+
setTimeout(async () => {
|
|
1680
|
+
const { _id } = visitorSocketData.value;
|
|
1681
|
+
await handleRegistrationUnregisteredVisitor({ _id });
|
|
1682
|
+
socketUnregisteredVisitorTrigger.value = false;
|
|
1683
|
+
|
|
1684
|
+
setTimeout(() => {
|
|
1685
|
+
visitorSocketData.value = null;
|
|
2064
1686
|
}, 1000)
|
|
2065
|
-
}
|
|
2066
|
-
}
|
|
1687
|
+
}, 1000)
|
|
1688
|
+
}
|
|
1689
|
+
})
|
|
2067
1690
|
|
|
2068
1691
|
</script>
|
|
2069
1692
|
|