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
01ab61b4
Commit
01ab61b4
authored
Sep 08, 2025
by
Ooh-Ao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
linker
parent
0e1eb15e
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
154 additions
and
43 deletions
+154
-43
dataset-widget-linker.component.html
...ge/widget-management/dataset-widget-linker.component.html
+22
-4
dataset-widget-linker.component.ts
...nage/widget-management/dataset-widget-linker.component.ts
+132
-39
No files found.
src/app/portal-manage/widget-management/dataset-widget-linker.component.html
View file @
01ab61b4
...
...
@@ -64,13 +64,31 @@
</div>
</div>
<div
*
ngIf=
"widgetToPreview"
>
<div
class=
"mb-4 p-4 border rounded-lg"
>
<h3
class=
"text-md font-bold mb-2"
>
{{ widgetToPreview.widget.thName }}
</h3>
<p
class=
"text-sm text-gray-600"
>
{{ widgetToPreview.widget.component }}
</p>
<!-- Dynamic Widget Rendering Area -->
<div
class=
"p-4 border rounded-lg mb-4"
>
<div
*
ngIf=
"!previewPanel"
class=
"text-center text-red-500"
>
Preview not available for this widget type.
</div>
<ng-container
*
ngIf=
"previewPanel"
[
ngComponentOutlet
]="
previewPanel
.
componentType
"
[
ngComponentOutletInputs
]="
previewPanel
.
componentInputs
"
></ng-container>
</div>
<!-- Editable Configuration Form -->
<div
class=
"p-4 border rounded-lg bg-gray-50"
>
<h3
class=
"text-md font-bold mb-2"
>
Configuration
</h3>
<pre
class=
"bg-gray-900 text-white p-2 rounded-md text-xs"
>
{{ widgetToPreview.config | json }}
</pre>
<form
#
configForm=
"ngForm"
(
ngSubmit
)="
saveConfiguration
()"
>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-4"
>
<div
*
ngFor=
"let key of getObjectKeys(configAsObject)"
class=
"flex flex-col"
>
<label
[
for
]="
key
"
class=
"text-sm font-medium text-gray-600 mb-1 capitalize"
>
{{ key }}
</label>
<input
type=
"text"
[
id
]="
key
"
[
name
]="
key
"
[(
ngModel
)]="
configAsObject
[
key
]"
class=
"p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500"
>
</div>
</div>
<div
*
ngIf=
"getObjectKeys(configAsObject).length === 0"
class=
"text-center text-gray-500"
>
This widget has no configurable properties.
</div>
<div
class=
"mt-4 flex justify-end"
*
ngIf=
"getObjectKeys(configAsObject).length > 0"
>
<button
ejs-button
type=
"submit"
cssClass=
"e-primary"
>
Save Configuration
</button>
</div>
</form>
</div>
</div>
</div>
...
...
src/app/portal-manage/widget-management/dataset-widget-linker.component.ts
View file @
01ab61b4
import
{
Component
,
OnInit
,
ViewChild
}
from
'@angular/core'
;
import
{
Component
,
OnInit
,
ViewChild
,
Type
}
from
'@angular/core'
;
import
{
CommonModule
,
JsonPipe
}
from
'@angular/common'
;
import
{
FormsModule
}
from
'@angular/forms'
;
import
{
MatDialog
}
from
'@angular/material/dialog'
;
...
...
@@ -7,7 +7,7 @@ import { MatDialog } from '@angular/material/dialog';
import
{
DropDownListModule
}
from
'@syncfusion/ej2-angular-dropdowns'
;
import
{
DialogComponent
,
DialogModule
}
from
'@syncfusion/ej2-angular-popups'
;
import
{
GridComponent
,
GridModule
,
PageService
,
SelectionService
}
from
'@syncfusion/ej2-angular-grids'
;
import
{
Button
Component
,
Button
Module
}
from
'@syncfusion/ej2-angular-buttons'
;
import
{
ButtonModule
}
from
'@syncfusion/ej2-angular-buttons'
;
import
{
NotificationService
}
from
'../../shared/services/notification.service'
;
import
{
WidgetService
}
from
'../services/widgets.service'
;
...
...
@@ -19,17 +19,54 @@ import { WidgetConfigComponent } from '../dashboard-management/widget-config/wid
import
{
take
}
from
'rxjs/operators'
;
import
{
DashboardStateService
}
from
'../services/dashboard-state.service'
;
// --- Copied from DashboardViewerComponent for dynamic rendering ---
import
{
NgComponentOutlet
}
from
'@angular/common'
;
import
{
PanelModel
}
from
'@syncfusion/ej2-angular-layouts'
;
// Import all widget components
import
{
CompanyInfoWidgetComponent
}
from
'../widgets/company-info-widget.component'
;
import
{
HeadcountWidgetComponent
}
from
'../widgets/headcount-widget.component'
;
import
{
AttendanceOverviewWidgetComponent
}
from
'../widgets/attendance-overview-widget.component'
;
import
{
PayrollSummaryWidgetComponent
}
from
'../widgets/payroll-summary-widget.component'
;
import
{
EmployeeDirectoryWidgetComponent
}
from
'../widgets/employee-directory-widget.component'
;
import
{
KpiWidgetComponent
}
from
'../widgets/kpi-widget/kpi-widget.component'
;
import
{
WelcomeWidgetComponent
}
from
'../widgets/welcome-widget/welcome-widget.component'
;
import
{
ChartWidgetComponent
}
from
'../widgets/chart-widget/chart-widget.component'
;
import
{
QuickLinksWidgetComponent
}
from
'../widgets/quick-links-widget/quick-links-widget.component'
;
import
{
SyncfusionDatagridWidgetComponent
}
from
'../widgets/syncfusion-datagrid-widget/syncfusion-datagrid-widget.component'
;
import
{
SyncfusionPivotWidgetComponent
}
from
'../widgets/syncfusion-pivot-widget/syncfusion-pivot-widget.component'
;
import
{
SyncfusionChartWidgetComponent
}
from
'../widgets/syncfusion-chart-widget/syncfusion-chart-widget.component'
;
import
{
DataTableWidgetComponent
}
from
'../widgets/dynamic-widgets/data-table-widget.component'
;
import
{
AreaChartWidgetComponent
}
from
'../widgets/area-chart-widget/area-chart-widget.component'
;
import
{
BarChartWidgetComponent
}
from
'../widgets/bar-chart-widget/bar-chart-widget.component'
;
import
{
PieChartWidgetComponent
}
from
'../widgets/pie-chart-widget/pie-chart-widget.component'
;
import
{
ScatterBubbleChartWidgetComponent
}
from
'../widgets/scatter-bubble-chart-widget/scatter-bubble-chart-widget.component'
;
import
{
MultiRowCardWidgetComponent
}
from
'../widgets/multi-row-card-widget/multi-row-card-widget.component'
;
import
{
ComboChartWidgetComponent
}
from
'../widgets/combo-chart-widget/combo-chart-widget.component'
;
import
{
DoughnutChartWidgetComponent
}
from
'../widgets/doughnut-chart-widget/doughnut-chart-widget.component'
;
import
{
FunnelChartWidgetComponent
}
from
'../widgets/funnel-chart-widget/funnel-chart-widget.component'
;
import
{
GaugeChartWidgetComponent
}
from
'../widgets/gauge-chart-widget/gauge-chart-widget.component'
;
import
{
SimpleKpiWidgetComponent
}
from
'../widgets/simple-kpi-widget/simple-kpi-widget.component'
;
import
{
FilledMapWidgetComponent
}
from
'../widgets/filled-map-widget/filled-map-widget.component'
;
import
{
MatrixWidgetComponent
}
from
'../widgets/matrix-widget/matrix-widget.component'
;
import
{
SlicerWidgetComponent
}
from
'../widgets/slicer-widget/slicer-widget.component'
;
import
{
SimpleTableWidgetComponent
}
from
'../widgets/simple-table-widget/simple-table-widget.component'
;
import
{
WaterfallChartWidgetComponent
}
from
'../widgets/waterfall-chart-widget/waterfall-chart-widget.component'
;
import
{
TreemapWidgetComponent
}
from
'../widgets/treemap-widget/treemap-widget.component'
;
// Interface for the panel object used by ngComponentOutlet
export
interface
DashboardPanel
extends
PanelModel
{
componentType
:
Type
<
any
>
;
componentInputs
?:
{
[
key
:
string
]:
any
};
}
// ----------------------------------------------------------------
@
Component
({
selector
:
'app-dataset-widget-linker'
,
standalone
:
true
,
imports
:
[
CommonModule
,
FormsModule
,
JsonPipe
,
DropDownListModule
,
DialogModule
,
GridModule
,
ButtonModule
CommonModule
,
FormsModule
,
JsonPipe
,
DropDownListModule
,
DialogModule
,
GridModule
,
ButtonModule
,
NgComponentOutlet
,
// Add all widget components here to make them available for NgComponentOutlet
CompanyInfoWidgetComponent
,
HeadcountWidgetComponent
,
AttendanceOverviewWidgetComponent
,
PayrollSummaryWidgetComponent
,
EmployeeDirectoryWidgetComponent
,
KpiWidgetComponent
,
WelcomeWidgetComponent
,
ChartWidgetComponent
,
QuickLinksWidgetComponent
,
SyncfusionDatagridWidgetComponent
,
SyncfusionPivotWidgetComponent
,
SyncfusionChartWidgetComponent
,
DataTableWidgetComponent
,
AreaChartWidgetComponent
,
BarChartWidgetComponent
,
PieChartWidgetComponent
,
ScatterBubbleChartWidgetComponent
,
MultiRowCardWidgetComponent
,
ComboChartWidgetComponent
,
DoughnutChartWidgetComponent
,
FunnelChartWidgetComponent
,
GaugeChartWidgetComponent
,
SimpleKpiWidgetComponent
,
FilledMapWidgetComponent
,
MatrixWidgetComponent
,
SlicerWidgetComponent
,
SimpleTableWidgetComponent
,
WaterfallChartWidgetComponent
,
TreemapWidgetComponent
],
providers
:
[
PageService
,
SelectionService
],
templateUrl
:
'./dataset-widget-linker.component.html'
,
...
...
@@ -37,20 +74,54 @@ import { DashboardStateService } from '../services/dashboard-state.service';
})
export
class
DatasetWidgetLinkerComponent
implements
OnInit
{
// Syncfusion Component References
@
ViewChild
(
'addWidgetDialog'
)
addWidgetDialog
!
:
DialogComponent
;
@
ViewChild
(
'grid'
)
grid
!
:
GridComponent
;
// Data lists
public
datasets
:
DatasetModel
[]
=
[];
public
masterWidgets
:
WidgetModel
[]
=
[];
public
linkedWidgets
:
MenuItemsWidget
[]
=
[];
// UI State
public
selectedDatasetId
:
string
|
null
=
null
;
public
widgetToPreview
:
MenuItemsWidget
|
null
=
null
;
public
isModalVisible
=
false
;
// --- State for the right panel ---
public
widgetToPreview
:
MenuItemsWidget
|
null
=
null
;
public
configAsObject
:
any
=
{};
public
previewPanel
:
DashboardPanel
|
null
=
null
;
// --------------------------------
private
widgetComponentMap
:
{
[
key
:
string
]:
Type
<
any
>
}
=
{
'CompanyInfoWidgetComponent'
:
CompanyInfoWidgetComponent
,
'HeadcountWidgetComponent'
:
HeadcountWidgetComponent
,
'AttendanceOverviewWidgetComponent'
:
AttendanceOverviewWidgetComponent
,
'PayrollSummaryWidgetComponent'
:
PayrollSummaryWidgetComponent
,
'EmployeeDirectoryWidgetComponent'
:
EmployeeDirectoryWidgetComponent
,
'KpiWidgetComponent'
:
KpiWidgetComponent
,
'WelcomeWidgetComponent'
:
WelcomeWidgetComponent
,
'ChartWidgetComponent'
:
ChartWidgetComponent
,
'QuickLinksWidgetComponent'
:
QuickLinksWidgetComponent
,
'SyncfusionDatagridWidgetComponent'
:
SyncfusionDatagridWidgetComponent
,
'SyncfusionPivotWidgetComponent'
:
SyncfusionPivotWidgetComponent
,
'SyncfusionChartWidgetComponent'
:
SyncfusionChartWidgetComponent
,
'NewDataTableWidget'
:
DataTableWidgetComponent
,
'AreaChartWidgetComponent'
:
AreaChartWidgetComponent
,
'BarChartWidgetComponent'
:
BarChartWidgetComponent
,
'PieChartWidgetComponent'
:
PieChartWidgetComponent
,
'ScatterBubbleChartWidgetComponent'
:
ScatterBubbleChartWidgetComponent
,
'MultiRowCardWidgetComponent'
:
MultiRowCardWidgetComponent
,
'ComboChartWidgetComponent'
:
ComboChartWidgetComponent
,
'DoughnutChartWidgetComponent'
:
DoughnutChartWidgetComponent
,
'FunnelChartWidgetComponent'
:
FunnelChartWidgetComponent
,
'GaugeChartWidgetComponent'
:
GaugeChartWidgetComponent
,
'SimpleKpiWidgetComponent'
:
SimpleKpiWidgetComponent
,
'FilledMapWidgetComponent'
:
FilledMapWidgetComponent
,
'MatrixWidgetComponent'
:
MatrixWidgetComponent
,
'SlicerWidgetComponent'
:
SlicerWidgetComponent
,
'SimpleTableWidgetComponent'
:
SimpleTableWidgetComponent
,
'WaterfallChartWidgetComponent'
:
WaterfallChartWidgetComponent
,
'TreemapWidgetComponent'
:
TreemapWidgetComponent
,
};
constructor
(
private
widgetService
:
WidgetService
,
private
mMenuitemsWidgetService
:
MMenuitemsWidgetService
,
...
...
@@ -61,17 +132,16 @@ export class DatasetWidgetLinkerComponent implements OnInit {
)
{
}
ngOnInit
():
void
{
this
.
datasetService
.
getDatasets
().
subscribe
(
datasets
=>
{
this
.
datasets
=
datasets
;
});
this
.
datasetService
.
getDatasets
().
subscribe
(
datasets
=>
{
this
.
datasets
=
datasets
;
});
this
.
widgetService
.
getListWidgets
().
subscribe
(
widgets
=>
{
this
.
masterWidgets
=
widgets
;
})
;
}
this
.
widgetService
.
getListWidgets
().
subscribe
(
widgets
=>
{
this
.
masterWidgets
=
widgets
;
});
getObjectKeys
(
obj
:
any
):
string
[]
{
return
Object
.
keys
(
obj
);
}
onDatasetChange
():
void
{
this
.
widgetToPreview
=
null
;
this
.
clearPreview
()
;
if
(
this
.
selectedDatasetId
)
{
this
.
loadLinkedWidgetsForDataset
(
this
.
selectedDatasetId
);
this
.
dashboardStateService
.
selectDataset
(
this
.
selectedDatasetId
);
...
...
@@ -82,22 +152,50 @@ export class DatasetWidgetLinkerComponent implements OnInit {
}
loadLinkedWidgetsForDataset
(
datasetId
:
string
):
void
{
this
.
mMenuitemsWidgetService
.
getWidgetsForDataset
(
datasetId
).
subscribe
(
linked
=>
{
this
.
linkedWidgets
=
linked
;
});
this
.
mMenuitemsWidgetService
.
getWidgetsForDataset
(
datasetId
).
subscribe
(
linked
=>
{
this
.
linkedWidgets
=
linked
;
});
}
viewWidget
(
widget
:
MenuItemsWidget
):
void
{
this
.
widgetToPreview
=
widget
;
try
{
this
.
configAsObject
=
JSON
.
parse
(
widget
.
config
||
'{}'
);
}
catch
(
e
)
{
this
.
notificationService
.
error
(
'Error'
,
'Failed to parse widget config.'
);
this
.
configAsObject
=
{};
}
const
componentType
=
this
.
widgetComponentMap
[
widget
.
widget
.
component
];
if
(
componentType
)
{
this
.
previewPanel
=
{
id
:
widget
.
widget
.
widgetId
,
componentType
:
componentType
,
componentInputs
:
{
config
:
this
.
configAsObject
},
row
:
0
,
col
:
0
,
sizeX
:
1
,
sizeY
:
1
// Dummy values, not used for layout here
};
}
else
{
this
.
previewPanel
=
null
;
this
.
notificationService
.
error
(
'Error'
,
`Preview not available for widget type:
${
widget
.
widget
.
component
}
`
);
}
}
saveConfiguration
():
void
{
if
(
!
this
.
widgetToPreview
)
return
;
this
.
widgetToPreview
.
config
=
JSON
.
stringify
(
this
.
configAsObject
);
this
.
mMenuitemsWidgetService
.
saveLinkedWidget
(
this
.
widgetToPreview
).
subscribe
(()
=>
{
this
.
notificationService
.
success
(
'Success'
,
'Configuration saved successfully!'
);
// Refresh the preview to apply the new config
this
.
viewWidget
(
this
.
widgetToPreview
!
);
});
}
removeWidget
(
widget
:
MenuItemsWidget
,
event
:
MouseEvent
):
void
{
event
.
stopPropagation
();
// Prevent row selection when clicking the button
event
.
stopPropagation
();
if
(
confirm
(
'Are you sure you want to unlink this widget?'
))
{
this
.
mMenuitemsWidgetService
.
deleteLinkedWidget
(
widget
.
itemId
).
subscribe
(()
=>
{
this
.
notificationService
.
success
(
'Success'
,
'Widget unlinked successfully!'
);
if
(
this
.
widgetToPreview
?.
itemId
===
widget
.
itemId
)
{
this
.
widgetToPreview
=
null
;
this
.
clearPreview
()
;
}
this
.
loadLinkedWidgetsForDataset
(
this
.
selectedDatasetId
!
);
});
...
...
@@ -112,19 +210,19 @@ export class DatasetWidgetLinkerComponent implements OnInit {
addSelectedWidgets
():
void
{
const
selectedWidgets
=
this
.
grid
.
getSelectedRecords
()
as
WidgetModel
[];
if
(
selectedWidgets
.
length
>
0
)
{
selectedWidgets
.
forEach
(
widgetModel
=>
{
this
.
linkWidget
(
widgetModel
);
});
selectedWidgets
.
forEach
(
widgetModel
=>
{
this
.
linkWidget
(
widgetModel
);
});
}
this
.
addWidgetDialog
.
hide
();
}
private
linkWidget
(
widget
:
WidgetModel
):
void
{
if
(
!
this
.
selectedDatasetId
)
{
this
.
notificationService
.
warning
(
'Warning'
,
'Something went wrong. No dataset selected.'
)
;
return
;
private
clearPreview
(
):
void
{
this
.
widgetToPreview
=
null
;
this
.
previewPanel
=
null
;
this
.
configAsObject
=
{}
;
}
private
linkWidget
(
widget
:
WidgetModel
):
void
{
if
(
!
this
.
selectedDatasetId
)
return
;
if
(
this
.
linkedWidgets
.
some
(
lw
=>
lw
.
widget
.
widgetId
===
widget
.
widgetId
))
{
this
.
notificationService
.
info
(
'Info'
,
`'
${
widget
.
thName
}
' is already linked.`
);
return
;
...
...
@@ -140,14 +238,9 @@ export class DatasetWidgetLinkerComponent implements OnInit {
dialogRef
.
afterClosed
().
subscribe
(
resultConfig
=>
{
if
(
resultConfig
)
{
const
newLinkedWidget
:
MenuItemsWidget
=
{
companyId
:
'DEMO'
,
// Placeholder
itemId
:
this
.
selectedDatasetId
!
,
widget
:
widget
,
config
:
JSON
.
stringify
(
resultConfig
),
data
:
''
,
perspective
:
''
companyId
:
'DEMO'
,
itemId
:
this
.
selectedDatasetId
!
,
widget
:
widget
,
config
:
JSON
.
stringify
(
resultConfig
),
data
:
''
,
perspective
:
''
};
this
.
mMenuitemsWidgetService
.
saveLinkedWidget
(
newLinkedWidget
).
subscribe
(()
=>
{
this
.
notificationService
.
success
(
'Success'
,
`'
${
widget
.
thName
}
' linked successfully!`
);
this
.
loadLinkedWidgetsForDataset
(
this
.
selectedDatasetId
!
);
...
...
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