Commit 25fccddd by Ooh-Ao

ss

parent 5c675edd
......@@ -3,8 +3,8 @@
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve",
"build": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng build --configuration=production --aot --output-hashing=all",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"postcss": "sass ./src/assets/scss/style.scss ./src/assets/css/style.css && postcss ./src/assets/css/style.css -o ./src/assets/css/style.css",
......
<div class="flex h-screen bg-gray-50">
<div class="dashboard-container">
<!-- Widget Sidebar -->
<div class="w-72 bg-white p-4 overflow-y-auto shadow-lg border-r flex flex-col">
<div class="widget-sidebar">
<h2 class="text-xl font-bold mb-4 text-gray-800">Widgets</h2>
<!-- Widget Search -->
......@@ -21,9 +21,9 @@
</div>
<!-- Widget List -->
<div class="flex-grow">
<div class="widget-list space-y-2">
<div *ngFor="let widget of filteredAvailableWidgets"
class="p-3 mb-2 bg-gray-50 rounded-lg border border-gray-200 cursor-pointer hover:bg-indigo-50 hover:border-indigo-100 hover:shadow-md transition-all duration-200"
class="widget-item"
(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>
......@@ -33,8 +33,8 @@
</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">
<div class="dashboard-area">
<div class="dashboard-header flex justify-between items-center shadow-sm">
<div class="flex items-center space-x-4">
<h1 class="text-xl font-bold text-gray-700">Dashboard:</h1>
<!-- Dashboard Selector -->
......@@ -52,29 +52,46 @@
</button>
</div>
<button (click)="createNewDashboard()"
class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded-lg shadow-md transition-transform transform hover:scale-105 flex items-center gap-2">
class="ti-btn ti-btn-primary-full flex items-center gap-2">
<i class="bi bi-plus-circle-fill"></i> New Dashboard
</button>
<button (click)="addDataDrivenWidget()"
class="ti-btn ti-btn-info-full flex items-center gap-2">
<i class="bi bi-plus-circle-fill"></i> Add Data-Driven Widget (Test)
</button>
<button *ngIf="selectedDashboardId" (click)="deleteDashboard()"
class="bg-rose-600 hover:bg-rose-700 text-white font-bold py-2 px-4 rounded-lg shadow-md transition-transform transform hover:scale-105 flex items-center gap-2">
class="ti-btn ti-btn-danger-full flex items-center gap-2">
<i class="bi bi-trash-fill"></i> Delete Dashboard
</button>
</div>
<button (click)="saveLayout()"
class="bg-emerald-600 hover:bg-emerald-700 text-white font-bold py-2 px-4 rounded-lg shadow-md transition-transform transform hover:scale-105 flex items-center gap-2">
class="ti-btn ti-btn-success-full flex items-center gap-2">
<i class="bi bi-check-circle-fill"></i> Save Layout
</button>
<button *ngIf="selectedDashboardId" [routerLink]="['/dpu/dashboard-viewer', selectedDashboardId]"
class="ti-btn ti-btn-secondary-full flex items-center gap-2 ml-2">
<i class="bi bi-eye-fill"></i> View Dashboard
</button>
</div>
<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 class="dashboard-content">
<ejs-dashboardlayout id='dashboard_management' #managementLayout [cellSpacing]="cellSpacing" [panels]="panels" [columns]="6" [allowResizing]="true" [allowDragging]="true">
<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="p-2 bg-white border-b border-gray-200 text-gray-700 font-semibold flex justify-between items-center">
<span>{{panel.header}}</span>
<button (click)="removeWidgetFromDashboard(panel.id!)" class="text-red-500 hover:text-red-700">
<i class="bi bi-trash"></i>
</button>
</div>
</ng-template>
<ng-template [e-content]>
<ng-container [ngComponentOutlet]="panel.componentType" [ngComponentOutletInputs]="panel.componentInputs"></ng-container>
</ng-template>
</e-panel>
</e-panels>
</ejs-dashboardlayout>
</div>
</div>
</div>
/* Add any specific styles for the dashboard management component here */
.dashboard-container {
display: flex;
height: 100vh;
background-color: #f3f4f6; /* bg-gray-100 */
}
.widget-sidebar {
width: 18rem; /* w-72 */
background-color: #ffffff; /* bg-white */
padding: 1rem; /* p-4 */
overflow-y: auto;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-lg */
border-right: 1px solid #e5e7eb; /* border-r border-gray-200 */
display: flex;
flex-direction: column;
}
.widget-list {
flex-grow: 1;
/* space-y-2 is handled by margin-bottom on widget-item */
}
.widget-item {
padding: 0.75rem; /* p-3 */
margin-bottom: 0.5rem; /* mb-2 */
background-color: #f9fafb; /* bg-gray-50 */
border-radius: 0.5rem; /* rounded-lg */
border: 1px solid #e5e7eb; /* border border-gray-200 */
cursor: pointer;
transition: all 0.2s ease-in-out;
&:hover {
background-color: #e0e7ff; /* hover:bg-indigo-50 */
border-color: #c7d2fe; /* hover:border-indigo-100 */
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* hover:shadow-md */
}
}
.dashboard-area {
flex: 1;
display: flex;
flex-direction: column;
background-color: #f3f4f6; /* bg-gray-100 */
}
.dashboard-header {
padding: 1rem; /* p-4 */
background-color: #ffffff; /* bg-white */
border-bottom: 1px solid #e5e7eb; /* border-b border-gray-200 */
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); /* shadow-sm */
}
.dashboard-content {
flex: 1;
padding: 1rem; /* p-4 */
overflow: auto;
min-height: 600px; /* min-h-[600px] */
}
/* Syncfusion specific styles */
.e-dashboardlayout .e-panel .e-panel-container .e-panel-header {
font-size: 16px;
font-weight: bold;
}
/* Basic styling for the placeholder content */
.e-dashboardlayout .e-panel .e-panel-container .e-panel-content {
padding: 10px;
text-align: center;
font-style: italic;
color: #888;
}
/* Button styles (assuming ti-btn is a base class) */
.ti-btn {
font-weight: bold;
padding: 0.5rem 1rem; /* py-2 px-4 */
border-radius: 0.5rem; /* rounded-lg */
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-md */
transition: transform 0.2s ease-in-out; /* transition-transform */
transform: scale(1); /* transform */
&:hover {
transform: scale(1.05); /* hover:scale-105 */
}
}
.ti-btn-primary-full {
background-color: #4f46e5; /* bg-indigo-600 */
color: #ffffff; /* text-white */
&:hover {
background-color: #4338ca; /* hover:bg-indigo-700 */
}
}
.ti-btn-info-full {
background-color: #14b8a6; /* bg-teal-600 */
color: #ffffff; /* text-white */
&:hover {
background-color: #0d9488; /* hover:bg-teal-700 */
}
}
.ti-btn-danger-full {
background-color: #ef4444; /* bg-rose-600 */
color: #ffffff; /* text-white */
&:hover {
background-color: #dc2626; /* hover:bg-rose-700 */
}
}
.ti-btn-success-full {
background-color: #10b981; /* bg-emerald-600 */
color: #ffffff; /* text-white */
&:hover {
background-color: #059669; /* hover:bg-emerald-700 */
}
}
.ti-btn-secondary-full {
background-color: #6b7280; /* A shade of gray, similar to default secondary */
color: #ffffff;
&:hover {
background-color: #4b5563;
}
}
\ No newline at end of file
......@@ -3,10 +3,11 @@ import { Component, OnInit, Type, ChangeDetectionStrategy } from '@angular/core'
import { CommonModule } from '@angular/common';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { DashboardLayoutModule, PanelModel } from '@syncfusion/ej2-angular-layouts';
import { Observable, of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { Observable, of, forkJoin } from 'rxjs'; // Import forkJoin
import { map, switchMap, tap } from 'rxjs/operators'; // Import tap
import { NgComponentOutlet } from '@angular/common'; // Import NgComponentOutlet
import { DashboardDataService } from '../services/dashboard-data.service';
import { WidgetDataService } from '../services/widget-data.service'; // Import WidgetDataService
import { DashboardModel } from '../models/widgets.model';
// Import all the widget components
......@@ -23,11 +24,30 @@ import { QuickLinksWidgetComponent } from '../widgets/quick-links-widget/quick-l
import { SyncfusionDatagridWidgetComponent } from '../widgets/syncfusion-datagrid-widget/syncfusion-datagrid-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 { DataTableWidgetComponent } from '../widgets/dynamic-widgets/data-table-widget.component'; // Import new widget
@Component({
selector: 'app-dashboard-viewer',
standalone: true,
imports: [CommonModule, RouterModule, DashboardLayoutModule, NgComponentOutlet, CompanyInfoWidgetComponent, HeadcountWidgetComponent, AttendanceOverviewWidgetComponent, PayrollSummaryWidgetComponent, EmployeeDirectoryWidgetComponent, KpiWidgetComponent, WelcomeWidgetComponent, ChartWidgetComponent, QuickLinksWidgetComponent, SyncfusionDatagridWidgetComponent, SyncfusionPivotWidgetComponent, SyncfusionChartWidgetComponent],
imports: [
CommonModule,
RouterModule,
DashboardLayoutModule,
NgComponentOutlet,
CompanyInfoWidgetComponent,
HeadcountWidgetComponent,
AttendanceOverviewWidgetComponent,
PayrollSummaryWidgetComponent,
EmployeeDirectoryWidgetComponent,
KpiWidgetComponent,
WelcomeWidgetComponent,
ChartWidgetComponent,
QuickLinksWidgetComponent,
SyncfusionDatagridWidgetComponent,
SyncfusionPivotWidgetComponent,
SyncfusionChartWidgetComponent,
DataTableWidgetComponent // Add new widget to imports
],
templateUrl: './dashboard-viewer.component.html',
styleUrls: ['./dashboard-viewer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
......@@ -52,14 +72,16 @@ export class DashboardViewerComponent implements OnInit {
// New Syncfusion Widget Mappings
'SyncfusionDatagridWidgetComponent': SyncfusionDatagridWidgetComponent,
'SyncfusionPivotWidgetComponent': SyncfusionPivotWidgetComponent,
'SyncfusionChartWidgetComponent': SyncfusionChartWidgetComponent
'SyncfusionChartWidgetComponent': SyncfusionChartWidgetComponent,
'NewDataTableWidget': DataTableWidgetComponent // Add new widget to map
};
public errorMessage: string | null = null;
constructor(
private route: ActivatedRoute,
private dashboardDataService: DashboardDataService
private dashboardDataService: DashboardDataService,
private widgetDataService: WidgetDataService // Inject WidgetDataService
) { }
ngOnInit(): void {
......@@ -84,15 +106,67 @@ export class DashboardViewerComponent implements OnInit {
loadDashboard(dashboard: DashboardModel): void {
this.dashboardName = dashboard.name;
this.panels = dashboard.widgets.map(widget => ({
id: widget.id,
row: widget.y,
col: widget.x,
sizeX: widget.cols,
sizeY: widget.rows,
header: widget.name,
componentType: this.widgetComponentMap[widget.component], // Attach the component Type
componentInputs: widget.data // Pass inputs if available
}));
const dataFetchTasks: Observable<any>[] = [];
// Prepare data fetching tasks for NewDataTableWidget components
dashboard.widgets.forEach(widget => {
if (widget.component === 'NewDataTableWidget' && widget.config?.source?.url && !widget.config.data) {
dataFetchTasks.push(
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(() => {
console.log('All widget data fetched for viewer.');
this.panels = dashboard.widgets.map(widget => {
let inputs = {};
if (widget.component === 'NewDataTableWidget') {
inputs = { config: widget.config };
} else {
inputs = { ...widget.data, datasetId: dashboard.datasetId };
}
return {
id: widget.id,
row: widget.y,
col: widget.x,
sizeX: widget.cols,
sizeY: widget.rows,
header: widget.name,
componentType: this.widgetComponentMap[widget.component], // Attach the component Type
componentInputs: inputs // Pass inputs
};
});
});
} else {
// No data to fetch, directly map panels
this.panels = dashboard.widgets.map(widget => {
let inputs = {};
if (widget.component === 'NewDataTableWidget') {
inputs = { config: widget.config };
} else {
inputs = { ...widget.data, datasetId: dashboard.datasetId };
}
return {
id: widget.id,
row: widget.y,
col: widget.x,
sizeX: widget.cols,
sizeY: widget.rows,
header: widget.name,
componentType: this.widgetComponentMap[widget.component], // Attach the component Type
componentInputs: inputs // Pass inputs
};
});
}
}
}
......@@ -28,7 +28,8 @@ export interface IWidget {
rows: number;
x: number;
y: number;
data: any;
data: any; // Legacy, to be phased out
config: any; // New: For data-driven configuration
}
export class WidgetModel implements IWidget {
......@@ -39,7 +40,8 @@ export class WidgetModel implements IWidget {
rows: number;
x: number;
y: number;
data: any;
data: any; // Legacy, to be phased out
config: any; // New: For data-driven configuration
constructor(data: Partial<IWidget>) {
this.id = data.id ?? '';
......@@ -49,7 +51,8 @@ export class WidgetModel implements IWidget {
this.rows = data.rows ?? 1;
this.x = data.x ?? 0;
this.y = data.y ?? 0;
this.data = data.data ?? {};
this.data = data.data ?? {}; // Keep for now for compatibility
this.config = data.config ?? {}; // Initialize new config object
}
}
......
......@@ -8,8 +8,8 @@ export const MOCK_DASHBOARD_DATA: DashboardModel[] = [
name: 'Sales Dashboard',
description: 'Dashboard showing sales performance over time.',
widgets: [
{ id: '101', name: 'Monthly Sales', component: 'ChartWidgetComponent', cols: 2, rows: 1, x: 0, y: 0, data: { type: 'bar', labels: ['Jan', 'Feb', 'Mar'], datasets: [{ data: [65, 59, 80], label: 'Series A' }] } },
{ id: '102', name: 'Top Products', component: 'SyncfusionDatagridWidgetComponent', cols: 2, rows: 1, x: 2, y: 0, data: { columns: ['Product', 'Sales'], data: [{ Product: 'A', Sales: 100 }, { Product: 'B', Sales: 150 }] } }
{ id: '101', name: 'Monthly Sales', component: 'ChartWidgetComponent', cols: 2, rows: 1, x: 0, y: 0, data: { type: 'bar', labels: ['Jan', 'Feb', 'Mar'], datasets: [{ data: [65, 59, 80], label: 'Series A' }] }, config: {} },
{ id: '102', name: 'Top Products', component: 'SyncfusionDatagridWidgetComponent', cols: 2, rows: 1, x: 2, y: 0, data: { columns: ['Product', 'Sales'], data: [{ Product: 'A', Sales: 100 }, { Product: 'B', Sales: 150 }] }, config: {} }
]
},
{
......@@ -17,8 +17,8 @@ export const MOCK_DASHBOARD_DATA: DashboardModel[] = [
name: 'Marketing Overview',
description: 'Overview of marketing campaign performance.',
widgets: [
{ 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: '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' }] }, config: {} },
{ id: '202', name: 'Conversion Rate', component: 'KpiWidgetComponent', cols: 1, rows: 1, x: 2, y: 0, data: { value: '2.5%', label: 'Conversion' }, config: {} }
]
},
{
......@@ -26,18 +26,33 @@ export const MOCK_DASHBOARD_DATA: DashboardModel[] = [
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' }] } }
{ id: '301', name: 'Company Info', component: 'CompanyInfoWidgetComponent', cols: 2, rows: 1, x: 0, y: 0, data: { companyName: 'Test Company', address: '123 Test St.' }, config: {} },
{ id: '302', name: 'Headcount', component: 'HeadcountWidgetComponent', cols: 1, rows: 1, x: 2, y: 0, data: { count: 150 }, config: {} },
{ id: '303', name: 'Attendance Overview', component: 'AttendanceOverviewWidgetComponent', cols: 2, rows: 1, x: 3, y: 0, data: { present: 140, absent: 10 }, config: {} },
{ id: '304', name: 'Payroll Summary', component: 'PayrollSummaryWidgetComponent', cols: 2, rows: 1, x: 0, y: 1, data: { totalPayroll: 100000, currency: 'USD' }, config: {} },
{ 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' }] }, config: {} },
{ id: '306', name: 'KPI Widget', component: 'KpiWidgetComponent', cols: 1, rows: 1, x: 4, y: 1, data: { value: '95%', label: 'Success Rate' }, config: {} },
{ id: '307', name: 'Welcome Widget', component: 'WelcomeWidgetComponent', cols: 2, rows: 1, x: 0, y: 3, data: { message: 'Welcome to the Dashboard!' }, config: {} },
{ 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' }] }, config: {} },
{ id: '309', name: 'Quick Links', component: 'QuickLinksWidgetComponent', cols: 1, rows: 1, x: 4, y: 3, data: { links: [{ name: 'Google', url: 'https://google.com' }] }, config: {} },
{ 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' }] }, config: {} },
{ 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' }] }, config: {} },
{ 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' }] }, config: {} },
{ id: '313', name: 'Employee Data (Pre-configured)', component: 'NewDataTableWidget', cols: 4, rows: 3, x: 0, y: 7, data: {}, config: {
title: 'Employee List (Pre-configured)',
source: {
type: 'url',
url: 'assets/data/sample1.json'
},
data: [], // This will be populated by the service
columns: [
{ field: 'EmployeeID', headerText: 'ID', width: 70 },
{ field: 'FirstName', headerText: 'First Name', width: 120 },
{ field: 'LastName', headerText: 'Last Name', width: 120 },
{ field: 'Position', headerText: 'Position', width: 150 },
{ field: 'Department', headerText: 'Department', width: 150 },
]
} }
]
}
];
......
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class WidgetDataService {
constructor(private http: HttpClient) { }
/**
* Fetches the data for a widget based on its configuration.
* The config must have a `source.url` property.
* @param config The widget's configuration object.
* @returns An observable of the data, or an empty array if fetching fails.
*/
fetchDataForWidget(config: any): Observable<any[]> {
if (config && config.source && config.source.url) {
return this.http.get<any[]>(config.source.url).pipe(
catchError(error => {
console.error(`Failed to fetch widget data from ${config.source.url}`, error);
return of([]); // Return an empty array on error
})
);
}
return of([]); // Return empty array if config is invalid
}
}
<div class="bg-white p-4 rounded-lg shadow-md h-full flex flex-col">
<h3 class="text-lg font-semibold text-gray-600 mb-4">{{ title }}</h3>
<div class="flex-grow">
<ejs-grid [dataSource]="data"
[allowPaging]="true"
[pageSettings]="pageSettings"
[allowSorting]="true"
[allowFiltering]="true"
[allowGrouping]="true"
height="100%">
<e-columns>
<e-column *ngFor="let col of columns"
[field]="col.field"
[headerText]="col.headerText"
[width]="col.width">
</e-column>
</e-columns>
</ejs-grid>
</div>
</div>
/* Styles for the data-table-widget component */
import { Component, Input, OnInit, OnChanges, SimpleChanges } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GridModule, PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-data-table-widget',
standalone: true,
imports: [CommonModule, GridModule],
providers: [PageService, SortService, FilterService, GroupService],
templateUrl: './data-table-widget.component.html',
styleUrls: ['./data-table-widget.component.scss']
})
export class DataTableWidgetComponent implements OnInit, OnChanges {
// This component is "Dumb". It receives all its configuration and data via this input.
@Input() config: any;
public data: object[] = [];
public columns: any[] = [];
public title: string = 'Data Table';
public pageSettings: object = { pageSize: 10 };
constructor() { }
ngOnInit(): void {
this.updateWidgetFromConfig();
}
ngOnChanges(changes: SimpleChanges): void {
// If the config object changes, re-render the widget
if (changes['config']) {
this.updateWidgetFromConfig();
}
}
private updateWidgetFromConfig(): void {
if (this.config) {
this.title = this.config.title || 'Data Table';
this.data = this.config.data || [];
// If columns are defined in config, use them. Otherwise, generate from data.
if (this.config.columns && this.config.columns.length > 0) {
this.columns = this.config.columns;
} else if (this.data.length > 0) {
// Auto-generate columns from the first data item's keys
this.columns = Object.keys(this.data[0]).map(key => ({
field: key,
headerText: this.formatHeader(key),
width: 150
}));
}
}
}
private formatHeader(key: string): string {
//- Replaces underscores with spaces and capitalizes words
return key.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase());
}
}
......@@ -48,6 +48,7 @@
@import '../node_modules/@syncfusion/ej2-lists/styles/material.css';
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css";
@import '../node_modules/@syncfusion/ej2-angular-pivotview/styles/material.css';
@import "../node_modules/@syncfusion/ej2-angular-layouts/styles/material.css";
// @import "../node_modules/angular-calendar/scss/angular-calendar.scss";
//swiperjs
......
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