@7365admin1/layer-common 3.0.21 → 3.0.23
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/DashboardMain.vue +211 -58
- package/components/InvitationMain.vue +32 -3
- package/components/MemberMain.vue +1 -1
- package/components/Nfc/NFCPatrolReportMain.vue +48 -541
- package/components/Nfc/PatrolReport/DailyReportTab.vue +59 -0
- package/components/Nfc/PatrolReport/MonthlyReportTab.vue +59 -0
- package/components/Nfc/PatrolReport/PatrolActivityTable.vue +81 -0
- package/components/Nfc/PatrolReport/PatrolReportTab.vue +73 -0
- package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +63 -0
- package/components/Nfc/PatrolReport/RemarksDialog.vue +37 -0
- package/components/Nfc/PatrolReport/ReportEmptyState.vue +24 -0
- package/components/Nfc/PatrolReport/ReportFilters.vue +88 -0
- package/components/Nfc/PatrolReport/ReportLocationHeader.vue +17 -0
- package/components/Nfc/PatrolReport/SummaryReportTable.vue +51 -0
- package/components/ScheduleTaskMain.vue +47 -12
- package/components/ServiceProviderMain.vue +182 -184
- package/components/UnitMain.vue +8 -1
- package/components/VisitorForm.vue +2 -0
- package/components/VisitorManagement.vue +31 -0
- package/components/VisitorSocketPopUp.vue +292 -0
- package/composables/useNFCPatrolDailyReport.ts +27 -0
- package/composables/useNFCPatrolMonthlyReport.ts +27 -0
- package/composables/useNFCPatrolReport.ts +38 -0
- package/composables/useNFCPatrolReportExport.ts +31 -0
- package/composables/useNFCPatrolReportFilters.ts +39 -0
- package/composables/useVisitor.ts +3 -0
- package/composables/useVisitorSocket.ts +25 -0
- package/package.json +2 -1
- package/services/nfcPatrolReport.ts +172 -0
- package/types/nfc-patrol-report.ts +73 -0
- package/types/verification.d.ts +6 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<ReportFilters
|
|
4
|
+
:model-value="filters"
|
|
5
|
+
:options="options"
|
|
6
|
+
@update:model-value="$emit('update:filters', $event)"
|
|
7
|
+
@export="$emit('export')"
|
|
8
|
+
/>
|
|
9
|
+
|
|
10
|
+
<v-divider :thickness="1" class="border-opacity-100" />
|
|
11
|
+
|
|
12
|
+
<template v-if="report">
|
|
13
|
+
<ReportLocationHeader :location="report.location" />
|
|
14
|
+
<v-divider :thickness="1" class="border-opacity-100 mb-6" />
|
|
15
|
+
<SummaryReportTable :title="report.title" :rows="report.rows" />
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<ReportEmptyState
|
|
19
|
+
v-else-if="!loading"
|
|
20
|
+
title="No Monthly Report Selected"
|
|
21
|
+
message="Please select a route, time range, and date to view the monthly report"
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import type {
|
|
28
|
+
NFCPatrolReportFilterOptions,
|
|
29
|
+
NFCPatrolReportFilters,
|
|
30
|
+
} from "../../../types/nfc-patrol-report";
|
|
31
|
+
import { useNFCPatrolMonthlyReport } from "../../../composables/useNFCPatrolMonthlyReport";
|
|
32
|
+
import ReportEmptyState from "./ReportEmptyState.vue";
|
|
33
|
+
import ReportFilters from "./ReportFilters.vue";
|
|
34
|
+
import ReportLocationHeader from "./ReportLocationHeader.vue";
|
|
35
|
+
import SummaryReportTable from "./SummaryReportTable.vue";
|
|
36
|
+
|
|
37
|
+
const props = defineProps<{
|
|
38
|
+
active: boolean;
|
|
39
|
+
filters: NFCPatrolReportFilters;
|
|
40
|
+
options: NFCPatrolReportFilterOptions;
|
|
41
|
+
}>();
|
|
42
|
+
|
|
43
|
+
defineEmits<{
|
|
44
|
+
"update:filters": [value: NFCPatrolReportFilters];
|
|
45
|
+
export: [];
|
|
46
|
+
}>();
|
|
47
|
+
|
|
48
|
+
const { report, loading, fetchReport } = useNFCPatrolMonthlyReport(props.filters);
|
|
49
|
+
|
|
50
|
+
watch(
|
|
51
|
+
() => [props.filters.route, props.filters.timeRange, props.filters.date, props.active],
|
|
52
|
+
() => {
|
|
53
|
+
if (props.active) {
|
|
54
|
+
void fetchReport();
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ immediate: true },
|
|
58
|
+
);
|
|
59
|
+
</script>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card variant="flat" rounded="lg">
|
|
3
|
+
<v-card-title>Patrol Activity</v-card-title>
|
|
4
|
+
|
|
5
|
+
<v-card-text class="pa-0">
|
|
6
|
+
<v-table class="border-thin">
|
|
7
|
+
<thead>
|
|
8
|
+
<tr>
|
|
9
|
+
<th>Checkpoints</th>
|
|
10
|
+
<th>Start - End Time</th>
|
|
11
|
+
<th>
|
|
12
|
+
Travel time<br />
|
|
13
|
+
<span class="text-caption text-medium-emphasis">Actual/Expected</span>
|
|
14
|
+
</th>
|
|
15
|
+
<th>Status</th>
|
|
16
|
+
</tr>
|
|
17
|
+
</thead>
|
|
18
|
+
<tbody>
|
|
19
|
+
<tr
|
|
20
|
+
v-for="activity in activities"
|
|
21
|
+
:key="activity.id"
|
|
22
|
+
:class="{ 'cursor-pointer': activity.status === 'Skipped' }"
|
|
23
|
+
@click="handleActivityClick(activity)"
|
|
24
|
+
>
|
|
25
|
+
<td>{{ activity.checkpoint }}</td>
|
|
26
|
+
<td>
|
|
27
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
28
|
+
{{ activity.startTime }}
|
|
29
|
+
<v-icon size="small" class="mx-2">mdi-arrow-right</v-icon>
|
|
30
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
31
|
+
{{ activity.endTime }}
|
|
32
|
+
</td>
|
|
33
|
+
<td>
|
|
34
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
35
|
+
{{ activity.actualTime }} / {{ activity.expectedTime }}
|
|
36
|
+
</td>
|
|
37
|
+
<td>
|
|
38
|
+
<v-chip :color="statusColor(activity.status)" size="small" variant="flat">
|
|
39
|
+
{{ activity.status }}
|
|
40
|
+
<v-icon v-if="activity.status === 'Skipped'" size="small" class="ml-1">
|
|
41
|
+
mdi-flag
|
|
42
|
+
</v-icon>
|
|
43
|
+
</v-chip>
|
|
44
|
+
</td>
|
|
45
|
+
</tr>
|
|
46
|
+
</tbody>
|
|
47
|
+
</v-table>
|
|
48
|
+
</v-card-text>
|
|
49
|
+
</v-card>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<script setup lang="ts">
|
|
53
|
+
import type {
|
|
54
|
+
NFCPatrolActivity,
|
|
55
|
+
NFCPatrolActivityStatus,
|
|
56
|
+
} from "../../../types/nfc-patrol-report";
|
|
57
|
+
|
|
58
|
+
defineProps<{
|
|
59
|
+
activities: NFCPatrolActivity[];
|
|
60
|
+
}>();
|
|
61
|
+
|
|
62
|
+
const emit = defineEmits<{
|
|
63
|
+
remarks: [activity: NFCPatrolActivity];
|
|
64
|
+
}>();
|
|
65
|
+
|
|
66
|
+
function statusColor(status: NFCPatrolActivityStatus) {
|
|
67
|
+
return status === "Completed" ? "success" : "error";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function handleActivityClick(activity: NFCPatrolActivity) {
|
|
71
|
+
if (activity.status === "Skipped") {
|
|
72
|
+
emit("remarks", activity);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<style scoped>
|
|
78
|
+
.cursor-pointer {
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<ReportFilters
|
|
4
|
+
:model-value="filters"
|
|
5
|
+
:options="options"
|
|
6
|
+
@update:model-value="$emit('update:filters', $event)"
|
|
7
|
+
@export="$emit('export')"
|
|
8
|
+
/>
|
|
9
|
+
|
|
10
|
+
<v-divider :thickness="1" class="border-opacity-100" />
|
|
11
|
+
|
|
12
|
+
<template v-if="report">
|
|
13
|
+
<ReportLocationHeader :location="report.location" />
|
|
14
|
+
<v-divider :thickness="1" class="border-opacity-100 mb-6" />
|
|
15
|
+
|
|
16
|
+
<PatrolRouteSummary v-if="report.routeSummary" :summary="report.routeSummary" />
|
|
17
|
+
|
|
18
|
+
<PatrolActivityTable
|
|
19
|
+
v-if="report.activities.length"
|
|
20
|
+
:activities="report.activities"
|
|
21
|
+
@remarks="openRemarksDialog"
|
|
22
|
+
/>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<ReportEmptyState v-else-if="!loading" />
|
|
26
|
+
|
|
27
|
+
<RemarksDialog v-model="dialogRemarks" :remarks="selectedActivityRemarks" />
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import type {
|
|
33
|
+
NFCPatrolReportFilterOptions,
|
|
34
|
+
NFCPatrolReportFilters,
|
|
35
|
+
} from "../../../types/nfc-patrol-report";
|
|
36
|
+
import { useNFCPatrolReport } from "../../../composables/useNFCPatrolReport";
|
|
37
|
+
import PatrolActivityTable from "./PatrolActivityTable.vue";
|
|
38
|
+
import PatrolRouteSummary from "./PatrolRouteSummary.vue";
|
|
39
|
+
import RemarksDialog from "./RemarksDialog.vue";
|
|
40
|
+
import ReportEmptyState from "./ReportEmptyState.vue";
|
|
41
|
+
import ReportFilters from "./ReportFilters.vue";
|
|
42
|
+
import ReportLocationHeader from "./ReportLocationHeader.vue";
|
|
43
|
+
|
|
44
|
+
const props = defineProps<{
|
|
45
|
+
active: boolean;
|
|
46
|
+
filters: NFCPatrolReportFilters;
|
|
47
|
+
options: NFCPatrolReportFilterOptions;
|
|
48
|
+
}>();
|
|
49
|
+
|
|
50
|
+
defineEmits<{
|
|
51
|
+
"update:filters": [value: NFCPatrolReportFilters];
|
|
52
|
+
export: [];
|
|
53
|
+
}>();
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
report,
|
|
57
|
+
loading,
|
|
58
|
+
selectedActivityRemarks,
|
|
59
|
+
dialogRemarks,
|
|
60
|
+
fetchReport,
|
|
61
|
+
openRemarksDialog,
|
|
62
|
+
} = useNFCPatrolReport(props.filters);
|
|
63
|
+
|
|
64
|
+
watch(
|
|
65
|
+
() => [props.filters.route, props.filters.timeRange, props.filters.date, props.active],
|
|
66
|
+
() => {
|
|
67
|
+
if (props.active) {
|
|
68
|
+
void fetchReport();
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{ immediate: true },
|
|
72
|
+
);
|
|
73
|
+
</script>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="mb-6" variant="flat" rounded="lg">
|
|
3
|
+
<v-card-title class="d-flex justify-space-between align-center">
|
|
4
|
+
<span>Patrol Route Summary: {{ summary.routeName }}</span>
|
|
5
|
+
<span class="text-body-2 text-medium-emphasis">
|
|
6
|
+
Tolerance: {{ summary.tolerance }} mins
|
|
7
|
+
</span>
|
|
8
|
+
</v-card-title>
|
|
9
|
+
|
|
10
|
+
<v-card-text class="pa-0">
|
|
11
|
+
<v-table class="border-thin">
|
|
12
|
+
<thead>
|
|
13
|
+
<tr>
|
|
14
|
+
<th>Time</th>
|
|
15
|
+
<th>Start</th>
|
|
16
|
+
<th>End</th>
|
|
17
|
+
<th>Total travel time</th>
|
|
18
|
+
</tr>
|
|
19
|
+
</thead>
|
|
20
|
+
<tbody>
|
|
21
|
+
<tr>
|
|
22
|
+
<td class="font-weight-medium">Schedule</td>
|
|
23
|
+
<td>
|
|
24
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
25
|
+
{{ summary.schedule.start }}
|
|
26
|
+
</td>
|
|
27
|
+
<td>
|
|
28
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
29
|
+
{{ summary.schedule.end }}
|
|
30
|
+
</td>
|
|
31
|
+
<td>
|
|
32
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
33
|
+
{{ summary.schedule.totalTime }}
|
|
34
|
+
</td>
|
|
35
|
+
</tr>
|
|
36
|
+
<tr>
|
|
37
|
+
<td class="font-weight-medium">Actual</td>
|
|
38
|
+
<td>
|
|
39
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
40
|
+
{{ summary.actual.start }}
|
|
41
|
+
</td>
|
|
42
|
+
<td>
|
|
43
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
44
|
+
{{ summary.actual.end }}
|
|
45
|
+
</td>
|
|
46
|
+
<td>
|
|
47
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
48
|
+
{{ summary.actual.totalTime }}
|
|
49
|
+
</td>
|
|
50
|
+
</tr>
|
|
51
|
+
</tbody>
|
|
52
|
+
</v-table>
|
|
53
|
+
</v-card-text>
|
|
54
|
+
</v-card>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script setup lang="ts">
|
|
58
|
+
import type { NFCPatrolRouteSummary } from "../../../types/nfc-patrol-report";
|
|
59
|
+
|
|
60
|
+
defineProps<{
|
|
61
|
+
summary: NFCPatrolRouteSummary;
|
|
62
|
+
}>();
|
|
63
|
+
</script>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-dialog :model-value="modelValue" max-width="600" persistent>
|
|
3
|
+
<v-card rounded="xl">
|
|
4
|
+
<v-card-title class="d-flex align-center pa-6">
|
|
5
|
+
<span class="text-h6">Skip Checkpoint Remarks</span>
|
|
6
|
+
<v-spacer />
|
|
7
|
+
<v-btn icon="mdi-close" variant="text" size="small" @click="close" />
|
|
8
|
+
</v-card-title>
|
|
9
|
+
|
|
10
|
+
<v-card-text class="px-6 pb-6">
|
|
11
|
+
<v-textarea
|
|
12
|
+
:model-value="remarks"
|
|
13
|
+
variant="outlined"
|
|
14
|
+
rounded="lg"
|
|
15
|
+
readonly
|
|
16
|
+
rows="4"
|
|
17
|
+
hide-details
|
|
18
|
+
/>
|
|
19
|
+
</v-card-text>
|
|
20
|
+
</v-card>
|
|
21
|
+
</v-dialog>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
defineProps<{
|
|
26
|
+
modelValue: boolean;
|
|
27
|
+
remarks: string;
|
|
28
|
+
}>();
|
|
29
|
+
|
|
30
|
+
const emit = defineEmits<{
|
|
31
|
+
"update:modelValue": [value: boolean];
|
|
32
|
+
}>();
|
|
33
|
+
|
|
34
|
+
function close() {
|
|
35
|
+
emit("update:modelValue", false);
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="text-center pa-12" variant="outlined" rounded="lg">
|
|
3
|
+
<v-icon size="64" color="grey-lighten-1" class="mb-4">
|
|
4
|
+
mdi-clipboard-text-outline
|
|
5
|
+
</v-icon>
|
|
6
|
+
<h3 class="text-h6 mb-2">{{ title }}</h3>
|
|
7
|
+
<p class="text-body-2 text-medium-emphasis">
|
|
8
|
+
{{ message }}
|
|
9
|
+
</p>
|
|
10
|
+
</v-card>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
withDefaults(
|
|
15
|
+
defineProps<{
|
|
16
|
+
title?: string;
|
|
17
|
+
message?: string;
|
|
18
|
+
}>(),
|
|
19
|
+
{
|
|
20
|
+
title: "No Patrol Report Selected",
|
|
21
|
+
message: "Please select a route, time range, and date to view the patrol report",
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
</script>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row no-gutters class="mb-6">
|
|
3
|
+
<v-col cols="12" md="3" class="pr-md-2 mb-2 mb-md-0">
|
|
4
|
+
<v-select
|
|
5
|
+
:model-value="modelValue.route"
|
|
6
|
+
:items="options.routes"
|
|
7
|
+
label="Select Route"
|
|
8
|
+
variant="outlined"
|
|
9
|
+
density="comfortable"
|
|
10
|
+
rounded="lg"
|
|
11
|
+
hide-details
|
|
12
|
+
bg-color="white"
|
|
13
|
+
@update:model-value="updateFilter('route', $event)"
|
|
14
|
+
/>
|
|
15
|
+
</v-col>
|
|
16
|
+
|
|
17
|
+
<v-col cols="12" md="2" class="px-md-2 mb-2 mb-md-0">
|
|
18
|
+
<v-select
|
|
19
|
+
:model-value="modelValue.timeRange"
|
|
20
|
+
:items="options.timeRanges"
|
|
21
|
+
label="Time Range"
|
|
22
|
+
variant="outlined"
|
|
23
|
+
density="comfortable"
|
|
24
|
+
rounded="lg"
|
|
25
|
+
hide-details
|
|
26
|
+
bg-color="white"
|
|
27
|
+
@update:model-value="updateFilter('timeRange', $event)"
|
|
28
|
+
/>
|
|
29
|
+
</v-col>
|
|
30
|
+
|
|
31
|
+
<v-col cols="12" md="2" class="px-md-2 mb-2 mb-md-0">
|
|
32
|
+
<v-text-field
|
|
33
|
+
:model-value="modelValue.date"
|
|
34
|
+
type="date"
|
|
35
|
+
variant="outlined"
|
|
36
|
+
density="comfortable"
|
|
37
|
+
rounded="lg"
|
|
38
|
+
hide-details
|
|
39
|
+
bg-color="white"
|
|
40
|
+
@update:model-value="updateFilter('date', $event)"
|
|
41
|
+
/>
|
|
42
|
+
</v-col>
|
|
43
|
+
|
|
44
|
+
<v-spacer />
|
|
45
|
+
|
|
46
|
+
<v-col cols="12" md="2" class="pl-md-2">
|
|
47
|
+
<v-btn
|
|
48
|
+
variant="outlined"
|
|
49
|
+
block
|
|
50
|
+
size="large"
|
|
51
|
+
rounded="lg"
|
|
52
|
+
class="text-none"
|
|
53
|
+
prepend-icon="mdi-export"
|
|
54
|
+
@click="$emit('export')"
|
|
55
|
+
>
|
|
56
|
+
Export
|
|
57
|
+
</v-btn>
|
|
58
|
+
</v-col>
|
|
59
|
+
</v-row>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<script setup lang="ts">
|
|
63
|
+
import type {
|
|
64
|
+
NFCPatrolReportFilterOptions,
|
|
65
|
+
NFCPatrolReportFilters,
|
|
66
|
+
} from "../../../types/nfc-patrol-report";
|
|
67
|
+
|
|
68
|
+
const props = defineProps<{
|
|
69
|
+
modelValue: NFCPatrolReportFilters;
|
|
70
|
+
options: NFCPatrolReportFilterOptions;
|
|
71
|
+
}>();
|
|
72
|
+
|
|
73
|
+
const emit = defineEmits<{
|
|
74
|
+
"update:modelValue": [value: NFCPatrolReportFilters];
|
|
75
|
+
export: [];
|
|
76
|
+
}>();
|
|
77
|
+
|
|
78
|
+
function updateFilter(key: keyof NFCPatrolReportFilters, value: unknown) {
|
|
79
|
+
if (typeof value !== "string") {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
emit("update:modelValue", {
|
|
84
|
+
...props.modelValue,
|
|
85
|
+
[key]: value,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="text-center pa-8 mt-2" variant="flat" rounded="lg">
|
|
3
|
+
<v-icon size="64" class="mb-4">mdi-office-building</v-icon>
|
|
4
|
+
<h2 class="text-h5 mb-2">{{ location.name }}</h2>
|
|
5
|
+
<p class="text-body-2 text-medium-emphasis">
|
|
6
|
+
{{ location.address }}
|
|
7
|
+
</p>
|
|
8
|
+
</v-card>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import type { NFCPatrolReportLocation } from "../../../types/nfc-patrol-report";
|
|
13
|
+
|
|
14
|
+
defineProps<{
|
|
15
|
+
location: NFCPatrolReportLocation;
|
|
16
|
+
}>();
|
|
17
|
+
</script>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="mb-6" variant="flat" rounded="lg">
|
|
3
|
+
<v-card-title>{{ title }}</v-card-title>
|
|
4
|
+
|
|
5
|
+
<v-card-text>
|
|
6
|
+
<v-table class="border-thin">
|
|
7
|
+
<thead>
|
|
8
|
+
<tr>
|
|
9
|
+
<th>Patrol Route Name</th>
|
|
10
|
+
<th>Security Check Schedule</th>
|
|
11
|
+
<th>Checked</th>
|
|
12
|
+
<th>Missed</th>
|
|
13
|
+
<th>Status</th>
|
|
14
|
+
</tr>
|
|
15
|
+
</thead>
|
|
16
|
+
<tbody>
|
|
17
|
+
<tr v-for="row in rows" :key="row.id">
|
|
18
|
+
<td>{{ row.routeName }}</td>
|
|
19
|
+
<td>
|
|
20
|
+
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
21
|
+
{{ row.securityCheckSchedule }}
|
|
22
|
+
</td>
|
|
23
|
+
<td>{{ row.checked }}</td>
|
|
24
|
+
<td>{{ row.missed }}</td>
|
|
25
|
+
<td>
|
|
26
|
+
<span :class="statusClass(row.status)">
|
|
27
|
+
{{ row.status }}
|
|
28
|
+
</span>
|
|
29
|
+
</td>
|
|
30
|
+
</tr>
|
|
31
|
+
</tbody>
|
|
32
|
+
</v-table>
|
|
33
|
+
</v-card-text>
|
|
34
|
+
</v-card>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import type {
|
|
39
|
+
NFCPatrolReportSummaryRow,
|
|
40
|
+
NFCPatrolReportSummaryStatus,
|
|
41
|
+
} from "../../../types/nfc-patrol-report";
|
|
42
|
+
|
|
43
|
+
defineProps<{
|
|
44
|
+
title: string;
|
|
45
|
+
rows: NFCPatrolReportSummaryRow[];
|
|
46
|
+
}>();
|
|
47
|
+
|
|
48
|
+
function statusClass(status: NFCPatrolReportSummaryStatus) {
|
|
49
|
+
return status === "Completed" ? "text-success" : "text-error";
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
@@ -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) {
|