show error overlay for a backend error

This commit is contained in:
Chneemann 2024-08-08 00:28:58 +02:00
parent f00533de9d
commit 4870349e23
20 changed files with 126 additions and 106 deletions

View file

@ -25,9 +25,9 @@ class LoginView(APIView):
if user:
if not user.is_active:
return Response({'password': 'Account is inactive, please check your mails'}, status=status.HTTP_403_FORBIDDEN)
return Response({'error': 'Account is inactive, please check your mails'}, status=status.HTTP_403_FORBIDDEN)
return self._create_token_response(user)
return Response({'mail': 'Unable to login with provided credentials.'}, status=status.HTTP_401_UNAUTHORIZED)
return Response({'error': 'Unable to login with provided credentials.'}, status=status.HTTP_401_UNAUTHORIZED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@ -40,7 +40,7 @@ class RegisterView(APIView):
email = request.data.get('email')
password = request.data.get('password')
if CustomUser.objects.filter(email=email).exists():
return Response({'mail': 'A user with this email already exists.'}, status=status.HTTP_400_BAD_REQUEST)
return Response({'error': 'A user with this email already exists.'}, status=status.HTTP_400_BAD_REQUEST)
serialized = UserSerializer(data=request.data)
if serialized.is_valid():
@ -93,7 +93,7 @@ class ForgotPasswordView(APIView):
def post(self, request):
email = request.data.get('email')
if not email:
return Response({"mail": "Email required."}, status=status.HTTP_400_BAD_REQUEST)
return Response({"error": "Email required."}, status=status.HTTP_400_BAD_REQUEST)
try:
user = CustomUser.objects.get(email=email)
@ -101,7 +101,7 @@ class ForgotPasswordView(APIView):
user.save()
self.send_email(user)
except CustomUser.DoesNotExist:
return Response({"mail": "This email does not exist."}, status=status.HTTP_400_BAD_REQUEST)
return Response({"error": "This email does not exist."}, status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_200_OK)
def send_email(self, user):
@ -149,5 +149,5 @@ class AuthView(ObtainAuthToken):
def post(self, request):
email = request.data.get('email')
if CustomUser.objects.filter(email=email).exists():
return Response({'mail': 'A user with this email already exists.'}, status=status.HTTP_400_BAD_REQUEST)
return Response({'error': 'A user with this email already exists.'}, status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_200_OK)

View file

@ -1,2 +1 @@
<!-- <router-outlet /> -->
<app-error-toast></app-error-toast>
<router-outlet />

View file

@ -5,7 +5,7 @@ import { ErrorToastComponent } from './shared/components/error-toast/error-toast
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, ErrorToastComponent],
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
})

View file

@ -33,11 +33,11 @@
!isUserEmailValid(authData.mail.toLowerCase())) {
<p>This is not a valid email format</p>
}}
<div *ngIf="errorService.error$ | async as errors">
<!-- <div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['mail']">
<p>{{ errors["mail"] }}</p>
</div>
</div>
</div> -->
</div>
<app-btn-large
[value]="'Sign Up'"

View file

@ -5,6 +5,7 @@ import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ErrorService } from '../../services/error.service';
import { AuthService } from '../../services/auth.service';
import { HttpErrorResponse } from '@angular/common/http';
@Component({
selector: 'app-auth',
@ -46,7 +47,7 @@ export class AuthComponent {
this.router.navigate(['/register'], { queryParams });
this.errorService.clearError();
} catch (error) {
this.errorService.errorMsg(error);
this.errorService.handleError(error);
}
}
}

View file

@ -35,11 +35,11 @@
!isUserEmailValid(authData.mail.toLowerCase())) {
<p>This is not a valid email format</p>
}}
<div *ngIf="errorService.error$ | async as errors">
<!-- <div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['mail']">
<p>{{ errors["mail"] }}</p>
</div>
</div>
</div> -->
</div>
<app-btn-large
[value]="'Send Email'"
@ -116,11 +116,11 @@
passwordConfirm.touched) {
<p>The passwords do not match</p>
}
<div *ngIf="errorService.error$ | async as errors">
<!-- <div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['error']" class="error-msg">
<p>{{ errors["error"] }}</p>
</div>
</div>
</div> -->
</div>
<app-btn-large
[value]="'Update Password'"

View file

@ -76,7 +76,9 @@ export class ForgotPasswordComponent {
this.errorService.clearError();
} catch (error) {
this.sendMailSuccess = false;
this.errorService.errorMsg(error);
const errorMessage =
error instanceof Error ? error.message : 'An unknown error occurred';
this.errorService.errorMsg(errorMessage);
}
}
@ -94,7 +96,9 @@ export class ForgotPasswordComponent {
this.queryEmailSuccess = true;
this.errorService.clearError();
} catch (error) {
this.errorService.errorMsg(error);
const errorMessage =
error instanceof Error ? error.message : 'An unknown error occurred';
this.errorService.errorMsg(errorMessage);
}
}
}

View file

@ -31,11 +31,11 @@
<p>This is not a valid email format</p>
}}
<!-- Backend error handling -->
<div *ngIf="errorService.error$ | async as errors">
<!-- <div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['mail']">
<p>{{ errors["mail"] }}</p>
</div>
</div>
</div> -->
</div>
<input
type="password"
@ -61,11 +61,11 @@
<p>Password is too short, min 6 characters</p>
} }
<!-- Backend error handling -->
<div *ngIf="errorService.error$ | async as errors">
<!-- <div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['password']">
<p>{{ errors["password"] }}</p>
</div>
</div>
</div> -->
</div>
<div class="checkbox">
<label class="container"

View file

@ -45,7 +45,9 @@ export class LoginComponent {
this.router.navigate(['/browse/']);
this.errorService.clearError();
} catch (error) {
this.errorService.errorMsg(error);
const errorMessage =
error instanceof Error ? error.message : 'An unknown error occurred';
this.errorService.errorMsg(errorMessage);
}
}
}

View file

@ -39,11 +39,11 @@
!isUserEmailValid(authData.mail.toLowerCase())) {
<p>This is not a valid email format</p>
}}
<div *ngIf="errorService.error$ | async as errors">
<!-- <div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['mail']" class="error-msg">
<p>{{ errors["mail"] }}</p>
</div>
</div>
</div> -->
</div>
<input
type="password"

View file

@ -55,7 +55,9 @@ export class RegisterComponent {
this.registrationSuccess = true;
this.errorService.clearError();
} catch (error) {
this.errorService.errorMsg(error);
const errorMessage =
error instanceof Error ? error.message : 'An unknown error occurred';
this.errorService.errorMsg(errorMessage);
}
}
}

View file

@ -12,10 +12,10 @@
</p>
}
</div>
<div *ngIf="errorService.error$ | async as errors">
<!-- <div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['error']" class="error-msg">
<p>{{ errors["error"] }}</p>
</div>
</div>
</div> -->
</div>
</section>

View file

@ -45,20 +45,9 @@ export class VerifyEmailComponent {
this.errorService.clearError();
} catch (error) {
this.verified = false;
this.errorMsg(error);
}
}
errorMsg(error: any) {
if (error instanceof HttpErrorResponse) {
const errorTypes = ['error'];
for (const type of errorTypes) {
if (error.error[type]) {
this.errorService.setError(type, error.error[type]);
return;
}
}
this.errorService.clearError();
const errorMessage =
error instanceof Error ? error.message : 'An unknown error occurred';
this.errorService.errorMsg(errorMessage);
}
}
}

View file

@ -8,4 +8,5 @@
></app-forgot-password>
<app-verify-email *ngIf="currentRoute === 'verify-email'"></app-verify-email>
<app-footer></app-footer>
<app-error-toast *ngIf="errorService.displayError"></app-error-toast>
</section>

View file

@ -2,11 +2,3 @@ section {
width: 100vw;
height: 100vh;
}
// .bg-home {
// background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)),
// url(./../../../assets/img/backgrounds/home.png);
// background-size: cover;
// background-position: center;
// background-repeat: no-repeat;
// }

View file

@ -9,6 +9,8 @@ import { LoginComponent } from '../auth/login/login.component';
import { ForgotPasswordComponent } from '../auth/forgot-password/forgot-password.component';
import { BrowseComponent } from './browse/browse.component';
import { VerifyEmailComponent } from '../auth/verify-email/verify-email.component';
import { ErrorToastComponent } from '../../shared/components/error-toast/error-toast.component';
import { ErrorService } from '../../services/error.service';
@Component({
selector: 'app-home',
@ -23,14 +25,19 @@ import { VerifyEmailComponent } from '../auth/verify-email/verify-email.componen
ForgotPasswordComponent,
VerifyEmailComponent,
BrowseComponent,
ErrorToastComponent,
],
templateUrl: './home.component.html',
styleUrl: './home.component.scss',
})
export class HomeComponent implements OnInit {
currentRoute: any;
displayErrorToast: boolean = false;
constructor(private route: ActivatedRoute) {}
constructor(
private route: ActivatedRoute,
public errorService: ErrorService
) {}
ngOnInit(): void {
this.route.url.subscribe((url) => {

View file

@ -1,49 +1,31 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface ErrorDetail {
type: string;
message: string;
}
import { BehaviorSubject } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
@Injectable({
providedIn: 'root',
})
export class ErrorService {
private errorSubject = new BehaviorSubject<ErrorDetail | null>(null);
private errorSubject = new BehaviorSubject<string>('');
errorText$ = this.errorSubject.asObservable();
displayError = false;
error$: Observable<{ [key: string]: string } | null> = this.errorSubject
.asObservable()
.pipe(
map((errorDetail) => {
if (errorDetail) {
return { [errorDetail.type]: errorDetail.message };
}
return null;
})
);
setError(type: string, message: string) {
const errorDetail: ErrorDetail = { type, message };
this.errorSubject.next(errorDetail);
errorMsg(message: string) {
this.errorSubject.next(message);
this.displayError = true;
}
clearError() {
this.errorSubject.next(null);
this.errorSubject.next('');
this.displayError = false;
}
errorMsg(error: any) {
handleError(error: unknown) {
if (error instanceof HttpErrorResponse) {
const errorTypes = ['mail', 'password', 'error'];
for (const type of errorTypes) {
if (error.error[type]) {
this.setError(type, error.error[type]);
return;
}
}
this.clearError();
const errorMessage = error.error.error || 'An unknown error occurred';
this.errorMsg(errorMessage);
} else {
this.errorMsg('An unexpected error occurred');
}
}
}

View file

@ -1,20 +1,21 @@
<section>
<div class="overlay">
<section class="slide-in-right">
<div class="left"></div>
<div class="content">
<img src="./../../../../assets/img/attention.svg" alt="" />
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Provident aliquam
ex earum esse, totam placeat omnis sequi sint eveniet maiores explicabo
necessitatibus, est voluptatibus aperiam velit alias quae! Dicta aliquam
aspernatur libero maiores tenetur aut ratione et esse nesciunt nostrum,
cupiditate recusandae perferendis? Sapiente reprehenderit nemo repellendus
libero adipisci explicabo!
{{ errorText }}
</p>
</div>
<div class="right">
<div class="line"></div>
<div class="img">
<img src="./../../../../assets/img/close.svg" alt="" />
<img
src="./../../../../assets/img/close.svg"
alt=""
(click)="closeError()"
/>
</div>
</div>
</section>
</section>
</div>

View file

@ -8,6 +8,16 @@ section {
border-radius: 20px;
background-color: $purple;
box-shadow: 1px 1px 3px rgba($black, 0.5);
opacity: 0;
transform: translateY(100vh);
animation: slide-in-bottom 500ms forwards;
}
@keyframes slide-in-bottom {
to {
opacity: 1;
transform: translateY(0);
}
}
.left {
@ -26,6 +36,7 @@ section {
display: flex;
align-items: center;
width: 82%;
min-height: 48px;
margin-left: 24px;
p {
font-size: 18px;
@ -64,3 +75,19 @@ section {
}
}
}
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100vw;
height: 100vh;
backdrop-filter: blur(5px);
background-color: rgba($color: #000000, $alpha: 0.2);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
}

View file

@ -1,4 +1,5 @@
import { Component, Input } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { ErrorService } from '../../../services/error.service';
@Component({
selector: 'app-error-toast',
@ -7,6 +8,18 @@ import { Component, Input } from '@angular/core';
templateUrl: './error-toast.component.html',
styleUrl: './error-toast.component.scss',
})
export class ErrorToastComponent {
@Input() errorText: string = '';
export class ErrorToastComponent implements OnInit {
errorText: string = '';
constructor(private errorService: ErrorService) {}
ngOnInit(): void {
this.errorService.errorText$.subscribe((message: string) => {
this.errorText = message;
});
}
closeError() {
this.errorService.clearError();
}
}