Commit 2ad457f5 by Ooh-Ao

s

parent cc462cc0
...@@ -744,8 +744,7 @@ export class DashboardManagementComponent implements OnInit { ...@@ -744,8 +744,7 @@ export class DashboardManagementComponent implements OnInit {
this.dashboardData.datasetId = dataset.itemId; this.dashboardData.datasetId = dataset.itemId;
this.dashboardData.templateId = dataset.templateId; this.dashboardData.templateId = dataset.templateId;
this.dashboardData.fileName = dataset.fileName; this.dashboardData.fileName = dataset.fileName;
this.dashboardStateService.selectDataset(dataset); this.dashboardStateService.selectDataset(dataset.itemId);
// this.getDatasetByTemplate(dataset);
} }
} }
......
<div *ngIf="errorMessage" class="alert alert-danger">{{errorMessage}}</div> <div *ngIf="errorMessage" class="alert alert-danger">{{errorMessage}}</div>
<div class="dashboard-viewer-container"> <div *ngIf="dashboardData" class="dashboard-viewer-container p-4">
<h1 class="text-2xl font-bold mb-4 text-gray-800">{{ dashboardData.thName }}</h1>
<div class="control-section"> <div class="control-section">
<ejs-dashboardlayout id='dashboard_viewer' #viewerLayout [cellSpacing]="cellSpacing" [panels]="panels" [columns]="6" [allowResizing]="false" [allowDragging]="false"> <ejs-dashboardlayout id='dashboard_viewer' #viewerLayout [cellSpacing]="cellSpacing" [panels]="panels" [columns]="6" [allowResizing]="false" [allowDragging]="false">
<e-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"> <e-panel *ngFor="let panel of panels" [row]="panel.row" [col]="panel.col" [sizeX]="panel.sizeX" [sizeY]="panel.sizeY" [id]="panel.id">
<ng-template #header> <ng-template #header>
<div class="p-2 bg-white border-b border-gray-200 text-gray-700 font-semibold flex justify-between items-center"> <div class="e-panel-header flex justify-between items-center">
<span>{{panel.header}}</span> <span>{{panel.header}}</span>
</div> </div>
</ng-template> </ng-template>
......
import { Component, OnInit, Type, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; // Import Type import { Component, OnInit, Type, ChangeDetectorRef } 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 { Observable, of, forkJoin } from 'rxjs'; // Import forkJoin import { of } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators'; // Import tap import { map, switchMap } from 'rxjs/operators';
import { NgComponentOutlet } from '@angular/common'; // Import NgComponentOutlet import { NgComponentOutlet } from '@angular/common';
import { DashboardDataService } from '../services/dashboard-data.service'; import { DashboardDataService } from '../services/dashboard-data.service';
import { WidgetDataService } from '../services/widget-data.service';
import { DashboardModel, WidgetModel } from '../models/widgets.model'; import { DashboardModel, WidgetModel } from '../models/widgets.model';
import { DashboardStateService } from '../services/dashboard-state.service'; // Import DashboardStateService import { DashboardStateService } from '../services/dashboard-state.service';
// Import all the widget components // Import all widget components
import { CompanyInfoWidgetComponent } from '../widgets/company-info-widget.component'; import { CompanyInfoWidgetComponent } from '../widgets/company-info-widget.component';
import { HeadcountWidgetComponent } from '../widgets/headcount-widget.component'; import { HeadcountWidgetComponent } from '../widgets/headcount-widget.component';
import { AttendanceOverviewWidgetComponent } from '../widgets/attendance-overview-widget.component'; import { AttendanceOverviewWidgetComponent } from '../widgets/attendance-overview-widget.component';
...@@ -20,11 +19,10 @@ import { KpiWidgetComponent } from '../widgets/kpi-widget/kpi-widget.component'; ...@@ -20,11 +19,10 @@ import { KpiWidgetComponent } from '../widgets/kpi-widget/kpi-widget.component';
import { WelcomeWidgetComponent } from '../widgets/welcome-widget/welcome-widget.component'; import { WelcomeWidgetComponent } from '../widgets/welcome-widget/welcome-widget.component';
import { ChartWidgetComponent } from '../widgets/chart-widget/chart-widget.component'; import { ChartWidgetComponent } from '../widgets/chart-widget/chart-widget.component';
import { QuickLinksWidgetComponent } from '../widgets/quick-links-widget/quick-links-widget.component'; import { QuickLinksWidgetComponent } from '../widgets/quick-links-widget/quick-links-widget.component';
// New Syncfusion Widget Imports
import { SyncfusionDatagridWidgetComponent } from '../widgets/syncfusion-datagrid-widget/syncfusion-datagrid-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 { 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 { DataTableWidgetComponent } from '../widgets/dynamic-widgets/data-table-widget.component'; // Import new widget import { DataTableWidgetComponent } from '../widgets/dynamic-widgets/data-table-widget.component';
import { AreaChartWidgetComponent } from '../widgets/area-chart-widget/area-chart-widget.component'; import { AreaChartWidgetComponent } from '../widgets/area-chart-widget/area-chart-widget.component';
import { BarChartWidgetComponent } from '../widgets/bar-chart-widget/bar-chart-widget.component'; import { BarChartWidgetComponent } from '../widgets/bar-chart-widget/bar-chart-widget.component';
import { PieChartWidgetComponent } from '../widgets/pie-chart-widget/pie-chart-widget.component'; import { PieChartWidgetComponent } from '../widgets/pie-chart-widget/pie-chart-widget.component';
...@@ -41,49 +39,30 @@ import { SlicerWidgetComponent } from '../widgets/slicer-widget/slicer-widget.co ...@@ -41,49 +39,30 @@ import { SlicerWidgetComponent } from '../widgets/slicer-widget/slicer-widget.co
import { SimpleTableWidgetComponent } from '../widgets/simple-table-widget/simple-table-widget.component'; import { SimpleTableWidgetComponent } from '../widgets/simple-table-widget/simple-table-widget.component';
import { WaterfallChartWidgetComponent } from '../widgets/waterfall-chart-widget/waterfall-chart-widget.component'; import { WaterfallChartWidgetComponent } from '../widgets/waterfall-chart-widget/waterfall-chart-widget.component';
import { TreemapWidgetComponent } from '../widgets/treemap-widget/treemap-widget.component'; import { TreemapWidgetComponent } from '../widgets/treemap-widget/treemap-widget.component';
import { HttpClientModule } from '@angular/common/http';
export interface DashboardPanel extends PanelModel {
componentType: Type<any>;
componentInputs?: { [key: string]: any };
}
@Component({ @Component({
selector: 'app-dashboard-viewer', selector: 'app-dashboard-viewer',
standalone: true, standalone: true,
imports: [ imports: [
CommonModule, CommonModule, RouterModule, DashboardLayoutModule, NgComponentOutlet,
RouterModule, // Add all widget components here to make them available for NgComponentOutlet
DashboardLayoutModule, CompanyInfoWidgetComponent, HeadcountWidgetComponent, AttendanceOverviewWidgetComponent, PayrollSummaryWidgetComponent, EmployeeDirectoryWidgetComponent, KpiWidgetComponent, WelcomeWidgetComponent, ChartWidgetComponent, QuickLinksWidgetComponent, SyncfusionDatagridWidgetComponent, SyncfusionPivotWidgetComponent, SyncfusionChartWidgetComponent, DataTableWidgetComponent, AreaChartWidgetComponent, BarChartWidgetComponent, PieChartWidgetComponent, ScatterBubbleChartWidgetComponent, MultiRowCardWidgetComponent, ComboChartWidgetComponent, DoughnutChartWidgetComponent, FunnelChartWidgetComponent, GaugeChartWidgetComponent, SimpleKpiWidgetComponent, FilledMapWidgetComponent, MatrixWidgetComponent, SlicerWidgetComponent, SimpleTableWidgetComponent, WaterfallChartWidgetComponent, TreemapWidgetComponent
NgComponentOutlet,
SyncfusionDatagridWidgetComponent,
SyncfusionPivotWidgetComponent,
SyncfusionChartWidgetComponent,
DataTableWidgetComponent, // Add new widget to imports
AreaChartWidgetComponent,
BarChartWidgetComponent,
PieChartWidgetComponent,
ScatterBubbleChartWidgetComponent,
MultiRowCardWidgetComponent,
ComboChartWidgetComponent,
DoughnutChartWidgetComponent,
FunnelChartWidgetComponent,
GaugeChartWidgetComponent,
SimpleKpiWidgetComponent,
FilledMapWidgetComponent,
MatrixWidgetComponent,
SlicerWidgetComponent,
SimpleTableWidgetComponent,
WaterfallChartWidgetComponent,
TreemapWidgetComponent,
], ],
templateUrl: './dashboard-viewer.component.html', templateUrl: './dashboard-viewer.component.html',
styleUrls: ['./dashboard-viewer.component.scss'], styleUrls: ['./dashboard-viewer.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
}) })
export class DashboardViewerComponent implements OnInit { export class DashboardViewerComponent implements OnInit {
public panels: (PanelModel & { componentType: Type<any>, componentInputs?: { [key: string]: any }, componentName?: string })[] = []; // Update type public panels: DashboardPanel[] = [];
public cellSpacing: number[] = [10, 10]; public cellSpacing: number[] = [10, 10];
public dashboardName: string = ''; public dashboardData: DashboardModel | null = null;
public errorMessage: string | null = null;
// Map string names to actual component classes
private widgetComponentMap: { [key: string]: Type<any> } = { private widgetComponentMap: { [key: string]: Type<any> } = {
'CompanyInfoWidgetComponent': CompanyInfoWidgetComponent, 'CompanyInfoWidgetComponent': CompanyInfoWidgetComponent,
'HeadcountWidgetComponent': HeadcountWidgetComponent, 'HeadcountWidgetComponent': HeadcountWidgetComponent,
...@@ -94,11 +73,10 @@ export class DashboardViewerComponent implements OnInit { ...@@ -94,11 +73,10 @@ export class DashboardViewerComponent implements OnInit {
'WelcomeWidgetComponent': WelcomeWidgetComponent, 'WelcomeWidgetComponent': WelcomeWidgetComponent,
'ChartWidgetComponent': ChartWidgetComponent, 'ChartWidgetComponent': ChartWidgetComponent,
'QuickLinksWidgetComponent': QuickLinksWidgetComponent, 'QuickLinksWidgetComponent': QuickLinksWidgetComponent,
// New Syncfusion Widget Mappings
'SyncfusionDatagridWidgetComponent': SyncfusionDatagridWidgetComponent, 'SyncfusionDatagridWidgetComponent': SyncfusionDatagridWidgetComponent,
'SyncfusionPivotWidgetComponent': SyncfusionPivotWidgetComponent, 'SyncfusionPivotWidgetComponent': SyncfusionPivotWidgetComponent,
'SyncfusionChartWidgetComponent': SyncfusionChartWidgetComponent, 'SyncfusionChartWidgetComponent': SyncfusionChartWidgetComponent,
'NewDataTableWidget': DataTableWidgetComponent, // Add new widget to map 'NewDataTableWidget': DataTableWidgetComponent,
'AreaChartWidgetComponent': AreaChartWidgetComponent, 'AreaChartWidgetComponent': AreaChartWidgetComponent,
'BarChartWidgetComponent': BarChartWidgetComponent, 'BarChartWidgetComponent': BarChartWidgetComponent,
'PieChartWidgetComponent': PieChartWidgetComponent, 'PieChartWidgetComponent': PieChartWidgetComponent,
...@@ -117,14 +95,10 @@ export class DashboardViewerComponent implements OnInit { ...@@ -117,14 +95,10 @@ export class DashboardViewerComponent implements OnInit {
'TreemapWidgetComponent': TreemapWidgetComponent, 'TreemapWidgetComponent': TreemapWidgetComponent,
}; };
public errorMessage: string | null = null;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private dashboardDataService: DashboardDataService, private dashboardDataService: DashboardDataService,
private dashboardStateService: DashboardStateService, // Inject DashboardStateService private dashboardStateService: DashboardStateService,
private widgetDataService: WidgetDataService,
private cdr: ChangeDetectorRef
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
...@@ -133,7 +107,6 @@ export class DashboardViewerComponent implements OnInit { ...@@ -133,7 +107,6 @@ export class DashboardViewerComponent implements OnInit {
switchMap(id => { switchMap(id => {
if (!id) { if (!id) {
this.errorMessage = 'Dashboard ID is missing from the route.'; this.errorMessage = 'Dashboard ID is missing from the route.';
console.error(this.errorMessage);
return of(null); return of(null);
} }
return this.dashboardDataService.getDashboardById(id); return this.dashboardDataService.getDashboardById(id);
...@@ -141,7 +114,11 @@ export class DashboardViewerComponent implements OnInit { ...@@ -141,7 +114,11 @@ export class DashboardViewerComponent implements OnInit {
).subscribe({ ).subscribe({
next: dashboard => { next: dashboard => {
if (dashboard) { if (dashboard) {
this.loadDashboard(dashboard); this.dashboardData = dashboard;
this.panels = this.mapWidgetsToPanels(dashboard.widgets || []);
if (dashboard.datasetId) {
this.dashboardStateService.selectDataset(dashboard.datasetId);
}
} else { } else {
this.errorMessage = 'Could not load the dashboard.'; this.errorMessage = 'Could not load the dashboard.';
} }
...@@ -150,49 +127,19 @@ export class DashboardViewerComponent implements OnInit { ...@@ -150,49 +127,19 @@ export class DashboardViewerComponent implements OnInit {
}); });
} }
loadDashboard(dashboard: DashboardModel): void { mapWidgetsToPanels(widgets: WidgetModel[]): DashboardPanel[] {
this.dashboardName = dashboard.thName; return widgets.map(widget => {
if (dashboard.datasetId) {
// this.dashboardStateService.selectDataset(dashboard.datasetId);
}
// Process widgets and fetch data if necessary
const dataFetchTasks: Observable<any>[] = dashboard.widgets
.filter(widget => widget.config?.source?.type === 'url' && widget.config?.source?.url)
.map(widget =>
this.widgetDataService.fetchDataForWidget(widget.config).pipe(
tap(data => {
// Mutate the config object to include the fetched data
widget.config.data = data;
})
)
);
if (dataFetchTasks.length > 0) {
forkJoin(dataFetchTasks).subscribe(() => {
this.createPanels(dashboard.widgets);
});
} else {
this.createPanels(dashboard.widgets);
}
}
createPanels(widgets: WidgetModel[]): void {
this.panels = widgets.map(widget => {
const widgetConfig = widget.config || {}; const widgetConfig = widget.config || {};
return { return {
id: widget.widgetId, id: widget.widgetId,
row: widget.y, header: widget.thName,
col: widget.x,
sizeX: widget.cols, sizeX: widget.cols,
sizeY: widget.rows, sizeY: widget.rows,
header: widget.thName, row: widget.y,
componentName: widget.component, col: widget.x,
componentType: this.widgetComponentMap[widget.component], componentType: this.widgetComponentMap[widget.component],
componentInputs: { config: widgetConfig } componentInputs: { config: widgetConfig },
}; };
}); });
this.cdr.detectChanges(); // Manually trigger change detection
} }
} }
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, of } from 'rxjs'; import { BehaviorSubject, Observable, of } from 'rxjs';
import { switchMap, map, shareReplay } from 'rxjs/operators'; import { switchMap, map, shareReplay, tap } from 'rxjs/operators';
import { DatasetService } from './dataset.service'; import { DatasetService } from './dataset.service';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { DatasetModel } from '../models/widgets.model'; import { DatasetModel } from '../models/widgets.model';
...@@ -14,35 +14,43 @@ export interface SelectedDataset { ...@@ -14,35 +14,43 @@ export interface SelectedDataset {
providedIn: 'root' providedIn: 'root'
}) })
export class DashboardStateService { export class DashboardStateService {
private selectedDatasetId = new BehaviorSubject<DatasetModel | null>(null); private selectedDatasetId = new BehaviorSubject<string | null>(null);
public selectedDataset$: Observable<SelectedDataset | null>; public selectedDataset$: Observable<SelectedDataset | null>;
constructor(private datasetService: DatasetService, private http: HttpClient) { constructor(private datasetService: DatasetService, private http: HttpClient) {
this.selectedDataset$ = this.selectedDatasetId.pipe( this.selectedDataset$ = this.selectedDatasetId.pipe(
switchMap(dataset => { switchMap(id => {
if (dataset) { if (!id) {
return this.datasetService.getDatasetByTemplate(dataset.templateId, dataset.fileName).pipe( return of(null);
map(response => {
// The API might return an array directly, or an object with a 'data' property.
const dataArray = Array.isArray(response) ? response : (response && Array.isArray(response.data)) ? response.data : null;
if (dataArray && dataArray.length > 0) {
return {
data: dataArray,
columns: Object.keys(dataArray[0])
};
}
return null; // Return null if data is not in the expected format
})
);
} }
return of(null); // No dataset ID selected // First, get all datasets to find the selected one's details
return this.datasetService.getDatasets().pipe(
switchMap(datasets => {
const selected = datasets.find(d => d.itemId === id);
if (selected) {
// Now fetch the actual data using templateId and fileName
return this.datasetService.getDatasetByTemplate(selected.templateId, selected.fileName).pipe(
map(response => {
const dataArray = Array.isArray(response) ? response : (response && Array.isArray(response.data)) ? response.data : null;
if (dataArray && dataArray.length > 0) {
return {
data: dataArray,
columns: Object.keys(dataArray[0])
};
}
return null;
})
);
}
return of(null); // Dataset with the given ID not found
})
);
}), }),
shareReplay(1) // Cache and replay the last emitted value shareReplay(1)
); );
} }
selectDataset(dataset: DatasetModel): void { selectDataset(datasetId: string | null): void {
this.selectedDatasetId.next(dataset); this.selectedDatasetId.next(datasetId);
} }
} }
...@@ -49,3 +49,4 @@ export class ComboChartWidgetComponent extends BaseWidgetComponent { ...@@ -49,3 +49,4 @@ export class ComboChartWidgetComponent extends BaseWidgetComponent {
]; ];
} }
} }
...@@ -65,3 +65,4 @@ export class FilledMapWidgetComponent extends BaseWidgetComponent { ...@@ -65,3 +65,4 @@ export class FilledMapWidgetComponent extends BaseWidgetComponent {
]; ];
} }
} }
...@@ -64,3 +64,4 @@ export class GaugeChartWidgetComponent extends BaseWidgetComponent { ...@@ -64,3 +64,4 @@ export class GaugeChartWidgetComponent extends BaseWidgetComponent {
}]; }];
} }
} }
...@@ -58,3 +58,4 @@ export class ScatterBubbleChartWidgetComponent extends BaseWidgetComponent { ...@@ -58,3 +58,4 @@ export class ScatterBubbleChartWidgetComponent extends BaseWidgetComponent {
this.primaryYAxis = { title: 'Y-Value' }; this.primaryYAxis = { title: 'Y-Value' };
} }
} }
...@@ -51,3 +51,4 @@ export class TreemapWidgetComponent extends BaseWidgetComponent { ...@@ -51,3 +51,4 @@ export class TreemapWidgetComponent extends BaseWidgetComponent {
}; };
} }
} }
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