CZQM-Ops-Demo/components/metar-table.tsx
2024-11-15 19:14:42 -05:00

178 lines
6.2 KiB
TypeScript

"use client";
import { useState, Fragment } from "react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Skeleton } from "@/components/ui/skeleton";
import { ChevronDown, ChevronUp, Plane, PlaneLanding, PlaneTakeoff } from "lucide-react";
interface Flight {
callsign: string;
type: 'Departure' | 'Arrival';
time: string;
route: string;
origin: string;
destination: string;
}
interface MetarTableProps {
data: any[];
loading: boolean;
}
export function MetarTable({ data, loading }: MetarTableProps) {
const [expandedAirport, setExpandedAirport] = useState<string | null>(null);
const [flights, setFlights] = useState<Flight[]>([]);
const [loadingFlights, setLoadingFlights] = useState(false);
const handleAirportClick = async (airport: string) => {
if (expandedAirport === airport) {
setExpandedAirport(null);
return;
}
setExpandedAirport(airport);
setLoadingFlights(true);
try {
const response = await fetch(`/api/vatsim/${airport}`);
const data = await response.json();
setFlights(data);
} catch (error) {
console.error('Failed to fetch flights:', error);
} finally {
setLoadingFlights(false);
}
};
if (loading) {
return <LoadingSkeleton />;
}
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Airport</TableHead>
<TableHead>Conditions</TableHead>
<TableHead>RWY</TableHead>
<TableHead>Wind (°/kts)</TableHead>
<TableHead>Headwind (kts)</TableHead>
<TableHead>Xwind (kts)</TableHead>
<TableHead>Altimeter</TableHead>
<TableHead>METAR</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((row, index) => (
<Fragment key={`${row.airport}-${index}`}>
<TableRow
className="cursor-pointer hover:bg-muted/50"
onClick={() => handleAirportClick(row.airport)}
>
<TableCell className="font-medium flex items-center gap-2">
{expandedAirport === row.airport ? (
<ChevronUp className="h-4 w-4" />
) : (
<ChevronDown className="h-4 w-4" />
)}
{row.airport}
</TableCell>
<TableCell className={`text-${row.flightRulesClass}`}>
{row.flightRules}
</TableCell>
<TableCell className={row.runwayClass}>{row.bestRunway}</TableCell>
<TableCell>{row.wind}</TableCell>
<TableCell className={`wind-${row.headwindClass}`}>
{row.headwind}
</TableCell>
<TableCell className={`wind-${row.crosswindClass}`}>
{row.crosswind}
</TableCell>
<TableCell>{row.altimeter}</TableCell>
<TableCell className="font-mono text-sm">{row.metar}</TableCell>
</TableRow>
{expandedAirport === row.airport && (
<TableRow>
<TableCell colSpan={8} className="p-0 bg-muted/50">
{loadingFlights ? (
<div className="p-4 space-y-2">
<Skeleton className="h-8 w-full" />
<Skeleton className="h-8 w-full" />
<Skeleton className="h-8 w-full" />
</div>
) : flights.length > 0 ? (
<div className="p-4 space-y-4">
<h3 className="font-semibold flex items-center gap-2">
<Plane className="h-4 w-4" />
Active Flights
</h3>
<div className="grid grid-cols-4 gap-4">
{flights.map((flight, i) => (
<div
key={`${flight.callsign}-${i}`}
className="bg-background p-4 rounded-lg shadow-sm space-y-2"
>
<div className="flex items-center gap-2">
{flight.type === 'Departure' ? (
<PlaneTakeoff className="h-4 w-4 text-blue-500" />
) : (
<PlaneLanding className="h-4 w-4 text-green-500" />
)}
<span className="font-mono font-semibold">
{flight.callsign}
</span>
</div>
<div className="text-sm text-muted-foreground">
<p>
{flight.type}: {flight.time}Z
</p>
<p>
{flight.origin} {flight.destination}
</p>
<p className="font-mono text-xs mt-2 truncate" title={flight.route}>
{flight.route}
</p>
</div>
</div>
))}
</div>
</div>
) : (
<p className="text-muted-foreground text-center py-4">
No active flights found for {row.airport}
</p>
)}
</TableCell>
</TableRow>
)}
</Fragment>
))}
</TableBody>
</Table>
);
}
function LoadingSkeleton() {
return (
<div className="space-y-3 p-4">
{Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="flex gap-4">
<Skeleton className="h-4 w-[100px]" />
<Skeleton className="h-4 w-[80px]" />
<Skeleton className="h-4 w-[60px]" />
<Skeleton className="h-4 w-[100px]" />
<Skeleton className="h-4 w-[80px]" />
<Skeleton className="h-4 w-[80px]" />
<Skeleton className="h-4 w-[80px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
))}
</div>
);
}