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
9e7c3447
Commit
9e7c3447
authored
Sep 07, 2025
by
Ooh-Ao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
คลังวิดเจ็ท
parent
71f98397
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
418 additions
and
84 deletions
+418
-84
dashboard-management.component.html
.../dashboard-management/dashboard-management.component.html
+5
-5
dashboard-management.component.ts
...ge/dashboard-management/dashboard-management.component.ts
+0
-0
m-menuitems-widget.model.ts
src/app/portal-manage/models/m-menuitems-widget.model.ts
+16
-0
portal-manage.routes.ts
src/app/portal-manage/portal-manage.routes.ts
+7
-0
m-menuitems-widget.service.ts
src/app/portal-manage/services/m-menuitems-widget.service.ts
+77
-0
dataset-widget-linker.component.html
...ge/widget-management/dataset-widget-linker.component.html
+63
-0
dataset-widget-linker.component.scss
...ge/widget-management/dataset-widget-linker.component.scss
+0
-0
dataset-widget-linker.component.ts
...nage/widget-management/dataset-widget-linker.component.ts
+117
-0
widget-list.component.html
...ortal-manage/widget-management/widget-list.component.html
+46
-40
widget-list.component.ts
.../portal-manage/widget-management/widget-list.component.ts
+42
-39
d-menuitems-widget.json
src/assets/data/d-menuitems-widget.json
+2
-0
master-widgets.json
src/assets/data/master-widgets.json
+43
-0
No files found.
src/app/portal-manage/dashboard-management/dashboard-management.component.html
View file @
9e7c3447
...
...
@@ -51,20 +51,20 @@
<!-- Widget List -->
<div
class=
"widget-list space-y-2"
>
<div
*
ngFor=
"let
widget
of filteredAvailableWidgets"
(
click
)="
addWidgetToDashboard
(
widget
)"
*
ngFor=
"let
menuItem
of filteredAvailableWidgets"
(
click
)="
addWidgetToDashboard
(
menuItem
)"
class=
"widget-item p-3 rounded-lg hover:bg-gray-100 cursor-pointer transition-colors duration-200"
>
<p
class=
"font-semibold text-gray-700"
>
{{ widget.thName }}
</p>
<p
class=
"font-semibold text-gray-700"
>
{{
menuItem.
widget.thName }}
</p>
<p
class=
"text-xs text-gray-500"
>
Size: {{
widget.cols }}x{{
widget.rows }}
Size: {{
menuItem.widget.cols }}x{{ menuItem.
widget.rows }}
</p>
</div>
<p
*
ngIf=
"filteredAvailableWidgets.length === 0"
class=
"text-gray-500 text-center mt-4"
>
No widgets found.
No widgets found
for this dataset
.
</p>
</div>
</div>
...
...
src/app/portal-manage/dashboard-management/dashboard-management.component.ts
View file @
9e7c3447
This diff is collapsed.
Click to expand it.
src/app/portal-manage/models/m-menuitems-widget.model.ts
0 → 100644
View file @
9e7c3447
import
{
WidgetModel
}
from
"./widgets.model"
;
export
class
MenuItemsWidget
{
companyId
:
string
;
itemId
:
string
;
widget
:
WidgetModel
;
data
?:
string
;
config
?:
string
;
perspective
?:
string
;
constructor
(
initialValues
:
Partial
<
MenuItemsWidget
>
=
{})
{
if
(
initialValues
)
{
Object
.
assign
(
this
,
initialValues
);
}
}
}
src/app/portal-manage/portal-manage.routes.ts
View file @
9e7c3447
import
{
DatasetWidgetLinkerComponent
}
from
'./widget-management/dataset-widget-linker.component'
;
import
{
Routes
}
from
'@angular/router'
;
import
{
DashboardManagementComponent
}
from
'./dashboard-management/dashboard-management.component'
;
...
...
@@ -25,6 +26,12 @@ export const portalManageRoutes: Routes = [
path
:
'dashboard-viewer/:dashboardId'
,
component
:
DashboardViewerComponent
},
{
path
:
'widget-management'
,
children
:
[
{
path
:
'linker'
,
component
:
DatasetWidgetLinkerComponent
,
title
:
'Dataset Widget Linker'
}
]
}
// Optional: A redirect for the base portal-manage path
// {
// path: '',
...
...
src/app/portal-manage/services/m-menuitems-widget.service.ts
0 → 100644
View file @
9e7c3447
import
{
Injectable
}
from
'@angular/core'
;
import
{
HttpClient
}
from
'@angular/common/http'
;
import
{
Observable
,
of
}
from
'rxjs'
;
import
{
map
,
catchError
,
tap
}
from
'rxjs/operators'
;
import
{
MenuItemsWidget
}
from
'../models/m-menuitems-widget.model'
;
@
Injectable
({
providedIn
:
'root'
})
export
class
MMenuitemsWidgetService
{
private
dataUrl
=
'assets/data/d-menuitems-widget.json'
;
private
linkedWidgetsCache
:
MenuItemsWidget
[]
=
[];
// In-memory cache for simulation
constructor
(
private
http
:
HttpClient
)
{
}
/**
* Gets all widget menu items and filters them by the provided datasetId.
* In a real application, this filtering would ideally be done on the backend.
* @param datasetId The ID of the dataset to filter widgets for.
* @returns An Observable of MenuItemsWidget[] that are linked to the given datasetId.
*/
getWidgetsForDataset
(
datasetId
:
string
):
Observable
<
MenuItemsWidget
[]
>
{
// Simulate fetching from backend or use cache if available
if
(
this
.
linkedWidgetsCache
.
length
>
0
)
{
return
of
(
this
.
linkedWidgetsCache
.
filter
(
item
=>
(
item
as
any
).
datasetId
===
datasetId
));
}
else
{
return
this
.
http
.
get
<
any
[]
>
(
this
.
dataUrl
).
pipe
(
tap
(
items
=>
{
// Populate cache on first load
this
.
linkedWidgetsCache
=
items
.
map
(
item
=>
new
MenuItemsWidget
(
item
));
}),
map
(
items
=>
{
const
filteredItems
=
items
.
filter
(
item
=>
item
.
datasetId
===
datasetId
);
return
filteredItems
.
map
(
item
=>
new
MenuItemsWidget
(
item
));
}),
catchError
(
error
=>
{
console
.
error
(
'Error loading widget menu items:'
,
error
);
return
of
([]);
// Return an empty array on error
})
);
}
}
/**
* SIMULATED: Saves a linked widget. In a real app, this would be a POST/PUT request.
* Updates the in-memory cache and logs the action.
* @param menuItem The MenuItemsWidget to save.
* @returns An Observable of the saved MenuItemsWidget.
*/
saveLinkedWidget
(
menuItem
:
MenuItemsWidget
):
Observable
<
MenuItemsWidget
>
{
// Check if it already exists (for update scenario)
const
index
=
this
.
linkedWidgetsCache
.
findIndex
(
item
=>
item
.
itemId
===
menuItem
.
itemId
);
if
(
index
>
-
1
)
{
this
.
linkedWidgetsCache
[
index
]
=
menuItem
;
console
.
log
(
'Simulating update linked widget:'
,
menuItem
);
}
else
{
this
.
linkedWidgetsCache
.
push
(
menuItem
);
console
.
log
(
'Simulating save new linked widget:'
,
menuItem
);
}
// In a real app, the backend would return the saved item.
return
of
(
menuItem
);
}
/**
* SIMULATED: Deletes a linked widget by its itemId. In a real app, this would be a DELETE request.
* Updates the in-memory cache and logs the action.
* @param itemId The itemId of the MenuItemsWidget to delete.
* @returns An Observable indicating success.
*/
deleteLinkedWidget
(
itemId
:
string
):
Observable
<
any
>
{
this
.
linkedWidgetsCache
=
this
.
linkedWidgetsCache
.
filter
(
item
=>
item
.
itemId
!==
itemId
);
console
.
log
(
'Simulating delete linked widget with ID:'
,
itemId
);
// In a real app, this would return a status or confirmation.
return
of
({
success
:
true
});
}
}
src/app/portal-manage/widget-management/dataset-widget-linker.component.html
0 → 100644
View file @
9e7c3447
<div
class=
"p-4 sm:p-6 lg:p-8"
>
<div
class=
"sm:flex sm:items-center"
>
<div
class=
"sm:flex-auto"
>
<h1
class=
"text-xl font-semibold text-gray-900"
>
Dataset Widget Linker
</h1>
<p
class=
"mt-2 text-sm text-gray-700"
>
Link available widgets to specific datasets and configure their default settings.
</p>
</div>
</div>
<div
class=
"mt-6"
>
<label
for=
"dataset-select"
class=
"block text-sm font-medium text-gray-700"
>
Select Dataset
</label>
<select
id=
"dataset-select"
name=
"dataset-select"
[(
ngModel
)]="
selectedDataset
"
(
change
)="
onDatasetSelected
()"
class=
"mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md"
>
<option
[
ngValue
]="
null
"
>
-- Select a Dataset --
</option>
<option
*
ngFor=
"let dataset of datasets"
[
ngValue
]="
dataset
"
>
{{ dataset.tdesc }} ({{ dataset.itemId }})
</option>
</select>
</div>
<div
class=
"mt-8 grid grid-cols-1 lg:grid-cols-2 gap-8"
>
<!-- Available Master Widgets -->
<div>
<h2
class=
"text-lg font-semibold text-gray-900"
>
Available Widgets
</h2>
<p
class=
"mt-2 text-sm text-gray-700"
>
Widgets registered in the system.
</p>
<div
class=
"mt-4 border border-gray-200 rounded-lg overflow-hidden shadow-sm"
>
<ul
role=
"list"
class=
"divide-y divide-gray-200"
>
<li
*
ngFor=
"let widget of masterWidgets"
class=
"p-4 flex items-center justify-between hover:bg-gray-50"
>
<div>
<p
class=
"text-sm font-medium text-gray-900"
>
{{ widget.thName }}
</p>
<p
class=
"text-sm text-gray-500"
>
{{ widget.component }} ({{ widget.cols }}x{{ widget.rows }})
</p>
</div>
<button
(
click
)="
linkWidget
(
widget
)"
type=
"button"
class=
"ml-3 inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Link
</button>
</li>
<li
*
ngIf=
"masterWidgets.length === 0"
class=
"p-4 text-center text-sm text-gray-500"
>
No master widgets found.
</li>
</ul>
</div>
</div>
<!-- Linked Widgets for Selected Dataset -->
<div>
<h2
class=
"text-lg font-semibold text-gray-900"
>
Linked Widgets ({{ selectedDataset?.tdesc || 'No Dataset Selected' }})
</h2>
<p
class=
"mt-2 text-sm text-gray-700"
>
Widgets configured for the selected dataset.
</p>
<div
class=
"mt-4 border border-gray-200 rounded-lg overflow-hidden shadow-sm"
>
<ul
role=
"list"
class=
"divide-y divide-gray-200"
>
<li
*
ngFor=
"let linkedWidget of linkedWidgets"
class=
"p-4 flex items-center justify-between hover:bg-gray-50"
>
<div>
<p
class=
"text-sm font-medium text-gray-900"
>
{{ linkedWidget.widget.thName }}
</p>
<p
class=
"text-sm text-gray-500"
>
{{ linkedWidget.widget.component }}
</p>
<p
*
ngIf=
"linkedWidget.config"
class=
"text-xs text-gray-400"
>
Config: {{ linkedWidget.config | slice:0:50 }}...
</p>
</div>
<button
(
click
)="
unlinkWidget
(
linkedWidget
.
itemId
)"
type=
"button"
class=
"ml-3 inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
>
Unlink
</button>
</li>
<li
*
ngIf=
"linkedWidgets.length === 0"
class=
"p-4 text-center text-sm text-gray-500"
>
No widgets linked to this dataset.
</li>
</ul>
</div>
</div>
</div>
</div>
src/app/portal-manage/widget-management/dataset-widget-linker.component.scss
0 → 100644
View file @
9e7c3447
src/app/portal-manage/widget-management/dataset-widget-linker.component.ts
0 → 100644
View file @
9e7c3447
import
{
Component
,
OnInit
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
FormsModule
}
from
'@angular/forms'
;
import
{
MatDialog
}
from
'@angular/material/dialog'
;
import
{
NotificationService
}
from
'../../shared/services/notification.service'
;
import
{
WidgetService
}
from
'../services/widgets.service'
;
import
{
MMenuitemsWidgetService
}
from
'../services/m-menuitems-widget.service'
;
import
{
DatasetService
}
from
'../services/dataset.service'
;
import
{
WidgetModel
,
DatasetModel
}
from
'../models/widgets.model'
;
import
{
MenuItemsWidget
}
from
'../models/m-menuitems-widget.model'
;
import
{
WidgetConfigComponent
}
from
'../dashboard-management/widget-config/widget-config.component'
;
import
{
take
}
from
'rxjs/operators'
;
import
{
DashboardStateService
}
from
'../services/dashboard-state.service'
;
@
Component
({
selector
:
'app-dataset-widget-linker'
,
standalone
:
true
,
imports
:
[
CommonModule
,
FormsModule
],
templateUrl
:
'./dataset-widget-linker.component.html'
,
styleUrls
:
[
'./dataset-widget-linker.component.scss'
]
})
export
class
DatasetWidgetLinkerComponent
implements
OnInit
{
public
datasets
:
DatasetModel
[]
=
[];
public
selectedDataset
:
DatasetModel
|
null
=
null
;
public
masterWidgets
:
WidgetModel
[]
=
[];
public
linkedWidgets
:
MenuItemsWidget
[]
=
[];
constructor
(
private
widgetService
:
WidgetService
,
private
mMenuitemsWidgetService
:
MMenuitemsWidgetService
,
private
datasetService
:
DatasetService
,
private
notificationService
:
NotificationService
,
private
dialog
:
MatDialog
,
private
dashboardStateService
:
DashboardStateService
// To get columns for config
)
{
}
ngOnInit
():
void
{
this
.
datasetService
.
getDatasets
().
subscribe
(
datasets
=>
{
this
.
datasets
=
datasets
;
});
this
.
widgetService
.
getListWidgets
().
subscribe
(
widgets
=>
{
this
.
masterWidgets
=
widgets
;
});
}
onDatasetSelected
():
void
{
if
(
this
.
selectedDataset
)
{
this
.
loadLinkedWidgetsForDataset
(
this
.
selectedDataset
.
itemId
);
// Also select the dataset in the global state to get its columns
this
.
dashboardStateService
.
selectDataset
(
this
.
selectedDataset
.
itemId
);
}
else
{
this
.
linkedWidgets
=
[];
this
.
dashboardStateService
.
selectDataset
(
null
);
}
}
loadLinkedWidgetsForDataset
(
datasetId
:
string
):
void
{
this
.
mMenuitemsWidgetService
.
getWidgetsForDataset
(
datasetId
).
subscribe
(
linked
=>
{
this
.
linkedWidgets
=
linked
;
});
}
linkWidget
(
widget
:
WidgetModel
):
void
{
if
(
!
this
.
selectedDataset
)
{
this
.
notificationService
.
warning
(
'Warning'
,
'Please select a dataset first.'
);
return
;
}
// Check if widget is already linked
if
(
this
.
linkedWidgets
.
some
(
lw
=>
lw
.
widget
.
widgetId
===
widget
.
widgetId
))
{
this
.
notificationService
.
info
(
'Info'
,
'This widget is already linked to the selected dataset.'
);
return
;
}
// Get available columns for the selected dataset to pass to config dialog
this
.
dashboardStateService
.
selectedDataset$
.
pipe
(
take
(
1
)).
subscribe
(
selectedDataset
=>
{
const
availableColumns
=
selectedDataset
?
selectedDataset
.
columns
:
[];
const
dialogRef
=
this
.
dialog
.
open
(
WidgetConfigComponent
,
{
width
:
'600px'
,
data
:
{
widget
:
widget
,
// Pass the master widget template
availableColumns
:
availableColumns
}
});
dialogRef
.
afterClosed
().
subscribe
(
resultConfig
=>
{
if
(
resultConfig
)
{
const
newLinkedWidget
:
MenuItemsWidget
=
{
companyId
:
'1'
,
// Placeholder, ideally from user context
itemId
:
`link-
${
this
.
selectedDataset
!
.
itemId
}
-
${
widget
.
widgetId
}
`
,
widget
:
widget
,
// The base widget definition
config
:
JSON
.
stringify
(
resultConfig
),
// Save configured object as string
data
:
''
,
// Not used for linking, but part of model
perspective
:
''
// Not used for linking, but part of model
};
this
.
mMenuitemsWidgetService
.
saveLinkedWidget
(
newLinkedWidget
).
subscribe
(()
=>
{
this
.
notificationService
.
success
(
'Success'
,
'Widget linked successfully!'
);
this
.
loadLinkedWidgetsForDataset
(
this
.
selectedDataset
!
.
itemId
);
});
}
});
});
}
unlinkWidget
(
itemId
:
string
):
void
{
if
(
confirm
(
'Are you sure you want to unlink this widget from the dataset?'
))
{
this
.
mMenuitemsWidgetService
.
deleteLinkedWidget
(
itemId
).
subscribe
(()
=>
{
this
.
notificationService
.
success
(
'Success'
,
'Widget unlinked successfully!'
);
this
.
loadLinkedWidgetsForDataset
(
this
.
selectedDataset
!
.
itemId
);
});
}
}
}
src/app/portal-manage/widget-management/widget-list.component.html
View file @
9e7c3447
<div
class=
"container mx-auto p-6
"
>
<div
class=
"flex justify-between items-center mb-4
"
>
<h1
class=
"text-2xl font-bold"
>
Widget Warehouse for {{ appName | titlecase }}
</h1>
<button
(
click
)="
addNewWidget
()"
class=
"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Add New Widget
<
/button
>
<div
class=
"p-4 sm:p-6 lg:p-8"
>
<div
class=
"sm:flex sm:items-center
"
>
<div
class=
"sm:flex-auto
"
>
<h1
class=
"text-xl font-semibold text-gray-900"
>
Widget Registry
</h1>
<p
class=
"mt-2 text-sm text-gray-700"
>
A list of all base widgets registered in the system, fetched from the central API.
</p
>
</div>
<
!-- Add/Delete functionality removed as this now points to a real API --
>
</div>
<div
class=
"bg-white shadow-md rounded my-6"
>
<table
class=
"min-w-full table-auto"
>
<thead
class=
"bg-gray-200"
>
<tr>
<th
class=
"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Widget Name
</th>
<th
class=
"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Component
</th>
<th
class=
"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Default Size (Cols x Rows)
</th>
<th
class=
"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Preview
</th>
<th
class=
"px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Actions
</th>
</tr>
</thead>
<tbody
class=
"bg-white divide-y divide-gray-200"
>
<tr
*
ngFor=
"let widget of widgets$ | async"
>
<td
class=
"px-6 py-4 whitespace-nowrap"
>
{{ widget.thName }}
</td>
<td
class=
"px-6 py-4 whitespace-nowrap"
>
{{ widget.component }}
</td>
<td
class=
"px-6 py-4 whitespace-nowrap"
>
{{ widget.cols }} x {{ widget.rows }}
</td>
<td
class=
"px-6 py-4"
>
<div
class=
"w-48 h-32 border border-gray-300 rounded-lg overflow-hidden shadow-sm flex items-center justify-center bg-gray-50"
>
<ng-container
*
ngComponentOutlet=
"getComponentType(widget.component)"
></ng-container>
</div>
</td>
<td
class=
"px-6 py-4 whitespace-nowrap text-right"
>
<button
(
click
)="
editWidget
(
widget
.
widgetId
)"
class=
"text-indigo-600 hover:text-indigo-900"
>
Edit
</button>
</td>
</tr>
<!-- Show a message if there are no widgets -->
<tr
*
ngIf=
"!(widgets$ | async)?.length"
>
<td
colspan=
"5"
class=
"text-center py-4"
>
No widgets found.
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Registered Widgets List -->
<div
class=
"mt-8 flex flex-col"
>
<div
class=
"-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8"
>
<div
class=
"inline-block min-w-full py-2 align-middle md:px-6 lg:px-8"
>
<div
class=
"overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg"
>
<table
class=
"min-w-full divide-y divide-gray-300"
>
<thead
class=
"bg-gray-50"
>
<tr>
<th
scope=
"col"
class=
"py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6"
>
Name
</th>
<th
scope=
"col"
class=
"px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
>
Component
</th>
<th
scope=
"col"
class=
"px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
>
Default Size
</th>
<th
scope=
"col"
class=
"relative py-3.5 pl-3 pr-4 sm:pr-6"
>
<span
class=
"sr-only"
>
Actions
</span>
</th>
</tr>
</thead>
<tbody
class=
"divide-y divide-gray-200 bg-white"
>
<tr
*
ngFor=
"let widget of registeredWidgets"
>
<td
class=
"whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6"
>
<div
class=
"flex items-center"
>
<div
class=
"ml-4"
>
<div
class=
"font-medium text-gray-900"
>
{{ widget.thName }}
</div>
<div
class=
"text-gray-500"
>
{{ widget.engName }}
</div>
</div>
</div>
</td>
<td
class=
"whitespace-nowrap px-3 py-4 text-sm text-gray-500"
>
<span
class=
"inline-flex rounded-full bg-green-100 px-2 text-xs font-semibold leading-5 text-green-800"
>
{{ widget.component }}
</span>
</td>
<td
class=
"whitespace-nowrap px-3 py-4 text-sm text-gray-500"
>
{{ widget.cols }}x{{ widget.rows }}
</td>
<td
class=
"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6"
>
<!-- Preview button can be added here later -->
</td>
</tr>
<tr
*
ngIf=
"registeredWidgets.length === 0"
>
<td
colspan=
"4"
class=
"whitespace-nowrap px-3 py-4 text-sm text-center text-gray-500"
>
No widgets registered yet.
</td>
</tr>
</tbody>
\ No newline at end of file
src/app/portal-manage/widget-management/widget-list.component.ts
View file @
9e7c3447
import
{
Component
,
OnInit
,
Type
}
from
'@angular/core'
;
import
{
ActivatedRoute
,
Router
,
RouterModule
}
from
'@angular/router'
;
import
{
Observable
}
from
'rxjs'
;
import
{
CommonModule
,
TitleCasePipe
}
from
'@angular/common'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
FormsModule
}
from
'@angular/forms'
;
import
{
MatDialog
,
MatDialogModule
}
from
'@angular/material/dialog'
;
import
{
NgComponentOutlet
}
from
'@angular/common'
;
import
{
Store
}
from
'@ngrx/store'
;
import
{
WidgetModel
}
from
'../models/widgets.model'
;
// Import all
the widget components
// Import all
widget components for preview
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
{
NotificationService
}
from
'../../shared/services/notification.service'
;
import
{
WidgetModel
}
from
'../models/widgets.model'
;
import
{
WidgetService
}
from
'../services/widgets.service'
;
import
{
SimpleKpiWidgetComponent
}
from
'../widgets/simple-kpi-widget/simple-kpi-widget.component'
;
@
Component
({
selector
:
'app-widget-list'
,
standalone
:
true
,
imports
:
[
CommonModule
,
RouterModule
,
TitleCasePipe
,
NgComponentOutlet
,
CompanyInfoWidgetComponent
,
HeadcountWidgetComponent
,
AttendanceOverviewWidgetComponent
,
PayrollSummaryWidgetComponent
,
EmployeeDirectoryWidgetComponent
,
KpiWidgetComponent
,
WelcomeWidgetComponent
,
ChartWidgetComponent
,
QuickLinksWidgetComponent
,
SyncfusionDatagridWidgetComponent
,
SyncfusionPivotWidgetComponent
,
SyncfusionChartWidgetComponent
],
templateUrl
:
'./widget-list.component.html'
,
imports
:
[
CommonModule
,
FormsModule
,
MatDialogModule
,
NgComponentOutlet
,
// Import widgets to be available for NgComponentOutlet
CompanyInfoWidgetComponent
,
HeadcountWidgetComponent
,
AttendanceOverviewWidgetComponent
,
PayrollSummaryWidgetComponent
,
EmployeeDirectoryWidgetComponent
,
WelcomeWidgetComponent
,
QuickLinksWidgetComponent
,
SyncfusionDatagridWidgetComponent
,
SyncfusionPivotWidgetComponent
,
SyncfusionChartWidgetComponent
,
SimpleKpiWidgetComponent
],
templateUrl
:
'./widget-list.component.html'
})
export
class
WidgetListComponent
implements
OnInit
{
widgets$
!
:
Observable
<
WidgetModel
[]
>
;
appName
:
string
=
''
;
// Map string names to actual component classes
public
registeredWidgets
:
WidgetModel
[]
=
[];
// This map is crucial for mapping component string names to actual component types for previewing.
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
CompanyInfoWidgetComponent
,
HeadcountWidgetComponent
,
AttendanceOverviewWidgetComponent
,
PayrollSummaryWidgetComponent
,
EmployeeDirectoryWidgetComponent
,
WelcomeWidgetComponent
,
QuickLinksWidgetComponent
,
SyncfusionDatagridWidgetComponent
,
SyncfusionPivotWidgetComponent
,
SyncfusionChartWidgetComponent
,
SimpleKpiWidgetComponent
,
};
constructor
(
private
store
:
Stor
e
,
private
route
:
ActivatedRout
e
,
private
router
:
Router
private
widgetService
:
WidgetServic
e
,
private
notificationService
:
NotificationServic
e
,
private
dialog
:
MatDialog
)
{
}
ngOnInit
():
void
{
// this.store.dispatch(DashboardActions.loadWidgets());
// this.widgets$ = this.store.select(DashboardSelectors.selectAllWidgets);
}
getComponentType
(
componentName
:
string
):
Type
<
any
>
|
null
{
return
this
.
widgetComponentMap
[
componentName
]
||
null
;
this
.
loadRegisteredWidgets
();
}
editWidget
(
widgetId
:
string
):
void
{
this
.
router
.
navigate
([
'/portal-manage'
,
this
.
appName
,
'widget-warehouse'
,
'edit'
,
widgetId
]);
loadRegisteredWidgets
():
void
{
this
.
widgetService
.
getListWidgets
().
subscribe
(
widgets
=>
{
this
.
registeredWidgets
=
widgets
;
});
}
addNewWidget
():
void
{
this
.
router
.
navigate
([
'/portal-manage'
,
this
.
appName
,
'widget-warehouse'
,
'edit'
,
'new'
])
;
getComponentType
(
componentName
:
string
):
Type
<
any
>
{
return
this
.
widgetComponentMap
[
componentName
]
;
}
}
src/assets/data/d-menuitems-widget.json
0 → 100644
View file @
9e7c3447
[{
"companyId"
:
"1"
,
"itemId"
:
"dataset-1-headcount-bar"
,
"datasetId"
:
"people-analytics-dataset"
,
"widget"
:
{
"widgetId"
:
"widget-headcount-bar"
,
"thName"
:
"จำนวนพนักงาน (แผนภูมิแท่ง)"
,
"engName"
:
"Headcount (Bar Chart)"
,
"component"
:
"HeadcountWidgetComponent"
,
"cols"
:
3
,
"rows"
:
2
,
"x"
:
0
,
"y"
:
0
,
"config"
:
"{
\"
title
\"
:
\"
Headcount by Department
\"
,
\"
categoryField
\"
:
\"
department
\"
,
\"
chartType
\"
:
\"
bar
\"
}"
}},
{
"companyId"
:
"1"
,
"itemId"
:
"dataset-1-headcount-doughnut"
,
"datasetId"
:
"people-analytics-dataset"
,
"widget"
:
{
"widgetId"
:
"widget-headcount-doughnut"
,
"thName"
:
"สัดส่วนพนักงาน (แผนภูมิโดนัท)"
,
"engName"
:
"Headcount (Doughnut Chart)"
,
"component"
:
"HeadcountWidgetComponent"
,
"cols"
:
2
,
"rows"
:
2
,
"x"
:
0
,
"y"
:
0
,
"config"
:
"{
\"
title
\"
:
\"
Headcount by Gender
\"
,
\"
categoryField
\"
:
\"
gender
\"
,
\"
chartType
\"
:
\"
doughnut
\"
}"
}},
{
"companyId"
:
"1"
,
"itemId"
:
"dataset-1-emp-directory"
,
"datasetId"
:
"people-analytics-dataset"
,
"widget"
:
{
"widgetId"
:
"widget-emp-directory"
,
"thName"
:
"ทำเนียบพนักงาน"
,
"engName"
:
"Employee Directory"
,
"component"
:
"EmployeeDirectoryWidgetComponent"
,
"cols"
:
2
,
"rows"
:
4
,
"x"
:
0
,
"y"
:
0
,
"config"
:
"{
\"
title
\"
:
\"
Our Employees
\"
,
\"
nameField
\"
:
\"
name
\"
,
\"
positionField
\"
:
\"
position
\"
,
\"
departmentField
\"
:
\"
department
\"
,
\"
photoField
\"
:
\"
photoUrl
\"
}"
}},
{
"companyId"
:
"1"
,
"itemId"
:
"dataset-2-kpi"
,
"datasetId"
:
"financial-dataset"
,
"widget"
:
{
"widgetId"
:
"widget-revenue-kpi"
,
"thName"
:
"รายได้รวม"
,
"engName"
:
"Total Revenue"
,
"component"
:
"SimpleKpiWidgetComponent"
,
"cols"
:
1
,
"rows"
:
1
,
"x"
:
0
,
"y"
:
0
,
"config"
:
"{
\"
title
\"
:
\"
Total Revenue
\"
,
\"
valueField
\"
:
\"
revenue
\"
,
\"
aggregation
\"
:
\"
sum
\"
,
\"
unit
\"
:
\"
THB
\"
,
\"
icon
\"
:
\"
cash-stack
\"
,
\"
color
\"
:
\"
#10b981
\"
}"
}}]}
\ No newline at end of file
src/assets/data/master-widgets.json
0 → 100644
View file @
9e7c3447
[
{
"widgetId"
:
"widget-headcount"
,
"thName"
:
"จำนวนพนักงาน"
,
"engName"
:
"Headcount"
,
"component"
:
"HeadcountWidgetComponent"
,
"cols"
:
3
,
"rows"
:
2
,
"x"
:
0
,
"y"
:
0
},
{
"widgetId"
:
"widget-simple-kpi"
,
"thName"
:
"ตัวชี้วัดอย่างง่าย"
,
"engName"
:
"Simple KPI"
,
"component"
:
"SimpleKpiWidgetComponent"
,
"cols"
:
1
,
"rows"
:
1
,
"x"
:
0
,
"y"
:
0
},
{
"widgetId"
:
"widget-quick-links"
,
"thName"
:
"ลิงก์ด่วน"
,
"engName"
:
"Quick Links"
,
"component"
:
"QuickLinksWidgetComponent"
,
"cols"
:
2
,
"rows"
:
2
,
"x"
:
0
,
"y"
:
0
},
{
"widgetId"
:
"widget-employee-directory"
,
"thName"
:
"ทำเนียบพนักงาน"
,
"engName"
:
"Employee Directory"
,
"component"
:
"EmployeeDirectoryWidgetComponent"
,
"cols"
:
3
,
"rows"
:
4
,
"x"
:
0
,
"y"
:
0
}
]
\ No newline at end of file
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