dashboard
This commit is contained in:
parent
56e1e3b0fe
commit
c66e217b0c
Binary file not shown.
@ -62,6 +62,11 @@
|
|||||||
SureConnect
|
SureConnect
|
||||||
</ng-container></clr-dg-column>
|
</ng-container></clr-dg-column>
|
||||||
|
|
||||||
|
<!-- Add ref_datalake_id column -->
|
||||||
|
<clr-dg-column [clrDgField]="'ref_datalake_id'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Reference Data Lake
|
||||||
|
</ng-container></clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'calculated_field_json'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-column [clrDgField]="'calculated_field_json'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
Calculated Fields
|
Calculated Fields
|
||||||
</ng-container></clr-dg-column>
|
</ng-container></clr-dg-column>
|
||||||
@ -126,6 +131,9 @@
|
|||||||
|
|
||||||
<clr-dg-cell>{{user.sureconnect_name}}</clr-dg-cell>
|
<clr-dg-cell>{{user.sureconnect_name}}</clr-dg-cell>
|
||||||
|
|
||||||
|
<!-- Add ref_datalake_id cell -->
|
||||||
|
<clr-dg-cell>{{user.ref_datalake_name}}</clr-dg-cell>
|
||||||
|
|
||||||
<clr-dg-cell (click)="goToReplaceStringjson(user.calculated_field_json)"
|
<clr-dg-cell (click)="goToReplaceStringjson(user.calculated_field_json)"
|
||||||
style="cursor: pointer; align-items: center;"><clr-icon shape="details"
|
style="cursor: pointer; align-items: center;"><clr-icon shape="details"
|
||||||
*ngIf="user.calculated_field_json"></clr-icon></clr-dg-cell>
|
*ngIf="user.calculated_field_json"></clr-icon></clr-dg-cell>
|
||||||
@ -154,7 +162,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Calculated Field button -->
|
<!-- Calculated Field button -->
|
||||||
<button class="btn btn-icon btn-sm" (click)="fetchAvailableKeys(user)" title="Create Calculated Field">
|
<button class="btn btn-icon btn-sm" (click)="fetchAvailableKeys(user, false)" title="Create Calculated Field">
|
||||||
<clr-icon shape="calculator"></clr-icon>
|
<clr-icon shape="calculator"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -426,6 +434,15 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Add ref_datalake_id dropdown -->
|
||||||
|
<div class="clr-col-sm-12">
|
||||||
|
<label>Reference Data Lake</label>
|
||||||
|
<select class="clr-input" [(ngModel)]="rowSelected.ref_datalake_id" name="ref_datalake_id">
|
||||||
|
<option value="">Select Data Lake</option>
|
||||||
|
<option *ngFor="let dataLake of dataLakeList" [value]="dataLake.id">{{dataLake.name}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- form code start -->
|
<!-- form code start -->
|
||||||
<div *ngIf="checkFormCode">
|
<div *ngIf="checkFormCode">
|
||||||
@ -546,6 +563,15 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Add ref_datalake_id dropdown -->
|
||||||
|
<div class="clr-col-sm-12">
|
||||||
|
<label>Reference Data Lake</label>
|
||||||
|
<select formControlName="ref_datalake_id">
|
||||||
|
<option value="">Select Data Lake</option>
|
||||||
|
<option *ngFor="let dataLake of dataLakeList" [value]="dataLake.id">{{dataLake.name}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,10 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
sureConnectList;
|
sureConnectList;
|
||||||
selectedSureConnect: any = null;
|
selectedSureConnect: any = null;
|
||||||
|
|
||||||
|
// Data Lake reference properties
|
||||||
|
dataLakeList: any[] = [];
|
||||||
|
selectedDataLake: any = null;
|
||||||
|
|
||||||
// Calculated field properties
|
// Calculated field properties
|
||||||
calculatedFieldModalOpen = false;
|
calculatedFieldModalOpen = false;
|
||||||
availableKeys: string[] = [];
|
availableKeys: string[] = [];
|
||||||
@ -117,6 +121,7 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.userrole = this.userInfoService.getRoles();
|
this.userrole = this.userInfoService.getRoles();
|
||||||
this.getData();
|
this.getData();
|
||||||
this.getSureConnectList(); // Fetch SureConnect list
|
this.getSureConnectList(); // Fetch SureConnect list
|
||||||
|
this.getDataLakeList(); // Fetch Data Lake list for reference dropdown
|
||||||
this.entryForm = this._fb.group({
|
this.entryForm = this._fb.group({
|
||||||
name: [null],
|
name: [null],
|
||||||
|
|
||||||
@ -133,6 +138,7 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
batch_volume: [null],
|
batch_volume: [null],
|
||||||
|
|
||||||
sure_connect_id: [null], // Add SureConnect field
|
sure_connect_id: [null], // Add SureConnect field
|
||||||
|
ref_datalake_id: [null], // Add Data Lake reference field
|
||||||
|
|
||||||
}); // component_button200
|
}); // component_button200
|
||||||
// form code start
|
// 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
|
// Get operation symbol for display
|
||||||
getOperationSymbol(operation: string): string {
|
getOperationSymbol(operation: string): string {
|
||||||
switch(operation) {
|
switch(operation) {
|
||||||
@ -245,7 +262,7 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch available keys from API
|
// Fetch available keys from API
|
||||||
fetchAvailableKeys(dataLakeItem: any) {
|
fetchAvailableKeys(dataLakeItem: any, isGroupBy: boolean = false) {
|
||||||
if (!dataLakeItem.url || !dataLakeItem.sure_connect_id) {
|
if (!dataLakeItem.url || !dataLakeItem.sure_connect_id) {
|
||||||
this.toastr.error('URL and SureConnect ID are required to fetch keys');
|
this.toastr.error('URL and SureConnect ID are required to fetch keys');
|
||||||
return;
|
return;
|
||||||
@ -256,11 +273,56 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
// Call the service method to get all keys
|
// Call the service method to get all keys
|
||||||
this.mainService.fetchAvailableKeys(dataLakeItem.url, dataLakeItem.sure_connect_id).subscribe(
|
this.mainService.fetchAvailableKeys(dataLakeItem.url, dataLakeItem.sure_connect_id).subscribe(
|
||||||
(keys: string[]) => {
|
(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
|
// Reset the calculated field form to ensure it starts fresh
|
||||||
this.resetCalculatedFieldForm();
|
this.resetCalculatedFieldForm();
|
||||||
|
|
||||||
|
// 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;
|
this.calculatedFieldModalOpen = true;
|
||||||
console.log('Available keys:', keys);
|
}
|
||||||
|
|
||||||
|
console.log('Available keys:', this.availableKeys);
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error('Error fetching keys:', error);
|
console.error('Error fetching keys:', error);
|
||||||
@ -335,7 +397,6 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create calculated field object for complex equation
|
// Create calculated field object for complex equation
|
||||||
const calculatedField = {
|
const calculatedField = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
@ -348,6 +409,9 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.calculatedFields.push(calculatedField);
|
this.calculatedFields.push(calculatedField);
|
||||||
this.toastr.success('Complex calculated field added successfully');
|
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
|
// Reset form but keep one empty field component
|
||||||
this.calculatedFieldForm.reset({
|
this.calculatedFieldForm.reset({
|
||||||
fieldName: '',
|
fieldName: '',
|
||||||
@ -420,6 +484,9 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.calculatedFields.push(calculatedField);
|
this.calculatedFields.push(calculatedField);
|
||||||
this.toastr.success('Calculated field added successfully');
|
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
|
// Reset form but keep one empty field component
|
||||||
this.calculatedFieldForm.reset({
|
this.calculatedFieldForm.reset({
|
||||||
fieldName: '',
|
fieldName: '',
|
||||||
@ -609,6 +676,9 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
deleteCalculatedField(id: number) {
|
deleteCalculatedField(id: number) {
|
||||||
this.calculatedFields = this.calculatedFields.filter(field => field.id !== id);
|
this.calculatedFields = this.calculatedFields.filter(field => field.id !== id);
|
||||||
this.toastr.success('Calculated field deleted');
|
this.toastr.success('Calculated field deleted');
|
||||||
|
|
||||||
|
// Refresh available keys to reflect the deleted field
|
||||||
|
this.refreshAvailableKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
onEdit(row) {
|
onEdit(row) {
|
||||||
@ -616,6 +686,8 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.editCronExpression = row.cron_job || '';
|
this.editCronExpression = row.cron_job || '';
|
||||||
// Set the selected SureConnect for edit form
|
// Set the selected SureConnect for edit form
|
||||||
this.selectedSureConnect = row.sure_connect_id || null;
|
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
|
// Use setTimeout to ensure the component has time to initialize
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.modalEdit = true;
|
this.modalEdit = true;
|
||||||
@ -769,6 +841,7 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.modalAdd = true;
|
this.modalAdd = true;
|
||||||
this.addCronExpression = '';
|
this.addCronExpression = '';
|
||||||
this.selectedSureConnect = null; // Reset SureConnect selection
|
this.selectedSureConnect = null; // Reset SureConnect selection
|
||||||
|
this.selectedDataLake = null; // Reset Data Lake selection
|
||||||
this.submitted = false;
|
this.submitted = false;
|
||||||
// Reset the form control for cron_job and sure_connect_id
|
// Reset the form control for cron_job and sure_connect_id
|
||||||
if (this.entryForm) {
|
if (this.entryForm) {
|
||||||
@ -801,7 +874,7 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate JSON from calculated fields and update the record
|
// Generate JSON from calculated fields and update the record
|
||||||
updateCalculatedFields() {
|
updateCalculatedFields(closeModal: boolean = true) {
|
||||||
if (!this.selectedDataLakeItem || this.calculatedFields.length === 0) {
|
if (!this.selectedDataLakeItem || this.calculatedFields.length === 0) {
|
||||||
this.toastr.error('No calculated fields to update');
|
this.toastr.error('No calculated fields to update');
|
||||||
return;
|
return;
|
||||||
@ -827,8 +900,11 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.product[index].iscalculatedfield = true;
|
this.product[index].iscalculatedfield = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the modal only when called directly from the UI
|
// Refresh available keys to ensure they include the latest calculated fields
|
||||||
if (this.calculatedFieldModalOpen) {
|
this.refreshAvailableKeys();
|
||||||
|
|
||||||
|
// Close the modal only when called directly from the UI and closeModal is true
|
||||||
|
if (closeModal && this.calculatedFieldModalOpen) {
|
||||||
this.calculatedFieldModalOpen = false;
|
this.calculatedFieldModalOpen = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -995,16 +1071,45 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
openGroupByModal(dataLakeItem: any) {
|
openGroupByModal(dataLakeItem: any) {
|
||||||
this.selectedDataLakeItem = dataLakeItem;
|
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) {
|
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
|
// Initialize selected group by fields as empty array
|
||||||
this.selectedGroupByFields = [];
|
this.selectedGroupByFields = [];
|
||||||
this.selectedAggregationFields = [];
|
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;
|
this.showGroupByModal = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Method to add a field to group by selection
|
// Method to add a field to group by selection
|
||||||
addGroupByField(field: string) {
|
addGroupByField(field: string) {
|
||||||
@ -1072,41 +1177,17 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
|
|
||||||
// New method to update calculated fields after adding group by configuration
|
// New method to update calculated fields after adding group by configuration
|
||||||
updateCalculatedFieldsAfterGroupBy() {
|
updateCalculatedFieldsAfterGroupBy() {
|
||||||
if (!this.selectedDataLakeItem || this.calculatedFields.length === 0) {
|
// Simply call the main update method without closing the modal
|
||||||
return;
|
this.updateCalculatedFields(false);
|
||||||
}
|
|
||||||
|
|
||||||
// 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');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to remove group by configuration
|
// Method to remove group by configuration
|
||||||
removeGroupByConfig(id: number) {
|
removeGroupByConfig(id: number) {
|
||||||
this.calculatedFields = this.calculatedFields.filter(field => field.id !== id || field.type !== 'groupby');
|
this.calculatedFields = this.calculatedFields.filter(field => field.id !== id || field.type !== 'groupby');
|
||||||
this.toastr.success('Group by configuration removed');
|
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
|
// 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);
|
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
|
// Helper method to get the count of calculated fields
|
||||||
getCalculatedFieldsCount(calculatedFieldJson: string): number {
|
getCalculatedFieldsCount(calculatedFieldJson: string): number {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -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.
|
||||||
@ -202,6 +202,51 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Base API Filters -->
|
||||||
|
<div class="clr-row" style="margin-top: 15px;">
|
||||||
|
<div class="clr-col-sm-12">
|
||||||
|
<h5>Base API Filters</h5>
|
||||||
|
<div class="clr-subtext">Configure filters for the main API (applied regardless of drilldown settings)</div>
|
||||||
|
|
||||||
|
<!-- Add Base Filter Button -->
|
||||||
|
<button class="btn btn-sm btn-primary" (click)="addBaseFilter()" style="margin-top: 10px; margin-bottom: 10px;">
|
||||||
|
<clr-icon shape="plus"></clr-icon> Add Filter
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Base Filter Fields List -->
|
||||||
|
<div *ngFor="let filter of gadgetsEditdata.baseFilters; let i = index"
|
||||||
|
style="margin-bottom: 10px; padding: 8px; border: 1px solid #eee; border-radius: 4px; background-color: #f9f9f9;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<span>Filter {{i + 1}}</span>
|
||||||
|
<button class="btn btn-icon btn-danger btn-sm" (click)="removeBaseFilter(i)">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-row" style="margin-top: 8px;">
|
||||||
|
<div class="clr-col-sm-5">
|
||||||
|
<select [(ngModel)]="filter.field" [ngModelOptions]="{standalone: true}" class="clr-select">
|
||||||
|
<option value="">Select Field</option>
|
||||||
|
<!-- Use columnData for base API filters when drilldown is not enabled, drilldownColumnData when it is -->
|
||||||
|
<option *ngFor="let column of (gadgetsEditdata.drilldownEnabled ? drilldownColumnData : columnData)" [value]="column">{{column}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-sm-5">
|
||||||
|
<input type="text" [(ngModel)]="filter.value" [ngModelOptions]="{standalone: true}"
|
||||||
|
class="clr-input" placeholder="Filter Value" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-sm-2">
|
||||||
|
<button class="btn btn-icon btn-danger btn-sm" (click)="removeBaseFilter(i)">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Base Drilldown Configuration Section -->
|
<!-- Base Drilldown Configuration Section -->
|
||||||
<div class="clr-row" style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #ddd;">
|
<div class="clr-row" style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #ddd;">
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
@ -273,6 +318,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Multi-Layer Drilldown Configurations -->
|
<!-- Multi-Layer Drilldown Configurations -->
|
||||||
<div class="clr-row" *ngIf="gadgetsEditdata.drilldownEnabled" style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #ddd;">
|
<div class="clr-row" *ngIf="gadgetsEditdata.drilldownEnabled" style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #ddd;">
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
@ -356,8 +403,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Layer Parameter Configuration -->
|
<!-- Layer Filter Configuration -->
|
||||||
<!-- Removed parameter key input since we're using URL templates -->
|
<div class="clr-row" style="margin-top: 15px;">
|
||||||
|
<div class="clr-col-sm-12">
|
||||||
|
<h5>Layer {{i + 1}} Filters</h5>
|
||||||
|
<div class="clr-subtext">Configure filters for this drilldown layer</div>
|
||||||
|
|
||||||
|
<!-- Add Layer Filter Button -->
|
||||||
|
<button class="btn btn-sm btn-primary" (click)="addLayerFilter(i)" style="margin-top: 10px; margin-bottom: 10px;">
|
||||||
|
<clr-icon shape="plus"></clr-icon> Add Filter
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Layer Filter Fields List -->
|
||||||
|
<div *ngFor="let filter of layer.filters; let j = index"
|
||||||
|
style="margin-bottom: 10px; padding: 8px; border: 1px solid #eee; border-radius: 4px; background-color: #f9f9f9;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<span>Filter {{j + 1}}</span>
|
||||||
|
<button class="btn btn-icon btn-danger btn-sm" (click)="removeLayerFilter(i, j)">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-row" style="margin-top: 8px;">
|
||||||
|
<div class="clr-col-sm-5">
|
||||||
|
<select [(ngModel)]="filter.field" [ngModelOptions]="{standalone: true}" class="clr-select">
|
||||||
|
<option value="">Select Field</option>
|
||||||
|
<option *ngFor="let column of layerColumnData[i] || []" [value]="column">{{column}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-sm-5">
|
||||||
|
<input type="text" [(ngModel)]="filter.value" [ngModelOptions]="{standalone: true}"
|
||||||
|
class="clr-input" placeholder="Filter Value" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-sm-2">
|
||||||
|
<button class="btn btn-icon btn-danger btn-sm" (click)="removeLayerFilter(i, j)">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -142,8 +142,9 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
drilldownXAxis: '',
|
drilldownXAxis: '',
|
||||||
drilldownYAxis: '',
|
drilldownYAxis: '',
|
||||||
drilldownParameter: '', // Add drilldown parameter property
|
drilldownParameter: '', // Add drilldown parameter property
|
||||||
|
baseFilters: [] as any[], // Add base filters
|
||||||
// Multi-layer drilldown configurations
|
// Multi-layer drilldown configurations
|
||||||
drilldownLayers: [] as any[]
|
drilldownLayers: [] as any[],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add sureconnect data property
|
// Add sureconnect data property
|
||||||
@ -219,7 +220,7 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
drilldownXAxis: [null],
|
drilldownXAxis: [null],
|
||||||
drilldownYAxis: [null],
|
drilldownYAxis: [null],
|
||||||
drilldownParameter: [null] // Add drilldown parameter to form group
|
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
|
// Load sureconnect data first, then load dashboard data
|
||||||
@ -510,6 +511,11 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
this.gadgetsEditdata['drilldownParameter'] = '';
|
this.gadgetsEditdata['drilldownParameter'] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize base filters if not present
|
||||||
|
if (item['baseFilters'] === undefined) {
|
||||||
|
this.gadgetsEditdata['baseFilters'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize drilldown layers if not present
|
// Initialize drilldown layers if not present
|
||||||
if (item['drilldownLayers'] === undefined) {
|
if (item['drilldownLayers'] === undefined) {
|
||||||
this.gadgetsEditdata['drilldownLayers'] = [];
|
this.gadgetsEditdata['drilldownLayers'] = [];
|
||||||
@ -520,6 +526,10 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
if (layer['parameter'] === undefined) {
|
if (layer['parameter'] === undefined) {
|
||||||
layer['parameter'] = '';
|
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.drilldownXAxis = this.gadgetsEditdata.drilldownXAxis;
|
||||||
xyz.drilldownYAxis = this.gadgetsEditdata.drilldownYAxis;
|
xyz.drilldownYAxis = this.gadgetsEditdata.drilldownYAxis;
|
||||||
xyz.drilldownParameter = this.gadgetsEditdata.drilldownParameter;
|
xyz.drilldownParameter = this.gadgetsEditdata.drilldownParameter;
|
||||||
|
xyz.baseFilters = this.gadgetsEditdata.baseFilters; // Add base filters
|
||||||
xyz.drilldownLayers = this.gadgetsEditdata.drilldownLayers;
|
xyz.drilldownLayers = this.gadgetsEditdata.drilldownLayers;
|
||||||
|
|
||||||
console.log(xyz);
|
console.log(xyz);
|
||||||
@ -667,6 +678,7 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
drilldownXAxis: item['drilldownXAxis'],
|
drilldownXAxis: item['drilldownXAxis'],
|
||||||
drilldownYAxis: item['drilldownYAxis'],
|
drilldownYAxis: item['drilldownYAxis'],
|
||||||
drilldownParameter: item['drilldownParameter'], // Add drilldown parameter
|
drilldownParameter: item['drilldownParameter'], // Add drilldown parameter
|
||||||
|
baseFilters: item['baseFilters'] || [], // Add base filters
|
||||||
// Multi-layer drilldown configurations
|
// Multi-layer drilldown configurations
|
||||||
drilldownLayers: item['drilldownLayers'] || []
|
drilldownLayers: item['drilldownLayers'] || []
|
||||||
};
|
};
|
||||||
@ -713,6 +725,7 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
updatedItem.drilldownXAxis = this.gadgetsEditdata.drilldownXAxis;
|
updatedItem.drilldownXAxis = this.gadgetsEditdata.drilldownXAxis;
|
||||||
updatedItem.drilldownYAxis = this.gadgetsEditdata.drilldownYAxis;
|
updatedItem.drilldownYAxis = this.gadgetsEditdata.drilldownYAxis;
|
||||||
updatedItem.drilldownParameter = this.gadgetsEditdata.drilldownParameter;
|
updatedItem.drilldownParameter = this.gadgetsEditdata.drilldownParameter;
|
||||||
|
updatedItem.baseFilters = this.gadgetsEditdata.baseFilters; // Add base filters
|
||||||
updatedItem.drilldownLayers = this.gadgetsEditdata.drilldownLayers;
|
updatedItem.drilldownLayers = this.gadgetsEditdata.drilldownLayers;
|
||||||
|
|
||||||
console.log('Updated item:', updatedItem);
|
console.log('Updated item:', updatedItem);
|
||||||
@ -919,4 +932,47 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
const match = baseUrl.match(/<([^>]+)>/);
|
const match = baseUrl.match(/<([^>]+)>/);
|
||||||
return match ? match[1] : '';
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
<app-bar-chart
|
||||||
|
[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' }
|
||||||
|
]"
|
||||||
|
[table]="'sales_data'"
|
||||||
|
[xAxis]="'product'"
|
||||||
|
[yAxis]="'amount'">
|
||||||
|
</app-bar-chart>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
@ -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
|
||||||
|
<app-bar-chart
|
||||||
|
[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' }
|
||||||
|
]"
|
||||||
|
[table]="'sales_data'"
|
||||||
|
[xAxis]="'product'"
|
||||||
|
[yAxis]="'amount'">
|
||||||
|
</app-bar-chart>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
@ -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
|
||||||
|
<app-bar-chart
|
||||||
|
[enableFilters]="true"
|
||||||
|
[filterFields]="[
|
||||||
|
{ field: 'region', label: 'Region', type: 'dropdown', options: ['North', 'South', 'East', 'West'] },
|
||||||
|
{ field: 'product', label: 'Product', type: 'text' },
|
||||||
|
{ field: 'sales', label: 'Sales Amount', type: 'number-range' }
|
||||||
|
]"
|
||||||
|
[table]="'sales_data'"
|
||||||
|
[xAxis]="'product'"
|
||||||
|
[yAxis]="'amount'"
|
||||||
|
[charttitle]="'Sales by Product'">
|
||||||
|
</app-bar-chart>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
|
<app-bar-chart
|
||||||
|
[enableFilters]="true"
|
||||||
|
[filterFields]="[
|
||||||
|
{ field: 'year', label: 'Year', type: 'dropdown', options: ['2020', '2021', '2022', '2023'] },
|
||||||
|
{ field: 'region', label: 'Region', type: 'text' }
|
||||||
|
]"
|
||||||
|
[table]="'sales_summary'"
|
||||||
|
[xAxis]="'category'"
|
||||||
|
[yAxis]="'revenue'"
|
||||||
|
[charttitle]="'Revenue by Category'"
|
||||||
|
[drilldownEnabled]="true"
|
||||||
|
[drilldownApiUrl]="'sales_detail/<category>'"
|
||||||
|
[drilldownXAxis]="'product'"
|
||||||
|
[drilldownYAxis]="'amount'"
|
||||||
|
[drilldownParameter]="'category'">
|
||||||
|
</app-bar-chart>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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' }
|
||||||
|
]
|
||||||
|
```
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-bar-chart-example',
|
||||||
|
template: `
|
||||||
|
<div style="padding: 20px;">
|
||||||
|
<h2>Bar Chart with Filter Example</h2>
|
||||||
|
|
||||||
|
<!-- Example 1: Bar chart with text and dropdown filters -->
|
||||||
|
<div style="margin-bottom: 30px;">
|
||||||
|
<h3>Example 1: Sales Data with Filters</h3>
|
||||||
|
<app-bar-chart
|
||||||
|
[enableFilters]="true"
|
||||||
|
[filterFields]="[
|
||||||
|
{ field: 'region', label: 'Region', type: 'dropdown', options: ['North', 'South', 'East', 'West'] },
|
||||||
|
{ field: 'product', label: 'Product', type: 'text' },
|
||||||
|
{ field: 'sales', label: 'Sales Amount', type: 'number-range' }
|
||||||
|
]"
|
||||||
|
[table]="'sales_data'"
|
||||||
|
[xAxis]="'product'"
|
||||||
|
[yAxis]="'amount'"
|
||||||
|
[charttitle]="'Sales by Product'"
|
||||||
|
[chartlegend]="true"
|
||||||
|
[showlabel]="true">
|
||||||
|
</app-bar-chart>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Example 2: Bar chart with drilldown and filters -->
|
||||||
|
<div style="margin-bottom: 30px;">
|
||||||
|
<h3>Example 2: Sales Data with Drilldown and Filters</h3>
|
||||||
|
<app-bar-chart
|
||||||
|
[enableFilters]="true"
|
||||||
|
[filterFields]="[
|
||||||
|
{ field: 'year', label: 'Year', type: 'dropdown', options: ['2020', '2021', '2022', '2023'] },
|
||||||
|
{ field: 'category', label: 'Category', type: 'text' }
|
||||||
|
]"
|
||||||
|
[table]="'sales_summary'"
|
||||||
|
[xAxis]="'category'"
|
||||||
|
[yAxis]="'revenue'"
|
||||||
|
[charttitle]="'Revenue by Category'"
|
||||||
|
[drilldownEnabled]="true"
|
||||||
|
[drilldownApiUrl]="'sales_detail/<category>'"
|
||||||
|
[drilldownXAxis]="'product'"
|
||||||
|
[drilldownYAxis]="'amount'"
|
||||||
|
[drilldownParameter]="'category'">
|
||||||
|
</app-bar-chart>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class BarChartExampleComponent {
|
||||||
|
constructor() { }
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-bar-chart-test',
|
||||||
|
template: `
|
||||||
|
<div style="padding: 20px;">
|
||||||
|
<h2>Bar Chart Filter Test</h2>
|
||||||
|
|
||||||
|
<app-bar-chart
|
||||||
|
[enableFilters]="true"
|
||||||
|
[filterFields]="[
|
||||||
|
{ field: 'region', label: 'Region', type: 'dropdown', options: ['North', 'South', 'East', 'West'] },
|
||||||
|
{ field: 'product', label: 'Product', type: 'text' },
|
||||||
|
{ field: 'sales', label: 'Sales Amount', type: 'number-range' }
|
||||||
|
]"
|
||||||
|
[table]="'sales_data'"
|
||||||
|
[xAxis]="'product'"
|
||||||
|
[yAxis]="'amount'"
|
||||||
|
[charttitle]="'Sales by Product'"
|
||||||
|
[chartlegend]="true"
|
||||||
|
[showlabel]="true">
|
||||||
|
</app-bar-chart>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class BarChartTestComponent {
|
||||||
|
constructor() { }
|
||||||
|
}
|
||||||
@ -1,4 +1,7 @@
|
|||||||
<div style="display: block">
|
<div style="display: block">
|
||||||
|
<!-- No filter controls needed with the new simplified approach -->
|
||||||
|
<!-- Filters are now configured at the drilldown level -->
|
||||||
|
|
||||||
<!-- Drilldown mode indicator -->
|
<!-- Drilldown mode indicator -->
|
||||||
<div *ngIf="currentDrilldownLevel > 0" style="background-color: #e0e0e0; padding: 5px; margin-bottom: 10px; border-radius: 4px; text-align: center;">
|
<div *ngIf="currentDrilldownLevel > 0" style="background-color: #e0e0e0; padding: 5px; margin-bottom: 10px; border-radius: 4px; text-align: center;">
|
||||||
<span style="font-weight: bold; color: #333;">Drilldown Level: {{currentDrilldownLevel}}</span>
|
<span style="font-weight: bold; color: #333;">Drilldown Level: {{currentDrilldownLevel}}</span>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core';
|
import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
|
||||||
import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service';
|
import { Dashboard3Service } from '../../../../../../services/builder/dashboard3.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bar-chart',
|
selector: 'app-bar-chart',
|
||||||
templateUrl: './bar-chart.component.html',
|
templateUrl: './bar-chart.component.html',
|
||||||
styleUrls: ['./bar-chart.component.scss']
|
styleUrls: ['./bar-chart.component.scss']
|
||||||
})
|
})
|
||||||
export class BarChartComponent implements OnInit, OnChanges {
|
export class BarChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
@Input() xAxis: string;
|
@Input() xAxis: string;
|
||||||
@Input() yAxis: string | string[];
|
@Input() yAxis: string | string[];
|
||||||
@Input() table: string;
|
@Input() table: string;
|
||||||
@ -28,6 +28,7 @@ export class BarChartComponent implements OnInit, OnChanges {
|
|||||||
@Input() drilldownXAxis: string;
|
@Input() drilldownXAxis: string;
|
||||||
@Input() drilldownYAxis: string;
|
@Input() drilldownYAxis: string;
|
||||||
@Input() drilldownParameter: string; // Add drilldown parameter input
|
@Input() drilldownParameter: string; // Add drilldown parameter input
|
||||||
|
// @Input() baseFilters: any[] = []; // Removed base filters input
|
||||||
// Multi-layer drilldown configuration inputs
|
// Multi-layer drilldown configuration inputs
|
||||||
@Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations
|
@Input() drilldownLayers: any[] = []; // Array of drilldown layer configurations
|
||||||
|
|
||||||
@ -48,6 +49,8 @@ export class BarChartComponent implements OnInit, OnChanges {
|
|||||||
// No data state
|
// No data state
|
||||||
noDataAvailable: boolean = false;
|
noDataAvailable: boolean = false;
|
||||||
|
|
||||||
|
// Filter update timeout property removed
|
||||||
|
|
||||||
constructor(private dashboardService: Dashboard3Service) { }
|
constructor(private dashboardService: Dashboard3Service) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -70,12 +73,11 @@ export class BarChartComponent implements OnInit, OnChanges {
|
|||||||
const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange;
|
const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange;
|
||||||
const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.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 ||
|
if (xAxisChanged || yAxisChanged || tableChanged || connectionChanged ||
|
||||||
drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged ||
|
drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged ||
|
||||||
drilldownLayersChanged) {
|
drilldownLayersChanged) {
|
||||||
console.log('X or Y axis or table or connection or drilldown config changed, fetching new data');
|
console.log('Chart configuration changed, fetching new data');
|
||||||
// Only fetch data if xAxis, yAxis, table, connection, or drilldown config has changed (and it's not the first change)
|
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +88,8 @@ export class BarChartComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter parameters method removed
|
||||||
|
|
||||||
fetchChartData(): void {
|
fetchChartData(): void {
|
||||||
// If we're in drilldown mode, fetch the appropriate drilldown data
|
// If we're in drilldown mode, fetch the appropriate drilldown data
|
||||||
if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) {
|
if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) {
|
||||||
@ -104,7 +108,7 @@ export class BarChartComponent implements OnInit, OnChanges {
|
|||||||
let parameterValue = '';
|
let parameterValue = '';
|
||||||
|
|
||||||
// Log the URL that will be called
|
// 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);
|
console.log('Bar chart data URL:', url);
|
||||||
|
|
||||||
// Fetch data from the dashboard service with parameter field and value
|
// 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
|
// 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);
|
console.log('Drilldown data URL:', url);
|
||||||
|
|
||||||
// Fetch data from the dashboard service with parameter field and value
|
// 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 {
|
public chartHovered(e: any): void {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
// Cleanup method - no timeouts to clean up
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user