diff --git a/src/app/app.config.ts b/src/app/app.config.ts index d32885d..b4c8ce0 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -17,6 +17,8 @@ import { firebaseConfig } from './environments/config'; import * as Sentry from '@sentry/angular'; import { ToastrModule } from 'ngx-toastr'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { HttpErrorInterceptor } from './interceptors/http-interceptor'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; export function createTranslateLoader(http: HttpClient) { return new TranslateHttpLoader(http, './assets/i18n/', '.json'); @@ -61,5 +63,10 @@ export const appConfig: ApplicationConfig = { deps: [Sentry.TraceService], multi: true, }, + { + provide: HTTP_INTERCEPTORS, + useClass: HttpErrorInterceptor, + multi: true, + }, ], }; diff --git a/src/app/components/add-task/add-task.component.ts b/src/app/components/add-task/add-task.component.ts index 72e5407..783e39e 100644 --- a/src/app/components/add-task/add-task.component.ts +++ b/src/app/components/add-task/add-task.component.ts @@ -15,6 +15,7 @@ import { firstValueFrom, map } from 'rxjs'; import { TaskService } from '../../services/task.service'; import { ApiService } from '../../services/api.service'; import { TaskUpdateService } from '../../services/task-update.service'; +import { ToastNotificationService } from '../../services/toast-notification.servic'; @Component({ selector: 'app-add-task', @@ -53,6 +54,7 @@ export class AddTaskComponent implements OnInit { private apiService: ApiService, private authService: AuthService, private taskUpdateService: TaskUpdateService, + private toastNotificationService: ToastNotificationService, private route: ActivatedRoute, private router: Router ) {} @@ -379,6 +381,7 @@ export class AddTaskComponent implements OnInit { this.apiService.saveNewTask(taskWithoutId).subscribe({ next: (response) => { + this.toastNotificationService.createTaskSuccessToast(); this.taskUpdateService.notifyTaskUpdate(); this.removeTaskData(ngForm); this.closeOverlay(); diff --git a/src/app/components/board/board.component.ts b/src/app/components/board/board.component.ts index 4f95594..557739f 100644 --- a/src/app/components/board/board.component.ts +++ b/src/app/components/board/board.component.ts @@ -15,6 +15,7 @@ import { TaskService } from '../../services/task.service'; import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component'; import { finalize, Subject, takeUntil } from 'rxjs'; import { TaskUpdateService } from '../../services/task-update.service'; +import { ToastNotificationService } from '../../services/toast-notification.servic'; @Component({ selector: 'app-board', @@ -46,6 +47,7 @@ export class BoardComponent { private taskService: TaskService, private apiService: ApiService, private taskUpdateService: TaskUpdateService, + private toastNotificationService: ToastNotificationService, private router: Router ) {} @@ -140,6 +142,7 @@ export class BoardComponent { handleItemDropped(taskId: string, status: string): void { this.apiService.updateTaskStatus(taskId, status).subscribe({ next: () => { + this.toastNotificationService.taskStatusUpdatedToast(); this.updateTaskStatus(taskId, status); }, error: (error) => console.error('Error updating task:', error), diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts index 2c0ecdd..5f60dc7 100644 --- a/src/app/components/login/login.component.ts +++ b/src/app/components/login/login.component.ts @@ -12,7 +12,6 @@ import { TranslateModule } from '@ngx-translate/core'; import { LoadingDialogComponent } from './loading-dialog/loading-dialog.component'; import { OverlayService } from '../../services/overlay.service'; import { AuthService } from '../../services/auth.service'; -import { ErrorHandlingService } from '../../services/error-handling.service'; import { TokenService } from '../../services/token.service'; @Component({ diff --git a/src/app/interceptors/http-interceptor.ts b/src/app/interceptors/http-interceptor.ts new file mode 100644 index 0000000..136ce06 --- /dev/null +++ b/src/app/interceptors/http-interceptor.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { + HttpEvent, + HttpInterceptor, + HttpHandler, + HttpRequest, + HttpErrorResponse, +} from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { ErrorNotificationService } from '../services/error-notification.service'; + +@Injectable() +export class HttpErrorInterceptor implements HttpInterceptor { + constructor(private errorNotificationService: ErrorNotificationService) {} + + intercept( + req: HttpRequest, + next: HttpHandler + ): Observable> { + return next.handle(req).pipe( + catchError((error: HttpErrorResponse) => { + this.errorNotificationService.handleHttpError(error); + return throwError(error); + }) + ); + } +} diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index a34fd1b..c898060 100755 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -9,8 +9,8 @@ import { } from 'rxjs'; import { ApiService } from './api.service'; import { TokenService } from './token.service'; -import { ErrorHandlingService } from './error-handling.service'; import { Router } from '@angular/router'; +import { ToastNotificationService } from './toast-notification.servic'; @Injectable({ providedIn: 'root', @@ -21,7 +21,7 @@ export class AuthService { constructor( private apiService: ApiService, private tokenService: TokenService, - private errorHandlingService: ErrorHandlingService, + private toastNotificationService: ToastNotificationService, private router: Router ) { this.currentUserIdSubject.next(this.tokenService.getUserId()); @@ -40,10 +40,9 @@ export class AuthService { this.tokenService.storeAuthToken(token, storage); this.tokenService.storeUserId(userId, storage); this.currentUserIdSubject.next(userId); - this.errorHandlingService.loginSuccessToast(); + this.toastNotificationService.loginSuccessToast(); } catch (error) { console.error('Login failed:', error); - this.errorHandlingService.handleHttpError(error); } } @@ -54,11 +53,10 @@ export class AuthService { this.tokenService.deleteAuthToken(); this.tokenService.deleteUserId(); this.currentUserIdSubject.next(null); - this.errorHandlingService.logoutSuccessToast(); + this.toastNotificationService.logoutSuccessToast(); this.router.navigate(['/logout']); } catch (error) { console.error('Logout failed:', error); - this.errorHandlingService.handleHttpError(error); } } diff --git a/src/app/services/error-handling.service.ts b/src/app/services/error-notification.service.ts similarity index 64% rename from src/app/services/error-handling.service.ts rename to src/app/services/error-notification.service.ts index 5c83bb6..90ea665 100755 --- a/src/app/services/error-handling.service.ts +++ b/src/app/services/error-notification.service.ts @@ -5,7 +5,7 @@ import { ToastrService } from 'ngx-toastr'; @Injectable({ providedIn: 'root', }) -export class ErrorHandlingService { +export class ErrorNotificationService { constructor(private toastr: ToastrService) {} handleHttpError(error: unknown): void { @@ -28,36 +28,23 @@ export class ErrorHandlingService { errorMessage = 'The requested resource was not found.'; break; case 500: - errorMessage = 'Server error: Please try again later.'; + errorMessage = + 'Server error: Please try again later. If the issue persists, please contact support.'; + break; + case 408: + errorMessage = + 'Request timed out. Please check your connection and try again.'; break; default: - errorMessage = `Unknown error: ${error.statusText || error.message}`; + errorMessage = + error.error?.message || + `Unknown error: ${error.statusText || error.message}`; break; } } else if (error instanceof Error) { - errorMessage = `${error.message || error.toString()}`; + errorMessage = `Network error: ${error.message || error.toString()}`; } this.toastr.error(errorMessage, 'Error'); } - - loginSuccessToast(): void { - this.toastr.success( - 'You have successfully logged in.', - 'Login Successful', - { - timeOut: 3000, - } - ); - } - - logoutSuccessToast(): void { - this.toastr.info( - 'You have successfully logged out. Have a nice day!', - 'Logout Successful', - { - timeOut: 3000, - } - ); - } } diff --git a/src/app/services/toast-notification.servic.ts b/src/app/services/toast-notification.servic.ts new file mode 100755 index 0000000..f79927f --- /dev/null +++ b/src/app/services/toast-notification.servic.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@angular/core'; +import { ToastrService } from 'ngx-toastr'; + +@Injectable({ + providedIn: 'root', +}) +export class ToastNotificationService { + constructor(private toastr: ToastrService) {} + + // Auth + loginSuccessToast(): void { + this.createSuccessToast( + 'You have successfully logged in.', + 'Login Successful' + ); + } + + logoutSuccessToast(): void { + this.createInfoToast( + 'You have successfully logged out. Have a nice day!', + 'Logout Successful' + ); + } + + // Tasks + createTaskSuccessToast(): void { + this.createSuccessToast('Task created successfully!', 'Task Created'); + } + + deleteTaskSuccessToast(): void { + this.createSuccessToast('Task deleted successfully!', 'Task Deleted'); + } + + taskStatusUpdatedToast(): void { + this.createInfoToast('Task successfully moved!', 'Task Moved'); + } + + // Helperfunctions + + createSuccessToast(message: string, title: string = 'Success'): void { + this.toastr.success(message, title, { + timeOut: 3000, + positionClass: 'toast-top-right', + progressBar: true, + }); + } + + createErrorToast(message: string, title: string = 'Error'): void { + this.toastr.error(message, title, { + timeOut: 3000, + positionClass: 'toast-top-right', + progressBar: true, + }); + } + + createInfoToast(message: string, title: string = 'Info'): void { + this.toastr.info(message, title, { + timeOut: 3000, + positionClass: 'toast-top-right', + progressBar: true, + }); + } +} diff --git a/src/app/shared/components/overlay/task-overlay/task-overlay.component.ts b/src/app/shared/components/overlay/task-overlay/task-overlay.component.ts index 06393ed..8905943 100644 --- a/src/app/shared/components/overlay/task-overlay/task-overlay.component.ts +++ b/src/app/shared/components/overlay/task-overlay/task-overlay.component.ts @@ -12,6 +12,7 @@ import { AuthService } from '../../../../services/auth.service'; import { map } from 'rxjs'; import { ApiService } from '../../../../services/api.service'; import { TaskUpdateService } from '../../../../services/task-update.service'; +import { ToastNotificationService } from '../../../../services/toast-notification.servic'; @Component({ selector: 'app-task-overlay', @@ -35,6 +36,7 @@ export class TaskOverlayComponent implements OnInit { private authService: AuthService, private apiService: ApiService, private taskUpdateService: TaskUpdateService, + private toastNotificationService: ToastNotificationService, private router: Router, private route: ActivatedRoute ) {} @@ -123,6 +125,7 @@ export class TaskOverlayComponent implements OnInit { deleteTask(taskId: string) { this.apiService.deleteTaskById(taskId).subscribe({ next: (task) => { + this.toastNotificationService.deleteTaskSuccessToast(); this.taskUpdateService.notifyTaskUpdate(); }, error: (err) => {