@7365admin1/layer-common 3.0.6 → 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.
@@ -52,7 +52,7 @@
52
52
  density="comfortable"
53
53
  divided
54
54
  variant="outlined"
55
- color="blue-grey-darken-4"
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 v-for="card in visibleKpiCards" :key="card.key" cols="12" sm="6">
79
- <v-card flat border rounded="lg" class="pa-4" style="min-height: 158px">
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
- <div class="d-flex align-center mb-3" style="min-height: 31px">
131
- <span class="text-h5 font-weight-black text-blue-grey-darken-4">{{
132
- card.value
133
- }}</span>
134
- </div>
135
- <div class="d-flex align-center flex-wrap ga-2">
136
- <span v-if="card.meta" class="text-caption text-blue-grey">{{
137
- card.meta
138
- }}</span>
139
- <v-chip
140
- :color="card.percentage >= 0 ? 'success' : 'error'"
141
- size="x-small"
142
- variant="flat"
143
- class="font-weight-bold"
144
- >
145
- <v-icon
146
- :icon="
147
- card.percentage >= 0
148
- ? 'mdi-arrow-up-right'
149
- : 'mdi-arrow-down-right'
150
- "
151
- size="13"
152
- start
153
- />
154
- {{ Math.abs(card.percentage) }}%
155
- </v-chip>
156
- </div>
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 920 260"
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="900"
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: "landscapeSchedule",
759
+ key: "supplyAlert",
736
760
  periodKey: "period",
737
- title: "Landscape Schedule",
738
- icon: "mdi-sprinkler-variant",
739
- color: "#4caf50",
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(props.site, props.serviceType)
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((item: any) => !item.workOrder && !item.taskCompleted);
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((item: any) => item.workOrder),
900
- taskCompleted: weeklyActivity.map((item: any) => item.taskCompleted),
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: "7d", value: "This Week" },
929
- { label: "30d", value: "This Month" },
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
- return ["Week 1", "Week 2", "Week 3", "Week 4"];
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: `$${Math.round(max * 0.2)}`, y: 181.6 },
1144
- { text: `$${Math.round(max * 0.4)}`, y: 143.2 },
1145
- { text: `$${Math.round(max * 0.6)}`, y: 104.8 },
1146
- { text: `$${Math.round(max * 0.8)}`, y: 66.4 },
1147
- { text: `$${Math.round(max)}`, y: 28 },
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 + (846 / count) * index,
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
- const secondaryCandidate = getSecondaryChartSeries(source);
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 + (846 / count) * index,
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>