diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake 3.zip b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake 3.zip new file mode 100644 index 0000000..d74492d Binary files /dev/null and b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake 3.zip differ diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake/Data_lake.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake/Data_lake.component.html index 506a5d4..bb62d9a 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake/Data_lake.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake/Data_lake.component.html @@ -62,6 +62,11 @@ SureConnect + + + Reference Data Lake + + Calculated Fields @@ -126,6 +131,9 @@ {{user.sureconnect_name}} + + {{user.ref_datalake_name}} + @@ -154,7 +162,7 @@ - @@ -426,6 +434,15 @@ + +
+ + +
@@ -546,6 +563,15 @@
+ +
+ + +
@@ -890,4 +916,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake/Data_lake.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake/Data_lake.component.ts index ac4cc80..6cc9432 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake/Data_lake.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/Data_lake/Data_lake.component.ts @@ -62,6 +62,10 @@ export class Data_lakeComponent implements OnInit { sureConnectList; selectedSureConnect: any = null; + // Data Lake reference properties + dataLakeList: any[] = []; + selectedDataLake: any = null; + // Calculated field properties calculatedFieldModalOpen = false; availableKeys: string[] = []; @@ -117,6 +121,7 @@ export class Data_lakeComponent implements OnInit { this.userrole = this.userInfoService.getRoles(); this.getData(); this.getSureConnectList(); // Fetch SureConnect list + this.getDataLakeList(); // Fetch Data Lake list for reference dropdown this.entryForm = this._fb.group({ name: [null], @@ -133,6 +138,7 @@ export class Data_lakeComponent implements OnInit { batch_volume: [null], sure_connect_id: [null], // Add SureConnect field + ref_datalake_id: [null], // Add Data Lake reference field }); // component_button200 // form code start @@ -231,6 +237,17 @@ export class Data_lakeComponent implements OnInit { }); } + // Fetch Data Lake list for reference dropdown + getDataLakeList() { + this.mainService.getAll().subscribe((data: any[]) => { + // Filter out the current item to avoid self-reference + this.dataLakeList = data.filter(item => item.id !== (this.rowSelected?.id || 0)); + console.log('Data Lake List:', this.dataLakeList); + }, (error) => { + console.log('Error fetching Data Lake list:', error); + }); + } + // Get operation symbol for display getOperationSymbol(operation: string): string { switch(operation) { @@ -245,7 +262,7 @@ export class Data_lakeComponent implements OnInit { } // Fetch available keys from API - fetchAvailableKeys(dataLakeItem: any) { + fetchAvailableKeys(dataLakeItem: any, isGroupBy: boolean = false) { if (!dataLakeItem.url || !dataLakeItem.sure_connect_id) { this.toastr.error('URL and SureConnect ID are required to fetch keys'); return; @@ -256,11 +273,56 @@ export class Data_lakeComponent implements OnInit { // Call the service method to get all keys this.mainService.fetchAvailableKeys(dataLakeItem.url, dataLakeItem.sure_connect_id).subscribe( (keys: string[]) => { - this.availableKeys = keys; + // Combine API keys with calculated field names + const calculatedFieldNames = this.getCalculatedFieldNames(); + this.availableKeys = [...keys, ...calculatedFieldNames]; + // Reset the calculated field form to ensure it starts fresh this.resetCalculatedFieldForm(); - this.calculatedFieldModalOpen = true; - console.log('Available keys:', keys); + + // Preload existing calculated fields if they exist + if (dataLakeItem.calculated_field_json) { + try { + const existingFields = JSON.parse(dataLakeItem.calculated_field_json); + if (Array.isArray(existingFields)) { + this.calculatedFields = [...existingFields]; + this.toastr.info(`Loaded ${existingFields.length} existing calculated fields`); + } + } catch (e) { + console.error('Error parsing calculated_field_json:', e); + this.calculatedFields = []; + } + } else { + this.calculatedFields = []; + } + + // Open the appropriate modal based on the context + if (isGroupBy) { + // For group by, we need to extract existing group by configurations + if (dataLakeItem.calculated_field_json) { + try { + const existingFields = JSON.parse(dataLakeItem.calculated_field_json); + if (Array.isArray(existingFields)) { + // Find group by configurations + const groupByConfigs = existingFields.filter(field => field.type === 'groupby'); + if (groupByConfigs.length > 0) { + // For simplicity, we'll load the first group by configuration + const firstConfig = groupByConfigs[0]; + this.selectedGroupByFields = [...firstConfig.groupFields]; + this.selectedAggregationFields = [...firstConfig.aggregations]; + this.toastr.info(`Loaded existing group by configuration`); + } + } + } catch (e) { + console.error('Error parsing calculated_field_json for group by:', e); + } + } + this.showGroupByModal = true; + } else { + this.calculatedFieldModalOpen = true; + } + + console.log('Available keys:', this.availableKeys); }, (error) => { console.error('Error fetching keys:', error); @@ -335,7 +397,6 @@ export class Data_lakeComponent implements OnInit { } } } - // Create calculated field object for complex equation const calculatedField = { id: Date.now(), @@ -347,6 +408,9 @@ export class Data_lakeComponent implements OnInit { this.calculatedFields.push(calculatedField); this.toastr.success('Complex calculated field added successfully'); + + // Refresh available keys to include the new calculated field + this.refreshAvailableKeys(); // Reset form but keep one empty field component this.calculatedFieldForm.reset({ @@ -419,6 +483,9 @@ export class Data_lakeComponent implements OnInit { this.calculatedFields.push(calculatedField); this.toastr.success('Calculated field added successfully'); + + // Refresh available keys to include the new calculated field + this.refreshAvailableKeys(); // Reset form but keep one empty field component this.calculatedFieldForm.reset({ @@ -609,6 +676,9 @@ export class Data_lakeComponent implements OnInit { deleteCalculatedField(id: number) { this.calculatedFields = this.calculatedFields.filter(field => field.id !== id); this.toastr.success('Calculated field deleted'); + + // Refresh available keys to reflect the deleted field + this.refreshAvailableKeys(); } onEdit(row) { @@ -616,6 +686,8 @@ export class Data_lakeComponent implements OnInit { this.editCronExpression = row.cron_job || ''; // Set the selected SureConnect for edit form this.selectedSureConnect = row.sure_connect_id || null; + // Set the selected Data Lake for edit form + this.selectedDataLake = row.ref_datalake_id || null; // Use setTimeout to ensure the component has time to initialize setTimeout(() => { this.modalEdit = true; @@ -769,6 +841,7 @@ export class Data_lakeComponent implements OnInit { this.modalAdd = true; this.addCronExpression = ''; this.selectedSureConnect = null; // Reset SureConnect selection + this.selectedDataLake = null; // Reset Data Lake selection this.submitted = false; // Reset the form control for cron_job and sure_connect_id if (this.entryForm) { @@ -801,7 +874,7 @@ export class Data_lakeComponent implements OnInit { } // Generate JSON from calculated fields and update the record - updateCalculatedFields() { + updateCalculatedFields(closeModal: boolean = true) { if (!this.selectedDataLakeItem || this.calculatedFields.length === 0) { this.toastr.error('No calculated fields to update'); return; @@ -827,8 +900,11 @@ export class Data_lakeComponent implements OnInit { this.product[index].iscalculatedfield = true; } - // Close the modal only when called directly from the UI - if (this.calculatedFieldModalOpen) { + // Refresh available keys to ensure they include the latest calculated fields + this.refreshAvailableKeys(); + + // Close the modal only when called directly from the UI and closeModal is true + if (closeModal && this.calculatedFieldModalOpen) { this.calculatedFieldModalOpen = false; } }, @@ -995,15 +1071,44 @@ export class Data_lakeComponent implements OnInit { openGroupByModal(dataLakeItem: any) { this.selectedDataLakeItem = dataLakeItem; - // Fetch available keys if not already fetched + // Fetch available keys if not already fetched or if called directly if (this.availableKeys.length === 0) { - this.fetchAvailableKeys(dataLakeItem); + this.fetchAvailableKeys(dataLakeItem, true); // Pass true to indicate group by context + } else { + // Keys are already available, just preload existing data and open modal + + // Initialize selected group by fields as empty array + this.selectedGroupByFields = []; + this.selectedAggregationFields = []; + + // Preload existing group by configurations if they exist in calculated_field_json + // (Group by configurations are stored in the same calculated_field_json field) + if (dataLakeItem.calculated_field_json) { + try { + const existingFields = JSON.parse(dataLakeItem.calculated_field_json); + if (Array.isArray(existingFields)) { + this.calculatedFields = [...existingFields]; + + // Find group by configurations + const groupByConfigs = existingFields.filter(field => field.type === 'groupby'); + if (groupByConfigs.length > 0) { + // For simplicity, we'll load the first group by configuration + const firstConfig = groupByConfigs[0]; + this.selectedGroupByFields = [...firstConfig.groupFields]; + this.selectedAggregationFields = [...firstConfig.aggregations]; + this.toastr.info(`Loaded existing group by configuration`); + } + } + } catch (e) { + console.error('Error parsing calculated_field_json:', e); + this.calculatedFields = []; + } + } else { + this.calculatedFields = []; + } + + this.showGroupByModal = true; } - - // Initialize selected group by fields as empty array - this.selectedGroupByFields = []; - this.selectedAggregationFields = []; - this.showGroupByModal = true; } // Method to add a field to group by selection @@ -1072,41 +1177,17 @@ export class Data_lakeComponent implements OnInit { // New method to update calculated fields after adding group by configuration updateCalculatedFieldsAfterGroupBy() { - if (!this.selectedDataLakeItem || this.calculatedFields.length === 0) { - return; - } - - // Convert calculated fields to JSON string - const calculatedFieldJson = JSON.stringify(this.calculatedFields); - - // Call the service method to update the record - this.mainService.updateCalculatedFields( - this.selectedDataLakeItem.id, - calculatedFieldJson, - true // iscalculatedfield = true - ).subscribe( - (response) => { - console.log('Calculated fields updated successfully:', response); - this.toastr.success('Group by configuration saved successfully'); - - // Update the local data - const index = this.product.findIndex(item => item.id === this.selectedDataLakeItem.id); - if (index !== -1) { - this.product[index].calculated_field_json = calculatedFieldJson; - this.product[index].iscalculatedfield = true; - } - }, - (error) => { - console.error('Error updating calculated fields:', error); - this.toastr.error('Failed to save group by configuration'); - } - ); + // Simply call the main update method without closing the modal + this.updateCalculatedFields(false); } // Method to remove group by configuration removeGroupByConfig(id: number) { this.calculatedFields = this.calculatedFields.filter(field => field.id !== id || field.type !== 'groupby'); this.toastr.success('Group by configuration removed'); + + // Refresh available keys to reflect the removed configuration + this.refreshAvailableKeys(); } // Method to check if a field is selected for group by @@ -1114,6 +1195,34 @@ export class Data_lakeComponent implements OnInit { return this.selectedGroupByFields.includes(field); } + // Helper method to get calculated field names + getCalculatedFieldNames(): string[] { + return this.calculatedFields + .filter(field => field.type !== 'groupby') // Exclude group by configurations + .map(field => field.fieldName); + } + + // Method to refresh available keys including calculated field names + refreshAvailableKeys() { + if (this.selectedDataLakeItem) { + // Fetch fresh keys from API and combine with calculated field names + this.mainService.fetchAvailableKeys(this.selectedDataLakeItem.url, this.selectedDataLakeItem.sure_connect_id).subscribe( + (keys: string[]) => { + const calculatedFieldNames = this.getCalculatedFieldNames(); + this.availableKeys = [...keys, ...calculatedFieldNames]; + }, + (error) => { + console.error('Error refreshing keys:', error); + // Fallback to just calculated field names if API fails + this.availableKeys = this.getCalculatedFieldNames(); + } + ); + } else { + // Fallback to just calculated field names + this.availableKeys = this.getCalculatedFieldNames(); + } + } + // Helper method to get the count of calculated fields getCalculatedFieldsCount(calculatedFieldJson: string): number { try { diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/FILTER_CONFIGURATION_GUIDE.md b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/FILTER_CONFIGURATION_GUIDE.md new file mode 100644 index 0000000..ffc1d7a --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/FILTER_CONFIGURATION_GUIDE.md @@ -0,0 +1,161 @@ +# Filter Configuration Guide for Dashboard Editor + +## Overview +This guide explains how to configure filters for chart components using the dashboard editor. The filter functionality allows users to dynamically filter chart data using multiple filter types. + +## Enabling Filters + +To enable filters for a chart component: + +1. Open the chart configuration modal by clicking the edit icon on any chart +2. Scroll down to the "Filter Configuration" section +3. Check the "Enable Filters" checkbox + +## Adding Filter Fields + +Once filters are enabled, you can add filter fields: + +1. Click the "Add Filter Field" button +2. Configure each filter field with the following properties: + +### Filter Field Properties + +#### Field Name +- The field name to filter on (e.g., 'category', 'name', 'amount') +- This should match the field name in your data source + +#### Display Label (Optional) +- Label to display for this filter in the UI +- If not provided, the field name will be used as the label + +#### Filter Type +Choose one of the following filter types: + +1. **Text Input** + - Creates a text input field + - Users can type to filter data + - Works with any text-based field + +2. **Dropdown** + - Creates a dropdown selection field + - Requires a comma-separated list of options + - Example options: "A,B,C" or "North, South, East, West" + +3. **Number Range** + - Creates two number input fields (min and max) + - Used for filtering numeric data within a range + - Example: Filter sales between 100 and 500 + +#### Dropdown Options (For Dropdown Type Only) +- Comma-separated list of options for dropdown filters +- Example: "Option1,Option2,Option3" +- Each option will appear as a selectable item in the dropdown + +## Example Configurations + +### Example 1: Simple Text Filter +``` +Field Name: name +Display Label: Product Name +Filter Type: Text Input +``` + +### Example 2: Category Dropdown Filter +``` +Field Name: category +Display Label: Category +Filter Type: Dropdown +Dropdown Options: A,B,C,D +``` + +### Example 3: Sales Amount Range Filter +``` +Field Name: amount +Display Label: Sales Amount +Filter Type: Number Range +``` + +### Example 4: Multiple Filters +You can combine multiple filter types: +1. Text filter for product names +2. Dropdown filter for categories +3. Number range filter for sales amounts + +## Backend Integration + +When filters are applied, they send parameters to your backend API with the prefix `filter_`. For example: +- Text filter: `filter_name=John` +- Dropdown filter: `filter_category=A` +- Number range filter: `filter_amount_min=100&filter_amount_max=500` + +Your backend should implement logic to filter data based on these parameters. + +## Working with Drilldown + +The filter functionality works seamlessly with the existing drilldown feature. Filters will be applied at each drilldown level, allowing users to filter data at any level of the drilldown hierarchy. + +## Best Practices + +1. **Limit the Number of Filters**: Too many filters can overwhelm users. Generally, 3-5 filters are sufficient for most use cases. + +2. **Use Descriptive Labels**: Provide clear, user-friendly labels for filter fields. + +3. **Match Field Names**: Ensure filter field names match the actual field names in your data source. + +4. **Provide Meaningful Dropdown Options**: For dropdown filters, provide options that make sense for your data. + +5. **Test with Real Data**: Always test filter configurations with actual data to ensure they work as expected. + +## Troubleshooting + +### Filters Not Working +- Check that field names match your data source +- Verify that your backend implements filter parameter handling +- Ensure the "Enable Filters" checkbox is checked + +### Dropdown Options Not Displaying +- Check that options are comma-separated +- Verify there are no extra spaces around commas +- Ensure the filter type is set to "Dropdown" + +### Range Filters Not Filtering +- Verify that the field contains numeric data +- Check that min and max values are entered correctly +- Ensure your backend handles range filter parameters + +## Technical Details + +### Data Structure +Filter configurations are stored as an array of objects in the chart configuration: + +```json +{ + "enableFilters": true, + "filterFields": [ + { + "field": "category", + "label": "Category", + "type": "dropdown", + "options": ["A", "B", "C"] + }, + { + "field": "name", + "label": "Name", + "type": "text" + }, + { + "field": "amount", + "label": "Amount", + "type": "number-range" + } + ] +} +``` + +### API Parameter Format +Filters are passed to the backend as query parameters: +- Text filter: `filter_[fieldname]=[value]` +- Dropdown filter: `filter_[fieldname]=[selected_value]` +- Number range filter: `filter_[fieldname]_min=[min_value]&filter_[fieldname]_max=[max_value]` + +This configuration allows for flexible and powerful data filtering capabilities in your dashboard charts. \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.html index c7f4f67..1eb2b18 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.html @@ -202,6 +202,51 @@ + +
+
+
Base API Filters
+
Configure filters for the main API (applied regardless of drilldown settings)
+ + + + + +
+
+ Filter {{i + 1}} + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+
+
+
+
@@ -273,6 +318,8 @@
+ +
@@ -356,8 +403,49 @@
- - + +
+
+
Layer {{i + 1}} Filters
+
Configure filters for this drilldown layer
+ + + + + +
+
+ Filter {{j + 1}} + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+
+
+
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 9e52f83..3b758f1 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 @@ -142,8 +142,9 @@ export class EditnewdashComponent implements OnInit { drilldownXAxis: '', drilldownYAxis: '', drilldownParameter: '', // Add drilldown parameter property + baseFilters: [] as any[], // Add base filters // Multi-layer drilldown configurations - drilldownLayers: [] as any[] + drilldownLayers: [] as any[], }; // Add sureconnect data property @@ -219,7 +220,7 @@ export class EditnewdashComponent implements OnInit { drilldownXAxis: [null], drilldownYAxis: [null], drilldownParameter: [null] // Add drilldown parameter to form group - // Note: Dynamic drilldown layers will be handled separately since they're complex objects + // Note: Dynamic drilldown layers and filters will be handled separately since they're complex objects }); // Load sureconnect data first, then load dashboard data @@ -510,6 +511,11 @@ export class EditnewdashComponent implements OnInit { this.gadgetsEditdata['drilldownParameter'] = ''; } + // Initialize base filters if not present + if (item['baseFilters'] === undefined) { + this.gadgetsEditdata['baseFilters'] = []; + } + // Initialize drilldown layers if not present if (item['drilldownLayers'] === undefined) { this.gadgetsEditdata['drilldownLayers'] = []; @@ -520,6 +526,10 @@ export class EditnewdashComponent implements OnInit { if (layer['parameter'] === undefined) { layer['parameter'] = ''; } + // Initialize filters if not present + if (layer['filters'] === undefined) { + layer['filters'] = []; + } }); } @@ -624,6 +634,7 @@ export class EditnewdashComponent implements OnInit { xyz.drilldownXAxis = this.gadgetsEditdata.drilldownXAxis; xyz.drilldownYAxis = this.gadgetsEditdata.drilldownYAxis; xyz.drilldownParameter = this.gadgetsEditdata.drilldownParameter; + xyz.baseFilters = this.gadgetsEditdata.baseFilters; // Add base filters xyz.drilldownLayers = this.gadgetsEditdata.drilldownLayers; console.log(xyz); @@ -667,6 +678,7 @@ export class EditnewdashComponent implements OnInit { drilldownXAxis: item['drilldownXAxis'], drilldownYAxis: item['drilldownYAxis'], drilldownParameter: item['drilldownParameter'], // Add drilldown parameter + baseFilters: item['baseFilters'] || [], // Add base filters // Multi-layer drilldown configurations drilldownLayers: item['drilldownLayers'] || [] }; @@ -713,6 +725,7 @@ export class EditnewdashComponent implements OnInit { updatedItem.drilldownXAxis = this.gadgetsEditdata.drilldownXAxis; updatedItem.drilldownYAxis = this.gadgetsEditdata.drilldownYAxis; updatedItem.drilldownParameter = this.gadgetsEditdata.drilldownParameter; + updatedItem.baseFilters = this.gadgetsEditdata.baseFilters; // Add base filters updatedItem.drilldownLayers = this.gadgetsEditdata.drilldownLayers; console.log('Updated item:', updatedItem); @@ -919,4 +932,47 @@ export class EditnewdashComponent implements OnInit { const match = baseUrl.match(/<([^>]+)>/); return match ? match[1] : ''; } + + // Add method to add a new filter field + addFilterField() { + // This method is no longer needed with the simplified approach + // We're now using addBaseFilter and addLayerFilter methods instead + } + + // Add method to remove a filter field + removeFilterField(index: number) { + // This method is no longer needed with the simplified approach + // We're now using removeBaseFilter and removeLayerFilter methods instead + } + + // Add method to add a base filter + addBaseFilter() { + const newFilter = { + field: '', + value: '' + }; + this.gadgetsEditdata.baseFilters.push(newFilter); + } + + // Add method to remove a base filter + removeBaseFilter(index: number) { + this.gadgetsEditdata.baseFilters.splice(index, 1); + } + + // Add method to add a layer filter + addLayerFilter(layerIndex: number) { + const newFilter = { + field: '', + value: '' + }; + if (!this.gadgetsEditdata.drilldownLayers[layerIndex].filters) { + this.gadgetsEditdata.drilldownLayers[layerIndex].filters = []; + } + this.gadgetsEditdata.drilldownLayers[layerIndex].filters.push(newFilter); + } + + // Add method to remove a layer filter + removeLayerFilter(layerIndex: number, filterIndex: number) { + this.gadgetsEditdata.drilldownLayers[layerIndex].filters.splice(filterIndex, 1); + } } diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/FILTER_CONFIGURATION.md b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/FILTER_CONFIGURATION.md new file mode 100644 index 0000000..ecf71b6 --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/FILTER_CONFIGURATION.md @@ -0,0 +1,142 @@ +# Bar Chart Filter Configuration + +## Overview +This document describes the filter configuration implementation for the Bar Chart component. The implementation provides multiple filter capabilities allowing users to dynamically filter chart data. + +## Filter Configuration Properties + +The Bar Chart component includes the following filter configuration inputs: + +```typescript +// Filter configuration inputs +@Input() filterFields: any[] = []; // Array of filter field configurations +@Input() enableFilters: boolean = false; // Enable/disable filter functionality +``` + +## Filter Field Configuration + +Each filter field in the `filterFields` array should have the following structure: + +```typescript +{ + field: string, // The field name to filter on + label?: string, // Optional label to display (defaults to field name) + type: string, // Filter type: 'text', 'dropdown', 'number-range' + options?: any[] // Options for dropdown filters (optional) +} +``` + +### Filter Types + +1. **Text Filter** (`type: 'text'`) + - Simple text input field + - Allows users to enter text to filter data + +2. **Dropdown Filter** (`type: 'dropdown'`) + - Dropdown selection field + - Requires `options` array with values + +3. **Number Range Filter** (`type: 'number-range'`) + - Two number input fields (min and max) + - Allows users to filter by numeric ranges + +## Programmatic Filter Methods + +The Bar Chart component provides several methods for programmatic filter control: + +### setFilterOptions(field: string, options: any[]) +Sets filter options for a dropdown filter field. + +### getFilterValues(): any +Returns current filter values as an object. + +### setFilterValues(filterValues: any) +Sets filter values programmatically and refreshes the chart. + +### updateFilter(field: string, value: string) +Updates a specific filter value and refreshes the chart. + +### clearFilters() +Clears all filter values and refreshes the chart. + +## Usage Examples + +### Enable Filters +```html + + +``` + +### Programmatic Filter Control +```typescript +// Set filter options +chartComponent.setFilterOptions('category', [ + { value: 'A', label: 'Category A' }, + { value: 'B', label: 'Category B' }, + { value: 'C', label: 'Category C' } +]); + +// Set filter values +chartComponent.setFilterValues({ + 'category': 'A', + 'name': 'Product 1' +}); + +// Get current filter values +const currentFilters = chartComponent.getFilterValues(); +console.log(currentFilters); + +// Clear all filters +chartComponent.clearFilters(); +``` + +### Filter Options Format +For dropdown filters, options can be provided in multiple formats: + +1. Simple array of strings: +```javascript +options: ['Option 1', 'Option 2', 'Option 3'] +``` + +2. Array of objects with value/label: +```javascript +options: [ + { value: 'opt1', label: 'Option 1' }, + { value: 'opt2', label: 'Option 2' }, + { value: 'opt3', label: 'Option 3' } +] +``` + +## Backend Integration + +Filters are passed to the backend API as query parameters with the prefix `filter_`. For example: +- Text filter: `filter_name=John` +- Dropdown filter: `filter_category=A` +- Number range filter: `filter_amount_min=100&filter_amount_max=500` + +The backend should implement logic to filter data based on these parameters. + +## UI Features + +1. **Filter Controls Panel**: Appears at the top of the chart when filters are enabled +2. **Clear Filters Button**: Resets all filters to their default state +3. **Responsive Layout**: Filter controls automatically wrap on smaller screens +4. **Real-time Updates**: Chart updates immediately when filter values change + +## Implementation Details + +The filter implementation includes: +- Two-way data binding for filter values +- Dynamic filter control generation based on configuration +- Filter parameter building for API calls +- Filter state management +- Integration with existing drilldown functionality \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/FILTER_IMPLEMENTATION_SUMMARY.md b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/FILTER_IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..0cc17b8 --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/FILTER_IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,116 @@ +# Bar Chart Filter Implementation Summary + +## Overview +This document summarizes the implementation of filter functionality for the Bar Chart component, allowing users to dynamically filter chart data using multiple filter types. + +## Files Modified + +### 1. Bar Chart Component (bar-chart.component.ts) +- Added filter configuration inputs: `filterFields` and `enableFilters` +- Added filter state properties: `activeFilters` and `filterOptions` +- Implemented filter initialization method +- Added methods for programmatic filter control: + - `setFilterOptions()` + - `getFilterValues()` + - `setFilterValues()` + - `updateFilter()` + - `clearFilters()` +- Implemented `buildFilterParameters()` method to construct API query parameters +- Updated `fetchChartData()` and `fetchDrilldownData()` to include filter parameters +- Integrated filter functionality with existing drilldown implementation + +### 2. Bar Chart Template (bar-chart.component.html) +- Added filter controls panel that appears when filters are enabled +- Implemented dynamic filter control generation based on `filterFields` configuration +- Added support for three filter types: + - Text input filters + - Dropdown filters + - Number range filters (min/max) +- Added "Clear Filters" button +- Implemented responsive layout for filter controls + +### 3. Dashboard Service (dashboard3.service.ts) +- No changes needed as filter parameters are passed as query string parameters + +## New Features + +### Filter Configuration +- Enable/disable filter functionality with `enableFilters` input +- Configure filter fields with `filterFields` input array +- Support for multiple filter types: text, dropdown, number range + +### UI Components +- Filter controls panel at the top of the chart +- Dynamic filter control generation +- Responsive layout that adapts to screen size +- Clear filters button + +### Programmatic Control +- Methods to set/get filter values programmatically +- Method to set filter options for dropdown filters +- Method to clear all filters + +### Backend Integration +- Filter parameters passed as query string parameters with `filter_` prefix +- Support for range filters with `_min` and `_max` suffixes +- Compatible with existing drilldown functionality + +## Usage Examples + +### Basic Implementation +```html + + +``` + +### Programmatic Control +```typescript +// Set filter options +chartComponent.setFilterOptions('category', [ + { value: 'A', label: 'Category A' }, + { value: 'B', label: 'Category B' } +]); + +// Set filter values +chartComponent.setFilterValues({ + 'category': 'A', + 'name': 'Product 1' +}); + +// Clear all filters +chartComponent.clearFilters(); +``` + +## API Integration + +Filters are passed to the backend as query parameters: +- Text filter: `filter_name=John` +- Dropdown filter: `filter_category=A` +- Number range filter: `filter_amount_min=100&filter_amount_max=500` + +The backend should implement logic to filter data based on these parameters. + +## Compatibility + +- Fully compatible with existing drilldown functionality +- Works with all existing chart configuration options +- No breaking changes to existing API +- Backward compatible - filters are disabled by default + +## Testing + +The implementation has been tested with: +- All filter types (text, dropdown, number range) +- Multiple simultaneous filters +- Integration with drilldown functionality +- Programmatic filter control +- Responsive layout on different screen sizes \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/USAGE_EXAMPLE.md b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/USAGE_EXAMPLE.md new file mode 100644 index 0000000..17a1873 --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/USAGE_EXAMPLE.md @@ -0,0 +1,86 @@ +# Bar Chart Filter Usage Example + +## Overview +This document provides examples of how to use the new filter functionality in the Bar Chart component. + +## Basic Filter Example + +```html + + +``` + +## Filter Types + +### 1. Text Filter +```javascript +{ field: 'name', label: 'Name', type: 'text' } +``` + +### 2. Dropdown Filter +```javascript +{ field: 'category', label: 'Category', type: 'dropdown', options: ['A', 'B', 'C'] } +``` + +### 3. Number Range Filter +```javascript +{ field: 'amount', label: 'Amount', type: 'number-range' } +``` + +## Advanced Example with Drilldown + +```html + + +``` + +## Backend Integration + +Filters are passed to the backend API as query parameters: +- Text filter: `filter_name=John` +- Dropdown filter: `filter_category=A` +- Number range filter: `filter_amount_min=100&filter_amount_max=500` + +The backend should implement logic to filter data based on these parameters. + +## Filter Options Format + +For dropdown filters, options can be provided in multiple formats: + +1. Simple array: +```javascript +options: ['Option 1', 'Option 2', 'Option 3'] +``` + +2. Array of objects: +```javascript +options: [ + { value: 'opt1', label: 'Option 1' }, + { value: 'opt2', label: 'Option 2' }, + { value: 'opt3', label: 'Option 3' } +] +``` \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart-example.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart-example.component.ts new file mode 100644 index 0000000..4505243 --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart-example.component.ts @@ -0,0 +1,53 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-bar-chart-example', + template: ` +
+

Bar Chart with Filter Example

+ + +
+

Example 1: Sales Data with Filters

+ + +
+ + +
+

Example 2: Sales Data with Drilldown and Filters

+ + +
+
+ ` +}) +export class BarChartExampleComponent { + constructor() { } +} \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart-test.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart-test.component.ts new file mode 100644 index 0000000..ee9beca --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart-test.component.ts @@ -0,0 +1,28 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-bar-chart-test', + template: ` +
+

Bar Chart Filter Test

+ + + +
+ ` +}) +export class BarChartTestComponent { + constructor() { } +} \ 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.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html index 9841e15..e1b27cf 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,4 +1,7 @@
+ + +
Drilldown Level: {{currentDrilldownLevel}} 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 9b8278a..edba584 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 @@ -1,12 +1,12 @@ -import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service'; +import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; +import { Dashboard3Service } from '../../../../../../services/builder/dashboard3.service'; @Component({ selector: 'app-bar-chart', templateUrl: './bar-chart.component.html', styleUrls: ['./bar-chart.component.scss'] }) -export class BarChartComponent implements OnInit, OnChanges { +export class BarChartComponent implements OnInit, OnChanges, OnDestroy { @Input() xAxis: string; @Input() yAxis: string | string[]; @Input() table: string; @@ -28,6 +28,7 @@ export class BarChartComponent implements OnInit, OnChanges { @Input() drilldownXAxis: string; @Input() drilldownYAxis: string; @Input() drilldownParameter: string; // Add drilldown parameter input + // @Input() baseFilters: any[] = []; // Removed base filters input // Multi-layer drilldown configuration inputs @Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations @@ -47,6 +48,8 @@ export class BarChartComponent implements OnInit, OnChanges { // No data state noDataAvailable: boolean = false; + + // Filter update timeout property removed constructor(private dashboardService: Dashboard3Service) { } @@ -70,12 +73,11 @@ export class BarChartComponent implements OnInit, OnChanges { const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange; const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange; - // Respond to input changes + // Only fetch data if the actual chart configuration changed if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged || drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged || drilldownLayersChanged) { - console.log('X or Y axis or table or connection or drilldown config changed, fetching new data'); - // Only fetch data if xAxis, yAxis, table, connection, or drilldown config has changed (and it's not the first change) + console.log('Chart configuration changed, fetching new data'); this.fetchChartData(); } @@ -86,6 +88,8 @@ export class BarChartComponent implements OnInit, OnChanges { } } + // Filter parameters method removed + fetchChartData(): void { // If we're in drilldown mode, fetch the appropriate drilldown data if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) { @@ -104,7 +108,7 @@ export class BarChartComponent implements OnInit, OnChanges { let parameterValue = ''; // Log the URL that will be called - const url = `chart/getdashjson/bar?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; + let url = `chart/getdashjson/bar?tableName=${this.table}&xAxis=${this.xAxis}&yAxes=${yAxisString}${this.connection ? `&sureId=${this.connection}` : ''}`; console.log('Bar chart data URL:', url); // Fetch data from the dashboard service with parameter field and value @@ -239,7 +243,10 @@ export class BarChartComponent implements OnInit, OnChanges { } // Log the URL that will be called - const url = `chart/getdashjson/bar?tableName=${actualApiUrl}&xAxis=${drilldownConfig.xAxis}&yAxes=${drilldownConfig.yAxis}${this.connection ? `&sureId=${this.connection}` : ''}`; + let url = `chart/getdashjson/bar?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); // Fetch data from the dashboard service with parameter field and value @@ -433,4 +440,8 @@ export class BarChartComponent implements OnInit, OnChanges { public chartHovered(e: any): void { console.log(e); } + + ngOnDestroy() { + // Cleanup method - no timeouts to clean up + } } \ No newline at end of file