diff --git a/app/src/app/app.component.html b/app/src/app/app.component.html index 5e19703..5db2da5 100644 --- a/app/src/app/app.component.html +++ b/app/src/app/app.component.html @@ -1,4 +1,4 @@ - + @@ -56,10 +56,7 @@ - + - - + + diff --git a/app/src/app/app.component.ts b/app/src/app/app.component.ts index 8e171e5..2015f11 100644 --- a/app/src/app/app.component.ts +++ b/app/src/app/app.component.ts @@ -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, @@ -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', @@ -23,19 +23,21 @@ export class AppComponent implements OnInit { topMenuItems$ = this.store.select(getTopItems); bottomMenuItems$ = this.store.select(getBottomItems); isLoading$: Observable = this.store.select(isLoading); - isMapLoading$: Observable = this.store.select(isMapLoading); - hasError$: Observable = this.store.select(hasError); - librariesAreLoaded$: Observable = - 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 = this.store.select( + clientHasErrorConnection, ); + canShowPage$ = this.store.select(areLibrariesLoaded); - constructor(private store: Store) { - this.canShowPage$.subscribe((canShowPage) => { - console.log('canShowPage', canShowPage); - }); - } + constructor(private store: Store) {} ngOnInit(): void {} } diff --git a/app/src/app/pages/reservations/reservations.page.html b/app/src/app/pages/reservations/reservations.page.html index 440430e..00ca5da 100644 --- a/app/src/app/pages/reservations/reservations.page.html +++ b/app/src/app/pages/reservations/reservations.page.html @@ -9,7 +9,7 @@ - + {{ res.refuge.name }} @@ -36,4 +36,23 @@

+ + + + + + + diff --git a/app/src/app/pages/reservations/reservations.page.ts b/app/src/app/pages/reservations/reservations.page.ts index ebcfd61..3ae0755 100644 --- a/app/src/app/pages/reservations/reservations.page.ts +++ b/app/src/app/pages/reservations/reservations.page.ts @@ -5,7 +5,11 @@ 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', @@ -13,11 +17,13 @@ import { getReservationsSortedByRefuge } from '../../state/reservations/reservat 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(); } @@ -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, diff --git a/app/src/app/schemas/errors/common.ts b/app/src/app/schemas/errors/common.ts new file mode 100644 index 0000000..8bae00d --- /dev/null +++ b/app/src/app/schemas/errors/common.ts @@ -0,0 +1,3 @@ +export enum CommonErrors { + PROGRAMMING_ERROR = 'PROGRAMMING_ERROR', +} diff --git a/app/src/app/schemas/errors/permissions.ts b/app/src/app/schemas/errors/permissions.ts new file mode 100644 index 0000000..1817251 --- /dev/null +++ b/app/src/app/schemas/errors/permissions.ts @@ -0,0 +1,4 @@ +export enum PermissionsErrors { + NOT_ALLOWED_OPERATION_FOR_USER = 'NOT_ALLOWED_OPERATION_FOR_USER', + NOT_AUTHENTICATED = 'NOT_AUTHENTICATED', +} diff --git a/app/src/app/schemas/reservations/create-reservation.ts b/app/src/app/schemas/reservations/create-reservation.ts index d311cbf..3f6a165 100644 --- a/app/src/app/schemas/reservations/create-reservation.ts +++ b/app/src/app/schemas/reservations/create-reservation.ts @@ -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, @@ -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); } } @@ -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 { diff --git a/app/src/app/schemas/reservations/delete-reservation.ts b/app/src/app/schemas/reservations/delete-reservation.ts index bb339ea..2b4251a 100644 --- a/app/src/app/schemas/reservations/delete-reservation.ts +++ b/app/src/app/schemas/reservations/delete-reservation.ts @@ -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, @@ -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); } } @@ -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 { diff --git a/app/src/app/state/components/loading/loading.selector.ts b/app/src/app/state/components/loading/loading.selector.ts index 4c401ab..c48fc4a 100644 --- a/app/src/app/state/components/loading/loading.selector.ts +++ b/app/src/app/state/components/loading/loading.selector.ts @@ -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; @@ -10,7 +11,8 @@ 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) @@ -18,6 +20,11 @@ export const isLoading = createSelector( isLoading: true, keyMessage: 'SIGNUP.LOADING', } as LoadingState; + if (map.loadingMap) + return { + isLoading: true, + keyMessage: 'TODO: LOADING_MAP', + } as LoadingState; return { isLoading: false } as LoadingState; }, ); diff --git a/app/src/app/state/create-user/create-user.actions.ts b/app/src/app/state/create-user/create-user.actions.ts index c6dd251..5a6e632 100644 --- a/app/src/app/state/create-user/create-user.actions.ts +++ b/app/src/app/state/create-user/create-user.actions.ts @@ -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<{ diff --git a/app/src/app/state/create-user/create-user.effects.ts b/app/src/app/state/create-user/create-user.effects.ts index 0bbfdff..28f9baa 100644 --- a/app/src/app/state/create-user/create-user.effects.ts +++ b/app/src/app/state/create-user/create-user.effects.ts @@ -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'; @@ -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 { @@ -64,6 +69,7 @@ export class CreateUserEffects { map((response) => this.getNewStateFromUserCreateServer(response, createUserData), ), + catchError((error) => [connectionError(), createUserDevicesError()]), ); } diff --git a/app/src/app/state/create-user/create-user.reducer.ts b/app/src/app/state/create-user/create-user.reducer.ts index 52f5bf3..34a2401 100644 --- a/app/src/app/state/create-user/create-user.reducer.ts +++ b/app/src/app/state/create-user/create-user.reducer.ts @@ -3,6 +3,7 @@ import { RepeatedData } from '../../schemas/user/create/create-user-error'; import { UserFormError } from '../../schemas/user/validate/form'; import { createUserCorrect, + createUserDevicesError, createUserError, createUserRequest, } from './create-user.actions'; @@ -54,4 +55,8 @@ export const createUserReducer = createReducer( isLoading: false, error: action.error, })), + on(createUserDevicesError, (state, action) => ({ + ...state, + isLoading: false, + })), ); diff --git a/app/src/app/state/errors/error.actions.ts b/app/src/app/state/errors/error.actions.ts index dd4bd7e..e1d3e3c 100644 --- a/app/src/app/state/errors/error.actions.ts +++ b/app/src/app/state/errors/error.actions.ts @@ -4,6 +4,8 @@ export const resourceNotFound = createAction('[Error] Resource Not Found'); export const programmingError = createAction('[Error] Programming Error'); +export const connectionError = createAction('[Error] Connection Error'); + export const unknownError = createAction('[Error] Unknown Error'); export const cleanError = createAction('[Error] Clean Error'); diff --git a/app/src/app/state/errors/error.reducer.ts b/app/src/app/state/errors/error.reducer.ts index 2e9007f..339d89b 100644 --- a/app/src/app/state/errors/error.reducer.ts +++ b/app/src/app/state/errors/error.reducer.ts @@ -1,9 +1,19 @@ import { createReducer, on } from '@ngrx/store'; -import { cleanError, resourceNotFound, unknownError } from './error.actions'; +import { + cleanError, + connectionError, + resourceNotFound, + unknownError, +} from './error.actions'; export type ErrorState = { hasError: boolean; - type: 'resourceNotFound' | 'programmingError' | 'unknownError' | undefined; + type: + | 'resourceNotFound' + | 'programmingError' + | 'unknownError' + | 'connectionError' + | undefined; }; export const reservationState = { @@ -24,6 +34,10 @@ export const errorReducer = createReducer( hasError: true, type: 'programmingError', })), + on(connectionError, (state, action) => ({ + hasError: true, + type: 'connectionError', + })), on(cleanError, (state, action) => ({ hasError: false, type: undefined, diff --git a/app/src/app/state/errors/error.selectors.ts b/app/src/app/state/errors/error.selectors.ts index e5c3825..284f952 100644 --- a/app/src/app/state/errors/error.selectors.ts +++ b/app/src/app/state/errors/error.selectors.ts @@ -7,3 +7,8 @@ export const hasError = createSelector( selectError, (errorState) => errorState.hasError, ); + +export const clientHasErrorConnection = createSelector( + selectError, + (errorState) => errorState.type === 'connectionError', +); diff --git a/app/src/app/state/init/init.effects.ts b/app/src/app/state/init/init.effects.ts index 9afb740..6bdb1cd 100644 --- a/app/src/app/state/init/init.effects.ts +++ b/app/src/app/state/init/init.effects.ts @@ -13,7 +13,7 @@ import { } from './init.actions'; import { fromPromise } from 'rxjs/internal/observable/innerFrom'; import { secretEnvironment } from '../../../environments/environment.secret'; -import { unknownError } from '../errors/error.actions'; +import { connectionError, unknownError } from '../errors/error.actions'; @Injectable() export class InitEffects { @@ -32,7 +32,7 @@ export class InitEffects { switchMap((createData) => fromPromise(this.fetchGoogleMapsLibrary()).pipe( map(() => loadedMapLibrary()), - catchError(() => [unknownError(), errorLoadingMapLibrary()]), + catchError(() => [connectionError(), errorLoadingMapLibrary()]), ), ), ), diff --git a/app/src/app/state/map/map.effects.ts b/app/src/app/state/map/map.effects.ts index 75afc16..f399703 100644 --- a/app/src/app/state/map/map.effects.ts +++ b/app/src/app/state/map/map.effects.ts @@ -24,7 +24,11 @@ import { moveMapTo, } from './map.actions'; import { loadedMapLibrary } from '../init/init.actions'; -import { programmingError, unknownError } from '../errors/error.actions'; +import { + connectionError, + programmingError, + unknownError, +} from '../errors/error.actions'; import { match } from 'ts-pattern'; import { GetAllRefugesErrors } from '../../schemas/refuge/get-all-refuges-schema'; @@ -81,6 +85,7 @@ export class MapEffects { return loadedRefuges({ refuges: refuges.data }); return loadRefugesError({ error: refuges.error }); }), + catchError(() => of(connectionError())), ), ); diff --git a/app/src/app/state/reservations/reservations.actions.ts b/app/src/app/state/reservations/reservations.actions.ts index 5835a48..1200571 100644 --- a/app/src/app/state/reservations/reservations.actions.ts +++ b/app/src/app/state/reservations/reservations.actions.ts @@ -5,10 +5,12 @@ import { } from '../../schemas/reservations/reservation'; import { RefugeReservationsRelations } from '../../services/reservations/grouped-by/refuge'; import { + CreateReservationDataError, CreateReservationError, ErrorCreateReservation, } from '../../schemas/reservations/create-reservation'; import { + DeleteReservationDataError, DeleteReservationError, ErrorDeleteReservation, } from '../../schemas/reservations/delete-reservation'; @@ -28,16 +30,28 @@ export const fetchReservations = createAction( props<{ reservations: RefugeReservationsRelations }>(), ); +export const connectionErrorFetchReservations = createAction( + '[Reservations] Fetch Reservations had a connection error', +); + export const errorAddingReservation = createAction( '[Reservations] Error Adding Reservation', props<{ error: CreateReservationError }>(), ); +export const connectionErrorAddReservation = createAction( + '[Reservations] Add Reservation had a connection error', +); + export const deleteReservation = createAction( '[Reservations] Delete Reservation', props<{ id: string }>(), ); +export const connectionErrorDeleteReservation = createAction( + '[Reservations] Delete Reservation had a connection error', +); + export const deletedReservation = createAction( '[Reservations] Deleted Reservation', props<{ reservation: ReservationWithId }>(), diff --git a/app/src/app/state/reservations/reservations.effects.ts b/app/src/app/state/reservations/reservations.effects.ts index f584f48..c877cf8 100644 --- a/app/src/app/state/reservations/reservations.effects.ts +++ b/app/src/app/state/reservations/reservations.effects.ts @@ -5,12 +5,15 @@ import { ofType, ROOT_EFFECTS_INIT, } from '@ngrx/effects'; -import { combineLatest, map, mergeMap, switchMap } from 'rxjs'; +import { catchError, combineLatest, map, mergeMap, of, switchMap } from 'rxjs'; import { loginCompleted } from '../auth/auth.actions'; import { UserReservationService } from '../../services/reservations/user-reservation.service'; import { addedReservation, addReservation, + connectionErrorAddReservation, + connectionErrorDeleteReservation, + connectionErrorFetchReservations, deletedReservation, deleteReservation, errorAddingReservation, @@ -43,9 +46,8 @@ export class ReservationsEffects { this.userReservationService .getReservationsGroupedByRefugeForUser(actions[0].userId) .pipe( - map((reservations) => { - return fetchReservations({ reservations }); - }), + map((reservations) => fetchReservations({ reservations })), + catchError(() => of(connectionErrorFetchReservations())), ), ), ), @@ -65,6 +67,7 @@ export class ReservationsEffects { }); return errorDeletingReservation({ error: reservations.error }); }), + catchError(() => of(connectionErrorDeleteReservation())), ), ), ), @@ -89,6 +92,7 @@ export class ReservationsEffects { }); return errorAddingReservation({ error: reservations.error }); }), + catchError(() => of(connectionErrorAddReservation())), ), ), ), @@ -107,6 +111,7 @@ export class ReservationsEffects { orderByRefuge(reservations, this.reservationFactory), ), map((reservations) => fetchReservations({ reservations })), + catchError(() => of(connectionErrorFetchReservations())), ), ), ), diff --git a/app/src/app/state/reservations/reservations.reducer.ts b/app/src/app/state/reservations/reservations.reducer.ts index 26527d2..5782296 100644 --- a/app/src/app/state/reservations/reservations.reducer.ts +++ b/app/src/app/state/reservations/reservations.reducer.ts @@ -2,6 +2,9 @@ import { createReducer, on } from '@ngrx/store'; import { addedReservation, addReservation, + connectionErrorAddReservation, + connectionErrorDeleteReservation, + connectionErrorFetchReservations, deletedReservation, deleteReservation, errorAddingReservation, @@ -9,54 +12,83 @@ import { fetchReservations, } from './reservations.actions'; import { RefugeReservationsRelations } from '../../services/reservations/grouped-by/refuge'; +import { CreateReservationError } from '../../schemas/reservations/create-reservation'; +import { DeleteReservationError } from '../../schemas/reservations/delete-reservation'; export type ReservationsState = { reservations: RefugeReservationsRelations; - createError?: any; - deleteError?: any; + createError?: CreateReservationError; + deleteError?: DeleteReservationError; isLoading: boolean; + hasNewReservation: boolean; + hasDeletedReservation: boolean; }; export const reservationState = { reservations: [], isLoading: false, + hasNewReservation: false, + hasDeletedReservation: false, } as ReservationsState; export const reservationsReducer = createReducer( reservationState, on(deleteReservation, (state, action) => ({ - ...state, - deleteError: undefined, + reservations: state.reservations, + hasNewReservation: false, + hasDeletedReservation: false, isLoading: true, })), on(fetchReservations, (state, action) => ({ - ...state, + hasNewReservation: true, + hasDeletedReservation: false, + isLoading: false, reservations: action.reservations, })), - on(addReservation, (state, action) => ({ + on(connectionErrorFetchReservations, (state, action) => ({ ...state, - createError: undefined, + isLoading: false, + })), + on(connectionErrorAddReservation, (state, action) => ({ + ...state, + isLoading: false, + })), + on(connectionErrorDeleteReservation, (state, action) => ({ + ...state, + isLoading: false, + })), + on(addReservation, (state, action) => ({ + reservations: state.reservations, + hasNewReservation: false, + hasDeletedReservation: false, isLoading: true, })), on(addedReservation, (state, action) => ({ - ...state, + reservations: state.reservations, + hasNewReservation: true, + hasDeletedReservation: false, isLoading: false, })), on(deletedReservation, (state, action) => ({ - ...state, isLoading: false, reservations: removeReservationWithId( action.reservation.id, state.reservations, ), + hasNewReservation: false, + hasDeletedReservation: true, })), on(errorAddingReservation, (state, action) => ({ - ...state, + reservations: state.reservations, + hasNewReservation: false, + hasDeletedReservation: false, isLoading: false, createError: action.error, })), on(errorDeletingReservation, (state, action) => ({ - ...state, + reservations: state.reservations, + hasNewReservation: false, + hasDeletedReservation: false, isLoading: false, deleteError: action.error, })), @@ -66,12 +98,17 @@ function removeReservationWithId( id: string, reservations: RefugeReservationsRelations, ) { - return reservations.map((relation) => { - return { - ...relation, - reservations: relation.reservations.filter( + return reservations + .map((relation) => { + const newReservations = relation.reservations.filter( (reservation) => reservation.id !== id, - ), - }; - }); + ); + if (newReservations.length !== 0) + return { + ...relation, + reservations: newReservations, + }; + return null; + }) + .filter((relation) => relation !== null) as RefugeReservationsRelations; } diff --git a/app/src/app/state/reservations/reservations.selectors.ts b/app/src/app/state/reservations/reservations.selectors.ts index 9de912d..30f7cb4 100644 --- a/app/src/app/state/reservations/reservations.selectors.ts +++ b/app/src/app/state/reservations/reservations.selectors.ts @@ -1,5 +1,11 @@ import { createSelector } from '@ngrx/store'; import { AppState } from '../app.state'; +import { match } from 'ts-pattern'; +import { DeleteReservationDataError } from '../../schemas/reservations/delete-reservation'; +import { ServerErrors } from '../../schemas/errors/server'; +import { PermissionsErrors } from '../../schemas/errors/permissions'; +import { CommonErrors } from '../../schemas/errors/common'; +import { CreateReservationDataError } from '../../schemas/reservations/create-reservation'; export const selectReservations = (state: AppState) => state.reservations; @@ -10,10 +16,80 @@ export const getReservationsSortedByRefuge = createSelector( export const getCreateReservationErrors = createSelector( selectReservations, - (reservations) => reservations.createError, + (reservations) => { + if (reservations.createError) { + return match(reservations.createError) + .with( + CreateReservationDataError.INVALID_DATE, + () => 'TODO: INVALID_DATE_STRING', + ) + .with( + CreateReservationDataError.NTP_SERVER_IS_DOWN, + () => 'TODO: NTP_SERVER_IS_DOWN_STRING', + ) + .with( + CreateReservationDataError.REFUGE_OR_USER_NOT_FOUND, + () => 'TODO: REFUGE_OR_USER_NOT_FOUND_STRING', + ) + .with( + ServerErrors.UNKNOWN_ERROR, + ServerErrors.INCORRECT_DATA_FORMAT, + () => 'TODO: SERVER_ERROR_STRING', + ) + .with( + PermissionsErrors.NOT_ALLOWED_OPERATION_FOR_USER, + PermissionsErrors.NOT_AUTHENTICATED, + () => 'TODO: PERMISSIONS_ERROR_STRING', + ) + .with( + CommonErrors.PROGRAMMING_ERROR, + () => 'TODO: PROGRAMMING_ERROR_STRING', + ) + .exhaustive(); + } + return null; + }, ); -export const isLoadingReservations = createSelector( +export const getDeleteReservationErrors = createSelector( + selectReservations, + (reservations) => { + if (reservations.deleteError) + return match(reservations.deleteError) + .with( + DeleteReservationDataError.RESERVATION_NOT_FOUND, + () => 'TODO: RESERVATION_NOT_FOUND_STRING', + ) + .with( + ServerErrors.UNKNOWN_ERROR, + ServerErrors.INCORRECT_DATA_FORMAT, + () => 'TODO: SERVER_ERROR_STRING', + ) + .with( + PermissionsErrors.NOT_ALLOWED_OPERATION_FOR_USER, + PermissionsErrors.NOT_AUTHENTICATED, + () => 'TODO: PERMISSIONS_ERROR_STRING', + ) + .with( + CommonErrors.PROGRAMMING_ERROR, + () => 'TODO: PROGRAMMING_ERROR_STRING', + ) + .exhaustive(); + return null; + }, +); + +export const addedNewReservation = createSelector( + selectReservations, + (reservations) => reservations.hasNewReservation, +); + +export const deletedReservation = createSelector( + selectReservations, + (reservations) => reservations.hasDeletedReservation, +); + +export const isDoingReservationOperation = createSelector( selectReservations, (reservations) => reservations.isLoading, );