From 1b17bb706dc6b326c1ad334a343c8a5aeaee56cf Mon Sep 17 00:00:00 2001 From: Gaurav Kumar Date: Wed, 29 Oct 2025 11:11:36 +0530 Subject: [PATCH] scatter --- .../doughnut-chart.component.html | 64 +- .../doughnut-chart.component.scss | 749 +++++++----------- .../doughnut-chart.component.ts | 128 ++- .../scatter-chart.component.html | 1 + .../scatter-chart/scatter-chart.component.ts | 151 +++- 5 files changed, 589 insertions(+), 504 deletions(-) diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.html index a0ef6cf..600d4b2 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.html @@ -1,4 +1,4 @@ -
+
@@ -245,34 +245,36 @@
-
-
-

{{ charttitle }}

+
+
+
+

{{ charttitle }}

+
+
+ + +
-
- - -
-
- - -
-
-
-
-
-
- -
- - Drilldown Level: {{currentDrilldownLevel}} - - (Clicked on: {{drilldownStack[drilldownStack.length - 1].clickedLabel}}) + + +
+
+
+
+
+
+ +
+ + Drilldown Level: {{currentDrilldownLevel}} + + (Clicked on: {{drilldownStack[drilldownStack.length - 1].clickedLabel}}) + - +
@@ -280,7 +282,7 @@
-
+

No chart data available

@@ -289,7 +291,7 @@ -
+
@@ -308,7 +310,7 @@
{{ label }} - {{ doughnutChartData && doughnutChartData[i] !== undefined ? doughnutChartData[i] : 0 }} + {{ doughnutChartData && doughnutChartData[0] && doughnutChartData[0].data && doughnutChartData[0].data[i] !== undefined ? doughnutChartData[0].data[i] : 0 }}
\ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.scss b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.scss index 6d11281..f682297 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.scss +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.scss @@ -1,239 +1,292 @@ -.doughnut-chart-container { +// Chart container structure - simplified to match shield dashboard +.chart-container { + height: 100%; + min-height: 400px; // Ensure minimum height display: flex; flex-direction: column; - height: 400px; - min-height: 400px; - padding: 20px; - background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); - border-radius: 12px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - transition: all 0.3s ease; - border: 1px solid #eaeaea; -} - -.doughnut-chart-container:hover { - box-shadow: 0 6px 25px rgba(0, 0, 0, 0.2); - transform: translateY(-2px); -} - -.compact-filters-container { - display: flex; - flex-wrap: wrap; - gap: 5px; - margin-bottom: 10px; - padding: 5px; - background: #ffffff; - border: 1px solid #e9ecef; - border-radius: 6px; - min-height: 40px; -} - -.drilldown-indicator { - background-color: #e0e0e0; - padding: 10px; - margin-bottom: 15px; - border-radius: 8px; - text-align: center; - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.drilldown-text { - font-weight: bold; - color: #333; - font-size: 16px; -} - -.btn { - padding: 6px 12px; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - transition: all 0.3s ease; -} - -.btn:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); -} - -.btn-sm { - padding: 4px 8px; - font-size: 12px; -} - -.btn-secondary { - background-color: #007cba; - color: white; -} - -.btn-danger { - background-color: #dc3545; - color: white; -} - -.chart-header { - margin-bottom: 20px; - .chart-title { - font-size: 22px; - font-weight: 600; - color: #0a192f; - margin: 0; - text-align: center; - padding-bottom: 10px; - border-bottom: 2px solid #3498db; + // Filter section styling + .filter-section { + margin-bottom: 20px; + padding: 15px; + border: 1px solid #ddd; + border-radius: 4px; + background-color: #f9f9f9; } -} -.chart-wrapper { - position: relative; - flex: 1; - min-height: 250px; - margin: 15px 0; - background: #f8f9fa; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 10px; - display: flex; - align-items: center; - justify-content: center; -} - -.chart-wrapper canvas { - max-width: 100%; - max-height: 100%; - filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); -} - -.chart-wrapper canvas:hover { - filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15)); - transform: scale(1.02); - transition: all 0.3s ease; -} - -.chart-content { - position: relative; - height: 100%; - width: 100%; - display: flex; - align-items: center; - justify-content: center; - - &.loading { - opacity: 0.7; + .filter-group { + margin-bottom: 15px; - canvas { - filter: blur(2px); + h4 { + margin-top: 0; + margin-bottom: 10px; + color: #333; + font-weight: 600; } } - - .no-data-message { - text-align: center; - padding: 30px; - color: #666; - font-size: 18px; - font-style: italic; + + .filter-controls { display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; + flex-wrap: wrap; + gap: 15px; + } + + .filter-item { + flex: 1 1 300px; + min-width: 250px; + padding: 10px; + background: white; + border: 1px solid #e0e0e0; + border-radius: 4px; + } + + .filter-label { + font-weight: 500; + margin-bottom: 8px; + color: #555; + font-size: 14px; + } + + .filter-input { width: 100%; + + .filter-text-input, + .filter-select, + .filter-date { + width: 100%; + padding: 6px 12px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 14px; + } + + .filter-select { + height: 34px; + } } - - .no-data-message p { - margin: 0; + + .multiselect-container { + position: relative; } - - .loading-overlay { + + .multiselect-display { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 12px; + border: 1px solid #ccc; + border-radius: 4px; + background: white; + cursor: pointer; + min-height: 34px; + + .multiselect-label { + flex: 1; + font-size: 14px; + } + + .multiselect-value { + color: #666; + font-size: 12px; + margin-right: 8px; + } + + .dropdown-icon { + flex-shrink: 0; + transition: transform 0.2s ease; + } + + &:hover { + border-color: #999; + } + } + + .multiselect-dropdown { position: absolute; - top: 0; + top: 100%; left: 0; right: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - background: rgba(255, 255, 255, 0.8); + z-index: 1000; + background: white; + border: 1px solid #ccc; + border-top: none; + border-radius: 0 0 4px 4px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + max-height: 200px; + overflow-y: auto; - .shimmer-donut { - width: 120px; - height: 120px; - border-radius: 50%; - background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); - background-size: 200% 100%; - animation: shimmer 1.5s infinite; + .checkbox-group { + padding: 8px; + + .checkbox-item { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 0; + + .checkbox-label { + margin: 0; + font-size: 14px; + cursor: pointer; + } + } } } -} -.chart-legend { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 15px; - margin-top: 20px; - padding: 20px; - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); - border-radius: 8px; - border: 1px solid #dee2e6; - box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05); -} + .date-range { + .date-input-group { + display: flex; + align-items: center; + gap: 8px; + } + + .date-separator { + margin: 0 5px; + color: #777; + } + } -.legend-item { - display: flex; - align-items: center; - padding: 12px 20px; - background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); - border-radius: 25px; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); - transition: all 0.3s ease; - border: 1px solid #eaeaea; - cursor: pointer; -} + .toggle { + display: flex; + align-items: center; + gap: 8px; + + .toggle-label { + margin: 0; + font-size: 14px; + cursor: pointer; + } + } -.legend-item:hover { - transform: translateY(-3px); - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); - border-color: #3498db; - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); -} - -.legend-color { - width: 20px; - height: 20px; - border-radius: 50%; - margin-right: 12px; - display: inline-block; - border: 2px solid white; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); -} - -.legend-label { - font-size: 16px; - font-weight: 600; - color: #2c3e50; - margin-right: 15px; - white-space: nowrap; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); -} - -.legend-value { - font-size: 16px; - font-weight: 700; - color: #3498db; - background: linear-gradient(135deg, #e9ecef 0%, #dde1e5 100%); - padding: 6px 12px; - border-radius: 12px; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); - min-width: 40px; - text-align: center; + .filter-actions { + margin-top: 15px; + padding-top: 15px; + border-top: 1px solid #eee; + + .btn { + font-size: 13px; + } + } + + // Chart header styling + .chart-header { + margin-bottom: 20px; + + .header-row { + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #eee; + + .chart-title { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #0a192f; + } + } + } + + // Chart wrapper and content - simplified to match shield dashboard + .chart-wrapper { + flex: 1; + position: relative; + + .chart-content { + position: relative; + height: 100%; + min-height: 300px; + + &.loading { + opacity: 0.7; + + canvas { + filter: blur(2px); + } + } + + .no-data-message { + text-align: center; + padding: 20px; + color: #666; + font-style: italic; + } + + canvas { + max-width: 100%; + max-height: calc(100% - 40px); // Leave space for legend + transition: filter 0.3s ease; + } + + .loading-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.8); + + .shimmer-donut { + width: 120px; + height: 120px; + border-radius: 50%; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + } + } + } + } + + // Chart legend - simplified + .chart-legend { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 15px; + margin: 20px 0; + padding: 15px; + + .legend-item { + display: flex; + align-items: center; + padding: 8px 15px; + background: #f8f9fa; + border-radius: 20px; + border: 1px solid #e9ecef; + min-width: 120px; + justify-content: space-between; + } + + .legend-color { + width: 15px; + height: 15px; + border-radius: 50%; + margin-right: 8px; + display: inline-block; + flex-shrink: 0; + } + + .legend-label { + font-size: 14px; + font-weight: 500; + color: #2c3e50; + margin-right: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .legend-value { + font-size: 14px; + font-weight: 600; + color: #3498db; + min-width: 30px; + text-align: right; + } + } } @keyframes shimmer { @@ -245,240 +298,46 @@ } } -// Filter section styles -.filter-section { - margin-bottom: 20px; - padding: 15px; - border: 1px solid #ddd; - border-radius: 4px; - background-color: #f9f9f9; -} - -.filter-group { - margin-bottom: 15px; - - h4 { - margin-top: 0; - margin-bottom: 10px; - color: #333; - font-weight: 600; - } -} - -.filter-controls { - display: flex; - flex-wrap: wrap; - gap: 15px; -} - -.filter-item { - flex: 1 1 300px; - min-width: 250px; - padding: 10px; - background: white; - border: 1px solid #e0e0e0; - border-radius: 4px; -} - -.filter-label { - font-weight: 500; - margin-bottom: 8px; - color: #555; - font-size: 14px; -} - -.filter-input { - width: 100%; - - .filter-text-input, - .filter-select, - .filter-date { - width: 100%; - padding: 6px 12px; - border: 1px solid #ccc; - border-radius: 4px; - font-size: 14px; - } - - .filter-select { - height: 34px; - } -} - -.multiselect-container { - position: relative; -} - -.multiselect-display { - display: flex; - align-items: center; - justify-content: space-between; - padding: 6px 12px; - border: 1px solid #ccc; - border-radius: 4px; - background: white; - cursor: pointer; - min-height: 34px; - - .multiselect-label { - flex: 1; - font-size: 14px; - } - - .multiselect-value { - color: #666; - font-size: 12px; - margin-right: 8px; - } - - .dropdown-icon { - flex-shrink: 0; - transition: transform 0.2s ease; - } - - &:hover { - border-color: #999; - } -} - -.multiselect-dropdown { - position: absolute; - top: 100%; - left: 0; - right: 0; - z-index: 1000; - background: white; - border: 1px solid #ccc; - border-top: none; - border-radius: 0 0 4px 4px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); - max-height: 200px; - overflow-y: auto; - - .checkbox-group { - padding: 8px; +// Responsive design +@media (max-width: 768px) { + .chart-container { + .filter-controls { + flex-direction: column; + } - .checkbox-item { - display: flex; + .filter-item { + min-width: 100%; + } + + .chart-header { + .header-row { + .chart-title { + font-size: 16px; + } + } + } + + .chart-content { + min-height: 250px; + } + + .chart-legend { + flex-direction: column; align-items: center; - gap: 8px; - padding: 4px 0; - .checkbox-label { - margin: 0; - font-size: 14px; - cursor: pointer; + .legend-item { + width: 100%; + max-width: 300px; + justify-content: space-between; + } + } + + .chart-content { + min-height: 250px; + + canvas { + max-height: calc(100% - 60px); // More space for legend on mobile } } } -} - -.date-range { - .date-input-group { - display: flex; - align-items: center; - gap: 8px; - } - - .date-separator { - margin: 0 5px; - color: #777; - } -} - -.toggle { - display: flex; - align-items: center; - gap: 8px; - - .toggle-label { - margin: 0; - font-size: 14px; - cursor: pointer; - } -} - -.filter-actions { - margin-top: 15px; - padding-top: 15px; - border-top: 1px solid #eee; - - .btn { - font-size: 13px; - } -} - -// New header row styling -.header-row { - margin-bottom: 15px; - padding-bottom: 10px; - border-bottom: 1px solid #eee; - - .chart-title { - margin: 0; - font-size: 18px; - font-weight: 600; - color: #333; - text-align: left; - padding-bottom: 0; - border-bottom: none; - } -} - -/* Responsive design */ -@media (max-width: 768px) { - .doughnut-chart-container { - padding: 15px; - } - - .chart-header .chart-title { - font-size: 18px; - } - - .drilldown-indicator { - flex-direction: column; - gap: 5px; - } - - .drilldown-text { - font-size: 14px; - } - - .chart-wrapper { - min-height: 200px; - } - - .chart-legend { - flex-direction: column; - align-items: center; - } - - .legend-item { - width: 100%; - max-width: 300px; - justify-content: space-between; - } - - .no-data-message { - font-size: 16px; - padding: 20px; - } - - .compact-filters-container { - flex-wrap: wrap; - } - - .filter-controls { - flex-direction: column; - } - - .filter-item { - min-width: 100%; - } - - .header-row { - .chart-title { - font-size: 16px; - } - } } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.ts index c7373b5..6faa761 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.ts @@ -36,7 +36,21 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations public doughnutChartLabels: string[] = ["Category A", "Category B", "Category C"]; - public doughnutChartData: number[] = [30, 50, 20]; + public doughnutChartData: any[] = [ + { + data: [30, 50, 20], + backgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56' + ], + hoverBackgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56' + ] + } + ]; public doughnutChartType: string = "doughnut"; public doughnutChartOptions: any = { responsive: true, @@ -72,6 +86,14 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck borderWidth: 2, borderColor: '#fff' } + }, + layout: { + padding: { + top: 20, + bottom: 20, + left: 20, + right: 20 + } } }; @@ -96,6 +118,9 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck // No data state noDataAvailable: boolean = false; + // Loading state + isLoading: boolean = false; + // Flag to prevent infinite loops private isFetchingData: boolean = false; @@ -143,17 +168,33 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck if (this.doughnutChartLabels.length === 0 && this.doughnutChartData.length === 0) { // Add default data to ensure chart visibility this.doughnutChartLabels = ["Category A", "Category B", "Category C"]; - this.doughnutChartData = [30, 50, 20]; + this.doughnutChartData = [ + { + data: [30, 50, 20], + backgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56' + ], + hoverBackgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56' + ] + } + ]; } // Ensure we have matching arrays - if (this.doughnutChartLabels.length !== this.doughnutChartData.length) { - const maxLength = Math.max(this.doughnutChartLabels.length, this.doughnutChartData.length); + if (this.doughnutChartLabels.length !== (this.doughnutChartData[0]?.data?.length || 0)) { + const maxLength = Math.max(this.doughnutChartLabels.length, this.doughnutChartData[0]?.data?.length || 0); while (this.doughnutChartLabels.length < maxLength) { this.doughnutChartLabels.push(`Label ${this.doughnutChartLabels.length + 1}`); } - while (this.doughnutChartData.length < maxLength) { - this.doughnutChartData.push(0); + if (this.doughnutChartData[0]) { + while (this.doughnutChartData[0].data.length < maxLength) { + this.doughnutChartData[0].data.push(0); + } } } } @@ -529,14 +570,18 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck } fetchChartData(): void { + // Set loading state + this.isLoading = true; + // Set flag to prevent recursive calls this.isFetchingData = true; // If we're in drilldown mode, fetch the appropriate drilldown data if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) { this.fetchDrilldownData(); - // Reset flag after fetching + // Reset flags after fetching this.isFetchingData = false; + this.isLoading = false; return; } @@ -618,8 +663,9 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck this.noDataAvailable = true; this.doughnutChartLabels = []; this.doughnutChartData = []; - // Reset flag after fetching + // Reset flags after fetching this.isFetchingData = false; + this.isLoading = false; return; } @@ -628,34 +674,44 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck // Backend has already filtered the data, just display it this.noDataAvailable = data.chartLabels.length === 0; this.doughnutChartLabels = data.chartLabels; - this.doughnutChartData = data.chartData; - // Trigger change detection - this.doughnutChartData = [...this.doughnutChartData]; + this.doughnutChartData = [ + { + data: data.chartData, + backgroundColor: this.chartColors.slice(0, data.chartData.length), + hoverBackgroundColor: this.chartColors.slice(0, data.chartData.length) + } + ]; console.log('Updated doughnut chart with data:', { labels: this.doughnutChartLabels, data: this.doughnutChartData }); } else if (data && data.labels && data.datasets) { // Backend has already filtered the data, just display it this.noDataAvailable = data.labels.length === 0; this.doughnutChartLabels = data.labels; - this.doughnutChartData = data.datasets[0]?.data || []; - // Trigger change detection - this.doughnutChartData = [...this.doughnutChartData]; + this.doughnutChartData = data.datasets; console.log('Updated doughnut chart with legacy data format:', { labels: this.doughnutChartLabels, data: this.doughnutChartData }); } else { console.warn('Received data does not have expected structure', data); this.noDataAvailable = true; this.doughnutChartLabels = []; - this.doughnutChartData = []; + this.doughnutChartData = [ + { + data: [], + backgroundColor: [], + hoverBackgroundColor: [] + } + ]; } - // Reset flag after fetching + // Reset flags after fetching this.isFetchingData = false; + this.isLoading = false; }, (error) => { console.error('Error fetching doughnut chart data:', error); this.noDataAvailable = true; this.doughnutChartLabels = []; this.doughnutChartData = []; - // Reset flag after fetching + // Reset flags after fetching this.isFetchingData = false; + this.isLoading = false; // Keep default data in case of error } ); @@ -664,8 +720,9 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck this.noDataAvailable = true; this.doughnutChartLabels = []; this.doughnutChartData = []; - // Reset flag after fetching + // Reset flags after fetching this.isFetchingData = false; + this.isLoading = false; } } @@ -812,23 +869,37 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck // Backend has already filtered the data, just display it this.noDataAvailable = data.chartLabels.length === 0; this.doughnutChartLabels = data.chartLabels; - this.doughnutChartData = data.chartData; - // Trigger change detection - this.doughnutChartData = [...this.doughnutChartData]; + this.doughnutChartData = [ + { + data: data.chartData, + backgroundColor: this.chartColors.slice(0, data.chartData.length), + hoverBackgroundColor: this.chartColors.slice(0, data.chartData.length) + } + ]; console.log('Updated doughnut chart with drilldown data:', { labels: this.doughnutChartLabels, data: this.doughnutChartData }); + // Set loading state to false + this.isLoading = false; } else if (data && data.labels && data.datasets) { // Backend has already filtered the data, just display it this.noDataAvailable = data.labels.length === 0; this.doughnutChartLabels = data.labels; - this.doughnutChartData = data.datasets[0]?.data || []; - // Trigger change detection - this.doughnutChartData = [...this.doughnutChartData]; + this.doughnutChartData = data.datasets; console.log('Updated doughnut chart with drilldown legacy data format:', { labels: this.doughnutChartLabels, data: this.doughnutChartData }); + // Set loading state to false + this.isLoading = false; } else { console.warn('Drilldown received data does not have expected structure', data); this.noDataAvailable = true; this.doughnutChartLabels = []; - this.doughnutChartData = []; + this.doughnutChartData = [ + { + data: [], + backgroundColor: [], + hoverBackgroundColor: [] + } + ]; + // Set loading state to false + this.isLoading = false; } }, (error) => { @@ -836,9 +907,14 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck this.noDataAvailable = true; this.doughnutChartLabels = []; this.doughnutChartData = []; + // Set loading state to false + this.isLoading = false; // Keep current data in case of error } ); + + // Set loading state + this.isLoading = true; } // Reset to original data (go back to base level) @@ -855,7 +931,7 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck console.log('Restored original labels'); } if (this.originalDoughnutChartData.length > 0) { - this.doughnutChartData = [...this.originalDoughnutChartData]; + this.doughnutChartData = JSON.parse(JSON.stringify(this.originalDoughnutChartData)); console.log('Restored original data'); } diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/scatter-chart/scatter-chart.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/scatter-chart/scatter-chart.component.html index 4b606a3..14cd562 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/scatter-chart/scatter-chart.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/scatter-chart/scatter-chart.component.html @@ -288,6 +288,7 @@
diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/scatter-chart/scatter-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/scatter-chart/scatter-chart.component.ts index 5d90f80..8cffcbc 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/scatter-chart/scatter-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/scatter-chart/scatter-chart.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { ChartData,ChartDataset } from 'chart.js'; +import { ChartData,ChartDataset,ChartOptions } from 'chart.js'; import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; import { FilterService } from '../../common-filter/filter.service'; import { Subscription } from 'rxjs'; @@ -113,6 +113,81 @@ export class ScatterChartComponent implements OnInit, OnChanges { ], }, ]; + + public scatterChartOptions: any = { + responsive: true, + maintainAspectRatio: false, + scales: { + x: { + type: 'linear', + position: 'bottom', + title: { + display: true, + text: 'X Axis' + }, + ticks: { + autoSkip: true, + maxTicksLimit: 10, + callback: function(value: any) { + if (typeof value === 'number') { + // Format large numbers for better readability + if (Math.abs(value) >= 1000000) { + return (value / 1000000).toFixed(1) + 'M'; + } else if (Math.abs(value) >= 1000) { + return (value / 1000).toFixed(1) + 'K'; + } + return value.toString(); + } + return value; + } + }, + grid: { + display: true, + color: 'rgba(0, 0, 0, 0.1)' + } + }, + y: { + title: { + display: true, + text: 'Y Axis' + }, + ticks: { + autoSkip: true, + maxTicksLimit: 10, + callback: function(value: any) { + if (typeof value === 'number') { + // Format large numbers for better readability + if (Math.abs(value) >= 1000000) { + return (value / 1000000).toFixed(1) + 'M'; + } else if (Math.abs(value) >= 1000) { + return (value / 1000).toFixed(1) + 'K'; + } + return value.toString(); + } + return value; + } + }, + grid: { + display: true, + color: 'rgba(0, 0, 0, 0.1)' + } + } + }, + plugins: { + legend: { + display: true, + position: 'top', + }, + tooltip: { + callbacks: { + label: function(context: any) { + return `(${context.parsed.x}, ${context.parsed.y})`; + } + } + } + } + }; + public scatterChartType: string = 'scatter'; // Multi-layer drilldown state tracking @@ -457,6 +532,56 @@ export class ScatterChartComponent implements OnInit, OnChanges { return data; } + // Transform the data properly for scatter chart + // Assuming labels are x-values and data[0].data are y-values + if (labels && data && data.length > 0 && data[0].data) { + const yValues = data[0].data; + const label = data[0].label || 'Dataset 1'; + + // Create scatter points from labels (x) and data (y) + const scatterPoints = []; + const minLength = Math.min(labels.length, yValues.length); + + for (let i = 0; i < minLength; i++) { + // Convert to numbers if they're strings + const x = typeof labels[i] === 'string' ? parseFloat(labels[i]) : labels[i]; + const y = typeof yValues[i] === 'string' ? parseFloat(yValues[i]) : yValues[i]; + + // Only add valid points + if (!isNaN(x) && !isNaN(y)) { + scatterPoints.push({ x, y }); + } + } + + // Generate different colors for each point to avoid all points showing the same color + const backgroundColors = []; + const borderColors = []; + + for (let i = 0; i < scatterPoints.length; i++) { + // Generate a color based on the point index for variety + const hue = (i * 137.508) % 360; // Use golden angle to spread colors + backgroundColors.push(`hsla(${hue}, 70%, 50%, 0.6)`); + borderColors.push(`hsla(${hue}, 70%, 40%, 1)`); + } + + // Create a single dataset with all scatter points + const scatterDatasets: ChartDataset[] = [ + { + data: scatterPoints, + label: label, + pointRadius: 8, + pointHoverRadius: 10, + backgroundColor: backgroundColors, + borderColor: borderColors, + borderWidth: 1, + pointHoverBackgroundColor: 'rgba(255, 99, 132, 1)', + } + ]; + + console.log('Transformed scatter data:', scatterDatasets); + return scatterDatasets; + } + // Otherwise, create a default scatter dataset const scatterDatasets: ChartDataset[] = [ { @@ -587,11 +712,19 @@ export class ScatterChartComponent implements OnInit, OnChanges { // Scatter charts expect data in the format: {x: number, y: number} this.noDataAvailable = data.chartLabels.length === 0; this.scatterChartData = this.transformToScatterData(data.chartLabels, data.chartData); + + // Update chart options with axis titles + this.updateChartOptionsWithAxisTitles(); + console.log('Updated scatter chart with data:', this.scatterChartData); } else if (data && data.labels && data.datasets) { // Handle the original expected format as fallback this.noDataAvailable = data.labels.length === 0; this.scatterChartData = data.datasets; + + // Update chart options with axis titles + this.updateChartOptionsWithAxisTitles(); + console.log('Updated scatter chart with legacy data format:', this.scatterChartData); } else { console.warn('Scatter chart received data does not have expected structure', data); @@ -619,6 +752,20 @@ export class ScatterChartComponent implements OnInit, OnChanges { } } + // Update chart options with axis titles + private updateChartOptionsWithAxisTitles(): void { + // Update X axis title + if (this.scatterChartOptions.scales && this.scatterChartOptions.scales.x) { + this.scatterChartOptions.scales.x.title.text = this.xAxis || 'X Axis'; + } + + // Update Y axis title + if (this.scatterChartOptions.scales && this.scatterChartOptions.scales.y) { + const yAxisLabel = Array.isArray(this.yAxis) ? this.yAxis[0] : this.yAxis; + this.scatterChartOptions.scales.y.title.text = yAxisLabel || 'Y Axis'; + } + } + // Fetch drilldown data based on current drilldown level fetchDrilldownData(): void { console.log('Fetching drilldown data, current level:', this.currentDrilldownLevel); @@ -928,4 +1075,4 @@ export class ScatterChartComponent implements OnInit, OnChanges { // Clean up document click handler this.removeDocumentClickHandler(); } -} \ No newline at end of file +}