@7365admin1/layer-common 3.0.21 → 3.0.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 +6 -0
- package/components/DashboardMain.vue +300 -782
- package/components/InvitationMain.vue +32 -3
- package/components/MemberMain.vue +1 -1
- package/components/Nfc/NFCPatrolReportMain.vue +3 -25
- package/components/ScheduleTaskMain.vue +47 -12
- package/components/UnitMain.vue +8 -1
- package/components/VisitorForm.vue +2 -0
- package/components/VisitorManagement.vue +23 -0
- package/components/VisitorSocketPopUp.vue +312 -0
- package/composables/useVisitor.ts +3 -0
- package/composables/useVisitorSocket.ts +25 -0
- package/package.json +2 -1
- package/types/verification.d.ts +6 -1
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
v-if="props.cancelInvitation"
|
|
91
91
|
>
|
|
92
92
|
<template v-slot:activator="{ props }">
|
|
93
|
-
<v-icon v-bind="props">mdi-dots-
|
|
93
|
+
<v-icon v-bind="props">mdi-dots-vertical</v-icon>
|
|
94
94
|
</template>
|
|
95
95
|
<v-list>
|
|
96
96
|
<v-list-item @click="openConfirmDialog(item._id ?? '')" v-if="item.status === 'pending'">
|
|
@@ -99,6 +99,9 @@
|
|
|
99
99
|
</v-list>
|
|
100
100
|
</v-menu>
|
|
101
101
|
</template>
|
|
102
|
+
<template #item.status="{value}">
|
|
103
|
+
{{ formatStatus(value) }}
|
|
104
|
+
</template>
|
|
102
105
|
</v-data-table>
|
|
103
106
|
</v-card>
|
|
104
107
|
</v-col>
|
|
@@ -198,14 +201,27 @@ authenticate();
|
|
|
198
201
|
|
|
199
202
|
const headers = computed(() => {
|
|
200
203
|
const baseHeaders = [
|
|
204
|
+
{
|
|
205
|
+
title: "E-mail",
|
|
206
|
+
value: "email",
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
title: "Roles and Permission",
|
|
210
|
+
value: "roleName",
|
|
211
|
+
},
|
|
201
212
|
{
|
|
202
213
|
title: "Date",
|
|
203
214
|
value: "createdAt",
|
|
204
215
|
},
|
|
205
216
|
{
|
|
206
|
-
title: "
|
|
207
|
-
value: "
|
|
217
|
+
title: "Site Invited To",
|
|
218
|
+
value: "metadata.siteName",
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
title: "Status",
|
|
222
|
+
value: "status",
|
|
208
223
|
},
|
|
224
|
+
|
|
209
225
|
];
|
|
210
226
|
|
|
211
227
|
if (props.status === "pending" && props.cancelInvitation) {
|
|
@@ -318,6 +334,19 @@ watchEffect(() => {
|
|
|
318
334
|
}
|
|
319
335
|
});
|
|
320
336
|
|
|
337
|
+
const formatStatus = (status: string) => {
|
|
338
|
+
switch (status) {
|
|
339
|
+
case "pending":
|
|
340
|
+
return "Pending Invite";
|
|
341
|
+
case "expired":
|
|
342
|
+
return "Invitation expired";
|
|
343
|
+
case "accepted":
|
|
344
|
+
return "Accepted";
|
|
345
|
+
default:
|
|
346
|
+
return status?.[0]?.toUpperCase() + status?.slice(1) || "";
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
321
350
|
const dialogMember = ref(false);
|
|
322
351
|
|
|
323
352
|
function inviteMember() {
|
|
@@ -13,18 +13,7 @@
|
|
|
13
13
|
Patrol Report Log
|
|
14
14
|
</v-btn>
|
|
15
15
|
</v-col>
|
|
16
|
-
|
|
17
|
-
<v-btn
|
|
18
|
-
color="primary"
|
|
19
|
-
|
|
20
|
-
variant="flat"
|
|
21
|
-
size="large"
|
|
22
|
-
class="text-none px-8"
|
|
23
|
-
prepend-icon="mdi-plus"
|
|
24
|
-
>
|
|
25
|
-
Create Routes & Schedule
|
|
26
|
-
</v-btn>
|
|
27
|
-
</v-col>
|
|
16
|
+
|
|
28
17
|
</v-row>
|
|
29
18
|
|
|
30
19
|
<!-- Tabs -->
|
|
@@ -74,18 +63,7 @@
|
|
|
74
63
|
bg-color="white"
|
|
75
64
|
/>
|
|
76
65
|
</v-col>
|
|
77
|
-
|
|
78
|
-
<v-btn
|
|
79
|
-
color="primary"
|
|
80
|
-
block
|
|
81
|
-
size="large"
|
|
82
|
-
rounded="lg"
|
|
83
|
-
class="text-none"
|
|
84
|
-
@click="loadReport"
|
|
85
|
-
>
|
|
86
|
-
Select Routes & Schedule
|
|
87
|
-
</v-btn>
|
|
88
|
-
</v-col>
|
|
66
|
+
|
|
89
67
|
<v-col cols="12" md="2" class="pl-md-2">
|
|
90
68
|
<v-btn
|
|
91
69
|
variant="outlined"
|
|
@@ -312,7 +290,7 @@
|
|
|
312
290
|
size="small"
|
|
313
291
|
@click="dialogSelectRoute = false"
|
|
314
292
|
/>
|
|
315
|
-
<span class="text-h6 ml-2">Select Routes & Schedule</span>
|
|
293
|
+
<span class="text-h6 ml-2">Select Routes & Schedule </span>
|
|
316
294
|
</v-card-title>
|
|
317
295
|
|
|
318
296
|
<v-card-text class="px-6 pb-6">
|
|
@@ -162,7 +162,9 @@
|
|
|
162
162
|
class="mb-2"
|
|
163
163
|
>
|
|
164
164
|
<v-col cols="12">
|
|
165
|
-
<div class="text-subtitle-2 font-weight-bold mb-2">
|
|
165
|
+
<div class="text-subtitle-2 font-weight-bold mb-2">
|
|
166
|
+
Units Under Selected Areas:
|
|
167
|
+
</div>
|
|
166
168
|
<v-card variant="outlined" rounded>
|
|
167
169
|
<v-list density="compact" nav>
|
|
168
170
|
<template
|
|
@@ -179,33 +181,62 @@
|
|
|
179
181
|
<v-icon size="16">
|
|
180
182
|
{{
|
|
181
183
|
openViewAreas.includes(area.value || area._id)
|
|
182
|
-
?
|
|
183
|
-
:
|
|
184
|
+
? "mdi-chevron-down"
|
|
185
|
+
: "mdi-chevron-right"
|
|
184
186
|
}}
|
|
185
187
|
</v-icon>
|
|
186
188
|
</template>
|
|
187
189
|
<template #append>
|
|
188
190
|
<v-progress-circular
|
|
189
|
-
v-if="
|
|
191
|
+
v-if="
|
|
192
|
+
loadingViewUnits &&
|
|
193
|
+
!(
|
|
194
|
+
area.value ||
|
|
195
|
+
area._id in fetchedViewAreaUnits
|
|
196
|
+
)
|
|
197
|
+
"
|
|
190
198
|
size="14"
|
|
191
199
|
width="2"
|
|
192
200
|
indeterminate
|
|
193
201
|
color="primary"
|
|
194
202
|
/>
|
|
195
|
-
<v-chip
|
|
196
|
-
|
|
203
|
+
<v-chip
|
|
204
|
+
v-else
|
|
205
|
+
size="x-small"
|
|
206
|
+
variant="tonal"
|
|
207
|
+
color="primary"
|
|
208
|
+
>
|
|
209
|
+
{{
|
|
210
|
+
(
|
|
211
|
+
fetchedViewAreaUnits[
|
|
212
|
+
area.value || area._id
|
|
213
|
+
] || []
|
|
214
|
+
).length
|
|
215
|
+
}}
|
|
197
216
|
</v-chip>
|
|
198
217
|
</template>
|
|
199
218
|
</v-list-item>
|
|
200
219
|
<v-expand-transition>
|
|
201
|
-
<div
|
|
220
|
+
<div
|
|
221
|
+
v-show="
|
|
222
|
+
openViewAreas.includes(area.value || area._id)
|
|
223
|
+
"
|
|
224
|
+
>
|
|
202
225
|
<v-card-text class="px-3 py-2">
|
|
203
226
|
<div
|
|
204
|
-
v-if="
|
|
227
|
+
v-if="
|
|
228
|
+
(
|
|
229
|
+
fetchedViewAreaUnits[
|
|
230
|
+
area.value || area._id
|
|
231
|
+
] || []
|
|
232
|
+
).length > 0
|
|
233
|
+
"
|
|
205
234
|
class="d-flex flex-wrap ga-3"
|
|
206
235
|
>
|
|
207
236
|
<v-chip
|
|
208
|
-
v-for="unit in fetchedViewAreaUnits[
|
|
237
|
+
v-for="unit in fetchedViewAreaUnits[
|
|
238
|
+
area.value || area._id
|
|
239
|
+
]"
|
|
209
240
|
:key="unit.unit"
|
|
210
241
|
size="small"
|
|
211
242
|
variant="tonal"
|
|
@@ -213,7 +244,9 @@
|
|
|
213
244
|
{{ unit.name }}
|
|
214
245
|
</v-chip>
|
|
215
246
|
</div>
|
|
216
|
-
<span v-else class="text-caption text-disabled"
|
|
247
|
+
<span v-else class="text-caption text-disabled"
|
|
248
|
+
>No units configured</span
|
|
249
|
+
>
|
|
217
250
|
</v-card-text>
|
|
218
251
|
</div>
|
|
219
252
|
</v-expand-transition>
|
|
@@ -371,7 +404,9 @@ const messageSnackbar = ref(false);
|
|
|
371
404
|
const messageColor = ref("success");
|
|
372
405
|
|
|
373
406
|
const loadingViewUnits = ref(false);
|
|
374
|
-
const fetchedViewAreaUnits = ref<
|
|
407
|
+
const fetchedViewAreaUnits = ref<
|
|
408
|
+
Record<string, { name: string; unit: string }[]>
|
|
409
|
+
>({});
|
|
375
410
|
const openViewAreas = ref<string[]>([]);
|
|
376
411
|
|
|
377
412
|
const toggleViewArea = (id: string) => {
|
|
@@ -528,7 +563,7 @@ async function _deleteScheduleTask() {
|
|
|
528
563
|
|
|
529
564
|
const response = await deleteScheduleTask(id);
|
|
530
565
|
dialogDeleteTask.value = false;
|
|
531
|
-
showMessage(
|
|
566
|
+
showMessage("Scheduled task successfully deleted.", "success");
|
|
532
567
|
|
|
533
568
|
await getTaskRefresh();
|
|
534
569
|
} catch (error: any) {
|
package/components/UnitMain.vue
CHANGED
|
@@ -197,7 +197,11 @@
|
|
|
197
197
|
</v-card>
|
|
198
198
|
</v-dialog>
|
|
199
199
|
|
|
200
|
-
<Snackbar
|
|
200
|
+
<Snackbar
|
|
201
|
+
v-model="messageSnackbar"
|
|
202
|
+
:text="snackBarMessage"
|
|
203
|
+
:color="messageColor"
|
|
204
|
+
/>
|
|
201
205
|
|
|
202
206
|
<input
|
|
203
207
|
ref="fileInput"
|
|
@@ -297,8 +301,11 @@ const dialogMode = ref<"add" | "edit">("add");
|
|
|
297
301
|
const editingUnit = ref<Record<string, any>>({});
|
|
298
302
|
const areaName = ref("");
|
|
299
303
|
|
|
304
|
+
const snackBarMessage = ref("");
|
|
305
|
+
|
|
300
306
|
function showMessage(msg: string, color: string = "error") {
|
|
301
307
|
message.value = msg;
|
|
308
|
+
snackBarMessage.value = msg;
|
|
302
309
|
messageColor.value = color;
|
|
303
310
|
messageSnackbar.value = true;
|
|
304
311
|
}
|
|
@@ -2034,6 +2034,29 @@ onMounted(() => {
|
|
|
2034
2034
|
(route.query.checkedOut as string) == "true" || false;
|
|
2035
2035
|
}
|
|
2036
2036
|
});
|
|
2037
|
+
|
|
2038
|
+
const { socketUnregisteredVisitorTrigger, visitorSocketData } =
|
|
2039
|
+
useVisitorSocket();
|
|
2040
|
+
|
|
2041
|
+
watchEffect(async () => {
|
|
2042
|
+
if (visitorSocketData.value && visitorSocketData.value.reload && activeTab.value === "unregistered") {
|
|
2043
|
+
await getVisitorRefresh();
|
|
2044
|
+
visitorSocketData.value = null;
|
|
2045
|
+
socketUnregisteredVisitorTrigger.value = false;
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
if(visitorSocketData.value && visitorSocketData.value.plateNumber && socketUnregisteredVisitorTrigger.value && activeTab.value === "unregistered" && dialog.showSelection === false) {
|
|
2049
|
+
// await getVisitorRefresh();
|
|
2050
|
+
// visitorSocketData.value = null;
|
|
2051
|
+
// socketUnregisteredVisitorTrigger.value = false;
|
|
2052
|
+
setTimeout(async() => {
|
|
2053
|
+
const { _id } = visitorSocketData.value;
|
|
2054
|
+
await handleRegistrationUnregisteredVisitor({ _id });
|
|
2055
|
+
socketUnregisteredVisitorTrigger.value = false;
|
|
2056
|
+
}, 1000)
|
|
2057
|
+
}
|
|
2058
|
+
})
|
|
2059
|
+
|
|
2037
2060
|
</script>
|
|
2038
2061
|
|
|
2039
2062
|
<style scoped></style>
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<!-- <h1>Live Monitoring</h1>
|
|
4
|
+
|
|
5
|
+
Connection Status Indicator
|
|
6
|
+
<div :class="['status-badge', status]">Status: {{ status }}</div>
|
|
7
|
+
<pre>
|
|
8
|
+
path: {{ path }}
|
|
9
|
+
</pre>
|
|
10
|
+
<pre>visitorData: {{ visitorSocketData }}</pre>
|
|
11
|
+
<pre>socketTrigger:{{ socketUnregisteredVisitorTrigger }}</pre>
|
|
12
|
+
|
|
13
|
+
<section v-if="newVisitor">
|
|
14
|
+
<h3>New Plate Detected!</h3>
|
|
15
|
+
<div class="visitor-card">
|
|
16
|
+
<p>
|
|
17
|
+
<strong>Plate Number:</strong>
|
|
18
|
+
{{ newVisitor.plateNumber || "Unknown" }}
|
|
19
|
+
</p>
|
|
20
|
+
<p><strong>Site ID:</strong> {{ newVisitor.site }}</p>
|
|
21
|
+
<p><strong>Time:</strong> {{ new Date().toLocaleTimeString() }}</p>
|
|
22
|
+
</div>
|
|
23
|
+
</section>
|
|
24
|
+
|
|
25
|
+
<section v-if="visitorList.length > 0">
|
|
26
|
+
<h3>Recent History</h3>
|
|
27
|
+
<ul>
|
|
28
|
+
<li v-for="(v, index) in visitorList" :key="index">
|
|
29
|
+
{{ v?.plateNumber }} — {{ new Date().toLocaleTimeString() }}
|
|
30
|
+
</li>
|
|
31
|
+
</ul>
|
|
32
|
+
</section> -->
|
|
33
|
+
|
|
34
|
+
<v-snackbar
|
|
35
|
+
v-if="!isVisitorRoute && !socketUnregisteredVisitorTrigger"
|
|
36
|
+
v-model="snackBarMessage.modal"
|
|
37
|
+
:color="snackBarMessage.color"
|
|
38
|
+
:timeout="snackBarMessage.timeout"
|
|
39
|
+
location="bottom center"
|
|
40
|
+
class="rounded-xl"
|
|
41
|
+
multi-line
|
|
42
|
+
:z-index="100000000"
|
|
43
|
+
>
|
|
44
|
+
<div class="d-flex flex-column align-start ga-2">
|
|
45
|
+
<div class="d-flex align-center ga-2">
|
|
46
|
+
<span class="text-1-4rem mr-2">{{ snackBarMessage.text }}</span>
|
|
47
|
+
</div>
|
|
48
|
+
<v-btn
|
|
49
|
+
v-if="snackBarMessage.isUnregistered"
|
|
50
|
+
rounded="md"
|
|
51
|
+
variant="text"
|
|
52
|
+
color="baseButton"
|
|
53
|
+
class="text-truncate text-decoration-underline px-0"
|
|
54
|
+
@click="goToUnregistered"
|
|
55
|
+
>
|
|
56
|
+
Go to Unregistered
|
|
57
|
+
</v-btn>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<template #actions>
|
|
61
|
+
<v-btn
|
|
62
|
+
icon="mdi-close"
|
|
63
|
+
variant="text"
|
|
64
|
+
@click="handleCloseSnackbar"
|
|
65
|
+
/>
|
|
66
|
+
</template>
|
|
67
|
+
</v-snackbar>
|
|
68
|
+
|
|
69
|
+
<v-dialog v-model="permanentMessageDialog.modal" max-width="480" persistent>
|
|
70
|
+
<v-card>
|
|
71
|
+
<v-card-title class="text-h6">Notice</v-card-title>
|
|
72
|
+
<v-card-text>{{ permanentMessageDialog.text }}</v-card-text>
|
|
73
|
+
<v-card-actions>
|
|
74
|
+
<v-spacer />
|
|
75
|
+
<v-btn
|
|
76
|
+
color="primary"
|
|
77
|
+
variant="elevated"
|
|
78
|
+
@click="permanentMessageDialog.modal = false"
|
|
79
|
+
>
|
|
80
|
+
Close
|
|
81
|
+
</v-btn>
|
|
82
|
+
</v-card-actions>
|
|
83
|
+
</v-card>
|
|
84
|
+
</v-dialog>
|
|
85
|
+
</div>
|
|
86
|
+
</template>
|
|
87
|
+
|
|
88
|
+
<script setup lang="ts">
|
|
89
|
+
import { io, type Socket } from "socket.io-client";
|
|
90
|
+
import type { TVisitorSocketData } from "../composables/useVisitorSocket";
|
|
91
|
+
|
|
92
|
+
const props = defineProps({
|
|
93
|
+
siteId: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: "",
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const config = useRuntimeConfig();
|
|
100
|
+
const publicConfig = config.public as Record<string, string | undefined>;
|
|
101
|
+
const route = useRoute();
|
|
102
|
+
const { tab } = useVisitor();
|
|
103
|
+
const { socketUnregisteredVisitorTrigger, visitorSocketData } =
|
|
104
|
+
useVisitorSocket();
|
|
105
|
+
|
|
106
|
+
const socket = shallowRef<Socket | null>(null);
|
|
107
|
+
const status = ref("disconnected");
|
|
108
|
+
const newVisitor = ref<TVisitorSocketData | null>(null);
|
|
109
|
+
const visitorList = ref<TVisitorSocketData[]>([]);
|
|
110
|
+
const lastPlateNumber = ref("");
|
|
111
|
+
|
|
112
|
+
const snackBarMessage = reactive({
|
|
113
|
+
timeout: 3000,
|
|
114
|
+
text: "",
|
|
115
|
+
color: "",
|
|
116
|
+
modal: false,
|
|
117
|
+
isUnregistered: false,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const permanentMessageDialog = reactive({
|
|
121
|
+
modal: false,
|
|
122
|
+
text: "",
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const path = computed(() => route.path);
|
|
126
|
+
const isVisitorManagementPath = (path: string) =>
|
|
127
|
+
path.includes("/visitor-management");
|
|
128
|
+
const isVisitorRoute = computed(
|
|
129
|
+
() =>
|
|
130
|
+
path.value === "/visitors" ||
|
|
131
|
+
isVisitorManagementPath(path.value)
|
|
132
|
+
);
|
|
133
|
+
const isUnregisteredVisitorRoute = computed(
|
|
134
|
+
() => isVisitorRoute.value && route.query.tab === "unregistered"
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const socketBaseUrl = computed(
|
|
138
|
+
() => publicConfig.socketUrl || publicConfig.API_CORE || ""
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const formatDateQuery = (date: Date) => date.toISOString().slice(0, 10);
|
|
142
|
+
|
|
143
|
+
const getDefaultDateQuery = () => {
|
|
144
|
+
const dateTo = new Date();
|
|
145
|
+
const dateFrom = new Date(dateTo);
|
|
146
|
+
dateFrom.setMonth(dateFrom.getMonth() - 1);
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
dateFrom: formatDateQuery(dateFrom),
|
|
150
|
+
dateTo: formatDateQuery(dateTo),
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleCloseSnackbar = () => {
|
|
155
|
+
snackBarMessage.modal = false;
|
|
156
|
+
socketUnregisteredVisitorTrigger.value = false;
|
|
157
|
+
newVisitor.value = null;
|
|
158
|
+
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const showTransientMessage = (
|
|
162
|
+
text: string,
|
|
163
|
+
color: string,
|
|
164
|
+
isUnregistered = false
|
|
165
|
+
) => {
|
|
166
|
+
snackBarMessage.text = text;
|
|
167
|
+
snackBarMessage.color = color;
|
|
168
|
+
snackBarMessage.modal = true;
|
|
169
|
+
snackBarMessage.timeout = 20000;
|
|
170
|
+
snackBarMessage.isUnregistered = isUnregistered;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const connectSocket = () => {
|
|
174
|
+
if (!props.siteId || !socketBaseUrl.value || socket.value) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const namespace = `/site-${props.siteId}`;
|
|
179
|
+
socket.value = io(`${socketBaseUrl.value}${namespace}`, {
|
|
180
|
+
path: "/socket.io",
|
|
181
|
+
transports: ["websocket"],
|
|
182
|
+
reconnectionAttempts: 5,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
socket.value.on("connect", () => {
|
|
186
|
+
status.value = "connected";
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
socket.value.on("anpr-messages", (data: TVisitorSocketData) => {
|
|
190
|
+
const plateNumber = data?.plateNumber?.trim() || "";
|
|
191
|
+
const isCheckIn = Boolean(plateNumber);
|
|
192
|
+
const isPlateNumberChanging =
|
|
193
|
+
Boolean(lastPlateNumber.value) && lastPlateNumber.value !== plateNumber;
|
|
194
|
+
|
|
195
|
+
socketUnregisteredVisitorTrigger.value = false;
|
|
196
|
+
visitorSocketData.value = null;
|
|
197
|
+
visitorSocketData.value = data;
|
|
198
|
+
newVisitor.value = data;
|
|
199
|
+
|
|
200
|
+
if (!isVisitorRoute.value && isCheckIn) {
|
|
201
|
+
showTransientMessage(
|
|
202
|
+
`New plate detected: ${plateNumber}`,
|
|
203
|
+
"info",
|
|
204
|
+
true
|
|
205
|
+
);
|
|
206
|
+
lastPlateNumber.value = plateNumber;
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (isUnregisteredVisitorRoute.value && isCheckIn) {
|
|
211
|
+
if (isPlateNumberChanging) {
|
|
212
|
+
showTransientMessage(`New plate detected: ${plateNumber}`, "info", true);
|
|
213
|
+
} else {
|
|
214
|
+
snackBarMessage.modal = false;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
lastPlateNumber.value = plateNumber;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (isCheckIn) {
|
|
222
|
+
lastPlateNumber.value = plateNumber;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (data?.message) {
|
|
226
|
+
showTransientMessage(data.message, "error");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (data?.messagePermanent) {
|
|
230
|
+
permanentMessageDialog.text = data.messagePermanent;
|
|
231
|
+
permanentMessageDialog.modal = true;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
visitorList.value.unshift(data);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
socket.value.on("connect_error", () => {
|
|
238
|
+
status.value = "disconnected";
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const disconnectSocket = () => {
|
|
243
|
+
if (socket.value) {
|
|
244
|
+
socket.value.disconnect();
|
|
245
|
+
socket.value = null;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const goToUnregistered = async () => {
|
|
250
|
+
tab.value = "unregistered";
|
|
251
|
+
socketUnregisteredVisitorTrigger.value = true;
|
|
252
|
+
snackBarMessage.modal = false;
|
|
253
|
+
|
|
254
|
+
if (route.params.org && route.params.site) {
|
|
255
|
+
const defaultDateQuery = getDefaultDateQuery();
|
|
256
|
+
|
|
257
|
+
await navigateTo({
|
|
258
|
+
path: `/${route.params.org}/${route.params.site}/visitor-management`,
|
|
259
|
+
query: {
|
|
260
|
+
tab: "unregistered",
|
|
261
|
+
dateFrom: route.query.dateFrom || defaultDateQuery.dateFrom,
|
|
262
|
+
dateTo: route.query.dateTo || defaultDateQuery.dateTo,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
await navigateTo({
|
|
269
|
+
name: "visitors",
|
|
270
|
+
query: {
|
|
271
|
+
tab: "unregistered",
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
watch(
|
|
277
|
+
() => props.siteId,
|
|
278
|
+
() => {
|
|
279
|
+
disconnectSocket();
|
|
280
|
+
connectSocket();
|
|
281
|
+
},
|
|
282
|
+
{ immediate: true }
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
watch(
|
|
286
|
+
() => route.fullPath,
|
|
287
|
+
() => {
|
|
288
|
+
if (isUnregisteredVisitorRoute.value) {
|
|
289
|
+
snackBarMessage.modal = false;
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
{ immediate: true }
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
onUnmounted(() => {
|
|
296
|
+
disconnectSocket();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
watch(
|
|
300
|
+
() => route.path,
|
|
301
|
+
(currentPath, previousPath) => {
|
|
302
|
+
if (
|
|
303
|
+
previousPath &&
|
|
304
|
+
isVisitorManagementPath(previousPath) &&
|
|
305
|
+
!isVisitorManagementPath(currentPath)
|
|
306
|
+
) {
|
|
307
|
+
handleCloseSnackbar();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
</script>
|
|
@@ -94,6 +94,8 @@ export default function () {
|
|
|
94
94
|
tab?: string;
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
+
const tab = useState("visitorsTab", () => "visitor");
|
|
98
|
+
|
|
97
99
|
async function getVisitors({
|
|
98
100
|
page = 1,
|
|
99
101
|
limit = 10,
|
|
@@ -190,6 +192,7 @@ export default function () {
|
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
return {
|
|
195
|
+
tab,
|
|
193
196
|
typeFieldMap,
|
|
194
197
|
visitorSelection,
|
|
195
198
|
contractorTypes,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type TVisitorSocketData = {
|
|
2
|
+
_id: string;
|
|
3
|
+
site: string;
|
|
4
|
+
plateNumber: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
messagePermanent?: string;
|
|
7
|
+
reload?: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const useVisitorSocket = () => {
|
|
11
|
+
const socketUnregisteredVisitorTrigger = useState(
|
|
12
|
+
"socketUnregisteredVisitorTrigger",
|
|
13
|
+
() => false,
|
|
14
|
+
);
|
|
15
|
+
const visitorSocketData = useState(
|
|
16
|
+
"visitorSocketData",
|
|
17
|
+
(): TVisitorSocketData | null => null,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
socketUnregisteredVisitorTrigger,
|
|
23
|
+
visitorSocketData,
|
|
24
|
+
};
|
|
25
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@7365admin1/layer-common",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "3.0.
|
|
5
|
+
"version": "3.0.22",
|
|
6
6
|
"author": "7365admin1",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
8
8
|
"publishConfig": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"qrcode": "^1.5.4",
|
|
41
41
|
"qrcode.vue": "^3.4.1",
|
|
42
42
|
"sass": "^1.80.6",
|
|
43
|
+
"socket.io-client": "^4.8.3",
|
|
43
44
|
"vue-draggable-next": "^2.3.0",
|
|
44
45
|
"vue3-signature": "^0.2.4",
|
|
45
46
|
"vuetify-pro-tiptap": "^2.8.2",
|