@iservice365/layer-common 1.5.5 → 1.5.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.
@@ -3,7 +3,9 @@
3
3
  <!-- Header -->
4
4
  <v-row no-gutters class="mb-6">
5
5
  <v-col cols="12">
6
- <h1 class="text-h4 text-md-h3 font-weight-bold">Dashboard</h1>
6
+ <h1 class="text-h4 text-md-h3 font-weight-bold">
7
+ Dashboard{{ currentSiteName ? ` - ${currentSiteName}` : "" }}
8
+ </h1>
7
9
  </v-col>
8
10
  </v-row>
9
11
 
@@ -38,9 +40,14 @@
38
40
  </v-chip>
39
41
  </v-col>
40
42
  <v-col cols="4" class="text-right">
41
- <v-avatar :color="card.color" size="56" rounded="lg">
42
- <v-icon :icon="card.icon" color="white" size="28"></v-icon>
43
- </v-avatar>
43
+ <div class="d-flex justify-end">
44
+ <div
45
+ class="avatar-custom"
46
+ :style="{ backgroundColor: card.color }"
47
+ >
48
+ <v-icon :icon="card.icon" color="white" size="28"></v-icon>
49
+ </div>
50
+ </div>
44
51
  </v-col>
45
52
  </v-row>
46
53
  <v-row no-gutters class="mt-4">
@@ -52,7 +59,7 @@
52
59
  hide-details
53
60
  variant="outlined"
54
61
  @update:model-value="
55
- updateCardPeriod(card.label.toLowerCase(), $event)
62
+ updateCardPeriodDynamic(card.label, $event)
56
63
  "
57
64
  ></v-select>
58
65
  </v-col>
@@ -63,7 +70,7 @@
63
70
  </v-row>
64
71
 
65
72
  <!-- Line Chart -->
66
- <v-row class="mb-4">
73
+ <!-- <v-row class="mb-4">
67
74
  <v-col cols="12">
68
75
  <v-card flat border elevation="0">
69
76
  <v-card-title class="pa-4 pa-md-6">
@@ -95,7 +102,7 @@
95
102
  style="min-width: 800px; width: 100%; height: 100%"
96
103
  preserveAspectRatio="xMidYMid meet"
97
104
  >
98
- <!-- Y-axis labels -->
105
+
99
106
  <text x="20" y="30" font-size="12" fill="#666">6</text>
100
107
  <text x="20" y="80" font-size="12" fill="#666">5</text>
101
108
  <text x="20" y="130" font-size="12" fill="#666">4</text>
@@ -104,7 +111,7 @@
104
111
  <text x="20" y="280" font-size="12" fill="#666">1</text>
105
112
  <text x="20" y="310" font-size="12" fill="#666">0</text>
106
113
 
107
- <!-- Grid lines -->
114
+
108
115
  <line
109
116
  x1="60"
110
117
  y1="20"
@@ -162,14 +169,14 @@
162
169
  stroke-width="1"
163
170
  />
164
171
 
165
- <!-- Visitor line data -->
172
+
166
173
  <polyline
167
174
  :points="visitorPoints.map((p) => `${p.x},${p.y}`).join(' ')"
168
175
  fill="none"
169
176
  stroke="#2196F3"
170
177
  stroke-width="3"
171
178
  />
172
- <!-- Data points -->
179
+
173
180
  <circle
174
181
  v-for="point in visitorPoints"
175
182
  :key="point.x"
@@ -178,7 +185,7 @@
178
185
  r="5"
179
186
  fill="#2196F3"
180
187
  />
181
- <!-- X-axis labels -->
188
+
182
189
  <text
183
190
  v-for="(label, idx) in visitorLabels"
184
191
  :key="idx"
@@ -195,10 +202,10 @@
195
202
  </v-card-text>
196
203
  </v-card>
197
204
  </v-col>
198
- </v-row>
205
+ </v-row> -->
199
206
 
200
207
  <!-- Bar Chart -->
201
- <v-row class="mb-4">
208
+ <!-- <v-row class="mb-4">
202
209
  <v-col cols="12">
203
210
  <v-card flat border elevation="0">
204
211
  <v-card-title class="pa-4 pa-md-6">
@@ -208,7 +215,25 @@
208
215
  Feedback Tickets
209
216
  </h3>
210
217
  </v-col>
211
- <v-col cols="12" md="6" class="d-flex justify-md-end">
218
+ <v-col
219
+ cols="12"
220
+ md="6"
221
+ class="d-flex flex-column flex-md-row justify-md-end gap-2"
222
+ >
223
+ <v-select
224
+ v-model="selectedService"
225
+ :items="serviceList"
226
+ item-title="name"
227
+ item-value="_id"
228
+ label="Select Service"
229
+ density="compact"
230
+ hide-details
231
+ variant="outlined"
232
+ style="max-width: 200px"
233
+ clearable
234
+ class="pr-4"
235
+ @update:model-value="selectedService = $event"
236
+ ></v-select>
212
237
  <v-select
213
238
  v-model="feedbackPeriod"
214
239
  :items="['This Week', 'This Month', 'This Year']"
@@ -221,7 +246,6 @@
221
246
  </v-row>
222
247
  </v-card-title>
223
248
  <v-card-text class="pa-4 pa-md-6 pt-2">
224
- <!-- Legend -->
225
249
  <div class="d-flex justify-center gap-4 mb-4">
226
250
  <div class="d-flex align-center">
227
251
  <div class="legend-box mr-2" style="background: #2196f3"></div>
@@ -242,7 +266,6 @@
242
266
  style="min-width: 800px; width: 100%; height: 100%"
243
267
  preserveAspectRatio="xMidYMid meet"
244
268
  >
245
- <!-- Y-axis labels -->
246
269
  <text x="20" y="20" font-size="12" fill="#999">100</text>
247
270
  <text x="20" y="80" font-size="12" fill="#999">80</text>
248
271
  <text x="20" y="140" font-size="12" fill="#999">60</text>
@@ -250,7 +273,6 @@
250
273
  <text x="20" y="260" font-size="12" fill="#999">20</text>
251
274
  <text x="30" y="295" font-size="12" fill="#999">0</text>
252
275
 
253
- <!-- Grid lines -->
254
276
  <line
255
277
  x1="60"
256
278
  y1="20"
@@ -300,9 +322,7 @@
300
322
  stroke-width="1"
301
323
  />
302
324
 
303
- <!-- Bars with data -->
304
325
  <g v-for="(bar, idx) in feedbackBars" :key="idx">
305
- <!-- Bar -->
306
326
  <rect
307
327
  :x="bar.x"
308
328
  :y="bar.y"
@@ -311,7 +331,7 @@
311
331
  :fill="bar.color"
312
332
  rx="4"
313
333
  />
314
- <!-- Value label -->
334
+
315
335
  <text
316
336
  :x="bar.x + bar.width / 2"
317
337
  :y="bar.y - 8"
@@ -322,7 +342,7 @@
322
342
  >
323
343
  {{ bar.value }}
324
344
  </text>
325
- <!-- X-axis label -->
345
+
326
346
  <text
327
347
  :x="bar.x + bar.width / 2"
328
348
  y="310"
@@ -338,14 +358,14 @@
338
358
  </v-card-text>
339
359
  </v-card>
340
360
  </v-col>
341
- </v-row>
361
+ </v-row> -->
342
362
 
343
363
  <!-- Pie Charts -->
344
- <v-row class="mb-4">
364
+ <!-- <v-row class="mb-4">
345
365
  <v-col v-for="pie in pieChartList" :key="pie.id" cols="12" md="6" lg="4">
346
366
  <v-card flat border elevation="0" class="h-100">
347
367
  <v-card-text class="pa-4 pa-md-6">
348
- <!-- Header with title and total -->
368
+
349
369
  <div class="mb-3">
350
370
  <h3 class="text-h6 font-weight-bold mb-1">{{ pie.title }}</h3>
351
371
  <p class="text-body-2 text-grey-darken-1">
@@ -353,8 +373,23 @@
353
373
  </p>
354
374
  </div>
355
375
 
356
- <!-- Dropdown -->
357
- <div class="mb-4">
376
+
377
+ <div class="mb-4 d-flex flex-column gap-2">
378
+
379
+ <v-select
380
+ v-if="pie.id === 1"
381
+ v-model="selectedBooking"
382
+ :items="bookingsList"
383
+ item-title="name"
384
+ item-value="_id"
385
+ label="Select Facility"
386
+ density="compact"
387
+ hide-details
388
+ variant="outlined"
389
+ clearable
390
+ class="pb-4"
391
+ ></v-select>
392
+
358
393
  <v-select
359
394
  :model-value="pie.period"
360
395
  :items="['This Week', 'This Month', 'This Year']"
@@ -365,7 +400,7 @@
365
400
  ></v-select>
366
401
  </div>
367
402
 
368
- <!-- Legend -->
403
+
369
404
  <div class="d-flex flex-wrap gap-3 mb-4">
370
405
  <div
371
406
  v-for="(segment, idx) in pie.segments"
@@ -380,7 +415,7 @@
380
415
  </div>
381
416
  </div>
382
417
 
383
- <!-- Donut Chart -->
418
+
384
419
  <div class="d-flex justify-center align-center">
385
420
  <div class="position-relative">
386
421
  <svg viewBox="0 0 200 200" width="220" height="220">
@@ -398,7 +433,7 @@
398
433
  class="donut-segment"
399
434
  />
400
435
  </svg>
401
- <!-- Center value -->
436
+
402
437
  <div class="donut-center">
403
438
  <p class="text-h5 font-weight-bold">{{ pie.centerValue }}</p>
404
439
  </div>
@@ -407,10 +442,10 @@
407
442
  </v-card-text>
408
443
  </v-card>
409
444
  </v-col>
410
- </v-row>
445
+ </v-row> -->
411
446
 
412
447
  <!-- Facility Section -->
413
- <v-row v-if="facilityBookings.length" class="mt-4">
448
+ <!-- <v-row v-if="facilityBookings.length" class="mt-4">
414
449
  <v-col cols="12" class="mb-4">
415
450
  <h2 class="text-h5 font-weight-bold">Facility</h2>
416
451
  </v-col>
@@ -444,19 +479,44 @@
444
479
  </v-card-text>
445
480
  </v-card>
446
481
  </v-col>
447
- </v-row>
482
+ </v-row> -->
448
483
  </v-container>
449
484
  </template>
450
485
 
451
486
  <script setup lang="ts">
452
- import { ref, computed } from "vue";
453
- import { useDisplay } from "vuetify";
487
+ const { getServiceProviderNames } = useServiceProvider();
488
+ const { getAll: getAllBuildings } = useBuilding();
489
+ const { getSiteById } = useSite();
490
+ const route = useRoute();
491
+ const { APP_NAME } = useRuntimeConfig().public;
492
+
493
+ // Get siteId from route params
494
+ const siteId = computed(() => (route.params.site as string) || "");
495
+
496
+ // Reactive site data that updates when siteId changes
497
+ const siteData = ref<any>(null);
454
498
 
455
- const display = useDisplay();
499
+ // Fetch site data whenever siteId changes
500
+ watchEffect(async () => {
501
+ if (siteId.value) {
502
+ try {
503
+ const site = await getSiteById(siteId.value);
504
+ siteData.value = site;
505
+ } catch (error) {
506
+ console.error("Error fetching site:", error);
507
+ siteData.value = null;
508
+ }
509
+ } else {
510
+ siteData.value = null;
511
+ }
512
+ });
513
+
514
+ // Get current site name from fetched site data
515
+ const currentSiteName = computed(() => siteData.value?.name || "");
456
516
 
457
517
  // Type definitions
458
- type Period = 'This Week' | 'This Month' | 'This Year';
459
- type CardType = 'guest' | 'pickup' | 'dropoff' | 'contractor' | 'delivery';
518
+ type Period = "This Week" | "This Month" | "This Year";
519
+ type CardType = "guest" | "pickup" | "dropoff" | "contractor" | "delivery";
460
520
 
461
521
  interface CardPeriods {
462
522
  guest: Period;
@@ -470,6 +530,14 @@ interface CardPeriods {
470
530
  const isDashboardLoading = ref(false);
471
531
  const isFeedbackTicketLoading = ref(false);
472
532
 
533
+ // Get dashboard data based on APP_NAME
534
+ const {
535
+ cards: dashboardCards,
536
+ feedbackItems,
537
+ feedbackChartDataMap,
538
+ periodMultipliers,
539
+ } = useDashboardData(APP_NAME);
540
+
473
541
  // Period states for each section
474
542
  const visitorPeriod = ref<Period>("This Week");
475
543
  const feedbackPeriod = ref<Period>("This Week");
@@ -477,6 +545,20 @@ const bookingsPeriod = ref<Period>("This Week");
477
545
  const buildingPeriod = ref<Period>("This Week");
478
546
  const workOrdersPeriod = ref<Period>("This Week");
479
547
 
548
+ // Service provider states - now using feedback items from composable
549
+ const selectedService = ref<any>(null);
550
+ const serviceList = ref<Array<{ _id: string; name: string }>>(feedbackItems);
551
+
552
+ // Per-card periods keyed by label
553
+ const dynamicCardPeriods = ref<Record<string, Period>>({});
554
+ function updateCardPeriodDynamic(label: string, period: string) {
555
+ dynamicCardPeriods.value[label] = period as Period;
556
+ }
557
+
558
+ // Booking filter - list of facilities/buildings
559
+ const selectedBooking = ref<any>(null);
560
+ const bookingsList = ref<Array<{ _id: string; name: string }>>([]);
561
+
480
562
  // Individual card periods
481
563
  const cardPeriods = ref<CardPeriods>({
482
564
  guest: "This Week",
@@ -487,7 +569,7 @@ const cardPeriods = ref<CardPeriods>({
487
569
  });
488
570
 
489
571
  // Data sets for different periods
490
- const dataByPeriod = {
572
+ const dataByPeriod: Record<string, any> = {
491
573
  "This Week": {
492
574
  guest: { value: "156", percentage: "12.5 %", chipColor: "success" },
493
575
  pickup: { value: "42", percentage: "8.3 %", chipColor: "success" },
@@ -799,60 +881,40 @@ const dataByPeriod = {
799
881
  },
800
882
  };
801
883
 
802
- // Computed properties for reactive data
803
- const countCardList = computed(() => [
804
- {
805
- id: 1,
806
- label: "Guest",
807
- value: dataByPeriod[cardPeriods.value.guest].guest.value,
808
- icon: "mdi-account-multiple",
809
- color: "primary",
810
- percentage: dataByPeriod[cardPeriods.value.guest].guest.percentage,
811
- chipColor: dataByPeriod[cardPeriods.value.guest].guest.chipColor,
812
- period: cardPeriods.value.guest,
813
- },
814
- {
815
- id: 2,
816
- label: "Pickup",
817
- value: dataByPeriod[cardPeriods.value.pickup].pickup.value,
818
- icon: "mdi-package",
819
- color: "error",
820
- percentage: dataByPeriod[cardPeriods.value.pickup].pickup.percentage,
821
- chipColor: dataByPeriod[cardPeriods.value.pickup].pickup.chipColor,
822
- period: cardPeriods.value.pickup,
823
- },
824
- {
825
- id: 3,
826
- label: "Drop-off",
827
- value: dataByPeriod[cardPeriods.value.dropoff].dropoff.value,
828
- icon: "mdi-package-down",
829
- color: "warning",
830
- percentage: dataByPeriod[cardPeriods.value.dropoff].dropoff.percentage,
831
- chipColor: dataByPeriod[cardPeriods.value.dropoff].dropoff.chipColor,
832
- period: cardPeriods.value.dropoff,
833
- },
834
- {
835
- id: 4,
836
- label: "Contractor",
837
- value: dataByPeriod[cardPeriods.value.contractor].contractor.value,
838
- icon: "mdi-hard-hat",
839
- color: "success",
840
- percentage:
841
- dataByPeriod[cardPeriods.value.contractor].contractor.percentage,
842
- chipColor: dataByPeriod[cardPeriods.value.contractor].contractor.chipColor,
843
- period: cardPeriods.value.contractor,
844
- },
845
- {
846
- id: 5,
847
- label: "Delivery",
848
- value: dataByPeriod[cardPeriods.value.delivery].delivery.value,
849
- icon: "mdi-truck",
850
- color: "grey",
851
- percentage: dataByPeriod[cardPeriods.value.delivery].delivery.percentage,
852
- chipColor: dataByPeriod[cardPeriods.value.delivery].delivery.chipColor,
853
- period: cardPeriods.value.delivery,
854
- },
855
- ]);
884
+ // Computed properties for reactive data with site-based multiplier and APP_NAME-based cards
885
+ const countCardList = computed(() => {
886
+ // Calculate multiplier based on siteId (different sites get different multipliers)
887
+ const siteHash = siteId.value
888
+ ? siteId.value.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0)
889
+ : 0;
890
+ const siteMultiplier = siteId.value ? 0.6 + (siteHash % 40) / 100 : 1; // Between 0.6 and 1.0
891
+
892
+ const applyMultiplier = (value: number) => {
893
+ const newValue = Math.round(value * siteMultiplier);
894
+ return newValue >= 1000 ? newValue.toLocaleString() : newValue.toString();
895
+ };
896
+
897
+ // Map dashboard cards to card list format
898
+ return dashboardCards.map((card, idx) => {
899
+ const label = card.title;
900
+ const period =
901
+ dynamicCardPeriods.value[label] ||
902
+ (dynamicCardPeriods.value[label] = "This Week");
903
+ const periodFactor = periodMultipliers[period] ?? 1;
904
+ const multipliedValue = Math.round(card.value * periodFactor);
905
+
906
+ return {
907
+ id: idx + 1,
908
+ label,
909
+ value: applyMultiplier(multipliedValue),
910
+ icon: card.icon,
911
+ color: card.color,
912
+ percentage: `${card.percentage > 0 ? "+" : ""}${card.percentage} %`,
913
+ chipColor: card.isPositive ? "success" : "error",
914
+ period,
915
+ };
916
+ });
917
+ });
856
918
 
857
919
  // Computed reactive data
858
920
  const visitorPoints = computed(
@@ -868,9 +930,21 @@ const visitorLabels = [
868
930
  { x: 1100, text: "Sat" },
869
931
  ];
870
932
 
871
- const feedbackBars = computed(
872
- () => dataByPeriod[feedbackPeriod.value].feedback
873
- );
933
+ const feedbackBars = computed(() => {
934
+ // If no service is selected, return the base data
935
+ if (!selectedService.value) {
936
+ return dataByPeriod[feedbackPeriod.value].feedback;
937
+ }
938
+
939
+ // If a service is selected, use the feedback chart data from composable
940
+ const chartData = feedbackChartDataMap[selectedService.value];
941
+ if (chartData && chartData[feedbackPeriod.value]) {
942
+ return chartData[feedbackPeriod.value];
943
+ }
944
+
945
+ // Fallback to base data if service data not found
946
+ return dataByPeriod[feedbackPeriod.value].feedback;
947
+ });
874
948
 
875
949
  // Function to update individual card periods
876
950
  const updateCardPeriod = (cardType: string, period: string) => {
@@ -886,6 +960,19 @@ const updatePiePeriod = (pieId: number, period: string) => {
886
960
  else if (pieId === 3) workOrdersPeriod.value = validPeriod;
887
961
  };
888
962
 
963
+ // Function to fetch bookings data based on selected facility
964
+ const fetchBookingsData = async () => {
965
+ if (!selectedBooking.value) {
966
+ // Reset to base data when no booking selected
967
+ return;
968
+ }
969
+ };
970
+
971
+ // Watch for booking selection changes
972
+ watch(selectedBooking, () => {
973
+ fetchBookingsData();
974
+ });
975
+
889
976
  // Pie chart data by period
890
977
  const pieChartDataByPeriod = {
891
978
  "This Week": {
@@ -1070,67 +1157,186 @@ const pieChartDataByPeriod = {
1070
1157
  };
1071
1158
 
1072
1159
  // Computed Pie Charts Data
1073
- const pieChartList = computed(() => [
1074
- {
1075
- id: 1,
1076
- title: "Bookings",
1077
- total: pieChartDataByPeriod[bookingsPeriod.value].bookings.total,
1078
- centerValue:
1079
- pieChartDataByPeriod[bookingsPeriod.value].bookings.centerValue,
1080
- segments: pieChartDataByPeriod[bookingsPeriod.value].bookings.segments,
1081
- period: bookingsPeriod.value,
1082
- },
1083
- {
1084
- id: 2,
1085
- title: "Building Mngm.",
1086
- total: pieChartDataByPeriod[buildingPeriod.value].building.total,
1087
- centerValue:
1088
- pieChartDataByPeriod[buildingPeriod.value].building.centerValue,
1089
- segments: pieChartDataByPeriod[buildingPeriod.value].building.segments,
1090
- period: buildingPeriod.value,
1091
- },
1092
- {
1093
- id: 3,
1094
- title: "Work Orders",
1095
- total: pieChartDataByPeriod[workOrdersPeriod.value].workOrders.total,
1096
- centerValue:
1097
- pieChartDataByPeriod[workOrdersPeriod.value].workOrders.centerValue,
1098
- segments: pieChartDataByPeriod[workOrdersPeriod.value].workOrders.segments,
1099
- period: workOrdersPeriod.value,
1100
- },
1101
- ]);
1160
+ const pieChartList = computed(() => {
1161
+ // Get base bookings data
1162
+ const baseBookingsData = pieChartDataByPeriod[bookingsPeriod.value].bookings;
1163
+ let bookingsData = { ...baseBookingsData };
1164
+
1165
+ if (selectedBooking.value) {
1166
+ const bookingHash = selectedBooking.value
1167
+ .split("")
1168
+ .reduce((acc, char) => acc + char.charCodeAt(0), 0);
1169
+ const multiplier = 0.35 + (bookingHash % 45) / 100; // Between 0.35 and 0.8
1170
+ const newTotal = Math.round(baseBookingsData.total * multiplier);
1171
+
1172
+ const segmentMultipliers = [
1173
+ 0.8 + (bookingHash % 15) / 100, // approved: 0.8-0.95
1174
+ 0.4 + (bookingHash % 30) / 100, // rejected: 0.4-0.7
1175
+ 0.5 + (bookingHash % 25) / 100, // pending: 0.5-0.75
1176
+ ];
1177
+
1178
+ const newSegments = baseBookingsData.segments.map(
1179
+ (seg: any, idx: number) => {
1180
+ const segMultiplier = segmentMultipliers[idx] || multiplier;
1181
+ const newPercentage = seg.percentage * segMultiplier;
1182
+ return {
1183
+ ...seg,
1184
+ percentage: newPercentage,
1185
+ };
1186
+ }
1187
+ );
1188
+
1189
+ // Normalize percentages to total 100% (make full circle)
1190
+ const totalPercentage = newSegments.reduce(
1191
+ (sum: number, seg: any) => sum + seg.percentage,
1192
+ 0
1193
+ );
1194
+ const normalizedSegments = newSegments.map((seg: any) => ({
1195
+ ...seg,
1196
+ percentage: (seg.percentage / totalPercentage) * 100,
1197
+ }));
1198
+
1199
+ // Recalculate rotations based on normalized percentages
1200
+ let currentRotation = -90;
1201
+ const adjustedSegments = normalizedSegments.map((seg: any) => {
1202
+ const segmentWithRotation = {
1203
+ ...seg,
1204
+ rotation: currentRotation,
1205
+ };
1206
+ currentRotation += seg.percentage * 3.6; // 360 degrees / 100 percentage
1207
+ return segmentWithRotation;
1208
+ });
1209
+
1210
+ // Calculate new center value based on the largest segment
1211
+ const maxSegment = adjustedSegments.reduce((max: any, seg: any) =>
1212
+ seg.percentage > max.percentage ? seg : max
1213
+ );
1214
+ const newCenterValue = `${Math.round(maxSegment.percentage)}%`;
1102
1215
 
1103
- // Facility Bookings Data
1104
- const facilityBookings = [
1216
+ bookingsData = {
1217
+ total: newTotal,
1218
+ centerValue: newCenterValue,
1219
+ segments: adjustedSegments,
1220
+ };
1221
+ }
1222
+
1223
+ return [
1224
+ {
1225
+ id: 1,
1226
+ title: "Bookings",
1227
+ total: bookingsData.total,
1228
+ centerValue: bookingsData.centerValue,
1229
+ segments: bookingsData.segments,
1230
+ period: bookingsPeriod.value,
1231
+ },
1232
+ {
1233
+ id: 2,
1234
+ title: "Building Mngm.",
1235
+ total: pieChartDataByPeriod[buildingPeriod.value].building.total,
1236
+ centerValue:
1237
+ pieChartDataByPeriod[buildingPeriod.value].building.centerValue,
1238
+ segments: pieChartDataByPeriod[buildingPeriod.value].building.segments,
1239
+ period: buildingPeriod.value,
1240
+ },
1241
+ {
1242
+ id: 3,
1243
+ title: "Work Orders",
1244
+ total: pieChartDataByPeriod[workOrdersPeriod.value].workOrders.total,
1245
+ centerValue:
1246
+ pieChartDataByPeriod[workOrdersPeriod.value].workOrders.centerValue,
1247
+ segments:
1248
+ pieChartDataByPeriod[workOrdersPeriod.value].workOrders.segments,
1249
+ period: workOrdersPeriod.value,
1250
+ },
1251
+ ];
1252
+ });
1253
+
1254
+ // Function to fetch feedback data based on selected service
1255
+ const fetchFeedbackData = async () => {
1256
+ if (!selectedService.value) {
1257
+ // Reset to base data when no service selected
1258
+ return;
1259
+ }
1260
+ };
1261
+
1262
+ // Watch for service selection changes
1263
+ watch(selectedService, () => {
1264
+ fetchFeedbackData();
1265
+ });
1266
+
1267
+ // Fetch buildings/facilities on mount
1268
+ onMounted(async () => {
1269
+ try {
1270
+ // Fetch buildings/facilities for bookings dropdown
1271
+ // Using buildings as facilities - you can adjust to use actual facilities API
1272
+ const buildingsResponse = await getAllBuildings({
1273
+ limit: 100,
1274
+ status: "active", // Only show active buildings
1275
+ });
1276
+ if (buildingsResponse && buildingsResponse.items) {
1277
+ bookingsList.value = buildingsResponse.items.map((building: any) => ({
1278
+ _id: building._id,
1279
+ name: building.name || building.block || `Building ${building._id}`,
1280
+ }));
1281
+ }
1282
+ } catch (error) {
1283
+ console.error("Error fetching data:", error);
1284
+ }
1285
+ });
1286
+
1287
+ // Base Facility Bookings Data
1288
+ const baseFacilityBookings = [
1105
1289
  {
1106
1290
  id: 1,
1107
1291
  label: "Approved Bookings",
1108
- value: "34",
1292
+ value: 34,
1109
1293
  icon: "mdi-calendar-check",
1110
1294
  iconColor: "success",
1111
1295
  },
1112
1296
  {
1113
1297
  id: 2,
1114
1298
  label: "Pending Bookings",
1115
- value: "97",
1299
+ value: 97,
1116
1300
  icon: "mdi-calendar-clock",
1117
1301
  iconColor: "warning",
1118
1302
  },
1119
1303
  {
1120
1304
  id: 3,
1121
1305
  label: "Cancelled Bookings",
1122
- value: "28",
1306
+ value: 28,
1123
1307
  icon: "mdi-calendar-remove",
1124
1308
  iconColor: "error",
1125
1309
  },
1126
1310
  {
1127
1311
  id: 4,
1128
1312
  label: "Rejected Bookings",
1129
- value: "27",
1313
+ value: 27,
1130
1314
  icon: "mdi-calendar-remove",
1131
1315
  iconColor: "error",
1132
1316
  },
1133
1317
  ];
1318
+
1319
+ // Computed Facility Bookings - changes based on selected facility
1320
+ const facilityBookings = computed(() => {
1321
+ // If no facility is selected, return base data
1322
+ if (!selectedBooking.value) {
1323
+ return baseFacilityBookings.map((booking) => ({
1324
+ ...booking,
1325
+ value: booking.value.toString(),
1326
+ }));
1327
+ }
1328
+
1329
+ // If a facility is selected, multiply values
1330
+ const bookingHash = selectedBooking.value
1331
+ .split("")
1332
+ .reduce((acc, char) => acc + char.charCodeAt(0), 0);
1333
+ const multiplier = 0.35 + (bookingHash % 45) / 100; // Between 0.35 and 0.8
1334
+
1335
+ return baseFacilityBookings.map((booking) => ({
1336
+ ...booking,
1337
+ value: Math.round(booking.value * multiplier).toString(),
1338
+ }));
1339
+ });
1134
1340
  </script>
1135
1341
 
1136
1342
  <style scoped>
@@ -1244,6 +1450,16 @@ svg rect {
1244
1450
  border-radius: 12px !important;
1245
1451
  }
1246
1452
 
1453
+ .avatar-custom {
1454
+ width: 56px;
1455
+ height: 56px;
1456
+ border-radius: 12px;
1457
+ display: flex;
1458
+ align-items: center;
1459
+ justify-content: center;
1460
+ flex-shrink: 0;
1461
+ }
1462
+
1247
1463
  /* Better chip styling */
1248
1464
  .v-chip {
1249
1465
  font-weight: 600;