diff --git a/src/app/reports/run-report/chart/chart.component.html b/src/app/reports/run-report/chart/chart.component.html index 0e0015caaf..eef8d1aa4e 100644 --- a/src/app/reports/run-report/chart/chart.component.html +++ b/src/app/reports/run-report/chart/chart.component.html @@ -6,17 +6,27 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
- - {{ - 'labels.buttons.Bar Chart' | translate - }} - {{ - 'labels.buttons.Pie Chart' | translate - }} - -
+
+
+ + {{ + 'labels.buttons.Bar Chart' | translate + }} + {{ + 'labels.buttons.Pie Chart' | translate + }} + {{ + 'labels.buttons.Polar Area Chart' | translate + }} + +
-
- +
+ +
diff --git a/src/app/reports/run-report/chart/chart.component.scss b/src/app/reports/run-report/chart/chart.component.scss index af9f4c0753..df369383a7 100644 --- a/src/app/reports/run-report/chart/chart.component.scss +++ b/src/app/reports/run-report/chart/chart.component.scss @@ -5,3 +5,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +.chart-output-container { + position: relative; + width: 100%; + height: 500px; + + canvas { + width: 100% !important; + height: 100% !important; + } +} diff --git a/src/app/reports/run-report/chart/chart.component.ts b/src/app/reports/run-report/chart/chart.component.ts index 4908ac0649..fa6294ef1e 100644 --- a/src/app/reports/run-report/chart/chart.component.ts +++ b/src/app/reports/run-report/chart/chart.component.ts @@ -7,10 +7,11 @@ */ /** Angular Imports */ -import { Component, OnChanges, Input, inject } from '@angular/core'; +import { Component, OnChanges, OnInit, OnDestroy, Input, inject } from '@angular/core'; /** Custom Services */ import { ReportsService } from '../../reports.service'; +import { ThemeStorageService } from 'app/shared/theme-picker/theme-storage.service'; /** Custom Models */ import { ChartData } from '../../common-models/chart-data.model'; @@ -38,8 +39,11 @@ Chart.register(...registerables); NgStyle ] }) -export class ChartComponent implements OnChanges { +export class ChartComponent implements OnChanges, OnInit, OnDestroy { private reportsService = inject(ReportsService); + private themeStorageService = inject(ThemeStorageService); + private resizeTimeoutId: ReturnType | undefined; + private initialRenderTimeoutId: ReturnType | undefined; /** Run Report Data */ @Input() dataObject: any; @@ -50,6 +54,52 @@ export class ChartComponent implements OnChanges { hideOutput = true; /** Data object for witching charts in view. */ inputData: ChartData; + /** Tracks the currently selected chart type */ + selectedChartType: string = 'Pie'; + /** Resize listener */ + private readonly resizeListener = () => this.resizeChart(); + + /** + * Initialize component and add resize listener. + */ + ngOnInit() { + window.addEventListener('resize', this.resizeListener); + } + + /** + * Clean up on component destroy. + */ + ngOnDestroy() { + window.removeEventListener('resize', this.resizeListener); + if (this.resizeTimeoutId !== undefined) { + clearTimeout(this.resizeTimeoutId); + this.resizeTimeoutId = undefined; + } + if (this.initialRenderTimeoutId !== undefined) { + clearTimeout(this.initialRenderTimeoutId); + this.initialRenderTimeoutId = undefined; + } + if (this.chart) { + this.chart.destroy(); + this.chart = undefined; + } + } + + /** + * Resize and redraw chart when window size changes. + */ + resizeChart() { + if (this.chart) { + if (this.resizeTimeoutId !== undefined) { + clearTimeout(this.resizeTimeoutId); + } + // Debounce resize calls to avoid queuing multiple chart operations. + this.resizeTimeoutId = setTimeout(() => { + this.chart?.resize(); + this.resizeTimeoutId = undefined; + }, 100); + } + } /** * Fetches run report data post changes in run report form. @@ -63,19 +113,59 @@ export class ChartComponent implements OnChanges { .getChartRunReportData(this.dataObject.report.name, this.dataObject.formData) .subscribe((response: ChartData) => { this.inputData = response; - this.setPieChart(this.inputData); + this.selectedChartType = 'Pie'; this.hideOutput = false; + if (this.initialRenderTimeoutId !== undefined) { + clearTimeout(this.initialRenderTimeoutId); + } + this.initialRenderTimeoutId = setTimeout(() => { + this.setPieChart(response); + this.initialRenderTimeoutId = undefined; + }); }); } + /** + * Handles chart type selection and renders the selected chart. + * @param {string} chartType The type of chart to display + */ + selectChart(chartType: string) { + if (!this.inputData) { + return; + } + const chartColors = this.randomColorArray(this.inputData.values.length); + this.selectedChartType = chartType; + switch (chartType) { + case 'Bar': + this.setBarChart(this.inputData, chartColors); + break; + case 'Pie': + this.setPieChart(this.inputData, chartColors); + break; + case 'Polar': + this.setPolarAreaChart(this.inputData, chartColors); + break; + } + } + + /** + * Refreshes colors when user clicks the currently selected toggle. + */ + refreshChartIfSameType(chartType: string) { + if (chartType === this.selectedChartType) { + this.selectChart(chartType); + } + } + /** * Creates instance of chart.js pie chart. * Refer: https://www.chartjs.org/docs/latest/charts/doughnut.html for configuration details. */ - setPieChart(inputData: ChartData) { + setPieChart(inputData: ChartData, chartColors?: string[]) { if (this.chart) { this.chart.destroy(); } + const colors = chartColors ?? this.randomColorArray(inputData.values.length); this.chart = new Chart('output', { type: 'pie', data: { @@ -84,11 +174,13 @@ export class ChartComponent implements OnChanges { { label: inputData.valuesLabel, data: inputData.values, - backgroundColor: this.randomColorArray(inputData.values.length) + backgroundColor: colors } ] }, options: { + responsive: true, + maintainAspectRatio: false, plugins: { title: { display: true, @@ -103,10 +195,11 @@ export class ChartComponent implements OnChanges { * Creates instance of chart.js bar chart. * Refer: https://www.chartjs.org/docs/latest/charts/bar.html for configuration details. */ - setBarChart(inputData: ChartData) { + setBarChart(inputData: ChartData, chartColors?: string[]) { if (this.chart) { this.chart.destroy(); } + const colors = chartColors ?? this.randomColorArray(inputData.values.length); this.chart = new Chart('output', { type: 'bar', data: { @@ -115,11 +208,13 @@ export class ChartComponent implements OnChanges { { label: inputData.valuesLabel, data: inputData.values, - backgroundColor: this.randomColorArray(inputData.values.length) + backgroundColor: colors } ] }, options: { + responsive: true, + maintainAspectRatio: false, plugins: { legend: { display: false } }, @@ -138,12 +233,53 @@ export class ChartComponent implements OnChanges { }); } + /** + * Creates instance of chart.js polar area chart. + * Refer: https://www.chartjs.org/docs/latest/charts/polar.html for configuration details. + */ + setPolarAreaChart(inputData: ChartData, chartColors?: string[]) { + if (this.chart) { + this.chart.destroy(); + } + const colors = chartColors ?? this.randomColorArray(inputData.values.length); + this.chart = new Chart('output', { + type: 'polarArea', + data: { + labels: inputData.keys, + datasets: [ + { + label: inputData.valuesLabel, + data: inputData.values, + backgroundColor: colors, + borderColor: colors + } + ] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + title: { + display: true, + text: inputData.keysLabel + }, + legend: { display: true } + }, + scales: { + r: { + min: 0 + } + } + } + }); + } + /** * Generates bar/pie-slice colors array for dynamic charts. * @param {number} length Length of dataset array. */ randomColorArray(length: number) { - const colorArray: any[] = []; + const colorArray: string[] = []; while (length--) { const color = this.randomColor(); colorArray.push(color); @@ -152,12 +288,66 @@ export class ChartComponent implements OnChanges { } /** - * Returns a random rgb color. + * Returns a semi-random color based on the active theme palette. */ randomColor() { - const r = Math.floor(Math.random() * 255); - const g = Math.floor(Math.random() * 255); - const b = Math.floor(Math.random() * 255); - return `rgb(${r},${g},${b},0.6)`; + const baseColors = this.getThemeBaseColors(); + const baseColor = baseColors[Math.floor(Math.random() * baseColors.length)]; + const variation = Math.floor(Math.random() * 61) - 30; + const [ + r, + g, + b + ] = this.hexToRgb(baseColor).map((channel) => this.clamp(channel + variation)); + return `rgba(${r},${g},${b},0.6)`; + } + + /** + * Derives chart base colors from the user's selected theme and current dark mode. + */ + private getThemeBaseColors(): string[] { + const savedTheme = this.themeStorageService.getTheme(); + const primary = savedTheme?.primary || '#1074B9'; + const accent = savedTheme?.accent || '#B4D575'; + const isDark = document.body.classList.contains('dark-theme'); + + if (isDark) { + return [ + primary, + accent, + '#5BA2EC', + '#83A447', + '#C4C6D0' + ]; + } + + return [ + primary, + accent, + '#004989', + '#E7FFA5', + '#6E8A3B' + ]; + } + + private hexToRgb(hexColor: string): number[] { + const hex = hexColor.replace('#', ''); + const normalized = + hex.length === 3 + ? hex + .split('') + .map((char) => char + char) + .join('') + : hex; + const numeric = parseInt(normalized, 16); + return [ + (numeric >> 16) & 255, + (numeric >> 8) & 255, + numeric & 255 + ]; + } + + private clamp(value: number): number { + return Math.max(0, Math.min(255, value)); } } diff --git a/src/app/reports/run-report/run-report.component.html b/src/app/reports/run-report/run-report.component.html index f5a4f55b19..73c8250787 100644 --- a/src/app/reports/run-report/run-report.component.html +++ b/src/app/reports/run-report/run-report.component.html @@ -119,7 +119,7 @@
-
+
diff --git a/src/assets/translations/cs-CS.json b/src/assets/translations/cs-CS.json index c2adb85e01..a089a48ea0 100644 --- a/src/assets/translations/cs-CS.json +++ b/src/assets/translations/cs-CS.json @@ -524,6 +524,7 @@ "Payments": "Platby", "Permissions": "Oprávnění", "Pie Chart": "Koláčový graf", + "Polar Area Chart": "Polární plošný graf", "Post Dividend": "Vyplácet dividendu", "Previous": "Předchozí", "Preview": "Náhled", @@ -562,6 +563,7 @@ "Setup Products": "Nastavení produktů", "Setup System": "Systém nastavení", "Select All": "Vybrat vše", + "Select Chart Type": "Vybrat typ grafu", "Show more": "Zobrazit více", "Show less": "Ukaž méně", "Signing in...": "Přihlašování", diff --git a/src/assets/translations/de-DE.json b/src/assets/translations/de-DE.json index d7e05ec07a..04d62918e7 100644 --- a/src/assets/translations/de-DE.json +++ b/src/assets/translations/de-DE.json @@ -525,6 +525,7 @@ "Payments": "Zahlungen", "Permissions": "Berechtigungen", "Pie Chart": "Kuchendiagramm", + "Polar Area Chart": "Polarflächendiagramm", "Post Dividend": "Post-Dividende", "Previous": "Vorherige", "Preview": "Vorschau", @@ -563,6 +564,7 @@ "Setup Products": "Setup-Produkte", "Setup System": "Setup-System", "Select All": "Wählen Sie Alle", + "Select Chart Type": "Diagrammtyp auswählen", "Show more": "Zeig mehr", "Show less": "Zeige weniger", "Signing in...": "Anmelden", diff --git a/src/assets/translations/en-US.json b/src/assets/translations/en-US.json index f21196a668..d1baf0256a 100644 --- a/src/assets/translations/en-US.json +++ b/src/assets/translations/en-US.json @@ -529,6 +529,7 @@ "Payments": "Payments", "Permissions": "Permissions", "Pie Chart": "Pie Chart", + "Polar Area Chart": "Polar Area Chart", "Post Dividend": "Post Dividend", "Previous": "Previous", "Preview": "Preview", @@ -568,6 +569,7 @@ "Setup Products": "Setup Products", "Setup System": "Setup System", "Select All": "Select All", + "Select Chart Type": "Select Chart Type", "Show less": "Show less", "Show more": "Show more", "Signing in...": "Signing in...", diff --git a/src/assets/translations/es-CL.json b/src/assets/translations/es-CL.json index b7b4b86e94..361e28ce45 100644 --- a/src/assets/translations/es-CL.json +++ b/src/assets/translations/es-CL.json @@ -525,6 +525,7 @@ "Payments": "Pagos", "Permissions": "Permisos", "Pie Chart": "Gráfico circular", + "Polar Area Chart": "Gráfico de área polar", "Post Dividend": "Publicar dividendo", "Previous": "Anterior", "Preview": "Vista previa", @@ -563,6 +564,7 @@ "Setup Products": "Configuración de Productos", "Setup System": "Configuración de Sistema", "Select All": "Seleccionar todo", + "Select Chart Type": "Seleccionar tipo de gráfico", "Show more": "Mostrar más", "Show less": "Muestra menos", "Signing in...": "Iniciando sesión", diff --git a/src/assets/translations/es-MX.json b/src/assets/translations/es-MX.json index 8180ac1918..c2d93c816a 100644 --- a/src/assets/translations/es-MX.json +++ b/src/assets/translations/es-MX.json @@ -525,6 +525,7 @@ "Payments": "Pagos", "Permissions": "Permisos", "Pie Chart": "Gráfico circular", + "Polar Area Chart": "Gráfico de área polar", "Post Dividend": "Publicar dividendo", "Previous": "Anterior", "Preview": "Vista previa", @@ -563,6 +564,7 @@ "Setup Products": "Configuración de Productos", "Setup System": "Configuración de Sistema", "Select All": "Seleccionar todo", + "Select Chart Type": "Seleccionar tipo de gráfico", "Show more": "Mostrar más", "Show less": "Muestra menos", "Signing in...": "Iniciando sesión", diff --git a/src/assets/translations/fr-FR.json b/src/assets/translations/fr-FR.json index e3c2f03d44..4cb5e55b04 100644 --- a/src/assets/translations/fr-FR.json +++ b/src/assets/translations/fr-FR.json @@ -525,6 +525,7 @@ "Payments": "Paiements", "Permissions": "Autorisations", "Pie Chart": "Diagramme circulaire", + "Polar Area Chart": "Diagramme en aires polaires", "Post Dividend": "Après dividende", "Previous": "Précédent", "Preview": "Aperçu", @@ -563,6 +564,7 @@ "Setup Products": "Produits d'installation", "Setup System": "Système de configuration", "Select All": "Tout sélectionner", + "Select Chart Type": "Sélectionner le type de graphique", "Show more": "Montre plus", "Show less": "Montrer moins", "Signing in...": "Connexion", diff --git a/src/assets/translations/it-IT.json b/src/assets/translations/it-IT.json index f3f9a268e0..01ce1a9c86 100644 --- a/src/assets/translations/it-IT.json +++ b/src/assets/translations/it-IT.json @@ -525,6 +525,7 @@ "Payments": "Pagamenti", "Permissions": "Autorizzazioni", "Pie Chart": "Grafico a torta", + "Polar Area Chart": "Grafico ad area polare", "Post Dividend": "Post dividendo", "Previous": "Precedente", "Preview": "Anteprima", @@ -563,6 +564,7 @@ "Setup Products": "Imposta prodotti", "Setup System": "Sistema di installazione", "Select All": "Seleziona tutto", + "Select Chart Type": "Seleziona tipo di grafico", "Show more": "Mostra di più", "Show less": "Mostra meno", "Signing in...": "Accesso", diff --git a/src/assets/translations/ko-KO.json b/src/assets/translations/ko-KO.json index dad664dcf8..c83da5da92 100644 --- a/src/assets/translations/ko-KO.json +++ b/src/assets/translations/ko-KO.json @@ -525,6 +525,7 @@ "Payments": "결제", "Permissions": "권한", "Pie Chart": "파이 차트", + "Polar Area Chart": "극좌표 영역 차트", "Post Dividend": "배당 후", "Previous": "이전의", "Preview": "미리보기", @@ -563,6 +564,7 @@ "Setup Products": "설치 제품", "Setup System": "설정 시스템", "Select All": "모두 선택", + "Select Chart Type": "차트 유형 선택", "Show more": "자세히보기", "Show less": "간략히 보기", "Signing in...": "로그인", diff --git a/src/assets/translations/lt-LT.json b/src/assets/translations/lt-LT.json index 7c3c2a01fc..a7d9b7545f 100644 --- a/src/assets/translations/lt-LT.json +++ b/src/assets/translations/lt-LT.json @@ -525,6 +525,7 @@ "Payments": "Mokėjimai", "Permissions": "Leidimai", "Pie Chart": "Skritulinė diagrama", + "Polar Area Chart": "Poliarinės srities diagrama", "Post Dividend": "Paskelbkite dividendus", "Previous": "Ankstesnis", "Preview": "Peržiūra", @@ -563,6 +564,7 @@ "Setup Products": "Sąrankos produktai", "Setup System": "Sistemos sąranka", "Select All": "Pasirinkti viską", + "Select Chart Type": "Pasirinkite diagramos tipą", "Show more": "Rodyti daugiau", "Show less": "Rodyti mažiau", "Signing in...": "Prisijungimas", diff --git a/src/assets/translations/lv-LV.json b/src/assets/translations/lv-LV.json index 5a69c342c6..c9dd137f23 100644 --- a/src/assets/translations/lv-LV.json +++ b/src/assets/translations/lv-LV.json @@ -525,6 +525,7 @@ "Payments": "Maksājumi", "Permissions": "Atļaujas", "Pie Chart": "Sektoru diagramma", + "Polar Area Chart": "Polārā apgabala diagramma", "Post Dividend": "Pēc dividendes", "Previous": "Iepriekšējais", "Preview": "Priekšskatījums", @@ -563,6 +564,7 @@ "Setup Products": "Iestatīšanas produkti", "Setup System": "Uzstādīšanas sistēma", "Select All": "Izvēlēties visus", + "Select Chart Type": "Atlasiet diagrammas veidu", "Show more": "Parādīt vairāk", "Show less": "Rādīt mazāk", "Signing in...": "Pierakstīšanās", diff --git a/src/assets/translations/ne-NE.json b/src/assets/translations/ne-NE.json index bfa8fa2aff..5f43629738 100644 --- a/src/assets/translations/ne-NE.json +++ b/src/assets/translations/ne-NE.json @@ -525,6 +525,7 @@ "Payments": "भुक्तानीहरू", "Permissions": "अनुमतिहरू", "Pie Chart": "पाई चार्ट", + "Polar Area Chart": "ध्रुवीय क्षेत्र चार्ट", "Post Dividend": "लाभांश पोस्ट गर्नुहोस्", "Previous": "अघिल्लो", "Preview": "पूर्वावलोकन", @@ -563,6 +564,7 @@ "Setup Products": "उत्पादनहरू सेटअप गर्नुहोस्", "Setup System": "सेटअप प्रणाली", "Select All": "सबै छान्नु", + "Select Chart Type": "चार्ट प्रकार चयन गर्नुहोस्", "Show more": "थप देखाउनुहोस्", "Show less": "कम देखाउनुहोस्", "Signing in...": "साइन इन गर्दै", diff --git a/src/assets/translations/pt-PT.json b/src/assets/translations/pt-PT.json index d8f264b6b6..194720ac38 100644 --- a/src/assets/translations/pt-PT.json +++ b/src/assets/translations/pt-PT.json @@ -525,6 +525,7 @@ "Payments": "Pagamentos", "Permissions": "Permissões", "Pie Chart": "Gráfico de pizza", + "Polar Area Chart": "Gráfico de área polar", "Post Dividend": "Pós Dividendo", "Previous": "Anterior", "Preview": "Pré-visualização", @@ -563,6 +564,7 @@ "Setup Products": "Configurar produtos", "Setup System": "Sistema de configuração", "Select All": "Selecionar tudo", + "Select Chart Type": "Selecionar tipo de gráfico", "Show more": "Mostre mais", "Show less": "Mostre menos", "Signing in...": "Entrando", diff --git a/src/assets/translations/sw-SW.json b/src/assets/translations/sw-SW.json index c108d12b51..50862beae4 100644 --- a/src/assets/translations/sw-SW.json +++ b/src/assets/translations/sw-SW.json @@ -525,6 +525,7 @@ "Payments": "Malipo", "Permissions": "Ruhusa", "Pie Chart": "Jedwali la mdwara", + "Polar Area Chart": "Chati ya Eneo la Polar", "Post Dividend": "Chapisha Gawio", "Previous": "Iliyotangulia", "Preview": "Onyesha awali", @@ -563,6 +564,7 @@ "Setup Products": "Weka Bidhaa", "Setup System": "Kuanzisha Mfumo", "Select All": "Chagua Zote", + "Select Chart Type": "Chagua Aina ya Chati", "Show more": "Onyesha zaidi", "Show less": "Onyesha kidogo", "Signing in...": "Kuingia",