diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bubble-chart/bubble-chart.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bubble-chart/bubble-chart.component.html
index dfddf26..1c2b01e 100644
--- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bubble-chart/bubble-chart.component.html
+++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bubble-chart/bubble-chart.component.html
@@ -279,19 +279,31 @@
-
-
- No data available
-
-
-
-
+
+
+
+
+ Loading data...
+
+
+
+
+ No data available
+
+
+
\ 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 094e1d0..3b812dc 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
@@ -37,55 +37,43 @@ export class BubbleChartComponent implements OnInit, OnChanges {
@Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations
public bubbleChartOptions: ChartConfiguration['options'] = {
- // scales: {
- // x: {
- // min: 0,
- // max: 30,
- // ticks: {}
- // },
- // y: {
- // min: 0,
- // max: 30,
- // ticks: {}
- // },
- // plugins: {
- // title: {
- // display: true,
- // text: 'Bubble Chart'
- // }
- // }
- // }
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ x: {
+ beginAtZero: true,
+ title: {
+ display: true,
+ text: 'X Axis'
+ }
+ },
+ y: {
+ beginAtZero: true,
+ title: {
+ display: true,
+ text: 'Y Axis'
+ }
+ }
+ },
+ plugins: {
+ legend: {
+ display: true,
+ position: 'top',
+ },
+ tooltip: {
+ enabled: true,
+ mode: 'point',
+ intersect: false
+ }
+ },
+ animation: {
+ duration: 800,
+ easing: 'easeInOutQuart'
+ }
};
public bubbleChartType: string = 'bubble';
- public bubbleChartData: ChartDataset[] = [
- {
- data: [
- { x: 10, y: 10, r: 10 },
- { x: 15, y: 5, r: 15 },
- { x: 26, y: 12, r: 23 },
- { x: 7, y: 8, r: 8 },
- ],
- label: 'Investment Equities',
- backgroundColor: 'rgba(255, 0, 0, 0.6)', // Red
- borderColor: 'blue',
- hoverBackgroundColor: 'purple',
- hoverBorderColor: 'red',
- },
- {
- data: [
- { x: 5, y: 15, r: 12 },
- { x: 20, y: 7, r: 8 },
- { x: 12, y: 18, r: 15 },
- { x: 8, y: 6, r: 10 },
- ],
- label: 'Investment Bonds',
- backgroundColor: 'rgba(0, 255, 0, 0.6)', // Green
- borderColor: 'green',
- hoverBackgroundColor: 'yellow',
- hoverBorderColor: 'blue',
- },
- ];
+ public bubbleChartData: ChartDataset[] = [];
// Multi-layer drilldown state tracking
drilldownStack: any[] = []; // Stack to track drilldown navigation history
@@ -94,6 +82,7 @@ export class BubbleChartComponent implements OnInit, OnChanges {
// No data state
noDataAvailable: boolean = false;
+ dataLoaded: boolean = false; // Track if data has been loaded
// Flag to prevent infinite loops
private isFetchingData: boolean = false;
@@ -471,39 +460,126 @@ export class BubbleChartComponent implements OnInit, OnChanges {
// Bubble charts expect data in the format: {x: number, y: number, r: number}
console.log('Transforming data to bubble format:', { labels, data });
+ // Handle null/undefined data
+ if (!labels || !data) {
+ console.log('Labels or data is null/undefined, returning empty dataset');
+ return [];
+ }
+
// If we have the expected bubble data format, return it as is
if (data && data.length > 0 && data[0].data && data[0].data.length > 0 &&
typeof data[0].data[0] === 'object' && data[0].data[0].hasOwnProperty('x') &&
data[0].data[0].hasOwnProperty('y') && data[0].data[0].hasOwnProperty('r')) {
+ console.log('Data is already in bubble format, returning as is');
return data;
}
- // Otherwise, create a default bubble dataset
- const bubbleDatasets: ChartDataset[] = [
- {
- data: [
- { x: 10, y: 10, r: 10 },
- { x: 15, y: 5, r: 15 },
- { x: 26, y: 12, r: 23 },
- { x: 7, y: 8, r: 8 },
- ],
- label: 'Dataset 1',
- backgroundColor: 'rgba(255, 0, 0, 0.6)',
- borderColor: 'blue',
- hoverBackgroundColor: 'purple',
- hoverBorderColor: 'red',
+ // Transform the data properly for bubble chart
+ // Assuming labels are x-values and data[0].data are y-values
+ if (labels && data && data.length > 0 && data[0].data) {
+ console.log('Transforming regular data to bubble format');
+ const yValues = data[0].data;
+ const label = data[0].label || 'Dataset 1';
+
+ // Handle case where yValues might not be an array
+ if (!Array.isArray(yValues)) {
+ console.log('yValues is not an array, returning empty dataset');
+ return [];
}
- ];
+
+ console.log('yValues type:', typeof yValues);
+ console.log('yValues length:', yValues.length);
+ console.log('First few yValues:', yValues.slice(0, 5));
+
+ // Create bubble points from labels (x) and data (y)
+ const bubblePoints = [];
+ const minLength = Math.min(labels.length, yValues.length);
+
+ console.log('Processing data points:', { labels, yValues, minLength });
+
+ for (let i = 0; i < minLength; i++) {
+ // Log each point for debugging
+ console.log(`Processing point ${i}: label=${labels[i]}, yValue=${yValues[i]}, type=${typeof yValues[i]}`);
+
+ // Convert y to number if it's a string
+ let y;
+ if (typeof yValues[i] === 'string') {
+ y = parseFloat(yValues[i]);
+ console.log(`Converted string yValue to number: ${yValues[i]} -> ${y}`);
+ } else {
+ y = yValues[i];
+ }
+
+ // Handle NaN values
+ if (isNaN(y)) {
+ console.log(`Skipping point ${i} due to NaN y value: ${yValues[i]}`);
+ continue;
+ }
+
+ // Calculate radius based on the y-value
+ const r = Math.max(5, Math.min(30, Math.abs(y) / 10));
+
+ // For x-value, we'll use the index position since labels are strings
+ const x = i;
+
+ // Add the point
+ const point = {
+ x,
+ y,
+ r
+ };
+ console.log(`Adding point ${i}:`, point);
+ bubblePoints.push(point);
+ }
+
+ console.log('Generated bubble points:', bubblePoints);
+ console.log('Generated bubble points count:', bubblePoints.length);
+
+ // If we have no valid points, return empty array
+ if (bubblePoints.length === 0) {
+ console.log('No valid bubble points generated, returning empty dataset');
+ return [];
+ }
+
+ // Create a single dataset with all bubble points
+ const bubbleDatasets: ChartDataset[] = [
+ {
+ data: bubblePoints,
+ label: label,
+ backgroundColor: 'rgba(255, 99, 132, 0.6)',
+ borderColor: 'rgba(255, 99, 132, 1)',
+ hoverBackgroundColor: 'rgba(255, 99, 132, 0.8)',
+ hoverBorderColor: 'rgba(255, 99, 132, 1)',
+ borderWidth: 1,
+ pointHoverRadius: 10,
+ }
+ ];
+
+ console.log('Transformed bubble data:', bubbleDatasets);
+ return bubbleDatasets;
+ }
- return bubbleDatasets;
+ console.log('Could not transform data, returning empty dataset');
+ // Return empty dataset instead of default data
+ return [];
}
fetchChartData(): void {
// Set flag to prevent recursive calls
this.isFetchingData = true;
+ this.dataLoaded = false; // Mark data as not loaded yet
+ this.noDataAvailable = false; // Reset no data flag
+
+ console.log('Starting fetchChartData, current state:', {
+ table: this.table,
+ xAxis: this.xAxis,
+ yAxis: this.yAxis,
+ connection: this.connection
+ });
// If we're in drilldown mode, fetch the appropriate drilldown data
if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) {
+ console.log('Fetching drilldown data');
this.fetchDrilldownData();
// Reset flag after fetching
this.isFetchingData = false;
@@ -583,32 +659,82 @@ export class BubbleChartComponent implements OnInit, OnChanges {
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;
- }
- // Handle the actual data structure returned by the API
- if (data && data.chartLabels && data.chartData) {
+ // Reset chart data to empty first
+ this.bubbleChartData = [];
+
+ if (data === null || data === undefined) {
+ console.warn('Bubble chart API returned null/undefined data. Check if the API endpoint is working correctly.');
+ this.noDataAvailable = true;
+ } else if (data && data.chartLabels && data.chartData) {
// 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.noDataAvailable = data.chartLabels.length === 0;
- this.bubbleChartData = this.transformToBubbleData(data.chartLabels, data.chartData);
- console.log('Updated bubble chart with data:', this.bubbleChartData);
+ console.log('Processing chartLabels and chartData format');
+ const transformedData = this.transformToBubbleData(data.chartLabels, data.chartData);
+ console.log('Transformed data:', transformedData);
+
+ // Check if we have valid data
+ let hasValidData = false;
+ if (transformedData && transformedData.length > 0) {
+ for (const dataset of transformedData) {
+ if (dataset.data && dataset.data.length > 0) {
+ hasValidData = true;
+ break;
+ }
+ }
+ }
+
+ if (hasValidData) {
+ // Create a new array reference to trigger change detection
+ this.bubbleChartData = [...transformedData];
+ this.noDataAvailable = false;
+ console.log('Updated bubble chart with data:', this.bubbleChartData);
+ } else {
+ console.log('No valid data after transformation');
+ this.noDataAvailable = true;
+ }
} else if (data && data.labels && data.datasets) {
// Handle the original expected format as fallback
- this.noDataAvailable = data.labels.length === 0;
- this.bubbleChartData = data.datasets;
- console.log('Updated bubble chart with legacy data format:', this.bubbleChartData);
+ console.log('Processing labels and datasets format');
+ // Check if we have valid data
+ let hasValidData = false;
+ if (data.datasets && data.datasets.length > 0) {
+ for (const dataset of data.datasets) {
+ if (dataset.data && dataset.data.length > 0) {
+ hasValidData = true;
+ break;
+ }
+ }
+ }
+
+ if (hasValidData) {
+ // Create a new array reference to trigger change detection
+ this.bubbleChartData = [...data.datasets];
+ this.noDataAvailable = false;
+ console.log('Updated bubble chart with legacy data format:', this.bubbleChartData);
+ } else {
+ console.log('No valid data in legacy format');
+ this.noDataAvailable = true;
+ }
} else {
console.warn('Bubble chart received data does not have expected structure', data);
this.noDataAvailable = true;
- this.bubbleChartData = [];
}
+
+ this.dataLoaded = true; // Mark data as loaded
+
+ console.log('Final state after data fetch:', {
+ noDataAvailable: this.noDataAvailable,
+ dataLoaded: this.dataLoaded,
+ bubbleChartDataLength: this.bubbleChartData.length,
+ isChartDataValid: this.isChartDataValid()
+ });
+
+ // Trigger change detection with a small delay to ensure proper rendering
+ setTimeout(() => {
+ this.forceChartUpdate();
+ }, 100);
+
// Reset flag after fetching
this.isFetchingData = false;
},
@@ -616,15 +742,24 @@ export class BubbleChartComponent implements OnInit, OnChanges {
console.error('Error fetching bubble chart data:', error);
this.noDataAvailable = true;
this.bubbleChartData = [];
+ this.dataLoaded = true;
+ // Trigger change detection
+ setTimeout(() => {
+ this.forceChartUpdate();
+ }, 100);
// Reset flag after fetching
this.isFetchingData = false;
- // Keep default data in case of error
}
);
} 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 = [];
+ this.dataLoaded = true;
+ // Trigger change detection
+ setTimeout(() => {
+ this.forceChartUpdate();
+ }, 100);
// Reset flag after fetching
this.isFetchingData = false;
}
@@ -654,6 +789,10 @@ export class BubbleChartComponent implements OnInit, OnChanges {
console.warn('Invalid drilldown layer index:', layerIndex);
this.noDataAvailable = true;
this.bubbleChartData = [];
+ this.dataLoaded = true;
+ setTimeout(() => {
+ this.forceChartUpdate();
+ }, 100);
return;
}
}
@@ -665,6 +804,10 @@ export class BubbleChartComponent implements OnInit, OnChanges {
console.warn('Missing drilldown configuration for level:', this.currentDrilldownLevel);
this.noDataAvailable = true;
this.bubbleChartData = [];
+ this.dataLoaded = true;
+ setTimeout(() => {
+ this.forceChartUpdate();
+ }, 100);
return;
}
@@ -758,35 +901,84 @@ export class BubbleChartComponent implements OnInit, OnChanges {
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) {
- console.warn('Drilldown API returned null data. Check if the API endpoint is working correctly.');
- this.noDataAvailable = true;
- this.bubbleChartData = [];
- return;
- }
- // Handle the actual data structure returned by the API
- if (data && data.chartLabels && data.chartData) {
+ // Reset chart data to empty first
+ this.bubbleChartData = [];
+
+ if (data === null || data === undefined) {
+ console.warn('Drilldown API returned null/undefined data. Check if the API endpoint is working correctly.');
+ this.noDataAvailable = true;
+ } else if (data && data.chartLabels && data.chartData) {
// For bubble charts, we need to transform the data into bubble format
- this.noDataAvailable = data.chartLabels.length === 0;
- this.bubbleChartData = this.transformToBubbleData(data.chartLabels, data.chartData);
- console.log('Updated bubble chart with drilldown data:', this.bubbleChartData);
+ const transformedData = this.transformToBubbleData(data.chartLabels, data.chartData);
+
+ // Check if we have valid data
+ let hasValidData = false;
+ if (transformedData && transformedData.length > 0) {
+ for (const dataset of transformedData) {
+ if (dataset.data && dataset.data.length > 0) {
+ hasValidData = true;
+ break;
+ }
+ }
+ }
+
+ if (hasValidData) {
+ this.bubbleChartData = transformedData;
+ this.noDataAvailable = false;
+ console.log('Updated bubble chart with drilldown data:', this.bubbleChartData);
+ } else {
+ console.log('No valid data after transformation in drilldown');
+ this.noDataAvailable = true;
+ }
} else if (data && data.labels && data.datasets) {
// Handle the original expected format as fallback
- this.noDataAvailable = data.labels.length === 0;
- this.bubbleChartData = data.datasets;
- console.log('Updated bubble chart with drilldown legacy data format:', this.bubbleChartData);
+ // Check if we have valid data
+ let hasValidData = false;
+ if (data.datasets && data.datasets.length > 0) {
+ for (const dataset of data.datasets) {
+ if (dataset.data && dataset.data.length > 0) {
+ hasValidData = true;
+ break;
+ }
+ }
+ }
+
+ if (hasValidData) {
+ this.bubbleChartData = data.datasets;
+ this.noDataAvailable = false;
+ console.log('Updated bubble chart with drilldown legacy data format:', this.bubbleChartData);
+ } else {
+ console.log('No valid data in legacy format in drilldown');
+ this.noDataAvailable = true;
+ }
} else {
console.warn('Drilldown received data does not have expected structure', data);
this.noDataAvailable = true;
- this.bubbleChartData = [];
}
+
+ this.dataLoaded = true; // Mark data as loaded
+
+ console.log('Final state after drilldown data fetch:', {
+ noDataAvailable: this.noDataAvailable,
+ dataLoaded: this.dataLoaded,
+ bubbleChartDataLength: this.bubbleChartData.length,
+ isChartDataValid: this.isChartDataValid()
+ });
+
+ // Trigger change detection
+ setTimeout(() => {
+ this.forceChartUpdate();
+ }, 100);
},
(error) => {
console.error('Error fetching drilldown data:', error);
this.noDataAvailable = true;
this.bubbleChartData = [];
- // Keep current data in case of error
+ this.dataLoaded = true;
+ setTimeout(() => {
+ this.forceChartUpdate();
+ }, 100);
}
);
}
@@ -933,4 +1125,52 @@ export class BubbleChartComponent implements OnInit, OnChanges {
public chartHovered(e: any): void {
console.log('Bubble chart hovered:', e);
}
+
+ // Method to check if chart data is valid
+ public isChartDataValid(): boolean {
+ console.log('Checking if chart data is valid:', this.bubbleChartData);
+ if (!this.bubbleChartData || this.bubbleChartData.length === 0) {
+ console.log('Chart data is null or empty');
+ return false;
+ }
+
+ // Check if any dataset has data
+ for (const dataset of this.bubbleChartData) {
+ console.log('Checking dataset:', dataset);
+ if (dataset.data && dataset.data.length > 0) {
+ console.log('Dataset has data, length:', dataset.data.length);
+ // For bubble charts, check if data points have x, y, r properties
+ for (const point of dataset.data) {
+ console.log('Checking point:', point);
+ if (typeof point === 'object' && point.hasOwnProperty('x') && point.hasOwnProperty('y') && point.hasOwnProperty('r')) {
+ // Valid bubble point
+ console.log('Found valid bubble point');
+ return true;
+ }
+ }
+ }
+ }
+
+ console.log('No valid chart data found');
+ return false;
+ }
+
+ // Method to force chart update
+ private forceChartUpdate(): void {
+ console.log('Forcing chart update');
+ console.log('Current bubbleChartData:', this.bubbleChartData);
+ console.log('Current bubbleChartData length:', this.bubbleChartData ? this.bubbleChartData.length : 0);
+ if (this.bubbleChartData && this.bubbleChartData.length > 0) {
+ console.log('First dataset data length:', this.bubbleChartData[0].data ? this.bubbleChartData[0].data.length : 0);
+ }
+ // Create a new reference to trigger change detection
+ if (this.bubbleChartData) {
+ this.bubbleChartData = [...this.bubbleChartData];
+ }
+ // Also update noDataAvailable to trigger UI changes
+ this.noDataAvailable = this.noDataAvailable;
+ console.log('Chart update forced, noDataAvailable:', this.noDataAvailable);
+ console.log('Chart update forced, bubbleChartData length:', this.bubbleChartData ? this.bubbleChartData.length : 0);
+ console.log('Chart update forced, isChartDataValid:', this.isChartDataValid());
+ }
}
\ No newline at end of file