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
ed89bf27
Commit
ed89bf27
authored
Oct 27, 2025
by
sawit
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
แก้ไข widget
parent
aac8ace4
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
279 additions
and
59 deletions
+279
-59
dashboard-management.component.scss
.../dashboard-management/dashboard-management.component.scss
+21
-21
dashboard-management.component.ts
...ge/dashboard-management/dashboard-management.component.ts
+6
-0
dashboard-viewer.component.scss
...nagement/dashboard-viewer/dashboard-viewer.component.scss
+0
-0
dashboard-viewer.component.ts
...management/dashboard-viewer/dashboard-viewer.component.ts
+8
-1
widget-config.component.html
...ard-management/widget-config/widget-config.component.html
+17
-5
widget-config.component.ts
...board-management/widget-config/widget-config.component.ts
+4
-1
bar-chart-widget.component.ts
...nt/widgets/bar-chart-widget/bar-chart-widget.component.ts
+60
-4
base-widget.component.ts
...age/dashboard-management/widgets/base-widget.component.ts
+16
-2
pie-chart-widget.component.ts
...nt/widgets/pie-chart-widget/pie-chart-widget.component.ts
+78
-9
simple-kpi-widget.component.ts
.../widgets/simple-kpi-widget/simple-kpi-widget.component.ts
+29
-2
syncfusion-datagrid-widget.component.html
...datagrid-widget/syncfusion-datagrid-widget.component.html
+1
-0
syncfusion-datagrid-widget.component.scss
...datagrid-widget/syncfusion-datagrid-widget.component.scss
+25
-0
syncfusion-datagrid-widget.component.ts
...n-datagrid-widget/syncfusion-datagrid-widget.component.ts
+9
-9
syncfusion-pivot-widget.component.ts
...cfusion-pivot-widget/syncfusion-pivot-widget.component.ts
+5
-5
No files found.
src/app/portal-manage/dashboard-management/dashboard-management.component.scss
View file @
ed89bf27
...
...
@@ -135,7 +135,7 @@
border-radius
:
0
.75rem
;
box-shadow
:
0
10px
15px
-3px
rgba
(
0
,
0
,
0
,
0
.1
)
,
0
4px
6px
-2px
rgba
(
0
,
0
,
0
,
0
.05
);
border
:
1px
solid
#e5e7eb
;
min-height
:
6
00px
;
min-height
:
12
00px
;
}
/* Empty State */
...
...
@@ -144,7 +144,7 @@
flex-direction
:
column
;
justify-content
:
center
;
align-items
:
center
;
height
:
6
00px
;
height
:
12
00px
;
text-align
:
center
;
color
:
#6b7280
;
}
...
...
@@ -437,39 +437,39 @@
/* Ensure widgets don't exceed panel boundaries and fill full space */
.e-panel
.e-panel-container
.content
>
*
{
max-width
:
100%
!
important
;
max-height
:
100%
!
important
;
width
:
100%
!
important
;
height
:
100%
!
important
;
overflow
:
hidden
!
important
;
box-sizing
:
border-box
!
important
;
flex
:
1
!
important
;
display
:
flex
!
important
;
flex-direction
:
column
!
important
;
max-width
:
100%
!
important
;
min-width
:
0
!
important
;
}
/* Specific
widget component styling
*/
.e-panel
.e-panel-container
.content
>
*
{
/* Specific
styling for Syncfusion Datagrid Widget
*/
.e-panel
.e-panel-container
.content
app-syncfusion-datagrid-widget
{
width
:
100%
!
important
;
height
:
100%
!
important
;
flex
:
1
!
important
;
display
:
flex
!
important
;
flex-direction
:
column
!
important
;
min-height
:
0
!
important
;
}
/* Ensure all child elements fill the space */
.e-panel
.e-panel-container
.content
>
*
>
*
{
width
:
100%
!
important
;
height
:
100%
!
important
;
flex
:
1
!
important
;
min-height
:
0
!
important
;
margin
:
0
!
important
;
padding
:
0
!
important
;
}
/* Force all nested elements to use full space */
.e-panel
.e-panel-container
.content
*
{
box-sizing
:
border-box
!
important
;
}
/* Remove margin/padding from grid container and grid */
.e-panel
.e-panel-container
.content
app-syncfusion-datagrid-widget
.grid-container
{
margin
:
0
!
important
;
padding
:
0
!
important
;
}
.e-panel
.e-panel-container
.content
app-syncfusion-datagrid-widget
.grid-container
.ejs-grid
{
margin
:
0
!
important
;
padding
:
0
!
important
;
width
:
100%
!
important
;
max-width
:
100%
!
important
;
}
}
}
...
...
src/app/portal-manage/dashboard-management/dashboard-management.component.ts
View file @
ed89bf27
...
...
@@ -486,6 +486,12 @@ export class DashboardManagementComponent implements OnInit, OnDestroy {
originalWidget
:
widget
};
// Force SyncfusionDatagridWidgetComponent to use full width
if
(
widget
.
component
===
'SyncfusionDatagridWidgetComponent'
)
{
panel
.
sizeX
=
this
.
columns
;
panel
.
maxSizeX
=
this
.
columns
;
}
return
panel
;
})
...
...
src/app/portal-manage/dashboard-management/dashboard-viewer/dashboard-viewer.component.scss
View file @
ed89bf27
This diff is collapsed.
Click to expand it.
src/app/portal-manage/dashboard-management/dashboard-viewer/dashboard-viewer.component.ts
View file @
ed89bf27
...
...
@@ -163,7 +163,7 @@ export class DashboardViewerComponent implements OnInit, OnDestroy {
return
null
;
}
return
{
const
panel
=
{
id
:
`
${(
widget
as
any
).
instanceId
}
-
${
widget
.
y
}
-
${
widget
.
x
}
`
,
header
:
widget
.
thName
,
sizeX
:
widget
.
cols
||
4
,
// Default size if not specified
...
...
@@ -177,6 +177,13 @@ export class DashboardViewerComponent implements OnInit, OnDestroy {
data
:
JSON
.
stringify
(
dataObject
)
},
};
// Force SyncfusionDatagridWidgetComponent to use full width in viewer
if
(
widget
.
component
===
'SyncfusionDatagridWidgetComponent'
)
{
panel
.
sizeX
=
this
.
columns
;
}
return
panel
;
})
.
filter
(
panel
=>
panel
!==
null
)
as
DashboardPanel
[];
}
...
...
src/app/portal-manage/dashboard-management/widget-config/widget-config.component.html
View file @
ed89bf27
...
...
@@ -609,6 +609,10 @@
<div
*
ngIf=
"widgetType === 'PieChartWidgetComponent' || widgetType === 'BarChartWidgetComponent' || widgetType === 'AreaChartWidgetComponent' || widgetType === 'DoughnutChartWidgetComponent' || widgetType === 'FunnelChartWidgetComponent'"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
X-Axis Field
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
xField
"
name=
"xField"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
...
...
@@ -620,18 +624,26 @@
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance=
"fill"
*
ngIf=
"widgetType === 'PieChartWidgetComponent' || widgetType === 'DoughnutChartWidgetComponent' || widgetType === 'FunnelChartWidgetComponent'"
>
<mat-label>
Label Field
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
labelField
"
name=
"labelField"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance=
"fill"
*
ngIf=
"widgetType === 'PieChartWidgetComponent' || widgetType === 'DoughnutChartWidgetComponent' || widgetType === 'FunnelChartWidgetComponent'"
>
<mat-label>
Value Field
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
valueField
"
name=
"valueField"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Aggregation
(for Pie/Doughnut/Funnel)
</mat-label>
<mat-label>
Aggregation
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
aggregation
"
name=
"aggregation"
>
<mat-option
value=
"none"
>
None
</mat-option>
<mat-option
value=
"count"
>
Count
</mat-option>
<mat-option
value=
"sum"
>
Sum
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
</mat-form-field>
</div>
<div
*
ngIf=
"widgetType === 'GaugeChartWidgetComponent'"
>
...
...
src/app/portal-manage/dashboard-management/widget-config/widget-config.component.ts
View file @
ed89bf27
...
...
@@ -218,9 +218,11 @@ export class WidgetConfigComponent implements OnInit, AfterViewInit, OnDestroy {
if
(
!
this
.
currentConfig
.
clickAction
)
this
.
currentConfig
.
clickAction
=
'none'
;
// Layout configuration defaults
if
(
this
.
currentConfig
.
width
===
undefined
)
this
.
currentConfig
.
width
=
6
00
;
if
(
this
.
currentConfig
.
width
===
undefined
)
this
.
currentConfig
.
width
=
1
00
;
if
(
this
.
currentConfig
.
height
===
undefined
)
this
.
currentConfig
.
height
=
400
;
if
(
this
.
currentConfig
.
responsive
===
undefined
)
this
.
currentConfig
.
responsive
=
true
;
if
(
this
.
currentConfig
.
widthUnit
===
undefined
)
this
.
currentConfig
.
widthUnit
=
'%'
;
if
(
this
.
currentConfig
.
fullWidth
===
undefined
)
this
.
currentConfig
.
fullWidth
=
true
;
// Data configuration defaults
if
(
!
this
.
currentConfig
.
dataSource
)
this
.
currentConfig
.
dataSource
=
'static'
;
...
...
@@ -257,6 +259,7 @@ export class WidgetConfigComponent implements OnInit, AfterViewInit, OnDestroy {
if
(
!
this
.
currentConfig
.
yAxisField
)
this
.
currentConfig
.
yAxisField
=
''
;
if
(
!
this
.
currentConfig
.
valueField
)
this
.
currentConfig
.
valueField
=
''
;
if
(
!
this
.
currentConfig
.
labelField
)
this
.
currentConfig
.
labelField
=
''
;
if
(
!
this
.
currentConfig
.
aggregation
)
this
.
currentConfig
.
aggregation
=
'count'
;
if
(
!
this
.
currentConfig
.
apiEndpoint
)
this
.
currentConfig
.
apiEndpoint
=
''
;
// Chart options defaults
...
...
src/app/portal-manage/dashboard-management/widgets/bar-chart-widget/bar-chart-widget.component.ts
View file @
ed89bf27
...
...
@@ -31,20 +31,76 @@ export class BarChartWidgetComponent extends BaseWidgetComponent {
}
onDataUpdate
(
data
:
any
[]):
void
{
if
(
!
data
||
data
.
length
===
0
)
{
// Check if data is an array
if
(
!
Array
.
isArray
(
data
)
||
data
.
length
===
0
)
{
this
.
chartData
=
[];
return
;
}
const
xField
=
this
.
configObj
.
xField
||
'x'
;
const
yField
=
this
.
configObj
.
yField
||
'y'
;
// Support multiple field name formats from config
const
xField
=
this
.
configObj
.
xField
||
this
.
configObj
.
xAxisField
||
'x'
;
const
yField
=
this
.
configObj
.
yField
||
this
.
configObj
.
yAxisField
||
'y'
;
const
valueField
=
this
.
configObj
.
valueField
||
this
.
configObj
.
aggregation
?
this
.
configObj
.
valueField
:
'y'
;
console
.
log
(
'BarChart onDataUpdate:'
,
{
data
:
data
.
slice
(
0
,
3
),
xField
,
yField
,
valueField
,
aggregation
:
this
.
configObj
.
aggregation
,
config
:
this
.
configObj
});
// Handle aggregation if needed
// Auto-detect if using ID field
let
effectiveAggregation
=
this
.
configObj
.
aggregation
;
if
(
this
.
configObj
.
aggregation
===
'sum'
&&
valueField
)
{
const
fieldLower
=
valueField
.
toLowerCase
();
if
(
fieldLower
.
includes
(
'id'
)
||
(
fieldLower
.
includes
(
'employee'
)
&&
fieldLower
.
includes
(
'id'
)))
{
effectiveAggregation
=
'count'
;
console
.
warn
(
`BarChart: Detected ID field "
${
valueField
}
", switching from sum to count`
);
}
}
if
(
effectiveAggregation
===
'sum'
||
effectiveAggregation
===
'count'
)
{
const
groupedData
=
data
.
reduce
((
acc
,
item
)
=>
{
const
key
=
item
[
xField
]
||
''
;
if
(
!
acc
[
key
])
{
acc
[
key
]
=
0
;
}
if
(
effectiveAggregation
===
'count'
)
{
// Count: เพียงแค่นับจำนวน record
acc
[
key
]
+=
1
;
}
else
if
(
effectiveAggregation
===
'sum'
&&
valueField
&&
item
[
valueField
])
{
// Sum: ใช้ valueField ในการรวมค่า (เช่น salary, amount)
const
value
=
typeof
item
[
valueField
]
===
'number'
?
item
[
valueField
]
:
parseFloat
(
item
[
valueField
])
||
0
;
acc
[
key
]
+=
value
;
}
else
{
// ถ้าไม่มี valueField หรือไม่ใช่ number ให้ใช้ count แทน
acc
[
key
]
+=
1
;
}
return
acc
;
},
{});
this
.
chartData
=
Object
.
keys
(
groupedData
).
map
(
key
=>
({
x
:
key
,
y
:
groupedData
[
key
]
}));
}
else
{
// No aggregation - map directly
const
fieldToUse
=
valueField
||
yField
;
this
.
chartData
=
data
.
map
(
item
=>
({
x
:
item
[
xField
]
||
''
,
y
:
item
[
yField
]
||
0
y
:
item
[
fieldToUse
]
||
0
}));
}
console
.
log
(
'BarChart chartData:'
,
this
.
chartData
.
slice
(
0
,
5
));
}
onReset
():
void
{
this
.
title
=
'Bar Chart (Default)'
;
this
.
chartData
=
[
...
...
src/app/portal-manage/dashboard-management/widgets/base-widget.component.ts
View file @
ed89bf27
...
...
@@ -35,22 +35,34 @@ export abstract class BaseWidgetComponent implements OnInit, OnDestroy {
next
:
(
selectedDataset
:
SelectedDataset
|
null
)
=>
{
this
.
isLoading
=
true
;
this
.
hasError
=
false
;
if
(
selectedDataset
&&
selectedDataset
.
data
)
{
console
.
log
(
'BaseWidget received dataset:'
,
{
selectedDataset
,
hasData
:
selectedDataset
?.
data
?.
length
,
widgetConfig
:
this
.
configObj
});
if
(
selectedDataset
&&
selectedDataset
.
data
&&
Array
.
isArray
(
selectedDataset
.
data
))
{
try
{
this
.
originalData
=
selectedDataset
.
data
;
console
.
log
(
'BaseWidget applying filters on data of length:'
,
this
.
originalData
.
length
);
this
.
applyFilters
();
console
.
log
(
'BaseWidget filtered data length:'
,
this
.
filteredData
.
length
);
this
.
onDataUpdate
(
this
.
filteredData
);
this
.
isLoading
=
false
;
}
catch
(
error
)
{
console
.
error
(
'BaseWidget error in data processing:'
,
error
);
this
.
handleError
(
error
);
}
}
else
{
console
.
warn
(
'BaseWidget no dataset available, showing loading state'
);
// If no dataset is selected, just keep showing the initial config with a loading state.
// The initial state is set by applyInitialConfig().
this
.
isLoading
=
true
;
}
},
error
:
(
err
)
=>
{
console
.
error
(
'BaseWidget error in subscription:'
,
err
);
this
.
handleError
(
err
);
}
});
...
...
@@ -105,7 +117,9 @@ export abstract class BaseWidgetComponent implements OnInit, OnDestroy {
// Parse data string to array
try
{
this
.
originalData
=
JSON
.
parse
(
this
.
data
);
const
parsedData
=
JSON
.
parse
(
this
.
data
);
// Ensure parsed data is an array, not an object
this
.
originalData
=
Array
.
isArray
(
parsedData
)
?
parsedData
:
[];
}
catch
(
error
)
{
console
.
warn
(
'Failed to parse data JSON:'
,
error
);
this
.
originalData
=
[];
...
...
src/app/portal-manage/dashboard-management/widgets/pie-chart-widget/pie-chart-widget.component.ts
View file @
ed89bf27
...
...
@@ -21,33 +21,102 @@ export class PieChartWidgetComponent extends BaseWidgetComponent {
applyInitialConfig
():
void
{
this
.
title
=
this
.
configObj
.
title
||
'Pie Chart'
;
this
.
legendSettings
=
{
visible
:
true
};
this
.
legendSettings
=
{
visible
:
true
,
position
:
'Bottom'
,
textStyle
:
{
size
:
'14px'
},
height
:
'50px'
,
width
:
'100%'
,
alignment
:
'Center'
};
this
.
chartData
=
[];
}
onDataUpdate
(
data
:
any
[]):
void
{
if
(
!
data
||
data
.
length
===
0
)
{
// Check if data is an array
if
(
!
Array
.
isArray
(
data
)
||
data
.
length
===
0
)
{
this
.
chartData
=
[];
return
;
}
const
xField
=
this
.
configObj
.
xField
||
'x'
;
const
yField
=
this
.
configObj
.
yField
||
'y'
;
// Support multiple field name formats from config
const
labelField
=
this
.
configObj
.
labelField
||
this
.
configObj
.
xField
||
this
.
configObj
.
xAxisField
||
'x'
;
const
valueField
=
this
.
configObj
.
valueField
||
this
.
configObj
.
yField
||
this
.
configObj
.
yAxisField
||
'y'
;
console
.
log
(
'PieChart onDataUpdate:'
,
{
title
:
this
.
title
,
dataLength
:
data
.
length
,
labelField
,
valueField
,
aggregation
:
this
.
configObj
.
aggregation
,
sampleData
:
data
.
slice
(
0
,
2
)
});
let
transformedData
=
data
;
if
(
this
.
configObj
.
aggregation
===
'count'
)
{
// Handle aggregation if needed
// Default to count if using any id field as value
let
effectiveAggregation
=
this
.
configObj
.
aggregation
;
if
(
this
.
configObj
.
aggregation
===
'sum'
&&
valueField
)
{
const
fieldLower
=
valueField
.
toLowerCase
();
if
(
fieldLower
.
includes
(
'id'
)
||
fieldLower
.
includes
(
'employee'
)
&&
fieldLower
.
includes
(
'id'
))
{
effectiveAggregation
=
'count'
;
console
.
warn
(
`Detected ID field "
${
valueField
}
", switching from sum to count aggregation`
);
}
}
console
.
log
(
'Effective aggregation:'
,
effectiveAggregation
,
'original:'
,
this
.
configObj
.
aggregation
,
'labelField:'
,
labelField
,
'valueField:'
,
valueField
);
if
(
effectiveAggregation
===
'count'
||
(
effectiveAggregation
===
'sum'
&&
!
valueField
))
{
// Count: นับจำนวนรายการตาม labelField
// หรือ Sum ถ้าไม่ระบุ valueField (คล้าย count)
const
counts
=
transformedData
.
reduce
((
acc
,
item
)
=>
{
const
key
=
item
[
x
Field
]
||
''
;
const
key
=
item
[
label
Field
]
||
''
;
acc
[
key
]
=
(
acc
[
key
]
||
0
)
+
1
;
return
acc
;
},
{});
transformedData
=
Object
.
keys
(
counts
).
map
(
key
=>
({
x
:
key
,
y
:
counts
[
key
]
}));
transformedData
=
Object
.
keys
(
counts
).
map
(
key
=>
({
x
:
key
||
'(Empty)'
,
y
:
counts
[
key
],
text
:
`
${
key
||
'(Empty)'
}
:
${
counts
[
key
]}
`
}));
console
.
log
(
'PieChart using COUNT aggregation, result:'
,
transformedData
);
}
else
if
(
effectiveAggregation
===
'sum'
&&
valueField
)
{
// Sum: รวมค่า valueField ตาม labelField (เฉพาะกรณีที่ระบุ valueField)
// ตรวจสอบว่า valueField มีค่าที่สมเหตุสมผลหรือไม่
const
groupedData
=
transformedData
.
reduce
((
acc
,
item
)
=>
{
const
key
=
item
[
labelField
]
||
''
;
const
value
=
item
[
valueField
]
||
0
;
if
(
!
acc
[
key
])
{
acc
[
key
]
=
0
;
}
// ถ้า value เป็น ID หรือ string ที่ไม่ได้เป็นตัวเลข ให้ไม่ sum
if
(
typeof
value
===
'string'
&&
isNaN
(
Number
(
value
)))
{
// ถ้าเป็น ID หรือ string ธรรมดา ให้ข้าม
return
acc
;
}
acc
[
key
]
+=
typeof
value
===
'number'
?
value
:
parseFloat
(
value
)
||
0
;
return
acc
;
},
{});
transformedData
=
Object
.
keys
(
groupedData
).
map
(
key
=>
({
x
:
key
||
'(Empty)'
,
y
:
groupedData
[
key
],
text
:
`
${
key
||
'(Empty)'
}
:
${
groupedData
[
key
]}
`
}));
}
else
{
// No aggregation: map directly
transformedData
=
transformedData
.
map
(
item
=>
({
x
:
item
[
xField
]
||
''
,
y
:
item
[
yField
]
||
0
x
:
item
[
labelField
]
||
'(Empty)'
,
y
:
item
[
valueField
]
||
0
,
text
:
`
${
item
[
labelField
]
||
'(Empty)'
}
:
${
item
[
valueField
]
||
0
}
`
}));
}
console
.
log
(
'PieChart chartData:'
,
transformedData
.
slice
(
0
,
10
));
this
.
chartData
=
transformedData
;
}
...
...
src/app/portal-manage/dashboard-management/widgets/simple-kpi-widget/simple-kpi-widget.component.ts
View file @
ed89bf27
...
...
@@ -325,17 +325,37 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
return
;
}
console
.
log
(
'SimpleKpiWidget onDataUpdate:'
,
{
title
:
this
.
title
,
dataLength
:
data
?.
length
,
hasFilter
:
this
.
enableFilter
,
filterField
:
this
.
filterField
,
filterValue
:
this
.
filterValue
});
// Transform data if transform function is provided
let
transformedData
=
this
.
transformData
(
data
);
// Apply filtering if enabled
if
(
this
.
enableFilter
&&
this
.
filterField
&&
this
.
filterValue
)
{
transformedData
=
this
.
applyFilter
(
transformedData
);
console
.
log
(
'SimpleKpiWidget after filter:'
,
{
filteredLength
:
transformedData
?.
length
});
}
// Handle count aggregation separately as it doesn't need a valueField
if
(
this
.
aggregation
===
'count'
)
{
// If valueField is specified and it's a date/unique field, count unique values
if
(
this
.
valueField
&&
[
'dateid'
,
'date'
,
'datetime'
,
'day'
].
some
(
part
=>
this
.
valueField
.
toLowerCase
().
includes
(
part
)))
{
// Count unique values instead of all records
const
uniqueValues
=
new
Set
(
transformedData
.
map
(
item
=>
item
[
this
.
valueField
]));
this
.
value
=
uniqueValues
.
size
.
toLocaleString
();
console
.
log
(
`SimpleKpiWidget counting unique
${
this
.
valueField
}
:
${
uniqueValues
.
size
}
from
${
transformedData
.
length
}
records`
);
}
else
{
// Regular count: count all records
this
.
value
=
(
transformedData
?.
length
||
0
).
toLocaleString
();
}
this
.
updateTrendData
(
transformedData
);
this
.
updateLabel
(
transformedData
);
return
;
...
...
@@ -914,15 +934,22 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
return
data
;
}
console
.
log
(
'SimpleKpiWidget applying filter:'
,
{
filterField
:
this
.
filterField
,
filterValue
:
this
.
filterValue
,
filterOperator
:
this
.
filterOperator
,
dataLength
:
data
?.
length
});
return
data
.
filter
(
item
=>
{
const
fieldValue
=
item
[
this
.
filterField
];
const
filterValue
=
this
.
filterValue
;
switch
(
this
.
filterOperator
)
{
case
'equals'
:
return
fieldValue
==
filterValue
;
return
String
(
fieldValue
).
toLowerCase
()
===
String
(
filterValue
).
toLowerCase
()
;
case
'not_equals'
:
return
fieldValue
!=
filterValue
;
return
String
(
fieldValue
).
toLowerCase
()
!==
String
(
filterValue
).
toLowerCase
()
;
case
'greater_than'
:
return
Number
(
fieldValue
)
>
Number
(
filterValue
);
case
'less_than'
:
...
...
src/app/portal-manage/dashboard-management/widgets/syncfusion-datagrid-widget/syncfusion-datagrid-widget.component.html
View file @
ed89bf27
...
...
@@ -115,6 +115,7 @@
(
actionComplete
)="
actionComplete
($
event
)"
(
dataBound
)="
onDataBound
($
event
)"
[
height
]="'
100
%'"
[
width
]="'
100
%'"
[
allowResizing
]="
allowResizing
"
[
rowHeight
]="
rowHeight
"
>
...
...
src/app/portal-manage/dashboard-management/widgets/syncfusion-datagrid-widget/syncfusion-datagrid-widget.component.scss
View file @
ed89bf27
...
...
@@ -5,6 +5,9 @@
height
:
100%
;
position
:
relative
;
overflow
:
hidden
;
width
:
100%
;
margin
:
0
;
padding
:
0
;
// Widget Header
.widget-header
{
...
...
@@ -300,6 +303,10 @@
background
:
white
;
border-radius
:
8px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0
.1
);
width
:
100%
;
height
:
100%
;
margin
:
0
;
padding
:
0
;
}
// Interaction States
...
...
@@ -454,10 +461,18 @@
.e-grid
{
border
:
none
!
important
;
font-family
:
inherit
;
width
:
100%
!
important
;
height
:
100%
!
important
;
min-width
:
0
!
important
;
min-height
:
0
!
important
;
margin
:
0
!
important
;
padding
:
0
!
important
;
max-width
:
100%
!
important
;
.e-gridheader
{
background-color
:
inherit
;
border-bottom
:
1px
solid
rgba
(
229
,
231
,
235
,
0
.5
);
width
:
100%
!
important
;
.e-headercell
{
background-color
:
inherit
;
...
...
@@ -466,6 +481,10 @@
font-size
:
13px
;
padding
:
12px
8px
;
border-right
:
1px
solid
rgba
(
229
,
231
,
235
,
0
.3
);
min-width
:
0
!
important
;
overflow
:
hidden
!
important
;
text-overflow
:
ellipsis
!
important
;
white-space
:
nowrap
!
important
;
&
:last-child
{
border-right
:
none
;
...
...
@@ -475,6 +494,8 @@
.e-content
{
background-color
:
transparent
;
width
:
100%
!
important
;
min-width
:
0
!
important
;
.e-row
{
border-bottom
:
1px
solid
rgba
(
229
,
231
,
235
,
0
.2
);
...
...
@@ -497,6 +518,10 @@
border-right
:
1px
solid
rgba
(
229
,
231
,
235
,
0
.2
);
font-size
:
13px
;
color
:
inherit
;
min-width
:
0
!
important
;
overflow
:
hidden
!
important
;
text-overflow
:
ellipsis
!
important
;
white-space
:
nowrap
!
important
;
&
:last-child
{
border-right
:
none
;
...
...
src/app/portal-manage/dashboard-management/widgets/syncfusion-datagrid-widget/syncfusion-datagrid-widget.component.ts
View file @
ed89bf27
...
...
@@ -167,7 +167,7 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
public
animateConditionalChange
:
boolean
=
true
;
// Layout properties
public
width
:
number
=
6
00
;
public
width
:
number
=
1
00
;
public
height
:
number
=
400
;
public
minWidth
:
number
=
400
;
public
minHeight
:
number
=
300
;
...
...
@@ -175,11 +175,11 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
public
maxHeight
:
number
=
800
;
public
aspectRatio
:
string
=
'auto'
;
public
responsive
:
boolean
=
true
;
public
widthUnit
:
string
=
'
px
'
;
public
widthUnit
:
string
=
'
%
'
;
public
heightUnit
:
string
=
'px'
;
public
fullWidth
:
boolean
=
fals
e
;
public
fullWidth
:
boolean
=
tru
e
;
public
fullHeight
:
boolean
=
false
;
public
sizeOption
:
string
=
'
medium
'
;
public
sizeOption
:
string
=
'
full-width
'
;
// Data properties
public
dataSource
:
string
=
'static'
;
...
...
@@ -392,7 +392,7 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
this
.
customClickHandler
=
this
.
configObj
.
customClickHandler
||
''
;
// Layout configuration
this
.
width
=
this
.
configObj
.
width
||
6
00
;
this
.
width
=
this
.
configObj
.
width
||
1
00
;
this
.
height
=
this
.
configObj
.
height
||
400
;
this
.
minWidth
=
this
.
configObj
.
minWidth
||
400
;
this
.
minHeight
=
this
.
configObj
.
minHeight
||
300
;
...
...
@@ -400,9 +400,9 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
this
.
maxHeight
=
this
.
configObj
.
maxHeight
||
800
;
this
.
aspectRatio
=
this
.
configObj
.
aspectRatio
||
'auto'
;
this
.
responsive
=
this
.
configObj
.
responsive
!==
undefined
?
this
.
configObj
.
responsive
:
true
;
this
.
widthUnit
=
this
.
configObj
.
widthUnit
||
'
px
'
;
this
.
widthUnit
=
this
.
configObj
.
widthUnit
||
'
%
'
;
this
.
heightUnit
=
this
.
configObj
.
heightUnit
||
'px'
;
this
.
fullWidth
=
this
.
configObj
.
fullWidth
!==
undefined
?
this
.
configObj
.
fullWidth
:
fals
e
;
this
.
fullWidth
=
this
.
configObj
.
fullWidth
!==
undefined
?
this
.
configObj
.
fullWidth
:
tru
e
;
this
.
fullHeight
=
this
.
configObj
.
fullHeight
!==
undefined
?
this
.
configObj
.
fullHeight
:
false
;
this
.
sizeOption
=
this
.
configObj
.
sizeOption
||
'medium'
;
...
...
@@ -843,8 +843,8 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
'border-color'
:
this
.
borderColor
,
'border-radius'
:
`
${
this
.
borderRadius
}
px`
,
'border-width'
:
`
${
this
.
borderWidth
}
px`
,
'padding'
:
`
${
this
.
padding
}
px`
,
'margin'
:
`
${
this
.
margin
}
px`
,
'padding'
:
'0px'
,
// Force no padding for full width
'margin'
:
'0px'
,
// Force no margin for full width
'font-size'
:
`
${
this
.
fontSize
}
px`
,
'font-weight'
:
this
.
fontWeight
,
'font-family'
:
this
.
fontFamily
,
...
...
src/app/portal-manage/dashboard-management/widgets/syncfusion-pivot-widget/syncfusion-pivot-widget.component.ts
View file @
ed89bf27
...
...
@@ -469,11 +469,11 @@ export class SyncfusionPivotWidgetComponent extends BaseWidgetComponent implemen
onDataBound
(
args
:
any
):
void
{
// Apply perspective after data is loaded and rendered, but only once.
if
(
this
.
perspective
&&
!
this
.
isPerspectiveApplied
&&
this
.
perspective
!==
'{}'
)
{
setTimeout
(()
=>
{
this
.
setWidgetState
(
this
.
perspective
as
string
);
},
50
);
// Small delay to ensure rendering is complete
}
//
if (this.perspective && !this.isPerspectiveApplied && this.perspective !== '{}') {
//
setTimeout(() => {
//
this.setWidgetState(this.perspective as string);
//
}, 50); // Small delay to ensure rendering is complete
//
}
}
onReset
():
void
{
...
...
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