104 switch to daisyui #106
@@ -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
|
||||
})
|
||||
|
||||
@@ -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')) {
|
||||
|
||||
44
client/src/components/alert/Alert.tsx
Normal file
44
client/src/components/alert/Alert.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { AlertProps } from "./AlertProps.interface";
|
||||
import { faCircleCheck, faCircleInfo, faCircleXmark, faTriangleExclamation, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
const Alert = ({
|
||||
children,
|
||||
className,
|
||||
closeIcon,
|
||||
severity,
|
||||
onClose,
|
||||
...rest
|
||||
}: AlertProps) => {
|
||||
const severityVariants = {
|
||||
info: 'alert-info',
|
||||
error: 'alert-error',
|
||||
success: 'alert-success',
|
||||
warning: 'alert-warning'
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
role='alert'
|
||||
className={`alert ${severity ? severityVariants[severity] : ''} ${className ? className : ''}`}
|
||||
{...rest}
|
||||
>
|
||||
<span>
|
||||
{severity === 'info' && <FontAwesomeIcon icon={faCircleInfo} />}
|
||||
{severity === 'error' && <FontAwesomeIcon icon={faCircleXmark} />}
|
||||
{severity === 'success' && (
|
||||
<FontAwesomeIcon icon={faCircleCheck} />
|
||||
)}
|
||||
{severity === 'warning' && (
|
||||
<FontAwesomeIcon icon={faTriangleExclamation} />
|
||||
)}
|
||||
</span>
|
||||
<span>{children}</span>
|
||||
{closeIcon && <button className='btn' onClick={onClose}>{<FontAwesomeIcon icon={faXmark} />}</button>}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Alert;
|
||||
7
client/src/components/alert/AlertProps.interface.ts
Normal file
7
client/src/components/alert/AlertProps.interface.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface AlertProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
closeIcon?: React.ReactNode;
|
||||
onClose?: () => void;
|
||||
severity: 'info' | 'error' | 'success' | 'warning';
|
||||
}
|
||||
@@ -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}
|
||||
// />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export enum ScreenSize {
|
||||
SM,
|
||||
MD,
|
||||
LG,
|
||||
XL,
|
||||
XXL
|
||||
SM = 'SM',
|
||||
MD = 'MD',
|
||||
LG = 'LG',
|
||||
XL = 'XL',
|
||||
XXL = 'XXL'
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user