Updated UI with accounts and fixed routing

This commit is contained in:
2025-04-13 21:35:08 -04:00
parent d5bc4cafb8
commit 592de030c8
24 changed files with 256 additions and 108 deletions

View File

@@ -0,0 +1,18 @@
import { Header } from '@components/Header';
import { Navigate } from 'react-router';
import { useUserContext } from '@components/context/UserContext.tsx';
export function Administration() {
const { user } = useUserContext();
if (user == undefined) {
return <Navigate to={'/'} />;
}
return (
<>
<Header />
Todo: administration {user?.email}
</>
);
}

View File

@@ -34,18 +34,18 @@ export default function AirportMarker({
);
}
function getMarkerColor(flightCategory: 'VFR' | 'MVFR' | 'LIFR' | 'IFR' | 'UNKN'): string {
function getMarkerInfo(flightCategory: 'VFR' | 'MVFR' | 'LIFR' | 'IFR' | 'UNKN'): [string, number] {
switch (flightCategory) {
case 'IFR':
return '#ff0100';
return ['#ff0100', 5];
case 'LIFR':
return '#7f007f';
return ['#7f007f', 6];
case 'MVFR':
return '#00f';
return ['#00f', 7];
case 'VFR':
return '#018000';
return ['#018000', 8];
case 'UNKN':
return '#696969';
return ['#696969', 4];
}
}
@@ -61,7 +61,8 @@ function createCustomIcon(airport: Airport): L.DivIcon {
background-color: white;
display: flex;
align-items: center;
justify-content: center;">
justify-content: center;
z-index: {info[1]}">
<span style="color: black; font-size: 8px; font-weight: bold;">H</span>
</div>
`,
@@ -72,15 +73,16 @@ function createCustomIcon(airport: Airport): L.DivIcon {
} else {
// Default to a filled circle.
const flightCategory = airport.latest_metar?.flight_category || 'UNKN';
const color = getMarkerColor(flightCategory);
const info = getMarkerInfo(flightCategory);
if (flightCategory == 'UNKN') {
return L.divIcon({
html: `
<div style="
background-color: ${color};
background-color: ${info[0]};
width: 10px;
height: 10px;
border-radius: 50%;">
border-radius: 50%;
z-index: {info[1]}">
</div>
`,
className: '',
@@ -91,11 +93,12 @@ function createCustomIcon(airport: Airport): L.DivIcon {
return L.divIcon({
html: `
<div style="
background-color: ${color};
background-color: ${info[0]};
width: 18px;
height: 18px;
border-radius: 50%;
border: 2px solid #fff;">
border: 2px solid #fff;
z-index: {info[1]}">
</div>
`,
className: '',

View File

@@ -157,7 +157,7 @@ export function HeaderModal({ type, toggle, login, register }: HeaderModalProps)
/>
<PasswordInput
label='Password'
description='Passwords must be at least 10 characters long, contain at least one number, one uppercase letter, one lowercase letter, and one special character.'
description='Passwords must be at least 8 characters long, contain at least one number, one uppercase letter, one lowercase letter, and one special character.'
placeholder='Your password'
required
mt='md'

View File

@@ -1,6 +1,7 @@
import { User } from '@/lib/account.types';
// import { setPicture } from "@/api/users";
import { Menu, UnstyledButton, Group, Avatar, Card, FileButton, Grid, Button, Text } from '@mantine/core';
import { useNavigate } from 'react-router';
// import './styles.css';
interface HeaderUserProps {
@@ -10,6 +11,8 @@ interface HeaderUserProps {
}
export default function HeaderUser({ user, profilePicture, logout }: HeaderUserProps) {
const navigate = useNavigate();
return (
<Menu shadow='md' width={200} openDelay={100} closeDelay={400} zIndex={1000}>
<Menu.Target>
@@ -66,7 +69,7 @@ export default function HeaderUser({ user, profilePicture, logout }: HeaderUserP
</Text>
<Grid mt='xl'>
<Grid.Col span={6}>
<Button fullWidth radius='md' size='xs' variant='default'>
<Button fullWidth radius='md' size='xs' variant='default' onClick={() => navigate('/profile')}>
Profile
</Button>
</Grid.Col>
@@ -75,9 +78,9 @@ export default function HeaderUser({ user, profilePicture, logout }: HeaderUserP
Logout
</Button>
</Grid.Col>
{user.role == 'admin' && (
{user.role == 'ADMIN' && (
<Grid.Col span={12}>
<Button fullWidth radius='md' size='xs' variant='default'>
<Button fullWidth radius='md' size='xs' variant='default' onClick={() => navigate('/administration')}>
Administration
</Button>
</Grid.Col>

View File

@@ -1,13 +1,12 @@
import { useState } from 'react';
import { Avatar, Box, Burger, Button, Group, Text } from '@mantine/core';
import { useDisclosure, useToggle } from '@mantine/hooks';
import classes from './Header.module.css';
import { HeaderModal } from '@components/Header/HeaderModal.tsx';
import { notifications } from '@mantine/notifications';
import Cookies from 'js-cookie';
import { User } from '@lib/account.types.ts';
import { login, logout, register } from '@lib/account.ts';
import HeaderUser from '@components/Header/HeaderUser.tsx';
import { useUserContext } from '@components/context/UserContext.tsx';
import { Link } from 'react-router';
// const links = [
// { link: '/', label: 'Map' },
@@ -16,9 +15,9 @@ import HeaderUser from '@components/Header/HeaderUser.tsx';
// ];
export function Header() {
const { user, setUser } = useUserContext();
const [opened, { toggle }] = useDisclosure(false);
const [modalType, modalToggle] = useToggle([undefined, 'login', 'register', 'reset']);
const [user, setUser] = useState<User | undefined>(undefined);
// const [active, setActive] = useState(links[0].link);
// const navItems = links.map((link) => (
@@ -62,8 +61,8 @@ export function Header() {
async function logoutUser(): Promise<void> {
await logout();
Cookies.remove('logged_in');
setUser(undefined);
window.location.reload();
}
async function registerUser({
@@ -131,12 +130,14 @@ export function Header() {
<Box>
<header className={classes.header}>
<Group justify='space-between' h='100%'>
<Burger opened={opened} onClick={toggle} hiddenFrom='sm' size='sm' />
<Group align='center' gap='xs'>
<Burger opened={opened} onClick={toggle} hiddenFrom='xs' size='sm' />
<Avatar src='/logo.svg' alt='logo' />
<Link to='/'>
<Avatar src='/logo.svg' alt='logo' onClick={toggle} />
</Link>
<Text>Aviation Data</Text>
</Group>
{/*<Group gap={5} visibleFrom='xs' className={classes.navGroup}>*/}
{/*<Group gap={5} visibleFrom='sm' className={classes.navGroup}>*/}
{/* {navItems}*/}
{/*</Group>*/}
<Group align='center' gap='xs'>

View File

@@ -0,0 +1,18 @@
import { Header } from '@components/Header';
import { useUserContext } from '@components/context/UserContext.tsx';
import { Navigate } from 'react-router';
export function Profile() {
const { user } = useUserContext();
if (user == undefined) {
return <Navigate to={'/'} />;
}
return (
<>
<Header />
Todo: profile {user?.email}
</>
);
}

View File

@@ -0,0 +1,18 @@
import { User } from '@lib/account.types.ts';
import { createContext, useContext } from 'react';
interface UserContextType {
user?: User;
setUser: (user: User | undefined) => void;
loading: boolean;
}
export const UserContext = createContext<UserContextType>({
user: undefined,
setUser: () => {},
loading: true
});
export function useUserContext(): UserContextType {
return useContext(UserContext);
}

View File

@@ -0,0 +1,33 @@
import { ReactNode, useEffect, useState } from 'react';
import { UserContext } from './UserContext.tsx';
import { refresh } from '@lib/account.ts';
import { User } from '@lib/account.types.ts';
import { Center, Loader } from '@mantine/core';
export function UserProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | undefined>(undefined);
const [loading, setLoading] = useState(true);
useEffect(() => {
refresh().then((refreshUser) => {
if (refreshUser) {
setUser(refreshUser);
} else {
setUser(undefined);
}
setLoading(false);
});
}, []);
return (
<UserContext.Provider value={{ user, setUser, loading }}>
{loading ? (
<Center style={{ height: '100vh' }}>
<Loader size='xl' />
</Center>
) : (
<>{children}</>
)}
</UserContext.Provider>
);
}