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
cf761296
Commit
cf761296
authored
Sep 16, 2025
by
Ooh-Ao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
widgets
parent
b0dd4799
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
2301 additions
and
238 deletions
+2301
-238
widget-config-registry.service.ts
.../widget-config/services/widget-config-registry.service.ts
+1279
-180
simple-kpi-config.component.ts
.../configs/simple-kpi-config/simple-kpi-config.component.ts
+775
-38
simple-kpi-widget.component.ts
.../widgets/simple-kpi-widget/simple-kpi-widget.component.ts
+247
-20
No files found.
src/app/portal-manage/dashboard-management/widget-config/services/widget-config-registry.service.ts
View file @
cf761296
...
...
@@ -11,6 +11,87 @@ import { PayrollConfigComponent } from '../../widgets/configs/payroll-config/pay
import
{
BaseConfigComponent
}
from
'../base-config/base-config.component'
;
/**
* Widget configuration metadata interface
*/
export
interface
WidgetMetadata
{
name
:
string
;
displayName
:
string
;
description
:
string
;
category
:
WidgetCategory
;
icon
:
string
;
version
:
string
;
author
:
string
;
tags
:
string
[];
isDeprecated
:
boolean
;
replacement
?:
string
;
minWidth
?:
number
;
minHeight
?:
number
;
maxWidth
?:
number
;
maxHeight
?:
number
;
defaultWidth
?:
number
;
defaultHeight
?:
number
;
supportedDataSources
:
DataSourceType
[];
features
:
WidgetFeature
[];
complexity
:
'simple'
|
'intermediate'
|
'advanced'
;
}
/**
* Widget categories for better organization
*/
export
enum
WidgetCategory
{
CHARTS
=
'charts'
,
TABLES
=
'tables'
,
CARDS
=
'cards'
,
KPI
=
'kpi'
,
LAYOUT
=
'layout'
,
UTILITY
=
'utility'
,
BUSINESS
=
'business'
,
DATA_GRID
=
'data-grid'
,
PIVOT
=
'pivot'
,
FORMS
=
'forms'
,
MEDIA
=
'media'
,
NAVIGATION
=
'navigation'
}
/**
* Data source types supported by widgets
*/
export
enum
DataSourceType
{
STATIC
=
'static'
,
API
=
'api'
,
DATASET
=
'dataset'
,
WEBSOCKET
=
'websocket'
,
FILE
=
'file'
,
ODATA
=
'odata'
,
WEBAPI
=
'webapi'
}
/**
* Widget features for capability discovery
*/
export
enum
WidgetFeature
{
EXPORT
=
'export'
,
FILTER
=
'filter'
,
SORT
=
'sort'
,
PAGINATION
=
'pagination'
,
SEARCH
=
'search'
,
GROUPING
=
'grouping'
,
EDITING
=
'editing'
,
SELECTION
=
'selection'
,
REFRESH
=
'refresh'
,
ZOOM
=
'zoom'
,
DRILL_DOWN
=
'drill_down'
,
CUSTOMIZATION
=
'customization'
,
RESPONSIVE
=
'responsive'
,
ANIMATION
=
'animation'
,
TOOLTIP
=
'tooltip'
,
PRINT
=
'print'
}
/**
* Widget configuration component interface
*/
export
interface
WidgetConfigComponent
extends
BaseConfigComponent
{
// All config components extend BaseConfigComponent which already has:
// currentConfig: any;
...
...
@@ -23,288 +104,1306 @@ export interface WidgetConfigComponent extends BaseConfigComponent {
})
export
class
WidgetConfigRegistryService
{
private
configComponents
:
Map
<
string
,
Type
<
WidgetConfigComponent
>>
=
new
Map
();
private
widgetMetadata
:
Map
<
string
,
WidgetMetadata
>
=
new
Map
();
private
categoryIndex
:
Map
<
WidgetCategory
,
string
[]
>
=
new
Map
();
constructor
()
{
this
.
initializeCategories
();
this
.
registerDefaultConfigs
();
}
/**
* Initialize widget categories
*/
private
initializeCategories
():
void
{
Object
.
values
(
WidgetCategory
).
forEach
(
category
=>
{
this
.
categoryIndex
.
set
(
category
,
[]);
});
}
/**
* Register all default widget configurations with comprehensive metadata
*/
private
registerDefaultConfigs
():
void
{
// Register existing config components
this
.
registerConfig
(
'SimpleKpiWidgetComponent'
,
SimpleKpiConfigComponent
);
this
.
registerConfig
(
'SyncfusionDatagridWidgetComponent'
,
SyncfusionDatagridConfigComponent
);
// Register new config components
this
.
registerConfig
(
'SyncfusionPivotWidgetComponent'
,
SyncfusionPivotConfigComponent
);
this
.
registerConfig
(
'ChartWidgetComponent'
,
ChartConfigComponent
);
// this.registerConfig('CompanyInfoWidgetComponent', CompanyInfoConfigComponent);
// this.registerConfig('CompanyInfoSubfolderWidgetComponent', CompanyInfoConfigComponent);
this
.
registerConfig
(
'AttendanceOverviewWidgetComponent'
,
AttendanceConfigComponent
);
this
.
registerConfig
(
'PayrollSummaryWidgetComponent'
,
PayrollConfigComponent
);
this
.
registerConfig
(
'PayrollWidgetComponent'
,
PayrollConfigComponent
);
// Register chart widgets with ChartConfigComponent
this
.
registerConfig
(
'SyncfusionChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'PieChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'BarChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'AreaChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'DoughnutChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'FunnelChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'ScatterBubbleChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'GaugeChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'TreemapWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'WaterfallChartWidgetComponent'
,
ChartConfigComponent
);
this
.
registerConfig
(
'ComboChartWidgetComponent'
,
ChartConfigComponent
);
// Register table widgets with TableConfigComponent
this
.
registerConfig
(
'DataTableWidgetComponent'
,
TableConfigComponent
);
this
.
registerConfig
(
'SimpleTableWidgetComponent'
,
TableConfigComponent
);
// Register card widgets with CardConfigComponent
this
.
registerConfig
(
'MultiRowCardWidgetComponent'
,
CardConfigComponent
);
this
.
registerConfig
(
'NotificationWidgetComponent'
,
CardConfigComponent
);
this
.
registerConfig
(
'WelcomeWidgetComponent'
,
CardConfigComponent
);
this
.
registerConfig
(
'QuickLinksWidgetComponent'
,
CardConfigComponent
);
this
.
registerConfig
(
'CompanyInfoWidgetComponent'
,
CardConfigComponent
);
this
.
registerConfig
(
'CompanyInfoSubfolderWidgetComponent'
,
CardConfigComponent
);
// Register KPI widgets with SimpleKpiConfigComponent
this
.
registerConfig
(
'KpiWidgetComponent'
,
SimpleKpiConfigComponent
);
// Register utility widgets with appropriate config components
this
.
registerConfig
(
'ClockWidgetComponent'
,
SimpleKpiConfigComponent
);
this
.
registerConfig
(
'WeatherWidgetComponent'
,
SimpleKpiConfigComponent
);
this
.
registerConfig
(
'CalendarWidgetComponent'
,
SimpleKpiConfigComponent
);
this
.
registerConfig
(
'MatrixWidgetComponent'
,
TableConfigComponent
);
this
.
registerConfig
(
'SlicerWidgetComponent'
,
SimpleKpiConfigComponent
);
this
.
registerConfig
(
'FilledMapWidgetComponent'
,
SimpleKpiConfigComponent
);
this
.
registerConfig
(
'EmployeeDirectoryWidgetComponent'
,
CardConfigComponent
);
this
.
registerConfig
(
'HeadcountWidgetComponent'
,
SimpleKpiConfigComponent
);
// ========================================
// KPI WIDGETS
// ========================================
this
.
registerWidgetWithMetadata
(
'SimpleKpiWidgetComponent'
,
SimpleKpiConfigComponent
,
{
name
:
'SimpleKpiWidgetComponent'
,
displayName
:
'Simple KPI Widget'
,
description
:
'A simple key performance indicator widget for displaying single metrics'
,
category
:
WidgetCategory
.
KPI
,
icon
:
'trending-up'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'kpi'
,
'metric'
,
'simple'
,
'dashboard'
],
isDeprecated
:
false
,
minWidth
:
200
,
minHeight
:
150
,
maxWidth
:
600
,
maxHeight
:
400
,
defaultWidth
:
300
,
defaultHeight
:
200
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
TOOLTIP
],
complexity
:
'simple'
});
this
.
registerWidgetWithMetadata
(
'KpiWidgetComponent'
,
SimpleKpiConfigComponent
,
{
name
:
'KpiWidgetComponent'
,
displayName
:
'KPI Widget'
,
description
:
'Advanced KPI widget with multiple metrics and visualizations'
,
category
:
WidgetCategory
.
KPI
,
icon
:
'bar-chart-2'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'kpi'
,
'metrics'
,
'dashboard'
,
'analytics'
],
isDeprecated
:
false
,
minWidth
:
250
,
minHeight
:
180
,
maxWidth
:
800
,
maxHeight
:
500
,
defaultWidth
:
350
,
defaultHeight
:
250
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
TOOLTIP
,
WidgetFeature
.
EXPORT
],
complexity
:
'intermediate'
});
// ========================================
// DATA GRID WIDGETS
// ========================================
this
.
registerWidgetWithMetadata
(
'SyncfusionDatagridWidgetComponent'
,
SyncfusionDatagridConfigComponent
,
{
name
:
'SyncfusionDatagridWidgetComponent'
,
displayName
:
'Data Grid Widget'
,
description
:
'Advanced data grid with sorting, filtering, paging, and export capabilities'
,
category
:
WidgetCategory
.
DATA_GRID
,
icon
:
'grid'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'datagrid'
,
'table'
,
'data'
,
'syncfusion'
,
'advanced'
],
isDeprecated
:
false
,
minWidth
:
400
,
minHeight
:
300
,
maxWidth
:
1200
,
maxHeight
:
800
,
defaultWidth
:
600
,
defaultHeight
:
400
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
,
DataSourceType
.
ODATA
,
DataSourceType
.
WEBAPI
],
features
:
[
WidgetFeature
.
EXPORT
,
WidgetFeature
.
FILTER
,
WidgetFeature
.
SORT
,
WidgetFeature
.
PAGINATION
,
WidgetFeature
.
SEARCH
,
WidgetFeature
.
GROUPING
,
WidgetFeature
.
EDITING
,
WidgetFeature
.
SELECTION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
RESPONSIVE
,
WidgetFeature
.
PRINT
],
complexity
:
'advanced'
});
// Continue with other widget registrations...
this
.
registerRemainingWidgets
();
}
/**
* Register remaining widgets with their configurations
*/
private
registerRemainingWidgets
():
void
{
// ========================================
// PIVOT WIDGETS
// ========================================
this
.
registerWidgetWithMetadata
(
'SyncfusionPivotWidgetComponent'
,
SyncfusionPivotConfigComponent
,
{
name
:
'SyncfusionPivotWidgetComponent'
,
displayName
:
'Pivot Table Widget'
,
description
:
'Interactive pivot table for data analysis and cross-tabulation'
,
category
:
WidgetCategory
.
PIVOT
,
icon
:
'layers'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'pivot'
,
'analysis'
,
'cross-tab'
,
'syncfusion'
],
isDeprecated
:
false
,
minWidth
:
500
,
minHeight
:
400
,
maxWidth
:
1200
,
maxHeight
:
800
,
defaultWidth
:
700
,
defaultHeight
:
500
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
,
DataSourceType
.
ODATA
],
features
:
[
WidgetFeature
.
EXPORT
,
WidgetFeature
.
FILTER
,
WidgetFeature
.
SORT
,
WidgetFeature
.
GROUPING
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
RESPONSIVE
,
WidgetFeature
.
DRILL_DOWN
],
complexity
:
'advanced'
});
// ========================================
// CHART WIDGETS
// ========================================
const
chartWidgets
=
[
{
name
:
'SyncfusionChartWidgetComponent'
,
displayName
:
'Chart Widget'
,
description
:
'General purpose chart widget'
},
{
name
:
'PieChartWidgetComponent'
,
displayName
:
'Pie Chart'
,
description
:
'Circular chart for showing proportions'
},
{
name
:
'BarChartWidgetComponent'
,
displayName
:
'Bar Chart'
,
description
:
'Vertical bar chart for comparisons'
},
{
name
:
'AreaChartWidgetComponent'
,
displayName
:
'Area Chart'
,
description
:
'Filled area chart for trends'
},
{
name
:
'DoughnutChartWidgetComponent'
,
displayName
:
'Doughnut Chart'
,
description
:
'Ring chart with center space'
},
{
name
:
'FunnelChartWidgetComponent'
,
displayName
:
'Funnel Chart'
,
description
:
'Funnel visualization for processes'
},
{
name
:
'ScatterBubbleChartWidgetComponent'
,
displayName
:
'Scatter/Bubble Chart'
,
description
:
'Scatter plot with optional bubble sizing'
},
{
name
:
'GaugeChartWidgetComponent'
,
displayName
:
'Gauge Chart'
,
description
:
'Radial gauge for single values'
},
{
name
:
'TreemapWidgetComponent'
,
displayName
:
'Treemap'
,
description
:
'Hierarchical data visualization'
},
{
name
:
'WaterfallChartWidgetComponent'
,
displayName
:
'Waterfall Chart'
,
description
:
'Cumulative effect visualization'
},
{
name
:
'ComboChartWidgetComponent'
,
displayName
:
'Combo Chart'
,
description
:
'Combined chart types'
}
];
chartWidgets
.
forEach
(
widget
=>
{
this
.
registerWidgetWithMetadata
(
widget
.
name
,
ChartConfigComponent
,
{
name
:
widget
.
name
,
displayName
:
widget
.
displayName
,
description
:
widget
.
description
,
category
:
WidgetCategory
.
CHARTS
,
icon
:
'bar-chart'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'chart'
,
'visualization'
,
'data'
,
'graph'
],
isDeprecated
:
false
,
minWidth
:
300
,
minHeight
:
200
,
maxWidth
:
1000
,
maxHeight
:
600
,
defaultWidth
:
400
,
defaultHeight
:
300
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
EXPORT
,
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
ZOOM
,
WidgetFeature
.
TOOLTIP
,
WidgetFeature
.
RESPONSIVE
,
WidgetFeature
.
ANIMATION
],
complexity
:
'intermediate'
});
});
// ========================================
// TABLE WIDGETS
// ========================================
this
.
registerWidgetWithMetadata
(
'DataTableWidgetComponent'
,
TableConfigComponent
,
{
name
:
'DataTableWidgetComponent'
,
displayName
:
'Data Table'
,
description
:
'Simple data table with basic functionality'
,
category
:
WidgetCategory
.
TABLES
,
icon
:
'table'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'table'
,
'data'
,
'simple'
],
isDeprecated
:
false
,
minWidth
:
300
,
minHeight
:
200
,
maxWidth
:
800
,
maxHeight
:
600
,
defaultWidth
:
500
,
defaultHeight
:
350
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
SORT
,
WidgetFeature
.
FILTER
,
WidgetFeature
.
EXPORT
,
WidgetFeature
.
REFRESH
],
complexity
:
'simple'
});
this
.
registerWidgetWithMetadata
(
'SimpleTableWidgetComponent'
,
TableConfigComponent
,
{
name
:
'SimpleTableWidgetComponent'
,
displayName
:
'Simple Table'
,
description
:
'Basic table widget for simple data display'
,
category
:
WidgetCategory
.
TABLES
,
icon
:
'list'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'table'
,
'simple'
,
'basic'
],
isDeprecated
:
false
,
minWidth
:
250
,
minHeight
:
150
,
maxWidth
:
600
,
maxHeight
:
400
,
defaultWidth
:
350
,
defaultHeight
:
250
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
],
complexity
:
'simple'
});
this
.
registerWidgetWithMetadata
(
'MatrixWidgetComponent'
,
TableConfigComponent
,
{
name
:
'MatrixWidgetComponent'
,
displayName
:
'Matrix Table'
,
description
:
'Matrix-style table for cross-tabulated data'
,
category
:
WidgetCategory
.
TABLES
,
icon
:
'grid-3x3'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'matrix'
,
'table'
,
'cross-tab'
],
isDeprecated
:
false
,
minWidth
:
400
,
minHeight
:
300
,
maxWidth
:
1000
,
maxHeight
:
700
,
defaultWidth
:
600
,
defaultHeight
:
450
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
SORT
,
WidgetFeature
.
FILTER
,
WidgetFeature
.
EXPORT
,
WidgetFeature
.
CUSTOMIZATION
],
complexity
:
'intermediate'
});
// ========================================
// CARD WIDGETS
// ========================================
const
cardWidgets
=
[
{
name
:
'MultiRowCardWidgetComponent'
,
displayName
:
'Multi Row Card'
,
description
:
'Card widget with multiple data rows'
},
{
name
:
'NotificationWidgetComponent'
,
displayName
:
'Notification Widget'
,
description
:
'Widget for displaying notifications and alerts'
},
{
name
:
'WelcomeWidgetComponent'
,
displayName
:
'Welcome Widget'
,
description
:
'Welcome message and greeting widget'
},
{
name
:
'QuickLinksWidgetComponent'
,
displayName
:
'Quick Links'
,
description
:
'Widget with quick navigation links'
},
{
name
:
'CompanyInfoWidgetComponent'
,
displayName
:
'Company Information'
,
description
:
'Company details and information display'
},
{
name
:
'CompanyInfoSubfolderWidgetComponent'
,
displayName
:
'Company Info Subfolder'
,
description
:
'Company information with subfolder structure'
},
{
name
:
'EmployeeDirectoryWidgetComponent'
,
displayName
:
'Employee Directory'
,
description
:
'Employee listing and directory widget'
}
];
cardWidgets
.
forEach
(
widget
=>
{
this
.
registerWidgetWithMetadata
(
widget
.
name
,
CardConfigComponent
,
{
name
:
widget
.
name
,
displayName
:
widget
.
displayName
,
description
:
widget
.
description
,
category
:
WidgetCategory
.
CARDS
,
icon
:
'credit-card'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'card'
,
'display'
,
'information'
],
isDeprecated
:
false
,
minWidth
:
200
,
minHeight
:
150
,
maxWidth
:
500
,
maxHeight
:
400
,
defaultWidth
:
300
,
defaultHeight
:
200
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
RESPONSIVE
],
complexity
:
'simple'
});
});
// ========================================
// BUSINESS WIDGETS
// ========================================
this
.
registerWidgetWithMetadata
(
'AttendanceOverviewWidgetComponent'
,
AttendanceConfigComponent
,
{
name
:
'AttendanceOverviewWidgetComponent'
,
displayName
:
'Attendance Overview'
,
description
:
'Widget for displaying employee attendance statistics'
,
category
:
WidgetCategory
.
BUSINESS
,
icon
:
'users'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'attendance'
,
'hr'
,
'business'
,
'employees'
],
isDeprecated
:
false
,
minWidth
:
300
,
minHeight
:
200
,
maxWidth
:
600
,
maxHeight
:
400
,
defaultWidth
:
400
,
defaultHeight
:
300
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
EXPORT
],
complexity
:
'intermediate'
});
this
.
registerWidgetWithMetadata
(
'PayrollSummaryWidgetComponent'
,
PayrollConfigComponent
,
{
name
:
'PayrollSummaryWidgetComponent'
,
displayName
:
'Payroll Summary'
,
description
:
'Summary widget for payroll information and statistics'
,
category
:
WidgetCategory
.
BUSINESS
,
icon
:
'dollar-sign'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'payroll'
,
'hr'
,
'business'
,
'finance'
],
isDeprecated
:
false
,
minWidth
:
300
,
minHeight
:
200
,
maxWidth
:
600
,
maxHeight
:
400
,
defaultWidth
:
400
,
defaultHeight
:
300
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
EXPORT
],
complexity
:
'intermediate'
});
this
.
registerWidgetWithMetadata
(
'PayrollWidgetComponent'
,
PayrollConfigComponent
,
{
name
:
'PayrollWidgetComponent'
,
displayName
:
'Payroll Widget'
,
description
:
'Detailed payroll widget with comprehensive information'
,
category
:
WidgetCategory
.
BUSINESS
,
icon
:
'file-text'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'payroll'
,
'hr'
,
'business'
,
'detailed'
],
isDeprecated
:
false
,
minWidth
:
400
,
minHeight
:
300
,
maxWidth
:
800
,
maxHeight
:
600
,
defaultWidth
:
500
,
defaultHeight
:
400
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
EXPORT
,
WidgetFeature
.
FILTER
],
complexity
:
'advanced'
});
this
.
registerWidgetWithMetadata
(
'HeadcountWidgetComponent'
,
SimpleKpiConfigComponent
,
{
name
:
'HeadcountWidgetComponent'
,
displayName
:
'Headcount Widget'
,
description
:
'Widget for displaying employee headcount statistics'
,
category
:
WidgetCategory
.
BUSINESS
,
icon
:
'user-check'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'headcount'
,
'hr'
,
'business'
,
'employees'
],
isDeprecated
:
false
,
minWidth
:
250
,
minHeight
:
150
,
maxWidth
:
500
,
maxHeight
:
350
,
defaultWidth
:
350
,
defaultHeight
:
200
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
,
DataSourceType
.
DATASET
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
TOOLTIP
],
complexity
:
'simple'
});
// ========================================
// UTILITY WIDGETS
// ========================================
const
utilityWidgets
=
[
{
name
:
'ClockWidgetComponent'
,
displayName
:
'Clock Widget'
,
description
:
'Real-time clock and date display'
},
{
name
:
'WeatherWidgetComponent'
,
displayName
:
'Weather Widget'
,
description
:
'Current weather information display'
},
{
name
:
'CalendarWidgetComponent'
,
displayName
:
'Calendar Widget'
,
description
:
'Calendar and event display widget'
},
{
name
:
'SlicerWidgetComponent'
,
displayName
:
'Slicer Widget'
,
description
:
'Data filtering and slicing widget'
},
{
name
:
'FilledMapWidgetComponent'
,
displayName
:
'Map Widget'
,
description
:
'Geographic data visualization widget'
}
];
utilityWidgets
.
forEach
(
widget
=>
{
this
.
registerWidgetWithMetadata
(
widget
.
name
,
SimpleKpiConfigComponent
,
{
name
:
widget
.
name
,
displayName
:
widget
.
displayName
,
description
:
widget
.
description
,
category
:
WidgetCategory
.
UTILITY
,
icon
:
'tool'
,
version
:
'1.0.0'
,
author
:
'Portal Team'
,
tags
:
[
'utility'
,
'tool'
,
'helper'
],
isDeprecated
:
false
,
minWidth
:
200
,
minHeight
:
150
,
maxWidth
:
400
,
maxHeight
:
300
,
defaultWidth
:
300
,
defaultHeight
:
200
,
supportedDataSources
:
[
DataSourceType
.
STATIC
,
DataSourceType
.
API
],
features
:
[
WidgetFeature
.
CUSTOMIZATION
,
WidgetFeature
.
REFRESH
,
WidgetFeature
.
RESPONSIVE
],
complexity
:
'simple'
});
});
}
/**
* Register a widget with comprehensive metadata
*/
registerWidgetWithMetadata
(
widgetType
:
string
,
configComponent
:
Type
<
WidgetConfigComponent
>
,
metadata
:
WidgetMetadata
):
void
{
this
.
configComponents
.
set
(
widgetType
,
configComponent
);
this
.
widgetMetadata
.
set
(
widgetType
,
metadata
);
// Add to category index
const
categoryWidgets
=
this
.
categoryIndex
.
get
(
metadata
.
category
)
||
[];
if
(
!
categoryWidgets
.
includes
(
widgetType
))
{
categoryWidgets
.
push
(
widgetType
);
this
.
categoryIndex
.
set
(
metadata
.
category
,
categoryWidgets
);
}
}
/**
* Legacy method for backward compatibility
*/
registerConfig
(
widgetType
:
string
,
configComponent
:
Type
<
WidgetConfigComponent
>
):
void
{
this
.
configComponents
.
set
(
widgetType
,
configComponent
);
}
/**
* Get configuration component for a widget type
*/
getConfigComponent
(
widgetType
:
string
):
Type
<
WidgetConfigComponent
>
|
null
{
return
this
.
configComponents
.
get
(
widgetType
)
||
null
;
}
/**
* Check if a widget type has a configuration component
*/
hasConfigComponent
(
widgetType
:
string
):
boolean
{
return
this
.
configComponents
.
has
(
widgetType
);
}
/**
* Get all registered widget types
*/
getAllRegisteredWidgets
():
string
[]
{
return
Array
.
from
(
this
.
configComponents
.
keys
());
}
// Helper method to get fallback config for widgets without specific config components
/**
* Get widget metadata
*/
getWidgetMetadata
(
widgetType
:
string
):
WidgetMetadata
|
null
{
return
this
.
widgetMetadata
.
get
(
widgetType
)
||
null
;
}
/**
* Get all widget metadata
*/
getAllWidgetMetadata
():
Map
<
string
,
WidgetMetadata
>
{
return
new
Map
(
this
.
widgetMetadata
);
}
/**
* Get widgets by category
*/
getWidgetsByCategory
(
category
:
WidgetCategory
):
string
[]
{
return
this
.
categoryIndex
.
get
(
category
)
||
[];
}
/**
* Get all categories with their widget counts
*/
getCategoriesWithCounts
():
Map
<
WidgetCategory
,
number
>
{
const
categories
=
new
Map
<
WidgetCategory
,
number
>
();
this
.
categoryIndex
.
forEach
((
widgets
,
category
)
=>
{
categories
.
set
(
category
,
widgets
.
length
);
});
return
categories
;
}
/**
* Search widgets by name, description, or tags
*/
searchWidgets
(
query
:
string
):
string
[]
{
const
searchTerm
=
query
.
toLowerCase
();
const
results
:
string
[]
=
[];
this
.
widgetMetadata
.
forEach
((
metadata
,
widgetType
)
=>
{
const
searchableText
=
[
metadata
.
name
,
metadata
.
displayName
,
metadata
.
description
,
...
metadata
.
tags
].
join
(
' '
).
toLowerCase
();
if
(
searchableText
.
includes
(
searchTerm
))
{
results
.
push
(
widgetType
);
}
});
return
results
;
}
/**
* Get widgets by complexity level
*/
getWidgetsByComplexity
(
complexity
:
'simple'
|
'intermediate'
|
'advanced'
):
string
[]
{
const
results
:
string
[]
=
[];
this
.
widgetMetadata
.
forEach
((
metadata
,
widgetType
)
=>
{
if
(
metadata
.
complexity
===
complexity
)
{
results
.
push
(
widgetType
);
}
});
return
results
;
}
/**
* Get widgets that support a specific data source
*/
getWidgetsByDataSource
(
dataSource
:
DataSourceType
):
string
[]
{
const
results
:
string
[]
=
[];
this
.
widgetMetadata
.
forEach
((
metadata
,
widgetType
)
=>
{
if
(
metadata
.
supportedDataSources
.
includes
(
dataSource
))
{
results
.
push
(
widgetType
);
}
});
return
results
;
}
/**
* Get widgets that have a specific feature
*/
getWidgetsByFeature
(
feature
:
WidgetFeature
):
string
[]
{
const
results
:
string
[]
=
[];
this
.
widgetMetadata
.
forEach
((
metadata
,
widgetType
)
=>
{
if
(
metadata
.
features
.
includes
(
feature
))
{
results
.
push
(
widgetType
);
}
});
return
results
;
}
/**
* Get deprecated widgets
*/
getDeprecatedWidgets
():
string
[]
{
const
results
:
string
[]
=
[];
this
.
widgetMetadata
.
forEach
((
metadata
,
widgetType
)
=>
{
if
(
metadata
.
isDeprecated
)
{
results
.
push
(
widgetType
);
}
});
return
results
;
}
/**
* Get widgets within size constraints
*/
getWidgetsBySize
(
minWidth
:
number
,
minHeight
:
number
,
maxWidth
?:
number
,
maxHeight
?:
number
):
string
[]
{
const
results
:
string
[]
=
[];
this
.
widgetMetadata
.
forEach
((
metadata
,
widgetType
)
=>
{
const
fitsWidth
=
(
metadata
.
minWidth
||
0
)
<=
minWidth
&&
(
!
maxWidth
||
!
metadata
.
maxWidth
||
metadata
.
maxWidth
>=
maxWidth
);
const
fitsHeight
=
(
metadata
.
minHeight
||
0
)
<=
minHeight
&&
(
!
maxHeight
||
!
metadata
.
maxHeight
||
metadata
.
maxHeight
>=
maxHeight
);
if
(
fitsWidth
&&
fitsHeight
)
{
results
.
push
(
widgetType
);
}
});
return
results
;
}
/**
* Validate widget configuration
*/
validateWidgetConfig
(
widgetType
:
string
,
config
:
any
):
{
isValid
:
boolean
;
errors
:
string
[]
}
{
const
errors
:
string
[]
=
[];
const
metadata
=
this
.
getWidgetMetadata
(
widgetType
);
if
(
!
metadata
)
{
return
{
isValid
:
false
,
errors
:
[
'Widget type not found'
]
};
}
// Check required fields
if
(
!
config
.
title
)
{
errors
.
push
(
'Title is required'
);
}
// Check size constraints
if
(
config
.
width
&&
metadata
.
minWidth
&&
config
.
width
<
metadata
.
minWidth
)
{
errors
.
push
(
`Width must be at least
${
metadata
.
minWidth
}
px`
);
}
if
(
config
.
width
&&
metadata
.
maxWidth
&&
config
.
width
>
metadata
.
maxWidth
)
{
errors
.
push
(
`Width must be at most
${
metadata
.
maxWidth
}
px`
);
}
if
(
config
.
height
&&
metadata
.
minHeight
&&
config
.
height
<
metadata
.
minHeight
)
{
errors
.
push
(
`Height must be at least
${
metadata
.
minHeight
}
px`
);
}
if
(
config
.
height
&&
metadata
.
maxHeight
&&
config
.
height
>
metadata
.
maxHeight
)
{
errors
.
push
(
`Height must be at most
${
metadata
.
maxHeight
}
px`
);
}
return
{
isValid
:
errors
.
length
===
0
,
errors
};
}
/**
* Get recommended widgets based on criteria
*/
getRecommendedWidgets
(
criteria
:
{
category
?:
WidgetCategory
;
complexity
?:
'simple'
|
'intermediate'
|
'advanced'
;
dataSource
?:
DataSourceType
;
features
?:
WidgetFeature
[];
size
?:
{
width
:
number
;
height
:
number
};
}):
string
[]
{
let
candidates
=
Array
.
from
(
this
.
widgetMetadata
.
keys
());
// Filter by category
if
(
criteria
.
category
)
{
candidates
=
candidates
.
filter
(
widgetType
=>
{
const
metadata
=
this
.
widgetMetadata
.
get
(
widgetType
);
return
metadata
?.
category
===
criteria
.
category
;
});
}
// Filter by complexity
if
(
criteria
.
complexity
)
{
candidates
=
candidates
.
filter
(
widgetType
=>
{
const
metadata
=
this
.
widgetMetadata
.
get
(
widgetType
);
return
metadata
?.
complexity
===
criteria
.
complexity
;
});
}
// Filter by data source
if
(
criteria
.
dataSource
)
{
candidates
=
candidates
.
filter
(
widgetType
=>
{
const
metadata
=
this
.
widgetMetadata
.
get
(
widgetType
);
return
metadata
?.
supportedDataSources
.
includes
(
criteria
.
dataSource
!
);
});
}
// Filter by features
if
(
criteria
.
features
&&
criteria
.
features
.
length
>
0
)
{
candidates
=
candidates
.
filter
(
widgetType
=>
{
const
metadata
=
this
.
widgetMetadata
.
get
(
widgetType
);
return
criteria
.
features
!
.
every
(
feature
=>
metadata
?.
features
.
includes
(
feature
));
});
}
// Filter by size
if
(
criteria
.
size
)
{
candidates
=
candidates
.
filter
(
widgetType
=>
{
const
metadata
=
this
.
widgetMetadata
.
get
(
widgetType
);
if
(
!
metadata
)
return
false
;
const
fitsWidth
=
(
metadata
.
minWidth
||
0
)
<=
criteria
.
size
!
.
width
&&
(
!
metadata
.
maxWidth
||
metadata
.
maxWidth
>=
criteria
.
size
!
.
width
);
const
fitsHeight
=
(
metadata
.
minHeight
||
0
)
<=
criteria
.
size
!
.
height
&&
(
!
metadata
.
maxHeight
||
metadata
.
maxHeight
>=
criteria
.
size
!
.
height
);
return
fitsWidth
&&
fitsHeight
;
});
}
return
candidates
;
}
/**
* Get comprehensive fallback configuration for widgets
*/
getFallbackConfig
(
widgetType
:
string
):
any
{
const
fallbackConfigs
:
{
[
key
:
string
]:
any
}
=
{
'CompanyInfoWidgetComponent'
:
{
title
:
'Company Information'
,
companyNameField
:
''
,
addressField
:
''
,
contactField
:
''
},
'AttendanceOverviewWidgetComponent'
:
{
title
:
'Attendance Overview'
,
presentField
:
''
,
onLeaveField
:
''
,
absentField
:
''
const
metadata
=
this
.
getWidgetMetadata
(
widgetType
);
const
baseConfig
=
{
title
:
metadata
?.
displayName
||
'Widget'
,
width
:
metadata
?.
defaultWidth
||
300
,
height
:
metadata
?.
defaultHeight
||
200
,
...
this
.
getBaseFallbackConfig
()
};
const
specificConfigs
:
{
[
key
:
string
]:
any
}
=
{
// ========================================
// KPI WIDGETS
// ========================================
'SimpleKpiWidgetComponent'
:
{
...
baseConfig
,
valueField
:
'value'
,
labelField
:
'label'
,
aggregation
:
'sum'
,
unit
:
''
,
icon
:
'trending-up'
,
decimalPlaces
:
0
,
showTrend
:
true
,
trendField
:
'trend'
,
color
:
'#3B82F6'
},
'EmployeeDirectoryWidgetComponent'
:
{
title
:
'Employee Directory'
,
nameField
:
''
,
positionField
:
''
,
departmentField
:
''
,
photoField
:
''
'KpiWidgetComponent'
:
{
...
baseConfig
,
valueField
:
'value'
,
labelField
:
'label'
,
aggregation
:
'sum'
,
unit
:
''
,
icon
:
'bar-chart-2'
,
decimalPlaces
:
0
,
showTrend
:
true
,
trendField
:
'trend'
,
color
:
'#3B82F6'
,
showComparison
:
true
,
comparisonField
:
'previousValue'
,
comparisonLabel
:
'vs Previous'
},
'HeadcountWidgetComponent'
:
{
title
:
'Headcount'
,
categoryField
:
''
,
chartType
:
'bar'
},
'PayrollSummaryWidgetComponent'
:
{
title
:
'Payroll Summary'
,
totalPayrollField
:
''
,
employeesPaidField
:
''
},
'PayrollWidgetComponent'
:
{
title
:
'Payroll'
,
employeeNameField
:
''
,
payPeriodField
:
''
,
netPayField
:
''
},
'WelcomeWidgetComponent'
:
{
title
:
'Welcome'
,
messageType
:
'static'
,
staticMessage
:
'Welcome!'
,
messageField
:
''
},
'ChartWidgetComponent'
:
{
title
:
'Chart'
,
xField
:
''
,
yAxisTitle
:
''
,
yFields
:
[]
},
'QuickLinksWidgetComponent'
:
{
title
:
'Quick Links'
,
nameField
:
''
,
urlField
:
''
,
iconField
:
''
...
baseConfig
,
valueField
:
'count'
,
labelField
:
'department'
,
aggregation
:
'count'
,
unit
:
'employees'
,
icon
:
'user-check'
,
decimalPlaces
:
0
,
showTrend
:
true
,
trendField
:
'change'
,
color
:
'#10B981'
},
// ========================================
// DATA GRID WIDGETS
// ========================================
'SyncfusionDatagridWidgetComponent'
:
{
title
:
'Data Grid'
,
...
baseConfig
,
columns
:
[],
dataSource
:
''
,
dataSource
:
'
static
'
,
allowPaging
:
true
,
allowSorting
:
true
,
allowFiltering
:
true
allowFiltering
:
true
,
allowGrouping
:
false
,
allowReordering
:
true
,
allowResizing
:
true
,
allowSelection
:
false
,
allowMultiSelection
:
false
,
allowEditing
:
false
,
allowAdding
:
false
,
pageSize
:
10
,
pageSizes
:
[
5
,
10
,
20
,
50
,
100
],
showToolbar
:
true
,
showHeader
:
true
,
showFooter
:
false
,
enableExport
:
true
,
exportFormats
:
[
'excel'
,
'pdf'
,
'csv'
],
enableSearch
:
false
,
enableVirtualization
:
false
,
rowHeight
:
40
,
headerRowHeight
:
40
,
showAlternateRows
:
false
,
alternateRowColor
:
'#F9FAFB'
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
headerBackgroundColor
:
'#F9FAFB'
,
headerTextColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
fontSize
:
14
,
fontFamily
:
'system-ui, -apple-system, sans-serif'
},
// ========================================
// PIVOT WIDGETS
// ========================================
'SyncfusionPivotWidgetComponent'
:
{
title
:
'Pivot Table'
,
...
baseConfig
,
expandAll
:
false
,
displayOptionView
:
'Both'
,
showToolbar
:
true
,
showFieldList
:
true
,
rows
:
[],
columns
:
[],
values
:
[],
filters
:
[]
},
'SyncfusionChartWidgetComponent'
:
{
title
:
'Chart'
,
xField
:
''
,
yField
:
''
,
xAxisTitle
:
''
,
yAxisTitle
:
''
},
'TimeTrackingWidgetComponent'
:
{
title
:
'Time Tracking'
,
statusField
:
''
,
hoursField
:
''
filters
:
[],
enableDrillThrough
:
true
,
enableSorting
:
true
,
enableFiltering
:
true
,
enableGrouping
:
true
,
showGrandTotals
:
true
,
showSubTotals
:
true
,
showRowTotals
:
true
,
showColumnTotals
:
true
,
enableExport
:
true
,
exportFormats
:
[
'excel'
,
'pdf'
,
'csv'
]
},
'ScatterBubbleChartWidgetComponent'
:
{
title
:
'Scatter/Bubble Chart'
,
// ========================================
// CHART WIDGETS
// ========================================
'SyncfusionChartWidgetComponent'
:
{
...
baseConfig
,
xField
:
''
,
yField
:
''
,
xAxisTitle
:
''
,
yAxisTitle
:
''
,
type
:
'Scatter'
},
'MultiRowCardWidgetComponent'
:
{
title
:
'Multi Row Card'
,
labelField
:
''
,
valueField
:
''
,
unitField
:
''
},
'ComboChartWidgetComponent'
:
{
title
:
'Combo Chart'
,
xField
:
''
,
xAxisTitle
:
''
,
yAxisTitle
:
''
,
yFields
:
[],
series
:
[]
},
'WaterfallChartWidgetComponent'
:
{
title
:
'Waterfall Chart'
,
xField
:
''
,
yField
:
''
,
xAxisTitle
:
''
,
yAxisTitle
:
''
chartType
:
'column'
,
showLegend
:
true
,
showDataLabels
:
false
,
enableAnimation
:
true
,
enableZoom
:
false
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
backgroundColor
:
'#FFFFFF'
,
textColor
:
'#374151'
,
gridLines
:
true
,
axisLines
:
true
},
'PieChartWidgetComponent'
:
{
title
:
'Pie Chart'
,
...
baseConfig
,
xField
:
''
,
yField
:
''
,
aggregation
:
'none'
aggregation
:
'sum'
,
showLegend
:
true
,
showDataLabels
:
true
,
enableAnimation
:
true
,
enableTooltip
:
true
,
innerRadius
:
0
,
startAngle
:
0
,
endAngle
:
360
,
colorScheme
:
'Material'
},
'BarChartWidgetComponent'
:
{
title
:
'Bar Chart'
,
...
baseConfig
,
xField
:
''
,
yField
:
''
,
aggregation
:
'none'
aggregation
:
'sum'
,
orientation
:
'vertical'
,
showLegend
:
true
,
showDataLabels
:
false
,
enableAnimation
:
true
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
showGridLines
:
true
},
'AreaChartWidgetComponent'
:
{
title
:
'Area Chart'
,
...
baseConfig
,
xField
:
''
,
yField
:
''
,
aggregation
:
'none'
aggregation
:
'sum'
,
showLegend
:
true
,
showDataLabels
:
false
,
enableAnimation
:
true
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
fillOpacity
:
0.6
,
showGridLines
:
true
},
'DoughnutChartWidgetComponent'
:
{
title
:
'Doughnut Chart'
,
...
baseConfig
,
xField
:
''
,
yField
:
''
,
aggregation
:
'none'
aggregation
:
'sum'
,
showLegend
:
true
,
showDataLabels
:
true
,
enableAnimation
:
true
,
enableTooltip
:
true
,
innerRadius
:
50
,
startAngle
:
0
,
endAngle
:
360
,
colorScheme
:
'Material'
},
'FunnelChartWidgetComponent'
:
{
title
:
'Funnel Chart'
,
...
baseConfig
,
xField
:
''
,
yField
:
''
,
aggregation
:
'none'
aggregation
:
'sum'
,
showLegend
:
true
,
showDataLabels
:
true
,
enableAnimation
:
true
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
funnelType
:
'funnel'
},
'GaugeChartWidgetComponent'
:
{
title
:
'Gauge Chart'
,
valueField
:
''
'ScatterBubbleChartWidgetComponent'
:
{
...
baseConfig
,
xField
:
''
,
yField
:
''
,
bubbleSizeField
:
''
,
xAxisTitle
:
''
,
yAxisTitle
:
''
,
showLegend
:
true
,
showDataLabels
:
false
,
enableAnimation
:
true
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
showGridLines
:
true
},
'SimpleTableWidgetComponent'
:
{
title
:
'Simple Table'
'GaugeChartWidgetComponent'
:
{
...
baseConfig
,
valueField
:
''
,
minValue
:
0
,
maxValue
:
100
,
showLegend
:
true
,
showDataLabels
:
true
,
enableAnimation
:
true
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
gaugeType
:
'circular'
,
startAngle
:
0
,
endAngle
:
360
},
'TreemapWidgetComponent'
:
{
title
:
'Treemap'
,
...
baseConfig
,
groupField
:
''
,
valueField
:
''
valueField
:
''
,
showLegend
:
true
,
showDataLabels
:
true
,
enableAnimation
:
true
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
layoutType
:
'squarified'
},
'WaterfallChartWidgetComponent'
:
{
...
baseConfig
,
xField
:
''
,
yField
:
''
,
xAxisTitle
:
''
,
yAxisTitle
:
''
,
showLegend
:
true
,
showDataLabels
:
true
,
enableAnimation
:
true
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
showGridLines
:
true
},
'ComboChartWidgetComponent'
:
{
...
baseConfig
,
xField
:
''
,
xAxisTitle
:
''
,
yAxisTitle
:
''
,
yFields
:
[],
series
:
[],
showLegend
:
true
,
showDataLabels
:
false
,
enableAnimation
:
true
,
enableTooltip
:
true
,
colorScheme
:
'Material'
,
showGridLines
:
true
},
// ========================================
// TABLE WIDGETS
// ========================================
'DataTableWidgetComponent'
:
{
...
baseConfig
,
columns
:
[],
dataSource
:
'static'
,
allowSorting
:
true
,
allowFiltering
:
true
,
allowPaging
:
true
,
pageSize
:
10
,
showHeader
:
true
,
showFooter
:
false
,
enableExport
:
true
,
exportFormats
:
[
'excel'
,
'csv'
],
rowHeight
:
40
,
headerRowHeight
:
40
,
showAlternateRows
:
false
,
alternateRowColor
:
'#F9FAFB'
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
headerBackgroundColor
:
'#F9FAFB'
,
headerTextColor
:
'#374151'
},
'SimpleTableWidgetComponent'
:
{
...
baseConfig
,
columns
:
[],
dataSource
:
'static'
,
showHeader
:
true
,
showFooter
:
false
,
rowHeight
:
40
,
headerRowHeight
:
40
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
headerBackgroundColor
:
'#F9FAFB'
,
headerTextColor
:
'#374151'
},
'MatrixWidgetComponent'
:
{
title
:
'Matrix'
,
columns
:
[]
...
baseConfig
,
columns
:
[],
rows
:
[],
values
:
[],
dataSource
:
'static'
,
showHeader
:
true
,
showFooter
:
true
,
enableExport
:
true
,
exportFormats
:
[
'excel'
,
'csv'
],
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
headerBackgroundColor
:
'#F9FAFB'
,
headerTextColor
:
'#374151'
},
// ========================================
// CARD WIDGETS
// ========================================
'MultiRowCardWidgetComponent'
:
{
...
baseConfig
,
labelField
:
''
,
valueField
:
''
,
unitField
:
''
,
dataSource
:
'static'
,
cardStyle
:
'default'
,
showIcon
:
true
,
iconField
:
''
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
,
shadow
:
'small'
},
'NotificationWidgetComponent'
:
{
...
baseConfig
,
messageField
:
''
,
typeField
:
''
,
timestampField
:
''
,
dataSource
:
'static'
,
maxItems
:
10
,
showTimestamp
:
true
,
showType
:
true
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
'WelcomeWidgetComponent'
:
{
...
baseConfig
,
messageType
:
'static'
,
staticMessage
:
'Welcome!'
,
messageField
:
''
,
showUserInfo
:
true
,
userField
:
''
,
backgroundColor
:
'#F0F9FF'
,
borderColor
:
'#0EA5E9'
,
textColor
:
'#0C4A6E'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
20
},
'QuickLinksWidgetComponent'
:
{
...
baseConfig
,
nameField
:
''
,
urlField
:
''
,
iconField
:
''
,
dataSource
:
'static'
,
layout
:
'grid'
,
columns
:
3
,
showIcons
:
true
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
'CompanyInfoWidgetComponent'
:
{
...
baseConfig
,
companyNameField
:
''
,
addressField
:
''
,
contactField
:
''
,
logoField
:
''
,
dataSource
:
'static'
,
showLogo
:
true
,
showAddress
:
true
,
showContact
:
true
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
20
},
'CompanyInfoSubfolderWidgetComponent'
:
{
...
baseConfig
,
companyNameField
:
''
,
addressField
:
''
,
contactField
:
''
,
logoField
:
''
,
subfolderField
:
''
,
dataSource
:
'static'
,
showLogo
:
true
,
showAddress
:
true
,
showContact
:
true
,
showSubfolders
:
true
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
20
},
'EmployeeDirectoryWidgetComponent'
:
{
...
baseConfig
,
nameField
:
''
,
positionField
:
''
,
departmentField
:
''
,
photoField
:
''
,
emailField
:
''
,
phoneField
:
''
,
dataSource
:
'static'
,
showPhotos
:
true
,
showContact
:
true
,
layout
:
'grid'
,
columns
:
3
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
// ========================================
// BUSINESS WIDGETS
// ========================================
'AttendanceOverviewWidgetComponent'
:
{
...
baseConfig
,
presentField
:
''
,
onLeaveField
:
''
,
absentField
:
''
,
dataSource
:
'static'
,
chartType
:
'doughnut'
,
showLegend
:
true
,
showDataLabels
:
true
,
enableAnimation
:
true
,
colorScheme
:
'Material'
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
'PayrollSummaryWidgetComponent'
:
{
...
baseConfig
,
totalPayrollField
:
''
,
employeesPaidField
:
''
,
averagePayField
:
''
,
dataSource
:
'static'
,
showTrend
:
true
,
trendField
:
'change'
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
'PayrollWidgetComponent'
:
{
...
baseConfig
,
employeeNameField
:
''
,
payPeriodField
:
''
,
netPayField
:
''
,
grossPayField
:
''
,
deductionsField
:
''
,
dataSource
:
'static'
,
allowFiltering
:
true
,
allowSorting
:
true
,
allowPaging
:
true
,
pageSize
:
10
,
showHeader
:
true
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
// ========================================
// UTILITY WIDGETS
// ========================================
'ClockWidgetComponent'
:
{
title
:
'Clock Widget'
,
...
baseConfig
,
timezone
:
'local'
,
timeFormat
:
'12'
,
dateFormat
:
'MM/DD/YYYY'
,
showSeconds
:
true
,
showDate
:
true
,
showDay
:
true
showDay
:
true
,
showTime
:
true
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
,
fontSize
:
18
,
fontFamily
:
'monospace'
},
'WeatherWidgetComponent'
:
{
title
:
'Weather Widget'
,
...
baseConfig
,
location
:
'Bangkok'
,
units
:
'metric'
,
showForecast
:
true
,
forecastDays
:
5
forecastDays
:
5
,
showTemperature
:
true
,
showHumidity
:
true
,
showWind
:
true
,
backgroundColor
:
'#F0F9FF'
,
borderColor
:
'#0EA5E9'
,
textColor
:
'#0C4A6E'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
'CalendarWidgetComponent'
:
{
title
:
'Calendar Widget'
,
...
baseConfig
,
viewMode
:
'month'
,
showWeekends
:
true
,
showToday
:
true
,
enableEvents
:
true
enableEvents
:
true
,
eventField
:
''
,
dateField
:
''
,
titleField
:
''
,
dataSource
:
'static'
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
'SlicerWidgetComponent'
:
{
title
:
'Slicer Widget'
,
...
baseConfig
,
slicerField
:
''
,
slicerType
:
'dropdown'
,
multiSelect
:
false
multiSelect
:
false
,
dataSource
:
'static'
,
showLabel
:
true
,
labelText
:
'Filter by:'
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
},
'FilledMapWidgetComponent'
:
{
title
:
'Map Widget'
,
...
baseConfig
,
mapType
:
'world'
,
regionField
:
'region'
,
valueField
:
'value'
},
'KpiWidgetComponent'
:
{
title
:
'KPI Widget'
,
valueField
:
'value'
,
labelField
:
'label'
,
aggregation
:
'sum'
,
unit
:
''
,
icon
:
'info'
,
decimalPlaces
:
0
dataSource
:
'static'
,
showLegend
:
true
,
showTooltip
:
true
,
colorScheme
:
'Material'
,
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
}
};
return
fallbackConfigs
[
widgetType
]
||
{
title
:
'Widget'
};
return
specificConfigs
[
widgetType
]
||
baseConfig
;
}
/**
* Get base fallback configuration common to all widgets
*/
private
getBaseFallbackConfig
():
any
{
return
{
// Common styling
backgroundColor
:
'#FFFFFF'
,
borderColor
:
'#E5E7EB'
,
textColor
:
'#374151'
,
borderRadius
:
8
,
borderWidth
:
1
,
padding
:
16
,
margin
:
8
,
fontSize
:
14
,
fontFamily
:
'system-ui, -apple-system, sans-serif'
,
fontWeight
:
'normal'
,
// Common layout
responsive
:
true
,
aspectRatio
:
'auto'
,
// Common data
dataSource
:
'static'
,
refreshInterval
:
0
,
cacheEnabled
:
false
,
cacheDuration
:
300
,
// Common features
enableAnimations
:
true
,
enableTooltip
:
true
,
enableRefresh
:
true
,
enableExport
:
false
,
// Common security
requireAuth
:
false
,
allowedRoles
:
''
,
permissionLevel
:
'read'
,
dataEncryption
:
false
,
auditLog
:
false
,
rateLimit
:
0
,
sessionTimeout
:
30
};
}
}
src/app/portal-manage/dashboard-management/widgets/configs/simple-kpi-config/simple-kpi-config.component.ts
View file @
cf761296
...
...
@@ -58,6 +58,20 @@ 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>Static Label</mat-label>
<input matInput [(ngModel)]="currentConfig.staticLabel" name="staticLabel" placeholder="Enter static label">
<mat-hint>Override label field with static text</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Static Value</mat-label>
<input matInput [(ngModel)]="currentConfig.staticValue" name="staticValue" placeholder="Enter static value">
<mat-hint>Override value field with static text</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>Value Format</mat-label>
<mat-select [(ngModel)]="currentConfig.valueFormat" name="valueFormat">
<mat-option value="number">Number</mat-option>
...
...
@@ -137,28 +151,177 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<!-- Size Configuration -->
<div class="config-section">
<h3 class="text-blue-600">Size Configuration</h3>
<div class="size-config">
<div
*ngFor="let option of sizeOptions"
class="size-option"
[class.selected]="currentConfig.sizeOption === option.id"
(click)="setSizeOption(option.id)">
<h4>{{ option.label }}</h4>
<p>{{ option.description }}</p>
<!-- Size Presets -->
<div class="size-presets">
<h4 class="text-sm font-medium text-gray-700 mb-3">Quick Presets</h4>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<div
*ngFor="let option of sizeOptions"
class="size-preset-card"
[class.selected]="currentConfig.sizeOption === option.id"
(click)="setSizeOption(option.id)">
<div class="preset-icon">
<i [class]="getSizeIcon(option.id)"></i>
</div>
<div class="preset-info">
<h5>{{ option.label }}</h5>
<p>{{ option.description }}</p>
</div>
</div>
</div>
</div>
<div *ngIf="currentConfig.sizeOption === 'custom'" class="grid grid-cols-2 gap-4 mt-4">
<mat-form-field appearance="fill">
<mat-label>Width</mat-label>
<input matInput [(ngModel)]="currentConfig.width" name="width" placeholder="e.g., 300px, 50%, auto">
<mat-hint>Widget width</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Height</mat-label>
<input matInput [(ngModel)]="currentConfig.height" name="height" placeholder="e.g., 200px, 50%, auto">
<mat-hint>Widget height</mat-hint>
</mat-form-field>
<!-- Custom Size Configuration -->
<div *ngIf="currentConfig.sizeOption === 'custom'" class="custom-size-config mt-6">
<h4 class="text-sm font-medium text-gray-700 mb-4">Custom Dimensions</h4>
<!-- Width Configuration -->
<div class="dimension-group">
<label class="dimension-label">Width</label>
<div class="dimension-controls">
<mat-form-field appearance="outline" class="flex-1">
<mat-label>Width Value</mat-label>
<input matInput [(ngModel)]="currentConfig.width" name="width"
placeholder="e.g., 300px, 50%, 100%, auto">
<mat-hint>Width value with unit</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline" class="w-32">
<mat-label>Unit</mat-label>
<mat-select [(ngModel)]="currentConfig.widthUnit" name="widthUnit">
<mat-option value="px">px</mat-option>
<mat-option value="%">%</mat-option>
<mat-option value="rem">rem</mat-option>
<mat-option value="em">em</mat-option>
<mat-option value="vh">vh</mat-option>
<mat-option value="vw">vw</mat-option>
<mat-option value="auto">auto</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<!-- Height Configuration -->
<div class="dimension-group">
<label class="dimension-label">Height</label>
<div class="dimension-controls">
<mat-form-field appearance="outline" class="flex-1">
<mat-label>Height Value</mat-label>
<input matInput [(ngModel)]="currentConfig.height" name="height"
placeholder="e.g., 200px, 50%, 100%, auto">
<mat-hint>Height value with unit</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline" class="w-32">
<mat-label>Unit</mat-label>
<mat-select [(ngModel)]="currentConfig.heightUnit" name="heightUnit">
<mat-option value="px">px</mat-option>
<mat-option value="%">%</mat-option>
<mat-option value="rem">rem</mat-option>
<mat-option value="em">em</mat-option>
<mat-option value="vh">vh</mat-option>
<mat-option value="vw">vw</mat-option>
<mat-option value="auto">auto</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<!-- Full Panel Options -->
<div class="full-panel-options">
<h5 class="text-sm font-medium text-gray-600 mb-3">Full Panel Options</h5>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="full-panel-card" (click)="setFullPanel('width')"
[class.selected]="currentConfig.fullWidth">
<div class="card-icon">
<i class="fas fa-arrows-alt-h"></i>
</div>
<div class="card-content">
<h6>Full Width</h6>
<p>100% width of container</p>
</div>
<mat-checkbox [(ngModel)]="currentConfig.fullWidth"
(change)="onFullWidthChange($event)"></mat-checkbox>
</div>
<div class="full-panel-card" (click)="setFullPanel('height')"
[class.selected]="currentConfig.fullHeight">
<div class="card-icon">
<i class="fas fa-arrows-alt-v"></i>
</div>
<div class="card-content">
<h6>Full Height</h6>
<p>100% height of container</p>
</div>
<mat-checkbox [(ngModel)]="currentConfig.fullHeight"
(change)="onFullHeightChange($event)"></mat-checkbox>
</div>
</div>
</div>
<!-- Responsive Settings -->
<div class="responsive-settings mt-6">
<h5 class="text-sm font-medium text-gray-600 mb-3">Responsive Settings</h5>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="outline">
<mat-label>Min Width</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.minWidth"
name="minWidth" min="0" placeholder="200">
<mat-hint>Minimum width in pixels</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Min Height</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.minHeight"
name="minHeight" min="0" placeholder="150">
<mat-hint>Minimum height in pixels</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Max Width</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.maxWidth"
name="maxWidth" min="0" placeholder="800">
<mat-hint>Maximum width in pixels</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Max Height</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.maxHeight"
name="maxHeight" min="0" placeholder="600">
<mat-hint>Maximum height in pixels</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Aspect Ratio -->
<div class="aspect-ratio-settings mt-6">
<h5 class="text-sm font-medium text-gray-600 mb-3">Aspect Ratio</h5>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<div class="aspect-ratio-option"
[class.selected]="currentConfig.aspectRatio === 'auto'"
(click)="setAspectRatio('auto')">
<div class="ratio-icon">📐</div>
<span>Auto</span>
</div>
<div class="aspect-ratio-option"
[class.selected]="currentConfig.aspectRatio === '16:9'"
(click)="setAspectRatio('16:9')">
<div class="ratio-icon">📺</div>
<span>16:9</span>
</div>
<div class="aspect-ratio-option"
[class.selected]="currentConfig.aspectRatio === '4:3'"
(click)="setAspectRatio('4:3')">
<div class="ratio-icon">📱</div>
<span>4:3</span>
</div>
<div class="aspect-ratio-option"
[class.selected]="currentConfig.aspectRatio === '1:1'"
(click)="setAspectRatio('1:1')">
<div class="ratio-icon">⬜</div>
<span>1:1</span>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
@@ -171,16 +334,95 @@ 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>Background Type</mat-label>
<mat-select [(ngModel)]="currentConfig.backgroundType" name="backgroundType">
<mat-option value="solid">Solid Color</mat-option>
<mat-option value="gradient">Gradient</mat-option>
<mat-option value="image">Image</mat-option>
</mat-select>
<mat-hint>Type of background</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Background Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.backgroundColor" name="backgroundColor">
<mat-hint>Widget background color</mat-hint>
</mat-form-field>
</div>
<div *ngIf="currentConfig.backgroundType === 'gradient'" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Gradient Start Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.gradientStartColor" name="gradientStartColor">
<mat-hint>Gradient start color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Gradient End Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.gradientEndColor" name="gradientEndColor">
<mat-hint>Gradient end color</mat-hint>
</mat-form-field>
</div>
<div *ngIf="currentConfig.backgroundType === 'gradient'" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Gradient Direction</mat-label>
<mat-select [(ngModel)]="currentConfig.gradientDirection" name="gradientDirection">
<mat-option value="to right">Left to Right</mat-option>
<mat-option value="to left">Right to Left</mat-option>
<mat-option value="to bottom">Top to Bottom</mat-option>
<mat-option value="to top">Bottom to Top</mat-option>
<mat-option value="to bottom right">Top Left to Bottom Right</mat-option>
<mat-option value="to top left">Bottom Right to Top Left</mat-option>
</mat-select>
<mat-hint>Gradient direction</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Gradient Type</mat-label>
<mat-select [(ngModel)]="currentConfig.gradientType" name="gradientType">
<mat-option value="linear">Linear</mat-option>
<mat-option value="radial">Radial</mat-option>
</mat-select>
<mat-hint>Gradient type</mat-hint>
</mat-form-field>
</div>
<div *ngIf="currentConfig.backgroundType === 'image'" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Background Image URL</mat-label>
<input matInput [(ngModel)]="currentConfig.backgroundImage" name="backgroundImage" placeholder="https://example.com/image.jpg">
<mat-hint>Background image URL</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Background Size</mat-label>
<mat-select [(ngModel)]="currentConfig.backgroundSize" name="backgroundSize">
<mat-option value="cover">Cover</mat-option>
<mat-option value="contain">Contain</mat-option>
<mat-option value="100% 100%">Stretch</mat-option>
</mat-select>
<mat-hint>How background image is sized</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>Text Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.textColor" name="textColor">
<mat-hint>Text color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Text Shadow</mat-label>
<mat-select [(ngModel)]="currentConfig.textShadow" name="textShadow">
<mat-option value="none">None</mat-option>
<mat-option value="small">Small</mat-option>
<mat-option value="medium">Medium</mat-option>
<mat-option value="large">Large</mat-option>
</mat-select>
<mat-hint>Text shadow effect</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
...
...
@@ -312,28 +554,24 @@ 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</mat-label>
<mat-select [(ngModel)]="currentConfig.icon" name="icon">
<mat-option value="none">None</mat-option>
<mat-option value="trending_up">Trending Up</mat-option>
<mat-option value="trending_down">Trending Down</mat-option>
<mat-option value="show_chart">Chart</mat-option>
<mat-option value="assessment">Assessment</mat-option>
<mat-option value="analytics">Analytics</mat-option>
<mat-option value="bar_chart">Bar Chart</mat-option>
<mat-option value="pie_chart">Pie Chart</mat-option>
<mat-option value="donut_large">Donut</mat-option>
<mat-option value="account_balance">Account Balance</mat-option>
<mat-option value="attach_money">Money</mat-option>
<mat-option value="people">People</mat-option>
<mat-option value="business">Business</mat-option>
<mat-option value="home">Home</mat-option>
<mat-option value="star">Star</mat-option>
<mat-option value="favorite">Favorite</mat-option>
<mat-label>Icon Library</mat-label>
<mat-select [(ngModel)]="currentConfig.iconLibrary" name="iconLibrary">
<mat-option value="material">Material Icons</mat-option>
<mat-option value="bootstrap">Bootstrap Icons</mat-option>
<mat-option value="fontawesome">Font Awesome</mat-option>
<mat-option value="feather">Feather Icons</mat-option>
</mat-select>
<mat-hint>Icon to display with KPI</mat-hint>
<mat-hint>Icon library to use</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Icon</mat-label>
<input matInput [(ngModel)]="currentConfig.icon" name="icon" placeholder="e.g., trending-up, chart-bar">
<mat-hint>Icon name from selected library</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 Position</mat-label>
<mat-select [(ngModel)]="currentConfig.iconPosition" name="iconPosition">
...
...
@@ -341,9 +579,20 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-option value="right">Right</mat-option>
<mat-option value="top">Top</mat-option>
<mat-option value="bottom">Bottom</mat-option>
<mat-option value="center">Center</mat-option>
</mat-select>
<mat-hint>Position of the icon</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Icon Style</mat-label>
<mat-select [(ngModel)]="currentConfig.iconStyle" name="iconStyle">
<mat-option value="outline">Outline</mat-option>
<mat-option value="filled">Filled</mat-option>
<mat-option value="duotone">Duotone</mat-option>
</mat-select>
<mat-hint>Icon style variant</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
...
...
@@ -359,6 +608,30 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Icon 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 Background</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.iconBackground" name="iconBackground">
<mat-hint>Icon background color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Icon Border Radius (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.iconBorderRadius" name="iconBorderRadius" min="0" max="50">
<mat-hint>Icon background border radius</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.showIconBackground" name="showIconBackground">
Show Icon Background
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.iconPulse" name="iconPulse">
Icon Pulse Animation
</mat-checkbox>
</div>
</div>
</mat-tab>
...
...
@@ -486,6 +759,10 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-option value="scale">Scale In</mat-option>
<mat-option value="bounce">Bounce</mat-option>
<mat-option value="pulse">Pulse</mat-option>
<mat-option value="flip">Flip</mat-option>
<mat-option value="zoom">Zoom</mat-option>
<mat-option value="rotate">Rotate</mat-option>
<mat-option value="shake">Shake</mat-option>
</mat-select>
<mat-hint>Type of animation effect</mat-hint>
</mat-form-field>
...
...
@@ -499,6 +776,30 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<div *ngIf="currentConfig.enableAnimations" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Animation Direction</mat-label>
<mat-select [(ngModel)]="currentConfig.animationDirection" name="animationDirection">
<mat-option value="normal">Normal</mat-option>
<mat-option value="reverse">Reverse</mat-option>
<mat-option value="alternate">Alternate</mat-option>
<mat-option value="alternate-reverse">Alternate Reverse</mat-option>
</mat-select>
<mat-hint>Animation direction</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Animation Iteration Count</mat-label>
<mat-select [(ngModel)]="currentConfig.animationIteration" name="animationIteration">
<mat-option value="1">Once</mat-option>
<mat-option value="2">Twice</mat-option>
<mat-option value="3">Three Times</mat-option>
<mat-option value="infinite">Infinite</mat-option>
</mat-select>
<mat-hint>How many times to repeat animation</mat-hint>
</mat-form-field>
</div>
<div *ngIf="currentConfig.enableAnimations" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Animation Delay (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.animationDelay" name="animationDelay" min="0" max="1000">
<mat-hint>Delay before animation starts</mat-hint>
...
...
@@ -631,6 +932,34 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
placeholder="data => data.map(item => ({ ...item, formattedValue: formatCurrency(item.value) }))"></textarea>
<mat-hint>JavaScript function to transform data</mat-hint>
</mat-form-field>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Data Validation</mat-label>
<mat-select [(ngModel)]="currentConfig.dataValidation" name="dataValidation">
<mat-option value="none">None</mat-option>
<mat-option value="required">Required</mat-option>
<mat-option value="numeric">Numeric Only</mat-option>
<mat-option value="positive">Positive Numbers Only</mat-option>
</mat-select>
<mat-hint>Data validation rules</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Error Message</mat-label>
<input matInput [(ngModel)]="currentConfig.errorMessage" name="errorMessage" placeholder="Failed to load data">
<mat-hint>Error message to display</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.showDataCount" name="showDataCount">
Show Data Count
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.showLastUpdated" name="showLastUpdated">
Show Last Updated
</mat-checkbox>
</div>
</div>
</mat-tab>
...
...
@@ -685,6 +1014,35 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
placeholder="function(event) { console.log('Widget clicked:', event); }"></textarea>
<mat-hint>Custom JavaScript function for click events</mat-hint>
</mat-form-field>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Hover Effect</mat-label>
<mat-select [(ngModel)]="currentConfig.hoverEffect" name="hoverEffect">
<mat-option value="none">None</mat-option>
<mat-option value="scale">Scale</mat-option>
<mat-option value="glow">Glow</mat-option>
<mat-option value="shadow">Shadow</mat-option>
<mat-option value="tilt">Tilt</mat-option>
</mat-select>
<mat-hint>Hover effect type</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Tooltip Content</mat-label>
<input matInput [(ngModel)]="currentConfig.tooltipContent" name="tooltipContent" placeholder="Custom tooltip text">
<mat-hint>Custom tooltip content</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.enableKeyboard" name="enableKeyboard">
Enable Keyboard Navigation
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.enableFocus" name="enableFocus">
Enable Focus Indicator
</mat-checkbox>
</div>
</div>
</mat-tab>
...
...
@@ -726,6 +1084,33 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<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 class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Permission Level</mat-label>
<mat-select [(ngModel)]="currentConfig.permissionLevel" name="permissionLevel">
<mat-option value="read">Read Only</mat-option>
<mat-option value="write">Read/Write</mat-option>
<mat-option value="admin">Full Access</mat-option>
</mat-select>
<mat-hint>Required permission level</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Session Timeout (minutes)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.sessionTimeout" name="sessionTimeout" min="5" max="480">
<mat-hint>Session timeout in minutes</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.requireHttps" name="requireHttps">
Require HTTPS
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.enableCors" name="enableCors">
Enable CORS
</mat-checkbox>
</div>
</div>
</mat-tab>
...
...
@@ -783,6 +1168,29 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-hint>Color when condition is false</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>True Icon</mat-label>
<input matInput [(ngModel)]="currentConfig.trueIcon" name="trueIcon" placeholder="e.g., check-circle">
<mat-hint>Icon when condition is true</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>False Icon</mat-label>
<input matInput [(ngModel)]="currentConfig.falseIcon" name="falseIcon" placeholder="e.g., x-circle">
<mat-hint>Icon when condition is false</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.showConditionalIcon" name="showConditionalIcon">
Show Conditional Icon
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.animateConditionalChange" name="animateConditionalChange">
Animate Conditional Change
</mat-checkbox>
</div>
</div>
</div>
</mat-tab>
...
...
@@ -899,6 +1307,209 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
.text-teal-600 { color: #0d9488; }
.text-red-600 { color: #dc2626; }
.text-gray-600 { color: #4b5563; }
/* Size Configuration Styles */
.size-presets {
margin-bottom: 24px;
}
.size-preset-card {
display: flex;
align-items: center;
padding: 16px;
border: 2px solid #e5e7eb;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
background: white;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.size-preset-card:hover {
border-color: #3b82f6;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
transform: translateY(-2px);
}
.size-preset-card.selected {
border-color: #3b82f6;
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
color: white;
box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3);
}
.preset-icon {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(59, 130, 246, 0.1);
border-radius: 12px;
margin-right: 12px;
font-size: 20px;
color: #3b82f6;
}
.size-preset-card.selected .preset-icon {
background: rgba(255, 255, 255, 0.2);
color: white;
}
.preset-info h5 {
margin: 0 0 4px 0;
font-size: 14px;
font-weight: 600;
}
.preset-info p {
margin: 0;
font-size: 12px;
opacity: 0.8;
}
.custom-size-config {
background: #f8fafc;
border-radius: 12px;
padding: 24px;
border: 1px solid #e2e8f0;
}
.dimension-group {
margin-bottom: 20px;
}
.dimension-label {
display: block;
font-size: 14px;
font-weight: 600;
color: #374151;
margin-bottom: 8px;
}
.dimension-controls {
display: flex;
gap: 12px;
align-items: flex-end;
}
.full-panel-options {
background: white;
border-radius: 8px;
padding: 20px;
border: 1px solid #e5e7eb;
}
.full-panel-card {
display: flex;
align-items: center;
padding: 16px;
border: 2px solid #e5e7eb;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
background: white;
}
.full-panel-card:hover {
border-color: #10b981;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15);
}
.full-panel-card.selected {
border-color: #10b981;
background: linear-gradient(135deg, #10b981, #059669);
color: white;
}
.card-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(16, 185, 129, 0.1);
border-radius: 8px;
margin-right: 12px;
font-size: 16px;
color: #10b981;
}
.full-panel-card.selected .card-icon {
background: rgba(255, 255, 255, 0.2);
color: white;
}
.card-content {
flex: 1;
}
.card-content h6 {
margin: 0 0 4px 0;
font-size: 14px;
font-weight: 600;
}
.card-content p {
margin: 0;
font-size: 12px;
opacity: 0.8;
}
.responsive-settings {
background: white;
border-radius: 8px;
padding: 20px;
border: 1px solid #e5e7eb;
}
.aspect-ratio-settings {
background: white;
border-radius: 8px;
padding: 20px;
border: 1px solid #e5e7eb;
}
.aspect-ratio-option {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
border: 2px solid #e5e7eb;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
background: white;
}
.aspect-ratio-option:hover {
border-color: #8b5cf6;
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.15);
}
.aspect-ratio-option.selected {
border-color: #8b5cf6;
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
color: white;
}
.ratio-icon {
font-size: 24px;
margin-bottom: 8px;
}
.aspect-ratio-option span {
font-size: 12px;
font-weight: 600;
}
.flex-1 {
flex: 1;
}
.w-32 {
width: 8rem;
}
`
]
})
export
class
SimpleKpiConfigComponent
extends
BaseConfigComponent
implements
OnInit
{
...
...
@@ -906,6 +1517,9 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
{
id
:
'small'
,
label
:
'Small'
,
description
:
'200x150px'
},
{
id
:
'medium'
,
label
:
'Medium'
,
description
:
'300x200px'
},
{
id
:
'large'
,
label
:
'Large'
,
description
:
'400x300px'
},
{
id
:
'full-width'
,
label
:
'Full Width'
,
description
:
'100% width'
},
{
id
:
'full-height'
,
label
:
'Full Height'
,
description
:
'100% height'
},
{
id
:
'full-panel'
,
label
:
'Full Panel'
,
description
:
'100% width & height'
},
{
id
:
'custom'
,
label
:
'Custom'
,
description
:
'Custom size'
}
];
...
...
@@ -915,9 +1529,12 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
}
override
initializeDefaultConfig
()
{
// Basic configuration
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
.
staticLabel
)
this
.
currentConfig
.
staticLabel
=
''
;
if
(
!
this
.
currentConfig
.
staticValue
)
this
.
currentConfig
.
staticValue
=
''
;
if
(
!
this
.
currentConfig
.
aggregation
)
this
.
currentConfig
.
aggregation
=
'sum'
;
if
(
!
this
.
currentConfig
.
unit
)
this
.
currentConfig
.
unit
=
''
;
if
(
!
this
.
currentConfig
.
icon
)
this
.
currentConfig
.
icon
=
'info'
;
...
...
@@ -925,6 +1542,55 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
if
(
!
this
.
currentConfig
.
sizeOption
)
this
.
currentConfig
.
sizeOption
=
'medium'
;
if
(
!
this
.
currentConfig
.
width
)
this
.
currentConfig
.
width
=
'300px'
;
if
(
!
this
.
currentConfig
.
height
)
this
.
currentConfig
.
height
=
'200px'
;
if
(
!
this
.
currentConfig
.
widthUnit
)
this
.
currentConfig
.
widthUnit
=
'px'
;
if
(
!
this
.
currentConfig
.
heightUnit
)
this
.
currentConfig
.
heightUnit
=
'px'
;
if
(
this
.
currentConfig
.
fullWidth
===
undefined
)
this
.
currentConfig
.
fullWidth
=
false
;
if
(
this
.
currentConfig
.
fullHeight
===
undefined
)
this
.
currentConfig
.
fullHeight
=
false
;
// Background configuration
if
(
!
this
.
currentConfig
.
backgroundType
)
this
.
currentConfig
.
backgroundType
=
'solid'
;
if
(
!
this
.
currentConfig
.
gradientStartColor
)
this
.
currentConfig
.
gradientStartColor
=
'#3366FF'
;
if
(
!
this
.
currentConfig
.
gradientEndColor
)
this
.
currentConfig
.
gradientEndColor
=
'#00CCFF'
;
if
(
!
this
.
currentConfig
.
gradientDirection
)
this
.
currentConfig
.
gradientDirection
=
'to bottom right'
;
if
(
!
this
.
currentConfig
.
gradientType
)
this
.
currentConfig
.
gradientType
=
'linear'
;
if
(
!
this
.
currentConfig
.
backgroundImage
)
this
.
currentConfig
.
backgroundImage
=
''
;
if
(
!
this
.
currentConfig
.
backgroundSize
)
this
.
currentConfig
.
backgroundSize
=
'cover'
;
// Icon configuration
if
(
!
this
.
currentConfig
.
iconLibrary
)
this
.
currentConfig
.
iconLibrary
=
'material'
;
if
(
!
this
.
currentConfig
.
iconStyle
)
this
.
currentConfig
.
iconStyle
=
'outline'
;
if
(
!
this
.
currentConfig
.
iconBackground
)
this
.
currentConfig
.
iconBackground
=
'#FFFFFF'
;
if
(
!
this
.
currentConfig
.
iconBorderRadius
)
this
.
currentConfig
.
iconBorderRadius
=
0
;
if
(
this
.
currentConfig
.
showIconBackground
===
undefined
)
this
.
currentConfig
.
showIconBackground
=
false
;
if
(
this
.
currentConfig
.
iconPulse
===
undefined
)
this
.
currentConfig
.
iconPulse
=
false
;
// Animation configuration
if
(
!
this
.
currentConfig
.
animationDirection
)
this
.
currentConfig
.
animationDirection
=
'normal'
;
if
(
!
this
.
currentConfig
.
animationIteration
)
this
.
currentConfig
.
animationIteration
=
'1'
;
// Data configuration
if
(
!
this
.
currentConfig
.
dataValidation
)
this
.
currentConfig
.
dataValidation
=
'none'
;
if
(
!
this
.
currentConfig
.
errorMessage
)
this
.
currentConfig
.
errorMessage
=
'Failed to load data'
;
if
(
this
.
currentConfig
.
showDataCount
===
undefined
)
this
.
currentConfig
.
showDataCount
=
false
;
if
(
this
.
currentConfig
.
showLastUpdated
===
undefined
)
this
.
currentConfig
.
showLastUpdated
=
false
;
// Interaction configuration
if
(
!
this
.
currentConfig
.
hoverEffect
)
this
.
currentConfig
.
hoverEffect
=
'scale'
;
if
(
!
this
.
currentConfig
.
tooltipContent
)
this
.
currentConfig
.
tooltipContent
=
''
;
if
(
this
.
currentConfig
.
enableKeyboard
===
undefined
)
this
.
currentConfig
.
enableKeyboard
=
true
;
if
(
this
.
currentConfig
.
enableFocus
===
undefined
)
this
.
currentConfig
.
enableFocus
=
true
;
// Security configuration
if
(
!
this
.
currentConfig
.
permissionLevel
)
this
.
currentConfig
.
permissionLevel
=
'read'
;
if
(
!
this
.
currentConfig
.
sessionTimeout
)
this
.
currentConfig
.
sessionTimeout
=
30
;
if
(
this
.
currentConfig
.
requireHttps
===
undefined
)
this
.
currentConfig
.
requireHttps
=
false
;
if
(
this
.
currentConfig
.
enableCors
===
undefined
)
this
.
currentConfig
.
enableCors
=
true
;
// Conditional formatting configuration
if
(
!
this
.
currentConfig
.
trueIcon
)
this
.
currentConfig
.
trueIcon
=
'check-circle'
;
if
(
!
this
.
currentConfig
.
falseIcon
)
this
.
currentConfig
.
falseIcon
=
'x-circle'
;
if
(
this
.
currentConfig
.
showConditionalIcon
===
undefined
)
this
.
currentConfig
.
showConditionalIcon
=
false
;
if
(
this
.
currentConfig
.
animateConditionalChange
===
undefined
)
this
.
currentConfig
.
animateConditionalChange
=
true
;
}
private
initializeColorDefaults
()
{
...
...
@@ -937,6 +1603,7 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
if
(
!
this
.
currentConfig
.
accentColor
)
this
.
currentConfig
.
accentColor
=
'#3B82F6'
;
if
(
!
this
.
currentConfig
.
trueColor
)
this
.
currentConfig
.
trueColor
=
'#10B981'
;
if
(
!
this
.
currentConfig
.
falseColor
)
this
.
currentConfig
.
falseColor
=
'#EF4444'
;
if
(
!
this
.
currentConfig
.
textShadow
)
this
.
currentConfig
.
textShadow
=
'none'
;
// Initialize new fields
if
(
!
this
.
currentConfig
.
valueFormat
)
this
.
currentConfig
.
valueFormat
=
'number'
;
...
...
@@ -992,13 +1659,83 @@ export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnI
if
(
optionId
===
'small'
)
{
this
.
currentConfig
.
width
=
'200px'
;
this
.
currentConfig
.
height
=
'150px'
;
this
.
currentConfig
.
fullWidth
=
false
;
this
.
currentConfig
.
fullHeight
=
false
;
}
else
if
(
optionId
===
'medium'
)
{
this
.
currentConfig
.
width
=
'300px'
;
this
.
currentConfig
.
height
=
'200px'
;
this
.
currentConfig
.
fullWidth
=
false
;
this
.
currentConfig
.
fullHeight
=
false
;
}
else
if
(
optionId
===
'large'
)
{
this
.
currentConfig
.
width
=
'400px'
;
this
.
currentConfig
.
height
=
'300px'
;
this
.
currentConfig
.
fullWidth
=
false
;
this
.
currentConfig
.
fullHeight
=
false
;
}
else
if
(
optionId
===
'full-width'
)
{
this
.
currentConfig
.
width
=
'100%'
;
this
.
currentConfig
.
height
=
'200px'
;
this
.
currentConfig
.
fullWidth
=
true
;
this
.
currentConfig
.
fullHeight
=
false
;
}
else
if
(
optionId
===
'full-height'
)
{
this
.
currentConfig
.
width
=
'300px'
;
this
.
currentConfig
.
height
=
'100%'
;
this
.
currentConfig
.
fullWidth
=
false
;
this
.
currentConfig
.
fullHeight
=
true
;
}
else
if
(
optionId
===
'full-panel'
)
{
this
.
currentConfig
.
width
=
'100%'
;
this
.
currentConfig
.
height
=
'100%'
;
this
.
currentConfig
.
fullWidth
=
true
;
this
.
currentConfig
.
fullHeight
=
true
;
}
this
.
configChange
.
emit
(
this
.
currentConfig
);
}
getSizeIcon
(
optionId
:
string
):
string
{
const
iconMap
:
{
[
key
:
string
]:
string
}
=
{
'small'
:
'fas fa-square'
,
'medium'
:
'fas fa-expand-arrows-alt'
,
'large'
:
'fas fa-expand'
,
'full-width'
:
'fas fa-arrows-alt-h'
,
'full-height'
:
'fas fa-arrows-alt-v'
,
'full-panel'
:
'fas fa-expand-arrows-alt'
,
'custom'
:
'fas fa-cog'
};
return
iconMap
[
optionId
]
||
'fas fa-square'
;
}
setFullPanel
(
type
:
'width'
|
'height'
)
{
if
(
type
===
'width'
)
{
this
.
currentConfig
.
fullWidth
=
!
this
.
currentConfig
.
fullWidth
;
if
(
this
.
currentConfig
.
fullWidth
)
{
this
.
currentConfig
.
width
=
'100%'
;
}
}
else
{
this
.
currentConfig
.
fullHeight
=
!
this
.
currentConfig
.
fullHeight
;
if
(
this
.
currentConfig
.
fullHeight
)
{
this
.
currentConfig
.
height
=
'100%'
;
}
}
this
.
configChange
.
emit
(
this
.
currentConfig
);
}
onFullWidthChange
(
event
:
any
)
{
this
.
currentConfig
.
fullWidth
=
event
.
checked
;
if
(
this
.
currentConfig
.
fullWidth
)
{
this
.
currentConfig
.
width
=
'100%'
;
}
this
.
configChange
.
emit
(
this
.
currentConfig
);
}
onFullHeightChange
(
event
:
any
)
{
this
.
currentConfig
.
fullHeight
=
event
.
checked
;
if
(
this
.
currentConfig
.
fullHeight
)
{
this
.
currentConfig
.
height
=
'100%'
;
}
this
.
configChange
.
emit
(
this
.
currentConfig
);
}
setAspectRatio
(
ratio
:
string
)
{
this
.
currentConfig
.
aspectRatio
=
ratio
;
this
.
configChange
.
emit
(
this
.
currentConfig
);
}
}
src/app/portal-manage/dashboard-management/widgets/simple-kpi-widget/simple-kpi-widget.component.ts
View file @
cf761296
...
...
@@ -33,6 +33,24 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public
borderWidth
:
number
=
1
;
public
shadow
:
string
=
'medium'
;
// New display properties
public
staticLabel
:
string
=
''
;
public
staticValue
:
string
=
''
;
public
backgroundType
:
string
=
'solid'
;
public
gradientStartColor
:
string
=
'#3366FF'
;
public
gradientEndColor
:
string
=
'#00CCFF'
;
public
gradientDirection
:
string
=
'to bottom right'
;
public
gradientType
:
string
=
'linear'
;
public
backgroundImage
:
string
=
''
;
public
backgroundSize
:
string
=
'cover'
;
public
textShadow
:
string
=
'none'
;
public
iconLibrary
:
string
=
'material'
;
public
iconStyle
:
string
=
'outline'
;
public
iconBackground
:
string
=
'#FFFFFF'
;
public
iconBorderRadius
:
number
=
0
;
public
showIconBackground
:
boolean
=
false
;
public
iconPulse
:
boolean
=
false
;
// Trend properties
public
showTrend
:
boolean
=
false
;
public
trendValue
:
string
=
''
;
...
...
@@ -66,6 +84,8 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public
animationEasing
:
string
=
'ease'
;
public
hoverEffects
:
boolean
=
true
;
public
autoRefresh
:
boolean
=
false
;
public
animationDirection
:
string
=
'normal'
;
public
animationIteration
:
string
=
'1'
;
// Interaction properties
public
enableTooltip
:
boolean
=
true
;
...
...
@@ -76,6 +96,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public
enableRefresh
:
boolean
=
true
;
public
clickAction
:
string
=
'none'
;
public
customClickHandler
:
string
=
''
;
public
hoverEffect
:
string
=
'scale'
;
public
tooltipContent
:
string
=
''
;
public
enableKeyboard
:
boolean
=
true
;
public
enableFocus
:
boolean
=
true
;
// Layout properties
public
width
:
number
=
300
;
...
...
@@ -86,6 +110,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public
maxHeight
:
number
=
400
;
public
aspectRatio
:
string
=
'auto'
;
public
responsive
:
boolean
=
true
;
public
widthUnit
:
string
=
'px'
;
public
heightUnit
:
string
=
'px'
;
public
fullWidth
:
boolean
=
false
;
public
fullHeight
:
boolean
=
false
;
// Data properties
public
dataSource
:
string
=
'static'
;
...
...
@@ -94,6 +122,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public
cacheEnabled
:
boolean
=
false
;
public
cacheDuration
:
number
=
300
;
public
dataTransform
:
string
=
''
;
public
dataValidation
:
string
=
'none'
;
public
override
errorMessage
:
string
=
'Failed to load data'
;
public
showDataCount
:
boolean
=
false
;
public
showLastUpdated
:
boolean
=
false
;
// Filter properties
public
enableFilter
:
boolean
=
false
;
...
...
@@ -109,6 +141,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public
conditionValue
:
string
=
''
;
public
trueColor
:
string
=
'#10B981'
;
public
falseColor
:
string
=
'#EF4444'
;
public
trueIcon
:
string
=
'check-circle'
;
public
falseIcon
:
string
=
'x-circle'
;
public
showConditionalIcon
:
boolean
=
false
;
public
animateConditionalChange
:
boolean
=
true
;
// Security properties
public
requireAuth
:
boolean
=
false
;
...
...
@@ -116,12 +152,16 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
public
dataEncryption
:
boolean
=
false
;
public
auditLog
:
boolean
=
false
;
public
rateLimit
:
number
=
0
;
public
permissionLevel
:
string
=
'read'
;
public
sessionTimeout
:
number
=
30
;
public
requireHttps
:
boolean
=
false
;
public
enableCors
:
boolean
=
true
;
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
applyInitialConfig
():
void
{
override
applyInitialConfig
():
void
{
// Basic configuration
this
.
title
=
this
.
configObj
.
title
||
'KPI'
;
this
.
label
=
this
.
configObj
.
labelField
?
''
:
'KPI'
;
...
...
@@ -131,6 +171,8 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
iconSize
=
this
.
configObj
.
iconSize
||
24
;
this
.
valueField
=
this
.
configObj
.
valueField
||
''
;
this
.
labelField
=
this
.
configObj
.
labelField
||
''
;
this
.
staticLabel
=
this
.
configObj
.
staticLabel
||
''
;
this
.
staticValue
=
this
.
configObj
.
staticValue
||
''
;
this
.
valueFormat
=
this
.
configObj
.
valueFormat
||
'number'
;
this
.
decimalPlaces
=
this
.
configObj
.
decimalPlaces
||
0
;
this
.
aggregation
=
this
.
configObj
.
aggregation
||
'sum'
;
...
...
@@ -147,6 +189,24 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
shadow
=
this
.
configObj
.
shadow
||
'medium'
;
this
.
iconColor
=
this
.
configObj
.
iconColor
||
'#FFFFFF'
;
// Background configuration
this
.
backgroundType
=
this
.
configObj
.
backgroundType
||
'solid'
;
this
.
gradientStartColor
=
this
.
configObj
.
gradientStartColor
||
'#3366FF'
;
this
.
gradientEndColor
=
this
.
configObj
.
gradientEndColor
||
'#00CCFF'
;
this
.
gradientDirection
=
this
.
configObj
.
gradientDirection
||
'to bottom right'
;
this
.
gradientType
=
this
.
configObj
.
gradientType
||
'linear'
;
this
.
backgroundImage
=
this
.
configObj
.
backgroundImage
||
''
;
this
.
backgroundSize
=
this
.
configObj
.
backgroundSize
||
'cover'
;
this
.
textShadow
=
this
.
configObj
.
textShadow
||
'none'
;
// Icon configuration
this
.
iconLibrary
=
this
.
configObj
.
iconLibrary
||
'material'
;
this
.
iconStyle
=
this
.
configObj
.
iconStyle
||
'outline'
;
this
.
iconBackground
=
this
.
configObj
.
iconBackground
||
'#FFFFFF'
;
this
.
iconBorderRadius
=
this
.
configObj
.
iconBorderRadius
||
0
;
this
.
showIconBackground
=
this
.
configObj
.
showIconBackground
!==
undefined
?
this
.
configObj
.
showIconBackground
:
false
;
this
.
iconPulse
=
this
.
configObj
.
iconPulse
!==
undefined
?
this
.
configObj
.
iconPulse
:
false
;
// Typography configuration
this
.
fontSize
=
this
.
configObj
.
fontSize
||
16
;
this
.
valueFontSize
=
this
.
configObj
.
valueFontSize
||
32
;
...
...
@@ -176,6 +236,8 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
animationEasing
=
this
.
configObj
.
animationEasing
||
'ease'
;
this
.
hoverEffects
=
this
.
configObj
.
hoverEffects
!==
undefined
?
this
.
configObj
.
hoverEffects
:
true
;
this
.
autoRefresh
=
this
.
configObj
.
autoRefresh
!==
undefined
?
this
.
configObj
.
autoRefresh
:
false
;
this
.
animationDirection
=
this
.
configObj
.
animationDirection
||
'normal'
;
this
.
animationIteration
=
this
.
configObj
.
animationIteration
||
'1'
;
// Interaction configuration
this
.
enableTooltip
=
this
.
configObj
.
enableTooltip
!==
undefined
?
this
.
configObj
.
enableTooltip
:
true
;
...
...
@@ -186,6 +248,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
enableRefresh
=
this
.
configObj
.
enableRefresh
!==
undefined
?
this
.
configObj
.
enableRefresh
:
true
;
this
.
clickAction
=
this
.
configObj
.
clickAction
||
'none'
;
this
.
customClickHandler
=
this
.
configObj
.
customClickHandler
||
''
;
this
.
hoverEffect
=
this
.
configObj
.
hoverEffect
||
'scale'
;
this
.
tooltipContent
=
this
.
configObj
.
tooltipContent
||
''
;
this
.
enableKeyboard
=
this
.
configObj
.
enableKeyboard
!==
undefined
?
this
.
configObj
.
enableKeyboard
:
true
;
this
.
enableFocus
=
this
.
configObj
.
enableFocus
!==
undefined
?
this
.
configObj
.
enableFocus
:
true
;
// Layout configuration
this
.
width
=
this
.
configObj
.
width
||
300
;
...
...
@@ -196,6 +262,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
maxHeight
=
this
.
configObj
.
maxHeight
||
400
;
this
.
aspectRatio
=
this
.
configObj
.
aspectRatio
||
'auto'
;
this
.
responsive
=
this
.
configObj
.
responsive
!==
undefined
?
this
.
configObj
.
responsive
:
true
;
this
.
widthUnit
=
this
.
configObj
.
widthUnit
||
'px'
;
this
.
heightUnit
=
this
.
configObj
.
heightUnit
||
'px'
;
this
.
fullWidth
=
this
.
configObj
.
fullWidth
!==
undefined
?
this
.
configObj
.
fullWidth
:
false
;
this
.
fullHeight
=
this
.
configObj
.
fullHeight
!==
undefined
?
this
.
configObj
.
fullHeight
:
false
;
// Data configuration
this
.
dataSource
=
this
.
configObj
.
dataSource
||
'static'
;
...
...
@@ -204,6 +274,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
cacheEnabled
=
this
.
configObj
.
cacheEnabled
!==
undefined
?
this
.
configObj
.
cacheEnabled
:
false
;
this
.
cacheDuration
=
this
.
configObj
.
cacheDuration
||
300
;
this
.
dataTransform
=
this
.
configObj
.
dataTransform
||
''
;
this
.
dataValidation
=
this
.
configObj
.
dataValidation
||
'none'
;
this
.
errorMessage
=
this
.
configObj
.
errorMessage
||
'Failed to load data'
;
this
.
showDataCount
=
this
.
configObj
.
showDataCount
!==
undefined
?
this
.
configObj
.
showDataCount
:
false
;
this
.
showLastUpdated
=
this
.
configObj
.
showLastUpdated
!==
undefined
?
this
.
configObj
.
showLastUpdated
:
false
;
// Filter configuration
this
.
enableFilter
=
this
.
configObj
.
enableFilter
||
false
;
...
...
@@ -219,6 +293,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
conditionValue
=
this
.
configObj
.
conditionValue
||
''
;
this
.
trueColor
=
this
.
configObj
.
trueColor
||
'#10B981'
;
this
.
falseColor
=
this
.
configObj
.
falseColor
||
'#EF4444'
;
this
.
trueIcon
=
this
.
configObj
.
trueIcon
||
'check-circle'
;
this
.
falseIcon
=
this
.
configObj
.
falseIcon
||
'x-circle'
;
this
.
showConditionalIcon
=
this
.
configObj
.
showConditionalIcon
!==
undefined
?
this
.
configObj
.
showConditionalIcon
:
false
;
this
.
animateConditionalChange
=
this
.
configObj
.
animateConditionalChange
!==
undefined
?
this
.
configObj
.
animateConditionalChange
:
true
;
// Security configuration
this
.
requireAuth
=
this
.
configObj
.
requireAuth
!==
undefined
?
this
.
configObj
.
requireAuth
:
false
;
...
...
@@ -226,6 +304,10 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
this
.
dataEncryption
=
this
.
configObj
.
dataEncryption
!==
undefined
?
this
.
configObj
.
dataEncryption
:
false
;
this
.
auditLog
=
this
.
configObj
.
auditLog
!==
undefined
?
this
.
configObj
.
auditLog
:
false
;
this
.
rateLimit
=
this
.
configObj
.
rateLimit
||
0
;
this
.
permissionLevel
=
this
.
configObj
.
permissionLevel
||
'read'
;
this
.
sessionTimeout
=
this
.
configObj
.
sessionTimeout
||
30
;
this
.
requireHttps
=
this
.
configObj
.
requireHttps
!==
undefined
?
this
.
configObj
.
requireHttps
:
false
;
this
.
enableCors
=
this
.
configObj
.
enableCors
!==
undefined
?
this
.
configObj
.
enableCors
:
true
;
// Handle gradient background for hex colors
if
(
this
.
backgroundColor
.
startsWith
(
'#'
))
{
...
...
@@ -236,6 +318,13 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
}
override
onDataUpdate
(
data
:
any
[]):
void
{
// Check if using static values
if
(
this
.
staticValue
)
{
this
.
value
=
this
.
staticValue
;
this
.
label
=
this
.
staticLabel
||
this
.
label
;
return
;
}
// Transform data if transform function is provided
let
transformedData
=
this
.
transformData
(
data
);
...
...
@@ -370,7 +459,7 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
}
}
onReset
():
void
{
o
verride
o
nReset
():
void
{
// Reset to default values
this
.
title
=
'KPI (Default)'
;
this
.
value
=
'123,456'
;
...
...
@@ -448,8 +537,7 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
// Helper method to get computed styles for dynamic styling
getWidgetStyles
():
{
[
key
:
string
]:
string
}
{
return
{
'background'
:
this
.
backgroundColor
,
const
styles
:
{
[
key
:
string
]:
string
}
=
{
'color'
:
this
.
textColor
,
'border-color'
:
this
.
borderColor
,
'border-radius'
:
`
${
this
.
borderRadius
}
px`
,
...
...
@@ -460,6 +548,32 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
'font-weight'
:
this
.
fontWeight
,
'font-family'
:
this
.
fontFamily
};
// Handle different background types
if
(
this
.
backgroundType
===
'gradient'
)
{
const
gradientType
=
this
.
gradientType
===
'radial'
?
'radial-gradient'
:
'linear-gradient'
;
const
direction
=
this
.
gradientType
===
'radial'
?
'circle'
:
this
.
gradientDirection
;
styles
[
'background'
]
=
`
${
gradientType
}
(
${
direction
}
,
${
this
.
gradientStartColor
}
,
${
this
.
gradientEndColor
}
)`
;
}
else
if
(
this
.
backgroundType
===
'image'
)
{
styles
[
'background-image'
]
=
`url(
${
this
.
backgroundImage
}
)`
;
styles
[
'background-size'
]
=
this
.
backgroundSize
;
styles
[
'background-position'
]
=
'center'
;
styles
[
'background-repeat'
]
=
'no-repeat'
;
}
else
{
styles
[
'background'
]
=
this
.
backgroundColor
;
}
// Add text shadow
if
(
this
.
textShadow
!==
'none'
)
{
const
shadowMap
:
{
[
key
:
string
]:
string
}
=
{
'small'
:
'0 1px 2px rgba(0,0,0,0.1)'
,
'medium'
:
'0 2px 4px rgba(0,0,0,0.2)'
,
'large'
:
'0 4px 8px rgba(0,0,0,0.3)'
};
styles
[
'text-shadow'
]
=
shadowMap
[
this
.
textShadow
]
||
'none'
;
}
return
styles
;
}
// Helper method to combine all styles
...
...
@@ -495,34 +609,73 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
'slide'
:
'slideInUp'
,
'bounce'
:
'bounceIn'
,
'pulse'
:
'pulse'
,
'flip'
:
'flipInX'
,
'zoom'
:
'zoomIn'
,
'rotate'
:
'rotateIn'
,
'shake'
:
'shake'
,
'none'
:
'none'
};
return
{
const
styles
:
{
[
key
:
string
]:
string
}
=
{
'animation'
:
`
${
animationMap
[
this
.
animationType
]}
${
this
.
animationDuration
}
ms
${
this
.
animationEasing
}
`
,
'animation-delay'
:
`
${
this
.
animationDelay
}
ms`
,
'animation-fill-mode'
:
'both'
};
// Add animation direction and iteration
if
(
this
.
animationDirection
!==
'normal'
)
{
styles
[
'animation-direction'
]
=
this
.
animationDirection
;
}
if
(
this
.
animationIteration
!==
'1'
)
{
styles
[
'animation-iteration-count'
]
=
this
.
animationIteration
;
}
return
styles
;
}
// Helper method to get layout styles
getLayoutStyles
():
{
[
key
:
string
]:
string
}
{
const
styles
:
{
[
key
:
string
]:
string
}
=
{
'width'
:
`
${
this
.
width
}
px`
,
'height'
:
`
${
this
.
height
}
px`
,
'min-width'
:
`
${
this
.
minWidth
}
px`
,
'min-height'
:
`
${
this
.
minHeight
}
px`
,
'max-width'
:
`
${
this
.
maxWidth
}
px`
,
'max-height'
:
`
${
this
.
maxHeight
}
px`
};
const
styles
:
{
[
key
:
string
]:
string
}
=
{};
// Handle width
if
(
this
.
fullWidth
)
{
styles
[
'width'
]
=
'100%'
;
}
else
{
styles
[
'width'
]
=
`
${
this
.
width
}${
this
.
widthUnit
}
`
;
}
// Handle height
if
(
this
.
fullHeight
)
{
styles
[
'height'
]
=
'100%'
;
}
else
{
styles
[
'height'
]
=
`
${
this
.
height
}${
this
.
heightUnit
}
`
;
}
// Handle min/max dimensions
if
(
this
.
minWidth
>
0
)
{
styles
[
'min-width'
]
=
`
${
this
.
minWidth
}
px`
;
}
if
(
this
.
minHeight
>
0
)
{
styles
[
'min-height'
]
=
`
${
this
.
minHeight
}
px`
;
}
if
(
this
.
maxWidth
>
0
)
{
styles
[
'max-width'
]
=
`
${
this
.
maxWidth
}
px`
;
}
if
(
this
.
maxHeight
>
0
)
{
styles
[
'max-height'
]
=
`
${
this
.
maxHeight
}
px`
;
}
// Handle aspect ratio
if
(
this
.
aspectRatio
!==
'auto'
)
{
const
[
width
,
height
]
=
this
.
aspectRatio
.
split
(
':'
);
const
ratio
=
parseFloat
(
height
)
/
parseFloat
(
width
);
styles
[
'aspect-ratio'
]
=
this
.
aspectRatio
;
}
// Handle responsive
if
(
this
.
responsive
)
{
styles
[
'box-sizing'
]
=
'border-box'
;
}
return
styles
;
}
...
...
@@ -546,6 +699,21 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
classes
.
push
(
'responsive'
);
}
// Add hover effect classes
if
(
this
.
hoverEffect
!==
'none'
)
{
classes
.
push
(
`hover-
${
this
.
hoverEffect
}
`
);
}
// Add keyboard navigation classes
if
(
this
.
enableKeyboard
)
{
classes
.
push
(
'keyboard-enabled'
);
}
// Add focus indicator classes
if
(
this
.
enableFocus
)
{
classes
.
push
(
'focus-enabled'
);
}
return
classes
.
join
(
' '
);
}
...
...
@@ -643,16 +811,37 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
// Helper method to get data source info
getDataSourceInfo
():
string
{
let
info
=
''
;
switch
(
this
.
dataSource
)
{
case
'api'
:
return
`API:
${
this
.
apiEndpoint
}
`
;
info
=
`API:
${
this
.
apiEndpoint
}
`
;
break
;
case
'websocket'
:
return
'WebSocket Connection'
;
info
=
'WebSocket Connection'
;
break
;
case
'file'
:
return
'File Upload'
;
info
=
'File Upload'
;
break
;
default
:
return
'Static Data'
;
info
=
'Static Data'
;
}
// Add validation info
if
(
this
.
dataValidation
!==
'none'
)
{
info
+=
` (
${
this
.
dataValidation
}
)`
;
}
// Add data count if enabled
if
(
this
.
showDataCount
&&
this
.
originalData
)
{
info
+=
` -
${
this
.
originalData
.
length
}
records`
;
}
// Add last updated if enabled
if
(
this
.
showLastUpdated
)
{
info
+=
` - Updated:
${
new
Date
().
toLocaleTimeString
()}
`
;
}
return
info
;
}
// Export data functionality
...
...
@@ -793,6 +982,27 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
}
else
{
this
.
valueColor
=
this
.
falseColor
;
}
// Apply conditional icons if enabled
if
(
this
.
showConditionalIcon
)
{
if
(
conditionMet
)
{
this
.
icon
=
this
.
trueIcon
;
}
else
{
this
.
icon
=
this
.
falseIcon
;
}
}
// Apply conditional animation if enabled
if
(
this
.
animateConditionalChange
)
{
// Add a temporary class for animation
const
element
=
document
.
querySelector
(
`[data-widget-id="
${
this
.
widgetId
}
"]`
);
if
(
element
)
{
element
.
classList
.
add
(
'conditional-change'
);
setTimeout
(()
=>
{
element
.
classList
.
remove
(
'conditional-change'
);
},
500
);
}
}
}
// Method to get shadow styles
...
...
@@ -808,12 +1018,29 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
// Method to get icon styles
getIconStyles
():
{
[
key
:
string
]:
string
}
{
return
{
const
styles
:
{
[
key
:
string
]:
string
}
=
{
'font-size'
:
`
${
this
.
iconSize
}
px`
,
'color'
:
this
.
iconColor
,
'width'
:
`
${
this
.
iconSize
}
px`
,
'height'
:
`
${
this
.
iconSize
}
px`
};
// Add icon background if enabled
if
(
this
.
showIconBackground
)
{
styles
[
'background-color'
]
=
this
.
iconBackground
;
styles
[
'border-radius'
]
=
`
${
this
.
iconBorderRadius
}
px`
;
styles
[
'padding'
]
=
'8px'
;
styles
[
'display'
]
=
'inline-flex'
;
styles
[
'align-items'
]
=
'center'
;
styles
[
'justify-content'
]
=
'center'
;
}
// Add pulse animation if enabled
if
(
this
.
iconPulse
)
{
styles
[
'animation'
]
=
'pulse 2s infinite'
;
}
return
styles
;
}
// Method to get value styles
...
...
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