From 7dedc7a8dc41cc620bd5d3269f41cdfefd22fb18 Mon Sep 17 00:00:00 2001 From: Ben Sherriff Date: Wed, 28 May 2025 21:21:25 -0400 Subject: [PATCH] Working on airport drawer --- api/src/account/routes.rs | 12 ++++--- scripts/tag.sh | 19 +++++++++++ ui/src/components/AirportDrawer/index.tsx | 37 ++++++++++++++++++++-- ui/src/components/context/UserContext.tsx | 6 +++- ui/src/components/context/UserProvider.tsx | 15 ++++++++- 5 files changed, 79 insertions(+), 10 deletions(-) diff --git a/api/src/account/routes.rs b/api/src/account/routes.rs index 5118efd..bbbd103 100644 --- a/api/src/account/routes.rs +++ b/api/src/account/routes.rs @@ -45,11 +45,13 @@ async fn register(user: web::Json, req: HttpRequest) -> HttpRes // Send confirmation email if let Some(email) = email { - tokio::spawn(async move { - if let Err(err) = send_confirm_email(&email, &ip_address).await { - log::error!("Failed to send confirmation email: {}", err); - }; - }); + if !email.is_empty() { + tokio::spawn(async move { + if let Err(err) = send_confirm_email(&email, &ip_address).await { + log::error!("Failed to send confirmation email: {}", err); + }; + }); + } } HttpResponse::Created().json(user_response) diff --git a/scripts/tag.sh b/scripts/tag.sh index 8459c67..413b0ef 100755 --- a/scripts/tag.sh +++ b/scripts/tag.sh @@ -2,6 +2,7 @@ force=0 push=0 +push_all=0 API_VERSION=$(sed -n 's/^version *= *"\([^"]*\)".*/\1/p' "$(pwd)"/api/Cargo.toml) UI_VERSION=$(sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' "$(pwd)"/ui/package.json) @@ -17,6 +18,13 @@ for arg in "$@"; do push=1 shift ;; + -a|--push-all) + push_all=1 + shift + ;; + *) + shift + ;; esac done @@ -69,3 +77,14 @@ if echo "$changed_files" | grep -q "^api/"; then fi fi fi + +# Push all tags +if [ $push_all -eq 1 ]; then + if [ $force -eq 1 ]; then + echo "Force-pushing ALL tags to remote" + git push -f origin --tags + else + echo "Pushing ALL tags to remote" + git push origin --tags + fi +fi diff --git a/ui/src/components/AirportDrawer/index.tsx b/ui/src/components/AirportDrawer/index.tsx index bcf9abf..0a82baa 100644 --- a/ui/src/components/AirportDrawer/index.tsx +++ b/ui/src/components/AirportDrawer/index.tsx @@ -1,10 +1,10 @@ import { Accordion, Badge, - Box, + Box, Button, Divider, Drawer, - Group, + Group, Stack, Tabs, TabsList, Text, @@ -15,12 +15,13 @@ import { Airport, AirportCategory } from '@lib/airport.types.ts'; import { getMarkerColor, Metar } from '@lib/metar.types.ts'; import { CSSProperties, forwardRef, ReactNode, useEffect, useState } from 'react'; import { useMediaQuery } from '@mantine/hooks'; -import { IconViewfinder } from '@tabler/icons-react'; +import { IconStar, IconStarFilled, IconViewfinder } from '@tabler/icons-react'; import { RunwayTable } from '@components/AirportDrawer/RunwayTable.tsx'; import { CommunicationTable } from '@components/AirportDrawer/CommunicationTable.tsx'; import { useMap } from 'react-leaflet'; import type { Map as LeafletMap } from 'leaflet'; import { getMetars } from '@lib/metar.ts'; +import { useUserContext } from '@components/context/UserContext.tsx'; export function AirportDrawer({ airport, @@ -29,6 +30,9 @@ export function AirportDrawer({ airport: Airport | null; setAirport: (airport: Airport | null) => void; }) { + const { user, favorites, toggleFavorite } = useUserContext(); + const isAdmin = user?.role === 'ADMIN'; + const isFavorite = airport ? favorites.includes(airport.icao) : false; const [metar, setMetar] = useState(undefined); const isMobile = useMediaQuery('(max-width: 768px)'); const map = useMap(); @@ -73,6 +77,15 @@ export function AirportDrawer({ > + toggleFavorite(airport.icao)} + aria-label={isFavorite ? 'Unfavorite airport' : 'Favorite airport'} + style={{ padding: 4 }} + > + {isFavorite + ? + : } + {airport.name} @@ -104,6 +117,7 @@ export function AirportDrawer({ Info Weather + { user && Manage } @@ -111,6 +125,23 @@ export function AirportDrawer({ + {user && ( + + {isAdmin ? ( + + + + + + ) : ( + + + + )} + + )} diff --git a/ui/src/components/context/UserContext.tsx b/ui/src/components/context/UserContext.tsx index 73f2e7d..eae6f82 100644 --- a/ui/src/components/context/UserContext.tsx +++ b/ui/src/components/context/UserContext.tsx @@ -5,12 +5,16 @@ interface UserContextType { user?: User; setUser: (user: User | undefined) => void; loading: boolean; + favorites: string[]; + toggleFavorite: (icao: string) => void; } export const UserContext = createContext({ user: undefined, setUser: () => {}, - loading: true + loading: true, + favorites: [], + toggleFavorite: () => {}, }); export function useUserContext(): UserContextType { diff --git a/ui/src/components/context/UserProvider.tsx b/ui/src/components/context/UserProvider.tsx index 681c750..0fbe245 100644 --- a/ui/src/components/context/UserProvider.tsx +++ b/ui/src/components/context/UserProvider.tsx @@ -9,8 +9,21 @@ const sessionExpirationName = 'session_expiration'; export function UserProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(undefined); + const [favorites, setFavorites] = useState(() => { + return JSON.parse(localStorage.getItem('favorites') || '[]') + }) const [loading, setLoading] = useState(true); + const toggleFavorite = (icao: string) => { + setFavorites((prev) => { + const next = prev.includes(icao) + ? prev.filter((i) => i !== icao) + : [...prev, icao] + localStorage.setItem('favorites', JSON.stringify(next)) + return next + }) + } + useEffect(() => { const sessionExpiration = Cookies.get(sessionExpirationName); @@ -36,7 +49,7 @@ export function UserProvider({ children }: { children: ReactNode }) { }, []); return ( - + {loading ? (