filter
This commit is contained in:
		
							parent
							
								
									cdcf1e07c7
								
							
						
					
					
						commit
						35e3f411a8
					
				| @ -38,19 +38,6 @@ export class CompactFilterComponent implements OnInit, OnChanges { | ||||
|   ) { } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     // Subscribe to filter definitions to get available filters
 | ||||
|     this.filterService.filters$.subscribe(filters => { | ||||
|       this.availableFilters = filters; | ||||
|       this.updateSelectedFilter(); | ||||
|     }); | ||||
|      | ||||
|     // Subscribe to filter state changes
 | ||||
|     this.filterService.filterState$.subscribe(state => { | ||||
|       if (this.selectedFilter && state.hasOwnProperty(this.selectedFilter.id)) { | ||||
|         this.filterValue = state[this.selectedFilter.id]; | ||||
|       } | ||||
|     }); | ||||
|      | ||||
|     // Initialize configuration from inputs
 | ||||
|     this.configFilterKey = this.filterKey; | ||||
|     this.configFilterType = this.filterType; | ||||
| @ -70,26 +57,53 @@ export class CompactFilterComponent implements OnInit, OnChanges { | ||||
|      | ||||
|     // Register this filter with the filter service
 | ||||
|     this.registerFilter(); | ||||
|      | ||||
|     // Subscribe to filter definitions to get available filters
 | ||||
|     this.filterService.filters$.subscribe(filters => { | ||||
|       this.availableFilters = filters; | ||||
|       this.updateSelectedFilter(); | ||||
|     }); | ||||
|      | ||||
|     // Subscribe to filter state changes
 | ||||
|     this.filterService.filterState$.subscribe(state => { | ||||
|       if (this.selectedFilter && state.hasOwnProperty(this.selectedFilter.id)) { | ||||
|         this.filterValue = state[this.selectedFilter.id]; | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   ngOnChanges(changes: SimpleChanges): void { | ||||
|     // If filterKey or filterType changes, re-register the filter
 | ||||
|     if (changes.filterKey || changes.filterType) { | ||||
|       // Load available values for the current filter key if it's a dropdown or multiselect
 | ||||
|       if ((this.filterType === 'dropdown' || this.filterType === 'multiselect') && this.filterKey) { | ||||
|         this.loadAvailableValues(this.filterKey); | ||||
|       } | ||||
|       this.registerFilter(); | ||||
|     } | ||||
|      | ||||
|     // Handle API URL changes
 | ||||
|     if (changes.apiUrl && !changes.apiUrl.firstChange) { | ||||
|       if (this.apiUrl) { | ||||
|         this.loadAvailableKeys(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   // Register this filter with the filter service
 | ||||
|   registerFilter(): void { | ||||
|     if (this.filterKey) { | ||||
|       // Get current filter values from the service
 | ||||
|       const currentFilterValues = this.filterService.getFilterValues(); | ||||
|        | ||||
|       // Create a filter definition for this compact filter
 | ||||
|       const filterDef: Filter = { | ||||
|         id: `compact-filter-${this.filterKey}`, | ||||
|         id: `${this.filterKey}`, | ||||
|         field: this.filterKey, | ||||
|         label: this.filterLabel || this.filterKey, | ||||
|         type: this.filterType as any, | ||||
|         options: this.filterOptions, | ||||
|         value: this.filterValue | ||||
|         value: this.filterValue // Use the current filter value
 | ||||
|       }; | ||||
|        | ||||
|       // Get current filters
 | ||||
| @ -99,9 +113,32 @@ export class CompactFilterComponent implements OnInit, OnChanges { | ||||
|       const existingFilterIndex = currentFilters.findIndex(f => f.id === filterDef.id); | ||||
|        | ||||
|       if (existingFilterIndex >= 0) { | ||||
|         // Preserve the existing filter configuration
 | ||||
|         const existingFilter = currentFilters[existingFilterIndex]; | ||||
|          | ||||
|         // Preserve the existing filter value if it exists in the service
 | ||||
|         if (currentFilterValues.hasOwnProperty(existingFilter.id)) { | ||||
|           filterDef.value = currentFilterValues[existingFilter.id]; | ||||
|           this.filterValue = filterDef.value; // Update local value
 | ||||
|         } else if (existingFilter.value !== undefined) { | ||||
|           // Fallback to existing filter's value if no service value
 | ||||
|           filterDef.value = existingFilter.value; | ||||
|           this.filterValue = filterDef.value; | ||||
|         } | ||||
|          | ||||
|         // Preserve other configuration properties
 | ||||
|         filterDef.label = existingFilter.label; | ||||
|         filterDef.options = existingFilter.options || this.filterOptions; | ||||
|          | ||||
|         // Update existing filter
 | ||||
|         currentFilters[existingFilterIndex] = filterDef; | ||||
|       } else { | ||||
|         // For new filters, check if there's already a value in the service
 | ||||
|         if (currentFilterValues.hasOwnProperty(filterDef.id)) { | ||||
|           filterDef.value = currentFilterValues[filterDef.id]; | ||||
|           this.filterValue = filterDef.value; // Update local value
 | ||||
|         } | ||||
|          | ||||
|         // Add new filter
 | ||||
|         currentFilters.push(filterDef); | ||||
|       } | ||||
| @ -118,9 +155,24 @@ export class CompactFilterComponent implements OnInit, OnChanges { | ||||
|     if (this.filterKey && this.availableFilters.length > 0) { | ||||
|       this.selectedFilter = this.availableFilters.find(f => f.field === this.filterKey) || null; | ||||
|       if (this.selectedFilter) { | ||||
|         // Get current value for this filter
 | ||||
|         // Get current value for this filter from the service
 | ||||
|         const currentState = this.filterService.getFilterValues(); | ||||
|         this.filterValue = currentState[this.selectedFilter.id] || ''; | ||||
|         const filterValue = currentState[this.selectedFilter.id]; | ||||
|         if (filterValue !== undefined) { | ||||
|           this.filterValue = filterValue; | ||||
|         } else if (this.selectedFilter.value !== undefined) { | ||||
|           // Use the filter's default value if no service value
 | ||||
|           this.filterValue = this.selectedFilter.value; | ||||
|         } else { | ||||
|           // Use the current filter value as fallback
 | ||||
|           this.filterValue = this.filterValue || ''; | ||||
|         } | ||||
|          | ||||
|         // Also update configuration properties from the selected filter
 | ||||
|         this.configFilterKey = this.selectedFilter.field; | ||||
|         this.configFilterType = this.selectedFilter.type; | ||||
|         this.configFilterLabel = this.selectedFilter.label; | ||||
|         this.configFilterOptions = (this.selectedFilter.options || []).join(','); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @ -130,6 +182,14 @@ export class CompactFilterComponent implements OnInit, OnChanges { | ||||
|       this.filterValue = value; | ||||
|       this.filterService.updateFilterValue(this.selectedFilter.id, value); | ||||
|       this.filterChange.emit({ filterId: this.selectedFilter.id, value: value }); | ||||
|        | ||||
|       // Update the filter definition in the service to reflect the new value
 | ||||
|       const currentFilters = this.filterService.getFilters(); | ||||
|       const filterIndex = currentFilters.findIndex(f => f.id === this.selectedFilter.id); | ||||
|       if (filterIndex >= 0) { | ||||
|         currentFilters[filterIndex].value = value; | ||||
|         this.filterService.setFilters(currentFilters); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|    | ||||
| @ -179,11 +239,19 @@ export class CompactFilterComponent implements OnInit, OnChanges { | ||||
|   toggleConfigMode(): void { | ||||
|     this.isConfigMode = !this.isConfigMode; | ||||
|     if (this.isConfigMode) { | ||||
|       // Initialize config values
 | ||||
|       this.configFilterKey = this.filterKey; | ||||
|       this.configFilterType = this.filterType; | ||||
|       this.configFilterLabel = this.filterLabel; | ||||
|       this.configFilterOptions = this.filterOptions.join(','); | ||||
|       // Initialize config values from current filter if available
 | ||||
|       if (this.selectedFilter) { | ||||
|         this.configFilterKey = this.selectedFilter.field; | ||||
|         this.configFilterType = this.selectedFilter.type; | ||||
|         this.configFilterLabel = this.selectedFilter.label; | ||||
|         this.configFilterOptions = (this.selectedFilter.options || []).join(','); | ||||
|       } else { | ||||
|         // Fallback to current properties
 | ||||
|         this.configFilterKey = this.filterKey; | ||||
|         this.configFilterType = this.filterType; | ||||
|         this.configFilterLabel = this.filterLabel; | ||||
|         this.configFilterOptions = this.filterOptions.join(','); | ||||
|       } | ||||
|       this.configApiUrl = this.apiUrl; | ||||
|       this.configConnectionId = this.connectionId; | ||||
|     } | ||||
| @ -216,8 +284,8 @@ export class CompactFilterComponent implements OnInit, OnChanges { | ||||
|     } | ||||
|      | ||||
|     // Load available values for the selected key if it's a dropdown or multiselect
 | ||||
|     if ((this.filterType === 'dropdown' || this.filterType === 'multiselect') && this.filterKey) { | ||||
|       this.loadAvailableValues(this.filterKey); | ||||
|     if ((this.configFilterType === 'dropdown' || this.configFilterType === 'multiselect') && this.configFilterKey) { | ||||
|       this.loadAvailableValues(this.configFilterKey); | ||||
|     } | ||||
|      | ||||
|     // Register the updated filter with the filter service
 | ||||
|  | ||||
| @ -782,6 +782,8 @@ export class EditnewdashComponent implements OnInit { | ||||
|           xyz.filterType = this.gadgetsEditdata.filterType || 'text'; | ||||
|           xyz.filterLabel = this.gadgetsEditdata.filterLabel || ''; | ||||
|           xyz.filterOptions = this.gadgetsEditdata.filterOptions || []; | ||||
|           xyz.table = this.gadgetsEditdata.table || ''; | ||||
|           xyz.connection = this.gadgetsEditdata.connection || undefined; | ||||
|         } | ||||
|          | ||||
|         console.log(xyz); | ||||
| @ -818,7 +820,97 @@ export class EditnewdashComponent implements OnInit { | ||||
|    * This prevents errors when trying to set properties that don't exist on the components | ||||
|    */ | ||||
|   getChartInputs(item: any): any { | ||||
|     // Only pass properties that are relevant to chart components
 | ||||
|     // For CompactFilterComponent, pass only filter configuration properties
 | ||||
|     if (item.component && item.component.name === 'CompactFilterComponent') { | ||||
|       const filterInputs = { | ||||
|         filterKey: item['filterKey'] || '', | ||||
|         filterType: item['filterType'] || 'text', | ||||
|         filterLabel: item['filterLabel'] || '', | ||||
|         filterOptions: item['filterOptions'] || [], | ||||
|         apiUrl: item['table'] || '', // Use table as API URL
 | ||||
|         connectionId: item['connection'] ? parseInt(item['connection'], 10) : undefined | ||||
|       }; | ||||
|        | ||||
|       // Preserve configuration in the item itself
 | ||||
|       item['filterKey'] = filterInputs['filterKey']; | ||||
|       item['filterType'] = filterInputs['filterType']; | ||||
|       item['filterLabel'] = filterInputs['filterLabel']; | ||||
|       item['filterOptions'] = filterInputs['filterOptions']; | ||||
|       item['table'] = filterInputs['apiUrl']; | ||||
|       item['connection'] = item['connection']; | ||||
|        | ||||
|       // Remove undefined properties to avoid passing unnecessary data
 | ||||
|       Object.keys(filterInputs).forEach(key => { | ||||
|         if (filterInputs[key] === undefined) { | ||||
|           delete filterInputs[key]; | ||||
|         } | ||||
|       }); | ||||
|        | ||||
|       return filterInputs; | ||||
|     } | ||||
|      | ||||
|     // For CommonFilterComponent, pass only filter-related properties
 | ||||
|     if (item.component && item.component.name === 'CommonFilterComponent') { | ||||
|       const commonFilterInputs = { | ||||
|         baseFilters: item['baseFilters'] || [], | ||||
|         drilldownFilters: item['drilldownFilters'] || [], | ||||
|         drilldownLayers: item['drilldownLayers'] || [], | ||||
|         fieldName: item['name'] || '', | ||||
|         connection: item['connection'] || undefined | ||||
|       }; | ||||
|        | ||||
|       // Remove undefined properties to avoid passing unnecessary data
 | ||||
|       Object.keys(commonFilterInputs).forEach(key => { | ||||
|         if (commonFilterInputs[key] === undefined) { | ||||
|           delete commonFilterInputs[key]; | ||||
|         } | ||||
|       }); | ||||
|        | ||||
|       return commonFilterInputs; | ||||
|     } | ||||
|      | ||||
|     // For GridViewComponent, pass chart properties with drilldown support
 | ||||
|     if (item.component && item.component.name === 'GridViewComponent') { | ||||
|       const gridInputs = { | ||||
|         xAxis: item.xAxis, | ||||
|         yAxis: item.yAxis, | ||||
|         table: item.table, | ||||
|         datastore: item.datastore, | ||||
|         charttitle: item.charttitle, | ||||
|         chartlegend: item.chartlegend, | ||||
|         showlabel: item.showlabel, | ||||
|         chartcolor: item.chartcolor, | ||||
|         slices: item.slices, | ||||
|         donut: item.donut, | ||||
|         charturl: item.charturl, | ||||
|         chartparameter: item.chartparameter, | ||||
|         datasource: item.datasource, | ||||
|         fieldName: item.name, // Using item.name as fieldName
 | ||||
|         connection: item['connection'], // Add connection field using bracket notation
 | ||||
|         // Base drilldown configuration properties
 | ||||
|         drilldownEnabled: item['drilldownEnabled'], | ||||
|         drilldownApiUrl: item['drilldownApiUrl'], | ||||
|         // Removed drilldownParameterKey since we're using URL templates
 | ||||
|         drilldownXAxis: item['drilldownXAxis'], | ||||
|         drilldownYAxis: item['drilldownYAxis'], | ||||
|         drilldownParameter: item['drilldownParameter'], // Add drilldown parameter
 | ||||
|         baseFilters: item['baseFilters'] || [], // Add base filters
 | ||||
|         drilldownFilters: item['drilldownFilters'] || [], // Add drilldown filters
 | ||||
|         // Multi-layer drilldown configurations
 | ||||
|         drilldownLayers: item['drilldownLayers'] || [] | ||||
|       }; | ||||
|        | ||||
|       // Remove undefined properties to avoid passing unnecessary data
 | ||||
|       Object.keys(gridInputs).forEach(key => { | ||||
|         if (gridInputs[key] === undefined) { | ||||
|           delete gridInputs[key]; | ||||
|         } | ||||
|       }); | ||||
|        | ||||
|       return gridInputs; | ||||
|     } | ||||
|      | ||||
|     // For all other chart components, pass chart-specific properties
 | ||||
|     const chartInputs = { | ||||
|       xAxis: item.xAxis, | ||||
|       yAxis: item.yAxis, | ||||
| @ -848,25 +940,6 @@ export class EditnewdashComponent implements OnInit { | ||||
|       drilldownLayers: item['drilldownLayers'] || [] | ||||
|     }; | ||||
|      | ||||
|     // For CommonFilterComponent, also pass baseFilters, drilldownFilters, drilldownLayers, fieldName, and connection
 | ||||
|     if (item.component && item.component.name === 'CommonFilterComponent') { | ||||
|       chartInputs['baseFilters'] = item['baseFilters'] || []; | ||||
|       chartInputs['drilldownFilters'] = item['drilldownFilters'] || []; | ||||
|       chartInputs['drilldownLayers'] = item['drilldownLayers'] || []; | ||||
|       chartInputs['fieldName'] = item['name'] || ''; | ||||
|       chartInputs['connection'] = item['connection'] || undefined; | ||||
|     } | ||||
|      | ||||
|     // For CompactFilterComponent, pass filter configuration properties
 | ||||
|     if (item.component && item.component.name === 'CompactFilterComponent') { | ||||
|       chartInputs['filterKey'] = item['filterKey'] || ''; | ||||
|       chartInputs['filterType'] = item['filterType'] || 'text'; | ||||
|       chartInputs['filterLabel'] = item['filterLabel'] || ''; | ||||
|       chartInputs['filterOptions'] = item['filterOptions'] || []; | ||||
|       chartInputs['apiUrl'] = item['table'] || ''; // Use table as API URL
 | ||||
|       chartInputs['connectionId'] = item['connection'] ? parseInt(item['connection'], 10) : undefined; | ||||
|     } | ||||
|      | ||||
|     // Remove undefined properties to avoid passing unnecessary data
 | ||||
|     Object.keys(chartInputs).forEach(key => { | ||||
|       if (chartInputs[key] === undefined) { | ||||
| @ -929,6 +1002,12 @@ export class EditnewdashComponent implements OnInit { | ||||
|           updatedItem.filterOptions = this.gadgetsEditdata.filterOptions || []; | ||||
|           updatedItem.table = this.gadgetsEditdata.table || ''; // API URL
 | ||||
|           updatedItem.connection = this.gadgetsEditdata.connection || undefined; // Connection ID
 | ||||
|            | ||||
|           // Also preserve these properties in gadgetsEditdata for consistency
 | ||||
|           this.gadgetsEditdata.filterKey = updatedItem.filterKey; | ||||
|           this.gadgetsEditdata.filterType = updatedItem.filterType; | ||||
|           this.gadgetsEditdata.filterLabel = updatedItem.filterLabel; | ||||
|           this.gadgetsEditdata.filterOptions = updatedItem.filterOptions; | ||||
|         } | ||||
|          | ||||
|         console.log('Updated item:', updatedItem); | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							| @ -1,50 +1,32 @@ | ||||
| <div class="chart-container"> | ||||
|   <!-- Compact Filters --> | ||||
|   <div class="compact-filters-container" *ngIf="baseFilters && baseFilters.length > 0"> | ||||
|     <app-compact-filter  | ||||
|       *ngFor="let filter of baseFilters"  | ||||
|       [filterKey]="filter.field" | ||||
|       (filterChange)="onFilterChange($event)"> | ||||
|     </app-compact-filter> | ||||
|   </div> | ||||
| <div style="display: block; height: 100%; width: 100%;"> | ||||
|   <!-- No filter controls needed with the new simplified approach --> | ||||
|   <!-- Filters are now configured at the drilldown level --> | ||||
|    | ||||
|   <!-- Drilldown mode indicator --> | ||||
|   <div *ngIf="currentDrilldownLevel > 0" class="drilldown-indicator"> | ||||
|     <span class="drilldown-text">Drilldown Level: {{currentDrilldownLevel}}</span> | ||||
|     <button class="btn btn-secondary btn-sm" (click)="navigateBack()"> | ||||
|   <div *ngIf="currentDrilldownLevel > 0" style="background-color: #e0e0e0; padding: 5px; margin-bottom: 10px; border-radius: 4px; text-align: center;"> | ||||
|     <span style="font-weight: bold; color: #333;">Drilldown Level: {{currentDrilldownLevel}}</span> | ||||
|     <button (click)="navigateBack()" style="margin-left: 10px; padding: 2px 8px; background-color: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;"> | ||||
|       Back to Level {{currentDrilldownLevel - 1}} | ||||
|     </button> | ||||
|     <button class="btn btn-danger btn-sm" (click)="resetToOriginalData()"> | ||||
|     <button (click)="resetToOriginalData()" style="margin-left: 10px; padding: 2px 8px; background-color: #dc3545; color: white; border: none; border-radius: 3px; cursor: pointer;"> | ||||
|       Back to Main View | ||||
|     </button> | ||||
|   </div> | ||||
|    | ||||
|   <div class="chart-header"> | ||||
|     <h3>{{ charttitle || 'Bar Chart' }}</h3> | ||||
|   <!-- No data message --> | ||||
|   <div *ngIf="noDataAvailable" style="text-align: center; padding: 20px; color: #666; font-style: italic;"> | ||||
|     No data available | ||||
|   </div> | ||||
|    | ||||
|   <div class="chart-wrapper"> | ||||
|     <div class="chart-content" [class.loading]="barChartData.length === 0 && barChartLabels.length === 0 && !noDataAvailable"> | ||||
|       <!-- No data message --> | ||||
|       <div class="no-data-message" *ngIf="noDataAvailable"> | ||||
|         <p>No data available</p> | ||||
|       </div> | ||||
|        | ||||
|       <!-- Chart display --> | ||||
|       <canvas baseChart | ||||
|         *ngIf="!noDataAvailable" | ||||
|         [datasets]="barChartData" | ||||
|         [labels]="barChartLabels" | ||||
|         [type]="barChartType" | ||||
|         [options]="barChartOptions" | ||||
|         (chartHover)="chartHovered($event)" | ||||
|         (chartClick)="chartClicked($event)"> | ||||
|       </canvas> | ||||
|        | ||||
|       <!-- Loading overlay --> | ||||
|       <div class="loading-overlay" *ngIf="barChartData.length === 0 && barChartLabels.length === 0 && !noDataAvailable"> | ||||
|         <div class="shimmer-bar"></div> | ||||
|       </div> | ||||
|     </div> | ||||
|   <!-- Chart display --> | ||||
|   <div *ngIf="!noDataAvailable" style="position: relative; height: calc(100% - 50px);"> | ||||
|     <canvas baseChart | ||||
|       [datasets]="barChartData" | ||||
|       [labels]="barChartLabels" | ||||
|       [type]="barChartType" | ||||
|       [options]="barChartOptions" | ||||
|       (chartHover)="chartHovered($event)" | ||||
|       (chartClick)="chartClicked($event)"> | ||||
|     </canvas> | ||||
|   </div> | ||||
| </div> | ||||
| @ -5,214 +5,27 @@ | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .chart-container { | ||||
|   height: 100%; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); | ||||
|   border-radius: 12px; | ||||
|   box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); | ||||
|   font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | ||||
|   transition: all 0.3s ease; | ||||
|   border: 1px solid #eaeaea; | ||||
|   padding: 20px; | ||||
| } | ||||
| 
 | ||||
| .chart-container:hover { | ||||
|   box-shadow: 0 6px 25px rgba(0, 0, 0, 0.2); | ||||
|   transform: translateY(-2px); | ||||
| } | ||||
| 
 | ||||
| .compact-filters-container { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
|   gap: 5px; | ||||
|   margin-bottom: 10px; | ||||
|   padding: 5px; | ||||
|   background: #ffffff; | ||||
|   border: 1px solid #e9ecef; | ||||
|   border-radius: 6px; | ||||
|   min-height: 40px; | ||||
| } | ||||
| 
 | ||||
| .drilldown-indicator { | ||||
|   background-color: #e0e0e0; | ||||
|   padding: 10px; | ||||
|   margin-bottom: 15px; | ||||
|   border-radius: 8px; | ||||
|   text-align: center; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   gap: 10px; | ||||
|   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | ||||
| } | ||||
| 
 | ||||
| .drilldown-text { | ||||
|   font-weight: bold; | ||||
|   color: #333; | ||||
|   font-size: 16px; | ||||
| } | ||||
| 
 | ||||
| .btn { | ||||
|   padding: 6px 12px; | ||||
|   border: none; | ||||
|   border-radius: 4px; | ||||
|   cursor: pointer; | ||||
|   font-size: 14px; | ||||
|   transition: all 0.3s ease; | ||||
| } | ||||
| 
 | ||||
| .btn:hover { | ||||
|   transform: translateY(-2px); | ||||
|   box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); | ||||
| } | ||||
| 
 | ||||
| .btn-sm { | ||||
|   padding: 4px 8px; | ||||
|   font-size: 12px; | ||||
| } | ||||
| 
 | ||||
| .btn-secondary { | ||||
|   background-color: #007cba; | ||||
|   color: white; | ||||
| } | ||||
| 
 | ||||
| .btn-danger { | ||||
|   background-color: #dc3545; | ||||
|   color: white; | ||||
| } | ||||
| 
 | ||||
| .chart-header { | ||||
|   margin-bottom: 20px; | ||||
|    | ||||
|   h3 { | ||||
|     font-size: 22px; | ||||
|     font-weight: 600; | ||||
|     color: #0a192f; | ||||
|     margin: 0; | ||||
|     text-align: center; | ||||
|     padding-bottom: 10px; | ||||
|     border-bottom: 2px solid #3498db; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .chart-wrapper { | ||||
|   flex: 1; | ||||
| .bar-chart-container { | ||||
|   position: relative; | ||||
|    | ||||
|   .chart-content { | ||||
|     position: relative; | ||||
|     height: 100%; | ||||
|     background: #f8f9fa; | ||||
|     border: 1px solid #e9ecef; | ||||
|     border-radius: 8px; | ||||
|     padding: 10px; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|      | ||||
|     &.loading { | ||||
|       opacity: 0.7; | ||||
|        | ||||
|       canvas { | ||||
|         filter: blur(2px); | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     canvas { | ||||
|       max-width: 100%; | ||||
|       max-height: 100%; | ||||
|       transition: filter 0.3s ease; | ||||
|       filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); | ||||
|     } | ||||
|      | ||||
|     canvas:hover { | ||||
|       filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15)); | ||||
|       transform: scale(1.02); | ||||
|       transition: all 0.3s ease; | ||||
|     } | ||||
|      | ||||
|     .no-data-message { | ||||
|       text-align: center; | ||||
|       padding: 30px; | ||||
|       color: #666; | ||||
|       font-size: 18px; | ||||
|       font-style: italic; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|       height: 100%; | ||||
|       width: 100%; | ||||
|     } | ||||
|      | ||||
|     .no-data-message p { | ||||
|       margin: 0; | ||||
|     } | ||||
|      | ||||
|     .loading-overlay { | ||||
|       position: absolute; | ||||
|       top: 0; | ||||
|       left: 0; | ||||
|       right: 0; | ||||
|       bottom: 0; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|       background: rgba(255, 255, 255, 0.8); | ||||
|        | ||||
|       .shimmer-bar { | ||||
|         width: 80%; | ||||
|         height: 20px; | ||||
|         background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); | ||||
|         background-size: 200% 100%; | ||||
|         animation: shimmer 1.5s infinite; | ||||
|         border-radius: 4px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   height: 100%; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| @keyframes shimmer { | ||||
|   0% { | ||||
|     background-position: -200% 0; | ||||
|   } | ||||
|   100% { | ||||
|     background-position: 200% 0; | ||||
|   } | ||||
| canvas { | ||||
|   display: block; | ||||
|   max-width: 100%; | ||||
|   max-height: 100%; | ||||
| } | ||||
| 
 | ||||
| // Responsive design for chart container | ||||
| @media (max-width: 768px) { | ||||
|   .chart-container { | ||||
|     padding: 15px; | ||||
|   } | ||||
|    | ||||
|   .chart-header h3 { | ||||
|     font-size: 18px; | ||||
|   } | ||||
|    | ||||
|   .drilldown-indicator { | ||||
|     flex-direction: column; | ||||
|     gap: 5px; | ||||
|   } | ||||
|    | ||||
|   .drilldown-text { | ||||
|     font-size: 14px; | ||||
|   } | ||||
|    | ||||
|   .compact-filters-container { | ||||
|     flex-wrap: wrap; | ||||
|   .bar-chart-container { | ||||
|     height: 300px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 480px) { | ||||
|   .chart-container { | ||||
|     padding: 10px; | ||||
|   } | ||||
|    | ||||
|   .chart-header h3 { | ||||
|     font-size: 16px; | ||||
|   .bar-chart-container { | ||||
|     height: 250px; | ||||
|   } | ||||
| } | ||||
| @ -141,12 +141,6 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Handle filter changes from compact filters
 | ||||
|   onFilterChange(event: { filterId: string, value: any }): void { | ||||
|     console.log('Compact filter changed:', event); | ||||
|     // The filter service will automatically trigger chart updates through the subscription
 | ||||
|   } | ||||
| 
 | ||||
|   fetchChartData(): void { | ||||
|     // Set flag to prevent recursive calls
 | ||||
|     this.isFetchingData = true; | ||||
| @ -179,73 +173,41 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { | ||||
|       console.log('Bar chart data URL:', url); | ||||
|        | ||||
|       // Convert baseFilters to filter parameters
 | ||||
|       let filterParams = ''; | ||||
|       const filterObj = {}; | ||||
|        | ||||
|       // Add base filters
 | ||||
|       if (this.baseFilters && this.baseFilters.length > 0) { | ||||
|         const filterObj = {}; | ||||
|         this.baseFilters.forEach(filter => { | ||||
|           if (filter.field && filter.value) { | ||||
|             filterObj[filter.field] = filter.value; | ||||
|           } | ||||
|         }); | ||||
|         if (Object.keys(filterObj).length > 0) { | ||||
|           filterParams = JSON.stringify(filterObj); | ||||
|         } | ||||
|       } | ||||
|       console.log('Base filters:', this.baseFilters); | ||||
|       console.log('Base filter params:', filterParams); | ||||
|        | ||||
|       // Add common filters to filter parameters
 | ||||
|       // Add common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|       console.log('Common filters from service:', commonFilters); | ||||
|       console.log('Filter definitions:', filterDefinitions); | ||||
|       Object.keys(commonFilters).forEach(filterId => { | ||||
|         const filterValue = commonFilters[filterId]; | ||||
|          | ||||
|       if (Object.keys(commonFilters).length > 0) { | ||||
|         // Merge common filters with base filters
 | ||||
|         const mergedFilterObj = {}; | ||||
|         // Find the filter definition to get the field name
 | ||||
|         const filterDef = this.filterService.getFilters().find(f => f.id === filterId); | ||||
|          | ||||
|         // Add base filters first
 | ||||
|         if (filterParams) { | ||||
|           try { | ||||
|             const baseFilterObj = JSON.parse(filterParams); | ||||
|             Object.assign(mergedFilterObj, baseFilterObj); | ||||
|           } catch (e) { | ||||
|             console.warn('Failed to parse base filter parameters:', e); | ||||
|         if (filterDef && filterDef.field) { | ||||
|           const fieldName = filterDef.field; | ||||
|           if (filterValue !== undefined && filterValue !== null && filterValue !== '') { | ||||
|             filterObj[fieldName] = filterValue; | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|        | ||||
|         // Add common filters using the field name as the key, not the filter id
 | ||||
|         Object.keys(commonFilters).forEach(filterId => { | ||||
|           const filterValue = commonFilters[filterId]; | ||||
|           console.log(`Processing filter ID: ${filterId}, Value:`, filterValue); | ||||
|            | ||||
|           // Find the filter definition to get the field name
 | ||||
|           const filterDef = this.filterService.getFilters().find(f => f.id === filterId); | ||||
|           console.log(`Filter definition for ${filterId}:`, filterDef); | ||||
|            | ||||
|           if (filterDef && filterDef.field) { | ||||
|             const fieldName = filterDef.field; | ||||
|             console.log(`Mapping filter ID ${filterId} to field name: ${fieldName}`); | ||||
|             if (filterValue !== undefined && filterValue !== null && filterValue !== '') { | ||||
|               mergedFilterObj[fieldName] = filterValue; | ||||
|               console.log(`Added to merged filters: ${fieldName} =`, filterValue); | ||||
|             } | ||||
|           } else { | ||||
|             // Fallback to using filterId as field name if no field is defined
 | ||||
|             console.log(`No field name found for filter ID ${filterId}, using ID as field name`); | ||||
|             if (filterValue !== undefined && filterValue !== null && filterValue !== '') { | ||||
|               mergedFilterObj[filterId] = filterValue; | ||||
|               console.log(`Added to merged filters: ${filterId} =`, filterValue); | ||||
|             } | ||||
|           } | ||||
|         }); | ||||
|          | ||||
|         if (Object.keys(mergedFilterObj).length > 0) { | ||||
|           filterParams = JSON.stringify(mergedFilterObj); | ||||
|         } | ||||
|       // Convert to JSON string for API call
 | ||||
|       let filterParams = ''; | ||||
|       if (Object.keys(filterObj).length > 0) { | ||||
|         filterParams = JSON.stringify(filterObj); | ||||
|       } | ||||
|        | ||||
|       console.log('Final merged filter object:', filterParams); | ||||
|       console.log('Final filter object:', filterObj); | ||||
|       // Fetch data from the dashboard service with parameter field and value
 | ||||
|       // For base level, we pass empty parameter and value, but now also pass filters
 | ||||
|       const subscription = this.dashboardService.getChartData(this.table, 'bar', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( | ||||
| @ -400,61 +362,47 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { | ||||
|     console.log('Drilldown data URL:', url); | ||||
|      | ||||
|     // Convert drilldown layer filters to filter parameters (if applicable)
 | ||||
|     let filterParams = ''; | ||||
|     const filterObj = {}; | ||||
|      | ||||
|     // Add drilldown layer filters
 | ||||
|     if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { | ||||
|       const filterObj = {}; | ||||
|       drilldownConfig.filters.forEach((filter: any) => { | ||||
|         if (filter.field && filter.value) { | ||||
|           filterObj[filter.field] = filter.value; | ||||
|         } | ||||
|       }); | ||||
|       if (Object.keys(filterObj).length > 0) { | ||||
|         filterParams = JSON.stringify(filterObj); | ||||
|       } | ||||
|     } | ||||
|     console.log('Drilldown layer filter parameters:', filterParams); | ||||
|      | ||||
|     // Convert drilldownFilters to filter parameters for drilldown level
 | ||||
|     let drilldownFilterParams = ''; | ||||
|     // Add drilldownFilters
 | ||||
|     if (this.drilldownFilters && this.drilldownFilters.length > 0) { | ||||
|       const filterObj = {}; | ||||
|       this.drilldownFilters.forEach(filter => { | ||||
|         if (filter.field && filter.value) { | ||||
|           filterObj[filter.field] = filter.value; | ||||
|         } | ||||
|       }); | ||||
|       if (Object.keys(filterObj).length > 0) { | ||||
|         drilldownFilterParams = JSON.stringify(filterObj); | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     // Add common filters to drilldown filter parameters
 | ||||
|     // Add common filters
 | ||||
|     const commonFilters = this.filterService.getFilterValues(); | ||||
|     if (Object.keys(commonFilters).length > 0) { | ||||
|       // Merge common filters with drilldown filters
 | ||||
|       const mergedFilterObj = {}; | ||||
|     const filterDefinitions = this.filterService.getFilters(); | ||||
|     Object.keys(commonFilters).forEach(filterId => { | ||||
|       const filterValue = commonFilters[filterId]; | ||||
|        | ||||
|       // Add drilldown filters first
 | ||||
|       if (drilldownFilterParams) { | ||||
|         try { | ||||
|           const drilldownFilterObj = JSON.parse(drilldownFilterParams); | ||||
|           Object.assign(mergedFilterObj, drilldownFilterObj); | ||||
|         } catch (e) { | ||||
|           console.warn('Failed to parse drilldown filter parameters:', e); | ||||
|       // Find the filter definition to get the field name
 | ||||
|       const filterDef = this.filterService.getFilters().find(f => f.id === filterId); | ||||
|        | ||||
|       if (filterDef && filterDef.field) { | ||||
|         const fieldName = filterDef.field; | ||||
|         if (filterValue !== undefined && filterValue !== null && filterValue !== '') { | ||||
|           filterObj[fieldName] = filterValue; | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|      | ||||
|       // Add common filters
 | ||||
|       Object.keys(commonFilters).forEach(key => { | ||||
|         const value = commonFilters[key]; | ||||
|         if (value !== undefined && value !== null && value !== '') { | ||||
|           mergedFilterObj[key] = value; | ||||
|         } | ||||
|       }); | ||||
|        | ||||
|       if (Object.keys(mergedFilterObj).length > 0) { | ||||
|         drilldownFilterParams = JSON.stringify(mergedFilterObj); | ||||
|       } | ||||
|     // Convert to JSON string for API call
 | ||||
|     let drilldownFilterParams = ''; | ||||
|     if (Object.keys(filterObj).length > 0) { | ||||
|       drilldownFilterParams = JSON.stringify(filterObj); | ||||
|     } | ||||
|      | ||||
|     console.log('Drilldown filter parameters:', drilldownFilterParams); | ||||
|  | ||||
| @ -4,9 +4,41 @@ | ||||
|       <div class="clr-col-8"> | ||||
|         <h3>{{charttitle || 'Data Grid'}}</h3> | ||||
|       </div> | ||||
|       <!-- Add drilldown navigation controls --> | ||||
|       <div class="clr-col-4" *ngIf="drilldownEnabled && drilldownStack.length > 0" style="text-align: right;"> | ||||
|         <button class="btn btn-sm btn-link" (click)="navigateBack()"> | ||||
|           <cds-icon shape="arrow" direction="left"></cds-icon> | ||||
|           Back to {{drilldownStack.length > 1 ? 'Previous Level' : 'Main Data'}} | ||||
|         </button> | ||||
|       </div> | ||||
|     </div> | ||||
|      | ||||
|     <!-- Show current drilldown level --> | ||||
|     <div class="clr-row" *ngIf="drilldownEnabled && drilldownStack.length > 0"> | ||||
|       <div class="clr-col-12"> | ||||
|         <div class="alert alert-info" style="padding: 8px 12px; margin-bottom: 12px;"> | ||||
|           <div class="alert-items"> | ||||
|             <div class="alert-item static"> | ||||
|               <div class="alert-icon-wrapper"> | ||||
|                 <cds-icon class="alert-icon" shape="info-circle"></cds-icon> | ||||
|               </div> | ||||
|               <span class="alert-text"> | ||||
|                 Drilldown Level: {{currentDrilldownLevel}}  | ||||
|                 <span *ngIf="drilldownStack.length > 0"> | ||||
|                   (Clicked on: {{drilldownStack[drilldownStack.length - 1].clickedKey}} = {{drilldownStack[drilldownStack.length - 1].clickedValue}}) | ||||
|                 </span> | ||||
|               </span> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|      | ||||
|     <clr-datagrid [clrDgLoading]="loading"> | ||||
|       <clr-dg-placeholder> <ng-template #loadingSpinner><clr-spinner>Loading ... </clr-spinner></ng-template> | ||||
|       <clr-dg-placeholder>  | ||||
|         <ng-template #loadingSpinner> | ||||
|           <clr-spinner>Loading ... </clr-spinner> | ||||
|         </ng-template> | ||||
|         <div *ngIf="error;else loadingSpinner">{{error}}</div> | ||||
|       </clr-dg-placeholder> | ||||
| 
 | ||||
| @ -19,7 +51,7 @@ | ||||
| 
 | ||||
|       <clr-dg-row *clrDgItems="let item of givendata" [clrDgItem]="item"> | ||||
|         <!-- Dynamic cells based on response keys --> | ||||
|         <clr-dg-cell *ngFor="let header of dynamicHeaders"> | ||||
|         <clr-dg-cell *ngFor="let header of dynamicHeaders" (click)="onRowClick(item, header.key)"> | ||||
|           {{item[header.key]}} | ||||
|         </clr-dg-cell> | ||||
|       </clr-dg-row> | ||||
|  | ||||
| @ -1,12 +1,28 @@ | ||||
| @import '../../../../../../../styles1.scss'; | ||||
| input.ng-invalid.ng-touched { | ||||
|   border-color: red; | ||||
| // Add styles for drilldown navigation | ||||
| .alert-info { | ||||
|   background-color: #dcedf7; | ||||
|   border-color: #a3d4f5; | ||||
|   color: #21333b; | ||||
| } | ||||
| 
 | ||||
| .error_mess { | ||||
|   color: red; | ||||
| .alert-info .alert-icon { | ||||
|   color: #0072a3; | ||||
| } | ||||
| clr-datagrid{ | ||||
|     height: 400px; /* Adjust the height as needed */ | ||||
|   overflow-y: auto; | ||||
| 
 | ||||
| .btn-link { | ||||
|   color: #0072a3; | ||||
|   text-decoration: none; | ||||
| } | ||||
| 
 | ||||
| .btn-link:hover { | ||||
|   color: #00567a; | ||||
|   text-decoration: underline; | ||||
| } | ||||
| 
 | ||||
| .dg-wrapper { | ||||
|   padding: 12px; | ||||
| } | ||||
| 
 | ||||
| .clr-row { | ||||
|   margin-bottom: 12px; | ||||
| } | ||||
| @ -1,13 +1,17 @@ | ||||
| import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core'; | ||||
| import { Component, OnInit, Input, OnChanges, OnDestroy, SimpleChanges, ViewChild } from '@angular/core'; | ||||
| import { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-grid-view', | ||||
|   templateUrl: './grid-view.component.html', | ||||
|   styleUrls: ['./grid-view.component.scss'] | ||||
| }) | ||||
| export class GridViewComponent implements OnInit, OnChanges { | ||||
| export class GridViewComponent implements OnInit, OnChanges, OnDestroy { | ||||
|   @Input() xAxis: string; | ||||
|   @Input() yAxis: string | string[]; | ||||
|   @Input() table: string; | ||||
| @ -23,6 +27,16 @@ export class GridViewComponent implements OnInit, OnChanges { | ||||
|   @Input() datasource: string; | ||||
|   @Input() fieldName: string; | ||||
|   @Input() connection: number; // Add connection input
 | ||||
|   // Drilldown configuration inputs
 | ||||
|   @Input() drilldownEnabled: boolean = false; | ||||
|   @Input() drilldownApiUrl: string; | ||||
|   @Input() drilldownXAxis: string; | ||||
|   @Input() drilldownYAxis: string; | ||||
|   @Input() drilldownParameter: string; // Add drilldown parameter input
 | ||||
|   @Input() baseFilters: any[] = []; // Add base filters input
 | ||||
|   @Input() drilldownFilters: any[] = []; // Add drilldown filters input
 | ||||
|   // Multi-layer drilldown configuration inputs
 | ||||
|   @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations
 | ||||
| 
 | ||||
|   loading = false; | ||||
|   givendata: any[] = []; | ||||
| @ -38,13 +52,37 @@ export class GridViewComponent implements OnInit, OnChanges { | ||||
|   submitted = false; | ||||
|   dynamicHeaders: any[] = []; | ||||
| 
 | ||||
|   constructor( | ||||
|   // Multi-layer drilldown state tracking
 | ||||
|   drilldownStack: any[] = []; // Stack to track drilldown navigation history
 | ||||
|   currentDrilldownLevel: number = 0; // Current drilldown level (0 = base level)
 | ||||
|   originalGridData: any[] = []; | ||||
| 
 | ||||
|   // No data state
 | ||||
|   noDataAvailable: boolean = false; | ||||
| 
 | ||||
|   // Flag to prevent infinite loops
 | ||||
|   private isFetchingData: boolean = false; | ||||
| 
 | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
| 
 | ||||
|   constructor( | ||||
|     private mainservice: UsergrpmaintainceService, | ||||
|     private dashboardService: Dashboard3Service, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService | ||||
|   ) { } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         // When filters change, refresh the grid data
 | ||||
|         console.log('GridView: Filter state changed:', filters); | ||||
|         this.fetchGridData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.fetchGridData(); | ||||
|   } | ||||
| 
 | ||||
| @ -55,12 +93,21 @@ export class GridViewComponent implements OnInit, OnChanges { | ||||
|     const xAxisChanged = changes.xAxis && !changes.xAxis.firstChange; | ||||
|     const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; | ||||
|     const tableChanged = changes.table && !changes.table.firstChange; | ||||
|     const connectionChanged = changes.connection && !changes.connection.firstChange; // Add connection change detection
 | ||||
|     const connectionChanged = changes.connection && !changes.connection.firstChange; | ||||
|     const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; | ||||
|     // Drilldown configuration changes
 | ||||
|     const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; | ||||
|     const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; | ||||
|     const drilldownXAxisChanged = changes.drilldownXAxis && !changes.drilldownXAxis.firstChange; | ||||
|     const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; | ||||
|     const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; | ||||
| 
 | ||||
|     // Respond to input changes
 | ||||
|     if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged) { | ||||
|       console.log('X or Y axis or table or connection changed, fetching new data'); | ||||
|       // Only fetch data if xAxis, yAxis, table, or connection has changed (and it's not the first change)
 | ||||
|     if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || | ||||
|       drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || | ||||
|       drilldownLayersChanged)) { | ||||
|       console.log('X or Y axis or table or connection or base filters or drilldown config changed, fetching new data'); | ||||
|       // Only fetch data if xAxis, yAxis, table, connection, baseFilters or drilldown config has changed (and it's not the first change)
 | ||||
|       this.fetchGridData(); | ||||
|     } | ||||
|   } | ||||
| @ -68,20 +115,87 @@ export class GridViewComponent implements OnInit, OnChanges { | ||||
|   // Dynamic headers for the grid
 | ||||
| 
 | ||||
|   fetchGridData(): void { | ||||
|     // Set flag to prevent recursive calls
 | ||||
|     this.isFetchingData = true; | ||||
| 
 | ||||
|     // If we're in drilldown mode, fetch the appropriate drilldown data
 | ||||
|     if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) { | ||||
|       this.fetchDrilldownData(); | ||||
|       // Reset flag after fetching
 | ||||
|       this.isFetchingData = false; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     // If we have the necessary data, fetch grid data from the service
 | ||||
|     if (this.table && this.xAxis) { | ||||
|       console.log('Fetching grid data for:', { table: this.table, xAxis: this.xAxis, yAxis: this.yAxis, connection: this.connection }); | ||||
|     // if (this.table && this.xAxis) {
 | ||||
|     if (this.table) { | ||||
| 
 | ||||
|       console.log('=== GRID VIEW DEBUG INFO ==='); | ||||
|       console.log('Table:', this.table); | ||||
|       console.log('X-Axis:', this.xAxis); | ||||
|       console.log('Y-Axis:', this.yAxis); | ||||
|       console.log('Connection:', this.connection); | ||||
| 
 | ||||
|       // Convert yAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; | ||||
| 
 | ||||
|       // Get the parameter value from the drilldown stack for base level (should be empty)
 | ||||
|       let parameterValue = ''; | ||||
| 
 | ||||
|       // Log the URL that will be called
 | ||||
|       let url = `chart/getdashjson/grid?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; | ||||
|       console.log('Grid data URL:', url); | ||||
| 
 | ||||
|       // Get filter parameters from base filters
 | ||||
|       const filterObj = {}; | ||||
| 
 | ||||
|       // Add base filters
 | ||||
|       if (this.baseFilters && this.baseFilters.length > 0) { | ||||
|         this.baseFilters.forEach(filter => { | ||||
|           if (filter.field && filter.value) { | ||||
|             filterObj[filter.field] = filter.value; | ||||
|           } | ||||
|         }); | ||||
|       } | ||||
| 
 | ||||
|       // Add common filters directly as key-value pairs
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
| 
 | ||||
|       // Add common filters using the field name as the key
 | ||||
|       Object.keys(commonFilters).forEach(filterId => { | ||||
|         const filterValue = commonFilters[filterId]; | ||||
| 
 | ||||
|         // Find the filter definition to get the field name
 | ||||
|         const filterDef = this.filterService.getFilters().find(f => f.id === filterId); | ||||
| 
 | ||||
|         if (filterDef && filterDef.field) { | ||||
|           const fieldName = filterDef.field; | ||||
|           if (filterValue !== undefined && filterValue !== null && filterValue !== '') { | ||||
|             filterObj[fieldName] = filterValue; | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
| 
 | ||||
|       // Convert to JSON string for API call
 | ||||
|       let filterParams = ''; | ||||
|       if (Object.keys(filterObj).length > 0) { | ||||
|         filterParams = JSON.stringify(filterObj); | ||||
|       } | ||||
| 
 | ||||
|       console.log('GridView: Final filter object to send to API:', filterObj); | ||||
| 
 | ||||
|       // Fetch data from the dashboard service, similar to other chart components
 | ||||
|       this.dashboardService.getChartData(this.table, 'grid', this.xAxis, yAxisString, this.connection).subscribe( | ||||
|       this.dashboardService.getChartData(this.table, 'grid', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( | ||||
|         (data: any) => { | ||||
|           console.log('=== GRID VIEW DATA RESPONSE ==='); | ||||
|           console.log('Received grid data:', data); | ||||
|           if (data === null) { | ||||
|             console.warn('Grid API returned null data. Check if the API endpoint is working correctly.'); | ||||
|             this.error = "No data Available"; | ||||
|             this.noDataAvailable = true; | ||||
|             // Reset flag after fetching
 | ||||
|             this.isFetchingData = false; | ||||
|             return; | ||||
|           } | ||||
| 
 | ||||
| @ -90,49 +204,387 @@ export class GridViewComponent implements OnInit, OnChanges { | ||||
|             this.givendata = data.chartData; | ||||
|             this.extractDynamicHeaders(data.chartData); | ||||
|             this.error = this.givendata.length === 0 ? "No data Available" : undefined; | ||||
|             this.noDataAvailable = this.givendata.length === 0; | ||||
|             console.log('Updated grid with data:', this.givendata); | ||||
|           } else if (data && data.data) { | ||||
|             // Handle the original expected format as fallback
 | ||||
|             this.givendata = data.data; | ||||
|             this.extractDynamicHeaders(data.data); | ||||
|             this.error = this.givendata.length === 0 ? "No data Available" : undefined; | ||||
|             this.noDataAvailable = this.givendata.length === 0; | ||||
|             console.log('Updated grid with legacy data format:', this.givendata); | ||||
|           } else if (Array.isArray(data)) { | ||||
|             // Handle case where data is directly an array
 | ||||
|             this.givendata = data; | ||||
|             this.extractDynamicHeaders(data); | ||||
|             this.error = this.givendata.length === 0 ? "No data Available" : undefined; | ||||
|             this.noDataAvailable = this.givendata.length === 0; | ||||
|             console.log('Updated grid with array data:', this.givendata); | ||||
|           } else { | ||||
|             console.warn('Grid received data does not have expected structure', data); | ||||
|             this.error = "No valid data received"; | ||||
|             this.givendata = []; | ||||
|             this.noDataAvailable = true; | ||||
|           } | ||||
|           // Reset flag after fetching
 | ||||
|           this.isFetchingData = false; | ||||
|         }, (error) => { | ||||
|           console.log('Error fetching grid data:', error); | ||||
|           this.error = "Server Error"; | ||||
|           this.noDataAvailable = true; | ||||
|           // Reset flag after fetching
 | ||||
|           this.isFetchingData = false; | ||||
|         }); | ||||
|     } else if (this.table) { | ||||
|       console.log('Missing xAxis, falling back to default data fetching'); | ||||
|       // Fallback to default data fetching when only table is provided
 | ||||
|        // Convert yAxis to string if it's an array
 | ||||
|       // Convert yAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; | ||||
| 
 | ||||
|       // Fetch data from the dashboard service, similar to other chart components
 | ||||
|       this.dashboardService.getChartData(this.table, 'grid', this.xAxis, yAxisString, this.connection).subscribe( | ||||
|         (data: any) => { | ||||
|       // this.mainservice.getAll().subscribe((data: any) => {
 | ||||
|         console.log('recv data ', data); | ||||
|         this.givendata = Array.isArray(data) ? data : []; | ||||
|         this.extractDynamicHeaders(data); | ||||
|         this.error = this.givendata && this.givendata.length === 0 ? "No data Available" : undefined; | ||||
|       }, (error) => { | ||||
|         console.log(error); | ||||
|         this.error = "Server Error"; | ||||
|       }); | ||||
|           // this.mainservice.getAll().subscribe((data: any) => {
 | ||||
|           console.log('recv data ', data); | ||||
|           this.givendata = Array.isArray(data) ? data : []; | ||||
|           this.extractDynamicHeaders(data); | ||||
|           this.error = this.givendata && this.givendata.length === 0 ? "No data Available" : undefined; | ||||
|           this.noDataAvailable = this.givendata && this.givendata.length === 0; | ||||
|           // Reset flag after fetching
 | ||||
|           this.isFetchingData = false; | ||||
|         }, (error) => { | ||||
|           console.log(error); | ||||
|           this.error = "Server Error"; | ||||
|           this.noDataAvailable = true; | ||||
|           // Reset flag after fetching
 | ||||
|           this.isFetchingData = false; | ||||
|         }); | ||||
|     } else { | ||||
|       console.log('Missing required data for grid:', { table: this.table }); | ||||
|       this.error = "Table name is required"; | ||||
|       this.noDataAvailable = true; | ||||
|       // Reset flag after fetching
 | ||||
|       this.isFetchingData = false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Fetch drilldown data based on current drilldown level
 | ||||
|   fetchDrilldownData(): void { | ||||
|     console.log('Fetching drilldown data, current level:', this.currentDrilldownLevel); | ||||
|     console.log('Drilldown stack:', this.drilldownStack); | ||||
| 
 | ||||
|     // Get the current drilldown configuration based on the current level
 | ||||
|     let drilldownConfig; | ||||
|     if (this.currentDrilldownLevel === 1) { | ||||
|       // Base drilldown level
 | ||||
|       drilldownConfig = { | ||||
|         apiUrl: this.drilldownApiUrl, | ||||
|         xAxis: this.drilldownXAxis, | ||||
|         yAxis: this.drilldownYAxis, | ||||
|         parameter: this.drilldownParameter | ||||
|       }; | ||||
|     } else { | ||||
|       // Multi-layer drilldown level
 | ||||
|       const layerIndex = this.currentDrilldownLevel - 2; // -2 because level 1 is base drilldown
 | ||||
|       if (layerIndex >= 0 && layerIndex < this.drilldownLayers.length) { | ||||
|         drilldownConfig = this.drilldownLayers[layerIndex]; | ||||
|       } else { | ||||
|         console.warn('Invalid drilldown layer index:', layerIndex); | ||||
|         this.error = "Invalid drilldown configuration"; | ||||
|         this.givendata = []; | ||||
|         this.noDataAvailable = true; | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     console.log('Drilldown config for level', this.currentDrilldownLevel, ':', drilldownConfig); | ||||
| 
 | ||||
|     // Check if we have valid drilldown configuration
 | ||||
|     if (!drilldownConfig || !drilldownConfig.apiUrl || !drilldownConfig.xAxis || !drilldownConfig.yAxis) { | ||||
|       console.warn('Missing drilldown configuration for level:', this.currentDrilldownLevel); | ||||
|       this.error = "Missing drilldown configuration"; | ||||
|       this.givendata = []; | ||||
|       this.noDataAvailable = true; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     // Get the parameter value from the drilldown stack
 | ||||
|     let parameterValue = ''; | ||||
|     if (this.drilldownStack.length > 0) { | ||||
|       const lastEntry = this.drilldownStack[this.drilldownStack.length - 1]; | ||||
|       parameterValue = lastEntry.clickedValue || ''; | ||||
|       console.log('Parameter value from last click:', parameterValue); | ||||
|     } | ||||
| 
 | ||||
|     // Get the parameter field from drilldown config
 | ||||
|     const parameterField = drilldownConfig.parameter || ''; | ||||
|     console.log('Parameter field:', parameterField); | ||||
| 
 | ||||
|     console.log('Fetching drilldown data for level:', this.currentDrilldownLevel, { | ||||
|       apiUrl: drilldownConfig.apiUrl, | ||||
|       xAxis: drilldownConfig.xAxis, | ||||
|       yAxis: drilldownConfig.yAxis, | ||||
|       parameterField: parameterField, | ||||
|       parameterValue: parameterValue, | ||||
|       connection: this.connection | ||||
|     }); | ||||
| 
 | ||||
|     // Build the actual API URL with parameter replacement
 | ||||
|     let actualApiUrl = drilldownConfig.apiUrl; | ||||
|     console.log('Original API URL:', actualApiUrl); | ||||
|     console.log('Parameter value to use:', parameterValue); | ||||
|     console.log('Parameter field:', parameterField); | ||||
| 
 | ||||
|     // Check if the URL contains angle brackets for parameter replacement
 | ||||
|     const hasAngleBrackets = /<[^>]+>/.test(actualApiUrl); | ||||
| 
 | ||||
|     if (hasAngleBrackets && parameterValue) { | ||||
|       // Replace angle brackets placeholder with actual value
 | ||||
|       console.log('Replacing angle brackets with parameter value'); | ||||
|       const encodedValue = encodeURIComponent(parameterValue); | ||||
|       actualApiUrl = actualApiUrl.replace(/<[^>]+>/g, encodedValue); | ||||
|       console.log('URL after angle bracket replacement:', actualApiUrl); | ||||
|     } | ||||
| 
 | ||||
|     // Log the URL that will be called
 | ||||
|     let url = `chart/getdashjson/grid?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; | ||||
|     if (parameterField && parameterValue) { | ||||
|       url += `¶meter=${encodeURIComponent(parameterField)}¶meterValue=${encodeURIComponent(parameterValue)}`; | ||||
|     } | ||||
|     console.log('Drilldown data URL:', url); | ||||
| 
 | ||||
|     // Convert drilldown layer filters to filter parameters (if applicable)
 | ||||
|     const filterObj = {}; | ||||
| 
 | ||||
|     // Add drilldown layer filters
 | ||||
|     if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { | ||||
|       drilldownConfig.filters.forEach((filter: any) => { | ||||
|         if (filter.field && filter.value) { | ||||
|           filterObj[filter.field] = filter.value; | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     // Add drilldownFilters
 | ||||
|     if (this.drilldownFilters && this.drilldownFilters.length > 0) { | ||||
|       this.drilldownFilters.forEach(filter => { | ||||
|         if (filter.field && filter.value) { | ||||
|           filterObj[filter.field] = filter.value; | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     // Add common filters
 | ||||
|     const commonFilters = this.filterService.getFilterValues(); | ||||
|     const filterDefinitions = this.filterService.getFilters(); | ||||
|     Object.keys(commonFilters).forEach(filterId => { | ||||
|       const filterValue = commonFilters[filterId]; | ||||
| 
 | ||||
|       // Find the filter definition to get the field name
 | ||||
|       const filterDef = this.filterService.getFilters().find(f => f.id === filterId); | ||||
| 
 | ||||
|       if (filterDef && filterDef.field) { | ||||
|         const fieldName = filterDef.field; | ||||
|         if (filterValue !== undefined && filterValue !== null && filterValue !== '') { | ||||
|           filterObj[fieldName] = filterValue; | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     // Convert to JSON string for API call
 | ||||
|     let drilldownFilterParams = ''; | ||||
|     if (Object.keys(filterObj).length > 0) { | ||||
|       drilldownFilterParams = JSON.stringify(filterObj); | ||||
|     } | ||||
| 
 | ||||
|     console.log('Drilldown filter parameters:', drilldownFilterParams); | ||||
| 
 | ||||
|     // For drilldown level, we pass the parameter value from the drilldown stack and drilldown filters
 | ||||
|     this.dashboardService.getChartData( | ||||
|       drilldownConfig.apiUrl, 'grid', | ||||
|       drilldownConfig.xAxis, drilldownConfig.yAxis, | ||||
|       this.connection, | ||||
|       parameterField, parameterValue, | ||||
|       drilldownFilterParams | ||||
|     ).subscribe( | ||||
|       (data: any) => { | ||||
|         console.log('Received drilldown data:', data); | ||||
|         if (data === null) { | ||||
|           console.warn('Drilldown API returned null data. Check if the API endpoint is working correctly.'); | ||||
|           this.error = "No data Available"; | ||||
|           this.givendata = []; | ||||
|           this.noDataAvailable = true; | ||||
|           return; | ||||
|         } | ||||
| 
 | ||||
|         // Handle the actual data structure returned by the API
 | ||||
|         if (data && data.chartData) { | ||||
|           this.givendata = data.chartData; | ||||
|           this.extractDynamicHeaders(data.chartData); | ||||
|           this.error = this.givendata.length === 0 ? "No data Available" : undefined; | ||||
|           this.noDataAvailable = this.givendata.length === 0; | ||||
|           console.log('Updated grid with drilldown data:', this.givendata); | ||||
|         } else if (data && data.data) { | ||||
|           // Handle the original expected format as fallback
 | ||||
|           this.givendata = data.data; | ||||
|           this.extractDynamicHeaders(data.data); | ||||
|           this.error = this.givendata.length === 0 ? "No data Available" : undefined; | ||||
|           this.noDataAvailable = this.givendata.length === 0; | ||||
|           console.log('Updated grid with drilldown legacy data format:', this.givendata); | ||||
|         } else if (Array.isArray(data)) { | ||||
|           // Handle case where data is directly an array
 | ||||
|           this.givendata = data; | ||||
|           this.extractDynamicHeaders(data); | ||||
|           this.error = this.givendata.length === 0 ? "No data Available" : undefined; | ||||
|           this.noDataAvailable = this.givendata.length === 0; | ||||
|           console.log('Updated grid with drilldown array data:', this.givendata); | ||||
|         } else { | ||||
|           console.warn('Drilldown received data does not have expected structure', data); | ||||
|           this.error = "No valid data received"; | ||||
|           this.givendata = []; | ||||
|           this.noDataAvailable = true; | ||||
|         } | ||||
|       }, | ||||
|       (error) => { | ||||
|         console.error('Error fetching drilldown data:', error); | ||||
|         this.error = "Server Error"; | ||||
|         this.givendata = []; | ||||
|         this.noDataAvailable = true; | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   // Reset to original data (go back to base level)
 | ||||
|   resetToOriginalData(): void { | ||||
|     console.log('Resetting to original data'); | ||||
|     console.log('Current stack before reset:', this.drilldownStack); | ||||
|     console.log('Current level before reset:', this.currentDrilldownLevel); | ||||
| 
 | ||||
|     this.currentDrilldownLevel = 0; | ||||
|     this.drilldownStack = []; | ||||
| 
 | ||||
|     if (this.originalGridData.length > 0) { | ||||
|       // Create a deep copy to avoid reference issues
 | ||||
|       this.givendata = JSON.parse(JSON.stringify(this.originalGridData)); | ||||
|       this.extractDynamicHeaders(this.givendata); | ||||
|       console.log('Restored original data'); | ||||
|     } | ||||
| 
 | ||||
|     console.log('After reset - data:', this.givendata); | ||||
| 
 | ||||
|     // Re-fetch original data
 | ||||
|     this.fetchGridData(); | ||||
|   } | ||||
| 
 | ||||
|   // Navigate back to previous drilldown level
 | ||||
|   navigateBack(): void { | ||||
|     console.log('Navigating back, current stack:', this.drilldownStack); | ||||
|     console.log('Current level:', this.currentDrilldownLevel); | ||||
| 
 | ||||
|     if (this.drilldownStack.length > 0) { | ||||
|       // Remove the last entry from the stack
 | ||||
|       const removedEntry = this.drilldownStack.pop(); | ||||
|       console.log('Removed entry from stack:', removedEntry); | ||||
| 
 | ||||
|       // Update the current drilldown level
 | ||||
|       this.currentDrilldownLevel = this.drilldownStack.length; | ||||
|       console.log('New level after pop:', this.currentDrilldownLevel); | ||||
|       console.log('Stack after pop:', this.drilldownStack); | ||||
| 
 | ||||
|       if (this.drilldownStack.length > 0) { | ||||
|         // Fetch data for the previous level
 | ||||
|         console.log('Fetching data for previous level'); | ||||
|         this.fetchDrilldownData(); | ||||
|       } else { | ||||
|         // Back to base level
 | ||||
|         console.log('Back to base level, resetting to original data'); | ||||
|         this.resetToOriginalData(); | ||||
|       } | ||||
|     } else { | ||||
|       // Already at base level, reset to original data
 | ||||
|       console.log('Already at base level, resetting to original data'); | ||||
|       this.resetToOriginalData(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Method to handle grid row clicks for drilldown
 | ||||
|   onRowClick(item: any, key: string): void { | ||||
|     console.log('Grid row clicked:', { item, key }); | ||||
| 
 | ||||
|     // If drilldown is enabled
 | ||||
|     if (this.drilldownEnabled) { | ||||
|       // Get the value for the clicked key
 | ||||
|       const clickedValue = item[key]; | ||||
| 
 | ||||
|       console.log('Clicked on row value:', { key, value: clickedValue }); | ||||
| 
 | ||||
|       // If we're not at the base level, store original data
 | ||||
|       if (this.currentDrilldownLevel === 0) { | ||||
|         // Store original data before entering drilldown mode
 | ||||
|         // Create a deep copy to avoid reference issues
 | ||||
|         this.originalGridData = JSON.parse(JSON.stringify(this.givendata)); | ||||
|         console.log('Stored original data for drilldown'); | ||||
|       } | ||||
| 
 | ||||
|       // Determine the next drilldown level
 | ||||
|       const nextDrilldownLevel = this.currentDrilldownLevel + 1; | ||||
| 
 | ||||
|       console.log('Next drilldown level will be:', nextDrilldownLevel); | ||||
| 
 | ||||
|       // Check if there's a drilldown configuration for this level
 | ||||
|       let hasDrilldownConfig = false; | ||||
|       let drilldownConfig; | ||||
| 
 | ||||
|       if (nextDrilldownLevel === 1) { | ||||
|         // Base drilldown level
 | ||||
|         drilldownConfig = { | ||||
|           apiUrl: this.drilldownApiUrl, | ||||
|           xAxis: this.drilldownXAxis, | ||||
|           yAxis: this.drilldownYAxis, | ||||
|           parameter: this.drilldownParameter | ||||
|         }; | ||||
|         hasDrilldownConfig = !!this.drilldownApiUrl && !!this.drilldownXAxis && !!this.drilldownYAxis; | ||||
|       } else { | ||||
|         // Multi-layer drilldown level
 | ||||
|         const layerIndex = nextDrilldownLevel - 2; // -2 because level 1 is base drilldown
 | ||||
|         if (layerIndex < this.drilldownLayers.length) { | ||||
|           drilldownConfig = this.drilldownLayers[layerIndex]; | ||||
|           hasDrilldownConfig = drilldownConfig.enabled && | ||||
|             !!drilldownConfig.apiUrl && | ||||
|             !!drilldownConfig.xAxis && | ||||
|             !!drilldownConfig.yAxis; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       console.log('Drilldown config for next level:', drilldownConfig); | ||||
|       console.log('Has drilldown config:', hasDrilldownConfig); | ||||
| 
 | ||||
|       // If there's a drilldown configuration for the next level, proceed
 | ||||
|       if (hasDrilldownConfig) { | ||||
|         // Add this click to the drilldown stack
 | ||||
|         const stackEntry = { | ||||
|           level: nextDrilldownLevel, | ||||
|           clickedKey: key, | ||||
|           clickedValue: clickedValue | ||||
|         }; | ||||
| 
 | ||||
|         this.drilldownStack.push(stackEntry); | ||||
| 
 | ||||
|         console.log('Added to drilldown stack:', stackEntry); | ||||
|         console.log('Current drilldown stack:', this.drilldownStack); | ||||
| 
 | ||||
|         // Update the current drilldown level
 | ||||
|         this.currentDrilldownLevel = nextDrilldownLevel; | ||||
| 
 | ||||
|         console.log('Entering drilldown level:', this.currentDrilldownLevel); | ||||
| 
 | ||||
|         // Fetch drilldown data for the new level
 | ||||
|         this.fetchDrilldownData(); | ||||
|       } else { | ||||
|         console.log('No drilldown configuration for level:', nextDrilldownLevel); | ||||
|       } | ||||
|     } else { | ||||
|       console.log('Drilldown not enabled'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -175,4 +627,23 @@ export class GridViewComponent implements OnInit, OnChanges { | ||||
|       .replace(/([A-Z])/g, ' $1') | ||||
|       .replace(/^./, str => str.toUpperCase()); | ||||
|   } | ||||
| 
 | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('GridViewComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
| 
 | ||||
|     // Clear data to help with garbage collection
 | ||||
|     this.givendata = []; | ||||
|     this.dynamicHeaders = []; | ||||
|     this.drilldownStack = []; | ||||
|     this.originalGridData = []; | ||||
| 
 | ||||
|     console.log('GridViewComponent destroyed and cleaned up'); | ||||
|   } | ||||
| } | ||||
| @ -11,6 +11,9 @@ | ||||
|           <h3>{{ 'all_dashboard' | translate }}</h3> | ||||
|         </div> | ||||
|         <div class="clr-col-4" style="text-align: right;"> | ||||
|           <button class="btn btn-success" [routerLink]="['/cns-portal/shield-dashboard']"> | ||||
|             <clr-icon shape="shield"></clr-icon>Shield Dashboard | ||||
|           </button> | ||||
|           <button id="add" class="btn btn-primary"  (click)="gotoadd()"> | ||||
|             <clr-icon shape="plus"></clr-icon>{{ 'dashboard_builder' | translate }} | ||||
|           </button> | ||||
| @ -112,6 +115,3 @@ | ||||
|         </div> | ||||
|       </div> | ||||
|     </clr-modal> | ||||
|     | ||||
|    | ||||
|    | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user