@7365admin1/layer-common 4.97.1-staging.586 → 4.97.1-staging.588
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/components/AddPassKeyToVisitor.vue +332 -54
- package/components/FileInput.vue +4 -0
- package/components/MemberInformation.vue +580 -281
- package/components/Nfc/NFCPatrolRouteForm.vue +81 -13
- package/components/Nfc/NFCPatrolRouteMain.vue +17 -5
- package/components/Nfc/PatrolReport/SummaryReportTable.vue +1 -0
- package/components/NricScanDialog.vue +512 -0
- package/components/PassInformation.vue +11 -2
- package/components/VisitorForm.vue +1793 -642
- package/components/VisitorFormSelection.vue +51 -18
- package/components/VisitorManagement.vue +4507 -3518
- package/components/VisitorPassKeyQRScanner.vue +1 -1
- package/components/VisitorSocketPopUp.vue +72 -23
- package/composables/useNFCPatrolReportFilters.ts +55 -15
- package/composables/useNFCPatrolRoute.ts +4 -2
- package/composables/usePeople.ts +54 -0
- package/composables/useVisitor.ts +4 -2
- package/composables/useVisitorSocket.ts +19 -0
- package/package.json +2 -1
- package/types/nfc-patrol-report.ts +4 -2
- package/utils/nfc-patrol-report.ts +47 -10
- package/utils/nfc-patrol-schedule.ts +20 -0
- package/utils/nric-card.ts +278 -0
- package/utils/visitor-people.ts +262 -0
|
@@ -33,17 +33,21 @@
|
|
|
33
33
|
<label class="text-caption screen-hint mb-1 d-block">
|
|
34
34
|
Checkpoints <span class="text-error">*</span>
|
|
35
35
|
</label>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
:
|
|
36
|
+
<!-- Any number of checkpoints (it was a 1-10 list). -->
|
|
37
|
+
<v-text-field
|
|
38
|
+
:model-value="form.checkPointNumber"
|
|
39
|
+
type="number"
|
|
40
|
+
min="1"
|
|
41
|
+
inputmode="numeric"
|
|
39
42
|
variant="outlined"
|
|
40
43
|
density="comfortable"
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
:rules="[rules.checkpointCount]"
|
|
45
|
+
hide-details="auto"
|
|
46
|
+
@update:model-value="onCheckpointCountInput"
|
|
43
47
|
/>
|
|
44
48
|
</v-col>
|
|
45
49
|
|
|
46
|
-
<v-col cols="12" md="4" class="mb-4 px-md-1">
|
|
50
|
+
<v-col v-if="form.timed" cols="12" md="4" class="mb-4 px-md-1">
|
|
47
51
|
<label class="text-caption screen-hint mb-1 d-block">
|
|
48
52
|
Tolerance (mins) <span class="text-error">*</span>
|
|
49
53
|
</label>
|
|
@@ -74,11 +78,46 @@
|
|
|
74
78
|
density="comfortable"
|
|
75
79
|
hide-details
|
|
76
80
|
chips
|
|
81
|
+
>
|
|
82
|
+
<template #prepend-item>
|
|
83
|
+
<v-list-item
|
|
84
|
+
:title="allDaysSelected ? 'Unselect all' : 'Select all'"
|
|
85
|
+
@click="toggleAllDays"
|
|
86
|
+
>
|
|
87
|
+
<template #prepend>
|
|
88
|
+
<v-checkbox-btn
|
|
89
|
+
:model-value="allDaysSelected"
|
|
90
|
+
:indeterminate="someDaysSelected"
|
|
91
|
+
@click.stop="toggleAllDays"
|
|
92
|
+
/>
|
|
93
|
+
</template>
|
|
94
|
+
</v-list-item>
|
|
95
|
+
<v-divider class="mt-1" />
|
|
96
|
+
</template>
|
|
97
|
+
</v-select>
|
|
98
|
+
</v-col>
|
|
99
|
+
|
|
100
|
+
<!-- Route timing. Off: no start times or tolerance, and the route can be
|
|
101
|
+
patrolled at any time of the day. -->
|
|
102
|
+
<v-col cols="12" class="mb-4">
|
|
103
|
+
<v-switch
|
|
104
|
+
v-model="form.timed"
|
|
105
|
+
color="primary"
|
|
106
|
+
inset
|
|
107
|
+
hide-details
|
|
108
|
+
:label="form.timed ? 'Route timing: On' : 'Route timing: Off'"
|
|
77
109
|
/>
|
|
110
|
+
<div class="text-caption screen-hint">
|
|
111
|
+
{{
|
|
112
|
+
form.timed
|
|
113
|
+
? "Patrols start at the route's start times, within the tolerance."
|
|
114
|
+
: "No time window: this route can be patrolled at any time."
|
|
115
|
+
}}
|
|
116
|
+
</div>
|
|
78
117
|
</v-col>
|
|
79
118
|
|
|
80
119
|
<!-- Start Times -->
|
|
81
|
-
<v-col cols="12" class="mb-4">
|
|
120
|
+
<v-col v-if="form.timed" cols="12" class="mb-4">
|
|
82
121
|
<v-card variant="outlined" class="pa-4" rounded="lg">
|
|
83
122
|
<div class="d-flex align-center justify-space-between mb-3">
|
|
84
123
|
<span class="text-h6 font-weight-medium">Start Times</span>
|
|
@@ -264,6 +303,8 @@
|
|
|
264
303
|
<script setup lang="ts">
|
|
265
304
|
import useNFCPatrolRoute from "../../composables/useNFCPatrolRoute";
|
|
266
305
|
import useNFCPatrolTag from "../../composables/useNFCPatrolTag";
|
|
306
|
+
import { collectAllPages } from "../../utils/nfc-patrol-report";
|
|
307
|
+
import { allDaysToggled, checkpointCountError } from "../../utils/nfc-patrol-schedule";
|
|
267
308
|
|
|
268
309
|
const props = defineProps({
|
|
269
310
|
title: {
|
|
@@ -329,11 +370,12 @@ const form = ref({
|
|
|
329
370
|
startTimes: ["08:00"] as string[],
|
|
330
371
|
endTimes: ["09:00"] as string[],
|
|
331
372
|
checkPoints: [] as Checkpoint[],
|
|
373
|
+
// Route timing; false = no time window (patrol at any time).
|
|
374
|
+
timed: true,
|
|
332
375
|
});
|
|
333
376
|
|
|
334
377
|
const startTimeKeys = ref<number[]>([Date.now()]);
|
|
335
378
|
|
|
336
|
-
const checkpointOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
337
379
|
const dayOptions = [
|
|
338
380
|
{ title: "Monday", value: 1 },
|
|
339
381
|
{ title: "Tuesday", value: 2 },
|
|
@@ -344,10 +386,29 @@ const dayOptions = [
|
|
|
344
386
|
{ title: "Sunday", value: 0 },
|
|
345
387
|
];
|
|
346
388
|
|
|
389
|
+
const allDaysSelected = computed(() => form.value.days.length === dayOptions.length);
|
|
390
|
+
const someDaysSelected = computed(
|
|
391
|
+
() => form.value.days.length > 0 && !allDaysSelected.value,
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
function toggleAllDays() {
|
|
395
|
+
form.value.days = allDaysToggled(
|
|
396
|
+
form.value.days,
|
|
397
|
+
dayOptions.map((day) => day.value),
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function onCheckpointCountInput(value: string | number) {
|
|
402
|
+
const count = Number(value);
|
|
403
|
+
form.value.checkPointNumber = count;
|
|
404
|
+
if (Number.isInteger(count) && count >= 1) updateCheckpointsCount(count);
|
|
405
|
+
}
|
|
406
|
+
|
|
347
407
|
const MAX_TOLERANCE_MINUTES = 60;
|
|
348
408
|
|
|
349
409
|
const rules = {
|
|
350
410
|
required: (v: any) => !!v || "This field is required",
|
|
411
|
+
checkpointCount: (v: any) => checkpointCountError(v) ?? true,
|
|
351
412
|
tolerance: (v: any) => {
|
|
352
413
|
if (!/^\d+$/.test(String(v ?? ""))) return "Numbers only";
|
|
353
414
|
if (Number(v) > MAX_TOLERANCE_MINUTES)
|
|
@@ -374,8 +435,11 @@ const { getAll: getAllNFCTags } = useNFCPatrolTag();
|
|
|
374
435
|
|
|
375
436
|
async function fetchNFCTags() {
|
|
376
437
|
try {
|
|
377
|
-
|
|
378
|
-
nfcTags.value =
|
|
438
|
+
// Every page: a site can have any number of tags (it read the first 100).
|
|
439
|
+
nfcTags.value = await collectAllPages(
|
|
440
|
+
(page) => getAllNFCTags({ site: props.site, page, limit: 100 }) as any,
|
|
441
|
+
1000,
|
|
442
|
+
);
|
|
379
443
|
} catch (error) {
|
|
380
444
|
console.error("Error fetching NFC tags:", error);
|
|
381
445
|
}
|
|
@@ -554,6 +618,7 @@ async function initializeEditForm(newRoute: any) {
|
|
|
554
618
|
form.value.startTimes = startTimes;
|
|
555
619
|
form.value.endTimes = endTimes;
|
|
556
620
|
form.value.checkPoints = existingCheckPoints;
|
|
621
|
+
form.value.timed = newRoute.timed !== false;
|
|
557
622
|
|
|
558
623
|
// Reinitialize keys for each start time
|
|
559
624
|
startTimeKeys.value = startTimes.map(() => Date.now() + Math.random());
|
|
@@ -598,14 +663,17 @@ async function submit() {
|
|
|
598
663
|
),
|
|
599
664
|
];
|
|
600
665
|
|
|
666
|
+
// Timing off keeps the tolerance as it was (the server requires one) but
|
|
667
|
+
// sends no start times: the route has no schedule.
|
|
601
668
|
const payload = {
|
|
602
669
|
name: form.value.name,
|
|
603
|
-
checkPointNumber: form.value.checkPointNumber,
|
|
604
|
-
tolerance: form.value.tolerance,
|
|
670
|
+
checkPointNumber: Number(form.value.checkPointNumber),
|
|
671
|
+
tolerance: Number(form.value.tolerance) || 0,
|
|
605
672
|
days: form.value.days,
|
|
606
|
-
startTimes: uniqueStartTimes,
|
|
673
|
+
startTimes: form.value.timed ? uniqueStartTimes : [],
|
|
607
674
|
checkPoints: form.value.checkPoints,
|
|
608
675
|
site: props.site,
|
|
676
|
+
timed: form.value.timed,
|
|
609
677
|
};
|
|
610
678
|
|
|
611
679
|
if (props.mode === "create") {
|
|
@@ -50,14 +50,15 @@
|
|
|
50
50
|
|
|
51
51
|
<template #item.tolerance="{ item }">
|
|
52
52
|
<div class="text-center">
|
|
53
|
-
{{ item.tolerance ? `${item.tolerance} mins` : "--" }}
|
|
53
|
+
{{ item.timed && item.tolerance ? `${item.tolerance} mins` : "--" }}
|
|
54
54
|
</div>
|
|
55
55
|
</template>
|
|
56
56
|
|
|
57
57
|
<!-- Earliest first, one unbreakable pill per window; past three the
|
|
58
58
|
rest fold into "+N more" so a busy route keeps a normal row. -->
|
|
59
|
-
<template #item.schedules="{ value }">
|
|
60
|
-
<
|
|
59
|
+
<template #item.schedules="{ item, value }">
|
|
60
|
+
<span v-if="!item.timed">Anytime (timing off)</span>
|
|
61
|
+
<NFCRouteScheduleTimes v-else :windows="value || []" :limit="3" />
|
|
61
62
|
</template>
|
|
62
63
|
|
|
63
64
|
<template #item.repeatDays="{ value }">
|
|
@@ -132,7 +133,12 @@
|
|
|
132
133
|
</span>
|
|
133
134
|
</v-col>
|
|
134
135
|
|
|
135
|
-
<v-col cols="12" class="mb-2" v-if="selectedRoute.
|
|
136
|
+
<v-col cols="12" class="mb-2" v-if="selectedRoute.timed === false">
|
|
137
|
+
Security Check Schedule:
|
|
138
|
+
<span class="font-weight-bold">Anytime (timing off)</span>
|
|
139
|
+
</v-col>
|
|
140
|
+
|
|
141
|
+
<v-col cols="12" class="mb-2" v-if="selectedRoute.timed !== false && selectedRoute.tolerance">
|
|
136
142
|
Tolerance:
|
|
137
143
|
<span class="font-weight-bold">
|
|
138
144
|
{{ selectedRoute.tolerance }} mins
|
|
@@ -149,7 +155,7 @@
|
|
|
149
155
|
<v-col
|
|
150
156
|
cols="12"
|
|
151
157
|
class="mb-2"
|
|
152
|
-
v-if="selectedRoute.schedules && selectedRoute.schedules.length"
|
|
158
|
+
v-if="selectedRoute.timed !== false && selectedRoute.schedules && selectedRoute.schedules.length"
|
|
153
159
|
>
|
|
154
160
|
<div class="mb-2">
|
|
155
161
|
Security Check Schedule
|
|
@@ -376,6 +382,7 @@ watchEffect(() => {
|
|
|
376
382
|
days: item.days,
|
|
377
383
|
startTimes: item.startTimes || [],
|
|
378
384
|
checkPoints: item.checkPoints || [],
|
|
385
|
+
timed: item.timed !== false,
|
|
379
386
|
// Display fields for the table
|
|
380
387
|
routeName: item.name,
|
|
381
388
|
numberOfCheckpoints: item.checkPointNumber,
|
|
@@ -392,6 +399,11 @@ watchEffect(() => {
|
|
|
392
399
|
}
|
|
393
400
|
});
|
|
394
401
|
|
|
402
|
+
// A new search starts on page 1 (from page 2 or later it could show nothing).
|
|
403
|
+
watch(searchInput, () => {
|
|
404
|
+
page.value = 1;
|
|
405
|
+
});
|
|
406
|
+
|
|
395
407
|
const dialogAdd = ref(false);
|
|
396
408
|
const dialogEdit = ref(false);
|
|
397
409
|
const dialogPreview = ref(false);
|
|
@@ -108,6 +108,7 @@ function skipped(row: NFCPatrolReportSummaryRow) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function statusClass(status: NFCPatrolReportSummaryStatus) {
|
|
111
|
+
if (status === "In progress") return "text-warning";
|
|
111
112
|
return status === "Completed" ? "text-success" : "text-error";
|
|
112
113
|
}
|
|
113
114
|
</script>
|