builder
This commit is contained in:
		
							parent
							
								
									cdcf1e07c7
								
							
						
					
					
						commit
						cdd752469c
					
				| @ -70,6 +70,41 @@ | |||||||
|     min-height: 24px; |     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 { |   .toggle-label { | ||||||
|     margin: 0; |     margin: 0; | ||||||
|     font-size: 12px; |     font-size: 12px; | ||||||
|  | |||||||
| @ -38,19 +38,6 @@ export class CompactFilterComponent implements OnInit, OnChanges { | |||||||
|   ) { } |   ) { } | ||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   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
 |     // Initialize configuration from inputs
 | ||||||
|     this.configFilterKey = this.filterKey; |     this.configFilterKey = this.filterKey; | ||||||
|     this.configFilterType = this.filterType; |     this.configFilterType = this.filterType; | ||||||
| @ -70,26 +57,53 @@ export class CompactFilterComponent implements OnInit, OnChanges { | |||||||
|      |      | ||||||
|     // Register this filter with the filter service
 |     // Register this filter with the filter service
 | ||||||
|     this.registerFilter(); |     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 { |   ngOnChanges(changes: SimpleChanges): void { | ||||||
|     // If filterKey or filterType changes, re-register the filter
 |     // If filterKey or filterType changes, re-register the filter
 | ||||||
|     if (changes.filterKey || changes.filterType) { |     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(); |       this.registerFilter(); | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     // Handle API URL changes
 | ||||||
|  |     if (changes.apiUrl && !changes.apiUrl.firstChange) { | ||||||
|  |       if (this.apiUrl) { | ||||||
|  |         this.loadAvailableKeys(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   // Register this filter with the filter service
 |   // Register this filter with the filter service
 | ||||||
|   registerFilter(): void { |   registerFilter(): void { | ||||||
|     if (this.filterKey) { |     if (this.filterKey) { | ||||||
|  |       // Get current filter values from the service
 | ||||||
|  |       const currentFilterValues = this.filterService.getFilterValues(); | ||||||
|  |        | ||||||
|       // Create a filter definition for this compact filter
 |       // Create a filter definition for this compact filter
 | ||||||
|       const filterDef: Filter = { |       const filterDef: Filter = { | ||||||
|         id: `compact-filter-${this.filterKey}`, |         id: `${this.filterKey}`, | ||||||
|         field: this.filterKey, |         field: this.filterKey, | ||||||
|         label: this.filterLabel || this.filterKey, |         label: this.filterLabel || this.filterKey, | ||||||
|         type: this.filterType as any, |         type: this.filterType as any, | ||||||
|         options: this.filterOptions, |         options: this.filterOptions, | ||||||
|         value: this.filterValue |         value: this.filterValue // Use the current filter value
 | ||||||
|       }; |       }; | ||||||
|        |        | ||||||
|       // Get current filters
 |       // Get current filters
 | ||||||
| @ -99,9 +113,32 @@ export class CompactFilterComponent implements OnInit, OnChanges { | |||||||
|       const existingFilterIndex = currentFilters.findIndex(f => f.id === filterDef.id); |       const existingFilterIndex = currentFilters.findIndex(f => f.id === filterDef.id); | ||||||
|        |        | ||||||
|       if (existingFilterIndex >= 0) { |       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
 |         // Update existing filter
 | ||||||
|         currentFilters[existingFilterIndex] = filterDef; |         currentFilters[existingFilterIndex] = filterDef; | ||||||
|       } else { |       } 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
 |         // Add new filter
 | ||||||
|         currentFilters.push(filterDef); |         currentFilters.push(filterDef); | ||||||
|       } |       } | ||||||
| @ -118,9 +155,24 @@ export class CompactFilterComponent implements OnInit, OnChanges { | |||||||
|     if (this.filterKey && this.availableFilters.length > 0) { |     if (this.filterKey && this.availableFilters.length > 0) { | ||||||
|       this.selectedFilter = this.availableFilters.find(f => f.field === this.filterKey) || null; |       this.selectedFilter = this.availableFilters.find(f => f.field === this.filterKey) || null; | ||||||
|       if (this.selectedFilter) { |       if (this.selectedFilter) { | ||||||
|         // Get current value for this filter
 |         // Get current value for this filter from the service
 | ||||||
|         const currentState = this.filterService.getFilterValues(); |         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.filterValue = value; | ||||||
|       this.filterService.updateFilterValue(this.selectedFilter.id, value); |       this.filterService.updateFilterValue(this.selectedFilter.id, value); | ||||||
|       this.filterChange.emit({ filterId: this.selectedFilter.id, value: 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 { |   toggleConfigMode(): void { | ||||||
|     this.isConfigMode = !this.isConfigMode; |     this.isConfigMode = !this.isConfigMode; | ||||||
|     if (this.isConfigMode) { |     if (this.isConfigMode) { | ||||||
|       // Initialize config values
 |       // Initialize config values from current filter if available
 | ||||||
|       this.configFilterKey = this.filterKey; |       if (this.selectedFilter) { | ||||||
|       this.configFilterType = this.filterType; |         this.configFilterKey = this.selectedFilter.field; | ||||||
|       this.configFilterLabel = this.filterLabel; |         this.configFilterType = this.selectedFilter.type; | ||||||
|       this.configFilterOptions = this.filterOptions.join(','); |         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.configApiUrl = this.apiUrl; | ||||||
|       this.configConnectionId = this.connectionId; |       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
 |     // Load available values for the selected key if it's a dropdown or multiselect
 | ||||||
|     if ((this.filterType === 'dropdown' || this.filterType === 'multiselect') && this.filterKey) { |     if ((this.configFilterType === 'dropdown' || this.configFilterType === 'multiselect') && this.configFilterKey) { | ||||||
|       this.loadAvailableValues(this.filterKey); |       this.loadAvailableValues(this.configFilterKey); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Register the updated filter with the filter service
 |     // 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"> |           <input id="chartparameter" type="text" formControlName="chartparameter" class="clr-input" [(ngModel)]="gadgetsEditdata.chartparameter"> | ||||||
|         </div> |         </div> | ||||||
|       </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"> |       <div class="modal-footer"> | ||||||
|         <button type="button" class="btn btn-outline" (click)="modeledit = false">Cancel</button> |         <button type="button" class="btn btn-outline" (click)="modeledit = false">Cancel</button> | ||||||
|         <button type="button" class="btn btn-primary" (click)="applyChanges(modelid)">Apply</button> |         <button type="button" class="btn btn-primary" (click)="applyChanges(modelid)">Apply</button> | ||||||
|  | |||||||
| @ -47,6 +47,12 @@ export class EditnewdashComponent implements OnInit { | |||||||
|   commonFilterModalOpen: boolean = false; // Add common filter modal state
 |   commonFilterModalOpen: boolean = false; // Add common filter modal state
 | ||||||
|   public entryForm: FormGroup; |   public entryForm: FormGroup; | ||||||
|   public commonFilterForm: FormGroup; // Add common filter form
 |   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[] = [ |   WidgetsMock: WidgetModel[] = [ | ||||||
|     { |     { | ||||||
| @ -360,6 +366,16 @@ export class EditnewdashComponent implements OnInit { | |||||||
|           dashboard.component = component.componentInstance; |           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; |           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)
 |   // 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) {  |     if (item['filterOptions'] === undefined) {  | ||||||
|       this.gadgetsEditdata['filterOptions'] = [];  |       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(); |     this.getStores(); | ||||||
|      |      | ||||||
|     // Set default connection if none is set and we have connections
 |     // 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
 |     //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 => { |     let cmp = this.dashboardCollection.dashboard.forEach(dashboard => { | ||||||
|       this.componentCollection.forEach(component => { |       this.componentCollection.forEach(component => { | ||||||
|         if (dashboard.name === component.name) { |         if (dashboard.name === component.name) { | ||||||
| @ -719,8 +760,6 @@ export class EditnewdashComponent implements OnInit { | |||||||
|     //console.log(merged);
 |     //console.log(merged);
 | ||||||
|     console.log("temp data", typeof tmp); |     console.log("temp data", typeof tmp); | ||||||
|     console.log(tmp); |     console.log(tmp); | ||||||
|     let parsed = JSON.parse(tmp); |  | ||||||
|     this.serialize(parsed.dashboard); |  | ||||||
|     this.dashbord1_Line.model = tmp; |     this.dashbord1_Line.model = tmp; | ||||||
| 
 | 
 | ||||||
|     // let obj = this.dashboardCollection;
 |     // let obj = this.dashboardCollection;
 | ||||||
| @ -777,11 +816,18 @@ export class EditnewdashComponent implements OnInit { | |||||||
|         xyz.commonFilterEnabled = this.gadgetsEditdata.commonFilterEnabled; // Add common filter property
 |         xyz.commonFilterEnabled = this.gadgetsEditdata.commonFilterEnabled; // Add common filter property
 | ||||||
|          |          | ||||||
|         // For compact filter, preserve filter configuration properties
 |         // 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.filterKey = this.gadgetsEditdata.filterKey || ''; | ||||||
|           xyz.filterType = this.gadgetsEditdata.filterType || 'text'; |           xyz.filterType = this.gadgetsEditdata.filterType || 'text'; | ||||||
|           xyz.filterLabel = this.gadgetsEditdata.filterLabel || ''; |           xyz.filterLabel = this.gadgetsEditdata.filterLabel || ''; | ||||||
|           xyz.filterOptions = this.gadgetsEditdata.filterOptions || []; |           // 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); |         console.log(xyz); | ||||||
| @ -818,7 +864,97 @@ export class EditnewdashComponent implements OnInit { | |||||||
|    * This prevents errors when trying to set properties that don't exist on the components |    * This prevents errors when trying to set properties that don't exist on the components | ||||||
|    */ |    */ | ||||||
|   getChartInputs(item: any): any { |   getChartInputs(item: any): any { | ||||||
|     // Only pass properties that are relevant to chart components
 |     // 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, | ||||||
|  |         datastore: item.datastore, | ||||||
|  |         charttitle: item.charttitle, | ||||||
|  |         chartlegend: item.chartlegend, | ||||||
|  |         showlabel: item.showlabel, | ||||||
|  |         chartcolor: item.chartcolor, | ||||||
|  |         slices: item.slices, | ||||||
|  |         donut: item.donut, | ||||||
|  |         charturl: item.charturl, | ||||||
|  |         chartparameter: item.chartparameter, | ||||||
|  |         datasource: item.datasource, | ||||||
|  |         fieldName: item.name, // Using item.name as fieldName
 | ||||||
|  |         connection: item['connection'], // Add connection field using bracket notation
 | ||||||
|  |         // Base drilldown configuration properties
 | ||||||
|  |         drilldownEnabled: item['drilldownEnabled'], | ||||||
|  |         drilldownApiUrl: item['drilldownApiUrl'], | ||||||
|  |         // Removed drilldownParameterKey since we're using URL templates
 | ||||||
|  |         drilldownXAxis: item['drilldownXAxis'], | ||||||
|  |         drilldownYAxis: item['drilldownYAxis'], | ||||||
|  |         drilldownParameter: item['drilldownParameter'], // Add drilldown parameter
 | ||||||
|  |         baseFilters: item['baseFilters'] || [], // Add base filters
 | ||||||
|  |         drilldownFilters: item['drilldownFilters'] || [], // Add drilldown filters
 | ||||||
|  |         // Multi-layer drilldown configurations
 | ||||||
|  |         drilldownLayers: item['drilldownLayers'] || [] | ||||||
|  |       }; | ||||||
|  |        | ||||||
|  |       // Remove undefined properties to avoid passing unnecessary data
 | ||||||
|  |       Object.keys(gridInputs).forEach(key => { | ||||||
|  |         if (gridInputs[key] === undefined) { | ||||||
|  |           delete gridInputs[key]; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |        | ||||||
|  |       return gridInputs; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // For all other chart components, pass chart-specific properties
 | ||||||
|     const chartInputs = { |     const chartInputs = { | ||||||
|       xAxis: item.xAxis, |       xAxis: item.xAxis, | ||||||
|       yAxis: item.yAxis, |       yAxis: item.yAxis, | ||||||
| @ -848,25 +984,6 @@ export class EditnewdashComponent implements OnInit { | |||||||
|       drilldownLayers: item['drilldownLayers'] || [] |       drilldownLayers: item['drilldownLayers'] || [] | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     // For CommonFilterComponent, also pass baseFilters, drilldownFilters, drilldownLayers, fieldName, and connection
 |  | ||||||
|     if (item.component && item.component.name === 'CommonFilterComponent') { |  | ||||||
|       chartInputs['baseFilters'] = item['baseFilters'] || []; |  | ||||||
|       chartInputs['drilldownFilters'] = item['drilldownFilters'] || []; |  | ||||||
|       chartInputs['drilldownLayers'] = item['drilldownLayers'] || []; |  | ||||||
|       chartInputs['fieldName'] = item['name'] || ''; |  | ||||||
|       chartInputs['connection'] = item['connection'] || undefined; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     // For CompactFilterComponent, pass filter configuration properties
 |  | ||||||
|     if (item.component && item.component.name === 'CompactFilterComponent') { |  | ||||||
|       chartInputs['filterKey'] = item['filterKey'] || ''; |  | ||||||
|       chartInputs['filterType'] = item['filterType'] || 'text'; |  | ||||||
|       chartInputs['filterLabel'] = item['filterLabel'] || ''; |  | ||||||
|       chartInputs['filterOptions'] = item['filterOptions'] || []; |  | ||||||
|       chartInputs['apiUrl'] = item['table'] || ''; // Use table as API URL
 |  | ||||||
|       chartInputs['connectionId'] = item['connection'] ? parseInt(item['connection'], 10) : undefined; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     // Remove undefined properties to avoid passing unnecessary data
 |     // Remove undefined properties to avoid passing unnecessary data
 | ||||||
|     Object.keys(chartInputs).forEach(key => { |     Object.keys(chartInputs).forEach(key => { | ||||||
|       if (chartInputs[key] === undefined) { |       if (chartInputs[key] === undefined) { | ||||||
| @ -922,13 +1039,24 @@ export class EditnewdashComponent implements OnInit { | |||||||
|         updatedItem.commonFilterEnabledDrilldown = this.gadgetsEditdata.commonFilterEnabledDrilldown; // Add drilldown common filter property
 |         updatedItem.commonFilterEnabledDrilldown = this.gadgetsEditdata.commonFilterEnabledDrilldown; // Add drilldown common filter property
 | ||||||
|          |          | ||||||
|         // For compact filter, preserve filter configuration properties
 |         // 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.filterKey = this.gadgetsEditdata.filterKey || ''; | ||||||
|           updatedItem.filterType = this.gadgetsEditdata.filterType || 'text'; |           updatedItem.filterType = this.gadgetsEditdata.filterType || 'text'; | ||||||
|           updatedItem.filterLabel = this.gadgetsEditdata.filterLabel || ''; |           updatedItem.filterLabel = this.gadgetsEditdata.filterLabel || ''; | ||||||
|           updatedItem.filterOptions = this.gadgetsEditdata.filterOptions || []; |           // 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.table = this.gadgetsEditdata.table || ''; // API URL
 | ||||||
|           updatedItem.connection = this.gadgetsEditdata.connection || undefined; // Connection ID
 |           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); |         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
 |       // 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,50 +1,32 @@ | |||||||
| <div class="chart-container"> | <div style="display: block; height: 100%; width: 100%;"> | ||||||
|   <!-- Compact Filters --> |   <!-- No filter controls needed with the new simplified approach --> | ||||||
|   <div class="compact-filters-container" *ngIf="baseFilters && baseFilters.length > 0"> |   <!-- Filters are now configured at the drilldown level --> | ||||||
|     <app-compact-filter  |  | ||||||
|       *ngFor="let filter of baseFilters"  |  | ||||||
|       [filterKey]="filter.field" |  | ||||||
|       (filterChange)="onFilterChange($event)"> |  | ||||||
|     </app-compact-filter> |  | ||||||
|   </div> |  | ||||||
|    |    | ||||||
|   <!-- Drilldown mode indicator --> |   <!-- Drilldown mode indicator --> | ||||||
|   <div *ngIf="currentDrilldownLevel > 0" class="drilldown-indicator"> |   <div *ngIf="currentDrilldownLevel > 0" style="background-color: #e0e0e0; padding: 5px; margin-bottom: 10px; border-radius: 4px; text-align: center;"> | ||||||
|     <span class="drilldown-text">Drilldown Level: {{currentDrilldownLevel}}</span> |     <span style="font-weight: bold; color: #333;">Drilldown Level: {{currentDrilldownLevel}}</span> | ||||||
|     <button class="btn btn-secondary btn-sm" (click)="navigateBack()"> |     <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}} |       Back to Level {{currentDrilldownLevel - 1}} | ||||||
|     </button> |     </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 |       Back to Main View | ||||||
|     </button> |     </button> | ||||||
|   </div> |   </div> | ||||||
|    |    | ||||||
|   <div class="chart-header"> |   <!-- No data message --> | ||||||
|     <h3>{{ charttitle || 'Bar Chart' }}</h3> |   <div *ngIf="noDataAvailable" style="text-align: center; padding: 20px; color: #666; font-style: italic;"> | ||||||
|  |     No data available | ||||||
|   </div> |   </div> | ||||||
|    |    | ||||||
|   <div class="chart-wrapper"> |   <!-- Chart display --> | ||||||
|     <div class="chart-content" [class.loading]="barChartData.length === 0 && barChartLabels.length === 0 && !noDataAvailable"> |   <div *ngIf="!noDataAvailable" style="position: relative; height: calc(100% - 50px);"> | ||||||
|       <!-- No data message --> |     <canvas baseChart | ||||||
|       <div class="no-data-message" *ngIf="noDataAvailable"> |       [datasets]="barChartData" | ||||||
|         <p>No data available</p> |       [labels]="barChartLabels" | ||||||
|       </div> |       [type]="barChartType" | ||||||
|        |       [options]="barChartOptions" | ||||||
|       <!-- Chart display --> |       (chartHover)="chartHovered($event)" | ||||||
|       <canvas baseChart |       (chartClick)="chartClicked($event)"> | ||||||
|         *ngIf="!noDataAvailable" |     </canvas> | ||||||
|         [datasets]="barChartData" |  | ||||||
|         [labels]="barChartLabels" |  | ||||||
|         [type]="barChartType" |  | ||||||
|         [options]="barChartOptions" |  | ||||||
|         (chartHover)="chartHovered($event)" |  | ||||||
|         (chartClick)="chartClicked($event)"> |  | ||||||
|       </canvas> |  | ||||||
|        |  | ||||||
|       <!-- Loading overlay --> |  | ||||||
|       <div class="loading-overlay" *ngIf="barChartData.length === 0 && barChartLabels.length === 0 && !noDataAvailable"> |  | ||||||
|         <div class="shimmer-bar"></div> |  | ||||||
|       </div> |  | ||||||
|     </div> |  | ||||||
|   </div> |   </div> | ||||||
| </div> | </div> | ||||||
| @ -5,214 +5,27 @@ | |||||||
|   width: 100%; |   width: 100%; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .chart-container { | .bar-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; |  | ||||||
|   position: relative; |   position: relative; | ||||||
|    |   height: 100%; | ||||||
|   .chart-content { |   width: 100%; | ||||||
|     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 { | canvas { | ||||||
|   0% { |   display: block; | ||||||
|     background-position: -200% 0; |   max-width: 100%; | ||||||
|   } |   max-height: 100%; | ||||||
|   100% { |  | ||||||
|     background-position: 200% 0; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Responsive design for chart container | // Responsive design for chart container | ||||||
| @media (max-width: 768px) { | @media (max-width: 768px) { | ||||||
|   .chart-container { |   .bar-chart-container { | ||||||
|     padding: 15px; |     height: 300px; | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .chart-header h3 { |  | ||||||
|     font-size: 18px; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .drilldown-indicator { |  | ||||||
|     flex-direction: column; |  | ||||||
|     gap: 5px; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .drilldown-text { |  | ||||||
|     font-size: 14px; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .compact-filters-container { |  | ||||||
|     flex-wrap: wrap; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @media (max-width: 480px) { | @media (max-width: 480px) { | ||||||
|   .chart-container { |   .bar-chart-container { | ||||||
|     padding: 10px; |     height: 250px; | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .chart-header h3 { |  | ||||||
|     font-size: 16px; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -140,12 +140,6 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|       console.log('Chart legend changed to:', this.barChartLegend); |       console.log('Chart legend changed to:', this.barChartLegend); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|    |  | ||||||
|   // 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 { |   fetchChartData(): void { | ||||||
|     // Set flag to prevent recursive calls
 |     // Set flag to prevent recursive calls
 | ||||||
| @ -179,73 +173,41 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|       console.log('Bar chart data URL:', url); |       console.log('Bar chart data URL:', url); | ||||||
|        |        | ||||||
|       // Convert baseFilters to filter parameters
 |       // Convert baseFilters to filter parameters
 | ||||||
|       let filterParams = ''; |       const filterObj = {}; | ||||||
|  |        | ||||||
|  |       // Add base filters
 | ||||||
|       if (this.baseFilters && this.baseFilters.length > 0) { |       if (this.baseFilters && this.baseFilters.length > 0) { | ||||||
|         const filterObj = {}; |  | ||||||
|         this.baseFilters.forEach(filter => { |         this.baseFilters.forEach(filter => { | ||||||
|           if (filter.field && filter.value) { |           if (filter.field && filter.value) { | ||||||
|             filterObj[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 commonFilters = this.filterService.getFilterValues(); | ||||||
|       const filterDefinitions = this.filterService.getFilters(); |       const filterDefinitions = this.filterService.getFilters(); | ||||||
|       console.log('Common filters from service:', commonFilters); |       Object.keys(commonFilters).forEach(filterId => { | ||||||
|       console.log('Filter definitions:', filterDefinitions); |         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(commonFilters).length > 0) { |       // Convert to JSON string for API call
 | ||||||
|         // Merge common filters with base filters
 |       let filterParams = ''; | ||||||
|         const mergedFilterObj = {}; |       if (Object.keys(filterObj).length > 0) { | ||||||
|          |         filterParams = JSON.stringify(filterObj); | ||||||
|         // 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); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|          |  | ||||||
|         if (Object.keys(mergedFilterObj).length > 0) { |  | ||||||
|           filterParams = JSON.stringify(mergedFilterObj); |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|        |        | ||||||
|       console.log('Final merged filter object:', filterParams); |       console.log('Final filter object:', filterObj); | ||||||
|       // Fetch data from the dashboard service with parameter field and value
 |       // 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
 |       // 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( |       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); |     console.log('Drilldown data URL:', url); | ||||||
|      |      | ||||||
|     // Convert drilldown layer filters to filter parameters (if applicable)
 |     // Convert drilldown layer filters to filter parameters (if applicable)
 | ||||||
|     let filterParams = ''; |     const filterObj = {}; | ||||||
|  |      | ||||||
|  |     // Add drilldown layer filters
 | ||||||
|     if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { |     if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { | ||||||
|       const filterObj = {}; |  | ||||||
|       drilldownConfig.filters.forEach((filter: any) => { |       drilldownConfig.filters.forEach((filter: any) => { | ||||||
|         if (filter.field && filter.value) { |         if (filter.field && filter.value) { | ||||||
|           filterObj[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
 |     // Add drilldownFilters
 | ||||||
|     let drilldownFilterParams = ''; |  | ||||||
|     if (this.drilldownFilters && this.drilldownFilters.length > 0) { |     if (this.drilldownFilters && this.drilldownFilters.length > 0) { | ||||||
|       const filterObj = {}; |  | ||||||
|       this.drilldownFilters.forEach(filter => { |       this.drilldownFilters.forEach(filter => { | ||||||
|         if (filter.field && filter.value) { |         if (filter.field && filter.value) { | ||||||
|           filterObj[filter.field] = filter.value; |           filterObj[filter.field] = filter.value; | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|       if (Object.keys(filterObj).length > 0) { |  | ||||||
|         drilldownFilterParams = JSON.stringify(filterObj); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Add common filters to drilldown filter parameters
 |     // Add common filters
 | ||||||
|     const commonFilters = this.filterService.getFilterValues(); |     const commonFilters = this.filterService.getFilterValues(); | ||||||
|     if (Object.keys(commonFilters).length > 0) { |     const filterDefinitions = this.filterService.getFilters(); | ||||||
|       // Merge common filters with drilldown filters
 |     Object.keys(commonFilters).forEach(filterId => { | ||||||
|       const mergedFilterObj = {}; |       const filterValue = commonFilters[filterId]; | ||||||
|        |        | ||||||
|       // Add drilldown filters first
 |       // Find the filter definition to get the field name
 | ||||||
|       if (drilldownFilterParams) { |       const filterDef = this.filterService.getFilters().find(f => f.id === filterId); | ||||||
|         try { |        | ||||||
|           const drilldownFilterObj = JSON.parse(drilldownFilterParams); |       if (filterDef && filterDef.field) { | ||||||
|           Object.assign(mergedFilterObj, drilldownFilterObj); |         const fieldName = filterDef.field; | ||||||
|         } catch (e) { |         if (filterValue !== undefined && filterValue !== null && filterValue !== '') { | ||||||
|           console.warn('Failed to parse drilldown filter parameters:', e); |           filterObj[fieldName] = filterValue; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|        |     }); | ||||||
|       // Add common filters
 |      | ||||||
|       Object.keys(commonFilters).forEach(key => { |     // Convert to JSON string for API call
 | ||||||
|         const value = commonFilters[key]; |     let drilldownFilterParams = ''; | ||||||
|         if (value !== undefined && value !== null && value !== '') { |     if (Object.keys(filterObj).length > 0) { | ||||||
|           mergedFilterObj[key] = value; |       drilldownFilterParams = JSON.stringify(filterObj); | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|        |  | ||||||
|       if (Object.keys(mergedFilterObj).length > 0) { |  | ||||||
|         drilldownFilterParams = JSON.stringify(mergedFilterObj); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     console.log('Drilldown filter parameters:', drilldownFilterParams); |     console.log('Drilldown filter parameters:', drilldownFilterParams); | ||||||
|  | |||||||
| @ -4,9 +4,41 @@ | |||||||
|       <div class="clr-col-8"> |       <div class="clr-col-8"> | ||||||
|         <h3>{{charttitle || 'Data Grid'}}</h3> |         <h3>{{charttitle || 'Data Grid'}}</h3> | ||||||
|       </div> |       </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> |     </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-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> |         <div *ngIf="error;else loadingSpinner">{{error}}</div> | ||||||
|       </clr-dg-placeholder> |       </clr-dg-placeholder> | ||||||
| 
 | 
 | ||||||
| @ -19,7 +51,7 @@ | |||||||
| 
 | 
 | ||||||
|       <clr-dg-row *clrDgItems="let item of givendata" [clrDgItem]="item"> |       <clr-dg-row *clrDgItems="let item of givendata" [clrDgItem]="item"> | ||||||
|         <!-- Dynamic cells based on response keys --> |         <!-- 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]}} |           {{item[header.key]}} | ||||||
|         </clr-dg-cell> |         </clr-dg-cell> | ||||||
|       </clr-dg-row> |       </clr-dg-row> | ||||||
|  | |||||||
| @ -1,12 +1,28 @@ | |||||||
| @import '../../../../../../../styles1.scss'; | // Add styles for drilldown navigation | ||||||
| input.ng-invalid.ng-touched { | .alert-info { | ||||||
|   border-color: red; |   background-color: #dcedf7; | ||||||
|  |   border-color: #a3d4f5; | ||||||
|  |   color: #21333b; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .error_mess { | .alert-info .alert-icon { | ||||||
|   color: red; |   color: #0072a3; | ||||||
| } | } | ||||||
| clr-datagrid{ | 
 | ||||||
|     height: 400px; /* Adjust the height as needed */ | .btn-link { | ||||||
|   overflow-y: auto; |   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 { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service'; | ||||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.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({ | @Component({ | ||||||
|   selector: 'app-grid-view', |   selector: 'app-grid-view', | ||||||
|   templateUrl: './grid-view.component.html', |   templateUrl: './grid-view.component.html', | ||||||
|   styleUrls: ['./grid-view.component.scss'] |   styleUrls: ['./grid-view.component.scss'] | ||||||
| }) | }) | ||||||
| export class GridViewComponent implements OnInit, OnChanges { | export class GridViewComponent implements OnInit, OnChanges, OnDestroy { | ||||||
|   @Input() xAxis: string; |   @Input() xAxis: string; | ||||||
|   @Input() yAxis: string | string[]; |   @Input() yAxis: string | string[]; | ||||||
|   @Input() table: string; |   @Input() table: string; | ||||||
| @ -23,6 +27,16 @@ export class GridViewComponent implements OnInit, OnChanges { | |||||||
|   @Input() datasource: string; |   @Input() datasource: string; | ||||||
|   @Input() fieldName: string; |   @Input() fieldName: string; | ||||||
|   @Input() connection: number; // Add connection input
 |   @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; |   loading = false; | ||||||
|   givendata: any[] = []; |   givendata: any[] = []; | ||||||
| @ -38,13 +52,37 @@ export class GridViewComponent implements OnInit, OnChanges { | |||||||
|   submitted = false; |   submitted = false; | ||||||
|   dynamicHeaders: any[] = []; |   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 mainservice: UsergrpmaintainceService, | ||||||
|     private dashboardService: Dashboard3Service, |     private dashboardService: Dashboard3Service, | ||||||
|  |     // Add FilterService to constructor
 | ||||||
|  |     private filterService: FilterService | ||||||
|   ) { } |   ) { } | ||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   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(); |     this.fetchGridData(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -55,87 +93,501 @@ export class GridViewComponent implements OnInit, OnChanges { | |||||||
|     const xAxisChanged = changes.xAxis && !changes.xAxis.firstChange; |     const xAxisChanged = changes.xAxis && !changes.xAxis.firstChange; | ||||||
|     const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; |     const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; | ||||||
|     const tableChanged = changes.table && !changes.table.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
 |     // Respond to input changes
 | ||||||
|     if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged) { |     if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || | ||||||
|       console.log('X or Y axis or table or connection changed, fetching new data'); |       drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || | ||||||
|       // Only fetch data if xAxis, yAxis, table, or connection has changed (and it's not the first change)
 |       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(); |       this.fetchGridData(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Dynamic headers for the grid
 |   // Dynamic headers for the grid
 | ||||||
|    | 
 | ||||||
|   fetchGridData(): void { |   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 we have the necessary data, fetch grid data from the service
 | ||||||
|     if (this.table && this.xAxis) { |     // 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) { | ||||||
|  | 
 | ||||||
|  |       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
 |       // Convert yAxis to string if it's an array
 | ||||||
|       const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; |       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
 |       // 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) => { |         (data: any) => { | ||||||
|  |           console.log('=== GRID VIEW DATA RESPONSE ==='); | ||||||
|           console.log('Received grid data:', data); |           console.log('Received grid data:', data); | ||||||
|           if (data === null) { |           if (data === null) { | ||||||
|             console.warn('Grid API returned null data. Check if the API endpoint is working correctly.'); |             console.warn('Grid API returned null data. Check if the API endpoint is working correctly.'); | ||||||
|             this.error = "No data Available"; |             this.error = "No data Available"; | ||||||
|  |             this.noDataAvailable = true; | ||||||
|  |             // Reset flag after fetching
 | ||||||
|  |             this.isFetchingData = false; | ||||||
|             return; |             return; | ||||||
|           } |           } | ||||||
|            | 
 | ||||||
|           // Handle the actual data structure returned by the API
 |           // Handle the actual data structure returned by the API
 | ||||||
|           if (data && data.chartData) { |           if (data && data.chartData) { | ||||||
|             this.givendata = data.chartData; |             this.givendata = data.chartData; | ||||||
|             this.extractDynamicHeaders(data.chartData); |             this.extractDynamicHeaders(data.chartData); | ||||||
|             this.error = this.givendata.length === 0 ? "No data Available" : undefined; |             this.error = this.givendata.length === 0 ? "No data Available" : undefined; | ||||||
|  |             this.noDataAvailable = this.givendata.length === 0; | ||||||
|             console.log('Updated grid with data:', this.givendata); |             console.log('Updated grid with data:', this.givendata); | ||||||
|           } else if (data && data.data) { |           } else if (data && data.data) { | ||||||
|             // Handle the original expected format as fallback
 |             // Handle the original expected format as fallback
 | ||||||
|             this.givendata = data.data; |             this.givendata = data.data; | ||||||
|             this.extractDynamicHeaders(data.data); |             this.extractDynamicHeaders(data.data); | ||||||
|             this.error = this.givendata.length === 0 ? "No data Available" : undefined; |             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); |             console.log('Updated grid with legacy data format:', this.givendata); | ||||||
|           } else if (Array.isArray(data)) { |           } else if (Array.isArray(data)) { | ||||||
|             // Handle case where data is directly an array
 |             // Handle case where data is directly an array
 | ||||||
|             this.givendata = data; |             this.givendata = data; | ||||||
|             this.extractDynamicHeaders(data); |             this.extractDynamicHeaders(data); | ||||||
|             this.error = this.givendata.length === 0 ? "No data Available" : undefined; |             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); |             console.log('Updated grid with array data:', this.givendata); | ||||||
|           } else { |           } else { | ||||||
|             console.warn('Grid received data does not have expected structure', data); |             console.warn('Grid received data does not have expected structure', data); | ||||||
|             this.error = "No valid data received"; |             this.error = "No valid data received"; | ||||||
|             this.givendata = []; |             this.givendata = []; | ||||||
|  |             this.noDataAvailable = true; | ||||||
|           } |           } | ||||||
|  |           // Reset flag after fetching
 | ||||||
|  |           this.isFetchingData = false; | ||||||
|         }, (error) => { |         }, (error) => { | ||||||
|           console.log('Error fetching grid data:', error); |           console.log('Error fetching grid data:', error); | ||||||
|           this.error = "Server Error"; |           this.error = "Server Error"; | ||||||
|  |           this.noDataAvailable = true; | ||||||
|  |           // Reset flag after fetching
 | ||||||
|  |           this.isFetchingData = false; | ||||||
|         }); |         }); | ||||||
|     } else if (this.table) { |     } else if (this.table) { | ||||||
|       console.log('Missing xAxis, falling back to default data fetching'); |       console.log('Missing xAxis, falling back to default data fetching'); | ||||||
|       // Fallback to default data fetching when only table is provided
 |       // Fallback to default data fetching when only table is provided
 | ||||||
|        // Convert yAxis to string if it's an array
 |       // Convert yAxis to string if it's an array
 | ||||||
|       const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; |       const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; | ||||||
| 
 | 
 | ||||||
|       // Fetch data from the dashboard service, similar to other chart components
 |       // 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).subscribe( | ||||||
|         (data: any) => { |         (data: any) => { | ||||||
|       // this.mainservice.getAll().subscribe((data: any) => {
 |           // this.mainservice.getAll().subscribe((data: any) => {
 | ||||||
|         console.log('recv data ', data); |           console.log('recv data ', data); | ||||||
|         this.givendata = Array.isArray(data) ? data : []; |           this.givendata = Array.isArray(data) ? data : []; | ||||||
|         this.extractDynamicHeaders(data); |           this.extractDynamicHeaders(data); | ||||||
|         this.error = this.givendata && this.givendata.length === 0 ? "No data Available" : undefined; |           this.error = this.givendata && this.givendata.length === 0 ? "No data Available" : undefined; | ||||||
|       }, (error) => { |           this.noDataAvailable = this.givendata && this.givendata.length === 0; | ||||||
|         console.log(error); |           // Reset flag after fetching
 | ||||||
|         this.error = "Server Error"; |           this.isFetchingData = false; | ||||||
|       }); |         }, (error) => { | ||||||
|  |           console.log(error); | ||||||
|  |           this.error = "Server Error"; | ||||||
|  |           this.noDataAvailable = true; | ||||||
|  |           // Reset flag after fetching
 | ||||||
|  |           this.isFetchingData = false; | ||||||
|  |         }); | ||||||
|     } else { |     } else { | ||||||
|       console.log('Missing required data for grid:', { table: this.table }); |       console.log('Missing required data for grid:', { table: this.table }); | ||||||
|       this.error = "Table name is required"; |       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'); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * Extract dynamic headers from the data |    * Extract dynamic headers from the data | ||||||
|    * @param data Array of data objects |    * @param data Array of data objects | ||||||
| @ -143,7 +595,7 @@ export class GridViewComponent implements OnInit, OnChanges { | |||||||
|   private extractDynamicHeaders(data: any): void { |   private extractDynamicHeaders(data: any): void { | ||||||
|     // Ensure data is an array
 |     // Ensure data is an array
 | ||||||
|     const dataArray = Array.isArray(data) ? data : []; |     const dataArray = Array.isArray(data) ? data : []; | ||||||
|      | 
 | ||||||
|     if (dataArray && dataArray.length > 0) { |     if (dataArray && dataArray.length > 0) { | ||||||
|       // Get all unique keys from the data objects
 |       // Get all unique keys from the data objects
 | ||||||
|       const allKeys = new Set<string>(); |       const allKeys = new Set<string>(); | ||||||
| @ -152,19 +604,19 @@ export class GridViewComponent implements OnInit, OnChanges { | |||||||
|           Object.keys(item).forEach(key => allKeys.add(key)); |           Object.keys(item).forEach(key => allKeys.add(key)); | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|        | 
 | ||||||
|       // Convert to array of header objects with key and display name
 |       // Convert to array of header objects with key and display name
 | ||||||
|       this.dynamicHeaders = Array.from(allKeys).map(key => ({ |       this.dynamicHeaders = Array.from(allKeys).map(key => ({ | ||||||
|         key: key, |         key: key, | ||||||
|         displayName: this.formatHeader(key) |         displayName: this.formatHeader(key) | ||||||
|       })); |       })); | ||||||
|        | 
 | ||||||
|       console.log('Dynamic headers extracted:', this.dynamicHeaders); |       console.log('Dynamic headers extracted:', this.dynamicHeaders); | ||||||
|     } else { |     } else { | ||||||
|       this.dynamicHeaders = []; |       this.dynamicHeaders = []; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|    | 
 | ||||||
|   /** |   /** | ||||||
|    * Format header name for better display |    * Format header name for better display | ||||||
|    * @param key The key to format |    * @param key The key to format | ||||||
| @ -175,4 +627,23 @@ export class GridViewComponent implements OnInit, OnChanges { | |||||||
|       .replace(/([A-Z])/g, ' $1') |       .replace(/([A-Z])/g, ' $1') | ||||||
|       .replace(/^./, str => str.toUpperCase()); |       .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> |           <h3>{{ 'all_dashboard' | translate }}</h3> | ||||||
|         </div> |         </div> | ||||||
|         <div class="clr-col-4" style="text-align: right;"> |         <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()"> |           <button id="add" class="btn btn-primary"  (click)="gotoadd()"> | ||||||
|             <clr-icon shape="plus"></clr-icon>{{ 'dashboard_builder' | translate }} |             <clr-icon shape="plus"></clr-icon>{{ 'dashboard_builder' | translate }} | ||||||
|           </button> |           </button> | ||||||
| @ -111,7 +114,4 @@ | |||||||
|           <button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary" >{{'DELETE' |translate}}</button> |           <button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary" >{{'DELETE' |translate}}</button> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </clr-modal> |     </clr-modal> | ||||||
|     |  | ||||||
|    |  | ||||||
|    |  | ||||||
| @ -3,6 +3,10 @@ import { DashrunnerService } from '../dashrunner.service'; | |||||||
| import { ActivatedRoute, Router } from '@angular/router'; | import { ActivatedRoute, Router } from '@angular/router'; | ||||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | 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({ | @Component({ | ||||||
|   selector: 'app-bar-runner', |   selector: 'app-bar-runner', | ||||||
| @ -24,8 +28,19 @@ export class BarRunnerComponent implements OnInit { | |||||||
|   JsonData; |   JsonData; | ||||||
| 
 | 
 | ||||||
|   barData; |   barData; | ||||||
|   constructor(private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, |   ConnectionId: number; // Add ConnectionId property
 | ||||||
|     private router : Router,) { } |    | ||||||
|  |   // 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[] = []; |   barChartLabels: any[] = []; | ||||||
|   barChartType: string = 'bar'; |   barChartType: string = 'bar'; | ||||||
| @ -47,6 +62,13 @@ export class BarRunnerComponent implements OnInit { | |||||||
|       this.editId = this.route.snapshot.params.id; |       this.editId = this.route.snapshot.params.id; | ||||||
|       console.log(this.editId); |       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)=>{ |       this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|         console.log(data); |         console.log(data); | ||||||
| @ -74,22 +96,62 @@ export class BarRunnerComponent implements OnInit { | |||||||
|             this.YAxis = ChartObject[i].yAxis; |             this.YAxis = ChartObject[i].yAxis; | ||||||
|             this.showlabel = ChartObject[i].showlabel; |             this.showlabel = ChartObject[i].showlabel; | ||||||
|             this.barChartLegend = ChartObject[i].chartlegend; |             this.barChartLegend = ChartObject[i].chartlegend; | ||||||
|  |             this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|             console.log(this.TableName); |             console.log(this.TableName); | ||||||
|             this.Dashtestservive.getChartData(this.TableName,"Bar Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { |             // Fetch data with filters
 | ||||||
|               console.log(Ldata); |             this.fetchChartData(); | ||||||
|               this.JsonData = Ldata; |  | ||||||
|               this.barChartData = this.JsonData.barChartData; |  | ||||||
|               this.barChartLabels = this.JsonData.barChartLabels; |  | ||||||
|                |  | ||||||
|              },(error) => { |  | ||||||
|               console.log(error); |  | ||||||
|              }); |  | ||||||
|             break; // No need to continue the loop once the correct placeholder is found
 |             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(){ |   generatePDFFile(){ | ||||||
|     this.buttonClicked.emit(); |     this.buttonClicked.emit(); | ||||||
|     const content = this.contentContainerRef.nativeElement; |     const content = this.contentContainerRef.nativeElement; | ||||||
| @ -97,5 +159,17 @@ export class BarRunnerComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|     this.Dashtestservive.generatePDF(content, filename); |     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 { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||||
| import { DashrunnerService } from '../dashrunner.service'; | import { DashrunnerService } from '../dashrunner.service'; | ||||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.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({ | @Component({ | ||||||
|   selector: 'app-bubble-runner', |   selector: 'app-bubble-runner', | ||||||
| @ -25,9 +29,15 @@ export class BubbleRunnerComponent implements OnInit { | |||||||
|   JsonData; |   JsonData; | ||||||
|   lineChartNoLabels: [] = []; |   lineChartNoLabels: [] = []; | ||||||
|   ChartLegend = false; |   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, |   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'] = { |   public bubbleChartOptions: ChartConfiguration['options'] = { | ||||||
|     // scales: {
 |     // scales: {
 | ||||||
| @ -87,6 +97,13 @@ export class BubbleRunnerComponent implements OnInit { | |||||||
|     this.editId = this.route.snapshot.params.id; |     this.editId = this.route.snapshot.params.id; | ||||||
|     console.log(this.editId); |     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)=>{ |     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|       console.log(data); |       console.log(data); | ||||||
| @ -112,22 +129,62 @@ export class BubbleRunnerComponent implements OnInit { | |||||||
|           this.YAxis = ChartObject[i].yAxis; |           this.YAxis = ChartObject[i].yAxis; | ||||||
|           this.showlabel = ChartObject[i].showlabel; |           this.showlabel = ChartObject[i].showlabel; | ||||||
|           this.ChartLegend = ChartObject[i].chartlegend; |           this.ChartLegend = ChartObject[i].chartlegend; | ||||||
|  |           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|           console.log(this.TableName); |           console.log(this.TableName); | ||||||
|           this.Dashtestservive.getChartData(this.TableName,"Bubble Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { |           // Fetch data with filters
 | ||||||
|             console.log(Ldata); |           this.fetchChartData(); | ||||||
|             this.JsonData = Ldata; |  | ||||||
|             this.bubbleChartData = this.JsonData.bubbleChartData; |  | ||||||
|             // this.radarChartLabels = this.JsonData.radarChartLabels;
 |  | ||||||
|              |  | ||||||
|            },(error) => { |  | ||||||
|             console.log(error); |  | ||||||
|            }); |  | ||||||
|           break; // No need to continue the loop once the correct placeholder is found
 |           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(){ |   generatePDFFile(){ | ||||||
|     this.buttonClicked.emit(); |     this.buttonClicked.emit(); | ||||||
|     const content = this.contentContainerRef.nativeElement; |     const content = this.contentContainerRef.nativeElement; | ||||||
| @ -135,6 +192,19 @@ export class BubbleRunnerComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|     this.Dashtestservive.generatePDF(content, filename); |     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); |     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><button class="btn btn-primary" (click)="Export(item.name)">Export</button></span> --> | ||||||
|         <!-- <span><app-line-runner (buttonClicked)="generatePDFFile()"></app-line-runner></span> --> |         <!-- <span><app-line-runner (buttonClicked)="generatePDFFile()"></app-line-runner></span> --> | ||||||
|         <!-- <h4 style="margin-top: 10px; margin-left: 10px;">{{ item.charttitle }}</h4> --> |         <!-- <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-item> | ||||||
|     </gridster> |     </gridster> | ||||||
|   </div> |   </div> | ||||||
|  | |||||||
| @ -17,6 +17,10 @@ import { BubbleRunnerComponent } from './bubble-runner/bubble-runner.component'; | |||||||
| import { ScatterRunnerComponent } from './scatter-runner/scatter-runner.component'; | import { ScatterRunnerComponent } from './scatter-runner/scatter-runner.component'; | ||||||
| import { PolarRunnerComponent } from './polar-runner/polar-runner.component'; | import { PolarRunnerComponent } from './polar-runner/polar-runner.component'; | ||||||
| import { RadarRunnerComponent } from './radar-runner/radar-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({ | @Component({ | ||||||
|   selector: 'app-dashrunnerline', |   selector: 'app-dashrunnerline', | ||||||
| @ -44,10 +48,13 @@ export class DashrunnerlineComponent implements OnInit { | |||||||
|   { name: "Radar Chart", componentInstance: RadarRunnerComponent }, |   { name: "Radar Chart", componentInstance: RadarRunnerComponent }, | ||||||
|   { name: "Grid View", componentInstance: GridRunnerComponent }, |   { name: "Grid View", componentInstance: GridRunnerComponent }, | ||||||
|   { name: "To Do Chart", componentInstance: TodoRunnerComponent }, |   { 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, |   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 { |   ngOnInit(): void { | ||||||
| 
 | 
 | ||||||
| @ -288,4 +295,48 @@ dashboard_name = "Dashtest"; | |||||||
|     console.log('Button clicked in SomeComponent'); |     console.log('Button clicked in SomeComponent'); | ||||||
|     // Add your custom logic here when the button is 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 { ActivatedRoute, Router } from '@angular/router'; | ||||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | 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({ | @Component({ | ||||||
|   selector: 'app-doughnut-runner', |   selector: 'app-doughnut-runner', | ||||||
| @ -33,10 +37,16 @@ export class DoughnutRunnerComponent implements OnInit { | |||||||
|     "chartLabels": ["Project", "Repository", "Wireframe"] |     "chartLabels": ["Project", "Repository", "Wireframe"] | ||||||
|   } |   } | ||||||
|   doughnutChartType: ChartType = 'doughnut'; |   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, |     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 { |   ngOnInit(): void { | ||||||
|     this.doughnutChartData = this.doughnutData.chartData; |     this.doughnutChartData = this.doughnutData.chartData; | ||||||
|     this.doughnutChartLabels = this.doughnutData.chartLabels; |     this.doughnutChartLabels = this.doughnutData.chartLabels; | ||||||
| @ -44,6 +54,14 @@ export class DoughnutRunnerComponent implements OnInit { | |||||||
|     this.editId = this.route.snapshot.params.id; |     this.editId = this.route.snapshot.params.id; | ||||||
|     console.log(this.editId); |     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)=>{ |     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|       console.log(data); |       console.log(data); | ||||||
|       this.workflowLine = data.dashbord1_Line[0].model; |       this.workflowLine = data.dashbord1_Line[0].model; | ||||||
| @ -70,22 +88,62 @@ export class DoughnutRunnerComponent implements OnInit { | |||||||
|           this.YAxis = ChartObject[i].yAxis; |           this.YAxis = ChartObject[i].yAxis; | ||||||
|           this.showlabel = ChartObject[i].showlabel; |           this.showlabel = ChartObject[i].showlabel; | ||||||
|           this.doughnutChartLegend = ChartObject[i].chartlegend; |           this.doughnutChartLegend = ChartObject[i].chartlegend; | ||||||
|  |           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|           console.log(this.TableName); |           console.log(this.TableName); | ||||||
|           this.Dashtestservive.getChartData(this.TableName,"Doughnut Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { |           // Fetch data with filters
 | ||||||
|             console.log(Ldata); |           this.fetchChartData(); | ||||||
|             this.JsonData = Ldata; |  | ||||||
|             this.doughnutChartData = this.JsonData.chartData; |  | ||||||
|             this.doughnutChartLabels = this.JsonData.chartLabels; |  | ||||||
|              |  | ||||||
|            },(error) => { |  | ||||||
|             console.log(error); |  | ||||||
|            }); |  | ||||||
|           break; // No need to continue the loop once the correct placeholder is found
 |           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(){ |   generatePDFFile(){ | ||||||
|     // this.buttonClicked.emit();
 |     // this.buttonClicked.emit();
 | ||||||
|     const content = this.contentContainerRef.nativeElement; |     const content = this.contentContainerRef.nativeElement; | ||||||
| @ -93,6 +151,19 @@ export class DoughnutRunnerComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|     this.Dashtestservive.generatePDF(content, filename); |     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> --> | ||||||
|       <div><button class="btn btn-primary" (click)="generatePDFFile()">Export</button></div> |       <div><button class="btn btn-primary" (click)="generatePDFFile()">Export</button></div> | ||||||
|       <div style="max-height: 400px; overflow: auto; padding: 10px;"> |       <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> |         <thead> | ||||||
|           <tr> |           <tr> | ||||||
|             <th *ngFor="let co of getHeaders();let i=index">{{co}}</th> |             <th *ngFor="let co of getHeaders();let i=index">{{co}}</th> | ||||||
|           </tr> |           </tr> | ||||||
|         </thead> |         </thead> | ||||||
|         <tbody> |         <tbody> | ||||||
|           <tr *ngFor="let item of rows?.slice()?.reverse()"> |           <tr *ngFor="let item of rows"> | ||||||
|             <td *ngFor="let key of getHeaders()">{{item[key]}}</td> |             <td *ngFor="let key of getHeaders()">{{item[key]}}</td> | ||||||
|           </tr> |           </tr> | ||||||
|         </tbody> |         </tbody> | ||||||
|       </table> |       </table> | ||||||
|  |        | ||||||
|  |       <ng-template #noData> | ||||||
|  |         <p *ngIf="!error">No data available</p> | ||||||
|  |       </ng-template> | ||||||
|       </div> |       </div> | ||||||
| @ -3,6 +3,10 @@ import { DashrunnerService } from '../dashrunner.service'; | |||||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||||
| import { ActivatedRoute, Router } from '@angular/router'; | import { ActivatedRoute, Router } from '@angular/router'; | ||||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.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({ | @Component({ | ||||||
|   selector: 'app-grid-runner', |   selector: 'app-grid-runner', | ||||||
| @ -26,86 +30,191 @@ export class GridRunnerComponent implements OnInit { | |||||||
|   public DashtestboardArray: DashboardContentModel[] = []; |   public DashtestboardArray: DashboardContentModel[] = []; | ||||||
|   workflowLine; |   workflowLine; | ||||||
|   TableName; |   TableName; | ||||||
|  |   ConnectionId: number; // Add ConnectionId property
 | ||||||
|  |    | ||||||
|  |   // Add subscriptions to unsubscribe on destroy
 | ||||||
|  |   private subscriptions: Subscription[] = []; | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, |     private Dashtestservive:DashrunnerService, | ||||||
|     private router : Router |     private route: ActivatedRoute, | ||||||
|  |     private dashboardService: Dashboard3Service, | ||||||
|  |     private router : Router, | ||||||
|  |     // Add FilterService to constructor
 | ||||||
|  |     private filterService: FilterService | ||||||
|   ) { } |   ) { } | ||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
| 
 | 
 | ||||||
|     this.editId = this.route.snapshot.params.id; |     this.editId = this.route.snapshot.params.id; | ||||||
|     console.log(this.editId); |     console.log('GridRunner: Component initialized with editId:', this.editId); | ||||||
|     // this.getbyId();
 |     // this.getbyId();
 | ||||||
| 
 | 
 | ||||||
|     this.dashboardService.getById(this.editId).subscribe((data)=>{ |     // Subscribe to filter changes
 | ||||||
|       console.log(data); |     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('GridRunner: Received dashboard data:', data); | ||||||
|       this.workflowLine = data.dashbord1_Line[0].model; |       this.workflowLine = data.dashbord1_Line[0].model; | ||||||
|       const dash = JSON.parse(this.workflowLine) ; |       const dash = JSON.parse(this.workflowLine); | ||||||
|       // this.DashtestboardArray = dash.dashboard;
 |       // this.DashtestboardArray = dash.dashboard;
 | ||||||
|       // console.log(this.DashtestboardArray);
 |       // console.log(this.DashtestboardArray);
 | ||||||
| 
 | 
 | ||||||
|       const ChartObject = dash.dashboard.filter(obj => obj.name === "Grid View"); |       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++) { |       for (let i = 0; i < ChartObject.length; i++) { | ||||||
|         const ids = this.Dashtestservive.getgridview(); |         const ids = this.Dashtestservive.getgridview(); | ||||||
|  |         console.log('GridRunner: Current gridview ids:', ids); | ||||||
|  |         console.log('GridRunner: Checking chartid:', ChartObject[i].chartid); | ||||||
|         // console.log(ids);
 |         // console.log(ids);
 | ||||||
|         if (ids.includes(ChartObject[i].chartid)) { |         if (ids.includes(ChartObject[i].chartid)) { | ||||||
|           // If the chartid is already in the ids array, continue to the next iteration
 |           // 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; |           continue; | ||||||
|         } |         } | ||||||
|  |         console.log('GridRunner: Adding new chartid:', ChartObject[i].chartid); | ||||||
|         this.Dashtestservive.setgridview(ChartObject[i].chartid); |         this.Dashtestservive.setgridview(ChartObject[i].chartid); | ||||||
|         const id = ids[i]; | 
 | ||||||
|         console.log(id); |         this.TableName = ChartObject[i].table; | ||||||
|        |         this.XAxis = ChartObject[i].xAxis; | ||||||
|         if (ChartObject[i].chartid === id) { |         this.YAxis = ChartObject[i].yAxis; | ||||||
|           this.TableName = ChartObject[i].table; |         // Add connection ID if available
 | ||||||
|           this.XAxis = ChartObject[i].xAxis; |         this.ConnectionId = ChartObject[i].connection; | ||||||
|           this.YAxis = ChartObject[i].yAxis; |         console.log('GridRunner: TableName:', this.TableName); | ||||||
|           console.log(this.TableName); |         console.log('GridRunner: XAxis:', this.XAxis); | ||||||
|           this.Dashtestservive.getChartData(this.TableName,"Grid View",this.XAxis,this.YAxis).subscribe((Ldata) => { |         console.log('GridRunner: YAxis:', this.YAxis); | ||||||
|             console.log(Ldata); |         console.log('GridRunner: ConnectionId:', this.ConnectionId); | ||||||
|             this.rows = Ldata; |         // Fetch data with filters
 | ||||||
|             this.rowdata = this.rows |         this.fetchGridData(); | ||||||
|              |         break; // No need to continue the loop once the correct placeholder is found
 | ||||||
|            },(error) => { |  | ||||||
|             console.log(error); |  | ||||||
|            }); |  | ||||||
|           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
 | ||||||
| //dynamic table
 |       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]; | ||||||
| 
 | 
 | ||||||
| getTableData(id){ |         // Find the filter definition to get the field name
 | ||||||
| } |         const filterDef = this.filterService.getFilters().find(f => f.id === filterId); | ||||||
| getHeaders() { | 
 | ||||||
| let headers: string[] = []; |         if (filterDef && filterDef.field) { | ||||||
| if(this.rows) { |           const fieldName = filterDef.field; | ||||||
|   this.rows.forEach((value) => { |           if (filterValue !== undefined && filterValue !== null && filterValue !== '') { | ||||||
|     Object.keys(value).forEach((key) => { |             filterObj[fieldName] = filterValue; | ||||||
|       if(!headers.find((header) => header == key)){ |           } | ||||||
|         headers.push(key) |         } | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       // 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) => { | ||||||
| return headers; |         console.log('GridRunner: Received data from API:', Ldata); | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| generatePDFFile(){ |         // Handle the actual data structure returned by the API
 | ||||||
|   this.buttonClicked.emit(); |         if (Ldata && Ldata.chartData) { | ||||||
|   const content = this.contentContainerRef.nativeElement; |           this.rows = Ldata.chartData; | ||||||
|   const filename = 'gridview.pdf'; // You can provide any desired filename here
 |           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 = []; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|   this.Dashtestservive.generatePDF(content, filename); |         // 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
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   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; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   generatePDFFile() { | ||||||
|  |     this.buttonClicked.emit(); | ||||||
|  |     const content = this.contentContainerRef.nativeElement; | ||||||
|  |     const filename = 'gridview.pdf'; // You can provide any desired filename here
 | ||||||
|  | 
 | ||||||
|  |     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 { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||||
| import { jsPDF } from 'jspdf'; | import { jsPDF } from 'jspdf'; | ||||||
| import domtoimage from 'dom-to-image'; | 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({ | @Component({ | ||||||
|   selector: 'app-line-runner', |   selector: 'app-line-runner', | ||||||
|   templateUrl: './line-runner.component.html', |   templateUrl: './line-runner.component.html', | ||||||
| @ -54,8 +58,14 @@ export class LineRunnerComponent implements OnInit { | |||||||
|    lineChartLegend = false; |    lineChartLegend = false; | ||||||
|   lineChartPlugins = []; |   lineChartPlugins = []; | ||||||
|   lineChartType = 'line'; |   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, |   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 { |   ngOnInit(): void { | ||||||
| 
 | 
 | ||||||
| @ -65,6 +75,13 @@ export class LineRunnerComponent implements OnInit { | |||||||
|       this.editId = this.route.snapshot.params.id; |       this.editId = this.route.snapshot.params.id; | ||||||
|       console.log(this.editId); |       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)=>{ |       this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|         console.log(data); |         console.log(data); | ||||||
| @ -92,16 +109,10 @@ export class LineRunnerComponent implements OnInit { | |||||||
|             this.YAxis = ChartObject[i].yAxis; |             this.YAxis = ChartObject[i].yAxis; | ||||||
|             this.showlabel = ChartObject[i].showlabel; |             this.showlabel = ChartObject[i].showlabel; | ||||||
|             this.lineChartLegend = ChartObject[i].chartlegend; |             this.lineChartLegend = ChartObject[i].chartlegend; | ||||||
|  |             this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|             console.log(this.TableName); |             console.log(this.TableName); | ||||||
|             this.Dashtestservive.getChartData(this.TableName,"Line Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { |             // Fetch data with filters
 | ||||||
|               console.log(Ldata); |             this.fetchChartData(); | ||||||
|               this.JsonData = Ldata; |  | ||||||
|               this.lineChartData = this.JsonData.chartData; |  | ||||||
|               this.lineChartLabels = this.JsonData.chartLabels; |  | ||||||
|                |  | ||||||
|              },(error) => { |  | ||||||
|               console.log(error); |  | ||||||
|              }); |  | ||||||
|             break; // No need to continue the loop once the correct placeholder is found
 |             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(){ |   generatePDFFile(){ | ||||||
|     this.buttonClicked.emit(); |     this.buttonClicked.emit(); | ||||||
|     const content = this.contentContainerRef.nativeElement; |     const content = this.contentContainerRef.nativeElement; | ||||||
| @ -165,5 +222,18 @@ export class LineRunnerComponent implements OnInit { | |||||||
|   //     console.error('Error generating PDF:', error);
 |   //     console.error('Error generating PDF:', error);
 | ||||||
|   //   }
 |   //   }
 | ||||||
|   // }
 |   // }
 | ||||||
|  |    | ||||||
|  |   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 { ActivatedRoute, Router } from '@angular/router'; | ||||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | 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({ | @Component({ | ||||||
| @ -23,9 +27,15 @@ export class PieRunnerComponent implements OnInit { | |||||||
|   showlabel; |   showlabel; | ||||||
|   JsonData; |   JsonData; | ||||||
|   lineChartNoLabels: any[] = []; |   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, |   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 pieChartLabels: string[] = ['SciFi', 'Drama', 'Comedy']; | ||||||
|   public pieChartData: number[] = [30, 50, 20]; |   public pieChartData: number[] = [30, 50, 20]; | ||||||
| @ -39,6 +49,13 @@ export class PieRunnerComponent implements OnInit { | |||||||
|     this.editId = this.route.snapshot.params.id; |     this.editId = this.route.snapshot.params.id; | ||||||
|     console.log(this.editId); |     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)=>{ |     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|       console.log(data); |       console.log(data); | ||||||
| @ -66,22 +83,62 @@ export class PieRunnerComponent implements OnInit { | |||||||
|           this.YAxis = ChartObject[i].yAxis; |           this.YAxis = ChartObject[i].yAxis; | ||||||
|           this.showlabel = ChartObject[i].showlabel; |           this.showlabel = ChartObject[i].showlabel; | ||||||
|           this.ChartLegend = ChartObject[i].chartlegend; |           this.ChartLegend = ChartObject[i].chartlegend; | ||||||
|  |           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|           console.log(this.TableName); |           console.log(this.TableName); | ||||||
|           this.Dashtestservive.getChartData(this.TableName,"Pie Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { |           // Fetch data with filters
 | ||||||
|             console.log(Ldata); |           this.fetchChartData(); | ||||||
|             this.JsonData = Ldata; |  | ||||||
|             this.pieChartData = this.JsonData.pieChartData; |  | ||||||
|             this.pieChartLabels = this.JsonData.pieChartLabels; |  | ||||||
|              |  | ||||||
|            },(error) => { |  | ||||||
|             console.log(error); |  | ||||||
|            }); |  | ||||||
|           break; // No need to continue the loop once the correct placeholder is found
 |           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(){ |   generatePDFFile(){ | ||||||
|     this.buttonClicked.emit(); |     this.buttonClicked.emit(); | ||||||
|     const content = this.contentContainerRef.nativeElement; |     const content = this.contentContainerRef.nativeElement; | ||||||
| @ -89,4 +146,17 @@ export class PieRunnerComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|     this.Dashtestservive.generatePDF(content, filename); |     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 { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||||
| // import { Label } from 'ng2-charts';
 | // import { Label } from 'ng2-charts';
 | ||||||
|  | // Add FilterService import
 | ||||||
|  | import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||||
|  | // Add Subscription import
 | ||||||
|  | import { Subscription } from 'rxjs'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-polar-runner', |   selector: 'app-polar-runner', | ||||||
| @ -23,9 +27,15 @@ export class PolarRunnerComponent implements OnInit { | |||||||
|   showlabel; |   showlabel; | ||||||
|   JsonData; |   JsonData; | ||||||
|   lineChartNoLabels: any[] = []; |   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, |   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 polarAreaChartLabels: string[] = [ 'Download Sales', 'In-Store Sales', 'Mail Sales', 'Telesales', 'Corporate Sales' ]; | ||||||
|   public polarAreaChartData: any = [ |   public polarAreaChartData: any = [ | ||||||
| @ -41,6 +51,13 @@ export class PolarRunnerComponent implements OnInit { | |||||||
|     this.editId = this.route.snapshot.params.id; |     this.editId = this.route.snapshot.params.id; | ||||||
|     console.log(this.editId); |     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)=>{ |     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|       console.log(data); |       console.log(data); | ||||||
| @ -66,22 +83,62 @@ export class PolarRunnerComponent implements OnInit { | |||||||
|           this.YAxis = ChartObject[i].yAxis; |           this.YAxis = ChartObject[i].yAxis; | ||||||
|           this.showlabel = ChartObject[i].showlabel; |           this.showlabel = ChartObject[i].showlabel; | ||||||
|           this.ChartLegend = ChartObject[i].chartlegend; |           this.ChartLegend = ChartObject[i].chartlegend; | ||||||
|  |           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|           console.log(this.TableName); |           console.log(this.TableName); | ||||||
|           this.Dashtestservive.getChartData(this.TableName,"PolarArea Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { |           // Fetch data with filters
 | ||||||
|             console.log(Ldata); |           this.fetchChartData(); | ||||||
|             this.JsonData = Ldata; |  | ||||||
|             this.polarAreaChartData = this.JsonData.polarAreaChartData; |  | ||||||
|             this.polarAreaChartLabels = this.JsonData.polarAreaChartLabels; |  | ||||||
|              |  | ||||||
|            },(error) => { |  | ||||||
|             console.log(error); |  | ||||||
|            }); |  | ||||||
|           break; // No need to continue the loop once the correct placeholder is found
 |           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(){ |   generatePDFFile(){ | ||||||
|     this.buttonClicked.emit(); |     this.buttonClicked.emit(); | ||||||
|     const content = this.contentContainerRef.nativeElement; |     const content = this.contentContainerRef.nativeElement; | ||||||
| @ -89,5 +146,18 @@ export class PolarRunnerComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|     this.Dashtestservive.generatePDF(content, filename); |     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 { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | ||||||
| import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | import { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||||
| // import { Label } from 'ng2-charts';
 | // import { Label } from 'ng2-charts';
 | ||||||
|  | // Add FilterService import
 | ||||||
|  | import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||||
|  | // Add Subscription import
 | ||||||
|  | import { Subscription } from 'rxjs'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-radar-runner', |   selector: 'app-radar-runner', | ||||||
| @ -24,9 +28,15 @@ export class RadarRunnerComponent implements OnInit { | |||||||
|   JsonData; |   JsonData; | ||||||
|   lineChartNoLabels: any[] = []; |   lineChartNoLabels: any[] = []; | ||||||
|   ChartLegend = false; |   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, |   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[] = [ |   public radarChartLabels: string[] = [ | ||||||
|     "Eating", |     "Eating", | ||||||
| @ -50,6 +60,13 @@ export class RadarRunnerComponent implements OnInit { | |||||||
|     this.editId = this.route.snapshot.params.id; |     this.editId = this.route.snapshot.params.id; | ||||||
|     console.log(this.editId); |     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)=>{ |     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|       console.log(data); |       console.log(data); | ||||||
| @ -75,22 +92,62 @@ export class RadarRunnerComponent implements OnInit { | |||||||
|           this.YAxis = ChartObject[i].yAxis; |           this.YAxis = ChartObject[i].yAxis; | ||||||
|           this.showlabel = ChartObject[i].showlabel; |           this.showlabel = ChartObject[i].showlabel; | ||||||
|           this.ChartLegend = ChartObject[i].chartlegend; |           this.ChartLegend = ChartObject[i].chartlegend; | ||||||
|  |           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|           console.log(this.TableName); |           console.log(this.TableName); | ||||||
|           this.Dashtestservive.getChartData(this.TableName,"Radar Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { |           // Fetch data with filters
 | ||||||
|             console.log(Ldata); |           this.fetchChartData(); | ||||||
|             this.JsonData = Ldata; |  | ||||||
|             this.radarChartData = this.JsonData.radarChartData; |  | ||||||
|             this.radarChartLabels = this.JsonData.radarChartLabels; |  | ||||||
|              |  | ||||||
|            },(error) => { |  | ||||||
|             console.log(error); |  | ||||||
|            }); |  | ||||||
|           break; // No need to continue the loop once the correct placeholder is found
 |           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(){ |   generatePDFFile(){ | ||||||
|     this.buttonClicked.emit(); |     this.buttonClicked.emit(); | ||||||
|     const content = this.contentContainerRef.nativeElement; |     const content = this.contentContainerRef.nativeElement; | ||||||
| @ -98,5 +155,18 @@ export class RadarRunnerComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|     this.Dashtestservive.generatePDF(content, filename); |     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 { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||||
| // import { Label } from 'ng2-charts';
 | // import { Label } from 'ng2-charts';
 | ||||||
| import { ChartDataset } from 'chart.js'; | import { ChartDataset } from 'chart.js'; | ||||||
|  | // Add FilterService import
 | ||||||
|  | import { FilterService } from '../../../dashboardnew/common-filter/filter.service'; | ||||||
|  | // Add Subscription import
 | ||||||
|  | import { Subscription } from 'rxjs'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-scatter-runner', |   selector: 'app-scatter-runner', | ||||||
| @ -25,9 +29,15 @@ export class ScatterRunnerComponent implements OnInit { | |||||||
|   JsonData; |   JsonData; | ||||||
|   lineChartNoLabels: any[] = []; |   lineChartNoLabels: any[] = []; | ||||||
|   ChartLegend = false; |   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, |   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' ]; |   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; |     this.editId = this.route.snapshot.params.id; | ||||||
|     console.log(this.editId); |     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)=>{ |     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|       console.log(data); |       console.log(data); | ||||||
| @ -94,22 +111,62 @@ export class ScatterRunnerComponent implements OnInit { | |||||||
|           this.YAxis = ChartObject[i].yAxis; |           this.YAxis = ChartObject[i].yAxis; | ||||||
|           this.showlabel = ChartObject[i].showlabel; |           this.showlabel = ChartObject[i].showlabel; | ||||||
|           this.ChartLegend = ChartObject[i].chartlegend; |           this.ChartLegend = ChartObject[i].chartlegend; | ||||||
|  |           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|           console.log(this.TableName); |           console.log(this.TableName); | ||||||
|           this.Dashtestservive.getChartData(this.TableName,"Scatter Chart",this.XAxis,this.YAxis).subscribe((Ldata) => { |           // Fetch data with filters
 | ||||||
|             console.log(Ldata); |           this.fetchChartData(); | ||||||
|             this.JsonData = Ldata; |  | ||||||
|             this.scatterChartData = this.JsonData.scatterChartData; |  | ||||||
|             this.scatterChartLabels = this.JsonData.scatterChartLabels; |  | ||||||
|              |  | ||||||
|            },(error) => { |  | ||||||
|             console.log(error); |  | ||||||
|            }); |  | ||||||
|           break; // No need to continue the loop once the correct placeholder is found
 |           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(){ |   generatePDFFile(){ | ||||||
|     this.buttonClicked.emit(); |     this.buttonClicked.emit(); | ||||||
|     const content = this.contentContainerRef.nativeElement; |     const content = this.contentContainerRef.nativeElement; | ||||||
| @ -117,5 +174,18 @@ export class ScatterRunnerComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|     this.Dashtestservive.generatePDF(content, filename); |     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 { DashboardContentModel } from 'src/app/models/builder/dashboard'; | ||||||
| import { ActivatedRoute, Router } from '@angular/router'; | import { ActivatedRoute, Router } from '@angular/router'; | ||||||
| import { Dashboard3Service } from 'src/app/services/builder/dashboard3.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({ | @Component({ | ||||||
|   selector: 'app-todo-runner', |   selector: 'app-todo-runner', | ||||||
| @ -12,19 +16,21 @@ import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; | |||||||
| export class TodoRunnerComponent implements OnInit { | export class TodoRunnerComponent implements OnInit { | ||||||
|   @ViewChild('contentContainer') contentContainerRef!: ElementRef; |   @ViewChild('contentContainer') contentContainerRef!: ElementRef; | ||||||
|   @Output() buttonClicked = new EventEmitter<void>(); |   @Output() buttonClicked = new EventEmitter<void>(); | ||||||
|   constructor( private Dashtestservive:DashrunnerService,private route: ActivatedRoute,private dashboardService: Dashboard3Service, |  | ||||||
|     private router : Router) { } |  | ||||||
| 
 |  | ||||||
|     loading = false; |  | ||||||
|     givendata; |  | ||||||
|     error; |  | ||||||
|     XAxis; |  | ||||||
|     YAxis; |  | ||||||
|    |    | ||||||
|     editId; |   // Add subscriptions to unsubscribe on destroy
 | ||||||
|     public DashtestboardArray: DashboardContentModel[] = []; |   private subscriptions: Subscription[] = []; | ||||||
|     workflowLine; | 
 | ||||||
|     TableName; |   loading = false; | ||||||
|  |   givendata; | ||||||
|  |   error; | ||||||
|  |   XAxis; | ||||||
|  |   YAxis; | ||||||
|  | 
 | ||||||
|  |   editId; | ||||||
|  |   public DashtestboardArray: DashboardContentModel[] = []; | ||||||
|  |   workflowLine; | ||||||
|  |   TableName; | ||||||
|  |   ConnectionId: number; // Add ConnectionId property
 | ||||||
| 
 | 
 | ||||||
|   list; |   list; | ||||||
|   data: any; |   data: any; | ||||||
| @ -34,11 +40,25 @@ export class TodoRunnerComponent implements OnInit { | |||||||
|     listName: "title123", |     listName: "title123", | ||||||
|     List:['todo 1','todo 2'], |     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 { |   ngOnInit(): void { | ||||||
|     this.editId = this.route.snapshot.params.id; |     this.editId = this.route.snapshot.params.id; | ||||||
|     console.log(this.editId); |     console.log(this.editId); | ||||||
|     // this.getbyId();
 |     // 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)=>{ |     this.dashboardService.getById(this.editId).subscribe((data)=>{ | ||||||
|       console.log(data); |       console.log(data); | ||||||
|       this.workflowLine = data.dashbord1_Line[0].model; |       this.workflowLine = data.dashbord1_Line[0].model; | ||||||
| @ -63,15 +83,10 @@ export class TodoRunnerComponent implements OnInit { | |||||||
|           this.TableName = ChartObject[i].table; |           this.TableName = ChartObject[i].table; | ||||||
|           this.XAxis = ChartObject[i].xAxis; |           this.XAxis = ChartObject[i].xAxis; | ||||||
|           this.YAxis = ChartObject[i].yAxis; |           this.YAxis = ChartObject[i].yAxis; | ||||||
|  |           this.ConnectionId = ChartObject[i].connection; // Add connection ID
 | ||||||
|           console.log(this.TableName); |           console.log(this.TableName); | ||||||
|           this.Dashtestservive.getChartData(this.TableName,"Todo List",this.XAxis,this.YAxis).subscribe((Ldata) => { |           // Fetch data with filters
 | ||||||
|             console.log(Ldata); |           this.fetchTodoData(); | ||||||
|             this.todoList.listName = Ldata.listName; |  | ||||||
|             this.todoList.List = Ldata.List; |  | ||||||
|              |  | ||||||
|            },(error) => { |  | ||||||
|             console.log(error); |  | ||||||
|            }); |  | ||||||
|           break; // No need to continue the loop once the correct placeholder is found
 |           break; // No need to continue the loop once the correct placeholder is found
 | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @ -100,4 +115,58 @@ generatePDFFile(){ | |||||||
| 
 | 
 | ||||||
|   this.Dashtestservive.generatePDF(content, filename); |   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