Commit 188a3ec4 by Ooh-Ao

widet

parent ba652c3a
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MockDataService {
constructor() { }
getKpiData(kpiName: string) {
const kpiData: { [key: string]: { value: number, trend: 'up' | 'down' | 'flat', percentage: number } } = {
'New Hires': { value: 12, trend: 'up', percentage: 15 },
'Turnover Rate': { value: 4, trend: 'down', percentage: 10 },
'Employee Satisfaction': { value: 4.5, trend: 'up', percentage: 5 },
'Projects Completed': { value: 25, trend: 'up', percentage: 20 }
};
return kpiData[kpiName] || { value: 0, trend: 'flat', percentage: 0 };
}
getChartData() {
return {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
series: [
{
name: 'Hiring',
data: [5, 8, 12, 10, 15, 18]
},
{
name: 'Resignations',
data: [2, 3, 5, 4, 6, 5]
}
]
};
}
getWelcomeMessage(userName: string = 'Admin') {
return `Welcome back, ${userName}!`;
}
getQuickLinks() {
return [
{ name: 'Company Portal', url: '#' },
{ name: 'HR Handbook', url: '#' },
{ name: 'Submit IT Ticket', url: '#' },
{ name: 'View Paystubs', url: '#' }
];
}
}
<div class="flex h-screen bg-gray-50">
<div class="flex h-screen bg-gray-100">
<!-- Widget Sidebar -->
<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-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)">
<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 class="w-72 bg-white p-4 overflow-y-auto shadow-lg border-r flex flex-col">
<h2 class="text-xl font-bold mb-4 text-gray-800">Widgets</h2>
<!-- Widget Search -->
<div class="relative mb-4">
<input type="text" [(ngModel)]="widgetSearchTerm" (input)="filterWidgets()" placeholder="Search widgets..."
class="w-full p-2 pl-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<svg class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
<button *ngIf="widgetSearchTerm" (click)="clearSearch()" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<!-- Widget List -->
<div class="flex-grow">
<div *ngFor="let widget of filteredAvailableWidgets"
class="p-3 mb-2 bg-gray-50 rounded-lg border border-gray-200 cursor-pointer hover:bg-blue-50 hover:border-blue-400 hover:shadow-md transition-all duration-200"
(click)="addWidgetToDashboard(widget)">
<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>
<p *ngIf="filteredAvailableWidgets.length === 0" class="text-gray-500 text-center mt-4">No widgets found.</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 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">
<div class="flex items-center space-x-4">
<h1 class="text-xl font-bold text-gray-700">Dashboard:</h1>
<!-- Dashboard Selector -->
<select [(ngModel)]="selectedDashboardId" (change)="loadSelectedDashboard()"
class="p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white">
<option *ngFor="let dash of userDashboards" [value]="dash.id">{{ dash.name }}</option>
</select>
<div class="relative flex items-center">
<input type="text" [(ngModel)]="dashboardData.name" (blur)="saveDashboardName()"
class="p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white text-gray-800 font-semibold">
<button *ngIf="dashboardData?.name !== dashboardData?.originalName" (click)="saveDashboardName()"
class="ml-2 text-green-500 hover:text-green-700 focus:outline-none">
<i class="bi bi-check-circle-fill text-2xl"></i>
</button>
</div>
<button (click)="createNewDashboard()"
class="bg-blue-500 hover:bg-blue-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-plus-circle-fill"></i> New Dashboard
</button>
<button *ngIf="selectedDashboardId" (click)="deleteDashboard()"
class="bg-red-500 hover:bg-red-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-trash-fill"></i> Delete Dashboard
</button>
</div>
<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>
......@@ -25,7 +68,12 @@
<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>
<div class="flex justify-between items-center w-full">
<span>{{panel.header}}</span>
<button (click)="removeWidgetFromDashboard(panel.id!)" class="text-gray-400 hover:text-red-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>
......
......@@ -8,6 +8,7 @@ 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 { FormsModule } from '@angular/forms'; // Import FormsModule for ngModel
// Import all the widget components
import { CompanyInfoWidgetComponent } from '../widgets/company-info-widget.component';
......@@ -15,11 +16,16 @@ 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';
// New Widget Imports
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';
@Component({
selector: 'app-dashboard-management',
standalone: true,
imports: [CommonModule, RouterModule, DashboardLayoutModule, TitleCasePipe, NgComponentOutlet, /* Also add widgets to imports */ CompanyInfoWidgetComponent, HeadcountWidgetComponent, AttendanceOverviewWidgetComponent, PayrollSummaryWidgetComponent, EmployeeDirectoryWidgetComponent],
imports: [CommonModule, RouterModule, DashboardLayoutModule, TitleCasePipe, NgComponentOutlet, FormsModule, /* Also add widgets to imports */ CompanyInfoWidgetComponent, HeadcountWidgetComponent, AttendanceOverviewWidgetComponent, PayrollSummaryWidgetComponent, EmployeeDirectoryWidgetComponent, KpiWidgetComponent, WelcomeWidgetComponent, ChartWidgetComponent, QuickLinksWidgetComponent],
templateUrl: './dashboard-management.component.html',
styleUrls: ['./dashboard-management.component.scss']
})
......@@ -31,16 +37,27 @@ export class DashboardManagementComponent implements OnInit {
public cellSpacing: number[] = [10, 10];
public availableWidgets: Widget[] = [];
public filteredAvailableWidgets: Widget[] = []; // For search functionality
public widgetSearchTerm: string = ''; // For search functionality
public appName: string = '';
public dashboardData!: DashboardLayout;
public userDashboards: DashboardLayout[] = []; // To hold all dashboards for the app
public selectedDashboardId: string = ''; // To hold the ID of the currently selected dashboard
// Map string names to actual component classes
private widgetComponentMap: { [key: string]: Type<any> } = {
'CompanyInfoWidgetComponent': CompanyInfoWidgetComponent,
'HeadcountWidgetComponent': HeadcountWidgetComponent,
'AttendanceOverviewWidgetComponent': AttendanceOverviewWidgetComponent,
'PayrollSummaryWidgetComponent': PayrollSummaryWidgetComponent,
'EmployeeDirectoryWidgetComponent': EmployeeDirectoryWidgetComponent
'EmployeeDirectoryWidgetComponent': EmployeeDirectoryWidgetComponent,
// New Widget Mappings
'KpiWidgetComponent': KpiWidgetComponent,
'WelcomeWidgetComponent': WelcomeWidgetComponent,
'ChartWidgetComponent': ChartWidgetComponent,
'QuickLinksWidgetComponent': QuickLinksWidgetComponent
};
constructor(
......@@ -49,32 +66,84 @@ export class DashboardManagementComponent implements OnInit {
) { }
ngOnInit(): void {
this.appName = this.route.snapshot.paramMap.get('appName') || '';
this.appName = this.route.snapshot.paramMap.get('appName') || 'redesign'; // Default to 'redesign'
if (this.appName) {
this.dashboardService.getDashboards(this.appName).pipe(
switchMap(dashboards => {
if (dashboards.length > 0) {
return this.dashboardService.getDashboardLayout(dashboards[0].id);
} else {
const newDashboard: DashboardLayout = {
id: `dash-${this.appName}-${Date.now()}`,
appName: this.appName,
name: `${this.appName} Dashboard`,
widgets: []
};
return this.dashboardService.saveDashboardLayout(newDashboard);
}
})
).subscribe(dashboard => {
this.dashboardService.getDashboards(this.appName).subscribe(dashboards => {
this.userDashboards = dashboards;
if (this.userDashboards.length > 0) {
// Try to load the 'redesign' dashboard if it exists, otherwise load the first one
const redesignDashboard = this.userDashboards.find(d => d.id === 'dash-redesign-main');
this.selectedDashboardId = redesignDashboard ? redesignDashboard.id : this.userDashboards[0].id;
this.loadSelectedDashboard();
} else {
this.createNewDashboard(); // Create a new dashboard if none exist
}
});
this.dashboardService.getWidgets(this.appName).subscribe(widgets => {
this.availableWidgets = widgets;
this.filterWidgets(); // Initialize filtered widgets
});
}
}
filterWidgets(): void {
if (!this.widgetSearchTerm) {
this.filteredAvailableWidgets = [...this.availableWidgets];
} else {
this.filteredAvailableWidgets = this.availableWidgets.filter(widget =>
widget.name.toLowerCase().includes(this.widgetSearchTerm.toLowerCase())
);
}
}
clearSearch(): void {
this.widgetSearchTerm = '';
this.filterWidgets();
}
loadSelectedDashboard(): void {
if (this.selectedDashboardId) {
this.dashboardService.getDashboardLayout(this.selectedDashboardId).subscribe(dashboard => {
if (dashboard) {
this.dashboardData = dashboard;
// Store the original name for comparison
this.dashboardData.originalName = dashboard.name;
this.panels = this.mapWidgetsToPanels(dashboard.widgets);
}
});
}
}
createNewDashboard(): void {
const newDashboardName = prompt('Enter a name for the new dashboard:');
if (newDashboardName) {
const newDashboard: DashboardLayout = {
id: `dash-${this.appName}-${Date.now()}`,
appName: this.appName,
name: newDashboardName,
widgets: []
};
this.dashboardService.saveDashboardLayout(newDashboard).subscribe(savedDashboard => {
this.userDashboards.push(savedDashboard);
this.selectedDashboardId = savedDashboard.id;
this.loadSelectedDashboard();
});
}
}
this.dashboardService.getWidgets(this.appName).subscribe(widgets => {
this.availableWidgets = widgets;
saveDashboardName(): void {
if (this.dashboardData && this.dashboardData.name !== this.dashboardData.originalName) {
this.dashboardService.saveDashboardLayout(this.dashboardData).subscribe(savedDashboard => {
// Update the original name after saving
this.dashboardData.originalName = savedDashboard.name;
// Update the name in the userDashboards array for the dropdown
const index = this.userDashboards.findIndex(d => d.id === savedDashboard.id);
if (index !== -1) {
this.userDashboards[index].name = savedDashboard.name;
}
alert('Dashboard name updated successfully!');
});
}
}
......@@ -123,4 +192,32 @@ export class DashboardManagementComponent implements OnInit {
}
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.');
}
});
}
}
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
}
}
}
......@@ -2,7 +2,17 @@
<div class="container mx-auto p-4">
<h2 class="text-2xl font-bold mb-4">Viewing Dashboard: {{ dashboardName }}</h2>
<div class="control-section">
<ejs-dashboardlayout id='dashboard_viewer' #viewerLayout [cellSpacing]="cellSpacing" [panels]="panels" [columns]="6">
<ejs-dashboardlayout id='dashboard_viewer' #viewerLayout [cellSpacing]="cellSpacing" [panels]="panels" [columns]="6" [allowResizing]="false" [allowDragging]="false">
<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>
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, Type } from '@angular/core'; // Import Type
import { CommonModule } from '@angular/common';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { DashboardLayoutModule, PanelModel } from '@syncfusion/ej2-angular-layouts';
import { DashboardService } from '../../shared/services/dashboard.service';
import { DashboardLayout, Widget } from '../../shared/models/dashboard.model'; // Correctly import DashboardLayout and Widget
import { DashboardLayout, Widget } from '../../shared/models/dashboard.model';
import { map, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
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';
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';
@Component({
selector: 'app-dashboard-viewer',
standalone: true,
imports: [CommonModule, RouterModule, DashboardLayoutModule],
imports: [CommonModule, RouterModule, DashboardLayoutModule, NgComponentOutlet, CompanyInfoWidgetComponent, HeadcountWidgetComponent, AttendanceOverviewWidgetComponent, PayrollSummaryWidgetComponent, EmployeeDirectoryWidgetComponent, KpiWidgetComponent, WelcomeWidgetComponent, ChartWidgetComponent, QuickLinksWidgetComponent],
templateUrl: './dashboard-viewer.component.html',
styleUrls: ['./dashboard-viewer.component.scss']
})
export class DashboardViewerComponent implements OnInit {
public panels: PanelModel[] = [];
public panels: (PanelModel & { componentType: Type<any> })[] = []; // Update type
public cellSpacing: number[] = [10, 10];
public dashboardName: string = '';
// Map string names to actual component classes
private widgetComponentMap: { [key: string]: Type<any> } = {
'CompanyInfoWidgetComponent': CompanyInfoWidgetComponent,
'HeadcountWidgetComponent': HeadcountWidgetComponent,
'AttendanceOverviewWidgetComponent': AttendanceOverviewWidgetComponent,
'PayrollSummaryWidgetComponent': PayrollSummaryWidgetComponent,
'EmployeeDirectoryWidgetComponent': EmployeeDirectoryWidgetComponent,
'KpiWidgetComponent': KpiWidgetComponent,
'WelcomeWidgetComponent': WelcomeWidgetComponent,
'ChartWidgetComponent': ChartWidgetComponent,
'QuickLinksWidgetComponent': QuickLinksWidgetComponent
};
constructor(
private route: ActivatedRoute,
private dashboardService: DashboardService
......@@ -28,14 +53,12 @@ export class DashboardViewerComponent implements OnInit {
ngOnInit(): void {
this.route.paramMap.pipe(
map(params => params.get('dashboardId')), // Changed from 'id' to 'dashboardId' to match routing
map(params => params.get('dashboardId')),
switchMap(id => {
if (!id) {
// If no ID, return an observable of null or a default dashboard
console.error('Dashboard ID is missing from the route.');
return of(null);
}
// Use the correct service method
return this.dashboardService.getDashboardLayout(id);
})
).subscribe(dashboard => {
......@@ -47,7 +70,7 @@ export class DashboardViewerComponent implements OnInit {
loadDashboard(dashboard: DashboardLayout): void {
this.dashboardName = dashboard.name;
// Map widgets from the layout to Syncfusion PanelModels
// Map widgets from the layout to Syncfusion PanelModels, attaching componentType
this.panels = dashboard.widgets.map((widget: Widget) => ({
id: widget.id,
row: widget.y,
......@@ -55,7 +78,7 @@ export class DashboardViewerComponent implements OnInit {
sizeX: widget.cols,
sizeY: widget.rows,
header: widget.name,
content: `<div id='${widget.component}'></div>` // Placeholder for component rendering
componentType: this.widgetComponentMap[widget.component] // Attach the component Type
}));
}
}
<div class="bg-white p-6 rounded-lg shadow-md h-full flex flex-col">
<h3 class="text-lg font-semibold text-gray-500 mb-4">Monthly Activity</h3>
<div id="chart" #chart class="flex-grow"></div>
</div>
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MockDataService } from '../../common/mock-data.service';
// Declare ApexCharts globally if it's loaded via a script tag
declare var ApexCharts: any;
@Component({
selector: 'app-chart-widget',
standalone: true,
imports: [CommonModule],
templateUrl: './chart-widget.component.html',
})
export class ChartWidgetComponent implements OnInit, AfterViewInit {
@ViewChild('chart') chartElement!: ElementRef;
chartOptions: any;
constructor(private mockDataService: MockDataService) { }
ngOnInit(): void {
const chartData = this.mockDataService.getChartData();
this.chartOptions = {
series: chartData.series,
chart: {
type: 'bar',
height: 250,
toolbar: {
show: false
}
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '55%',
endingShape: 'rounded'
},
},
dataLabels: {
enabled: false
},
stroke: {
show: true,
width: 2,
colors: ['transparent']
},
xaxis: {
categories: chartData.categories,
},
yaxis: {
title: {
text: 'Number'
}
},
fill: {
opacity: 1
},
tooltip: {
y: {
formatter: function (val: number) {
return val + " units"
}
}
}
};
}
ngAfterViewInit(): void {
if (typeof ApexCharts !== 'undefined') {
const chart = new ApexCharts(this.chartElement.nativeElement, this.chartOptions);
chart.render();
} else {
console.error('ApexCharts is not loaded.');
}
}
}
<div class="bg-white p-6 rounded-lg shadow-md flex flex-col justify-between h-full">
<div>
<h3 class="text-lg font-semibold text-gray-500">{{ kpiName }}</h3>
<p class="text-4xl font-bold text-gray-800 mt-2">{{ kpiData.value }}</p>
</div>
<div class="flex items-center mt-4 text-sm" [ngClass]="{'text-green-500': kpiData.trend === 'up', 'text-red-500': kpiData.trend === 'down'}">
<svg *ngIf="kpiData.trend === 'up'" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-8.707a1 1 0 00-1.414-1.414L11 9.586V6a1 1 0 10-2 0v3.586l-1.293-1.293a1 1 0 00-1.414 1.414l3 3a1 1 0 001.414 0l3-3z" clip-rule="evenodd" />
</svg>
<svg *ngIf="kpiData.trend === 'down'" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm-3.707-7.293a1 1 0 011.414 0L9 11.414V14a1 1 0 112 0v-2.586l1.293-1.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
<span>{{ kpiData.percentage }}%</span>
<span class="ml-1">vs last month</span>
</div>
</div>
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MockDataService } from '../../common/mock-data.service';
@Component({
selector: 'app-kpi-widget',
standalone: true,
imports: [CommonModule],
templateUrl: './kpi-widget.component.html',
})
export class KpiWidgetComponent implements OnInit {
@Input() kpiName: string = 'New Hires'; // Default KPI
kpiData!: { value: number, trend: 'up' | 'down' | 'flat', percentage: number };
constructor(private mockDataService: MockDataService) { }
ngOnInit(): void {
this.kpiData = this.mockDataService.getKpiData(this.kpiName);
}
}
<div class="bg-white p-6 rounded-lg shadow-md h-full flex flex-col">
<h3 class="text-lg font-semibold text-gray-500 mb-4">Quick Links</h3>
<ul class="space-y-2">
<li *ngFor="let link of quickLinks">
<a [href]="link.url" class="text-blue-600 hover:text-blue-800 hover:underline text-base font-medium block p-2 rounded-md transition-colors duration-200 hover:bg-blue-50">
{{ link.name }}
</a>
</li>
</ul>
</div>
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MockDataService } from '../../common/mock-data.service';
@Component({
selector: 'app-quick-links-widget',
standalone: true,
imports: [CommonModule],
templateUrl: './quick-links-widget.component.html',
})
export class QuickLinksWidgetComponent implements OnInit {
quickLinks: { name: string, url: string }[] = [];
constructor(private mockDataService: MockDataService) { }
ngOnInit(): void {
this.quickLinks = this.mockDataService.getQuickLinks();
}
}
<div class="bg-gradient-to-r from-blue-500 to-purple-600 p-6 rounded-lg shadow-md text-white flex flex-col justify-center items-center h-full text-center">
<h2 class="text-3xl font-bold mb-2">{{ welcomeMessage }}</h2>
<p class="text-lg">Have a productive day!</p>
</div>
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MockDataService } from '../../common/mock-data.service';
@Component({
selector: 'app-welcome-widget',
standalone: true,
imports: [CommonModule],
templateUrl: './welcome-widget.component.html',
})
export class WelcomeWidgetComponent implements OnInit {
welcomeMessage: string = '';
constructor(private mockDataService: MockDataService) { }
ngOnInit(): void {
this.welcomeMessage = this.mockDataService.getWelcomeMessage();
}
}
......@@ -17,4 +17,5 @@ export interface DashboardLayout {
appName: string; // The application module this dashboard belongs to (e.g., 'myhr-plus')
widgets: Widget[]; // An array of widgets included in this dashboard
layoutConfig?: any; // Optional property for Syncfusion-specific layout configurations
originalName?: string; // Used to track the original name for editing purposes
}
\ No newline at end of file
......@@ -35,6 +35,37 @@ const HR_WIDGETS: Widget[] = [
component: 'EmployeeDirectoryWidgetComponent',
cols: 6, rows: 3, y: 0, x: 0
},
// New Widgets
{
id: 'widget-kpi-hires',
name: 'KPI: New Hires',
component: 'KpiWidgetComponent',
cols: 2, rows: 2, y: 0, x: 0
},
{
id: 'widget-kpi-turnover',
name: 'KPI: Turnover Rate',
component: 'KpiWidgetComponent',
cols: 2, rows: 2, y: 0, x: 2
},
{
id: 'widget-welcome',
name: 'Welcome Message',
component: 'WelcomeWidgetComponent',
cols: 3, rows: 2, y: 0, x: 0
},
{
id: 'widget-chart-activity',
name: 'Monthly Activity Chart',
component: 'ChartWidgetComponent',
cols: 4, rows: 3, y: 0, x: 0
},
{
id: 'widget-quick-links',
name: 'Quick Links',
component: 'QuickLinksWidgetComponent',
cols: 2, rows: 3, y: 0, x: 0
},
];
const HR_DASHBOARDS: DashboardLayout[] = [
......@@ -68,6 +99,19 @@ const HR_DASHBOARDS: DashboardLayout[] = [
name: 'Company Dashboard',
appName: 'myhr-lite',
widgets: [HR_WIDGETS[0]]
},
// New Dashboard for 'redesign' app
{
id: 'dash-redesign-main',
name: 'Redesign Dashboard',
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[9], cols: 2, rows: 3, x: 4, y: 2 }, // Quick Links
]
}
];
......@@ -118,4 +162,13 @@ export class DashboardService {
}
return of(widget);
}
deleteDashboard(dashboardId: string): Observable<boolean> {
const index = HR_DASHBOARDS.findIndex(d => d.id === dashboardId);
if (index !== -1) {
HR_DASHBOARDS.splice(index, 1);
return of(true);
}
return of(false);
}
}
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