104 switch to daisyui #106
@@ -35,7 +35,7 @@
|
|||||||
"@nestjs/serve-static": "^5.0.3",
|
"@nestjs/serve-static": "^5.0.3",
|
||||||
"@nestjs/typeorm": "^11.0.0",
|
"@nestjs/typeorm": "^11.0.0",
|
||||||
"@noahspan/azure-database": "^3.1.2",
|
"@noahspan/azure-database": "^3.1.2",
|
||||||
"@noahspan/noahspan-modules": "^1.2.9",
|
"@noahspan/noahspan-modules": "^1.2.11",
|
||||||
"@schematics/angular": "^17.3.7",
|
"@schematics/angular": "^17.3.7",
|
||||||
"@types/multer": "^1.4.12",
|
"@types/multer": "^1.4.12",
|
||||||
"better-sqlite3": "^12.2.0",
|
"better-sqlite3": "^12.2.0",
|
||||||
|
|||||||
@@ -32,17 +32,18 @@ import { join } from 'path';
|
|||||||
}),
|
}),
|
||||||
HealthModule,
|
HealthModule,
|
||||||
LogModule,
|
LogModule,
|
||||||
// MsGraphModule.registerAsync({
|
MsGraphModule.registerAsync({
|
||||||
// inject: [ConfigService],
|
inject: [ConfigService],
|
||||||
// imports: [ConfigModule],
|
imports: [ConfigModule],
|
||||||
// useFactory: async (configService: ConfigService) => {
|
useFactory: async (configService: ConfigService) => {
|
||||||
// return {
|
return {
|
||||||
// clientId: configService.get<string>('clientId'),
|
authority: configService.get<string>('authority'),
|
||||||
// clientSecret: configService.get<string>('clientSecret'),
|
clientId: configService.get<string>('clientId'),
|
||||||
// tenantId: configService.get<string>('tenantId')
|
clientSecret: configService.get<string>('clientSecret'),
|
||||||
// }
|
tenantId: configService.get<string>('tenantId')
|
||||||
// }
|
}
|
||||||
// }),
|
}
|
||||||
|
}),
|
||||||
PilotModule,
|
PilotModule,
|
||||||
ServeStaticModule.forRoot({
|
ServeStaticModule.forRoot({
|
||||||
rootPath: join(__dirname, '../..', 'client', 'dist')
|
rootPath: join(__dirname, '../..', 'client', 'dist')
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export default () => ({
|
export default () => ({
|
||||||
azureStorageConnectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
|
azureStorageConnectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
|
||||||
audience: process.env.AUDIENCE,
|
audience: process.env.AUDIENCE,
|
||||||
|
authority: process.env.AUTHORITY,
|
||||||
clientId: process.env.CLIENT_ID,
|
clientId: process.env.CLIENT_ID,
|
||||||
clientSecret: process.env.CLIENT_SECRET,
|
clientSecret: process.env.CLIENT_SECRET,
|
||||||
issuer: process.env.ISSUER_URL,
|
issuer: process.env.ISSUER_URL,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class LogController {
|
|||||||
return await this.logService.create(logDto);
|
return await this.logService.create(logDto);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const customError = error as CustomError;
|
const customError = error as CustomError;
|
||||||
|
console.log(error)
|
||||||
throw new HttpException(customError.message, customError.statusCode);
|
throw new HttpException(customError.message, customError.statusCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ async function bootstrap() {
|
|||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: 'http://localhost:8080', // Allow requests from your frontend's origin
|
origin: 'http://localhost:8080',
|
||||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||||
credentials: true, // If you need to send cookies or authorization headers
|
credentials: true,
|
||||||
});
|
});
|
||||||
app.setGlobalPrefix('api');
|
app.setGlobalPrefix('api');
|
||||||
app.useGlobalFilters(new HttpExceptionFilter());
|
app.useGlobalFilters(new HttpExceptionFilter());
|
||||||
app.use(
|
app.use(
|
||||||
session({
|
session({
|
||||||
secret: 'blah',
|
secret: process.env.SESSION_SECRET,
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false
|
saveUninitialized: false
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export class PilotInterceptor implements NestInterceptor {
|
|||||||
if (req.headers.authorization) {
|
if (req.headers.authorization) {
|
||||||
const authHeader = req.headers.authorization;
|
const authHeader = req.headers.authorization;
|
||||||
const token = authHeader && authHeader.split(' ')[1];
|
const token = authHeader && authHeader.split(' ')[1];
|
||||||
|
|
||||||
const jwtPayload = jwtDecode(token);
|
const jwtPayload = jwtDecode(token);
|
||||||
const rolesKeyName = Object.keys(jwtPayload).find((key) => key.includes('roles'));
|
const rolesKeyName = Object.keys(jwtPayload).find((key) => key.includes('roles'));
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,12 @@
|
|||||||
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
||||||
"@fortawesome/react-fontawesome": "^3.1.0",
|
"@fortawesome/react-fontawesome": "^3.1.0",
|
||||||
"@heroui/react": "^2.8.5",
|
|
||||||
"@noahspan/noahspan-components": "^2.0.0-alpha-14",
|
"@noahspan/noahspan-components": "^2.0.0-alpha-14",
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@tailwindcss/vite": "^4.1.13",
|
"@tailwindcss/vite": "^4.1.13",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"framer-motion": "^12.23.24",
|
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"oidc-spa": "^7.2.4",
|
"oidc-spa": "^7.2.4",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
import { Route, Routes } from 'react-router-dom';
|
||||||
import Flights from './components/flights/Flights';
|
import Flights from './components/flights/Flights';
|
||||||
import Logbook from './components/logbook/Logbook';
|
import Logbook from './components/logbook/Logbook';
|
||||||
import Pilots from './components/pilots/Pilots';
|
import Pilots from './components/pilots/Pilots';
|
||||||
import SiteNav from './components/siteNav/SiteNav';
|
import SiteNav from './components/siteNav/SiteNav';
|
||||||
import { HeroUIProvider } from '@heroui/react';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useHref, useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeroUIProvider navigate={navigate} useHref={useHref}>
|
<div className='bg-[#f5f5f5]' data-theme="lofi">
|
||||||
<SiteNav />
|
<SiteNav />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/' element={<Flights />} />
|
<Route path='/' element={<Flights />} />
|
||||||
<Route path="/logbook" element={<Logbook />} />
|
<Route path="/logbook" element={<Logbook />} />
|
||||||
<Route path="/pilots" element={<Pilots />} />
|
<Route path="/pilots" element={<Pilots />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</HeroUIProvider>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ export const { OidcProvider, useOidc, getOidc } = createReactOidc(async () => ({
|
|||||||
issuerUri: import.meta.env.VITE_ISSUER_URI,
|
issuerUri: import.meta.env.VITE_ISSUER_URI,
|
||||||
clientId: import.meta.env.VITE_CLIENT_ID,
|
clientId: import.meta.env.VITE_CLIENT_ID,
|
||||||
homeUrl: import.meta.env.VITE_BASE_URL,
|
homeUrl: import.meta.env.VITE_BASE_URL,
|
||||||
|
scopes: ['email', 'openid', 'profile', `api://${import.meta.env.VITE_CLIENT_ID}/user_impersonation`],
|
||||||
autoLogin: false,
|
autoLogin: false,
|
||||||
postLoginRedirectUrl: '/',
|
postLoginRedirectUrl: '/',
|
||||||
noIframe: true,
|
noIframe: true,
|
||||||
extraQueryParams: {
|
|
||||||
audience: "api://flying-test-api"
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
44
client/src/components/alert/Alert.tsx
Normal file
44
client/src/components/alert/Alert.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { AlertProps } from "./AlertProps.interface";
|
||||||
|
import { faCircleCheck, faCircleInfo, faCircleXmark, faTriangleExclamation, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
const Alert = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
closeIcon,
|
||||||
|
severity,
|
||||||
|
onClose,
|
||||||
|
...rest
|
||||||
|
}: AlertProps) => {
|
||||||
|
const severityVariants = {
|
||||||
|
info: 'alert-info',
|
||||||
|
error: 'alert-error',
|
||||||
|
success: 'alert-success',
|
||||||
|
warning: 'alert-warning'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
role='alert'
|
||||||
|
className={`alert ${severity ? severityVariants[severity] : ''} ${className ? className : ''}`}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{severity === 'info' && <FontAwesomeIcon icon={faCircleInfo} />}
|
||||||
|
{severity === 'error' && <FontAwesomeIcon icon={faCircleXmark} />}
|
||||||
|
{severity === 'success' && (
|
||||||
|
<FontAwesomeIcon icon={faCircleCheck} />
|
||||||
|
)}
|
||||||
|
{severity === 'warning' && (
|
||||||
|
<FontAwesomeIcon icon={faTriangleExclamation} />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span>{children}</span>
|
||||||
|
{closeIcon && <button className='btn' onClick={onClose}>{<FontAwesomeIcon icon={faXmark} />}</button>}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Alert;
|
||||||
7
client/src/components/alert/AlertProps.interface.ts
Normal file
7
client/src/components/alert/AlertProps.interface.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export interface AlertProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
closeIcon?: React.ReactNode;
|
||||||
|
onClose?: () => void;
|
||||||
|
severity: 'info' | 'error' | 'success' | 'warning';
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
// IconName,
|
// IconName,
|
||||||
// Loading
|
// Loading
|
||||||
// } from '@noahspan/noahspan-components';
|
// } from '@noahspan/noahspan-components';
|
||||||
import { Button, Modal, ModalBody, ModalContent, ModalHeader, ModalFooter, Spinner } from '@heroui/react'
|
|
||||||
import { DialogConfirmationProps } from './ConfirmationDialogProps.interface';
|
import { DialogConfirmationProps } from './ConfirmationDialogProps.interface';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faCircleCheck, faXmark } from '@fortawesome/free-solid-svg-icons';
|
import { faCircleCheck, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||||
@@ -21,31 +20,25 @@ const ConfirmationDialog = ({
|
|||||||
title
|
title
|
||||||
}: DialogConfirmationProps) => {
|
}: DialogConfirmationProps) => {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<>
|
||||||
isDismissable={false}
|
<input type='checkbox' id='dialog' className='modal-toggle' onChange={() => {}} checked={isOpen} />
|
||||||
isKeyboardDismissDisabled={true}
|
<div className='modal' role='dialog'>
|
||||||
isOpen={isOpen}
|
<div className='modal-box'>
|
||||||
>
|
<h3 className="text-lg font-bold">{title}</h3>
|
||||||
<ModalContent>
|
<p className="py-4">{contentText}</p>
|
||||||
<ModalHeader>{title}</ModalHeader>
|
<div className='modal-action'>
|
||||||
<ModalBody>
|
<button className='btn' onClick={onCancel}>
|
||||||
{!isLoading && <div>{contentText}</div>}
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
{isLoading && <Spinner size='lg' />}
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={onCancel} startContent={<FontAwesomeIcon icon={faXmark} />}>
|
|
||||||
No
|
No
|
||||||
</Button>
|
</button>
|
||||||
<Button
|
<button className='btn btn-primary' onClick={onConfirm}>
|
||||||
color='primary'
|
<FontAwesomeIcon icon={faCircleCheck} />
|
||||||
onPress={onConfirm}
|
|
||||||
startContent={<FontAwesomeIcon icon={faCircleCheck} />}
|
|
||||||
>
|
|
||||||
Yes
|
Yes
|
||||||
</Button>
|
</button>
|
||||||
</ModalFooter>
|
</div>
|
||||||
</ModalContent>
|
</div>
|
||||||
</Modal>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { Icon, IconName, Skeleton } from "@noahspan/noahspan-components";
|
|
||||||
import { Card } from '@heroui/react';
|
|
||||||
import LogbookCard from "../logbookCard/LogbookCard";
|
import LogbookCard from "../logbookCard/LogbookCard";
|
||||||
import { useEffect, useReducer } from "react";
|
import { useEffect, useReducer } from "react";
|
||||||
import { useLogs } from "../../hooks/logs/UseLogs";
|
import { useLogs } from "../../hooks/logs/UseLogs";
|
||||||
import { LogbookEntry } from "../logbook/LogbookEntry.interface";
|
import { LogbookEntry } from "../logbook/LogbookEntry.interface";
|
||||||
import { initialState, reducer } from "./reducer";
|
import { initialState, reducer } from "./reducer";
|
||||||
import { Alert } from '@heroui/react'
|
import Alert from "../alert/Alert";
|
||||||
|
|
||||||
const Flights = () => {
|
const Flights = () => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
@@ -23,7 +21,7 @@ const Flights = () => {
|
|||||||
dispatch({ type: 'SET_FLIGHTS', payload: flights})
|
dispatch({ type: 'SET_FLIGHTS', payload: flights})
|
||||||
dispatch({ type: 'SET_ALERT', payload: undefined })
|
dispatch({ type: 'SET_ALERT', payload: undefined })
|
||||||
} else {
|
} else {
|
||||||
dispatch({ type: 'SET_ALERT', payload: { severity: 'default', message: 'No flights found' }})
|
dispatch({ type: 'SET_ALERT', payload: { severity: 'info', message: 'No flights found' }})
|
||||||
}
|
}
|
||||||
}, [logs])
|
}, [logs])
|
||||||
|
|
||||||
@@ -35,12 +33,14 @@ const Flights = () => {
|
|||||||
{!logsLoading && state.alert && (
|
{!logsLoading && state.alert && (
|
||||||
<div>
|
<div>
|
||||||
<Alert
|
<Alert
|
||||||
|
className='mb-5'
|
||||||
onClose={() =>
|
onClose={() =>
|
||||||
dispatch({ type: 'SET_ALERT', payload: undefined })
|
dispatch({ type: 'SET_ALERT', payload: undefined })
|
||||||
}
|
}
|
||||||
color={state.alert.severity}
|
severity={state.alert.severity}
|
||||||
title={state.alert.message}
|
>
|
||||||
/>
|
{state.alert.message}
|
||||||
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!logsLoading &&
|
{!logsLoading &&
|
||||||
@@ -48,7 +48,7 @@ const Flights = () => {
|
|||||||
<LogbookCard logs={state.flights} mode='flights' />
|
<LogbookCard logs={state.flights} mode='flights' />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{logsLoading && [...Array(6)].map((_element, index) => {
|
{/* {logsLoading && [...Array(6)].map((_element, index) => {
|
||||||
return (
|
return (
|
||||||
<div className='mb-5'>
|
<div className='mb-5'>
|
||||||
<Card
|
<Card
|
||||||
@@ -70,7 +70,7 @@ const Flights = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})} */}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useEffect, useReducer } from 'react';
|
import { useEffect, useReducer } from 'react';
|
||||||
import { Alert, Button, DatePicker, Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader, Input, NumberInput, Select, Selection, SelectItem, SharedSelection, Textarea } from '@heroui/react'
|
import { useForm, Controller, useFormContext } from 'react-hook-form';
|
||||||
import { useForm, Controller, FormProvider, useFormContext } from 'react-hook-form';
|
|
||||||
import { LogFormProps } from './LogFormProps.interface';
|
import { LogFormProps } from './LogFormProps.interface';
|
||||||
import { initialState, reducer } from './reducer';
|
import { initialState, reducer } from './reducer';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
@@ -10,7 +9,6 @@ import { useOidc } from '../../auth/oidcConfig';
|
|||||||
import httpClient from '../../httpClient/httpClient';
|
import httpClient from '../../httpClient/httpClient';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faSave, faXmark } from '@fortawesome/free-solid-svg-icons'
|
import { faSave, faXmark } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { parseAbsolute, parseDate, getLocalTimeZone, CalendarDate, ZonedDateTime } from '@internationalized/date';
|
|
||||||
import { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
|
import { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
|
||||||
|
|
||||||
const LogForm = () => {
|
const LogForm = () => {
|
||||||
@@ -40,7 +38,7 @@ const LogForm = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
const axiosError = error as AxiosError;
|
const axiosError = error as AxiosError;
|
||||||
|
|
||||||
dispatch({ type: 'SET_ALERT', payload: { severity: 'danger', message: axiosError.message }});
|
dispatch({ type: 'SET_ALERT', payload: { severity: 'error', message: axiosError.message }});
|
||||||
} finally {
|
} finally {
|
||||||
dispatch({ type: 'SET_IS_LOADING', payload: false });
|
dispatch({ type: 'SET_IS_LOADING', payload: false });
|
||||||
}
|
}
|
||||||
@@ -59,7 +57,7 @@ const LogForm = () => {
|
|||||||
label: pilot.name,
|
label: pilot.name,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log(newPilotsOptions)
|
|
||||||
dispatch({ type: 'SET_PILOT_OPTIONS', payload: newPilotsOptions });
|
dispatch({ type: 'SET_PILOT_OPTIONS', payload: newPilotsOptions });
|
||||||
}
|
}
|
||||||
}, [pilots]);
|
}, [pilots]);
|
||||||
@@ -73,25 +71,21 @@ const LogForm = () => {
|
|||||||
<Controller
|
<Controller
|
||||||
name="pilotId"
|
name="pilotId"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value } }) => {
|
render={({ field: { onChange, value } }) => {
|
||||||
return (
|
return (
|
||||||
<Select
|
<select
|
||||||
|
className='select w-full'
|
||||||
aria-labelledby='pilot'
|
aria-labelledby='pilot'
|
||||||
isDisabled={state.isDisabled}
|
disabled={state.isDisabled}
|
||||||
fullWidth={true}
|
onChange={onChange}
|
||||||
isRequired={true}
|
value={[value]}
|
||||||
onSelectionChange={(keys: SharedSelection) => {
|
|
||||||
setValue('pilotId', keys.currentKey);
|
|
||||||
}}
|
|
||||||
selectedKeys={[value]}
|
|
||||||
size='lg'
|
|
||||||
>
|
>
|
||||||
{state.pilotOptions?.map((pilotOption: { key: string; label: string, }) => {
|
{state.pilotOptions?.map((pilotOption: { key: string; label: string, }) => {
|
||||||
return (
|
return (
|
||||||
<SelectItem key={pilotOption.key}>{pilotOption.label}</SelectItem>
|
<option key={pilotOption.key}>{pilotOption.label}</option>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Select>
|
</select>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -104,20 +98,13 @@ const LogForm = () => {
|
|||||||
name="date"
|
name="date"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => {
|
render={({ field: { onChange, value } }) => {
|
||||||
const parsedAbsoluteDate = value ? parseAbsolute(value, getLocalTimeZone()) : value
|
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<DatePicker
|
<input
|
||||||
aria-labelledby='date'
|
type='date'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
isRequired={true}
|
disabled={state.isDisabled}
|
||||||
onChange={(selectedDate) => {
|
onChange={onChange}
|
||||||
let date = selectedDate as CalendarDate;
|
value={value ? value.split('T')[0] : ''}
|
||||||
|
|
||||||
setValue('date', date.toDate(getLocalTimeZone()).toISOString())
|
|
||||||
}}
|
|
||||||
size='lg'
|
|
||||||
value={parsedAbsoluteDate ? new CalendarDate(parsedAbsoluteDate.year, parsedAbsoluteDate.month, parsedAbsoluteDate.day) : value}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -131,18 +118,11 @@ const LogForm = () => {
|
|||||||
name="aircraftMakeModel"
|
name="aircraftMakeModel"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
aria-labelledby='aircraftMakeModel'
|
type='text'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={formState.errors.address ? 'danger' : undefined}
|
disabled={state.isDisabled}
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isRequired={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
size='lg'
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -158,18 +138,11 @@ const LogForm = () => {
|
|||||||
name="aircraftIdentity"
|
name="aircraftIdentity"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
aria-labelledby='aircraftIdentity'
|
type='text'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={formState.errors.address ? 'danger' : undefined}
|
disabled={state.isDisabled}
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isRequired={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
size='lg'
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -185,18 +158,11 @@ const LogForm = () => {
|
|||||||
name="routeFrom"
|
name="routeFrom"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
aria-labelledby='routeFrom'
|
type='text'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={formState.errors.address ? 'danger' : undefined}
|
disabled={state.isDisabled}
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isRequired={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
size='lg'
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -210,18 +176,11 @@ const LogForm = () => {
|
|||||||
name="routeTo"
|
name="routeTo"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
aria-labelledby='routeTo'
|
type='text'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={formState.errors.address ? 'danger' : undefined}
|
disabled={state.isDisabled}
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isRequired={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
size='lg'
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -235,21 +194,11 @@ const LogForm = () => {
|
|||||||
name="durationOfFlight"
|
name="durationOfFlight"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='durationOfFlight'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={formState.errors.address ? 'danger' : undefined}
|
disabled={state.isDisabled}
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isRequired={true}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -265,21 +214,11 @@ const LogForm = () => {
|
|||||||
name="singleEngineLand"
|
name="singleEngineLand"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='singleEngineLand'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={formState.errors.address ? 'danger' : undefined}
|
disabled={state.isDisabled}
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isRequired={true}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -293,20 +232,11 @@ const LogForm = () => {
|
|||||||
name="simulatorAtd"
|
name="simulatorAtd"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='simulaterAtd'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={formState.errors.address ? 'danger' : undefined}
|
disabled={state.isDisabled}
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -323,22 +253,11 @@ const LogForm = () => {
|
|||||||
name="landingsDay"
|
name="landingsDay"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='landingsDay'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -352,22 +271,11 @@ const LogForm = () => {
|
|||||||
name="landingsNight"
|
name="landingsNight"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='landingsNight'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -386,24 +294,12 @@ const LogForm = () => {
|
|||||||
name="groundTrainingReceived"
|
name="groundTrainingReceived"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='groundTrainingReceived'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
width='w-full'
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -418,22 +314,11 @@ const LogForm = () => {
|
|||||||
name="flightTrainingReceived"
|
name="flightTrainingReceived"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='flightTrainingReceived'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -447,22 +332,11 @@ const LogForm = () => {
|
|||||||
name="crossCountry"
|
name="crossCountry"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='crossCountry'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -476,22 +350,11 @@ const LogForm = () => {
|
|||||||
name="night"
|
name="night"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='night'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -505,22 +368,11 @@ const LogForm = () => {
|
|||||||
name="solo"
|
name="solo"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='solo'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -534,22 +386,11 @@ const LogForm = () => {
|
|||||||
name="pilotInCommand"
|
name="pilotInCommand"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='pilotInCommand'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -566,22 +407,11 @@ const LogForm = () => {
|
|||||||
name="instrumentActual"
|
name="instrumentActual"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='instrumentActual'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -595,22 +425,11 @@ const LogForm = () => {
|
|||||||
name="instrumentSimulated"
|
name="instrumentSimulated"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='instrumentSimulated'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -626,22 +445,11 @@ const LogForm = () => {
|
|||||||
name="instrumentApproaches"
|
name="instrumentApproaches"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='instrumentApproaches'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -655,22 +463,11 @@ const LogForm = () => {
|
|||||||
name="instrumentHolds"
|
name="instrumentHolds"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='instrumentHolds'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -684,23 +481,11 @@ const LogForm = () => {
|
|||||||
name="instrumentNavTrack"
|
name="instrumentNavTrack"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<NumberInput
|
<input
|
||||||
aria-labelledby='instrumentNavTrack'
|
type='number'
|
||||||
isDisabled={state.isDisabled}
|
className='input w-full'
|
||||||
color={
|
disabled={state.isDisabled}
|
||||||
formState.errors.address ? 'danger' : undefined
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
fullWidth={true}
|
|
||||||
isWheelDisabled={state.isDisabled}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
radius='lg'
|
|
||||||
size='sm'
|
|
||||||
type="number"
|
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -716,19 +501,11 @@ const LogForm = () => {
|
|||||||
name="notes"
|
name="notes"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Textarea
|
<textarea
|
||||||
aria-labelledby='notes'
|
className='textarea w-full'
|
||||||
isDisabled={state.isDisabled}
|
disabled={state.isDisabled}
|
||||||
color={formState.errors.address ? 'danger' : undefined}
|
|
||||||
errorMessage={
|
|
||||||
formState.errors.address
|
|
||||||
? formState.errors.address.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
fullWidth={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
></textarea>
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import { Selection } from "@heroui/react";
|
|
||||||
import { Alert } from "../../interfaces/Alert.interface";
|
import { Alert } from "../../interfaces/Alert.interface";
|
||||||
|
|
||||||
export interface LogFormState {
|
export interface LogFormState {
|
||||||
alert: Alert | undefined;
|
alert: Alert | undefined;
|
||||||
experienceSelectedKeys: Selection;
|
|
||||||
instrumentSelectedKeys: Selection;
|
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
landingsSelectedKeys: Selection;
|
|
||||||
pilotOptions: { key: string; label: string; }[];
|
pilotOptions: { key: string; label: string; }[];
|
||||||
selectedPilotName: string;
|
selectedPilotName: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,17 @@
|
|||||||
import { Alert } from '../../interfaces/Alert.interface';
|
import { Alert } from '../../interfaces/Alert.interface';
|
||||||
import { LogFormState } from './LogFormState.interface';
|
import { LogFormState } from './LogFormState.interface';
|
||||||
import { Selection } from '@heroui/react';
|
|
||||||
|
|
||||||
type Action =
|
type Action =
|
||||||
| { type: 'SET_ALERT'; payload: Alert | undefined }
|
| { type: 'SET_ALERT'; payload: Alert | undefined }
|
||||||
| { type: 'SET_EXPERIENCE_SELECTED_KEYS'; payload: Selection }
|
|
||||||
| { type: 'SET_INSTRUMENT_SELECTED_KEYS'; payload: Selection }
|
|
||||||
| { type: 'SET_IS_DISABLED'; payload: boolean }
|
| { type: 'SET_IS_DISABLED'; payload: boolean }
|
||||||
| { type: 'SET_IS_LOADING'; payload: boolean }
|
| { type: 'SET_IS_LOADING'; payload: boolean }
|
||||||
| { type: 'SET_LANDINGS_SELECTED_KEYS'; payload: Selection }
|
|
||||||
| { type: 'SET_PILOT_OPTIONS'; payload: { key: string, label: string; }[] }
|
| { type: 'SET_PILOT_OPTIONS'; payload: { key: string, label: string; }[] }
|
||||||
| { type: 'SET_SELECTED_PILOT_NAME'; payload: string };
|
| { type: 'SET_SELECTED_PILOT_NAME'; payload: string };
|
||||||
|
|
||||||
export const initialState: LogFormState = {
|
export const initialState: LogFormState = {
|
||||||
alert: undefined,
|
alert: undefined,
|
||||||
experienceSelectedKeys: new Set([]),
|
|
||||||
instrumentSelectedKeys: new Set([]),
|
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
landingsSelectedKeys: new Set(['1']),
|
|
||||||
pilotOptions: [],
|
pilotOptions: [],
|
||||||
selectedPilotName: ''
|
selectedPilotName: ''
|
||||||
};
|
};
|
||||||
@@ -34,36 +27,18 @@ export const reducer = (
|
|||||||
alert: action.payload
|
alert: action.payload
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'SET_EXPERIENCE_SELECTED_KEYS': {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
experienceSelectedKeys: action.payload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'SET_IS_DISABLED': {
|
case 'SET_IS_DISABLED': {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isDisabled: action.payload
|
isDisabled: action.payload
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'SET_INSTRUMENT_SELECTED_KEYS': {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
instrumentSelectedKeys: action.payload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'SET_IS_LOADING': {
|
case 'SET_IS_LOADING': {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isLoading: action.payload
|
isLoading: action.payload
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'SET_LANDINGS_SELECTED_KEYS': {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
landingsSelectedKeys: action.payload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'SET_PILOT_OPTIONS': {
|
case 'SET_PILOT_OPTIONS': {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { Key, useEffect, useReducer } from 'react';
|
import { useEffect, useReducer } from 'react';
|
||||||
import LogForm from '../logForm/LogForm';
|
|
||||||
import { initialState, reducer } from './reducer';
|
import { initialState, reducer } from './reducer';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
import { FormMode } from '../../enums/formMode';
|
import { FormMode } from '../../enums/formMode';
|
||||||
import { authColumns, unauthColumns } from './columns';
|
import { authColumns, unauthColumns } from './columns';
|
||||||
import ActionMenu from '../actionMenu/ActionMenu';
|
|
||||||
import ConfirmationDialog from '../confirmationDialog/ConfirmationDialog';
|
import ConfirmationDialog from '../confirmationDialog/ConfirmationDialog';
|
||||||
import { LogbookEntry } from './LogbookEntry.interface';
|
import { LogbookEntry } from './LogbookEntry.interface';
|
||||||
import LogbookCard from '../logbookCard/LogbookCard';
|
import LogbookCard from '../logbookCard/LogbookCard';
|
||||||
@@ -14,12 +12,12 @@ import { useUserRole } from '../../hooks/userRole/UseUserRole';
|
|||||||
import { UserRole } from '../../enums/userRole';
|
import { UserRole } from '../../enums/userRole';
|
||||||
import { useBreakpoints } from '../../hooks/useBreakpoints/UseBreakpoints';
|
import { useBreakpoints } from '../../hooks/useBreakpoints/UseBreakpoints';
|
||||||
import { ScreenSize } from '../../enums/screenSize';
|
import { ScreenSize } from '../../enums/screenSize';
|
||||||
import { Table, TableHeader, TableBody, TableColumn, Dropdown, DropdownTrigger, Button, DropdownSection, DropdownMenu, DropdownItem, Alert, TableRow, TableCell } from '@heroui/react';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faAdd, faEllipsisVertical, faPen, faEye, faTrash, faMapLocationDot } from '@fortawesome/free-solid-svg-icons'
|
import { faAdd, faEllipsisVertical, faPen, faEye, faTrash, faMapLocationDot } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, HeaderContext, useReactTable } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, HeaderContext, useReactTable } from '@tanstack/react-table';
|
||||||
import { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
|
import { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
|
||||||
import LogbookDrawer from '../logbookDrawer/LogbookDrawer';
|
import LogbookDrawer from '../logbookDrawer/LogbookDrawer';
|
||||||
|
import Alert from '../alert/Alert';
|
||||||
|
|
||||||
const Logbook: React.FC<unknown> = () => {
|
const Logbook: React.FC<unknown> = () => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
@@ -28,7 +26,6 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
const { userRole } = useUserRole();
|
const { userRole } = useUserRole();
|
||||||
const { screenSize } = useBreakpoints();
|
const { screenSize } = useBreakpoints();
|
||||||
const columnTotal = (info: HeaderContext<LogbookEntry, unknown>): number => {
|
const columnTotal = (info: HeaderContext<LogbookEntry, unknown>): number => {
|
||||||
const blah = info.table
|
|
||||||
const values: number[] = info.table.getPaginationRowModel().rows.map((row: any) => Number(row.getValue(info.column.id))).filter((value: any) => !Number.isNaN(value));
|
const values: number[] = info.table.getPaginationRowModel().rows.map((row: any) => Number(row.getValue(info.column.id))).filter((value: any) => !Number.isNaN(value));
|
||||||
let total: number = 0;
|
let total: number = 0;
|
||||||
|
|
||||||
@@ -69,13 +66,17 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
id: 'route',
|
id: 'route',
|
||||||
header: 'Route of Flight',
|
header: 'Route of Flight',
|
||||||
meta: {
|
meta: {
|
||||||
headerAlign: 'center'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-center'
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
id: 'routeFrom',
|
id: 'routeFrom',
|
||||||
accessorKey: 'routeFrom',
|
accessorKey: 'routeFrom',
|
||||||
header: 'From'
|
header: 'From',
|
||||||
|
meta: {
|
||||||
|
className: 'border-l border-base-300',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'routeTo',
|
id: 'routeTo',
|
||||||
@@ -89,8 +90,9 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
accessorKey: 'durationOfFlight',
|
accessorKey: 'durationOfFlight',
|
||||||
header: 'Duration Of Flight',
|
header: 'Duration Of Flight',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
||||||
@@ -99,51 +101,33 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
const notes: ColumnDef<LogbookEntry> = {
|
const notes: ColumnDef<LogbookEntry> = {
|
||||||
id: 'notes',
|
id: 'notes',
|
||||||
accessorKey: 'notes',
|
accessorKey: 'notes',
|
||||||
header: 'Notes'
|
header: 'Notes',
|
||||||
|
meta: {
|
||||||
|
className: 'border-l border-base-300',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const actions: ColumnDef<LogbookEntry> = {
|
const actions: ColumnDef<LogbookEntry> = {
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
header: 'Actions',
|
header: 'Actions',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'center',
|
align: 'text-center',
|
||||||
headerAlign: 'center'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-center'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) => {
|
cell: (info: CellContext<LogbookEntry, unknown>) => {
|
||||||
return (
|
return (
|
||||||
<Dropdown>
|
<div className='dropdown dropdown-end'>
|
||||||
<DropdownTrigger>
|
<div tabIndex={0} role='button' className='btn btn-ghost'><FontAwesomeIcon icon={faEllipsisVertical} /></div>
|
||||||
<Button isIconOnly variant='light' size='lg'>
|
<ul tabIndex={-1} className="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm border border-base-300">
|
||||||
<FontAwesomeIcon icon={faEllipsisVertical} />
|
{isUserLoggedIn && userRole === UserRole.WRITE &&
|
||||||
</Button>
|
<li><a onClick={() => onOpenCloseDrawer(FormMode.EDIT, info.row.original.id)}><FontAwesomeIcon icon={faPen} />Edit</a></li>
|
||||||
</DropdownTrigger>
|
}
|
||||||
<DropdownMenu>
|
<li><a onClick={() => onOpenCloseDrawer(FormMode.VIEW, info.row.original.id)}><FontAwesomeIcon icon={faEye} />View</a></li>
|
||||||
<DropdownSection showDivider>
|
{isUserLoggedIn && userRole === UserRole.WRITE &&
|
||||||
<DropdownItem
|
<li><a onClick={() => onDeleteLog(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
|
||||||
key='edit'
|
}
|
||||||
onPress={() => onOpenCloseDrawer(FormMode.EDIT, info.row.original.id)}
|
</ul>
|
||||||
startContent={<FontAwesomeIcon icon={faPen} />}
|
</div>
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
key='view'
|
|
||||||
onPress={() => onOpenCloseDrawer(FormMode.VIEW, info.row.original.id)}
|
|
||||||
startContent={<FontAwesomeIcon icon={faEye} />}
|
|
||||||
>
|
|
||||||
View
|
|
||||||
</DropdownItem>
|
|
||||||
</DropdownSection>
|
|
||||||
<DropdownSection>
|
|
||||||
<DropdownItem
|
|
||||||
key='Delete'
|
|
||||||
onPress={() => onDeleteLog(info.row.original.id)}
|
|
||||||
startContent={<FontAwesomeIcon icon={faTrash} />}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</DropdownItem>
|
|
||||||
</DropdownSection>
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,8 +157,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
accessorKey: 'singleEngineLand',
|
accessorKey: 'singleEngineLand',
|
||||||
header: 'Single Engine Land',
|
header: 'Single Engine Land',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
||||||
@@ -184,7 +168,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
id: 'landings',
|
id: 'landings',
|
||||||
header: 'Landings',
|
header: 'Landings',
|
||||||
meta: {
|
meta: {
|
||||||
headerAlign: 'center'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-center'
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -193,8 +178,9 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Day',
|
header: 'Day',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -203,8 +189,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Night',
|
header: 'Night',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -213,7 +199,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
id: 'instrument',
|
id: 'instrument',
|
||||||
header: 'Instrument',
|
header: 'Instrument',
|
||||||
meta: {
|
meta: {
|
||||||
headerAlign: 'center'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-center'
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -222,8 +209,9 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Actual',
|
header: 'Actual',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
||||||
@@ -234,8 +222,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Simulated',
|
header: 'Simulated',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -246,8 +234,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Approaches',
|
header: 'Approaches',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -256,8 +244,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Holds',
|
header: 'Holds',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -266,8 +254,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Nav/Track',
|
header: 'Nav/Track',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -276,7 +264,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
id: 'experienceTraining',
|
id: 'experienceTraining',
|
||||||
header: 'Type of pilot experience or training',
|
header: 'Type of pilot experience or training',
|
||||||
meta: {
|
meta: {
|
||||||
headerAlign: 'center'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-center'
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -285,8 +274,9 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Ground Training Received',
|
header: 'Ground Training Received',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
className: 'border-l border-base-300',
|
||||||
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -297,8 +287,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Flight Training Received',
|
header: 'Flight Training Received',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -309,8 +299,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Cross Country',
|
header: 'Cross Country',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -321,8 +311,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Night',
|
header: 'Night',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -333,8 +323,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Solo',
|
header: 'Solo',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -345,8 +335,8 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
header: 'Pilot In Command',
|
header: 'Pilot In Command',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -372,14 +362,14 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
dispatch({ type: 'SET_ALERT', payload: undefined})
|
dispatch({ type: 'SET_ALERT', payload: undefined})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dispatch({ type: 'SET_ALERT', payload: { severity: 'default', message: 'No logbook entries found.'}})
|
dispatch({ type: 'SET_ALERT', payload: { severity: 'info', message: 'No logbook entries found.'}})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const axiosError = error as AxiosError;
|
const axiosError = error as AxiosError;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_ALERT',
|
type: 'SET_ALERT',
|
||||||
payload: { severity: 'danger', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`}
|
payload: { severity: 'error', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`}
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
dispatch({ type: 'SET_IS_LOADING', payload: false });
|
dispatch({ type: 'SET_IS_LOADING', payload: false });
|
||||||
@@ -437,7 +427,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_ALERT',
|
type: 'SET_ALERT',
|
||||||
payload: { severity: 'danger', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`}
|
payload: { severity: 'error', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`}
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: false });
|
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: false });
|
||||||
@@ -503,44 +493,52 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className='col-span-2 justify-self-end self-center'>
|
<div className='col-span-2 justify-self-end self-center'>
|
||||||
{userRole === UserRole.WRITE &&
|
{userRole === UserRole.WRITE &&
|
||||||
<Button
|
// <Button
|
||||||
color='primary'
|
// color='primary'
|
||||||
onPress={() => onOpenCloseDrawer(FormMode.ADD)}
|
// onPress={() => onOpenCloseDrawer(FormMode.ADD)}
|
||||||
startContent={<FontAwesomeIcon icon={faAdd} />}
|
// startContent={<FontAwesomeIcon icon={faAdd} />}
|
||||||
data-testid="pilot-add-button"
|
// data-testid="pilot-add-button"
|
||||||
|
// >
|
||||||
|
// Add Entry
|
||||||
|
// </Button>
|
||||||
|
<button className='btn btn-primary'
|
||||||
|
onClick={() => onOpenCloseDrawer(FormMode.ADD)}
|
||||||
>
|
>
|
||||||
|
<FontAwesomeIcon icon={faAdd} />
|
||||||
Add Entry
|
Add Entry
|
||||||
</Button>
|
</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{!state.isLoading && state.alert && (
|
{!state.isLoading && state.alert && (
|
||||||
<div className='col-span-12 mb-5'>
|
<div className='col-span-12 mb-5'>
|
||||||
<Alert
|
<Alert
|
||||||
|
className='mb-5'
|
||||||
onClose={() =>
|
onClose={() =>
|
||||||
dispatch({ type: 'SET_ALERT', payload: undefined })
|
dispatch({ type: 'SET_ALERT', payload: undefined })
|
||||||
}
|
}
|
||||||
color={'default'}
|
severity={'info'}
|
||||||
title={state.alert.message}
|
>
|
||||||
/>
|
{state.alert.message}
|
||||||
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!state.isLoading && (
|
{!state.isLoading && (
|
||||||
<div className='col-span-12'>
|
<div className='col-span-12 p-5 bg-base-100 border border-base-100 rounded-lg'>
|
||||||
{state.entries.length > 0 && screenSize !== ScreenSize.SM && (
|
{state.entries.length > 0 && screenSize !== ScreenSize.SM && (
|
||||||
<div className='p-4 z-0 flex flex-col relative justify-between gap-4 bg-content1 overflow-auto shadow-small rounded-large w-full'>
|
<div className='overflow-x-auto'>
|
||||||
<table className='min-w-full h-auto table-auto w-full'>
|
<table className='table min-w-full h-auto table-auto w-full'>
|
||||||
<thead className='[&>tr]:first:rounded-lg'>
|
<thead className='bg-base-200'>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup, headerGroupIndex) => (
|
||||||
<tr className='group/tr outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2' key={headerGroup.id}>
|
<tr className='group/tr outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2' key={headerGroup.id}>
|
||||||
{headerGroup.headers.map((header) => {
|
{headerGroup.headers.map((header, headerIndex) => {
|
||||||
return (
|
return (
|
||||||
<th
|
<th
|
||||||
className={`${header.column.columnDef.meta?.align ? textAlignment[header.column.columnDef.meta?.align] : ''} group/th px-3 h-10 align-middle bg-default-100 whitespace-nowrap text-foreground-500 text-tiny font-semibold first:rounded-s-lg last:rounded-e-lg data-[sortable=true]:cursor-pointer data-[hover=true]:text-foreground-400 outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 text-start`}
|
className={`${header.column.columnDef.meta?.className ? header.column.columnDef.meta?.className : ''} group/th px-3 h-10 align-middle whitespace-nowrap text-foreground-500 text-tiny font-semibold ${headerGroupIndex === 0 ? 'first:rounded-tl-lg last:rounded-tr-lg' : ''} ${headerGroupIndex === table.getHeaderGroups().length - 1 ? 'first:rounded-bl-lg last:rounded-br-lg' : ''} data-[sortable=true]:cursor-pointer data-[hover=true]:text-foreground-400 outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 text-start`}
|
||||||
colSpan={header.colSpan}
|
colSpan={header.colSpan}
|
||||||
key={header.id}
|
key={header.id}
|
||||||
>
|
>
|
||||||
{header.isPlaceholder ? null : (
|
{header.isPlaceholder ? null : (
|
||||||
<div>
|
<div className={`${header.column.columnDef.meta?.headerAlign ? header.column.columnDef.meta?.headerAlign : ''}`}>
|
||||||
{flexRender(
|
{flexRender(
|
||||||
header.column.columnDef.header,
|
header.column.columnDef.header,
|
||||||
header.getContext()
|
header.getContext()
|
||||||
@@ -566,13 +564,15 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
{row.getVisibleCells().map((cell) => {
|
{row.getVisibleCells().map((cell) => {
|
||||||
return (
|
return (
|
||||||
<td
|
<td
|
||||||
className={`${cell.column.columnDef.meta?.align ? textAlignment[cell.column.columnDef.meta?.align] : ''} py-2 px-3 relative align-middle whitespace-normal text-small font-normal [&>*]:z-1 [&>*]:relative outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 before:pointer-events-none before:content-[''] before:absolute before:z-0 before:inset-0 before:opacity-0 data-[selected=true]:before:opacity-100 group-data-[disabled=true]/tr:text-foreground-300 group-data-[disabled=true]/tr:cursor-not-allowed before:bg-default/60 data-[selected=true]:text-default-foreground first:before:rounded-s-lg last:before:rounded-e-lg text-start`}
|
className={`py-2 px-3 relative align-middle whitespace-normal text-small font-normal [&>*]:z-1 [&>*]:relative outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 before:pointer-events-none before:content-[''] before:absolute before:z-0 before:inset-0 before:opacity-0 data-[selected=true]:before:opacity-100 group-data-[disabled=true]/tr:text-foreground-300 group-data-[disabled=true]/tr:cursor-not-allowed before:bg-default/60 data-[selected=true]:text-default-foreground first:before:rounded-s-lg last:before:rounded-e-lg text-start`}
|
||||||
key={cell.id}
|
key={cell.id}
|
||||||
>
|
>
|
||||||
|
<div className={`${cell.column.columnDef.meta?.align ? cell.column.columnDef.meta?.align : ''}`}>
|
||||||
{flexRender(
|
{flexRender(
|
||||||
cell.column.columnDef.cell,
|
cell.column.columnDef.cell,
|
||||||
cell.getContext()
|
cell.getContext()
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -581,7 +581,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
</tbody>
|
</tbody>
|
||||||
<thead className='[&>tr]:first:rounded-lg'>
|
<thead className='[&>tr]:first:rounded-lg bg-base-200'>
|
||||||
{table.getFooterGroups().map((footerGroup, index) => {
|
{table.getFooterGroups().map((footerGroup, index) => {
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
return (
|
return (
|
||||||
@@ -591,14 +591,16 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
<td
|
<td
|
||||||
className='group/th px-3 h-10 align-middle bg-default-100 whitespace-nowrap text-foreground-500 text-tiny font-semibold first:rounded-s-lg last:rounded-e-lg data-[sortable=true]:cursor-pointer data-[hover=true]:text-foreground-400 outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 text-start'
|
className='group/th px-3 h-10 align-middle bg-default-100 whitespace-nowrap text-foreground-500 text-tiny font-semibold first:rounded-s-lg last:rounded-e-lg data-[sortable=true]:cursor-pointer data-[hover=true]:text-foreground-400 outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 text-start'
|
||||||
key={header.id}
|
key={header.id}
|
||||||
align={header.column.columnDef.meta?.align}
|
|
||||||
>
|
>
|
||||||
|
<div className={`${header.column.columnDef.meta?.headerAlign ? header.column.columnDef.meta?.headerAlign : ''}`}>
|
||||||
{header.isPlaceholder
|
{header.isPlaceholder
|
||||||
? null
|
? null
|
||||||
: flexRender(
|
: flexRender(
|
||||||
header.column.columnDef.footer,
|
header.column.columnDef.footer,
|
||||||
header.getContext()
|
header.getContext()
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -641,14 +643,6 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
title="Confirm Delete"
|
title="Confirm Delete"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {state.isTracksOpen &&
|
|
||||||
<LogTracks
|
|
||||||
isDrawerOpen={state.isTracksOpen}
|
|
||||||
mode={state.tracksMode}
|
|
||||||
onOpenClose={(mode) => onOpenCloseTracks(mode)}
|
|
||||||
selectedLogId={logbookContext.state.selectedLogId}
|
|
||||||
/>
|
|
||||||
} */}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ const route: ColumnDef<LogbookEntry> = {
|
|||||||
id: 'route',
|
id: 'route',
|
||||||
header: 'Route of Flight',
|
header: 'Route of Flight',
|
||||||
meta: {
|
meta: {
|
||||||
headerAlign: 'center'
|
headerAlign: 'text-center',
|
||||||
|
className: 'border-l border-base-400'
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -68,8 +69,8 @@ const durationOfFlight: ColumnDef<LogbookEntry> = {
|
|||||||
accessorKey: 'durationOfFlight',
|
accessorKey: 'durationOfFlight',
|
||||||
header: 'Duration Of Flight',
|
header: 'Duration Of Flight',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
||||||
@@ -106,8 +107,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
accessorKey: 'singleEngineLand',
|
accessorKey: 'singleEngineLand',
|
||||||
header: 'Single Engine Land',
|
header: 'Single Engine Land',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
||||||
@@ -117,7 +118,7 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
id: 'landings',
|
id: 'landings',
|
||||||
header: 'Landings',
|
header: 'Landings',
|
||||||
meta: {
|
meta: {
|
||||||
headerAlign: 'center'
|
headerAlign: 'text-center'
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -126,8 +127,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Day',
|
header: 'Day',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -136,8 +137,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Night',
|
header: 'Night',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -146,7 +147,7 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
id: 'instrument',
|
id: 'instrument',
|
||||||
header: 'Instrument',
|
header: 'Instrument',
|
||||||
meta: {
|
meta: {
|
||||||
headerAlign: 'center'
|
headerAlign: 'text-center'
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -155,8 +156,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Actual',
|
header: 'Actual',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
|
||||||
@@ -167,8 +168,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Simulated',
|
header: 'Simulated',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -179,8 +180,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Approaches',
|
header: 'Approaches',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -189,8 +190,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Holds',
|
header: 'Holds',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -199,8 +200,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Nav/Track',
|
header: 'Nav/Track',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -209,7 +210,7 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
id: 'experienceTraining',
|
id: 'experienceTraining',
|
||||||
header: 'Type of pilot experience or training',
|
header: 'Type of pilot experience or training',
|
||||||
meta: {
|
meta: {
|
||||||
headerAlign: 'center'
|
headerAlign: 'text-center'
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -218,8 +219,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Ground Training Received',
|
header: 'Ground Training Received',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -230,8 +231,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Flight Training Received',
|
header: 'Flight Training Received',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -242,8 +243,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Cross Country',
|
header: 'Cross Country',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -254,8 +255,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Night',
|
header: 'Night',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -266,8 +267,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Solo',
|
header: 'Solo',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
@@ -278,8 +279,8 @@ export const authColumns: ColumnDef<LogbookEntry>[] = [
|
|||||||
header: 'Pilot In Command',
|
header: 'Pilot In Command',
|
||||||
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'text-right',
|
||||||
headerAlign: 'right'
|
headerAlign: 'text-right'
|
||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
cell: (info: CellContext<LogbookEntry, unknown>) =>
|
||||||
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Accordion, AccordionItem, Card, CardBody, CardHeader } from '@heroui/react'
|
|
||||||
import { LogbookCardProps } from "./LogbookCardProps.interface";
|
import { LogbookCardProps } from "./LogbookCardProps.interface";
|
||||||
import TrackMap from "../trackMap/TrackMap";
|
import TrackMap from "../trackMap/TrackMap";
|
||||||
|
|
||||||
@@ -10,12 +9,10 @@ const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps
|
|||||||
const formattedDate: string = `${date.getMonth()}/${date.getDate()}/${date.getFullYear()}`;
|
const formattedDate: string = `${date.getMonth()}/${date.getDate()}/${date.getFullYear()}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className='card bg-base-100 border border-base-300 p-2'>
|
||||||
|
<div className='card-body' key={log.id}>
|
||||||
|
<h2 className='card-title font-bold text-2xl'>{formattedDate}</h2>
|
||||||
<div>
|
<div>
|
||||||
<Card className='p-4' key={log.id}>
|
|
||||||
<CardHeader>
|
|
||||||
<h2 className='font-bold text-2xl'>{formattedDate}</h2>
|
|
||||||
</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
{mode === 'flights' && log.tracks && log.tracks.length > 0 &&
|
{mode === 'flights' && log.tracks && log.tracks.length > 0 &&
|
||||||
<div className='mb-5'>
|
<div className='mb-5'>
|
||||||
<TrackMap
|
<TrackMap
|
||||||
@@ -25,8 +22,11 @@ const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<Accordion variant='bordered'>
|
</div>
|
||||||
<AccordionItem key='1' title='Details'>
|
<div className='collapse collapse-arrow bg-base-100 border-base-300 border'>
|
||||||
|
<input type='radio' name='details-accordion' />
|
||||||
|
<div className='collapse-title font-semibold'>Details</div>
|
||||||
|
<div className='collapse-content'>
|
||||||
<div className='grid grid-cols-12 gap-3 mr-[30%] ml-[30%] mt-4 mb-4'>
|
<div className='grid grid-cols-12 gap-3 mr-[30%] ml-[30%] mt-4 mb-4'>
|
||||||
<div className='col-span-6 font-bold'>
|
<div className='col-span-6 font-bold'>
|
||||||
<span>Aircraft Make and Model</span>
|
<span>Aircraft Make and Model</span>
|
||||||
@@ -63,11 +63,9 @@ const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</AccordionItem>
|
</div>
|
||||||
</Accordion>
|
</div>
|
||||||
|
</div>
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Alert, Button, Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader, Tab, Tabs } from "@heroui/react";
|
|
||||||
import { LogbookDrawerProps } from "./LogbookDrawerProps.interface";
|
import { LogbookDrawerProps } from "./LogbookDrawerProps.interface";
|
||||||
import LogForm from "../logForm/LogForm";
|
import LogForm from "../logForm/LogForm";
|
||||||
import TracksForm from "../tracksForm/TracksForm";
|
import TracksForm from "../tracksForm/TracksForm";
|
||||||
|
import Alert from '../alert/Alert';
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faClock, faMapLocationDot, faSave, faXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faClock, faMapLocationDot, faSave, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
@@ -9,10 +9,10 @@ import { FormMode } from "../../enums/formMode";
|
|||||||
import httpClient from "../../httpClient/httpClient";
|
import httpClient from "../../httpClient/httpClient";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { useLogbookContext } from "../../hooks/logbookContext/UseLogbookContext";
|
import { useLogbookContext } from "../../hooks/logbookContext/UseLogbookContext";
|
||||||
import { Key, useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => {
|
const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => {
|
||||||
const [activeTab, setActiveTab] = useState<Key>('time');
|
const [activeTab, setActiveTab] = useState<string>('time');
|
||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
pilotId: '',
|
pilotId: '',
|
||||||
date: null,
|
date: null,
|
||||||
@@ -63,111 +63,107 @@ const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
const axiosError = error as AxiosError;
|
const axiosError = error as AxiosError;
|
||||||
|
|
||||||
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: { severity: 'danger', message: axiosError.message }});
|
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: { severity: 'error', message: axiosError.message }});
|
||||||
} finally {
|
} finally {
|
||||||
logbookContext.dispatch({ type: 'SET_IS_FORM_LOADING', payload: false });
|
logbookContext.dispatch({ type: 'SET_IS_FORM_LOADING', payload: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSelectedKeyChanged = (key: React.Key) => {
|
const onSelectedKeyChanged = (tab: string) => {
|
||||||
setActiveTab(key)
|
setActiveTab(tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<div className='drawer drawer-end'
|
||||||
closeButton={
|
// closeButton={
|
||||||
<Button isIconOnly>
|
// <Button isIconOnly>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
// <FontAwesomeIcon icon={faXmark} />
|
||||||
</Button>
|
// </Button>
|
||||||
}
|
// }
|
||||||
isOpen={logbookContext.state.isDrawerOpen}
|
// isOpen={logbookContext.state.isDrawerOpen}
|
||||||
onClose={onCancel}
|
// onClose={onCancel}
|
||||||
>
|
>
|
||||||
<DrawerContent>
|
<input type='checkbox' className='drawer-toggle' onChange={() => {}} checked={logbookContext.state.isDrawerOpen} />
|
||||||
|
<div className="drawer-side">
|
||||||
|
<label
|
||||||
|
htmlFor='my-drawer-1'
|
||||||
|
aria-label='close-sidebar'
|
||||||
|
className='drawer-overlay'
|
||||||
|
></label>
|
||||||
|
<div className='menu bg-base-100 text-base-content min-h-full p-4' style={{ width: '25%' }}>
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<form className='prose max-w-none' onSubmit={methods.handleSubmit(onSubmit)} style={{ paddingBottom: '50px' }}>
|
<form className='prose max-w-none' onSubmit={methods.handleSubmit(onSubmit)} style={{ paddingBottom: '50px' }}>
|
||||||
<DrawerHeader>
|
<h2>
|
||||||
{`${logbookContext.state.formMode.toString().toLowerCase().charAt(0).toUpperCase() + logbookContext.state.formMode.toString().slice(1).toLowerCase()} Entry`}
|
{`${logbookContext.state.formMode.toString().toLowerCase().charAt(0).toUpperCase() + logbookContext.state.formMode.toString().slice(1).toLowerCase()} Entry`}
|
||||||
</DrawerHeader>
|
</h2>
|
||||||
<DrawerBody>
|
|
||||||
{logbookContext.state.formAlert && (
|
{logbookContext.state.formAlert && (
|
||||||
<div className='col-span-12'>
|
|
||||||
<Alert
|
<Alert
|
||||||
|
className='mb-5'
|
||||||
onClose={() =>
|
onClose={() =>
|
||||||
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: undefined })
|
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: undefined })
|
||||||
}
|
}
|
||||||
color={logbookContext.state.formAlert.severity}
|
severity={logbookContext.state.formAlert.severity}
|
||||||
title={logbookContext.state.formAlert.message}
|
>
|
||||||
/>
|
{logbookContext.state.formAlert.message}
|
||||||
</div>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<Tabs
|
<div className='tabs tabs-lift mb-5'>
|
||||||
color='default'
|
<label className='tab'>
|
||||||
fullWidth={true}
|
<input type='radio' name='my_tabs' defaultChecked={true} />
|
||||||
onSelectionChange={onSelectedKeyChanged}
|
<FontAwesomeIcon className='mr-1'icon={faClock} />
|
||||||
selectedKey={activeTab as string}
|
Time
|
||||||
variant='solid'
|
</label>
|
||||||
>
|
<div className='tab-content border-base-300 p-6'>
|
||||||
<Tab
|
|
||||||
key='time'
|
|
||||||
title={
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<FontAwesomeIcon icon={faClock} />
|
|
||||||
<span>Time</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<LogForm />
|
<LogForm />
|
||||||
</Tab>
|
|
||||||
{logbookContext.state.formMode !== FormMode.ADD &&
|
|
||||||
<Tab
|
|
||||||
key='tracks'
|
|
||||||
title={
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<FontAwesomeIcon icon={faMapLocationDot} />
|
|
||||||
<span>Tracks</span>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
{logbookContext.state.formMode !== FormMode.ADD &&
|
||||||
>
|
<>
|
||||||
|
<label className='tab'>
|
||||||
|
<input type='radio' name='my_tabs'/>
|
||||||
|
<FontAwesomeIcon className='mr-1' icon={faMapLocationDot} />
|
||||||
|
Tracks
|
||||||
|
</label>
|
||||||
|
<div className='tab-content border-base-300 p-6'>
|
||||||
<TracksForm />
|
<TracksForm />
|
||||||
</Tab>
|
</div>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
</Tabs>
|
</div>
|
||||||
</DrawerBody>
|
|
||||||
{activeTab !== 'tracks' &&
|
{activeTab !== 'tracks' &&
|
||||||
<DrawerFooter>
|
<div>
|
||||||
<div className='grid grid-cols-12 gap-3'>
|
<div className='grid grid-cols-12 gap-3'>
|
||||||
<div className='col-span-12 justify-self-end self-center'>
|
<div className='col-span-12 justify-self-end self-center'>
|
||||||
<Button
|
<button
|
||||||
|
className='btn'
|
||||||
disabled={
|
disabled={
|
||||||
logbookContext.state.isFormDisabled && logbookContext.state.formMode.toString() !== FormMode.VIEW
|
logbookContext.state.isFormDisabled && logbookContext.state.formMode.toString() !== FormMode.VIEW
|
||||||
? logbookContext.state.isFormDisabled
|
? logbookContext.state.isFormDisabled
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
startContent={<FontAwesomeIcon icon={faXmark} />}
|
onClick={onCancel}
|
||||||
onPress={onCancel}
|
|
||||||
>
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
{logbookContext.state.formMode.toString() !== FormMode.VIEW ? 'Cancel' : 'Close'}
|
{logbookContext.state.formMode.toString() !== FormMode.VIEW ? 'Cancel' : 'Close'}
|
||||||
</Button>
|
</button>
|
||||||
{logbookContext.state.formMode.toString() !== FormMode.VIEW && (
|
{logbookContext.state.formMode.toString() !== FormMode.VIEW && (
|
||||||
<Button
|
<button
|
||||||
className='ml-[10px]'
|
className='btn btn-primary ml-2.5'
|
||||||
color='primary'
|
|
||||||
disabled={logbookContext.state.isFormDisabled}
|
disabled={logbookContext.state.isFormDisabled}
|
||||||
startContent={<FontAwesomeIcon icon={faSave} />}
|
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
|
<FontAwesomeIcon icon={faSave} />
|
||||||
Save
|
Save
|
||||||
</Button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DrawerFooter>
|
</div>
|
||||||
}
|
}
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</DrawerContent>
|
</div>
|
||||||
</Drawer>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Key, useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useForm, Controller, FormProvider } from 'react-hook-form';
|
import { useForm, Controller, FormProvider } from 'react-hook-form';
|
||||||
import { IPilotFormProps } from './IPilotFormProps';
|
import { IPilotFormProps } from './IPilotFormProps';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
@@ -9,7 +9,6 @@ import PilotFormEndorsements from '../pilotFormEndorsements/PilotFormEndorsement
|
|||||||
import PilotFormMedical from '../pilotFormMedical/PilotFormMedical';
|
import PilotFormMedical from '../pilotFormMedical/PilotFormMedical';
|
||||||
import { useOidc } from '../../auth/oidcConfig';
|
import { useOidc } from '../../auth/oidcConfig';
|
||||||
import httpClient from '../../httpClient/httpClient';
|
import httpClient from '../../httpClient/httpClient';
|
||||||
import { Button, Drawer, DrawerHeader, DrawerContent, DrawerBody, DrawerFooter, Input, Autocomplete, AutocompleteItem, SelectItem, Select } from '@heroui/react'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faSave, faXmark } from '@fortawesome/free-solid-svg-icons';
|
import { faSave, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { states } from './states';
|
import { states } from './states';
|
||||||
@@ -45,39 +44,6 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
||||||
const [isError, setIsError] = useState<boolean>(false);
|
const [isError, setIsError] = useState<boolean>(false);
|
||||||
|
|
||||||
const onPeoplePickerSearch = async (
|
|
||||||
value: string
|
|
||||||
) => {
|
|
||||||
setIsPeoplePickerLoading(true);
|
|
||||||
setPeoplePickerValue(value)
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (value !== '') {
|
|
||||||
const searchString: string = value;
|
|
||||||
const response: AxiosResponse = await httpClient.get(
|
|
||||||
`api/msgraph/search?search=${searchString}`
|
|
||||||
);
|
|
||||||
|
|
||||||
setPeoplePickerResults(response.data);
|
|
||||||
} else {
|
|
||||||
setPeoplePickerResults([]);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
setIsPeoplePickerLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPersonSelected = (userPrincipalName: string) => {
|
|
||||||
const person: Person | undefined = peoplePickerResults.find((person) => person.userPrincipalName === userPrincipalName as string);
|
|
||||||
|
|
||||||
methods.setValue('name', person?.displayName!);
|
|
||||||
methods.setValue('userId', person?.userPrincipalName!);
|
|
||||||
setPeoplePickerValue(person?.displayName!);
|
|
||||||
setPeoplePickerResults([])
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
methods.reset(defaultValues);
|
methods.reset(defaultValues);
|
||||||
onOpenClose(FormMode.CANCEL);
|
onOpenClose(FormMode.CANCEL);
|
||||||
@@ -138,43 +104,50 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
}, [pilotId]);
|
}, [pilotId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<div className='drawer drawer-end'
|
||||||
closeButton={
|
// closeButton={
|
||||||
<Button isIconOnly>
|
// <Button isIconOnly>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
// <FontAwesomeIcon icon={faXmark} />
|
||||||
</Button>
|
// </Button>
|
||||||
}
|
// }
|
||||||
isOpen={isDrawerOpen}
|
// isOpen={isDrawerOpen}
|
||||||
placement='right'
|
// placement='right'
|
||||||
data-testid="pilot-drawer"
|
// data-testid="pilot-drawer"
|
||||||
onClose={onCancel}
|
// onClose={onCancel}
|
||||||
size='xl'
|
// size='xl'
|
||||||
>
|
>
|
||||||
<DrawerContent>
|
<input type='checkbox' className='drawer-toggle' onChange={() => {}} checked={isDrawerOpen} />
|
||||||
|
<div className='drawer-side'>
|
||||||
|
<label
|
||||||
|
htmlFor='my-drawer-1'
|
||||||
|
aria-label='close-sidebar'
|
||||||
|
className='drawer-overlay'
|
||||||
|
></label>
|
||||||
|
<div className='menu bg-base-100 text-base-content min-h-full p-4' style={{ width: '25%' }}>
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<form className='prose max-w-none' onSubmit={methods.handleSubmit(onSubmit)} style={{ paddingBottom: '50px' }}>
|
<form className='prose max-w-none' onSubmit={methods.handleSubmit(onSubmit)} style={{ paddingBottom: '50px' }}>
|
||||||
<DrawerHeader>
|
<h2>
|
||||||
{`${mode.toString().toLowerCase().charAt(0).toUpperCase() + mode.toString().slice(1).toLowerCase()} Pilot`}
|
{`${mode.toString().toLowerCase().charAt(0).toUpperCase() + mode.toString().slice(1).toLowerCase()} Pilot`}
|
||||||
</DrawerHeader>
|
</h2>
|
||||||
<DrawerBody>
|
|
||||||
<div className='grid grid-cols-12 gap-3'>
|
<div className='grid grid-cols-12 gap-3'>
|
||||||
<div className='col-span-3 self-center'>
|
<div className='col-span-3 self-center'>
|
||||||
<h6>Name *</h6>
|
<span>Name *</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='col-span-9'>
|
<div className='col-span-9'>
|
||||||
<Autocomplete
|
<Controller
|
||||||
inputValue={peoplePickerValue}
|
name="name"
|
||||||
isLoading={isPeoplePickerLoading}
|
control={methods.control}
|
||||||
items={peoplePickerResults}
|
rules={{ required: 'A name is required' }}
|
||||||
onInputChange={onPeoplePickerSearch}
|
render={({ field: { onChange, value } }) => (
|
||||||
onSelectionChange={(key: Key | null) => onPersonSelected(key as string)}
|
<input
|
||||||
>
|
type='text'
|
||||||
{peoplePickerResults.map((person: Person) => (
|
className='input w-full'
|
||||||
<AutocompleteItem key={person.userPrincipalName}>
|
disabled={isDisabled}
|
||||||
{person.displayName}
|
onChange={onChange}
|
||||||
</AutocompleteItem>
|
value={value}
|
||||||
))}
|
/>
|
||||||
</Autocomplete>
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{isUserLoggedIn &&
|
{isUserLoggedIn &&
|
||||||
<>
|
<>
|
||||||
@@ -187,15 +160,10 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: 'An address is required' }}
|
rules={{ required: 'An address is required' }}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
|
type='text'
|
||||||
|
className='input w-full'
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
color={methods.formState.errors.address ? 'danger' : undefined}
|
|
||||||
errorMessage={
|
|
||||||
methods.formState.errors.address
|
|
||||||
? methods.formState.errors.address.message
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
fullWidth={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
@@ -211,15 +179,10 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: 'A city is required' }}
|
rules={{ required: 'A city is required' }}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
|
type='text'
|
||||||
|
className='input w-full'
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
color={methods.formState.errors.city ? 'danger' : undefined}
|
|
||||||
errorMessage={
|
|
||||||
methods.formState.errors.city
|
|
||||||
? methods.formState.errors.city.message
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
fullWidth={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
@@ -235,14 +198,15 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: 'A state must be selected' }}
|
rules={{ required: 'A state must be selected' }}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Select
|
<select
|
||||||
|
className='select w-full'
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
selectedKeys={[value]}
|
value={[value]}
|
||||||
>
|
>
|
||||||
{states.map((state) => (
|
{states.map((state) => (
|
||||||
<SelectItem key={state.value}>{state.label}</SelectItem>
|
<option key={state.value}>{state.label}</option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</select>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -255,15 +219,10 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: 'A postal code is required' }}
|
rules={{ required: 'A postal code is required' }}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
|
type='text'
|
||||||
|
className='input w-full'
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
color={methods.formState.errors.postalCode ? 'danger' : undefined}
|
|
||||||
errorMessage={
|
|
||||||
methods.formState.errors.postalCode
|
|
||||||
? methods.formState.errors.postalCode.message
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
fullWidth={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
@@ -284,15 +243,10 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
|
type='text'
|
||||||
|
className='input w-full'
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
color={methods.formState.errors.email ? 'danger' : undefined}
|
|
||||||
errorMessage={
|
|
||||||
methods.formState.errors.email
|
|
||||||
? methods.formState.errors.email.message
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
fullWidth={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
@@ -313,15 +267,10 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<input
|
||||||
|
type='text'
|
||||||
|
className='input w-full'
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
color={methods.formState.errors.phone ? 'danger' : undefined}
|
|
||||||
errorMessage={
|
|
||||||
methods.formState.errors.phone
|
|
||||||
? methods.formState.errors.phone.message
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
fullWidth={true}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
@@ -345,38 +294,39 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
</Grid> */}
|
</Grid> */}
|
||||||
{/* <div className='col-span-12 justify-self-end self-center'> */}
|
{/* <div className='col-span-12 justify-self-end self-center'> */}
|
||||||
|
|
||||||
{/* </div> */}
|
|
||||||
</div>
|
<div className='col-span-12 justify-self-end self-center'>
|
||||||
</DrawerBody>
|
<button
|
||||||
<DrawerFooter>
|
className='btn'
|
||||||
<Button
|
|
||||||
disabled={
|
disabled={
|
||||||
isDisabled && mode.toString() !== FormMode.VIEW
|
isDisabled && mode.toString() !== FormMode.VIEW
|
||||||
? isDisabled
|
? isDisabled
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
startContent={<FontAwesomeIcon icon={faXmark} />}
|
onClick={onCancel}
|
||||||
onPress={onCancel}
|
|
||||||
data-testid="pilot-cancel-button"
|
data-testid="pilot-cancel-button"
|
||||||
>
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
{mode.toString() !== FormMode.VIEW ? 'Cancel' : 'Close'}
|
{mode.toString() !== FormMode.VIEW ? 'Cancel' : 'Close'}
|
||||||
</Button>
|
</button>
|
||||||
{mode.toString() !== FormMode.VIEW && (
|
{mode.toString() !== FormMode.VIEW && (
|
||||||
<Button
|
<button
|
||||||
color='primary'
|
className='btn btn-primary ml-2.5'
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
startContent={<FontAwesomeIcon icon={faSave} />}
|
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="pilot-save-button"
|
data-testid="pilot-save-button"
|
||||||
>
|
>
|
||||||
|
<FontAwesomeIcon icon={faSave} />
|
||||||
Save
|
Save
|
||||||
</Button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</DrawerFooter>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</DrawerContent>
|
</div>
|
||||||
</Drawer>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,18 @@ import { UserRole } from '../../enums/userRole';
|
|||||||
import httpClient from '../../httpClient/httpClient'
|
import httpClient from '../../httpClient/httpClient'
|
||||||
import { ScreenSize } from '../../enums/screenSize';
|
import { ScreenSize } from '../../enums/screenSize';
|
||||||
import { useBreakpoints } from '../../hooks/useBreakpoints/UseBreakpoints';
|
import { useBreakpoints } from '../../hooks/useBreakpoints/UseBreakpoints';
|
||||||
import { Alert, Button, Dropdown, DropdownItem, DropdownSection, Table, TableHeader, TableBody, TableColumn, TableRow, TableCell, DropdownTrigger, DropdownMenu } from '@heroui/react'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faEllipsisVertical, faPen, faEye, faTrash, faPlus } from '@fortawesome/free-solid-svg-icons'
|
import { faEllipsisVertical, faPen, faEye, faTrash, faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, useReactTable } from '@tanstack/react-table';
|
||||||
|
import { Pilot } from './Pilot.interface';
|
||||||
|
import Alert from '../alert/Alert';
|
||||||
|
import { useOidc } from '../../auth/oidcConfig';
|
||||||
|
|
||||||
const Pilots: React.FC<unknown> = () => {
|
const Pilots: React.FC<unknown> = () => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
const { userRole } = useUserRole();
|
const { userRole } = useUserRole();
|
||||||
const { screenSize } = useBreakpoints()
|
const { screenSize } = useBreakpoints();
|
||||||
|
const { isUserLoggedIn } = useOidc();
|
||||||
|
|
||||||
const getPilots = async () => {
|
const getPilots = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -27,6 +31,8 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
`api/pilots`
|
`api/pilots`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(response)
|
||||||
|
|
||||||
if (response.data.length > 0) {
|
if (response.data.length > 0) {
|
||||||
dispatch({ type: 'SET_PILOTS', payload: response.data });
|
dispatch({ type: 'SET_PILOTS', payload: response.data });
|
||||||
|
|
||||||
@@ -34,7 +40,7 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
dispatch({ type: 'SET_ALERT', payload: undefined })
|
dispatch({ type: 'SET_ALERT', payload: undefined })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dispatch({ type: 'SET_ALERT', payload: { severity: 'default', message: 'No pilots found.' }})
|
dispatch({ type: 'SET_ALERT', payload: { severity: 'info', message: 'No pilots found.' }})
|
||||||
dispatch({ type: 'SET_PILOTS', payload: [] });
|
dispatch({ type: 'SET_PILOTS', payload: [] });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -42,7 +48,7 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_ALERT',
|
type: 'SET_ALERT',
|
||||||
payload: { severity: 'danger', message: `Loading of pilots failed with the following message: ${axiosError.message}`}
|
payload: { severity: 'error', message: `Loading of pilots failed with the following message: ${axiosError.message}`}
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
dispatch({ type: 'SET_IS_LOADING', payload: false })
|
dispatch({ type: 'SET_IS_LOADING', payload: false })
|
||||||
@@ -101,7 +107,7 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_ALERT',
|
type: 'SET_ALERT',
|
||||||
payload: { severity: 'danger', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`}
|
payload: { severity: 'error', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`}
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: false });
|
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: false });
|
||||||
@@ -115,64 +121,50 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns: ColumnDef<Pilot>[]= [
|
||||||
{
|
{
|
||||||
id: 'name',
|
id: 'name',
|
||||||
name: 'Name'
|
accessorKey: 'name',
|
||||||
|
header: 'Name'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
name: 'Actions'
|
header: 'Actions',
|
||||||
|
meta: {
|
||||||
|
align: 'text-center',
|
||||||
|
headerAlign: 'text-center'
|
||||||
|
},
|
||||||
|
cell: (info: CellContext<Pilot, unknown>) => {
|
||||||
|
return (
|
||||||
|
<div className='dropdown dropdown-end'>
|
||||||
|
<div tabIndex={0} role='button' className='btn btn-ghost'><FontAwesomeIcon icon={faEllipsisVertical} /></div>
|
||||||
|
<ul tabIndex={-1} className="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm border border-base-300">
|
||||||
|
{isUserLoggedIn && userRole === UserRole.WRITE &&
|
||||||
|
<li><a onClick={() => onOpenClosePilotForm(FormMode.EDIT, info.row.original.id)}><FontAwesomeIcon icon={faPen} />Edit</a></li>
|
||||||
|
}
|
||||||
|
<li><a onClick={() => onOpenClosePilotForm(FormMode.VIEW, info.row.original.id)}><FontAwesomeIcon icon={faEye} />View</a></li>
|
||||||
|
{isUserLoggedIn && userRole === UserRole.WRITE &&
|
||||||
|
<li><a onClick={() => onDeletePilot(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const renderCell = (pilot: any, columnKey: any) => {
|
const textAlignment = {
|
||||||
const cellValue = pilot[columnKey]
|
center: 'text-center',
|
||||||
|
left: 'text-start',
|
||||||
|
right: 'text-end'
|
||||||
|
};
|
||||||
|
|
||||||
switch (columnKey) {
|
const table = useReactTable({
|
||||||
case 'actions': {
|
data: state.pilots,
|
||||||
return (
|
columns: columns,
|
||||||
<Dropdown>
|
getCoreRowModel: getCoreRowModel(),
|
||||||
<DropdownTrigger>
|
getPaginationRowModel: getPaginationRowModel()
|
||||||
<Button isIconOnly variant='light' size='lg'>
|
});
|
||||||
<FontAwesomeIcon icon={faEllipsisVertical} />
|
|
||||||
</Button>
|
|
||||||
</DropdownTrigger>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownSection showDivider>
|
|
||||||
<DropdownItem
|
|
||||||
key='edit'
|
|
||||||
onPress={() => onOpenClosePilotForm(FormMode.EDIT, pilot.id)}
|
|
||||||
startContent={<FontAwesomeIcon icon={faPen} />}
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
key='view'
|
|
||||||
onPress={() => onOpenClosePilotForm(FormMode.VIEW, pilot.id)}
|
|
||||||
startContent={<FontAwesomeIcon icon={faEye} />}
|
|
||||||
>
|
|
||||||
View
|
|
||||||
</DropdownItem>
|
|
||||||
</DropdownSection>
|
|
||||||
<DropdownSection>
|
|
||||||
<DropdownItem
|
|
||||||
key='Delete'
|
|
||||||
onPress={() => onDeletePilot(pilot.id)}
|
|
||||||
startContent={<FontAwesomeIcon icon={faTrash} />}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</DropdownItem>
|
|
||||||
</DropdownSection>
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return cellValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!state.isFormOpen) {
|
if (!state.isFormOpen) {
|
||||||
@@ -188,52 +180,85 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className='col-span-2 justify-self-end self-center'>
|
<div className='col-span-2 justify-self-end self-center'>
|
||||||
{userRole === UserRole.WRITE &&
|
{userRole === UserRole.WRITE &&
|
||||||
<Button
|
<button
|
||||||
color='primary'
|
className='btn btn-primary'
|
||||||
onPress={() => onOpenClosePilotForm(FormMode.ADD)}
|
onClick={() => onOpenClosePilotForm(FormMode.ADD)}
|
||||||
startContent={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
data-testid="pilot-add-button"
|
data-testid="pilot-add-button"
|
||||||
>
|
>
|
||||||
|
<FontAwesomeIcon icon={faPlus} />
|
||||||
Add Pilot
|
Add Pilot
|
||||||
</Button>
|
</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{!state.isLoading && state.alert && (
|
{!state.isLoading && state.alert && (
|
||||||
<div className='col-span-12'>
|
<div className='col-span-12'>
|
||||||
<Alert
|
<Alert
|
||||||
|
className='mb-5'
|
||||||
onClose={() =>
|
onClose={() =>
|
||||||
dispatch({ type: 'SET_ALERT', payload: undefined })
|
dispatch({ type: 'SET_ALERT', payload: undefined })
|
||||||
}
|
}
|
||||||
color={state.alert.severity}
|
severity={state.alert.severity}
|
||||||
title={state.alert.message}
|
>
|
||||||
/>
|
{state.alert.message}
|
||||||
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className='col-span-12'>
|
<div className='col-span-12 bg-base-100 p-5 border border-base-100 rounded-lg'>
|
||||||
{state.pilots.length > 0 && screenSize !== ScreenSize.SM &&
|
{state.pilots.length > 0 && screenSize !== ScreenSize.SM &&
|
||||||
<Table>
|
<table className='table min-w-full h-auto table-auto w-full'>
|
||||||
<TableHeader columns={columns}>
|
<thead className='[&>tr]:first:rounded-lg bg-base-200'>
|
||||||
{(column) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<TableColumn
|
<tr className='group/tr outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2' key={headerGroup.id}>
|
||||||
key={column.id}
|
{headerGroup.headers.map((header) => {
|
||||||
align={column.id === "actions" ? "center" : "start"}
|
return (
|
||||||
|
<th
|
||||||
|
className={`${header.column.columnDef.meta?.headerAlign ? header.column.columnDef.meta?.headerAlign : ''} group/th px-3 h-10 align-middle bg-default-100 whitespace-nowrap text-foreground-500 text-tiny font-semibold rounded data-[sortable=true]:cursor-pointer data-[hover=true]:text-foreground-400 outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 text-start`}
|
||||||
|
colSpan={header.colSpan}
|
||||||
|
key={header.id}
|
||||||
>
|
>
|
||||||
{column.name}
|
{header.isPlaceholder ? null : (
|
||||||
</TableColumn>
|
<div>
|
||||||
|
{flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
)}
|
)}
|
||||||
</TableHeader>
|
{/* {header.column.getCanFilter() ? (
|
||||||
<TableBody items={state.pilots}>
|
<div>
|
||||||
{(item) => (
|
<Filter column={header.column} table={table} />
|
||||||
<TableRow key={item.id}>
|
</div>
|
||||||
{(columnKey => (
|
) : null} */}
|
||||||
<TableCell>
|
</div>
|
||||||
{renderCell(item, columnKey)}
|
)}
|
||||||
</TableCell>
|
</th>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<>
|
||||||
|
{table.getRowModel().rows.map((row) => {
|
||||||
|
return (
|
||||||
|
<tr className='group/tr outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2' key={row.id}>
|
||||||
|
{row.getVisibleCells().map((cell) => {
|
||||||
|
return (
|
||||||
|
<td
|
||||||
|
className={`${cell.column.columnDef.meta?.align ? cell.column.columnDef.meta?.align : ''} py-2 px-3 relative align-middle whitespace-normal text-small font-normal [&>*]:z-1 [&>*]:relative outline-solid outline-transparent data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 before:pointer-events-none before:content-[''] before:absolute before:z-0 before:inset-0 before:opacity-0 data-[selected=true]:before:opacity-100 group-data-[disabled=true]/tr:text-foreground-300 group-data-[disabled=true]/tr:cursor-not-allowed before:bg-default/60 data-[selected=true]:text-default-foreground first:before:rounded-s-lg last:before:rounded-e-lg text-start`}
|
||||||
|
key={cell.id}
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</td>
|
||||||
</Table>
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
}
|
}
|
||||||
{state.pilots.length > 0 && screenSize === ScreenSize.SM &&
|
{state.pilots.length > 0 && screenSize === ScreenSize.SM &&
|
||||||
<PilotCard pilots={state.pilots} onDelete={onDeletePilot} onOpenCloseForm={onOpenClosePilotForm} />
|
<PilotCard pilots={state.pilots} onDelete={onDeletePilot} onOpenCloseForm={onOpenClosePilotForm} />
|
||||||
|
|||||||
@@ -3,17 +3,18 @@ import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
|||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { User } from '@microsoft/microsoft-graph-types';
|
import { User } from '@microsoft/microsoft-graph-types';
|
||||||
import { useOidc } from '../../auth/oidcConfig';
|
import { useOidc } from '../../auth/oidcConfig';
|
||||||
import { Avatar, Button, Link, Navbar, NavbarBrand, NavbarContent, NavbarItem, DropdownTrigger, DropdownMenu, DropdownItem, Dropdown } from '@heroui/react';
|
|
||||||
import httpClient from '../../httpClient/httpClient'
|
import httpClient from '../../httpClient/httpClient'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faPlane, faSignIn, faSignOut } from '@fortawesome/free-solid-svg-icons'
|
import { faBars, faPlane, faSignIn, faSignOut } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { useLocation } from 'react-router-dom';
|
import { NavLink, useLocation } from 'react-router-dom';
|
||||||
|
import { useBreakpoints } from '../../hooks/useBreakpoints/UseBreakpoints';
|
||||||
|
import { ScreenSize } from '../../enums/screenSize';
|
||||||
|
|
||||||
const SiteNav = () => {
|
const SiteNav = () => {
|
||||||
const [userPhoto, setUserPhoto] = useState<string>();
|
const [userPhoto, setUserPhoto] = useState<string>();
|
||||||
const appContext = useAppContext();
|
const appContext = useAppContext();
|
||||||
|
const { screenSize } = useBreakpoints();
|
||||||
const { isUserLoggedIn, logout, login } = useOidc()
|
const { isUserLoggedIn, logout, login } = useOidc()
|
||||||
const { pathname } = useLocation()
|
|
||||||
const pages = [
|
const pages = [
|
||||||
{
|
{
|
||||||
name: 'Flights',
|
name: 'Flights',
|
||||||
@@ -53,13 +54,39 @@ const SiteNav = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Brand = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<img
|
||||||
|
className='mr-1'
|
||||||
|
height={35}
|
||||||
|
width={35}
|
||||||
|
src='noahspan-logo.png'
|
||||||
|
/>
|
||||||
|
<FontAwesomeIcon className='mt-1' icon={faPlane} size='2x' />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Links = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{pages.map((page) => {
|
||||||
|
return (
|
||||||
|
<li><NavLink to={page.path}>{page.name}</NavLink></li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const setUserProfile = async () => {
|
const setUserProfile = async () => {
|
||||||
try {
|
try {
|
||||||
const userProfile = await getUserProfile();
|
const userProfile = await getUserProfile();
|
||||||
const userPhoto = await getUserPhoto();
|
// const userPhoto = await getUserPhoto();
|
||||||
|
|
||||||
setUserPhoto(userPhoto);
|
// setUserPhoto(userPhoto);
|
||||||
|
|
||||||
appContext.dispatch({
|
appContext.dispatch({
|
||||||
type: 'SET_USER_PROFILE',
|
type: 'SET_USER_PROFILE',
|
||||||
@@ -78,54 +105,67 @@ const SiteNav = () => {
|
|||||||
}
|
}
|
||||||
}, [isUserLoggedIn]);
|
}, [isUserLoggedIn]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(screenSize)
|
||||||
|
}, [screenSize])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Navbar isBordered maxWidth='full' position='static'>
|
<div className="navbar bg-base-100 shadow-sm w-full">
|
||||||
<NavbarContent>
|
<div className={`navbar-start ${screenSize !== ScreenSize.SM && screenSize !== ScreenSize.MD ? 'ml-8' : ''}`}>
|
||||||
<NavbarBrand>
|
{screenSize === ScreenSize.SM || screenSize === ScreenSize.MD ? (
|
||||||
<img
|
<div className="dropdown">
|
||||||
height={35}
|
<div tabIndex={0} role="button" className="btn btn-ghost lg:hidden">
|
||||||
width={35}
|
<FontAwesomeIcon icon={faBars} size='xl' />
|
||||||
src='noahspan-logo.png'
|
</div>
|
||||||
style={{ marginRight: '5px' }}
|
<ul
|
||||||
/>
|
tabIndex={-1}
|
||||||
<FontAwesomeIcon icon={faPlane} size='2x' />
|
className="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
|
||||||
</NavbarBrand>
|
<Links />
|
||||||
</NavbarContent>
|
</ul>
|
||||||
<NavbarContent justify='center'>
|
</div>
|
||||||
{pages.length > 0 && pages.map((page, index) => {
|
) : (
|
||||||
return (
|
<Brand />
|
||||||
<NavbarItem isActive={pathname === page.path ? true : false} key={index}>
|
)}
|
||||||
<Link color={pathname === page.path ? 'primary' : 'foreground'} href={page.path}>
|
</div>
|
||||||
{page.name}
|
<div className="navbar-center">
|
||||||
</Link>
|
<ul className="menu menu-horizontal px-1">
|
||||||
</NavbarItem>
|
{screenSize === ScreenSize.SM || screenSize === ScreenSize.MD ? (
|
||||||
)
|
<Brand />
|
||||||
})}
|
) : (
|
||||||
</NavbarContent>
|
<Links />
|
||||||
<NavbarContent justify='end'>
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className={`navbar-end ${screenSize !== ScreenSize.SM && screenSize !== ScreenSize.MD ? 'ml-8' : ''}`}>
|
||||||
{!isUserLoggedIn &&
|
{!isUserLoggedIn &&
|
||||||
<Button
|
<button className='btn btn-ghost' onClick={() => login()}><FontAwesomeIcon icon={faSignIn} />Sign In</button>
|
||||||
color='default'
|
|
||||||
onPress={() => login()}
|
|
||||||
startContent={<FontAwesomeIcon icon={faSignIn} />}
|
|
||||||
>
|
|
||||||
Sign In
|
|
||||||
</Button>
|
|
||||||
}
|
}
|
||||||
{isUserLoggedIn &&
|
{isUserLoggedIn &&
|
||||||
<Dropdown>
|
<div className='dropdown dropdown-end'>
|
||||||
<DropdownTrigger>
|
<div tabIndex={0} role='button'>
|
||||||
<Avatar name={appContext.state.userProfile.displayName?.toString()} src={userPhoto}></Avatar>
|
<div className={`avatar ${userPhoto ? userPhoto : 'avatar-placeholder'}`}>
|
||||||
</DropdownTrigger>
|
{userPhoto &&
|
||||||
<DropdownMenu>
|
<div className='w-12 rounded-full'>
|
||||||
<DropdownItem key='signout' onPress={() => logout({redirectTo: 'specific url', url: '/'})} startContent={<FontAwesomeIcon icon={faSignOut} />}>
|
<img src={userPhoto} />
|
||||||
Sign Out
|
</div>
|
||||||
</DropdownItem>
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
|
||||||
}
|
}
|
||||||
</NavbarContent>
|
{!userPhoto &&
|
||||||
</Navbar>
|
<div className='bg-neutral text-neutral-content w-10 rounded-full'>
|
||||||
|
<span>NS</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
tabIndex={0}
|
||||||
|
className='dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm border border-base-300'
|
||||||
|
>
|
||||||
|
<li><a onClick={() => logout({redirectTo: 'specific url', url: '/'})}><FontAwesomeIcon icon={faSignOut} />Sign Out</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
#app {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #eee;
|
|
||||||
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #000;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swiper {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swiper-wrapper {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swiper-slide {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 18px;
|
|
||||||
background: #fff;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swiper-slide img {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swiper-pagination-bullet {
|
|
||||||
background-color: #000000;
|
|
||||||
height: 13px;
|
|
||||||
width: 13px;
|
|
||||||
border: 2px solid #FFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swiper-pagination-bullet-active {
|
|
||||||
box-shadow: 0 0 0 1px #000000;
|
|
||||||
}
|
|
||||||
@@ -4,10 +4,6 @@ import { AxiosInstance, AxiosResponse } from 'axios';
|
|||||||
import { useAuth } from 'react-oidc-context'
|
import { useAuth } from 'react-oidc-context'
|
||||||
import { MapContainer, TileLayer } from 'react-leaflet';
|
import { MapContainer, TileLayer } from 'react-leaflet';
|
||||||
import ReactLeafletKml from 'react-leaflet-kml';
|
import ReactLeafletKml from 'react-leaflet-kml';
|
||||||
import 'swiper/css';
|
|
||||||
import 'swiper/css/pagination';
|
|
||||||
import 'swiper/css';
|
|
||||||
import './TrackMap.css';
|
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import httpClient from '../../httpClient/httpClient'
|
import httpClient from '../../httpClient/httpClient'
|
||||||
|
|
||||||
@@ -41,7 +37,7 @@ const TrackMap = ({ height, logId, tracks }: TrackMapProps) => {
|
|||||||
center={[45.14489, -93.21019]}
|
center={[45.14489, -93.21019]}
|
||||||
scrollWheelZoom={false}
|
scrollWheelZoom={false}
|
||||||
style={{ height: height, width: '100%' }}
|
style={{ height: height, width: '100%' }}
|
||||||
zoom={8}
|
zoom={7}
|
||||||
>
|
>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import ConfirmationDialog from "../confirmationDialog/ConfirmationDialog";
|
|||||||
import { initialState, reducer } from "./reducer";
|
import { initialState, reducer } from "./reducer";
|
||||||
import TrackMap from "../trackMap/TrackMap";
|
import TrackMap from "../trackMap/TrackMap";
|
||||||
import httpClient from "../../httpClient/httpClient";
|
import httpClient from "../../httpClient/httpClient";
|
||||||
import { Button, Input, Spinner } from '@heroui/react';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faUpload, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
import { faUpload, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { useLogbookContext } from "../../hooks/logbookContext/UseLogbookContext";
|
import { useLogbookContext } from "../../hooks/logbookContext/UseLogbookContext";
|
||||||
@@ -28,7 +27,7 @@ const TracksForm = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
const axiosError = error as AxiosError;
|
const axiosError = error as AxiosError;
|
||||||
|
|
||||||
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: { severity: 'danger', message: axiosError.message }})
|
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: { severity: 'error', message: axiosError.message }})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,11 +107,6 @@ const TracksForm = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='grid grid-cols-12 gap-3'>
|
<div className='grid grid-cols-12 gap-3'>
|
||||||
{state.tracks.length > 0 &&
|
|
||||||
<div className="col-span-12">
|
|
||||||
<TrackMap height='400px' logId={logbookContext.state.selectedLogId!} tracks={state.tracks} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<>
|
<>
|
||||||
{state.tracks.length > 0 && state.tracks.map((track, index) => {
|
{state.tracks.length > 0 && state.tracks.map((track, index) => {
|
||||||
const filename: string = track.url.substring(track.url.lastIndexOf('/') + 1);
|
const filename: string = track.url.substring(track.url.lastIndexOf('/') + 1);
|
||||||
@@ -120,26 +114,25 @@ const TracksForm = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='col-span-10'>
|
<div className='col-span-10'>
|
||||||
<Input isDisabled={state.isDisabled} key={index} type='text' value={filename}/>
|
<input className='input w-full' disabled={state.isDisabled} key={index} readOnly type='text' value={filename} />
|
||||||
</div>
|
</div>
|
||||||
<div className='col-span-2'>
|
<div className='col-span-2'>
|
||||||
<Button isDisabled={state.isDisabled} key={index} isIconOnly onPress={() => onDeleteTrack(track.id, filename, index)}><FontAwesomeIcon icon={faTrash} /></Button>
|
<button className='btn w-full' disabled={state.isDisabled} key={index} onClick={() => onDeleteTrack(track.id, filename, index)}><FontAwesomeIcon icon={faTrash} /></button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
{logbookContext.state.formMode === FormMode.ADD &&
|
||||||
<div className='col-span-12'>
|
<div className='col-span-12'>
|
||||||
<Button
|
<label
|
||||||
as='label'
|
className='btn cursor-pointer w-full'
|
||||||
color='primary'
|
|
||||||
isDisabled={state.isDisabled}
|
|
||||||
fullWidth={true}
|
|
||||||
startContent={<FontAwesomeIcon icon={faUpload} />}
|
|
||||||
>
|
>
|
||||||
|
<FontAwesomeIcon icon={faUpload} />
|
||||||
Upload Track
|
Upload Track
|
||||||
<input hidden onChange={handleFileUpload} type='file' />
|
<input className='hidden' id='track-upload' onChange={handleFileUpload} type='file' />
|
||||||
</Button>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
{state.isConfirmDialogOpen && (
|
{state.isConfirmDialogOpen && (
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
contentText="Are you sure you want to delete this track?"
|
contentText="Are you sure you want to delete this track?"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export enum ScreenSize {
|
export enum ScreenSize {
|
||||||
SM,
|
SM = 'SM',
|
||||||
MD,
|
MD = 'MD',
|
||||||
LG,
|
LG = 'LG',
|
||||||
XL,
|
XL = 'XL',
|
||||||
XXL
|
XXL = 'XXL'
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
// hero.ts
|
|
||||||
import { heroui } from "@heroui/react";
|
|
||||||
export default heroui();
|
|
||||||
@@ -14,17 +14,17 @@ export const useBreakpoints = () => {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case width >= 640: {
|
case width >= 640 && width < 1024: {
|
||||||
size = ScreenSize.MD;
|
size = ScreenSize.MD;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case width >= 1024: {
|
case width >= 1024 && width < 1280: {
|
||||||
size = ScreenSize.LG
|
size = ScreenSize.LG
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case width >= 1280: {
|
case width >= 1280 && width < 1536: {
|
||||||
size = ScreenSize.XL;
|
size = ScreenSize.XL;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ httpClient.interceptors.request.use(async (config) => {
|
|||||||
|
|
||||||
if (oidc.isUserLoggedIn) {
|
if (oidc.isUserLoggedIn) {
|
||||||
const { accessToken } = await oidc.getTokens();
|
const { accessToken } = await oidc.getTokens();
|
||||||
|
console.log(accessToken)
|
||||||
config.headers.Authorization = `Bearer ${accessToken}`;
|
config.headers.Authorization = `Bearer ${accessToken}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Flying</title>
|
<title>Flying</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body style="background-color: #f5f5f5;">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface Alert {
|
export interface Alert {
|
||||||
severity: 'danger' | 'default' | 'success' | 'warning';
|
severity: 'info' | 'error' | 'success' | 'warning';
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,42 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@plugin './hero.ts';
|
@plugin "daisyui";
|
||||||
/* Note: You may need to change the path to fit your project structure */
|
@plugin "daisyui/theme" {
|
||||||
@source '../../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}';
|
name: "lofi";
|
||||||
@custom-variant dark (&:is(.dark *));
|
default: true;
|
||||||
|
prefersdark: false;
|
||||||
|
color-scheme: "light";
|
||||||
|
--color-base-100: oklch(100% 0 0);
|
||||||
|
--color-base-200: oklch(97% 0 0);
|
||||||
|
--color-base-300: oklch(94% 0 0);
|
||||||
|
--color-base-content: oklch(0% 0 0);
|
||||||
|
--color-primary: oklch(15.906% 0 0);
|
||||||
|
--color-primary-content: oklch(100% 0 0);
|
||||||
|
--color-secondary: oklch(21.455% 0.001 17.278);
|
||||||
|
--color-secondary-content: oklch(100% 0 0);
|
||||||
|
--color-accent: oklch(26.861% 0 0);
|
||||||
|
--color-accent-content: oklch(100% 0 0);
|
||||||
|
--color-neutral: oklch(0% 0 0);
|
||||||
|
--color-neutral-content: oklch(100% 0 0);
|
||||||
|
--color-info: oklch(79.54% 0.103 205.9);
|
||||||
|
--color-info-content: oklch(15.908% 0.02 205.9);
|
||||||
|
--color-success: oklch(90.13% 0.153 164.14);
|
||||||
|
--color-success-content: oklch(18.026% 0.03 164.14);
|
||||||
|
--color-warning: oklch(88.37% 0.135 79.94);
|
||||||
|
--color-warning-content: oklch(17.674% 0.027 79.94);
|
||||||
|
--color-error: oklch(78.66% 0.15 28.47);
|
||||||
|
--color-error-content: oklch(15.732% 0.03 28.47);
|
||||||
|
--radius-selector: 0.5rem;
|
||||||
|
--radius-field: 0.5rem;
|
||||||
|
--radius-box: 0.5rem;
|
||||||
|
--size-selector: 0.25rem;
|
||||||
|
--size-field: 0.25rem;
|
||||||
|
--border: 1px;
|
||||||
|
--depth: 0;
|
||||||
|
--noise: 0;
|
||||||
|
}
|
||||||
@plugin "@tailwindcss/typography";
|
@plugin "@tailwindcss/typography";
|
||||||
|
|
||||||
@theme inline {
|
body {
|
||||||
--color-primary: #000000;
|
background-color: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
5
client/src/tanstack.d.ts
vendored
5
client/src/tanstack.d.ts
vendored
@@ -3,8 +3,9 @@ import '@tanstack/react-table';
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
declare module '@tanstack/react-table' {
|
declare module '@tanstack/react-table' {
|
||||||
interface ColumnMeta<TData extends RowData, TValue> {
|
interface ColumnMeta<TData extends RowData, TValue> {
|
||||||
align?: 'left' | 'center' | 'right';
|
align?: 'text-left' | 'text-center' | 'text-right';
|
||||||
headerAlign?: 'left' | 'center' | 'right';
|
className?: string;
|
||||||
|
headerAlign?: 'text-left' | 'text-center' | 'text-right';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|||||||
1
client/src/vite-env.d.ts
vendored
1
client/src/vite-env.d.ts
vendored
@@ -5,6 +5,7 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_CLIENT_ID: string;
|
readonly VITE_CLIENT_ID: string;
|
||||||
readonly VITE_ISSUER_URI: string;
|
readonly VITE_ISSUER_URI: string;
|
||||||
readonly VITE_TENANT_ID: string;
|
readonly VITE_TENANT_ID: string;
|
||||||
|
readonly VITE_USERINFO_ENDPOINT: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
|||||||
Binary file not shown.
@@ -74,6 +74,16 @@ resource "azurerm_container_app" "container_app" {
|
|||||||
secret_name = "azure-storage-connection-string"
|
secret_name = "azure-storage-connection-string"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
name = "AUTHORITY"
|
||||||
|
value = var.AUTHORITY
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
name = "AUDIENCE"
|
||||||
|
value = var.CLIENT_ID
|
||||||
|
}
|
||||||
|
|
||||||
env {
|
env {
|
||||||
name = "CLIENT_ID"
|
name = "CLIENT_ID"
|
||||||
value = var.CLIENT_ID
|
value = var.CLIENT_ID
|
||||||
@@ -84,6 +94,16 @@ resource "azurerm_container_app" "container_app" {
|
|||||||
secret_name = "client-secret"
|
secret_name = "client-secret"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
name = "ISSUER_URL"
|
||||||
|
value = var.ISSUER_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
name = "JWKS_URI"
|
||||||
|
value = var.JWKS_URI
|
||||||
|
}
|
||||||
|
|
||||||
env {
|
env {
|
||||||
name = "TENANT_ID"
|
name = "TENANT_ID"
|
||||||
value = var.EXTERNAL_TENANT_ID
|
value = var.EXTERNAL_TENANT_ID
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
variable "AUTHORITY" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
variable "CLIENT_ID" {
|
variable "CLIENT_ID" {
|
||||||
type = string
|
type = string
|
||||||
@@ -17,6 +20,14 @@ variable "DOCKER_IO_USERNAME" {
|
|||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "ISSUER_URL" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "JWKS_URI" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
variable "RESOURCE_GROUP_NAME" {
|
variable "RESOURCE_GROUP_NAME" {
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|||||||
3078
package-lock.json
generated
3078
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||||
"@typescript-eslint/parser": "^7.2.0",
|
"@typescript-eslint/parser": "^7.2.0",
|
||||||
|
"daisyui": "^5.5.5",
|
||||||
"eslint": "^8.42.0",
|
"eslint": "^8.42.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user