104 switch to daisyui #106

Merged
noahspannbauer merged 6 commits from 104-switch-to-daisyui into main 2025-12-29 20:43:27 -05:00
24 changed files with 497 additions and 3887 deletions
Showing only changes of commit 1bfc952233 - Show all commits

View File

@@ -35,7 +35,7 @@
"@nestjs/serve-static": "^5.0.3",
"@nestjs/typeorm": "^11.0.0",
"@noahspan/azure-database": "^3.1.2",
"@noahspan/noahspan-modules": "^1.2.9",
"@noahspan/noahspan-modules": "^1.2.11",
"@schematics/angular": "^17.3.7",
"@types/multer": "^1.4.12",
"better-sqlite3": "^12.2.0",

View File

@@ -32,17 +32,18 @@ import { join } from 'path';
}),
HealthModule,
LogModule,
// MsGraphModule.registerAsync({
// inject: [ConfigService],
// imports: [ConfigModule],
// useFactory: async (configService: ConfigService) => {
// return {
// clientId: configService.get<string>('clientId'),
// clientSecret: configService.get<string>('clientSecret'),
// tenantId: configService.get<string>('tenantId')
// }
// }
// }),
MsGraphModule.registerAsync({
inject: [ConfigService],
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => {
return {
authority: configService.get<string>('authority'),
clientId: configService.get<string>('clientId'),
clientSecret: configService.get<string>('clientSecret'),
tenantId: configService.get<string>('tenantId')
}
}
}),
PilotModule,
ServeStaticModule.forRoot({
rootPath: join(__dirname, '../..', 'client', 'dist')

View File

@@ -1,6 +1,7 @@
export default () => ({
azureStorageConnectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
audience: process.env.AUDIENCE,
authority: process.env.AUTHORITY,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
issuer: process.env.ISSUER_URL,

View File

@@ -65,7 +65,7 @@ export class LogController {
return await this.logService.create(logDto);
} catch (error) {
const customError = error as CustomError;
console.log(error)
throw new HttpException(customError.message, customError.statusCode);
}
}

View File

@@ -29,7 +29,9 @@ export class PilotInterceptor implements NestInterceptor {
if (req.headers.authorization) {
const authHeader = req.headers.authorization;
const token = authHeader && authHeader.split(' ')[1];
const jwtPayload = jwtDecode(token);
console.log(jwtPayload)
const rolesKeyName = Object.keys(jwtPayload).find((key) => key.includes('roles'));
if (jwtPayload[rolesKeyName].includes('Flying.Read')) {

View File

@@ -14,14 +14,12 @@
"@fortawesome/fontawesome-svg-core": "^7.1.0",
"@fortawesome/free-solid-svg-icons": "^7.1.0",
"@fortawesome/react-fontawesome": "^3.1.0",
"@heroui/react": "^2.8.5",
"@noahspan/noahspan-components": "^2.0.0-alpha-14",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.1.13",
"@tanstack/react-table": "^8.21.3",
"axios": "^1.7.2",
"dotenv": "^16.4.7",
"framer-motion": "^12.23.24",
"leaflet": "^1.9.4",
"oidc-spa": "^7.2.4",
"react": "^19.1.1",

View File

@@ -4,10 +4,8 @@ export const { OidcProvider, useOidc, getOidc } = createReactOidc(async () => ({
issuerUri: import.meta.env.VITE_ISSUER_URI,
clientId: import.meta.env.VITE_CLIENT_ID,
homeUrl: import.meta.env.VITE_BASE_URL,
scopes: ['email', 'openid', 'profile', `api://${import.meta.env.VITE_CLIENT_ID}/user_impersonation`],
autoLogin: false,
postLoginRedirectUrl: '/',
noIframe: true,
extraQueryParams: {
audience: "api://flying-test-api"
}
}));

View File

@@ -7,7 +7,6 @@
// IconName,
// Loading
// } from '@noahspan/noahspan-components';
import { Button, Modal, ModalBody, ModalContent, ModalHeader, ModalFooter, Spinner } from '@heroui/react'
import { DialogConfirmationProps } from './ConfirmationDialogProps.interface';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCircleCheck, faXmark } from '@fortawesome/free-solid-svg-icons';
@@ -21,31 +20,25 @@ const ConfirmationDialog = ({
title
}: DialogConfirmationProps) => {
return (
<Modal
isDismissable={false}
isKeyboardDismissDisabled={true}
isOpen={isOpen}
>
<ModalContent>
<ModalHeader>{title}</ModalHeader>
<ModalBody>
{!isLoading && <div>{contentText}</div>}
{isLoading && <Spinner size='lg' />}
</ModalBody>
<ModalFooter>
<Button onPress={onCancel} startContent={<FontAwesomeIcon icon={faXmark} />}>
No
</Button>
<Button
color='primary'
onPress={onConfirm}
startContent={<FontAwesomeIcon icon={faCircleCheck} />}
>
Yes
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<>
<input type='checkbox' id='dialog' className='modal-toggle' onChange={() => {}} checked={isOpen} />
<div className='modal' role='dialog'>
<div className='modal-box'>
<h3 className="text-lg font-bold">{title}</h3>
<p className="py-4">{contentText}</p>
<div className='modal-action'>
<button className='btn' onClick={onCancel}>
<FontAwesomeIcon icon={faXmark} />
No
</button>
<button className='btn btn-primary' onClick={onConfirm}>
<FontAwesomeIcon icon={faCircleCheck} />
Yes
</button>
</div>
</div>
</div>
</>
);
};

View File

@@ -1,11 +1,9 @@
import { Icon, IconName, Skeleton } from "@noahspan/noahspan-components";
import { Card } from '@heroui/react';
import LogbookCard from "../logbookCard/LogbookCard";
import { useEffect, useReducer } from "react";
import { useLogs } from "../../hooks/logs/UseLogs";
import { LogbookEntry } from "../logbook/LogbookEntry.interface";
import { initialState, reducer } from "./reducer";
import { Alert } from '@heroui/react'
import Alert from "../alert/Alert";
const Flights = () => {
const [state, dispatch] = useReducer(reducer, initialState);
@@ -23,7 +21,7 @@ const Flights = () => {
dispatch({ type: 'SET_FLIGHTS', payload: flights})
dispatch({ type: 'SET_ALERT', payload: undefined })
} else {
dispatch({ type: 'SET_ALERT', payload: { severity: 'default', message: 'No flights found' }})
dispatch({ type: 'SET_ALERT', payload: { severity: 'info', message: 'No flights found' }})
}
}, [logs])
@@ -35,12 +33,14 @@ const Flights = () => {
{!logsLoading && state.alert && (
<div>
<Alert
className='mb-5'
onClose={() =>
dispatch({ type: 'SET_ALERT', payload: undefined })
}
color={state.alert.severity}
title={state.alert.message}
/>
severity={state.alert.severity}
>
{state.alert.message}
</Alert>
</div>
)}
{!logsLoading &&
@@ -48,7 +48,7 @@ const Flights = () => {
<LogbookCard logs={state.flights} mode='flights' />
</div>
}
{logsLoading && [...Array(6)].map((_element, index) => {
{/* {logsLoading && [...Array(6)].map((_element, index) => {
return (
<div className='mb-5'>
<Card
@@ -70,7 +70,7 @@ const Flights = () => {
</Card>
</div>
)
})}
})} */}
</div>
)
}

View File

@@ -1,5 +1,4 @@
import React, { 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, FormProvider, useFormContext } from 'react-hook-form';
import { LogFormProps } from './LogFormProps.interface';
import { initialState, reducer } from './reducer';
@@ -10,7 +9,6 @@ import { useOidc } from '../../auth/oidcConfig';
import httpClient from '../../httpClient/httpClient';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSave, faXmark } from '@fortawesome/free-solid-svg-icons'
import { parseAbsolute, parseDate, getLocalTimeZone, CalendarDate, ZonedDateTime } from '@internationalized/date';
import { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
const LogForm = () => {
@@ -40,7 +38,7 @@ const LogForm = () => {
} catch (error) {
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 {
dispatch({ type: 'SET_IS_LOADING', payload: false });
}
@@ -59,7 +57,7 @@ const LogForm = () => {
label: pilot.name,
};
});
console.log(newPilotsOptions)
dispatch({ type: 'SET_PILOT_OPTIONS', payload: newPilotsOptions });
}
}, [pilots]);
@@ -75,23 +73,21 @@ const LogForm = () => {
control={control}
render={({ field: { value } }) => {
return (
<Select
<select
className='select w-full'
aria-labelledby='pilot'
isDisabled={state.isDisabled}
fullWidth={true}
isRequired={true}
onSelectionChange={(keys: SharedSelection) => {
setValue('pilotId', keys.currentKey);
}}
selectedKeys={[value]}
size='lg'
disabled={state.isDisabled}
// onChange={(keys: SharedSelection) => {
// setValue('pilotId', keys.currentKey);
// }}=
value={[value]}
>
{state.pilotOptions?.map((pilotOption: { key: string; label: string, }) => {
return (
<SelectItem key={pilotOption.key}>{pilotOption.label}</SelectItem>
<option key={pilotOption.key}>{pilotOption.label}</option>
)
})}
</Select>
</select>
);
}}
/>
@@ -104,21 +100,26 @@ const LogForm = () => {
name="date"
control={control}
render={({ field: { onChange, value } }) => {
const parsedAbsoluteDate = value ? parseAbsolute(value, getLocalTimeZone()) : value
return(
<DatePicker
aria-labelledby='date'
isDisabled={state.isDisabled}
isRequired={true}
onChange={(selectedDate) => {
let date = selectedDate as CalendarDate;
<input
type='date'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
value={value}
/>
// <DatePicker
// aria-labelledby='date'
// isDisabled={state.isDisabled}
// isRequired={true}
// onChange={(selectedDate) => {
// let date = selectedDate as CalendarDate;
setValue('date', date.toDate(getLocalTimeZone()).toISOString())
}}
size='lg'
value={parsedAbsoluteDate ? new CalendarDate(parsedAbsoluteDate.year, parsedAbsoluteDate.month, parsedAbsoluteDate.day) : value}
/>
// setValue('date', date.toDate(getLocalTimeZone()).toISOString())
// }}
// size='lg'
// value={parsedAbsoluteDate ? new CalendarDate(parsedAbsoluteDate.year, parsedAbsoluteDate.month, parsedAbsoluteDate.day) : value}
// />
)
}}
/>
@@ -131,18 +132,11 @@ const LogForm = () => {
name="aircraftMakeModel"
control={control}
render={({ field: { onChange, value } }) => (
<Input
aria-labelledby='aircraftMakeModel'
isDisabled={state.isDisabled}
color={formState.errors.address ? 'danger' : undefined}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isRequired={true}
<input
type='text'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
size='lg'
value={value}
/>
)}
@@ -158,18 +152,11 @@ const LogForm = () => {
name="aircraftIdentity"
control={control}
render={({ field: { onChange, value } }) => (
<Input
aria-labelledby='aircraftIdentity'
isDisabled={state.isDisabled}
color={formState.errors.address ? 'danger' : undefined}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isRequired={true}
<input
type='text'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
size='lg'
value={value}
/>
)}
@@ -185,18 +172,11 @@ const LogForm = () => {
name="routeFrom"
control={control}
render={({ field: { onChange, value } }) => (
<Input
aria-labelledby='routeFrom'
isDisabled={state.isDisabled}
color={formState.errors.address ? 'danger' : undefined}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isRequired={true}
<input
type='text'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
size='lg'
value={value}
/>
)}
@@ -210,18 +190,11 @@ const LogForm = () => {
name="routeTo"
control={control}
render={({ field: { onChange, value } }) => (
<Input
aria-labelledby='routeTo'
isDisabled={state.isDisabled}
color={formState.errors.address ? 'danger' : undefined}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isRequired={true}
<input
type='text'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
size='lg'
value={value}
/>
)}
@@ -235,21 +208,11 @@ const LogForm = () => {
name="durationOfFlight"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='durationOfFlight'
isDisabled={state.isDisabled}
color={formState.errors.address ? 'danger' : undefined}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isRequired={true}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -265,21 +228,11 @@ const LogForm = () => {
name="singleEngineLand"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='singleEngineLand'
isDisabled={state.isDisabled}
color={formState.errors.address ? 'danger' : undefined}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isRequired={true}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -293,20 +246,11 @@ const LogForm = () => {
name="simulatorAtd"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='simulaterAtd'
isDisabled={state.isDisabled}
color={formState.errors.address ? 'danger' : undefined}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -323,22 +267,11 @@ const LogForm = () => {
name="landingsDay"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='landingsDay'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -352,22 +285,11 @@ const LogForm = () => {
name="landingsNight"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='landingsNight'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -386,24 +308,12 @@ const LogForm = () => {
name="groundTrainingReceived"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='groundTrainingReceived'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
width='w-full'
/>
)}
/>
@@ -418,22 +328,11 @@ const LogForm = () => {
name="flightTrainingReceived"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='flightTrainingReceived'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -447,22 +346,11 @@ const LogForm = () => {
name="crossCountry"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='crossCountry'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -476,22 +364,11 @@ const LogForm = () => {
name="night"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='night'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -505,22 +382,11 @@ const LogForm = () => {
name="solo"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='solo'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -534,22 +400,11 @@ const LogForm = () => {
name="pilotInCommand"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='pilotInCommand'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -566,22 +421,11 @@ const LogForm = () => {
name="instrumentActual"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='instrumentActual'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -595,22 +439,11 @@ const LogForm = () => {
name="instrumentSimulated"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='instrumentSimulated'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -626,22 +459,11 @@ const LogForm = () => {
name="instrumentApproaches"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='instrumentApproaches'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -655,22 +477,11 @@ const LogForm = () => {
name="instrumentHolds"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='instrumentHolds'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -684,23 +495,11 @@ const LogForm = () => {
name="instrumentNavTrack"
control={control}
render={({ field: { onChange, value } }) => (
<NumberInput
aria-labelledby='instrumentNavTrack'
isDisabled={state.isDisabled}
color={
formState.errors.address ? 'danger' : undefined
}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
fullWidth={true}
isWheelDisabled={state.isDisabled}
<input
type='number'
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
radius='lg'
size='sm'
type="number"
value={value}
/>
)}
@@ -716,19 +515,11 @@ const LogForm = () => {
name="notes"
control={control}
render={({ field: { onChange, value } }) => (
<Textarea
aria-labelledby='notes'
isDisabled={state.isDisabled}
color={formState.errors.address ? 'danger' : undefined}
errorMessage={
formState.errors.address
? formState.errors.address.message?.toString()
: undefined
}
fullWidth={true}
<textarea
className='textarea w-full'
disabled={state.isDisabled}
onChange={onChange}
value={value}
/>
></textarea>
)}
/>
</div>

View File

@@ -1,10 +1,8 @@
import { Key, useEffect, useReducer } from 'react';
import LogForm from '../logForm/LogForm';
import { useEffect, useReducer } from 'react';
import { initialState, reducer } from './reducer';
import { AxiosError, AxiosResponse } from 'axios';
import { FormMode } from '../../enums/formMode';
import { authColumns, unauthColumns } from './columns';
import ActionMenu from '../actionMenu/ActionMenu';
import ConfirmationDialog from '../confirmationDialog/ConfirmationDialog';
import { LogbookEntry } from './LogbookEntry.interface';
import LogbookCard from '../logbookCard/LogbookCard';
@@ -14,12 +12,12 @@ import { useUserRole } from '../../hooks/userRole/UseUserRole';
import { UserRole } from '../../enums/userRole';
import { useBreakpoints } from '../../hooks/useBreakpoints/UseBreakpoints';
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 { 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 { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
import LogbookDrawer from '../logbookDrawer/LogbookDrawer';
import Alert from '../alert/Alert';
const Logbook: React.FC<unknown> = () => {
const [state, dispatch] = useReducer(reducer, initialState);
@@ -28,7 +26,6 @@ const Logbook: React.FC<unknown> = () => {
const { userRole } = useUserRole();
const { screenSize } = useBreakpoints();
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));
let total: number = 0;
@@ -124,43 +121,9 @@ const Logbook: React.FC<unknown> = () => {
<ul tabIndex={-1} className="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm border border-base-300">
<li><a onClick={() => onOpenCloseDrawer(FormMode.EDIT, info.row.original.id)}><FontAwesomeIcon icon={faPen} />Edit</a></li>
<li><a onClick={() => onOpenCloseDrawer(FormMode.VIEW, info.row.original.id)}><FontAwesomeIcon icon={faEye} />View</a></li>
<li><a onClick={() => onDeleteLog(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />View</a></li>
<li><a onClick={() => onDeleteLog(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
</ul>
</div>
// <Dropdown>
// <DropdownTrigger>
// <Button isIconOnly variant='light' size='lg'>
// <FontAwesomeIcon icon={faEllipsisVertical} />
// </Button>
// </DropdownTrigger>
// <DropdownMenu>
// <DropdownSection showDivider>
// <DropdownItem
// key='edit'
// onPress={() => onOpenCloseDrawer(FormMode.EDIT, info.row.original.id)}
// startContent={<FontAwesomeIcon icon={faPen} />}
// >
// 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>
)
}
}
@@ -395,14 +358,14 @@ const Logbook: React.FC<unknown> = () => {
dispatch({ type: 'SET_ALERT', payload: undefined})
}
} 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) {
const axiosError = error as AxiosError;
dispatch({
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 {
dispatch({ type: 'SET_IS_LOADING', payload: false });
@@ -460,7 +423,7 @@ const Logbook: React.FC<unknown> = () => {
dispatch({
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 {
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: false });
@@ -545,12 +508,14 @@ const Logbook: React.FC<unknown> = () => {
{!state.isLoading && state.alert && (
<div className='col-span-12 mb-5'>
<Alert
className='mb-5'
onClose={() =>
dispatch({ type: 'SET_ALERT', payload: undefined })
}
color={'default'}
title={state.alert.message}
/>
severity={'info'}
>
{state.alert.message}
</Alert>
</div>
)}
{!state.isLoading && (
@@ -674,14 +639,6 @@ const Logbook: React.FC<unknown> = () => {
title="Confirm Delete"
/>
)}
{/* {state.isTracksOpen &&
<LogTracks
isDrawerOpen={state.isTracksOpen}
mode={state.tracksMode}
onOpenClose={(mode) => onOpenCloseTracks(mode)}
selectedLogId={logbookContext.state.selectedLogId}
/>
} */}
</>
);
};

View File

@@ -1,6 +1,5 @@
import { LogbookCardProps } from "./LogbookCardProps.interface";
import TrackMap from "../trackMap/TrackMap";
import { Button, Collapse, CollapseContent, CollapseTitle } from '@noahspan/noahspan-components'
const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps) => {
return (

View File

@@ -1,7 +1,7 @@
import { Alert, Button, Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader, Tab, Tabs } from "@heroui/react";
import { LogbookDrawerProps } from "./LogbookDrawerProps.interface";
import LogForm from "../logForm/LogForm";
import TracksForm from "../tracksForm/TracksForm";
import Alert from '../alert/Alert';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faClock, faMapLocationDot, faSave, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FormProvider, useForm } from "react-hook-form";
@@ -12,7 +12,7 @@ import { useLogbookContext } from "../../hooks/logbookContext/UseLogbookContext"
import { Key, useState } from "react";
const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => {
const [activeTab, setActiveTab] = useState<Key>('time');
const [activeTab, setActiveTab] = useState<string>('time');
const defaultValues = {
pilotId: '',
date: null,
@@ -63,14 +63,14 @@ const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => {
} catch (error) {
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 {
logbookContext.dispatch({ type: 'SET_IS_FORM_LOADING', payload: false });
}
};
const onSelectedKeyChanged = (key: React.Key) => {
setActiveTab(key)
const onSelectedKeyChanged = (tab: string) => {
setActiveTab(tab)
}
return (
@@ -90,66 +90,44 @@ const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => {
aria-label='close-sidebar'
className='drawer-overlay'
></label>
<div className='menu bg-base-100 text-base-content min-h-full p-4' style={{ width: '50%' }}>
<div className='menu bg-base-100 text-base-content min-h-full p-4' style={{ width: '25%' }}>
<FormProvider {...methods}>
<form className='prose max-w-none' onSubmit={methods.handleSubmit(onSubmit)} style={{ paddingBottom: '50px' }}>
<h2>
{`${logbookContext.state.formMode.toString().toLowerCase().charAt(0).toUpperCase() + logbookContext.state.formMode.toString().slice(1).toLowerCase()} Entry`}
</h2>
{/* {logbookContext.state.formAlert && (
<div className='col-span-12'>
<Alert
onClose={() =>
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: undefined })
}
color={logbookContext.state.formAlert.severity}
title={logbookContext.state.formAlert.message}
/>
</div>
)} */}
<LogForm />
{/* <div className='tabs tabs-lift'>
{logbookContext.state.formAlert && (
<Alert
className='mb-5'
onClose={() =>
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: undefined })
}
severity={logbookContext.state.formAlert.severity}
>
{logbookContext.state.formAlert.message}
</Alert>
)}
<div className='tabs tabs-lift mb-5'>
<label className='tab'>
<input type='radio' name='my_tabs_1' />
<FontAwesomeIcon icon={faClock} />
<input type='radio' name='my_tabs' defaultChecked={true} />
<FontAwesomeIcon className='mr-1'icon={faClock} />
Time
</label>
<div className="tab-content bg-base-100 border-base-300 p-6">
<div className='tab-content border-base-300 p-6'>
<LogForm />
</div>
<label className='tab'>
<input type='radio' name='my_tabs_1' />
<FontAwesomeIcon icon={faMapLocationDot} />
Tracks
</label>
<div className="tab-content bg-base-100 border-base-300 p-6">
<TracksForm />
</div> */}
{/* <input type='radio' name='my_tabs_1' className='tab' aria-label='Tracks' /> */}
{/* <input
key='time'
title={
<div className="flex items-center space-x-2">
<FontAwesomeIcon icon={faClock} />
<span>Time</span>
</div>
}
>
</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>
}
>
<TracksForm />
</Tab>
} */}
<>
<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 />
</div>
</>
}
</div>
{activeTab !== 'tracks' &&
<div>
@@ -169,7 +147,7 @@ const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => {
</button>
{logbookContext.state.formMode.toString() !== FormMode.VIEW && (
<button
className='btn btn-primary ml-[10px]'
className='btn btn-primary ml-2.5'
disabled={logbookContext.state.isFormDisabled}
type="submit"
>

View File

@@ -1,4 +1,4 @@
import { Key, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useForm, Controller, FormProvider } from 'react-hook-form';
import { IPilotFormProps } from './IPilotFormProps';
import { AxiosError, AxiosResponse } from 'axios';
@@ -9,7 +9,6 @@ import PilotFormEndorsements from '../pilotFormEndorsements/PilotFormEndorsement
import PilotFormMedical from '../pilotFormMedical/PilotFormMedical';
import { useOidc } from '../../auth/oidcConfig';
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 { faSave, faXmark } from '@fortawesome/free-solid-svg-icons';
import { states } from './states';
@@ -45,39 +44,6 @@ const PilotForm: React.FC<IPilotFormProps> = ({
const [isDisabled, setIsDisabled] = 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 = () => {
methods.reset(defaultValues);
onOpenClose(FormMode.CANCEL);
@@ -138,245 +104,229 @@ const PilotForm: React.FC<IPilotFormProps> = ({
}, [pilotId]);
return (
<Drawer
closeButton={
<Button isIconOnly>
<FontAwesomeIcon icon={faXmark} />
</Button>
}
isOpen={isDrawerOpen}
placement='right'
data-testid="pilot-drawer"
onClose={onCancel}
size='xl'
<div className='drawer drawer-end'
// closeButton={
// <Button isIconOnly>
// <FontAwesomeIcon icon={faXmark} />
// </Button>
// }
// isOpen={isDrawerOpen}
// placement='right'
// data-testid="pilot-drawer"
// onClose={onCancel}
// size='xl'
>
<DrawerContent>
<FormProvider {...methods}>
<form className='prose max-w-none' onSubmit={methods.handleSubmit(onSubmit)} style={{ paddingBottom: '50px' }}>
<DrawerHeader>
{`${mode.toString().toLowerCase().charAt(0).toUpperCase() + mode.toString().slice(1).toLowerCase()} Pilot`}
</DrawerHeader>
<DrawerBody>
<div className='grid grid-cols-12 gap-3'>
<div className='col-span-3 self-center'>
<h6>Name *</h6>
</div>
<div className='col-span-9'>
<Autocomplete
inputValue={peoplePickerValue}
isLoading={isPeoplePickerLoading}
items={peoplePickerResults}
onInputChange={onPeoplePickerSearch}
onSelectionChange={(key: Key | null) => onPersonSelected(key as string)}
>
{peoplePickerResults.map((person: Person) => (
<AutocompleteItem key={person.userPrincipalName}>
{person.displayName}
</AutocompleteItem>
))}
</Autocomplete>
</div>
{isUserLoggedIn &&
<>
<div className='col-span-3 self-center'>
<span>Address *</span>
</div>
<div className='col-span-9'>
<Controller
name="address"
control={methods.control}
rules={{ required: 'An address is required' }}
render={({ field: { onChange, value } }) => (
<Input
disabled={isDisabled}
color={methods.formState.errors.address ? 'danger' : undefined}
errorMessage={
methods.formState.errors.address
? methods.formState.errors.address.message
: undefined
}
fullWidth={true}
onChange={onChange}
value={value}
/>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>City *</h6>
</div>
<div className='col-span-9'>
<Controller
name="city"
control={methods.control}
rules={{ required: 'A city is required' }}
render={({ field: { onChange, value } }) => (
<Input
disabled={isDisabled}
color={methods.formState.errors.city ? 'danger' : undefined}
errorMessage={
methods.formState.errors.city
? methods.formState.errors.city.message
: undefined
}
fullWidth={true}
onChange={onChange}
value={value}
/>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>State *</h6>
</div>
<div className='col-span-9'>
<Controller
name="state"
control={methods.control}
rules={{ required: 'A state must be selected' }}
render={({ field: { onChange, value } }) => (
<Select
onChange={onChange}
selectedKeys={[value]}
>
{states.map((state) => (
<SelectItem key={state.value}>{state.label}</SelectItem>
))}
</Select>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>Postal Code *</h6>
</div>
<div className='col-span-9'>
<Controller
name="postalCode"
control={methods.control}
rules={{ required: 'A postal code is required' }}
render={({ field: { onChange, value } }) => (
<Input
disabled={isDisabled}
color={methods.formState.errors.postalCode ? 'danger' : undefined}
errorMessage={
methods.formState.errors.postalCode
? methods.formState.errors.postalCode.message
: undefined
}
fullWidth={true}
onChange={onChange}
value={value}
/>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>Email</h6>
</div>
<div className='col-span-9'>
<Controller
name="email"
control={methods.control}
rules={{
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
}}
render={({ field: { onChange, value } }) => (
<Input
disabled={isDisabled}
color={methods.formState.errors.email ? 'danger' : undefined}
errorMessage={
methods.formState.errors.email
? methods.formState.errors.email.message
: undefined
}
fullWidth={true}
onChange={onChange}
value={value}
/>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>Phone Number</h6>
</div>
<div className='col-span-9'>
<Controller
name="phone"
control={methods.control}
rules={{
pattern: {
value: /^[0-9]{3}[-\s\.][0-9]{3}[-\s\.][0-9]{4}$/i,
message: 'Enter phone number as 123-456-7890'
}
}}
render={({ field: { onChange, value } }) => (
<Input
disabled={isDisabled}
color={methods.formState.errors.phone ? 'danger' : undefined}
errorMessage={
methods.formState.errors.phone
? methods.formState.errors.phone.message
: undefined
}
fullWidth={true}
onChange={onChange}
value={value}
/>
)}
/>
</div>
</>
}
{/* {isAuthenticated &&
<Grid size={12}>
<PilotFormMedical
isDisabled={isDisabled}
<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}>
<form className='prose max-w-none' onSubmit={methods.handleSubmit(onSubmit)} style={{ paddingBottom: '50px' }}>
<h2>
{`${mode.toString().toLowerCase().charAt(0).toUpperCase() + mode.toString().slice(1).toLowerCase()} Pilot`}
</h2>
<div className='grid grid-cols-12 gap-3'>
<div className='col-span-3 self-center'>
<span>Name *</span>
</div>
<div className='col-span-9'>
<Controller
name="name"
control={methods.control}
rules={{ required: 'A name is required' }}
render={({ field: { onChange, value } }) => (
<input
type='text'
className='input w-full'
disabled={isDisabled}
onChange={onChange}
value={value}
/>
)}
/>
</div>
{isUserLoggedIn &&
<>
<div className='col-span-3 self-center'>
<span>Address *</span>
</div>
<div className='col-span-9'>
<Controller
name="address"
control={methods.control}
rules={{ required: 'An address is required' }}
render={({ field: { onChange, value } }) => (
<input
type='text'
className='input w-full'
disabled={isDisabled}
onChange={onChange}
value={value}
/>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>City *</h6>
</div>
<div className='col-span-9'>
<Controller
name="city"
control={methods.control}
rules={{ required: 'A city is required' }}
render={({ field: { onChange, value } }) => (
<input
type='text'
className='input w-full'
disabled={isDisabled}
onChange={onChange}
value={value}
/>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>State *</h6>
</div>
<div className='col-span-9'>
<Controller
name="state"
control={methods.control}
rules={{ required: 'A state must be selected' }}
render={({ field: { onChange, value } }) => (
<select
className='select w-full'
onChange={onChange}
value={[value]}
>
{states.map((state) => (
<option key={state.value}>{state.label}</option>
))}
</select>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>Postal Code *</h6>
</div>
<div className='col-span-9'>
<Controller
name="postalCode"
control={methods.control}
rules={{ required: 'A postal code is required' }}
render={({ field: { onChange, value } }) => (
<input
type='text'
className='input w-full'
disabled={isDisabled}
onChange={onChange}
value={value}
/>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>Email</h6>
</div>
<div className='col-span-9'>
<Controller
name="email"
control={methods.control}
rules={{
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
}}
render={({ field: { onChange, value } }) => (
<input
type='text'
className='input w-full'
disabled={isDisabled}
onChange={onChange}
value={value}
/>
)}
/>
</div>
<div className='col-span-3 self-center'>
<h6>Phone Number</h6>
</div>
<div className='col-span-9'>
<Controller
name="phone"
control={methods.control}
rules={{
pattern: {
value: /^[0-9]{3}[-\s\.][0-9]{3}[-\s\.][0-9]{4}$/i,
message: 'Enter phone number as 123-456-7890'
}
}}
render={({ field: { onChange, value } }) => (
<input
type='text'
className='input w-full'
disabled={isDisabled}
onChange={onChange}
value={value}
/>
)}
/>
</div>
</>
}
{/* {isAuthenticated &&
<Grid size={12}>
<PilotFormMedical
isDisabled={isDisabled}
/>
</Grid>
}
<Grid size={12}>
<PilotFormCertificates isDisabled={isDisabled} mode={mode} />
</Grid>
}
<Grid size={12}>
<PilotFormCertificates isDisabled={isDisabled} mode={mode} />
</Grid>
<Grid size={12}>
<PilotFormEndorsements isDisabled={isDisabled} mode={mode} />
</Grid> */}
{/* <div className='col-span-12 justify-self-end self-center'> */}
<Grid size={12}>
<PilotFormEndorsements isDisabled={isDisabled} mode={mode} />
</Grid> */}
{/* <div className='col-span-12 justify-self-end self-center'> */}
{/* </div> */}
</div>
</DrawerBody>
<DrawerFooter>
<Button
disabled={
isDisabled && mode.toString() !== FormMode.VIEW
? isDisabled
: false
}
startContent={<FontAwesomeIcon icon={faXmark} />}
onPress={onCancel}
data-testid="pilot-cancel-button"
>
{mode.toString() !== FormMode.VIEW ? 'Cancel' : 'Close'}
</Button>
{mode.toString() !== FormMode.VIEW && (
<Button
color='primary'
disabled={isDisabled}
startContent={<FontAwesomeIcon icon={faSave} />}
type="submit"
data-testid="pilot-save-button"
<div className='col-span-12 justify-self-end self-center'>
<button
className='btn'
disabled={
isDisabled && mode.toString() !== FormMode.VIEW
? isDisabled
: false
}
onClick={onCancel}
data-testid="pilot-cancel-button"
>
Save
</Button>
)}
</DrawerFooter>
</form>
</FormProvider>
</DrawerContent>
</Drawer>
<FontAwesomeIcon icon={faXmark} />
{mode.toString() !== FormMode.VIEW ? 'Cancel' : 'Close'}
</button>
{mode.toString() !== FormMode.VIEW && (
<button
className='btn btn-primary ml-2.5'
disabled={isDisabled}
type="submit"
data-testid="pilot-save-button"
>
<FontAwesomeIcon icon={faSave} />
Save
</button>
)}
</div>
</div>
</form>
</FormProvider>
</div>
</div>
</div>
);
};

View File

@@ -10,11 +10,11 @@ import { UserRole } from '../../enums/userRole';
import httpClient from '../../httpClient/httpClient'
import { ScreenSize } from '../../enums/screenSize';
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 { faEllipsisVertical, faPen, faEye, faTrash, faPlus } from '@fortawesome/free-solid-svg-icons';
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, HeaderContext, useReactTable } from '@tanstack/react-table';
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, useReactTable } from '@tanstack/react-table';
import { Pilot } from './Pilot.interface';
import Alert from '../alert/Alert';
const Pilots: React.FC<unknown> = () => {
const [state, dispatch] = useReducer(reducer, initialState);
@@ -38,7 +38,7 @@ const Pilots: React.FC<unknown> = () => {
dispatch({ type: 'SET_ALERT', payload: undefined })
}
} 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: [] });
}
} catch (error) {
@@ -46,7 +46,7 @@ const Pilots: React.FC<unknown> = () => {
dispatch({
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 {
dispatch({ type: 'SET_IS_LOADING', payload: false })
@@ -105,7 +105,7 @@ const Pilots: React.FC<unknown> = () => {
dispatch({
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 {
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: false });
@@ -131,58 +131,22 @@ const Pilots: React.FC<unknown> = () => {
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">
<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>
<li><a onClick={() => onDeletePilot(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
</ul>
</div>
)
}
}
]
const renderCell = (pilot: any, columnKey: any) => {
const cellValue = pilot[columnKey]
switch (columnKey) {
case 'actions': {
return (
<Dropdown>
<DropdownTrigger>
<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
}
}
}
const textAlignment = {
center: 'text-center',
left: 'text-start',
@@ -223,12 +187,14 @@ const Pilots: React.FC<unknown> = () => {
{!state.isLoading && state.alert && (
<div className='col-span-12'>
<Alert
className='mb-5'
onClose={() =>
dispatch({ type: 'SET_ALERT', payload: undefined })
dispatch({ type: 'SET_FORM_ALERT', payload: undefined })
}
color={state.alert.severity}
title={state.alert.message}
/>
severity={state.alert.severity}
>
{state.alert.message}
</Alert>
</div>
)}
<div className='col-span-12 bg-base-100 p-5 border border-base-100 rounded-lg'>

View File

@@ -3,7 +3,6 @@ import { useAppContext } from '../../hooks/appContext/UseAppContext';
import { AxiosResponse } from 'axios';
import { User } from '@microsoft/microsoft-graph-types';
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 { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlane, faSignIn, faSignOut } from '@fortawesome/free-solid-svg-icons'
@@ -13,7 +12,6 @@ const SiteNav = () => {
const [userPhoto, setUserPhoto] = useState<string>();
const appContext = useAppContext();
const { isUserLoggedIn, logout, login } = useOidc()
const { pathname } = useLocation()
const pages = [
{
name: 'Flights',
@@ -57,8 +55,8 @@ const SiteNav = () => {
const setUserProfile = async () => {
try {
const userProfile = await getUserProfile();
const userPhoto = await getUserPhoto();
console.log(userPhoto)
// const userPhoto = await getUserPhoto();
// setUserPhoto(userPhoto);
appContext.dispatch({
@@ -81,7 +79,7 @@ const SiteNav = () => {
return (
<div className="navbar bg-base-100 shadow-sm w-full">
<div className="navbar-start ml-8">
<div className="dropdown">
{/* <div className="dropdown">
<div tabIndex={0} role="button" className="btn btn-ghost lg:hidden">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h8m-8 6h16" /> </svg>
</div>
@@ -98,7 +96,7 @@ const SiteNav = () => {
</li>
<li><a>Item 3</a></li>
</ul>
</div>
</div> */}
<img
height={35}
width={35}
@@ -107,7 +105,7 @@ const SiteNav = () => {
/>
<FontAwesomeIcon icon={faPlane} size='2x' />
</div>
<div className="navbar-center hidden lg:flex">
<div className="navbar-center lg:flex">
<ul className="menu menu-horizontal px-1">
{/* <li><a>Item 1</a></li>
<li>

View File

@@ -7,7 +7,6 @@ import ConfirmationDialog from "../confirmationDialog/ConfirmationDialog";
import { initialState, reducer } from "./reducer";
import TrackMap from "../trackMap/TrackMap";
import httpClient from "../../httpClient/httpClient";
import { Button, Input, Spinner } from '@heroui/react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUpload, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
import { useLogbookContext } from "../../hooks/logbookContext/UseLogbookContext";
@@ -28,7 +27,7 @@ const TracksForm = () => {
} catch (error) {
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 }})
}
}
@@ -120,25 +119,22 @@ const TracksForm = () => {
return (
<>
<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 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 className='col-span-12'>
<Button
as='label'
color='primary'
isDisabled={state.isDisabled}
fullWidth={true}
startContent={<FontAwesomeIcon icon={faUpload} />}
<label
className='btn cursor-pointer w-full'
>
<FontAwesomeIcon icon={faUpload} />
Upload Track
<input hidden onChange={handleFileUpload} type='file' />
</Button>
<input className='hidden' id='track-upload' onChange={handleFileUpload} type='file' />
</label>
</div>
{state.isConfirmDialogOpen && (
<ConfirmationDialog

View File

@@ -15,7 +15,7 @@ httpClient.interceptors.request.use(async (config) => {
if (oidc.isUserLoggedIn) {
const { accessToken } = await oidc.getTokens();
console.log(accessToken)
config.headers.Authorization = `Bearer ${accessToken}`;
}

View File

@@ -1,4 +1,4 @@
export interface Alert {
severity: 'danger' | 'default' | 'success' | 'warning';
severity: 'info' | 'error' | 'success' | 'warning';
message: string;
}

View File

@@ -5,6 +5,7 @@ interface ImportMetaEnv {
readonly VITE_CLIENT_ID: string;
readonly VITE_ISSUER_URI: string;
readonly VITE_TENANT_ID: string;
readonly VITE_USERINFO_ENDPOINT: string;
}
interface ImportMeta {

Binary file not shown.

View File

@@ -74,6 +74,16 @@ resource "azurerm_container_app" "container_app" {
secret_name = "azure-storage-connection-string"
}
env {
name = "AUTHORITY"
value = var.AUTHORITY
}
env {
name = "AUDIENCE"
value = var.CLIENT_ID
}
env {
name = "CLIENT_ID"
value = var.CLIENT_ID
@@ -84,6 +94,16 @@ resource "azurerm_container_app" "container_app" {
secret_name = "client-secret"
}
env {
name = "ISSUER_URL"
value = var.ISSUER_URL
}
env {
name = "JWKS_URI"
value = var.JWKS_URI
}
env {
name = "TENANT_ID"
value = var.EXTERNAL_TENANT_ID

View File

@@ -1,3 +1,6 @@
variable "AUTHORITY" {
type = string
}
variable "CLIENT_ID" {
type = string
@@ -17,6 +20,14 @@ variable "DOCKER_IO_USERNAME" {
type = string
}
variable "ISSUER_URL" {
type = string
}
variable "JWKS_URI" {
type = string
}
variable "RESOURCE_GROUP_NAME" {
type = string
}

3060
package-lock.json generated

File diff suppressed because it is too large Load Diff