Files
aviation/ui/src/components/AirportDrawer.tsx

139 lines
4.1 KiB
TypeScript

import { Box, Drawer, Group, Tabs, TabsList, Text, Tooltip } from '@mantine/core';
import { Airport, AirportCategory } from '@lib/airport.types.ts';
import { getMarkerColor, Metar } from '@lib/metar.types.ts';
import { useEffect, useState } from 'react';
import { getMetars } from '@lib/metar.ts';
import { useMediaQuery } from '@mantine/hooks';
export default function AirportDrawer({
airport,
setAirport
}: {
airport: Airport | null;
setAirport: (airport: Airport | null) => void;
}) {
const [metar, setMetar] = useState<Metar | undefined>(undefined);
const isMobile = useMediaQuery('(max-width: 768px)');
useEffect(() => {
if (!airport) return;
function updateMetar() {
if (!airport) return;
console.log(airport.icao);
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 (
<Drawer
opened={true}
onClose={() => setAirport(null)}
title={airport.name}
withinPortal
zIndex={1000}
styles={{ root: { padding: 0, margin: 0, width: 0, height: 0 } }}
padding='md'
size={isMobile ? '100%' : 'md'}
position='left'
withOverlay={false}
closeOnClickOutside={false}
>
<Box mb='lg'>
{metar && metar.flight_category && (
<Group
justify='space-between'
mb='md'
style={{
backgroundColor: '#272f38',
borderTop: '1px solid #1a242f',
borderBottom: '1px solid #1a242f',
padding: '10px'
}}
>
<Text style={{ color: metarColor }}>{metar.flight_category}</Text>
<Tooltip zIndex={1001} label={new Date(metar.observation_time).toLocaleString()}>
<TimeSince date={metar.observation_time} />
</Tooltip>
</Group>
)}
<Tabs variant={'outline'} defaultValue={'info'}>
<TabsList grow>
<Tabs.Tab value={'info'}>Info</Tabs.Tab>
<Tabs.Tab value={'weather'}>Weather</Tabs.Tab>
</TabsList>
<Tabs.Panel value={'info'}><AirportInfo airport={airport}/></Tabs.Panel>
{airport.latest_metar && (
<Tabs.Panel value={'weather'}><WeatherInfo metar={airport.latest_metar} /></Tabs.Panel>
)}
</Tabs>
</Box>
</Drawer>
);
}
function AirportInfo({ airport }: { airport: Airport }) {
return (<div>
<Text>ICAO: {airport.icao}</Text>
<Text>Category: {airportCategoryToText(airport.category)}</Text>
</div>);
}
function WeatherInfo({ metar }: { metar: Metar }) {
return <>{metar.raw_text}</>
}
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';
}
}
function TimeSince({ date }: { date: string }) {
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 <Text>{content}</Text>;
} 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 <Text style={{ color: minutes >= 60 ? '#fca903' : undefined }}>{content}</Text>;
}
}