@7365admin1/layer-common 3.0.1 → 3.0.3
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 +13 -0
- package/components/AttendanceCheckInOutDialog.vue +0 -1
- package/components/DashboardEmptyState.vue +18 -0
- package/components/DashboardMain.vue +1000 -1268
- package/components/DashboardPanel.vue +44 -0
- package/components/DocumentForm.vue +22 -4
- package/components/DocumentManagement.vue +43 -6
- package/components/Editor.vue +47 -22
- package/components/Facility/BookingSetup.vue +36 -12
- package/components/MyAttendanceMain.vue +1 -2
- package/components/VisitorDataFromScannedQRCodeForm.vue +20 -8
- package/components/VisitorManagement.vue +2 -2
- package/components/VisitorsReportPreview.vue +4 -4
- package/composables/useAttendance.ts +2 -2
- package/composables/useDashboard.ts +77 -18
- package/composables/useDocument.ts +1 -0
- package/composables/useFacility.ts +0 -9
- package/package.json +1 -1
- package/types/dashboard.d.ts +66 -0
- package/types/facility.d.ts +0 -3
- package/types/visitor.d.ts +8 -2
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card
|
|
3
|
+
flat
|
|
4
|
+
border
|
|
5
|
+
rounded="lg"
|
|
6
|
+
class="d-flex flex-column w-100"
|
|
7
|
+
style="min-height: 200px"
|
|
8
|
+
>
|
|
9
|
+
<div class="d-flex align-center justify-space-between pa-5 pb-3">
|
|
10
|
+
<h2 class="text-body-1 font-weight-black text-blue-grey-darken-4 mb-0">
|
|
11
|
+
{{ title }}
|
|
12
|
+
</h2>
|
|
13
|
+
<v-btn
|
|
14
|
+
v-if="actionLabel"
|
|
15
|
+
variant="text"
|
|
16
|
+
color="primary"
|
|
17
|
+
size="small"
|
|
18
|
+
class="text-none font-weight-bold"
|
|
19
|
+
@click="$emit('action')"
|
|
20
|
+
>
|
|
21
|
+
{{ actionLabel }}
|
|
22
|
+
</v-btn>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="flex-1-1-100 overflow-y-auto pa-5 pt-0" style="max-height: 400px">
|
|
26
|
+
<slot />
|
|
27
|
+
</div>
|
|
28
|
+
</v-card>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
defineProps({
|
|
33
|
+
title: {
|
|
34
|
+
type: String,
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
actionLabel: {
|
|
38
|
+
type: String,
|
|
39
|
+
default: '',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
defineEmits(['action']);
|
|
44
|
+
</script>
|
|
@@ -125,17 +125,35 @@ const routeParentId = computed(() =>
|
|
|
125
125
|
);
|
|
126
126
|
const effectiveParentId = computed(() => prop.parentId || routeParentId.value);
|
|
127
127
|
|
|
128
|
+
function getAttachmentId(attachment: any): string {
|
|
129
|
+
if (!attachment) return "";
|
|
130
|
+
if (Array.isArray(attachment)) {
|
|
131
|
+
const first = attachment[0];
|
|
132
|
+
if (!first) return "";
|
|
133
|
+
if (typeof first === "string") return first;
|
|
134
|
+
return String(first?._id || first?.id || "");
|
|
135
|
+
}
|
|
136
|
+
if (typeof attachment === "string") return attachment;
|
|
137
|
+
return String(attachment?._id || attachment?.id || "");
|
|
138
|
+
}
|
|
139
|
+
|
|
128
140
|
if (prop.mode === "edit") {
|
|
129
141
|
document.value.name = prop.document.name;
|
|
130
|
-
|
|
142
|
+
|
|
143
|
+
const extractedId = getAttachmentId(prop.document.attachment);
|
|
144
|
+
document.value.attachment = extractedId ? [extractedId] : [];
|
|
145
|
+
|
|
131
146
|
document.value.type = prop.document.type;
|
|
132
147
|
}
|
|
133
148
|
|
|
134
149
|
watch(
|
|
135
150
|
() => document.value.attachment,
|
|
136
|
-
async (newVal) => {
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
async (newVal, oldVal) => {
|
|
152
|
+
const newId = Array.isArray(newVal) ? newVal[0] : newVal;
|
|
153
|
+
const oldId = Array.isArray(oldVal) ? oldVal[0] : oldVal;
|
|
154
|
+
|
|
155
|
+
if (newId && newId !== oldId) {
|
|
156
|
+
const fileId = newId;
|
|
139
157
|
const fileData = await getFileById(fileId);
|
|
140
158
|
if (fileData) {
|
|
141
159
|
document.value.name = fileData.data.name;
|
|
@@ -177,6 +177,7 @@
|
|
|
177
177
|
<!-- Create Dialog -->
|
|
178
178
|
<v-dialog v-model="createDialog" width="450" persistent>
|
|
179
179
|
<DocumentForm
|
|
180
|
+
v-if="createDialog"
|
|
180
181
|
:parent-id="activeParentId"
|
|
181
182
|
@cancel="createDialog = false"
|
|
182
183
|
@success="successCreate()"
|
|
@@ -253,6 +254,7 @@
|
|
|
253
254
|
<!-- Edit Dialog -->
|
|
254
255
|
<v-dialog v-model="editDialog" width="450" persistent>
|
|
255
256
|
<DocumentForm
|
|
257
|
+
v-if="editDialog"
|
|
256
258
|
mode="edit"
|
|
257
259
|
@cancel="editDialog = false"
|
|
258
260
|
@success="successUpdate()"
|
|
@@ -277,13 +279,19 @@
|
|
|
277
279
|
<v-col cols="6" class="text-right text-body-1">
|
|
278
280
|
<NuxtLink
|
|
279
281
|
v-if="selectedDocument?.attachment && isViewable"
|
|
280
|
-
:to="
|
|
282
|
+
:to="
|
|
283
|
+
getFileUrl(getAttachmentId(selectedDocument.attachment))
|
|
284
|
+
"
|
|
281
285
|
external
|
|
282
286
|
target="_blank"
|
|
283
287
|
>
|
|
284
288
|
View Document
|
|
285
289
|
</NuxtLink>
|
|
286
|
-
<span
|
|
290
|
+
<span
|
|
291
|
+
v-else-if="selectedDocument?.attachment"
|
|
292
|
+
class="text-grey"
|
|
293
|
+
style="opacity: 0.6; cursor: not-allowed"
|
|
294
|
+
>
|
|
287
295
|
View Document
|
|
288
296
|
</span>
|
|
289
297
|
<span v-else>N/A</span>
|
|
@@ -528,6 +536,20 @@ const {
|
|
|
528
536
|
} = documentService;
|
|
529
537
|
const { getFileUrl } = useFile();
|
|
530
538
|
|
|
539
|
+
function getAttachmentId(attachment: any): string {
|
|
540
|
+
if (!attachment) return "";
|
|
541
|
+
|
|
542
|
+
if (Array.isArray(attachment)) {
|
|
543
|
+
const first = attachment[0];
|
|
544
|
+
if (!first) return "";
|
|
545
|
+
if (typeof first === "string") return first;
|
|
546
|
+
return String(first?._id || first?.id || "");
|
|
547
|
+
}
|
|
548
|
+
if (typeof attachment === "string") return attachment;
|
|
549
|
+
|
|
550
|
+
return String(attachment?._id || attachment?.id || "");
|
|
551
|
+
}
|
|
552
|
+
|
|
531
553
|
const router = useRouter();
|
|
532
554
|
const route = useRoute();
|
|
533
555
|
|
|
@@ -627,11 +649,26 @@ function formatBytes(bytes = 0) {
|
|
|
627
649
|
return `${parseFloat((b / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
628
650
|
}
|
|
629
651
|
|
|
652
|
+
const KNOWN_FILE_TYPES = new Set(["pdf", "csv", "doc", "docx", "folder", "mixed"]);
|
|
653
|
+
|
|
654
|
+
function inferDisplayType(doc: any): string {
|
|
655
|
+
if (doc.type === "folder") return "Folder";
|
|
656
|
+
// If stored type is a known file format, use it directly
|
|
657
|
+
const t = String(doc.type || "").toLowerCase().trim();
|
|
658
|
+
if (t && KNOWN_FILE_TYPES.has(t)) return t;
|
|
659
|
+
// Otherwise infer from the document name (handles legacy "public" ACL type)
|
|
660
|
+
const name = String(doc.name || "").toLowerCase();
|
|
661
|
+
if (name.endsWith(".pdf")) return "pdf";
|
|
662
|
+
if (name.endsWith(".csv")) return "csv";
|
|
663
|
+
if (name.endsWith(".docx") || name.endsWith(".doc")) return "doc";
|
|
664
|
+
return t || "N/A";
|
|
665
|
+
}
|
|
666
|
+
|
|
630
667
|
const tableItems = computed(() => {
|
|
631
668
|
return items.value.map((doc: any) => ({
|
|
632
669
|
...doc,
|
|
633
670
|
_raw: doc,
|
|
634
|
-
file_type: doc
|
|
671
|
+
file_type: inferDisplayType(doc),
|
|
635
672
|
file_size:
|
|
636
673
|
doc.type === "folder"
|
|
637
674
|
? "—"
|
|
@@ -664,8 +701,8 @@ const selectedDocument = ref<TDocument>({
|
|
|
664
701
|
});
|
|
665
702
|
|
|
666
703
|
const isViewable = computed(() => {
|
|
667
|
-
const
|
|
668
|
-
return ["pdf", "doc", "csv"].includes(
|
|
704
|
+
const effectiveType = inferDisplayType(selectedDocument.value as any).toLowerCase();
|
|
705
|
+
return ["pdf", "doc", "csv"].includes(effectiveType);
|
|
669
706
|
});
|
|
670
707
|
|
|
671
708
|
const previewDetails = computed(() => {
|
|
@@ -769,7 +806,7 @@ function downloadDocument(document?: TDocument) {
|
|
|
769
806
|
return;
|
|
770
807
|
}
|
|
771
808
|
|
|
772
|
-
const fileUrl = getFileUrl(document.attachment);
|
|
809
|
+
const fileUrl = getFileUrl(getAttachmentId(document.attachment));
|
|
773
810
|
if (fileUrl) {
|
|
774
811
|
window.open(fileUrl, "_blank", "noopener,noreferrer");
|
|
775
812
|
}
|
package/components/Editor.vue
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<ClientOnly>
|
|
3
3
|
<v-row no-gutters class="ckeditor-container w-100">
|
|
4
|
-
<ckeditor
|
|
4
|
+
<ckeditor
|
|
5
|
+
v-model="data"
|
|
6
|
+
:editor="ClassicEditor"
|
|
7
|
+
:config="config"
|
|
8
|
+
@ready="onReady"
|
|
9
|
+
:key="prop.readOnly"
|
|
10
|
+
/>
|
|
5
11
|
</v-row>
|
|
6
12
|
</ClientOnly>
|
|
7
13
|
</template>
|
|
8
14
|
|
|
9
15
|
<script setup>
|
|
10
|
-
|
|
11
16
|
const prop = defineProps({
|
|
12
17
|
readOnly: {
|
|
13
18
|
type: Boolean,
|
|
14
|
-
required: false
|
|
15
|
-
|
|
16
|
-
}
|
|
19
|
+
required: false,
|
|
20
|
+
default: false,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
17
23
|
import {
|
|
18
24
|
ClassicEditor,
|
|
19
25
|
Essentials,
|
|
@@ -25,28 +31,50 @@ import {
|
|
|
25
31
|
Link,
|
|
26
32
|
Alignment,
|
|
27
33
|
Font,
|
|
28
|
-
} from
|
|
29
|
-
import { Ckeditor } from
|
|
30
|
-
import
|
|
34
|
+
} from "ckeditor5";
|
|
35
|
+
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
|
36
|
+
import "ckeditor5/ckeditor5.css";
|
|
31
37
|
|
|
32
|
-
import
|
|
38
|
+
import "ckeditor5/ckeditor5.css";
|
|
33
39
|
|
|
34
|
-
const data = defineModel({ required: true, default: "" })
|
|
40
|
+
const data = defineModel({ required: true, default: "" });
|
|
35
41
|
|
|
36
42
|
const editorInstance = ref(null);
|
|
37
43
|
|
|
38
44
|
const config = computed(() => ({
|
|
39
|
-
licenseKey:
|
|
40
|
-
plugins: [
|
|
45
|
+
licenseKey: "GPL",
|
|
46
|
+
plugins: [
|
|
47
|
+
Essentials,
|
|
48
|
+
Paragraph,
|
|
49
|
+
Bold,
|
|
50
|
+
Italic,
|
|
51
|
+
Heading,
|
|
52
|
+
List,
|
|
53
|
+
Alignment,
|
|
54
|
+
Font,
|
|
55
|
+
],
|
|
41
56
|
toolbar: [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
"undo",
|
|
58
|
+
"redo",
|
|
59
|
+
"|",
|
|
60
|
+
"bold",
|
|
61
|
+
"italic",
|
|
62
|
+
"|",
|
|
63
|
+
"fontSize",
|
|
64
|
+
"fontColor",
|
|
65
|
+
"fontBackgroundColor",
|
|
66
|
+
"|",
|
|
67
|
+
"bulletedList",
|
|
68
|
+
"numberedList",
|
|
69
|
+
"|",
|
|
70
|
+
"heading",
|
|
71
|
+
"|",
|
|
72
|
+
"alignment:left",
|
|
73
|
+
"alignment:center",
|
|
74
|
+
"alignment:right",
|
|
75
|
+
"alignment:justify",
|
|
47
76
|
],
|
|
48
|
-
}))
|
|
49
|
-
|
|
77
|
+
}));
|
|
50
78
|
|
|
51
79
|
function onReady(editor) {
|
|
52
80
|
editorInstance.value = editor;
|
|
@@ -56,7 +84,6 @@ function onReady(editor) {
|
|
|
56
84
|
function toggleReadOnly(isReadOnly) {
|
|
57
85
|
if (!editorInstance.value) return;
|
|
58
86
|
|
|
59
|
-
|
|
60
87
|
if (isReadOnly) {
|
|
61
88
|
editorInstance.value.enableReadOnlyMode("view-mode-lock");
|
|
62
89
|
} else {
|
|
@@ -70,8 +97,6 @@ watch(
|
|
|
70
97
|
toggleReadOnly(newVal);
|
|
71
98
|
}
|
|
72
99
|
);
|
|
73
|
-
|
|
74
|
-
|
|
75
100
|
</script>
|
|
76
101
|
|
|
77
102
|
<style scoped>
|
|
@@ -501,7 +501,7 @@
|
|
|
501
501
|
</v-col>
|
|
502
502
|
<v-col cols="3" class="d-flex align-center justify-end">
|
|
503
503
|
<v-switch
|
|
504
|
-
v-model="
|
|
504
|
+
v-model="isBookingUnitLimitEnabled"
|
|
505
505
|
class="d-flex align-center"
|
|
506
506
|
color="primary"
|
|
507
507
|
hide-details
|
|
@@ -519,7 +519,7 @@
|
|
|
519
519
|
:min="1"
|
|
520
520
|
:max="500"
|
|
521
521
|
hide-details
|
|
522
|
-
:disabled="!
|
|
522
|
+
:disabled="!isBookingUnitLimitEnabled"
|
|
523
523
|
/>
|
|
524
524
|
</v-col>
|
|
525
525
|
</v-row>
|
|
@@ -539,7 +539,7 @@
|
|
|
539
539
|
</v-col>
|
|
540
540
|
<v-col cols="3" class="d-flex align-center justify-end">
|
|
541
541
|
<v-switch
|
|
542
|
-
v-model="
|
|
542
|
+
v-model="isMaxNumberGuestLimitEnabled"
|
|
543
543
|
class="d-flex align-center"
|
|
544
544
|
color="primary"
|
|
545
545
|
hide-details
|
|
@@ -557,7 +557,7 @@
|
|
|
557
557
|
:min="1"
|
|
558
558
|
:max="500"
|
|
559
559
|
hide-details
|
|
560
|
-
:disabled="!
|
|
560
|
+
:disabled="!isMaxNumberGuestLimitEnabled"
|
|
561
561
|
/>
|
|
562
562
|
</v-col>
|
|
563
563
|
</v-row>
|
|
@@ -576,7 +576,7 @@
|
|
|
576
576
|
</v-col>
|
|
577
577
|
<v-col cols="3" class="d-flex align-center justify-end">
|
|
578
578
|
<v-switch
|
|
579
|
-
v-model="
|
|
579
|
+
v-model="isCancelAllowedLimitEnabled"
|
|
580
580
|
class="d-flex align-center"
|
|
581
581
|
color="primary"
|
|
582
582
|
hide-details
|
|
@@ -594,7 +594,7 @@
|
|
|
594
594
|
:min="1"
|
|
595
595
|
:max="500"
|
|
596
596
|
hide-details
|
|
597
|
-
:disabled="!
|
|
597
|
+
:disabled="!isCancelAllowedLimitEnabled"
|
|
598
598
|
/>
|
|
599
599
|
</v-col>
|
|
600
600
|
</v-row>
|
|
@@ -1013,32 +1013,56 @@ const handlePolicy = (newContent: string) => {
|
|
|
1013
1013
|
facility.value.bookingPolicy = newContent;
|
|
1014
1014
|
};
|
|
1015
1015
|
|
|
1016
|
+
const isBookingUnitLimitEnabled = ref<boolean>(false);
|
|
1017
|
+
const isMaxNumberGuestLimitEnabled = ref<boolean>(false);
|
|
1018
|
+
const isCancelAllowedLimitEnabled = ref<boolean>(false);
|
|
1019
|
+
|
|
1020
|
+
watchEffect(() => {
|
|
1021
|
+
if (facility.value.bookingUnitLimit > 0) {
|
|
1022
|
+
isBookingUnitLimitEnabled.value = true;
|
|
1023
|
+
} else {
|
|
1024
|
+
isBookingUnitLimitEnabled.value = false;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (facility.value.maxNumberGuestLimit > 0) {
|
|
1028
|
+
isMaxNumberGuestLimitEnabled.value = true;
|
|
1029
|
+
} else {
|
|
1030
|
+
isMaxNumberGuestLimitEnabled.value = false;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (facility.value.daysAllowedCancel > 0) {
|
|
1034
|
+
isCancelAllowedLimitEnabled.value = true;
|
|
1035
|
+
} else {
|
|
1036
|
+
isCancelAllowedLimitEnabled.value = false;
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1016
1040
|
function onToggleBookingUnitLimit(val: any) {
|
|
1017
1041
|
if (val) {
|
|
1018
|
-
|
|
1042
|
+
isBookingUnitLimitEnabled.value = true;
|
|
1019
1043
|
facility.value.bookingUnitLimit = 1;
|
|
1020
1044
|
} else {
|
|
1021
|
-
|
|
1045
|
+
isBookingUnitLimitEnabled.value = false;
|
|
1022
1046
|
facility.value.bookingUnitLimit = null;
|
|
1023
1047
|
}
|
|
1024
1048
|
}
|
|
1025
1049
|
|
|
1026
1050
|
function onToggleMaxNumberGuestLimit(val: any) {
|
|
1027
1051
|
if (val) {
|
|
1028
|
-
|
|
1052
|
+
isMaxNumberGuestLimitEnabled.value = true;
|
|
1029
1053
|
facility.value.maxNumberGuestLimit = 1;
|
|
1030
1054
|
} else {
|
|
1031
|
-
|
|
1055
|
+
isMaxNumberGuestLimitEnabled.value = false;
|
|
1032
1056
|
facility.value.maxNumberGuestLimit = null;
|
|
1033
1057
|
}
|
|
1034
1058
|
}
|
|
1035
1059
|
|
|
1036
1060
|
function onToggleCancelAllowedLimit(val: any) {
|
|
1037
1061
|
if (val) {
|
|
1038
|
-
|
|
1062
|
+
isCancelAllowedLimitEnabled.value = true;
|
|
1039
1063
|
facility.value.daysAllowedCancel = 1;
|
|
1040
1064
|
} else {
|
|
1041
|
-
|
|
1065
|
+
isCancelAllowedLimitEnabled.value = false;
|
|
1042
1066
|
facility.value.daysAllowedCancel = null;
|
|
1043
1067
|
}
|
|
1044
1068
|
}
|
|
@@ -62,7 +62,6 @@
|
|
|
62
62
|
<AttendanceCheckInOutDialog
|
|
63
63
|
v-model="dialogShowForm"
|
|
64
64
|
:action="currentAction"
|
|
65
|
-
:serviceType="props.serviceType"
|
|
66
65
|
@saved="onAttendanceSaved"
|
|
67
66
|
@close="dialogShowForm = false"
|
|
68
67
|
/>
|
|
@@ -203,7 +202,7 @@ const onAttendanceSaved = async (
|
|
|
203
202
|
try {
|
|
204
203
|
let response;
|
|
205
204
|
if (currentAction.value === "checkIn") {
|
|
206
|
-
response = await checkIn(props.site, payload as TAttendanceCheckIn);
|
|
205
|
+
response = await checkIn(props.site, payload as TAttendanceCheckIn, props.serviceType);
|
|
207
206
|
} else {
|
|
208
207
|
if (!todayAttendance.value?._id) {
|
|
209
208
|
throw new Error("Attendance ID not found");
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
</v-col>
|
|
30
30
|
|
|
31
31
|
<v-col v-if="!visitorInfo.checkIn" cols="12">
|
|
32
|
-
<InputLabel class="text-capitalize" title="NRIC
|
|
33
|
-
<v-text-field
|
|
32
|
+
<InputLabel class="text-capitalize" title="NRIC" />
|
|
33
|
+
<!-- <v-text-field
|
|
34
34
|
v-if="!visitorInfo.checkIn"
|
|
35
35
|
v-model="visitorInfo.nric"
|
|
36
36
|
density="comfortable"
|
|
@@ -43,7 +43,17 @@
|
|
|
43
43
|
density="comfortable"
|
|
44
44
|
hide-details
|
|
45
45
|
readonly
|
|
46
|
-
/>
|
|
46
|
+
/> -->
|
|
47
|
+
<NRICNumber v-if="!visitorInfo.checkIn"
|
|
48
|
+
v-model="visitorInfo.nric"
|
|
49
|
+
density="comfortable"
|
|
50
|
+
hide-details
|
|
51
|
+
clearable />
|
|
52
|
+
<NRICNumber v-else
|
|
53
|
+
v-model="visitorNric"
|
|
54
|
+
density="comfortable"
|
|
55
|
+
hide-details
|
|
56
|
+
readonly />
|
|
47
57
|
</v-col>
|
|
48
58
|
|
|
49
59
|
<v-col v-if="!visitorInfo.checkIn" cols="12" class="mt-4">
|
|
@@ -222,6 +232,8 @@
|
|
|
222
232
|
</v-card>
|
|
223
233
|
</template>
|
|
224
234
|
<script setup lang="ts">
|
|
235
|
+
import NRICNumber from './Input/NRICNumber.vue';
|
|
236
|
+
|
|
225
237
|
const prop = defineProps({
|
|
226
238
|
site: {
|
|
227
239
|
type: String,
|
|
@@ -271,15 +283,15 @@ const location = computed({
|
|
|
271
283
|
get: () => {
|
|
272
284
|
if (
|
|
273
285
|
visitorInfo.value &&
|
|
274
|
-
visitorInfo.value.block &&
|
|
275
|
-
visitorInfo.value.level &&
|
|
286
|
+
visitorInfo.value.block?.name &&
|
|
287
|
+
visitorInfo.value.level?.name &&
|
|
276
288
|
visitorInfo.value.unitName
|
|
277
289
|
) {
|
|
278
|
-
return `${visitorInfo.value.block} / ${visitorInfo.value.level} / ${visitorInfo.value.unitName}`;
|
|
290
|
+
return `${visitorInfo.value.block?.name} / ${visitorInfo.value.level?.name} / ${visitorInfo.value.unitName}`;
|
|
279
291
|
} else if (
|
|
280
292
|
visitorInfo.value &&
|
|
281
|
-
visitorInfo.value.block &&
|
|
282
|
-
!visitorInfo.value.level &&
|
|
293
|
+
visitorInfo.value.block?.name &&
|
|
294
|
+
!visitorInfo.value.level?.name &&
|
|
283
295
|
!visitorInfo.value.unitName
|
|
284
296
|
) {
|
|
285
297
|
return visitorInfo.value.block;
|
|
@@ -1446,8 +1446,8 @@ function generateCSVData(visitors: any[]): string {
|
|
|
1446
1446
|
visitor.name || "",
|
|
1447
1447
|
formatExportTypes(formatType(visitor)) || "",
|
|
1448
1448
|
visitor.company || "",
|
|
1449
|
-
visitor.block || "",
|
|
1450
|
-
visitor.level || "",
|
|
1449
|
+
visitor.block?.name || "",
|
|
1450
|
+
visitor.level?.name || "",
|
|
1451
1451
|
visitor.unitName || "",
|
|
1452
1452
|
visitor.contact || "",
|
|
1453
1453
|
visitor.plateNumber || "",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
<v-icon icon="mdi-storefront-outline" size="15" />
|
|
41
41
|
<span class="text-capitalize" style="word-break: break-word;">{{
|
|
42
42
|
formatLocation({
|
|
43
|
-
block: item.block,
|
|
44
|
-
level: item.level,
|
|
43
|
+
block: item.block?.name || "",
|
|
44
|
+
level: item.level?.name,
|
|
45
45
|
unit: item.unitName,
|
|
46
46
|
})
|
|
47
47
|
}}</span>
|
|
@@ -182,10 +182,10 @@ watch(getSitesReq, (newData: any) => {
|
|
|
182
182
|
}
|
|
183
183
|
})
|
|
184
184
|
|
|
185
|
-
const formatLocation = ({ block, level, unit }: { block:
|
|
185
|
+
const formatLocation = ({ block, level, unit }: { block: string, level: string, unit: string | TBuildingUnit }) => {
|
|
186
186
|
const parts = [];
|
|
187
187
|
|
|
188
|
-
if (block) parts.push(
|
|
188
|
+
if (block) parts.push(`${block}`);
|
|
189
189
|
if (level) parts.push(level);
|
|
190
190
|
if (unit) parts.push(unit);
|
|
191
191
|
|
|
@@ -37,12 +37,12 @@ export default function useAttendance() {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function checkIn(site: string, payload: TAttendanceCheckIn) {
|
|
40
|
+
function checkIn(site: string, payload: TAttendanceCheckIn, serviceType: string = "") {
|
|
41
41
|
return useNuxtApp().$api<Record<string, any>>(
|
|
42
42
|
`/api/attendances/site/${site}/check-in`,
|
|
43
43
|
{
|
|
44
44
|
method: "POST",
|
|
45
|
-
body: payload,
|
|
45
|
+
body: { ...payload, serviceType },
|
|
46
46
|
}
|
|
47
47
|
);
|
|
48
48
|
}
|
|
@@ -1,31 +1,90 @@
|
|
|
1
|
-
export default function(){
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export default function () {
|
|
2
|
+
function getDashboardHygiene(
|
|
3
|
+
site = "",
|
|
4
|
+
period: TDashboardValues = "today",
|
|
5
|
+
serviceType: TServiceType
|
|
6
|
+
) {
|
|
7
|
+
return useNuxtApp().$api<THygieneDashboardData>(
|
|
8
|
+
`/api/dashboard/serviceType/${serviceType}/site/${site}`,
|
|
9
|
+
{
|
|
10
|
+
method: "GET",
|
|
11
|
+
query: {
|
|
12
|
+
period,
|
|
13
|
+
},
|
|
6
14
|
}
|
|
15
|
+
);
|
|
16
|
+
}
|
|
7
17
|
|
|
18
|
+
function getDashboardHygieneWeeklyActivity(
|
|
19
|
+
site = "",
|
|
20
|
+
serviceType: TServiceType = "cleaning_services"
|
|
21
|
+
) {
|
|
22
|
+
return useNuxtApp().$api<THygieneWeeklyActivityItem[]>(
|
|
23
|
+
`/api/dashboard/serviceType/${serviceType}/site/${site}/weekly-activity`,
|
|
24
|
+
{
|
|
25
|
+
method: "GET",
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
}
|
|
8
29
|
|
|
30
|
+
function getDashboardHygieneTaskSchedule(
|
|
31
|
+
site = "",
|
|
32
|
+
serviceType: TServiceType,
|
|
33
|
+
page = 1,
|
|
34
|
+
limit = 4
|
|
35
|
+
) {
|
|
36
|
+
return useNuxtApp().$api<
|
|
37
|
+
THygienePaginatedResponse<THygieneTaskScheduleItem>
|
|
38
|
+
>(`/api/dashboard/serviceType/${serviceType}/site/${site}/task-schedule`, {
|
|
39
|
+
method: "GET",
|
|
40
|
+
query: {
|
|
41
|
+
page,
|
|
42
|
+
limit,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
9
46
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
47
|
+
function getDashboardHygieneAttendance(
|
|
48
|
+
site = "",
|
|
49
|
+
serviceType: TServiceType,
|
|
50
|
+
page = 1,
|
|
51
|
+
limit = 4
|
|
52
|
+
) {
|
|
53
|
+
return useNuxtApp().$api<THygienePaginatedResponse<THygieneAttendanceItem>>(
|
|
54
|
+
`/api/dashboard/serviceType/${serviceType}/site/${site}/attendance`,
|
|
15
55
|
{
|
|
16
56
|
method: "GET",
|
|
17
57
|
query: {
|
|
18
|
-
|
|
58
|
+
page,
|
|
59
|
+
limit,
|
|
19
60
|
},
|
|
20
61
|
}
|
|
21
62
|
);
|
|
22
63
|
}
|
|
23
64
|
|
|
65
|
+
function getDashboardHygieneFeedbacks(
|
|
66
|
+
site = "",
|
|
67
|
+
serviceType: TServiceType,
|
|
68
|
+
page = 1,
|
|
69
|
+
limit = 4
|
|
70
|
+
) {
|
|
71
|
+
return useNuxtApp().$api<THygienePaginatedResponse<THygieneFeedbackItem>>(
|
|
72
|
+
`/api/dashboard/serviceType/${serviceType}/site/${site}/feedbacks`,
|
|
73
|
+
{
|
|
74
|
+
method: "GET",
|
|
75
|
+
query: {
|
|
76
|
+
page,
|
|
77
|
+
limit,
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
24
82
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
83
|
+
return {
|
|
84
|
+
getDashboardHygiene,
|
|
85
|
+
getDashboardHygieneWeeklyActivity,
|
|
86
|
+
getDashboardHygieneTaskSchedule,
|
|
87
|
+
getDashboardHygieneAttendance,
|
|
88
|
+
getDashboardHygieneFeedbacks,
|
|
89
|
+
};
|
|
90
|
+
}
|