@iservice365/layer-common 1.5.2 → 1.5.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.
@@ -0,0 +1,1284 @@
1
+ <template>
2
+ <v-container fluid class="">
3
+ <!-- Header -->
4
+ <v-row no-gutters class="mb-6">
5
+ <v-col cols="12">
6
+ <h1 class="text-h4 text-md-h3 font-weight-bold">Dashboard</h1>
7
+ </v-col>
8
+ </v-row>
9
+
10
+ <!-- Count Cards -->
11
+ <v-row class="mb-4">
12
+ <v-col
13
+ v-for="card in countCardList"
14
+ :key="card.id"
15
+ cols="12"
16
+ sm="6"
17
+ md="6"
18
+ lg="4"
19
+ xl="2.4"
20
+ >
21
+ <v-card flat border class="h-100 card-hover" elevation="0">
22
+ <v-card-text class="pa-4 pa-md-6">
23
+ <v-row no-gutters class="align-start">
24
+ <v-col cols="8">
25
+ <p class="text-caption text-grey-darken-1 mb-2 text-uppercase">
26
+ {{ card.label }}
27
+ </p>
28
+ <h2 class="text-h4 text-md-h3 font-weight-bold mb-3">
29
+ {{ card.value }}
30
+ </h2>
31
+ <v-chip :color="card.chipColor" size="small" variant="flat">
32
+ <v-icon
33
+ size="14"
34
+ icon="mdi-trending-up"
35
+ class="mr-1"
36
+ ></v-icon>
37
+ {{ card.percentage }}
38
+ </v-chip>
39
+ </v-col>
40
+ <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>
44
+ </v-col>
45
+ </v-row>
46
+ <v-row no-gutters class="mt-4">
47
+ <v-col cols="12">
48
+ <v-select
49
+ :model-value="card.period"
50
+ :items="['This Week', 'This Month', 'This Year']"
51
+ density="compact"
52
+ hide-details
53
+ variant="outlined"
54
+ @update:model-value="
55
+ updateCardPeriod(card.label.toLowerCase(), $event)
56
+ "
57
+ ></v-select>
58
+ </v-col>
59
+ </v-row>
60
+ </v-card-text>
61
+ </v-card>
62
+ </v-col>
63
+ </v-row>
64
+
65
+ <!-- Line Chart -->
66
+ <v-row class="mb-4">
67
+ <v-col cols="12">
68
+ <v-card flat border elevation="0">
69
+ <v-card-title class="pa-4 pa-md-6">
70
+ <v-row no-gutters align="center">
71
+ <v-col cols="12" md="6" class="mb-4 mb-md-0">
72
+ <h3 class="text-h6 text-md-h5 font-weight-medium">
73
+ No. of Visitors
74
+ </h3>
75
+ </v-col>
76
+ <v-col cols="12" md="6" class="d-flex justify-md-end">
77
+ <v-select
78
+ v-model="visitorPeriod"
79
+ :items="['This Week', 'This Month', 'This Year']"
80
+ density="compact"
81
+ hide-details
82
+ variant="outlined"
83
+ style="max-width: 200px"
84
+ ></v-select>
85
+ </v-col>
86
+ </v-row>
87
+ </v-card-title>
88
+ <v-card-text class="pa-4 pa-md-6 pt-0">
89
+ <div
90
+ class="chart-container"
91
+ style="min-height: 280px; overflow-x: auto"
92
+ >
93
+ <svg
94
+ viewBox="0 0 1200 300"
95
+ style="min-width: 800px; width: 100%; height: 100%"
96
+ preserveAspectRatio="xMidYMid meet"
97
+ >
98
+ <!-- Y-axis labels -->
99
+ <text x="20" y="30" font-size="12" fill="#666">6</text>
100
+ <text x="20" y="80" font-size="12" fill="#666">5</text>
101
+ <text x="20" y="130" font-size="12" fill="#666">4</text>
102
+ <text x="20" y="180" font-size="12" fill="#666">3</text>
103
+ <text x="20" y="230" font-size="12" fill="#666">2</text>
104
+ <text x="20" y="280" font-size="12" fill="#666">1</text>
105
+ <text x="20" y="310" font-size="12" fill="#666">0</text>
106
+
107
+ <!-- Grid lines -->
108
+ <line
109
+ x1="60"
110
+ y1="20"
111
+ x2="1180"
112
+ y2="20"
113
+ stroke="#E0E0E0"
114
+ stroke-width="1"
115
+ />
116
+ <line
117
+ x1="60"
118
+ y1="70"
119
+ x2="1180"
120
+ y2="70"
121
+ stroke="#E0E0E0"
122
+ stroke-width="1"
123
+ />
124
+ <line
125
+ x1="60"
126
+ y1="120"
127
+ x2="1180"
128
+ y2="120"
129
+ stroke="#E0E0E0"
130
+ stroke-width="1"
131
+ />
132
+ <line
133
+ x1="60"
134
+ y1="170"
135
+ x2="1180"
136
+ y2="170"
137
+ stroke="#E0E0E0"
138
+ stroke-width="1"
139
+ />
140
+ <line
141
+ x1="60"
142
+ y1="220"
143
+ x2="1180"
144
+ y2="220"
145
+ stroke="#E0E0E0"
146
+ stroke-width="1"
147
+ />
148
+ <line
149
+ x1="60"
150
+ y1="270"
151
+ x2="1180"
152
+ y2="270"
153
+ stroke="#E0E0E0"
154
+ stroke-width="1"
155
+ />
156
+ <line
157
+ x1="60"
158
+ y1="300"
159
+ x2="1180"
160
+ y2="300"
161
+ stroke="#E0E0E0"
162
+ stroke-width="1"
163
+ />
164
+
165
+ <!-- Visitor line data -->
166
+ <polyline
167
+ :points="visitorPoints.map((p) => `${p.x},${p.y}`).join(' ')"
168
+ fill="none"
169
+ stroke="#2196F3"
170
+ stroke-width="3"
171
+ />
172
+ <!-- Data points -->
173
+ <circle
174
+ v-for="point in visitorPoints"
175
+ :key="point.x"
176
+ :cx="point.x"
177
+ :cy="point.y"
178
+ r="5"
179
+ fill="#2196F3"
180
+ />
181
+ <!-- X-axis labels -->
182
+ <text
183
+ v-for="(label, idx) in visitorLabels"
184
+ :key="idx"
185
+ :x="label.x"
186
+ y="320"
187
+ font-size="11"
188
+ fill="#666"
189
+ text-anchor="middle"
190
+ >
191
+ {{ label.text }}
192
+ </text>
193
+ </svg>
194
+ </div>
195
+ </v-card-text>
196
+ </v-card>
197
+ </v-col>
198
+ </v-row>
199
+
200
+ <!-- Bar Chart -->
201
+ <v-row class="mb-4">
202
+ <v-col cols="12">
203
+ <v-card flat border elevation="0">
204
+ <v-card-title class="pa-4 pa-md-6">
205
+ <v-row no-gutters align="center">
206
+ <v-col cols="12" md="6" class="mb-4 mb-md-0">
207
+ <h3 class="text-h6 text-md-h5 font-weight-medium">
208
+ Feedback Tickets
209
+ </h3>
210
+ </v-col>
211
+ <v-col cols="12" md="6" class="d-flex justify-md-end">
212
+ <v-select
213
+ v-model="feedbackPeriod"
214
+ :items="['This Week', 'This Month', 'This Year']"
215
+ density="compact"
216
+ hide-details
217
+ variant="outlined"
218
+ style="max-width: 200px"
219
+ ></v-select>
220
+ </v-col>
221
+ </v-row>
222
+ </v-card-title>
223
+ <v-card-text class="pa-4 pa-md-6 pt-2">
224
+ <!-- Legend -->
225
+ <div class="d-flex justify-center gap-4 mb-4">
226
+ <div class="d-flex align-center">
227
+ <div class="legend-box mr-2" style="background: #2196f3"></div>
228
+ <span class="text-caption">Resolved</span>
229
+ </div>
230
+ <div class="d-flex align-center">
231
+ <div class="legend-box mr-2" style="background: #4caf50"></div>
232
+ <span class="text-caption">Unresolved</span>
233
+ </div>
234
+ </div>
235
+
236
+ <div
237
+ class="chart-container"
238
+ style="min-height: 280px; overflow-x: auto"
239
+ >
240
+ <svg
241
+ viewBox="0 0 1200 300"
242
+ style="min-width: 800px; width: 100%; height: 100%"
243
+ preserveAspectRatio="xMidYMid meet"
244
+ >
245
+ <!-- Y-axis labels -->
246
+ <text x="20" y="20" font-size="12" fill="#999">100</text>
247
+ <text x="20" y="80" font-size="12" fill="#999">80</text>
248
+ <text x="20" y="140" font-size="12" fill="#999">60</text>
249
+ <text x="20" y="200" font-size="12" fill="#999">40</text>
250
+ <text x="20" y="260" font-size="12" fill="#999">20</text>
251
+ <text x="30" y="295" font-size="12" fill="#999">0</text>
252
+
253
+ <!-- Grid lines -->
254
+ <line
255
+ x1="60"
256
+ y1="20"
257
+ x2="1180"
258
+ y2="20"
259
+ stroke="#E0E0E0"
260
+ stroke-width="1"
261
+ />
262
+ <line
263
+ x1="60"
264
+ y1="80"
265
+ x2="1180"
266
+ y2="80"
267
+ stroke="#E0E0E0"
268
+ stroke-width="1"
269
+ />
270
+ <line
271
+ x1="60"
272
+ y1="140"
273
+ x2="1180"
274
+ y2="140"
275
+ stroke="#E0E0E0"
276
+ stroke-width="1"
277
+ />
278
+ <line
279
+ x1="60"
280
+ y1="200"
281
+ x2="1180"
282
+ y2="200"
283
+ stroke="#E0E0E0"
284
+ stroke-width="1"
285
+ />
286
+ <line
287
+ x1="60"
288
+ y1="260"
289
+ x2="1180"
290
+ y2="260"
291
+ stroke="#E0E0E0"
292
+ stroke-width="1"
293
+ />
294
+ <line
295
+ x1="60"
296
+ y1="290"
297
+ x2="1180"
298
+ y2="290"
299
+ stroke="#E0E0E0"
300
+ stroke-width="1"
301
+ />
302
+
303
+ <!-- Bars with data -->
304
+ <g v-for="(bar, idx) in feedbackBars" :key="idx">
305
+ <!-- Bar -->
306
+ <rect
307
+ :x="bar.x"
308
+ :y="bar.y"
309
+ :width="bar.width"
310
+ :height="bar.height"
311
+ :fill="bar.color"
312
+ rx="4"
313
+ />
314
+ <!-- Value label -->
315
+ <text
316
+ :x="bar.x + bar.width / 2"
317
+ :y="bar.y - 8"
318
+ font-size="14"
319
+ font-weight="bold"
320
+ fill="#333"
321
+ text-anchor="middle"
322
+ >
323
+ {{ bar.value }}
324
+ </text>
325
+ <!-- X-axis label -->
326
+ <text
327
+ :x="bar.x + bar.width / 2"
328
+ y="310"
329
+ font-size="12"
330
+ fill="#666"
331
+ text-anchor="middle"
332
+ >
333
+ {{ bar.label }}
334
+ </text>
335
+ </g>
336
+ </svg>
337
+ </div>
338
+ </v-card-text>
339
+ </v-card>
340
+ </v-col>
341
+ </v-row>
342
+
343
+ <!-- Pie Charts -->
344
+ <v-row class="mb-4">
345
+ <v-col v-for="pie in pieChartList" :key="pie.id" cols="12" md="6" lg="4">
346
+ <v-card flat border elevation="0" class="h-100">
347
+ <v-card-text class="pa-4 pa-md-6">
348
+ <!-- Header with title and total -->
349
+ <div class="mb-3">
350
+ <h3 class="text-h6 font-weight-bold mb-1">{{ pie.title }}</h3>
351
+ <p class="text-body-2 text-grey-darken-1">
352
+ Total: {{ pie.total }}
353
+ </p>
354
+ </div>
355
+
356
+ <!-- Dropdown -->
357
+ <div class="mb-4">
358
+ <v-select
359
+ :model-value="pie.period"
360
+ :items="['This Week', 'This Month', 'This Year']"
361
+ density="compact"
362
+ hide-details
363
+ variant="outlined"
364
+ @update:model-value="updatePiePeriod(pie.id, $event)"
365
+ ></v-select>
366
+ </div>
367
+
368
+ <!-- Legend -->
369
+ <div class="d-flex flex-wrap gap-3 mb-4">
370
+ <div
371
+ v-for="(segment, idx) in pie.segments"
372
+ :key="idx"
373
+ class="d-flex align-center mr-4 mb-2"
374
+ >
375
+ <div
376
+ class="legend-dot mr-2"
377
+ :style="{ backgroundColor: segment.color }"
378
+ ></div>
379
+ <span class="text-caption">{{ segment.label }}</span>
380
+ </div>
381
+ </div>
382
+
383
+ <!-- Donut Chart -->
384
+ <div class="d-flex justify-center align-center">
385
+ <div class="position-relative">
386
+ <svg viewBox="0 0 200 200" width="220" height="220">
387
+ <circle
388
+ v-for="(segment, idx) in pie.segments"
389
+ :key="idx"
390
+ cx="100"
391
+ cy="100"
392
+ r="70"
393
+ fill="none"
394
+ :stroke="segment.color"
395
+ stroke-width="45"
396
+ :stroke-dasharray="`${segment.percentage * 4.398} 439.8`"
397
+ :transform="`rotate(${segment.rotation} 100 100)`"
398
+ class="donut-segment"
399
+ />
400
+ </svg>
401
+ <!-- Center value -->
402
+ <div class="donut-center">
403
+ <p class="text-h5 font-weight-bold">{{ pie.centerValue }}</p>
404
+ </div>
405
+ </div>
406
+ </div>
407
+ </v-card-text>
408
+ </v-card>
409
+ </v-col>
410
+ </v-row>
411
+
412
+ <!-- Facility Section -->
413
+ <v-row v-if="facilityBookings.length" class="mt-4">
414
+ <v-col cols="12" class="mb-4">
415
+ <h2 class="text-h5 font-weight-bold">Facility</h2>
416
+ </v-col>
417
+ <v-col
418
+ v-for="booking in facilityBookings"
419
+ :key="booking.id"
420
+ cols="12"
421
+ sm="6"
422
+ md="6"
423
+ lg="3"
424
+ >
425
+ <v-card flat border elevation="0" class="h-100 card-hover">
426
+ <v-card-text class="pa-6">
427
+ <v-row no-gutters align="start" class="mb-3">
428
+ <v-col>
429
+ <p class="text-body-2 text-grey-darken-1 mb-1">
430
+ {{ booking.label }}
431
+ </p>
432
+ </v-col>
433
+ <v-col cols="auto">
434
+ <v-icon
435
+ :icon="booking.icon"
436
+ :color="booking.iconColor"
437
+ size="28"
438
+ ></v-icon>
439
+ </v-col>
440
+ </v-row>
441
+ <h3 class="text-h3 font-weight-bold">
442
+ {{ booking.value }}
443
+ </h3>
444
+ </v-card-text>
445
+ </v-card>
446
+ </v-col>
447
+ </v-row>
448
+ </v-container>
449
+ </template>
450
+
451
+ <script setup lang="ts">
452
+ import { ref, computed } from "vue";
453
+ import { useDisplay } from "vuetify";
454
+
455
+ const display = useDisplay();
456
+
457
+ // Type definitions
458
+ type Period = 'This Week' | 'This Month' | 'This Year';
459
+ type CardType = 'guest' | 'pickup' | 'dropoff' | 'contractor' | 'delivery';
460
+
461
+ interface CardPeriods {
462
+ guest: Period;
463
+ pickup: Period;
464
+ dropoff: Period;
465
+ contractor: Period;
466
+ delivery: Period;
467
+ }
468
+
469
+ // Loading states
470
+ const isDashboardLoading = ref(false);
471
+ const isFeedbackTicketLoading = ref(false);
472
+
473
+ // Period states for each section
474
+ const visitorPeriod = ref<Period>("This Week");
475
+ const feedbackPeriod = ref<Period>("This Week");
476
+ const bookingsPeriod = ref<Period>("This Week");
477
+ const buildingPeriod = ref<Period>("This Week");
478
+ const workOrdersPeriod = ref<Period>("This Week");
479
+
480
+ // Individual card periods
481
+ const cardPeriods = ref<CardPeriods>({
482
+ guest: "This Week",
483
+ pickup: "This Week",
484
+ dropoff: "This Week",
485
+ contractor: "This Week",
486
+ delivery: "This Week",
487
+ });
488
+
489
+ // Data sets for different periods
490
+ const dataByPeriod = {
491
+ "This Week": {
492
+ guest: { value: "156", percentage: "12.5 %", chipColor: "success" },
493
+ pickup: { value: "42", percentage: "8.3 %", chipColor: "success" },
494
+ dropoff: { value: "38", percentage: "5.2 %", chipColor: "success" },
495
+ contractor: { value: "23", percentage: "-15.4 %", chipColor: "error" },
496
+ delivery: { value: "67", percentage: "18.9 %", chipColor: "success" },
497
+ visitors: [
498
+ { x: 100, y: 220 },
499
+ { x: 200, y: 180 },
500
+ { x: 300, y: 150 },
501
+ { x: 400, y: 200 },
502
+ { x: 500, y: 140 },
503
+ { x: 600, y: 160 },
504
+ { x: 700, y: 100 },
505
+ { x: 800, y: 130 },
506
+ { x: 900, y: 90 },
507
+ { x: 1000, y: 120 },
508
+ { x: 1100, y: 80 },
509
+ ],
510
+ feedback: [
511
+ {
512
+ x: 100,
513
+ y: 272,
514
+ width: 60,
515
+ height: 18,
516
+ value: 1,
517
+ label: "1",
518
+ color: "#4CAF50",
519
+ },
520
+ {
521
+ x: 200,
522
+ y: 176,
523
+ width: 60,
524
+ height: 114,
525
+ value: 19,
526
+ label: "2",
527
+ color: "#4CAF50",
528
+ },
529
+ {
530
+ x: 300,
531
+ y: 230,
532
+ width: 60,
533
+ height: 60,
534
+ value: 10,
535
+ label: "3",
536
+ color: "#4CAF50",
537
+ },
538
+ {
539
+ x: 400,
540
+ y: 248,
541
+ width: 60,
542
+ height: 42,
543
+ value: 7,
544
+ label: "4",
545
+ color: "#4CAF50",
546
+ },
547
+ {
548
+ x: 500,
549
+ y: 260,
550
+ width: 60,
551
+ height: 30,
552
+ value: 5,
553
+ label: "5",
554
+ color: "#4CAF50",
555
+ },
556
+ {
557
+ x: 600,
558
+ y: 272,
559
+ width: 60,
560
+ height: 18,
561
+ value: 3,
562
+ label: "6",
563
+ color: "#4CAF50",
564
+ },
565
+ {
566
+ x: 700,
567
+ y: 194,
568
+ width: 60,
569
+ height: 96,
570
+ value: 16,
571
+ label: "7",
572
+ color: "#4CAF50",
573
+ },
574
+ {
575
+ x: 800,
576
+ y: 188,
577
+ width: 60,
578
+ height: 102,
579
+ value: 17,
580
+ label: "8",
581
+ color: "#4CAF50",
582
+ },
583
+ {
584
+ x: 900,
585
+ y: 32,
586
+ width: 60,
587
+ height: 258,
588
+ value: 86,
589
+ label: "9",
590
+ color: "#4CAF50",
591
+ },
592
+ ],
593
+ },
594
+ "This Month": {
595
+ guest: { value: "624", percentage: "22.3 %", chipColor: "success" },
596
+ pickup: { value: "178", percentage: "15.7 %", chipColor: "success" },
597
+ dropoff: { value: "156", percentage: "12.1 %", chipColor: "success" },
598
+ contractor: { value: "89", percentage: "-8.2 %", chipColor: "error" },
599
+ delivery: { value: "245", percentage: "28.5 %", chipColor: "success" },
600
+ visitors: [
601
+ { x: 100, y: 180 },
602
+ { x: 200, y: 150 },
603
+ { x: 300, y: 120 },
604
+ { x: 400, y: 160 },
605
+ { x: 500, y: 100 },
606
+ { x: 600, y: 130 },
607
+ { x: 700, y: 80 },
608
+ { x: 800, y: 110 },
609
+ { x: 900, y: 70 },
610
+ { x: 1000, y: 90 },
611
+ { x: 1100, y: 60 },
612
+ ],
613
+ feedback: [
614
+ {
615
+ x: 100,
616
+ y: 254,
617
+ width: 60,
618
+ height: 36,
619
+ value: 6,
620
+ label: "1",
621
+ color: "#4CAF50",
622
+ },
623
+ {
624
+ x: 200,
625
+ y: 140,
626
+ width: 60,
627
+ height: 150,
628
+ value: 25,
629
+ label: "2",
630
+ color: "#4CAF50",
631
+ },
632
+ {
633
+ x: 300,
634
+ y: 194,
635
+ width: 60,
636
+ height: 96,
637
+ value: 16,
638
+ label: "3",
639
+ color: "#4CAF50",
640
+ },
641
+ {
642
+ x: 400,
643
+ y: 212,
644
+ width: 60,
645
+ height: 78,
646
+ value: 13,
647
+ label: "4",
648
+ color: "#4CAF50",
649
+ },
650
+ {
651
+ x: 500,
652
+ y: 230,
653
+ width: 60,
654
+ height: 60,
655
+ value: 10,
656
+ label: "5",
657
+ color: "#4CAF50",
658
+ },
659
+ {
660
+ x: 600,
661
+ y: 248,
662
+ width: 60,
663
+ height: 42,
664
+ value: 7,
665
+ label: "6",
666
+ color: "#4CAF50",
667
+ },
668
+ {
669
+ x: 700,
670
+ y: 158,
671
+ width: 60,
672
+ height: 132,
673
+ value: 22,
674
+ label: "7",
675
+ color: "#4CAF50",
676
+ },
677
+ {
678
+ x: 800,
679
+ y: 176,
680
+ width: 60,
681
+ height: 114,
682
+ value: 19,
683
+ label: "8",
684
+ color: "#4CAF50",
685
+ },
686
+ {
687
+ x: 900,
688
+ y: 50,
689
+ width: 60,
690
+ height: 240,
691
+ value: 80,
692
+ label: "9",
693
+ color: "#4CAF50",
694
+ },
695
+ ],
696
+ },
697
+ "This Year": {
698
+ guest: { value: "7,842", percentage: "35.6 %", chipColor: "success" },
699
+ pickup: { value: "2,134", percentage: "28.4 %", chipColor: "success" },
700
+ dropoff: { value: "1,987", percentage: "24.9 %", chipColor: "success" },
701
+ contractor: { value: "1,045", percentage: "5.3 %", chipColor: "success" },
702
+ delivery: { value: "3,256", percentage: "42.8 %", chipColor: "success" },
703
+ visitors: [
704
+ { x: 100, y: 240 },
705
+ { x: 200, y: 210 },
706
+ { x: 300, y: 190 },
707
+ { x: 400, y: 220 },
708
+ { x: 500, y: 170 },
709
+ { x: 600, y: 200 },
710
+ { x: 700, y: 140 },
711
+ { x: 800, y: 180 },
712
+ { x: 900, y: 130 },
713
+ { x: 1000, y: 160 },
714
+ { x: 1100, y: 110 },
715
+ ],
716
+ feedback: [
717
+ {
718
+ x: 100,
719
+ y: 236,
720
+ width: 60,
721
+ height: 54,
722
+ value: 9,
723
+ label: "1",
724
+ color: "#4CAF50",
725
+ },
726
+ {
727
+ x: 200,
728
+ y: 104,
729
+ width: 60,
730
+ height: 186,
731
+ value: 31,
732
+ label: "2",
733
+ color: "#4CAF50",
734
+ },
735
+ {
736
+ x: 300,
737
+ y: 176,
738
+ width: 60,
739
+ height: 114,
740
+ value: 19,
741
+ label: "3",
742
+ color: "#4CAF50",
743
+ },
744
+ {
745
+ x: 400,
746
+ y: 194,
747
+ width: 60,
748
+ height: 96,
749
+ value: 16,
750
+ label: "4",
751
+ color: "#4CAF50",
752
+ },
753
+ {
754
+ x: 500,
755
+ y: 212,
756
+ width: 60,
757
+ height: 78,
758
+ value: 13,
759
+ label: "5",
760
+ color: "#4CAF50",
761
+ },
762
+ {
763
+ x: 600,
764
+ y: 230,
765
+ width: 60,
766
+ height: 60,
767
+ value: 10,
768
+ label: "6",
769
+ color: "#4CAF50",
770
+ },
771
+ {
772
+ x: 700,
773
+ y: 122,
774
+ width: 60,
775
+ height: 168,
776
+ value: 28,
777
+ label: "7",
778
+ color: "#4CAF50",
779
+ },
780
+ {
781
+ x: 800,
782
+ y: 158,
783
+ width: 60,
784
+ height: 132,
785
+ value: 22,
786
+ label: "8",
787
+ color: "#4CAF50",
788
+ },
789
+ {
790
+ x: 900,
791
+ y: 20,
792
+ width: 60,
793
+ height: 270,
794
+ value: 90,
795
+ label: "9",
796
+ color: "#4CAF50",
797
+ },
798
+ ],
799
+ },
800
+ };
801
+
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
+ ]);
856
+
857
+ // Computed reactive data
858
+ const visitorPoints = computed(
859
+ () => dataByPeriod[visitorPeriod.value].visitors
860
+ );
861
+
862
+ const visitorLabels = [
863
+ { x: 100, text: "Mon" },
864
+ { x: 300, text: "Tue" },
865
+ { x: 500, text: "Wed" },
866
+ { x: 700, text: "Thu" },
867
+ { x: 900, text: "Fri" },
868
+ { x: 1100, text: "Sat" },
869
+ ];
870
+
871
+ const feedbackBars = computed(
872
+ () => dataByPeriod[feedbackPeriod.value].feedback
873
+ );
874
+
875
+ // Function to update individual card periods
876
+ const updateCardPeriod = (cardType: string, period: string) => {
877
+ const validCardType = cardType as CardType;
878
+ cardPeriods.value[validCardType] = period as Period;
879
+ };
880
+
881
+ // Function to update pie chart periods
882
+ const updatePiePeriod = (pieId: number, period: string) => {
883
+ const validPeriod = period as Period;
884
+ if (pieId === 1) bookingsPeriod.value = validPeriod;
885
+ else if (pieId === 2) buildingPeriod.value = validPeriod;
886
+ else if (pieId === 3) workOrdersPeriod.value = validPeriod;
887
+ };
888
+
889
+ // Pie chart data by period
890
+ const pieChartDataByPeriod = {
891
+ "This Week": {
892
+ bookings: {
893
+ total: 186,
894
+ centerValue: "75.4%",
895
+ segments: [
896
+ {
897
+ label: "approved",
898
+ color: "#2196F3",
899
+ percentage: 14.0,
900
+ rotation: -90,
901
+ },
902
+ {
903
+ label: "rejected",
904
+ color: "#4CAF50",
905
+ percentage: 10.6,
906
+ rotation: -39.6,
907
+ },
908
+ {
909
+ label: "pending",
910
+ color: "#FFC107",
911
+ percentage: 75.4,
912
+ rotation: -1.44,
913
+ },
914
+ ],
915
+ },
916
+ building: {
917
+ total: 278,
918
+ centerValue: "98.2%",
919
+ segments: [
920
+ { label: "occupied", color: "#2196F3", percentage: 1.8, rotation: -90 },
921
+ {
922
+ label: "unoccupied",
923
+ color: "#4CAF50",
924
+ percentage: 98.2,
925
+ rotation: -83.5,
926
+ },
927
+ ],
928
+ },
929
+ workOrders: {
930
+ total: 127,
931
+ centerValue: "33.3%",
932
+ segments: [
933
+ { label: "to-do", color: "#2196F3", percentage: 33.3, rotation: -90 },
934
+ {
935
+ label: "in-progress",
936
+ color: "#4CAF50",
937
+ percentage: 33.3,
938
+ rotation: 29.9,
939
+ },
940
+ {
941
+ label: "completed",
942
+ color: "#FFC107",
943
+ percentage: 33.4,
944
+ rotation: 149.8,
945
+ },
946
+ ],
947
+ },
948
+ },
949
+ "This Month": {
950
+ bookings: {
951
+ total: 742,
952
+ centerValue: "68.2%",
953
+ segments: [
954
+ {
955
+ label: "approved",
956
+ color: "#2196F3",
957
+ percentage: 22.5,
958
+ rotation: -90,
959
+ },
960
+ {
961
+ label: "rejected",
962
+ color: "#4CAF50",
963
+ percentage: 9.3,
964
+ rotation: -9.0,
965
+ },
966
+ {
967
+ label: "pending",
968
+ color: "#FFC107",
969
+ percentage: 68.2,
970
+ rotation: 24.48,
971
+ },
972
+ ],
973
+ },
974
+ building: {
975
+ total: 278,
976
+ centerValue: "95.7%",
977
+ segments: [
978
+ { label: "occupied", color: "#2196F3", percentage: 4.3, rotation: -90 },
979
+ {
980
+ label: "unoccupied",
981
+ color: "#4CAF50",
982
+ percentage: 95.7,
983
+ rotation: -74.5,
984
+ },
985
+ ],
986
+ },
987
+ workOrders: {
988
+ total: 456,
989
+ centerValue: "42.1%",
990
+ segments: [
991
+ { label: "to-do", color: "#2196F3", percentage: 28.5, rotation: -90 },
992
+ {
993
+ label: "in-progress",
994
+ color: "#4CAF50",
995
+ percentage: 29.4,
996
+ rotation: 12.6,
997
+ },
998
+ {
999
+ label: "completed",
1000
+ color: "#FFC107",
1001
+ percentage: 42.1,
1002
+ rotation: 118.4,
1003
+ },
1004
+ ],
1005
+ },
1006
+ },
1007
+ "This Year": {
1008
+ bookings: {
1009
+ total: 8924,
1010
+ centerValue: "58.9%",
1011
+ segments: [
1012
+ {
1013
+ label: "approved",
1014
+ color: "#2196F3",
1015
+ percentage: 35.6,
1016
+ rotation: -90,
1017
+ },
1018
+ {
1019
+ label: "rejected",
1020
+ color: "#4CAF50",
1021
+ percentage: 5.5,
1022
+ rotation: 38.16,
1023
+ },
1024
+ {
1025
+ label: "pending",
1026
+ color: "#FFC107",
1027
+ percentage: 58.9,
1028
+ rotation: 57.96,
1029
+ },
1030
+ ],
1031
+ },
1032
+ building: {
1033
+ total: 278,
1034
+ centerValue: "89.2%",
1035
+ segments: [
1036
+ {
1037
+ label: "occupied",
1038
+ color: "#2196F3",
1039
+ percentage: 10.8,
1040
+ rotation: -90,
1041
+ },
1042
+ {
1043
+ label: "unoccupied",
1044
+ color: "#4CAF50",
1045
+ percentage: 89.2,
1046
+ rotation: -51.1,
1047
+ },
1048
+ ],
1049
+ },
1050
+ workOrders: {
1051
+ total: 5234,
1052
+ centerValue: "62.8%",
1053
+ segments: [
1054
+ { label: "to-do", color: "#2196F3", percentage: 18.4, rotation: -90 },
1055
+ {
1056
+ label: "in-progress",
1057
+ color: "#4CAF50",
1058
+ percentage: 18.8,
1059
+ rotation: -23.8,
1060
+ },
1061
+ {
1062
+ label: "completed",
1063
+ color: "#FFC107",
1064
+ percentage: 62.8,
1065
+ rotation: 43.8,
1066
+ },
1067
+ ],
1068
+ },
1069
+ },
1070
+ };
1071
+
1072
+ // 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
+ ]);
1102
+
1103
+ // Facility Bookings Data
1104
+ const facilityBookings = [
1105
+ {
1106
+ id: 1,
1107
+ label: "Approved Bookings",
1108
+ value: "34",
1109
+ icon: "mdi-calendar-check",
1110
+ iconColor: "success",
1111
+ },
1112
+ {
1113
+ id: 2,
1114
+ label: "Pending Bookings",
1115
+ value: "97",
1116
+ icon: "mdi-calendar-clock",
1117
+ iconColor: "warning",
1118
+ },
1119
+ {
1120
+ id: 3,
1121
+ label: "Cancelled Bookings",
1122
+ value: "28",
1123
+ icon: "mdi-calendar-remove",
1124
+ iconColor: "error",
1125
+ },
1126
+ {
1127
+ id: 4,
1128
+ label: "Rejected Bookings",
1129
+ value: "27",
1130
+ icon: "mdi-calendar-remove",
1131
+ iconColor: "error",
1132
+ },
1133
+ ];
1134
+ </script>
1135
+
1136
+ <style scoped>
1137
+ .dashboard-container {
1138
+ max-width: 100%;
1139
+ background-color: #f5f5f5;
1140
+ }
1141
+
1142
+ .card-hover {
1143
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1144
+ cursor: pointer;
1145
+ }
1146
+
1147
+ .card-hover:hover {
1148
+ transform: translateY(-4px);
1149
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1) !important;
1150
+ }
1151
+
1152
+ .chart-container {
1153
+ min-height: 250px;
1154
+ display: flex;
1155
+ align-items: center;
1156
+ justify-content: center;
1157
+ overflow-x: auto;
1158
+ -webkit-overflow-scrolling: touch;
1159
+ }
1160
+
1161
+ .h-100 {
1162
+ height: 100%;
1163
+ }
1164
+
1165
+ .w-100 {
1166
+ width: 100%;
1167
+ }
1168
+
1169
+ .position-relative {
1170
+ position: relative;
1171
+ }
1172
+
1173
+ .position-absolute {
1174
+ position: absolute;
1175
+ }
1176
+
1177
+ /* Responsive text sizing */
1178
+ @media (max-width: 600px) {
1179
+ .dashboard-container {
1180
+ padding: 12px !important;
1181
+ }
1182
+
1183
+ .chart-container {
1184
+ min-height: 250px;
1185
+ padding-bottom: 10px;
1186
+ }
1187
+
1188
+ .chart-container svg {
1189
+ min-width: 700px !important;
1190
+ }
1191
+ }
1192
+
1193
+ @media (min-width: 600px) and (max-width: 960px) {
1194
+ .chart-container {
1195
+ min-height: 260px;
1196
+ }
1197
+
1198
+ .chart-container svg {
1199
+ min-width: 800px !important;
1200
+ }
1201
+ }
1202
+
1203
+ @media (min-width: 960px) {
1204
+ .chart-container {
1205
+ min-height: 300px;
1206
+ }
1207
+
1208
+ .chart-container svg {
1209
+ min-width: auto !important;
1210
+ }
1211
+ }
1212
+
1213
+ /* SVG Animations */
1214
+ svg {
1215
+ max-width: 100%;
1216
+ height: auto;
1217
+ }
1218
+
1219
+ svg circle {
1220
+ transition: all 0.5s ease;
1221
+ }
1222
+
1223
+ svg polyline,
1224
+ svg rect {
1225
+ animation: fadeIn 1s ease-in-out;
1226
+ }
1227
+
1228
+ @keyframes fadeIn {
1229
+ from {
1230
+ opacity: 0;
1231
+ }
1232
+ to {
1233
+ opacity: 0.3;
1234
+ }
1235
+ }
1236
+
1237
+ /* Card styling improvements */
1238
+ .v-card {
1239
+ border-radius: 12px !important;
1240
+ overflow: hidden;
1241
+ }
1242
+
1243
+ .v-avatar {
1244
+ border-radius: 12px !important;
1245
+ }
1246
+
1247
+ /* Better chip styling */
1248
+ .v-chip {
1249
+ font-weight: 600;
1250
+ }
1251
+
1252
+ /* Select dropdown styling */
1253
+ .v-select {
1254
+ border-radius: 8px;
1255
+ }
1256
+
1257
+ .legend-dot {
1258
+ width: 10px;
1259
+ height: 10px;
1260
+ border-radius: 50%;
1261
+ }
1262
+
1263
+ .legend-box {
1264
+ width: 12px;
1265
+ height: 12px;
1266
+ border-radius: 2px;
1267
+ }
1268
+
1269
+ .donut-center {
1270
+ position: absolute;
1271
+ top: 50%;
1272
+ left: 50%;
1273
+ transform: translate(-50%, -50%);
1274
+ text-align: center;
1275
+ }
1276
+
1277
+ .donut-segment {
1278
+ transition: opacity 0.2s ease;
1279
+ }
1280
+
1281
+ .donut-segment:hover {
1282
+ opacity: 0.8;
1283
+ }
1284
+ </style>