From 35e3f411a88549fe1e28cdf33635cb6b76cd4ffd Mon Sep 17 00:00:00 2001 From: Gaurav Kumar Date: Mon, 27 Oct 2025 10:59:35 +0530 Subject: [PATCH] filter --- .../common-filter/compact-filter.component.ts | 116 +++- .../editnewdash/editnewdash.component.ts | 119 +++- .../dashboardnew/gadgets/bar-chart.zip | Bin 0 -> 18577 bytes .../bar-chart/bar-chart.component.html | 58 +- .../bar-chart/bar-chart.component.scss | 209 +------ .../gadgets/bar-chart/bar-chart.component.ts | 138 ++--- .../grid-view/grid-view.component.html | 36 +- .../grid-view/grid-view.component.scss | 32 +- .../gadgets/grid-view/grid-view.component.ts | 525 +++++++++++++++++- .../dashrunnerall.component.html | 8 +- 10 files changed, 825 insertions(+), 416 deletions(-) create mode 100644 frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart.zip diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.ts index 90d5bcf..7424e96 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.ts @@ -38,19 +38,6 @@ export class CompactFilterComponent implements OnInit, OnChanges { ) { } ngOnInit(): void { - // Subscribe to filter definitions to get available filters - this.filterService.filters$.subscribe(filters => { - this.availableFilters = filters; - this.updateSelectedFilter(); - }); - - // Subscribe to filter state changes - this.filterService.filterState$.subscribe(state => { - if (this.selectedFilter && state.hasOwnProperty(this.selectedFilter.id)) { - this.filterValue = state[this.selectedFilter.id]; - } - }); - // Initialize configuration from inputs this.configFilterKey = this.filterKey; this.configFilterType = this.filterType; @@ -70,26 +57,53 @@ export class CompactFilterComponent implements OnInit, OnChanges { // Register this filter with the filter service this.registerFilter(); + + // Subscribe to filter definitions to get available filters + this.filterService.filters$.subscribe(filters => { + this.availableFilters = filters; + this.updateSelectedFilter(); + }); + + // Subscribe to filter state changes + this.filterService.filterState$.subscribe(state => { + if (this.selectedFilter && state.hasOwnProperty(this.selectedFilter.id)) { + this.filterValue = state[this.selectedFilter.id]; + } + }); } ngOnChanges(changes: SimpleChanges): void { // If filterKey or filterType changes, re-register the filter if (changes.filterKey || changes.filterType) { + // Load available values for the current filter key if it's a dropdown or multiselect + if ((this.filterType === 'dropdown' || this.filterType === 'multiselect') && this.filterKey) { + this.loadAvailableValues(this.filterKey); + } this.registerFilter(); } + + // Handle API URL changes + if (changes.apiUrl && !changes.apiUrl.firstChange) { + if (this.apiUrl) { + this.loadAvailableKeys(); + } + } } // Register this filter with the filter service registerFilter(): void { if (this.filterKey) { + // Get current filter values from the service + const currentFilterValues = this.filterService.getFilterValues(); + // Create a filter definition for this compact filter const filterDef: Filter = { - id: `compact-filter-${this.filterKey}`, + id: `${this.filterKey}`, field: this.filterKey, label: this.filterLabel || this.filterKey, type: this.filterType as any, options: this.filterOptions, - value: this.filterValue + value: this.filterValue // Use the current filter value }; // Get current filters @@ -99,9 +113,32 @@ export class CompactFilterComponent implements OnInit, OnChanges { const existingFilterIndex = currentFilters.findIndex(f => f.id === filterDef.id); if (existingFilterIndex >= 0) { + // Preserve the existing filter configuration + const existingFilter = currentFilters[existingFilterIndex]; + + // Preserve the existing filter value if it exists in the service + if (currentFilterValues.hasOwnProperty(existingFilter.id)) { + filterDef.value = currentFilterValues[existingFilter.id]; + this.filterValue = filterDef.value; // Update local value + } else if (existingFilter.value !== undefined) { + // Fallback to existing filter's value if no service value + filterDef.value = existingFilter.value; + this.filterValue = filterDef.value; + } + + // Preserve other configuration properties + filterDef.label = existingFilter.label; + filterDef.options = existingFilter.options || this.filterOptions; + // Update existing filter currentFilters[existingFilterIndex] = filterDef; } else { + // For new filters, check if there's already a value in the service + if (currentFilterValues.hasOwnProperty(filterDef.id)) { + filterDef.value = currentFilterValues[filterDef.id]; + this.filterValue = filterDef.value; // Update local value + } + // Add new filter currentFilters.push(filterDef); } @@ -118,9 +155,24 @@ export class CompactFilterComponent implements OnInit, OnChanges { if (this.filterKey && this.availableFilters.length > 0) { this.selectedFilter = this.availableFilters.find(f => f.field === this.filterKey) || null; if (this.selectedFilter) { - // Get current value for this filter + // Get current value for this filter from the service const currentState = this.filterService.getFilterValues(); - this.filterValue = currentState[this.selectedFilter.id] || ''; + const filterValue = currentState[this.selectedFilter.id]; + if (filterValue !== undefined) { + this.filterValue = filterValue; + } else if (this.selectedFilter.value !== undefined) { + // Use the filter's default value if no service value + this.filterValue = this.selectedFilter.value; + } else { + // Use the current filter value as fallback + this.filterValue = this.filterValue || ''; + } + + // Also update configuration properties from the selected filter + this.configFilterKey = this.selectedFilter.field; + this.configFilterType = this.selectedFilter.type; + this.configFilterLabel = this.selectedFilter.label; + this.configFilterOptions = (this.selectedFilter.options || []).join(','); } } } @@ -130,6 +182,14 @@ export class CompactFilterComponent implements OnInit, OnChanges { this.filterValue = value; this.filterService.updateFilterValue(this.selectedFilter.id, value); this.filterChange.emit({ filterId: this.selectedFilter.id, value: value }); + + // Update the filter definition in the service to reflect the new value + const currentFilters = this.filterService.getFilters(); + const filterIndex = currentFilters.findIndex(f => f.id === this.selectedFilter.id); + if (filterIndex >= 0) { + currentFilters[filterIndex].value = value; + this.filterService.setFilters(currentFilters); + } } } @@ -179,11 +239,19 @@ export class CompactFilterComponent implements OnInit, OnChanges { toggleConfigMode(): void { this.isConfigMode = !this.isConfigMode; if (this.isConfigMode) { - // Initialize config values - this.configFilterKey = this.filterKey; - this.configFilterType = this.filterType; - this.configFilterLabel = this.filterLabel; - this.configFilterOptions = this.filterOptions.join(','); + // Initialize config values from current filter if available + if (this.selectedFilter) { + this.configFilterKey = this.selectedFilter.field; + this.configFilterType = this.selectedFilter.type; + this.configFilterLabel = this.selectedFilter.label; + this.configFilterOptions = (this.selectedFilter.options || []).join(','); + } else { + // Fallback to current properties + this.configFilterKey = this.filterKey; + this.configFilterType = this.filterType; + this.configFilterLabel = this.filterLabel; + this.configFilterOptions = this.filterOptions.join(','); + } this.configApiUrl = this.apiUrl; this.configConnectionId = this.connectionId; } @@ -216,8 +284,8 @@ export class CompactFilterComponent implements OnInit, OnChanges { } // Load available values for the selected key if it's a dropdown or multiselect - if ((this.filterType === 'dropdown' || this.filterType === 'multiselect') && this.filterKey) { - this.loadAvailableValues(this.filterKey); + if ((this.configFilterType === 'dropdown' || this.configFilterType === 'multiselect') && this.configFilterKey) { + this.loadAvailableValues(this.configFilterKey); } // Register the updated filter with the filter service diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.ts index 3f0ce66..367d1a4 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.ts @@ -782,6 +782,8 @@ export class EditnewdashComponent implements OnInit { xyz.filterType = this.gadgetsEditdata.filterType || 'text'; xyz.filterLabel = this.gadgetsEditdata.filterLabel || ''; xyz.filterOptions = this.gadgetsEditdata.filterOptions || []; + xyz.table = this.gadgetsEditdata.table || ''; + xyz.connection = this.gadgetsEditdata.connection || undefined; } console.log(xyz); @@ -818,7 +820,97 @@ export class EditnewdashComponent implements OnInit { * This prevents errors when trying to set properties that don't exist on the components */ getChartInputs(item: any): any { - // Only pass properties that are relevant to chart components + // For CompactFilterComponent, pass only filter configuration properties + if (item.component && item.component.name === 'CompactFilterComponent') { + const filterInputs = { + filterKey: item['filterKey'] || '', + filterType: item['filterType'] || 'text', + filterLabel: item['filterLabel'] || '', + filterOptions: item['filterOptions'] || [], + apiUrl: item['table'] || '', // Use table as API URL + connectionId: item['connection'] ? parseInt(item['connection'], 10) : undefined + }; + + // Preserve configuration in the item itself + item['filterKey'] = filterInputs['filterKey']; + item['filterType'] = filterInputs['filterType']; + item['filterLabel'] = filterInputs['filterLabel']; + item['filterOptions'] = filterInputs['filterOptions']; + item['table'] = filterInputs['apiUrl']; + item['connection'] = item['connection']; + + // Remove undefined properties to avoid passing unnecessary data + Object.keys(filterInputs).forEach(key => { + if (filterInputs[key] === undefined) { + delete filterInputs[key]; + } + }); + + return filterInputs; + } + + // For CommonFilterComponent, pass only filter-related properties + if (item.component && item.component.name === 'CommonFilterComponent') { + const commonFilterInputs = { + baseFilters: item['baseFilters'] || [], + drilldownFilters: item['drilldownFilters'] || [], + drilldownLayers: item['drilldownLayers'] || [], + fieldName: item['name'] || '', + connection: item['connection'] || undefined + }; + + // Remove undefined properties to avoid passing unnecessary data + Object.keys(commonFilterInputs).forEach(key => { + if (commonFilterInputs[key] === undefined) { + delete commonFilterInputs[key]; + } + }); + + return commonFilterInputs; + } + + // For GridViewComponent, pass chart properties with drilldown support + if (item.component && item.component.name === 'GridViewComponent') { + const gridInputs = { + xAxis: item.xAxis, + yAxis: item.yAxis, + table: item.table, + datastore: item.datastore, + charttitle: item.charttitle, + chartlegend: item.chartlegend, + showlabel: item.showlabel, + chartcolor: item.chartcolor, + slices: item.slices, + donut: item.donut, + charturl: item.charturl, + chartparameter: item.chartparameter, + datasource: item.datasource, + fieldName: item.name, // Using item.name as fieldName + connection: item['connection'], // Add connection field using bracket notation + // Base drilldown configuration properties + drilldownEnabled: item['drilldownEnabled'], + drilldownApiUrl: item['drilldownApiUrl'], + // Removed drilldownParameterKey since we're using URL templates + drilldownXAxis: item['drilldownXAxis'], + drilldownYAxis: item['drilldownYAxis'], + drilldownParameter: item['drilldownParameter'], // Add drilldown parameter + baseFilters: item['baseFilters'] || [], // Add base filters + drilldownFilters: item['drilldownFilters'] || [], // Add drilldown filters + // Multi-layer drilldown configurations + drilldownLayers: item['drilldownLayers'] || [] + }; + + // Remove undefined properties to avoid passing unnecessary data + Object.keys(gridInputs).forEach(key => { + if (gridInputs[key] === undefined) { + delete gridInputs[key]; + } + }); + + return gridInputs; + } + + // For all other chart components, pass chart-specific properties const chartInputs = { xAxis: item.xAxis, yAxis: item.yAxis, @@ -848,25 +940,6 @@ export class EditnewdashComponent implements OnInit { drilldownLayers: item['drilldownLayers'] || [] }; - // For CommonFilterComponent, also pass baseFilters, drilldownFilters, drilldownLayers, fieldName, and connection - if (item.component && item.component.name === 'CommonFilterComponent') { - chartInputs['baseFilters'] = item['baseFilters'] || []; - chartInputs['drilldownFilters'] = item['drilldownFilters'] || []; - chartInputs['drilldownLayers'] = item['drilldownLayers'] || []; - chartInputs['fieldName'] = item['name'] || ''; - chartInputs['connection'] = item['connection'] || undefined; - } - - // For CompactFilterComponent, pass filter configuration properties - if (item.component && item.component.name === 'CompactFilterComponent') { - chartInputs['filterKey'] = item['filterKey'] || ''; - chartInputs['filterType'] = item['filterType'] || 'text'; - chartInputs['filterLabel'] = item['filterLabel'] || ''; - chartInputs['filterOptions'] = item['filterOptions'] || []; - chartInputs['apiUrl'] = item['table'] || ''; // Use table as API URL - chartInputs['connectionId'] = item['connection'] ? parseInt(item['connection'], 10) : undefined; - } - // Remove undefined properties to avoid passing unnecessary data Object.keys(chartInputs).forEach(key => { if (chartInputs[key] === undefined) { @@ -929,6 +1002,12 @@ export class EditnewdashComponent implements OnInit { updatedItem.filterOptions = this.gadgetsEditdata.filterOptions || []; updatedItem.table = this.gadgetsEditdata.table || ''; // API URL updatedItem.connection = this.gadgetsEditdata.connection || undefined; // Connection ID + + // Also preserve these properties in gadgetsEditdata for consistency + this.gadgetsEditdata.filterKey = updatedItem.filterKey; + this.gadgetsEditdata.filterType = updatedItem.filterType; + this.gadgetsEditdata.filterLabel = updatedItem.filterLabel; + this.gadgetsEditdata.filterOptions = updatedItem.filterOptions; } console.log('Updated item:', updatedItem); diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart.zip b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart.zip new file mode 100644 index 0000000000000000000000000000000000000000..bf020b9a60063f7b4a0287101f0ababdc87116eb GIT binary patch literal 18577 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 - -
- - -
+
+ + -
- Drilldown Level: {{currentDrilldownLevel}} - -
-
-

{{ charttitle || 'Bar Chart' }}

+ +
+ No data available
-
-
- -
-

No data available

-
- - - - - - -
-
-
-
+ +
+ +
\ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss index 05c97cb..2f11ebd 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss @@ -5,214 +5,27 @@ width: 100%; } -.chart-container { - height: 100%; - display: flex; - flex-direction: column; - background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); - border-radius: 12px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - transition: all 0.3s ease; - border: 1px solid #eaeaea; - padding: 20px; -} - -.chart-container:hover { - box-shadow: 0 6px 25px rgba(0, 0, 0, 0.2); - transform: translateY(-2px); -} - -.compact-filters-container { - display: flex; - flex-wrap: wrap; - gap: 5px; - margin-bottom: 10px; - padding: 5px; - background: #ffffff; - border: 1px solid #e9ecef; - border-radius: 6px; - min-height: 40px; -} - -.drilldown-indicator { - background-color: #e0e0e0; - padding: 10px; - margin-bottom: 15px; - border-radius: 8px; - text-align: center; - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.drilldown-text { - font-weight: bold; - color: #333; - font-size: 16px; -} - -.btn { - padding: 6px 12px; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - transition: all 0.3s ease; -} - -.btn:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); -} - -.btn-sm { - padding: 4px 8px; - font-size: 12px; -} - -.btn-secondary { - background-color: #007cba; - color: white; -} - -.btn-danger { - background-color: #dc3545; - color: white; -} - -.chart-header { - margin-bottom: 20px; - - h3 { - font-size: 22px; - font-weight: 600; - color: #0a192f; - margin: 0; - text-align: center; - padding-bottom: 10px; - border-bottom: 2px solid #3498db; - } -} - -.chart-wrapper { - flex: 1; +.bar-chart-container { position: relative; - - .chart-content { - position: relative; - height: 100%; - background: #f8f9fa; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 10px; - display: flex; - align-items: center; - justify-content: center; - - &.loading { - opacity: 0.7; - - canvas { - filter: blur(2px); - } - } - - canvas { - max-width: 100%; - max-height: 100%; - transition: filter 0.3s ease; - filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); - } - - canvas:hover { - filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15)); - transform: scale(1.02); - transition: all 0.3s ease; - } - - .no-data-message { - text-align: center; - padding: 30px; - color: #666; - font-size: 18px; - font-style: italic; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; - width: 100%; - } - - .no-data-message p { - margin: 0; - } - - .loading-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - background: rgba(255, 255, 255, 0.8); - - .shimmer-bar { - width: 80%; - height: 20px; - background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); - background-size: 200% 100%; - animation: shimmer 1.5s infinite; - border-radius: 4px; - } - } - } + height: 100%; + width: 100%; } -@keyframes shimmer { - 0% { - background-position: -200% 0; - } - 100% { - background-position: 200% 0; - } +canvas { + display: block; + max-width: 100%; + max-height: 100%; } // Responsive design for chart container @media (max-width: 768px) { - .chart-container { - padding: 15px; - } - - .chart-header h3 { - font-size: 18px; - } - - .drilldown-indicator { - flex-direction: column; - gap: 5px; - } - - .drilldown-text { - font-size: 14px; - } - - .compact-filters-container { - flex-wrap: wrap; + .bar-chart-container { + height: 300px; } } @media (max-width: 480px) { - .chart-container { - padding: 10px; - } - - .chart-header h3 { - font-size: 16px; + .bar-chart-container { + height: 250px; } } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts index 401b182..68105a0 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts @@ -140,12 +140,6 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { 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 { // Set flag to prevent recursive calls @@ -179,73 +173,41 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { console.log('Bar chart data URL:', url); // Convert baseFilters to filter parameters - let filterParams = ''; + const filterObj = {}; + + // Add base filters if (this.baseFilters && this.baseFilters.length > 0) { - const filterObj = {}; this.baseFilters.forEach(filter => { if (filter.field && filter.value) { filterObj[filter.field] = filter.value; } }); - if (Object.keys(filterObj).length > 0) { - filterParams = JSON.stringify(filterObj); - } } - console.log('Base filters:', this.baseFilters); - console.log('Base filter params:', filterParams); - // Add common filters to filter parameters + // Add common filters const commonFilters = this.filterService.getFilterValues(); const filterDefinitions = this.filterService.getFilters(); - console.log('Common filters from service:', commonFilters); - console.log('Filter definitions:', filterDefinitions); + Object.keys(commonFilters).forEach(filterId => { + const filterValue = commonFilters[filterId]; + + // 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) { - // Merge common filters with base filters - const mergedFilterObj = {}; - - // Add base filters first - if (filterParams) { - try { - const baseFilterObj = JSON.parse(filterParams); - Object.assign(mergedFilterObj, baseFilterObj); - } catch (e) { - console.warn('Failed to parse base filter parameters:', e); - } - } - - // Add common filters using the field name as the key, not the filter id - Object.keys(commonFilters).forEach(filterId => { - const filterValue = commonFilters[filterId]; - console.log(`Processing filter ID: ${filterId}, Value:`, filterValue); - - // Find the filter definition to get the field name - const filterDef = this.filterService.getFilters().find(f => f.id === filterId); - console.log(`Filter definition for ${filterId}:`, filterDef); - - if (filterDef && filterDef.field) { - const fieldName = filterDef.field; - console.log(`Mapping filter ID ${filterId} to field name: ${fieldName}`); - if (filterValue !== undefined && filterValue !== null && filterValue !== '') { - mergedFilterObj[fieldName] = filterValue; - console.log(`Added to merged filters: ${fieldName} =`, filterValue); - } - } else { - // Fallback to using filterId as field name if no field is defined - console.log(`No field name found for filter ID ${filterId}, using ID as field name`); - if (filterValue !== undefined && filterValue !== null && filterValue !== '') { - mergedFilterObj[filterId] = filterValue; - console.log(`Added to merged filters: ${filterId} =`, filterValue); - } - } - }); - - if (Object.keys(mergedFilterObj).length > 0) { - filterParams = JSON.stringify(mergedFilterObj); - } + // Convert to JSON string for API call + let filterParams = ''; + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); } - console.log('Final merged filter object:', filterParams); + console.log('Final filter object:', filterObj); // Fetch data from the dashboard service with parameter field and value // For base level, we pass empty parameter and value, but now also pass filters const subscription = this.dashboardService.getChartData(this.table, 'bar', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( @@ -400,61 +362,47 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { console.log('Drilldown data URL:', url); // Convert drilldown layer filters to filter parameters (if applicable) - let filterParams = ''; + const filterObj = {}; + + // Add drilldown layer filters if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { - const filterObj = {}; drilldownConfig.filters.forEach((filter: any) => { if (filter.field && filter.value) { filterObj[filter.field] = filter.value; } }); - if (Object.keys(filterObj).length > 0) { - filterParams = JSON.stringify(filterObj); - } } - console.log('Drilldown layer filter parameters:', filterParams); - // Convert drilldownFilters to filter parameters for drilldown level - let drilldownFilterParams = ''; + // Add drilldownFilters if (this.drilldownFilters && this.drilldownFilters.length > 0) { - const filterObj = {}; this.drilldownFilters.forEach(filter => { if (filter.field && filter.value) { filterObj[filter.field] = filter.value; } }); - if (Object.keys(filterObj).length > 0) { - drilldownFilterParams = JSON.stringify(filterObj); - } } - // Add common filters to drilldown filter parameters + // Add common filters const commonFilters = this.filterService.getFilterValues(); - if (Object.keys(commonFilters).length > 0) { - // Merge common filters with drilldown filters - const mergedFilterObj = {}; + const filterDefinitions = this.filterService.getFilters(); + Object.keys(commonFilters).forEach(filterId => { + const filterValue = commonFilters[filterId]; - // Add drilldown filters first - if (drilldownFilterParams) { - try { - const drilldownFilterObj = JSON.parse(drilldownFilterParams); - Object.assign(mergedFilterObj, drilldownFilterObj); - } catch (e) { - console.warn('Failed to parse drilldown filter parameters:', e); + // Find the filter definition to get the field name + const filterDef = this.filterService.getFilters().find(f => f.id === filterId); + + if (filterDef && filterDef.field) { + const fieldName = filterDef.field; + if (filterValue !== undefined && filterValue !== null && filterValue !== '') { + filterObj[fieldName] = filterValue; } } - - // Add common filters - Object.keys(commonFilters).forEach(key => { - const value = commonFilters[key]; - if (value !== undefined && value !== null && value !== '') { - mergedFilterObj[key] = value; - } - }); - - if (Object.keys(mergedFilterObj).length > 0) { - drilldownFilterParams = JSON.stringify(mergedFilterObj); - } + }); + + // Convert to JSON string for API call + let drilldownFilterParams = ''; + if (Object.keys(filterObj).length > 0) { + drilldownFilterParams = JSON.stringify(filterObj); } console.log('Drilldown filter parameters:', drilldownFilterParams); diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.html index 2aad01b..293bd06 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.html @@ -4,9 +4,41 @@

{{charttitle || 'Data Grid'}}

+ +
+ +
+ + +
+
+
+
+
+
+ +
+ + Drilldown Level: {{currentDrilldownLevel}} + + (Clicked on: {{drilldownStack[drilldownStack.length - 1].clickedKey}} = {{drilldownStack[drilldownStack.length - 1].clickedValue}}) + + +
+
+
+
+
+ - Loading ... + + + Loading ... +
{{error}}
@@ -19,7 +51,7 @@ - + {{item[header.key]}} diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.scss b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.scss index 99c5f44..4668268 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.scss +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.scss @@ -1,12 +1,28 @@ -@import '../../../../../../../styles1.scss'; -input.ng-invalid.ng-touched { - border-color: red; +// Add styles for drilldown navigation +.alert-info { + background-color: #dcedf7; + border-color: #a3d4f5; + color: #21333b; } -.error_mess { - color: red; +.alert-info .alert-icon { + color: #0072a3; } -clr-datagrid{ - height: 400px; /* Adjust the height as needed */ - overflow-y: auto; + +.btn-link { + color: #0072a3; + text-decoration: none; +} + +.btn-link:hover { + color: #00567a; + text-decoration: underline; +} + +.dg-wrapper { + padding: 12px; +} + +.clr-row { + margin-bottom: 12px; } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.ts index 06f3023..f51bc2b 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/grid-view/grid-view.component.ts @@ -1,13 +1,17 @@ -import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, OnInit, Input, OnChanges, OnDestroy, SimpleChanges, ViewChild } from '@angular/core'; import { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service'; import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; +// Add FilterService import +import { FilterService } from '../../common-filter/filter.service'; +// Add Subscription import +import { Subscription } from 'rxjs'; @Component({ selector: 'app-grid-view', templateUrl: './grid-view.component.html', styleUrls: ['./grid-view.component.scss'] }) -export class GridViewComponent implements OnInit, OnChanges { +export class GridViewComponent implements OnInit, OnChanges, OnDestroy { @Input() xAxis: string; @Input() yAxis: string | string[]; @Input() table: string; @@ -23,6 +27,16 @@ export class GridViewComponent implements OnInit, OnChanges { @Input() datasource: string; @Input() fieldName: string; @Input() connection: number; // Add connection input + // Drilldown configuration inputs + @Input() drilldownEnabled: boolean = false; + @Input() drilldownApiUrl: string; + @Input() drilldownXAxis: string; + @Input() drilldownYAxis: string; + @Input() drilldownParameter: string; // Add drilldown parameter input + @Input() baseFilters: any[] = []; // Add base filters input + @Input() drilldownFilters: any[] = []; // Add drilldown filters input + // Multi-layer drilldown configuration inputs + @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations loading = false; givendata: any[] = []; @@ -38,13 +52,37 @@ export class GridViewComponent implements OnInit, OnChanges { submitted = false; dynamicHeaders: any[] = []; - constructor( + // Multi-layer drilldown state tracking + drilldownStack: any[] = []; // Stack to track drilldown navigation history + currentDrilldownLevel: number = 0; // Current drilldown level (0 = base level) + originalGridData: any[] = []; + // No data state + noDataAvailable: boolean = false; + + // Flag to prevent infinite loops + private isFetchingData: boolean = false; + + // Add subscriptions to unsubscribe on destroy + private subscriptions: Subscription[] = []; + + constructor( private mainservice: UsergrpmaintainceService, private dashboardService: Dashboard3Service, + // Add FilterService to constructor + private filterService: FilterService ) { } ngOnInit(): void { + // Subscribe to filter changes + this.subscriptions.push( + this.filterService.filterState$.subscribe(filters => { + // When filters change, refresh the grid data + console.log('GridView: Filter state changed:', filters); + this.fetchGridData(); + }) + ); + this.fetchGridData(); } @@ -55,87 +93,501 @@ export class GridViewComponent implements OnInit, OnChanges { const xAxisChanged = changes.xAxis && !changes.xAxis.firstChange; const yAxisChanged = changes.yAxis && !changes.yAxis.firstChange; const tableChanged = changes.table && !changes.table.firstChange; - const connectionChanged = changes.connection && !changes.connection.firstChange; // Add connection change detection + const connectionChanged = changes.connection && !changes.connection.firstChange; + const baseFiltersChanged = changes.baseFilters && !changes.baseFilters.firstChange; + // Drilldown configuration changes + const drilldownEnabledChanged = changes.drilldownEnabled && !changes.drilldownEnabled.firstChange; + const drilldownApiUrlChanged = changes.drilldownApiUrl && !changes.drilldownApiUrl.firstChange; + const drilldownXAxisChanged = changes.drilldownXAxis && !changes.drilldownXAxis.firstChange; + const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; + const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; // Respond to input changes - if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged) { - console.log('X or Y axis or table or connection changed, fetching new data'); - // Only fetch data if xAxis, yAxis, table, or connection has changed (and it's not the first change) + if (!this.isFetchingData && (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || + drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || + drilldownLayersChanged)) { + console.log('X or Y axis or table or connection or base filters or drilldown config changed, fetching new data'); + // Only fetch data if xAxis, yAxis, table, connection, baseFilters or drilldown config has changed (and it's not the first change) this.fetchGridData(); } } // Dynamic headers for the grid - + fetchGridData(): void { + // Set flag to prevent recursive calls + this.isFetchingData = true; + + // If we're in drilldown mode, fetch the appropriate drilldown data + if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) { + this.fetchDrilldownData(); + // Reset flag after fetching + this.isFetchingData = false; + return; + } + // If we have the necessary data, fetch grid data from the service - if (this.table && this.xAxis) { - console.log('Fetching grid data for:', { table: this.table, xAxis: this.xAxis, yAxis: this.yAxis, connection: this.connection }); + // if (this.table && this.xAxis) { + if (this.table) { + + console.log('=== GRID VIEW DEBUG INFO ==='); + console.log('Table:', this.table); + console.log('X-Axis:', this.xAxis); + console.log('Y-Axis:', this.yAxis); + console.log('Connection:', this.connection); // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; + // Get the parameter value from the drilldown stack for base level (should be empty) + let parameterValue = ''; + + // Log the URL that will be called + let url = `chart/getdashjson/grid?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; + console.log('Grid data URL:', url); + + // Get filter parameters from base filters + const filterObj = {}; + + // Add base filters + if (this.baseFilters && this.baseFilters.length > 0) { + this.baseFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + } + + // Add common filters directly as key-value pairs + const commonFilters = this.filterService.getFilterValues(); + const filterDefinitions = this.filterService.getFilters(); + + // Add common filters using the field name as the key + Object.keys(commonFilters).forEach(filterId => { + const filterValue = commonFilters[filterId]; + + // Find the filter definition to get the field name + const filterDef = this.filterService.getFilters().find(f => f.id === filterId); + + if (filterDef && filterDef.field) { + const fieldName = filterDef.field; + if (filterValue !== undefined && filterValue !== null && filterValue !== '') { + filterObj[fieldName] = filterValue; + } + } + }); + + // Convert to JSON string for API call + let filterParams = ''; + if (Object.keys(filterObj).length > 0) { + filterParams = JSON.stringify(filterObj); + } + + console.log('GridView: Final filter object to send to API:', filterObj); + // Fetch data from the dashboard service, similar to other chart components - this.dashboardService.getChartData(this.table, 'grid', this.xAxis, yAxisString, this.connection).subscribe( + this.dashboardService.getChartData(this.table, 'grid', this.xAxis, yAxisString, this.connection, '', '', filterParams).subscribe( (data: any) => { + console.log('=== GRID VIEW DATA RESPONSE ==='); console.log('Received grid data:', data); if (data === null) { console.warn('Grid API returned null data. Check if the API endpoint is working correctly.'); this.error = "No data Available"; + this.noDataAvailable = true; + // Reset flag after fetching + this.isFetchingData = false; return; } - + // 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 data:', this.givendata); } else if (data && data.data) { // Handle the original expected format as fallback this.givendata = data.data; this.extractDynamicHeaders(data.data); this.error = this.givendata.length === 0 ? "No data Available" : undefined; + this.noDataAvailable = this.givendata.length === 0; console.log('Updated grid with legacy data format:', this.givendata); } else if (Array.isArray(data)) { // Handle case where data is directly an array this.givendata = data; this.extractDynamicHeaders(data); this.error = this.givendata.length === 0 ? "No data Available" : undefined; + this.noDataAvailable = this.givendata.length === 0; console.log('Updated grid with array data:', this.givendata); } else { console.warn('Grid received data does not have expected structure', data); this.error = "No valid data received"; this.givendata = []; + this.noDataAvailable = true; } + // Reset flag after fetching + this.isFetchingData = false; }, (error) => { console.log('Error fetching grid data:', error); this.error = "Server Error"; + this.noDataAvailable = true; + // Reset flag after fetching + this.isFetchingData = false; }); } else if (this.table) { console.log('Missing xAxis, falling back to default data fetching'); // Fallback to default data fetching when only table is provided - // Convert yAxis to string if it's an array + // Convert yAxis to string if it's an array const yAxisString = Array.isArray(this.yAxis) ? this.yAxis.join(',') : this.yAxis; // Fetch data from the dashboard service, similar to other chart components this.dashboardService.getChartData(this.table, 'grid', this.xAxis, yAxisString, this.connection).subscribe( (data: any) => { - // this.mainservice.getAll().subscribe((data: any) => { - console.log('recv data ', data); - this.givendata = Array.isArray(data) ? data : []; - this.extractDynamicHeaders(data); - this.error = this.givendata && this.givendata.length === 0 ? "No data Available" : undefined; - }, (error) => { - console.log(error); - this.error = "Server Error"; - }); + // this.mainservice.getAll().subscribe((data: any) => { + console.log('recv data ', data); + this.givendata = Array.isArray(data) ? data : []; + this.extractDynamicHeaders(data); + this.error = this.givendata && this.givendata.length === 0 ? "No data Available" : undefined; + this.noDataAvailable = this.givendata && this.givendata.length === 0; + // Reset flag after fetching + this.isFetchingData = false; + }, (error) => { + console.log(error); + this.error = "Server Error"; + this.noDataAvailable = true; + // Reset flag after fetching + this.isFetchingData = false; + }); } else { console.log('Missing required data for grid:', { table: this.table }); this.error = "Table name is required"; + this.noDataAvailable = true; + // Reset flag after fetching + this.isFetchingData = false; } } - + + // Fetch drilldown data based on current drilldown level + fetchDrilldownData(): void { + console.log('Fetching drilldown data, current level:', this.currentDrilldownLevel); + console.log('Drilldown stack:', this.drilldownStack); + + // Get the current drilldown configuration based on the current level + let drilldownConfig; + if (this.currentDrilldownLevel === 1) { + // Base drilldown level + drilldownConfig = { + apiUrl: this.drilldownApiUrl, + xAxis: this.drilldownXAxis, + yAxis: this.drilldownYAxis, + parameter: this.drilldownParameter + }; + } else { + // Multi-layer drilldown level + const layerIndex = this.currentDrilldownLevel - 2; // -2 because level 1 is base drilldown + if (layerIndex >= 0 && layerIndex < this.drilldownLayers.length) { + drilldownConfig = this.drilldownLayers[layerIndex]; + } else { + console.warn('Invalid drilldown layer index:', layerIndex); + this.error = "Invalid drilldown configuration"; + this.givendata = []; + this.noDataAvailable = true; + return; + } + } + + console.log('Drilldown config for level', this.currentDrilldownLevel, ':', drilldownConfig); + + // Check if we have valid drilldown configuration + if (!drilldownConfig || !drilldownConfig.apiUrl || !drilldownConfig.xAxis || !drilldownConfig.yAxis) { + console.warn('Missing drilldown configuration for level:', this.currentDrilldownLevel); + this.error = "Missing drilldown configuration"; + this.givendata = []; + this.noDataAvailable = true; + return; + } + + // Get the parameter value from the drilldown stack + let parameterValue = ''; + if (this.drilldownStack.length > 0) { + const lastEntry = this.drilldownStack[this.drilldownStack.length - 1]; + parameterValue = lastEntry.clickedValue || ''; + console.log('Parameter value from last click:', parameterValue); + } + + // Get the parameter field from drilldown config + const parameterField = drilldownConfig.parameter || ''; + console.log('Parameter field:', parameterField); + + console.log('Fetching drilldown data for level:', this.currentDrilldownLevel, { + apiUrl: drilldownConfig.apiUrl, + xAxis: drilldownConfig.xAxis, + yAxis: drilldownConfig.yAxis, + parameterField: parameterField, + parameterValue: parameterValue, + connection: this.connection + }); + + // Build the actual API URL with parameter replacement + let actualApiUrl = drilldownConfig.apiUrl; + console.log('Original API URL:', actualApiUrl); + console.log('Parameter value to use:', parameterValue); + console.log('Parameter field:', parameterField); + + // Check if the URL contains angle brackets for parameter replacement + const hasAngleBrackets = /<[^>]+>/.test(actualApiUrl); + + if (hasAngleBrackets && parameterValue) { + // Replace angle brackets placeholder with actual value + console.log('Replacing angle brackets with parameter value'); + const encodedValue = encodeURIComponent(parameterValue); + actualApiUrl = actualApiUrl.replace(/<[^>]+>/g, encodedValue); + console.log('URL after angle bracket replacement:', actualApiUrl); + } + + // Log the URL that will be called + let url = `chart/getdashjson/grid?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; + if (parameterField && parameterValue) { + url += `¶meter=${encodeURIComponent(parameterField)}¶meterValue=${encodeURIComponent(parameterValue)}`; + } + console.log('Drilldown data URL:', url); + + // Convert drilldown layer filters to filter parameters (if applicable) + const filterObj = {}; + + // Add drilldown layer filters + if (drilldownConfig.filters && drilldownConfig.filters.length > 0) { + drilldownConfig.filters.forEach((filter: any) => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + } + + // Add drilldownFilters + if (this.drilldownFilters && this.drilldownFilters.length > 0) { + this.drilldownFilters.forEach(filter => { + if (filter.field && filter.value) { + filterObj[filter.field] = filter.value; + } + }); + } + + // Add common filters + const commonFilters = this.filterService.getFilterValues(); + const filterDefinitions = this.filterService.getFilters(); + Object.keys(commonFilters).forEach(filterId => { + const filterValue = commonFilters[filterId]; + + // Find the filter definition to get the field name + const filterDef = this.filterService.getFilters().find(f => f.id === filterId); + + if (filterDef && filterDef.field) { + const fieldName = filterDef.field; + if (filterValue !== undefined && filterValue !== null && filterValue !== '') { + filterObj[fieldName] = filterValue; + } + } + }); + + // Convert to JSON string for API call + let drilldownFilterParams = ''; + if (Object.keys(filterObj).length > 0) { + drilldownFilterParams = JSON.stringify(filterObj); + } + + console.log('Drilldown filter parameters:', drilldownFilterParams); + + // For drilldown level, we pass the parameter value from the drilldown stack and drilldown filters + this.dashboardService.getChartData( + drilldownConfig.apiUrl, 'grid', + drilldownConfig.xAxis, drilldownConfig.yAxis, + this.connection, + parameterField, parameterValue, + drilldownFilterParams + ).subscribe( + (data: any) => { + console.log('Received drilldown data:', data); + if (data === null) { + console.warn('Drilldown API returned null data. Check if the API endpoint is working correctly.'); + this.error = "No data Available"; + this.givendata = []; + this.noDataAvailable = true; + return; + } + + // Handle the actual data structure returned by the API + if (data && data.chartData) { + this.givendata = data.chartData; + this.extractDynamicHeaders(data.chartData); + this.error = this.givendata.length === 0 ? "No data Available" : undefined; + this.noDataAvailable = this.givendata.length === 0; + console.log('Updated grid with drilldown data:', this.givendata); + } else if (data && data.data) { + // Handle the original expected format as fallback + this.givendata = data.data; + this.extractDynamicHeaders(data.data); + this.error = this.givendata.length === 0 ? "No data Available" : undefined; + this.noDataAvailable = this.givendata.length === 0; + console.log('Updated grid with drilldown legacy data format:', this.givendata); + } else if (Array.isArray(data)) { + // Handle case where data is directly an array + this.givendata = data; + this.extractDynamicHeaders(data); + this.error = this.givendata.length === 0 ? "No data Available" : undefined; + this.noDataAvailable = this.givendata.length === 0; + console.log('Updated grid with drilldown array data:', this.givendata); + } else { + console.warn('Drilldown received data does not have expected structure', data); + this.error = "No valid data received"; + this.givendata = []; + this.noDataAvailable = true; + } + }, + (error) => { + console.error('Error fetching drilldown data:', error); + this.error = "Server Error"; + this.givendata = []; + this.noDataAvailable = true; + } + ); + } + + // Reset to original data (go back to base level) + resetToOriginalData(): void { + console.log('Resetting to original data'); + console.log('Current stack before reset:', this.drilldownStack); + console.log('Current level before reset:', this.currentDrilldownLevel); + + this.currentDrilldownLevel = 0; + this.drilldownStack = []; + + if (this.originalGridData.length > 0) { + // Create a deep copy to avoid reference issues + this.givendata = JSON.parse(JSON.stringify(this.originalGridData)); + this.extractDynamicHeaders(this.givendata); + console.log('Restored original data'); + } + + console.log('After reset - data:', this.givendata); + + // Re-fetch original data + this.fetchGridData(); + } + + // Navigate back to previous drilldown level + navigateBack(): void { + console.log('Navigating back, current stack:', this.drilldownStack); + console.log('Current level:', this.currentDrilldownLevel); + + if (this.drilldownStack.length > 0) { + // Remove the last entry from the stack + const removedEntry = this.drilldownStack.pop(); + console.log('Removed entry from stack:', removedEntry); + + // Update the current drilldown level + this.currentDrilldownLevel = this.drilldownStack.length; + console.log('New level after pop:', this.currentDrilldownLevel); + console.log('Stack after pop:', this.drilldownStack); + + if (this.drilldownStack.length > 0) { + // Fetch data for the previous level + console.log('Fetching data for previous level'); + this.fetchDrilldownData(); + } else { + // Back to base level + console.log('Back to base level, resetting to original data'); + this.resetToOriginalData(); + } + } else { + // Already at base level, reset to original data + console.log('Already at base level, resetting to original data'); + this.resetToOriginalData(); + } + } + + // Method to handle grid row clicks for drilldown + onRowClick(item: any, key: string): void { + console.log('Grid row clicked:', { item, key }); + + // If drilldown is enabled + if (this.drilldownEnabled) { + // Get the value for the clicked key + const clickedValue = item[key]; + + console.log('Clicked on row value:', { key, value: clickedValue }); + + // If we're not at the base level, store original data + if (this.currentDrilldownLevel === 0) { + // Store original data before entering drilldown mode + // Create a deep copy to avoid reference issues + this.originalGridData = JSON.parse(JSON.stringify(this.givendata)); + console.log('Stored original data for drilldown'); + } + + // Determine the next drilldown level + const nextDrilldownLevel = this.currentDrilldownLevel + 1; + + console.log('Next drilldown level will be:', nextDrilldownLevel); + + // Check if there's a drilldown configuration for this level + let hasDrilldownConfig = false; + let drilldownConfig; + + if (nextDrilldownLevel === 1) { + // Base drilldown level + drilldownConfig = { + apiUrl: this.drilldownApiUrl, + xAxis: this.drilldownXAxis, + yAxis: this.drilldownYAxis, + parameter: this.drilldownParameter + }; + hasDrilldownConfig = !!this.drilldownApiUrl && !!this.drilldownXAxis && !!this.drilldownYAxis; + } else { + // Multi-layer drilldown level + const layerIndex = nextDrilldownLevel - 2; // -2 because level 1 is base drilldown + if (layerIndex < this.drilldownLayers.length) { + drilldownConfig = this.drilldownLayers[layerIndex]; + hasDrilldownConfig = drilldownConfig.enabled && + !!drilldownConfig.apiUrl && + !!drilldownConfig.xAxis && + !!drilldownConfig.yAxis; + } + } + + console.log('Drilldown config for next level:', drilldownConfig); + console.log('Has drilldown config:', hasDrilldownConfig); + + // If there's a drilldown configuration for the next level, proceed + if (hasDrilldownConfig) { + // Add this click to the drilldown stack + const stackEntry = { + level: nextDrilldownLevel, + clickedKey: key, + clickedValue: clickedValue + }; + + this.drilldownStack.push(stackEntry); + + console.log('Added to drilldown stack:', stackEntry); + console.log('Current drilldown stack:', this.drilldownStack); + + // Update the current drilldown level + this.currentDrilldownLevel = nextDrilldownLevel; + + console.log('Entering drilldown level:', this.currentDrilldownLevel); + + // Fetch drilldown data for the new level + this.fetchDrilldownData(); + } else { + console.log('No drilldown configuration for level:', nextDrilldownLevel); + } + } else { + console.log('Drilldown not enabled'); + } + } + /** * Extract dynamic headers from the data * @param data Array of data objects @@ -143,7 +595,7 @@ export class GridViewComponent implements OnInit, OnChanges { private extractDynamicHeaders(data: any): void { // Ensure data is an array const dataArray = Array.isArray(data) ? data : []; - + if (dataArray && dataArray.length > 0) { // Get all unique keys from the data objects const allKeys = new Set(); @@ -152,19 +604,19 @@ export class GridViewComponent implements OnInit, OnChanges { Object.keys(item).forEach(key => allKeys.add(key)); } }); - + // Convert to array of header objects with key and display name this.dynamicHeaders = Array.from(allKeys).map(key => ({ key: key, displayName: this.formatHeader(key) })); - + console.log('Dynamic headers extracted:', this.dynamicHeaders); } else { this.dynamicHeaders = []; } } - + /** * Format header name for better display * @param key The key to format @@ -175,4 +627,23 @@ export class GridViewComponent implements OnInit, OnChanges { .replace(/([A-Z])/g, ' $1') .replace(/^./, str => str.toUpperCase()); } + + ngOnDestroy() { + // Unsubscribe from all subscriptions to prevent memory leaks + console.log('GridViewComponent ngOnDestroy called, unsubscribing from', this.subscriptions.length, 'subscriptions'); + this.subscriptions.forEach(subscription => { + if (subscription && !subscription.closed) { + subscription.unsubscribe(); + } + }); + this.subscriptions = []; + + // Clear data to help with garbage collection + this.givendata = []; + this.dynamicHeaders = []; + this.drilldownStack = []; + this.originalGridData = []; + + console.log('GridViewComponent destroyed and cleaned up'); + } } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardrunner/dashrunnerall/dashrunnerall.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardrunner/dashrunnerall/dashrunnerall.component.html index 74d44e3..59fe60d 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardrunner/dashrunnerall/dashrunnerall.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardrunner/dashrunnerall/dashrunnerall.component.html @@ -11,6 +11,9 @@

{{ 'all_dashboard' | translate }}

+ @@ -111,7 +114,4 @@
- - - - \ No newline at end of file + \ No newline at end of file