Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cff84c448 | ||
|
|
807058e40d | ||
|
|
c6022b0e22 | ||
|
|
9aed6e0d43 | ||
|
|
64b664e625 | ||
|
|
49f1a2fbf2 | ||
|
|
6bfe890cd9 |
@@ -131,16 +131,16 @@ export class EditnewdashComponent implements OnInit {
|
||||
|
||||
protected componentCollection = [
|
||||
{ name: "Common Filter", componentInstance: CommonFilterComponent },
|
||||
{ name: "Line Chart", componentInstance: LineChartComponent },
|
||||
{ name: "Doughnut Chart", componentInstance: DoughnutChartComponent },
|
||||
{ name: "Radar Chart", componentInstance: RadarChartComponent },
|
||||
{ name: "Bar Chart", componentInstance: BarChartComponent },
|
||||
{ name: "Pie Chart", componentInstance: PieChartComponent },
|
||||
{ name: "Polar Area Chart", componentInstance: PolarChartComponent },
|
||||
{ name: "Bubble Chart", componentInstance: BubbleChartComponent },
|
||||
{ name: "Scatter Chart", componentInstance: ScatterChartComponent },
|
||||
{ name: "Dynamic Chart", componentInstance: DynamicChartComponent },
|
||||
{ name: "Financial Chart", componentInstance: FinancialChartComponent },
|
||||
{ name: "Line Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Doughnut Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Radar Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Bar Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Pie Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Polar Area Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Bubble Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Scatter Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Dynamic Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Financial Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "To Do Chart", componentInstance: ToDoChartComponent },
|
||||
{ name: "Grid View", componentInstance: GridViewComponent },
|
||||
{ name: "Compact Filter", componentInstance: CompactFilterComponent },
|
||||
@@ -393,6 +393,40 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
|
||||
// Handle charts that use UnifiedChartComponent but have different names
|
||||
// After serialization, these charts have component = "Unified Chart" but their name is still "Line Chart", "Pie Chart", etc.
|
||||
const unifiedChartNames = [
|
||||
'Radar Chart', 'Line Chart', 'Doughnut Chart', 'Bar Chart',
|
||||
'Pie Chart', 'Polar Area Chart', 'Bubble Chart', 'Scatter Chart',
|
||||
'Dynamic Chart', 'Financial Chart'
|
||||
];
|
||||
|
||||
if (dashboard.component === "Unified Chart" && unifiedChartNames.includes(dashboard.name)) {
|
||||
// Restore the actual UnifiedChartComponent reference
|
||||
dashboard.component = UnifiedChartComponent;
|
||||
}
|
||||
|
||||
// Map chart names to unified chart types
|
||||
const chartTypeMap = {
|
||||
'Radar Chart': 'radar',
|
||||
'Line Chart': 'line',
|
||||
'Doughnut Chart': 'doughnut',
|
||||
'Bar Chart': 'bar',
|
||||
'Pie Chart': 'pie',
|
||||
'Polar Area Chart': 'polar',
|
||||
'Bubble Chart': 'bubble',
|
||||
'Scatter Chart': 'scatter',
|
||||
'Dynamic Chart': 'line',
|
||||
'Financial Chart': 'line'
|
||||
};
|
||||
|
||||
// If this is a chart, set the chartType property
|
||||
if (chartTypeMap.hasOwnProperty(dashboard.name)) {
|
||||
dashboard.chartType = chartTypeMap[dashboard.name];
|
||||
// Keep the original name instead of changing it to "Unified Chart"
|
||||
// dashboard.name = "Unified Chart";
|
||||
}
|
||||
|
||||
// Ensure compact filter configuration properties are properly initialized
|
||||
if (dashboard.component === 'Compact Filter' || dashboard.name === 'Compact Filter') {
|
||||
// Make sure all compact filter properties exist
|
||||
@@ -417,6 +451,37 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
|
||||
// Map unified chart types back to chart names for serialization
|
||||
const chartNameMap = {
|
||||
'radar': 'Radar Chart',
|
||||
'line': 'Line Chart',
|
||||
'doughnut': 'Doughnut Chart',
|
||||
'bar': 'Bar Chart',
|
||||
'pie': 'Pie Chart',
|
||||
'polar': 'Polar Area Chart',
|
||||
'bubble': 'Bubble Chart',
|
||||
'scatter': 'Scatter Chart'
|
||||
};
|
||||
|
||||
// If this is a unified chart, set the name back to the appropriate chart name
|
||||
if (dashboard.name === 'Unified Chart' && dashboard.chartType && chartNameMap.hasOwnProperty(dashboard.chartType)) {
|
||||
dashboard.name = chartNameMap[dashboard.chartType];
|
||||
}
|
||||
// Also handle the case where the chart already has the correct name
|
||||
else if (dashboard.chartType && chartNameMap.hasOwnProperty(dashboard.chartType) &&
|
||||
dashboard.name === chartNameMap[dashboard.chartType]) {
|
||||
// The name is already correct, no need to change it
|
||||
dashboard.component = "Unified Chart";
|
||||
}
|
||||
// Handle charts that use UnifiedChartComponent but have different names
|
||||
else if (dashboard.component === UnifiedChartComponent && dashboard.chartType) {
|
||||
// Preserve the chartType property for UnifiedChartComponent
|
||||
// The name should already be correct (e.g., "Line Chart", "Pie Chart", etc.)
|
||||
// Instead of changing the component reference, we preserve it and only change the component name for serialization
|
||||
// But we need to ensure the component name is set correctly for proper deserialization
|
||||
dashboard.component = "Unified Chart";
|
||||
}
|
||||
|
||||
// Ensure compact filter configuration properties are preserved
|
||||
if (dashboard.name === 'Compact Filter') {
|
||||
// Make sure all compact filter properties exist
|
||||
@@ -428,6 +493,7 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add method to get available fields for a filter dropdown (excluding already selected fields)
|
||||
getAvailableFields(filters: any[], currentIndex: number, allFields: string[]): string[] {
|
||||
if (!filters || !allFields) {
|
||||
@@ -473,6 +539,7 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
switch (componentType) {
|
||||
// Handle all chart types by converting them to unified charts
|
||||
case "radar_chart":
|
||||
return this.dashboardArray.push({
|
||||
cols: 5,
|
||||
@@ -480,8 +547,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: RadarChartComponent,
|
||||
name: "Radar Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Radar Chart",
|
||||
chartType: 'radar',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "line_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -490,8 +562,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: LineChartComponent,
|
||||
name: "Line Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Line Chart",
|
||||
chartType: 'line',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "doughnut_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -500,8 +577,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: DoughnutChartComponent,
|
||||
name: "Doughnut Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Doughnut Chart",
|
||||
chartType: 'doughnut',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "bar_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -510,8 +592,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: BarChartComponent,
|
||||
name: "Bar Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Bar Chart",
|
||||
chartType: 'bar',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "pie_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -520,8 +607,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: PieChartComponent,
|
||||
name: "Pie Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Pie Chart",
|
||||
chartType: 'pie',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "polar_area_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -530,8 +622,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: PolarChartComponent,
|
||||
name: "Polar Area Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Polar Area Chart",
|
||||
chartType: 'polar',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "bubble_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -540,8 +637,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: BubbleChartComponent,
|
||||
name: "Bubble Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Bubble Chart",
|
||||
chartType: 'bubble',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "scatter_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -550,8 +652,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: ScatterChartComponent,
|
||||
name: "Scatter Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Scatter Chart",
|
||||
chartType: 'scatter',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "dynamic_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -560,8 +667,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: DynamicChartComponent,
|
||||
name: "Dynamic Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Dynamic Chart",
|
||||
chartType: 'line', // Default to line for dynamic chart
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "financial_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -570,8 +682,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: FinancialChartComponent,
|
||||
name: "Financial Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Financial Chart",
|
||||
chartType: 'line', // Default to line for financial chart
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "to_do_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -959,7 +1076,14 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
|
||||
// For unified chart, preserve chart configuration properties
|
||||
if (item.name === 'Unified Chart') {
|
||||
// Check if this item uses UnifiedChartComponent
|
||||
const unifiedChartNames = [
|
||||
'Radar Chart', 'Line Chart', 'Doughnut Chart', 'Bar Chart',
|
||||
'Pie Chart', 'Polar Area Chart', 'Bubble Chart', 'Scatter Chart',
|
||||
'Dynamic Chart', 'Financial Chart', 'Unified Chart'
|
||||
];
|
||||
|
||||
if (unifiedChartNames.includes(item.name)) {
|
||||
xyz.chartType = this.gadgetsEditdata.chartType || 'bar';
|
||||
}
|
||||
|
||||
@@ -1047,7 +1171,14 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
|
||||
// For UnifiedChartComponent, pass chart properties with chartType
|
||||
if (item.name === 'Unified Chart') {
|
||||
// Check for specific chart names that use UnifiedChartComponent
|
||||
const unifiedChartNames = [
|
||||
'Radar Chart', 'Line Chart', 'Doughnut Chart', 'Bar Chart',
|
||||
'Pie Chart', 'Polar Area Chart', 'Bubble Chart', 'Scatter Chart',
|
||||
'Dynamic Chart', 'Financial Chart', 'Unified Chart'
|
||||
];
|
||||
|
||||
if (unifiedChartNames.includes(item.name)) {
|
||||
const unifiedChartInputs = {
|
||||
chartType: item.chartType || 'bar',
|
||||
xAxis: item.xAxis,
|
||||
@@ -1245,6 +1376,18 @@ export class EditnewdashComponent implements OnInit {
|
||||
this.gadgetsEditdata.filterOptions = updatedItem.filterOptions;
|
||||
}
|
||||
|
||||
// For unified chart, preserve chart configuration properties
|
||||
// Check if this item uses UnifiedChartComponent
|
||||
const unifiedChartNames = [
|
||||
'Radar Chart', 'Line Chart', 'Doughnut Chart', 'Bar Chart',
|
||||
'Pie Chart', 'Polar Area Chart', 'Bubble Chart', 'Scatter Chart',
|
||||
'Dynamic Chart', 'Financial Chart', 'Unified Chart'
|
||||
];
|
||||
|
||||
if (unifiedChartNames.includes(item.name)) {
|
||||
updatedItem.chartType = this.gadgetsEditdata.chartType || 'bar';
|
||||
}
|
||||
|
||||
console.log('Updated item:', updatedItem);
|
||||
return updatedItem;
|
||||
}
|
||||
|
||||
@@ -13,141 +13,163 @@
|
||||
<h4>{{ charttitle }}</h4>
|
||||
</div>
|
||||
|
||||
<!-- Filter toggle icon -->
|
||||
<div class="filter-toggle-icon" *ngIf="baseFilters && baseFilters.length > 0"
|
||||
(click)="toggleFilters()"
|
||||
style="cursor: pointer; text-align: right; padding: 5px;">
|
||||
<clr-icon shape="filter" size="24"
|
||||
[style.color]="showFilters ? '#007cba' : '#666'"
|
||||
title="Toggle Filters">
|
||||
</clr-icon>
|
||||
<span style="margin-left: 5px; font-size: 12px; color: #666;">
|
||||
{{ showFilters ? 'Hide Filters' : 'Show Filters' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Render different chart types based on chartType input -->
|
||||
<div class="chart-wrapper">
|
||||
<!-- Bar Chart -->
|
||||
<div *ngIf="chartType === 'bar'">
|
||||
<div *ngIf="chartType === 'bar'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[data]="chartData"
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'bar'"
|
||||
[type]="'bar'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Line Chart -->
|
||||
<div *ngIf="chartType === 'line'">
|
||||
<div *ngIf="chartType === 'line'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[data]="chartData"
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'line'"
|
||||
[type]="'line'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Pie Chart -->
|
||||
<div *ngIf="chartType === 'pie'">
|
||||
<div *ngIf="chartType === 'pie'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[data]="chartData"
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'pie'"
|
||||
[type]="'pie'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Doughnut Chart -->
|
||||
<div *ngIf="chartType === 'doughnut'">
|
||||
<div *ngIf="chartType === 'doughnut'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[data]="chartData"
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'doughnut'"
|
||||
[type]="'doughnut'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Bubble Chart -->
|
||||
<div *ngIf="chartType === 'bubble'">
|
||||
<div *ngIf="chartType === 'bubble'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="bubbleChartData"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'bubble'"
|
||||
[type]="'bubble'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Radar Chart -->
|
||||
<div *ngIf="chartType === 'radar'">
|
||||
<div *ngIf="chartType === 'radar'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[data]="chartData"
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'radar'"
|
||||
[type]="'radar'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Polar Area Chart -->
|
||||
<div *ngIf="chartType === 'polar'">
|
||||
<div *ngIf="chartType === 'polar'" class="chart-canvas-container">
|
||||
<div style="position: absolute; top: 0; left: 0; background: magenta; color: white; padding: 5px; z-index: 1000; font-size: 12px;">
|
||||
POLAR CHART RENDERED (chartType: {{chartType}})
|
||||
</div>
|
||||
<canvas baseChart
|
||||
[data]="chartData"
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'polarArea'"
|
||||
[type]="'polarArea'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Scatter Chart -->
|
||||
<div *ngIf="chartType === 'scatter'">
|
||||
<div *ngIf="chartType === 'scatter'" class="chart-canvas-container">
|
||||
<div style="position: absolute; top: 0; left: 0; background: brown; color: white; padding: 5px; z-index: 1000; font-size: 12px;">
|
||||
SCATTER CHART RENDERED (chartType: {{chartType}})
|
||||
</div>
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'scatter'"
|
||||
[type]="'scatter'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Default/Unknown Chart Type -->
|
||||
<div *ngIf="!['bar', 'line', 'pie', 'doughnut', 'bubble', 'radar', 'polar', 'scatter'].includes(chartType)">
|
||||
<div *ngIf="!['bar', 'line', 'pie', 'doughnut', 'bubble', 'radar', 'polar', 'scatter'].includes(chartType)" class="chart-canvas-container">
|
||||
<div style="position: absolute; top: 0; left: 0; background: gray; color: white; padding: 5px; z-index: 1000; font-size: 12px;">
|
||||
DEFAULT CHART RENDERED (chartType: {{chartType}})
|
||||
</div>
|
||||
<canvas baseChart
|
||||
[data]="chartData"
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'bar'"
|
||||
[type]="'bar'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Base Filters -->
|
||||
<div class="filters-section" *ngIf="baseFilters && baseFilters.length > 0">
|
||||
<!-- Collapsible Base Filters -->
|
||||
<div class="filters-section" *ngIf="baseFilters && baseFilters.length > 0 && showFilters">
|
||||
<h5>Filters</h5>
|
||||
<div class="filters-container">
|
||||
<div class="filter-item" *ngFor="let filter of baseFilters; let i = index">
|
||||
<!-- Text Filter -->
|
||||
<div *ngIf="filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field }}</label>
|
||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)"
|
||||
class="form-control" placeholder="Enter {{ filter.field }}">
|
||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Filter -->
|
||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<select [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)" class="form-control">
|
||||
<option value="">Select {{ filter.field }}</option>
|
||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
@@ -156,12 +178,15 @@
|
||||
|
||||
<!-- Multiselect Filter -->
|
||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="multiselect-container">
|
||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'base')">
|
||||
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
|
||||
<span *ngIf="getSelectedOptionsCount(filter) > 0">
|
||||
{{ getSelectedOptionsCount(filter) }} selected
|
||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
||||
{{ filter.value }}
|
||||
</span>
|
||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
||||
{{ filter.value.length }} selected
|
||||
</span>
|
||||
</div>
|
||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'base')">
|
||||
@@ -178,18 +203,18 @@
|
||||
|
||||
<!-- Date Range Filter -->
|
||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="date-range-inputs">
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
||||
class="form-control" placeholder="Start Date">
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
||||
class="form-control" placeholder="End Date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Filter -->
|
||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="toggle-switch">
|
||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||
id="toggle-{{ filter.field }}">
|
||||
@@ -203,22 +228,22 @@
|
||||
</div>
|
||||
|
||||
<!-- Drilldown Filters -->
|
||||
<div class="filters-section" *ngIf="drilldownFilters && drilldownFilters.length > 0 && currentDrilldownLevel > 0">
|
||||
<div class="filters-section" *ngIf="drilldownFilters && drilldownFilters.length > 0 && currentDrilldownLevel > 0 && showFilters">
|
||||
<h5>Drilldown Filters</h5>
|
||||
<div class="filters-container">
|
||||
<div class="filter-item" *ngFor="let filter of drilldownFilters; let i = index">
|
||||
<!-- Text Filter -->
|
||||
<div *ngIf="filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field }}</label>
|
||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)"
|
||||
class="form-control" placeholder="Enter {{ filter.field }}">
|
||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Filter -->
|
||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<select [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)" class="form-control">
|
||||
<option value="">Select {{ filter.field }}</option>
|
||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
@@ -227,12 +252,15 @@
|
||||
|
||||
<!-- Multiselect Filter -->
|
||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="multiselect-container">
|
||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'drilldown')">
|
||||
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
|
||||
<span *ngIf="getSelectedOptionsCount(filter) > 0">
|
||||
{{ getSelectedOptionsCount(filter) }} selected
|
||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
||||
{{ filter.value }}
|
||||
</span>
|
||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
||||
{{ filter.value.length }} selected
|
||||
</span>
|
||||
</div>
|
||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'drilldown')">
|
||||
@@ -249,18 +277,18 @@
|
||||
|
||||
<!-- Date Range Filter -->
|
||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="date-range-inputs">
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
||||
class="form-control" placeholder="Start Date">
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
||||
class="form-control" placeholder="End Date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Filter -->
|
||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="toggle-switch">
|
||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||
id="drilldown-toggle-{{ filter.field }}">
|
||||
@@ -274,22 +302,22 @@
|
||||
</div>
|
||||
|
||||
<!-- Layer Filters -->
|
||||
<div class="filters-section" *ngIf="hasActiveLayerFilters()">
|
||||
<div class="filters-section" *ngIf="hasActiveLayerFilters() && showFilters">
|
||||
<h5>Layer Filters</h5>
|
||||
<div class="filters-container">
|
||||
<div class="filter-item" *ngFor="let filter of getActiveLayerFilters(); let i = index">
|
||||
<!-- Text Filter -->
|
||||
<div *ngIf="filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field }}</label>
|
||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)"
|
||||
class="form-control" placeholder="Enter {{ filter.field }}">
|
||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Filter -->
|
||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<select [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)" class="form-control">
|
||||
<option value="">Select {{ filter.field }}</option>
|
||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
@@ -298,12 +326,15 @@
|
||||
|
||||
<!-- Multiselect Filter -->
|
||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="multiselect-container">
|
||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'layer')">
|
||||
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
|
||||
<span *ngIf="getSelectedOptionsCount(filter) > 0">
|
||||
{{ getSelectedOptionsCount(filter) }} selected
|
||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
||||
{{ filter.value }}
|
||||
</span>
|
||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
||||
{{ filter.value.length }} selected
|
||||
</span>
|
||||
</div>
|
||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'layer')">
|
||||
@@ -320,18 +351,18 @@
|
||||
|
||||
<!-- Date Range Filter -->
|
||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="date-range-inputs">
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
||||
class="form-control" placeholder="Start Date">
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
||||
class="form-control" placeholder="End Date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Filter -->
|
||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||
<label>{{ filter.field }}</label>
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="toggle-switch">
|
||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||
id="layer-toggle-{{ filter.field }}">
|
||||
@@ -345,7 +376,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Clear Filters Button -->
|
||||
<div class="clear-filters" *ngIf="hasActiveFilters()">
|
||||
<div class="clear-filters" *ngIf="hasActiveFilters() && showFilters">
|
||||
<button class="btn btn-sm btn-outline" (click)="clearAllFilters()">
|
||||
Clear All Filters
|
||||
</button>
|
||||
|
||||
@@ -30,7 +30,49 @@
|
||||
.chart-wrapper {
|
||||
position: relative;
|
||||
height: calc(100% - 100px);
|
||||
min-height: 300px;
|
||||
min-height: 400px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.chart-canvas-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-toggle-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
clr-icon {
|
||||
transition: color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
color: #007cba !important;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
&:hover span {
|
||||
color: #007cba;
|
||||
}
|
||||
}
|
||||
|
||||
.filters-section {
|
||||
@@ -246,7 +288,6 @@
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@media (max-width: 768px) {
|
||||
.filters-container {
|
||||
flex-direction: column;
|
||||
@@ -259,4 +300,8 @@
|
||||
.chart-wrapper {
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.chart-canvas-container {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import { ChartConfiguration, ChartDataset } from 'chart.js';
|
||||
styleUrls: ['./unified-chart.component.scss']
|
||||
})
|
||||
export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() chartType: string;
|
||||
@Input() chartType: string = 'bar';
|
||||
@Input() xAxis: string;
|
||||
@Input() yAxis: string | string[];
|
||||
@Input() table: string;
|
||||
@@ -61,6 +61,9 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
currentDrilldownLevel: number = 0;
|
||||
originalChartData: any = {};
|
||||
|
||||
// Filter visibility toggle
|
||||
showFilters: boolean = false;
|
||||
|
||||
// Flag to prevent infinite loops
|
||||
private isFetchingData: boolean = false;
|
||||
|
||||
@@ -85,13 +88,37 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
})
|
||||
);
|
||||
|
||||
this.initializeChartOptions();
|
||||
this.fetchChartData();
|
||||
// Initialize filter values if they haven't been initialized yet
|
||||
if (!this.filtersInitialized) {
|
||||
this.initializeFilterValues();
|
||||
this.filtersInitialized = true;
|
||||
}
|
||||
|
||||
// Don't initialize chart options here since inputs might not be set yet
|
||||
// Chart options will be initialized in ngOnChanges when inputs are available
|
||||
// this.initializeChartOptions();
|
||||
// this.fetchChartData();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
console.log('UnifiedChartComponent input changes:', changes);
|
||||
|
||||
// Log all input values for debugging
|
||||
console.log('Current input values:', {
|
||||
chartType: this.chartType,
|
||||
xAxis: this.xAxis,
|
||||
yAxis: this.yAxis,
|
||||
table: this.table,
|
||||
baseFilters: this.baseFilters,
|
||||
drilldownFilters: this.drilldownFilters,
|
||||
drilldownLayers: this.drilldownLayers
|
||||
});
|
||||
|
||||
// Special logging for chartType changes
|
||||
if (changes.chartType) {
|
||||
console.log('Chart type changed from', changes.chartType.previousValue, 'to', changes.chartType.currentValue);
|
||||
}
|
||||
|
||||
// Initialize filter values if they haven't been initialized yet
|
||||
if (!this.filtersInitialized && (changes.baseFilters || changes.drilldownFilters || changes.drilldownLayers)) {
|
||||
this.initializeFilterValues();
|
||||
@@ -114,6 +141,36 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange;
|
||||
const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange;
|
||||
|
||||
// Log base filters changes for debugging
|
||||
if (baseFiltersChanged) {
|
||||
console.log('Base filters changed:', changes.baseFilters);
|
||||
console.log('Current base filters:', this.baseFilters);
|
||||
// Log detailed information about each filter
|
||||
if (this.baseFilters && Array.isArray(this.baseFilters)) {
|
||||
this.baseFilters.forEach((filter, index) => {
|
||||
console.log(`Base filter ${index} details:`, {
|
||||
field: filter.field,
|
||||
value: filter.value,
|
||||
type: filter.type,
|
||||
options: filter.options
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Also log when baseFilters is not changed but we still have filters
|
||||
if (!baseFiltersChanged && this.baseFilters && this.baseFilters.length > 0) {
|
||||
console.log('Base filters present but not changed, logging current state:');
|
||||
this.baseFilters.forEach((filter, index) => {
|
||||
console.log(`Base filter ${index} details:`, {
|
||||
field: filter.field,
|
||||
value: filter.value,
|
||||
type: filter.type,
|
||||
options: filter.options
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Only fetch data if the actual chart configuration changed and we're not already fetching
|
||||
if (!this.isFetchingData && (chartTypeChanged || xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownFiltersChanged ||
|
||||
drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged ||
|
||||
@@ -126,7 +183,18 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
// Update legend visibility if it changed
|
||||
if (changes.chartlegend !== undefined) {
|
||||
this.chartLegend = changes.chartlegend.currentValue;
|
||||
// Ensure chartOptions and required structures exist before accessing legend
|
||||
if (!this.chartOptions) {
|
||||
this.chartOptions = {};
|
||||
}
|
||||
if (!this.chartOptions.plugins) {
|
||||
this.chartOptions.plugins = {};
|
||||
}
|
||||
if (!this.chartOptions.plugins.legend) {
|
||||
this.chartOptions.plugins.legend = { display: this.chartLegend };
|
||||
} else {
|
||||
this.chartOptions.plugins.legend.display = this.chartLegend;
|
||||
}
|
||||
console.log('Chart legend changed to:', this.chartLegend);
|
||||
}
|
||||
}
|
||||
@@ -141,13 +209,30 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if filters are available
|
||||
hasFilters(): boolean {
|
||||
const hasBaseFilters = this.baseFilters && this.baseFilters.length > 0;
|
||||
console.log('Checking for filters - baseFilters:', this.baseFilters, 'hasBaseFilters:', hasBaseFilters);
|
||||
return hasBaseFilters;
|
||||
}
|
||||
|
||||
// Toggle filter visibility
|
||||
toggleFilters(): void {
|
||||
console.log('Toggling filters. Current state:', this.showFilters);
|
||||
console.log('Base filters available:', this.hasFilters());
|
||||
this.showFilters = !this.showFilters;
|
||||
console.log('New state:', this.showFilters);
|
||||
}
|
||||
|
||||
// Initialize filter values with proper default values based on type
|
||||
private initializeFilterValues(): void {
|
||||
console.log('Initializing filter values');
|
||||
console.log('Base filters before initialization:', this.baseFilters);
|
||||
|
||||
// Initialize base filters
|
||||
if (this.baseFilters) {
|
||||
this.baseFilters.forEach(filter => {
|
||||
if (this.baseFilters && Array.isArray(this.baseFilters)) {
|
||||
this.baseFilters.forEach((filter, index) => {
|
||||
console.log(`Processing base filter ${index}:`, filter);
|
||||
if (filter.value === undefined || filter.value === null) {
|
||||
switch (filter.type) {
|
||||
case 'multiselect':
|
||||
@@ -162,13 +247,22 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
default:
|
||||
filter.value = '';
|
||||
}
|
||||
console.log(`Initialized base filter ${index} value to:`, filter.value);
|
||||
} else {
|
||||
console.log(`Base filter ${index} already has value:`, filter.value);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Initialize as empty array if not provided
|
||||
this.baseFilters = [];
|
||||
}
|
||||
|
||||
console.log('Base filters after initialization:', this.baseFilters);
|
||||
|
||||
// Initialize drilldown filters
|
||||
if (this.drilldownFilters) {
|
||||
this.drilldownFilters.forEach(filter => {
|
||||
if (this.drilldownFilters && Array.isArray(this.drilldownFilters)) {
|
||||
this.drilldownFilters.forEach((filter, index) => {
|
||||
console.log(`Processing drilldown filter ${index}:`, filter);
|
||||
if (filter.value === undefined || filter.value === null) {
|
||||
switch (filter.type) {
|
||||
case 'multiselect':
|
||||
@@ -183,15 +277,23 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
default:
|
||||
filter.value = '';
|
||||
}
|
||||
console.log(`Initialized drilldown filter ${index} value to:`, filter.value);
|
||||
} else {
|
||||
console.log(`Drilldown filter ${index} already has value:`, filter.value);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Initialize as empty array if not provided
|
||||
this.drilldownFilters = [];
|
||||
}
|
||||
|
||||
// Initialize layer filters
|
||||
if (this.drilldownLayers) {
|
||||
this.drilldownLayers.forEach(layer => {
|
||||
if (layer.filters) {
|
||||
layer.filters.forEach((filter: any) => {
|
||||
if (this.drilldownLayers && Array.isArray(this.drilldownLayers)) {
|
||||
this.drilldownLayers.forEach((layer, layerIndex) => {
|
||||
console.log(`Processing drilldown layer ${layerIndex}:`, layer);
|
||||
if (layer.filters && Array.isArray(layer.filters)) {
|
||||
layer.filters.forEach((filter, filterIndex) => {
|
||||
console.log(`Processing layer ${layerIndex} filter ${filterIndex}:`, filter);
|
||||
if (filter.value === undefined || filter.value === null) {
|
||||
switch (filter.type) {
|
||||
case 'multiselect':
|
||||
@@ -206,10 +308,16 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
default:
|
||||
filter.value = '';
|
||||
}
|
||||
console.log(`Initialized layer ${layerIndex} filter ${filterIndex} value to:`, filter.value);
|
||||
} else {
|
||||
console.log(`Layer ${layerIndex} filter ${filterIndex} already has value:`, filter.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Initialize as empty array if not provided
|
||||
this.drilldownLayers = [];
|
||||
}
|
||||
|
||||
console.log('Filter values initialized:', {
|
||||
@@ -221,6 +329,18 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
// Initialize chart options based on chart type
|
||||
private initializeChartOptions(): void {
|
||||
// Initialize with default structure to ensure plugins.legend exists
|
||||
this.chartOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
switch (this.chartType) {
|
||||
case 'bar':
|
||||
this.initializeBarChartOptions();
|
||||
@@ -252,9 +372,12 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
private initializeBarChartOptions(): void {
|
||||
console.log('Initializing bar chart options');
|
||||
this.chartOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
barPercentage: 0.6, // Reduced from 0.8 to create more spacing between bars
|
||||
categoryPercentage: 0.8, // Reduced from 0.9 to create more spacing between categories
|
||||
scales: {
|
||||
x: {
|
||||
ticks: {
|
||||
@@ -276,7 +399,9 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
// Add some padding to the y-axis to prevent bars from touching the top
|
||||
suggestedMax: 10
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
@@ -286,11 +411,21 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
labels: {
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
},
|
||||
// Add padding to legend items
|
||||
padding: 20
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true
|
||||
enabled: true,
|
||||
// Improve tooltip appearance
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||
titleFont: {
|
||||
size: 14
|
||||
},
|
||||
bodyFont: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
@@ -300,11 +435,27 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
right: 15,
|
||||
top: 15
|
||||
}
|
||||
},
|
||||
// Add bar chart specific options
|
||||
indexAxis: 'x', // Horizontal bars
|
||||
elements: {
|
||||
bar: {
|
||||
borderWidth: 2, // Increased border width for better visibility
|
||||
borderSkipped: false, // Show all borders for better separation
|
||||
// Add border color to make bars more distinct
|
||||
borderColor: 'rgba(255, 255, 255, 0.8)' // White border for better separation
|
||||
}
|
||||
},
|
||||
// Animation settings for smoother transitions
|
||||
animation: {
|
||||
duration: 1000,
|
||||
easing: 'easeInOutQuart'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private initializeLineChartOptions(): void {
|
||||
console.log('Initializing line chart options');
|
||||
this.chartOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
@@ -544,8 +695,21 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.isLoading = true;
|
||||
this.noDataAvailable = false;
|
||||
|
||||
// Ensure chart options are initialized
|
||||
if (!this.chartOptions) {
|
||||
this.initializeChartOptions();
|
||||
}
|
||||
|
||||
console.log('Starting fetchChartData for chart type:', this.chartType);
|
||||
|
||||
// Log current state for debugging
|
||||
console.log('Current component state:', {
|
||||
chartType: this.chartType,
|
||||
chartData: this.chartData,
|
||||
chartLabels: this.chartLabels,
|
||||
chartOptions: this.chartOptions
|
||||
});
|
||||
|
||||
// If we're in drilldown mode, fetch the appropriate drilldown data
|
||||
if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) {
|
||||
console.log('Fetching drilldown data');
|
||||
@@ -630,13 +794,23 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.xAxis,
|
||||
yAxisString,
|
||||
this.connection,
|
||||
'',
|
||||
'',
|
||||
filterParams
|
||||
).subscribe(
|
||||
(data: any) => {
|
||||
console.log('Received chart data:', data);
|
||||
|
||||
// Log data structure for debugging
|
||||
console.log('Data structure analysis:', {
|
||||
hasChartLabels: !!(data && data.chartLabels),
|
||||
hasChartData: !!(data && data.chartData),
|
||||
hasLabels: !!(data && data.labels),
|
||||
hasDatasets: !!(data && data.datasets),
|
||||
chartLabelsLength: data && data.chartLabels ? data.chartLabels.length : 0,
|
||||
chartDataLength: data && data.chartData ? data.chartData.length : 0,
|
||||
labelsLength: data && data.labels ? data.labels.length : 0,
|
||||
datasetsLength: data && data.datasets ? data.datasets.length : 0
|
||||
});
|
||||
|
||||
if (data === null || data === undefined) {
|
||||
console.warn('Chart API returned null/undefined data.');
|
||||
this.noDataAvailable = true;
|
||||
@@ -667,6 +841,14 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.noDataAvailable = true;
|
||||
}
|
||||
|
||||
// Log final data state
|
||||
console.log('Final data state:', {
|
||||
chartLabels: this.chartLabels,
|
||||
chartData: this.chartData,
|
||||
bubbleChartData: this.bubbleChartData,
|
||||
noDataAvailable: this.noDataAvailable
|
||||
});
|
||||
|
||||
// Reset flags after fetching
|
||||
this.isFetchingData = false;
|
||||
this.isLoading = false;
|
||||
@@ -1291,7 +1473,7 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
public chartHovered(e: any): void {
|
||||
console.log('Chart hovered:', e);
|
||||
// console.log('Chart hovered:', e);
|
||||
}
|
||||
|
||||
// Method to check if chart data is valid
|
||||
@@ -1346,4 +1528,255 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are active filters
|
||||
hasActiveFilters(): boolean {
|
||||
return (this.baseFilters && this.baseFilters.length > 0) ||
|
||||
(this.drilldownFilters && this.drilldownFilters.length > 0) ||
|
||||
this.hasActiveLayerFilters();
|
||||
}
|
||||
|
||||
// Check if there are active layer filters for current drilldown level
|
||||
hasActiveLayerFilters(): boolean {
|
||||
if (this.currentDrilldownLevel > 1 && this.drilldownLayers && this.drilldownLayers.length > 0) {
|
||||
const layerIndex = this.currentDrilldownLevel - 2; // -2 because level 1 is base drilldown
|
||||
return layerIndex < this.drilldownLayers.length &&
|
||||
this.drilldownLayers[layerIndex].filters &&
|
||||
this.drilldownLayers[layerIndex].filters.length > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get active layer filters for current drilldown level
|
||||
getActiveLayerFilters(): any[] {
|
||||
if (this.currentDrilldownLevel > 1 && this.drilldownLayers && this.drilldownLayers.length > 0) {
|
||||
const layerIndex = this.currentDrilldownLevel - 2; // -2 because level 1 is base drilldown
|
||||
if (layerIndex < this.drilldownLayers.length &&
|
||||
this.drilldownLayers[layerIndex].filters) {
|
||||
return this.drilldownLayers[layerIndex].filters;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get filter options for dropdown/multiselect filters
|
||||
getFilterOptions(filter: any): string[] {
|
||||
if (filter.options) {
|
||||
if (Array.isArray(filter.options)) {
|
||||
return filter.options;
|
||||
} else if (typeof filter.options === 'string') {
|
||||
return filter.options.split(',').map(opt => opt.trim()).filter(opt => opt);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// Check if an option is selected for multiselect filters
|
||||
isOptionSelected(filter: any, option: string): boolean {
|
||||
if (!filter.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(filter.value)) {
|
||||
return filter.value.includes(option);
|
||||
}
|
||||
|
||||
return filter.value === option;
|
||||
}
|
||||
|
||||
// Handle base filter changes
|
||||
onBaseFilterChange(filter: any): void {
|
||||
console.log('Base filter changed:', filter);
|
||||
// Refresh data when filter changes
|
||||
this.fetchChartData();
|
||||
}
|
||||
|
||||
// Handle drilldown filter changes
|
||||
onDrilldownFilterChange(filter: any): void {
|
||||
console.log('Drilldown filter changed:', filter);
|
||||
// Refresh data when filter changes
|
||||
this.fetchChartData();
|
||||
}
|
||||
|
||||
// Handle layer filter changes
|
||||
onLayerFilterChange(filter: any): void {
|
||||
console.log('Layer filter changed:', filter);
|
||||
// Refresh data when filter changes
|
||||
this.fetchChartData();
|
||||
}
|
||||
|
||||
// Handle multiselect changes
|
||||
onMultiSelectChange(filter: any, option: string, event: any): void {
|
||||
const checked = event.target.checked;
|
||||
|
||||
// Initialize filter.value as array if it's not already
|
||||
if (!Array.isArray(filter.value)) {
|
||||
filter.value = [];
|
||||
}
|
||||
|
||||
if (checked) {
|
||||
// Add option to array if not already present
|
||||
if (!filter.value.includes(option)) {
|
||||
filter.value.push(option);
|
||||
}
|
||||
} else {
|
||||
// Remove option from array
|
||||
filter.value = filter.value.filter((item: string) => item !== option);
|
||||
}
|
||||
|
||||
// Refresh data when filter changes
|
||||
this.fetchChartData();
|
||||
}
|
||||
|
||||
// Handle date range changes
|
||||
onDateRangeChange(filter: any, event: any): void {
|
||||
// For date range filters, we need to handle the change differently
|
||||
// since we're binding to individual start/end properties
|
||||
if (!filter.value) {
|
||||
filter.value = { start: null, end: null };
|
||||
}
|
||||
|
||||
// Refresh data when filter changes
|
||||
this.fetchChartData();
|
||||
}
|
||||
|
||||
// Handle date range input changes for start/end dates
|
||||
onDateRangeInputChange(filter: any, dateType: string, event: any): void {
|
||||
// Initialize filter.value if it doesn't exist
|
||||
if (!filter.value) {
|
||||
filter.value = { start: null, end: null };
|
||||
}
|
||||
|
||||
// Update the specific date type (start or end)
|
||||
filter.value[dateType] = event.target.value;
|
||||
|
||||
// Refresh data when filter changes
|
||||
this.fetchChartData();
|
||||
}
|
||||
|
||||
// Handle toggle changes
|
||||
onToggleChange(filter: any, checked: boolean): void {
|
||||
filter.value = checked;
|
||||
// Refresh data when filter changes
|
||||
this.fetchChartData();
|
||||
}
|
||||
|
||||
// Toggle multiselect dropdown visibility
|
||||
toggleMultiselect(filter: any, context: string): void {
|
||||
const filterId = `${context}-${filter.field}`;
|
||||
if (this.isMultiselectOpen(filter, context)) {
|
||||
this.openMultiselects.delete(filterId);
|
||||
} else {
|
||||
// Close all other multiselects first
|
||||
this.openMultiselects.clear();
|
||||
this.openMultiselects.set(filterId, context);
|
||||
|
||||
// Add document click handler to close dropdown when clicking outside
|
||||
this.addDocumentClickHandler();
|
||||
}
|
||||
}
|
||||
|
||||
// Add document click handler to close dropdowns when clicking outside
|
||||
private addDocumentClickHandler(): void {
|
||||
if (!this.documentClickHandler) {
|
||||
this.documentClickHandler = (event: MouseEvent) => {
|
||||
const target = event.target as HTMLElement;
|
||||
// Check if click is outside any multiselect dropdown
|
||||
if (!target.closest('.multiselect-container')) {
|
||||
this.openMultiselects.clear();
|
||||
this.removeDocumentClickHandler();
|
||||
}
|
||||
};
|
||||
|
||||
// Use setTimeout to ensure the click event that opened the dropdown doesn't immediately close it
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', this.documentClickHandler!);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove document click handler
|
||||
private removeDocumentClickHandler(): void {
|
||||
if (this.documentClickHandler) {
|
||||
document.removeEventListener('click', this.documentClickHandler);
|
||||
this.documentClickHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if multiselect dropdown is open
|
||||
isMultiselectOpen(filter: any, context: string): boolean {
|
||||
const filterId = `${context}-${filter.field}`;
|
||||
return this.openMultiselects.has(filterId);
|
||||
}
|
||||
|
||||
// Get count of selected options for a multiselect filter
|
||||
getSelectedOptionsCount(filter: any): number {
|
||||
if (!filter.value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Array.isArray(filter.value)) {
|
||||
return filter.value.length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Clear all filters
|
||||
clearAllFilters(): void {
|
||||
// Clear base filters
|
||||
if (this.baseFilters) {
|
||||
this.baseFilters.forEach(filter => {
|
||||
if (filter.type === 'multiselect') {
|
||||
filter.value = [];
|
||||
} else if (filter.type === 'date-range') {
|
||||
filter.value = { start: null, end: null };
|
||||
} else if (filter.type === 'toggle') {
|
||||
filter.value = false;
|
||||
} else {
|
||||
filter.value = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Clear drilldown filters
|
||||
if (this.drilldownFilters) {
|
||||
this.drilldownFilters.forEach(filter => {
|
||||
if (filter.type === 'multiselect') {
|
||||
filter.value = [];
|
||||
} else if (filter.type === 'date-range') {
|
||||
filter.value = { start: null, end: null };
|
||||
} else if (filter.type === 'toggle') {
|
||||
filter.value = false;
|
||||
} else {
|
||||
filter.value = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Clear layer filters
|
||||
if (this.drilldownLayers) {
|
||||
this.drilldownLayers.forEach(layer => {
|
||||
if (layer.filters) {
|
||||
layer.filters.forEach((filter: any) => {
|
||||
if (filter.type === 'multiselect') {
|
||||
filter.value = [];
|
||||
} else if (filter.type === 'date-range') {
|
||||
filter.value = { start: null, end: null };
|
||||
} else if (filter.type === 'toggle') {
|
||||
filter.value = false;
|
||||
} else {
|
||||
filter.value = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Close all multiselect dropdowns
|
||||
this.openMultiselects.clear();
|
||||
|
||||
// Refresh data
|
||||
this.fetchChartData();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user