diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts index edba584..ee6b1f5 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts @@ -1,5 +1,6 @@ import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; import { Dashboard3Service } from '../../../../../../services/builder/dashboard3.service'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-bar-chart', @@ -28,7 +29,7 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input - // @Input() baseFilters: any[] = []; // Removed base filters input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -49,7 +50,11 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { // No data state noDataAvailable: boolean = false; - // Filter update timeout property removed + // Flag to prevent infinite loops + private isFetchingData: boolean = false; + + // Subscriptions to unsubscribe on destroy + private subscriptions: Subscription[] = []; constructor(private dashboardService: Dashboard3Service) { } @@ -66,6 +71,7 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -73,10 +79,10 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Only fetch data if the actual chart configuration changed - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { + drilldownLayersChanged)) { console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } @@ -88,12 +94,15 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { } } - // Filter parameters method removed - fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -111,9 +120,23 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { let url = `chart/getdashjson/bar?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Bar chart data URL:', url); + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'bar', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + const subscription = this.dashboardService.getChartData(this.table, 'bar', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received bar chart data:', data); if (data === null) { @@ -121,6 +144,8 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { this.noDataAvailable = true; this.barChartLabels = []; this.barChartData = []; + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -131,7 +156,7 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { this.barChartLabels = data.chartLabels; this.barChartData = data.chartData; // Trigger change detection - this.barChartData = [...this.barChartData]; + // this.barChartData = [...this.barChartData]; console.log('Updated bar chart with data:', { labels: this.barChartLabels, data: this.barChartData }); } else if (data && data.labels && data.datasets) { // Backend has already filtered the data, just display it @@ -139,7 +164,7 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { this.barChartLabels = data.labels; this.barChartData = data.datasets; // Trigger change detection - this.barChartData = [...this.barChartData]; + // this.barChartData = [...this.barChartData]; console.log('Updated bar chart with legacy data format:', { labels: this.barChartLabels, data: this.barChartData }); } else { console.warn('Bar chart received data does not have expected structure', data); @@ -147,20 +172,29 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { this.barChartLabels = []; this.barChartData = []; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching bar chart data:', error); this.noDataAvailable = true; this.barChartLabels = []; this.barChartData = []; + // Reset flag after fetching + this.isFetchingData = false; // Keep default data in case of error } ); + + // Add subscription to array for cleanup + this.subscriptions.push(subscription); } else { console.log('Missing required data for bar chart:', { table: this.table, xAxis: this.xAxis, yAxis: this.yAxis, connection: this.connection }); this.noDataAvailable = true; this.barChartLabels = []; this.barChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -249,9 +283,23 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { } console.log('Drilldown data URL:', url); + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'bar', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + const subscription = this.dashboardService.getChartData(actualApiUrl, 'bar', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -269,15 +317,13 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { this.barChartLabels = data.chartLabels; this.barChartData = data.chartData; // Trigger change detection - this.barChartData = [...this.barChartData]; + // this.barChartData = [...this.barChartData]; console.log('Updated bar chart with drilldown data:', { labels: this.barChartLabels, data: this.barChartData }); } else if (data && data.labels && data.datasets) { // Backend has already filtered the data, just display it this.noDataAvailable = data.labels.length === 0; this.barChartLabels = data.labels; this.barChartData = data.datasets; - // Trigger change detection - this.barChartData = [...this.barChartData]; console.log('Updated bar chart with drilldown legacy data format:', { labels: this.barChartLabels, data: this.barChartData }); } else { console.warn('Drilldown received data does not have expected structure', data); @@ -294,6 +340,9 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { // Keep current data in case of error } ); + + // Add subscription to array for cleanup + this.subscriptions.push(subscription); } // Reset to original data (go back to base level) @@ -306,11 +355,13 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { this.drilldownStack = []; if (this.originalBarChartLabels.length > 0) { - this.barChartLabels = [...this.originalBarChartLabels]; + // Create a deep copy to avoid reference issues + this.barChartLabels = JSON.parse(JSON.stringify(this.originalBarChartLabels)); console.log('Restored original labels'); } if (this.originalBarChartData.length > 0) { - this.barChartData = [...this.originalBarChartData]; + // Create a deep copy to avoid reference issues + this.barChartData = JSON.parse(JSON.stringify(this.originalBarChartData)); console.log('Restored original data'); } @@ -352,6 +403,29 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { } } + // Ensure labels and data arrays have the same length + private syncLabelAndDataArrays(): void { + // For bar charts, we need to ensure all datasets have the same number of data points + if (this.barChartData && this.barChartData.length > 0 && this.barChartLabels) { + const labelCount = this.barChartLabels.length; + + this.barChartData.forEach(dataset => { + if (dataset.data) { + // If dataset has more data points than labels, truncate the data + if (dataset.data.length > labelCount) { + dataset.data = dataset.data.slice(0, labelCount); + } + // If dataset has fewer data points than labels, pad with zeros + else if (dataset.data.length < labelCount) { + while (dataset.data.length < labelCount) { + dataset.data.push(0); + } + } + } + }); + } + } + // events public chartClicked(e: any): void { console.log('Bar chart clicked:', e); @@ -369,8 +443,10 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { // If we're not at the base level, store original data if (this.currentDrilldownLevel === 0) { // Store original data before entering drilldown mode - this.originalBarChartLabels = [...this.barChartLabels]; - this.originalBarChartData = [...this.barChartData]; + // Create a deep copy to avoid reference issues + this.originalBarChartLabels = JSON.parse(JSON.stringify(this.barChartLabels)); + // Create a deep copy to avoid reference issues + this.originalBarChartData = JSON.parse(JSON.stringify(this.barChartData)); console.log('Stored original data for drilldown'); } @@ -442,6 +518,22 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { } ngOnDestroy() { - // Cleanup method - no timeouts to clean up + // Unsubscribe from all subscriptions to prevent memory leaks + console.log('BarChartComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); + this.subscriptions.forEach(subscription => { + if (subscription && !subscription.closed) { + subscription.unsubscribe(); + } + }); + this.subscriptions = []; + + // Clear data to help with garbage collection + this.barChartLabels = []; + this.barChartData = []; + this.drilldownStack = []; + this.originalBarChartLabels = []; + this.originalBarChartData = []; + + console.log('BarChartComponent destroyed and cleaned up'); } } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bubble-chart/bubble-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bubble-chart/bubble-chart.component.ts index 40d4e6a..01da800 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bubble-chart/bubble-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bubble-chart/bubble-chart.component.ts @@ -29,6 +29,7 @@ export class BubbleChartComponent implements OnInit, OnChanges { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -90,6 +91,9 @@ export class BubbleChartComponent implements OnInit, OnChanges { // No data state noDataAvailable: boolean = false; + + // Flag to prevent infinite loops + private isFetchingData: boolean = false; constructor(private dashboardService: Dashboard3Service) { } @@ -105,6 +109,7 @@ export class BubbleChartComponent implements OnInit, OnChanges { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -112,19 +117,24 @@ export class BubbleChartComponent implements OnInit, OnChanges { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } } fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -135,19 +145,36 @@ export class BubbleChartComponent implements OnInit, OnChanges { // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/bubble?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Bubble chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'bubble', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'bubble', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received bubble chart data:', data); if (data === null) { console.warn('Bubble chart API returned null data. Check if the API endpoint is working correctly.'); this.noDataAvailable = true; this.bubbleChartData = []; + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -168,17 +195,23 @@ export class BubbleChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.bubbleChartData = []; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching bubble chart data:', error); this.noDataAvailable = true; this.bubbleChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } ); } else { console.log('Missing required data for bubble chart:', { table: this.table, xAxis: this.xAxis, yAxis: this.yAxis, connection: this.connection }); this.noDataAvailable = true; this.bubbleChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -258,13 +291,27 @@ export class BubbleChartComponent implements OnInit, OnChanges { console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/bubble?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'bubble', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'bubble', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -300,6 +347,35 @@ export class BubbleChartComponent implements OnInit, OnChanges { ); } + // Transform chart data to bubble chart format + private transformToBubbleData(labels: string[], datasets: any[]): ChartDataset[] { + // For bubble charts, we need to transform the data into bubble format + // Bubble charts expect data in the format: {x: number, y: number, r: number} + + // This is a simple transformation - in a real implementation, you might want to + // create a more sophisticated mapping based on your data structure + return datasets.map((dataset, index) => { + // Create bubble data points + const bubbleData = labels.map((label, i) => { + // Use x-axis data as x coordinate, y-axis data as y coordinate, and a fixed radius + const xValue = dataset.data[i] || 0; + const yValue = i < datasets.length ? (datasets[i].data[index] || 0) : 0; + const radius = 10; // Fixed radius for now + + return { x: xValue, y: yValue, r: radius }; + }); + + return { + data: bubbleData, + label: dataset.label || `Dataset ${index + 1}`, + backgroundColor: dataset.backgroundColor || `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 0.6)`, + borderColor: dataset.borderColor || 'rgba(0, 0, 0, 1)', + hoverBackgroundColor: dataset.hoverBackgroundColor || 'rgba(255, 255, 255, 0.8)', + hoverBorderColor: dataset.hoverBorderColor || 'rgba(0, 0, 0, 1)' + }; + }); + } + // Reset to original data (go back to base level) resetToOriginalData(): void { console.log('Resetting to original data'); @@ -351,70 +427,6 @@ export class BubbleChartComponent implements OnInit, OnChanges { } } - private transformToBubbleData(labels: any[], chartData: any[]): ChartDataset[] { - // Transform the API data into bubble chart format - const datasets: ChartDataset[] = []; - - // Create a dataset for each data series - chartData.forEach((series, index) => { - // For bubble charts, we need x, y, and r values - // We'll use the labels as x values and the data as y values - // For radius (r), we'll use a default value or derive it from the data - - const bubbleData = labels.map((label, i) => { - const xValue = isNaN(Number(label)) ? i : Number(label); - const yValue = series.data && series.data[i] !== undefined ? - (isNaN(Number(series.data[i])) ? 0 : Number(series.data[i])) : 0; - // Use a default radius or derive from data - const radius = Math.abs(yValue) > 0 ? Math.abs(yValue) / 10 : 5; - - return { - x: xValue, - y: yValue, - r: radius - }; - }); - - datasets.push({ - data: bubbleData, - label: series.label || `Series ${index + 1}`, - backgroundColor: this.getBackgroundColor(index), - borderColor: this.getBorderColor(index), - hoverBackgroundColor: this.getHoverBackgroundColor(index), - hoverBorderColor: this.getHoverBorderColor(index), - }); - }); - - return datasets; - } - - private getBackgroundColor(index: number): string { - const colors = [ - 'rgba(255, 0, 0, 0.6)', // Red - 'rgba(0, 255, 0, 0.6)', // Green - 'rgba(0, 0, 255, 0.6)', // Blue - 'rgba(255, 255, 0, 0.6)', // Yellow - 'rgba(255, 0, 255, 0.6)', // Magenta - 'rgba(0, 255, 255, 0.6)', // Cyan - ]; - return colors[index % colors.length]; - } - - private getBorderColor(index: number): string { - const colors = ['blue', 'green', 'red', 'orange', 'purple', 'cyan']; - return colors[index % colors.length]; - } - - private getHoverBackgroundColor(index: number): string { - const colors = ['purple', 'yellow', 'orange', 'red', 'blue', 'green']; - return colors[index % colors.length]; - } - - private getHoverBorderColor(index: number): string { - const colors = ['red', 'blue', 'green', 'purple', 'yellow', 'orange']; - return colors[index % colors.length]; - } - // events public chartClicked(e: any): void { console.log('Bubble chart clicked:', e); @@ -424,18 +436,16 @@ export class BubbleChartComponent implements OnInit, OnChanges { // Get the index of the clicked element const clickedIndex = e.active[0].index; - // Get the dataset index - const datasetIndex = e.active[0].datasetIndex; + // Get the label of the clicked element + // For bubble charts, we might not have labels in the same way as other charts + const clickedLabel = `Bubble ${clickedIndex}`; - // Get the data point - const dataPoint = this.bubbleChartData[datasetIndex].data[clickedIndex]; - - console.log('Clicked on bubble:', { datasetIndex: datasetIndex, index: clickedIndex, dataPoint: dataPoint }); + console.log('Clicked on bubble:', { index: clickedIndex, label: clickedLabel }); // If we're not at the base level, store original data if (this.currentDrilldownLevel === 0) { // Store original data before entering drilldown mode - this.originalBubbleChartData = JSON.parse(JSON.stringify(this.bubbleChartData)); + this.originalBubbleChartData = [...this.bubbleChartData]; console.log('Stored original data for drilldown'); } @@ -474,16 +484,12 @@ export class BubbleChartComponent implements OnInit, OnChanges { // If there's a drilldown configuration for the next level, proceed if (hasDrilldownConfig) { - // For bubble charts, we'll use the x value as the clicked value - const clickedValue = dataPoint && (dataPoint as any).x !== undefined ? - (dataPoint as any).x.toString() : ''; - // Add this click to the drilldown stack const stackEntry = { level: nextDrilldownLevel, - datasetIndex: datasetIndex, clickedIndex: clickedIndex, - clickedValue: clickedValue + clickedLabel: clickedLabel, + clickedValue: clickedLabel // Using label as value for now }; this.drilldownStack.push(stackEntry); 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 202a1e9..a1e5d36 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 @@ -28,6 +28,7 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -91,6 +92,9 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck // No data state noDataAvailable: boolean = false; + + // Flag to prevent infinite loops + private isFetchingData: boolean = false; constructor(private dashboardService: Dashboard3Service) { } @@ -148,6 +152,7 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -155,11 +160,11 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } } @@ -174,9 +179,14 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck } fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -187,13 +197,28 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/doughnut?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Doughnut chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'doughnut', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'doughnut', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received doughnut chart data:', data); if (data === null) { @@ -201,6 +226,10 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck this.noDataAvailable = true; this.doughnutChartLabels = []; this.doughnutChartData = []; + // Validate and sanitize data to show default data + this.validateChartData(); + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -248,31 +277,34 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck this.noDataAvailable = true; this.doughnutChartLabels = []; this.doughnutChartData = []; - // Validate and sanitize data + // Validate and sanitize data to show default data this.validateChartData(); } - - // Log final state for debugging - console.log('Final doughnut chart state:', { - labels: this.doughnutChartLabels, - data: this.doughnutChartData, - labelsLength: this.doughnutChartLabels.length, - dataLength: this.doughnutChartData.length - }); + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching doughnut chart data:', error); this.noDataAvailable = true; this.doughnutChartLabels = []; this.doughnutChartData = []; + // Validate and sanitize data to show default data + this.validateChartData(); + // Reset flag after fetching + this.isFetchingData = false; } ); } else { - console.log('Missing required data for doughnut chart:', { table: this.table, xAxis: this.xAxis, yAxis: this.yAxis, connection: this.connection }); + console.log('Missing required data for doughnut chart, showing default data:', { table: this.table, xAxis: this.xAxis, yAxis: this.yAxis, connection: this.connection }); // Don't set noDataAvailable to true when there's no required data // This allows static data to be displayed - // Only validate the chart data to ensure we have some data to display + this.noDataAvailable = false; + // Validate the chart data to ensure we have some data to display this.validateChartData(); + // Force a redraw to ensure the chart displays + this.doughnutChartData = [...this.doughnutChartData]; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -354,13 +386,27 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/doughnut?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'doughnut', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'doughnut', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -417,20 +463,13 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck // Validate and sanitize data this.validateChartData(); } - - // Log final state for debugging - console.log('Final doughnut chart state:', { - labels: this.doughnutChartLabels, - data: this.doughnutChartData, - labelsLength: this.doughnutChartLabels.length, - dataLength: this.doughnutChartData.length - }); }, (error) => { console.error('Error fetching drilldown data:', error); this.noDataAvailable = true; this.doughnutChartLabels = []; this.doughnutChartData = []; + // Keep current data in case of error } ); } diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/dynamic-chart/dynamic-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/dynamic-chart/dynamic-chart.component.ts index 856d9e0..cc46f31 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/dynamic-chart/dynamic-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/dynamic-chart/dynamic-chart.component.ts @@ -30,6 +30,7 @@ export class DynamicChartComponent implements OnInit, OnChanges { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -50,6 +51,7 @@ export class DynamicChartComponent implements OnInit, OnChanges { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -57,12 +59,11 @@ export class DynamicChartComponent implements OnInit, OnChanges { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); - // Only fetch data if xAxis, yAxis, table, connection, or drilldown config has changed (and it's not the first change) + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } } @@ -100,11 +101,19 @@ export class DynamicChartComponent implements OnInit, OnChanges { // No data state noDataAvailable: boolean = false; + + // Flag to prevent infinite loops + private isFetchingData: boolean = false; fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -115,13 +124,28 @@ export class DynamicChartComponent implements OnInit, OnChanges { // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/dynamic?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Dynamic chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'dynamic', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'dynamic', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received dynamic chart data:', data); if (data === null) { @@ -129,6 +153,8 @@ export class DynamicChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.dynamicChartLabels = []; this.dynamicChartData = []; + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -155,12 +181,16 @@ export class DynamicChartComponent implements OnInit, OnChanges { this.dynamicChartLabels = []; this.dynamicChartData = []; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching dynamic chart data:', error); this.noDataAvailable = true; this.dynamicChartLabels = []; this.dynamicChartData = []; + // Reset flag after fetching + this.isFetchingData = false; // Keep default data in case of error } ); @@ -169,6 +199,8 @@ export class DynamicChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.dynamicChartLabels = []; this.dynamicChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -250,13 +282,27 @@ export class DynamicChartComponent implements OnInit, OnChanges { console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/dynamic?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'dynamic', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'dynamic', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -356,7 +402,7 @@ export class DynamicChartComponent implements OnInit, OnChanges { this.resetToOriginalData(); } } - + // events public chartClicked(e: any): void { console.log('Dynamic chart clicked:', e); @@ -366,13 +412,10 @@ export class DynamicChartComponent implements OnInit, OnChanges { // Get the index of the clicked element const clickedIndex = e.active[0].index; - // Get the label of the clicked element (if available) - let clickedLabel = ''; - if (this.dynamicChartLabels && this.dynamicChartLabels[clickedIndex]) { - clickedLabel = this.dynamicChartLabels[clickedIndex]; - } + // Get the label of the clicked element + const clickedLabel = this.dynamicChartLabels[clickedIndex]; - console.log('Clicked on dynamic chart element:', { index: clickedIndex, label: clickedLabel }); + console.log('Clicked on dynamic chart point:', { index: clickedIndex, label: clickedLabel }); // If we're not at the base level, store original data if (this.currentDrilldownLevel === 0) { @@ -445,11 +488,18 @@ export class DynamicChartComponent implements OnInit, OnChanges { } } - public chartHovered(e: any): void { - console.log(e); - } - + public chartHovered(e: any): void { + console.log(e); + } + public randomize(): void { - this.barChartType = this.barChartType === 'bar' ? 'line' : 'bar'; + let _dynamicChartData: Array = new Array(this.dynamicChartData.length); + for (let i = 0; i < this.dynamicChartData.length; i++) { + _dynamicChartData[i] = {data: new Array(this.dynamicChartData[i].data.length), label: this.dynamicChartData[i].label}; + for (let j = 0; j < this.dynamicChartData[i].data.length; j++) { + _dynamicChartData[i].data[j] = Math.floor((Math.random() * 100) + 1); + } + } + this.dynamicChartData = _dynamicChartData; } } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/financial-chart/financial-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/financial-chart/financial-chart.component.ts index 4e957cd..5e3b85b 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/financial-chart/financial-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/financial-chart/financial-chart.component.ts @@ -28,6 +28,7 @@ export class FinancialChartComponent implements OnInit, OnChanges { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -46,6 +47,7 @@ export class FinancialChartComponent implements OnInit, OnChanges { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -53,12 +55,11 @@ export class FinancialChartComponent implements OnInit, OnChanges { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); - // Only fetch data if xAxis, yAxis, table, connection, or drilldown config has changed (and it's not the first change) + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } } @@ -80,10 +81,18 @@ export class FinancialChartComponent implements OnInit, OnChanges { // No data state noDataAvailable: boolean = false; + // Flag to prevent infinite loops + private isFetchingData: boolean = false; + fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -94,13 +103,28 @@ export class FinancialChartComponent implements OnInit, OnChanges { // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/financial?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Financial chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'financial', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'financial', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received financial chart data:', data); if (data === null) { @@ -108,6 +132,8 @@ export class FinancialChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.financialChartLabels = []; this.financialChartData = []; + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -146,12 +172,16 @@ export class FinancialChartComponent implements OnInit, OnChanges { this.financialChartLabels = []; this.financialChartData = []; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching financial chart data:', error); this.noDataAvailable = true; this.financialChartLabels = []; this.financialChartData = []; + // Reset flag after fetching + this.isFetchingData = false; // Keep default data in case of error } ); @@ -160,6 +190,8 @@ export class FinancialChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.financialChartLabels = []; this.financialChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -241,13 +273,27 @@ export class FinancialChartComponent implements OnInit, OnChanges { console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/financial?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'financial', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'financial', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -359,7 +405,7 @@ export class FinancialChartComponent implements OnInit, OnChanges { this.resetToOriginalData(); } } - + // events public chartClicked(e: any): void { console.log('Financial chart clicked:', e); @@ -369,13 +415,10 @@ export class FinancialChartComponent implements OnInit, OnChanges { // Get the index of the clicked element const clickedIndex = e.active[0].index; - // Get the label of the clicked element (if available) - let clickedLabel = ''; - if (this.financialChartLabels && this.financialChartLabels[clickedIndex]) { - clickedLabel = this.financialChartLabels[clickedIndex]; - } + // Get the label of the clicked element + const clickedLabel = this.financialChartLabels[clickedIndex]; - console.log('Clicked on financial chart element:', { index: clickedIndex, label: clickedLabel }); + console.log('Clicked on financial chart point:', { index: clickedIndex, label: clickedLabel }); // If we're not at the base level, store original data if (this.currentDrilldownLevel === 0) { diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/line-chart/line-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/line-chart/line-chart.component.ts index 13c2e54..eba99f3 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/line-chart/line-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/line-chart/line-chart.component.ts @@ -28,6 +28,7 @@ export class LineChartComponent implements OnInit, OnChanges { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -77,6 +78,9 @@ export class LineChartComponent implements OnInit, OnChanges { // No data state noDataAvailable: boolean = false; + + // Flag to prevent infinite loops + private isFetchingData: boolean = false; constructor(private dashboardService: Dashboard3Service) { } @@ -93,6 +97,7 @@ export class LineChartComponent implements OnInit, OnChanges { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -100,12 +105,11 @@ export class LineChartComponent implements OnInit, OnChanges { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); - // Only fetch data if xAxis, yAxis, table, connection, or drilldown config has changed (and it's not the first change) + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } @@ -117,9 +121,14 @@ export class LineChartComponent implements OnInit, OnChanges { } fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -130,13 +139,28 @@ export class LineChartComponent implements OnInit, OnChanges { // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/line?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'line', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'line', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received chart data:', data); if (data === null) { @@ -144,6 +168,8 @@ export class LineChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.lineChartLabels = []; this.lineChartData = []; + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -170,12 +196,16 @@ export class LineChartComponent implements OnInit, OnChanges { this.lineChartLabels = []; this.lineChartData = []; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching chart data:', error); this.noDataAvailable = true; this.lineChartLabels = []; this.lineChartData = []; + // Reset flag after fetching + this.isFetchingData = false; // Keep default data in case of error } ); @@ -184,6 +214,8 @@ export class LineChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.lineChartLabels = []; this.lineChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -265,13 +297,27 @@ export class LineChartComponent implements OnInit, OnChanges { console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/line?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'line', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'line', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -372,6 +418,29 @@ export class LineChartComponent implements OnInit, OnChanges { } } + // Ensure labels and data arrays have the same length + private syncLabelAndDataArrays(): void { + // For line charts, we need to ensure all datasets have the same number of data points + if (this.lineChartData && this.lineChartData.length > 0 && this.lineChartLabels) { + const labelCount = this.lineChartLabels.length; + + this.lineChartData.forEach(dataset => { + if (dataset.data) { + // If dataset has more data points than labels, truncate the data + if (dataset.data.length > labelCount) { + dataset.data = dataset.data.slice(0, labelCount); + } + // If dataset has fewer data points than labels, pad with zeros + else if (dataset.data.length < labelCount) { + while (dataset.data.length < labelCount) { + dataset.data.push(0); + } + } + } + }); + } + } + public randomize(): void { let _lineChartData: Array = new Array(this.lineChartData.length); for (let i = 0; i < this.lineChartData.length; i++) { diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/pie-chart/pie-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/pie-chart/pie-chart.component.ts index 1bbe120..d102ecf 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/pie-chart/pie-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/pie-chart/pie-chart.component.ts @@ -28,6 +28,7 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -90,6 +91,9 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { // No data state noDataAvailable: boolean = false; + + // Flag to prevent infinite loops + private isFetchingData: boolean = false; constructor(private dashboardService: Dashboard3Service) { } @@ -117,6 +121,7 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -124,20 +129,24 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); - // Only fetch data if xAxis, yAxis, table, connection, or drilldown config has changed (and it's not the first change) + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } } fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -148,13 +157,28 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/pie?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Pie chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'pie', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'pie', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received pie chart data:', data); if (data === null) { @@ -164,6 +188,8 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { this.pieChartData = []; // Validate and sanitize data to show default data this.validateChartData(); + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -214,6 +240,8 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { // Validate and sanitize data to show default data this.validateChartData(); } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching pie chart data:', error); @@ -222,6 +250,8 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { this.pieChartData = []; // Validate and sanitize data to show default data this.validateChartData(); + // Reset flag after fetching + this.isFetchingData = false; } ); } else { @@ -233,6 +263,8 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { this.validateChartData(); // Force a redraw to ensure the chart displays this.pieChartData = [...this.pieChartData]; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -314,13 +346,27 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/pie?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'pie', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'pie', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -443,7 +489,7 @@ export class PieChartComponent implements OnInit, OnChanges, AfterViewChecked { this.resetToOriginalData(); } } - + /** * Get color for legend item * @param index Index of the legend item diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/polar-chart/polar-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/polar-chart/polar-chart.component.ts index bc7d9dc..4f29bd5 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/polar-chart/polar-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/polar-chart/polar-chart.component.ts @@ -28,6 +28,7 @@ export class PolarChartComponent implements OnInit, OnChanges { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -46,6 +47,7 @@ export class PolarChartComponent implements OnInit, OnChanges { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -53,12 +55,11 @@ export class PolarChartComponent implements OnInit, OnChanges { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); - // Only fetch data if xAxis, yAxis, table, connection, or drilldown config has changed (and it's not the first change) + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } } @@ -79,10 +80,18 @@ export class PolarChartComponent implements OnInit, OnChanges { // No data state noDataAvailable: boolean = false; + // Flag to prevent infinite loops + private isFetchingData: boolean = false; + fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -93,13 +102,28 @@ export class PolarChartComponent implements OnInit, OnChanges { // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/polar?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Polar chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'polar', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'polar', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received polar chart data:', data); if (data === null) { @@ -107,6 +131,8 @@ export class PolarChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.polarAreaChartLabels = []; this.polarAreaChartData = []; + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -144,12 +170,16 @@ export class PolarChartComponent implements OnInit, OnChanges { this.polarAreaChartLabels = []; this.polarAreaChartData = []; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching polar chart data:', error); this.noDataAvailable = true; this.polarAreaChartLabels = []; this.polarAreaChartData = []; + // Reset flag after fetching + this.isFetchingData = false; // Keep default data in case of error } ); @@ -158,6 +188,8 @@ export class PolarChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.polarAreaChartLabels = []; this.polarAreaChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -239,13 +271,27 @@ export class PolarChartComponent implements OnInit, OnChanges { console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/polar?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'polar', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'polar', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -358,7 +404,7 @@ export class PolarChartComponent implements OnInit, OnChanges { } // events - public chartClicked(e: any): void { + public chartClicked(e: any): void { console.log('Polar chart clicked:', e); // If drilldown is enabled and we have a valid click event @@ -369,7 +415,7 @@ export class PolarChartComponent implements OnInit, OnChanges { // Get the label of the clicked element const clickedLabel = this.polarAreaChartLabels[clickedIndex]; - console.log('Clicked on polar slice:', { index: clickedIndex, label: clickedLabel }); + console.log('Clicked on polar area:', { index: clickedIndex, label: clickedLabel }); // If we're not at the base level, store original data if (this.currentDrilldownLevel === 0) { @@ -440,10 +486,9 @@ export class PolarChartComponent implements OnInit, OnChanges { } else { console.log('Drilldown not enabled or invalid click event'); } - } - - public chartHovered(e: any): void { - console.log(e); - } + } + public chartHovered(e: any): void { + console.log(e); + } } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/radar-chart/radar-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/radar-chart/radar-chart.component.ts index a5906e5..d11d81f 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/radar-chart/radar-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/radar-chart/radar-chart.component.ts @@ -28,6 +28,7 @@ export class RadarChartComponent implements OnInit, OnChanges { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -56,6 +57,9 @@ export class RadarChartComponent implements OnInit, OnChanges { // No data state noDataAvailable: boolean = false; + + // Flag to prevent infinite loops + private isFetchingData: boolean = false; constructor(private dashboardService: Dashboard3Service) { } @@ -71,6 +75,7 @@ export class RadarChartComponent implements OnInit, OnChanges { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -78,19 +83,24 @@ export class RadarChartComponent implements OnInit, OnChanges { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } } fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -101,13 +111,28 @@ export class RadarChartComponent implements OnInit, OnChanges { // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/radar?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Radar chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'radar', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'radar', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received radar chart data:', data); if (data === null) { @@ -115,6 +140,8 @@ export class RadarChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.radarChartLabels = []; this.radarChartData = []; + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -155,12 +182,16 @@ export class RadarChartComponent implements OnInit, OnChanges { this.radarChartLabels = []; this.radarChartData = []; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching radar chart data:', error); this.noDataAvailable = true; this.radarChartLabels = []; this.radarChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } ); } else { @@ -168,6 +199,8 @@ export class RadarChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.radarChartLabels = []; this.radarChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -249,13 +282,27 @@ export class RadarChartComponent implements OnInit, OnChanges { console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/radar?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'radar', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'radar', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { 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 63d6db4..6267fda 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 @@ -29,6 +29,7 @@ export class ScatterChartComponent implements OnInit, OnChanges { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -47,6 +48,7 @@ export class ScatterChartComponent implements OnInit, OnChanges { const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; // Drilldown configuration changes const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; @@ -54,12 +56,11 @@ export class ScatterChartComponent implements OnInit, OnChanges { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || + // Only fetch data if the actual chart configuration changed and we're not already fetching + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || - drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); - // Only fetch data if xAxis, yAxis, table, connection, or drilldown config has changed (and it's not the first change) + drilldownLayersChanged)) { + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } } @@ -101,10 +102,18 @@ export class ScatterChartComponent implements OnInit, OnChanges { // No data state noDataAvailable: boolean = false; + // Flag to prevent infinite loops + private isFetchingData: boolean = false; + fetchChartData(): void { + // 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 + this.isFetchingData = false; return; } @@ -115,19 +124,36 @@ export class ScatterChartComponent implements OnInit, OnChanges { // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Convert baseFilters to filter parameters + let filterParams = ''; + if (this.baseFilters && this.baseFilters.length > 0) { + const filterObj = {}; + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + console.log('Base filter parameters:', filterParams); + // Log the URL that will be called const url = `chart/getdashjson/scatter?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Scatter chart data URL:', url); // Fetch data from the dashboard service with parameter field and value - // For base level, we pass empty parameter and value - this.dashboardService.getChartData(this.table, 'scatter', this.xAxis, yAxisString, this.connection, '', '').subscribe( + // For base level, we pass empty parameter and value, but now also pass filters + this.dashboardService.getChartData(this.table, 'scatter', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { console.log('Received scatter chart data:', data); if (data === null) { console.warn('Scatter chart API returned null data. Check if the API endpoint is working correctly.'); this.noDataAvailable = true; this.scatterChartData = []; + // Reset flag after fetching + this.isFetchingData = false; return; } @@ -148,11 +174,15 @@ export class ScatterChartComponent implements OnInit, OnChanges { this.noDataAvailable = true; this.scatterChartData = []; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.error('Error fetching scatter chart data:', error); this.noDataAvailable = true; this.scatterChartData = []; + // Reset flag after fetching + this.isFetchingData = false; // Keep default data in case of error } ); @@ -160,6 +190,8 @@ export class ScatterChartComponent implements OnInit, OnChanges { console.log('Missing required data for scatter chart:', { table: this.table, xAxis: this.xAxis, yAxis: this.yAxis, connection: this.connection }); this.noDataAvailable = true; this.scatterChartData = []; + // Reset flag after fetching + this.isFetchingData = false; } } @@ -239,13 +271,27 @@ export class ScatterChartComponent implements OnInit, OnChanges { console.log('URL after angle bracket replacement:', actualApiUrl); } + // Convert drilldown layer filters to filter parameters (if applicable) + let filterParams = ''; + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + const filterObj = {}; + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + } + // Log the URL that will be called const url = `chart/getdashjson/scatter?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Drilldown data URL:', url); // Fetch data from the dashboard service with parameter field and value // Backend handles filtering, we just pass the parameter field and value - this.dashboardService.getChartData(actualApiUrl, 'scatter', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue).subscribe( + this.dashboardService.getChartData(actualApiUrl, 'scatter', drilldownConfig.xAxis, drilldownConfig.yAxis, this.connection, parameterField, parameterValue, filterParams).subscribe( (data: any) => { console.log('Received drilldown data:', data); if (data === null) { @@ -282,6 +328,33 @@ export class ScatterChartComponent implements OnInit, OnChanges { ); } + // Transform chart data to scatter chart format + private transformToScatterData(labels: string[], datasets: any[]): ChartDataset[] { + // For scatter charts, we need to transform the data into scatter format + // Scatter charts expect data in the format: {x: number, y: number} + + // This is a simple transformation - in a real implementation, you might want to + // create a more sophisticated mapping based on your data structure + return datasets.map((dataset, index) => { + // Create scatter data points + const scatterData = labels.map((label, i) => { + // Use x-axis data as x coordinate, y-axis data as y coordinate + const xValue = dataset.data[i] || 0; + const yValue = i < datasets.length ? (datasets[i].data[index] || 0) : 0; + + return { x: xValue, y: yValue }; + }); + + return { + data: scatterData, + label: dataset.label || `Dataset ${index + 1}`, + backgroundColor: dataset.backgroundColor || `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 0.6)`, + borderColor: dataset.borderColor || 'rgba(0, 0, 0, 1)', + pointRadius: dataset.pointRadius || 5 + }; + }); + } + // Reset to original data (go back to base level) resetToOriginalData(): void { console.log('Resetting to original data'); @@ -332,48 +405,9 @@ export class ScatterChartComponent implements OnInit, OnChanges { this.resetToOriginalData(); } } - - private transformToScatterData(labels: any[], chartData: any[]): ChartDataset[] { - // Transform the API data into scatter chart format - const datasets: ChartDataset[] = []; - - // Create a dataset for each data series - chartData.forEach((series, index) => { - // For scatter charts, we need x and y values - // We'll use the labels as x values and the data as y values - - const scatterData = labels.map((label, i) => { - const xValue = isNaN(Number(label)) ? i : Number(label); - const yValue = series.data && series.data[i] !== undefined ? - (isNaN(Number(series.data[i])) ? 0 : Number(series.data[i])) : 0; - - return { - x: xValue, - y: yValue - }; - }); - - datasets.push({ - data: scatterData, - label: series.label || `Series ${index + 1}`, - pointRadius: 10, - backgroundColor: this.getBackgroundColor(index), - }); - }); - - return datasets; - } - - private getBackgroundColor(index: number): string { - const colors = [ - 'red', 'green', 'blue', 'purple', 'yellow', - 'brown', 'magenta', 'cyan', 'orange', 'pink' - ]; - return colors[index % colors.length]; - } - // events - public chartClicked(e: any): void { + // events + public chartClicked(e: any): void { console.log('Scatter chart clicked:', e); // If drilldown is enabled and we have a valid click event @@ -381,18 +415,16 @@ export class ScatterChartComponent implements OnInit, OnChanges { // Get the index of the clicked element const clickedIndex = e.active[0].index; - // Get the dataset index - const datasetIndex = e.active[0].datasetIndex; + // Get the label of the clicked element + // For scatter charts, we might not have labels in the same way as other charts + const clickedLabel = `Point ${clickedIndex}`; - // Get the data point - const dataPoint = this.scatterChartData[datasetIndex].data[clickedIndex]; - - console.log('Clicked on scatter point:', { datasetIndex: datasetIndex, index: clickedIndex, dataPoint: dataPoint }); + console.log('Clicked on scatter point:', { index: clickedIndex, label: clickedLabel }); // If we're not at the base level, store original data if (this.currentDrilldownLevel === 0) { // Store original data before entering drilldown mode - this.originalScatterChartData = JSON.parse(JSON.stringify(this.scatterChartData)); + this.originalScatterChartData = [...this.scatterChartData]; console.log('Stored original data for drilldown'); } @@ -431,16 +463,12 @@ export class ScatterChartComponent implements OnInit, OnChanges { // If there's a drilldown configuration for the next level, proceed if (hasDrilldownConfig) { - // For scatter charts, we'll use the x value as the clicked value - const clickedValue = dataPoint && (dataPoint as any).x !== undefined ? - (dataPoint as any).x.toString() : ''; - // Add this click to the drilldown stack const stackEntry = { level: nextDrilldownLevel, - datasetIndex: datasetIndex, clickedIndex: clickedIndex, - clickedValue: clickedValue + clickedLabel: clickedLabel, + clickedValue: clickedLabel // Using label as value for now }; this.drilldownStack.push(stackEntry); @@ -461,9 +489,9 @@ export class ScatterChartComponent implements OnInit, OnChanges { } else { console.log('Drilldown not enabled or invalid click event'); } - } + } - public chartHovered(e: any): void { - console.log(e); - } + public chartHovered(e: any): void { + console.log(e); + } } \ No newline at end of file