@7365admin1/layer-common 3.0.22 → 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 +6 -0
- package/components/DashboardMain.vue +981 -346
- package/components/Nfc/NFCPatrolReportMain.vue +48 -519
- 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/ServiceProviderMain.vue +182 -184
- package/components/VisitorManagement.vue +8 -0
- package/components/VisitorSocketPopUp.vue +10 -30
- 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/package.json +1 -1
- package/services/nfcPatrolReport.ts +172 -0
- package/types/nfc-patrol-report.ts +73 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters class="border-thin pa-6 rounded-lg">
|
|
3
3
|
<v-col cols="12">
|
|
4
|
-
<!-- Header -->
|
|
5
4
|
<v-row no-gutters class="mb-4" align="center">
|
|
6
5
|
<v-col cols="12" md="6">
|
|
7
6
|
<v-btn
|
|
@@ -13,390 +12,59 @@
|
|
|
13
12
|
Patrol Report Log
|
|
14
13
|
</v-btn>
|
|
15
14
|
</v-col>
|
|
16
|
-
|
|
17
15
|
</v-row>
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<v-tab value="patrol" class="text-none border-thin" >Patrol Report</v-tab>
|
|
17
|
+
<v-tabs v-model="activeTab" class="mb-6" bg-color="transparent" grow>
|
|
18
|
+
<v-tab value="patrol" class="text-none border-thin">Patrol Report</v-tab>
|
|
22
19
|
<v-tab value="daily" class="text-none border-thin">Daily Report</v-tab>
|
|
23
20
|
<v-tab value="monthly" class="text-none border-thin">Monthly Report</v-tab>
|
|
24
21
|
</v-tabs>
|
|
25
22
|
|
|
26
23
|
<v-window v-model="activeTab" class="pt-4">
|
|
27
|
-
<!-- Patrol Report Tab -->
|
|
28
24
|
<v-window-item value="patrol">
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
variant="outlined"
|
|
37
|
-
density="comfortable"
|
|
38
|
-
rounded="lg"
|
|
39
|
-
hide-details
|
|
40
|
-
bg-color="white"
|
|
41
|
-
/>
|
|
42
|
-
</v-col>
|
|
43
|
-
<v-col cols="12" md="2" class="px-md-2 mb-2 mb-md-0">
|
|
44
|
-
<v-select
|
|
45
|
-
v-model="selectedTimeRange"
|
|
46
|
-
:items="timeRanges"
|
|
47
|
-
label="Time Range"
|
|
48
|
-
variant="outlined"
|
|
49
|
-
density="comfortable"
|
|
50
|
-
rounded="lg"
|
|
51
|
-
hide-details
|
|
52
|
-
bg-color="white"
|
|
53
|
-
/>
|
|
54
|
-
</v-col>
|
|
55
|
-
<v-col cols="12" md="2" class="px-md-2 mb-2 mb-md-0">
|
|
56
|
-
<v-text-field
|
|
57
|
-
v-model="selectedDate"
|
|
58
|
-
type="date"
|
|
59
|
-
variant="outlined"
|
|
60
|
-
density="comfortable"
|
|
61
|
-
rounded="lg"
|
|
62
|
-
hide-details
|
|
63
|
-
bg-color="white"
|
|
64
|
-
/>
|
|
65
|
-
</v-col>
|
|
66
|
-
|
|
67
|
-
<v-col cols="12" md="2" class="pl-md-2">
|
|
68
|
-
<v-btn
|
|
69
|
-
variant="outlined"
|
|
70
|
-
block
|
|
71
|
-
size="large"
|
|
72
|
-
rounded="lg"
|
|
73
|
-
class="text-none"
|
|
74
|
-
prepend-icon="mdi-export"
|
|
75
|
-
@click="exportReport"
|
|
76
|
-
>
|
|
77
|
-
Export
|
|
78
|
-
</v-btn>
|
|
79
|
-
</v-col>
|
|
80
|
-
</v-row>
|
|
81
|
-
|
|
82
|
-
<v-divider :thickness="1" class="border-opacity-100"></v-divider>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
<!-- Location Card -->
|
|
86
|
-
<v-card
|
|
87
|
-
v-if="locationData"
|
|
88
|
-
class=" text-center pa-8 mt-2"
|
|
89
|
-
variant="flat"
|
|
90
|
-
rounded="lg"
|
|
91
|
-
>
|
|
92
|
-
<v-icon size="64" class="mb-4">mdi-office-building</v-icon>
|
|
93
|
-
<h2 class="text-h5 mb-2">{{ locationData.name }}</h2>
|
|
94
|
-
<p class="text-body-2 text-medium-emphasis">
|
|
95
|
-
{{ locationData.address }}
|
|
96
|
-
</p>
|
|
97
|
-
</v-card>
|
|
98
|
-
<v-divider :thickness="1" class="border-opacity-100 mb-6"></v-divider>
|
|
99
|
-
<!-- Route Summary -->
|
|
100
|
-
<v-card v-if="routeSummary" class="mb-6" variant="flat" rounded="lg">
|
|
101
|
-
<v-card-title class="d-flex justify-space-between align-center">
|
|
102
|
-
<span>Patrol Route Summary: {{ routeSummary.routeName }}</span>
|
|
103
|
-
<span class="text-body-2 text-medium-emphasis">
|
|
104
|
-
Tolerance: {{ routeSummary.tolerance }} mins
|
|
105
|
-
</span>
|
|
106
|
-
</v-card-title>
|
|
107
|
-
<v-card-text class="pa-0">
|
|
108
|
-
<v-table class=" border-thin">
|
|
109
|
-
<thead>
|
|
110
|
-
<tr>
|
|
111
|
-
<th>Time</th>
|
|
112
|
-
<th>Start</th>
|
|
113
|
-
<th>End</th>
|
|
114
|
-
<th>Total travel time</th>
|
|
115
|
-
</tr>
|
|
116
|
-
</thead>
|
|
117
|
-
<tbody>
|
|
118
|
-
<tr>
|
|
119
|
-
<td class="font-weight-medium">Schedule</td>
|
|
120
|
-
<td>
|
|
121
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
122
|
-
{{ routeSummary.schedule.start }}
|
|
123
|
-
</td>
|
|
124
|
-
<td>
|
|
125
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
126
|
-
{{ routeSummary.schedule.end }}
|
|
127
|
-
</td>
|
|
128
|
-
<td>
|
|
129
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
130
|
-
{{ routeSummary.schedule.totalTime }}
|
|
131
|
-
</td>
|
|
132
|
-
</tr>
|
|
133
|
-
<tr>
|
|
134
|
-
<td class="font-weight-medium">Actual</td>
|
|
135
|
-
<td>
|
|
136
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
137
|
-
{{ routeSummary.actual.start }}
|
|
138
|
-
</td>
|
|
139
|
-
<td>
|
|
140
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
141
|
-
{{ routeSummary.actual.end }}
|
|
142
|
-
</td>
|
|
143
|
-
<td>
|
|
144
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
145
|
-
{{ routeSummary.actual.totalTime }}
|
|
146
|
-
</td>
|
|
147
|
-
</tr>
|
|
148
|
-
</tbody>
|
|
149
|
-
</v-table>
|
|
150
|
-
</v-card-text>
|
|
151
|
-
</v-card>
|
|
152
|
-
|
|
153
|
-
<!-- Patrol Activity -->
|
|
154
|
-
<v-card
|
|
155
|
-
v-if="patrolActivity.length"
|
|
156
|
-
variant="flat"
|
|
157
|
-
rounded="lg"
|
|
158
|
-
>
|
|
159
|
-
<v-card-title>Patrol Activity</v-card-title>
|
|
160
|
-
<v-card-text class="pa-0">
|
|
161
|
-
<v-table class="border-thin">
|
|
162
|
-
<thead>
|
|
163
|
-
<tr>
|
|
164
|
-
<th>Checkpoints</th>
|
|
165
|
-
<th>Start - End Time</th>
|
|
166
|
-
<th>
|
|
167
|
-
Travel time<br />
|
|
168
|
-
<span class="text-caption text-medium-emphasis">Actual/Expected</span>
|
|
169
|
-
</th>
|
|
170
|
-
<th>Status</th>
|
|
171
|
-
</tr>
|
|
172
|
-
</thead>
|
|
173
|
-
<tbody>
|
|
174
|
-
<tr
|
|
175
|
-
v-for="activity in patrolActivity"
|
|
176
|
-
:key="activity.id"
|
|
177
|
-
:class="{ 'cursor-pointer': activity.status === 'Skipped' }"
|
|
178
|
-
@click="activity.status === 'Skipped' && openRemarksDialog(activity)"
|
|
179
|
-
>
|
|
180
|
-
<td>{{ activity.checkpoint }}</td>
|
|
181
|
-
<td>
|
|
182
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
183
|
-
{{ activity.startTime }}
|
|
184
|
-
<v-icon size="small" class="mx-2">mdi-arrow-right</v-icon>
|
|
185
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
186
|
-
{{ activity.endTime }}
|
|
187
|
-
</td>
|
|
188
|
-
<td>
|
|
189
|
-
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
190
|
-
{{ activity.actualTime }} / {{ activity.expectedTime }}
|
|
191
|
-
</td>
|
|
192
|
-
<td>
|
|
193
|
-
<v-chip
|
|
194
|
-
:color="getStatusColor(activity.status)"
|
|
195
|
-
size="small"
|
|
196
|
-
variant="flat"
|
|
197
|
-
>
|
|
198
|
-
{{ activity.status }}
|
|
199
|
-
<v-icon
|
|
200
|
-
v-if="activity.status === 'Skipped'"
|
|
201
|
-
size="small"
|
|
202
|
-
class="ml-1"
|
|
203
|
-
>
|
|
204
|
-
mdi-flag
|
|
205
|
-
</v-icon>
|
|
206
|
-
</v-chip>
|
|
207
|
-
</td>
|
|
208
|
-
</tr>
|
|
209
|
-
</tbody>
|
|
210
|
-
</v-table>
|
|
211
|
-
</v-card-text>
|
|
212
|
-
</v-card>
|
|
213
|
-
|
|
214
|
-
<!-- Empty State -->
|
|
215
|
-
<v-card
|
|
216
|
-
v-if="!routeSummary && !loading"
|
|
217
|
-
class="text-center pa-12"
|
|
218
|
-
variant="outlined"
|
|
219
|
-
rounded="lg"
|
|
220
|
-
>
|
|
221
|
-
<v-icon size="64" color="grey-lighten-1" class="mb-4">
|
|
222
|
-
mdi-clipboard-text-outline
|
|
223
|
-
</v-icon>
|
|
224
|
-
<h3 class="text-h6 mb-2">No Patrol Report Selected</h3>
|
|
225
|
-
<p class="text-body-2 text-medium-emphasis">
|
|
226
|
-
Please select a route, time range, and date to view the patrol
|
|
227
|
-
report
|
|
228
|
-
</p>
|
|
229
|
-
</v-card>
|
|
25
|
+
<PatrolReportTab
|
|
26
|
+
:active="activeTab === 'patrol'"
|
|
27
|
+
:filters="filters"
|
|
28
|
+
:options="options"
|
|
29
|
+
@update:filters="updateFilters"
|
|
30
|
+
@export="exportReport"
|
|
31
|
+
/>
|
|
230
32
|
</v-window-item>
|
|
231
33
|
|
|
232
|
-
<!-- Daily Report Tab -->
|
|
233
34
|
<v-window-item value="daily">
|
|
234
|
-
<
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
35
|
+
<DailyReportTab
|
|
36
|
+
:active="activeTab === 'daily'"
|
|
37
|
+
:filters="filters"
|
|
38
|
+
:options="options"
|
|
39
|
+
@update:filters="updateFilters"
|
|
40
|
+
@export="exportReport"
|
|
41
|
+
/>
|
|
241
42
|
</v-window-item>
|
|
242
43
|
|
|
243
|
-
<!-- Monthly Report Tab -->
|
|
244
44
|
<v-window-item value="monthly">
|
|
245
|
-
<
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
45
|
+
<MonthlyReportTab
|
|
46
|
+
:active="activeTab === 'monthly'"
|
|
47
|
+
:filters="filters"
|
|
48
|
+
:options="options"
|
|
49
|
+
@update:filters="updateFilters"
|
|
50
|
+
@export="exportReport"
|
|
51
|
+
/>
|
|
252
52
|
</v-window-item>
|
|
253
53
|
</v-window>
|
|
254
54
|
</v-col>
|
|
255
|
-
|
|
256
|
-
<!-- Skip Checkpoint Remarks Dialog -->
|
|
257
|
-
<v-dialog v-model="dialogRemarks" max-width="600" persistent>
|
|
258
|
-
<v-card rounded="xl">
|
|
259
|
-
<v-card-title class="d-flex align-center pa-6">
|
|
260
|
-
<span class="text-h6">Skip Checkpoint Remarks</span>
|
|
261
|
-
<v-spacer />
|
|
262
|
-
<v-btn
|
|
263
|
-
icon="mdi-close"
|
|
264
|
-
variant="text"
|
|
265
|
-
size="small"
|
|
266
|
-
@click="dialogRemarks = false"
|
|
267
|
-
/>
|
|
268
|
-
</v-card-title>
|
|
269
|
-
|
|
270
|
-
<v-card-text class="px-6 pb-6">
|
|
271
|
-
<v-textarea
|
|
272
|
-
:model-value="selectedActivityRemarks"
|
|
273
|
-
variant="outlined"
|
|
274
|
-
rounded="lg"
|
|
275
|
-
readonly
|
|
276
|
-
rows="4"
|
|
277
|
-
hide-details
|
|
278
|
-
/>
|
|
279
|
-
</v-card-text>
|
|
280
|
-
</v-card>
|
|
281
|
-
</v-dialog>
|
|
282
|
-
|
|
283
|
-
<!-- Select Routes & Schedule Dialog -->
|
|
284
|
-
<v-dialog v-model="dialogSelectRoute" max-width="680" persistent>
|
|
285
|
-
<v-card rounded="xl">
|
|
286
|
-
<v-card-title class="pa-6">
|
|
287
|
-
<v-btn
|
|
288
|
-
icon="mdi-chevron-left"
|
|
289
|
-
variant="text"
|
|
290
|
-
size="small"
|
|
291
|
-
@click="dialogSelectRoute = false"
|
|
292
|
-
/>
|
|
293
|
-
<span class="text-h6 ml-2">Select Routes & Schedule </span>
|
|
294
|
-
</v-card-title>
|
|
295
|
-
|
|
296
|
-
<v-card-text class="px-6 pb-6">
|
|
297
|
-
<!-- Route Selector -->
|
|
298
|
-
<v-select
|
|
299
|
-
v-model="dialogRoute"
|
|
300
|
-
:items="routes"
|
|
301
|
-
variant="outlined"
|
|
302
|
-
rounded="lg"
|
|
303
|
-
density="comfortable"
|
|
304
|
-
hide-details
|
|
305
|
-
class="mb-4"
|
|
306
|
-
/>
|
|
307
|
-
|
|
308
|
-
<!-- Date and Time Row -->
|
|
309
|
-
<v-row no-gutters>
|
|
310
|
-
<v-col cols="12" md="6" class="pr-md-2">
|
|
311
|
-
<v-text-field
|
|
312
|
-
v-model="dialogDate"
|
|
313
|
-
type="date"
|
|
314
|
-
variant="outlined"
|
|
315
|
-
rounded="lg"
|
|
316
|
-
density="comfortable"
|
|
317
|
-
hide-details
|
|
318
|
-
prepend-inner-icon="mdi-calendar"
|
|
319
|
-
/>
|
|
320
|
-
</v-col>
|
|
321
|
-
<v-col cols="12" md="6" class="pl-md-2 mt-2 mt-md-0">
|
|
322
|
-
<v-select
|
|
323
|
-
v-model="dialogTime"
|
|
324
|
-
:items="timeRanges"
|
|
325
|
-
variant="outlined"
|
|
326
|
-
rounded="lg"
|
|
327
|
-
density="comfortable"
|
|
328
|
-
hide-details
|
|
329
|
-
prepend-inner-icon="mdi-clock-outline"
|
|
330
|
-
/>
|
|
331
|
-
</v-col>
|
|
332
|
-
</v-row>
|
|
333
|
-
</v-card-text>
|
|
334
|
-
|
|
335
|
-
<!-- Action Buttons -->
|
|
336
|
-
<v-card-actions class="px-6 pb-6">
|
|
337
|
-
<v-row no-gutters>
|
|
338
|
-
<v-col cols="6" class="pr-2">
|
|
339
|
-
<v-btn
|
|
340
|
-
block
|
|
341
|
-
size="x-large"
|
|
342
|
-
variant="outlined"
|
|
343
|
-
rounded="lg"
|
|
344
|
-
class="text-none"
|
|
345
|
-
@click="dialogSelectRoute = false"
|
|
346
|
-
>
|
|
347
|
-
Cancel
|
|
348
|
-
</v-btn>
|
|
349
|
-
</v-col>
|
|
350
|
-
<v-col cols="6" class="pl-2">
|
|
351
|
-
<v-btn
|
|
352
|
-
block
|
|
353
|
-
size="x-large"
|
|
354
|
-
color="primary"
|
|
355
|
-
rounded="lg"
|
|
356
|
-
class="text-none"
|
|
357
|
-
@click="submitSelection"
|
|
358
|
-
>
|
|
359
|
-
Submit
|
|
360
|
-
</v-btn>
|
|
361
|
-
</v-col>
|
|
362
|
-
</v-row>
|
|
363
|
-
</v-card-actions>
|
|
364
|
-
</v-card>
|
|
365
|
-
</v-dialog>
|
|
366
55
|
</v-row>
|
|
367
56
|
</template>
|
|
368
57
|
|
|
369
58
|
<script setup lang="ts">
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
interface RouteSummary {
|
|
382
|
-
routeName: string;
|
|
383
|
-
tolerance: number;
|
|
384
|
-
schedule: {
|
|
385
|
-
start: string;
|
|
386
|
-
end: string;
|
|
387
|
-
totalTime: string;
|
|
388
|
-
};
|
|
389
|
-
actual: {
|
|
390
|
-
start: string;
|
|
391
|
-
end: string;
|
|
392
|
-
totalTime: string;
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
interface LocationData {
|
|
397
|
-
name: string;
|
|
398
|
-
address: string;
|
|
399
|
-
}
|
|
59
|
+
import type {
|
|
60
|
+
NFCPatrolReportFilters,
|
|
61
|
+
NFCPatrolReportTab,
|
|
62
|
+
} from "../../types/nfc-patrol-report";
|
|
63
|
+
import { useNFCPatrolReportExport } from "../../composables/useNFCPatrolReportExport";
|
|
64
|
+
import { useNFCPatrolReportFilters } from "../../composables/useNFCPatrolReportFilters";
|
|
65
|
+
import DailyReportTab from "./PatrolReport/DailyReportTab.vue";
|
|
66
|
+
import MonthlyReportTab from "./PatrolReport/MonthlyReportTab.vue";
|
|
67
|
+
import PatrolReportTab from "./PatrolReport/PatrolReportTab.vue";
|
|
400
68
|
|
|
401
69
|
const props = defineProps<{
|
|
402
70
|
site: string;
|
|
@@ -404,166 +72,27 @@ const props = defineProps<{
|
|
|
404
72
|
}>();
|
|
405
73
|
|
|
406
74
|
const router = useRouter();
|
|
75
|
+
const activeTab = ref<NFCPatrolReportTab>("patrol");
|
|
76
|
+
const { filters, options, fetchOptions } = useNFCPatrolReportFilters(props.site);
|
|
77
|
+
const { exportReport: exportActiveReport } = useNFCPatrolReportExport();
|
|
407
78
|
|
|
408
|
-
|
|
409
|
-
const activeTab = ref("patrol");
|
|
410
|
-
const selectedRoute = ref("WH01 Patrol Route");
|
|
411
|
-
const selectedTimeRange = ref("15:00 - 17:00");
|
|
412
|
-
const selectedDate = ref("2025-11-27");
|
|
413
|
-
const loading = ref(false);
|
|
414
|
-
|
|
415
|
-
// Dialog state
|
|
416
|
-
const dialogSelectRoute = ref(false);
|
|
417
|
-
const dialogRoute = ref("WH01 Patrol Route");
|
|
418
|
-
const dialogDate = ref("2025-11-27");
|
|
419
|
-
const dialogTime = ref("15:00 - 17:00");
|
|
420
|
-
|
|
421
|
-
// Remarks dialog state
|
|
422
|
-
const dialogRemarks = ref(false);
|
|
423
|
-
const selectedActivityRemarks = ref("");
|
|
424
|
-
|
|
425
|
-
// Mock data - replace with actual API calls
|
|
426
|
-
const routes = ref([
|
|
427
|
-
"WH01 Patrol Route",
|
|
428
|
-
"WH02 Patrol Route",
|
|
429
|
-
"WH03 Patrol Route",
|
|
430
|
-
]);
|
|
431
|
-
|
|
432
|
-
const timeRanges = ref([
|
|
433
|
-
"15:00 - 17:00",
|
|
434
|
-
"08:00 - 10:00",
|
|
435
|
-
"13:00 - 15:00",
|
|
436
|
-
"18:00 - 20:00",
|
|
437
|
-
]);
|
|
438
|
-
|
|
439
|
-
const locationData = ref<LocationData>({
|
|
440
|
-
name: "Winsland Homes",
|
|
441
|
-
address: "221A Upper Thomson Road THOMSON RIDGE ESTATE",
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
const routeSummary = ref<RouteSummary>({
|
|
445
|
-
routeName: "WH01 Patrol Route",
|
|
446
|
-
tolerance: 30,
|
|
447
|
-
schedule: {
|
|
448
|
-
start: "14:00",
|
|
449
|
-
end: "15:00",
|
|
450
|
-
totalTime: "25 mins",
|
|
451
|
-
},
|
|
452
|
-
actual: {
|
|
453
|
-
start: "14:01",
|
|
454
|
-
end: "14:16",
|
|
455
|
-
totalTime: "15 mins",
|
|
456
|
-
},
|
|
457
|
-
});
|
|
79
|
+
onMounted(fetchOptions);
|
|
458
80
|
|
|
459
|
-
|
|
460
|
-
{
|
|
461
|
-
id: "1",
|
|
462
|
-
checkpoint: "Point 1 WH01",
|
|
463
|
-
startTime: "14:01",
|
|
464
|
-
endTime: "14:04",
|
|
465
|
-
actualTime: "3:00 mins",
|
|
466
|
-
expectedTime: "5:00 mins",
|
|
467
|
-
status: "Skipped",
|
|
468
|
-
remarks: "Raining",
|
|
469
|
-
},
|
|
470
|
-
{
|
|
471
|
-
id: "2",
|
|
472
|
-
checkpoint: "Point 2 WH02",
|
|
473
|
-
startTime: "14:04",
|
|
474
|
-
endTime: "14:07",
|
|
475
|
-
actualTime: "3:00 mins",
|
|
476
|
-
expectedTime: "5:00 mins",
|
|
477
|
-
status: "Completed",
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
id: "3",
|
|
481
|
-
checkpoint: "Point 3 WH03",
|
|
482
|
-
startTime: "14:07",
|
|
483
|
-
endTime: "15:10",
|
|
484
|
-
actualTime: "3:00 mins",
|
|
485
|
-
expectedTime: "5:00 mins",
|
|
486
|
-
status: "Completed",
|
|
487
|
-
},
|
|
488
|
-
{
|
|
489
|
-
id: "4",
|
|
490
|
-
checkpoint: "Point 4 WH04",
|
|
491
|
-
startTime: "14:10",
|
|
492
|
-
endTime: "14:13",
|
|
493
|
-
actualTime: "3:00 mins",
|
|
494
|
-
expectedTime: "5:00 mins",
|
|
495
|
-
status: "Completed",
|
|
496
|
-
},
|
|
497
|
-
{
|
|
498
|
-
id: "5",
|
|
499
|
-
checkpoint: "Point 5 WH05",
|
|
500
|
-
startTime: "14:13",
|
|
501
|
-
endTime: "14:16",
|
|
502
|
-
actualTime: "3:00 mins",
|
|
503
|
-
expectedTime: "5:00 mins",
|
|
504
|
-
status: "Completed",
|
|
505
|
-
},
|
|
506
|
-
]);
|
|
507
|
-
|
|
508
|
-
// Methods
|
|
509
|
-
const goBack = () => {
|
|
81
|
+
function goBack() {
|
|
510
82
|
router.back();
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
const loadReport = () => {
|
|
514
|
-
// Open the dialog
|
|
515
|
-
dialogRoute.value = selectedRoute.value;
|
|
516
|
-
dialogDate.value = selectedDate.value;
|
|
517
|
-
dialogTime.value = selectedTimeRange.value;
|
|
518
|
-
dialogSelectRoute.value = true;
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
const submitSelection = () => {
|
|
522
|
-
// Update the main filters with dialog values
|
|
523
|
-
selectedRoute.value = dialogRoute.value;
|
|
524
|
-
selectedDate.value = dialogDate.value;
|
|
525
|
-
selectedTimeRange.value = dialogTime.value;
|
|
526
|
-
|
|
527
|
-
// Close the dialog
|
|
528
|
-
dialogSelectRoute.value = false;
|
|
529
|
-
|
|
530
|
-
// TODO: Implement report loading based on filters
|
|
531
|
-
console.log("Loading report...", {
|
|
532
|
-
route: selectedRoute.value,
|
|
533
|
-
timeRange: selectedTimeRange.value,
|
|
534
|
-
date: selectedDate.value,
|
|
535
|
-
});
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
const exportReport = () => {
|
|
539
|
-
// TODO: Implement export functionality
|
|
540
|
-
console.log("Exporting report...");
|
|
541
|
-
};
|
|
542
|
-
|
|
543
|
-
const getStatusColor = (status: string) => {
|
|
544
|
-
return status === "Completed" ? "success" : "error";
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
const openRemarksDialog = (activity: PatrolActivity) => {
|
|
548
|
-
selectedActivityRemarks.value = activity.remarks || "No remarks provided";
|
|
549
|
-
dialogRemarks.value = true;
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
// TODO: Add API integration
|
|
553
|
-
// TODO: Add data fetching based on filters
|
|
554
|
-
// TODO: Implement Daily and Monthly reports
|
|
555
|
-
</script>
|
|
556
|
-
|
|
557
|
-
<style scoped>
|
|
558
|
-
.cursor-pointer {
|
|
559
|
-
cursor: pointer;
|
|
560
83
|
}
|
|
561
84
|
|
|
562
|
-
|
|
563
|
-
|
|
85
|
+
function updateFilters(value: NFCPatrolReportFilters) {
|
|
86
|
+
filters.route = value.route;
|
|
87
|
+
filters.timeRange = value.timeRange;
|
|
88
|
+
filters.date = value.date;
|
|
564
89
|
}
|
|
565
90
|
|
|
566
|
-
|
|
567
|
-
|
|
91
|
+
async function exportReport() {
|
|
92
|
+
await exportActiveReport(activeTab.value, {
|
|
93
|
+
route: filters.route,
|
|
94
|
+
timeRange: filters.timeRange,
|
|
95
|
+
date: filters.date,
|
|
96
|
+
});
|
|
568
97
|
}
|
|
569
|
-
</
|
|
98
|
+
</script>
|
|
@@ -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 Daily Report Selected"
|
|
21
|
+
message="Please select a route, time range, and date to view the daily 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 { useNFCPatrolDailyReport } from "../../../composables/useNFCPatrolDailyReport";
|
|
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 } = useNFCPatrolDailyReport(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>
|