From 51de91f8d427cf7cfe39e15ed11ce3316587269a Mon Sep 17 00:00:00 2001 From: Noah Spannbauer Date: Sun, 11 Jan 2026 10:51:28 -0600 Subject: [PATCH] minor fixes --- client/package.json | 1 - .../src/components/actionMenu/ActionMenu.tsx | 93 ---- .../actionMenu/IActionMenuProps.tsx | 8 - client/src/components/flights/Flights.tsx | 28 +- client/src/components/logbook/Logbook.tsx | 287 +++++------ client/src/components/logbook/columns.tsx | 291 ----------- .../components/logbookCard/LogbookCard.tsx | 4 +- .../logbookDrawer/LogbookDrawer.tsx | 35 +- client/src/components/pilotCard/PilotCard.tsx | 48 +- client/src/components/pilotForm/PilotForm.tsx | 32 +- .../PilotFormCertificates.tsx | 298 ++++++------ .../PilotFormEndorsements.tsx | 250 +++++----- .../pilotFormMedical/PilotFormMedical.tsx | 148 +++--- client/src/components/pilots/Pilots.tsx | 18 +- .../src/components/tracksForm/TracksForm.tsx | 2 +- package-lock.json | 455 +----------------- 16 files changed, 558 insertions(+), 1440 deletions(-) delete mode 100644 client/src/components/actionMenu/ActionMenu.tsx delete mode 100644 client/src/components/actionMenu/IActionMenuProps.tsx delete mode 100644 client/src/components/logbook/columns.tsx diff --git a/client/package.json b/client/package.json index a901e52..d741fda 100644 --- a/client/package.json +++ b/client/package.json @@ -14,7 +14,6 @@ "@fortawesome/fontawesome-svg-core": "^7.1.0", "@fortawesome/free-solid-svg-icons": "^7.1.0", "@fortawesome/react-fontawesome": "^3.1.0", - "@noahspan/noahspan-components": "^2.0.0-alpha-14", "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.1.13", "@tanstack/react-table": "^8.21.3", diff --git a/client/src/components/actionMenu/ActionMenu.tsx b/client/src/components/actionMenu/ActionMenu.tsx deleted file mode 100644 index 4988493..0000000 --- a/client/src/components/actionMenu/ActionMenu.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState } from 'react'; -import { IActionMenuProps } from './IActionMenuProps'; -import { - IconButton, - Icon, - IconName, - Dropdown -} from '@noahspan/noahspan-components'; -import { FormMode } from '../../enums/formMode'; -import { useAuth } from 'react-oidc-context'; - -const ActionMenu = ({ id, onDelete, onOpenCloseForm, onOpenCloseTracks }: IActionMenuProps) => { - // const [anchorElAction, setAnchorElAction] = useState( - // null - // ); - // const auth = useAuth(); - - // const onOpenActionMenu = (event: React.MouseEvent) => { - // setAnchorElAction(event.currentTarget); - // }; - - // const onCloseActionMenu = () => { - // setAnchorElAction(null); - // }; - - const options = [ - 'Item 1', - 'Item 2' - ] - - return ( - <> - console.log('clicked!')} - options={options} - > - - - - - - //
- // - // - // - // - // {auth.isAuthenticated && - // <> - // onOpenCloseForm(FormMode.EDIT, id)}> - // - // - // - // Edit - // - // {onOpenCloseTracks && - // onOpenCloseTracks!(FormMode.EDIT, id)}> - // - // - // - // Tracks - // - // } - // - - // } - // onOpenCloseForm(FormMode.VIEW, id)}> - // - // - // - // View - // - // {auth.isAuthenticated && - // <> - //
- // onDelete(id)}> - // - // - // - // Delete - // - // - // } - //
- //
- ); -}; - -export default ActionMenu; diff --git a/client/src/components/actionMenu/IActionMenuProps.tsx b/client/src/components/actionMenu/IActionMenuProps.tsx deleted file mode 100644 index 1194814..0000000 --- a/client/src/components/actionMenu/IActionMenuProps.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { FormMode } from '../../enums/formMode'; - -export interface IActionMenuProps { - id: string; - onDelete: (entryId: string) => void; - onOpenCloseForm: (formMode: FormMode, id: string) => void; - onOpenCloseTracks?: (formMode: FormMode, id: string) => void; -} diff --git a/client/src/components/flights/Flights.tsx b/client/src/components/flights/Flights.tsx index c903654..cf559e0 100644 --- a/client/src/components/flights/Flights.tsx +++ b/client/src/components/flights/Flights.tsx @@ -48,29 +48,17 @@ const Flights = () => { } - {/* {logsLoading && [...Array(6)].map((_element, index) => { + {logsLoading && [...Array(5)].map((_element, index) => { return ( -
- -
-
-
-
- {[...Array(4)].map((_element, index) => { - return ( - <> -
-
- - ) - })} -
-
+
+
+
+
+
+
) - })} */} + })}
) } diff --git a/client/src/components/logbook/Logbook.tsx b/client/src/components/logbook/Logbook.tsx index 3b3ed69..c2957e4 100644 --- a/client/src/components/logbook/Logbook.tsx +++ b/client/src/components/logbook/Logbook.tsx @@ -1,8 +1,7 @@ -import { useEffect, useReducer } from 'react'; +import { useEffect, useReducer, useState } from 'react'; import { initialState, reducer } from './reducer'; import { AxiosError, AxiosResponse } from 'axios'; import { FormMode } from '../../enums/formMode'; -import { authColumns, unauthColumns } from './columns'; import ConfirmationDialog from '../confirmationDialog/ConfirmationDialog'; import { LogbookEntry } from './LogbookEntry.interface'; import LogbookCard from '../logbookCard/LogbookCard'; @@ -21,6 +20,7 @@ import Alert from '../alert/Alert'; const Logbook: React.FC = () => { const [state, dispatch] = useReducer(reducer, initialState); + const [columnVisibility, setColumnVisibility] = useState({}) const logbookContext = useLogbookContext() const { isUserLoggedIn } = useOidc(); const { userRole } = useUserRole(); @@ -51,7 +51,7 @@ const Logbook: React.FC = () => { accessorKey: 'date', header: 'Date', cell: (info: CellContext) => { - const date = new Date(info.getValue() as string); + const date = new Date((info.getValue() as string).replace('Z', '')); const formattedDate = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}` return formattedDate; @@ -118,7 +118,7 @@ const Logbook: React.FC = () => { return (
-
    +
      {isUserLoggedIn && userRole === UserRole.WRITE &&
    • onOpenCloseDrawer(FormMode.EDIT, info.row.original.id)}>Edit
    • } @@ -132,16 +132,7 @@ const Logbook: React.FC = () => { } } - const unauthColumns: ColumnDef[] = [ - pilotName, - date, - aircraftMakeModel, - route, - durationOfFlight, - notes - ] - - const authColumns: ColumnDef[] = [ + const columns: ColumnDef[] = [ pilotName, date, aircraftMakeModel, @@ -345,18 +336,46 @@ const Logbook: React.FC = () => { }, notes, actions - ] + ]; + + const table = useReactTable({ + data: state.entries, + columns: columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onColumnVisibilityChange: setColumnVisibility, + state: { + columnVisibility: columnVisibility + } + }); const getLogbookEntries = async () => { try { dispatch({ type: 'SET_IS_LOADING', payload: true }); const response: AxiosResponse = await httpClient.get(`api/logs`); - const entries: LogbookEntry[] = response.data; - entries.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) if (response.data.length > 0) { - dispatch({ type: 'SET_ENTRIES', payload: response.data }); + const entries: LogbookEntry[] = response.data; + const entryColumns: string[] = Object.keys(entries[0]); + const columnVisibility: {[key: string]: boolean} = {} + + for (const column of table.getAllLeafColumns()) { + const entryColumnExists = entryColumns.find((entryColumn) => entryColumn === column.id); + + if (entryColumnExists) { + columnVisibility[column.id] = true + } else { + columnVisibility[column.id] = false + } + } + + columnVisibility['pilotName'] = true + columnVisibility['actions'] = true + + entries.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + setColumnVisibility(columnVisibility) + dispatch({ type: 'SET_ENTRIES', payload: entries }); if (state.alert) { dispatch({ type: 'SET_ALERT', payload: undefined}) @@ -441,44 +460,6 @@ const Logbook: React.FC = () => { }); }; - // useEffect(() => { - // let newColumns: ColumnDef[]; - - // if (userRole === UserRole.WRITE) { - // newColumns = [...authColumns]; - // } else { - // newColumns = [...unauthColumns]; - // } - - // const actionsColumnExists = newColumns.find((column) => column.id === 'actions'); - // const tracksColumnExists = newColumns.find((column) => column.id === 'actions'); - - // if (!actionsColumnExists) { - // newColumns.push(actionsColumn); - // } - - // if (!tracksColumnExists) { - // const notesColumnIndex = newColumns.findIndex((column) => column.id === 'notes') - - // newColumns.splice(notesColumnIndex, 0, tracksColumn) - // } - - // dispatch({ type: 'SET_COLUMNS', payload: newColumns }) - // }, [isUserLoggedIn]) - - const table = useReactTable({ - data: state.entries, - columns: authColumns, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel() - }); - - const textAlignment = { - center: 'text-center', - left: 'text-start', - right: 'text-end' - }; - useEffect(() => { if (!logbookContext.state.isDrawerOpen) { getLogbookEntries(); @@ -493,14 +474,6 @@ const Logbook: React.FC = () => {
{userRole === UserRole.WRITE && - //
)} - {!state.isLoading && ( + {!state.isLoading && state.entries.length > 0 && screenSize !== ScreenSize.SM && (
- {state.entries.length > 0 && screenSize !== ScreenSize.SM && ( -
- - - {table.getHeaderGroups().map((headerGroup, headerGroupIndex) => ( - - {headerGroup.headers.map((header, headerIndex) => { - return ( - - ); - })} - - ))} - - - <> - {table.getRowModel().rows.map((row) => { +
+
- {header.isPlaceholder ? null : ( -
- {flexRender( - header.column.columnDef.header, - header.getContext() - )} - {/* {header.column.getCanFilter() ? ( -
- -
- ) : null} */} -
- )} -
+ + {table.getHeaderGroups().map((headerGroup, headerGroupIndex) => ( + + {headerGroup.headers.map((header, headerIndex) => { return ( - - {row.getVisibleCells().map((cell) => { - return ( - + ) : null} */} + + )} + ); })} - - - - {table.getFooterGroups().map((footerGroup, index) => { - if (index === 0) { - return ( - - {footerGroup.headers.map((header) => { - return ( - - ); - })} - - ); - } + + ))} + + + <> + {table.getRowModel().rows.map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + ); + })} + + ); })} - -
*]: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() - )} +
+ {header.isPlaceholder ? null : ( +
+ {flexRender( + header.column.columnDef.header, + header.getContext() + )} + {/* {header.column.getCanFilter() ? ( +
+
- - ); - })} -
-
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.footer, - header.getContext() - ) - } -
-
*]: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() + )} +
+
-
- )} - {state.entries.length > 0 && screenSize === ScreenSize.SM && - - } + + + + {table.getFooterGroups().map((footerGroup, index) => { + if (index === 0) { + return ( + + {footerGroup.headers.map((header) => { + return ( + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.footer, + header.getContext() + ) + } +
+ + ); + })} + + ); + } + })} + + +
)} - {/* {state.isLoading && !state.alert && ( - <> -
- + {state.entries.length > 0 && screenSize === ScreenSize.SM && + + } + {state.isLoading && !state.alert && ( +
+
+
-
+
Loading...
- - )} */} +
+ )}
{logbookContext.state.isDrawerOpen && ( ): 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; - - if (values.length > 0) { - total = values.reduce((accumulator, currentValue) => accumulator + currentValue, total) - } - - return total; -} - -const pilotName: ColumnDef = { - id: 'pilotName', - accessorKey: 'pilot', - header: 'Pilot', - footer: 'PAGE TOTALS', - cell: (info: CellContext) => { - const pilot: any = info.getValue(); - - return pilot.name - } -} -const date: ColumnDef = { - id: 'date', - accessorKey: 'date', - header: 'Date', - cell: (info: CellContext) => { - const date = new Date(info.getValue() as string); - const formattedDate = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}` - - return formattedDate; - } -} -const aircraftMakeModel: ColumnDef = { - id: 'aircraftMakeModel', - accessorKey: 'aircraftMakeModel', - header: 'Aircraft Make & Model' -} -const route: ColumnDef = { - id: 'route', - header: 'Route of Flight', - meta: { - headerAlign: 'text-center', - className: 'border-l border-base-400' - }, - columns: [ - { - id: 'routeFrom', - accessorKey: 'routeFrom', - header: 'From' - }, - { - id: 'routeTo', - accessorKey: 'routeTo', - header: 'To' - } - ] -} -const durationOfFlight: ColumnDef = { - id: 'durationOfFlight', - accessorKey: 'durationOfFlight', - header: 'Duration Of Flight', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '' -} -const notes: ColumnDef = { - id: 'notes', - accessorKey: 'notes', - header: 'Notes' -} - -export const unauthColumns: ColumnDef[] = [ - pilotName, - date, - aircraftMakeModel, - route, - durationOfFlight, - notes -] - -export const authColumns: ColumnDef[] = [ - pilotName, - date, - aircraftMakeModel, - { - id: 'aircraftIdentity', - accessorKey: 'aircraftIdentity', - header: 'Aircraft Identity', - }, - route, - durationOfFlight, - { - id: 'singleEngineLand', - accessorKey: 'singleEngineLand', - header: 'Single Engine Land', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '' - }, - { - id: 'landings', - header: 'Landings', - meta: { - headerAlign: 'text-center' - }, - columns: [ - { - id: 'landingsDay', - accessorKey: 'landingsDay', - header: 'Day', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - } - }, - { - id: 'landingsNight', - accessorKey: 'landingsNight', - header: 'Night', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - } - } - ] - }, - { - id: 'instrument', - header: 'Instrument', - meta: { - headerAlign: 'text-center' - }, - columns: [ - { - id: 'instrumentActual', - accessorKey: 'instrumentActual', - header: 'Actual', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '', - }, - { - id: 'instrumentSimulated', - accessorKey: 'instrumentSimulated', - header: 'Simulated', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '' - }, - { - id: 'instrumentApproaches', - accessorKey: 'instrumentApproaches', - header: 'Approaches', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - } - }, - { - id: 'instrumentHolds', - accessorKey: 'instrumentHolds', - header: 'Holds', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - } - }, - { - id: 'instrumentNavTrack', - accessorKey: 'instrumentNavTrack', - header: 'Nav/Track', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - } - } - ] - }, - { - id: 'experienceTraining', - header: 'Type of pilot experience or training', - meta: { - headerAlign: 'text-center' - }, - columns: [ - { - id: 'groundTrainingReceived', - accessorKey: 'groundTrainingReceived', - header: 'Ground Training Received', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '' - }, - { - id: 'flightTrainingReceived', - accessorKey: 'flightTrainingReceived', - header: 'Flight Training Received', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '' - }, - { - id: 'crossCountry', - accessorKey: 'crossCountry', - header: 'Cross Country', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '' - }, - { - id: 'night', - accessorKey: 'night', - header: 'Night', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '' - }, - { - id: 'solo', - accessorKey: 'solo', - header: 'Solo', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '' - }, - { - id: 'pilotInCommand', - accessorKey: 'pilotInCommand', - header: 'Pilot In Command', - footer: (info: HeaderContext) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '', - meta: { - align: 'text-right', - headerAlign: 'text-right' - }, - cell: (info: CellContext) => - info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '' - } - ] - }, - notes -] \ No newline at end of file diff --git a/client/src/components/logbookCard/LogbookCard.tsx b/client/src/components/logbookCard/LogbookCard.tsx index 98843d3..c8143a7 100644 --- a/client/src/components/logbookCard/LogbookCard.tsx +++ b/client/src/components/logbookCard/LogbookCard.tsx @@ -5,8 +5,8 @@ const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps return (
{logs.map((log) => { - const date = new Date(log.date); - const formattedDate: string = `${date.getMonth()}/${date.getDate()}/${date.getFullYear()}`; + const date = new Date(log.date.replace('Z', '')); + const formattedDate: string = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`; return (
diff --git a/client/src/components/logbookDrawer/LogbookDrawer.tsx b/client/src/components/logbookDrawer/LogbookDrawer.tsx index 1f47e3d..2e6f5c8 100644 --- a/client/src/components/logbookDrawer/LogbookDrawer.tsx +++ b/client/src/components/logbookDrawer/LogbookDrawer.tsx @@ -9,7 +9,7 @@ import { FormMode } from "../../enums/formMode"; import httpClient from "../../httpClient/httpClient"; import { AxiosError } from "axios"; import { useLogbookContext } from "../../hooks/logbookContext/UseLogbookContext"; -import { useState } from "react"; +import { ChangeEventHandler, MouseEventHandler, useState } from "react"; const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => { const [activeTab, setActiveTab] = useState('time'); @@ -73,16 +73,12 @@ const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => { setActiveTab(tab) } + const onTabClicked = (event: any) => { + setActiveTab(event.target.ariaLabel) + } + return ( -
- // - // - // } - // isOpen={logbookContext.state.isDrawerOpen} - // onClose={onCancel} - > +
{}} checked={logbookContext.state.isDrawerOpen} />