0" style="background-color: #e0e0e0; padding: 5px; margin-bottom: 10px; border-radius: 4px; text-align: center;">
-
Drilldown Level: {{currentDrilldownLevel}}
-
-
+
+
+
+
+
0">
+
+
+
+
+
+
+
+
+ Drilldown Level: {{currentDrilldownLevel}}
+ 0">
+ (Clicked on: {{drilldownStack[drilldownStack.length - 1].clickedLabel}})
+
+
+
+
+
+
diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss
index 2f11ebd..a9282e4 100644
--- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss
+++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss
@@ -1,31 +1,192 @@
-// Bar Chart Component Styles
-:host {
- display: block;
- height: 100%;
- width: 100%;
+.filter-section {
+ margin-bottom: 20px;
+ padding: 15px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ background-color: #f9f9f9;
}
-.bar-chart-container {
- position: relative;
- height: 100%;
- width: 100%;
-}
-
-canvas {
- display: block;
- max-width: 100%;
- max-height: 100%;
-}
-
-// Responsive design for chart container
-@media (max-width: 768px) {
- .bar-chart-container {
- height: 300px;
+.filter-group {
+ margin-bottom: 15px;
+
+ h4 {
+ margin-top: 0;
+ margin-bottom: 10px;
+ color: #333;
+ font-weight: 600;
}
}
-@media (max-width: 480px) {
- .bar-chart-container {
- height: 250px;
+.filter-controls {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.filter-item {
+ flex: 1 1 300px;
+ min-width: 250px;
+ padding: 10px;
+ background: white;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+}
+
+.filter-label {
+ font-weight: 500;
+ margin-bottom: 8px;
+ color: #555;
+ font-size: 14px;
+}
+
+.filter-input {
+ width: 100%;
+
+ .filter-text-input,
+ .filter-select,
+ .filter-date {
+ width: 100%;
+ padding: 6px 12px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ font-size: 14px;
+ }
+
+ .filter-select {
+ height: 34px;
+ }
+}
+
+.multiselect-container {
+ position: relative;
+}
+
+.multiselect-display {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 6px 12px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ background: white;
+ cursor: pointer;
+ min-height: 34px;
+
+ .multiselect-label {
+ flex: 1;
+ font-size: 14px;
+ }
+
+ .multiselect-value {
+ color: #666;
+ font-size: 12px;
+ margin-right: 8px;
+ }
+
+ .dropdown-icon {
+ flex-shrink: 0;
+ transition: transform 0.2s ease;
+ }
+
+ &:hover {
+ border-color: #999;
+ }
+}
+
+.multiselect-dropdown {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ z-index: 1000;
+ background: white;
+ border: 1px solid #ccc;
+ border-top: none;
+ border-radius: 0 0 4px 4px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ max-height: 200px;
+ overflow-y: auto;
+
+ .checkbox-group {
+ padding: 8px;
+
+ .checkbox-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 4px 0;
+
+ .checkbox-label {
+ margin: 0;
+ font-size: 14px;
+ cursor: pointer;
+ }
+ }
+ }
+}
+
+.date-range {
+ .date-input-group {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .date-separator {
+ margin: 0 5px;
+ color: #777;
+ }
+}
+
+.toggle {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .toggle-label {
+ margin: 0;
+ font-size: 14px;
+ cursor: pointer;
+ }
+}
+
+.filter-actions {
+ margin-top: 15px;
+ padding-top: 15px;
+ border-top: 1px solid #eee;
+
+ .btn {
+ font-size: 13px;
+ }
+}
+
+// New header row styling
+.header-row {
+ margin-bottom: 15px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #eee;
+
+ .chart-title {
+ margin: 0;
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+}
+
+// Responsive design
+@media (max-width: 768px) {
+ .filter-controls {
+ flex-direction: column;
+ }
+
+ .filter-item {
+ min-width: 100%;
+ }
+
+ .header-row {
+ .chart-title {
+ font-size: 16px;
+ }
}
}
\ No newline at end of file
diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts
index 68105a0..f8aed36 100644
--- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts
+++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts
@@ -90,6 +90,11 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy {
// Subscriptions to unsubscribe on destroy
private subscriptions: Subscription[] = [];
+ // Add properties for filter functionality
+ private openMultiselects: Map
= new Map(); // Map of filterId -> context
+ private documentClickHandler: ((event: MouseEvent) => void) | null = null;
+ private filtersInitialized: boolean = false;
+
constructor(
private dashboardService: Dashboard3Service,
private filterService: FilterService
@@ -111,6 +116,12 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy {
ngOnChanges(changes: SimpleChanges): void {
console.log('BarChartComponent input changes:', changes);
+ // Initialize filter values if they haven't been initialized yet
+ if (!this.filtersInitialized && (changes.baseFilters || changes.drilldownFilters || changes.drilldownLayers)) {
+ this.initializeFilterValues();
+ this.filtersInitialized = true;
+ }
+
// Check if any of the key properties have changed
const xAxisChanged = changes.xAxis && !changes.xAxis.firstChange;
const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange;
@@ -141,6 +152,316 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy {
}
}
+ // Initialize filter values with proper default values based on type
+ private initializeFilterValues(): void {
+ console.log('Initializing filter values');
+
+ // Initialize base filters
+ if (this.baseFilters) {
+ this.baseFilters.forEach(filter => {
+ if (filter.value === undefined || filter.value === null) {
+ switch (filter.type) {
+ case 'multiselect':
+ filter.value = [];
+ break;
+ case 'date-range':
+ filter.value = { start: null, end: null };
+ break;
+ case 'toggle':
+ filter.value = false;
+ break;
+ default:
+ filter.value = '';
+ }
+ }
+ });
+ }
+
+ // Initialize drilldown filters
+ if (this.drilldownFilters) {
+ this.drilldownFilters.forEach(filter => {
+ if (filter.value === undefined || filter.value === null) {
+ switch (filter.type) {
+ case 'multiselect':
+ filter.value = [];
+ break;
+ case 'date-range':
+ filter.value = { start: null, end: null };
+ break;
+ case 'toggle':
+ filter.value = false;
+ break;
+ default:
+ filter.value = '';
+ }
+ }
+ });
+ }
+
+ // Initialize layer filters
+ if (this.drilldownLayers) {
+ this.drilldownLayers.forEach(layer => {
+ if (layer.filters) {
+ layer.filters.forEach((filter: any) => {
+ if (filter.value === undefined || filter.value === null) {
+ switch (filter.type) {
+ case 'multiselect':
+ filter.value = [];
+ break;
+ case 'date-range':
+ filter.value = { start: null, end: null };
+ break;
+ case 'toggle':
+ filter.value = false;
+ break;
+ default:
+ filter.value = '';
+ }
+ }
+ });
+ }
+ });
+ }
+
+ console.log('Filter values initialized:', {
+ baseFilters: this.baseFilters,
+ drilldownFilters: this.drilldownFilters,
+ drilldownLayers: this.drilldownLayers
+ });
+ }
+
+ // 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, dateRange: { start: string | null, end: string | null }): void {
+ filter.value = dateRange;
+ // 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();
+ }
+
fetchChartData(): void {
// Set flag to prevent recursive calls
this.isFetchingData = true;
@@ -661,6 +982,12 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy {
this.originalBarChartLabels = [];
this.originalBarChartData = [];
+ // Clear multiselect tracking
+ this.openMultiselects.clear();
+
+ // Remove document click handler
+ this.removeDocumentClickHandler();
+
console.log('BarChartComponent destroyed and cleaned up');
}
}
\ No newline at end of file
diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.html
index b4bf8f7..67590d9 100644
--- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.html
+++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.html
@@ -246,9 +246,9 @@