From dd90cf6e58ab95d4bd27b7268b3d12add25ebeb8 Mon Sep 17 00:00:00 2001 From: Chneemann Date: Wed, 26 Mar 2025 11:21:39 +0100 Subject: [PATCH] feat: integrated ngx-toastr for error handling and display in AuthenticatedGuard when token is expired --- angular.json | 4 +- package-lock.json | 142 +++++--------------- package.json | 1 + src/app/app.config.ts | 4 + src/app/components/login/login.component.ts | 8 ++ src/app/guards/authenticated.guard.ts | 16 ++- src/styles.scss | 2 + 7 files changed, 64 insertions(+), 113 deletions(-) diff --git a/angular.json b/angular.json index e620b1e..3c168f2 100644 --- a/angular.json +++ b/angular.json @@ -24,7 +24,7 @@ "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss", "assets": ["src/favicon.ico", "src/assets"], - "styles": ["src/styles.scss"], + "styles": ["src/styles.css", "node_modules/ngx-toastr/toastr.css"], "scripts": [] }, "configurations": { @@ -76,7 +76,7 @@ "tsConfig": "tsconfig.spec.json", "inlineStyleLanguage": "scss", "assets": ["src/favicon.ico", "src/assets"], - "styles": ["src/styles.scss"], + "styles": ["src/styles.css", "node_modules/ngx-toastr/toastr.css"], "scripts": [] } } diff --git a/package-lock.json b/package-lock.json index b22f963..334cf5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@sentry/angular": "^8.30.0", "crypto-es": "^2.1.0", "ngx-color-picker": "^16.0.0", + "ngx-toastr": "^19.0.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -6101,30 +6102,6 @@ "@types/node": "*" } }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -6742,7 +6719,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -6756,7 +6733,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -6944,7 +6921,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7039,7 +7016,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -7304,7 +7281,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -8325,6 +8302,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -8335,6 +8313,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -8951,7 +8930,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -9347,7 +9326,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -9957,7 +9936,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -10001,7 +9980,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10020,7 +9999,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -10097,7 +10076,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -11882,6 +11861,20 @@ "@angular/forms": ">=9.0.0" } }, + "node_modules/ngx-toastr": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-19.0.0.tgz", + "integrity": "sha512-6pTnktwwWD+kx342wuMOWB4+bkyX9221pAgGz3SHOJH0/MI9erLucS8PeeJDFwbUYyh75nQ6AzVtolgHxi52dQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=16.0.0-0", + "@angular/core": ">=16.0.0-0", + "@angular/platform-browser": ">=16.0.0-0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -12111,7 +12104,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13136,7 +13129,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -13149,7 +13142,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -14719,7 +14712,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -15598,21 +15591,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/wbuf": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", @@ -15645,54 +15623,6 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, - "node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, "node_modules/webpack-dev-middleware": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", @@ -15966,14 +15896,6 @@ } } }, - "node_modules/webpack/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", diff --git a/package.json b/package.json index dc96dad..584ff12 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@sentry/angular": "^8.30.0", "crypto-es": "^2.1.0", "ngx-color-picker": "^16.0.0", + "ngx-toastr": "^19.0.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 12e744d..d32885d 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -15,6 +15,8 @@ import { IMAGE_CONFIG } from '@angular/common'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { firebaseConfig } from './environments/config'; import * as Sentry from '@sentry/angular'; +import { ToastrModule } from 'ngx-toastr'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; export function createTranslateLoader(http: HttpClient) { return new TranslateHttpLoader(http, './assets/i18n/', '.json'); @@ -32,6 +34,8 @@ export const appConfig: ApplicationConfig = { }, }) ), + importProvidersFrom(BrowserAnimationsModule), + importProvidersFrom(ToastrModule.forRoot()), provideHttpClient(withInterceptorsFromDi()), provideRouter(routes, withViewTransitions()), provideFirebaseApp(() => initializeApp(firebaseConfig)), diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts index 551567f..212f6db 100644 --- a/src/app/components/login/login.component.ts +++ b/src/app/components/login/login.component.ts @@ -13,6 +13,7 @@ import { LoadingDialogComponent } from './loading-dialog/loading-dialog.componen 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({ selector: 'app-login', @@ -42,6 +43,7 @@ export class LoginComponent { constructor( private authService: AuthService, public loginService: LoginService, + private tokenService: TokenService, public sharedService: SharedService, private overlayService: OverlayService, private errorHandlingService: ErrorHandlingService, @@ -51,6 +53,7 @@ export class LoginComponent { ngOnInit() { this.routeParams(); + this.deleteTokens(); } routeParams() { @@ -64,6 +67,11 @@ export class LoginComponent { }); } + deleteTokens() { + this.tokenService.deleteAuthToken(); + this.tokenService.deleteUserId(); + } + async onSubmit(ngForm: NgForm) { this.sharedService.isBtnDisabled = true; if (ngForm.submitted && ngForm.form.valid) { diff --git a/src/app/guards/authenticated.guard.ts b/src/app/guards/authenticated.guard.ts index 380d09e..22809ed 100644 --- a/src/app/guards/authenticated.guard.ts +++ b/src/app/guards/authenticated.guard.ts @@ -3,6 +3,7 @@ import { Router } from '@angular/router'; import { catchError, map, Observable, of } from 'rxjs'; import { AuthService } from '../services/auth.service'; import { TokenService } from '../services/token.service'; +import { ToastrService } from 'ngx-toastr'; @Injectable({ providedIn: 'root', @@ -11,7 +12,8 @@ export class AuthenticatedGuard { constructor( private authService: AuthService, private tokenService: TokenService, - private router: Router + private router: Router, + private toastrService: ToastrService ) {} canActivate(): Observable | Promise | boolean { @@ -26,14 +28,26 @@ export class AuthenticatedGuard { if (isAuthenticated) { return true; } else { + this.showSessionExpiredMessage(); this.router.navigate(['/login']); return false; } }), catchError(() => { + this.showSessionExpiredMessage(); this.router.navigate(['/login']); return of(false); }) ); } + + private showSessionExpiredMessage() { + this.toastrService.error( + 'Your session has expired, please log in again.', + 'Session Expired', + { + timeOut: 3000, + } + ); + } } diff --git a/src/styles.scss b/src/styles.scss index 730f912..b561553 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,3 +1,5 @@ +@import "ngx-toastr/toastr"; + * { margin: 0; scroll-behavior: smooth;