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 0000000..bf020b9 Binary files /dev/null and b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart.zip differ diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html index 7a1bccb..83d155a 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html @@ -1,50 +1,32 @@ -
- -
- - -
+
+ + -
- 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