104 switch to daisyui #107

Merged
noahspannbauer merged 8 commits from 104-switch-to-daisyui into main 2025-12-29 22:51:56 -05:00
14 changed files with 150 additions and 143 deletions
Showing only changes of commit d0c4fed31b - Show all commits

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -10,15 +10,15 @@ async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: 'http://localhost:8080', // Allow requests from your frontend's origin
origin: 'http://localhost:8080',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true, // If you need to send cookies or authorization headers
credentials: true,
});
app.setGlobalPrefix('api');
app.useGlobalFilters(new HttpExceptionFilter());
app.use(
session({
secret: 'blah',
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
})

View File

@@ -31,7 +31,6 @@ export class PilotInterceptor implements NestInterceptor {
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

@@ -0,0 +1,44 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AlertProps } from "./AlertProps.interface";
import { faCircleCheck, faCircleInfo, faCircleXmark, faTriangleExclamation, faXmark } from "@fortawesome/free-solid-svg-icons";
const Alert = ({
children,
className,
closeIcon,
severity,
onClose,
...rest
}: AlertProps) => {
const severityVariants = {
info: 'alert-info',
error: 'alert-error',
success: 'alert-success',
warning: 'alert-warning'
};
return (
<>
<div
role='alert'
className={`alert ${severity ? severityVariants[severity] : ''} ${className ? className : ''}`}
{...rest}
>
<span>
{severity === 'info' && <FontAwesomeIcon icon={faCircleInfo} />}
{severity === 'error' && <FontAwesomeIcon icon={faCircleXmark} />}
{severity === 'success' && (
<FontAwesomeIcon icon={faCircleCheck} />
)}
{severity === 'warning' && (
<FontAwesomeIcon icon={faTriangleExclamation} />
)}
</span>
<span>{children}</span>
{closeIcon && <button className='btn' onClick={onClose}>{<FontAwesomeIcon icon={faXmark} />}</button>}
</div>
</>
);
};
export default Alert;

View File

@@ -0,0 +1,7 @@
export interface AlertProps {
children?: React.ReactNode;
className?: string;
closeIcon?: React.ReactNode;
onClose?: () => void;
severity: 'info' | 'error' | 'success' | 'warning';
}

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useReducer } from 'react';
import { useForm, Controller, FormProvider, useFormContext } from 'react-hook-form';
import { useEffect, useReducer } from 'react';
import { useForm, Controller, useFormContext } from 'react-hook-form';
import { LogFormProps } from './LogFormProps.interface';
import { initialState, reducer } from './reducer';
import { AxiosError, AxiosResponse } from 'axios';
@@ -71,15 +71,13 @@ const LogForm = () => {
<Controller
name="pilotId"
control={control}
render={({ field: { value } }) => {
render={({ field: { onChange, value } }) => {
return (
<select
className='select w-full'
aria-labelledby='pilot'
disabled={state.isDisabled}
// onChange={(keys: SharedSelection) => {
// setValue('pilotId', keys.currentKey);
// }}=
onChange={onChange}
value={[value]}
>
{state.pilotOptions?.map((pilotOption: { key: string; label: string, }) => {
@@ -106,20 +104,8 @@ const LogForm = () => {
className='input w-full'
disabled={state.isDisabled}
onChange={onChange}
value={value}
value={value ? value.split('T')[0] : ''}
/>
// <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}
// />
)
}}
/>

View File

@@ -119,9 +119,13 @@ const Logbook: React.FC<unknown> = () => {
<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={() => onOpenCloseDrawer(FormMode.EDIT, info.row.original.id)}><FontAwesomeIcon icon={faPen} />Edit</a></li>
{isUserLoggedIn && userRole === UserRole.WRITE &&
<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} />Delete</a></li>
{isUserLoggedIn && userRole === UserRole.WRITE &&
<li><a onClick={() => onDeleteLog(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
}
</ul>
</div>
)

View File

@@ -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 { Key, useState } from "react";
import { useState } from "react";
const LogbookDrawer = ({ onOpenClose }: LogbookDrawerProps) => {
const [activeTab, setActiveTab] = useState<string>('time');

View File

@@ -15,11 +15,13 @@ import { faEllipsisVertical, faPen, faEye, faTrash, faPlus } from '@fortawesome/
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, useReactTable } from '@tanstack/react-table';
import { Pilot } from './Pilot.interface';
import Alert from '../alert/Alert';
import { useOidc } from '../../auth/oidcConfig';
const Pilots: React.FC<unknown> = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const { userRole } = useUserRole();
const { screenSize } = useBreakpoints()
const { screenSize } = useBreakpoints();
const { isUserLoggedIn } = useOidc();
const getPilots = async () => {
try {
@@ -137,9 +139,13 @@ const Pilots: React.FC<unknown> = () => {
<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>
{isUserLoggedIn && userRole === UserRole.WRITE &&
<li><a onClick={() => onOpenClosePilotForm(FormMode.EDIT, info.row.original.id)}><FontAwesomeIcon icon={faPen} />Edit</a></li>
}
<li><a onClick={() => onOpenClosePilotForm(FormMode.VIEW, info.row.original.id)}><FontAwesomeIcon icon={faEye} />View</a></li>
<li><a onClick={() => onDeletePilot(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
{isUserLoggedIn && userRole === UserRole.WRITE &&
<li><a onClick={() => onDeletePilot(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
}
</ul>
</div>
)

View File

@@ -5,12 +5,15 @@ import { User } from '@microsoft/microsoft-graph-types';
import { useOidc } from '../../auth/oidcConfig';
import httpClient from '../../httpClient/httpClient'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlane, faSignIn, faSignOut } from '@fortawesome/free-solid-svg-icons'
import { faBars, faPlane, faSignIn, faSignOut } from '@fortawesome/free-solid-svg-icons'
import { NavLink, useLocation } from 'react-router-dom';
import { useBreakpoints } from '../../hooks/useBreakpoints/UseBreakpoints';
import { ScreenSize } from '../../enums/screenSize';
const SiteNav = () => {
const [userPhoto, setUserPhoto] = useState<string>();
const appContext = useAppContext();
const { screenSize } = useBreakpoints();
const { isUserLoggedIn, logout, login } = useOidc()
const pages = [
{
@@ -51,6 +54,32 @@ const SiteNav = () => {
}
};
const Brand = () => {
return (
<>
<img
className='mr-1'
height={35}
width={35}
src='noahspan-logo.png'
/>
<FontAwesomeIcon className='mt-1' icon={faPlane} size='2x' />
</>
)
}
const Links = () => {
return (
<>
{pages.map((page) => {
return (
<li><NavLink to={page.path}>{page.name}</NavLink></li>
)
})}
</>
)
}
useEffect(() => {
const setUserProfile = async () => {
try {
@@ -76,56 +105,38 @@ const SiteNav = () => {
}
}, [isUserLoggedIn]);
useEffect(() => {
console.log(screenSize)
}, [screenSize])
return (
<div className="navbar bg-base-100 shadow-sm w-full">
<div className="navbar-start ml-8">
{/* <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 className={`navbar-start ${screenSize !== ScreenSize.SM && screenSize !== ScreenSize.MD ? 'ml-8' : ''}`}>
{screenSize === ScreenSize.SM || screenSize === ScreenSize.MD ? (
<div className="dropdown">
<div tabIndex={0} role="button" className="btn btn-ghost lg:hidden">
<FontAwesomeIcon icon={faBars} size='xl' />
</div>
<ul
tabIndex={-1}
className="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
<Links />
</ul>
</div>
<ul
tabIndex={-1}
className="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
<li><a>Item 1</a></li>
<li>
<a>Parent</a>
<ul className="p-2">
<li><a>Submenu 1</a></li>
<li><a>Submenu 2</a></li>
</ul>
</li>
<li><a>Item 3</a></li>
</ul>
</div> */}
<img
height={35}
width={35}
src='noahspan-logo.png'
style={{ marginRight: '5px' }}
/>
<FontAwesomeIcon icon={faPlane} size='2x' />
) : (
<Brand />
)}
</div>
<div className="navbar-center lg:flex">
<div className="navbar-center">
<ul className="menu menu-horizontal px-1">
{/* <li><a>Item 1</a></li>
<li>
<details>
<summary>Parent</summary>
<ul className="p-2 bg-base-100 w-40 z-1">
<li><a>Submenu 1</a></li>
<li><a>Submenu 2</a></li>
</ul>
</details>
</li>
<li><a>Item 3</a></li> */}
{pages.length > 0 && pages.map((page, index) => {
return (
<li><NavLink to={page.path}>{page.name}</NavLink></li>
)
})}
{screenSize === ScreenSize.SM || screenSize === ScreenSize.MD ? (
<Brand />
) : (
<Links />
)}
</ul>
</div>
<div className="navbar-end mr-8">
<div className={`navbar-end ${screenSize !== ScreenSize.SM && screenSize !== ScreenSize.MD ? 'ml-8' : ''}`}>
{!isUserLoggedIn &&
<button className='btn btn-ghost' onClick={() => login()}><FontAwesomeIcon icon={faSignIn} />Sign In</button>
}
@@ -155,53 +166,6 @@ const SiteNav = () => {
}
</div>
</div>
// <Navbar isBordered maxWidth='full' position='static'>
// <NavbarContent>
// <NavbarBrand>
// <img
// height={35}
// width={35}
// src='noahspan-logo.png'
// style={{ marginRight: '5px' }}
// />
// <FontAwesomeIcon icon={faPlane} size='2x' />
// </NavbarBrand>
// </NavbarContent>
// <NavbarContent justify='center'>
// {pages.length > 0 && pages.map((page, index) => {
// return (
// <NavbarItem isActive={pathname === page.path ? true : false} key={index}>
// <Link color={pathname === page.path ? 'primary' : 'foreground'} href={page.path}>
// {page.name}
// </Link>
// </NavbarItem>
// )
// })}
// </NavbarContent>
// <NavbarContent justify='end'>
// {!isUserLoggedIn &&
// <Button
// color='default'
// onPress={() => login()}
// startContent={<FontAwesomeIcon icon={faSignIn} />}
// >
// Sign In
// </Button>
// }
// {isUserLoggedIn &&
// <Dropdown>
// <DropdownTrigger>
// <Avatar name={appContext.state.userProfile.displayName?.toString()} src={userPhoto}></Avatar>
// </DropdownTrigger>
// <DropdownMenu>
// <DropdownItem key='signout' onPress={() => logout({redirectTo: 'specific url', url: '/'})} startContent={<FontAwesomeIcon icon={faSignOut} />}>
// Sign Out
// </DropdownItem>
// </DropdownMenu>
// </Dropdown>
// }
// </NavbarContent>
// </Navbar>
);
};

View File

@@ -37,7 +37,7 @@ const TrackMap = ({ height, logId, tracks }: TrackMapProps) => {
center={[45.14489, -93.21019]}
scrollWheelZoom={false}
style={{ height: height, width: '100%' }}
zoom={8}
zoom={7}
>
<Suspense>
<TileLayer

View File

@@ -107,11 +107,6 @@ const TracksForm = () => {
return (
<div className='grid grid-cols-12 gap-3'>
{state.tracks.length > 0 &&
<div className="col-span-12">
<TrackMap height='400px' logId={logbookContext.state.selectedLogId!} tracks={state.tracks} />
</div>
}
<>
{state.tracks.length > 0 && state.tracks.map((track, index) => {
const filename: string = track.url.substring(track.url.lastIndexOf('/') + 1);
@@ -127,15 +122,17 @@ const TracksForm = () => {
</>
)
})}
<div className='col-span-12'>
<label
className='btn cursor-pointer w-full'
>
<FontAwesomeIcon icon={faUpload} />
Upload Track
<input className='hidden' id='track-upload' onChange={handleFileUpload} type='file' />
</label>
</div>
{logbookContext.state.formMode === FormMode.ADD &&
<div className='col-span-12'>
<label
className='btn cursor-pointer w-full'
>
<FontAwesomeIcon icon={faUpload} />
Upload Track
<input className='hidden' id='track-upload' onChange={handleFileUpload} type='file' />
</label>
</div>
}
{state.isConfirmDialogOpen && (
<ConfirmationDialog
contentText="Are you sure you want to delete this track?"

View File

@@ -1,7 +1,7 @@
export enum ScreenSize {
SM,
MD,
LG,
XL,
XXL
SM = 'SM',
MD = 'MD',
LG = 'LG',
XL = 'XL',
XXL = 'XXL'
}

View File

@@ -14,17 +14,17 @@ export const useBreakpoints = () => {
break;
}
case width >= 640: {
case width >= 640 && width < 1024: {
size = ScreenSize.MD;
break;
}
case width >= 1024: {
case width >= 1024 && width < 1280: {
size = ScreenSize.LG
break;
}
case width >= 1280: {
case width >= 1280 && width < 1536: {
size = ScreenSize.XL;
break;