Commit 7ff59ecb by Ooh-Ao

kpi

parent fec4de97
...@@ -22,12 +22,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -22,12 +22,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
MatCheckboxModule, MatCheckboxModule,
MatButtonModule, MatButtonModule,
MatIconModule, MatIconModule,
MatTabsModule, MatTabsModule
BaseConfigComponent
], ],
template: ` template: `
<app-base-config> <div class="config-container">
<mat-tab-group class="config-tabs">
<!-- Basic Configuration Tab --> <!-- Basic Configuration Tab -->
<mat-tab label="Basic">
<div class="config-section"> <div class="config-section">
<h3 class="text-blue-600">Basic Configuration</h3> <h3 class="text-blue-600">Basic Configuration</h3>
...@@ -142,8 +143,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -142,8 +143,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</div> </div>
</div> </div>
</div> </div>
</mat-tab>
<!-- Style Tab --> <!-- Style Tab -->
<mat-tab label="Style">
<div class="config-section"> <div class="config-section">
<h3 class="text-blue-600">Style Configuration</h3> <h3 class="text-blue-600">Style Configuration</h3>
...@@ -222,8 +225,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -222,8 +225,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</mat-tab>
<!-- Icon Tab --> <!-- Icon Tab -->
<mat-tab label="Icon">
<div class="config-section"> <div class="config-section">
<h3 class="text-blue-600">Icon Configuration</h3> <h3 class="text-blue-600">Icon Configuration</h3>
...@@ -277,8 +282,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -277,8 +282,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</mat-tab>
<!-- Filter Tab --> <!-- Filter Tab -->
<mat-tab label="Filter">
<div class="config-section"> <div class="config-section">
<h3 class="text-blue-600">Filter Configuration</h3> <h3 class="text-blue-600">Filter Configuration</h3>
...@@ -326,8 +333,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -326,8 +333,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</mat-tab>
<!-- Trend Tab --> <!-- Trend Tab -->
<mat-tab label="Trend">
<div class="config-section"> <div class="config-section">
<h3 class="text-blue-600">Trend Configuration</h3> <h3 class="text-blue-600">Trend Configuration</h3>
...@@ -377,8 +386,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -377,8 +386,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</mat-tab>
<!-- Animation Tab --> <!-- Animation Tab -->
<mat-tab label="Animation">
<div class="config-section"> <div class="config-section">
<h3 class="text-blue-600">Animation Configuration</h3> <h3 class="text-blue-600">Animation Configuration</h3>
...@@ -437,8 +448,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -437,8 +448,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-checkbox> </mat-checkbox>
</div> </div>
</div> </div>
</mat-tab>
<!-- Condition Tab --> <!-- Condition Tab -->
<mat-tab label="Condition">
<div class="config-section"> <div class="config-section">
<h3 class="text-blue-600">Conditional Formatting</h3> <h3 class="text-blue-600">Conditional Formatting</h3>
...@@ -493,20 +506,51 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -493,20 +506,51 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</div> </div>
</div> </div>
</div> </div>
</app-base-config> </mat-tab>
</mat-tab-group>
</div>
`, `,
styles: [` styles: [`
.config-container {
padding: 16px;
}
.config-section { .config-section {
margin-bottom: 24px; margin-bottom: 24px;
} }
.config-tabs {
margin-top: 16px;
}
.config-tabs .mat-tab-body-content {
padding: 16px 0;
}
`] `]
}) })
export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnInit { export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnInit {
override sizeOptions = [
{ id: 'small', label: 'Small', description: '200x150px' },
{ id: 'medium', label: 'Medium', description: '300x200px' },
{ id: 'large', label: 'Large', description: '400x300px' },
{ id: 'custom', label: 'Custom', description: 'Custom size' }
];
override ngOnInit() { override ngOnInit() {
this.initializeDefaultConfig(); this.initializeDefaultConfig();
this.initializeColorDefaults(); this.initializeColorDefaults();
} }
override initializeDefaultConfig() {
if (!this.currentConfig.title) this.currentConfig.title = 'KPI Widget';
if (!this.currentConfig.valueField) this.currentConfig.valueField = 'value';
if (!this.currentConfig.labelField) this.currentConfig.labelField = 'label';
if (!this.currentConfig.aggregation) this.currentConfig.aggregation = 'sum';
if (!this.currentConfig.unit) this.currentConfig.unit = '';
if (!this.currentConfig.icon) this.currentConfig.icon = 'info';
if (this.currentConfig.decimalPlaces === undefined) this.currentConfig.decimalPlaces = 0;
if (!this.currentConfig.sizeOption) this.currentConfig.sizeOption = 'medium';
if (!this.currentConfig.width) this.currentConfig.width = '300px';
if (!this.currentConfig.height) this.currentConfig.height = '200px';
}
private initializeColorDefaults() { private initializeColorDefaults() {
if (!this.currentConfig.backgroundColor) this.currentConfig.backgroundColor = '#3366FF'; if (!this.currentConfig.backgroundColor) this.currentConfig.backgroundColor = '#3366FF';
if (!this.currentConfig.textColor) this.currentConfig.textColor = '#FFFFFF'; if (!this.currentConfig.textColor) this.currentConfig.textColor = '#FFFFFF';
...@@ -540,4 +584,19 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI ...@@ -540,4 +584,19 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
if (!this.currentConfig.conditionOperator) this.currentConfig.conditionOperator = 'greater_than'; if (!this.currentConfig.conditionOperator) this.currentConfig.conditionOperator = 'greater_than';
if (!this.currentConfig.conditionValue) this.currentConfig.conditionValue = ''; if (!this.currentConfig.conditionValue) this.currentConfig.conditionValue = '';
} }
override setSizeOption(optionId: string) {
this.currentConfig.sizeOption = optionId;
if (optionId === 'small') {
this.currentConfig.width = '200px';
this.currentConfig.height = '150px';
} else if (optionId === 'medium') {
this.currentConfig.width = '300px';
this.currentConfig.height = '200px';
} else if (optionId === 'large') {
this.currentConfig.width = '400px';
this.currentConfig.height = '300px';
}
this.configChange.emit(this.currentConfig);
}
} }
...@@ -18,13 +18,13 @@ ...@@ -18,13 +18,13 @@
<style *ngIf="hasCustomCSS()" [innerHTML]="customCSS"></style> <style *ngIf="hasCustomCSS()" [innerHTML]="customCSS"></style>
<!-- Header --> <!-- Header -->
<div class="widget-header" [style.background]="backgroundColor"> <div class="widget-header" [style.background]="backgroundColor" [style.box-shadow]="getShadowStyles()">
<div class="header-content"> <div class="header-content">
<div class="header-left"> <div class="header-left">
<!-- {{icon}} <!-- Icon with position support -->
<i class="bi bi-{{icon}}"></i> --> <i *ngIf="icon && iconPosition === 'left'" [ngStyle]="getIconStyles()" class="bi bi-{{icon}} header-icon"></i>
<i *ngIf="icon" [style.color]="iconColor" class="bi bi-{{icon}} header-icon"></i>
<h4 class="widget-title" [style.color]="textColor">{{ title }}</h4> <h4 class="widget-title" [style.color]="textColor">{{ title }}</h4>
<i *ngIf="icon && iconPosition === 'right'" [ngStyle]="getIconStyles()" class="bi bi-{{icon}} header-icon"></i>
</div> </div>
<div *ngIf="showTrend && trendValue" class="trend-indicator" [ngStyle]="getTrendStyles()"> <div *ngIf="showTrend && trendValue" class="trend-indicator" [ngStyle]="getTrendStyles()">
{{ trendValue }} {{ trendValue }}
...@@ -50,13 +50,31 @@ ...@@ -50,13 +50,31 @@
<!-- Content --> <!-- Content -->
<div *ngIf="!isLoading && !hasError && hasRequiredRole()" class="widget-content"> <div *ngIf="!isLoading && !hasError && hasRequiredRole()" class="widget-content">
<div class="kpi-value" [style.color]="accentColor"> <!-- Icon at top position -->
<div *ngIf="icon && iconPosition === 'top'" class="icon-top" [ngStyle]="getIconStyles()">
<i class="bi bi-{{icon}}"></i>
</div>
<!-- KPI Value with conditional formatting -->
<div class="kpi-value" [ngStyle]="getValueStyles()">
{{ value }} {{ value }}
</div> </div>
<!-- Label from data field -->
<div *ngIf="label" class="kpi-label" [ngStyle]="getLabelStyles()">
{{ label }}
</div>
<!-- Unit display -->
<div *ngIf="unit" class="kpi-unit" [style.color]="textColor"> <div *ngIf="unit" class="kpi-unit" [style.color]="textColor">
{{ unit }} {{ unit }}
</div> </div>
<!-- Icon at bottom position -->
<div *ngIf="icon && iconPosition === 'bottom'" class="icon-bottom" [ngStyle]="getIconStyles()">
<i class="bi bi-{{icon}}"></i>
</div>
<!-- Data Source Info (Debug Mode) --> <!-- Data Source Info (Debug Mode) -->
<div *ngIf="dataSource !== 'static'" class="data-source-info" [style.color]="textColor"> <div *ngIf="dataSource !== 'static'" class="data-source-info" [style.color]="textColor">
<small>{{ getDataSourceInfo() }}</small> <small>{{ getDataSourceInfo() }}</small>
......
...@@ -174,6 +174,41 @@ ...@@ -174,6 +174,41 @@
opacity: 0.8; opacity: 0.8;
} }
.kpi-label {
font-size: 1rem;
font-weight: 500;
margin: 0;
opacity: 0.9;
text-align: center;
}
/* Icon positioning */
.icon-top {
display: flex;
justify-content: center;
margin-bottom: 0.5rem;
i {
display: block;
}
}
.icon-bottom {
display: flex;
justify-content: center;
margin-top: 0.5rem;
i {
display: block;
}
}
.header-left {
.header-icon {
flex-shrink: 0;
}
}
/* Responsive Design */ /* Responsive Design */
@media (max-width: 768px) { @media (max-width: 768px) {
.widget-header { .widget-header {
......
...@@ -17,14 +17,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -17,14 +17,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
// Display properties // Display properties
public value: string = '...'; public value: string = '...';
public label: string = '';
public unit: string = ''; public unit: string = '';
public icon: string = ''; public icon: string = '';
public iconPosition: string = 'left';
public iconSize: number = 24;
public backgroundColor: string = 'linear-gradient(to top right, #3366FF, #00CCFF)'; public backgroundColor: string = 'linear-gradient(to top right, #3366FF, #00CCFF)';
public iconColor: string = '#FFFFFF'; public iconColor: string = '#FFFFFF';
public borderColor: string = '#FFFFFF'; public borderColor: string = '#FFFFFF';
public textColor: string = '#FFFFFF'; public textColor: string = '#FFFFFF';
public valueColor: string = '#FFFFFF';
public labelColor: string = '#FFFFFF';
public accentColor: string = '#FFFFFF'; public accentColor: string = '#FFFFFF';
public borderRadius: number = 8; public borderRadius: number = 8;
public borderWidth: number = 1;
public shadow: string = 'medium';
// Trend properties // Trend properties
public showTrend: boolean = false; public showTrend: boolean = false;
...@@ -34,13 +41,20 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -34,13 +41,20 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
// Style properties // Style properties
public fontSize: number = 16; public fontSize: number = 16;
public valueFontSize: number = 32;
public fontWeight: string = 'normal'; public fontWeight: string = 'normal';
public fontFamily: string = 'system-ui, -apple-system, sans-serif'; public fontFamily: string = 'system-ui, -apple-system, sans-serif';
public padding: number = 16; public padding: number = 16;
public margin: number = 8; public margin: number = 8;
public borderWidth: number = 1;
public customCSS: string = ''; public customCSS: string = '';
// Data formatting properties
public valueFormat: string = 'number';
public decimalPlaces: number = 0;
public aggregation: string = 'sum';
public valueField: string = '';
public labelField: string = '';
// Animation properties // Animation properties
public enableAnimations: boolean = true; public enableAnimations: boolean = true;
public animationType: string = 'fade'; public animationType: string = 'fade';
...@@ -76,6 +90,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -76,6 +90,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public cacheDuration: number = 300; public cacheDuration: number = 300;
public dataTransform: string = ''; public dataTransform: string = '';
// Filter properties
public enableFilter: boolean = false;
public filterField: string = '';
public filterOperator: string = 'equals';
public filterValue: string = '';
public filterLabel: string = '';
// Conditional formatting properties
public enableConditionalFormatting: boolean = false;
public conditionField: string = '';
public conditionOperator: string = 'greater_than';
public conditionValue: string = '';
public trueColor: string = '#10B981';
public falseColor: string = '#EF4444';
// Security properties // Security properties
public requireAuth: boolean = false; public requireAuth: boolean = false;
public allowedRoles: string = ''; public allowedRoles: string = '';
...@@ -90,26 +119,38 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -90,26 +119,38 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
applyInitialConfig(): void { applyInitialConfig(): void {
// Basic configuration // Basic configuration
this.title = this.configObj.title || 'KPI'; this.title = this.configObj.title || 'KPI';
this.label = this.configObj.labelField ? '' : 'KPI';
this.unit = this.configObj.unit || ''; this.unit = this.configObj.unit || '';
this.icon = this.configObj.icon || 'info'; this.icon = this.configObj.icon || 'info';
this.iconPosition = this.configObj.iconPosition || 'left';
this.iconSize = this.configObj.iconSize || 24;
this.valueField = this.configObj.valueField || '';
this.labelField = this.configObj.labelField || '';
this.valueFormat = this.configObj.valueFormat || 'number';
this.decimalPlaces = this.configObj.decimalPlaces || 0;
this.aggregation = this.configObj.aggregation || 'sum';
// Style configuration // Style configuration
this.backgroundColor = this.configObj.backgroundColor || 'linear-gradient(to top right, #3366FF, #00CCFF)'; this.backgroundColor = this.configObj.backgroundColor || 'linear-gradient(to top right, #3366FF, #00CCFF)';
this.textColor = this.configObj.textColor || '#FFFFFF'; this.textColor = this.configObj.textColor || '#FFFFFF';
this.valueColor = this.configObj.valueColor || '#FFFFFF';
this.labelColor = this.configObj.labelColor || '#FFFFFF';
this.accentColor = this.configObj.accentColor || '#FFFFFF'; this.accentColor = this.configObj.accentColor || '#FFFFFF';
this.borderColor = this.configObj.borderColor || '#FFFFFF'; this.borderColor = this.configObj.borderColor || '#FFFFFF';
this.borderRadius = this.configObj.borderRadius || 8; this.borderRadius = this.configObj.borderRadius || 8;
this.borderWidth = this.configObj.borderWidth || 1;
this.shadow = this.configObj.shadow || 'medium';
this.iconColor = this.configObj.iconColor || '#FFFFFF'; this.iconColor = this.configObj.iconColor || '#FFFFFF';
// Typography configuration // Typography configuration
this.fontSize = this.configObj.fontSize || 16; this.fontSize = this.configObj.fontSize || 16;
this.valueFontSize = this.configObj.valueFontSize || 32;
this.fontWeight = this.configObj.fontWeight || 'normal'; this.fontWeight = this.configObj.fontWeight || 'normal';
this.fontFamily = this.configObj.fontFamily || 'system-ui, -apple-system, sans-serif'; this.fontFamily = this.configObj.fontFamily || 'system-ui, -apple-system, sans-serif';
// Layout configuration // Layout configuration
this.padding = this.configObj.padding || 16; this.padding = this.configObj.padding || 16;
this.margin = this.configObj.margin || 8; this.margin = this.configObj.margin || 8;
this.borderWidth = this.configObj.borderWidth || 1;
// Custom CSS // Custom CSS
this.customCSS = this.configObj.customCSS || ''; this.customCSS = this.configObj.customCSS || '';
...@@ -154,6 +195,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -154,6 +195,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this.cacheDuration = this.configObj.cacheDuration || 300; this.cacheDuration = this.configObj.cacheDuration || 300;
this.dataTransform = this.configObj.dataTransform || ''; this.dataTransform = this.configObj.dataTransform || '';
// Filter configuration
this.enableFilter = this.configObj.enableFilter || false;
this.filterField = this.configObj.filterField || '';
this.filterOperator = this.configObj.filterOperator || 'equals';
this.filterValue = this.configObj.filterValue || '';
this.filterLabel = this.configObj.filterLabel || '';
// Conditional formatting configuration
this.enableConditionalFormatting = this.configObj.enableConditionalFormatting || false;
this.conditionField = this.configObj.conditionField || '';
this.conditionOperator = this.configObj.conditionOperator || 'greater_than';
this.conditionValue = this.configObj.conditionValue || '';
this.trueColor = this.configObj.trueColor || '#10B981';
this.falseColor = this.configObj.falseColor || '#EF4444';
// Security configuration // Security configuration
this.requireAuth = this.configObj.requireAuth !== undefined ? this.configObj.requireAuth : false; this.requireAuth = this.configObj.requireAuth !== undefined ? this.configObj.requireAuth : false;
this.allowedRoles = this.configObj.allowedRoles || ''; this.allowedRoles = this.configObj.allowedRoles || '';
...@@ -171,17 +227,23 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -171,17 +227,23 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
override onDataUpdate(data: any[]): void { override onDataUpdate(data: any[]): void {
// Transform data if transform function is provided // Transform data if transform function is provided
const transformedData = this.transformData(data); let transformedData = this.transformData(data);
// Apply filtering if enabled
if (this.enableFilter && this.filterField && this.filterValue) {
transformedData = this.applyFilter(transformedData);
}
// Handle count aggregation separately as it doesn't need a valueField // Handle count aggregation separately as it doesn't need a valueField
if (this.configObj.aggregation === 'count') { if (this.aggregation === 'count') {
this.value = (transformedData?.length || 0).toLocaleString(); this.value = (transformedData?.length || 0).toLocaleString();
this.updateTrendData(transformedData); this.updateTrendData(transformedData);
this.updateLabel(transformedData);
return; return;
} }
// For other aggregations, valueField is required // For other aggregations, valueField is required
if (!this.configObj.valueField) { if (!this.valueField) {
this.value = 'N/A'; // Indicate a configuration error this.value = 'N/A'; // Indicate a configuration error
console.error('SimpleKpiWidget Error: valueField is not configured for this widget.', this.configObj); console.error('SimpleKpiWidget Error: valueField is not configured for this widget.', this.configObj);
return; return;
...@@ -191,36 +253,70 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -191,36 +253,70 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
if (!transformedData || transformedData.length === 0) { if (!transformedData || transformedData.length === 0) {
this.value = '0'; this.value = '0';
this.trendValue = ''; this.trendValue = '';
this.updateLabel(transformedData);
return; return;
} }
let kpiValue = 0; let kpiValue = 0;
if (this.configObj.aggregation === 'sum') { if (this.aggregation === 'sum') {
kpiValue = transformedData.reduce((sum, item) => sum + (item[this.configObj.valueField] || 0), 0); kpiValue = transformedData.reduce((sum, item) => sum + (item[this.valueField] || 0), 0);
} else if (this.configObj.aggregation === 'average') { } else if (this.aggregation === 'average') {
const sum = transformedData.reduce((sum, item) => sum + (item[this.configObj.valueField] || 0), 0); const sum = transformedData.reduce((sum, item) => sum + (item[this.valueField] || 0), 0);
kpiValue = sum / transformedData.length; kpiValue = sum / transformedData.length;
} else if (this.configObj.aggregation === 'max') { } else if (this.aggregation === 'max') {
kpiValue = Math.max(...transformedData.map(item => item[this.configObj.valueField] || 0)); kpiValue = Math.max(...transformedData.map(item => item[this.valueField] || 0));
} else if (this.configObj.aggregation === 'min') { } else if (this.aggregation === 'min') {
kpiValue = Math.min(...transformedData.map(item => item[this.configObj.valueField] || 0)); kpiValue = Math.min(...transformedData.map(item => item[this.valueField] || 0));
} else if (this.aggregation === 'first') {
kpiValue = transformedData[0][this.valueField] || 0;
} else if (this.aggregation === 'last') {
kpiValue = transformedData[transformedData.length - 1][this.valueField] || 0;
} else { } else {
// Default to first value if no aggregation is specified // Default to first value if no aggregation is specified
kpiValue = transformedData[0][this.configObj.valueField] || 0; kpiValue = transformedData[0][this.valueField] || 0;
} }
// Format the value based on configuration // Format the value based on configuration
this.value = this.formatValue(kpiValue); this.value = this.formatValue(kpiValue);
// Update label if labelField is configured
this.updateLabel(transformedData);
// Update trend data if enabled // Update trend data if enabled
this.updateTrendData(transformedData, kpiValue); this.updateTrendData(transformedData, kpiValue);
// Apply conditional formatting if enabled
this.applyConditionalFormatting(kpiValue, transformedData);
} }
private formatValue(value: number): string { private formatValue(value: number): string {
if (this.configObj.decimalPlaces !== undefined) { let formattedValue: string;
return value.toFixed(this.configObj.decimalPlaces);
if (this.valueFormat === 'currency') {
formattedValue = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: this.decimalPlaces,
maximumFractionDigits: this.decimalPlaces
}).format(value);
} else if (this.valueFormat === 'percentage') {
formattedValue = (value * 100).toFixed(this.decimalPlaces) + '%';
} else if (this.valueFormat === 'decimal') {
formattedValue = value.toFixed(this.decimalPlaces);
} else {
// Number format
formattedValue = value.toLocaleString('en-US', {
minimumFractionDigits: this.decimalPlaces,
maximumFractionDigits: this.decimalPlaces
});
}
// Add unit if specified
if (this.unit) {
formattedValue += ` ${this.unit}`;
} }
return value.toLocaleString();
return formattedValue;
} }
private updateTrendData(data: any[], currentValue?: number): void { private updateTrendData(data: any[], currentValue?: number): void {
...@@ -608,4 +704,118 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -608,4 +704,118 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
} }
} }
// Method to apply filtering
private applyFilter(data: any[]): any[] {
if (!this.enableFilter || !this.filterField || !this.filterValue) {
return data;
}
return data.filter(item => {
const fieldValue = item[this.filterField];
const filterValue = this.filterValue;
switch (this.filterOperator) {
case 'equals':
return fieldValue == filterValue;
case 'not_equals':
return fieldValue != filterValue;
case 'greater_than':
return Number(fieldValue) > Number(filterValue);
case 'less_than':
return Number(fieldValue) < Number(filterValue);
case 'contains':
return String(fieldValue).toLowerCase().includes(String(filterValue).toLowerCase());
case 'starts_with':
return String(fieldValue).toLowerCase().startsWith(String(filterValue).toLowerCase());
case 'ends_with':
return String(fieldValue).toLowerCase().endsWith(String(filterValue).toLowerCase());
default:
return true;
}
});
}
// Method to update label from data
private updateLabel(data: any[]): void {
if (this.labelField && data && data.length > 0) {
this.label = data[0][this.labelField] || '';
}
}
// Method to apply conditional formatting
private applyConditionalFormatting(value: number, data: any[]): void {
if (!this.enableConditionalFormatting || !this.conditionField || !this.conditionValue) {
return;
}
let conditionMet = false;
const conditionValue = Number(this.conditionValue);
switch (this.conditionOperator) {
case 'greater_than':
conditionMet = value > conditionValue;
break;
case 'less_than':
conditionMet = value < conditionValue;
break;
case 'equals':
conditionMet = value == conditionValue;
break;
case 'not_equals':
conditionMet = value != conditionValue;
break;
case 'greater_equal':
conditionMet = value >= conditionValue;
break;
case 'less_equal':
conditionMet = value <= conditionValue;
break;
}
// Apply conditional colors
if (conditionMet) {
this.valueColor = this.trueColor;
} else {
this.valueColor = this.falseColor;
}
}
// Method to get shadow styles
getShadowStyles(): string {
const shadowMap: { [key: string]: string } = {
'none': 'none',
'small': '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)',
'medium': '0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)',
'large': '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)'
};
return shadowMap[this.shadow] || shadowMap['medium'];
}
// Method to get icon styles
getIconStyles(): { [key: string]: string } {
return {
'font-size': `${this.iconSize}px`,
'color': this.iconColor,
'width': `${this.iconSize}px`,
'height': `${this.iconSize}px`
};
}
// Method to get value styles
getValueStyles(): { [key: string]: string } {
return {
'font-size': `${this.valueFontSize}px`,
'color': this.valueColor,
'font-weight': 'bold'
};
}
// Method to get label styles
getLabelStyles(): { [key: string]: string } {
return {
'font-size': `${this.fontSize}px`,
'color': this.labelColor
};
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment