@7365admin1/layer-common 3.0.5 → 3.0.7
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/BulletinBoardForm.vue +423 -333
- package/components/DashboardMain.vue +168 -74
- package/components/InvitationMain.vue +1 -0
- package/components/OnlineFormConfigurationForm.vue +2 -2
- package/components/OnlineFormFill.vue +193 -51
- package/components/VehicleManagement.vue +6 -2
- package/components/VisitorManagement.vue +1 -1
- package/composables/useDashboard.ts +25 -1
- package/composables/useOnlineForm.ts +25 -13
- package/composables/useOnlineFormPages.ts +2 -2
- package/package.json +1 -1
- package/types/dashboard.d.ts +1 -1
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
density="comfortable"
|
|
53
53
|
divided
|
|
54
54
|
variant="outlined"
|
|
55
|
-
|
|
55
|
+
class="bg-white"
|
|
56
56
|
>
|
|
57
57
|
<v-btn
|
|
58
58
|
v-for="range in rangeOptions"
|
|
@@ -60,6 +60,11 @@
|
|
|
60
60
|
:value="range.value"
|
|
61
61
|
class="text-none"
|
|
62
62
|
size="small"
|
|
63
|
+
:style="
|
|
64
|
+
selectedRange === range.value
|
|
65
|
+
? { backgroundColor: '#0b2f47', color: '#ffffff' }
|
|
66
|
+
: {}
|
|
67
|
+
"
|
|
63
68
|
>
|
|
64
69
|
{{ range.label }}
|
|
65
70
|
</v-btn>
|
|
@@ -74,9 +79,16 @@
|
|
|
74
79
|
class="mb-4"
|
|
75
80
|
/>
|
|
76
81
|
|
|
77
|
-
<v-row dense class="mb-1">
|
|
78
|
-
<v-col
|
|
79
|
-
|
|
82
|
+
<v-row dense class="mb-1 align-stretch">
|
|
83
|
+
<v-col
|
|
84
|
+
v-for="card in visibleKpiCards"
|
|
85
|
+
:key="card.key"
|
|
86
|
+
cols="12"
|
|
87
|
+
sm="6"
|
|
88
|
+
md="4"
|
|
89
|
+
class="d-flex"
|
|
90
|
+
>
|
|
91
|
+
<v-card flat border rounded="lg" class="pa-4 flex-grow-1" style="min-height: 158px">
|
|
80
92
|
<div class="d-flex align-start justify-space-between">
|
|
81
93
|
<div
|
|
82
94
|
class="d-inline-flex align-center justify-center rounded-lg"
|
|
@@ -121,39 +133,58 @@
|
|
|
121
133
|
</v-menu>
|
|
122
134
|
</div>
|
|
123
135
|
|
|
124
|
-
<div class="mt-6">
|
|
136
|
+
<div class="mt-6 w-100">
|
|
125
137
|
<p
|
|
126
138
|
class="text-caption font-weight-black text-uppercase text-blue-grey-darken-2 mb-3"
|
|
127
139
|
>
|
|
128
140
|
{{ card.title }}
|
|
129
141
|
</p>
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
card.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
>
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
142
|
+
<template
|
|
143
|
+
v-if="
|
|
144
|
+
card.key === 'supplyAlert' && Array.isArray(card.raw?.items)
|
|
145
|
+
"
|
|
146
|
+
>
|
|
147
|
+
<div class="d-flex flex-column ga-2 mt-2">
|
|
148
|
+
<div
|
|
149
|
+
v-for="item in card.raw.items"
|
|
150
|
+
:key="item._id || item.name"
|
|
151
|
+
class="d-flex justify-space-between text-body-2 text-blue-grey-darken-4 font-weight-medium"
|
|
152
|
+
>
|
|
153
|
+
<span>{{ item.name }}</span>
|
|
154
|
+
<span class="text-blue-grey">Balance: {{ item.qty }}</span>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</template>
|
|
158
|
+
<template v-else>
|
|
159
|
+
<div class="d-flex align-center mb-3" style="min-height: 31px">
|
|
160
|
+
<span
|
|
161
|
+
class="text-h5 font-weight-black text-blue-grey-darken-4"
|
|
162
|
+
>{{ card.value }}</span
|
|
163
|
+
>
|
|
164
|
+
</div>
|
|
165
|
+
<div class="d-flex align-center flex-wrap ga-2">
|
|
166
|
+
<span v-if="card.meta" class="text-caption text-blue-grey">{{
|
|
167
|
+
card.meta
|
|
168
|
+
}}</span>
|
|
169
|
+
<v-chip
|
|
170
|
+
:color="card.percentage >= 0 ? 'success' : 'error'"
|
|
171
|
+
size="x-small"
|
|
172
|
+
variant="flat"
|
|
173
|
+
class="font-weight-bold"
|
|
174
|
+
>
|
|
175
|
+
<v-icon
|
|
176
|
+
:icon="
|
|
177
|
+
card.percentage >= 0
|
|
178
|
+
? 'mdi-arrow-up-right'
|
|
179
|
+
: 'mdi-arrow-down-right'
|
|
180
|
+
"
|
|
181
|
+
size="13"
|
|
182
|
+
start
|
|
183
|
+
/>
|
|
184
|
+
{{ Math.abs(card.percentage) }}%
|
|
185
|
+
</v-chip>
|
|
186
|
+
</div>
|
|
187
|
+
</template>
|
|
157
188
|
</div>
|
|
158
189
|
</v-card>
|
|
159
190
|
</v-col>
|
|
@@ -172,19 +203,18 @@
|
|
|
172
203
|
</h2>
|
|
173
204
|
</div>
|
|
174
205
|
|
|
175
|
-
<div v-if="hasChartData" class="mt-4">
|
|
206
|
+
<div v-if="hasChartData" class="mt-4" ref="chartContainerRef">
|
|
176
207
|
<svg
|
|
177
|
-
viewBox="0 0
|
|
178
|
-
preserveAspectRatio="none"
|
|
208
|
+
:viewBox="`0 0 ${chartWidth} 260`"
|
|
179
209
|
class="d-block w-100"
|
|
180
|
-
style="height: 260px"
|
|
210
|
+
style="height: auto; max-height: 260px"
|
|
181
211
|
>
|
|
182
212
|
<line
|
|
183
213
|
v-for="tick in chartTicks"
|
|
184
214
|
:key="tick"
|
|
185
215
|
x1="48"
|
|
186
216
|
:y1="tick"
|
|
187
|
-
x2="
|
|
217
|
+
:x2="chartWidth - 20"
|
|
188
218
|
:y2="tick"
|
|
189
219
|
style="stroke: #e9eef2; stroke-width: 1"
|
|
190
220
|
/>
|
|
@@ -598,6 +628,7 @@ type DashboardMetric = {
|
|
|
598
628
|
outOfStock?: number;
|
|
599
629
|
completed?: number;
|
|
600
630
|
total?: number;
|
|
631
|
+
items?: { name: string; qty: number }[];
|
|
601
632
|
};
|
|
602
633
|
|
|
603
634
|
type DashboardListItem = {
|
|
@@ -693,13 +724,6 @@ const defaultCardValues: TDashboardCardValue[] = [
|
|
|
693
724
|
icon: "mdi-briefcase",
|
|
694
725
|
color: "#0b2f47",
|
|
695
726
|
},
|
|
696
|
-
{
|
|
697
|
-
key: "supplyAlert",
|
|
698
|
-
periodKey: "period",
|
|
699
|
-
title: "Supply Alert",
|
|
700
|
-
icon: "mdi-alert",
|
|
701
|
-
color: "#d32f2f",
|
|
702
|
-
},
|
|
703
727
|
{
|
|
704
728
|
key: "taskCompleted",
|
|
705
729
|
periodKey: "period",
|
|
@@ -707,6 +731,13 @@ const defaultCardValues: TDashboardCardValue[] = [
|
|
|
707
731
|
icon: "mdi-clipboard-check-outline",
|
|
708
732
|
color: "#f4a340",
|
|
709
733
|
},
|
|
734
|
+
{
|
|
735
|
+
key: "supplyAlert",
|
|
736
|
+
periodKey: "period",
|
|
737
|
+
title: "Supply Alert",
|
|
738
|
+
icon: "mdi-alert",
|
|
739
|
+
color: "#d32f2f",
|
|
740
|
+
},
|
|
710
741
|
];
|
|
711
742
|
|
|
712
743
|
const landscapeCardValues: TDashboardCardValue[] = [
|
|
@@ -717,13 +748,6 @@ const landscapeCardValues: TDashboardCardValue[] = [
|
|
|
717
748
|
icon: "mdi-briefcase",
|
|
718
749
|
color: "#0b2f47",
|
|
719
750
|
},
|
|
720
|
-
{
|
|
721
|
-
key: "supplyAlert",
|
|
722
|
-
periodKey: "period",
|
|
723
|
-
title: "Supply Alert",
|
|
724
|
-
icon: "mdi-alert",
|
|
725
|
-
color: "#d32f2f",
|
|
726
|
-
},
|
|
727
751
|
{
|
|
728
752
|
key: "taskCompleted",
|
|
729
753
|
periodKey: "period",
|
|
@@ -732,11 +756,11 @@ const landscapeCardValues: TDashboardCardValue[] = [
|
|
|
732
756
|
color: "#f4a340",
|
|
733
757
|
},
|
|
734
758
|
{
|
|
735
|
-
key: "
|
|
759
|
+
key: "supplyAlert",
|
|
736
760
|
periodKey: "period",
|
|
737
|
-
title: "
|
|
738
|
-
icon: "mdi-
|
|
739
|
-
color: "#
|
|
761
|
+
title: "Supply Alert",
|
|
762
|
+
icon: "mdi-alert",
|
|
763
|
+
color: "#d32f2f",
|
|
740
764
|
},
|
|
741
765
|
];
|
|
742
766
|
|
|
@@ -799,10 +823,14 @@ const { data: weeklyActivityReq, pending: loadingWeeklyActivity } =
|
|
|
799
823
|
`get-dashboard-weekly-activity-${props.site}-${props.serviceType}`,
|
|
800
824
|
() =>
|
|
801
825
|
props.serviceType
|
|
802
|
-
? getDashboardHygieneWeeklyActivity(
|
|
826
|
+
? getDashboardHygieneWeeklyActivity(
|
|
827
|
+
props.site,
|
|
828
|
+
props.serviceType,
|
|
829
|
+
currentPeriod.value
|
|
830
|
+
)
|
|
803
831
|
: Promise.resolve(null),
|
|
804
832
|
{
|
|
805
|
-
watch: [() => props.site, () => props.serviceType],
|
|
833
|
+
watch: [() => props.site, () => props.serviceType, currentPeriod],
|
|
806
834
|
}
|
|
807
835
|
);
|
|
808
836
|
|
|
@@ -862,7 +890,13 @@ const finalDashboardData = computed(() => {
|
|
|
862
890
|
let weeklyActivity = weeklyActivityReq.value ?? [];
|
|
863
891
|
const hasNoActivity =
|
|
864
892
|
!weeklyActivity.length ||
|
|
865
|
-
weeklyActivity.every(
|
|
893
|
+
weeklyActivity.every(
|
|
894
|
+
(item: any) =>
|
|
895
|
+
!item.workOrder &&
|
|
896
|
+
!item.taskCompleted &&
|
|
897
|
+
!item.openWorkOrder &&
|
|
898
|
+
!item.closedWorkOrder
|
|
899
|
+
);
|
|
866
900
|
|
|
867
901
|
if (hasNoActivity && currentPeriod.value === "thisWeek") {
|
|
868
902
|
weeklyActivity = [
|
|
@@ -895,9 +929,23 @@ const finalDashboardData = computed(() => {
|
|
|
895
929
|
percentage: 0,
|
|
896
930
|
},
|
|
897
931
|
activityChart: {
|
|
898
|
-
labels: weeklyActivity.map((item: any) => item.day),
|
|
899
|
-
workOrders: weeklyActivity.map(
|
|
900
|
-
|
|
932
|
+
labels: weeklyActivity.map((item: any) => item.day || item.label),
|
|
933
|
+
workOrders: weeklyActivity.map(
|
|
934
|
+
(item: any) => item.workOrder ?? item.openWorkOrder
|
|
935
|
+
),
|
|
936
|
+
taskCompleted: weeklyActivity.map(
|
|
937
|
+
(item: any) => item.taskCompleted ?? item.closedWorkOrder
|
|
938
|
+
),
|
|
939
|
+
openWorkOrder: weeklyActivity.some(
|
|
940
|
+
(item: any) => item.openWorkOrder !== undefined
|
|
941
|
+
)
|
|
942
|
+
? weeklyActivity.map((item: any) => item.openWorkOrder)
|
|
943
|
+
: undefined,
|
|
944
|
+
closedWorkOrder: weeklyActivity.some(
|
|
945
|
+
(item: any) => item.closedWorkOrder !== undefined
|
|
946
|
+
)
|
|
947
|
+
? weeklyActivity.map((item: any) => item.closedWorkOrder)
|
|
948
|
+
: undefined,
|
|
901
949
|
},
|
|
902
950
|
taskScheduleItems: taskScheduleReq.value?.items ?? [],
|
|
903
951
|
attendanceItems: attendanceReq.value?.items ?? [],
|
|
@@ -923,10 +971,32 @@ const snackbar = reactive({
|
|
|
923
971
|
text: "",
|
|
924
972
|
});
|
|
925
973
|
|
|
974
|
+
const chartContainerRef = ref<HTMLElement | null>(null);
|
|
975
|
+
const chartWidth = ref(920);
|
|
976
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
977
|
+
|
|
978
|
+
watch(chartContainerRef, (el) => {
|
|
979
|
+
if (resizeObserver) {
|
|
980
|
+
resizeObserver.disconnect();
|
|
981
|
+
}
|
|
982
|
+
if (el && window.ResizeObserver) {
|
|
983
|
+
resizeObserver = new ResizeObserver((entries) => {
|
|
984
|
+
if (entries[0] && entries[0].contentRect.width > 0) {
|
|
985
|
+
chartWidth.value = entries[0].contentRect.width;
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
resizeObserver.observe(el);
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
onUnmounted(() => {
|
|
993
|
+
if (resizeObserver) resizeObserver.disconnect();
|
|
994
|
+
});
|
|
995
|
+
|
|
926
996
|
const rangeOptions = [
|
|
927
997
|
{ label: "Today", value: "Today" },
|
|
928
|
-
{ label: "
|
|
929
|
-
{ label: "
|
|
998
|
+
{ label: "Week", value: "This Week" },
|
|
999
|
+
{ label: "Month", value: "This Month" },
|
|
930
1000
|
];
|
|
931
1001
|
const defaultVisitorOptions = [
|
|
932
1002
|
"All",
|
|
@@ -1078,6 +1148,7 @@ const kpiCards = computed(() =>
|
|
|
1078
1148
|
value: typeof value === "string" ? value : formatNumber(value),
|
|
1079
1149
|
percentage: toNumber(metric.percentage),
|
|
1080
1150
|
meta: getMetricMeta(metric),
|
|
1151
|
+
raw: metric,
|
|
1081
1152
|
filter:
|
|
1082
1153
|
mapping.key === "visitors" && isSecurityDashboard.value
|
|
1083
1154
|
? {
|
|
@@ -1112,8 +1183,14 @@ const chartLabels = computed<string[]>(() => {
|
|
|
1112
1183
|
|
|
1113
1184
|
if (selectedRange.value === "Today")
|
|
1114
1185
|
return ["12 AM", "4 AM", "8 AM", "12 PM", "4 PM", "8 PM"];
|
|
1115
|
-
if (selectedRange.value === "This Month")
|
|
1116
|
-
|
|
1186
|
+
if (selectedRange.value === "This Month") {
|
|
1187
|
+
const daysInMonth = new Date(
|
|
1188
|
+
new Date().getFullYear(),
|
|
1189
|
+
new Date().getMonth() + 1,
|
|
1190
|
+
0
|
|
1191
|
+
).getDate();
|
|
1192
|
+
return Array.from({ length: daysInMonth }, (_, i) => String(i + 1));
|
|
1193
|
+
}
|
|
1117
1194
|
return ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
1118
1195
|
});
|
|
1119
1196
|
const primarySeries = computed(() =>
|
|
@@ -1140,11 +1217,11 @@ const chartYLabels = computed(() => {
|
|
|
1140
1217
|
const max = chartMax.value;
|
|
1141
1218
|
return [
|
|
1142
1219
|
{ text: "0", y: 220 },
|
|
1143
|
-
{ text:
|
|
1144
|
-
{ text:
|
|
1145
|
-
{ text:
|
|
1146
|
-
{ text:
|
|
1147
|
-
{ text:
|
|
1220
|
+
{ text: `${Math.round(max * 0.2)}`, y: 181.6 },
|
|
1221
|
+
{ text: `${Math.round(max * 0.4)}`, y: 143.2 },
|
|
1222
|
+
{ text: `${Math.round(max * 0.6)}`, y: 104.8 },
|
|
1223
|
+
{ text: `${Math.round(max * 0.8)}`, y: 66.4 },
|
|
1224
|
+
{ text: `${Math.round(max)}`, y: 28 },
|
|
1148
1225
|
];
|
|
1149
1226
|
});
|
|
1150
1227
|
|
|
@@ -1170,10 +1247,11 @@ const secondaryPath = computed(() => buildSmoothPath(secondaryDots.value));
|
|
|
1170
1247
|
const chartAxisLabels = computed(() => {
|
|
1171
1248
|
const labels = chartLabels.value;
|
|
1172
1249
|
const count = Math.max(1, labels.length - 1);
|
|
1250
|
+
const width = chartWidth.value;
|
|
1173
1251
|
|
|
1174
1252
|
return labels.map((label, index) => ({
|
|
1175
1253
|
text: label,
|
|
1176
|
-
x: 54 + (
|
|
1254
|
+
x: 54 + ((width - 74) / count) * index,
|
|
1177
1255
|
}));
|
|
1178
1256
|
});
|
|
1179
1257
|
|
|
@@ -1339,18 +1417,33 @@ function normalizeActivityChart(source: any) {
|
|
|
1339
1417
|
const labels = Array.isArray(source?.labels)
|
|
1340
1418
|
? source.labels.map((label: any) => String(label))
|
|
1341
1419
|
: [];
|
|
1420
|
+
|
|
1421
|
+
const secondary = extractChartSeries(source, [
|
|
1422
|
+
"closedWorkOrder",
|
|
1423
|
+
"closedWorkOrders",
|
|
1424
|
+
"closed_work_orders",
|
|
1425
|
+
]);
|
|
1426
|
+
|
|
1427
|
+
const hasClosedSeries = secondary.length > 0;
|
|
1428
|
+
|
|
1342
1429
|
const primary = extractChartSeries(source, [
|
|
1430
|
+
"openWorkOrder",
|
|
1431
|
+
"openWorkOrders",
|
|
1432
|
+
"open_work_orders",
|
|
1343
1433
|
"workOrders",
|
|
1344
1434
|
"workOrder",
|
|
1345
1435
|
"work_orders",
|
|
1346
1436
|
]);
|
|
1347
|
-
|
|
1437
|
+
|
|
1438
|
+
const secondaryCandidate = hasClosedSeries
|
|
1439
|
+
? { label: "Closed Work Order", series: secondary }
|
|
1440
|
+
: getSecondaryChartSeries(source);
|
|
1348
1441
|
|
|
1349
1442
|
return {
|
|
1350
1443
|
labels,
|
|
1351
1444
|
primary,
|
|
1352
1445
|
secondary: secondaryCandidate.series,
|
|
1353
|
-
primaryLabel: "Work Order",
|
|
1446
|
+
primaryLabel: hasClosedSeries ? "Open Work Order" : "Work Order",
|
|
1354
1447
|
secondaryLabel: secondaryCandidate.label,
|
|
1355
1448
|
};
|
|
1356
1449
|
}
|
|
@@ -1401,9 +1494,10 @@ function normalizeSeries(value: any, targetLength: number) {
|
|
|
1401
1494
|
|
|
1402
1495
|
function buildChartDots(series: number[]) {
|
|
1403
1496
|
const count = Math.max(1, series.length - 1);
|
|
1497
|
+
const width = chartWidth.value;
|
|
1404
1498
|
|
|
1405
1499
|
return series.map((value, index) => ({
|
|
1406
|
-
x: 54 + (
|
|
1500
|
+
x: 54 + ((width - 74) / count) * index,
|
|
1407
1501
|
y: 220 - (toNumber(value) / chartMax.value) * 184,
|
|
1408
1502
|
}));
|
|
1409
1503
|
}
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
</v-col>
|
|
61
61
|
|
|
62
62
|
<!-- Attachment -->
|
|
63
|
-
<v-col cols="12" class="px-6 pt-3 pb-4">
|
|
63
|
+
<!-- <v-col cols="12" class="px-6 pt-3 pb-4">
|
|
64
64
|
<InputLabel title="Attachment (Optional)" />
|
|
65
65
|
|
|
66
66
|
<div v-if="attachmentId" class="d-flex align-center" style="gap: 8px">
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
<span v-if="attachmentError" class="text-caption text-error d-block mt-1">
|
|
131
131
|
{{ attachmentError }}
|
|
132
132
|
</span>
|
|
133
|
-
</v-col>
|
|
133
|
+
</v-col> -->
|
|
134
134
|
|
|
135
135
|
<v-col v-if="message" cols="12" class="px-6">
|
|
136
136
|
<span class="text-caption text-error">{{ message }}</span>
|