import { useEffect, useReducer } from 'react'; import LogForm from '../logForm/LogForm'; import { Alert, Box, Button, ColumnDef, Grid, Icon, IconName, Spinner, Table, theme, Typography, useMediaQuery } from '@noahspan/noahspan-components'; import { initialState, reducer } from './reducer'; import { useHttpClient } from '../../hooks/httpClient/UseHttpClient'; import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; import { useAccessToken } from '../../hooks/accessToken/UseAcessToken'; import { useIsAuthenticated } from '@azure/msal-react'; import { FormMode } from '../../enums/formMode'; import ActionMenu from '../actionMenu/ActionMenu'; import ConfirmationDialog from '../confirmationDialog/ConfirmationDialog'; import { ILogbookEntry } from './ILogbookEntry'; import LogbookCard from '../logbookCard/LogbookCard'; const Logbook: React.FC = () => { const [state, dispatch] = useReducer(reducer, initialState); const httpClient: AxiosInstance = useHttpClient(); const isAuthenticated = useIsAuthenticated(); const { getAccessToken } = useAccessToken(); const isMedium = useMediaQuery(theme.breakpoints.up('md')); const getLogbookEntries = async () => { try { dispatch({ type: 'SET_IS_LOADING', payload: true }); const response: AxiosResponse = await httpClient.get(`api/logs`); const entries: ILogbookEntry[] = response.data; entries.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) if (response.data.length > 0) { dispatch({ type: 'SET_ENTRIES', payload: response.data }); if (state.alert) { dispatch({ type: 'SET_ALERT', payload: undefined}) } } else { dispatch({ type: 'SET_ALERT', payload: { severity: 'info', message: 'No logbook entries found.'}}) } } catch (error) { const axiosError = error as AxiosError; dispatch({ type: 'SET_ALERT', payload: { severity: 'error', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`} }); } finally { dispatch({ type: 'SET_IS_LOADING', payload: false }); } }; const onOpenCloseEntryForm = (mode: FormMode, entryId?: string) => { switch (mode) { case FormMode.ADD: case FormMode.EDIT: case FormMode.VIEW: dispatch({ type: 'SET_OPEN_CLOSE_ENTRY_FORM', payload: { formMode: mode, selectedEntryId: entryId, isFormOpen: true } }); break; case FormMode.CANCEL: dispatch({ type: 'SET_OPEN_CLOSE_ENTRY_FORM', payload: { formMode: mode, selectedEntryId: undefined, isFormOpen: false } }); break; } }; const onDeleteEntry = (entryId: string) => { dispatch({ type: 'SET_DELETE', payload: { isConfirmationDialogOpen: true, selectedEntryId: entryId } }); }; const onConfirmationDialogConfirm = async () => { try { dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: true }); const token = await getAccessToken(); const config = isAuthenticated ? { headers: { Authorization: `${token}` } } : {}; await httpClient.delete(`api/logs/log/${state.selectedEntryId}`, config); dispatch({ type: 'SET_DELETE', payload: { isConfirmationDialogOpen: false, selectedEntryId: undefined } }); await getLogbookEntries(); } catch (error) { const axiosError = error as AxiosError; dispatch({ type: 'SET_ALERT', payload: { severity: 'error', message: `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: { isConfirmationDialogOpen: false, selectedEntryId: undefined } }); }; const unauthColumns: ColumnDef[] = [ { accessorKey: 'pilotName', header: 'Pilot', }, { accessorKey: 'date', header: 'Date' }, { accessorKey: 'aircraftMakeModel', header: 'Aircraft Make & Model' }, { id: 'route', header: 'Route of Flight', meta: { headerAlign: 'center' }, columns: [ { accessorKey: 'routeFrom', header: 'From' }, { accessorKey: 'routeTo', header: 'To' } ] }, { accessorKey: 'durationOfFlight', header: 'Duration Of Flight', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'notes', header: 'Notes' }, ] const authColumns: ColumnDef[] = [ { accessorKey: 'pilotName', header: 'Pilot', }, { accessorKey: 'date', header: 'Date' }, { accessorKey: 'aircraftMakeModel', header: 'Aircraft Make & Model' }, { accessorKey: 'aircraftIdentity', header: 'Aircraft Identity', }, { id: 'route', header: 'Route of Flight', meta: { headerAlign: 'center' }, columns: [ { accessorKey: 'routeFrom', header: 'From' }, { accessorKey: 'routeTo', header: 'To' } ] }, { accessorKey: 'durationOfFlight', header: 'Duration Of Flight', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'singleEngineLand', header: 'Single Engine Land', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { id: 'landings', header: 'Landings', meta: { headerAlign: 'center' }, columns: [ { accessorKey: 'landingsDay', header: 'Day', meta: { align: 'right', headerAlign: 'right' } }, { accessorKey: 'landingsNight', header: 'Night', meta: { align: 'right', headerAlign: 'right' } } ] }, { id: 'instrument', header: 'Instrument', meta: { headerAlign: 'center' }, columns: [ { accessorKey: 'instrumentActual', header: 'Actual', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'instrumentSimulated', header: 'Simulated', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'instrumentApproaches', header: 'Approaches', meta: { align: 'right', headerAlign: 'right' } }, { accessorKey: 'instrumentHolds', header: 'Holds', meta: { align: 'right', headerAlign: 'right' } }, { accessorKey: 'instrumentNavTrack', header: 'Nav/Track', meta: { align: 'right', headerAlign: 'right' } } ] }, { id: 'experienceTraining', header: 'Type of pilot experience or training', meta: { headerAlign: 'center' }, columns: [ { accessorKey: 'groundTrainingReceived', header: 'Ground Training Received', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'flightTrainingReceived', header: 'Flight Training Received', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'crossCountry', header: 'Cross Country', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'night', header: 'Night', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'solo', header: 'Solo', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' }, { accessorKey: 'pilotInCommand', header: 'Pilot In Command', meta: { align: 'right', headerAlign: 'right' }, cell: (info: any) => info.getValue() ? parseFloat(info.getValue()).toFixed(1) : '' } ] }, { accessorKey: 'notes', header: 'Notes' }, { header: 'Actions', meta: { align: 'center', headerAlign: 'center' }, cell: (info: any) => ( ) } ]; useEffect(() => { if (!state.isFormOpen) { getLogbookEntries(); } }, [state.isFormOpen]); return ( Logbook {isAuthenticated && } {!state.isLoading && state.alert && ( dispatch({ type: 'SET_ALERT', payload: undefined }) } severity={state.alert.severity} sx={{ width: '100%' }} > {state.alert.message} )} {!state.isLoading && ( {isMedium && state.entries.length > 0 && ( )} {!isMedium && state.entries.length > 0 && } )} {state.isLoading && !state.alert && ( <> Loading... )} {state.isFormOpen && ( onOpenCloseEntryForm(mode)} /> )} {state.isConfirmDialogOpen && ( )} ); }; export default Logbook;