7 Commits

Author SHA1 Message Date
Gaurav Kumar
3cff84c448 working unified chart with different type chart 2025-11-18 19:12:02 +05:30
Gaurav Kumar
807058e40d chart 2025-11-01 16:20:59 +05:30
Gaurav Kumar
c6022b0e22 bar chart 2025-11-01 12:34:16 +05:30
Gaurav Kumar
9aed6e0d43 Update unified-chart.component.html 2025-11-01 12:18:01 +05:30
Gaurav Kumar
64b664e625 Update editnewdash.component.ts 2025-11-01 11:00:10 +05:30
Gaurav Kumar
49f1a2fbf2 Update unified-chart.component.ts 2025-11-01 10:54:41 +05:30
Gaurav Kumar
6bfe890cd9 Update editnewdash.component.ts 2025-11-01 10:03:00 +05:30
4 changed files with 781 additions and 129 deletions

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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;
this.chartOptions.plugins.legend.display = this.chartLegend;
// 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();
}
}