Commit d608b7f9 by Ooh-Ao

ss

parent 2cc38e98
......@@ -63,24 +63,15 @@
</button>
</div>
<div class="flex-1 p-4 overflow-auto">
<ejs-dashboardlayout #dashboard id='dashboard_layout' [columns]="6" [cellSpacing]="cellSpacing" [panels]="panels">
<e-panels>
<e-panel *ngFor="let panel of panels" [row]="panel.row" [col]="panel.col" [sizeX]="panel.sizeX" [sizeY]="panel.sizeY" [id]="panel.id">
<ng-template [e-header]>
<div class="flex justify-between items-center w-full">
<span>{{panel.header}}</span>
<button (click)="removeWidgetFromDashboard(panel.id!)" class="text-gray-400 hover:text-rose-500 focus:outline-none">
<i class="bi bi-x-lg"></i>
</button>
</div>
</ng-template>
<ng-template [e-content]>
<ng-container *ngComponentOutlet="panel.componentType"></ng-container>
</ng-template>
</e-panel>
</e-panels>
</ejs-dashboardlayout>
<div class="flex-1 p-4 overflow-auto min-h-[600px]">
<div #dashboard>
<div *ngFor="let panel of panels">
<!-- <h3>{{panel.header}}</h3> -->
<div>
<ng-container [ngComponentOutlet]="panel.componentType" [ngComponentOutletInputs]="panel.componentInputs"></ng-container>
</div>
</div>
</div>
</div>
</div>
</div>
......@@ -2,7 +2,7 @@
import { Component, OnInit, ViewChild, Type } from '@angular/core'; // Import Type
import { ActivatedRoute, RouterModule } from '@angular/router';
import { DashboardLayoutComponent, DashboardLayoutModule, PanelModel } from '@syncfusion/ej2-angular-layouts';
// import { DashboardLayoutComponent, DashboardLayoutModule, PanelModel } from '@syncfusion/ej2-angular-layouts';
import { DashboardService } from '../../shared/services/dashboard.service';
import { DashboardLayout, Widget } from '../../shared/models/dashboard.model';
import { switchMap } from 'rxjs/operators';
......@@ -29,17 +29,17 @@ import { SyncfusionChartWidgetComponent } from '../widgets/syncfusion-chart-widg
@Component({
selector: 'app-dashboard-management',
standalone: true,
imports: [CommonModule, RouterModule, DashboardLayoutModule, TitleCasePipe, NgComponentOutlet, FormsModule, /* Also add widgets to imports */ CompanyInfoWidgetComponent, HeadcountWidgetComponent, AttendanceOverviewWidgetComponent, PayrollSummaryWidgetComponent, EmployeeDirectoryWidgetComponent, KpiWidgetComponent, WelcomeWidgetComponent, ChartWidgetComponent, QuickLinksWidgetComponent, SyncfusionDatagridWidgetComponent, SyncfusionPivotWidgetComponent, SyncfusionChartWidgetComponent],
imports: [CommonModule, RouterModule, /*DashboardLayoutModule,*/ TitleCasePipe, NgComponentOutlet, FormsModule, CompanyInfoWidgetComponent, HeadcountWidgetComponent, AttendanceOverviewWidgetComponent, PayrollSummaryWidgetComponent, EmployeeDirectoryWidgetComponent, KpiWidgetComponent, WelcomeWidgetComponent, ChartWidgetComponent, QuickLinksWidgetComponent, SyncfusionDatagridWidgetComponent, SyncfusionPivotWidgetComponent, SyncfusionChartWidgetComponent],
templateUrl: './dashboard-management.component.html',
styleUrls: ['./dashboard-management.component.scss']
})
export class DashboardManagementComponent implements OnInit {
@ViewChild('dashboard') dashboardComponent!: DashboardLayoutComponent;
// @ViewChild('dashboard') dashboardComponent!: DashboardLayoutComponent;
// The panels array will now hold a reference to the component Type
public panels: (PanelModel & { componentType: Type<any> })[] = [];
public panels: (/*PanelModel &*/ { componentType: Type<any>, componentInputs?: { [key: string]: any } })[] = [];
public cellSpacing: number[] = [10, 10];
public availableWidgets: Widget[] = [];
public filteredAvailableWidgets: Widget[] = []; // For search functionality
public widgetSearchTerm: string = ''; // For search functionality
......@@ -118,7 +118,14 @@ export class DashboardManagementComponent implements OnInit {
this.dashboardData = dashboard;
// Store the original name for comparison
this.dashboardData.originalName = dashboard.name;
this.panels = this.mapWidgetsToPanels(dashboard.widgets);
this.panels = this.mapWidgetsToPanels(dashboard.widgets || []);
// Force Syncfusion dashboard to refresh its layout
// Use a setTimeout to ensure the DOM is updated before refresh
// setTimeout(() => {
// if (this.dashboardComponent) {
// this.dashboardComponent.refresh();
// }
// }, 0);
}
});
}
......@@ -156,7 +163,7 @@ export class DashboardManagementComponent implements OnInit {
}
}
mapWidgetsToPanels(widgets: Widget[]): (PanelModel & { componentType: Type<any> })[] {
mapWidgetsToPanels(widgets: Widget[]): (/*PanelModel &*/ { componentType: Type<any>, componentInputs?: { [key: string]: any } })[] {
return widgets.map(widget => ({
id: widget.id,
header: widget.name,
......@@ -165,67 +172,73 @@ export class DashboardManagementComponent implements OnInit {
row: widget.y,
col: widget.x,
// Attach the component Type to the panel object
componentType: this.widgetComponentMap[widget.component]
componentType: this.widgetComponentMap[widget.component],
// Pass inputs if available
componentInputs: widget.inputs
}));
}
saveLayout(): void {
if (!this.dashboardData) return;
const updatedWidgets: Widget[] = this.dashboardComponent.panels.map(panel => {
const baseWidget = this.dashboardData.widgets.find(w => w.id === panel.id) ||
this.availableWidgets.find(w => w.id === panel.id);
return {
...baseWidget!,
id: panel.id!,
cols: panel.sizeX!,
rows: panel.sizeY!,
y: panel.row!,
x: panel.col!,
component: baseWidget!.component // ensure component name is preserved
};
});
this.dashboardData.widgets = updatedWidgets;
this.dashboardService.saveDashboardLayout(this.dashboardData).subscribe(() => {
alert('Dashboard saved successfully!');
});
// if (!this.dashboardData) return;
// if (!this.dashboardComponent || !this.dashboardComponent.panels) {
// console.warn('Dashboard component or panels not ready for saving.');
// return;
// }
// const updatedWidgets: Widget[] = this.dashboardComponent.panels.map(panel => {
// const baseWidget = this.dashboardData.widgets.find(w => w.id === panel.id) ||
// this.availableWidgets.find(w => w.id === panel.id);
// return {
// ...baseWidget!,
// id: panel.id!,
// cols: panel.sizeX!,
// rows: panel.sizeY!,
// y: panel.row!,
// x: panel.col!,
// component: baseWidget!.component // ensure component name is preserved
// };
// });
// this.dashboardData.widgets = updatedWidgets;
// this.dashboardService.saveDashboardLayout(this.dashboardData).subscribe(() => {
// alert('Dashboard saved successfully!');
// });
}
addWidgetToDashboard(widget: Widget): void {
const newPanel = this.mapWidgetsToPanels([widget])[0];
if (this.dashboardComponent.panels.find(p => p.id === newPanel.id)) {
alert('Widget already exists in the dashboard.');
return;
}
this.dashboardComponent.addPanel(newPanel);
// const newPanel = this.mapWidgetsToPanels([widget])[0];
// if (this.dashboardComponent.panels.find(p => p.id === newPanel.id)) {
// alert('Widget already exists in the dashboard.');
// return;
// }
// this.dashboardComponent.addPanel(newPanel);
}
deleteDashboard(): void {
if (this.selectedDashboardId && confirm('Are you sure you want to delete this dashboard?')) {
this.dashboardService.deleteDashboard(this.selectedDashboardId).subscribe(success => {
if (success) {
alert('Dashboard deleted successfully!');
// Remove from userDashboards
this.userDashboards = this.userDashboards.filter(d => d.id !== this.selectedDashboardId);
// Select a new dashboard or create a new one
if (this.userDashboards.length > 0) {
this.selectedDashboardId = this.userDashboards[0].id;
this.loadSelectedDashboard();
} else {
this.createNewDashboard(); // Create a new dashboard if none exist
}
} else {
alert('Failed to delete dashboard.');
}
});
}
// if (this.selectedDashboardId && confirm('Are you sure you want to delete this dashboard?')) {
// this.dashboardService.deleteDashboard(this.selectedDashboardId).subscribe(success => {
// if (success) {
// alert('Dashboard deleted successfully!');
// // Remove from userDashboards
// this.userDashboards = this.userDashboards.filter(d => d.id !== this.selectedDashboardId);
// // Select a new dashboard or create a new one
// if (this.userDashboards.length > 0) {
// this.selectedDashboardId = this.userDashboards[0].id;
// this.loadSelectedDashboard();
// } else {
// this.createNewDashboard(); // Create a new dashboard if none exist
// }
// } else {
// alert('Failed to delete dashboard.');
// }
// });
// }
}
removeWidgetFromDashboard(panelId: string): void {
if (confirm('Are you sure you want to remove this widget?')) {
this.panels = this.panels.filter(panel => panel.id !== panelId);
this.saveLayout(); // Save the layout after removing the widget
}
// if (confirm('Are you sure you want to remove this widget?')) {
// this.panels = this.panels.filter(panel => panel.id !== panelId);
// this.saveLayout(); // Save the layout after removing the widget
// }
}
}
......@@ -9,7 +9,7 @@
<div class="p-2 bg-white border-b border-gray-200 text-gray-700 font-semibold">{{panel.header}}</div>
</ng-template>
<ng-template [e-content]>
<ng-container *ngComponentOutlet="panel.componentType"></ng-container>
<ng-container [ngComponentOutlet]="panel.componentType" [ngComponentOutletInputs]="panel.componentInputs"></ng-container>
</ng-template>
</e-panel>
</e-panels>
......
......@@ -33,7 +33,7 @@ import { SyncfusionChartWidgetComponent } from '../widgets/syncfusion-chart-widg
})
export class DashboardViewerComponent implements OnInit {
public panels: (PanelModel & { componentType: Type<any> })[] = []; // Update type
public panels: (PanelModel & { componentType: Type<any>, componentInputs?: { [key: string]: any } })[] = []; // Update type
public cellSpacing: number[] = [10, 10];
public dashboardName: string = '';
......@@ -86,7 +86,8 @@ export class DashboardViewerComponent implements OnInit {
sizeX: widget.cols,
sizeY: widget.rows,
header: widget.name,
componentType: this.widgetComponentMap[widget.component] // Attach the component Type
componentType: this.widgetComponentMap[widget.component], // Attach the component Type
componentInputs: widget.inputs // Pass inputs if available
}));
}
}
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MockDataService } from '../../common/mock-data.service';
......@@ -13,16 +13,18 @@ declare var ApexCharts: any;
})
export class ChartWidgetComponent implements OnInit, AfterViewInit {
@ViewChild('chart') chartElement!: ElementRef;
@Input() title: string = 'Chart'; // New input for title
@Input() chartData: { categories: string[], series: { name: string, data: number[] }[] } | undefined; // New input for chart data
chartOptions: any;
constructor(private mockDataService: MockDataService) { }
ngOnInit(): void {
const chartData = this.mockDataService.getChartData();
const dataToUse = this.chartData || this.mockDataService.getChartData();
this.chartOptions = {
series: chartData.series,
series: dataToUse.series,
chart: {
type: 'bar',
height: 250,
......@@ -46,7 +48,7 @@ export class ChartWidgetComponent implements OnInit, AfterViewInit {
colors: ['transparent']
},
xaxis: {
categories: chartData.categories,
categories: dataToUse.categories,
},
yaxis: {
title: {
......
import { Component, OnInit } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MockDataService } from '../../common/mock-data.service';
......@@ -9,11 +9,12 @@ import { MockDataService } from '../../common/mock-data.service';
templateUrl: './quick-links-widget.component.html',
})
export class QuickLinksWidgetComponent implements OnInit {
@Input() links: { name: string, url: string }[] | undefined; // New input for links
quickLinks: { name: string, url: string }[] = [];
constructor(private mockDataService: MockDataService) { }
ngOnInit(): void {
this.quickLinks = this.mockDataService.getQuickLinks();
this.quickLinks = this.links || this.mockDataService.getQuickLinks();
}
}
import { Component, OnInit } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ChartModule, ColumnSeriesService, CategoryService, LegendService, TooltipService, DataLabelService } from '@syncfusion/ej2-angular-charts';
......@@ -10,12 +10,14 @@ import { ChartModule, ColumnSeriesService, CategoryService, LegendService, Toolt
templateUrl: './syncfusion-chart-widget.component.html',
})
export class SyncfusionChartWidgetComponent implements OnInit {
@Input() title: string = 'Monthly Sales Analysis'; // New input for title
@Input() chartData: Object[] | undefined; // New input for chart data
public primaryXAxis: Object;
public chartData: Object[];
public title: string;
public localChartData: Object[]; // Renamed to avoid conflict with input
constructor() {
this.chartData = [
this.localChartData = this.chartData || [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: 28 },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: 30 },
......@@ -24,8 +26,12 @@ export class SyncfusionChartWidgetComponent implements OnInit {
{ month: 'Nov', sales: 35 }, { month: 'Dec', sales: 41 }
];
this.primaryXAxis = { valueType: 'Category' };
this.title = 'Monthly Sales Analysis';
}
ngOnInit(): void { }
ngOnInit(): void {
// If chartData is provided as an input, update localChartData
if (this.chartData) {
this.localChartData = this.chartData;
}
}
}
import { Component, OnInit } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GridModule, PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
......@@ -10,11 +10,14 @@ import { GridModule, PageService, SortService, FilterService, GroupService } fro
templateUrl: './syncfusion-datagrid-widget.component.html',
})
export class SyncfusionDatagridWidgetComponent implements OnInit {
@Input() title: string = 'Data Grid'; // New input for title
@Input() gridData: object[] | undefined; // New input for grid data
public data: object[] = [];
public pageSettings: Object = { pageSize: 6 };
ngOnInit(): void {
this.data = [
this.data = this.gridData || [
{ OrderID: 10248, CustomerID: 'VINET', EmployeeID: 5, ShipCity: 'Reims' },
{ OrderID: 10249, CustomerID: 'TOMSP', EmployeeID: 6, ShipCity: 'Münster' },
{ OrderID: 10250, CustomerID: 'HANAR', EmployeeID: 4, ShipCity: 'Rio de Janeiro' },
......
import { Component, OnInit } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PivotViewModule, IDataSet, FieldListService, CalculatedFieldService, ToolbarService, GroupingBarService, ConditionalFormattingService, LoadEventArgs } from '@syncfusion/ej2-angular-pivotview';
......@@ -10,11 +10,15 @@ import { PivotViewModule, IDataSet, FieldListService, CalculatedFieldService, To
templateUrl: './syncfusion-pivot-widget.component.html',
})
export class SyncfusionPivotWidgetComponent implements OnInit {
@Input() title: string = 'Pivot Table'; // New input for title
@Input() pivotData: IDataSet[] | undefined; // New input for pivot data
public dataSource: IDataSet[];
public dataSourceSettings: Object;
constructor() {
this.dataSource = [
// Initialize with default data if no input is provided
this.dataSource = this.pivotData || [
{ 'Sold': 31, 'Amount': 52824, 'Country': 'France', 'Products': 'Mountain Bikes', 'Year': 'FY 2015' },
{ 'Sold': 51, 'Amount': 91915, 'Country': 'France', 'Products': 'Mountain Bikes', 'Year': 'FY 2016' },
{ 'Sold': 90, 'Amount': 173155, 'Country': 'France', 'Products': 'Mountain Bikes', 'Year': 'FY 2017' },
......@@ -36,7 +40,7 @@ export class SyncfusionPivotWidgetComponent implements OnInit {
];
this.dataSourceSettings = {
dataSource: this.dataSource,
dataSource: this.dataSource, // Use the data source that was set
expandAll: false,
rows: [{ name: 'Country' }],
columns: [{ name: 'Year' }],
......@@ -46,5 +50,11 @@ export class SyncfusionPivotWidgetComponent implements OnInit {
};
}
ngOnInit(): void { }
ngOnInit(): void {
// If pivotData is provided as an input, update the dataSourceSettings
if (this.pivotData) {
this.dataSource = this.pivotData;
this.dataSourceSettings = { ...this.dataSourceSettings, dataSource: this.dataSource };
}
}
}
import { Component, OnInit } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MockDataService } from '../../common/mock-data.service';
......@@ -9,11 +9,12 @@ import { MockDataService } from '../../common/mock-data.service';
templateUrl: './welcome-widget.component.html',
})
export class WelcomeWidgetComponent implements OnInit {
@Input() userName: string = 'Admin'; // New input property
welcomeMessage: string = '';
constructor(private mockDataService: MockDataService) { }
ngOnInit(): void {
this.welcomeMessage = this.mockDataService.getWelcomeMessage();
this.welcomeMessage = this.mockDataService.getWelcomeMessage(this.userName);
}
}
......@@ -9,6 +9,7 @@ export interface Widget {
y: number; // Initial Y position in the grid
x: number; // Initial X position in the grid
metaData?: any; // Optional property for additional configuration or data
inputs?: { [key: string]: any }; // Optional property for passing data to the widget component
}
export interface DashboardLayout {
......
......@@ -40,13 +40,15 @@ const HR_WIDGETS: Widget[] = [
id: 'widget-kpi-hires',
name: 'KPI: New Hires',
component: 'KpiWidgetComponent',
cols: 2, rows: 2, y: 0, x: 0
cols: 2, rows: 2, y: 0, x: 0,
inputs: { kpiName: 'New Hires' } // Pass kpiName as input
},
{
id: 'widget-kpi-turnover',
name: 'KPI: Turnover Rate',
component: 'KpiWidgetComponent',
cols: 2, rows: 2, y: 0, x: 2
cols: 2, rows: 2, y: 0, x: 2,
inputs: { kpiName: 'Turnover Rate' } // Pass kpiName as input
},
{
id: 'widget-welcome',
......@@ -58,7 +60,8 @@ const HR_WIDGETS: Widget[] = [
id: 'widget-chart-activity',
name: 'Monthly Activity Chart',
component: 'ChartWidgetComponent',
cols: 4, rows: 3, y: 0, x: 0
cols: 4, rows: 3, y: 0, x: 0,
inputs: { title: 'Monthly Activity' } // Pass title as input
},
{
id: 'widget-quick-links',
......@@ -71,19 +74,22 @@ const HR_WIDGETS: Widget[] = [
id: 'widget-sf-datagrid',
name: 'SF Data Grid',
component: 'SyncfusionDatagridWidgetComponent',
cols: 6, rows: 4, y: 0, x: 0
cols: 6, rows: 4, y: 0, x: 0,
inputs: { title: 'Order Data Grid' } // Pass title as input
},
{
id: 'widget-sf-pivot',
name: 'SF Pivot Table',
component: 'SyncfusionPivotWidgetComponent',
cols: 6, rows: 4, y: 0, x: 0
cols: 6, rows: 4, y: 0, x: 0,
inputs: { title: 'Sales Pivot Analysis' } // Pass title as input
},
{
id: 'widget-sf-chart',
name: 'SF Chart',
component: 'SyncfusionChartWidgetComponent',
cols: 6, rows: 4, y: 0, x: 0
cols: 6, rows: 4, y: 0, x: 0,
inputs: { title: 'Monthly Sales Analysis' } // Pass title as input
},
];
......@@ -126,9 +132,9 @@ const HR_DASHBOARDS: DashboardLayout[] = [
appName: 'redesign',
widgets: [
{ ...HR_WIDGETS[7], cols: 3, rows: 2, x: 0, y: 0 }, // Welcome Message
{ ...HR_WIDGETS[5], cols: 2, rows: 2, x: 3, y: 0 }, // KPI: New Hires
{ ...HR_WIDGETS[6], cols: 2, rows: 2, x: 5, y: 0 }, // KPI: Turnover Rate
{ ...HR_WIDGETS[8], cols: 4, rows: 3, x: 0, y: 2 }, // Monthly Activity Chart
{ ...HR_WIDGETS[5], cols: 2, rows: 2, x: 3, y: 0, inputs: { kpiName: 'New Hires' } }, // KPI: New Hires
{ ...HR_WIDGETS[6], cols: 2, rows: 2, x: 5, y: 0, inputs: { kpiName: 'Turnover Rate' } }, // KPI: Turnover Rate
{ ...HR_WIDGETS[8], cols: 4, rows: 3, x: 0, y: 2, inputs: { title: 'Monthly Activity' } }, // Monthly Activity Chart
{ ...HR_WIDGETS[9], cols: 2, rows: 3, x: 4, y: 2 }, // Quick Links
]
},
......@@ -138,9 +144,9 @@ const HR_DASHBOARDS: DashboardLayout[] = [
name: 'Syncfusion Demo',
appName: 'syncfusion',
widgets: [
{ ...HR_WIDGETS[10], cols: 6, rows: 4, x: 0, y: 0 }, // SF Data Grid
{ ...HR_WIDGETS[11], cols: 6, rows: 4, x: 0, y: 4 }, // SF Pivot Table
{ ...HR_WIDGETS[12], cols: 6, rows: 4, x: 0, y: 8 }, // SF Chart
{ ...HR_WIDGETS[10], cols: 6, rows: 4, x: 0, y: 0, inputs: { title: 'Order Data Grid' } }, // SF Data Grid
{ ...HR_WIDGETS[11], cols: 6, rows: 4, x: 0, y: 4, inputs: { title: 'Sales Pivot Analysis' } }, // SF Pivot Table
{ ...HR_WIDGETS[12], cols: 6, rows: 4, x: 0, y: 8, inputs: { title: 'Monthly Sales Analysis' } }, // SF Chart
]
}
];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment