diff --git a/api/src/pilot/pilot.module.ts b/api/src/pilot/pilot.module.ts index 432f53c..78608b3 100644 --- a/api/src/pilot/pilot.module.ts +++ b/api/src/pilot/pilot.module.ts @@ -4,9 +4,23 @@ import { PilotService } from './pilot.service'; import { AzureTableStorageModule } from '@noahspan/azure-database'; import { Pilot } from './pilot.entity'; import { ConfigModule, ConfigService } from '@nestjs/config'; +import { Log } from 'src/log/log.entity'; @Module({ imports: [ + AzureTableStorageModule.forRootAsync({ + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => { + return { + connectionString: configService.get('azureStorageConnectionString') + }; + }, + inject: [ConfigService] + }), + AzureTableStorageModule.forFeature(Log, { + createTableIfNotExists: false, + table: 'logs' + }), AzureTableStorageModule.forRootAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => { @@ -19,7 +33,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; AzureTableStorageModule.forFeature(Pilot, { createTableIfNotExists: false, table: 'pilots' - }), + }) ], controllers: [PilotController], providers: [PilotService] diff --git a/api/src/pilot/pilot.service.ts b/api/src/pilot/pilot.service.ts index 2daccf9..3cc321e 100644 --- a/api/src/pilot/pilot.service.ts +++ b/api/src/pilot/pilot.service.ts @@ -1,12 +1,14 @@ import { InjectRepository, Repository } from '@noahspan/azure-database'; -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { Pilot } from './pilot.entity'; +import { Log } from 'src/log/log.entity'; +import { LogService } from 'src/log/log.service'; @Injectable() export class PilotService { constructor( - @InjectRepository(Pilot) - private readonly pilotRepository: Repository + @InjectRepository(Pilot) private readonly pilotRepository: Repository, + @InjectRepository(Log) private readonly logRepository: Repository ) {} async find(partitionKey: string, rowKey: string): Promise { @@ -35,6 +37,18 @@ export class PilotService { } async delete(partitionKey: string, rowKey: string): Promise { + const pilotLogs: Log[] = await this.logRepository.findAll({ + queryOptions: { + filter: `pilotId eq '${rowKey}'` + } + }) + + for (const pilotLog of pilotLogs) { + await this.logRepository.delete(pilotLog.partitionKey, pilotLog.rowKey); + } + await this.pilotRepository.delete(partitionKey, rowKey); + + return } } diff --git a/app/src/components/logbook/Logbook.tsx b/app/src/components/logbook/Logbook.tsx index 22869d6..5a18457 100644 --- a/app/src/components/logbook/Logbook.tsx +++ b/app/src/components/logbook/Logbook.tsx @@ -375,14 +375,16 @@ const Logbook: React.FC = () => { Logbook - + {isAuthenticated && + + } {!state.isLoading && state.error && ( diff --git a/app/src/components/pilots/Pilots.tsx b/app/src/components/pilots/Pilots.tsx index 79af763..73635e6 100644 --- a/app/src/components/pilots/Pilots.tsx +++ b/app/src/components/pilots/Pilots.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useReducer, useState } from 'react'; import PilotForm from '../pilotForm/PilotForm'; import { Box, @@ -19,26 +19,20 @@ import { Typography } from '@noahspan/noahspan-components'; import { useHttpClient } from '../../hooks/httpClient/UseHttpClient'; -import { AxiosInstance, AxiosResponse } from 'axios'; +import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; import { useAccessToken } from '../../hooks/accessToken/UseAcessToken'; import { useIsAuthenticated } from '@azure/msal-react'; import { FormMode } from '../../enums/formMode'; - -type Pilot = { - partitionKey: string; - rowKey: string; - id: string; - name: string; -}; +import { Pilot } from './Pilot.interface'; +import { initialState, reducer } from './reducer'; +import ActionMenu from '../actionMenu/ActionMenu'; +import ConfirmationDialog from '../confirmationDialog/ConfirmationDialog'; const Pilots: React.FC = () => { + const [state, dispatch] = useReducer(reducer, initialState); const httpClient: AxiosInstance = useHttpClient(); const isAuthenticated = useIsAuthenticated(); const { getAccessToken } = useAccessToken(); - const [isDrawerOpen, setIsDrawerOpen] = useState(false); - const [pilotFormMode, setPilotFormMode] = useState(FormMode.CANCEL); - const [selectedPilotId, setSelectedPilotId] = useState(); - const [pilots, setPilots] = useState([]); const getPilots = async (): Promise => { try { @@ -62,76 +56,70 @@ const Pilots: React.FC = () => { case FormMode.ADD: case FormMode.EDIT: case FormMode.VIEW: - setPilotFormMode(mode); - setSelectedPilotId(pilotId); - setIsDrawerOpen(true); + dispatch({ + type: 'SET_OPEN_CLOSE_ENTRY_FORM', + payload: { + formMode: mode, + selectedPilotId: pilotId, + isFormOpen: true + } + }) + break; case FormMode.CANCEL: - const pilots = await getPilots(); + dispatch({ + type: 'SET_OPEN_CLOSE_ENTRY_FORM', + payload: { + formMode: mode, + selectedPilotId: undefined, + isFormOpen: false + } + }) - setPilots(pilots); - setPilotFormMode(mode); - setSelectedPilotId(undefined); - setIsDrawerOpen(false); break; } }; - interface ActionMenuProps { - pilotId: string; - } + const onDeleteEntry = (pilotId: string) => { + dispatch({ + type: 'SET_DELETE', + payload: { isConfirmDialogOpen: true, selectedPilotId: pilotId } + }); + }; - const ActionMenu = ({ pilotId }: ActionMenuProps) => { - const [anchorElAction, setAnchorElAction] = useState( - null - ); + const onConfirmationDialogConfirm = async () => { + try { + dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: true }); - const onOpenActionMenu = (event: React.MouseEvent) => { - console.log(event); - setAnchorElAction(event.currentTarget); - }; + const token = await getAccessToken(); + const config = isAuthenticated + ? { headers: { Authorization: `${token}` } } + : {}; - const onCloseActionMenu = () => { - setAnchorElAction(null); - }; + await httpClient.delete(`api/pilots/pilot/${state.selectedPilotId}`, config); - return ( -
- - - - - onOpenClosePilotForm(FormMode.EDIT, pilotId)} - > - - - - Edit - - onOpenClosePilotForm(FormMode.VIEW, pilotId)} - > - - - - View - -
- - - - - Delete - -
-
- ); + dispatch({ + type: 'SET_DELETE', + payload: { isConfirmDialogOpen: false, selectedPilotId: undefined } + }); + await getPilots(); + } catch (error) { + const axiosError = error as AxiosError; + + dispatch({ + type: 'SET_ERROR', + payload: `Loading of logbook entries failed with the following message: ${axiosError.message}` + }); + } finally { + dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: false }); + } + }; + + const onConfirmationDialogCancel = () => { + dispatch({ + type: 'SET_DELETE', + payload: { isConfirmDialogOpen: false, selectedPilotId: undefined } + }); }; const columns: ColumnDef[] = [ @@ -141,7 +129,13 @@ const Pilots: React.FC = () => { }, { header: 'Actions', - cell: (info) => + cell: (info) => ( + + ) } ]; @@ -150,14 +144,16 @@ const Pilots: React.FC = () => { try { const pilots = await getPilots(); - setPilots(pilots); + dispatch({ type: 'SET_PILOTS', payload: pilots }) } catch (error) { console.log(error); } }; - loadPilots(); - }, []); + if (isAuthenticated && !state.isFormOpen) { + loadPilots(); + } + }, [isAuthenticated, state.isFormOpen]); return ( @@ -166,25 +162,39 @@ const Pilots: React.FC = () => { Pilots
- + {isAuthenticated && + + } - {pilots.length > 0 && } + {state.pilots.length > 0 &&
} - onOpenClosePilotForm(mode)} - pilotId={selectedPilotId} - /> + {state.isFormOpen && ( + onOpenClosePilotForm(mode)} + pilotId={state.selectedPilotId} + /> + )} + {state.isConfirmDialogOpen && ( + + )} ); };