Skip to content

Commit

Permalink
fix: menu bar, error handling and reservations information
Browse files Browse the repository at this point in the history
  • Loading branch information
Pablito2020 committed Nov 14, 2023
1 parent ec48947 commit 6ab703b
Show file tree
Hide file tree
Showing 21 changed files with 318 additions and 94 deletions.
16 changes: 9 additions & 7 deletions app/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<ion-app>
<ion-app *ngIf="canShowPage$ | async">
<ion-menu contentId="main-content" type="overlay">
<ion-content>
<ion-list id="top-menu-list">
Expand Down Expand Up @@ -56,17 +56,19 @@
</ion-footer>
</ion-list>
</ion-menu>
<ion-router-outlet
*ngIf="canShowPage$ | async"
id="main-content"
></ion-router-outlet>
<ion-router-outlet id="main-content"></ion-router-outlet>
<ng-container *ngIf="isLoading$ | async as loading">
<ion-loading
isOpen="{{ loading.isLoading }}"
message="{{ loading.keyMessage! | translate }}"
></ion-loading>
</ng-container>
</ion-app>
<ng-container *ngIf="isMapLoading$ | async as loading">
<ion-loading isOpen="{{ loading }}"> </ion-loading>
<ng-container *ngIf="hasConnectionError$ | async as error">
<ion-alert
isOpen="{{ error }}"
header="You are offline!"
message="You are offline or the server is not reachable. Please check your internet connection and try again."
[buttons]="alertButtons"
></ion-alert>
</ng-container>
30 changes: 16 additions & 14 deletions app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from './state/app.state';
import { combineLatest, map, Observable } from 'rxjs';
import { Observable } from 'rxjs';
import { areLibrariesLoaded } from './state/init/init.selectors';
import {
isLoading,
Expand All @@ -11,8 +11,8 @@ import {
getBottomItems,
getTopItems,
} from './state/components/menu/menu.selector';
import { isMapLoading } from './state/map/map.selectors';
import { hasError } from './state/errors/error.selectors';
import { clientHasErrorConnection } from './state/errors/error.selectors';
import { cleanError } from './state/errors/error.actions';

@Component({
selector: 'app-root',
Expand All @@ -23,19 +23,21 @@ export class AppComponent implements OnInit {
topMenuItems$ = this.store.select(getTopItems);
bottomMenuItems$ = this.store.select(getBottomItems);
isLoading$: Observable<LoadingState> = this.store.select(isLoading);
isMapLoading$: Observable<boolean> = this.store.select(isMapLoading);
hasError$: Observable<boolean> = this.store.select(hasError);
librariesAreLoaded$: Observable<boolean> =
this.store.select(areLibrariesLoaded);
canShowPage$ = combineLatest([this.librariesAreLoaded$, this.hasError$]).pipe(
map(([librariesAreLoaded, hasError]) => librariesAreLoaded || hasError),
public alertButtons = [
{
text: 'OK',
role: 'confirm',
handler: () => {
this.store.dispatch(cleanError());
},
},
];
hasConnectionError$: Observable<boolean> = this.store.select(
clientHasErrorConnection,
);
canShowPage$ = this.store.select(areLibrariesLoaded);

constructor(private store: Store<AppState>) {
this.canShowPage$.subscribe((canShowPage) => {
console.log('canShowPage', canShowPage);
});
}
constructor(private store: Store<AppState>) {}

ngOnInit(): void {}
}
21 changes: 20 additions & 1 deletion app/src/app/pages/reservations/reservations.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<ion-content [fullscreen]="true">
<ion-list style="padding-block: 0px">
<ion-item-group *ngFor="let res of reservations | async">
<ion-item-group *ngFor="let res of reservations$ | async">
<ion-item-divider sticky>
<ion-label> {{ res.refuge.name }} </ion-label>
</ion-item-divider>
Expand All @@ -36,4 +36,23 @@ <h3>
</ion-item>
</ion-item-group>
</ion-list>

<ng-container *ngIf="deleteErrors$ | async as errorMessage">
<ion-toast
[isOpen]="errorMessage !== null"
message="{{errorMessage | translate}}"
color="danger"
icon="bug-outline"
[duration]="3000"
></ion-toast>
</ng-container>
<ng-container *ngIf="deletedReservation$ | async as isDeleted">
<ion-toast
[isOpen]="isDeleted"
message="{{'RESERVATIONS.DELETE_SUCCESS' | translate}}"
color="success"
icon="checkmark-outline"
[duration]="3000"
></ion-toast>
</ng-container>
</ion-content>
16 changes: 9 additions & 7 deletions app/src/app/pages/reservations/reservations.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ import { TranslateService } from '@ngx-translate/core';
import { Store } from '@ngrx/store';
import { AppState } from '../../state/app.state';
import { deleteReservation } from '../../state/reservations/reservations.actions';
import { getReservationsSortedByRefuge } from '../../state/reservations/reservations.selectors';
import {
deletedReservation,
getDeleteReservationErrors,
getReservationsSortedByRefuge,
} from '../../state/reservations/reservations.selectors';

@Component({
selector: 'app-reservations',
templateUrl: './reservations.page.html',
styleUrls: ['./reservations.page.scss'],
})
export class ReservationsPage implements OnInit {
reservations = this.store.select(getReservationsSortedByRefuge);
reservations$ = this.store.select(getReservationsSortedByRefuge);
deleteErrors$ = this.store.select(getDeleteReservationErrors);
deletedReservation$ = this.store.select(deletedReservation);

onRemoveReservation(reservation: ReservationWithId) {
this.showDeleteReservationMessage(reservation, () => {
this.removeReservation(reservation);
this.store.dispatch(deleteReservation({ id: reservation.id }));
}).then();
}

Expand All @@ -27,10 +33,6 @@ export class ReservationsPage implements OnInit {
private translateService: TranslateService,
) {}

private removeReservation(reservation: ReservationWithId) {
this.store.dispatch(deleteReservation({ id: reservation.id }));
}

private async showDeleteReservationMessage(
reservation: ReservationWithId,
onClick: () => void,
Expand Down
3 changes: 3 additions & 0 deletions app/src/app/schemas/errors/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum CommonErrors {
PROGRAMMING_ERROR = 'PROGRAMMING_ERROR',
}
4 changes: 4 additions & 0 deletions app/src/app/schemas/errors/permissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum PermissionsErrors {
NOT_ALLOWED_OPERATION_FOR_USER = 'NOT_ALLOWED_OPERATION_FOR_USER',
NOT_AUTHENTICATED = 'NOT_AUTHENTICATED',
}
48 changes: 27 additions & 21 deletions app/src/app/schemas/reservations/create-reservation.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { isMatching, match } from 'ts-pattern';
import {
Reservation,
ReservationPattern,
ReservationWithId,
} from './reservation';
import { ReservationPattern, ReservationWithId } from './reservation';
import { ServerErrors } from '../errors/server';
import { PermissionsErrors } from '../errors/permissions';
import { CommonErrors } from '../errors/common';

export enum CreateReservationError {
export enum CreateReservationDataError {
REFUGE_OR_USER_NOT_FOUND = 'REFUGE_OR_USER_NOT_FOUND',
PROGRAMMING_ERROR = 'PROGRAMMING_ERROR',
SERVER_ERROR = 'SERVER_DATA_ERROR',
NOT_ALLOWED_CREATION_FOR_USER = 'NOT_ALLOWED_CREATION_FOR_USER',
NOT_AUTHENTICATED_OR_INVALID_DATE = 'NOT_AUTHENTICATED_OR_INVALID_DATE',
UNKNOWN_ERROR = 'UNKNOWN_ERROR',
INVALID_DATE = 'INVALID_DATE',
NTP_SERVER_IS_DOWN = 'NTP_SERVER_IS_DOWN',
}

export type CreateReservationError =
| CreateReservationDataError
| ServerErrors
| PermissionsErrors
| CommonErrors;

export namespace CreateReservationError {
export function from(
error: HttpErrorResponse,
Expand All @@ -24,27 +25,32 @@ export namespace CreateReservationError {
.with(0, () => {
throw new Error('You are offline or the server is down.');
})
.with(
HttpStatusCode.Unauthorized,
() => CreateReservationError.NOT_AUTHENTICATED_OR_INVALID_DATE,
)
.with(HttpStatusCode.Unauthorized, () => {
if (
error.message.includes(
'You are not allowed to get a reservation of another user',
)
)
return CreateReservationDataError.INVALID_DATE;
return PermissionsErrors.NOT_AUTHENTICATED;
})
.with(
HttpStatusCode.NotFound,
() => CreateReservationError.REFUGE_OR_USER_NOT_FOUND,
() => CreateReservationDataError.REFUGE_OR_USER_NOT_FOUND,
)
.with(
HttpStatusCode.Forbidden,
() => CreateReservationError.NOT_ALLOWED_CREATION_FOR_USER,
() => PermissionsErrors.NOT_ALLOWED_OPERATION_FOR_USER,
)
.with(
HttpStatusCode.UnprocessableEntity,
() => CreateReservationError.PROGRAMMING_ERROR,
() => CommonErrors.PROGRAMMING_ERROR,
)
.with(
HttpStatusCode.InternalServerError,
() => CreateReservationError.NTP_SERVER_IS_DOWN,
() => CreateReservationDataError.NTP_SERVER_IS_DOWN,
)
.otherwise(() => CreateReservationError.UNKNOWN_ERROR);
.otherwise(() => ServerErrors.UNKNOWN_ERROR);
}
}

Expand All @@ -65,7 +71,7 @@ export type CreateReservation =
export function fromResponse(response: any): CreateReservation {
if (isMatching(ReservationPattern, response))
return { status: 'ok', reservation: response };
return { status: 'error', error: CreateReservationError.SERVER_ERROR };
return { status: 'error', error: ServerErrors.INCORRECT_DATA_FORMAT };
}

export function fromError(error: HttpErrorResponse): CreateReservation | never {
Expand Down
30 changes: 18 additions & 12 deletions app/src/app/schemas/reservations/delete-reservation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ import {
ReservationPattern,
ReservationWithId,
} from './reservation';
import { ServerErrors } from '../errors/server';
import { PermissionsErrors } from '../errors/permissions';
import { CommonErrors } from '../errors/common';
import { CreateReservationDataError } from './create-reservation';
import { AuthenticationErrors } from '../auth/errors';

export enum DeleteReservationError {
export enum DeleteReservationDataError {
RESERVATION_NOT_FOUND = 'RESERVATION_NOT_FOUND',
PROGRAMMING_ERROR = 'PROGRAMMING_ERROR',
SERVER_ERROR = 'SERVER_DATA_ERROR',
NOT_ALLOWED_DELETION_FOR_USER = 'NOT_ALLOWED_DELETION_FOR_USER',
NOT_AUTHENTICATED = 'NOT_AUTHENTICATED',
UNKNOWN_ERROR = 'UNKNOWN_ERROR',
}

export type DeleteReservationError =
| DeleteReservationDataError
| ServerErrors
| PermissionsErrors
| CommonErrors;

export namespace DeleteReservationError {
export function from(
error: HttpErrorResponse,
Expand All @@ -25,21 +31,21 @@ export namespace DeleteReservationError {
})
.with(
HttpStatusCode.Unauthorized,
() => DeleteReservationError.NOT_AUTHENTICATED,
() => PermissionsErrors.NOT_AUTHENTICATED,
)
.with(
HttpStatusCode.NotFound,
() => DeleteReservationError.RESERVATION_NOT_FOUND,
() => DeleteReservationDataError.RESERVATION_NOT_FOUND,
)
.with(
HttpStatusCode.Forbidden,
() => DeleteReservationError.NOT_ALLOWED_DELETION_FOR_USER,
() => PermissionsErrors.NOT_ALLOWED_OPERATION_FOR_USER,
)
.with(
HttpStatusCode.UnprocessableEntity,
() => DeleteReservationError.PROGRAMMING_ERROR,
() => CommonErrors.PROGRAMMING_ERROR,
)
.otherwise(() => DeleteReservationError.UNKNOWN_ERROR);
.otherwise(() => ServerErrors.UNKNOWN_ERROR);
}
}

Expand All @@ -60,7 +66,7 @@ export type DeleteReservation =
export function fromResponse(response: any): DeleteReservation {
if (isMatching(ReservationPattern, response))
return { status: 'ok', reservation: response };
return { status: 'error', error: DeleteReservationError.SERVER_ERROR };
return { status: 'error', error: ServerErrors.INCORRECT_DATA_FORMAT };
}

export function fromError(error: HttpErrorResponse): DeleteReservation | never {
Expand Down
9 changes: 8 additions & 1 deletion app/src/app/state/components/loading/loading.selector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createSelector } from '@ngrx/store';
import { selectAuth } from '../../auth/auth.selectors';
import { selectCreateUser } from '../../create-user/create-user.selectors';
import { selectMap } from '../../map/map.selectors';

export type LoadingState = {
isLoading: boolean;
Expand All @@ -10,14 +11,20 @@ export type LoadingState = {
export const isLoading = createSelector(
selectAuth,
selectCreateUser,
(auth, create) => {
selectMap,
(auth, create, map) => {
if (auth.isLoading)
return { isLoading: true, keyMessage: 'LOGIN.LOADING' } as LoadingState;
if (create.isLoading)
return {
isLoading: true,
keyMessage: 'SIGNUP.LOADING',
} as LoadingState;
if (map.loadingMap)
return {
isLoading: true,
keyMessage: 'TODO: LOADING_MAP',
} as LoadingState;
return { isLoading: false } as LoadingState;
},
);
4 changes: 4 additions & 0 deletions app/src/app/state/create-user/create-user.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export const createUserRequest = createAction(
props<{ credentials: UserForm }>(),
);

export const createUserDevicesError = createAction(
'[User] Create User Device Error',
);

export const createUserError = createAction(
'[User] Create User Error',
props<{
Expand Down
10 changes: 8 additions & 2 deletions app/src/app/state/create-user/create-user.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { UserService } from '../../services/user/user.service';
import { loginRequest } from '../auth/auth.actions';
import { map, of, switchMap } from 'rxjs';
import { catchError, map, of, switchMap } from 'rxjs';
import {
createUserCorrect,
createUserDevicesError,
createUserError,
createUserRequest,
} from './create-user.actions';
Expand All @@ -18,7 +19,11 @@ import {
import { ServerErrors } from '../../schemas/errors/server';
import { CreateUserError } from '../../schemas/user/create/create-user-error';
import { CreateUserResponse } from '../../schemas/user/create/create-user-response';
import { programmingError, unknownError } from '../errors/error.actions';
import {
connectionError,
programmingError,
unknownError,
} from '../errors/error.actions';

@Injectable()
export class CreateUserEffects {
Expand Down Expand Up @@ -64,6 +69,7 @@ export class CreateUserEffects {
map((response) =>
this.getNewStateFromUserCreateServer(response, createUserData),
),
catchError((error) => [connectionError(), createUserDevicesError()]),
);
}

Expand Down
Loading

0 comments on commit 6ab703b

Please sign in to comment.