Files
noahspan-flying/client/src/components/logbook/Logbook.tsx
noahspannbauer 899f92f342 126 all logs shown when unauthenticated (#131)
* fixing all logs showing when user is unauthenticated

* updating api log interceptor

* updating api log interceptor

* updating api log interceptor

* logbook page unauthenticated alert
2026-04-26 17:08:34 -05:00

785 lines
31 KiB
TypeScript

import { useEffect, useReducer, useState } from 'react';
import { initialState, reducer } from './reducer';
import { AxiosError, AxiosResponse } from 'axios';
import { FormMode } from '../../enums/formMode';
import ConfirmationDialog from '../confirmationDialog/ConfirmationDialog';
import { LogbookEntry } from './LogbookEntry.interface';
import LogbookCard from '../logbookCard/LogbookCard';
import { useOidc } from '../../auth/oidcConfig';
import httpClient from '../../httpClient/httpClient';
import { useUserRole } from '../../hooks/userRole/UseUserRole';
import { UserRole } from '../../enums/userRole';
import { useBreakpoints } from '../../hooks/useBreakpoints/UseBreakpoints';
import { ScreenSize } from '../../enums/screenSize';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faAdd, faEllipsisVertical, faPen, faEye, faTrash, faAngleLeft, faAngleRight, faAnglesLeft, faAnglesRight } from '@fortawesome/free-solid-svg-icons'
import { CellContext, ColumnDef, flexRender, getCoreRowModel, HeaderContext, PaginationState, useReactTable } from '@tanstack/react-table';
import { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
import LogbookDrawer from '../logbookDrawer/LogbookDrawer';
import Alert from '../alert/Alert';
import TrackMap from '../trackMap/TrackMap';
interface ActionsProps {
id: string;
}
const Logbook: React.FC<unknown> = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const [columnVisibility, setColumnVisibility] = useState({});
const logbookContext = useLogbookContext();
const { isUserLoggedIn } = useOidc();
const { userRole } = useUserRole();
const { screenSize } = useBreakpoints();
const Actions = ({ id }: ActionsProps) => {
return (
<div className='dropdown dropdown-end'>
<div tabIndex={0} role='button' className='btn btn-ghost p-0'><FontAwesomeIcon icon={faEllipsisVertical} /></div>
<ul tabIndex={-1} className="dropdown-content menu bg-base-100 rounded-box w-52 p-2 shadow-sm border border-base-300 !z-[100]">
{isUserLoggedIn && userRole === UserRole.WRITE &&
<li><a onClick={() => onOpenCloseDrawer(FormMode.EDIT, id)}><FontAwesomeIcon icon={faPen} />Edit</a></li>
}
<li><a onClick={() => onOpenCloseDrawer(FormMode.VIEW, id)}><FontAwesomeIcon icon={faEye} />View</a></li>
{isUserLoggedIn && userRole === UserRole.WRITE &&
<li><a onClick={() => onDeleteLog(id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
}
</ul>
</div>
)
}
const columnTotal = (info: HeaderContext<LogbookEntry, unknown>): number => {
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<LogbookEntry> = {
id: 'pilotName',
accessorKey: 'pilot',
header: 'Pilot',
footer: 'PAGE TOTALS',
cell: (info: CellContext<LogbookEntry, unknown>) => {
const pilot: any = info.getValue();
return pilot.name
}
}
const date: ColumnDef<LogbookEntry> = {
id: 'date',
accessorKey: 'date',
header: 'Date',
cell: (info: CellContext<LogbookEntry, unknown>) => {
const date = new Date((info.getValue() as string).replace('Z', ''));
const formattedDate = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`
return formattedDate;
}
}
const aircraftMakeModel: ColumnDef<LogbookEntry> = {
id: 'aircraftMakeModel',
accessorKey: 'aircraftMakeModel',
header: 'Aircraft Make & Model'
}
const route: ColumnDef<LogbookEntry> = {
id: 'route',
header: 'Route of Flight',
meta: {
className: 'border-l border-base-300',
headerAlign: 'text-center'
},
columns: [
{
id: 'routeFrom',
accessorKey: 'routeFrom',
header: 'From',
meta: {
className: 'border-l border-base-300',
}
},
{
id: 'routeTo',
accessorKey: 'routeTo',
header: 'To'
}
]
}
const durationOfFlight: ColumnDef<LogbookEntry> = {
id: 'durationOfFlight',
accessorKey: 'durationOfFlight',
header: 'Duration Of Flight',
meta: {
align: 'text-right',
className: 'border-l border-base-300',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : ''
}
const notes: ColumnDef<LogbookEntry> = {
id: 'notes',
accessorKey: 'notes',
header: 'Notes',
meta: {
className: 'border-l border-base-300',
}
}
const actions: ColumnDef<LogbookEntry> = {
id: 'actions',
header: 'Actions',
meta: {
align: 'text-center',
className: 'border-l border-base-300',
headerAlign: 'text-center'
},
cell: (info: CellContext<LogbookEntry, unknown>) => {
return (
<Actions id={info.row.original.id} />
)
}
}
const columns: ColumnDef<LogbookEntry>[] = [
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<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : ''
},
{
id: 'landings',
header: 'Landings',
meta: {
className: 'border-l border-base-300',
headerAlign: 'text-center'
},
columns: [
{
id: 'landingsDay',
accessorKey: 'landingsDay',
header: 'Day',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
meta: {
align: 'text-right',
className: 'border-l border-base-300',
headerAlign: 'text-right'
}
},
{
id: 'landingsNight',
accessorKey: 'landingsNight',
header: 'Night',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
}
}
]
},
{
id: 'instrument',
header: 'Instrument',
meta: {
className: 'border-l border-base-300',
headerAlign: 'text-center'
},
columns: [
{
id: 'instrumentActual',
accessorKey: 'instrumentActual',
header: 'Actual',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
className: 'border-l border-base-300',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : '',
},
{
id: 'instrumentSimulated',
accessorKey: 'instrumentSimulated',
header: 'Simulated',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
},
{
id: 'instrumentApproaches',
accessorKey: 'instrumentApproaches',
header: 'Approaches',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
}
},
{
id: 'instrumentHolds',
accessorKey: 'instrumentHolds',
header: 'Holds',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
}
},
{
id: 'instrumentNavTrack',
accessorKey: 'instrumentNavTrack',
header: 'Nav/Track',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
}
}
]
},
{
id: 'experienceTraining',
header: 'Type of pilot experience or training',
meta: {
className: 'border-l border-base-300',
headerAlign: 'text-center'
},
columns: [
{
id: 'groundTrainingReceived',
accessorKey: 'groundTrainingReceived',
header: 'Ground Training Received',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
className: 'border-l border-base-300',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
},
{
id: 'flightTrainingReceived',
accessorKey: 'flightTrainingReceived',
header: 'Flight Training Received',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
},
{
id: 'crossCountry',
accessorKey: 'crossCountry',
header: 'Cross Country',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
},
{
id: 'night',
accessorKey: 'night',
header: 'Night',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
},
{
id: 'solo',
accessorKey: 'solo',
header: 'Solo',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
},
{
id: 'pilotInCommand',
accessorKey: 'pilotInCommand',
header: 'Pilot In Command',
footer: (info: HeaderContext<LogbookEntry, unknown>) => columnTotal(info) > 0 ? columnTotal(info).toFixed(1) : '',
meta: {
align: 'text-right',
headerAlign: 'text-right'
},
cell: (info: CellContext<LogbookEntry, unknown>) =>
info.getValue() ? parseFloat(info.getValue() as string).toFixed(1) : ''
}
]
},
notes,
actions
];
const onPaginationChange = (updater: any) => {
const newPaginationState: PaginationState = typeof updater === 'function' ? updater(state.pagination) : updater;
dispatch({ type: 'SET_PAGINATION', payload: newPaginationState })
}
const table = useReactTable({
data: state.entries,
columns: columns,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
onColumnVisibilityChange: setColumnVisibility,
onPaginationChange: onPaginationChange,
rowCount: state.totalEntries,
state: {
columnVisibility: columnVisibility,
pagination: state.pagination
}
});
const {
firstPage,
getCanNextPage,
getCanPreviousPage,
getPageCount,
getState,
lastPage,
nextPage,
previousPage,
setPageIndex
} = table;
const getLogbookEntries = async (pageIndex: number, pageSize: number) => {
try {
dispatch({ type: 'SET_IS_LOADING', payload: true });
const response: AxiosResponse = await httpClient.get(`api/logs`, {
params: {
skip: pageIndex * pageSize,
take: pageSize
}
});
if (response.data.entities.length > 0) {
const entries: LogbookEntry[] = response.data.entities;
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: entries, totalEntries: response.data.total }});
if (!isUserLoggedIn && response.data.entities.length >= 5) {
dispatch({ type: 'SET_ALERT', payload: { severity: 'info', message: 'A limited number of log entries displayed. Sign in to view all log entries.'}})
} else {
dispatch({ type: 'SET_ALERT', payload: undefined})
}
} else {
dispatch({ type: 'SET_ALERT', payload: { severity: 'info', message: 'No logbook entries found.'}})
}
} catch (error) {
const axiosError = error as AxiosError;
dispatch({
type: 'SET_ALERT',
payload: { severity: 'error', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`}
});
} finally {
dispatch({ type: 'SET_IS_LOADING', payload: false });
}
};
const onOpenCloseDrawer= (mode: FormMode, logId?: string) => {
switch (mode) {
case FormMode.ADD:
case FormMode.EDIT:
case FormMode.VIEW:
logbookContext.dispatch({
type: 'SET_OPEN_CLOSE_DRAWER',
payload: {
formMode: mode,
selectedLogId: logId!,
isDrawerOpen: true
}
});
break;
case FormMode.CANCEL:
logbookContext.dispatch({
type: 'SET_OPEN_CLOSE_DRAWER',
payload: {
formMode: mode,
selectedLogId: undefined,
isDrawerOpen: false
}
});
break;
}
};
const onDeleteLog = (logId: string) => {
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_OPEN', payload: true });
logbookContext.dispatch({ type: 'SET_SELECTED_LOG_ID', payload: logId })
};
const onConfirmationDialogConfirm = async () => {
try {
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: true });
await httpClient.delete(`api/logs/${logbookContext.state.selectedLogId}`);
dispatch({
type: 'SET_IS_CONFIRMATION_DIALOG_OPEN',
payload: false
});
logbookContext.dispatch({ type: 'SET_SELECTED_LOG_ID', payload: '' })
await getLogbookEntries(state.pagination?.pageIndex, state.pagination?.pageSize);
} catch (error) {
const axiosError = error as AxiosError;
dispatch({
type: 'SET_ALERT',
payload: { severity: 'error', message: `Loading of logbook entries failed with the following message: ${axiosError.message}`}
});
} finally {
dispatch({ type: 'SET_IS_CONFIRMATION_DIALOG_LOADING', payload: false });
}
};
const onConfirmationDialogCancel = () => {
dispatch({
type: 'SET_IS_CONFIRMATION_DIALOG_OPEN',
payload: false
});
};
const onRowsPerPageChanged = (event: any) => {
const newPaginationState: PaginationState = {
pageIndex: 0,
pageSize: event.target.value !== 'All' ? Number(event.target.value) : state.totalEntries
};
dispatch({ type: 'SET_PAGINATION', payload: newPaginationState })
}
useEffect(() => {
if (!logbookContext.state.isDrawerOpen) {
getLogbookEntries(state.pagination.pageIndex, state.pagination.pageSize);
}
}, [logbookContext.state.isDrawerOpen, state.pagination.pageIndex, state.pagination.pageSize]);
useEffect(() => {
const pages: number[] = [];
if (state.pagination?.pageSize) {
const totalPages = getPageCount();
for(let i = 0; i < totalPages; i++) {
pages.push(i + 1)
}
}
dispatch({ type: 'SET_PAGES', payload: pages })
}, [state.totalEntries, state.pagination?.pageSize])
useEffect(() => {
console.log(state.alert)
}, [state.alert])
return (
<>
<div className={`${screenSize === ScreenSize.SM ? 'mr-4 ml-4' : 'mr-10 ml-10'} grid grid-cols-12`}>
<div className='prose max-w-none col-span-6 mt-5 mb-5'>
<h1>Logbook</h1>
</div>
<div className='col-span-6 justify-self-end self-center'>
{userRole === UserRole.WRITE &&
<button className='btn btn-primary'
onClick={() => onOpenCloseDrawer(FormMode.ADD)}
>
<FontAwesomeIcon icon={faAdd} />
Add Entry
</button>
}
</div>
{!state.isLoading && state.alert && (
<div className='col-span-12 mb-5'>
<Alert
className='mb-5'
onClose={() =>
dispatch({ type: 'SET_ALERT', payload: undefined })
}
severity={state.alert.severity}
>
{state.alert.message}
</Alert>
</div>
)}
{!state.isLoading && state.entries.length > 0 && screenSize !== ScreenSize.SM && screenSize !== ScreenSize.MD && (
<div className='col-span-12 pr-5 pb-5 pl-5 bg-base-100 border border-base-100 rounded-lg '>
<div className='overflow-x-auto mb-5'>
<div className='col-span-12 justify-self-end self-center mt-1 mr-1 mb-2'>
<label className='select select-sm select-ghost'>
<span className='label'>Rows per page</span>
<select
defaultValue={1}
onChange={onRowsPerPageChanged}
value={state.pagination?.pageSize}
>
<option value={10}>10</option>
<option value={25}>25</option>
<option value={50}>50</option>
<option value={state.totalEntries}>All</option>
</select>
</label>
</div>
<table className='table min-w-full h-auto table-auto w-full'>
<thead className='bg-base-200'>
{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}>
{headerGroup.headers.map((header, headerIndex) => {
return (
<th
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}
key={header.id}
>
{header.isPlaceholder ? null : (
<div className={`${header.column.columnDef.meta?.headerAlign ? header.column.columnDef.meta?.headerAlign : ''}`}>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{/* {header.column.getCanFilter() ? (
<div>
<Filter column={header.column} table={table} />
</div>
) : null} */}
</div>
)}
</th>
);
})}
</tr>
))}
</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={`py-2 px-3 relative align-middle whitespace-normal text-small font-normal [&>*]: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}
>
<div className={`${cell.column.columnDef.meta?.align ? cell.column.columnDef.meta?.align : ''}`}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</div>
</td>
);
})}
</tr>
);
})}
</>
</tbody>
<thead className='[&>tr]:first:rounded-lg bg-base-200'>
{table.getFooterGroups().map((footerGroup, index) => {
if (index === 0) {
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={footerGroup.id}>
{footerGroup.headers.map((header) => {
return (
<td
className='roup/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}
>
<div className={`${header.column.columnDef.meta?.headerAlign ? header.column.columnDef.meta?.headerAlign : ''}`}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.footer,
header.getContext()
)
}
</div>
</td>
);
})}
</tr>
);
}
})}
</thead>
</table>
</div>
{state.pagination.pageSize !== state.totalEntries &&
<div className='col-span-12 justify-self-center self-center'>
<div className='join'>
<button
className='join-item btn btn-sm'
onClick={firstPage}
>
<FontAwesomeIcon icon={faAnglesLeft} />
</button>
<button
className='join-item btn btn-sm'
onClick={previousPage}
disabled={!getCanPreviousPage()}
>
<FontAwesomeIcon icon={faAngleLeft} />
</button>
{state.pages.map((page, index) => {
return (
<button
className={`join-item btn btn-sm ${getState().pagination.pageIndex === index ? 'btn-active' : ''}`}
onClick={() => setPageIndex(index)}
>
{page}
</button>
)
})}
<button
className='join-item btn btn-sm'
onClick={() => nextPage()}
disabled={!getCanNextPage()}
>
<FontAwesomeIcon icon={faAngleRight} />
</button>
<button
className='join-item btn btn-sm'
onClick={lastPage}
>
<FontAwesomeIcon icon={faAnglesRight} />
</button>
</div>
</div>
}
</div>
)}
{state.entries.length > 0 && screenSize === ScreenSize.SM &&
<div className='col-span-12'>
<>
{table.getRowModel().rows.map((row) => {
const date = new Date(row.original.date.replace('Z', ''));
const formattedDate: string = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
return (
<div className='card bg-base-100 border border-base-300 mb-5'>
<div className={`card-body ${screenSize === ScreenSize.SM || screenSize === ScreenSize.MD ? 'p-4' : ''}`} key={row.id}>
<div className={`grid grid-cols-12 gap-3`}>
<>
<div className='col-span-8'>
<h2 className='card-title font-bold text-2xl'>{formattedDate}</h2>
</div>
<div className='col-span-4 justify-self-end self-center'>
<Actions id={row.original.id} />
</div>
{row.getVisibleCells().map((cell) => {
return (
<>
{cell.column.columnDef.header !== 'Actions' && cell.column.columnDef.header !== 'Date' && cell.column.columnDef.header !== 'Pilot' &&
<>
<div className='col-span-8 font-bold'>
<span>{cell.getContext().column.columnDef.header?.toString()}</span>
</div>
<div className='col-span-4'>
<span>{flexRender(cell.column.columnDef.cell, cell.getContext())}</span>
</div>
</>
}
</>
)
})}
</>
</div>
</div>
</div>
)
})}
</>
</div>
}
{state.isLoading && !state.alert && (
<div className='col-span-12 p-5 bg-base-100 border border-base-100 rounded-lg'>
<div className='col-span-12 justify-self-center'>
<span className='loading loading-spinner loading-xl' />
</div>
<div className='col-span-12 justify-self-center'>
Loading...
</div>
</div>
)}
</div>
{logbookContext.state.isDrawerOpen && (
<LogbookDrawer
onOpenClose={(mode) => onOpenCloseDrawer(mode)}
/>
)}
{state.isConfirmDialogOpen && (
<ConfirmationDialog
contentText="Are you sure you want to delete the logbook entry?"
isLoading={state.isConfirmDialogLoading}
isOpen={state.isConfirmDialogOpen}
onCancel={onConfirmationDialogCancel}
onConfirm={onConfirmationDialogConfirm}
title="Confirm Delete"
/>
)}
</>
);
};
export default Logbook;