adminator-admin-dashboard 2.7.0 → 2.7.1

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,1350 @@
1
+ /**
2
+ * Modern Chart Component with TypeScript
3
+ * Replaces jQuery Sparkline with Chart.js
4
+ */
5
+
6
+ import { Chart, ChartConfiguration, registerables, ChartType as ChartJSType } from 'chart.js';
7
+ import type { ComponentInterface } from '../../../types';
8
+ import { COLORS } from '../constants/colors';
9
+
10
+ // Register Chart.js components
11
+ Chart.register(...registerables);
12
+
13
+ export interface SparklineConfig {
14
+ id: string;
15
+ data: number[];
16
+ color: string;
17
+ }
18
+
19
+ export interface ChartComponentOptions {
20
+ enableResize?: boolean;
21
+ resizeDebounceMs?: number;
22
+ enableAnimation?: boolean;
23
+ }
24
+
25
+ export interface ChartDimensions {
26
+ width: number;
27
+ height: number;
28
+ }
29
+
30
+ export type ChartElementType = 'sparkline' | 'sparkbar' | 'sparktri' | 'sparkdisc' | 'sparkbull' | 'sparkbox' | 'easypie';
31
+
32
+ export class ChartComponent implements ComponentInterface {
33
+ public name: string = 'ChartComponent';
34
+ public element: HTMLElement;
35
+ public options: ChartComponentOptions;
36
+ public isInitialized: boolean = false;
37
+
38
+ private charts: Map<string, Chart> = new Map();
39
+ private debounceTimer: number | null = null;
40
+
41
+ constructor(element?: HTMLElement, options: ChartComponentOptions = {}) {
42
+ this.element = element || document.body;
43
+ this.options = {
44
+ enableResize: true,
45
+ resizeDebounceMs: 150,
46
+ enableAnimation: true,
47
+ ...options,
48
+ };
49
+
50
+ this.init();
51
+ }
52
+
53
+ /**
54
+ * Initialize the chart component
55
+ */
56
+ public init(): void {
57
+ // Only disable resizing for small sparkline charts
58
+ this.createSparklines();
59
+ this.createOtherCharts();
60
+
61
+ if (this.options.enableResize) {
62
+ this.setupResizeHandler();
63
+ }
64
+
65
+ this.isInitialized = true;
66
+ }
67
+
68
+ /**
69
+ * Destroy the chart component
70
+ */
71
+ public destroy(): void {
72
+ this.charts.forEach(chart => {
73
+ chart.destroy();
74
+ });
75
+ this.charts.clear();
76
+
77
+ if (this.debounceTimer) {
78
+ clearTimeout(this.debounceTimer);
79
+ this.debounceTimer = null;
80
+ }
81
+
82
+ this.isInitialized = false;
83
+ }
84
+
85
+ /**
86
+ * Create sparklines (only for dashboard page)
87
+ */
88
+ private createSparklines(): void {
89
+ // Only create sparklines if we're on a page that has them
90
+ const sparklineExists = document.getElementById('sparklinedash');
91
+ if (!sparklineExists) {
92
+ return;
93
+ }
94
+
95
+ const sparklineConfigs: SparklineConfig[] = [
96
+ {
97
+ id: 'sparklinedash',
98
+ data: [0, 5, 6, 10, 9, 12, 4, 9],
99
+ color: '#4caf50',
100
+ },
101
+ {
102
+ id: 'sparklinedash2',
103
+ data: [0, 5, 6, 10, 9, 12, 4, 9],
104
+ color: '#9675ce',
105
+ },
106
+ {
107
+ id: 'sparklinedash3',
108
+ data: [0, 5, 6, 10, 9, 12, 4, 9],
109
+ color: '#03a9f3',
110
+ },
111
+ {
112
+ id: 'sparklinedash4',
113
+ data: [0, 5, 6, 10, 9, 12, 4, 9],
114
+ color: '#f96262',
115
+ },
116
+ ];
117
+
118
+ sparklineConfigs.forEach(config => {
119
+ // Only create if the target element exists
120
+ if (document.getElementById(config.id)) {
121
+ this.createSparklineChart(config);
122
+ }
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Create sparkline chart from configuration
128
+ */
129
+ private createSparklineChart(config: SparklineConfig): void {
130
+ let canvas = document.getElementById(config.id) as HTMLCanvasElement;
131
+
132
+ // Only proceed if we have a valid target element
133
+ if (!canvas) {
134
+ return;
135
+ }
136
+
137
+ // If element exists but isn't a canvas, replace it with canvas
138
+ if (canvas.tagName !== 'CANVAS') {
139
+ const parent = canvas.parentNode;
140
+ if (!parent) {
141
+ return;
142
+ }
143
+
144
+ // Create new canvas element
145
+ const newCanvas = document.createElement('canvas');
146
+ newCanvas.id = config.id;
147
+ this.setCanvasDimensions(newCanvas, { width: 100, height: 20 });
148
+
149
+ // Replace the span with canvas
150
+ parent.replaceChild(newCanvas, canvas);
151
+ canvas = newCanvas;
152
+ } else {
153
+ // Set canvas dimensions to match original sparkline
154
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
155
+ }
156
+
157
+ const ctx = canvas.getContext('2d');
158
+ if (!ctx) return;
159
+
160
+ const chartConfig: ChartConfiguration = {
161
+ type: 'bar',
162
+ data: {
163
+ labels: config.data.map((_, i) => i.toString()),
164
+ datasets: [{
165
+ data: config.data,
166
+ backgroundColor: config.color,
167
+ borderColor: config.color,
168
+ borderWidth: 0,
169
+ barPercentage: 0.6,
170
+ categoryPercentage: 0.8,
171
+ }],
172
+ },
173
+ options: {
174
+ responsive: false,
175
+ maintainAspectRatio: false,
176
+ animation: this.options.enableAnimation ? {} : false,
177
+ events: [],
178
+ scales: {
179
+ x: {
180
+ display: false,
181
+ },
182
+ y: {
183
+ display: false,
184
+ },
185
+ },
186
+ plugins: {
187
+ legend: {
188
+ display: false,
189
+ },
190
+ tooltip: {
191
+ enabled: false,
192
+ },
193
+ },
194
+ elements: {
195
+ bar: {
196
+ borderRadius: 1,
197
+ },
198
+ },
199
+ },
200
+ };
201
+
202
+ const chart = new Chart(ctx, chartConfig);
203
+ this.charts.set(config.id, chart);
204
+ }
205
+
206
+ /**
207
+ * Set canvas dimensions
208
+ */
209
+ private setCanvasDimensions(canvas: HTMLCanvasElement, dimensions: ChartDimensions): void {
210
+ canvas.width = dimensions.width;
211
+ canvas.height = dimensions.height;
212
+ canvas.style.width = `${dimensions.width}px`;
213
+ canvas.style.height = `${dimensions.height}px`;
214
+ }
215
+
216
+ /**
217
+ * Create other chart types (only if they exist on the page)
218
+ */
219
+ private createOtherCharts(): void {
220
+ // Determine if we're on the dashboard or charts page
221
+ const isChartsPage = document.getElementById('area-chart') !== null;
222
+ const isDashboard = !isChartsPage && document.getElementById('line-chart') !== null;
223
+
224
+ // Create Monthly Stats chart with enhanced dual-line data (dashboard only)
225
+ if (isDashboard) {
226
+ this.createMonthlyStatsChart();
227
+ }
228
+
229
+ // Charts page specific charts (only on charts page)
230
+ if (isChartsPage) {
231
+ this.createChartsPageCharts();
232
+ }
233
+
234
+ // Only create charts if their target elements exist
235
+ if (document.getElementById('sparkline')) {
236
+ this.createLineChart('sparkline', [5, 6, 7, 9, 9, 5, 3, 2, 2, 4, 6, 7]);
237
+ }
238
+
239
+ if (document.getElementById('compositebar')) {
240
+ this.createCompositeChart('compositebar', [4, 1, 5, 7, 9, 9, 8, 7, 6, 6, 4, 7, 8, 4, 3, 2, 2, 5, 6, 7]);
241
+ }
242
+
243
+ // Regular sparklines with custom colors (only on pages that have them)
244
+ this.createCustomSparklines();
245
+
246
+ // Easy Pie Charts (only if they exist)
247
+ this.createEasyPieCharts();
248
+ }
249
+
250
+ /**
251
+ * Create enhanced Monthly Stats chart with dual lines and more data
252
+ */
253
+ private createMonthlyStatsChart(): void {
254
+ const canvas = document.getElementById('line-chart') as HTMLCanvasElement;
255
+ if (!canvas) return;
256
+
257
+ const ctx = canvas.getContext('2d');
258
+ if (!ctx) return;
259
+
260
+ // Enhanced data for monthly stats
261
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
262
+ const salesData = [120, 135, 145, 165, 180, 195, 210, 225, 240, 220, 200, 185];
263
+ const profitData = [45, 52, 58, 62, 68, 75, 82, 88, 92, 85, 78, 72];
264
+
265
+ const chartConfig: ChartConfiguration = {
266
+ type: 'line',
267
+ data: {
268
+ labels: months,
269
+ datasets: [
270
+ {
271
+ label: 'Sales ($K)',
272
+ data: salesData,
273
+ borderColor: '#4caf50',
274
+ backgroundColor: 'rgba(76, 175, 80, 0.1)',
275
+ borderWidth: 3,
276
+ pointRadius: 5,
277
+ pointHoverRadius: 7,
278
+ pointBackgroundColor: '#4caf50',
279
+ pointBorderColor: '#ffffff',
280
+ pointBorderWidth: 2,
281
+ tension: 0.4,
282
+ fill: false,
283
+ },
284
+ {
285
+ label: 'Profit ($K)',
286
+ data: profitData,
287
+ borderColor: '#2196f3',
288
+ backgroundColor: 'rgba(33, 150, 243, 0.1)',
289
+ borderWidth: 3,
290
+ pointRadius: 5,
291
+ pointHoverRadius: 7,
292
+ pointBackgroundColor: '#2196f3',
293
+ pointBorderColor: '#ffffff',
294
+ pointBorderWidth: 2,
295
+ tension: 0.4,
296
+ fill: false,
297
+ },
298
+ ],
299
+ },
300
+ options: {
301
+ responsive: true,
302
+ maintainAspectRatio: false,
303
+ plugins: {
304
+ legend: {
305
+ display: true,
306
+ position: 'top',
307
+ labels: {
308
+ padding: 20,
309
+ font: {
310
+ size: 12,
311
+ weight: 600,
312
+ },
313
+ },
314
+ },
315
+ tooltip: {
316
+ enabled: true,
317
+ cornerRadius: 8,
318
+ displayColors: true,
319
+ intersect: false,
320
+ mode: 'index',
321
+ callbacks: {
322
+ label(context) {
323
+ return `${context.dataset.label}: $${context.parsed.y}K`;
324
+ },
325
+ },
326
+ },
327
+ },
328
+ scales: {
329
+ x: {
330
+ grid: {
331
+ display: false,
332
+ },
333
+ ticks: {
334
+ font: {
335
+ size: 11,
336
+ },
337
+ },
338
+ },
339
+ y: {
340
+ beginAtZero: true,
341
+ grid: {
342
+ borderDash: [5, 5] as [number, number],
343
+ },
344
+ ticks: {
345
+ font: {
346
+ size: 11,
347
+ },
348
+ callback(value) {
349
+ return `$${value}K`;
350
+ },
351
+ },
352
+ },
353
+ },
354
+ interaction: {
355
+ intersect: false,
356
+ mode: 'index',
357
+ },
358
+ },
359
+ };
360
+
361
+ const chart = new Chart(ctx, chartConfig);
362
+ this.charts.set('line-chart', chart);
363
+ }
364
+
365
+ /**
366
+ * Create line chart (only if target exists)
367
+ */
368
+ private createLineChart(id: string, data: number[]): void {
369
+ let canvas = document.getElementById(id) as HTMLCanvasElement;
370
+
371
+ // Only proceed if target element exists
372
+ if (!canvas) {
373
+ return;
374
+ }
375
+
376
+ // If element exists but isn't a canvas, replace it with canvas
377
+ if (canvas.tagName !== 'CANVAS') {
378
+ const parent = canvas.parentNode;
379
+ if (!parent) {
380
+ return;
381
+ }
382
+
383
+ // Create new canvas element
384
+ const newCanvas = document.createElement('canvas');
385
+ newCanvas.id = id;
386
+ this.setCanvasDimensions(newCanvas, { width: 100, height: 20 });
387
+
388
+ // Replace element with canvas
389
+ parent.replaceChild(newCanvas, canvas);
390
+ canvas = newCanvas;
391
+ } else {
392
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
393
+ }
394
+
395
+ const ctx = canvas.getContext('2d');
396
+ if (!ctx) return;
397
+
398
+ const chartConfig: ChartConfiguration = {
399
+ type: 'line',
400
+ data: {
401
+ labels: data.map((_, i) => i.toString()),
402
+ datasets: [{
403
+ data,
404
+ borderColor: COLORS['blue-500'],
405
+ backgroundColor: 'transparent',
406
+ borderWidth: 1,
407
+ pointRadius: 0,
408
+ tension: 0.4,
409
+ }],
410
+ },
411
+ options: {
412
+ responsive: false,
413
+ maintainAspectRatio: false,
414
+ animation: false,
415
+ events: [],
416
+ scales: {
417
+ x: { display: false },
418
+ y: { display: false },
419
+ },
420
+ plugins: {
421
+ legend: { display: false },
422
+ tooltip: { enabled: false },
423
+ },
424
+ },
425
+ };
426
+
427
+ const chart = new Chart(ctx, chartConfig);
428
+ this.charts.set(id, chart);
429
+ }
430
+
431
+ /**
432
+ * Create composite chart (only if target exists)
433
+ */
434
+ private createCompositeChart(id: string, data: number[]): void {
435
+ let canvas = document.getElementById(id) as HTMLCanvasElement;
436
+
437
+ // Only proceed if target element exists
438
+ if (!canvas) {
439
+ return;
440
+ }
441
+
442
+ // If element exists but isn't a canvas, replace it with canvas
443
+ if (canvas.tagName !== 'CANVAS') {
444
+ const parent = canvas.parentNode;
445
+ if (!parent) {
446
+ return;
447
+ }
448
+
449
+ // Create new canvas element
450
+ const newCanvas = document.createElement('canvas');
451
+ newCanvas.id = id;
452
+ this.setCanvasDimensions(newCanvas, { width: 100, height: 20 });
453
+
454
+ // Replace element with canvas
455
+ parent.replaceChild(newCanvas, canvas);
456
+ canvas = newCanvas;
457
+ } else {
458
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
459
+ }
460
+
461
+ const ctx = canvas.getContext('2d');
462
+ if (!ctx) return;
463
+
464
+ const chartConfig: ChartConfiguration = {
465
+ type: 'bar',
466
+ data: {
467
+ labels: data.map((_, i) => i.toString()),
468
+ datasets: [
469
+ {
470
+ type: 'bar',
471
+ data,
472
+ backgroundColor: '#aaf',
473
+ borderColor: '#aaf',
474
+ borderWidth: 0,
475
+ },
476
+ {
477
+ type: 'line',
478
+ data,
479
+ borderColor: 'red',
480
+ backgroundColor: 'transparent',
481
+ borderWidth: 1,
482
+ pointRadius: 0,
483
+ tension: 0.4,
484
+ } as any, // Type assertion needed for mixed chart types
485
+ ],
486
+ },
487
+ options: {
488
+ responsive: false,
489
+ maintainAspectRatio: false,
490
+ animation: false,
491
+ events: [],
492
+ scales: {
493
+ x: { display: false },
494
+ y: { display: false },
495
+ },
496
+ plugins: {
497
+ legend: { display: false },
498
+ tooltip: { enabled: false },
499
+ },
500
+ },
501
+ };
502
+
503
+ const chart = new Chart(ctx, chartConfig);
504
+ this.charts.set(id, chart);
505
+ }
506
+
507
+ /**
508
+ * Create custom sparklines for different elements (only if they exist)
509
+ */
510
+ private createCustomSparklines(): void {
511
+ const sparklineElements = document.querySelectorAll('.sparkline');
512
+ const sparkbarElements = document.querySelectorAll('.sparkbar');
513
+ const sparktriElements = document.querySelectorAll('.sparktri');
514
+ const sparkdiscElements = document.querySelectorAll('.sparkdisc');
515
+ const sparkbullElements = document.querySelectorAll('.sparkbull');
516
+ const sparkboxElements = document.querySelectorAll('.sparkbox');
517
+
518
+ // Only create if we have elements
519
+ if (sparklineElements.length === 0 && sparkbarElements.length === 0 &&
520
+ sparktriElements.length === 0 && sparkdiscElements.length === 0 &&
521
+ sparkbullElements.length === 0 && sparkboxElements.length === 0) {
522
+ return;
523
+ }
524
+
525
+ const values = [5, 4, 5, -2, 0, 3, -5, 6, 7, 9, 9, 5, -3, -2, 2, -4];
526
+ const valuesAlt = [1, 1, 0, 1, -1, -1, 1, -1, 0, 0, 1, 1];
527
+
528
+ sparklineElements.forEach((element, index) => {
529
+ this.createCustomLineChart(element as HTMLElement, values, `sparkline-${index}`);
530
+ });
531
+
532
+ sparkbarElements.forEach((element, index) => {
533
+ this.createCustomBarChart(element as HTMLElement, values, `sparkbar-${index}`);
534
+ });
535
+
536
+ sparktriElements.forEach((element, index) => {
537
+ this.createTristateChart(element as HTMLElement, valuesAlt, `sparktri-${index}`);
538
+ });
539
+
540
+ sparkdiscElements.forEach((element, index) => {
541
+ this.createDiscreteChart(element as HTMLElement, values, `sparkdisc-${index}`);
542
+ });
543
+
544
+ sparkbullElements.forEach((element, index) => {
545
+ this.createBulletChart(element as HTMLElement, values, `sparkbull-${index}`);
546
+ });
547
+
548
+ sparkboxElements.forEach((element, index) => {
549
+ this.createBoxChart(element as HTMLElement, values, `sparkbox-${index}`);
550
+ });
551
+ }
552
+
553
+ /**
554
+ * Create custom line chart for sparkline elements
555
+ */
556
+ private createCustomLineChart(element: HTMLElement, data: number[], id: string): void {
557
+ // Create canvas if it doesn't exist
558
+ let canvas = element.querySelector('canvas') as HTMLCanvasElement;
559
+ if (!canvas) {
560
+ canvas = document.createElement('canvas');
561
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
562
+ element.appendChild(canvas);
563
+ }
564
+
565
+ const ctx = canvas.getContext('2d');
566
+ if (!ctx) return;
567
+
568
+ const chartConfig: ChartConfiguration = {
569
+ type: 'line',
570
+ data: {
571
+ labels: data.map((_, i) => i.toString()),
572
+ datasets: [{
573
+ data,
574
+ borderColor: COLORS['red-500'],
575
+ backgroundColor: 'transparent',
576
+ borderWidth: 2,
577
+ pointRadius: 3,
578
+ pointBackgroundColor: COLORS['red-500'],
579
+ tension: 0.4,
580
+ }],
581
+ },
582
+ options: {
583
+ responsive: false,
584
+ maintainAspectRatio: false,
585
+ animation: false, // Disable animations to prevent resize triggers
586
+ events: [], // Disable all events to prevent resize
587
+ scales: {
588
+ x: { display: false },
589
+ y: { display: false },
590
+ },
591
+ plugins: {
592
+ legend: { display: false },
593
+ tooltip: { enabled: false }, // Disable tooltip to prevent events
594
+ },
595
+ },
596
+ };
597
+
598
+ const chart = new Chart(ctx, chartConfig);
599
+ this.charts.set(id, chart);
600
+ }
601
+
602
+ /**
603
+ * Create custom bar chart for sparkbar elements
604
+ */
605
+ private createCustomBarChart(element: HTMLElement, data: number[], id: string): void {
606
+ // Create canvas if it doesn't exist
607
+ let canvas = element.querySelector('canvas') as HTMLCanvasElement;
608
+ if (!canvas) {
609
+ canvas = document.createElement('canvas');
610
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
611
+ element.appendChild(canvas);
612
+ }
613
+
614
+ const ctx = canvas.getContext('2d');
615
+ if (!ctx) return;
616
+
617
+ const chartConfig: ChartConfiguration = {
618
+ type: 'bar',
619
+ data: {
620
+ labels: data.map((_, i) => i.toString()),
621
+ datasets: [{
622
+ data,
623
+ backgroundColor: data.map(val => val < 0 ? COLORS['deep-purple-500'] : '#39f'),
624
+ borderColor: data.map(val => val < 0 ? COLORS['deep-purple-500'] : '#39f'),
625
+ borderWidth: 1,
626
+ barPercentage: 0.8,
627
+ }],
628
+ },
629
+ options: {
630
+ responsive: false,
631
+ maintainAspectRatio: false,
632
+ scales: {
633
+ x: { display: false },
634
+ y: { display: false },
635
+ },
636
+ plugins: {
637
+ legend: { display: false },
638
+ tooltip: {
639
+ enabled: true,
640
+ callbacks: {
641
+ label: (context) => `${context.parsed.y}°Celsius`,
642
+ },
643
+ },
644
+ },
645
+ },
646
+ };
647
+
648
+ const chart = new Chart(ctx, chartConfig);
649
+ this.charts.set(id, chart);
650
+ }
651
+
652
+ /**
653
+ * Setup resize handler for charts
654
+ */
655
+ private setupResizeHandler(): void {
656
+ // Setup responsive resize for large charts only
657
+ window.addEventListener('resize', () => {
658
+ this.debounceResize();
659
+ });
660
+
661
+ // Listen for sidebar toggle events
662
+ window.addEventListener('sidebar:toggle', () => {
663
+ this.debounceResize();
664
+ });
665
+ }
666
+
667
+ /**
668
+ * Debounced resize handler
669
+ */
670
+ private debounceResize(): void {
671
+ if (this.debounceTimer) {
672
+ clearTimeout(this.debounceTimer);
673
+ }
674
+ this.debounceTimer = window.setTimeout(() => {
675
+ this.redrawLargeChartsOnly();
676
+ }, this.options.resizeDebounceMs || 150);
677
+ }
678
+
679
+ /**
680
+ * Redraw only large charts, not sparklines
681
+ */
682
+ private redrawLargeChartsOnly(): void {
683
+ const largeChartIds = [
684
+ 'line-chart', 'area-chart', 'scatter-chart', 'bar-chart',
685
+ 'doughnut-chart', 'polar-chart', 'radar-chart', 'mixed-chart', 'bubble-chart',
686
+ ];
687
+
688
+ largeChartIds.forEach(id => {
689
+ const chart = this.charts.get(id);
690
+ if (chart && chart.options.responsive) {
691
+ chart.resize();
692
+ }
693
+ });
694
+ }
695
+
696
+ /**
697
+ * Redraw all charts (used sparingly)
698
+ */
699
+ public redrawCharts(): void {
700
+ this.charts.forEach((chart) => {
701
+ if (chart.options.responsive) {
702
+ chart.resize();
703
+ }
704
+ });
705
+ }
706
+
707
+ /**
708
+ * Update chart data
709
+ */
710
+ public updateChart(id: string, newData: number[]): void {
711
+ const chart = this.charts.get(id);
712
+ if (chart && chart.data.datasets[0]) {
713
+ chart.data.datasets[0].data = newData;
714
+ chart.update();
715
+ }
716
+ }
717
+
718
+ /**
719
+ * Get chart instance by id
720
+ */
721
+ public getChart(id: string): Chart | undefined {
722
+ return this.charts.get(id);
723
+ }
724
+
725
+ /**
726
+ * Get all chart instances
727
+ */
728
+ public getAllCharts(): Map<string, Chart> {
729
+ return new Map(this.charts);
730
+ }
731
+
732
+ /**
733
+ * Create charts for the charts.html page
734
+ */
735
+ private createChartsPageCharts(): void {
736
+ // Line Chart
737
+ this.createLargeChart('line-chart', 'line', {
738
+ labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
739
+ datasets: [{
740
+ label: 'Dataset 1',
741
+ data: [65, 59, 80, 81, 56, 55, 40],
742
+ borderColor: 'rgb(75, 192, 192)',
743
+ backgroundColor: 'rgba(75, 192, 192, 0.2)',
744
+ tension: 0.4,
745
+ }],
746
+ });
747
+
748
+ // Area Chart
749
+ this.createLargeChart('area-chart', 'line', {
750
+ labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
751
+ datasets: [{
752
+ label: 'Dataset 1',
753
+ data: [65, 59, 80, 81, 56, 55, 40],
754
+ borderColor: 'rgb(54, 162, 235)',
755
+ backgroundColor: 'rgba(54, 162, 235, 0.4)',
756
+ fill: true,
757
+ tension: 0.4,
758
+ }],
759
+ });
760
+
761
+ // Scatter Chart with more data points
762
+ this.createLargeChart('scatter-chart', 'scatter', {
763
+ datasets: [{
764
+ label: 'Dataset 1',
765
+ data: [
766
+ {x: -15, y: 8}, {x: -12, y: 12}, {x: -8, y: 3}, {x: -5, y: 15},
767
+ {x: -2, y: 7}, {x: 0, y: 10}, {x: 3, y: 18}, {x: 6, y: 5},
768
+ {x: 9, y: 22}, {x: 12, y: 8}, {x: 15, y: 14}, {x: 18, y: 19},
769
+ {x: -10, y: 0}, {x: 10, y: 5}, {x: 0.5, y: 5.5}, {x: 7, y: 12},
770
+ {x: -7, y: 17}, {x: 4, y: 9}, {x: 11, y: 16}, {x: -3, y: 11},
771
+ ],
772
+ backgroundColor: 'rgba(255, 99, 132, 0.7)',
773
+ borderColor: 'rgb(255, 99, 132)',
774
+ borderWidth: 1,
775
+ }],
776
+ });
777
+
778
+ // Continue with other chart types...
779
+ this.createBarChart();
780
+ this.createDoughnutChart();
781
+ this.createPolarChart();
782
+ this.createRadarChart();
783
+ this.createMixedChart();
784
+ this.createBubbleChart();
785
+ }
786
+
787
+ /**
788
+ * Create large chart for charts page
789
+ */
790
+ private createLargeChart(id: string, type: ChartJSType, data: any): void {
791
+ const canvas = document.getElementById(id) as HTMLCanvasElement;
792
+ if (!canvas) return;
793
+
794
+ const ctx = canvas.getContext('2d');
795
+ if (!ctx) return;
796
+
797
+ // Define chart-specific options
798
+ const chartOptions = this.getChartOptions(type);
799
+
800
+ const chartConfig: ChartConfiguration = {
801
+ type,
802
+ data,
803
+ options: chartOptions,
804
+ };
805
+
806
+ const chart = new Chart(ctx, chartConfig);
807
+ this.charts.set(id, chart);
808
+ }
809
+
810
+ /**
811
+ * Get chart-specific options based on chart type
812
+ */
813
+ private getChartOptions(type: ChartJSType): any {
814
+ const baseOptions = {
815
+ responsive: true,
816
+ maintainAspectRatio: false,
817
+ plugins: {
818
+ legend: {
819
+ display: true,
820
+ position: 'top' as const,
821
+ labels: {
822
+ padding: 20,
823
+ font: {
824
+ size: 12,
825
+ weight: '600' as const,
826
+ },
827
+ },
828
+ },
829
+ tooltip: {
830
+ enabled: true,
831
+ cornerRadius: 8,
832
+ displayColors: true,
833
+ },
834
+ },
835
+ };
836
+
837
+ // Chart type specific configurations
838
+ switch (type) {
839
+ case 'doughnut':
840
+ case 'pie':
841
+ return {
842
+ ...baseOptions,
843
+ plugins: {
844
+ ...baseOptions.plugins,
845
+ legend: {
846
+ ...baseOptions.plugins.legend,
847
+ position: 'right' as const,
848
+ },
849
+ },
850
+ interaction: {
851
+ intersect: false,
852
+ },
853
+ };
854
+
855
+ case 'polarArea':
856
+ return {
857
+ ...baseOptions,
858
+ scales: {
859
+ r: {
860
+ pointLabels: {
861
+ display: true,
862
+ centerPointLabels: true,
863
+ font: {
864
+ size: 10,
865
+ },
866
+ },
867
+ grid: {},
868
+ },
869
+ },
870
+ };
871
+
872
+ case 'radar':
873
+ return {
874
+ ...baseOptions,
875
+ scales: {
876
+ r: {
877
+ angleLines: {
878
+ display: true,
879
+ },
880
+ grid: {},
881
+ pointLabels: {
882
+ font: {
883
+ size: 11,
884
+ },
885
+ },
886
+ ticks: {
887
+ display: true,
888
+ font: {
889
+ size: 10,
890
+ },
891
+ },
892
+ },
893
+ },
894
+ };
895
+
896
+ case 'bubble':
897
+ return {
898
+ ...baseOptions,
899
+ scales: {
900
+ x: {
901
+ type: 'linear' as const,
902
+ position: 'bottom' as const,
903
+ grid: {
904
+ borderDash: [5, 5] as [number, number],
905
+ },
906
+ ticks: {
907
+ font: {
908
+ size: 11,
909
+ },
910
+ },
911
+ },
912
+ y: {
913
+ beginAtZero: true,
914
+ grid: {
915
+ borderDash: [5, 5] as [number, number],
916
+ },
917
+ ticks: {
918
+ font: {
919
+ size: 11,
920
+ },
921
+ },
922
+ },
923
+ },
924
+ };
925
+
926
+ case 'scatter':
927
+ return {
928
+ ...baseOptions,
929
+ scales: {
930
+ x: {
931
+ type: 'linear' as const,
932
+ position: 'bottom' as const,
933
+ grid: {
934
+ borderDash: [5, 5] as [number, number],
935
+ },
936
+ ticks: {
937
+ font: {
938
+ size: 11,
939
+ },
940
+ },
941
+ },
942
+ y: {
943
+ grid: {
944
+ borderDash: [5, 5] as [number, number],
945
+ },
946
+ ticks: {
947
+ font: {
948
+ size: 11,
949
+ },
950
+ },
951
+ },
952
+ },
953
+ };
954
+
955
+ default:
956
+ // For line, bar, area, mixed charts
957
+ return {
958
+ ...baseOptions,
959
+ scales: {
960
+ x: {
961
+ grid: {
962
+ borderDash: [5, 5] as [number, number],
963
+ },
964
+ ticks: {
965
+ font: {
966
+ size: 11,
967
+ },
968
+ },
969
+ },
970
+ y: {
971
+ beginAtZero: true,
972
+ grid: {
973
+ borderDash: [5, 5] as [number, number],
974
+ },
975
+ ticks: {
976
+ font: {
977
+ size: 11,
978
+ },
979
+ },
980
+ },
981
+ },
982
+ };
983
+ }
984
+ }
985
+
986
+ // Additional chart creation methods...
987
+ private createBarChart(): void {
988
+ this.createLargeChart('bar-chart', 'bar', {
989
+ labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
990
+ datasets: [{
991
+ label: '# of Votes',
992
+ data: [12, 19, 3, 5, 2, 3],
993
+ backgroundColor: [
994
+ 'rgba(255, 99, 132, 0.6)',
995
+ 'rgba(54, 162, 235, 0.6)',
996
+ 'rgba(255, 205, 86, 0.6)',
997
+ 'rgba(75, 192, 192, 0.6)',
998
+ 'rgba(153, 102, 255, 0.6)',
999
+ 'rgba(255, 159, 64, 0.6)',
1000
+ ],
1001
+ borderColor: [
1002
+ 'rgba(255, 99, 132, 1)',
1003
+ 'rgba(54, 162, 235, 1)',
1004
+ 'rgba(255, 205, 86, 1)',
1005
+ 'rgba(75, 192, 192, 1)',
1006
+ 'rgba(153, 102, 255, 1)',
1007
+ 'rgba(255, 159, 64, 1)',
1008
+ ],
1009
+ borderWidth: 1,
1010
+ }],
1011
+ });
1012
+ }
1013
+
1014
+ private createDoughnutChart(): void {
1015
+ this.createLargeChart('doughnut-chart', 'doughnut', {
1016
+ labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
1017
+ datasets: [{
1018
+ label: 'My First Dataset',
1019
+ data: [300, 50, 100, 75, 120, 60],
1020
+ backgroundColor: [
1021
+ 'rgba(255, 99, 132, 0.8)',
1022
+ 'rgba(54, 162, 235, 0.8)',
1023
+ 'rgba(255, 205, 86, 0.8)',
1024
+ 'rgba(75, 192, 192, 0.8)',
1025
+ 'rgba(153, 102, 255, 0.8)',
1026
+ 'rgba(255, 159, 64, 0.8)',
1027
+ ],
1028
+ borderWidth: 2,
1029
+ hoverOffset: 10,
1030
+ }],
1031
+ });
1032
+ }
1033
+
1034
+ private createPolarChart(): void {
1035
+ this.createLargeChart('polar-chart', 'polarArea', {
1036
+ labels: ['Red', 'Green', 'Yellow', 'Grey', 'Blue'],
1037
+ datasets: [{
1038
+ label: 'My First Dataset',
1039
+ data: [11, 16, 7, 3, 14],
1040
+ backgroundColor: [
1041
+ 'rgba(255, 99, 132, 0.7)',
1042
+ 'rgba(75, 192, 192, 0.7)',
1043
+ 'rgba(255, 205, 86, 0.7)',
1044
+ 'rgba(201, 203, 207, 0.7)',
1045
+ 'rgba(54, 162, 235, 0.7)',
1046
+ ],
1047
+ borderWidth: 2,
1048
+ }],
1049
+ });
1050
+ }
1051
+
1052
+ private createRadarChart(): void {
1053
+ this.createLargeChart('radar-chart', 'radar', {
1054
+ labels: ['Speed', 'Reliability', 'Comfort', 'Safety', 'Efficiency', 'Innovation'],
1055
+ datasets: [{
1056
+ label: 'Product A',
1057
+ data: [65, 59, 90, 81, 56, 55],
1058
+ fill: true,
1059
+ backgroundColor: 'rgba(54, 162, 235, 0.2)',
1060
+ borderColor: 'rgb(54, 162, 235)',
1061
+ borderWidth: 2,
1062
+ }],
1063
+ });
1064
+ }
1065
+
1066
+ private createMixedChart(): void {
1067
+ this.createLargeChart('mixed-chart', 'bar', {
1068
+ labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
1069
+ datasets: [{
1070
+ type: 'bar',
1071
+ label: 'Sales',
1072
+ data: [12, 19, 3, 5, 2, 3],
1073
+ backgroundColor: 'rgba(54, 162, 235, 0.7)',
1074
+ }, {
1075
+ type: 'line',
1076
+ label: 'Revenue',
1077
+ data: [18, 25, 8, 15, 12, 18],
1078
+ borderColor: 'rgb(255, 99, 132)',
1079
+ tension: 0.4,
1080
+ }],
1081
+ });
1082
+ }
1083
+
1084
+ private createBubbleChart(): void {
1085
+ this.createLargeChart('bubble-chart', 'bubble', {
1086
+ datasets: [{
1087
+ label: 'First Dataset',
1088
+ data: [
1089
+ {x: 20, y: 30, r: 15},
1090
+ {x: 40, y: 10, r: 10},
1091
+ {x: 30, y: 40, r: 20},
1092
+ ],
1093
+ backgroundColor: 'rgba(54, 162, 235, 0.6)',
1094
+ borderColor: 'rgb(54, 162, 235)',
1095
+ borderWidth: 2,
1096
+ }],
1097
+ });
1098
+ }
1099
+
1100
+ /**
1101
+ * Create tristate chart (for .sparktri elements)
1102
+ */
1103
+ private createTristateChart(element: HTMLElement, data: number[], id: string): void {
1104
+ let canvas = element.querySelector('canvas') as HTMLCanvasElement;
1105
+ if (!canvas) {
1106
+ canvas = document.createElement('canvas');
1107
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
1108
+ element.appendChild(canvas);
1109
+ }
1110
+
1111
+ const ctx = canvas.getContext('2d');
1112
+ if (!ctx) return;
1113
+
1114
+ const chartConfig: ChartConfiguration = {
1115
+ type: 'bar',
1116
+ data: {
1117
+ labels: data.map((_, i) => i.toString()),
1118
+ datasets: [{
1119
+ data: data.map(val => Math.abs(val)),
1120
+ backgroundColor: data.map(val => {
1121
+ if (val > 0) return COLORS['light-blue-500'];
1122
+ if (val < 0) return '#f90';
1123
+ return '#000';
1124
+ }),
1125
+ borderWidth: 1,
1126
+ barPercentage: 0.8,
1127
+ }],
1128
+ },
1129
+ options: {
1130
+ responsive: false,
1131
+ maintainAspectRatio: false,
1132
+ scales: {
1133
+ x: { display: false },
1134
+ y: { display: false },
1135
+ },
1136
+ plugins: {
1137
+ legend: { display: false },
1138
+ tooltip: { enabled: false },
1139
+ },
1140
+ },
1141
+ };
1142
+
1143
+ const chart = new Chart(ctx, chartConfig);
1144
+ this.charts.set(id, chart);
1145
+ }
1146
+
1147
+ /**
1148
+ * Create discrete chart (for .sparkdisc elements)
1149
+ */
1150
+ private createDiscreteChart(element: HTMLElement, data: number[], id: string): void {
1151
+ let canvas = element.querySelector('canvas') as HTMLCanvasElement;
1152
+ if (!canvas) {
1153
+ canvas = document.createElement('canvas');
1154
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
1155
+ element.appendChild(canvas);
1156
+ }
1157
+
1158
+ const ctx = canvas.getContext('2d');
1159
+ if (!ctx) return;
1160
+
1161
+ const chartConfig: ChartConfiguration = {
1162
+ type: 'scatter',
1163
+ data: {
1164
+ datasets: [{
1165
+ data: data.map((val, index) => ({x: index, y: val})),
1166
+ backgroundColor: '#9f0',
1167
+ borderColor: '#9f0',
1168
+ pointRadius: 2,
1169
+ showLine: false,
1170
+ }],
1171
+ },
1172
+ options: {
1173
+ responsive: false,
1174
+ maintainAspectRatio: false,
1175
+ scales: {
1176
+ x: { display: false },
1177
+ y: { display: false },
1178
+ },
1179
+ plugins: {
1180
+ legend: { display: false },
1181
+ tooltip: { enabled: false },
1182
+ },
1183
+ },
1184
+ };
1185
+
1186
+ const chart = new Chart(ctx, chartConfig);
1187
+ this.charts.set(id, chart);
1188
+ }
1189
+
1190
+ /**
1191
+ * Create bullet chart (for .sparkbull elements)
1192
+ */
1193
+ private createBulletChart(element: HTMLElement, data: number[], id: string): void {
1194
+ let canvas = element.querySelector('canvas') as HTMLCanvasElement;
1195
+ if (!canvas) {
1196
+ canvas = document.createElement('canvas');
1197
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
1198
+ element.appendChild(canvas);
1199
+ }
1200
+
1201
+ const ctx = canvas.getContext('2d');
1202
+ if (!ctx) return;
1203
+
1204
+ const chartConfig: ChartConfiguration = {
1205
+ type: 'bar',
1206
+ data: {
1207
+ labels: [''],
1208
+ datasets: [{
1209
+ data: [Math.max(...data)],
1210
+ backgroundColor: COLORS['amber-500'],
1211
+ borderColor: COLORS['amber-500'],
1212
+ borderWidth: 1,
1213
+ barPercentage: 0.6,
1214
+ }],
1215
+ },
1216
+ options: {
1217
+ responsive: false,
1218
+ maintainAspectRatio: false,
1219
+ indexAxis: 'y',
1220
+ scales: {
1221
+ x: { display: false },
1222
+ y: { display: false },
1223
+ },
1224
+ plugins: {
1225
+ legend: { display: false },
1226
+ tooltip: { enabled: false },
1227
+ },
1228
+ },
1229
+ };
1230
+
1231
+ const chart = new Chart(ctx, chartConfig);
1232
+ this.charts.set(id, chart);
1233
+ }
1234
+
1235
+ /**
1236
+ * Create box chart (for .sparkbox elements)
1237
+ */
1238
+ private createBoxChart(element: HTMLElement, data: number[], id: string): void {
1239
+ let canvas = element.querySelector('canvas') as HTMLCanvasElement;
1240
+ if (!canvas) {
1241
+ canvas = document.createElement('canvas');
1242
+ this.setCanvasDimensions(canvas, { width: 100, height: 20 });
1243
+ element.appendChild(canvas);
1244
+ }
1245
+
1246
+ const ctx = canvas.getContext('2d');
1247
+ if (!ctx) return;
1248
+
1249
+ // Box plot simplified as bar chart showing quartiles
1250
+ const sortedData = [...data].sort((a, b) => a - b);
1251
+ const q1 = sortedData[Math.floor(sortedData.length * 0.25)];
1252
+ const median = sortedData[Math.floor(sortedData.length * 0.5)];
1253
+ const q3 = sortedData[Math.floor(sortedData.length * 0.75)];
1254
+
1255
+ const chartConfig: ChartConfiguration = {
1256
+ type: 'bar',
1257
+ data: {
1258
+ labels: ['Q1', 'Med', 'Q3'],
1259
+ datasets: [{
1260
+ data: [q1, median, q3],
1261
+ backgroundColor: '#9f0',
1262
+ borderColor: '#9f0',
1263
+ borderWidth: 1,
1264
+ barPercentage: 0.8,
1265
+ }],
1266
+ },
1267
+ options: {
1268
+ responsive: false,
1269
+ maintainAspectRatio: false,
1270
+ scales: {
1271
+ x: { display: false },
1272
+ y: { display: false },
1273
+ },
1274
+ plugins: {
1275
+ legend: { display: false },
1276
+ tooltip: { enabled: false },
1277
+ },
1278
+ },
1279
+ };
1280
+
1281
+ const chart = new Chart(ctx, chartConfig);
1282
+ this.charts.set(id, chart);
1283
+ }
1284
+
1285
+ /**
1286
+ * Create Easy Pie Charts (replaces jQuery Easy Pie Chart)
1287
+ */
1288
+ private createEasyPieCharts(): void {
1289
+ const easyPieElements = document.querySelectorAll('.easy-pie-chart');
1290
+
1291
+ easyPieElements.forEach((element, index) => {
1292
+ const htmlElement = element as HTMLElement;
1293
+ const size = parseInt(htmlElement.dataset.size || '80');
1294
+ const percent = parseInt(htmlElement.dataset.percent || '0');
1295
+ const barColor = htmlElement.dataset.barColor || '#f44336';
1296
+
1297
+ // Create canvas for the pie chart
1298
+ let canvas = element.querySelector('canvas') as HTMLCanvasElement;
1299
+ if (!canvas) {
1300
+ canvas = document.createElement('canvas');
1301
+ this.setCanvasDimensions(canvas, { width: size, height: size });
1302
+ element.appendChild(canvas);
1303
+ }
1304
+
1305
+ // Create percentage display
1306
+ const percentDisplay = element.querySelector('span') as HTMLSpanElement;
1307
+ if (percentDisplay) {
1308
+ percentDisplay.textContent = `${percent}%`;
1309
+ percentDisplay.style.position = 'absolute';
1310
+ percentDisplay.style.top = '50%';
1311
+ percentDisplay.style.left = '50%';
1312
+ percentDisplay.style.transform = 'translate(-50%, -50%)';
1313
+ percentDisplay.style.fontSize = '14px';
1314
+ percentDisplay.style.fontWeight = 'bold';
1315
+ }
1316
+
1317
+ // Set element position to relative for absolute positioning of text
1318
+ htmlElement.style.position = 'relative';
1319
+ htmlElement.style.display = 'inline-block';
1320
+
1321
+ const ctx = canvas.getContext('2d');
1322
+ if (!ctx) return;
1323
+
1324
+ const chartConfig: ChartConfiguration = {
1325
+ type: 'doughnut',
1326
+ data: {
1327
+ datasets: [{
1328
+ data: [percent, 100 - percent],
1329
+ backgroundColor: [barColor, '#f0f0f0'],
1330
+ borderWidth: 0,
1331
+ }],
1332
+ },
1333
+ options: {
1334
+ responsive: false,
1335
+ maintainAspectRatio: false,
1336
+ cutout: '70%',
1337
+ plugins: {
1338
+ legend: { display: false },
1339
+ tooltip: { enabled: false },
1340
+ },
1341
+ },
1342
+ };
1343
+
1344
+ const chart = new Chart(ctx, chartConfig);
1345
+ this.charts.set(`easy-pie-${index}`, chart);
1346
+ });
1347
+ }
1348
+ }
1349
+
1350
+ export default ChartComponent;