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
6b8b4df6
Commit
6b8b4df6
authored
Aug 31, 2025
by
Ooh-Ao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
widget
parent
a25087d6
Hide whitespace changes
Inline
Side-by-side
Showing
49 changed files
with
1087 additions
and
1184 deletions
+1087
-1184
dashboard-management.component.ts
...PU/dashboard-management/dashboard-management.component.ts
+1
-0
dashboard-state.service.ts
src/app/DPU/services/dashboard-state.service.ts
+3
-0
area-chart-widget.component.html
...idgets/area-chart-widget/area-chart-widget.component.html
+13
-1
area-chart-widget.component.ts
.../widgets/area-chart-widget/area-chart-widget.component.ts
+16
-33
attendance-overview-widget.component.ts
src/app/DPU/widgets/attendance-overview-widget.component.ts
+37
-49
bar-chart-widget.component.html
.../widgets/bar-chart-widget/bar-chart-widget.component.html
+13
-1
bar-chart-widget.component.ts
...PU/widgets/bar-chart-widget/bar-chart-widget.component.ts
+20
-37
base-widget.component.ts
src/app/DPU/widgets/base-widget.component.ts
+68
-0
chart-widget.component.html
src/app/DPU/widgets/chart-widget/chart-widget.component.html
+15
-2
chart-widget.component.ts
src/app/DPU/widgets/chart-widget/chart-widget.component.ts
+53
-79
combo-chart-widget.component.html
...gets/combo-chart-widget/combo-chart-widget.component.html
+20
-2
combo-chart-widget.component.ts
...idgets/combo-chart-widget/combo-chart-widget.component.ts
+29
-60
company-info-widget.component.ts
src/app/DPU/widgets/company-info-widget.component.ts
+35
-49
company-info-widget.component.html
...ts/company-info-widget/company-info-widget.component.html
+19
-3
company-info-widget.component.ts
...gets/company-info-widget/company-info-widget.component.ts
+18
-46
doughnut-chart-widget.component.html
...oughnut-chart-widget/doughnut-chart-widget.component.html
+13
-1
doughnut-chart-widget.component.ts
.../doughnut-chart-widget/doughnut-chart-widget.component.ts
+20
-29
employee-directory-widget.component.ts
src/app/DPU/widgets/employee-directory-widget.component.ts
+36
-48
filled-map-widget.component.html
...idgets/filled-map-widget/filled-map-widget.component.html
+13
-1
filled-map-widget.component.ts
.../widgets/filled-map-widget/filled-map-widget.component.ts
+25
-70
funnel-chart-widget.component.html
...ts/funnel-chart-widget/funnel-chart-widget.component.html
+13
-1
funnel-chart-widget.component.ts
...gets/funnel-chart-widget/funnel-chart-widget.component.ts
+14
-22
gauge-chart-widget.component.html
...gets/gauge-chart-widget/gauge-chart-widget.component.html
+13
-1
gauge-chart-widget.component.ts
...idgets/gauge-chart-widget/gauge-chart-widget.component.ts
+24
-30
headcount-widget.component.ts
src/app/DPU/widgets/headcount-widget.component.ts
+44
-56
matrix-widget.component.html
...pp/DPU/widgets/matrix-widget/matrix-widget.component.html
+37
-16
matrix-widget.component.ts
src/app/DPU/widgets/matrix-widget/matrix-widget.component.ts
+23
-48
multi-row-card-widget.component.html
...ulti-row-card-widget/multi-row-card-widget.component.html
+22
-3
multi-row-card-widget.component.ts
.../multi-row-card-widget/multi-row-card-widget.component.ts
+21
-44
payroll-summary-widget.component.ts
src/app/DPU/widgets/payroll-summary-widget.component.ts
+34
-48
payroll-widget.component.html
.../DPU/widgets/payroll-widget/payroll-widget.component.html
+19
-3
payroll-widget.component.ts
...pp/DPU/widgets/payroll-widget/payroll-widget.component.ts
+18
-46
pie-chart-widget.component.html
.../widgets/pie-chart-widget/pie-chart-widget.component.html
+13
-1
pie-chart-widget.component.ts
...PU/widgets/pie-chart-widget/pie-chart-widget.component.ts
+20
-29
scatter-bubble-chart-widget.component.html
...e-chart-widget/scatter-bubble-chart-widget.component.html
+13
-1
scatter-bubble-chart-widget.component.ts
...ble-chart-widget/scatter-bubble-chart-widget.component.ts
+29
-67
simple-kpi-widget.component.html
...idgets/simple-kpi-widget/simple-kpi-widget.component.html
+32
-16
simple-kpi-widget.component.ts
.../widgets/simple-kpi-widget/simple-kpi-widget.component.ts
+18
-26
simple-table-widget.component.html
...ts/simple-table-widget/simple-table-widget.component.html
+37
-16
simple-table-widget.component.ts
...gets/simple-table-widget/simple-table-widget.component.ts
+14
-24
slicer-widget.component.html
...pp/DPU/widgets/slicer-widget/slicer-widget.component.html
+15
-1
slicer-widget.component.ts
src/app/DPU/widgets/slicer-widget/slicer-widget.component.ts
+17
-37
time-tracking-widget.component.html
.../time-tracking-widget/time-tracking-widget.component.html
+19
-3
time-tracking-widget.component.ts
...ts/time-tracking-widget/time-tracking-widget.component.ts
+20
-40
treemap-widget.component.html
.../DPU/widgets/treemap-widget/treemap-widget.component.html
+13
-1
treemap-widget.component.ts
...pp/DPU/widgets/treemap-widget/treemap-widget.component.ts
+25
-36
waterfall-chart-widget.component.html
...erfall-chart-widget/waterfall-chart-widget.component.html
+13
-1
waterfall-chart-widget.component.ts
...aterfall-chart-widget/waterfall-chart-widget.component.ts
+23
-56
ข้อเสนอแนะเพื่อการปรับปรุง.txt
ข้อเสนอแนะเพื่อการปรับปรุง.txt
+17
-0
No files found.
src/app/DPU/dashboard-management/dashboard-management.component.ts
View file @
6b8b4df6
...
...
@@ -732,6 +732,7 @@ export class DashboardManagementComponent implements OnInit {
}
onDatasetSelected
(
datasetId
:
string
):
void
{
console
.
log
(
'Dataset selected:'
,
datasetId
);
// Added for debugging/clarity
if
(
this
.
dashboardData
)
{
this
.
dashboardData
.
datasetId
=
datasetId
;
this
.
dashboardStateService
.
selectDataset
(
datasetId
);
...
...
src/app/DPU/services/dashboard-state.service.ts
View file @
6b8b4df6
...
...
@@ -26,10 +26,13 @@ export class DashboardStateService {
if
(
dataset
&&
dataset
.
url
)
{
return
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
pipe
(
map
(
data
=>
{
console
.
log
(
'Fetched data for dataset:'
,
data
);
// Log fetched data
if
(
data
&&
data
.
length
>
0
)
{
const
columns
=
Object
.
keys
(
data
[
0
]);
console
.
log
(
'Derived columns:'
,
columns
);
// Log derived columns
return
{
data
,
columns
};
}
else
{
console
.
log
(
'Fetched data is empty or invalid.'
);
// Log empty data
return
{
data
:
[],
columns
:
[]
};
}
})
...
...
src/app/DPU/widgets/area-chart-widget/area-chart-widget.component.html
View file @
6b8b4df6
<ejs-chart
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-chart
*
ngIf=
"!isLoading && !hasError"
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<e-series-collection>
<e-series
[
dataSource
]="
chartData
"
type=
"Area"
xName=
"x"
yName=
"y"
name=
"Sales"
></e-series>
</e-series-collection>
...
...
src/app/DPU/widgets/area-chart-widget/area-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
ChartModule
,
AreaSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
}
from
'@syncfusion/ej2-angular-charts'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-area-chart-widget'
,
...
...
@@ -10,47 +11,29 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
providers
:
[
AreaSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
],
templateUrl
:
'./area-chart-widget.component.html'
,
})
export
class
AreaChartWidgetComponent
implements
OnInit
{
@
Input
()
config
:
any
;
export
class
AreaChartWidgetComponent
extends
BaseWidgetComponent
{
public
chartData
:
Object
[];
public
title
:
string
=
'Area Chart'
;
public
primaryXAxis
:
Object
;
public
primaryYAxis
:
Object
;
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
'Month'
};
this
.
primaryYAxis
=
{
title
:
'Sales'
};
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
xField
&&
this
.
config
.
yField
)
{
this
.
updateChart
(
selectedDataset
.
data
);
}
else
{
this
.
resetChart
();
}
});
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateChart
(
data
:
any
[]):
void
{
onDataUpdate
(
data
:
any
[]):
void
{
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
if
(
this
.
config
.
xAxisTitle
)
{
this
.
primaryXAxis
=
{
...
this
.
primaryXAxis
,
title
:
this
.
config
.
xAxisTitle
,
valueType
:
'Category'
};
}
if
(
this
.
config
.
yAxisTitle
)
{
this
.
primaryYAxis
=
{
...
this
.
primaryYAxis
,
title
:
this
.
config
.
yAxisTitle
};
}
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
this
.
config
.
xAxisTitle
||
''
};
this
.
primaryYAxis
=
{
title
:
this
.
config
.
yAxisTitle
||
''
};
}
resetChart
():
void
{
onReset
():
void
{
this
.
title
=
'Area Chart (Default)'
;
this
.
chartData
=
[
{
month
:
'Jan'
,
sales
:
35
},
{
month
:
'Feb'
,
sales
:
28
},
{
month
:
'Mar'
,
sales
:
34
},
{
month
:
'Apr'
,
sales
:
32
},
{
month
:
'May'
,
sales
:
40
},
{
month
:
'Jun'
,
sales
:
30
},
{
x
:
'Jan'
,
y
:
35
},
{
x
:
'Feb'
,
y
:
28
},
{
x
:
'Mar'
,
y
:
34
},
{
x
:
'Apr'
,
y
:
32
},
{
x
:
'May'
,
y
:
40
},
{
x
:
'Jun'
,
y
:
30
},
];
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
'Month'
};
this
.
primaryYAxis
=
{
title
:
'Sales'
};
}
}
src/app/DPU/widgets/attendance-overview-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DatasetService
}
from
'../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'./base-widget.component'
;
@
Component
({
selector
:
'app-attendance-overview-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
HttpClientModule
],
providers
:
[
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
],
template
:
`
<div class="p-4 h-full bg-yellow-50">
<h3 class="font-bold text-yellow-800">{{ title }}</h3>
<p class="text-sm text-yellow-600">Present: {{ present }}</p>
<p class="text-sm text-yellow-600">On Leave: {{ onLeave }}</p>
<p class="text-sm text-yellow-600">Absent: {{ absent }}</p>
<!-- Loading State -->
<div *ngIf="isLoading" class="flex justify-center items-center h-full">
<div class="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-yellow-500"></div>
</div>
<!-- Error State -->
<div *ngIf="hasError" class="flex flex-col justify-center items-center h-full text-red-500">
<i class="bi bi-exclamation-triangle-fill text-2xl"></i>
<p class="mt-1 text-sm">{{ errorMessage }}</p>
</div>
<!-- Content -->
<div *ngIf="!isLoading && !hasError">
<p class="text-sm text-yellow-600">Present: {{ present }}</p>
<p class="text-sm text-yellow-600">On Leave: {{ onLeave }}</p>
<p class="text-sm text-yellow-600">Absent: {{ absent }}</p>
</div>
</div>
`
})
export
class
AttendanceOverviewWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
title
:
string
=
'Today
\'
s Attendance'
;
present
:
number
=
0
;
onLeave
:
number
=
0
;
absent
:
number
=
0
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
export
class
AttendanceOverviewWidgetComponent
extends
BaseWidgetComponent
{
public
present
:
number
=
0
;
public
onLeave
:
number
=
0
;
public
absent
:
number
=
0
;
ngOnInit
():
void
{
if
(
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
{
this
.
resetData
();
}
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
resetData
();
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
present
=
firstItem
[
this
.
config
.
presentField
]
||
0
;
this
.
onLeave
=
firstItem
[
this
.
config
.
onLeaveField
]
||
0
;
this
.
absent
=
firstItem
[
this
.
config
.
absentField
]
||
0
;
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
onDataUpdate
(
data
:
any
[]):
void
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
present
=
firstItem
[
this
.
config
.
presentField
]
||
0
;
this
.
onLeave
=
firstItem
[
this
.
config
.
onLeaveField
]
||
0
;
this
.
absent
=
firstItem
[
this
.
config
.
absentField
]
||
0
;
}
}
resetData
():
void
{
onReset
():
void
{
this
.
title
=
'Attendance (Default)'
;
this
.
present
=
150
;
this
.
onLeave
=
10
;
this
.
absent
=
5
;
...
...
src/app/DPU/widgets/bar-chart-widget/bar-chart-widget.component.html
View file @
6b8b4df6
<ejs-chart
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-chart
*
ngIf=
"!isLoading && !hasError"
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<e-series-collection>
<e-series
[
dataSource
]="
chartData
"
[
type
]="
type
"
xName=
"x"
yName=
"y"
name=
"Data"
></e-series>
</e-series-collection>
...
...
src/app/DPU/widgets/bar-chart-widget/bar-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
ChartModule
,
BarSeriesService
,
ColumnSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
}
from
'@syncfusion/ej2-angular-charts'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-bar-chart-widget'
,
...
...
@@ -10,51 +11,33 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
providers
:
[
BarSeriesService
,
ColumnSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
],
templateUrl
:
'./bar-chart-widget.component.html'
,
})
export
class
BarChartWidgetComponent
implements
OnInit
{
@
Input
()
config
:
any
;
@
Input
()
chartData
:
Object
[];
@
Input
()
title
:
string
=
'Bar Chart'
;
@
Input
()
type
:
'Bar'
|
'Column'
=
'Column'
;
export
class
BarChartWidgetComponent
extends
BaseWidgetComponent
{
public
chartData
:
Object
[];
public
type
:
'Bar'
|
'Column'
=
'Column'
;
public
primaryXAxis
:
Object
;
public
primaryYAxis
:
Object
;
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
'Country'
};
this
.
primaryYAxis
=
{
title
:
'Sales'
};
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
xField
&&
this
.
config
.
yField
)
{
this
.
updateChart
(
selectedDataset
.
data
);
}
else
{
this
.
resetChart
();
}
});
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateChart
(
data
:
any
[]):
void
{
onDataUpdate
(
data
:
any
[]):
void
{
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
if
(
this
.
config
.
xAxisTitle
)
{
this
.
primaryXAxis
=
{
...
this
.
primaryXAxis
,
title
:
this
.
config
.
xAxisTitle
};
}
if
(
this
.
config
.
yAxisTitle
)
{
this
.
primaryYAxis
=
{
...
this
.
primaryYAxis
,
title
:
this
.
config
.
yAxisTitle
};
}
if
(
this
.
config
.
type
)
{
this
.
type
=
this
.
config
.
type
;
}
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
this
.
config
.
xAxisTitle
||
''
};
this
.
primaryYAxis
=
{
title
:
this
.
config
.
yAxisTitle
||
''
};
this
.
type
=
this
.
config
.
type
||
'Column'
;
}
resetChart
():
void
{
onReset
():
void
{
this
.
title
=
'Bar Chart (Default)'
;
this
.
chartData
=
[
{
country
:
'USA'
,
sales
:
20
},
{
country
:
'China'
,
sales
:
25
},
{
country
:
'Japan'
,
sales
:
18
},
{
country
:
'Germany'
,
sales
:
15
},
{
x
:
'USA'
,
y
:
20
},
{
x
:
'China'
,
y
:
25
},
{
x
:
'Japan'
,
y
:
18
},
{
x
:
'Germany'
,
y
:
15
},
];
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
'Country'
};
this
.
primaryYAxis
=
{
title
:
'Sales'
};
}
}
src/app/DPU/widgets/base-widget.component.ts
0 → 100644
View file @
6b8b4df6
import
{
Input
,
OnInit
,
OnDestroy
,
Directive
}
from
'@angular/core'
;
import
{
Subscription
}
from
'rxjs'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../services/dashboard-state.service'
;
@
Directive
()
// Use @Directive() for base classes without their own template
export
abstract
class
BaseWidgetComponent
implements
OnInit
,
OnDestroy
{
@
Input
()
config
:
any
;
public
title
:
string
;
public
isLoading
=
true
;
public
hasError
=
false
;
public
errorMessage
:
string
|
null
=
null
;
protected
subscription
:
Subscription
=
new
Subscription
();
constructor
(
protected
dashboardStateService
:
DashboardStateService
)
{}
ngOnInit
():
void
{
this
.
title
=
this
.
config
?.
title
||
'Widget'
;
const
datasetSub
=
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
({
next
:
(
selectedDataset
:
SelectedDataset
|
null
)
=>
{
this
.
isLoading
=
true
;
this
.
hasError
=
false
;
if
(
selectedDataset
&&
selectedDataset
.
data
)
{
try
{
this
.
onDataUpdate
(
selectedDataset
.
data
);
this
.
isLoading
=
false
;
}
catch
(
error
)
{
this
.
handleError
(
error
);
}
}
else
{
this
.
onReset
();
this
.
isLoading
=
false
;
}
},
error
:
(
err
)
=>
{
this
.
handleError
(
err
);
}
});
this
.
subscription
.
add
(
datasetSub
);
}
ngOnDestroy
():
void
{
this
.
subscription
.
unsubscribe
();
}
private
handleError
(
error
:
any
):
void
{
this
.
isLoading
=
false
;
this
.
hasError
=
true
;
this
.
errorMessage
=
'An error occurred while loading widget data.'
;
console
.
error
(
'Widget Error:'
,
error
);
this
.
onReset
();
}
/**
* Abstract method to be implemented by child components.
* This method is called when new data is available from the selected dataset.
* @param data The data from the selected dataset.
*/
abstract
onDataUpdate
(
data
:
any
[]):
void
;
/**
* Abstract method to be implemented by child components.
* This method is called when there is no dataset selected or data is not available.
*/
abstract
onReset
():
void
;
}
src/app/DPU/widgets/chart-widget/chart-widget.component.html
View file @
6b8b4df6
<div
class=
"bg-white p-6 rounded-lg shadow-md h-full flex flex-col"
>
<h3
class=
"text-lg font-semibold text-gray-500 mb-4"
>
Monthly Activity
</h3>
<div
#
chart
class=
"h-full w-full"
></div>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<div
*
ngIf=
"!isLoading && !hasError"
#
chart
class=
"h-full w-full"
></div>
</div>
src/app/DPU/widgets/chart-widget/chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
OnInit
,
ViewChild
,
ElementRef
,
AfterViewInit
,
Input
,
SimpleChanges
,
OnChanges
}
from
'@angular/core'
;
import
{
Component
,
ViewChild
,
ElementRef
,
AfterViewInit
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
MockDataService
}
from
'../../common/mock-data.service'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
}
from
'@angular/common/http'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
// Declare ApexCharts globally if it's loaded via a script tag
declare
var
ApexCharts
:
any
;
...
...
@@ -14,21 +12,56 @@ declare var ApexCharts: any;
imports
:
[
CommonModule
],
templateUrl
:
'./chart-widget.component.html'
,
})
export
class
ChartWidgetComponent
implements
OnInit
,
AfterViewInit
,
OnChanges
{
export
class
ChartWidgetComponent
extends
BaseWidgetComponent
implements
AfterViewInit
{
@
ViewChild
(
'chart'
)
chartElement
!
:
ElementRef
;
@
Input
()
config
:
any
;
chartOptions
:
any
;
private
chartInstance
:
any
;
// To store the ApexCharts instance
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngAfterViewInit
():
void
{
if
(
typeof
ApexCharts
!==
'undefined'
&&
this
.
chartElement
)
{
// Initial chart options are set in onReset
this
.
chartInstance
=
new
ApexCharts
(
this
.
chartElement
.
nativeElement
,
this
.
getChartOptions
([],
[]));
this
.
chartInstance
.
render
();
// The subscription in BaseWidget will trigger onDataUpdate or onReset, which will update the chart.
}
else
{
console
.
error
(
'ApexCharts is not loaded or chart element is not available.'
);
}
}
onDataUpdate
(
data
:
any
[]):
void
{
const
categories
=
data
.
map
(
item
=>
item
[
this
.
config
.
xField
]);
const
series
=
this
.
config
.
yFields
.
map
((
yField
:
any
)
=>
({
name
:
yField
.
name
||
yField
.
field
,
data
:
data
.
map
(
item
=>
item
[
yField
.
field
])
}));
if
(
this
.
chartInstance
)
{
this
.
chartInstance
.
updateOptions
(
this
.
getChartOptions
(
series
,
categories
));
}
}
ngOnInit
():
void
{
// Initial chart options, will be updated with fetched data
this
.
chartOptions
=
{
series
:
[],
onReset
():
void
{
this
.
title
=
'Chart (Default)'
;
const
defaultSeries
=
[{
name
:
'Sample Series'
,
data
:
[
10
,
41
,
35
,
51
,
49
,
62
,
69
,
91
,
148
]
}];
const
defaultCategories
=
[
'Jan'
,
'Feb'
,
'Mar'
,
'Apr'
,
'May'
,
'Jun'
,
'Jul'
,
'Aug'
,
'Sep'
];
if
(
this
.
chartInstance
)
{
this
.
chartInstance
.
updateOptions
(
this
.
getChartOptions
(
defaultSeries
,
defaultCategories
));
}
}
private
getChartOptions
(
series
:
any
[],
categories
:
any
[]):
any
{
return
{
series
:
series
,
chart
:
{
type
:
'bar'
,
type
:
this
.
config
?.
type
||
'bar'
,
height
:
250
,
toolbar
:
{
show
:
false
...
...
@@ -50,11 +83,11 @@ export class ChartWidgetComponent implements OnInit, AfterViewInit, OnChanges {
colors
:
[
'transparent'
]
},
xaxis
:
{
categories
:
[]
,
categories
:
categories
,
},
yaxis
:
{
title
:
{
text
:
'Number'
text
:
this
.
config
?.
yAxisTitle
||
'Number'
}
},
fill
:
{
...
...
@@ -66,69 +99,10 @@ export class ChartWidgetComponent implements OnInit, AfterViewInit, OnChanges {
return
val
+
" units"
}
}
},
title
:
{
text
:
this
.
title
}
};
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
// Reset chart if config or datasetId is removed
this
.
chartOptions
.
series
=
[];
this
.
chartOptions
.
xaxis
.
categories
=
[];
if
(
this
.
chartInstance
)
{
this
.
chartInstance
.
updateOptions
(
this
.
chartOptions
);
}
}
}
ngAfterViewInit
():
void
{
if
(
typeof
ApexCharts
!==
'undefined'
&&
this
.
chartElement
)
{
this
.
chartInstance
=
new
ApexCharts
(
this
.
chartElement
.
nativeElement
,
this
.
chartOptions
);
this
.
chartInstance
.
render
();
}
else
{
console
.
error
(
'ApexCharts is not loaded or chart element is not available.'
);
}
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
// Transform data for ApexCharts
// Assuming config.xField for categories and config.yFields for series
const
categories
=
data
.
map
(
item
=>
item
[
this
.
config
.
xField
]);
const
series
=
this
.
config
.
yFields
.
map
((
yField
:
any
)
=>
({
name
:
yField
.
name
||
yField
.
field
,
data
:
data
.
map
(
item
=>
item
[
yField
.
field
])
}));
const
newOptions
=
{
series
:
series
,
xaxis
:
{
categories
:
categories
,
},
title
:
{
text
:
this
.
config
.
title
||
'Chart'
},
yaxis
:
{
title
:
{
text
:
this
.
config
.
yAxisTitle
||
'Number'
}
}
};
if
(
this
.
chartInstance
)
{
this
.
chartInstance
.
updateOptions
(
newOptions
);
}
else
{
// If chartInstance is not yet created (e.g., first load), update chartOptions directly
this
.
chartOptions
=
{
...
this
.
chartOptions
,
...
newOptions
};
}
});
}
});
}
}
}
src/app/DPU/widgets/combo-chart-widget/combo-chart-widget.component.html
View file @
6b8b4df6
<ejs-chart
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-chart
*
ngIf=
"!isLoading && !hasError && config?.series"
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<e-series-collection>
<e-series
*
ngFor=
"let yField of config.yFields"
[
dataSource
]="
chartData
"
[
type
]="
yField
.
type
||
'
Column
'"
xName=
"x"
[
yName
]="
yField
.
field
"
[
name
]="
yField
.
name
"
></e-series>
<e-series
*
ngFor=
"let series of config.series"
[
dataSource
]="
chartData
"
[
type
]="
series
.
type
||
'
Column
'"
[
xName
]="
series
.
xName
"
[
yName
]="
series
.
yName
"
[
name
]="
series
.
name
"
>
</e-series>
</e-series-collection>
</ejs-chart>
src/app/DPU/widgets/combo-chart-widget/combo-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
ChartModule
,
ColumnSeriesService
,
LineSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
}
from
'@syncfusion/ej2-angular-charts'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-combo-chart-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
ChartModule
,
HttpClientModule
],
providers
:
[
ColumnSeriesService
,
LineSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
,
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
,
ChartModule
],
providers
:
[
ColumnSeriesService
,
LineSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
],
templateUrl
:
'./combo-chart-widget.component.html'
,
})
export
class
ComboChartWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
@
Input
()
chartData
:
Object
[];
@
Input
()
title
:
string
=
'Combo Chart'
;
export
class
ComboChartWidgetComponent
extends
BaseWidgetComponent
{
public
chartData
:
Object
[];
public
primaryXAxis
:
Object
;
public
primaryYAxis
:
Object
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
'Month'
};
this
.
primaryYAxis
=
{
title
:
'Value'
};
if
(
!
this
.
config
||
!
this
.
config
.
datasetId
)
{
this
.
chartData
=
[
{
month
:
'Jan'
,
sales
:
35
,
profit
:
10
},
{
month
:
'Feb'
,
sales
:
28
,
profit
:
12
},
{
month
:
'Mar'
,
sales
:
34
,
profit
:
8
},
{
month
:
'Apr'
,
sales
:
32
,
profit
:
15
},
{
month
:
'May'
,
sales
:
40
,
profit
:
11
},
{
month
:
'Jun'
,
sales
:
30
,
profit
:
13
},
];
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
chartData
=
[
{
month
:
'Jan'
,
sales
:
35
,
profit
:
10
},
{
month
:
'Feb'
,
sales
:
28
,
profit
:
12
},
{
month
:
'Mar'
,
sales
:
34
,
profit
:
8
},
{
month
:
'Apr'
,
sales
:
32
,
profit
:
15
},
{
month
:
'May'
,
sales
:
40
,
profit
:
11
},
{
month
:
'Jun'
,
sales
:
30
,
profit
:
13
},
];
}
onDataUpdate
(
data
:
any
[]):
void
{
this
.
chartData
=
data
;
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
this
.
config
.
xAxisTitle
||
''
};
this
.
primaryYAxis
=
{
title
:
this
.
config
.
yAxisTitle
||
''
};
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
// Transform data for combo chart
// Assuming config.xField and config.yFields (array of y-fields)
this
.
chartData
=
data
.
map
(
item
=>
{
const
transformedItem
:
any
=
{
x
:
item
[
this
.
config
.
xField
]
};
this
.
config
.
yFields
.
forEach
((
field
:
string
)
=>
{
transformedItem
[
field
]
=
item
[
field
];
});
return
transformedItem
;
});
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
if
(
this
.
config
.
xAxisTitle
)
{
this
.
primaryXAxis
=
{
...
this
.
primaryXAxis
,
title
:
this
.
config
.
xAxisTitle
};
}
if
(
this
.
config
.
yAxisTitle
)
{
this
.
primaryYAxis
=
{
...
this
.
primaryYAxis
,
title
:
this
.
config
.
yAxisTitle
};
}
});
}
});
onReset
():
void
{
this
.
title
=
'Combo Chart (Default)'
;
this
.
chartData
=
[
{
x
:
'Jan'
,
y1
:
35
,
y2
:
10
},
{
x
:
'Feb'
,
y1
:
28
,
y2
:
12
},
{
x
:
'Mar'
,
y1
:
34
,
y2
:
8
},
{
x
:
'Apr'
,
y1
:
32
,
y2
:
15
},
{
x
:
'May'
,
y1
:
40
,
y2
:
11
},
{
x
:
'Jun'
,
y1
:
30
,
y2
:
13
},
];
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
'Month'
};
this
.
primaryYAxis
=
{
title
:
'Value'
};
// Provide a default series config for the reset state
if
(
!
this
.
config
)
{
this
.
config
=
{};
}
this
.
config
.
series
=
[
{
type
:
'Column'
,
xName
:
'x'
,
yName
:
'y1'
,
name
:
'Sales'
},
{
type
:
'Line'
,
xName
:
'x'
,
yName
:
'y2'
,
name
:
'Profit'
}
];
}
}
src/app/DPU/widgets/company-info-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DatasetService
}
from
'../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'./base-widget.component'
;
@
Component
({
selector
:
'app-company-info-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
HttpClientModule
],
providers
:
[
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
],
template
:
`
<div class="p-4 h-full bg-blue-50">
<h3 class="font-bold text-blue-800">{{ title }}</h3>
<p class="text-sm text-blue-600">Company Name: {{ companyName }}</p>
<p class="text-sm text-blue-600">Address: {{ address }}</p>
<p class="text-sm text-blue-600">Contact: {{ contact }}</p>
<!-- Loading State -->
<div *ngIf="isLoading" class="flex justify-center items-center h-full">
<div class="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500"></div>
</div>
<!-- Error State -->
<div *ngIf="hasError" class="flex flex-col justify-center items-center h-full text-red-500">
<i class="bi bi-exclamation-triangle-fill text-2xl"></i>
<p class="mt-1 text-sm">{{ errorMessage }}</p>
</div>
<!-- Content -->
<div *ngIf="!isLoading && !hasError">
<p class="text-sm text-blue-600">Company Name: {{ companyName }}</p>
<p class="text-sm text-blue-600">Address: {{ address }}</p>
<p class="text-sm text-blue-600">Contact: {{ contact }}</p>
</div>
</div>
`
})
export
class
CompanyInfoWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
title
:
string
=
'Company Information'
;
companyName
:
string
=
''
;
address
:
string
=
''
;
contact
:
string
=
''
;
export
class
CompanyInfoWidgetComponent
extends
BaseWidgetComponent
{
public
companyName
:
string
=
''
;
public
address
:
string
=
''
;
public
contact
:
string
=
''
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
{
this
.
resetData
();
}
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
resetData
();
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
companyName
=
firstItem
[
this
.
config
.
companyNameField
]
||
''
;
this
.
address
=
firstItem
[
this
.
config
.
addressField
]
||
''
;
this
.
contact
=
firstItem
[
this
.
config
.
contactField
]
||
''
;
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
onDataUpdate
(
data
:
any
[]):
void
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
companyName
=
firstItem
[
this
.
config
.
companyNameField
]
||
''
;
this
.
address
=
firstItem
[
this
.
config
.
addressField
]
||
''
;
this
.
contact
=
firstItem
[
this
.
config
.
contactField
]
||
''
;
}
}
resetData
():
void
{
onReset
():
void
{
this
.
title
=
'Company Info (Default)'
;
this
.
companyName
=
'My Company Inc.'
;
this
.
address
=
'123 Main St, Anytown USA'
;
this
.
contact
=
'info@mycompany.com'
;
...
...
src/app/DPU/widgets/company-info-widget/company-info-widget.component.html
View file @
6b8b4df6
...
...
@@ -4,8 +4,24 @@
<h5
class=
"card-title"
>
{{ title }}
</h5>
</div>
<div
class=
"card-body"
>
<p>
Company Name: {{ companyName }}
</p>
<p>
Address: {{ address }}
</p>
<p>
Contact: {{ contact }}
</p>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Content -->
<div
*
ngIf=
"!isLoading && !hasError"
>
<p>
Company Name: {{ companyName }}
</p>
<p>
Address: {{ address }}
</p>
<p>
Contact: {{ contact }}
</p>
</div>
</div>
</div>
src/app/DPU/widgets/company-info-widget/company-info-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-company-info-subfolder-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
HttpClientModule
],
providers
:
[
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
],
templateUrl
:
'./company-info-widget.component.html'
,
styleUrls
:
[
'./company-info-widget.component.scss'
]
})
export
class
CompanyInfoSubfolderWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
title
:
string
=
'Company Information'
;
companyName
:
string
=
''
;
address
:
string
=
''
;
contact
:
string
=
''
;
export
class
CompanyInfoSubfolderWidgetComponent
extends
BaseWidgetComponent
{
public
companyName
:
string
=
''
;
public
address
:
string
=
''
;
public
contact
:
string
=
''
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
{
this
.
resetData
();
}
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
resetData
();
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
companyName
=
firstItem
[
this
.
config
.
companyNameField
]
||
''
;
this
.
address
=
firstItem
[
this
.
config
.
addressField
]
||
''
;
this
.
contact
=
firstItem
[
this
.
config
.
contactField
]
||
''
;
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
onDataUpdate
(
data
:
any
[]):
void
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
companyName
=
firstItem
[
this
.
config
.
companyNameField
]
||
''
;
this
.
address
=
firstItem
[
this
.
config
.
addressField
]
||
''
;
this
.
contact
=
firstItem
[
this
.
config
.
contactField
]
||
''
;
}
}
resetData
():
void
{
onReset
():
void
{
this
.
title
=
'Company Info (Default)'
;
this
.
companyName
=
'My Company Inc. (Subfolder)'
;
this
.
address
=
'456 Subfolder Ave, Anytown USA'
;
this
.
contact
=
'info@subfolder.com'
;
...
...
src/app/DPU/widgets/doughnut-chart-widget/doughnut-chart-widget.component.html
View file @
6b8b4df6
<ejs-accumulationchart
[
title
]="
title
"
[
legendSettings
]="
legendSettings
"
[
enableSmartLabels
]="
true
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-accumulationchart
*
ngIf=
"!isLoading && !hasError"
[
title
]="
title
"
[
legendSettings
]="
legendSettings
"
[
enableSmartLabels
]="
true
"
>
<e-accumulation-series-collection>
<e-accumulation-series
[
dataSource
]="
chartData
"
type=
"Doughnut"
xName=
"x"
yName=
"y"
[
dataLabel
]="{
visible:
true
,
name:
'
text
',
position:
'
Inside
'
}"
innerRadius=
"40%"
></e-accumulation-series>
</e-accumulation-series-collection>
...
...
src/app/DPU/widgets/doughnut-chart-widget/doughnut-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
AccumulationChartModule
,
AccumulationDataLabelService
,
AccumulationLegendService
,
AccumulationTooltipService
}
from
'@syncfusion/ej2-angular-charts'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-doughnut-chart-widget'
,
...
...
@@ -10,27 +11,18 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
providers
:
[
AccumulationDataLabelService
,
AccumulationLegendService
,
AccumulationTooltipService
],
templateUrl
:
'./doughnut-chart-widget.component.html'
,
})
export
class
DoughnutChartWidgetComponent
implements
OnInit
{
@
Input
()
chartData
:
Object
[];
@
Input
()
title
:
string
=
'Doughnut Chart'
;
@
Input
()
legendSettings
:
Object
;
@
Input
()
config
:
any
;
export
class
DoughnutChartWidgetComponent
extends
BaseWidgetComponent
{
public
chartData
:
Object
[];
public
legendSettings
:
Object
;
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
legendSettings
=
{
visible
:
true
};
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
xField
&&
this
.
config
.
yField
)
{
this
.
updateChart
(
selectedDataset
.
data
);
}
else
{
this
.
resetChart
();
}
});
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateChart
(
data
:
any
[]):
void
{
onDataUpdate
(
data
:
any
[]):
void
{
this
.
legendSettings
=
{
visible
:
true
};
let
transformedData
=
data
;
if
(
this
.
config
.
aggregation
===
'count'
)
{
const
counts
=
transformedData
.
reduce
((
acc
,
item
)
=>
{
const
key
=
item
[
this
.
config
.
xField
];
...
...
@@ -42,19 +34,18 @@ export class DoughnutChartWidgetComponent implements OnInit {
transformedData
=
transformedData
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
}
this
.
chartData
=
transformedData
;
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
}
resetChart
():
void
{
onReset
():
void
{
this
.
title
=
'Doughnut Chart (Default)'
;
this
.
legendSettings
=
{
visible
:
true
};
this
.
chartData
=
[
{
browser
:
'Chrome'
,
users
:
37
},
{
browser
:
'Firefox'
,
users
:
17
},
{
browser
:
'Internet Explorer'
,
users
:
19
},
{
browser
:
'Edge'
,
users
:
4
},
{
browser
:
'Safari'
,
users
:
11
},
{
browser
:
'Other'
,
users
:
12
},
{
x
:
'Chrome'
,
y
:
37
},
{
x
:
'Firefox'
,
y
:
17
},
{
x
:
'Internet Explorer'
,
y
:
19
},
{
x
:
'Edge'
,
y
:
4
},
{
x
:
'Safari'
,
y
:
11
},
{
x
:
'Other'
,
y
:
12
},
];
}
}
src/app/DPU/widgets/employee-directory-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DatasetService
}
from
'../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'./base-widget.component'
;
@
Component
({
selector
:
'app-employee-directory-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
HttpClientModule
],
providers
:
[
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
],
template
:
`
<div class="p-4 h-full bg-indigo-50">
<h3 class="font-bold text-indigo-800">{{ title }}</h3>
<div *ngIf="employees.length > 0">
<div *ngFor="let employee of employees" class="mb-2">
<p class="text-sm text-indigo-600">{{ employee.name }} - {{ employee.position }} ({{ employee.department }})</p>
<!-- Loading State -->
<div *ngIf="isLoading" class="flex justify-center items-center h-full">
<div class="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-indigo-500"></div>
</div>
<!-- Error State -->
<div *ngIf="hasError" class="flex flex-col justify-center items-center h-full text-red-500">
<i class="bi bi-exclamation-triangle-fill text-2xl"></i>
<p class="mt-1 text-sm">{{ errorMessage }}</p>
</div>
<!-- Content -->
<div *ngIf="!isLoading && !hasError">
<div *ngIf="employees.length > 0">
<div *ngFor="let employee of employees" class="mb-2">
<p class="text-sm text-indigo-600">{{ employee.name }} - {{ employee.position }} ({{ employee.department }})</p>
</div>
</div>
<p *ngIf="employees.length === 0" class="text-sm text-indigo-600">No employees found.</p>
</div>
<p *ngIf="employees.length === 0" class="text-sm text-indigo-600">No employees found.</p>
</div>
`
})
export
class
EmployeeDirectoryWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
title
:
string
=
'Employee Directory'
;
employees
:
any
[]
=
[];
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
{
this
.
resetData
();
}
}
export
class
EmployeeDirectoryWidgetComponent
extends
BaseWidgetComponent
{
public
employees
:
any
[]
=
[];
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
resetData
();
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
this
.
employees
=
data
.
map
(
item
=>
({
name
:
item
[
this
.
config
.
nameField
]
||
''
,
position
:
item
[
this
.
config
.
positionField
]
||
''
,
department
:
item
[
this
.
config
.
departmentField
]
||
''
,
}));
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
}
onDataUpdate
(
data
:
any
[]):
void
{
this
.
employees
=
data
.
map
(
item
=>
({
name
:
item
[
this
.
config
.
nameField
]
||
''
,
position
:
item
[
this
.
config
.
positionField
]
||
''
,
department
:
item
[
this
.
config
.
departmentField
]
||
''
,
}));
}
resetData
():
void
{
onReset
():
void
{
this
.
title
=
'Employee Directory (Default)'
;
this
.
employees
=
[
{
name
:
'John Doe'
,
position
:
'Software Engineer'
,
department
:
'IT'
},
{
name
:
'Jane Smith'
,
position
:
'Project Manager'
,
department
:
'Operations'
},
...
...
src/app/DPU/widgets/filled-map-widget/filled-map-widget.component.html
View file @
6b8b4df6
<ejs-maps
[
titleSettings
]="{
text:
title
}"
[
zoomSettings
]="
zoomSettings
"
[
layers
]="
layers
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-maps
*
ngIf=
"!isLoading && !hasError"
[
titleSettings
]="{
text:
title
}"
[
zoomSettings
]="
zoomSettings
"
[
layers
]="
layers
"
>
</ejs-maps>
src/app/DPU/widgets/filled-map-widget/filled-map-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
MapsModule
,
MarkerService
,
ZoomService
,
DataLabelService
,
LegendService
,
MapsTooltipService
}
from
'@syncfusion/ej2-angular-maps'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
}
from
'@angular/common/http'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-filled-map-widget'
,
...
...
@@ -12,90 +11,46 @@ import { HttpClient } from '@angular/common/http';
providers
:
[
MarkerService
,
ZoomService
,
DataLabelService
,
LegendService
,
MapsTooltipService
],
templateUrl
:
'./filled-map-widget.component.html'
,
})
export
class
FilledMapWidgetComponent
implements
OnInit
{
@
Input
()
config
:
any
;
@
Input
()
title
:
string
=
'Filled Map'
;
@
Input
()
mapData
:
Object
[];
export
class
FilledMapWidgetComponent
extends
BaseWidgetComponent
{
public
zoomSettings
:
Object
;
public
layers
:
Object
[];
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
this
.
zoomSettings
=
{
enable
:
true
};
if
(
!
this
.
config
||
!
this
.
config
.
datasetId
)
{
this
.
mapData
=
[
{
country
:
'United States'
,
value
:
80
},
{
country
:
'Canada'
,
value
:
60
},
{
country
:
'Mexico'
,
value
:
40
},
];
}
this
.
layers
=
[
{
shapeData
:
new
Object
({
data
:
'https://cdn.syncfusion.com/maps/map-data/world-map.json'
}),
shapeDataPath
:
'name'
,
shapePropertyPath
:
'name'
,
dataSource
:
this
.
mapData
,
tooltipSettings
:
{
visible
:
true
,
valuePath
:
'value'
},
shapeSettings
:
{
fill
:
'#E5EEF6'
,
colorValuePath
:
'value'
,
colorMapping
:
[
{
value
:
0
,
color
:
'#C3E6CB'
},
{
value
:
50
,
color
:
'#FFECB5'
},
{
value
:
100
,
color
:
'#F5C6CB'
}
]
}
}
];
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
mapData
=
[
{
country
:
'United States'
,
value
:
80
},
{
country
:
'Canada'
,
value
:
60
},
{
country
:
'Mexico'
,
value
:
40
},
];
this
.
updateLayers
();
}
onDataUpdate
(
data
:
any
[]):
void
{
this
.
zoomSettings
=
{
enable
:
true
};
const
mapData
=
data
.
map
(
item
=>
({
country
:
item
[
this
.
config
.
countryField
],
value
:
item
[
this
.
config
.
valueField
]
}));
this
.
updateLayers
(
mapData
);
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
this
.
mapData
=
data
.
map
(
item
=>
({
country
:
item
[
this
.
config
.
countryField
],
value
:
item
[
this
.
config
.
valueField
]
}));
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
this
.
updateLayers
();
});
}
});
}
onReset
():
void
{
this
.
title
=
'Filled Map (Default)'
;
this
.
zoomSettings
=
{
enable
:
true
};
const
defaultMapData
=
[
{
country
:
'United States'
,
value
:
80
},
{
country
:
'Canada'
,
value
:
60
},
{
country
:
'Mexico'
,
value
:
40
},
];
this
.
updateLayers
(
defaultMapData
);
}
updateLayers
(
):
void
{
private
updateLayers
(
dataSource
:
any
[]
):
void
{
this
.
layers
=
[
{
shapeData
:
new
Object
({
data
:
'https://cdn.syncfusion.com/maps/map-data/world-map.json'
}),
shapeDataPath
:
'name'
,
shapePropertyPath
:
'name'
,
dataSource
:
this
.
mapData
,
dataSource
:
dataSource
,
tooltipSettings
:
{
visible
:
true
,
valuePath
:
'value'
},
shapeSettings
:
{
fill
:
'#E5EEF6'
,
colorValuePath
:
'value'
,
colorMapping
:
[
colorMapping
:
this
.
config
?.
colorMapping
||
[
{
value
:
0
,
color
:
'#C3E6CB'
},
{
value
:
50
,
color
:
'#FFECB5'
},
{
value
:
100
,
color
:
'#F5C6CB'
}
...
...
src/app/DPU/widgets/funnel-chart-widget/funnel-chart-widget.component.html
View file @
6b8b4df6
<ejs-accumulationchart
[
title
]="
title
"
[
legendSettings
]="
legendSettings
"
[
enableSmartLabels
]="
true
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-accumulationchart
*
ngIf=
"!isLoading && !hasError"
[
title
]="
title
"
[
legendSettings
]="
legendSettings
"
[
enableSmartLabels
]="
true
"
>
<e-accumulation-series-collection>
<e-accumulation-series
[
dataSource
]="
chartData
"
type=
"Funnel"
xName=
"x"
yName=
"y"
[
dataLabel
]="{
visible:
true
,
name:
'
text
',
position:
'
Inside
'
}"
></e-accumulation-series>
</e-accumulation-series-collection>
...
...
src/app/DPU/widgets/funnel-chart-widget/funnel-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
AccumulationChartModule
,
FunnelSeriesService
,
AccumulationDataLabelService
,
AccumulationLegendService
,
AccumulationTooltipService
}
from
'@syncfusion/ej2-angular-charts'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-funnel-chart-widget'
,
...
...
@@ -10,31 +11,22 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
providers
:
[
FunnelSeriesService
,
AccumulationDataLabelService
,
AccumulationLegendService
,
AccumulationTooltipService
],
templateUrl
:
'./funnel-chart-widget.component.html'
,
})
export
class
FunnelChartWidgetComponent
implements
OnInit
{
@
Input
()
config
:
any
;
@
Input
()
chartData
:
Object
[];
@
Input
()
title
:
string
=
'Funnel Chart'
;
@
Input
()
legendSettings
:
Object
;
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
legendSettings
=
{
visible
:
true
};
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
xField
&&
this
.
config
.
yField
)
{
this
.
updateChart
(
selectedDataset
.
data
);
}
else
{
this
.
resetChart
();
}
});
export
class
FunnelChartWidgetComponent
extends
BaseWidgetComponent
{
public
chartData
:
Object
[];
public
legendSettings
:
Object
;
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateChart
(
data
:
any
[]):
void
{
onDataUpdate
(
data
:
any
[]):
void
{
this
.
legendSettings
=
{
visible
:
true
};
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
}
resetChart
():
void
{
onReset
():
void
{
this
.
title
=
'Funnel Chart (Default)'
;
this
.
legendSettings
=
{
visible
:
true
};
this
.
chartData
=
[
{
x
:
'Website Visitors'
,
y
:
10000
},
{
x
:
'Leads'
,
y
:
8000
},
...
...
src/app/DPU/widgets/gauge-chart-widget/gauge-chart-widget.component.html
View file @
6b8b4df6
<ejs-circulargauge
[
title
]="
title
"
[
axes
]="
axes
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-circulargauge
*
ngIf=
"!isLoading && !hasError"
[
title
]="
title
"
[
axes
]="
axes
"
>
</ejs-circulargauge>
src/app/DPU/widgets/gauge-chart-widget/gauge-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
CircularGaugeModule
,
GaugeTooltipService
,
AnnotationsService
}
from
'@syncfusion/ej2-angular-circulargauge'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-gauge-chart-widget'
,
...
...
@@ -10,55 +11,48 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
providers
:
[
GaugeTooltipService
,
AnnotationsService
],
templateUrl
:
'./gauge-chart-widget.component.html'
,
})
export
class
GaugeChartWidgetComponent
implements
OnInit
{
@
Input
()
config
:
any
;
@
Input
()
value
:
number
=
70
;
@
Input
()
title
:
string
=
'Gauge Chart'
;
export
class
GaugeChartWidgetComponent
extends
BaseWidgetComponent
{
public
axes
:
Object
[];
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
resetChart
();
// Set initial state
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
valueField
)
{
this
.
updateChart
(
selectedDataset
.
data
);
}
else
{
this
.
resetChart
();
}
});
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateChart
(
data
:
any
[]):
void
{
onDataUpdate
(
data
:
any
[]):
void
{
let
value
=
0
;
if
(
data
.
length
>
0
)
{
this
.
value
=
data
[
0
][
this
.
config
.
valueField
];
// Assuming single value for gauge
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
// Assuming gauge shows a single value, either aggregated or the first item's value
if
(
this
.
config
.
aggregation
===
'sum'
)
{
value
=
data
.
reduce
((
sum
,
item
)
=>
sum
+
(
item
[
this
.
config
.
valueField
]
||
0
),
0
);
}
else
if
(
this
.
config
.
aggregation
===
'avg'
)
{
const
sum
=
data
.
reduce
((
sum
,
item
)
=>
sum
+
(
item
[
this
.
config
.
valueField
]
||
0
),
0
);
value
=
sum
/
data
.
length
;
}
else
{
value
=
data
[
0
][
this
.
config
.
valueField
];
}
}
this
.
setAxes
();
this
.
setAxes
(
value
);
}
resetChar
t
():
void
{
this
.
value
=
70
;
this
.
setAxes
();
onRese
t
():
void
{
this
.
title
=
'Gauge (Default)'
;
this
.
setAxes
(
70
);
}
setAxes
(
):
void
{
private
setAxes
(
value
:
number
):
void
{
this
.
axes
=
[{
line
:
{
width
:
0
},
labelStyle
:
{
font
:
{
size
:
'0px'
}
},
majorTicks
:
{
height
:
0
},
minorTicks
:
{
height
:
0
},
pointers
:
[{
value
:
this
.
value
,
value
:
value
,
radius
:
'80%'
,
pointerWidth
:
8
,
cap
:
{
radius
:
7
},
needleTail
:
{
length
:
'18%'
}
}],
ranges
:
[
ranges
:
this
.
config
?.
ranges
||
[
{
start
:
0
,
end
:
50
,
color
:
'#E0B9B9'
},
{
start
:
50
,
end
:
75
,
color
:
'#B9D7EA'
},
{
start
:
75
,
end
:
100
,
color
:
'#B9EAB9'
}
...
...
src/app/DPU/widgets/headcount-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetService
}
from
'../services/dataset.service'
;
import
{
DatasetModel
}
from
'../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'./base-widget.component'
;
@
Component
({
selector
:
'app-headcount-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
HttpClientModule
],
providers
:
[
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
],
template
:
`
<div class="p-4 h-full bg-green-50">
<h3 class="font-bold text-green-800">{{ title }}</h3>
<p class="text-sm text-green-600">Total Headcount: {{ totalHeadcount }}</p>
<div *ngIf="breakdown.length > 0">
<p class="text-sm text-green-600">By {{ config?.categoryField || 'Category' }}:</p>
<ul>
<li *ngFor="let item of breakdown" class="text-xs text-green-600">
{{ item.category }}: {{ item.count }}
</li>
</ul>
<!-- Loading State -->
<div *ngIf="isLoading" class="flex justify-center items-center h-full">
<div class="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-green-500"></div>
</div>
<!-- Error State -->
<div *ngIf="hasError" class="flex flex-col justify-center items-center h-full text-red-500">
<i class="bi bi-exclamation-triangle-fill text-2xl"></i>
<p class="mt-1 text-sm">{{ errorMessage }}</p>
</div>
<!-- Content -->
<div *ngIf="!isLoading && !hasError">
<p class="text-sm text-green-600">Total Headcount: {{ totalHeadcount }}</p>
<div *ngIf="breakdown.length > 0">
<p class="text-sm text-green-600">By {{ config?.categoryField || 'Category' }}:</p>
<ul>
<li *ngFor="let item of breakdown" class="text-xs text-green-600">
{{ item.category }}: {{ item.count }}
</li>
</ul>
</div>
</div>
</div>
`
})
export
class
HeadcountWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
title
:
string
=
'Employee Headcount'
;
totalHeadcount
:
number
=
0
;
breakdown
:
{
category
:
string
,
count
:
number
}[]
=
[];
export
class
HeadcountWidgetComponent
extends
BaseWidgetComponent
{
public
totalHeadcount
:
number
=
0
;
public
breakdown
:
{
category
:
string
,
count
:
number
}[]
=
[];
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
{
this
.
resetData
();
}
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
resetData
();
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
this
.
totalHeadcount
=
data
.
length
;
if
(
this
.
config
.
categoryField
)
{
const
counts
=
data
.
reduce
((
acc
,
item
)
=>
{
const
category
=
item
[
this
.
config
.
categoryField
];
acc
[
category
]
=
(
acc
[
category
]
||
0
)
+
1
;
return
acc
;
},
{});
this
.
breakdown
=
Object
.
keys
(
counts
).
map
(
key
=>
({
category
:
key
,
count
:
counts
[
key
]
}));
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
onDataUpdate
(
data
:
any
[]):
void
{
this
.
totalHeadcount
=
data
.
length
;
if
(
this
.
config
.
categoryField
)
{
const
counts
=
data
.
reduce
((
acc
,
item
)
=>
{
const
category
=
item
[
this
.
config
.
categoryField
];
acc
[
category
]
=
(
acc
[
category
]
||
0
)
+
1
;
return
acc
;
},
{});
this
.
breakdown
=
Object
.
keys
(
counts
).
map
(
key
=>
({
category
:
key
,
count
:
counts
[
key
]
}));
}
}
resetData
():
void
{
onReset
():
void
{
this
.
title
=
'Headcount (Default)'
;
this
.
totalHeadcount
=
200
;
this
.
breakdown
=
[
{
category
:
'IT'
,
count
:
50
},
...
...
src/app/DPU/widgets/matrix-widget/matrix-widget.component.html
View file @
6b8b4df6
...
...
@@ -3,21 +3,42 @@
<h5
class=
"card-title"
>
{{ title }}
</h5>
</div>
<div
class=
"card-body"
>
<table
class=
"w-full text-sm text-left text-gray-500"
>
<thead
class=
"text-xs text-gray-700 uppercase bg-gray-50"
>
<tr>
<th
*
ngFor=
"let header of headers"
scope=
"col"
class=
"px-6 py-3"
>
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr
*
ngFor=
"let row of data"
class=
"bg-white border-b"
>
<td
*
ngFor=
"let cell of row"
class=
"px-6 py-4"
>
{{ cell }}
</td>
</tr>
</tbody>
</table>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Data Table -->
<div
*
ngIf=
"!isLoading && !hasError"
>
<table
class=
"w-full text-sm text-left text-gray-500"
>
<thead
class=
"text-xs text-gray-700 uppercase bg-gray-50"
>
<tr>
<th
*
ngFor=
"let header of headers"
scope=
"col"
class=
"px-6 py-3"
>
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr
*
ngFor=
"let row of data"
class=
"bg-white border-b"
>
<td
*
ngFor=
"let cell of row"
class=
"px-6 py-4"
>
{{ cell }}
</td>
</tr>
<tr
*
ngIf=
"data.length === 0"
>
<td
[
attr
.
colspan
]="
headers
.
length
"
class=
"px-6 py-4 text-center text-gray-500"
>
No data available.
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
src/app/DPU/widgets/matrix-widget/matrix-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-matrix-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
HttpClientModule
],
providers
:
[
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
],
templateUrl
:
'./matrix-widget.component.html'
,
styleUrls
:
[
'./matrix-widget.component.scss'
]
})
export
class
MatrixWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
title
:
string
=
'Matrix'
;
@
Input
()
headers
:
string
[]
=
[];
@
Input
()
data
:
any
[][]
=
[];
@
Input
()
config
:
any
;
export
class
MatrixWidgetComponent
extends
BaseWidgetComponent
{
public
headers
:
string
[]
=
[];
public
data
:
any
[][]
=
[];
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
!
this
.
config
||
!
this
.
config
.
datasetId
)
{
this
.
headers
=
[
'Category'
,
'Q1'
,
'Q2'
,
'Q3'
,
'Q4'
];
this
.
data
=
[
[
'Product A'
,
100
,
120
,
150
,
130
],
[
'Product B'
,
80
,
90
,
110
,
100
],
[
'Product C'
,
150
,
130
,
160
,
140
],
];
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
headers
=
[
'Category'
,
'Q1'
,
'Q2'
,
'Q3'
,
'Q4'
];
this
.
data
=
[
[
'Product A'
,
100
,
120
,
150
,
130
],
[
'Product B'
,
80
,
90
,
110
,
100
],
[
'Product C'
,
150
,
130
,
160
,
140
],
];
onDataUpdate
(
data
:
any
[]):
void
{
if
(
this
.
config
?.
columns
&&
data
?.
length
>
0
)
{
this
.
headers
=
this
.
config
.
columns
.
map
((
col
:
any
)
=>
col
.
headerText
);
this
.
data
=
data
.
map
(
row
=>
this
.
config
.
columns
.
map
((
col
:
any
)
=>
row
[
col
.
field
]));
}
else
{
this
.
onReset
();
}
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
if
(
this
.
config
.
columns
&&
data
.
length
>
0
)
{
this
.
headers
=
this
.
config
.
columns
.
map
((
col
:
any
)
=>
col
.
headerText
);
this
.
data
=
data
.
map
(
row
=>
this
.
config
.
columns
.
map
((
col
:
any
)
=>
row
[
col
.
field
]));
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
}
onReset
():
void
{
this
.
title
=
'Matrix (Default)'
;
this
.
headers
=
[
'Category'
,
'Q1'
,
'Q2'
,
'Q3'
,
'Q4'
];
this
.
data
=
[
[
'Product A'
,
100
,
120
,
150
,
130
],
[
'Product B'
,
80
,
90
,
110
,
100
],
[
'Product C'
,
150
,
130
,
160
,
140
],
];
}
}
src/app/DPU/widgets/multi-row-card-widget/multi-row-card-widget.component.html
View file @
6b8b4df6
...
...
@@ -3,9 +3,28 @@
<h5
class=
"card-title"
>
{{ title }}
</h5>
</div>
<div
class=
"card-body"
>
<div
*
ngFor=
"let item of cardData"
class=
"flex justify-between items-center py-2 border-b last:border-b-0"
>
<span
class=
"text-gray-600"
>
{{ item.label }}
</span>
<span
class=
"font-bold text-lg"
>
{{ item.value }}
<span
class=
"text-sm text-gray-500"
>
{{ item.unit }}
</span></span>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Content -->
<div
*
ngIf=
"!isLoading && !hasError"
>
<div
*
ngFor=
"let item of cardData"
class=
"flex justify-between items-center py-2 border-b last:border-b-0"
>
<span
class=
"text-gray-600"
>
{{ item.label }}
</span>
<span
class=
"font-bold text-lg"
>
{{ item.value }}
<span
class=
"text-sm text-gray-500"
>
{{ item.unit }}
</span></span>
</div>
<div
*
ngIf=
"!cardData || cardData.length === 0"
class=
"text-center text-gray-500 py-4"
>
No data available.
</div>
</div>
</div>
</div>
src/app/DPU/widgets/multi-row-card-widget/multi-row-card-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
}
from
'@angular/common/http'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-multi-row-card-widget'
,
...
...
@@ -11,49 +10,27 @@ import { HttpClient } from '@angular/common/http';
templateUrl
:
'./multi-row-card-widget.component.html'
,
styleUrls
:
[
'./multi-row-card-widget.component.scss'
]
})
export
class
MultiRowCardWidgetComponent
implements
OnInit
{
@
Input
()
config
:
any
;
@
Input
()
cardData
:
any
[];
@
Input
()
title
:
string
=
'Multi-Row Card'
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
!
this
.
config
||
!
this
.
config
.
datasetId
)
{
this
.
cardData
=
[
{
label
:
'Total Sales'
,
value
:
'1,234,567'
,
unit
:
'USD'
},
{
label
:
'New Customers'
,
value
:
'5,432'
,
unit
:
''
},
{
label
:
'Conversion Rate'
,
value
:
'12.34'
,
unit
:
'%'
},
];
}
export
class
MultiRowCardWidgetComponent
extends
BaseWidgetComponent
{
public
cardData
:
any
[];
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
cardData
=
[
{
label
:
'Total Sales'
,
value
:
'1,234,567'
,
unit
:
'USD'
},
{
label
:
'New Customers'
,
value
:
'5,432'
,
unit
:
''
},
{
label
:
'Conversion Rate'
,
value
:
'12.34'
,
unit
:
'%'
},
];
}
onDataUpdate
(
data
:
any
[]):
void
{
this
.
cardData
=
data
.
map
(
item
=>
({
label
:
item
[
this
.
config
.
labelField
],
value
:
item
[
this
.
config
.
valueField
],
unit
:
item
[
this
.
config
.
unitField
]
||
''
}));
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
this
.
cardData
=
data
.
map
(
item
=>
({
label
:
item
[
this
.
config
.
labelField
],
value
:
item
[
this
.
config
.
valueField
],
unit
:
item
[
this
.
config
.
unitField
]
||
''
}));
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
}
onReset
():
void
{
this
.
title
=
'Multi-Row Card (Default)'
;
this
.
cardData
=
[
{
label
:
'Total Sales'
,
value
:
'1,234,567'
,
unit
:
'USD'
},
{
label
:
'New Customers'
,
value
:
'5,432'
,
unit
:
''
},
{
label
:
'Conversion Rate'
,
value
:
'12.34'
,
unit
:
'%'
},
];
}
}
src/app/DPU/widgets/payroll-summary-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetService
}
from
'../services/dataset.service'
;
import
{
DatasetModel
}
from
'../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'./base-widget.component'
;
@
Component
({
selector
:
'app-payroll-summary-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
HttpClientModule
],
providers
:
[
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
],
template
:
`
<div class="p-4 h-full bg-red-50">
<h3 class="font-bold text-red-800">{{ title }}</h3>
<p class="text-sm text-red-600">Total Payroll: {{ totalPayroll | currency }}</p>
<p class="text-sm text-red-600">Employees Paid: {{ employeesPaid }}</p>
<!-- Loading State -->
<div *ngIf="isLoading" class="flex justify-center items-center h-full">
<div class="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-red-500"></div>
</div>
<!-- Error State -->
<div *ngIf="hasError" class="flex flex-col justify-center items-center h-full text-red-500">
<i class="bi bi-exclamation-triangle-fill text-2xl"></i>
<p class="mt-1 text-sm">{{ errorMessage }}</p>
</div>
<!-- Content -->
<div *ngIf="!isLoading && !hasError">
<p class="text-sm text-red-600">Total Payroll: {{ totalPayroll | currency }}</p>
<p class="text-sm text-red-600">Employees Paid: {{ employeesPaid }}</p>
</div>
</div>
`
})
export
class
PayrollSummaryWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
title
:
string
=
'Payroll Summary'
;
totalPayroll
:
number
=
0
;
employeesPaid
:
number
=
0
;
export
class
PayrollSummaryWidgetComponent
extends
BaseWidgetComponent
{
public
totalPayroll
:
number
=
0
;
public
employeesPaid
:
number
=
0
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
{
this
.
resetData
();
}
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
resetData
();
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
totalPayroll
=
firstItem
[
this
.
config
.
totalPayrollField
]
||
0
;
this
.
employeesPaid
=
firstItem
[
this
.
config
.
employeesPaidField
]
||
0
;
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
onDataUpdate
(
data
:
any
[]):
void
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
totalPayroll
=
firstItem
[
this
.
config
.
totalPayrollField
]
||
0
;
this
.
employeesPaid
=
firstItem
[
this
.
config
.
employeesPaidField
]
||
0
;
}
}
resetData
():
void
{
onReset
():
void
{
this
.
title
=
'Payroll Summary (Default)'
;
this
.
totalPayroll
=
1500000
;
this
.
employeesPaid
=
180
;
}
...
...
src/app/DPU/widgets/payroll-widget/payroll-widget.component.html
View file @
6b8b4df6
...
...
@@ -4,8 +4,24 @@
<h5
class=
"card-title"
>
{{ title }}
</h5>
</div>
<div
class=
"card-body"
>
<p>
Employee: {{ employeeName }}
</p>
<p>
Pay Period: {{ payPeriod }}
</p>
<p>
Net Pay: {{ netPay | currency }}
</p>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Content -->
<div
*
ngIf=
"!isLoading && !hasError"
>
<p>
Employee: {{ employeeName }}
</p>
<p>
Pay Period: {{ payPeriod }}
</p>
<p>
Net Pay: {{ netPay | currency }}
</p>
</div>
</div>
</div>
src/app/DPU/widgets/payroll-widget/payroll-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-payroll-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
HttpClientModule
],
providers
:
[
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
],
templateUrl
:
'./payroll-widget.component.html'
,
styleUrls
:
[
'./payroll-widget.component.scss'
]
})
export
class
PayrollWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
title
:
string
=
'Payroll Details'
;
employeeName
:
string
=
''
;
payPeriod
:
string
=
''
;
netPay
:
number
=
0
;
export
class
PayrollWidgetComponent
extends
BaseWidgetComponent
{
public
employeeName
:
string
=
''
;
public
payPeriod
:
string
=
''
;
public
netPay
:
number
=
0
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
{
this
.
resetData
();
}
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
resetData
();
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
employeeName
=
firstItem
[
this
.
config
.
employeeNameField
]
||
''
;
this
.
payPeriod
=
firstItem
[
this
.
config
.
payPeriodField
]
||
''
;
this
.
netPay
=
firstItem
[
this
.
config
.
netPayField
]
||
0
;
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
onDataUpdate
(
data
:
any
[]):
void
{
if
(
data
.
length
>
0
)
{
const
firstItem
=
data
[
0
];
this
.
employeeName
=
firstItem
[
this
.
config
.
employeeNameField
]
||
''
;
this
.
payPeriod
=
firstItem
[
this
.
config
.
payPeriodField
]
||
''
;
this
.
netPay
=
firstItem
[
this
.
config
.
netPayField
]
||
0
;
}
}
resetData
():
void
{
onReset
():
void
{
this
.
title
=
'Payroll (Default)'
;
this
.
employeeName
=
'N/A'
;
this
.
payPeriod
=
'N/A'
;
this
.
netPay
=
0
;
...
...
src/app/DPU/widgets/pie-chart-widget/pie-chart-widget.component.html
View file @
6b8b4df6
<ejs-accumulationchart
[
title
]="
title
"
[
legendSettings
]="
legendSettings
"
[
enableSmartLabels
]="
true
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-accumulationchart
*
ngIf=
"!isLoading && !hasError"
[
title
]="
title
"
[
legendSettings
]="
legendSettings
"
[
enableSmartLabels
]="
true
"
>
<e-accumulation-series-collection>
<e-accumulation-series
[
dataSource
]="
chartData
"
type=
"Pie"
xName=
"x"
yName=
"y"
[
dataLabel
]="{
visible:
true
,
name:
'
text
',
position:
'
Inside
'
}"
></e-accumulation-series>
</e-accumulation-series-collection>
...
...
src/app/DPU/widgets/pie-chart-widget/pie-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
AccumulationChartModule
,
PieSeriesService
,
AccumulationDataLabelService
,
AccumulationLegendService
,
AccumulationTooltipService
}
from
'@syncfusion/ej2-angular-charts'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-pie-chart-widget'
,
...
...
@@ -10,27 +11,18 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
providers
:
[
PieSeriesService
,
AccumulationDataLabelService
,
AccumulationLegendService
,
AccumulationTooltipService
],
templateUrl
:
'./pie-chart-widget.component.html'
,
})
export
class
PieChartWidgetComponent
implements
OnInit
{
@
Input
()
chartData
:
Object
[];
@
Input
()
title
:
string
=
'Pie Chart'
;
@
Input
()
legendSettings
:
Object
;
@
Input
()
config
:
any
;
export
class
PieChartWidgetComponent
extends
BaseWidgetComponent
{
public
chartData
:
Object
[];
public
legendSettings
:
Object
;
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
legendSettings
=
{
visible
:
true
};
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
xField
&&
this
.
config
.
yField
)
{
this
.
updateChart
(
selectedDataset
.
data
);
}
else
{
this
.
resetChart
();
}
});
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateChart
(
data
:
any
[]):
void
{
onDataUpdate
(
data
:
any
[]):
void
{
this
.
legendSettings
=
{
visible
:
true
};
let
transformedData
=
data
;
if
(
this
.
config
.
aggregation
===
'count'
)
{
const
counts
=
transformedData
.
reduce
((
acc
,
item
)
=>
{
const
key
=
item
[
this
.
config
.
xField
];
...
...
@@ -42,19 +34,18 @@ export class PieChartWidgetComponent implements OnInit {
transformedData
=
transformedData
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
}
this
.
chartData
=
transformedData
;
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
}
resetChart
():
void
{
onReset
():
void
{
this
.
title
=
'Pie Chart (Default)'
;
this
.
legendSettings
=
{
visible
:
true
};
this
.
chartData
=
[
{
browser
:
'Chrome'
,
users
:
37
},
{
browser
:
'Firefox'
,
users
:
17
},
{
browser
:
'Internet Explorer'
,
users
:
19
},
{
browser
:
'Edge'
,
users
:
4
},
{
browser
:
'Safari'
,
users
:
11
},
{
browser
:
'Other'
,
users
:
12
},
{
x
:
'Chrome'
,
y
:
37
},
{
x
:
'Firefox'
,
y
:
17
},
{
x
:
'Internet Explorer'
,
y
:
19
},
{
x
:
'Edge'
,
y
:
4
},
{
x
:
'Safari'
,
y
:
11
},
{
x
:
'Other'
,
y
:
12
},
];
}
}
src/app/DPU/widgets/scatter-bubble-chart-widget/scatter-bubble-chart-widget.component.html
View file @
6b8b4df6
<ejs-chart
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-chart
*
ngIf=
"!isLoading && !hasError"
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<e-series-collection>
<e-series
[
dataSource
]="
chartData
"
[
type
]="
type
"
xName=
"x"
yName=
"y"
[
size
]="
config
?.
sizeField
"
name=
"Data"
></e-series>
</e-series-collection>
...
...
src/app/DPU/widgets/scatter-bubble-chart-widget/scatter-bubble-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
OnChanges
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
ChartModule
,
ScatterSeriesService
,
BubbleSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
}
from
'@syncfusion/ej2-angular-charts'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-scatter-bubble-chart-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
ChartModule
,
HttpClientModule
],
providers
:
[
ScatterSeriesService
,
BubbleSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
,
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
,
ChartModule
],
providers
:
[
ScatterSeriesService
,
BubbleSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
],
templateUrl
:
'./scatter-bubble-chart-widget.component.html'
,
})
export
class
ScatterBubbleChartWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
chartData
:
Object
[];
@
Input
()
title
:
string
=
'Scatter & Bubble Chart'
;
@
Input
()
type
:
'Scatter'
|
'Bubble'
=
'Scatter'
;
@
Input
()
config
:
any
;
export
class
ScatterBubbleChartWidgetComponent
extends
BaseWidgetComponent
{
public
chartData
:
Object
[];
public
type
:
'Scatter'
|
'Bubble'
=
'Scatter'
;
public
primaryXAxis
:
Object
;
public
primaryYAxis
:
Object
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngOnInit
():
void
{
this
.
primaryXAxis
=
{
title
:
'X-Value'
};
this
.
primaryYAxis
=
{
title
:
'Y-Value'
};
if
(
!
this
.
config
||
!
this
.
config
.
datasetId
)
{
if
(
this
.
type
===
'Scatter'
)
{
this
.
chartData
=
[
{
x
:
10
,
y
:
35
},
{
x
:
15
,
y
:
28
},
{
x
:
20
,
y
:
34
},
{
x
:
25
,
y
:
32
},
{
x
:
30
,
y
:
40
},
{
x
:
35
,
y
:
30
},
];
}
else
{
// Bubble
onDataUpdate
(
data
:
any
[]):
void
{
this
.
type
=
this
.
config
.
type
||
'Scatter'
;
if
(
this
.
type
===
'Bubble'
)
{
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
],
size
:
item
[
this
.
config
.
sizeField
]
}));
}
else
{
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
}
this
.
primaryXAxis
=
{
title
:
this
.
config
.
xAxisTitle
||
''
};
this
.
primaryYAxis
=
{
title
:
this
.
config
.
yAxisTitle
||
''
};
}
onReset
():
void
{
this
.
title
=
'Scatter Chart (Default)'
;
this
.
type
=
this
.
config
?.
type
||
'Scatter'
;
if
(
this
.
type
===
'Bubble'
)
{
this
.
chartData
=
[
{
x
:
10
,
y
:
35
,
size
:
5
},
{
x
:
15
,
y
:
28
,
size
:
8
},
{
x
:
20
,
y
:
34
,
size
:
6
},
{
x
:
25
,
y
:
32
,
size
:
10
},
{
x
:
30
,
y
:
40
,
size
:
7
},
{
x
:
35
,
y
:
30
,
size
:
9
},
];
}
}
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
if
(
this
.
type
===
'Scatter'
)
{
}
else
{
this
.
chartData
=
[
{
x
:
10
,
y
:
35
},
{
x
:
15
,
y
:
28
},
{
x
:
20
,
y
:
34
},
{
x
:
25
,
y
:
32
},
{
x
:
30
,
y
:
40
},
{
x
:
35
,
y
:
30
},
];
}
else
{
// Bubble
this
.
chartData
=
[
{
x
:
10
,
y
:
35
,
size
:
5
},
{
x
:
15
,
y
:
28
,
size
:
8
},
{
x
:
20
,
y
:
34
,
size
:
6
},
{
x
:
25
,
y
:
32
,
size
:
10
},
{
x
:
30
,
y
:
40
,
size
:
7
},
{
x
:
35
,
y
:
30
,
size
:
9
},
];
}
}
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
if
(
this
.
config
.
type
===
'Bubble'
)
{
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
],
size
:
item
[
this
.
config
.
sizeField
]
}));
}
else
{
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
if
(
this
.
config
.
xAxisTitle
)
{
this
.
primaryXAxis
=
{
...
this
.
primaryXAxis
,
title
:
this
.
config
.
xAxisTitle
};
}
if
(
this
.
config
.
yAxisTitle
)
{
this
.
primaryYAxis
=
{
...
this
.
primaryYAxis
,
title
:
this
.
config
.
yAxisTitle
};
}
if
(
this
.
config
.
type
)
{
this
.
type
=
this
.
config
.
type
;
}
});
}
});
}
this
.
primaryXAxis
=
{
title
:
'X-Value'
};
this
.
primaryYAxis
=
{
title
:
'Y-Value'
};
}
}
src/app/DPU/widgets/simple-kpi-widget/simple-kpi-widget.component.html
View file @
6b8b4df6
<div
class=
"card h-100"
>
<div
class=
"card-body flex flex-col justify-center items-center"
>
<h5
class=
"text-lg font-semibold text-gray-600 mb-2"
>
{{ title }}
</h5>
<div
class=
"text-5xl font-bold text-gray-800 mb-2"
>
{{ value }}
<span
*
ngIf=
"unit"
class=
"text-2xl ml-1"
>
{{ unit }}
</span>
<div
class=
"card-body"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<div
*
ngIf=
"trendValue"
class=
"flex items-center text-sm"
>
<span
*
ngIf=
"trend === 'up'"
class=
"text-green-500 flex items-center"
>
<svg
class=
"w-4 h-4 mr-1"
fill=
"none"
stroke=
"currentColor"
viewBox=
"0 0 24 24"
xmlns=
"http://www.w3.org/2000/svg"
><path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M5 10l7-7m0 0l7 7m-7-7v18"
></path></svg>
{{ trendValue }}
</span>
<span
*
ngIf=
"trend === 'down'"
class=
"text-red-500 flex items-center"
>
<svg
class=
"w-4 h-4 mr-1"
fill=
"none"
stroke=
"currentColor"
viewBox=
"0 0 24 24"
xmlns=
"http://www.w3.org/2000/svg"
><path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M19 14l-7 7m0 0l-7-7m7 7V3"
></path></svg>
{{ trendValue }}
</span>
<span
*
ngIf=
"trend === 'neutral' && trendValue"
class=
"text-gray-500"
>
{{ trendValue }}
</span>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Content -->
<div
*
ngIf=
"!isLoading && !hasError"
class=
"flex flex-col justify-center items-center h-full"
>
<h5
class=
"text-lg font-semibold text-gray-600 mb-2"
>
{{ title }}
</h5>
<div
class=
"text-5xl font-bold text-gray-800 mb-2"
>
{{ value }}
<span
*
ngIf=
"unit"
class=
"text-2xl ml-1"
>
{{ unit }}
</span>
</div>
<div
*
ngIf=
"trendValue"
class=
"flex items-center text-sm"
>
<span
*
ngIf=
"trend === 'up'"
class=
"text-green-500 flex items-center"
>
<svg
class=
"w-4 h-4 mr-1"
fill=
"none"
stroke=
"currentColor"
viewBox=
"0 0 24 24"
xmlns=
"http://www.w3.org/2000/svg"
><path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M5 10l7-7m0 0l7 7m-7-7v18"
></path></svg>
{{ trendValue }}
</span>
<span
*
ngIf=
"trend === 'down'"
class=
"text-red-500 flex items-center"
>
<svg
class=
"w-4 h-4 mr-1"
fill=
"none"
stroke=
"currentColor"
viewBox=
"0 0 24 24"
xmlns=
"http://www.w3.org/2000/svg"
><path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M19 14l-7 7m0 0l-7-7m7 7V3"
></path></svg>
{{ trendValue }}
</span>
<span
*
ngIf=
"trend === 'neutral' && trendValue"
class=
"text-gray-500"
>
{{ trendValue }}
</span>
</div>
</div>
</div>
</div>
src/app/DPU/widgets/simple-kpi-widget/simple-kpi-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-simple-kpi-widget'
,
...
...
@@ -10,47 +11,38 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
templateUrl
:
'./simple-kpi-widget.component.html'
,
styleUrls
:
[
'./simple-kpi-widget.component.scss'
]
})
export
class
SimpleKpiWidgetComponent
implements
OnInit
{
@
Input
()
title
:
string
=
'Key Performance Indicator'
;
@
Input
()
value
:
string
=
'123,456'
;
@
Input
()
unit
:
string
=
''
;
@
Input
()
trend
:
'up'
|
'down'
|
'neutral'
=
'neutral'
;
@
Input
()
trendValue
:
string
=
''
;
@
Input
()
config
:
any
;
export
class
SimpleKpiWidgetComponent
extends
BaseWidgetComponent
{
public
value
:
string
=
'0'
;
public
unit
:
string
=
''
;
public
trend
:
'up'
|
'down'
|
'neutral'
=
'neutral'
;
public
trendValue
:
string
=
''
;
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
valueField
)
{
this
.
updateWidget
(
selectedDataset
.
data
);
}
else
{
this
.
resetWidget
();
}
});
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateWidget
(
data
:
any
[]):
void
{
onDataUpdate
(
data
:
any
[]):
void
{
if
(
data
.
length
>
0
)
{
let
kpiValue
=
data
[
0
][
this
.
config
.
valueField
]
;
let
kpiValue
=
0
;
if
(
this
.
config
.
aggregation
===
'count'
)
{
kpiValue
=
data
.
length
;
}
else
if
(
this
.
config
.
aggregation
===
'sum'
)
{
kpiValue
=
data
.
reduce
((
sum
,
item
)
=>
sum
+
(
item
[
this
.
config
.
valueField
]
||
0
),
0
);
}
else
{
// Default to first value if no aggregation
kpiValue
=
data
[
0
][
this
.
config
.
valueField
];
}
this
.
value
=
kpiValue
.
toLocaleString
();
this
.
unit
=
this
.
config
.
unit
||
''
;
this
.
trend
=
this
.
config
.
trend
||
'neutral'
;
this
.
trendValue
=
this
.
config
.
trendValue
||
''
;
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
}
resetWidget
():
void
{
onReset
():
void
{
this
.
title
=
'KPI (Default)'
;
this
.
value
=
'123,456'
;
this
.
unit
=
''
;
this
.
unit
=
'
#
'
;
this
.
trend
=
'neutral'
;
this
.
trendValue
=
''
;
}
...
...
src/app/DPU/widgets/simple-table-widget/simple-table-widget.component.html
View file @
6b8b4df6
...
...
@@ -3,21 +3,42 @@
<h5
class=
"card-title"
>
{{ title }}
</h5>
</div>
<div
class=
"card-body"
>
<table
class=
"w-full text-sm text-left text-gray-500"
>
<thead
class=
"text-xs text-gray-700 uppercase bg-gray-50"
>
<tr>
<th
*
ngFor=
"let header of headers"
scope=
"col"
class=
"px-6 py-3"
>
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr
*
ngFor=
"let row of data"
class=
"bg-white border-b"
>
<td
*
ngFor=
"let cell of row"
class=
"px-6 py-4"
>
{{ cell }}
</td>
</tr>
</tbody>
</table>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Data Table -->
<div
*
ngIf=
"!isLoading && !hasError"
>
<table
class=
"w-full text-sm text-left text-gray-500"
>
<thead
class=
"text-xs text-gray-700 uppercase bg-gray-50"
>
<tr>
<th
*
ngFor=
"let header of headers"
scope=
"col"
class=
"px-6 py-3"
>
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr
*
ngFor=
"let row of data"
class=
"bg-white border-b"
>
<td
*
ngFor=
"let cell of row"
class=
"px-6 py-4"
>
{{ cell }}
</td>
</tr>
<tr
*
ngIf=
"data.length === 0"
>
<td
[
attr
.
colspan
]="
headers
.
length
"
class=
"px-6 py-4 text-center text-gray-500"
>
No data available.
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
src/app/DPU/widgets/simple-table-widget/simple-table-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-simple-table-widget'
,
...
...
@@ -10,36 +11,25 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
templateUrl
:
'./simple-table-widget.component.html'
,
styleUrls
:
[
'./simple-table-widget.component.scss'
]
})
export
class
SimpleTableWidgetComponent
implements
OnInit
{
@
Input
()
config
:
any
;
@
Input
()
title
:
string
=
'Table'
;
@
Input
()
headers
:
string
[]
=
[];
@
Input
()
data
:
any
[][]
=
[];
export
class
SimpleTableWidgetComponent
extends
BaseWidgetComponent
{
public
headers
:
string
[]
=
[];
public
data
:
any
[][]
=
[];
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
resetTable
();
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
columns
)
{
this
.
updateTable
(
selectedDataset
.
data
);
}
else
{
this
.
resetTable
();
}
});
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateTabl
e
(
data
:
any
[]):
void
{
if
(
data
.
length
>
0
)
{
onDataUpdat
e
(
data
:
any
[]):
void
{
if
(
data
&&
data
.
length
>
0
&&
this
.
config
?.
columns
)
{
this
.
headers
=
this
.
config
.
columns
.
map
((
col
:
any
)
=>
col
.
headerText
);
this
.
data
=
data
.
map
(
row
=>
this
.
config
.
columns
.
map
((
col
:
any
)
=>
row
[
col
.
field
]));
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
else
{
this
.
onReset
();
}
}
resetTable
():
void
{
onReset
():
void
{
// Display placeholder data or an empty state
this
.
headers
=
[
'ID'
,
'Name'
,
'Status'
];
this
.
data
=
[
[
1
,
'Item A'
,
'Active'
],
...
...
src/app/DPU/widgets/slicer-widget/slicer-widget.component.html
View file @
6b8b4df6
...
...
@@ -3,7 +3,20 @@
<h5
class=
"card-title"
>
{{ title }}
</h5>
</div>
<div
class=
"card-body"
>
<select
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-2xl"
></i>
<p
class=
"mt-1"
>
{{ errorMessage }}
</p>
</div>
<!-- Content -->
<select
*
ngIf=
"!isLoading && !hasError"
[(
ngModel
)]="
selectedValue
"
(
change
)="
onSelectionChange
()"
class=
"w-full p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-white"
...
...
@@ -12,5 +25,6 @@
{{ option }}
</option>
</select>
</div>
</div>
src/app/DPU/widgets/slicer-widget/slicer-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
Output
,
EventEmitter
,
SimpleChanges
,
OnChanges
}
from
'@angular/core'
;
import
{
Component
,
Output
,
EventEmitter
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
FormsModule
}
from
'@angular/forms'
;
import
{
Da
tasetModel
}
from
'../../models/widgets.model
'
;
import
{
DatasetService
}
from
'../../services/dataset.service
'
;
import
{
HttpClient
}
from
'@angular/common/http'
;
import
{
Da
shboardStateService
}
from
'../../services/dashboard-state.service
'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component
'
;
@
Component
({
selector
:
'app-slicer-widget'
,
standalone
:
true
,
...
...
@@ -11,52 +11,32 @@ import { HttpClient } from '@angular/common/http';
templateUrl
:
'./slicer-widget.component.html'
,
styleUrls
:
[
'./slicer-widget.component.scss'
]
})
export
class
SlicerWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
public
title
:
string
=
'Slicer'
;
export
class
SlicerWidgetComponent
extends
BaseWidgetComponent
{
public
options
:
string
[]
=
[];
public
selectedValue
:
string
=
''
;
@
Output
()
selectionChange
=
new
EventEmitter
<
string
>
();
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
!
this
.
config
||
!
this
.
config
.
datasetId
)
{
this
.
options
=
[
'All'
,
'Option 1'
,
'Option 2'
,
'Option 3'
];
this
.
selectedValue
=
this
.
options
[
0
];
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
options
=
[
'All'
,
'Option 1'
,
'Option 2'
,
'Option 3'
];
onDataUpdate
(
data
:
any
[]):
void
{
if
(
this
.
config
.
optionsField
)
{
const
uniqueOptions
=
[...
new
Set
(
data
.
map
((
item
:
any
)
=>
item
[
this
.
config
.
optionsField
]))];
this
.
options
=
[
'All'
,
...
uniqueOptions
.
map
(
String
)];
this
.
selectedValue
=
this
.
options
[
0
];
}
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
((
data
:
any
[])
=>
{
if
(
this
.
config
.
optionsField
)
{
const
uniqueOptions
=
[...
new
Set
(
data
.
map
((
item
:
any
)
=>
item
[
this
.
config
.
optionsField
]))];
this
.
options
=
[
'All'
,
...
uniqueOptions
.
map
(
String
)];
// Ensure options are strings
this
.
selectedValue
=
this
.
options
[
0
];
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
}
onReset
():
void
{
this
.
title
=
'Slicer (Default)'
;
this
.
options
=
[
'All'
,
'Option 1'
,
'Option 2'
,
'Option 3'
];
this
.
selectedValue
=
this
.
options
[
0
];
}
onSelectionChange
():
void
{
// Note: In a real scenario, this should probably update the global state
// via DashboardStateService so other widgets can react to the filter.
this
.
selectionChange
.
emit
(
this
.
selectedValue
);
}
}
src/app/DPU/widgets/time-tracking-widget/time-tracking-widget.component.html
View file @
6b8b4df6
...
...
@@ -3,8 +3,24 @@
<div
class=
"card-header"
>
<h5
class=
"card-title"
>
{{ title }}
</h5>
</div>
<div
class=
"card-body flex flex-col justify-center items-center"
>
<p
class=
"text-xl font-semibold"
>
Status: {{ status }}
</p>
<p
class=
"text-lg"
>
Hours Today: {{ hours }}
</p>
<div
class=
"card-body"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Content -->
<div
*
ngIf=
"!isLoading && !hasError"
class=
"flex flex-col justify-center items-center h-full"
>
<p
class=
"text-xl font-semibold"
>
Status: {{ status }}
</p>
<p
class=
"text-lg"
>
Hours Today: {{ hours }}
</p>
</div>
</div>
</div>
src/app/DPU/widgets/time-tracking-widget/time-tracking-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
}
from
'@angular/common/http'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-time-tracking-widget'
,
...
...
@@ -12,47 +11,28 @@ import { DatasetModel } from '../../models/widgets.model';
templateUrl
:
'./time-tracking-widget.component.html'
,
styleUrls
:
[
'./time-tracking-widget.component.scss'
]
})
export
class
TimeTrackingWidgetComponent
implements
OnInit
{
@
Input
()
data
:
any
;
@
Input
()
config
:
any
;
@
Input
()
title
:
string
=
'Time Tracking'
;
status
:
string
=
''
;
hours
:
string
=
''
;
export
class
TimeTrackingWidgetComponent
extends
BaseWidgetComponent
{
public
status
:
string
=
''
;
public
hours
:
string
=
''
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
if
(
!
this
.
config
||
!
this
.
config
.
datasetId
)
{
this
.
status
=
'Clocked In'
;
this
.
hours
=
'8.0'
;
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
status
=
'Clocked In'
;
this
.
hours
=
'8.0'
;
onDataUpdate
(
data
:
any
[]):
void
{
if
(
data
.
length
>
0
)
{
// Assuming time tracking data is for a single user, so take the first item
const
timeEntry
=
data
[
0
];
this
.
status
=
this
.
config
.
statusField
?
timeEntry
[
this
.
config
.
statusField
]
:
'N/A'
;
this
.
hours
=
this
.
config
.
hoursField
?
timeEntry
[
this
.
config
.
hoursField
]
:
'N/A'
;
}
else
{
this
.
onReset
();
}
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
if
(
data
.
length
>
0
)
{
this
.
status
=
this
.
config
.
statusField
?
data
[
0
][
this
.
config
.
statusField
]
:
'N/A'
;
this
.
hours
=
this
.
config
.
hoursField
?
data
[
0
][
this
.
config
.
hoursField
]
:
'N/A'
;
}
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
});
}
});
}
onReset
():
void
{
this
.
title
=
'Time Tracking (Default)'
;
this
.
status
=
'Clocked In'
;
this
.
hours
=
'8.0'
;
}
}
src/app/DPU/widgets/treemap-widget/treemap-widget.component.html
View file @
6b8b4df6
<ejs-treemap
[
titleSettings
]="{
text:
title
}"
[
dataSource
]="
dataSource
"
[
weightValuePath
]="
weightValuePath
"
[
leafItemSettings
]="
leafItemSettings
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-treemap
*
ngIf=
"!isLoading && !hasError"
[
titleSettings
]="{
text:
title
}"
[
dataSource
]="
dataSource
"
[
weightValuePath
]="
weightValuePath
"
[
leafItemSettings
]="
leafItemSettings
"
>
</ejs-treemap>
src/app/DPU/widgets/treemap-widget/treemap-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
TreeMapModule
,
TreeMapTooltipService
,
TreeMapLegendService
,
TreeMapHighlightService
,
TreeMapSelectionService
}
from
'@syncfusion/ej2-angular-treemap'
;
import
{
DashboardStateService
,
SelectedDataset
}
from
'../../services/dashboard-state.service'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-treemap-widget'
,
...
...
@@ -10,50 +11,38 @@ import { DashboardStateService, SelectedDataset } from '../../services/dashboard
providers
:
[
TreeMapTooltipService
,
TreeMapLegendService
,
TreeMapHighlightService
,
TreeMapSelectionService
],
templateUrl
:
'./treemap-widget.component.html'
,
})
export
class
TreemapWidgetComponent
implements
OnInit
{
@
Input
()
config
:
any
;
@
Input
()
title
:
string
=
'Treemap'
;
export
class
TreemapWidgetComponent
extends
BaseWidgetComponent
{
public
dataSource
:
Object
[];
public
weightValuePath
:
string
;
public
leafItemSettings
:
Object
;
constructor
(
private
dashboardStateService
:
DashboardStateService
)
{
}
ngOnInit
():
void
{
this
.
resetChart
();
this
.
dashboardStateService
.
selectedDataset$
.
subscribe
((
selectedDataset
:
SelectedDataset
|
null
)
=>
{
if
(
selectedDataset
&&
selectedDataset
.
data
&&
this
.
config
&&
this
.
config
.
valueField
)
{
this
.
updateChart
(
selectedDataset
.
data
);
}
else
{
this
.
resetChart
();
}
});
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
updateChart
(
data
:
any
[]):
void
{
onDataUpdate
(
data
:
any
[]):
void
{
this
.
dataSource
=
data
;
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
if
(
this
.
config
.
groupField
)
{
this
.
leafItemSettings
=
{
labelPath
:
this
.
config
.
groupField
};
}
if
(
this
.
config
.
valueField
)
{
this
.
weightValuePath
=
this
.
config
.
valueField
;
}
this
.
weightValuePath
=
this
.
config
.
valueField
;
this
.
leafItemSettings
=
{
labelPath
:
this
.
config
.
groupField
,
showLabels
:
true
};
}
resetChart
():
void
{
onReset
():
void
{
this
.
title
=
'Treemap (Default)'
;
this
.
dataSource
=
[
{
Continent
:
'Asia'
,
Countries
:
'China'
,
population
:
1400
},
{
Continent
:
'Asia'
,
Countries
:
'India'
,
population
:
1350
},
{
Continent
:
'Europe'
,
Countries
:
'Germany'
,
population
:
83
},
{
Continent
:
'Europe'
,
Countries
:
'France'
,
population
:
67
},
{
Continent
:
'North America'
,
Countries
:
'USA'
,
population
:
330
},
{
Continent
:
'North America'
,
Countries
:
'Canada'
,
population
:
38
},
{
group
:
'Asia'
,
country
:
'China'
,
value
:
1400
},
{
group
:
'Asia'
,
country
:
'India'
,
value
:
1350
},
{
group
:
'Europe'
,
country
:
'Germany'
,
value
:
83
},
{
group
:
'Europe'
,
country
:
'France'
,
value
:
67
},
{
group
:
'North America'
,
country
:
'USA'
,
value
:
330
},
{
group
:
'North America'
,
country
:
'Canada'
,
value
:
38
},
];
this
.
weightValuePath
=
'population'
;
this
.
leafItemSettings
=
{
labelPath
:
'Countries'
};
this
.
weightValuePath
=
'value'
;
this
.
leafItemSettings
=
{
labelPath
:
'country'
,
showLabels
:
true
};
}
}
src/app/DPU/widgets/waterfall-chart-widget/waterfall-chart-widget.component.html
View file @
6b8b4df6
<ejs-chart
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<!-- Loading State -->
<div
*
ngIf=
"isLoading"
class=
"flex justify-center items-center h-full"
>
<div
class=
"animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500"
></div>
</div>
<!-- Error State -->
<div
*
ngIf=
"hasError"
class=
"flex flex-col justify-center items-center h-full text-red-500"
>
<i
class=
"bi bi-exclamation-triangle-fill text-4xl"
></i>
<p
class=
"mt-2"
>
{{ errorMessage }}
</p>
</div>
<!-- Chart -->
<ejs-chart
*
ngIf=
"!isLoading && !hasError"
[
title
]="
title
"
[
primaryXAxis
]="
primaryXAxis
"
[
primaryYAxis
]="
primaryYAxis
"
>
<e-series-collection>
<e-series
[
dataSource
]="
chartData
"
type=
"Waterfall"
xName=
"x"
yName=
"y"
name=
"Amount"
></e-series>
</e-series-collection>
...
...
src/app/DPU/widgets/waterfall-chart-widget/waterfall-chart-widget.component.ts
View file @
6b8b4df6
import
{
Component
,
Input
,
OnChanges
,
OnInit
,
SimpleChanges
}
from
'@angular/core'
;
import
{
Component
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
ChartModule
,
WaterfallSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
}
from
'@syncfusion/ej2-angular-charts'
;
import
{
DatasetModel
}
from
'../../models/widgets.model'
;
import
{
DatasetService
}
from
'../../services/dataset.service'
;
import
{
HttpClient
,
HttpClientModule
}
from
'@angular/common/http'
;
import
{
DashboardStateService
}
from
'../../services/dashboard-state.service'
;
import
{
BaseWidgetComponent
}
from
'../base-widget.component'
;
@
Component
({
selector
:
'app-waterfall-chart-widget'
,
standalone
:
true
,
imports
:
[
CommonModule
,
ChartModule
,
HttpClientModule
],
providers
:
[
WaterfallSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
,
DatasetService
,
HttpClient
],
imports
:
[
CommonModule
,
ChartModule
],
providers
:
[
WaterfallSeriesService
,
CategoryService
,
LegendService
,
TooltipService
,
DataLabelService
],
templateUrl
:
'./waterfall-chart-widget.component.html'
,
})
export
class
WaterfallChartWidgetComponent
implements
OnInit
,
OnChanges
{
@
Input
()
config
:
any
;
export
class
WaterfallChartWidgetComponent
extends
BaseWidgetComponent
{
public
chartData
:
Object
[];
public
title
:
string
=
'Waterfall Chart'
;
public
primaryXAxis
:
Object
;
public
primaryYAxis
:
Object
;
constructor
(
private
datasetService
:
DatasetService
,
private
http
:
HttpClient
)
{
}
ngOnInit
():
void
{
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
'Category'
};
this
.
primaryYAxis
=
{
title
:
'Amount'
};
if
(
!
this
.
config
||
!
this
.
config
.
datasetId
)
{
this
.
chartData
=
[
{
category
:
'Starting Balance'
,
amount
:
10000
},
{
category
:
'Revenue'
,
amount
:
5000
},
{
category
:
'Expenses'
,
amount
:
-
2000
},
{
category
:
'Investments'
,
amount
:
3000
},
{
category
:
'Ending Balance'
,
amount
:
16000
},
];
}
constructor
(
protected
override
dashboardStateService
:
DashboardStateService
)
{
super
(
dashboardStateService
);
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'config'
]
&&
this
.
config
&&
this
.
config
.
datasetId
)
{
this
.
loadData
();
}
else
if
(
changes
[
'config'
]
&&
(
!
this
.
config
||
!
this
.
config
.
datasetId
))
{
this
.
chartData
=
[
{
category
:
'Starting Balance'
,
amount
:
10000
},
{
category
:
'Revenue'
,
amount
:
5000
},
{
category
:
'Expenses'
,
amount
:
-
2000
},
{
category
:
'Investments'
,
amount
:
3000
},
{
category
:
'Ending Balance'
,
amount
:
16000
},
];
}
onDataUpdate
(
data
:
any
[]):
void
{
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
this
.
config
.
xAxisTitle
||
''
};
this
.
primaryYAxis
=
{
title
:
this
.
config
.
yAxisTitle
||
''
};
}
loadData
():
void
{
if
(
this
.
config
.
datasetId
)
{
this
.
datasetService
.
getDatasetById
(
this
.
config
.
datasetId
).
subscribe
((
dataset
:
DatasetModel
|
undefined
)
=>
{
if
(
dataset
&&
dataset
.
url
)
{
this
.
http
.
get
<
any
[]
>
(
dataset
.
url
).
subscribe
(
data
=>
{
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
this
.
config
.
xField
],
y
:
item
[
this
.
config
.
yField
]
}));
if
(
this
.
config
.
title
)
{
this
.
title
=
this
.
config
.
title
;
}
if
(
this
.
config
.
xAxisTitle
)
{
this
.
primaryXAxis
=
{
...
this
.
primaryXAxis
,
title
:
this
.
config
.
xAxisTitle
};
}
if
(
this
.
config
.
yAxisTitle
)
{
this
.
primaryYAxis
=
{
...
this
.
primaryYAxis
,
title
:
this
.
config
.
yAxisTitle
};
}
});
}
});
}
onReset
():
void
{
this
.
title
=
'Waterfall Chart (Default)'
;
this
.
chartData
=
[
{
x
:
'Initial'
,
y
:
100
},
{
x
:
'Sales'
,
y
:
50
},
{
x
:
'Expenses'
,
y
:
-
20
},
{
x
:
'Refunds'
,
y
:
-
15
},
{
x
:
'Final'
,
isSum
:
true
}
];
this
.
primaryXAxis
=
{
valueType
:
'Category'
,
title
:
'Category'
};
this
.
primaryYAxis
=
{
title
:
'Amount'
};
}
}
ข้อเสนอแนะเพื่อการปรับปรุง.txt
0 → 100644
View file @
6b8b4df6
ข้อเสนอแนะเพื่อการปรับปรุง
ข้อเสนอแนะเพื่อการปรับปรุง
โค้ดโดยรวมเขียนได้ดีครับ แต่มีบางจุดที่สามารถปรับปรุงเพื่อให้ดูแลรักษาง่ายและขยายความสามารถในอนาคตได้ดียิ่งขึ้นครับ
2. ปรับปรุงหน้าจอตั้งค่า Widget (`widget-config.component`):
* ปัญหา: ไฟล์ widget-config.component.html มี *ngIf จำนวนมากเพื่อแสดงฟอร์มสำหรับวิดเจ็ตแต่ละประเภท ซึ่งจะทำให้ไฟล์นี้ใหญ่และซับซ้อนขึ้นเรื่อยๆ เมื่อมีวิดเจ็ตใหม่ๆ เพิ่มเข้ามา
* ข้อเสนอแนะ: อาจจะแยกฟอร์มการตั้งค่าของแต่ละวิดเจ็ตออกเป็นคอมโพเนนต์ของตัวเอง แล้วให้ widget-config.component ทำหน้าที่แค่โหลดคอมโพเนนต์ฟอร์มนั้นๆ แบบไดนามิก (คล้ายกับวิธีที่แดชบอร์ดโหลดวิดเจ็ต)
3. แยกการลงทะเบียน Widget (`widgetComponentMap`):
* ปัญหา: ปัจจุบันการเพิ่มวิดเจ็ตใหม่จะต้องมีการแก้ไข widgetComponentMap ใน dashboard-management.component.ts ทุกครั้ง
* ข้อเสนอแนะ: สร้าง WidgetRegistryService เพื่อทำหน้าที่ลงทะเบียนวิดเจ็ตโดยเฉพาะ จะช่วยให้ dashboard-management.component สะอาดขึ้น และการเพิ่มวิดเจ็ตใหม่จะไม่ต้องไปยุ่งกับโค้ดส่วนกลาง
โดยรวมแล้วฟังก์ชันการทำงานของแดชบอร์ดสมบูรณ์ดีครับ ข้อเสนอแนะเหล่านี้เป็นแนวทางในการปรับปรุงโครงสร้างเพื่อรองรับการพัฒนาในระยะยาวครับ
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