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