Commit 5c675edd by Ooh-Ao

aa

parent 090de8bc
<div *ngIf="errorMessage$ | async as errorMessage" class="alert alert-danger">{{errorMessage}}</div>
<div class="flex h-screen bg-gray-50"> <div class="flex h-screen bg-gray-50">
<!-- Widget Sidebar --> <!-- Widget Sidebar -->
......
...@@ -5,12 +5,10 @@ import { ActivatedRoute, RouterModule } from '@angular/router'; ...@@ -5,12 +5,10 @@ import { ActivatedRoute, RouterModule } from '@angular/router';
import { CommonModule, TitleCasePipe } from '@angular/common'; import { CommonModule, TitleCasePipe } from '@angular/common';
import { NgComponentOutlet } from '@angular/common'; import { NgComponentOutlet } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Observable, of } from 'rxjs';
import { Observable } from 'rxjs';
import { DashboardModel, WidgetModel, DatasetModel } from '../models/widgets.model'; import { DashboardModel, WidgetModel, DatasetModel } from '../models/widgets.model';
import * as DashboardActions from '../state/dashboard.actions'; import { DashboardDataService } from '../services/dashboard-data.service';
import * as DashboardSelectors from '../state/dashboard.selectors';
// Import all the widget components // Import all the widget components
import { CompanyInfoWidgetComponent } from '../widgets/company-info-widget.component'; import { CompanyInfoWidgetComponent } from '../widgets/company-info-widget.component';
...@@ -26,7 +24,6 @@ import { SyncfusionDatagridWidgetComponent } from '../widgets/syncfusion-datagri ...@@ -26,7 +24,6 @@ import { SyncfusionDatagridWidgetComponent } from '../widgets/syncfusion-datagri
import { SyncfusionPivotWidgetComponent } from '../widgets/syncfusion-pivot-widget/syncfusion-pivot-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 { SyncfusionChartWidgetComponent } from '../widgets/syncfusion-chart-widget/syncfusion-chart-widget.component';
import { DatasetPickerComponent } from './dataset-picker.component'; import { DatasetPickerComponent } from './dataset-picker.component';
import { MockDashboardService } from '../services/mock-dashboard.service';
@Component({ @Component({
selector: 'app-dashboard-management', selector: 'app-dashboard-management',
...@@ -44,15 +41,11 @@ export class DashboardManagementComponent implements OnInit { ...@@ -44,15 +41,11 @@ export class DashboardManagementComponent implements OnInit {
public filteredAvailableWidgets: WidgetModel[] = []; public filteredAvailableWidgets: WidgetModel[] = [];
public widgetSearchTerm: string = ''; public widgetSearchTerm: string = '';
public dashboardData: DashboardModel; public dashboardData: DashboardModel | null = null;
public userDashboards: DashboardModel[] = []; public userDashboards: DashboardModel[] = [];
public selectedDashboardId: string = ''; public selectedDashboardId: string = '';
public userDashboards$: Observable<DashboardModel[]>;
public availableWidgets$: Observable<WidgetModel[]>;
public errorMessage$: Observable<string | null>;
private widgetComponentMap: { [key: string]: Type<any> } = { private widgetComponentMap: { [key: string]: Type<any> } = {
'CompanyInfoWidgetComponent': CompanyInfoWidgetComponent, 'CompanyInfoWidgetComponent': CompanyInfoWidgetComponent,
'HeadcountWidgetComponent': HeadcountWidgetComponent, 'HeadcountWidgetComponent': HeadcountWidgetComponent,
...@@ -69,24 +62,24 @@ export class DashboardManagementComponent implements OnInit { ...@@ -69,24 +62,24 @@ export class DashboardManagementComponent implements OnInit {
}; };
constructor( constructor(
private store: Store,
private route: ActivatedRoute, private route: ActivatedRoute,
private mockDashboardService: MockDashboardService private dashboardDataService: DashboardDataService
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
this.store.dispatch(DashboardActions.loadDashboards()); this.dashboardDataService.getDashboards().subscribe(dashboards => {
this.store.dispatch(DashboardActions.loadWidgets());
this.userDashboards$ = this.store.select(DashboardSelectors.selectAllDashboards);
this.availableWidgets$ = this.store.select(DashboardSelectors.selectAllWidgets);
this.errorMessage$ = this.store.select(DashboardSelectors.selectError);
this.mockDashboardService.getDashboards().subscribe(dashboards => {
this.userDashboards = dashboards; this.userDashboards = dashboards;
if (this.userDashboards.length > 0) { if (this.userDashboards.length > 0) {
this.selectedDashboardId = this.userDashboards[0].id; this.selectedDashboardId = this.userDashboards[0].id;
this.loadSelectedDashboard(); this.loadSelectedDashboard();
} }
}); });
// Assuming availableWidgets are loaded from a separate service or hardcoded for now
// If they come from an API, DashboardDataService should provide a getWidgets() method
// For now, we'll just use the mock service's getWidgets if it exists, or hardcode them.
// Since MockDashboardService doesn't have getWidgets(), we'll leave availableWidgets empty for now
// or you can populate it with static data if needed for testing the widget sidebar.
this.availableWidgets = []; // Or populate with static data if needed
} }
...@@ -108,7 +101,7 @@ export class DashboardManagementComponent implements OnInit { ...@@ -108,7 +101,7 @@ export class DashboardManagementComponent implements OnInit {
loadSelectedDashboard(): void { loadSelectedDashboard(): void {
if (this.selectedDashboardId) { if (this.selectedDashboardId) {
this.store.select(DashboardSelectors.selectDashboardById(this.selectedDashboardId)).subscribe(dashboard => { this.dashboardDataService.getDashboardById(this.selectedDashboardId).subscribe(dashboard => {
if (dashboard) { if (dashboard) {
this.dashboardData = dashboard; this.dashboardData = dashboard;
this.panels = this.mapWidgetsToPanels(dashboard.widgets || []); this.panels = this.mapWidgetsToPanels(dashboard.widgets || []);
...@@ -126,13 +119,17 @@ export class DashboardManagementComponent implements OnInit { ...@@ -126,13 +119,17 @@ export class DashboardManagementComponent implements OnInit {
name: newDashboardName, name: newDashboardName,
widgets: [] widgets: []
}); });
this.store.dispatch(DashboardActions.addDashboard({ dashboard: newDashboard })); this.dashboardDataService.addDashboard(newDashboard).subscribe(addedDashboard => {
this.userDashboards.push(addedDashboard);
this.selectedDashboardId = addedDashboard.id;
this.loadSelectedDashboard();
});
} }
} }
saveDashboardName(): void { saveDashboardName(): void {
if (this.dashboardData) { if (this.dashboardData) {
this.store.dispatch(DashboardActions.updateDashboard({ dashboard: this.dashboardData })); this.dashboardDataService.updateDashboard(this.dashboardData).subscribe();
} }
} }
...@@ -145,17 +142,18 @@ export class DashboardManagementComponent implements OnInit { ...@@ -145,17 +142,18 @@ export class DashboardManagementComponent implements OnInit {
row: widget.y, row: widget.y,
col: widget.x, col: widget.x,
componentType: this.widgetComponentMap[widget.component], componentType: this.widgetComponentMap[widget.component],
componentInputs: { ...widget.data, datasetId: this.dashboardData.datasetId } componentInputs: { ...widget.data, datasetId: this.dashboardData?.datasetId }
})); }));
} }
saveLayout(): void { saveLayout(): void {
if (!this.dashboardData) return; if (!this.dashboardData) return;
this.store.dispatch(DashboardActions.updateDashboard({ dashboard: this.dashboardData })); this.dashboardDataService.updateDashboard(this.dashboardData).subscribe();
} }
addWidgetToDashboard(widget: WidgetModel): void { addWidgetToDashboard(widget: WidgetModel): void {
if (!this.dashboardData) return; // Add null check
if (this.dashboardData.widgets.find(w => w.id === widget.id)) { if (this.dashboardData.widgets.find(w => w.id === widget.id)) {
alert('Widget already exists in the dashboard.'); alert('Widget already exists in the dashboard.');
return; return;
...@@ -166,14 +164,31 @@ export class DashboardManagementComponent implements OnInit { ...@@ -166,14 +164,31 @@ export class DashboardManagementComponent implements OnInit {
deleteDashboard(): void { deleteDashboard(): void {
if (this.selectedDashboardId && confirm('Are you sure you want to delete this dashboard?')) { if (this.selectedDashboardId && confirm('Are you sure you want to delete this dashboard?')) {
this.store.dispatch(DashboardActions.deleteDashboard({ id: this.selectedDashboardId })); this.dashboardDataService.deleteDashboard(this.selectedDashboardId).subscribe(() => {
// After deletion, reload dashboards and select the first one or clear selection
this.dashboardDataService.getDashboards().subscribe(dashboards => {
this.userDashboards = dashboards;
if (this.userDashboards.length > 0) {
this.selectedDashboardId = this.userDashboards[0].id;
this.loadSelectedDashboard();
} else {
this.selectedDashboardId = '';
this.dashboardData = null; // Clear dashboard data if no dashboards left
this.panels = [];
}
});
});
} }
} }
removeWidgetFromDashboard(panelId: string): void { removeWidgetFromDashboard(panelId: string): void {
if (!this.dashboardData) return; // Add null check
if (confirm('Are you sure you want to remove this widget?')) { if (confirm('Are you sure you want to remove this widget?')) {
const updatedDashboard = { ...this.dashboardData, widgets: this.dashboardData.widgets.filter(w => w.id !== panelId) }; const updatedDashboard = { ...this.dashboardData, widgets: this.dashboardData.widgets.filter(w => w.id !== panelId) };
this.store.dispatch(DashboardActions.updateDashboard({ dashboard: updatedDashboard })); this.dashboardDataService.updateDashboard(updatedDashboard).subscribe(updated => {
this.dashboardData = updated;
this.panels = this.mapWidgetsToPanels(this.dashboardData.widgets);
});
} }
} }
......
...@@ -3,12 +3,10 @@ import { Component, OnInit, Type, ChangeDetectionStrategy } from '@angular/core' ...@@ -3,12 +3,10 @@ import { Component, OnInit, Type, ChangeDetectionStrategy } from '@angular/core'
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ActivatedRoute, RouterModule } from '@angular/router'; import { ActivatedRoute, RouterModule } from '@angular/router';
import { DashboardLayoutModule, PanelModel } from '@syncfusion/ej2-angular-layouts'; import { DashboardLayoutModule, PanelModel } from '@syncfusion/ej2-angular-layouts';
import { Store } from '@ngrx/store'; import { Observable, of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
import { NgComponentOutlet } from '@angular/common'; // Import NgComponentOutlet import { NgComponentOutlet } from '@angular/common'; // Import NgComponentOutlet
import * as DashboardActions from '../state/dashboard.actions'; import { DashboardDataService } from '../services/dashboard-data.service';
import * as DashboardSelectors from '../state/dashboard.selectors';
import { DashboardModel } from '../models/widgets.model'; import { DashboardModel } from '../models/widgets.model';
// Import all the widget components // Import all the widget components
...@@ -61,7 +59,7 @@ export class DashboardViewerComponent implements OnInit { ...@@ -61,7 +59,7 @@ export class DashboardViewerComponent implements OnInit {
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private store: Store private dashboardDataService: DashboardDataService
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
...@@ -72,8 +70,7 @@ export class DashboardViewerComponent implements OnInit { ...@@ -72,8 +70,7 @@ export class DashboardViewerComponent implements OnInit {
console.error('Dashboard ID is missing from the route.'); console.error('Dashboard ID is missing from the route.');
return of(null); return of(null);
} }
this.store.dispatch(DashboardActions.loadDashboards()); return this.dashboardDataService.getDashboardById(id);
return this.store.select(DashboardSelectors.selectDashboardById(id));
}) })
).subscribe({ ).subscribe({
next: dashboard => { next: dashboard => {
......
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { DashboardModel, WidgetModel } from '../models/widgets.model';
import { MockDashboardService } from './mock-dashboard.service';
@Injectable({
providedIn: 'root'
})
export class DashboardDataService {
constructor(private mockDashboardService: MockDashboardService) { }
getDashboards(): Observable<DashboardModel[]> {
// In a real application, this would call an API
return this.mockDashboardService.getDashboards();
}
getDashboardById(id: string): Observable<DashboardModel | undefined> {
// In a real application, this would call an API
return this.mockDashboardService.getDashboardById(id);
}
// For now, these methods will operate on the in-memory mock data
// In a real application, these would call API endpoints for CUD operations
addDashboard(dashboard: DashboardModel): Observable<DashboardModel> {
// Simulate adding to mock data (not persistent across app restarts)
const currentDashboards = this.mockDashboardService.getDashboards();
currentDashboards.subscribe(dashboards => {
dashboards.push(dashboard);
});
return of(dashboard);
}
updateDashboard(updatedDashboard: DashboardModel): Observable<DashboardModel> {
// Simulate updating mock data (not persistent across app restarts)
const currentDashboards = this.mockDashboardService.getDashboards();
currentDashboards.subscribe(dashboards => {
const index = dashboards.findIndex(d => d.id === updatedDashboard.id);
if (index > -1) {
dashboards[index] = updatedDashboard;
}
});
return of(updatedDashboard);
}
deleteDashboard(id: string): Observable<void> {
// Simulate deleting from mock data (not persistent across app restarts)
const currentDashboards = this.mockDashboardService.getDashboards();
currentDashboards.subscribe(dashboards => {
const index = dashboards.findIndex(d => d.id === id);
if (index > -1) {
dashboards.splice(index, 1);
}
});
return of(undefined);
}
// Assuming widgets are managed as part of dashboards for now
// If widgets were separate entities, you'd have getWidgets, addWidget, etc.
}
...@@ -20,6 +20,25 @@ export const MOCK_DASHBOARD_DATA: DashboardModel[] = [ ...@@ -20,6 +20,25 @@ export const MOCK_DASHBOARD_DATA: DashboardModel[] = [
{ id: '201', name: 'Website Traffic', component: 'ChartWidgetComponent', cols: 2, rows: 1, x: 0, y: 0, data: { type: 'line', labels: ['Week 1', 'Week 2', 'Week 3'], datasets: [{ data: [120, 150, 130], label: 'Visitors' }] } }, { id: '201', name: 'Website Traffic', component: 'ChartWidgetComponent', cols: 2, rows: 1, x: 0, y: 0, data: { type: 'line', labels: ['Week 1', 'Week 2', 'Week 3'], datasets: [{ data: [120, 150, 130], label: 'Visitors' }] } },
{ id: '202', name: 'Conversion Rate', component: 'KpiWidgetComponent', cols: 1, rows: 1, x: 2, y: 0, data: { value: '2.5%', label: 'Conversion' } } { id: '202', name: 'Conversion Rate', component: 'KpiWidgetComponent', cols: 1, rows: 1, x: 2, y: 0, data: { value: '2.5%', label: 'Conversion' } }
] ]
},
{
id: '3',
name: 'All Widgets Test Dashboard',
description: 'Dashboard for testing all available widgets.',
widgets: [
{ id: '301', name: 'Company Info', component: 'CompanyInfoWidgetComponent', cols: 2, rows: 1, x: 0, y: 0, data: { companyName: 'Test Company', address: '123 Test St.' } },
{ id: '302', name: 'Headcount', component: 'HeadcountWidgetComponent', cols: 1, rows: 1, x: 2, y: 0, data: { count: 150 } },
{ id: '303', name: 'Attendance Overview', component: 'AttendanceOverviewWidgetComponent', cols: 2, rows: 1, x: 3, y: 0, data: { present: 140, absent: 10 } },
{ id: '304', name: 'Payroll Summary', component: 'PayrollSummaryWidgetComponent', cols: 2, rows: 1, x: 0, y: 1, data: { totalPayroll: 100000, currency: 'USD' } },
{ id: '305', name: 'Employee Directory', component: 'EmployeeDirectoryWidgetComponent', cols: 2, rows: 2, x: 2, y: 1, data: { employees: [{ name: 'Alice', dept: 'HR' }, { name: 'Bob', dept: 'IT' }] } },
{ id: '306', name: 'KPI Widget', component: 'KpiWidgetComponent', cols: 1, rows: 1, x: 4, y: 1, data: { value: '95%', label: 'Success Rate' } },
{ id: '307', name: 'Welcome Widget', component: 'WelcomeWidgetComponent', cols: 2, rows: 1, x: 0, y: 3, data: { message: 'Welcome to the Dashboard!' } },
{ id: '308', name: 'Chart Widget', component: 'ChartWidgetComponent', cols: 2, rows: 1, x: 2, y: 3, data: { type: 'bar', labels: ['A', 'B', 'C'], datasets: [{ data: [10, 20, 30], label: 'Data' }] } },
{ id: '309', name: 'Quick Links', component: 'QuickLinksWidgetComponent', cols: 1, rows: 1, x: 4, y: 3, data: { links: [{ name: 'Google', url: 'https://google.com' }] } },
{ id: '310', name: 'Syncfusion Datagrid', component: 'SyncfusionDatagridWidgetComponent', cols: 3, rows: 2, x: 0, y: 4, data: { columns: ['ID', 'Name'], data: [{ ID: 1, Name: 'Item 1' }, { ID: 2, Name: 'Item 2' }] } },
{ id: '311', name: 'Syncfusion Pivot', component: 'SyncfusionPivotWidgetComponent', cols: 3, rows: 2, x: 3, y: 4, data: { data: [{ year: '2023', sales: 100 }, { year: '2024', sales: 120 }], rows: [{ name: 'year' }], columns: [{ name: 'sales' }] } },
{ id: '312', name: 'Syncfusion Chart', component: 'SyncfusionChartWidgetComponent', cols: 2, rows: 1, x: 0, y: 6, data: { chartData: [{ x: 'Jan', y: 10 }, { x: 'Feb', y: 20 }], primaryXAxis: { valueType: 'Category' }, series: [{ type: 'Column', xName: 'x', yName: 'y' }] } }
]
} }
]; ];
......
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