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
49e784c7
Commit
49e784c7
authored
Sep 16, 2025
by
Ooh-Ao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
config
parent
7ff59ecb
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
415 additions
and
429 deletions
+415
-429
widget-config.component.html
...ard-management/widget-config/widget-config.component.html
+0
-416
simple-kpi-config.component.ts
.../configs/simple-kpi-config/simple-kpi-config.component.ts
+415
-13
No files found.
src/app/portal-manage/dashboard-management/widget-config/widget-config.component.html
View file @
49e784c7
...
...
@@ -605,423 +605,7 @@
</mat-form-field>
</div>
<div
*
ngIf=
"widgetType === 'SimpleKpiWidgetComponent'"
>
<!-- Basic Configuration -->
<div
class=
"config-section border p-4 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-3 text-blue-600"
>
Basic Configuration
</h3>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
aria-label=
"Widget title"
>
<mat-hint>
Widget title displayed in header
</mat-hint>
</mat-form-field>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Value Field
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
valueField
"
name=
"valueField"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
<mat-hint>
Field containing the KPI value
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Label Field
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
labelField
"
name=
"labelField"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
<mat-hint>
Field containing the KPI label
</mat-hint>
</mat-form-field>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Aggregation
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
aggregation
"
name=
"aggregation"
>
<mat-option
value=
"count"
>
Count
</mat-option>
<mat-option
value=
"sum"
>
Sum
</mat-option>
<mat-option
value=
"average"
>
Average
</mat-option>
<mat-option
value=
"max"
>
Maximum
</mat-option>
<mat-option
value=
"min"
>
Minimum
</mat-option>
</mat-select>
<mat-hint>
How to aggregate the data
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Unit
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
unit
"
name=
"unit"
placeholder=
"e.g., $, %, items"
aria-label=
"Unit"
>
<mat-hint>
Unit to display after the value
</mat-hint>
</mat-form-field>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Icon (Bootstrap Icons)
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
icon
"
name=
"icon"
placeholder=
"e.g., person-fill, building"
aria-label=
"Icon"
>
<mat-hint>
Find icons at icons.getbootstrap.com
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Decimal Places
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
decimalPlaces
"
name=
"decimalPlaces"
min=
"0"
max=
"10"
aria-label=
"Decimal places"
>
<mat-hint>
Number of decimal places to show
</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Trend Configuration -->
<div
class=
"config-section border p-4 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-3 text-green-600"
>
Trend Settings
</h3>
<div
class=
"flex items-center mb-3"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
showTrend
"
name=
"showTrend"
class=
"mr-2"
>
Show Trend Indicator
</mat-checkbox>
</div>
<div
*
ngIf=
"currentConfig.showTrend"
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Trend Field
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
trendField
"
name=
"trendField"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
<mat-hint>
Field containing trend data
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Trend Type
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
trendType
"
name=
"trendType"
>
<mat-option
value=
"percentage"
>
Percentage Change
</mat-option>
<mat-option
value=
"absolute"
>
Absolute Change
</mat-option>
<mat-option
value=
"ratio"
>
Ratio
</mat-option>
</mat-select>
<mat-hint>
How to calculate trend
</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Style Configuration -->
<div
class=
"config-section border p-4 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-3 text-purple-600"
>
Style
&
Colors
</h3>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Background Color
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
backgroundColor
"
name=
"backgroundColor"
placeholder=
"#FF0000 or linear-gradient(...)"
aria-label=
"Background color"
>
<mat-hint>
Header background color
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Text Color
</mat-label>
<input
matInput
type=
"color"
[(
ngModel
)]="
currentConfig
.
textColor
"
name=
"textColor"
class=
"h-[40px]"
aria-label=
"Text color"
>
<mat-hint>
Text color for labels
</mat-hint>
</mat-form-field>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Accent Color
</mat-label>
<input
matInput
type=
"color"
[(
ngModel
)]="
currentConfig
.
accentColor
"
name=
"accentColor"
class=
"h-[40px]"
aria-label=
"Accent color"
>
<mat-hint>
Color for the main KPI value
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Border Color
</mat-label>
<input
matInput
type=
"color"
[(
ngModel
)]="
currentConfig
.
borderColor
"
name=
"borderColor"
class=
"h-[40px]"
aria-label=
"Border color"
>
<mat-hint>
Card border color
</mat-hint>
</mat-form-field>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Icon Color
</mat-label>
<input
matInput
type=
"color"
[(
ngModel
)]="
currentConfig
.
iconColor
"
name=
"iconColor"
class=
"h-[40px]"
aria-label=
"Icon color"
>
<mat-hint>
Icon color
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Border Radius (px)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
borderRadius
"
name=
"borderRadius"
min=
"0"
max=
"50"
aria-label=
"Border radius"
>
<mat-hint>
Roundness of widget corners
</mat-hint>
</mat-form-field>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-3 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Padding (px)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
padding
"
name=
"padding"
min=
"0"
max=
"100"
aria-label=
"Padding"
>
<mat-hint>
Internal spacing
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Font Size (px)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
fontSize
"
name=
"fontSize"
min=
"10"
max=
"48"
aria-label=
"Font size"
>
<mat-hint>
Font size for values
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Font Weight
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
fontWeight
"
name=
"fontWeight"
>
<mat-option
value=
"normal"
>
Normal
</mat-option>
<mat-option
value=
"bold"
>
Bold
</mat-option>
<mat-option
value=
"lighter"
>
Lighter
</mat-option>
<mat-option
value=
"bolder"
>
Bolder
</mat-option>
</mat-select>
<mat-hint>
Font weight
</mat-hint>
</mat-form-field>
</div>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Font Family
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
fontFamily
"
name=
"fontFamily"
>
<mat-option
value=
"system-ui, -apple-system, sans-serif"
>
System Font
</mat-option>
<mat-option
value=
"Arial, sans-serif"
>
Arial
</mat-option>
<mat-option
value=
"Helvetica, sans-serif"
>
Helvetica
</mat-option>
<mat-option
value=
"Georgia, serif"
>
Georgia
</mat-option>
<mat-option
value=
"Times New Roman, serif"
>
Times New Roman
</mat-option>
<mat-option
value=
"Courier New, monospace"
>
Courier New
</mat-option>
</mat-select>
<mat-hint>
Font family for the widget
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Custom CSS
</mat-label>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
customCSS
"
name=
"customCSS"
rows=
"4"
placeholder=
"/* Add custom CSS rules here */ .simple-kpi-widget:hover { transform: translateY(-4px); }"
></textarea>
<mat-hint>
Add custom CSS for advanced styling
</mat-hint>
</mat-form-field>
</div>
<!-- Animation Configuration -->
<div
class=
"config-section border p-4 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-3 text-orange-600"
>
Animation Settings
</h3>
<div
class=
"flex items-center mb-3"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
enableAnimations
"
name=
"enableAnimations"
class=
"mr-2"
>
Enable Animations
</mat-checkbox>
</div>
<div
*
ngIf=
"currentConfig.enableAnimations"
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Animation Type
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
animationType
"
name=
"animationType"
>
<mat-option
value=
"fade"
>
Fade
</mat-option>
<mat-option
value=
"slide"
>
Slide
</mat-option>
<mat-option
value=
"bounce"
>
Bounce
</mat-option>
<mat-option
value=
"pulse"
>
Pulse
</mat-option>
<mat-option
value=
"none"
>
None
</mat-option>
</mat-select>
<mat-hint>
Animation effect type
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Animation Duration (ms)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
animationDuration
"
name=
"animationDuration"
min=
"100"
max=
"2000"
step=
"100"
aria-label=
"Animation duration"
>
<mat-hint>
Duration of animation
</mat-hint>
</mat-form-field>
</div>
<div
*
ngIf=
"currentConfig.enableAnimations"
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
hoverEffects
"
name=
"hoverEffects"
class=
"mr-2"
>
Enable Hover Effects
</mat-checkbox>
</div>
</div>
<!-- Interaction Configuration -->
<div
class=
"config-section border p-4 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-3 text-indigo-600"
>
Interaction Settings
</h3>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
enableTooltip
"
name=
"enableTooltip"
class=
"mr-2"
>
Enable Tooltips
</mat-checkbox>
</div>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
enableClick
"
name=
"enableClick"
class=
"mr-2"
>
Enable Click Events
</mat-checkbox>
</div>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
enableHover
"
name=
"enableHover"
class=
"mr-2"
>
Enable Hover Effects
</mat-checkbox>
</div>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
enableExport
"
name=
"enableExport"
class=
"mr-2"
>
Enable Export
</mat-checkbox>
</div>
</div>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Click Action
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
clickAction
"
name=
"clickAction"
>
<mat-option
value=
"none"
>
None
</mat-option>
<mat-option
value=
"drill_down"
>
Drill Down
</mat-option>
<mat-option
value=
"open_modal"
>
Open Modal
</mat-option>
<mat-option
value=
"navigate"
>
Navigate
</mat-option>
<mat-option
value=
"custom"
>
Custom
</mat-option>
</mat-select>
<mat-hint>
Action when widget is clicked
</mat-hint>
</mat-form-field>
<mat-form-field
*
ngIf=
"currentConfig.clickAction === 'custom'"
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Custom Click Handler
</mat-label>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
customClickHandler
"
name=
"customClickHandler"
rows=
"3"
placeholder=
"function(event) { console.log('Widget clicked:', event); }"
></textarea>
<mat-hint>
Custom JavaScript function for click events
</mat-hint>
</mat-form-field>
</div>
<!-- Layout Configuration -->
<div
class=
"config-section border p-4 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-3 text-teal-600"
>
Layout Settings
</h3>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Width (px)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
width
"
name=
"width"
min=
"100"
max=
"800"
aria-label=
"Widget width"
>
<mat-hint>
Widget width
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Height (px)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
height
"
name=
"height"
min=
"100"
max=
"600"
aria-label=
"Widget height"
>
<mat-hint>
Widget height
</mat-hint>
</mat-form-field>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Minimum Width (px)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
minWidth
"
name=
"minWidth"
min=
"50"
max=
"400"
aria-label=
"Minimum width"
>
<mat-hint>
Minimum widget width
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Minimum Height (px)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
minHeight
"
name=
"minHeight"
min=
"50"
max=
"300"
aria-label=
"Minimum height"
>
<mat-hint>
Minimum widget height
</mat-hint>
</mat-form-field>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Aspect Ratio
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
aspectRatio
"
name=
"aspectRatio"
>
<mat-option
value=
"auto"
>
Auto
</mat-option>
<mat-option
value=
"16:9"
>
16:9
</mat-option>
<mat-option
value=
"4:3"
>
4:3
</mat-option>
<mat-option
value=
"1:1"
>
1:1 (Square)
</mat-option>
<mat-option
value=
"3:2"
>
3:2
</mat-option>
</mat-select>
<mat-hint>
Widget aspect ratio
</mat-hint>
</mat-form-field>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
responsive
"
name=
"responsive"
class=
"mr-2"
>
Responsive Layout
</mat-checkbox>
</div>
</div>
</div>
<!-- Data Configuration -->
<div
class=
"config-section border p-4 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-3 text-red-600"
>
Data Settings
</h3>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Data Source
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
dataSource
"
name=
"dataSource"
>
<mat-option
value=
"static"
>
Static Data
</mat-option>
<mat-option
value=
"api"
>
API Endpoint
</mat-option>
<mat-option
value=
"websocket"
>
WebSocket
</mat-option>
<mat-option
value=
"file"
>
File Upload
</mat-option>
</mat-select>
<mat-hint>
Data source type
</mat-hint>
</mat-form-field>
<mat-form-field
*
ngIf=
"currentConfig.dataSource === 'api'"
appearance=
"fill"
class=
"w-full"
>
<mat-label>
API Endpoint
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
apiEndpoint
"
name=
"apiEndpoint"
placeholder=
"/api/kpi-data"
aria-label=
"API endpoint"
>
<mat-hint>
API endpoint URL
</mat-hint>
</mat-form-field>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Refresh Interval (ms)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
refreshInterval
"
name=
"refreshInterval"
min=
"0"
step=
"1000"
aria-label=
"Refresh interval"
>
<mat-hint>
0 = No auto-refresh
</mat-hint>
</mat-form-field>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
cacheEnabled
"
name=
"cacheEnabled"
class=
"mr-2"
>
Enable Data Caching
</mat-checkbox>
</div>
</div>
<mat-form-field
*
ngIf=
"currentConfig.cacheEnabled"
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Cache Duration (seconds)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
cacheDuration
"
name=
"cacheDuration"
min=
"1"
max=
"3600"
aria-label=
"Cache duration"
>
<mat-hint>
How long to cache data
</mat-hint>
</mat-form-field>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Data Transform Function
</mat-label>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
dataTransform
"
name=
"dataTransform"
rows=
"3"
placeholder=
"data => data.map(item => ({ ...item, formattedValue: formatCurrency(item.value) }))"
></textarea>
<mat-hint>
JavaScript function to transform data
</mat-hint>
</mat-form-field>
</div>
<!-- Security Configuration -->
<div
class=
"config-section border p-4 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-3 text-gray-600"
>
Security Settings
</h3>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
requireAuth
"
name=
"requireAuth"
class=
"mr-2"
>
Require Authentication
</mat-checkbox>
</div>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
dataEncryption
"
name=
"dataEncryption"
class=
"mr-2"
>
Enable Data Encryption
</mat-checkbox>
</div>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<div
class=
"flex items-center"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
auditLog
"
name=
"auditLog"
class=
"mr-2"
>
Enable Audit Logging
</mat-checkbox>
</div>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Rate Limit (requests/min)
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
currentConfig
.
rateLimit
"
name=
"rateLimit"
min=
"0"
aria-label=
"Rate limit"
>
<mat-hint>
0 = No limit
</mat-hint>
</mat-form-field>
</div>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Allowed Roles (comma-separated)
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
allowedRoles
"
name=
"allowedRoles"
placeholder=
"admin, analyst, manager"
aria-label=
"Allowed roles"
>
<mat-hint>
Roles that can access this widget
</mat-hint>
</mat-form-field>
</div>
</div>
<div
*
ngIf=
"widgetType === 'PieChartWidgetComponent' || widgetType === 'BarChartWidgetComponent' || widgetType === 'AreaChartWidgetComponent' || widgetType === 'DoughnutChartWidgetComponent' || widgetType === 'FunnelChartWidgetComponent'"
>
<mat-form-field
appearance=
"fill"
>
...
...
src/app/portal-manage/dashboard-management/widgets/configs/simple-kpi-config/simple-kpi-config.component.ts
View file @
49e784c7
...
...
@@ -26,11 +26,11 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
],
template
:
`
<div class="config-container">
<mat-tab-group class="config-tabs">
<mat-tab-group class="config-tabs"
dynamicHeight
>
<!-- Basic Configuration Tab -->
<mat-tab label="Basic">
<div class="config-section">
<h3 class="text-blue-600">Basic Configuration</h3>
<h3 class="
section-title
text-blue-600">Basic Configuration</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Title</mat-label>
...
...
@@ -77,6 +77,25 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Icon (Bootstrap Icons)</mat-label>
<input matInput [(ngModel)]="currentConfig.icon" name="icon" placeholder="e.g., person-fill, building">
<mat-hint>Find icons at icons.getbootstrap.com</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Icon Position</mat-label>
<mat-select [(ngModel)]="currentConfig.iconPosition" name="iconPosition">
<mat-option value="left">Left</mat-option>
<mat-option value="right">Right</mat-option>
<mat-option value="top">Top</mat-option>
<mat-option value="bottom">Bottom</mat-option>
</mat-select>
<mat-hint>Position of the icon</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Aggregation</mat-label>
<mat-select [(ngModel)]="currentConfig.aggregation" name="aggregation">
<mat-option value="sum">Sum</mat-option>
...
...
@@ -148,7 +167,7 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<!-- Style Tab -->
<mat-tab label="Style">
<div class="config-section">
<h3 class="
text-blue-600">Style Configuration
</h3>
<h3 class="
section-title text-purple-600">Style & Colors
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
...
...
@@ -166,19 +185,33 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Accent Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.accentColor" name="accentColor" class="color-picker">
<mat-hint>Color for the main KPI value</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Value Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.valueColor" name="valueColor">
<mat-hint>KPI value color</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Label Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.labelColor" name="labelColor">
<mat-hint>KPI label color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Icon Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.iconColor" name="iconColor" class="color-picker">
<mat-hint>Icon color</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-
2
gap-4">
<div class="grid grid-cols-1 md:grid-cols-
3
gap-4">
<mat-form-field appearance="fill">
<mat-label>Font Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.fontSize" name="fontSize" min="10" max="48">
...
...
@@ -190,13 +223,19 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<input matInput type="number" [(ngModel)]="currentConfig.valueFontSize" name="valueFontSize" min="10" max="72">
<mat-hint>KPI value font size</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Padding (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.padding" name="padding" min="0" max="100">
<mat-hint>Internal spacing</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Border Radius (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.borderRadius" name="borderRadius" min="0" max="
2
0">
<mat-hint>
Corner roundnes
s</mat-hint>
<input matInput type="number" [(ngModel)]="currentConfig.borderRadius" name="borderRadius" min="0" max="
5
0">
<mat-hint>
Roundness of widget corner
s</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
...
...
@@ -209,8 +248,8 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Border Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.borderColor" name="borderColor">
<mat-hint>
B
order color</mat-hint>
<input matInput type="color" [(ngModel)]="currentConfig.borderColor" name="borderColor"
class="color-picker"
>
<mat-hint>
Card b
order color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
...
...
@@ -224,6 +263,45 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Drop shadow effect</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<mat-form-field appearance="fill">
<mat-label>Font Weight</mat-label>
<mat-select [(ngModel)]="currentConfig.fontWeight" name="fontWeight">
<mat-option value="normal">Normal</mat-option>
<mat-option value="bold">Bold</mat-option>
<mat-option value="lighter">Lighter</mat-option>
<mat-option value="bolder">Bolder</mat-option>
</mat-select>
<mat-hint>Font weight</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Font Family</mat-label>
<mat-select [(ngModel)]="currentConfig.fontFamily" name="fontFamily">
<mat-option value="system-ui, -apple-system, sans-serif">System Font</mat-option>
<mat-option value="Arial, sans-serif">Arial</mat-option>
<mat-option value="Helvetica, sans-serif">Helvetica</mat-option>
<mat-option value="Georgia, serif">Georgia</mat-option>
<mat-option value="Times New Roman, serif">Times New Roman</mat-option>
<mat-option value="Courier New, monospace">Courier New</mat-option>
</mat-select>
<mat-hint>Font family for the widget</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Icon Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.iconSize" name="iconSize" min="16" max="64">
<mat-hint>Icon size in pixels</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Custom CSS</mat-label>
<textarea matInput [(ngModel)]="currentConfig.customCSS" name="customCSS" rows="4"
placeholder="/* Add custom CSS rules here */ .simple-kpi-widget:hover { transform: translateY(-4px); }"></textarea>
<mat-hint>Add custom CSS for advanced styling</mat-hint>
</mat-form-field>
</div>
</mat-tab>
...
...
@@ -450,10 +528,211 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</div>
</mat-tab>
<!-- Layout Tab -->
<mat-tab label="Layout">
<div class="config-section">
<h3 class="section-title text-teal-600">Layout Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Width (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.width" name="width" min="100" max="800">
<mat-hint>Widget width</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.height" name="height" min="100" max="600">
<mat-hint>Widget height</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Minimum Width (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.minWidth" name="minWidth" min="50" max="400">
<mat-hint>Minimum widget width</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Minimum Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.minHeight" name="minHeight" min="50" max="300">
<mat-hint>Minimum widget height</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Aspect Ratio</mat-label>
<mat-select [(ngModel)]="currentConfig.aspectRatio" name="aspectRatio">
<mat-option value="auto">Auto</mat-option>
<mat-option value="16:9">16:9</mat-option>
<mat-option value="4:3">4:3</mat-option>
<mat-option value="1:1">1:1 (Square)</mat-option>
<mat-option value="3:2">3:2</mat-option>
</mat-select>
<mat-hint>Widget aspect ratio</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.responsive" name="responsive" class="mr-2">
Responsive Layout
</mat-checkbox>
</div>
</div>
</div>
</mat-tab>
<!-- Data Tab -->
<mat-tab label="Data">
<div class="config-section">
<h3 class="section-title text-red-600">Data Settings</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Data Source</mat-label>
<mat-select [(ngModel)]="currentConfig.dataSource" name="dataSource">
<mat-option value="static">Static Data</mat-option>
<mat-option value="api">API Endpoint</mat-option>
<mat-option value="websocket">WebSocket</mat-option>
<mat-option value="file">File Upload</mat-option>
</mat-select>
<mat-hint>Data source type</mat-hint>
</mat-form-field>
<mat-form-field *ngIf="currentConfig.dataSource === 'api'" appearance="fill" class="w-full">
<mat-label>API Endpoint</mat-label>
<input matInput [(ngModel)]="currentConfig.apiEndpoint" name="apiEndpoint" placeholder="/api/kpi-data">
<mat-hint>API endpoint URL</mat-hint>
</mat-form-field>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Refresh Interval (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.refreshInterval" name="refreshInterval" min="0" step="1000">
<mat-hint>0 = No auto-refresh</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.cacheEnabled" name="cacheEnabled" class="mr-2">
Enable Data Caching
</mat-checkbox>
</div>
</div>
<mat-form-field *ngIf="currentConfig.cacheEnabled" appearance="fill" class="w-full">
<mat-label>Cache Duration (seconds)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.cacheDuration" name="cacheDuration" min="1" max="3600">
<mat-hint>How long to cache data</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Data Transform Function</mat-label>
<textarea matInput [(ngModel)]="currentConfig.dataTransform" name="dataTransform" rows="3"
placeholder="data => data.map(item => ({ ...item, formattedValue: formatCurrency(item.value) }))"></textarea>
<mat-hint>JavaScript function to transform data</mat-hint>
</mat-form-field>
</div>
</mat-tab>
<!-- Interaction Tab -->
<mat-tab label="Interaction">
<div class="config-section">
<h3 class="section-title text-indigo-600">Interaction Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableTooltip" name="enableTooltip" class="mr-2">
Enable Tooltips
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableClick" name="enableClick" class="mr-2">
Enable Click Events
</mat-checkbox>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableHover" name="enableHover" class="mr-2">
Enable Hover Effects
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableExport" name="enableExport" class="mr-2">
Enable Export
</mat-checkbox>
</div>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Click Action</mat-label>
<mat-select [(ngModel)]="currentConfig.clickAction" name="clickAction">
<mat-option value="none">None</mat-option>
<mat-option value="drill_down">Drill Down</mat-option>
<mat-option value="open_modal">Open Modal</mat-option>
<mat-option value="navigate">Navigate</mat-option>
<mat-option value="custom">Custom</mat-option>
</mat-select>
<mat-hint>Action when widget is clicked</mat-hint>
</mat-form-field>
<mat-form-field *ngIf="currentConfig.clickAction === 'custom'" appearance="fill" class="w-full">
<mat-label>Custom Click Handler</mat-label>
<textarea matInput [(ngModel)]="currentConfig.customClickHandler" name="customClickHandler" rows="3"
placeholder="function(event) { console.log('Widget clicked:', event); }"></textarea>
<mat-hint>Custom JavaScript function for click events</mat-hint>
</mat-form-field>
</div>
</mat-tab>
<!-- Security Tab -->
<mat-tab label="Security">
<div class="config-section">
<h3 class="section-title text-gray-600">Security Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.requireAuth" name="requireAuth" class="mr-2">
Require Authentication
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.dataEncryption" name="dataEncryption" class="mr-2">
Enable Data Encryption
</mat-checkbox>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.auditLog" name="auditLog" class="mr-2">
Enable Audit Logging
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Rate Limit (requests/min)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.rateLimit" name="rateLimit" min="0">
<mat-hint>0 = No limit</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Allowed Roles (comma-separated)</mat-label>
<input matInput [(ngModel)]="currentConfig.allowedRoles" name="allowedRoles" placeholder="admin, analyst, manager">
<mat-hint>Roles that can access this widget</mat-hint>
</mat-form-field>
</div>
</mat-tab>
<!-- Condition Tab -->
<mat-tab label="Condition">
<div class="config-section">
<h3 class="text-blue-600">Conditional Formatting</h3>
<h3 class="
section-title
text-blue-600">Conditional Formatting</h3>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableConditionalFormatting" name="enableConditionalFormatting">
...
...
@@ -512,17 +791,114 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
`
,
styles
:
[
`
.config-container {
padding: 16px;
padding: 20px;
background: #f8fafc;
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.config-section {
margin-bottom: 24px;
margin-bottom: 32px;
padding: 24px;
background: white;
border-radius: 8px;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.section-title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 20px;
padding-bottom: 8px;
border-bottom: 2px solid #e2e8f0;
}
.config-tabs {
margin-top:
16px
;
margin-top:
0
;
}
.config-tabs .mat-tab-body-content {
padding: 16px 0;
padding: 20px 0;
}
.config-tabs .mat-tab-header {
background: white;
border-radius: 8px 8px 0 0;
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.1);
}
.config-tabs .mat-tab-label {
min-width: 120px;
font-weight: 500;
}
.config-tabs .mat-tab-label-active {
color: #3b82f6;
}
.mat-form-field {
margin-bottom: 8px;
}
.mat-form-field.color-picker {
height: 40px;
}
.grid {
display: grid;
gap: 16px;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (min-width: 768px) {
.md\\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.md\\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.w-full {
width: 100%;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.mr-2 {
margin-right: 8px;
}
.mb-4 {
margin-bottom: 16px;
}
.text-blue-600 { color: #2563eb; }
.text-purple-600 { color: #9333ea; }
.text-green-600 { color: #16a34a; }
.text-orange-600 { color: #ea580c; }
.text-indigo-600 { color: #4f46e5; }
.text-teal-600 { color: #0d9488; }
.text-red-600 { color: #dc2626; }
.text-gray-600 { color: #4b5563; }
`
]
})
export
class
SimpleKpiConfigComponent
extends
BaseConfigComponent
implements
OnInit
{
...
...
@@ -558,6 +934,7 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
if
(
!
this
.
currentConfig
.
labelColor
)
this
.
currentConfig
.
labelColor
=
'#FFFFFF'
;
if
(
!
this
.
currentConfig
.
iconColor
)
this
.
currentConfig
.
iconColor
=
'#FFFFFF'
;
if
(
!
this
.
currentConfig
.
borderColor
)
this
.
currentConfig
.
borderColor
=
'#FFFFFF'
;
if
(
!
this
.
currentConfig
.
accentColor
)
this
.
currentConfig
.
accentColor
=
'#3B82F6'
;
if
(
!
this
.
currentConfig
.
trueColor
)
this
.
currentConfig
.
trueColor
=
'#10B981'
;
if
(
!
this
.
currentConfig
.
falseColor
)
this
.
currentConfig
.
falseColor
=
'#EF4444'
;
...
...
@@ -583,6 +960,31 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
if
(
!
this
.
currentConfig
.
conditionField
)
this
.
currentConfig
.
conditionField
=
''
;
if
(
!
this
.
currentConfig
.
conditionOperator
)
this
.
currentConfig
.
conditionOperator
=
'greater_than'
;
if
(
!
this
.
currentConfig
.
conditionValue
)
this
.
currentConfig
.
conditionValue
=
''
;
// Initialize additional fields
if
(
!
this
.
currentConfig
.
padding
)
this
.
currentConfig
.
padding
=
16
;
if
(
!
this
.
currentConfig
.
fontWeight
)
this
.
currentConfig
.
fontWeight
=
'normal'
;
if
(
!
this
.
currentConfig
.
fontFamily
)
this
.
currentConfig
.
fontFamily
=
'system-ui, -apple-system, sans-serif'
;
if
(
!
this
.
currentConfig
.
customCSS
)
this
.
currentConfig
.
customCSS
=
''
;
if
(
!
this
.
currentConfig
.
minWidth
)
this
.
currentConfig
.
minWidth
=
200
;
if
(
!
this
.
currentConfig
.
minHeight
)
this
.
currentConfig
.
minHeight
=
150
;
if
(
!
this
.
currentConfig
.
aspectRatio
)
this
.
currentConfig
.
aspectRatio
=
'auto'
;
if
(
!
this
.
currentConfig
.
responsive
)
this
.
currentConfig
.
responsive
=
true
;
if
(
!
this
.
currentConfig
.
refreshInterval
)
this
.
currentConfig
.
refreshInterval
=
0
;
if
(
!
this
.
currentConfig
.
cacheEnabled
)
this
.
currentConfig
.
cacheEnabled
=
false
;
if
(
!
this
.
currentConfig
.
cacheDuration
)
this
.
currentConfig
.
cacheDuration
=
300
;
if
(
!
this
.
currentConfig
.
dataTransform
)
this
.
currentConfig
.
dataTransform
=
''
;
if
(
!
this
.
currentConfig
.
enableTooltip
)
this
.
currentConfig
.
enableTooltip
=
false
;
if
(
!
this
.
currentConfig
.
enableClick
)
this
.
currentConfig
.
enableClick
=
false
;
if
(
!
this
.
currentConfig
.
enableHover
)
this
.
currentConfig
.
enableHover
=
true
;
if
(
!
this
.
currentConfig
.
enableExport
)
this
.
currentConfig
.
enableExport
=
false
;
if
(
!
this
.
currentConfig
.
clickAction
)
this
.
currentConfig
.
clickAction
=
'none'
;
if
(
!
this
.
currentConfig
.
customClickHandler
)
this
.
currentConfig
.
customClickHandler
=
''
;
if
(
!
this
.
currentConfig
.
requireAuth
)
this
.
currentConfig
.
requireAuth
=
false
;
if
(
!
this
.
currentConfig
.
dataEncryption
)
this
.
currentConfig
.
dataEncryption
=
false
;
if
(
!
this
.
currentConfig
.
auditLog
)
this
.
currentConfig
.
auditLog
=
false
;
if
(
!
this
.
currentConfig
.
rateLimit
)
this
.
currentConfig
.
rateLimit
=
0
;
if
(
!
this
.
currentConfig
.
allowedRoles
)
this
.
currentConfig
.
allowedRoles
=
''
;
}
override
setSizeOption
(
optionId
:
string
)
{
...
...
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