builder
This commit is contained in:
		
							parent
							
								
									cdcf1e07c7
								
							
						
					
					
						commit
						cdd752469c
					
				| @ -70,6 +70,41 @@ | ||||
|     min-height: 24px; | ||||
|   } | ||||
|    | ||||
|   .multiselect-container { | ||||
|     max-height: 150px; | ||||
|     overflow-y: auto; | ||||
|     border: 1px solid #ddd; | ||||
|     border-radius: 4px; | ||||
|     padding: 5px; | ||||
|     background: white; | ||||
|   } | ||||
|    | ||||
|   .checkbox-group { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     gap: 3px; | ||||
|   } | ||||
|    | ||||
|   .checkbox-item { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     gap: 5px; | ||||
|   } | ||||
|    | ||||
|   .checkbox-label { | ||||
|     font-size: 12px; | ||||
|     margin: 0; | ||||
|     cursor: pointer; | ||||
|     white-space: nowrap; | ||||
|     overflow: hidden; | ||||
|     text-overflow: ellipsis; | ||||
|   } | ||||
|    | ||||
|   .clr-checkbox { | ||||
|     margin: 0; | ||||
|     cursor: pointer; | ||||
|   } | ||||
|    | ||||
|   .toggle-label { | ||||
|     margin: 0; | ||||
|     font-size: 12px; | ||||
|  | ||||
| @ -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
 | ||||
|       // 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
 | ||||
|  | ||||
| @ -550,6 +550,95 @@ | ||||
|           <input id="chartparameter" type="text" formControlName="chartparameter" class="clr-input" [(ngModel)]="gadgetsEditdata.chartparameter"> | ||||
|         </div> | ||||
|       </div> --> | ||||
|        | ||||
|       <!-- Compact Filter Configuration (shown only for Compact Filter components) --> | ||||
|       <div class="clr-row" *ngIf="gadgetsEditdata?.fieldName === 'Compact Filter'" style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #ddd;"> | ||||
|         <div class="clr-col-sm-12"> | ||||
|           <h4>Compact Filter Configuration</h4> | ||||
|            | ||||
|           <div class="clr-row"> | ||||
|             <div class="clr-col-sm-12"> | ||||
|               <label for="compactFilterConnection">Connection</label> | ||||
|               <select id="compactFilterConnection" class="clr-select" [(ngModel)]="gadgetsEditdata.connection"  | ||||
|                       (ngModelChange)="onCompactFilterConnectionChange($event)" [ngModelOptions]="{standalone: true}"> | ||||
|                 <option value="">Select Connection</option> | ||||
|                 <option *ngFor="let conn of sureconnectData" [value]="conn.id"> | ||||
|                   {{conn.connection_name || conn.id}} | ||||
|                 </option> | ||||
|               </select> | ||||
|               <div class="clr-subtext">Select a connection for this compact filter</div> | ||||
|             </div> | ||||
|           </div> | ||||
|            | ||||
|           <div class="clr-row" style="margin-top: 10px;"> | ||||
|             <div class="clr-col-sm-12"> | ||||
|               <label for="compactFilterApiUrl">API URL</label> | ||||
|               <div> | ||||
|                 <input type="text" id="compactFilterApiUrl" class="clr-input" [(ngModel)]="gadgetsEditdata.table"  | ||||
|                        (ngModelChange)="onCompactFilterApiUrlChange($event)" [ngModelOptions]="{standalone: true}"  | ||||
|                        placeholder="Enter API URL"> | ||||
|                 <span> | ||||
|                   <button class="btn btn-icon btn-primary" style="margin: 0px;"  | ||||
|                           (click)="loadAvailableKeys(gadgetsEditdata.table, gadgetsEditdata.connection)"  | ||||
|                           [disabled]="!gadgetsEditdata.table"> | ||||
|                     <clr-icon shape="redo"></clr-icon> | ||||
|                   </button> | ||||
|                 </span> | ||||
|               </div> | ||||
|               <div class="clr-subtext">Enter the API URL to fetch data for this filter</div> | ||||
|             </div> | ||||
|           </div> | ||||
|            | ||||
|           <div class="clr-row" style="margin-top: 10px;"> | ||||
|             <div class="clr-col-sm-12"> | ||||
|               <label for="filterKey">Filter Key</label> | ||||
|               <select id="filterKey" class="clr-select" [(ngModel)]="gadgetsEditdata.filterKey"  | ||||
|                       (ngModelChange)="onFilterKeyChange($event)" [ngModelOptions]="{standalone: true}"> | ||||
|                 <option value="">Select Filter Key</option> | ||||
|                 <option *ngFor="let key of availableKeys" [value]="key">{{ key }}</option> | ||||
|               </select> | ||||
|               <div class="clr-subtext">Select the field name to filter on</div> | ||||
|             </div> | ||||
|           </div> | ||||
|            | ||||
|           <div class="clr-row" style="margin-top: 10px;"> | ||||
|             <div class="clr-col-sm-12"> | ||||
|               <label for="filterType">Filter Type</label> | ||||
|               <select id="filterType" class="clr-select" [(ngModel)]="gadgetsEditdata.filterType"  | ||||
|                       (ngModelChange)="onFilterTypeChange($event)" [ngModelOptions]="{standalone: true}"> | ||||
|                 <option value="text">Text</option> | ||||
|                 <option value="dropdown">Dropdown</option> | ||||
|                 <option value="multiselect">Multi-Select</option> | ||||
|                 <option value="date-range">Date Range</option> | ||||
|                 <option value="toggle">Toggle</option> | ||||
|               </select> | ||||
|               <div class="clr-subtext">Select the type of filter control to display</div> | ||||
|             </div> | ||||
|           </div> | ||||
|            | ||||
|           <div class="clr-row" style="margin-top: 10px;"> | ||||
|             <div class="clr-col-sm-12"> | ||||
|               <label for="filterLabel">Filter Label (Optional)</label> | ||||
|               <input type="text" id="filterLabel" class="clr-input" [(ngModel)]="gadgetsEditdata.filterLabel"  | ||||
|                      [ngModelOptions]="{standalone: true}" placeholder="Enter filter label"> | ||||
|               <div class="clr-subtext">Label to display for this filter in the UI (if not provided, filter key will be used)</div> | ||||
|             </div> | ||||
|           </div> | ||||
|            | ||||
|           <div class="clr-row" style="margin-top: 10px;" *ngIf="gadgetsEditdata.filterType === 'dropdown' || gadgetsEditdata.filterType === 'multiselect'"> | ||||
|             <div class="clr-col-sm-12"> | ||||
|               <label for="filterOptions">Filter Options (comma separated)</label> | ||||
|               <input type="text" id="filterOptions" class="clr-input" [(ngModel)]="filterOptionsString"  | ||||
|                      [ngModelOptions]="{standalone: true}" placeholder="Option1,Option2,Option3"> | ||||
|               <div class="clr-subtext">Comma-separated list of options for dropdown/multiselect filters</div> | ||||
|               <div class="clr-subtext" *ngIf="gadgetsEditdata.filterKey"> | ||||
|                 <strong>Available values for "{{ gadgetsEditdata.filterKey }}":</strong> {{ filterOptionsString }} | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="modal-footer"> | ||||
|         <button type="button" class="btn btn-outline" (click)="modeledit = false">Cancel</button> | ||||
|         <button type="button" class="btn btn-primary" (click)="applyChanges(modelid)">Apply</button> | ||||
|  | ||||
| @ -48,6 +48,12 @@ export class EditnewdashComponent implements OnInit { | ||||
|   public entryForm: FormGroup; | ||||
|   public commonFilterForm: FormGroup; // Add common filter form
 | ||||
|    | ||||
|   // Add filterOptionsString property for compact filter
 | ||||
|   filterOptionsString: string = ''; | ||||
|    | ||||
|   // Add availableKeys property for compact filter
 | ||||
|   availableKeys: string[] = []; | ||||
| 
 | ||||
|   WidgetsMock: WidgetModel[] = [ | ||||
|     { | ||||
|       name: 'Common Filter', | ||||
| @ -360,6 +366,16 @@ export class EditnewdashComponent implements OnInit { | ||||
|           dashboard.component = component.componentInstance; | ||||
|         } | ||||
|       }); | ||||
|        | ||||
|       // Ensure compact filter configuration properties are properly initialized
 | ||||
|       if (dashboard.component === 'Compact Filter' || dashboard.name === 'Compact Filter') { | ||||
|         // Make sure all compact filter properties exist
 | ||||
|         if (dashboard.filterKey === undefined) dashboard.filterKey = ''; | ||||
|         if (dashboard.filterType === undefined) dashboard.filterType = 'text'; | ||||
|         if (dashboard.filterLabel === undefined) dashboard.filterLabel = ''; | ||||
|         if (dashboard.filterOptions === undefined) dashboard.filterOptions = []; | ||||
|         // table and connection properties should already exist for all components
 | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
| @ -374,6 +390,16 @@ export class EditnewdashComponent implements OnInit { | ||||
|           dashboard.component = component.name; | ||||
|         } | ||||
|       }); | ||||
|        | ||||
|       // Ensure compact filter configuration properties are preserved
 | ||||
|       if (dashboard.name === 'Compact Filter') { | ||||
|         // Make sure all compact filter properties exist
 | ||||
|         if (dashboard.filterKey === undefined) dashboard.filterKey = ''; | ||||
|         if (dashboard.filterType === undefined) dashboard.filterType = 'text'; | ||||
|         if (dashboard.filterLabel === undefined) dashboard.filterLabel = ''; | ||||
|         if (dashboard.filterOptions === undefined) dashboard.filterOptions = []; | ||||
|         // table and connection properties should already exist for all components
 | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|   // Add method to get available fields for a filter dropdown (excluding already selected fields)
 | ||||
| @ -601,6 +627,18 @@ export class EditnewdashComponent implements OnInit { | ||||
|     if (item['filterOptions'] === undefined) {  | ||||
|       this.gadgetsEditdata['filterOptions'] = [];  | ||||
|     } | ||||
|      | ||||
|     // Initialize filterOptionsString for compact filter
 | ||||
|     if (item.name === 'Compact Filter') { | ||||
|       this.filterOptionsString = this.gadgetsEditdata['filterOptions'].join(', '); | ||||
|       // Load available keys when editing a compact filter
 | ||||
|       if (this.gadgetsEditdata['table']) { | ||||
|         this.loadAvailableKeys(this.gadgetsEditdata['table'], this.gadgetsEditdata['connection']); | ||||
|       } | ||||
|     } else { | ||||
|       this.filterOptionsString = ''; | ||||
|     } | ||||
|      | ||||
|     this.getStores(); | ||||
|      | ||||
|     // Set default connection if none is set and we have connections
 | ||||
| @ -699,6 +737,9 @@ export class EditnewdashComponent implements OnInit { | ||||
| 
 | ||||
|     //https://www.w3schools.com/js/tryit.asp?filename=tryjson_stringify_function_tostring
 | ||||
| 
 | ||||
|     // First serialize the dashboard collection to ensure component names are properly set
 | ||||
|     this.serialize(this.dashboardCollection.dashboard); | ||||
| 
 | ||||
|     let cmp = this.dashboardCollection.dashboard.forEach(dashboard => { | ||||
|       this.componentCollection.forEach(component => { | ||||
|         if (dashboard.name === component.name) { | ||||
| @ -719,8 +760,6 @@ export class EditnewdashComponent implements OnInit { | ||||
|     //console.log(merged);
 | ||||
|     console.log("temp data", typeof tmp); | ||||
|     console.log(tmp); | ||||
|     let parsed = JSON.parse(tmp); | ||||
|     this.serialize(parsed.dashboard); | ||||
|     this.dashbord1_Line.model = tmp; | ||||
| 
 | ||||
|     // let obj = this.dashboardCollection;
 | ||||
| @ -777,12 +816,19 @@ export class EditnewdashComponent implements OnInit { | ||||
|         xyz.commonFilterEnabled = this.gadgetsEditdata.commonFilterEnabled; // Add common filter property
 | ||||
|          | ||||
|         // For compact filter, preserve filter configuration properties
 | ||||
|         if (item.component && item.component.name === 'CompactFilterComponent') { | ||||
|         if (item.name === 'Compact Filter') { | ||||
|           xyz.filterKey = this.gadgetsEditdata.filterKey || ''; | ||||
|           xyz.filterType = this.gadgetsEditdata.filterType || 'text'; | ||||
|           xyz.filterLabel = this.gadgetsEditdata.filterLabel || ''; | ||||
|           // Convert filterOptionsString to array
 | ||||
|           if (this.gadgetsEditdata.fieldName === 'Compact Filter') { | ||||
|             xyz.filterOptions = this.filterOptionsString.split(',').map(opt => opt.trim()).filter(opt => opt); | ||||
|           } else { | ||||
|             xyz.filterOptions = this.gadgetsEditdata.filterOptions || []; | ||||
|           } | ||||
|           xyz.table = this.gadgetsEditdata.table || ''; | ||||
|           xyz.connection = this.gadgetsEditdata.connection || undefined; | ||||
|         } | ||||
|          | ||||
|         console.log(xyz); | ||||
|         return xyz; | ||||
| @ -818,8 +864,58 @@ 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
 | ||||
|     const chartInputs = { | ||||
|     // For CompactFilterComponent, pass only filter configuration properties
 | ||||
|     if (item.name === 'Compact Filter') { | ||||
|       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, | ||||
| @ -848,24 +944,45 @@ 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; | ||||
|       // Remove undefined properties to avoid passing unnecessary data
 | ||||
|       Object.keys(gridInputs).forEach(key => { | ||||
|         if (gridInputs[key] === undefined) { | ||||
|           delete gridInputs[key]; | ||||
|         } | ||||
|       }); | ||||
|        | ||||
|       return gridInputs; | ||||
|     } | ||||
|      | ||||
|     // 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; | ||||
|     } | ||||
|     // For all other chart components, pass chart-specific properties
 | ||||
|     const chartInputs = { | ||||
|       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(chartInputs).forEach(key => { | ||||
| @ -922,13 +1039,24 @@ export class EditnewdashComponent implements OnInit { | ||||
|         updatedItem.commonFilterEnabledDrilldown = this.gadgetsEditdata.commonFilterEnabledDrilldown; // Add drilldown common filter property
 | ||||
|          | ||||
|         // For compact filter, preserve filter configuration properties
 | ||||
|         if (item.component && item.component.name === 'CompactFilterComponent') { | ||||
|         if (item.name === 'Compact Filter') { | ||||
|           updatedItem.filterKey = this.gadgetsEditdata.filterKey || ''; | ||||
|           updatedItem.filterType = this.gadgetsEditdata.filterType || 'text'; | ||||
|           updatedItem.filterLabel = this.gadgetsEditdata.filterLabel || ''; | ||||
|           // Convert filterOptionsString to array
 | ||||
|           if (this.gadgetsEditdata.fieldName === 'Compact Filter') { | ||||
|             updatedItem.filterOptions = this.filterOptionsString.split(',').map(opt => opt.trim()).filter(opt => opt); | ||||
|           } else { | ||||
|             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); | ||||
| @ -1356,4 +1484,80 @@ export class EditnewdashComponent implements OnInit { | ||||
|       // This would require the chart component to have a public resize method
 | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   // Add method to load available keys for compact filter
 | ||||
|   loadAvailableKeys(apiUrl: string, connectionId: string | undefined) { | ||||
|     if (apiUrl) { | ||||
|       const connectionIdNum = connectionId ? parseInt(connectionId, 10) : undefined; | ||||
|       this.alertService.getColumnfromurl(apiUrl, connectionIdNum).subscribe( | ||||
|         (keys: string[]) => { | ||||
|           this.availableKeys = keys; | ||||
|         }, | ||||
|         (error) => { | ||||
|           console.error('Error loading available keys:', error); | ||||
|           this.availableKeys = []; | ||||
|         } | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Add method to load available values for a specific key
 | ||||
|   loadAvailableValues(key: string) { | ||||
|     if (key && this.gadgetsEditdata['table']) { | ||||
|       const connectionIdNum = this.gadgetsEditdata['connection'] ?  | ||||
|         parseInt(this.gadgetsEditdata['connection'], 10) : undefined; | ||||
|       this.alertService.getValuesFromUrl(this.gadgetsEditdata['table'], connectionIdNum, key).subscribe( | ||||
|         (values: string[]) => { | ||||
|           // Update filter options string for dropdown/multiselect
 | ||||
|           if (this.gadgetsEditdata['filterType'] === 'dropdown' ||  | ||||
|               this.gadgetsEditdata['filterType'] === 'multiselect') { | ||||
|             this.filterOptionsString = values.join(', '); | ||||
|             // Also update the gadgetsEditdata filterOptions array
 | ||||
|             this.gadgetsEditdata['filterOptions'] = values; | ||||
|           } | ||||
|         }, | ||||
|         (error) => { | ||||
|           console.error('Error loading available values:', error); | ||||
|         } | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Add method to handle filter key change
 | ||||
|   onFilterKeyChange(key: string) { | ||||
|     this.gadgetsEditdata['filterKey'] = key; | ||||
|     // Load available values when filter key changes
 | ||||
|     if (key && (this.gadgetsEditdata['filterType'] === 'dropdown' ||  | ||||
|                 this.gadgetsEditdata['filterType'] === 'multiselect')) { | ||||
|       this.loadAvailableValues(key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Add method to handle filter type change
 | ||||
|   onFilterTypeChange(type: string) { | ||||
|     this.gadgetsEditdata['filterType'] = type; | ||||
|     // Load available values when filter type changes to dropdown or multiselect
 | ||||
|     if ((type === 'dropdown' || type === 'multiselect') && this.gadgetsEditdata['filterKey']) { | ||||
|       this.loadAvailableValues(this.gadgetsEditdata['filterKey']); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Add method to handle API URL change for compact filter
 | ||||
|   onCompactFilterApiUrlChange(url: string) { | ||||
|     this.gadgetsEditdata['table'] = url; | ||||
|     // Load available keys when API URL changes
 | ||||
|     if (url) { | ||||
|       this.loadAvailableKeys(url, this.gadgetsEditdata['connection']); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Add method to handle connection change for compact filter
 | ||||
|   onCompactFilterConnectionChange(connectionId: string) { | ||||
|     this.gadgetsEditdata['connection'] = connectionId; | ||||
|     // Reload available keys when connection changes
 | ||||
|     if (this.gadgetsEditdata['table']) { | ||||
|       this.loadAvailableKeys(this.gadgetsEditdata['table'], connectionId); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							| @ -1,38 +1,26 @@ | ||||
| <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> | ||||
|   </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 *ngIf="noDataAvailable" style="text-align: center; padding: 20px; color: #666; font-style: italic;"> | ||||
|     No data available | ||||
|   </div> | ||||
|    | ||||
|   <!-- Chart display --> | ||||
|   <div *ngIf="!noDataAvailable" style="position: relative; height: calc(100% - 50px);"> | ||||
|     <canvas baseChart | ||||
|         *ngIf="!noDataAvailable" | ||||
|       [datasets]="barChartData" | ||||
|       [labels]="barChartLabels" | ||||
|       [type]="barChartType" | ||||
| @ -40,11 +28,5 @@ | ||||
|       (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> | ||||
|   </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; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @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 = ''; | ||||
|       if (this.baseFilters && this.baseFilters.length > 0) { | ||||
|       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; | ||||
|           } | ||||
|         }); | ||||
|         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); | ||||
|        | ||||
|       if (Object.keys(commonFilters).length > 0) { | ||||
|         // Merge common filters with base filters
 | ||||
|         const mergedFilterObj = {}; | ||||
|          | ||||
|         // 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); | ||||
|           } | ||||
|         } | ||||
|          | ||||
|         // 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); | ||||
|             filterObj[fieldName] = 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 = ''; | ||||
|     if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { | ||||
|     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; | ||||
|         } | ||||
|       }); | ||||
|       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
 | ||||
|     const commonFilters = this.filterService.getFilterValues(); | ||||
|     if (Object.keys(commonFilters).length > 0) { | ||||
|       // Merge common filters with drilldown filters
 | ||||
|       const mergedFilterObj = {}; | ||||
|        | ||||
|       // 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); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // Add common filters
 | ||||
|       Object.keys(commonFilters).forEach(key => { | ||||
|         const value = commonFilters[key]; | ||||
|         if (value !== undefined && value !== null && value !== '') { | ||||
|           mergedFilterObj[key] = value; | ||||
|     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; | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|      | ||||
|       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,27 +204,36 @@ 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'); | ||||
| @ -126,13 +249,342 @@ export class GridViewComponent implements OnInit, OnChanges { | ||||
|           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> | ||||
|     | ||||
|    | ||||
|    | ||||
| @ -3,6 +3,10 @@ import { DashrunnerService } from '../dashrunner.service'; | ||||
| import { ActivatedRoute, Router } from '@angular/router'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-bar-runner', | ||||
| @ -24,8 +28,19 @@ export class BarRunnerComponent implements OnInit { | ||||
|   JsonData; | ||||
| 
 | ||||
|   barData; | ||||
|   constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router,) { } | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
| 
 | ||||
|   constructor( | ||||
|     private Dashtestservive:DashrunnerService, | ||||
|     private route: ActivatedRoute, | ||||
|     private dashboardService: Dashboard3Service, | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService | ||||
|   ) { } | ||||
| 
 | ||||
|   barChartLabels: any[] = []; | ||||
|   barChartType: string = 'bar'; | ||||
| @ -47,6 +62,13 @@ export class BarRunnerComponent implements OnInit { | ||||
|       this.editId = this.route.snapshot.params.id; | ||||
|       console.log(this.editId); | ||||
| 
 | ||||
|       // Subscribe to filter changes
 | ||||
|       this.subscriptions.push( | ||||
|         this.filterService.filterState$.subscribe(filters => { | ||||
|           // When filters change, refresh the chart data
 | ||||
|           this.fetchChartData(); | ||||
|         }) | ||||
|       ); | ||||
| 
 | ||||
|       this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|         console.log(data); | ||||
| @ -74,22 +96,62 @@ export class BarRunnerComponent implements OnInit { | ||||
|             this.YAxis = ChartObject[i].yAxis; | ||||
|             this.showlabel = ChartObject[i].showlabel; | ||||
|             this.barChartLegend = ChartObject[i].chartlegend; | ||||
|             this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|             console.log(this.TableName); | ||||
|             this.Dashtestservive.getChartData(this.TableName,"Bar Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|               console.log(Ldata); | ||||
|               this.JsonData = Ldata; | ||||
|               this.barChartData = this.JsonData.barChartData; | ||||
|               this.barChartLabels = this.JsonData.barChartLabels; | ||||
|                | ||||
|              },(error) => { | ||||
|               console.log(error); | ||||
|              }); | ||||
|             // Fetch data with filters
 | ||||
|             this.fetchChartData(); | ||||
|             break; // No need to continue the loop once the correct placeholder is found
 | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|   } | ||||
|     | ||||
|   // Fetch chart data with filter support
 | ||||
|   fetchChartData(): void { | ||||
|     if (this.TableName && this.XAxis && this.YAxis) { | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
|        | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|        | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('BarRunner: Final filter object to send to API:', filterObj); | ||||
|        | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "Bar Chart", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log(Ldata); | ||||
|         this.JsonData = Ldata; | ||||
|         this.barChartData = this.JsonData.barChartData; | ||||
|         this.barChartLabels = this.JsonData.barChartLabels; | ||||
|       },(error) => { | ||||
|         console.log(error); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   generatePDFFile(){ | ||||
|     this.buttonClicked.emit(); | ||||
|     const content = this.contentContainerRef.nativeElement; | ||||
| @ -98,4 +160,16 @@ export class BarRunnerComponent implements OnInit { | ||||
|     this.Dashtestservive.generatePDF(content, filename); | ||||
|   } | ||||
|    | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('BarRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
|      | ||||
|     console.log('BarRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| } | ||||
| @ -5,6 +5,10 @@ import { ChartConfiguration,  ChartDataset,  ChartOptions } from 'chart.js'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| import { DashrunnerService } from '../dashrunner.service'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-bubble-runner', | ||||
| @ -25,9 +29,15 @@ export class BubbleRunnerComponent implements OnInit { | ||||
|   JsonData; | ||||
|   lineChartNoLabels: [] = []; | ||||
|   ChartLegend = false; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
|    | ||||
|   constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router,) { } | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService) { } | ||||
|      | ||||
|   public bubbleChartOptions: ChartConfiguration['options'] = { | ||||
|     // scales: {
 | ||||
| @ -87,6 +97,13 @@ export class BubbleRunnerComponent implements OnInit { | ||||
|     this.editId = this.route.snapshot.params.id; | ||||
|     console.log(this.editId); | ||||
| 
 | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         // When filters change, refresh the chart data
 | ||||
|         this.fetchChartData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|       console.log(data); | ||||
| @ -112,22 +129,62 @@ export class BubbleRunnerComponent implements OnInit { | ||||
|           this.YAxis = ChartObject[i].yAxis; | ||||
|           this.showlabel = ChartObject[i].showlabel; | ||||
|           this.ChartLegend = ChartObject[i].chartlegend; | ||||
|           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|           console.log(this.TableName); | ||||
|           this.Dashtestservive.getChartData(this.TableName,"Bubble Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|             console.log(Ldata); | ||||
|             this.JsonData = Ldata; | ||||
|             this.bubbleChartData = this.JsonData.bubbleChartData; | ||||
|             // this.radarChartLabels = this.JsonData.radarChartLabels;
 | ||||
|              | ||||
|            },(error) => { | ||||
|             console.log(error); | ||||
|            }); | ||||
|           // Fetch data with filters
 | ||||
|           this.fetchChartData(); | ||||
|           break; // No need to continue the loop once the correct placeholder is found
 | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   // Fetch chart data with filter support
 | ||||
|   fetchChartData(): void { | ||||
|     if (this.TableName && this.XAxis && this.YAxis) { | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
|        | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|        | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('BubbleRunner: Final filter object to send to API:', filterObj); | ||||
|        | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "Bubble Chart", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log(Ldata); | ||||
|         this.JsonData = Ldata; | ||||
|         this.bubbleChartData = this.JsonData.bubbleChartData; | ||||
|         // this.radarChartLabels = this.JsonData.radarChartLabels;
 | ||||
|       },(error) => { | ||||
|         console.log(error); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   generatePDFFile(){ | ||||
|     this.buttonClicked.emit(); | ||||
|     const content = this.contentContainerRef.nativeElement; | ||||
| @ -136,5 +193,18 @@ export class BubbleRunnerComponent implements OnInit { | ||||
|     this.Dashtestservive.generatePDF(content, filename); | ||||
|   } | ||||
|    | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('BubbleRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
|      | ||||
|     console.log('BubbleRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,64 @@ | ||||
| <!-- Display Mode - No configuration UI in runner --> | ||||
| <div class="compact-filter"> | ||||
|   <div class="filter-header"> | ||||
|     <span class="filter-label" *ngIf="filterLabel">{{ filterLabel }}</span> | ||||
|     <span class="filter-key" *ngIf="!filterLabel && filterKey">{{ filterKey }}</span> | ||||
|     <span class="filter-type">({{ filterType }})</span> | ||||
|   </div> | ||||
|    | ||||
|   <!-- Text Filter --> | ||||
|   <div class="filter-control" *ngIf="filterType === 'text'"> | ||||
|     <input type="text"  | ||||
|            [(ngModel)]="filterValue"  | ||||
|            (ngModelChange)="onFilterValueChange($event)" | ||||
|            [placeholder]="filterLabel || filterKey" | ||||
|            class="clr-input compact-input"> | ||||
|   </div> | ||||
| 
 | ||||
|   <!-- Dropdown Filter --> | ||||
|   <div class="filter-control" *ngIf="filterType === 'dropdown'"> | ||||
|     <select [(ngModel)]="filterValue"  | ||||
|             (ngModelChange)="onFilterValueChange($event)" | ||||
|             class="clr-select compact-select"> | ||||
|       <option value="">{{ filterLabel || filterKey }}</option> | ||||
|       <option *ngFor="let option of filterOptions" [value]="option">{{ option }}</option> | ||||
|     </select> | ||||
|   </div> | ||||
| 
 | ||||
|   <!-- Multi-Select Filter --> | ||||
|   <div class="filter-control" *ngIf="filterType === 'multiselect'"> | ||||
|     <div class="checkbox-group"> | ||||
|       <div *ngFor="let option of filterOptions" class="checkbox-item"> | ||||
|         <input type="checkbox"  | ||||
|                [checked]="filterValue && filterValue.includes(option)" | ||||
|                (change)="onMultiSelectChange(option, $event)" | ||||
|                [id]="'checkbox-' + option"> | ||||
|         <label [for]="'checkbox-' + option">{{ option }}</label> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|   <!-- Date Range Filter --> | ||||
|   <div class="filter-control date-range" *ngIf="filterType === 'date-range'"> | ||||
|     <input type="date"  | ||||
|            [(ngModel)]="filterValue.start"  | ||||
|            (ngModelChange)="onDateRangeChange({ start: $event, end: filterValue.end })" | ||||
|            placeholder="Start Date" | ||||
|            class="clr-input compact-date"> | ||||
|     <input type="date"  | ||||
|            [(ngModel)]="filterValue.end"  | ||||
|            (ngModelChange)="onDateRangeChange({ start: filterValue.start, end: $event })" | ||||
|            placeholder="End Date" | ||||
|            class="clr-input compact-date"> | ||||
|   </div> | ||||
| 
 | ||||
|   <!-- Toggle Filter --> | ||||
|   <div class="filter-control toggle" *ngIf="filterType === 'toggle'"> | ||||
|     <input type="checkbox"  | ||||
|            [(ngModel)]="filterValue"  | ||||
|            (ngModelChange)="onToggleChange($event)" | ||||
|            clrToggle  | ||||
|            class="clr-toggle"> | ||||
|     <label class="toggle-label">{{ filterLabel || filterKey }}</label> | ||||
|   </div> | ||||
| </div> | ||||
| @ -0,0 +1,74 @@ | ||||
| .compact-filter { | ||||
|   padding: 10px; | ||||
|   border: 1px solid #ddd; | ||||
|   border-radius: 4px; | ||||
|   margin-bottom: 10px; | ||||
|   background-color: #f8f8f8; | ||||
|    | ||||
|   .filter-header { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     align-items: center; | ||||
|     margin-bottom: 10px; | ||||
|      | ||||
|     .filter-label, .filter-key { | ||||
|       font-weight: bold; | ||||
|     } | ||||
|      | ||||
|     .filter-type { | ||||
|       font-size: 0.8em; | ||||
|       color: #666; | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   .filter-control { | ||||
|     margin-bottom: 10px; | ||||
|      | ||||
|     .compact-input, .compact-select, .compact-date { | ||||
|       width: 100%; | ||||
|       padding: 5px; | ||||
|       border: 1px solid #ccc; | ||||
|       border-radius: 4px; | ||||
|     } | ||||
|      | ||||
|     .compact-multiselect { | ||||
|       width: 100%; | ||||
|       height: 100px; | ||||
|     } | ||||
|      | ||||
|     .checkbox-group { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|        | ||||
|       .checkbox-item { | ||||
|         margin: 5px 0; | ||||
|          | ||||
|         input[type="checkbox"] { | ||||
|           margin-right: 5px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     &.date-range { | ||||
|       display: flex; | ||||
|       gap: 10px; | ||||
|        | ||||
|       .compact-date { | ||||
|         flex: 1; | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     &.toggle { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|        | ||||
|       .clr-toggle { | ||||
|         margin-right: 10px; | ||||
|       } | ||||
|        | ||||
|       .toggle-label { | ||||
|         margin: 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,206 @@ | ||||
| import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core'; | ||||
| import { FilterService, Filter } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-compact-filter-runner', | ||||
|   templateUrl: './compact-filter-runner.component.html', | ||||
|   styleUrls: ['./compact-filter-runner.component.scss'] | ||||
| }) | ||||
| export class CompactFilterRunnerComponent implements OnInit, OnChanges { | ||||
|   @Input() filterKey: string = ''; | ||||
|   @Input() filterType: string = 'text'; | ||||
|   @Input() filterOptions: string[] = []; | ||||
|   @Input() filterLabel: string = ''; | ||||
|   @Input() apiUrl: string = ''; | ||||
|   @Input() connection: number | undefined; | ||||
|   @Output() filterChange = new EventEmitter<any>(); | ||||
|    | ||||
|   selectedFilter: Filter | null = null; | ||||
|   filterValue: any = ''; | ||||
|   availableFilters: Filter[] = []; | ||||
|   availableKeys: string[] = []; | ||||
|   availableValues: string[] = []; | ||||
|    | ||||
|   constructor( | ||||
|     private filterService: FilterService | ||||
|   ) { } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     console.log('CompactFilterRunnerComponent initialized with inputs:', { | ||||
|       filterKey: this.filterKey, | ||||
|       filterType: this.filterType, | ||||
|       filterOptions: this.filterOptions, | ||||
|       filterLabel: this.filterLabel, | ||||
|       apiUrl: this.apiUrl, | ||||
|       connection: this.connection | ||||
|     }); | ||||
|      | ||||
|     // 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 { | ||||
|     console.log('CompactFilterRunnerComponent inputs changed:', changes); | ||||
|      | ||||
|     // If filterKey or filterType changes, re-register the filter
 | ||||
|     if (changes.filterKey || changes.filterType || changes.filterOptions) { | ||||
|       this.registerFilter(); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   // Register this filter with the filter service
 | ||||
|   registerFilter(): void { | ||||
|     console.log('Registering filter with key:', this.filterKey, 'type:', this.filterType); | ||||
|      | ||||
|     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: `${this.filterKey}`, | ||||
|         field: this.filterKey, | ||||
|         label: this.filterLabel || this.filterKey, | ||||
|         type: this.filterType as any, | ||||
|         options: this.filterOptions, | ||||
|         value: this.filterValue // Use the current filter value
 | ||||
|       }; | ||||
|        | ||||
|       console.log('Created filter definition:', filterDef); | ||||
|        | ||||
|       // Get current filters
 | ||||
|       const currentFilters = this.filterService.getFilters(); | ||||
|        | ||||
|       // Check if this filter is already registered
 | ||||
|       const existingFilterIndex = currentFilters.findIndex(f => f.id === filterDef.id); | ||||
|        | ||||
|       if (existingFilterIndex >= 0) { | ||||
|         // Preserve the existing filter configuration
 | ||||
|         const existingFilter = currentFilters[existingFilterIndex]; | ||||
|         console.log('Found existing filter:', existingFilter); | ||||
|          | ||||
|         // 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
 | ||||
|           console.log('Using value from service:', filterDef.value); | ||||
|         } else if (existingFilter.value !== undefined) { | ||||
|           // Fallback to existing filter's value if no service value
 | ||||
|           filterDef.value = existingFilter.value; | ||||
|           this.filterValue = filterDef.value; | ||||
|           console.log('Using value from existing filter:', filterDef.value); | ||||
|         } | ||||
|          | ||||
|         // Preserve other configuration properties
 | ||||
|         filterDef.label = existingFilter.label; | ||||
|         filterDef.options = existingFilter.options || this.filterOptions; | ||||
|          | ||||
|         // Update existing filter
 | ||||
|         currentFilters[existingFilterIndex] = filterDef; | ||||
|         console.log('Updated existing filter:', 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
 | ||||
|           console.log('Using value from service for new filter:', filterDef.value); | ||||
|         } | ||||
|          | ||||
|         // Add new filter
 | ||||
|         currentFilters.push(filterDef); | ||||
|         console.log('Added new filter:', filterDef); | ||||
|       } | ||||
|        | ||||
|       // Update the filter service with the new filter list
 | ||||
|       this.filterService.setFilters(currentFilters); | ||||
|        | ||||
|       // Update the selected filter reference
 | ||||
|       this.selectedFilter = filterDef; | ||||
|       console.log('Selected filter set to:', this.selectedFilter); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   updateSelectedFilter(): void { | ||||
|     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 from the service
 | ||||
|         const currentState = this.filterService.getFilterValues(); | ||||
|         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 || ''; | ||||
|         } | ||||
|          | ||||
|         console.log('Updated selected filter value:', this.filterValue); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   onFilterValueChange(value: any): void { | ||||
|     console.log('Filter value changed:', value); | ||||
|      | ||||
|     if (this.selectedFilter) { | ||||
|       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); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   onToggleChange(checked: boolean): void { | ||||
|     this.onFilterValueChange(checked); | ||||
|   } | ||||
|    | ||||
|   onDateRangeChange(dateRange: { start: string | null, end: string | null }): void { | ||||
|     this.onFilterValueChange(dateRange); | ||||
|   } | ||||
|    | ||||
|   // Handle multi-select changes
 | ||||
|   onMultiSelectChange(option: string, event: any): void { | ||||
|     const checked = event.target.checked; | ||||
|      | ||||
|     // Initialize filterValue as array if it's not already
 | ||||
|     if (!Array.isArray(this.filterValue)) { | ||||
|       this.filterValue = []; | ||||
|     } | ||||
|      | ||||
|     if (checked) { | ||||
|       // Add option to array if not already present
 | ||||
|       if (!this.filterValue.includes(option)) { | ||||
|         this.filterValue.push(option); | ||||
|       } | ||||
|     } else { | ||||
|       // Remove option from array
 | ||||
|       this.filterValue = this.filterValue.filter((item: string) => item !== option); | ||||
|     } | ||||
|      | ||||
|     // Emit the change
 | ||||
|     this.onFilterValueChange(this.filterValue); | ||||
|   } | ||||
| } | ||||
| @ -160,7 +160,27 @@ getlinechart(): any[] { | ||||
|     return this._http.get(url); | ||||
|   } | ||||
| 
 | ||||
|   // New method to support filters
 | ||||
|   public getChartDataWithFilters(tableName: string, jobType: string, xAxis:any, yAxes:any, sureId: number | undefined, parameterField: string, parameterValue: string, filterParams: string): Observable<any> { | ||||
|     let url = `${baseUrl}/chart/getdashjson/${jobType}?tableName=${tableName}&xAxis=${xAxis}&yAxes=${yAxes}`; | ||||
|      | ||||
|     // Add sureId if provided
 | ||||
|     if (sureId) { | ||||
|       url += `&sureId=${sureId}`; | ||||
|     } | ||||
|      | ||||
|     // Add parameter field and value if provided
 | ||||
|     if (parameterField && parameterValue) { | ||||
|       url += `¶meter=${encodeURIComponent(parameterField)}¶meterValue=${encodeURIComponent(parameterValue)}`; | ||||
|     } | ||||
|      | ||||
|     // Add filter parameters if provided
 | ||||
|     if (filterParams) { | ||||
|       url += `&filters=${encodeURIComponent(filterParams)}`; | ||||
|     } | ||||
|      | ||||
|     return this._http.get(url); | ||||
|   } | ||||
| 
 | ||||
|   //////////////////////////////////////////////
 | ||||
|    | ||||
|  | ||||
| @ -26,7 +26,11 @@ | ||||
|         <!-- <span><button class="btn btn-primary" (click)="Export(item.name)">Export</button></span> --> | ||||
|         <!-- <span><app-line-runner (buttonClicked)="generatePDFFile()"></app-line-runner></span> --> | ||||
|         <!-- <h4 style="margin-top: 10px; margin-left: 10px;">{{ item.charttitle }}</h4> --> | ||||
|         <ndc-dynamic class="no-drag" [ndcDynamicComponent]="item.component" (moduleInfo)="display($event)"></ndc-dynamic> | ||||
|         <ndc-dynamic class="no-drag"  | ||||
|                      [ndcDynamicComponent]="item.component"  | ||||
|                      [ndcDynamicInputs]="getComponentInputs(item)" | ||||
|                      (moduleInfo)="display($event)"> | ||||
|         </ndc-dynamic> | ||||
|       </gridster-item> | ||||
|     </gridster> | ||||
|   </div> | ||||
|  | ||||
| @ -17,6 +17,10 @@ import { BubbleRunnerComponent } from './bubble-runner/bubble-runner.component'; | ||||
| import { ScatterRunnerComponent } from './scatter-runner/scatter-runner.component'; | ||||
| import { PolarRunnerComponent } from './polar-runner/polar-runner.component'; | ||||
| import { RadarRunnerComponent } from './radar-runner/radar-runner.component'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../dashboardnew/common-filter/filter.service'; | ||||
| // Add CompactFilterRunnerComponent import
 | ||||
| import { CompactFilterRunnerComponent } from './compact-filter-runner/compact-filter-runner.component'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-dashrunnerline', | ||||
| @ -44,10 +48,13 @@ export class DashrunnerlineComponent implements OnInit { | ||||
|   { name: "Radar Chart", componentInstance: RadarRunnerComponent }, | ||||
|   { name: "Grid View", componentInstance: GridRunnerComponent }, | ||||
|   { name: "To Do Chart", componentInstance: TodoRunnerComponent }, | ||||
|   { name: "Compact Filter", componentInstance: CompactFilterRunnerComponent }, // Add Compact Filter Runner
 | ||||
| ]; | ||||
| 
 | ||||
|   constructor(private Dashtestservive:DashrunnerService, private dashboardService: Dashboard3Service,private route: ActivatedRoute, | ||||
|     private router : Router,) { } | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService) { } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
| 
 | ||||
| @ -288,4 +295,48 @@ dashboard_name = "Dashtest"; | ||||
|     console.log('Button clicked in SomeComponent'); | ||||
|     // Add your custom logic here when the button is clicked in SomeComponent
 | ||||
|   } | ||||
|    | ||||
|   // Method to provide inputs for dynamic components based on their type
 | ||||
|   getComponentInputs(item: any): any { | ||||
|     const inputs: any = {}; | ||||
|      | ||||
|     // Common inputs for all components
 | ||||
|     if (item.table !== undefined) inputs.table = item.table; | ||||
|     if (item.xAxis !== undefined) inputs.xAxis = item.xAxis; | ||||
|     if (item.yAxis !== undefined) inputs.yAxis = item.yAxis; | ||||
|     if (item.connection !== undefined) inputs.connection = item.connection; | ||||
|     if (item.charttitle !== undefined) inputs.charttitle = item.charttitle; | ||||
|     if (item.chartlegend !== undefined) inputs.chartlegend = item.chartlegend; | ||||
|     if (item.showlabel !== undefined) inputs.showlabel = item.showlabel; | ||||
|      | ||||
|     // Compact Filter specific inputs
 | ||||
|     if (item.name === 'Compact Filter') { | ||||
|       if (item.filterKey !== undefined) inputs.filterKey = item.filterKey; | ||||
|       if (item.filterType !== undefined) inputs.filterType = item.filterType; | ||||
|       if (item.filterLabel !== undefined) inputs.filterLabel = item.filterLabel; | ||||
|       if (item.filterOptions !== undefined) inputs.filterOptions = item.filterOptions; | ||||
|       if (item.table !== undefined) inputs.apiUrl = item.table; // Use table as API URL for compact filter
 | ||||
|       if (item.connection !== undefined) inputs.connection = item.connection ? parseInt(item.connection, 10) : undefined; | ||||
|     } | ||||
|      | ||||
|     // Grid View specific inputs
 | ||||
|     if (item.name === 'Grid View') { | ||||
|       if (item.baseFilters !== undefined) inputs.baseFilters = item.baseFilters; | ||||
|     } | ||||
|      | ||||
|     // Chart specific inputs
 | ||||
|     if (item.name.includes('Chart') && item.name !== 'Compact Filter') { | ||||
|       if (item.baseFilters !== undefined) inputs.baseFilters = item.baseFilters; | ||||
|       if (item.drilldownEnabled !== undefined) inputs.drilldownEnabled = item.drilldownEnabled; | ||||
|       if (item.drilldownApiUrl !== undefined) inputs.drilldownApiUrl = item.drilldownApiUrl; | ||||
|       if (item.drilldownXAxis !== undefined) inputs.drilldownXAxis = item.drilldownXAxis; | ||||
|       if (item.drilldownYAxis !== undefined) inputs.drilldownYAxis = item.drilldownYAxis; | ||||
|       if (item.drilldownParameter !== undefined) inputs.drilldownParameter = item.drilldownParameter; | ||||
|       if (item.drilldownFilters !== undefined) inputs.drilldownFilters = item.drilldownFilters; | ||||
|       if (item.drilldownLayers !== undefined) inputs.drilldownLayers = item.drilldownLayers; | ||||
|     } | ||||
|      | ||||
|     console.log('Component inputs for', item.name, ':', inputs); | ||||
|     return inputs; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,10 @@ import { ChartDataset, ChartType,   } from 'chart.js'; | ||||
| import { ActivatedRoute, Router } from '@angular/router'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-doughnut-runner', | ||||
| @ -33,10 +37,16 @@ export class DoughnutRunnerComponent implements OnInit { | ||||
|     "chartLabels": ["Project", "Repository", "Wireframe"] | ||||
|   } | ||||
|   doughnutChartType: ChartType = 'doughnut'; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
|    | ||||
|      | ||||
|     constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|       private router : Router,) { } | ||||
|       private router : Router, | ||||
|       // Add FilterService to constructor
 | ||||
|       private filterService: FilterService) { } | ||||
|   ngOnInit(): void { | ||||
|     this.doughnutChartData = this.doughnutData.chartData; | ||||
|     this.doughnutChartLabels = this.doughnutData.chartLabels; | ||||
| @ -44,6 +54,14 @@ export class DoughnutRunnerComponent implements OnInit { | ||||
|     this.editId = this.route.snapshot.params.id; | ||||
|     console.log(this.editId); | ||||
| 
 | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         // When filters change, refresh the chart data
 | ||||
|         this.fetchChartData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|       console.log(data); | ||||
|       this.workflowLine = data.dashbord1_Line[0].model; | ||||
| @ -70,22 +88,62 @@ export class DoughnutRunnerComponent implements OnInit { | ||||
|           this.YAxis = ChartObject[i].yAxis; | ||||
|           this.showlabel = ChartObject[i].showlabel; | ||||
|           this.doughnutChartLegend = ChartObject[i].chartlegend; | ||||
|           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|           console.log(this.TableName); | ||||
|           this.Dashtestservive.getChartData(this.TableName,"Doughnut Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|             console.log(Ldata); | ||||
|             this.JsonData = Ldata; | ||||
|             this.doughnutChartData = this.JsonData.chartData; | ||||
|             this.doughnutChartLabels = this.JsonData.chartLabels; | ||||
|              | ||||
|            },(error) => { | ||||
|             console.log(error); | ||||
|            }); | ||||
|           // Fetch data with filters
 | ||||
|           this.fetchChartData(); | ||||
|           break; // No need to continue the loop once the correct placeholder is found
 | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   // Fetch chart data with filter support
 | ||||
|   fetchChartData(): void { | ||||
|     if (this.TableName && this.XAxis && this.YAxis) { | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
|        | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|        | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('DoughnutRunner: Final filter object to send to API:', filterObj); | ||||
|        | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "Doughnut Chart", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log(Ldata); | ||||
|         this.JsonData = Ldata; | ||||
|         this.doughnutChartData = this.JsonData.chartData; | ||||
|         this.doughnutChartLabels = this.JsonData.chartLabels; | ||||
|       },(error) => { | ||||
|         console.log(error); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   generatePDFFile(){ | ||||
|     // this.buttonClicked.emit();
 | ||||
|     const content = this.contentContainerRef.nativeElement; | ||||
| @ -94,6 +152,19 @@ export class DoughnutRunnerComponent implements OnInit { | ||||
|     this.Dashtestservive.generatePDF(content, filename); | ||||
|   } | ||||
|    | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('DoughnutRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
|      | ||||
|     console.log('DoughnutRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -39,16 +39,35 @@ | ||||
|       </div> --> | ||||
|       <div><button class="btn btn-primary" (click)="generatePDFFile()">Export</button></div> | ||||
|       <div style="max-height: 400px; overflow: auto; padding: 10px;"> | ||||
|       <table class="table"> | ||||
|       <!-- Debug information --> | ||||
|       <div *ngIf="false" style="background-color: #f0f0f0; padding: 10px; margin-bottom: 10px;"> | ||||
|         <h4>Debug Information</h4> | ||||
|         <p><strong>TableName:</strong> {{ TableName }}</p> | ||||
|         <p><strong>XAxis:</strong> {{ XAxis }}</p> | ||||
|         <p><strong>YAxis:</strong> {{ YAxis }}</p> | ||||
|         <p><strong>Rows:</strong> {{ rows?.length }} items</p> | ||||
|         <p><strong>Headers:</strong> {{ getHeaders() | json }}</p> | ||||
|         <div *ngIf="error"><strong>Error:</strong> {{ error }}</div> | ||||
|       </div> | ||||
|        | ||||
|       <div *ngIf="error" class="error_mess"> | ||||
|         {{ error }} | ||||
|       </div> | ||||
|        | ||||
|       <table class="table" *ngIf="rows && rows.length > 0; else noData"> | ||||
|         <thead> | ||||
|           <tr> | ||||
|             <th *ngFor="let co of getHeaders();let i=index">{{co}}</th> | ||||
|           </tr> | ||||
|         </thead> | ||||
|         <tbody> | ||||
|           <tr *ngFor="let item of rows?.slice()?.reverse()"> | ||||
|           <tr *ngFor="let item of rows"> | ||||
|             <td *ngFor="let key of getHeaders()">{{item[key]}}</td> | ||||
|           </tr> | ||||
|         </tbody> | ||||
|       </table> | ||||
|        | ||||
|       <ng-template #noData> | ||||
|         <p *ngIf="!error">No data available</p> | ||||
|       </ng-template> | ||||
|       </div> | ||||
| @ -3,6 +3,10 @@ import { DashrunnerService } from '../dashrunner.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| import { ActivatedRoute, Router } from '@angular/router'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-grid-runner', | ||||
| @ -26,57 +30,150 @@ export class GridRunnerComponent implements OnInit { | ||||
|   public DashtestboardArray: DashboardContentModel[] = []; | ||||
|   workflowLine; | ||||
|   TableName; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
| 
 | ||||
|   constructor( | ||||
|     private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router | ||||
|     private Dashtestservive:DashrunnerService, | ||||
|     private route: ActivatedRoute, | ||||
|     private dashboardService: Dashboard3Service, | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService | ||||
|   ) { } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
| 
 | ||||
|     this.editId = this.route.snapshot.params.id; | ||||
|     console.log(this.editId); | ||||
|     console.log('GridRunner: Component initialized with editId:', this.editId); | ||||
|     // this.getbyId();
 | ||||
| 
 | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         console.log('GridRunner: Filter state changed:', filters); | ||||
|         // When filters change, refresh the grid data
 | ||||
|         this.fetchGridData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.dashboardService.getById(this.editId).subscribe((data) => { | ||||
|       console.log(data); | ||||
|       console.log('GridRunner: Received dashboard data:', data); | ||||
|       this.workflowLine = data.dashbord1_Line[0].model; | ||||
|       const dash = JSON.parse(this.workflowLine); | ||||
|       // this.DashtestboardArray = dash.dashboard;
 | ||||
|       // console.log(this.DashtestboardArray);
 | ||||
| 
 | ||||
|       const ChartObject = dash.dashboard.filter(obj => obj.name === "Grid View"); | ||||
|       console.log(ChartObject); | ||||
|       console.log('GridRunner: ChartObject for Grid View:', ChartObject); | ||||
|       for (let i = 0; i < ChartObject.length; i++) { | ||||
|         const ids = this.Dashtestservive.getgridview(); | ||||
|         console.log('GridRunner: Current gridview ids:', ids); | ||||
|         console.log('GridRunner: Checking chartid:', ChartObject[i].chartid); | ||||
|         // console.log(ids);
 | ||||
|         if (ids.includes(ChartObject[i].chartid)) { | ||||
|           // If the chartid is already in the ids array, continue to the next iteration
 | ||||
|           console.log('GridRunner: Skipping chartid as it already exists:', ChartObject[i].chartid); | ||||
|           continue; | ||||
|         } | ||||
|         console.log('GridRunner: Adding new chartid:', ChartObject[i].chartid); | ||||
|         this.Dashtestservive.setgridview(ChartObject[i].chartid); | ||||
|         const id = ids[i]; | ||||
|         console.log(id); | ||||
| 
 | ||||
|         if (ChartObject[i].chartid === id) { | ||||
|         this.TableName = ChartObject[i].table; | ||||
|         this.XAxis = ChartObject[i].xAxis; | ||||
|         this.YAxis = ChartObject[i].yAxis; | ||||
|           console.log(this.TableName); | ||||
|           this.Dashtestservive.getChartData(this.TableName,"Grid View",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|             console.log(Ldata); | ||||
|             this.rows = Ldata; | ||||
|             this.rowdata = this.rows | ||||
|              | ||||
|            },(error) => { | ||||
|             console.log(error); | ||||
|            }); | ||||
|         // Add connection ID if available
 | ||||
|         this.ConnectionId = ChartObject[i].connection; | ||||
|         console.log('GridRunner: TableName:', this.TableName); | ||||
|         console.log('GridRunner: XAxis:', this.XAxis); | ||||
|         console.log('GridRunner: YAxis:', this.YAxis); | ||||
|         console.log('GridRunner: ConnectionId:', this.ConnectionId); | ||||
|         // Fetch data with filters
 | ||||
|         this.fetchGridData(); | ||||
|         break; // No need to continue the loop once the correct placeholder is found
 | ||||
|       } | ||||
|       } | ||||
|     }, (error) => { | ||||
|       console.log('GridRunner: Error fetching dashboard data:', error); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   // Fetch grid data with filter support
 | ||||
|   fetchGridData(): void { | ||||
|     console.log('fetching grid data ...') | ||||
|     if (this.TableName) { | ||||
|       console.log('GridRunner: Fetching data for TableName:', this.TableName, 'XAxis:', this.XAxis, 'YAxis:', this.YAxis); | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
| 
 | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
| 
 | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('GridRunner: Final filter object to send to API:', filterObj); | ||||
| 
 | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "grid", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log('GridRunner: Received data from API:', Ldata); | ||||
| 
 | ||||
|         // Handle the actual data structure returned by the API
 | ||||
|         if (Ldata && Ldata.chartData) { | ||||
|           this.rows = Ldata.chartData; | ||||
|           this.rowdata = this.rows; | ||||
|         } else if (Ldata && Ldata.data) { | ||||
|           // Handle the original expected format as fallback
 | ||||
|           this.rows = Ldata.data; | ||||
|           this.rowdata = this.rows; | ||||
|         } else if (Array.isArray(Ldata)) { | ||||
|           // Handle case where data is directly an array
 | ||||
|           this.rows = Ldata; | ||||
|           this.rowdata = this.rows; | ||||
|         } else { | ||||
|           console.warn('GridRunner: Received data does not have expected structure', Ldata); | ||||
|           this.rows = []; | ||||
|           this.rowdata = []; | ||||
|         } | ||||
| 
 | ||||
|         // Log the structure of the received data
 | ||||
|         if (this.rows) { | ||||
|           console.log('GridRunner: Rows length:', this.rows.length); | ||||
|           if (this.rows.length > 0) { | ||||
|             console.log('GridRunner: First row structure:', this.rows[0]); | ||||
|           } | ||||
|         } else { | ||||
|           console.log('GridRunner: No data received'); | ||||
|         } | ||||
|       }, (error) => { | ||||
|         console.log('GridRunner: Error fetching data:', error); | ||||
|         this.error = error; | ||||
|       }); | ||||
|     } else { | ||||
|       console.log('GridRunner: Missing TableName or XAxis'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   //dynamic table
 | ||||
| @ -87,16 +184,16 @@ getTableData(id){ | ||||
|   getHeaders() { | ||||
|     let headers: string[] = []; | ||||
|     if (this.rows) { | ||||
|       console.log('GridRunner: Getting headers from rows:', this.rows); | ||||
|       this.rows.forEach((value) => { | ||||
|         Object.keys(value).forEach((key) => { | ||||
|           if (!headers.find((header) => header == key)) { | ||||
|             headers.push(key) | ||||
|           } | ||||
| 
 | ||||
|         }) | ||||
| 
 | ||||
|       }) | ||||
|     } | ||||
|     console.log('GridRunner: Computed headers:', headers); | ||||
|     return headers; | ||||
|   } | ||||
| 
 | ||||
| @ -107,5 +204,17 @@ generatePDFFile(){ | ||||
| 
 | ||||
|     this.Dashtestservive.generatePDF(content, filename); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('GridRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
| 
 | ||||
|     console.log('GridRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| } | ||||
| @ -7,6 +7,10 @@ import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| import { jsPDF } from 'jspdf'; | ||||
| import domtoimage from 'dom-to-image'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| @Component({ | ||||
|   selector: 'app-line-runner', | ||||
|   templateUrl: './line-runner.component.html', | ||||
| @ -54,8 +58,14 @@ export class LineRunnerComponent implements OnInit { | ||||
|    lineChartLegend = false; | ||||
|   lineChartPlugins = []; | ||||
|   lineChartType = 'line'; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
|   constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router,) { } | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService) { } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
| 
 | ||||
| @ -65,6 +75,13 @@ export class LineRunnerComponent implements OnInit { | ||||
|       this.editId = this.route.snapshot.params.id; | ||||
|       console.log(this.editId); | ||||
| 
 | ||||
|       // Subscribe to filter changes
 | ||||
|       this.subscriptions.push( | ||||
|         this.filterService.filterState$.subscribe(filters => { | ||||
|           // When filters change, refresh the chart data
 | ||||
|           this.fetchChartData(); | ||||
|         }) | ||||
|       ); | ||||
| 
 | ||||
|       this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|         console.log(data); | ||||
| @ -92,16 +109,10 @@ export class LineRunnerComponent implements OnInit { | ||||
|             this.YAxis = ChartObject[i].yAxis; | ||||
|             this.showlabel = ChartObject[i].showlabel; | ||||
|             this.lineChartLegend = ChartObject[i].chartlegend; | ||||
|             this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|             console.log(this.TableName); | ||||
|             this.Dashtestservive.getChartData(this.TableName,"Line Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|               console.log(Ldata); | ||||
|               this.JsonData = Ldata; | ||||
|               this.lineChartData = this.JsonData.chartData; | ||||
|               this.lineChartLabels = this.JsonData.chartLabels; | ||||
|                | ||||
|              },(error) => { | ||||
|               console.log(error); | ||||
|              }); | ||||
|             // Fetch data with filters
 | ||||
|             this.fetchChartData(); | ||||
|             break; // No need to continue the loop once the correct placeholder is found
 | ||||
|           } | ||||
|         } | ||||
| @ -128,6 +139,52 @@ export class LineRunnerComponent implements OnInit { | ||||
|   //     }
 | ||||
|   } | ||||
|    | ||||
|   // Fetch chart data with filter support
 | ||||
|   fetchChartData(): void { | ||||
|     if (this.TableName && this.XAxis && this.YAxis) { | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
|        | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|        | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('LineRunner: Final filter object to send to API:', filterObj); | ||||
|        | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "Line Chart", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log(Ldata); | ||||
|         this.JsonData = Ldata; | ||||
|         this.lineChartData = this.JsonData.chartData; | ||||
|         this.lineChartLabels = this.JsonData.chartLabels; | ||||
|       },(error) => { | ||||
|         console.log(error); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   generatePDFFile(){ | ||||
|     this.buttonClicked.emit(); | ||||
|     const content = this.contentContainerRef.nativeElement; | ||||
| @ -166,4 +223,17 @@ export class LineRunnerComponent implements OnInit { | ||||
|   //   }
 | ||||
|   // }
 | ||||
|    | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('LineRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
|      | ||||
|     console.log('LineRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -3,6 +3,10 @@ import { DashrunnerService } from '../dashrunner.service'; | ||||
| import { ActivatedRoute, Router } from '@angular/router'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| 
 | ||||
| @Component({ | ||||
| @ -23,9 +27,15 @@ export class PieRunnerComponent implements OnInit { | ||||
|   showlabel; | ||||
|   JsonData; | ||||
|   lineChartNoLabels: any[] = []; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
|    | ||||
|   constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router,) { } | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService) { } | ||||
| 
 | ||||
|   public pieChartLabels: string[] = ['SciFi', 'Drama', 'Comedy']; | ||||
|   public pieChartData: number[] = [30, 50, 20]; | ||||
| @ -39,6 +49,13 @@ export class PieRunnerComponent implements OnInit { | ||||
|     this.editId = this.route.snapshot.params.id; | ||||
|     console.log(this.editId); | ||||
| 
 | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         // When filters change, refresh the chart data
 | ||||
|         this.fetchChartData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|       console.log(data); | ||||
| @ -66,22 +83,62 @@ export class PieRunnerComponent implements OnInit { | ||||
|           this.YAxis = ChartObject[i].yAxis; | ||||
|           this.showlabel = ChartObject[i].showlabel; | ||||
|           this.ChartLegend = ChartObject[i].chartlegend; | ||||
|           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|           console.log(this.TableName); | ||||
|           this.Dashtestservive.getChartData(this.TableName,"Pie Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|             console.log(Ldata); | ||||
|             this.JsonData = Ldata; | ||||
|             this.pieChartData = this.JsonData.pieChartData; | ||||
|             this.pieChartLabels = this.JsonData.pieChartLabels; | ||||
|              | ||||
|            },(error) => { | ||||
|             console.log(error); | ||||
|            }); | ||||
|           // Fetch data with filters
 | ||||
|           this.fetchChartData(); | ||||
|           break; // No need to continue the loop once the correct placeholder is found
 | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   // Fetch chart data with filter support
 | ||||
|   fetchChartData(): void { | ||||
|     if (this.TableName && this.XAxis && this.YAxis) { | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
|        | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|        | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('PieRunner: Final filter object to send to API:', filterObj); | ||||
|        | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "Pie Chart", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log(Ldata); | ||||
|         this.JsonData = Ldata; | ||||
|         this.pieChartData = this.JsonData.pieChartData; | ||||
|         this.pieChartLabels = this.JsonData.pieChartLabels; | ||||
|       },(error) => { | ||||
|         console.log(error); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   generatePDFFile(){ | ||||
|     this.buttonClicked.emit(); | ||||
|     const content = this.contentContainerRef.nativeElement; | ||||
| @ -89,4 +146,17 @@ export class PieRunnerComponent implements OnInit { | ||||
| 
 | ||||
|     this.Dashtestservive.generatePDF(content, filename); | ||||
|   } | ||||
|    | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('PieRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
|      | ||||
|     console.log('PieRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,10 @@ import { ActivatedRoute, Router } from '@angular/router'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| // import { Label } from 'ng2-charts';
 | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-polar-runner', | ||||
| @ -23,9 +27,15 @@ export class PolarRunnerComponent implements OnInit { | ||||
|   showlabel; | ||||
|   JsonData; | ||||
|   lineChartNoLabels: any[] = []; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
|    | ||||
|   constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router,) { } | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService) { } | ||||
| 
 | ||||
|   public polarAreaChartLabels: string[] = [ 'Download Sales', 'In-Store Sales', 'Mail Sales', 'Telesales', 'Corporate Sales' ]; | ||||
|   public polarAreaChartData: any = [ | ||||
| @ -41,6 +51,13 @@ export class PolarRunnerComponent implements OnInit { | ||||
|     this.editId = this.route.snapshot.params.id; | ||||
|     console.log(this.editId); | ||||
| 
 | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         // When filters change, refresh the chart data
 | ||||
|         this.fetchChartData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|       console.log(data); | ||||
| @ -66,22 +83,62 @@ export class PolarRunnerComponent implements OnInit { | ||||
|           this.YAxis = ChartObject[i].yAxis; | ||||
|           this.showlabel = ChartObject[i].showlabel; | ||||
|           this.ChartLegend = ChartObject[i].chartlegend; | ||||
|           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|           console.log(this.TableName); | ||||
|           this.Dashtestservive.getChartData(this.TableName,"PolarArea Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|             console.log(Ldata); | ||||
|             this.JsonData = Ldata; | ||||
|             this.polarAreaChartData = this.JsonData.polarAreaChartData; | ||||
|             this.polarAreaChartLabels = this.JsonData.polarAreaChartLabels; | ||||
|              | ||||
|            },(error) => { | ||||
|             console.log(error); | ||||
|            }); | ||||
|           // Fetch data with filters
 | ||||
|           this.fetchChartData(); | ||||
|           break; // No need to continue the loop once the correct placeholder is found
 | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   // Fetch chart data with filter support
 | ||||
|   fetchChartData(): void { | ||||
|     if (this.TableName && this.XAxis && this.YAxis) { | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
|        | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|        | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('PolarRunner: Final filter object to send to API:', filterObj); | ||||
|        | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "PolarArea Chart", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log(Ldata); | ||||
|         this.JsonData = Ldata; | ||||
|         this.polarAreaChartData = this.JsonData.polarAreaChartData; | ||||
|         this.polarAreaChartLabels = this.JsonData.polarAreaChartLabels; | ||||
|       },(error) => { | ||||
|         console.log(error); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   generatePDFFile(){ | ||||
|     this.buttonClicked.emit(); | ||||
|     const content = this.contentContainerRef.nativeElement; | ||||
| @ -90,4 +147,17 @@ export class PolarRunnerComponent implements OnInit { | ||||
|     this.Dashtestservive.generatePDF(content, filename); | ||||
|   } | ||||
|    | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('PolarRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
|      | ||||
|     console.log('PolarRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,10 @@ import { ActivatedRoute, Router } from '@angular/router'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| // import { Label } from 'ng2-charts';
 | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-radar-runner', | ||||
| @ -24,9 +28,15 @@ export class RadarRunnerComponent implements OnInit { | ||||
|   JsonData; | ||||
|   lineChartNoLabels: any[] = []; | ||||
|   ChartLegend = false; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
|    | ||||
|   constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router,) { } | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService) { } | ||||
| 
 | ||||
|   public radarChartLabels: string[] = [ | ||||
|     "Eating", | ||||
| @ -50,6 +60,13 @@ export class RadarRunnerComponent implements OnInit { | ||||
|     this.editId = this.route.snapshot.params.id; | ||||
|     console.log(this.editId); | ||||
| 
 | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         // When filters change, refresh the chart data
 | ||||
|         this.fetchChartData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|       console.log(data); | ||||
| @ -75,22 +92,62 @@ export class RadarRunnerComponent implements OnInit { | ||||
|           this.YAxis = ChartObject[i].yAxis; | ||||
|           this.showlabel = ChartObject[i].showlabel; | ||||
|           this.ChartLegend = ChartObject[i].chartlegend; | ||||
|           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|           console.log(this.TableName); | ||||
|           this.Dashtestservive.getChartData(this.TableName,"Radar Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|             console.log(Ldata); | ||||
|             this.JsonData = Ldata; | ||||
|             this.radarChartData = this.JsonData.radarChartData; | ||||
|             this.radarChartLabels = this.JsonData.radarChartLabels; | ||||
|              | ||||
|            },(error) => { | ||||
|             console.log(error); | ||||
|            }); | ||||
|           // Fetch data with filters
 | ||||
|           this.fetchChartData(); | ||||
|           break; // No need to continue the loop once the correct placeholder is found
 | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   // Fetch chart data with filter support
 | ||||
|   fetchChartData(): void { | ||||
|     if (this.TableName && this.XAxis && this.YAxis) { | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
|        | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|        | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('RadarRunner: Final filter object to send to API:', filterObj); | ||||
|        | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "Radar Chart", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log(Ldata); | ||||
|         this.JsonData = Ldata; | ||||
|         this.radarChartData = this.JsonData.radarChartData; | ||||
|         this.radarChartLabels = this.JsonData.radarChartLabels; | ||||
|       },(error) => { | ||||
|         console.log(error); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   generatePDFFile(){ | ||||
|     this.buttonClicked.emit(); | ||||
|     const content = this.contentContainerRef.nativeElement; | ||||
| @ -99,4 +156,17 @@ export class RadarRunnerComponent implements OnInit { | ||||
|     this.Dashtestservive.generatePDF(content, filename); | ||||
|   } | ||||
|    | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('RadarRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
|      | ||||
|     console.log('RadarRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,10 @@ import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| // import { Label } from 'ng2-charts';
 | ||||
| import { ChartDataset } from 'chart.js'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-scatter-runner', | ||||
| @ -25,9 +29,15 @@ export class ScatterRunnerComponent implements OnInit { | ||||
|   JsonData; | ||||
|   lineChartNoLabels: any[] = []; | ||||
|   ChartLegend = false; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
|    | ||||
|   constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router,) { } | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService) { } | ||||
| 
 | ||||
|   public scatterChartLabels: string[] = [ 'Eating', 'Drinking', 'Sleeping', 'Designing', 'Coding', 'Cycling', 'Running' ]; | ||||
| 
 | ||||
| @ -69,6 +79,13 @@ export class ScatterRunnerComponent implements OnInit { | ||||
|     this.editId = this.route.snapshot.params.id; | ||||
|     console.log(this.editId); | ||||
| 
 | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         // When filters change, refresh the chart data
 | ||||
|         this.fetchChartData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|       console.log(data); | ||||
| @ -94,22 +111,62 @@ export class ScatterRunnerComponent implements OnInit { | ||||
|           this.YAxis = ChartObject[i].yAxis; | ||||
|           this.showlabel = ChartObject[i].showlabel; | ||||
|           this.ChartLegend = ChartObject[i].chartlegend; | ||||
|           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|           console.log(this.TableName); | ||||
|           this.Dashtestservive.getChartData(this.TableName,"Scatter Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|             console.log(Ldata); | ||||
|             this.JsonData = Ldata; | ||||
|             this.scatterChartData = this.JsonData.scatterChartData; | ||||
|             this.scatterChartLabels = this.JsonData.scatterChartLabels; | ||||
|              | ||||
|            },(error) => { | ||||
|             console.log(error); | ||||
|            }); | ||||
|           // Fetch data with filters
 | ||||
|           this.fetchChartData(); | ||||
|           break; // No need to continue the loop once the correct placeholder is found
 | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   // Fetch chart data with filter support
 | ||||
|   fetchChartData(): void { | ||||
|     if (this.TableName && this.XAxis && this.YAxis) { | ||||
|       // Convert YAxis to string if it's an array
 | ||||
|       const yAxisString = Array.isArray(this.YAxis) ? this.YAxis.join(',') : this.YAxis; | ||||
|        | ||||
|       // Get filter parameters from common filters
 | ||||
|       const commonFilters = this.filterService.getFilterValues(); | ||||
|       const filterDefinitions = this.filterService.getFilters(); | ||||
|        | ||||
|       // Build filter object using field names as keys
 | ||||
|       const filterObj = {}; | ||||
|       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('ScatterRunner: Final filter object to send to API:', filterObj); | ||||
|        | ||||
|       // Fetch data from the dashboard service with filters
 | ||||
|       this.Dashtestservive.getChartDataWithFilters(this.TableName, "Scatter Chart", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|         console.log(Ldata); | ||||
|         this.JsonData = Ldata; | ||||
|         this.scatterChartData = this.JsonData.scatterChartData; | ||||
|         this.scatterChartLabels = this.JsonData.scatterChartLabels; | ||||
|       },(error) => { | ||||
|         console.log(error); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   generatePDFFile(){ | ||||
|     this.buttonClicked.emit(); | ||||
|     const content = this.contentContainerRef.nativeElement; | ||||
| @ -118,4 +175,17 @@ export class ScatterRunnerComponent implements OnInit { | ||||
|     this.Dashtestservive.generatePDF(content, filename); | ||||
|   } | ||||
|    | ||||
|   ngOnDestroy() { | ||||
|     // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|     console.log('ScatterRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|     this.subscriptions.forEach(subscription => { | ||||
|       if (subscription && !subscription.closed) { | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     }); | ||||
|     this.subscriptions = []; | ||||
|      | ||||
|     console.log('ScatterRunnerComponent destroyed and cleaned up'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,10 @@ import { DashrunnerService } from '../dashrunner.service'; | ||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||
| import { ActivatedRoute, Router } from '@angular/router'; | ||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| // Add FilterService import
 | ||||
| import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||
| // Add Subscription import
 | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-todo-runner', | ||||
| @ -12,8 +16,9 @@ import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||
| export class TodoRunnerComponent implements OnInit { | ||||
|   @ViewChild('contentContainer') contentContainerRef!: ElementRef; | ||||
|   @Output() buttonClicked = new EventEmitter<void>(); | ||||
|   constructor( private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router) { } | ||||
|    | ||||
|   // Add subscriptions to unsubscribe on destroy
 | ||||
|   private subscriptions: Subscription[] = []; | ||||
| 
 | ||||
|   loading = false; | ||||
|   givendata; | ||||
| @ -25,6 +30,7 @@ export class TodoRunnerComponent implements OnInit { | ||||
|   public DashtestboardArray: DashboardContentModel[] = []; | ||||
|   workflowLine; | ||||
|   TableName; | ||||
|   ConnectionId: number; // Add ConnectionId property
 | ||||
| 
 | ||||
|   list; | ||||
|   data: any; | ||||
| @ -34,11 +40,25 @@ export class TodoRunnerComponent implements OnInit { | ||||
|     listName: "title123", | ||||
|     List:['todo 1','todo 2'], | ||||
|   } | ||||
|    | ||||
|   constructor( private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, | ||||
|     private router : Router, | ||||
|     // Add FilterService to constructor
 | ||||
|     private filterService: FilterService) { } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.editId = this.route.snapshot.params.id; | ||||
|     console.log(this.editId); | ||||
|     // this.getbyId();
 | ||||
| 
 | ||||
|     // Subscribe to filter changes
 | ||||
|     this.subscriptions.push( | ||||
|       this.filterService.filterState$.subscribe(filters => { | ||||
|         // When filters change, refresh the todo data
 | ||||
|         this.fetchTodoData(); | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||
|       console.log(data); | ||||
|       this.workflowLine = data.dashbord1_Line[0].model; | ||||
| @ -63,15 +83,10 @@ export class TodoRunnerComponent implements OnInit { | ||||
|           this.TableName = ChartObject[i].table; | ||||
|           this.XAxis = ChartObject[i].xAxis; | ||||
|           this.YAxis = ChartObject[i].yAxis; | ||||
|           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||
|           console.log(this.TableName); | ||||
|           this.Dashtestservive.getChartData(this.TableName,"Todo List",this.XAxis,this.YAxis).subscribe((Ldata) => { | ||||
|             console.log(Ldata); | ||||
|             this.todoList.listName = Ldata.listName; | ||||
|             this.todoList.List = Ldata.List; | ||||
|              | ||||
|            },(error) => { | ||||
|             console.log(error); | ||||
|            }); | ||||
|           // Fetch data with filters
 | ||||
|           this.fetchTodoData(); | ||||
|           break; // No need to continue the loop once the correct placeholder is found
 | ||||
|         } | ||||
|       } | ||||
| @ -100,4 +115,58 @@ generatePDFFile(){ | ||||
| 
 | ||||
|   this.Dashtestservive.generatePDF(content, filename); | ||||
| } | ||||
| 
 | ||||
| // Fetch todo data with filter support
 | ||||
| fetchTodoData(): void { | ||||
|   if (this.TableName && this.XAxis && this.YAxis) { | ||||
|     // Get filter parameters from common filters
 | ||||
|     const commonFilters = this.filterService.getFilterValues(); | ||||
|      | ||||
|     // Build filter object using field names as keys
 | ||||
|     const filterObj = {}; | ||||
|     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('TodoRunner: Final filter object to send to API:', filterObj); | ||||
|      | ||||
|     // Fetch data from the dashboard service with filters
 | ||||
|     this.Dashtestservive.getChartDataWithFilters(this.TableName, "Todo List", this.XAxis, this.YAxis, this.ConnectionId, '', '', filterParams).subscribe((Ldata) => { | ||||
|       console.log(Ldata); | ||||
|       this.todoList.listName = Ldata.listName; | ||||
|       this.todoList.List = Ldata.List; | ||||
|     },(error) => { | ||||
|       console.log(error); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| ngOnDestroy() { | ||||
|   // Unsubscribe from all subscriptions to prevent memory leaks
 | ||||
|   console.log('TodoRunnerComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); | ||||
|   this.subscriptions.forEach(subscription => { | ||||
|     if (subscription && !subscription.closed) { | ||||
|       subscription.unsubscribe(); | ||||
|     } | ||||
|   }); | ||||
|   this.subscriptions = []; | ||||
|    | ||||
|   console.log('TodoRunnerComponent destroyed and cleaned up'); | ||||
| } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user