fix: minor bugfixes and UI adjustments

This commit is contained in:
Chneemann 2025-04-05 04:31:38 +02:00
parent aae115a82d
commit 3898a9a2f1
10 changed files with 46 additions and 33 deletions

View file

@ -10,6 +10,7 @@ app-header {
height: 96px; height: 96px;
width: 100%; width: 100%;
background-color: var(--white); background-color: var(--white);
z-index: 1;
} }
app-sidebar { app-sidebar {

View file

@ -4,6 +4,7 @@ import { catchError, map, Observable, of } from 'rxjs';
import { AuthService } from '../services/auth.service'; import { AuthService } from '../services/auth.service';
import { TokenService } from '../services/token.service'; import { TokenService } from '../services/token.service';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { ToastNotificationService } from '../services/toast-notification.servic';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -13,12 +14,14 @@ export class AuthenticatedGuard {
private authService: AuthService, private authService: AuthService,
private tokenService: TokenService, private tokenService: TokenService,
private router: Router, private router: Router,
private toastrService: ToastrService private toastrService: ToastrService,
private toastNotificationService: ToastNotificationService
) {} ) {}
canActivate(): Observable<boolean> | Promise<boolean> | boolean { canActivate(): Observable<boolean> | Promise<boolean> | boolean {
const authToken = this.tokenService.getAuthToken(); const authToken = this.tokenService.getAuthToken();
if (!authToken) {
if (!authToken || !this.tokenService.isTokenExpired(authToken)) {
this.router.navigate(['/login']); this.router.navigate(['/login']);
return false; return false;
} }
@ -28,26 +31,16 @@ export class AuthenticatedGuard {
if (isAuthenticated) { if (isAuthenticated) {
return true; return true;
} else { } else {
this.showSessionExpiredMessage(); this.toastNotificationService.showSessionExpiredMessage();
this.router.navigate(['/login']); this.router.navigate(['/login']);
return false; return false;
} }
}), }),
catchError(() => { catchError(() => {
this.showSessionExpiredMessage(); this.toastNotificationService.showSessionExpiredMessage();
this.router.navigate(['/login']); this.router.navigate(['/login']);
return of(false); return of(false);
}) })
); );
} }
private showSessionExpiredMessage() {
this.toastrService.error(
'Your session has expired, please log in again.',
'Session Expired',
{
timeOut: 3000,
}
);
}
} }

View file

@ -17,7 +17,8 @@ export class RedirectIfAuthenticatedGuard {
canActivate(): Observable<boolean> | Promise<boolean> | boolean { canActivate(): Observable<boolean> | Promise<boolean> | boolean {
const authToken = this.tokenService.getAuthToken(); const authToken = this.tokenService.getAuthToken();
if (!authToken) {
if (!authToken || !this.tokenService.isTokenExpired(authToken)) {
return true; return true;
} }

View file

@ -22,6 +22,13 @@ export class ToastNotificationService {
); );
} }
showSessionExpiredMessage(): void {
this.createInfoToast(
'Your session has expired, please log in again.',
'Session Expired'
);
}
// Tasks // Tasks
createTaskSuccessToast(): void { createTaskSuccessToast(): void {
this.createSuccessToast('Task created successfully!', 'Task Created'); this.createSuccessToast('Task created successfully!', 'Task Created');

View file

@ -42,4 +42,15 @@ export class TokenService {
localStorage.removeItem(this.USER_ID_KEY); localStorage.removeItem(this.USER_ID_KEY);
sessionStorage.removeItem(this.USER_ID_KEY); sessionStorage.removeItem(this.USER_ID_KEY);
} }
isTokenExpired(token: string): boolean {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
const expiry = payload.exp;
if (!expiry) return true;
return expiry * 1000 < Date.now();
} catch (e) {
return true;
}
}
} }

View file

@ -9,9 +9,9 @@ section {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
z-index: 999;
backdrop-filter: blur(5px); backdrop-filter: blur(5px);
background-color: rgba($color: #000000, $alpha: 0.2); background-color: rgba($color: #000000, $alpha: 0.2);
z-index: 999;
} }
.dialog { .dialog {

View file

@ -12,7 +12,7 @@
<div class="img-language"> <div class="img-language">
<img <img
class="img-lang" class="img-lang"
(click)="toggleLanguage()" (click)="toggleLanguageNavbar()"
src="./../../../../assets/img/header/language.svg" src="./../../../../assets/img/header/language.svg"
alt="" alt=""
/> />
@ -22,8 +22,8 @@
</div> </div>
</div> </div>
</div> </div>
<app-navbar *ngIf="navbarVisible" [navbarVisible]="navbarVisible"></app-navbar> <app-navbar *ngIf="showNavbar" [showNavbar]="showNavbar"></app-navbar>
<app-navbar <app-navbar
*ngIf="navbarLanguageVisible" *ngIf="showLanguageNavbar"
[navbarLanguageVisible]="navbarLanguageVisible" [showLanguageNavbar]="showLanguageNavbar"
></app-navbar> ></app-navbar>

View file

@ -14,8 +14,8 @@ import { User } from '../../../interfaces/user.interface';
styleUrl: './header.component.scss', styleUrl: './header.component.scss',
}) })
export class HeaderComponent { export class HeaderComponent {
navbarVisible: boolean = false; showNavbar: boolean = false;
navbarLanguageVisible: boolean = false; showLanguageNavbar: boolean = false;
currentUser: User | null = null; currentUser: User | null = null;
constructor(private userService: UserService) {} constructor(private userService: UserService) {}
@ -41,15 +41,15 @@ export class HeaderComponent {
* @returns {void} * @returns {void}
*/ */
toggleNavbar(): void { toggleNavbar(): void {
this.navbarVisible = !this.navbarVisible; this.showNavbar = !this.showNavbar;
} }
/** /**
* Toggles the visibility of the language selection navbar. * Toggles the visibility of the language selection navbar.
* @returns {void} * @returns {void}
*/ */
toggleLanguage(): void { toggleLanguageNavbar(): void {
this.navbarLanguageVisible = !this.navbarLanguageVisible; this.showLanguageNavbar = !this.showLanguageNavbar;
} }
@HostListener('document:click', ['$event']) @HostListener('document:click', ['$event'])
@ -64,17 +64,17 @@ export class HeaderComponent {
!targetElement.closest('app-navbar') && !targetElement.closest('app-navbar') &&
!targetElement.closest('.img-user') !targetElement.closest('.img-user')
) { ) {
this.navbarVisible = false; this.showNavbar = false;
} }
if ( if (
!targetElement.closest('app-navbar') && !targetElement.closest('app-navbar') &&
!targetElement.closest('.img-lang') !targetElement.closest('.img-lang')
) { ) {
this.navbarLanguageVisible = false; this.showLanguageNavbar = false;
} }
if (targetElement.closest('.link')) { if (targetElement.closest('.link')) {
this.navbarVisible = false; this.showNavbar = false;
this.navbarLanguageVisible = false; this.showLanguageNavbar = false;
} }
} }
} }

View file

@ -1,4 +1,4 @@
@if (navbarVisible) { @if (showNavbar) {
<nav class="navbar d-none"> <nav class="navbar d-none">
<div <div
class="link" class="link"
@ -22,7 +22,7 @@
<span>{{ "navbar.logout" | translate }}</span> <span>{{ "navbar.logout" | translate }}</span>
</div> </div>
</nav> </nav>
} @else if (navbarLanguageVisible) { } @else if (showLanguageNavbar) {
<nav class="navbar language d-none"> <nav class="navbar language d-none">
<div <div
class="link" class="link"

View file

@ -14,8 +14,8 @@ import { AuthService } from '../../../../services/auth.service';
styleUrl: './navbar.component.scss', styleUrl: './navbar.component.scss',
}) })
export class NavbarComponent { export class NavbarComponent {
@Input() navbarVisible: boolean = false; @Input() showNavbar: boolean = false;
@Input() navbarLanguageVisible: boolean = false; @Input() showLanguageNavbar: boolean = false;
currentRoute: string = ''; currentRoute: string = '';