Commit 7ff59ecb by Ooh-Ao

kpi

parent fec4de97
...@@ -22,14 +22,15 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -22,14 +22,15 @@ 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">
<!-- Basic Configuration Tab --> <mat-tab-group class="config-tabs">
<div class="config-section"> <!-- Basic Configuration Tab -->
<h3 class="text-blue-600">Basic Configuration</h3> <mat-tab label="Basic">
<div class="config-section">
<h3 class="text-blue-600">Basic Configuration</h3>
<mat-form-field appearance="fill" class="w-full"> <mat-form-field appearance="fill" class="w-full">
<mat-label>Title</mat-label> <mat-label>Title</mat-label>
...@@ -141,11 +142,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -141,11 +142,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</div> </div>
</mat-tab>
<!-- Style Tab --> <!-- Style Tab -->
<div class="config-section"> <mat-tab label="Style">
<h3 class="text-blue-600">Style Configuration</h3> <div class="config-section">
<h3 class="text-blue-600">Style Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
...@@ -221,11 +224,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -221,11 +224,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Drop shadow effect</mat-hint> <mat-hint>Drop shadow effect</mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</mat-tab>
<!-- Icon Tab --> <!-- Icon Tab -->
<div class="config-section"> <mat-tab label="Icon">
<h3 class="text-blue-600">Icon Configuration</h3> <div class="config-section">
<h3 class="text-blue-600">Icon Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
...@@ -276,11 +281,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -276,11 +281,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Icon color</mat-hint> <mat-hint>Icon color</mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</mat-tab>
<!-- Filter Tab --> <!-- Filter Tab -->
<div class="config-section"> <mat-tab label="Filter">
<h3 class="text-blue-600">Filter Configuration</h3> <div class="config-section">
<h3 class="text-blue-600">Filter Configuration</h3>
<div class="flex items-center mb-4"> <div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableFilter" name="enableFilter"> <mat-checkbox [(ngModel)]="currentConfig.enableFilter" name="enableFilter">
...@@ -325,11 +332,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -325,11 +332,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Label for the filter</mat-hint> <mat-hint>Label for the filter</mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</mat-tab>
<!-- Trend Tab --> <!-- Trend Tab -->
<div class="config-section"> <mat-tab label="Trend">
<h3 class="text-blue-600">Trend Configuration</h3> <div class="config-section">
<h3 class="text-blue-600">Trend Configuration</h3>
<div class="flex items-center mb-4"> <div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.showTrend" name="showTrend"> <mat-checkbox [(ngModel)]="currentConfig.showTrend" name="showTrend">
...@@ -376,11 +385,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -376,11 +385,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Threshold for significant trend</mat-hint> <mat-hint>Threshold for significant trend</mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</mat-tab>
<!-- Animation Tab --> <!-- Animation Tab -->
<div class="config-section"> <mat-tab label="Animation">
<h3 class="text-blue-600">Animation Configuration</h3> <div class="config-section">
<h3 class="text-blue-600">Animation Configuration</h3>
<div class="flex items-center mb-4"> <div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableAnimations" name="enableAnimations"> <mat-checkbox [(ngModel)]="currentConfig.enableAnimations" name="enableAnimations">
...@@ -436,11 +447,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -436,11 +447,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
Auto Refresh Animation Auto Refresh Animation
</mat-checkbox> </mat-checkbox>
</div> </div>
</div> </div>
</mat-tab>
<!-- Condition Tab --> <!-- Condition Tab -->
<div class="config-section"> <mat-tab label="Condition">
<h3 class="text-blue-600">Conditional Formatting</h3> <div class="config-section">
<h3 class="text-blue-600">Conditional Formatting</h3>
<div class="flex items-center mb-4"> <div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableConditionalFormatting" name="enableConditionalFormatting"> <mat-checkbox [(ngModel)]="currentConfig.enableConditionalFormatting" name="enableConditionalFormatting">
...@@ -492,21 +505,52 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con ...@@ -492,21 +505,52 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field> </mat-form-field>
</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 {
......
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