diff --git a/src/app/app-module.ts b/src/app/app-module.ts index 06cbe6a..803e39c 100644 --- a/src/app/app-module.ts +++ b/src/app/app-module.ts @@ -10,7 +10,8 @@ import { App } from './app'; import { MatIconModule } from '@angular/material/icon'; import { MatTabsModule } from '@angular/material/tabs'; import { MatToolbarModule } from '@angular/material/toolbar'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; import { HeaderComponent } from './header/header.component'; @@ -60,7 +61,8 @@ import { Header2Component } from './header-2/header-2.component'; MatIconModule, MatTabsModule, MatToolbarModule, - MatProgressBarModule + MatProgressBarModule, + MatSnackBarModule ], providers: [ provideBrowserGlobalErrorListeners(), diff --git a/src/app/header-2/header-2.component.html b/src/app/header-2/header-2.component.html index 8f1c08a..9545660 100644 --- a/src/app/header-2/header-2.component.html +++ b/src/app/header-2/header-2.component.html @@ -24,7 +24,9 @@ - @if (loading$ | async) { + @if (loadingService.error$ | async) { +
+ } @else if (loadingService.loading$ | async){ (false); - readonly loading$ = this._loading.asObservable(); + private loadingSubject = new BehaviorSubject(false); + private errorSubject = new BehaviorSubject(false); - show() { - this._loading.next(true); + public loading$ = this.loadingSubject.asObservable(); + public error$ = this.errorSubject.asObservable(); + + constructor(private snackBar: MatSnackBar) {} + + show(): void { + this.loadingSubject.next(true); + this.errorSubject.next(false); } - hide() { - this._loading.next(false); + hide(): void { + this.loadingSubject.next(false); + this.errorSubject.next(false); + } + + showError(messageOrError?: string | HttpErrorResponse | any): void { + this.loadingSubject.next(false); + this.errorSubject.next(true); + + if (!messageOrError) { + return; + } + + const message = this.getErrorMessage(messageOrError); + + const snackBarRef = this.snackBar.open(message, 'Schließen', { + duration: 0, + panelClass: ['error-snackbar'], + horizontalPosition: 'center', + verticalPosition: 'bottom' + }); + + snackBarRef.afterDismissed().subscribe(() => { + this.hide(); + }); + } + + private getErrorMessage(error?: string | HttpErrorResponse | any): string { + + if (typeof error === 'string') { + return error; + } + + if (error instanceof HttpErrorResponse) { + + if (error.status === 0) { + return 'Netzwerkfehler: Keine Verbindung zum Server möglich!'; + } + + if (error.status >= 500) { + return `Serverfehler (${error.status}): ${error.statusText || 'Interner Serverfehler'}`; + } + + if (error.status >= 400) { + const errorMessage = error.error?.message || error.error?.error || error.statusText; + return `Fehler (${error.status}): ${errorMessage}`; + } + + return `HTTP Fehler (${error.status}): ${error.statusText}`; + } + + if (error.message) { + return error.message; + } + + return 'Ein unbekannter Fehler ist aufgetreten!'; } } diff --git a/src/app/schedule/schedule.component.ts b/src/app/schedule/schedule.component.ts index 7e89034..c7bbc7a 100644 --- a/src/app/schedule/schedule.component.ts +++ b/src/app/schedule/schedule.component.ts @@ -4,6 +4,7 @@ import { Vorstellung } from '@infinimotion/model-frontend'; import { Performance } from '../model/performance.model'; import { MovieGroup } from '../model/movie-group.model'; import { LoadingService } from '../loading.service'; +import { catchError, map, of, tap } from 'rxjs'; @Component({ selector: 'app-schedule', @@ -47,14 +48,19 @@ export class ScheduleComponent implements OnInit { loadPerformances() { this.loading.show(); - this.http.getPerformaces().subscribe({ - next: (data) => { - this.performaces = Array.isArray(data) ? data : [data]; + this.http.getPerformaces().pipe( + map(data => Array.isArray(data) ? data : [data]), + tap(performaces => { + this.performaces = performaces; this.assignPerformancesToDates(); - }, - error: (err) => console.error('Fehler beim Laden der Performances', err), - complete: () => this.loading.hide() - }); + this.loading.hide(); + }), + catchError(err => { + this.loading.showError(err); + console.error('Fehler beim Laden der Vorstellung', err); + return of([]); + }) + ).subscribe(); } assignPerformancesToDates() { diff --git a/src/custom-theme.scss b/src/custom-theme.scss index 6bdbd14..e55c90b 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -6,15 +6,30 @@ // custom components at https://material.angular.dev/guide/theming @use '@angular/material' as mat; - @include mat.progress-bar-overrides(( - active-indicator-color: white, - track-color: transparent, // Transparent machen - )); +@include mat.progress-bar-overrides(( + active-indicator-color: white, + track-color: transparent, +)); + +.mdc-linear-progress__buffer-bar { + background: linear-gradient(to right, #6366f1, #db2777) !important; +} + +@include mat.snack-bar-overrides(( + container-color: red, +)); + + + +.error-snackbar .mat-mdc-snack-bar-label { + color: white !important; +} + +.error-snackbar .mat-mdc-button { + color: white !important; +} + - // Gradient als Hintergrund - .mdc-linear-progress__buffer-bar { - background: linear-gradient(to right, #6366f1, #db2777) !important; - } html { @include mat.theme((