Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
portal-apps-manage
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
angular
portal-apps-manage
Commits
7ff59ecb
Commit
7ff59ecb
authored
Sep 16, 2025
by
Ooh-Ao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
kpi
parent
fec4de97
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
376 additions
and
54 deletions
+376
-54
simple-kpi-config.component.ts
.../configs/simple-kpi-config/simple-kpi-config.component.ts
+91
-32
simple-kpi-widget.component.html
...idgets/simple-kpi-widget/simple-kpi-widget.component.html
+23
-5
simple-kpi-widget.component.scss
...idgets/simple-kpi-widget/simple-kpi-widget.component.scss
+35
-0
simple-kpi-widget.component.ts
.../widgets/simple-kpi-widget/simple-kpi-widget.component.ts
+227
-17
No files found.
src/app/portal-manage/dashboard-management/widgets/configs/simple-kpi-config/simple-kpi-config.component.ts
View file @
7ff59ecb
...
...
@@ -22,14 +22,15 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
MatCheckboxModule
,
MatButtonModule
,
MatIconModule
,
MatTabsModule
,
BaseConfigComponent
MatTabsModule
],
template
:
`
<app-base-config>
<!-- Basic Configuration Tab -->
<div class="config-section">
<h3 class="text-blue-600">Basic Configuration</h3>
<div class="config-container">
<mat-tab-group class="config-tabs">
<!-- Basic Configuration Tab -->
<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-label>Title</mat-label>
...
...
@@ -141,11 +142,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field>
</div>
</div>
</div>
</div>
</mat-tab>
<!-- Style Tab -->
<div class="config-section">
<h3 class="text-blue-600">Style Configuration</h3>
<!-- Style Tab -->
<mat-tab label="Style">
<div class="config-section">
<h3 class="text-blue-600">Style Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
...
...
@@ -221,11 +224,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Drop shadow effect</mat-hint>
</mat-form-field>
</div>
</div>
</div>
</mat-tab>
<!-- Icon Tab -->
<div class="config-section">
<h3 class="text-blue-600">Icon Configuration</h3>
<!-- Icon Tab -->
<mat-tab label="Icon">
<div class="config-section">
<h3 class="text-blue-600">Icon Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
...
...
@@ -276,11 +281,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Icon color</mat-hint>
</mat-form-field>
</div>
</div>
</div>
</mat-tab>
<!-- Filter Tab -->
<div class="config-section">
<h3 class="text-blue-600">Filter Configuration</h3>
<!-- Filter Tab -->
<mat-tab label="Filter">
<div class="config-section">
<h3 class="text-blue-600">Filter Configuration</h3>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableFilter" name="enableFilter">
...
...
@@ -325,11 +332,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Label for the filter</mat-hint>
</mat-form-field>
</div>
</div>
</div>
</mat-tab>
<!-- Trend Tab -->
<div class="config-section">
<h3 class="text-blue-600">Trend Configuration</h3>
<!-- Trend Tab -->
<mat-tab label="Trend">
<div class="config-section">
<h3 class="text-blue-600">Trend Configuration</h3>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.showTrend" name="showTrend">
...
...
@@ -376,11 +385,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Threshold for significant trend</mat-hint>
</mat-form-field>
</div>
</div>
</div>
</mat-tab>
<!-- Animation Tab -->
<div class="config-section">
<h3 class="text-blue-600">Animation Configuration</h3>
<!-- Animation Tab -->
<mat-tab label="Animation">
<div class="config-section">
<h3 class="text-blue-600">Animation Configuration</h3>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableAnimations" name="enableAnimations">
...
...
@@ -436,11 +447,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
Auto Refresh Animation
</mat-checkbox>
</div>
</div>
</div>
</mat-tab>
<!-- Condition Tab -->
<div class="config-section">
<h3 class="text-blue-600">Conditional Formatting</h3>
<!-- Condition Tab -->
<mat-tab label="Condition">
<div class="config-section">
<h3 class="text-blue-600">Conditional Formatting</h3>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableConditionalFormatting" name="enableConditionalFormatting">
...
...
@@ -492,21 +505,52 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field>
</div>
</div>
</div>
</app-base-config>
</div>
</mat-tab>
</mat-tab-group>
</div>
`
,
styles
:
[
`
.config-container {
padding: 16px;
}
.config-section {
margin-bottom: 24px;
}
.config-tabs {
margin-top: 16px;
}
.config-tabs .mat-tab-body-content {
padding: 16px 0;
}
`
]
})
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
()
{
this
.
initializeDefaultConfig
();
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
()
{
if
(
!
this
.
currentConfig
.
backgroundColor
)
this
.
currentConfig
.
backgroundColor
=
'#3366FF'
;
if
(
!
this
.
currentConfig
.
textColor
)
this
.
currentConfig
.
textColor
=
'#FFFFFF'
;
...
...
@@ -540,4 +584,19 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
if
(
!
this
.
currentConfig
.
conditionOperator
)
this
.
currentConfig
.
conditionOperator
=
'greater_than'
;
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
);
}
}
src/app/portal-manage/dashboard-management/widgets/simple-kpi-widget/simple-kpi-widget.component.html
View file @
7ff59ecb
...
...
@@ -18,13 +18,13 @@
<style
*
ngIf=
"hasCustomCSS()"
[
innerHTML
]="
customCSS
"
></style>
<!-- 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-left"
>
<!-- {{icon}}
<i class="bi bi-{{icon}}"></i> -->
<i
*
ngIf=
"icon"
[
style
.
color
]="
iconColor
"
class=
"bi bi-{{icon}} header-icon"
></i>
<!-- Icon with position support -->
<i
*
ngIf=
"icon && iconPosition === 'left'"
[
ngStyle
]="
getIconStyles
()"
class=
"bi bi-{{icon}} header-icon"
></i>
<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
*
ngIf=
"showTrend && trendValue"
class=
"trend-indicator"
[
ngStyle
]="
getTrendStyles
()"
>
{{ trendValue }}
...
...
@@ -50,13 +50,31 @@
<!-- 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 }}
</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
"
>
{{ unit }}
</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) -->
<div
*
ngIf=
"dataSource !== 'static'"
class=
"data-source-info"
[
style
.
color
]="
textColor
"
>
<small>
{{ getDataSourceInfo() }}
</small>
...
...
src/app/portal-manage/dashboard-management/widgets/simple-kpi-widget/simple-kpi-widget.component.scss
View file @
7ff59ecb
...
...
@@ -174,6 +174,41 @@
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 */
@media
(
max-width
:
768px
)
{
.widget-header
{
...
...
src/app/portal-manage/dashboard-management/widgets/simple-kpi-widget/simple-kpi-widget.component.ts
View file @
7ff59ecb
...
...
@@ -17,14 +17,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
// Display properties
public
value
:
string
=
'...'
;
public
label
:
string
=
''
;
public
unit
:
string
=
''
;
public
icon
:
string
=
''
;
public
iconPosition
:
string
=
'left'
;
public
iconSize
:
number
=
24
;
public
backgroundColor
:
string
=
'linear-gradient(to top right, #3366FF, #00CCFF)'
;
public
iconColor
:
string
=
'#FFFFFF'
;
public
borderColor
:
string
=
'#FFFFFF'
;
public
textColor
:
string
=
'#FFFFFF'
;
public
valueColor
:
string
=
'#FFFFFF'
;
public
labelColor
:
string
=
'#FFFFFF'
;
public
accentColor
:
string
=
'#FFFFFF'
;
public
borderRadius
:
number
=
8
;
public
borderWidth
:
number
=
1
;
public
shadow
:
string
=
'medium'
;
// Trend properties
public
showTrend
:
boolean
=
false
;
...
...
@@ -34,13 +41,20 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
// Style properties
public
fontSize
:
number
=
16
;
public
valueFontSize
:
number
=
32
;
public
fontWeight
:
string
=
'normal'
;
public
fontFamily
:
string
=
'system-ui, -apple-system, sans-serif'
;
public
padding
:
number
=
16
;
public
margin
:
number
=
8
;
public
borderWidth
:
number
=
1
;
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
public
enableAnimations
:
boolean
=
true
;
public
animationType
:
string
=
'fade'
;
...
...
@@ -76,6 +90,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public
cacheDuration
:
number
=
300
;
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
public
requireAuth
:
boolean
=
false
;
public
allowedRoles
:
string
=
''
;
...
...
@@ -90,26 +119,38 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
applyInitialConfig
():
void
{
// Basic configuration
this
.
title
=
this
.
configObj
.
title
||
'KPI'
;
this
.
label
=
this
.
configObj
.
labelField
?
''
:
'KPI'
;
this
.
unit
=
this
.
configObj
.
unit
||
''
;
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
this
.
backgroundColor
=
this
.
configObj
.
backgroundColor
||
'linear-gradient(to top right, #3366FF, #00CCFF)'
;
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
.
borderColor
=
this
.
configObj
.
borderColor
||
'#FFFFFF'
;
this
.
borderRadius
=
this
.
configObj
.
borderRadius
||
8
;
this
.
borderWidth
=
this
.
configObj
.
borderWidth
||
1
;
this
.
shadow
=
this
.
configObj
.
shadow
||
'medium'
;
this
.
iconColor
=
this
.
configObj
.
iconColor
||
'#FFFFFF'
;
// Typography configuration
this
.
fontSize
=
this
.
configObj
.
fontSize
||
16
;
this
.
valueFontSize
=
this
.
configObj
.
valueFontSize
||
32
;
this
.
fontWeight
=
this
.
configObj
.
fontWeight
||
'normal'
;
this
.
fontFamily
=
this
.
configObj
.
fontFamily
||
'system-ui, -apple-system, sans-serif'
;
// Layout configuration
this
.
padding
=
this
.
configObj
.
padding
||
16
;
this
.
margin
=
this
.
configObj
.
margin
||
8
;
this
.
borderWidth
=
this
.
configObj
.
borderWidth
||
1
;
// Custom CSS
this
.
customCSS
=
this
.
configObj
.
customCSS
||
''
;
...
...
@@ -154,6 +195,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
cacheDuration
=
this
.
configObj
.
cacheDuration
||
300
;
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
this
.
requireAuth
=
this
.
configObj
.
requireAuth
!==
undefined
?
this
.
configObj
.
requireAuth
:
false
;
this
.
allowedRoles
=
this
.
configObj
.
allowedRoles
||
''
;
...
...
@@ -171,17 +227,23 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
override
onDataUpdate
(
data
:
any
[]):
void
{
// 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
if
(
this
.
configObj
.
aggregation
===
'count'
)
{
if
(
this
.
aggregation
===
'count'
)
{
this
.
value
=
(
transformedData
?.
length
||
0
).
toLocaleString
();
this
.
updateTrendData
(
transformedData
);
this
.
updateLabel
(
transformedData
);
return
;
}
// For other aggregations, valueField is required
if
(
!
this
.
configObj
.
valueField
)
{
if
(
!
this
.
valueField
)
{
this
.
value
=
'N/A'
;
// Indicate a configuration error
console
.
error
(
'SimpleKpiWidget Error: valueField is not configured for this widget.'
,
this
.
configObj
);
return
;
...
...
@@ -191,36 +253,70 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
if
(
!
transformedData
||
transformedData
.
length
===
0
)
{
this
.
value
=
'0'
;
this
.
trendValue
=
''
;
this
.
updateLabel
(
transformedData
);
return
;
}
let
kpiValue
=
0
;
if
(
this
.
configObj
.
aggregation
===
'sum'
)
{
kpiValue
=
transformedData
.
reduce
((
sum
,
item
)
=>
sum
+
(
item
[
this
.
configObj
.
valueField
]
||
0
),
0
);
}
else
if
(
this
.
configObj
.
aggregation
===
'average'
)
{
const
sum
=
transformedData
.
reduce
((
sum
,
item
)
=>
sum
+
(
item
[
this
.
configObj
.
valueField
]
||
0
),
0
);
if
(
this
.
aggregation
===
'sum'
)
{
kpiValue
=
transformedData
.
reduce
((
sum
,
item
)
=>
sum
+
(
item
[
this
.
valueField
]
||
0
),
0
);
}
else
if
(
this
.
aggregation
===
'average'
)
{
const
sum
=
transformedData
.
reduce
((
sum
,
item
)
=>
sum
+
(
item
[
this
.
valueField
]
||
0
),
0
);
kpiValue
=
sum
/
transformedData
.
length
;
}
else
if
(
this
.
configObj
.
aggregation
===
'max'
)
{
kpiValue
=
Math
.
max
(...
transformedData
.
map
(
item
=>
item
[
this
.
configObj
.
valueField
]
||
0
));
}
else
if
(
this
.
configObj
.
aggregation
===
'min'
)
{
kpiValue
=
Math
.
min
(...
transformedData
.
map
(
item
=>
item
[
this
.
configObj
.
valueField
]
||
0
));
}
else
if
(
this
.
aggregation
===
'max'
)
{
kpiValue
=
Math
.
max
(...
transformedData
.
map
(
item
=>
item
[
this
.
valueField
]
||
0
));
}
else
if
(
this
.
aggregation
===
'min'
)
{
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
{
// 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
this
.
value
=
this
.
formatValue
(
kpiValue
);
// Update label if labelField is configured
this
.
updateLabel
(
transformedData
);
// Update trend data if enabled
this
.
updateTrendData
(
transformedData
,
kpiValue
);
// Apply conditional formatting if enabled
this
.
applyConditionalFormatting
(
kpiValue
,
transformedData
);
}
private
formatValue
(
value
:
number
):
string
{
if
(
this
.
configObj
.
decimalPlaces
!==
undefined
)
{
return
value
.
toFixed
(
this
.
configObj
.
decimalPlaces
);
let
formattedValue
:
string
;
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
{
...
...
@@ -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
};
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment