Commit fafd66a2 by Ooh-Ao

เพิ่ม dialog

parent affd9160
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
"@syncfusion/ej2-angular-layouts": "^29.2.4", "@syncfusion/ej2-angular-layouts": "^29.2.4",
"@syncfusion/ej2-angular-maps": "^29.1.33", "@syncfusion/ej2-angular-maps": "^29.1.33",
"@syncfusion/ej2-angular-pivotview": "^29.2.4", "@syncfusion/ej2-angular-pivotview": "^29.2.4",
"@syncfusion/ej2-angular-popups": "^29.2.8",
"@syncfusion/ej2-angular-treemap": "^29.2.4", "@syncfusion/ej2-angular-treemap": "^29.2.4",
"@syncfusion/ej2-base": "^29.2.4", "@syncfusion/ej2-base": "^29.2.4",
"@syncfusion/ej2-buttons": "^29.2.4", "@syncfusion/ej2-buttons": "^29.2.4",
...@@ -6276,6 +6277,17 @@ ...@@ -6276,6 +6277,17 @@
"@syncfusion/ej2-pivotview": "29.2.10" "@syncfusion/ej2-pivotview": "29.2.10"
} }
}, },
"node_modules/@syncfusion/ej2-angular-popups": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-popups/-/ej2-angular-popups-29.2.8.tgz",
"integrity": "sha512-DThwDsjcRF4ngaEFbr+B2mLGtOrFN/ezisgFGLiBGsKsm7HK3RPKTBMQY9xwJ1xCX48dIMVEQnhen+76hbLWOQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-angular-base": "~29.2.4",
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-popups": "29.2.8"
}
},
"node_modules/@syncfusion/ej2-angular-treemap": { "node_modules/@syncfusion/ej2-angular-treemap": {
"version": "29.2.4", "version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-treemap/-/ej2-angular-treemap-29.2.4.tgz", "resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-treemap/-/ej2-angular-treemap-29.2.4.tgz",
......
<router-outlet></router-outlet> <router-outlet></router-outlet>
<ejs-dialog #notificationDialog
[header]="notification?.title"
[content]="notification?.content"
[visible]="isNotificationVisible"
(close)="onDialogClose()"
[isModal]="true"
width="400px"
[showCloseIcon]="true"
[target]="'body'"
[animationSettings]="{ effect: 'Zoom' }">
<ng-template #header>
<div [ngClass]="getHeaderClass()">
<span class="e-dlg-title">{{ notification?.title }}</span>
</div>
</ng-template>
<ng-template #content>
<div class="dialog-content">
<p>{{ notification?.content }}</p>
</div>
</ng-template>
<ng-template #footerTemplate>
<button ejs-button (click)="onDialogClose()" cssClass="e-primary">OK</button>
</ng-template>
</ejs-dialog>
import { Component } from '@angular/core'; import { Component, OnDestroy, ViewChild, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router'; import { RouterOutlet } from '@angular/router';
import { import {
...@@ -9,21 +9,57 @@ import { ...@@ -9,21 +9,57 @@ import {
transition, transition,
// ... // ...
} from '@angular/animations'; } from '@angular/animations';
import { fromEvent } from 'rxjs'; import { fromEvent, Subscription } from 'rxjs';
import { DialogComponent, DialogModule } from '@syncfusion/ej2-angular-popups';
import { Notification, NotificationService } from './shared/services/notification.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
standalone: true, standalone: true,
imports: [CommonModule, RouterOutlet], imports: [CommonModule, RouterOutlet, DialogModule],
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.scss', styleUrl: './app.component.scss',
animations:[] animations:[]
}) })
export class AppComponent { export class AppComponent implements OnDestroy, OnInit {
title = 'ynex'; title = 'ynex';
public isSpinner = true; public isSpinner = true;
@ViewChild('notificationDialog') notificationDialog!: DialogComponent;
private notificationSubscription: Subscription;
public notification: Notification | null = null;
public isNotificationVisible = false;
constructor(private notificationService: NotificationService) {
this.notificationSubscription = this.notificationService.notification$.subscribe(
(notification) => {
this.notification = notification;
this.isNotificationVisible = true;
if (this.notificationDialog) {
this.notificationDialog.show();
}
}
);
}
ngOnInit() { ngOnInit() {
// this.isSpinner = false // this.isSpinner = false
// fromEvent(window, 'load').subscribe(() => {document.querySelector('#loader')?.classList.remove('');}); // fromEvent(window, 'load').subscribe(() => {document.querySelector('#loader')?.classList.remove('');});
} }
getHeaderClass(): string {
if (!this.notification) {
return '';
}
return `e-dlg-header-content e-dlg-${this.notification.type}`;
}
onDialogClose() {
this.isNotificationVisible = false;
}
ngOnDestroy() {
this.notificationSubscription.unsubscribe();
}
} }
...@@ -14,6 +14,7 @@ import { AngularFireAuthModule } from '@angular/fire/compat/auth'; ...@@ -14,6 +14,7 @@ import { AngularFireAuthModule } from '@angular/fire/compat/auth';
import { AngularFireDatabaseModule } from '@angular/fire/compat/database'; import { AngularFireDatabaseModule } from '@angular/fire/compat/database';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore'; import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { ToastrModule } from 'ngx-toastr'; import { ToastrModule } from 'ngx-toastr';
import { DialogModule } from '@syncfusion/ej2-angular-popups';
import { NgDragDropModule } from 'ng-drag-drop'; import { NgDragDropModule } from 'ng-drag-drop';
import { HttpClientModule, HTTP_INTERCEPTORS, provideHttpClient, HttpClient, withInterceptors } from "@angular/common/http"; import { HttpClientModule, HTTP_INTERCEPTORS, provideHttpClient, HttpClient, withInterceptors } from "@angular/common/http";
import { HttpRequestInterceptor } from './shared/services/http-request.interceptor'; import { HttpRequestInterceptor } from './shared/services/http-request.interceptor';
...@@ -71,7 +72,8 @@ export const appConfig: ApplicationConfig = { ...@@ -71,7 +72,8 @@ export const appConfig: ApplicationConfig = {
progressBar: true, progressBar: true,
}), }),
NgDragDropModule.forRoot(), NgDragDropModule.forRoot(),
HttpClientModule HttpClientModule,
DialogModule
), ),
httpInterceptorProviders, httpInterceptorProviders,
DashboardDataService, DashboardDataService,
......
...@@ -5,10 +5,11 @@ import { ActivatedRoute, RouterModule } from '@angular/router'; ...@@ -5,10 +5,11 @@ 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 { Observable, of, forkJoin } from 'rxjs'; import { Observable, of, forkJoin, throwError } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators'; import { map, switchMap, tap, catchError } from 'rxjs/operators';
import { DashboardLayoutModule, PanelModel } from '@syncfusion/ej2-angular-layouts'; // Import Syncfusion modules import { DashboardLayoutModule, PanelModel } from '@syncfusion/ej2-angular-layouts'; // Import Syncfusion modules
import { MatDialog, MatDialogModule } from '@angular/material/dialog'; // Import MatDialog import { MatDialog, MatDialogModule } from '@angular/material/dialog'; // Import MatDialog
import { NotificationService } from '../../shared/services/notification.service';
import { DashboardModel, WidgetModel, DatasetModel } from '../models/widgets.model'; import { DashboardModel, WidgetModel, DatasetModel } from '../models/widgets.model';
import { DashboardDataService } from '../services/dashboard-data.service'; import { DashboardDataService } from '../services/dashboard-data.service';
...@@ -546,11 +547,17 @@ export class DashboardManagementComponent implements OnInit { ...@@ -546,11 +547,17 @@ export class DashboardManagementComponent implements OnInit {
private widgetService: WidgetService, // Inject WidgetService private widgetService: WidgetService, // Inject WidgetService
private dashboardStateService: DashboardStateService, private dashboardStateService: DashboardStateService,
private datasetService: DatasetService, // Inject DatasetService private datasetService: DatasetService, // Inject DatasetService
private dialog: MatDialog private dialog: MatDialog,
private notificationService: NotificationService
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
this.dashboardDataService.getDashboards().subscribe(dashboards => { this.dashboardDataService.getDashboards().pipe(
catchError(error => {
this.notificationService.error('Error', 'Failed to load user dashboards.');
return throwError(() => error);
})
).subscribe(dashboards => {
this.userDashboards = dashboards; this.userDashboards = dashboards;
// if (this.userDashboards.length > 0) { // if (this.userDashboards.length > 0) {
// this.selectedDashboardId = this.userDashboards[0]; // this.selectedDashboardId = this.userDashboards[0];
...@@ -559,7 +566,12 @@ export class DashboardManagementComponent implements OnInit { ...@@ -559,7 +566,12 @@ export class DashboardManagementComponent implements OnInit {
}); });
// Populate availableWidgets from WidgetService // Populate availableWidgets from WidgetService
this.widgetService.getListWidgets().subscribe(widgets => { this.widgetService.getListWidgets().pipe(
catchError(error => {
this.notificationService.error('Error', 'Failed to load available widgets.');
return throwError(() => error);
})
).subscribe(widgets => {
this.availableWidgets = [...widgets].map(widget => ({ this.availableWidgets = [...widgets].map(widget => ({
...widget, ...widget,
config: widget.config || {} // Ensure config property exists config: widget.config || {} // Ensure config property exists
...@@ -587,13 +599,20 @@ export class DashboardManagementComponent implements OnInit { ...@@ -587,13 +599,20 @@ export class DashboardManagementComponent implements OnInit {
loadSelectedDashboard(): void { loadSelectedDashboard(): void {
console.log(this.selectedDashboardId) console.log(this.selectedDashboardId)
if (this.selectedDashboardId) { if (this.selectedDashboardId) {
this.dashboardDataService.getDashboardById(this.selectedDashboardId.dashboardId).subscribe(dashboard => { this.notificationService.info('Info', 'Loading dashboard data...');
this.dashboardDataService.getDashboardById(this.selectedDashboardId.dashboardId).pipe(
catchError(error => {
this.notificationService.error('Error', 'Failed to load dashboard data.');
return throwError(() => error);
})
).subscribe(dashboard => {
if (dashboard) { if (dashboard) {
this.dashboardData = dashboard; this.dashboardData = dashboard;
this.panels = this.mapWidgetsToPanels(dashboard.widgets || []); this.panels = this.mapWidgetsToPanels(dashboard.widgets || []);
if (dashboard.datasetId) { if (dashboard.datasetId) {
this.dashboardStateService.selectDataset(dashboard.datasetId); this.dashboardStateService.selectDataset(dashboard.datasetId);
} }
this.notificationService.success('Success', 'Dashboard loaded successfully!');
} }
}); });
} }
...@@ -608,17 +627,30 @@ export class DashboardManagementComponent implements OnInit { ...@@ -608,17 +627,30 @@ export class DashboardManagementComponent implements OnInit {
application: sessionStorage.getItem('module') || '', application: sessionStorage.getItem('module') || '',
widgets: [] widgets: []
}); });
this.dashboardDataService.saveDashboard(newDashboard).subscribe(addedDashboard => { this.dashboardDataService.saveDashboard(newDashboard).pipe(
catchError(error => {
this.notificationService.error('Error', 'Failed to create new dashboard.');
return throwError(() => error);
})
).subscribe(addedDashboard => {
this.userDashboards.push(addedDashboard); this.userDashboards.push(addedDashboard);
this.selectedDashboardId = addedDashboard; this.selectedDashboardId = addedDashboard;
this.loadSelectedDashboard(); this.loadSelectedDashboard();
this.notificationService.success('Success', 'New dashboard created successfully!');
}); });
} }
} }
saveDashboardName(): void { saveDashboardName(): void {
if (this.dashboardData) { if (this.dashboardData) {
this.dashboardDataService.saveDashboard(this.dashboardData).subscribe(); this.dashboardDataService.saveDashboard(this.dashboardData).pipe(
catchError(error => {
this.notificationService.error('Error', 'Failed to save dashboard name.');
return throwError(() => error);
})
).subscribe(() => {
this.notificationService.success('Success', 'Dashboard name saved successfully!');
});
} }
} }
...@@ -668,11 +700,25 @@ export class DashboardManagementComponent implements OnInit { ...@@ -668,11 +700,25 @@ export class DashboardManagementComponent implements OnInit {
if (dataFetchTasks.length > 0) { if (dataFetchTasks.length > 0) {
forkJoin(dataFetchTasks).subscribe(() => { forkJoin(dataFetchTasks).subscribe(() => {
console.log('All data fetched, now saving dashboard...'); console.log('All data fetched, now saving dashboard...');
this.dashboardDataService.saveDashboard(this.dashboardData!).subscribe(); this.dashboardDataService.saveDashboard(this.dashboardData!).pipe(
catchError(error => {
this.notificationService.error('Error', 'Failed to save dashboard layout.');
return throwError(() => error);
})
).subscribe(() => {
this.notificationService.success('Success', 'Dashboard layout saved successfully!');
});
}); });
} else { } else {
console.log('No data to fetch, saving dashboard directly...'); console.log('No data to fetch, saving dashboard directly...');
this.dashboardDataService.saveDashboard(this.dashboardData).subscribe(); this.dashboardDataService.saveDashboard(this.dashboardData).pipe(
catchError(error => {
this.notificationService.error('Error', 'Failed to save dashboard layout.');
return throwError(() => error);
})
).subscribe(() => {
this.notificationService.success('Success', 'Dashboard layout saved successfully!');
});
} }
} }
...@@ -685,6 +731,7 @@ export class DashboardManagementComponent implements OnInit { ...@@ -685,6 +731,7 @@ export class DashboardManagementComponent implements OnInit {
} }
this.dashboardData.widgets.push(widget); this.dashboardData.widgets.push(widget);
this.panels = this.mapWidgetsToPanels(this.dashboardData.widgets); this.panels = this.mapWidgetsToPanels(this.dashboardData.widgets);
this.notificationService.info('Info', 'Widget added to dashboard.');
} }
// Test method to add the new data-driven widget // Test method to add the new data-driven widget
...@@ -722,9 +769,14 @@ export class DashboardManagementComponent implements OnInit { ...@@ -722,9 +769,14 @@ 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.dashboardDataService.deleteDashboard(this.dashboardData!).subscribe(result => { this.dashboardDataService.deleteDashboard(this.dashboardData!).pipe(
catchError(error => {
this.notificationService.error('Error', 'Failed to delete dashboard.');
return throwError(() => error);
})
).subscribe(result => {
console.log(result) console.log(result)
this.notificationService.success('Success', 'Dashboard deleted successfully!');
}); });
} }
} }
...@@ -735,6 +787,7 @@ export class DashboardManagementComponent implements OnInit { ...@@ -735,6 +787,7 @@ export class DashboardManagementComponent implements OnInit {
const updatedDashboard = { ...this.dashboardData, widgets: this.dashboardData.widgets.filter(w => w.widgetId !== panelId) }; const updatedDashboard = { ...this.dashboardData, widgets: this.dashboardData.widgets.filter(w => w.widgetId !== panelId) };
this.dashboardData = updatedDashboard; this.dashboardData = updatedDashboard;
this.panels = this.mapWidgetsToPanels(updatedDashboard.widgets); this.panels = this.mapWidgetsToPanels(updatedDashboard.widgets);
this.notificationService.info('Info', 'Widget removed from dashboard.');
// 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.dashboardDataService.saveDashboard(updatedDashboard).subscribe(updated => { // this.dashboardDataService.saveDashboard(updatedDashboard).subscribe(updated => {
......
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
export interface Notification {
title: string;
content: string;
type: 'success' | 'error' | 'warning' | 'info';
}
@Injectable({
providedIn: 'root'
})
export class NotificationService {
private notificationSubject = new Subject<Notification>();
notification$ = this.notificationSubject.asObservable();
show(notification: Notification) {
this.notificationSubject.next(notification);
}
success(title: string, content: string) {
this.show({ title, content, type: 'success' });
}
error(title: string, content: string) {
this.show({ title, content, type: 'error' });
}
warning(title: string, content: string) {
this.show({ title, content, type: 'warning' });
}
info(title: string, content: string) {
this.show({ title, content, type: 'info' });
}
}
\ No newline at end of file
...@@ -7,7 +7,6 @@ Created Date : 12/02/2024 ...@@ -7,7 +7,6 @@ Created Date : 12/02/2024
Author & Copyright Ownership : Spruko Technologies Private Limited Author & Copyright Ownership : Spruko Technologies Private Limited
Author URL : https://themeforest.net/user/spruko Author URL : https://themeforest.net/user/spruko
Support : https://support.spruko.com/ Support : https://support.spruko.com/
License Details : https://spruko.com/licenses-details License Details : https://spruko.com/licenses-details
...@@ -173,3 +172,33 @@ Background Image ...@@ -173,3 +172,33 @@ Background Image
@import "./tailwind/progress"; @import "./tailwind/progress";
/* General Dialog Header Styling */
.e-dialog .e-dlg-header-content {
padding: 10px;
color: white;
}
/* Success Notification */
.e-dialog .e-dlg-success {
background-color: #28a745; /* Green */
}
/* Error Notification */
.e-dialog .e-dlg-error {
background-color: #dc3545; /* Red */
}
/* Warning Notification */
.e-dialog .e-dlg-warning {
background-color: #ffc107; /* Yellow */
color: #212529; /* Dark text for better contrast */
}
/* Info Notification */
.e-dialog .e-dlg-info {
background-color: #17a2b8; /* Blue */
}
.dialog-content {
padding: 20px;
}
\ 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