@7365admin1/layer-common 3.0.17 → 3.0.19
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/BuildingManagement/buildings.vue +7 -4
- package/components/BuildingManagement/units.vue +187 -9
- package/components/DashboardMain.vue +54 -36
- package/components/HidAccessLogDashboard.vue +184 -21
- package/components/HidReaderForm.vue +20 -12
- package/components/HidReaderManagement.vue +113 -80
- package/components/MemberInformation.vue +240 -163
- package/components/PassInformation.vue +6 -4
- package/components/VisitorForm.vue +41 -18
- package/composables/useBuilding.ts +14 -0
- package/composables/useVehicle.ts +1 -1
- package/composables/useVisitor.ts +19 -15
- package/package.json +1 -1
- package/pages/[org]/[site]/access-mgmt/access-logs/index.vue +3 -1
- package/types/visitor.d.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
<v-btn fab icon density="comfortable" @click="getBuildings()">
|
|
16
16
|
<v-icon>mdi-refresh</v-icon>
|
|
17
17
|
</v-btn>
|
|
18
|
-
<v-text-field v-model="searchInput" autocomplete="off" placeholder="Search" density="compact" hide-details
|
|
19
|
-
class="mx-2" style="width: 200px;" />
|
|
18
|
+
<v-text-field v-model="searchInput" autocomplete="off" placeholder="Search" density="compact" hide-details
|
|
19
|
+
dense clearable class="mx-2" style="width: 200px;" />
|
|
20
20
|
</template>
|
|
21
21
|
|
|
22
22
|
<template #append>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
<v-dialog v-model="createDialog" width="450" persistent>
|
|
46
46
|
<BuildingForm :site="site" mode="add" :blocks="items" @cancel="createDialog = false" @success="successCreate()"
|
|
47
47
|
@success:create-more="getBuildings()" />
|
|
48
|
-
</v-dialog>
|
|
48
|
+
</v-dialog>
|
|
49
49
|
|
|
50
50
|
<!-- Edit Dialog -->
|
|
51
51
|
<v-dialog v-model="editDialog" width="450" persistent>
|
|
@@ -222,7 +222,10 @@ const site = (useRoute().params.site as string) ?? "";
|
|
|
222
222
|
const status = (useRoute().params.status as string) ?? "active";
|
|
223
223
|
|
|
224
224
|
const { debounce } = useUtils();
|
|
225
|
-
const {
|
|
225
|
+
const {
|
|
226
|
+
getAll: _getBuildings,
|
|
227
|
+
deleteById,
|
|
228
|
+
} = useBuilding();
|
|
226
229
|
|
|
227
230
|
const searchInput = ref("");
|
|
228
231
|
const headerSearch = ref("");
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
v-if="canCreate && canCreateUnit">
|
|
7
7
|
Add Unit
|
|
8
8
|
</v-btn>
|
|
9
|
+
<v-btn class="text-none ml-2" rounded="pill" variant="tonal" @click="openBulkUploadFilePicker()" size="large"
|
|
10
|
+
:loading="bulkUploadLoading" v-if="canCreate && canCreateUnit">
|
|
11
|
+
<v-icon start>mdi-upload</v-icon>
|
|
12
|
+
Bulk Upload
|
|
13
|
+
</v-btn>
|
|
14
|
+
<input ref="bulkUploadFileInput" type="file" accept=".xlsx,.xls,.csv" class="d-none"
|
|
15
|
+
@change="handleBulkUploadFileChange" />
|
|
9
16
|
</v-row>
|
|
10
17
|
</v-col>
|
|
11
18
|
<v-col cols="12">
|
|
@@ -30,8 +37,7 @@
|
|
|
30
37
|
</v-toolbar>
|
|
31
38
|
|
|
32
39
|
<v-data-table :headers="props.headers" :items="items" item-value="_id" items-per-page="20" fixed-header
|
|
33
|
-
hide-default-footer
|
|
34
|
-
style="max-height: calc(100vh - (200px))">
|
|
40
|
+
hide-default-footer @click:row="tableRowClickHandler" style="max-height: calc(100vh - (200px))">
|
|
35
41
|
<template #item.block="{ value, item }">
|
|
36
42
|
{{ value ? `Block ${value}` : "" }}
|
|
37
43
|
</template>
|
|
@@ -44,10 +50,10 @@
|
|
|
44
50
|
<BuildingUnitFormAdd :site="site" @cancel="createDialog = false" @success="successCreate()"
|
|
45
51
|
@success:create-more="getBuildingUnit()" />
|
|
46
52
|
</v-dialog>
|
|
47
|
-
|
|
48
|
-
<v-dialog v-if="canViewUnitDetails"
|
|
49
|
-
|
|
50
|
-
:roomFacility="selectedBuildingUnit" />
|
|
53
|
+
|
|
54
|
+
<v-dialog v-if="canViewUnitDetails" v-model="dialogPreview" :width="smAndUp ? '70svw' : '100%'" persistent>
|
|
55
|
+
<BuildingUnitFormEdit v-if="selectedBuildingUnit" @cancel="dialogPreview = false" @success="successUpdate()"
|
|
56
|
+
@delete-unit="openDeleteDialog()" :roomFacility="selectedBuildingUnit" />
|
|
51
57
|
</v-dialog>
|
|
52
58
|
|
|
53
59
|
<v-dialog v-model="dialogDelete" :loading="deleteLoading" width="450" persistent>
|
|
@@ -88,6 +94,67 @@
|
|
|
88
94
|
</v-card>
|
|
89
95
|
</v-dialog>
|
|
90
96
|
|
|
97
|
+
<v-dialog v-model="bulkUploadSkippedDialog" :width="smAndUp ? '720' : '100%'" persistent>
|
|
98
|
+
<v-card width="100%">
|
|
99
|
+
<v-toolbar density="compact" class="pl-4">
|
|
100
|
+
<span class="font-weight-medium text-h5">Skipped Rows</span>
|
|
101
|
+
</v-toolbar>
|
|
102
|
+
|
|
103
|
+
<v-card-text style="max-height: 70vh; overflow-y: auto">
|
|
104
|
+
<p class="text-subtitle-2 mb-4">
|
|
105
|
+
Some rows were not imported. Review the details below and update the sheet if needed.
|
|
106
|
+
</p>
|
|
107
|
+
|
|
108
|
+
<v-alert v-if="bulkUploadSkippedSummary.length" type="warning" variant="tonal" density="compact"
|
|
109
|
+
class="mb-4">
|
|
110
|
+
{{ bulkUploadSkippedSummary.length }} row(s) skipped during bulk upload.
|
|
111
|
+
</v-alert>
|
|
112
|
+
|
|
113
|
+
<!-- <v-list v-if="bulkUploadSkippedSummary.length" density="compact" class="pa-0 mb-4">
|
|
114
|
+
<v-list-item v-for="(item, index) in bulkUploadSkippedSummary" :key="`summary-${index}`"
|
|
115
|
+
class="px-0">
|
|
116
|
+
<v-list-item-title class="text-subtitle-2">
|
|
117
|
+
Row {{ item.rowNumber ?? item.row ?? "N/A" }}
|
|
118
|
+
</v-list-item-title>
|
|
119
|
+
<v-list-item-subtitle class="text-wrap">
|
|
120
|
+
{{ formatSkippedSummaryReason(item) }}
|
|
121
|
+
</v-list-item-subtitle>
|
|
122
|
+
</v-list-item>
|
|
123
|
+
</v-list> -->
|
|
124
|
+
|
|
125
|
+
<template v-if="bulkUploadSkippedErrors.length">
|
|
126
|
+
<!-- <v-divider class="mb-4" /> -->
|
|
127
|
+
<p class="text-subtitle-2 mb-2">Details</p>
|
|
128
|
+
<v-table density="compact">
|
|
129
|
+
<thead>
|
|
130
|
+
<tr>
|
|
131
|
+
<th class="text-left">Row</th>
|
|
132
|
+
<th class="text-left">Block</th>
|
|
133
|
+
<th class="text-left">Building</th>
|
|
134
|
+
<th class="text-left">Reason</th>
|
|
135
|
+
</tr>
|
|
136
|
+
</thead>
|
|
137
|
+
<tbody>
|
|
138
|
+
<tr v-for="(item, index) in bulkUploadSkippedErrors" :key="`error-${index}`">
|
|
139
|
+
<td>{{ item.row ?? "N/A" }}</td>
|
|
140
|
+
<td>{{ item.block ?? "N/A" }}</td>
|
|
141
|
+
<td>{{ item.name ?? "N/A" }}</td>
|
|
142
|
+
<td>{{ item.reason ?? "Skipped" }}</td>
|
|
143
|
+
</tr>
|
|
144
|
+
</tbody>
|
|
145
|
+
</v-table>
|
|
146
|
+
</template>
|
|
147
|
+
</v-card-text>
|
|
148
|
+
|
|
149
|
+
<v-toolbar density="compact">
|
|
150
|
+
<v-btn tile block size="48" color="black" variant="flat" class="text-none"
|
|
151
|
+
@click="bulkUploadSkippedDialog = false">
|
|
152
|
+
Close
|
|
153
|
+
</v-btn>
|
|
154
|
+
</v-toolbar>
|
|
155
|
+
</v-card>
|
|
156
|
+
</v-dialog>
|
|
157
|
+
|
|
91
158
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
92
159
|
</v-row>
|
|
93
160
|
</template>
|
|
@@ -103,7 +170,7 @@ const props = defineProps({
|
|
|
103
170
|
title: "Unit Name",
|
|
104
171
|
value: "name",
|
|
105
172
|
},
|
|
106
|
-
|
|
173
|
+
{
|
|
107
174
|
title: "Level",
|
|
108
175
|
value: "level.name",
|
|
109
176
|
},
|
|
@@ -115,7 +182,7 @@ const props = defineProps({
|
|
|
115
182
|
title: "Block Name",
|
|
116
183
|
value: "buildingName",
|
|
117
184
|
},
|
|
118
|
-
|
|
185
|
+
|
|
119
186
|
// {
|
|
120
187
|
// title: "Type",
|
|
121
188
|
// value: "type",
|
|
@@ -162,6 +229,8 @@ const { debounce, formatDateDDMMYYYYLocal, toOrdinal } = useUtils();
|
|
|
162
229
|
|
|
163
230
|
const { getAll, deleteById } = useBuildingUnit();
|
|
164
231
|
|
|
232
|
+
const { uploadSpreadsheetBuilding } = useBuilding();
|
|
233
|
+
|
|
165
234
|
const searchInput = ref("");
|
|
166
235
|
const headerSearch = ref("");
|
|
167
236
|
const page = ref(1);
|
|
@@ -243,6 +312,12 @@ function openEditDialog() {
|
|
|
243
312
|
|
|
244
313
|
const dialogDelete = ref(false);
|
|
245
314
|
const deleteLoading = ref(false);
|
|
315
|
+
const bulkUploadFile = ref<File | null>(null);
|
|
316
|
+
const bulkUploadFileInput = ref<HTMLInputElement | null>(null);
|
|
317
|
+
const bulkUploadLoading = ref(false);
|
|
318
|
+
const bulkUploadSkippedDialog = ref(false);
|
|
319
|
+
const bulkUploadSkippedErrors = ref<Array<Record<string, any>>>([]);
|
|
320
|
+
const bulkUploadSkippedSummary = ref<Array<Record<string, any>>>([]);
|
|
246
321
|
|
|
247
322
|
function openDeleteDialog() {
|
|
248
323
|
dialogDelete.value = true;
|
|
@@ -272,6 +347,109 @@ async function handleDeleteBuildingUnit() {
|
|
|
272
347
|
}
|
|
273
348
|
}
|
|
274
349
|
|
|
350
|
+
function openBulkUploadFilePicker() {
|
|
351
|
+
if (bulkUploadLoading.value) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
bulkUploadFile.value = null;
|
|
356
|
+
bulkUploadSkippedErrors.value = [];
|
|
357
|
+
bulkUploadSkippedSummary.value = [];
|
|
358
|
+
bulkUploadSkippedDialog.value = false;
|
|
359
|
+
if (bulkUploadFileInput.value) {
|
|
360
|
+
bulkUploadFileInput.value.value = "";
|
|
361
|
+
bulkUploadFileInput.value.click();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function formatSkippedSummaryReason(item: Record<string, any>) {
|
|
366
|
+
if (Array.isArray(item.errors) && item.errors.length) {
|
|
367
|
+
return item.errors.join(", ");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return item.reason ?? "Skipped";
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function getSkippedRowsCount(result: Record<string, any> | null | undefined) {
|
|
374
|
+
if (typeof result?.skippedRows === "number") {
|
|
375
|
+
return result.skippedRows;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (Array.isArray(result?.skippedRows)) {
|
|
379
|
+
return result.skippedRows.length;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return 0;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function getBulkUploadSkippedErrors(result: Record<string, any> | null | undefined) {
|
|
386
|
+
if (Array.isArray(result?.skippedErrors)) {
|
|
387
|
+
return result.skippedErrors;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (Array.isArray(result?.skippedRows)) {
|
|
391
|
+
return result.skippedRows;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return [];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function getBulkUploadSkippedSummary(result: Record<string, any> | null | undefined) {
|
|
398
|
+
if (Array.isArray(result?.skippedItemsSummary)) {
|
|
399
|
+
return result.skippedItemsSummary;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (Array.isArray(result?.skippedRows)) {
|
|
403
|
+
return result.skippedRows.map((item: Record<string, any>) => ({
|
|
404
|
+
rowNumber: item.row,
|
|
405
|
+
reason: item.reason,
|
|
406
|
+
block: item.block,
|
|
407
|
+
name: item.name,
|
|
408
|
+
data: item,
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async function handleBulkUploadFileChange(event: Event) {
|
|
416
|
+
const input = event.target as HTMLInputElement;
|
|
417
|
+
const file = input.files?.[0] ?? null;
|
|
418
|
+
|
|
419
|
+
if (!file) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
bulkUploadFile.value = file;
|
|
424
|
+
bulkUploadLoading.value = true;
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
const result = await uploadSpreadsheetBuilding({
|
|
428
|
+
site,
|
|
429
|
+
file,
|
|
430
|
+
});
|
|
431
|
+
await getBuildingUnit();
|
|
432
|
+
bulkUploadSkippedErrors.value = getBulkUploadSkippedErrors(result);
|
|
433
|
+
bulkUploadSkippedSummary.value = getBulkUploadSkippedSummary(result);
|
|
434
|
+
bulkUploadSkippedDialog.value =
|
|
435
|
+
bulkUploadSkippedErrors.value.length > 0 ||
|
|
436
|
+
bulkUploadSkippedSummary.value.length > 0;
|
|
437
|
+
showMessage(
|
|
438
|
+
`Bulk upload completed. Inserted ${result?.insertedUnits ?? 0} unit(s). Skipped ${getSkippedRowsCount(result)} row(s).`,
|
|
439
|
+
"success"
|
|
440
|
+
);
|
|
441
|
+
} catch (error: any) {
|
|
442
|
+
showMessage(
|
|
443
|
+
error?.response?._data?.message || "Failed to upload spreadsheet.",
|
|
444
|
+
"error"
|
|
445
|
+
);
|
|
446
|
+
} finally {
|
|
447
|
+
bulkUploadLoading.value = false;
|
|
448
|
+
bulkUploadFile.value = null;
|
|
449
|
+
input.value = "";
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
275
453
|
function setBuilding({
|
|
276
454
|
mode = "create",
|
|
277
455
|
dialog = true,
|
|
@@ -290,7 +468,7 @@ function setBuilding({
|
|
|
290
468
|
</script>
|
|
291
469
|
|
|
292
470
|
<style scoped>
|
|
293
|
-
|
|
471
|
+
.v-data-table :deep(thead th) {
|
|
294
472
|
font-weight: bold !important;
|
|
295
473
|
}
|
|
296
474
|
</style>
|
|
@@ -3547,10 +3547,10 @@ async function exportDashboard(format: "PDF" | "Word") {
|
|
|
3547
3547
|
}
|
|
3548
3548
|
|
|
3549
3549
|
if (isHygieneMode.value) {
|
|
3550
|
+
wordHtml += `<div class="section-title">${primaryListConfig.value.title}</div>`;
|
|
3551
|
+
wordHtml += `<table class="data-table">`;
|
|
3552
|
+
wordHtml += `<thead><tr><th>Task Title</th><th>Description / Subtitle</th><th>Assigned Staff</th><th>Time</th><th>Status</th></tr></thead><tbody>`;
|
|
3550
3553
|
if (taskScheduleItems.value.length > 0) {
|
|
3551
|
-
wordHtml += `<div class="section-title">${primaryListConfig.value.title}</div>`;
|
|
3552
|
-
wordHtml += `<table class="data-table">`;
|
|
3553
|
-
wordHtml += `<thead><tr><th>Task Title</th><th>Description / Subtitle</th><th>Assigned Staff</th><th>Time</th><th>Status</th></tr></thead><tbody>`;
|
|
3554
3554
|
taskScheduleItems.value.forEach((item) => {
|
|
3555
3555
|
wordHtml += `<tr>
|
|
3556
3556
|
<td><strong>${item.title}</strong></td>
|
|
@@ -3562,13 +3562,15 @@ async function exportDashboard(format: "PDF" | "Word") {
|
|
|
3562
3562
|
)}">${item.status || ""}</span></td>
|
|
3563
3563
|
</tr>`;
|
|
3564
3564
|
});
|
|
3565
|
-
|
|
3565
|
+
} else {
|
|
3566
|
+
wordHtml += `<tr><td colspan="5" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3566
3567
|
}
|
|
3568
|
+
wordHtml += `</tbody></table>`;
|
|
3567
3569
|
|
|
3570
|
+
wordHtml += `<div class="section-title">Staff's Total Attendance</div>`;
|
|
3571
|
+
wordHtml += `<table class="data-table">`;
|
|
3572
|
+
wordHtml += `<thead><tr><th>Staff Name</th><th>Role</th><th>Total Hours</th></tr></thead><tbody>`;
|
|
3568
3573
|
if (staffStatusItems.value.length > 0) {
|
|
3569
|
-
wordHtml += `<div class="section-title">Staff's Total Attendance</div>`;
|
|
3570
|
-
wordHtml += `<table class="data-table">`;
|
|
3571
|
-
wordHtml += `<thead><tr><th>Staff Name</th><th>Role</th><th>Total Hours</th></tr></thead><tbody>`;
|
|
3572
3574
|
staffStatusItems.value.forEach((item) => {
|
|
3573
3575
|
wordHtml += `<tr>
|
|
3574
3576
|
<td><strong>${item.name}</strong></td>
|
|
@@ -3576,69 +3578,79 @@ async function exportDashboard(format: "PDF" | "Word") {
|
|
|
3576
3578
|
<td>${item.totalHours || 0} hrs</td>
|
|
3577
3579
|
</tr>`;
|
|
3578
3580
|
});
|
|
3579
|
-
|
|
3581
|
+
} else {
|
|
3582
|
+
wordHtml += `<tr><td colspan="3" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3580
3583
|
}
|
|
3584
|
+
wordHtml += `</tbody></table>`;
|
|
3581
3585
|
}
|
|
3582
3586
|
|
|
3583
3587
|
if (isPropertyMode.value) {
|
|
3588
|
+
wordHtml += `<div class="section-title">Upcoming Events</div>`;
|
|
3589
|
+
wordHtml += `<table class="data-table">`;
|
|
3590
|
+
wordHtml += `<thead><tr><th>Event Title</th><th>Description / Subtitle</th></tr></thead><tbody>`;
|
|
3584
3591
|
if (upcomingEvents.value.length > 0) {
|
|
3585
|
-
wordHtml += `<div class="section-title">Upcoming Events</div>`;
|
|
3586
|
-
wordHtml += `<table class="data-table">`;
|
|
3587
|
-
wordHtml += `<thead><tr><th>Event Title</th><th>Description / Subtitle</th></tr></thead><tbody>`;
|
|
3588
3592
|
upcomingEvents.value.forEach((item) => {
|
|
3589
3593
|
wordHtml += `<tr>
|
|
3590
3594
|
<td><strong>${item.title}</strong></td>
|
|
3591
3595
|
<td>${item.subtitle || ""}</td>
|
|
3592
3596
|
</tr>`;
|
|
3593
3597
|
});
|
|
3594
|
-
|
|
3598
|
+
} else {
|
|
3599
|
+
wordHtml += `<tr><td colspan="2" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3595
3600
|
}
|
|
3601
|
+
wordHtml += `</tbody></table>`;
|
|
3596
3602
|
|
|
3603
|
+
wordHtml += `<div class="section-title">Today's Reminder</div>`;
|
|
3604
|
+
wordHtml += `<table class="data-table">`;
|
|
3605
|
+
wordHtml += `<thead><tr><th>Reminder</th><th>Details / Subtitle</th></tr></thead><tbody>`;
|
|
3597
3606
|
if (todayReminders.value.length > 0) {
|
|
3598
|
-
wordHtml += `<div class="section-title">Today's Reminder</div>`;
|
|
3599
|
-
wordHtml += `<table class="data-table">`;
|
|
3600
|
-
wordHtml += `<thead><tr><th>Reminder</th><th>Details / Subtitle</th></tr></thead><tbody>`;
|
|
3601
3607
|
todayReminders.value.forEach((item) => {
|
|
3602
3608
|
wordHtml += `<tr>
|
|
3603
3609
|
<td><strong>${item.title}</strong></td>
|
|
3604
3610
|
<td>${item.subtitle || ""}</td>
|
|
3605
3611
|
</tr>`;
|
|
3606
3612
|
});
|
|
3607
|
-
|
|
3613
|
+
} else {
|
|
3614
|
+
wordHtml += `<tr><td colspan="2" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3608
3615
|
}
|
|
3616
|
+
wordHtml += `</tbody></table>`;
|
|
3609
3617
|
|
|
3618
|
+
wordHtml += `<div class="section-title">Today's Attentions</div>`;
|
|
3619
|
+
wordHtml += `<table class="data-table">`;
|
|
3620
|
+
wordHtml += `<thead><tr><th>Attention</th><th>Details / Subtitle</th></tr></thead><tbody>`;
|
|
3610
3621
|
if (todayAttentions.value.length > 0) {
|
|
3611
|
-
wordHtml += `<div class="section-title">Today's Attentions</div>`;
|
|
3612
|
-
wordHtml += `<table class="data-table">`;
|
|
3613
|
-
wordHtml += `<thead><tr><th>Attention</th><th>Details / Subtitle</th></tr></thead><tbody>`;
|
|
3614
3622
|
todayAttentions.value.forEach((item) => {
|
|
3615
3623
|
wordHtml += `<tr>
|
|
3616
3624
|
<td><strong>${item.title}</strong></td>
|
|
3617
3625
|
<td>${item.subtitle || ""}</td>
|
|
3618
3626
|
</tr>`;
|
|
3619
3627
|
});
|
|
3620
|
-
|
|
3628
|
+
} else {
|
|
3629
|
+
wordHtml += `<tr><td colspan="2" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3621
3630
|
}
|
|
3631
|
+
wordHtml += `</tbody></table>`;
|
|
3622
3632
|
|
|
3633
|
+
wordHtml += `<div class="section-title">Work Order Status</div>`;
|
|
3634
|
+
wordHtml += `<table class="data-table">`;
|
|
3635
|
+
wordHtml += `<thead><tr><th>Status</th><th>Count / Value</th></tr></thead><tbody>`;
|
|
3623
3636
|
if (workOrderStatus.value.length > 0) {
|
|
3624
|
-
wordHtml += `<div class="section-title">Work Order Status</div>`;
|
|
3625
|
-
wordHtml += `<table class="data-table">`;
|
|
3626
|
-
wordHtml += `<thead><tr><th>Status</th><th>Count / Value</th></tr></thead><tbody>`;
|
|
3627
3637
|
workOrderStatus.value.forEach((item) => {
|
|
3628
3638
|
wordHtml += `<tr>
|
|
3629
3639
|
<td><strong>${item.label}</strong></td>
|
|
3630
3640
|
<td>${item.value || 0}</td>
|
|
3631
3641
|
</tr>`;
|
|
3632
3642
|
});
|
|
3633
|
-
|
|
3643
|
+
} else {
|
|
3644
|
+
wordHtml += `<tr><td colspan="2" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3634
3645
|
}
|
|
3646
|
+
wordHtml += `</tbody></table>`;
|
|
3635
3647
|
}
|
|
3636
3648
|
|
|
3637
3649
|
if (isSecurityMode.value) {
|
|
3650
|
+
wordHtml += `<div class="section-title">Active Patrol</div>`;
|
|
3651
|
+
wordHtml += `<table class="data-table">`;
|
|
3652
|
+
wordHtml += `<thead><tr><th>Patrol Route</th><th>Location</th><th>Assigned Officer</th><th>Status</th></tr></thead><tbody>`;
|
|
3638
3653
|
if (activePatrolItems.value.length > 0) {
|
|
3639
|
-
wordHtml += `<div class="section-title">Active Patrol</div>`;
|
|
3640
|
-
wordHtml += `<table class="data-table">`;
|
|
3641
|
-
wordHtml += `<thead><tr><th>Patrol Route</th><th>Location</th><th>Assigned Officer</th><th>Status</th></tr></thead><tbody>`;
|
|
3642
3654
|
activePatrolItems.value.forEach((item) => {
|
|
3643
3655
|
wordHtml += `<tr>
|
|
3644
3656
|
<td><strong>${item.title || item.name}</strong></td>
|
|
@@ -3649,13 +3661,15 @@ async function exportDashboard(format: "PDF" | "Word") {
|
|
|
3649
3661
|
)}">${item.status || ""}</span></td>
|
|
3650
3662
|
</tr>`;
|
|
3651
3663
|
});
|
|
3652
|
-
|
|
3664
|
+
} else {
|
|
3665
|
+
wordHtml += `<tr><td colspan="4" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3653
3666
|
}
|
|
3667
|
+
wordHtml += `</tbody></table>`;
|
|
3654
3668
|
|
|
3669
|
+
wordHtml += `<div class="section-title">Staff Status</div>`;
|
|
3670
|
+
wordHtml += `<table class="data-table">`;
|
|
3671
|
+
wordHtml += `<thead><tr><th>Officer Name</th><th>Role / Designation</th><th>Status</th></tr></thead><tbody>`;
|
|
3655
3672
|
if (securityStaffItems.value.length > 0) {
|
|
3656
|
-
wordHtml += `<div class="section-title">Staff Status</div>`;
|
|
3657
|
-
wordHtml += `<table class="data-table">`;
|
|
3658
|
-
wordHtml += `<thead><tr><th>Officer Name</th><th>Role / Designation</th><th>Status</th></tr></thead><tbody>`;
|
|
3659
3673
|
securityStaffItems.value.forEach((item) => {
|
|
3660
3674
|
wordHtml += `<tr>
|
|
3661
3675
|
<td><strong>${
|
|
@@ -3667,14 +3681,16 @@ async function exportDashboard(format: "PDF" | "Word") {
|
|
|
3667
3681
|
)}">${item.status || "On duty"}</span></td>
|
|
3668
3682
|
</tr>`;
|
|
3669
3683
|
});
|
|
3670
|
-
|
|
3684
|
+
} else {
|
|
3685
|
+
wordHtml += `<tr><td colspan="3" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3671
3686
|
}
|
|
3687
|
+
wordHtml += `</tbody></table>`;
|
|
3672
3688
|
}
|
|
3673
3689
|
|
|
3690
|
+
wordHtml += `<div class="section-title">Feedbacks</div>`;
|
|
3691
|
+
wordHtml += `<table class="data-table">`;
|
|
3692
|
+
wordHtml += `<thead><tr><th>Title</th><th>Description / Category</th><th>Reporter / Person</th><th>Status</th></tr></thead><tbody>`;
|
|
3674
3693
|
if (feedbackItems.value.length > 0) {
|
|
3675
|
-
wordHtml += `<div class="section-title">Feedbacks</div>`;
|
|
3676
|
-
wordHtml += `<table class="data-table">`;
|
|
3677
|
-
wordHtml += `<thead><tr><th>Title</th><th>Description / Category</th><th>Reporter / Person</th><th>Status</th></tr></thead><tbody>`;
|
|
3678
3694
|
feedbackItems.value.forEach((item) => {
|
|
3679
3695
|
wordHtml += `<tr>
|
|
3680
3696
|
<td><strong>${item.title}</strong></td>
|
|
@@ -3685,8 +3701,10 @@ async function exportDashboard(format: "PDF" | "Word") {
|
|
|
3685
3701
|
)}">${item.status || "To-Do"}</span></td>
|
|
3686
3702
|
</tr>`;
|
|
3687
3703
|
});
|
|
3688
|
-
|
|
3704
|
+
} else {
|
|
3705
|
+
wordHtml += `<tr><td colspan="4" style="padding: 10px; text-align: center; color: #64748b;">No data available</td></tr>`;
|
|
3689
3706
|
}
|
|
3707
|
+
wordHtml += `</tbody></table>`;
|
|
3690
3708
|
|
|
3691
3709
|
wordHtml += `
|
|
3692
3710
|
</body>
|