show error overlay for a backend error
This commit is contained in:
parent
f00533de9d
commit
4870349e23
20 changed files with 126 additions and 106 deletions
|
|
@ -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)
|
||||
|
|
@ -1,2 +1 @@
|
|||
<!-- <router-outlet /> -->
|
||||
<app-error-toast></app-error-toast>
|
||||
<router-outlet />
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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'"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue