Commit ba652c3a by Ooh-Ao

dashhbpard

parent 9e284e22
<div class="flex h-screen">
<div class="flex h-screen bg-gray-50">
<!-- Widget Sidebar -->
<div class="w-64 bg-gray-100 p-4 overflow-y-auto">
<h2 class="text-lg font-bold mb-4">Available Widgets</h2>
<div class="w-64 bg-white p-4 overflow-y-auto shadow-lg border-r">
<h2 class="text-lg font-bold mb-4 text-gray-800">Available Widgets</h2>
<div *ngFor="let widget of availableWidgets"
class="p-2 mb-2 bg-white rounded shadow cursor-pointer hover:bg-gray-200"
class="p-3 mb-2 bg-white rounded-lg border border-gray-200 cursor-pointer hover:bg-blue-50 hover:border-blue-400 hover:shadow-md transition-all"
(click)="addWidgetToDashboard(widget)">
{{ widget.name }}
<p class="font-semibold text-gray-700">{{ widget.name }}</p>
<p class="text-xs text-gray-500">Size: {{widget.cols}}x{{widget.rows}}</p>
</div>
</div>
<!-- Dashboard Area -->
<div class="flex-1 flex flex-col">
<div class="p-4 bg-white border-b border-gray-200 flex justify-between items-center">
<h1 class="text-xl font-bold">Dashboard Management for {{ appName | titlecase }}</h1>
<button (click)="saveLayout()" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
Save Layout
<div class="p-4 bg-white border-b border-gray-200 flex justify-between items-center shadow-sm">
<h1 class="text-xl font-bold text-gray-700">Dashboard: {{ dashboardData?.name || (appName | titlecase) }}</h1>
<button (click)="saveLayout()" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition-transform transform hover:scale-105 flex items-center gap-2">
<i class="bi bi-check-circle-fill"></i> Save Layout
</button>
</div>
<div class="flex-1 p-4 overflow-auto">
<ejs-dashboardlayout #dashboard id='dashboard_layout' [columns]="6" [cellSpacing]="cellSpacing" [panels]="panels">
<!-- Note: Dynamic component rendering inside panels requires more advanced setup -->
<!-- This template uses a simple placeholder in the content -->
<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>{{panel.header}}</div>
</ng-template>
<ng-template [e-content]>
<ng-container *ngComponentOutlet="panel.componentType"></ng-container>
</ng-template>
</e-panel>
</e-panels>
</ejs-dashboardlayout>
</div>
</div>
</div>
\ No newline at end of file
</div>
import { Component, OnInit, ViewChild } from '@angular/core';
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 { DashboardService } from '../../shared/services/dashboard.service';
import { DashboardLayout, Widget } from '../../shared/models/dashboard.model';
import { switchMap } from 'rxjs/operators';
import { CommonModule, TitleCasePipe } from '@angular/common';
import { NgComponentOutlet } from '@angular/common'; // Import NgComponentOutlet
// Import all the 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';
@Component({
selector: 'app-dashboard-management',
standalone: true,
imports: [CommonModule, RouterModule, DashboardLayoutModule, TitleCasePipe],
imports: [CommonModule, RouterModule, DashboardLayoutModule, TitleCasePipe, NgComponentOutlet, /* Also add widgets to imports */ CompanyInfoWidgetComponent, HeadcountWidgetComponent, AttendanceOverviewWidgetComponent, PayrollSummaryWidgetComponent, EmployeeDirectoryWidgetComponent],
templateUrl: './dashboard-management.component.html',
styleUrls: ['./dashboard-management.component.scss']
})
export class DashboardManagementComponent implements OnInit {
@ViewChild('dashboard') dashboardComponent!: DashboardLayoutComponent;
public panels: PanelModel[] = [];
// The panels array will now hold a reference to the component Type
public panels: (PanelModel & { componentType: Type<any> })[] = [];
public cellSpacing: number[] = [10, 10];
public availableWidgets: Widget[] = [];
private appName: string = '';
private dashboardData!: DashboardLayout;
public appName: string = '';
public dashboardData!: DashboardLayout;
// Map string names to actual component classes
private widgetComponentMap: { [key: string]: Type<any> } = {
'CompanyInfoWidgetComponent': CompanyInfoWidgetComponent,
'HeadcountWidgetComponent': HeadcountWidgetComponent,
'AttendanceOverviewWidgetComponent': AttendanceOverviewWidgetComponent,
'PayrollSummaryWidgetComponent': PayrollSummaryWidgetComponent,
'EmployeeDirectoryWidgetComponent': EmployeeDirectoryWidgetComponent
};
constructor(
private dashboardService: DashboardService,
......@@ -34,14 +52,11 @@ export class DashboardManagementComponent implements OnInit {
this.appName = this.route.snapshot.paramMap.get('appName') || '';
if (this.appName) {
// For simplicity, we'll load the first dashboard for the app
// In a real app, you would have a list of dashboards to choose from
this.dashboardService.getDashboards(this.appName).pipe(
switchMap(dashboards => {
if (dashboards.length > 0) {
return this.dashboardService.getDashboardLayout(dashboards[0].id);
} else {
// Create a new dashboard if none exist
const newDashboard: DashboardLayout = {
id: `dash-${this.appName}-${Date.now()}`,
appName: this.appName,
......@@ -58,22 +73,22 @@ export class DashboardManagementComponent implements OnInit {
}
});
// Load widgets available for this app
this.dashboardService.getWidgets(this.appName).subscribe(widgets => {
this.availableWidgets = widgets;
});
}
}
mapWidgetsToPanels(widgets: Widget[]): PanelModel[] {
mapWidgetsToPanels(widgets: Widget[]): (PanelModel & { componentType: Type<any> })[] {
return widgets.map(widget => ({
id: widget.id,
header: widget.name,
content: `<div id='${widget.component}'></div>`, // Placeholder for component rendering
sizeX: widget.cols,
sizeY: widget.rows,
row: widget.y,
col: widget.x
col: widget.x,
// Attach the component Type to the panel object
componentType: this.widgetComponentMap[widget.component]
}));
}
......@@ -89,7 +104,8 @@ export class DashboardManagementComponent implements OnInit {
cols: panel.sizeX!,
rows: panel.sizeY!,
y: panel.row!,
x: panel.col!
x: panel.col!,
component: baseWidget!.component // ensure component name is preserved
};
});
......@@ -100,7 +116,11 @@ export class DashboardManagementComponent implements OnInit {
}
addWidgetToDashboard(widget: Widget): void {
const newPanel: PanelModel = this.mapWidgetsToPanels([widget])[0];
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);
}
}
\ No newline at end of file
}
import { Component } from '@angular/core';
@Component({
selector: 'app-attendance-overview-widget',
standalone: true,
template: `
<div class="p-4 h-full bg-yellow-50">
<h3 class="font-bold text-yellow-800">Today's Attendance</h3>
<p class="text-sm text-yellow-600">Summary of employees present, on leave, or absent.</p>
</div>
`
})
export class AttendanceOverviewWidgetComponent { }
import { Component } from '@angular/core';
@Component({
selector: 'app-company-info-widget',
standalone: true,
template: `
<div class="p-4 h-full bg-blue-50">
<h3 class="font-bold text-blue-800">Company Information</h3>
<p class="text-sm text-blue-600">Details about the company profile and address.</p>
</div>
`
})
export class CompanyInfoWidgetComponent { }
import { Component } from '@angular/core';
@Component({
selector: 'app-employee-directory-widget',
standalone: true,
template: `
<div class="p-4 h-full bg-indigo-50">
<h3 class="font-bold text-indigo-800">Employee Directory</h3>
<p class="text-sm text-indigo-600">Searchable list of all employees.</p>
</div>
`
})
export class EmployeeDirectoryWidgetComponent { }
import { Component } from '@angular/core';
@Component({
selector: 'app-headcount-widget',
standalone: true,
template: `
<div class="p-4 h-full bg-green-50">
<h3 class="font-bold text-green-800">Employee Headcount</h3>
<p class="text-sm text-green-600">Chart showing number of employees by department.</p>
</div>
`
})
export class HeadcountWidgetComponent { }
import { Component } from '@angular/core';
@Component({
selector: 'app-payroll-summary-widget',
standalone: true,
template: `
<div class="p-4 h-full bg-red-50">
<h3 class="font-bold text-red-800">Payroll Summary</h3>
<p class="text-sm text-red-600">Overview of the latest payroll cycle.</p>
</div>
`
})
export class PayrollSummaryWidgetComponent { }
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { DashboardLayout, Widget } from '../models/dashboard.model';
// Mock data for demonstration purposes
const MOCK_WIDGETS: Widget[] = [
{ id: 'widget-1', name: 'Stock Ticker', component: 'WidgetStockComponent', cols: 2, rows: 1, y: 0, x: 0 },
{ id: 'widget-2', name: 'Company News', component: 'CompanyNewsComponent', cols: 3, rows: 2, y: 0, x: 2 },
{ id: 'widget-3', name: 'My Tasks', component: 'MyTasksComponent', cols: 2, rows: 2, y: 1, x: 0 },
{ id: 'widget-4', name: 'Quick Links', component: 'QuickLinksComponent', cols: 1, rows: 1, y: 2, x: 4 }
// More realistic mock data for an HR system
const HR_WIDGETS: Widget[] = [
{
id: 'widget-company-info',
name: 'Company Info',
component: 'CompanyInfoWidgetComponent',
cols: 3, rows: 2, y: 0, x: 0
},
{
id: 'widget-headcount',
name: 'Headcount by Dept',
component: 'HeadcountWidgetComponent',
cols: 3, rows: 2, y: 0, x: 3
},
{
id: 'widget-attendance',
name: 'Attendance Overview',
component: 'AttendanceOverviewWidgetComponent',
cols: 2, rows: 2, y: 0, x: 0
},
{
id: 'widget-payroll',
name: 'Payroll Summary',
component: 'PayrollSummaryWidgetComponent',
cols: 4, rows: 2, y: 0, x: 2
},
{
id: 'widget-directory',
name: 'Employee Directory',
component: 'EmployeeDirectoryWidgetComponent',
cols: 6, rows: 3, y: 0, x: 0
},
];
const MOCK_DASHBOARDS: DashboardLayout[] = [
const HR_DASHBOARDS: DashboardLayout[] = [
{
id: 'dash-company-main',
name: 'Company Overview',
appName: 'myhr-plus', // Example app
widgets: [HR_WIDGETS[0], HR_WIDGETS[1]]
},
{
id: 'dash-employee-main',
name: 'Employee Dashboard',
appName: 'myhr-plus',
widgets: [HR_WIDGETS[4]]
},
{
id: 'dash-time-main',
name: 'Time & Attendance',
appName: 'myhr-plus',
widgets: [HR_WIDGETS[2]]
},
{
id: 'dash-myhr-1',
name: 'MyHR Overview',
id: 'dash-payroll-main',
name: 'Payroll Dashboard',
appName: 'myhr-plus',
widgets: [MOCK_WIDGETS[0], MOCK_WIDGETS[2]]
widgets: [HR_WIDGETS[3]]
},
// You can create dashboards for other apps like 'myhr-lite' as well
{
id: 'dash-myjob-1',
name: 'MyJob Dashboard',
appName: 'myjob',
widgets: [MOCK_WIDGETS[1], MOCK_WIDGETS[3]]
id: 'dash-lite-company',
name: 'Company Dashboard',
appName: 'myhr-lite',
widgets: [HR_WIDGETS[0]]
}
];
......@@ -32,53 +78,44 @@ export class DashboardService {
constructor() { }
// Get all dashboards for a specific application
getDashboards(appName: string): Observable<DashboardLayout[]> {
const dashboards = MOCK_DASHBOARDS.filter(d => d.appName === appName);
const dashboards = HR_DASHBOARDS.filter(d => d.appName === appName);
return of(dashboards);
}
// Get a single dashboard layout by its ID
getDashboardLayout(dashboardId: string): Observable<DashboardLayout | undefined> {
const dashboard = MOCK_DASHBOARDS.find(d => d.id === dashboardId);
// Return a deep copy to prevent mutation of mock data
const dashboard = HR_DASHBOARDS.find(d => d.id === dashboardId);
return of(dashboard ? JSON.parse(JSON.stringify(dashboard)) : undefined);
}
// Save a dashboard layout
saveDashboardLayout(layout: DashboardLayout): Observable<DashboardLayout> {
const index = MOCK_DASHBOARDS.findIndex(d => d.id === layout.id);
const index = HR_DASHBOARDS.findIndex(d => d.id === layout.id);
if (index !== -1) {
MOCK_DASHBOARDS[index] = layout;
HR_DASHBOARDS[index] = layout;
} else {
MOCK_DASHBOARDS.push(layout);
HR_DASHBOARDS.push(layout);
}
return of(layout);
}
// Get all available widgets for a specific application
// In a real app, this might be filtered by appName as well
getWidgets(appName: string): Observable<Widget[]> {
// Currently returns all mock widgets for any app
return of(MOCK_WIDGETS);
// In a real app, you might filter widgets based on the appName or user permissions
return of(HR_WIDGETS);
}
// Get a single widget by its ID
getWidget(widgetId: string): Observable<Widget | undefined> {
const widget = MOCK_WIDGETS.find(w => w.id === widgetId);
const widget = HR_WIDGETS.find(w => w.id === widgetId);
return of(widget);
}
// Save a widget (create or update)
saveWidget(widget: Widget): Observable<Widget> {
const index = MOCK_WIDGETS.findIndex(w => w.id === widget.id);
const index = HR_WIDGETS.findIndex(w => w.id === widget.id);
if (index !== -1) {
MOCK_WIDGETS[index] = widget;
HR_WIDGETS[index] = widget;
} else {
// In a real app, generate a new ID
widget.id = `widget-${Date.now()}`;
MOCK_WIDGETS.push(widget);
HR_WIDGETS.push(widget);
}
return of(widget);
}
}
\ No newline at end of file
}
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