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
7d32073b
Commit
7d32073b
authored
Sep 02, 2025
by
Ooh-Ao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ดาต้า source
parent
fafd66a2
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
281 additions
and
222 deletions
+281
-222
dashboard-management.component.ts
...ge/dashboard-management/dashboard-management.component.ts
+21
-32
widget-config.component.html
...ard-management/widget-config/widget-config.component.html
+186
-54
widget-config.component.ts
...board-management/widget-config/widget-config.component.ts
+73
-123
dashboard-viewer.component.ts
...tal-manage/dashboard-viewer/dashboard-viewer.component.ts
+1
-13
No files found.
src/app/portal-manage/dashboard-management/dashboard-management.component.ts
View file @
7d32073b
...
@@ -683,43 +683,32 @@ export class DashboardManagementComponent implements OnInit {
...
@@ -683,43 +683,32 @@ export class DashboardManagementComponent implements OnInit {
}
}
saveLayout
():
void
{
saveLayout
():
void
{
console
.
log
(
'saveLayout called. Dashboard data to save:'
,
this
.
dashboardData
);
if
(
!
this
.
dashboardData
)
return
;
if
(
!
this
.
dashboardData
)
return
;
const
dataFetchTasks
:
Observable
<
any
>
[]
=
this
.
dashboardData
.
widgets
// Create a deep copy to avoid mutating the component's state
.
filter
(
widget
=>
widget
.
component
===
'NewDataTableWidget'
&&
widget
.
config
?.
source
?.
url
)
const
dashboardToSave
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
dashboardData
));
.
map
(
widget
=>
this
.
widgetDataService
.
fetchDataForWidget
(
widget
.
config
).
pipe
(
tap
(
data
=>
{
// Mutate the config object to include the fetched data
widget
.
config
.
data
=
data
;
})
)
);
if
(
dataFetchTasks
.
length
>
0
)
{
// Stringify all widget configs before saving
forkJoin
(
dataFetchTasks
).
subscribe
(()
=>
{
if
(
dashboardToSave
.
widgets
)
{
console
.
log
(
'All data fetched, now saving dashboard...'
);
dashboardToSave
.
widgets
.
forEach
((
widget
:
WidgetModel
)
=>
{
this
.
dashboardDataService
.
saveDashboard
(
this
.
dashboardData
!
).
pipe
(
if
(
widget
.
config
&&
typeof
widget
.
config
===
'object'
)
{
catchError
(
error
=>
{
widget
.
config
=
JSON
.
stringify
(
widget
.
config
);
this
.
notificationService
.
error
(
'Error'
,
'Failed to save dashboard layout.'
);
}
return
throwError
(()
=>
error
);
})
).
subscribe
(()
=>
{
this
.
notificationService
.
success
(
'Success'
,
'Dashboard layout saved successfully!'
);
});
});
}
else
{
console
.
log
(
'No data to fetch, saving dashboard directly...'
);
this
.
dashboardDataService
.
saveDashboard
(
this
.
dashboardData
).
pipe
(
catchError
(
error
=>
{
this
.
notificationService
.
error
(
'Error'
,
'Failed to save dashboard layout.'
);
return
throwError
(()
=>
error
);
})
).
subscribe
(()
=>
{
this
.
notificationService
.
success
(
'Success'
,
'Dashboard layout saved successfully!'
);
});
});
}
}
console
.
log
(
'Saving dashboard with stringified configs:'
,
dashboardToSave
);
this
.
dashboardDataService
.
saveDashboard
(
dashboardToSave
).
pipe
(
catchError
(
error
=>
{
this
.
notificationService
.
error
(
'Error'
,
'Failed to save dashboard layout.'
);
return
throwError
(()
=>
error
);
})
).
subscribe
(()
=>
{
this
.
notificationService
.
success
(
'Success'
,
'Dashboard layout saved successfully!'
);
// Optionally, reload the dashboard to ensure UI is consistent with saved state
this
.
loadSelectedDashboard
();
});
}
}
// Kept for compatibility with the sidebar, which is not the focus of this refactor
// Kept for compatibility with the sidebar, which is not the focus of this refactor
...
...
src/app/portal-manage/dashboard-management/widget-config/widget-config.component.html
View file @
7d32073b
...
@@ -148,24 +148,35 @@
...
@@ -148,24 +148,35 @@
</div>
</div>
<div
*
ngIf=
"widgetType === 'ChartWidgetComponent'"
>
<div
*
ngIf=
"widgetType === 'ChartWidgetComponent'"
>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Title
</mat-label>
<mat-label>
Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
X-Axis Field
</mat-label>
<mat-label>
X-Axis Field
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
xField
"
name=
"xField"
>
<mat-select
[(
ngModel
)]="
currentConfig
.
xField
"
name=
"xField"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-select>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Y-Axis Title
</mat-label>
<mat-label>
Y-Axis Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
yAxisTitle
"
name=
"yAxisTitle"
>
<input
matInput
[(
ngModel
)]="
currentConfig
.
yAxisTitle
"
name=
"yAxisTitle"
>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Y-Axis Fields (JSON Array)
</mat-label>
<!-- Y-Axis Fields -->
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
yFieldsJson
"
name=
"yFieldsJson"
rows=
"5"
></textarea>
<div
class=
"config-section border p-3 rounded-lg mb-4"
>
</mat-form-field>
<h3
class=
"text-lg font-semibold mb-2"
>
Y-Axis Fields
</h3>
<div
*
ngFor=
"let yField of currentConfig.yFields; let i = index"
class=
"flex items-center gap-2 mb-2"
>
<mat-form-field
appearance=
"fill"
class=
"flex-grow"
>
<mat-label>
Field {{i + 1}}
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
yFields
[
i
]"
[
name
]="'
yfield_
'
+
i
"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-form-field>
<button
mat-icon-button
color=
"warn"
(
click
)="
removeChartYField
(
i
)"
type=
"button"
><mat-icon>
delete
</mat-icon></button>
</div>
<button
mat-stroked-button
color=
"primary"
(
click
)="
addChartYField
()"
type=
"button"
><mat-icon>
add
</mat-icon>
Add Y-Axis Field
</button>
</div>
</div>
</div>
<div
*
ngIf=
"widgetType === 'QuickLinksWidgetComponent'"
>
<div
*
ngIf=
"widgetType === 'QuickLinksWidgetComponent'"
>
...
@@ -187,39 +198,127 @@
...
@@ -187,39 +198,127 @@
</mat-form-field>
</mat-form-field>
</div>
</div>
<div
*
ngIf=
"widgetType === 'SyncfusionDatagridWidgetComponent'"
>
<!-- Unified Column Editor for Grid-like Widgets -->
<mat-form-field
appearance=
"fill"
>
<ng-container
*
ngIf=
"widgetType === 'SyncfusionDatagridWidgetComponent' || widgetType === 'NewDataTableWidget' || widgetType === 'MatrixWidgetComponent'"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Title
</mat-label>
<mat-label>
Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Columns (JSON Array)
</mat-label>
<h3
class=
"text-lg font-semibold mt-4 mb-2"
>
Columns
</h3>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
columnsJson
"
name=
"columnsJson"
rows=
"5"
></textarea>
</mat-form-field>
<div
*
ngFor=
"let column of currentConfig.columns; let i = index"
class=
"column-config-item p-3 mb-3 border rounded-lg"
>
</div>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4 items-center"
>
<!-- Column Field -->
<mat-form-field
appearance=
"fill"
>
<mat-label>
Field
</mat-label>
<mat-select
[(
ngModel
)]="
column
.
field
"
[
name
]="'
field
'
+
i
"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-form-field>
<!-- Header Text -->
<mat-form-field
appearance=
"fill"
>
<mat-label>
Header Text
</mat-label>
<input
matInput
[(
ngModel
)]="
column
.
headerText
"
[
name
]="'
headerText
'
+
i
"
>
</mat-form-field>
<!-- Width -->
<mat-form-field
appearance=
"fill"
>
<mat-label>
Width
</mat-label>
<input
matInput
type=
"number"
[(
ngModel
)]="
column
.
width
"
[
name
]="'
width
'
+
i
"
>
</mat-form-field>
<!-- Is Primary Key -->
<mat-checkbox
[(
ngModel
)]="
column
.
isPrimary
"
[
name
]="'
isPrimary
'
+
i
"
class=
"mt-4"
>
Is Primary Key
</mat-checkbox>
</div>
<!-- Remove Button -->
<div
class=
"flex justify-end mt-2"
>
<button
mat-icon-button
color=
"warn"
(
click
)="
removeGridColumn
(
i
)"
type=
"button"
>
<mat-icon>
delete
</mat-icon>
</button>
</div>
</div>
<button
mat-stroked-button
color=
"primary"
(
click
)="
addGridColumn
()"
type=
"button"
class=
"mt-2"
>
<mat-icon>
add
</mat-icon>
Add Column
</button>
</ng-container>
<div
*
ngIf=
"widgetType === 'SyncfusionPivotWidgetComponent'"
>
<div
*
ngIf=
"widgetType === 'SyncfusionPivotWidgetComponent'"
>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Title
</mat-label>
<mat-label>
Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
expandAll
"
name=
"expandAll"
class=
"mb-4"
>
Expand All
</mat-checkbox>
<mat-label>
Rows (JSON Array)
</mat-label>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
rowsJson
"
name=
"rowsJson"
rows=
"5"
></textarea>
<!-- Pivot Rows -->
</mat-form-field>
<div
class=
"config-section border p-3 rounded-lg mb-4"
>
<mat-form-field
appearance=
"fill"
>
<h3
class=
"text-lg font-semibold mb-2"
>
Rows
</h3>
<mat-label>
Columns (JSON Array)
</mat-label>
<div
*
ngFor=
"let row of currentConfig.rows; let i = index"
class=
"flex items-center gap-2 mb-2"
>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
columnsJson
"
name=
"columnsJson"
rows=
"5"
></textarea>
<mat-form-field
appearance=
"fill"
class=
"flex-grow"
>
</mat-form-field>
<mat-label>
Row Field
</mat-label>
<mat-form-field
appearance=
"fill"
>
<mat-select
[(
ngModel
)]="
row
.
name
"
[
name
]="'
row_name_
'
+
i
"
>
<mat-label>
Values (JSON Array)
</mat-label>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
valuesJson
"
name=
"valuesJson"
rows=
"5"
></textarea>
</mat-select>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<button
mat-icon-button
color=
"warn"
(
click
)="
removePivotRow
(
i
)"
type=
"button"
><mat-icon>
delete
</mat-icon></button>
<mat-label>
Filters (JSON Array)
</mat-label>
</div>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
filtersJson
"
name=
"filtersJson"
rows=
"5"
></textarea>
<button
mat-stroked-button
color=
"primary"
(
click
)="
addPivotRow
()"
type=
"button"
><mat-icon>
add
</mat-icon>
Add Row
</button>
</mat-form-field>
</div>
<mat-checkbox
[(
ngModel
)]="
currentConfig
.
expandAll
"
name=
"expandAll"
>
Expand All
</mat-checkbox>
<!-- Pivot Columns -->
<div
class=
"config-section border p-3 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-2"
>
Columns
</h3>
<div
*
ngFor=
"let col of currentConfig.columns; let i = index"
class=
"flex items-center gap-2 mb-2"
>
<mat-form-field
appearance=
"fill"
class=
"flex-grow"
>
<mat-label>
Column Field
</mat-label>
<mat-select
[(
ngModel
)]="
col
.
name
"
[
name
]="'
col_name_
'
+
i
"
>
<mat-option
*
ngFor=
"let c of availableColumns"
[
value
]="
c
"
>
{{ c }}
</mat-option>
</mat-select>
</mat-form-field>
<button
mat-icon-button
color=
"warn"
(
click
)="
removePivotColumn
(
i
)"
type=
"button"
><mat-icon>
delete
</mat-icon></button>
</div>
<button
mat-stroked-button
color=
"primary"
(
click
)="
addPivotColumn
()"
type=
"button"
><mat-icon>
add
</mat-icon>
Add Column
</button>
</div>
<!-- Pivot Values -->
<div
class=
"config-section border p-3 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-2"
>
Values
</h3>
<div
*
ngFor=
"let val of currentConfig.values; let i = index"
class=
"flex items-center gap-2 mb-2"
>
<mat-form-field
appearance=
"fill"
class=
"flex-grow"
>
<mat-label>
Value Field
</mat-label>
<mat-select
[(
ngModel
)]="
val
.
name
"
[
name
]="'
val_name_
'
+
i
"
>
<mat-option
*
ngFor=
"let c of availableColumns"
[
value
]="
c
"
>
{{ c }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance=
"fill"
class=
"flex-grow"
>
<mat-label>
Caption
</mat-label>
<input
matInput
[(
ngModel
)]="
val
.
caption
"
[
name
]="'
val_caption_
'
+
i
"
>
</mat-form-field>
<button
mat-icon-button
color=
"warn"
(
click
)="
removePivotValue
(
i
)"
type=
"button"
><mat-icon>
delete
</mat-icon></button>
</div>
<button
mat-stroked-button
color=
"primary"
(
click
)="
addPivotValue
()"
type=
"button"
><mat-icon>
add
</mat-icon>
Add Value
</button>
</div>
<!-- Pivot Filters -->
<div
class=
"config-section border p-3 rounded-lg"
>
<h3
class=
"text-lg font-semibold mb-2"
>
Filters
</h3>
<div
*
ngFor=
"let fil of currentConfig.filters; let i = index"
class=
"flex items-center gap-2 mb-2"
>
<mat-form-field
appearance=
"fill"
class=
"flex-grow"
>
<mat-label>
Filter Field
</mat-label>
<mat-select
[(
ngModel
)]="
fil
.
name
"
[
name
]="'
fil_name_
'
+
i
"
>
<mat-option
*
ngFor=
"let c of availableColumns"
[
value
]="
c
"
>
{{ c }}
</mat-option>
</mat-select>
</mat-form-field>
<button
mat-icon-button
color=
"warn"
(
click
)="
removePivotFilter
(
i
)"
type=
"button"
><mat-icon>
delete
</mat-icon></button>
</div>
<button
mat-stroked-button
color=
"primary"
(
click
)="
addPivotFilter
()"
type=
"button"
><mat-icon>
add
</mat-icon>
Add Filter
</button>
</div>
</div>
</div>
<div
*
ngIf=
"widgetType === 'SyncfusionChartWidgetComponent'"
>
<div
*
ngIf=
"widgetType === 'SyncfusionChartWidgetComponent'"
>
...
@@ -345,45 +444,78 @@
...
@@ -345,45 +444,78 @@
</div>
</div>
<div
*
ngIf=
"widgetType === 'ComboChartWidgetComponent'"
>
<div
*
ngIf=
"widgetType === 'ComboChartWidgetComponent'"
>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Title
</mat-label>
<mat-label>
Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
X-Axis Field
</mat-label>
<mat-label>
X-Axis Field
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
xField
"
name=
"xField"
>
<mat-select
[(
ngModel
)]="
currentConfig
.
xField
"
name=
"xField"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-select>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Y-Axis Fields (JSON Array of Strings)
</mat-label>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
yFieldsJson
"
name=
"yFieldsJson"
rows=
"5"
></textarea>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
X-Axis Title
</mat-label>
<mat-label>
X-Axis Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
xAxisTitle
"
name=
"xAxisTitle"
>
<input
matInput
[(
ngModel
)]="
currentConfig
.
xAxisTitle
"
name=
"xAxisTitle"
>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
class=
"w-full"
>
<mat-label>
Y-Axis Title
</mat-label>
<mat-label>
Y-Axis Title
</mat-label>
<input
matInput
[(
ngModel
)]="
currentConfig
.
yAxisTitle
"
name=
"yAxisTitle"
>
<input
matInput
[(
ngModel
)]="
currentConfig
.
yAxisTitle
"
name=
"yAxisTitle"
>
</mat-form-field>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Series (JSON Array of Objects)
</mat-label>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
seriesJson
"
name=
"seriesJson"
rows=
"10"
></textarea>
</mat-form-field>
</div>
<div
*
ngIf=
"widgetType === 'MatrixWidgetComponent'"
>
<!-- Y-Axis Fields -->
<mat-form-field
appearance=
"fill"
>
<div
class=
"config-section border p-3 rounded-lg mb-4"
>
<mat-label>
Title
</mat-label>
<h3
class=
"text-lg font-semibold mb-2"
>
Y-Axis Fields
</h3>
<input
matInput
[(
ngModel
)]="
currentConfig
.
title
"
name=
"title"
>
<div
*
ngFor=
"let yField of currentConfig.yFields; let i = index"
class=
"flex items-center gap-2 mb-2"
>
</mat-form-field>
<mat-form-field
appearance=
"fill"
class=
"flex-grow"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Field {{i + 1}}
</mat-label>
<mat-label>
Columns (JSON Array)
</mat-label>
<mat-select
[(
ngModel
)]="
currentConfig
.
yFields
[
i
]"
[
name
]="'
yfield_
'
+
i
"
>
<textarea
matInput
[(
ngModel
)]="
currentConfig
.
columnsJson
"
name=
"columnsJson"
rows=
"5"
></textarea>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-form-field>
</mat-select>
</mat-form-field>
<button
mat-icon-button
color=
"warn"
(
click
)="
removeChartYField
(
i
)"
type=
"button"
><mat-icon>
delete
</mat-icon></button>
</div>
<button
mat-stroked-button
color=
"primary"
(
click
)="
addChartYField
()"
type=
"button"
><mat-icon>
add
</mat-icon>
Add Y-Axis Field
</button>
</div>
<!-- Series Configuration -->
<div
class=
"config-section border p-3 rounded-lg mb-4"
>
<h3
class=
"text-lg font-semibold mb-2"
>
Series
</h3>
<div
*
ngFor=
"let series of currentConfig.series; let i = index"
class=
"series-item p-3 mb-3 border rounded-md"
>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-x-4"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Series Name
</mat-label>
<input
matInput
[(
ngModel
)]="
series
.
name
"
[
name
]="'
series_name_
'
+
i
"
>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Chart Type
</mat-label>
<mat-select
[(
ngModel
)]="
series
.
type
"
[
name
]="'
series_type_
'
+
i
"
>
<mat-option
*
ngFor=
"let type of comboChartTypes"
[
value
]="
type
"
>
{{ type }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
X-Axis Name
</mat-label>
<mat-select
[(
ngModel
)]="
series
.
xName
"
[
name
]="'
series_xName_
'
+
i
"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Y-Axis Name
</mat-label>
<mat-select
[(
ngModel
)]="
series
.
yName
"
[
name
]="'
series_yName_
'
+
i
"
>
<mat-option
*
ngFor=
"let col of availableColumns"
[
value
]="
col
"
>
{{ col }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div
class=
"flex justify-end"
>
<button
mat-icon-button
color=
"warn"
(
click
)="
removeComboSeries
(
i
)"
type=
"button"
><mat-icon>
delete
</mat-icon></button>
</div>
</div>
<button
mat-stroked-button
color=
"primary"
(
click
)="
addComboSeries
()"
type=
"button"
><mat-icon>
add
</mat-icon>
Add Series
</button>
</div>
</div>
</div>
<div
*
ngIf=
"widgetType === 'SlicerWidgetComponent'"
>
<div
*
ngIf=
"widgetType === 'SlicerWidgetComponent'"
>
<mat-form-field
appearance=
"fill"
>
<mat-form-field
appearance=
"fill"
>
<mat-label>
Title
</mat-label>
<mat-label>
Title
</mat-label>
...
...
src/app/portal-manage/dashboard-management/widget-config/widget-config.component.ts
View file @
7d32073b
...
@@ -6,6 +6,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
...
@@ -6,6 +6,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import
{
MatSelectModule
}
from
'@angular/material/select'
;
import
{
MatSelectModule
}
from
'@angular/material/select'
;
import
{
MatInputModule
}
from
'@angular/material/input'
;
import
{
MatInputModule
}
from
'@angular/material/input'
;
import
{
MatButtonModule
}
from
'@angular/material/button'
;
import
{
MatButtonModule
}
from
'@angular/material/button'
;
import
{
MatIconModule
}
from
'@angular/material/icon'
;
import
{
MatCheckboxModule
}
from
'@angular/material/checkbox'
;
import
{
FormsModule
}
from
'@angular/forms'
;
import
{
FormsModule
}
from
'@angular/forms'
;
export
interface
WidgetConfigDialogData
{
export
interface
WidgetConfigDialogData
{
...
@@ -24,7 +26,9 @@ export interface WidgetConfigDialogData {
...
@@ -24,7 +26,9 @@ export interface WidgetConfigDialogData {
MatInputModule
,
MatInputModule
,
MatSelectModule
,
MatSelectModule
,
MatButtonModule
,
MatButtonModule
,
MatDialogModule
// Add MatDialogModule
MatDialogModule
,
MatIconModule
,
MatCheckboxModule
],
],
templateUrl
:
'./widget-config.component.html'
,
templateUrl
:
'./widget-config.component.html'
,
styleUrls
:
[
'./widget-config.component.scss'
]
styleUrls
:
[
'./widget-config.component.scss'
]
...
@@ -33,6 +37,7 @@ export class WidgetConfigComponent implements OnInit {
...
@@ -33,6 +37,7 @@ export class WidgetConfigComponent implements OnInit {
currentConfig
:
any
;
currentConfig
:
any
;
availableColumns
:
string
[];
availableColumns
:
string
[];
widgetType
:
string
;
widgetType
:
string
;
comboChartTypes
:
string
[]
=
[
'Line'
,
'Column'
,
'Area'
,
'Spline'
];
constructor
(
constructor
(
public
dialogRef
:
MatDialogRef
<
WidgetConfigComponent
>
,
public
dialogRef
:
MatDialogRef
<
WidgetConfigComponent
>
,
...
@@ -40,140 +45,85 @@ export class WidgetConfigComponent implements OnInit {
...
@@ -40,140 +45,85 @@ export class WidgetConfigComponent implements OnInit {
)
{
}
)
{
}
ngOnInit
():
void
{
ngOnInit
():
void
{
this
.
currentConfig
=
{
...
this
.
data
.
config
};
// Create a copy to avoid direct mutation
this
.
availableColumns
=
this
.
data
.
availableColumns
;
this
.
availableColumns
=
this
.
data
.
availableColumns
;
this
.
widgetType
=
this
.
data
.
widgetType
;
this
.
widgetType
=
this
.
data
.
widgetType
;
// Special handling for ChartWidgetComponent's yFields
// Handle config whether it is a string or an object
if
(
this
.
widgetType
===
'ChartWidgetComponent'
&&
this
.
currentConfig
.
yFields
)
{
try
{
this
.
currentConfig
.
yFieldsJson
=
JSON
.
stringify
(
this
.
currentConfig
.
yFields
,
null
,
2
);
if
(
typeof
this
.
data
.
config
===
'string'
)
{
}
this
.
currentConfig
=
JSON
.
parse
(
this
.
data
.
config
);
// Special handling for SyncfusionDatagridWidgetComponent's columns
}
else
{
if
(
this
.
widgetType
===
'SyncfusionDatagridWidgetComponent'
&&
this
.
currentConfig
.
columns
)
{
// Deep copy config to avoid direct mutation of the original object
this
.
currentConfig
.
columnsJson
=
JSON
.
stringify
(
this
.
currentConfig
.
columns
,
null
,
2
);
this
.
currentConfig
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
data
.
config
||
{}));
}
// Special handling for SyncfusionPivotWidgetComponent's properties
if
(
this
.
widgetType
===
'SyncfusionPivotWidgetComponent'
)
{
if
(
this
.
currentConfig
.
rows
)
{
this
.
currentConfig
.
rowsJson
=
JSON
.
stringify
(
this
.
currentConfig
.
rows
,
null
,
2
);
}
if
(
this
.
currentConfig
.
columns
)
{
this
.
currentConfig
.
columnsJson
=
JSON
.
stringify
(
this
.
currentConfig
.
columns
,
null
,
2
);
}
if
(
this
.
currentConfig
.
values
)
{
this
.
currentConfig
.
valuesJson
=
JSON
.
stringify
(
this
.
currentConfig
.
values
,
null
,
2
);
}
if
(
this
.
currentConfig
.
filters
)
{
this
.
currentConfig
.
filtersJson
=
JSON
.
stringify
(
this
.
currentConfig
.
filters
,
null
,
2
);
}
}
}
catch
(
e
)
{
console
.
error
(
'Error parsing widget config:'
,
e
);
this
.
currentConfig
=
{};
// Default to empty object on error
}
}
// Special handling for NewDataTableWidget's columns
if
(
this
.
widgetType
===
'NewDataTableWidget'
&&
this
.
currentConfig
.
columns
)
{
this
.
currentConfig
.
columnsJson
=
JSON
.
stringify
(
this
.
currentConfig
.
columns
,
null
,
2
);
}
}
onSave
():
void
{
// Initialize arrays if they don't exist
// Special handling for ChartWidgetComponent's yFields
const
gridLikeWidgets
=
[
'SyncfusionDatagridWidgetComponent'
,
'NewDataTableWidget'
,
'MatrixWidgetComponent'
];
if
(
this
.
widgetType
===
'ChartWidgetComponent'
&&
this
.
currentConfig
.
yFieldsJson
)
{
if
(
gridLikeWidgets
.
includes
(
this
.
widgetType
)
&&
!
this
.
currentConfig
.
columns
)
{
try
{
this
.
currentConfig
.
columns
=
[];
this
.
currentConfig
.
yFields
=
JSON
.
parse
(
this
.
currentConfig
.
yFieldsJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for yFields:'
,
e
);
alert
(
'Invalid JSON format for Y-Axis Fields. Please correct it.'
);
return
;
// Prevent closing dialog with invalid data
}
}
}
// Special handling for SyncfusionDatagridWidgetComponent's columns
if
(
this
.
widgetType
===
'SyncfusionDatagridWidgetComponent'
&&
this
.
currentConfig
.
columnsJson
)
{
try
{
this
.
currentConfig
.
columns
=
JSON
.
parse
(
this
.
currentConfig
.
columnsJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for columns:'
,
e
);
alert
(
'Invalid JSON format for Columns. Please correct it.'
);
return
;
// Prevent closing dialog with invalid data
}
}
// Special handling for SyncfusionPivotWidgetComponent's properties
if
(
this
.
widgetType
===
'SyncfusionPivotWidgetComponent'
)
{
if
(
this
.
widgetType
===
'SyncfusionPivotWidgetComponent'
)
{
if
(
this
.
currentConfig
.
rowsJson
)
{
if
(
!
this
.
currentConfig
.
rows
)
this
.
currentConfig
.
rows
=
[];
try
{
if
(
!
this
.
currentConfig
.
columns
)
this
.
currentConfig
.
columns
=
[];
this
.
currentConfig
.
rows
=
JSON
.
parse
(
this
.
currentConfig
.
rowsJson
);
if
(
!
this
.
currentConfig
.
values
)
this
.
currentConfig
.
values
=
[];
}
catch
(
e
)
{
if
(
!
this
.
currentConfig
.
filters
)
this
.
currentConfig
.
filters
=
[];
console
.
error
(
'Invalid JSON for rows:'
,
e
);
alert
(
'Invalid JSON format for Rows. Please correct it.'
);
return
;
}
}
if
(
this
.
currentConfig
.
columnsJson
)
{
try
{
this
.
currentConfig
.
columns
=
JSON
.
parse
(
this
.
currentConfig
.
columnsJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for columns:'
,
e
);
alert
(
'Invalid JSON format for Columns. Please correct it.'
);
return
;
}
}
if
(
this
.
currentConfig
.
valuesJson
)
{
try
{
this
.
currentConfig
.
values
=
JSON
.
parse
(
this
.
currentConfig
.
valuesJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for values:'
,
e
);
alert
(
'Invalid JSON format for Values. Please correct it.'
);
return
;
}
}
if
(
this
.
currentConfig
.
filtersJson
)
{
try
{
this
.
currentConfig
.
filters
=
JSON
.
parse
(
this
.
currentConfig
.
filtersJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for filters:'
,
e
);
alert
(
'Invalid JSON format for Filters. Please correct it.'
);
return
;
}
}
}
}
// Special handling for NewDataTableWidget's columns
if
(
this
.
widgetType
===
'ChartWidgetComponent'
&&
!
this
.
currentConfig
.
yFields
)
{
if
(
this
.
widgetType
===
'NewDataTableWidget'
&&
this
.
currentConfig
.
columnsJson
)
{
this
.
currentConfig
.
yFields
=
[];
try
{
this
.
currentConfig
.
columns
=
JSON
.
parse
(
this
.
currentConfig
.
columnsJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for columns:'
,
e
);
alert
(
'Invalid JSON format for Columns. Please correct it.'
);
return
;
// Prevent closing dialog with invalid data
}
}
}
// Special handling for ComboChartWidgetComponent's yFields and series
if
(
this
.
widgetType
===
'ComboChartWidgetComponent'
)
{
if
(
this
.
widgetType
===
'ComboChartWidgetComponent'
)
{
if
(
this
.
currentConfig
.
yFieldsJson
)
{
if
(
!
this
.
currentConfig
.
yFields
)
this
.
currentConfig
.
yFields
=
[];
try
{
if
(
!
this
.
currentConfig
.
series
)
this
.
currentConfig
.
series
=
[];
this
.
currentConfig
.
yFields
=
JSON
.
parse
(
this
.
currentConfig
.
yFieldsJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for yFields:'
,
e
);
alert
(
'Invalid JSON format for Y-Axis Fields. Please correct it.'
);
return
;
}
}
if
(
this
.
currentConfig
.
seriesJson
)
{
try
{
this
.
currentConfig
.
series
=
JSON
.
parse
(
this
.
currentConfig
.
seriesJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for series:'
,
e
);
alert
(
'Invalid JSON format for Series. Please correct it.'
);
return
;
}
}
}
// Special handling for MatrixWidgetComponent's columns
if
(
this
.
widgetType
===
'MatrixWidgetComponent'
&&
this
.
currentConfig
.
columnsJson
)
{
try
{
this
.
currentConfig
.
columns
=
JSON
.
parse
(
this
.
currentConfig
.
columnsJson
);
}
catch
(
e
)
{
console
.
error
(
'Invalid JSON for columns:'
,
e
);
alert
(
'Invalid JSON format for Columns. Please correct it.'
);
return
;
// Prevent closing dialog with invalid data
}
}
}
}
// Methods for Grid-like Widgets
addGridColumn
()
{
this
.
currentConfig
.
columns
.
push
({
field
:
''
,
headerText
:
''
,
width
:
100
,
isPrimary
:
false
});
}
removeGridColumn
(
index
:
number
)
{
this
.
currentConfig
.
columns
.
splice
(
index
,
1
);
}
// Methods for ChartWidgetComponent & ComboChartWidgetComponent yFields
addChartYField
()
{
this
.
currentConfig
.
yFields
.
push
(
''
);
}
removeChartYField
(
index
:
number
)
{
this
.
currentConfig
.
yFields
.
splice
(
index
,
1
);
}
// Methods for ComboChartWidgetComponent series
addComboSeries
()
{
this
.
currentConfig
.
series
.
push
({
name
:
''
,
type
:
'Line'
,
xName
:
''
,
yName
:
''
});
}
removeComboSeries
(
index
:
number
)
{
this
.
currentConfig
.
series
.
splice
(
index
,
1
);
}
// Methods for SyncfusionPivotWidgetComponent
addPivotRow
()
{
this
.
currentConfig
.
rows
.
push
({
name
:
''
});
}
removePivotRow
(
index
:
number
)
{
this
.
currentConfig
.
rows
.
splice
(
index
,
1
);
}
addPivotColumn
()
{
this
.
currentConfig
.
columns
.
push
({
name
:
''
});
}
removePivotColumn
(
index
:
number
)
{
this
.
currentConfig
.
columns
.
splice
(
index
,
1
);
}
addPivotValue
()
{
this
.
currentConfig
.
values
.
push
({
name
:
''
,
caption
:
''
});
}
removePivotValue
(
index
:
number
)
{
this
.
currentConfig
.
values
.
splice
(
index
,
1
);
}
addPivotFilter
()
{
this
.
currentConfig
.
filters
.
push
({
name
:
''
});
}
removePivotFilter
(
index
:
number
)
{
this
.
currentConfig
.
filters
.
splice
(
index
,
1
);
}
onSave
():
void
{
// No more JSON parsing needed for the refactored widgets
this
.
dialogRef
.
close
(
this
.
currentConfig
);
this
.
dialogRef
.
close
(
this
.
currentConfig
);
}
}
...
...
src/app/portal-manage/dashboard-viewer/dashboard-viewer.component.ts
View file @
7d32073b
...
@@ -129,18 +129,6 @@ export class DashboardViewerComponent implements OnInit {
...
@@ -129,18 +129,6 @@ export class DashboardViewerComponent implements OnInit {
mapWidgetsToPanels
(
widgets
:
WidgetModel
[]):
DashboardPanel
[]
{
mapWidgetsToPanels
(
widgets
:
WidgetModel
[]):
DashboardPanel
[]
{
return
widgets
.
map
(
widget
=>
{
return
widgets
.
map
(
widget
=>
{
let
widgetConfig
:
any
=
{};
if
(
typeof
widget
.
config
===
'string'
)
{
try
{
widgetConfig
=
JSON
.
parse
(
widget
.
config
);
}
catch
(
e
)
{
console
.
error
(
'Error parsing widget config string:'
,
widget
.
config
,
e
);
widgetConfig
=
{};
// Default to empty object on error
}
}
else
{
widgetConfig
=
widget
.
config
||
{};
}
return
{
return
{
id
:
widget
.
widgetId
,
id
:
widget
.
widgetId
,
header
:
widget
.
thName
,
header
:
widget
.
thName
,
...
@@ -149,7 +137,7 @@ export class DashboardViewerComponent implements OnInit {
...
@@ -149,7 +137,7 @@ export class DashboardViewerComponent implements OnInit {
row
:
widget
.
y
,
row
:
widget
.
y
,
col
:
widget
.
x
,
col
:
widget
.
x
,
componentType
:
this
.
widgetComponentMap
[
widget
.
component
],
componentType
:
this
.
widgetComponentMap
[
widget
.
component
],
componentInputs
:
{
config
:
widget
Config
},
componentInputs
:
{
config
:
widget
.
config
||
{}
},
};
};
});
});
}
}
...
...
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