import { Accordion, Badge, Box, Divider, Drawer, Group, Tabs, TabsList, Text, Tooltip, UnstyledButton } from '@mantine/core'; 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 { getMetars } from '@lib/metar.ts'; import { useMediaQuery } from '@mantine/hooks'; import { 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'; export function AirportDrawer({ airport, setAirport }: { airport: Airport | null; setAirport: (airport: Airport | null) => void; }) { const [metar, setMetar] = useState(undefined); const isMobile = useMediaQuery('(max-width: 768px)'); const map = useMap(); useEffect(() => { if (!airport) return; function updateMetar() { if (!airport) return; getMetars({ icaos: [airport.icao] }).then((m) => { if (m.length > 0) { setMetar(m[0]); } else { setMetar(undefined); } }); } updateMetar(); const interval = setInterval(updateMetar, 60000); return () => clearInterval(interval); }, [airport]); if (!airport) { return null; } const metarColor = getMarkerColor(metar?.flight_category || 'UNKN'); return ( setAirport(null)} withinPortal zIndex={1000} styles={{ root: { padding: 0, margin: 0, width: 0, height: 0, backgroundColor: 'red' } }} padding='md' size={isMobile ? '100%' : 'md'} position='left' closeOnClickOutside={false} > {airport.name} {metar && metar.flight_category && ( {metar.flight_category} {/*{metar.flight_category}*/} )} Info Weather ); } function AirportInfoSlot({ title, style, children }: { title?: string; style?: CSSProperties; children?: ReactNode }) { return (
{title && ( {title} )} {children}
); } function AirportInfoRow({ style, children }: { style?: CSSProperties; children: ReactNode }) { return (
{children}
); } function AirportInfo({ map, airport }: { map: LeafletMap; airport: Airport }) { function goToLocation(map: LeafletMap, latitude: number, longitude: number) { if (!map) return; map.setView([latitude, longitude], map.getZoom()); } return (
{airport.latitude}°, {airport.longitude}° { goToLocation(map, airport.latitude, airport.longitude); }} > {airport.runways != null && airport.runways.length > 0 && ( Runways )} {airport.communications != null && airport.communications.length > 0 && ( Communication )}
); } function WeatherInfo({ metar }: { metar?: Metar }) { if (metar) { return <>{metar.raw_text}; } else { return <>No METAR observation available; } } function airportCategoryToText(category: AirportCategory): string { switch (category) { case AirportCategory.SMALL: return 'Small'; case AirportCategory.MEDIUM: return 'Medium'; case AirportCategory.LARGE: return 'Large'; case AirportCategory.HELIPORT: return 'Helipad'; case AirportCategory.CLOSED: return 'Closed'; case AirportCategory.SEAPLANE: return 'Seaplane Base'; case AirportCategory.BALLOONPORT: return 'Balloon Port'; default: return 'Unknown'; } } const TimeSince = forwardRef(({ date }, ref) => { const inputDate = new Date(date); // @ts-expect-error doing arithmetic with dates const seconds = Math.floor((new Date() - inputDate) / 1000); if (seconds < 60) { const content = seconds + (seconds === 1 ? ' second ago' : ' seconds ago'); return ( {content} ); } else { const minutes = Math.floor(seconds / 60); const content = minutes + (minutes === 1 ? ' minute ago' : ' minutes ago'); // If more than 60 minutes have passed, set the text color to yellow return ( = 60 ? '#fca903' : undefined, userSelect: 'none' }}> {content} ); } });