Skip to content

Commit

Permalink
add flights table to home
Browse files Browse the repository at this point in the history
  • Loading branch information
its-felix committed May 10, 2024
1 parent f54ed14 commit a4a367b
Showing 1 changed file with 200 additions and 1 deletion.
201 changes: 200 additions & 1 deletion ui/src/pages/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Multiselect,
MultiselectProps, Popover,
Slider,
SpaceBetween
SpaceBetween, Table
} from '@cloudscape-design/components';
import Dagre from '@dagrejs/dagre';
import {
Expand All @@ -36,6 +36,7 @@ import { catchNotify, useAppControls } from '../components/util/context/app-cont
import { expectSuccess } from '../lib/api/api';
import { Airports, Connection, Connections, Flight, FlightNumber } from '../lib/api/api.model';
import { KeyValuePairs, ValueWithLabel } from '../components/common/key-value-pairs';
import { useCollection } from '@cloudscape-design/collection-hooks';

export function Home() {
const { apiClient } = useHttpClient();
Expand Down Expand Up @@ -76,6 +77,7 @@ export function Home() {
<ConnectionsGraph connections={connections} />
</ReactFlowProvider>
</Container>
<ConnectionsTable connections={connections} />
</ContentLayout>
);
}
Expand Down Expand Up @@ -621,4 +623,201 @@ function AirportMultiselect({ airports, loading, disabled, onChange }: { airport
statusType={loading ? 'loading' : 'finished'}
/>
);
}

interface ConnectionsTableBaseItem {
readonly flightNumber?: FlightNumber;
readonly departureTime: DateTime<true>;
readonly departureAirport: string;
readonly arrivalTime: DateTime<true>;
readonly arrivalAirport: string;
readonly aircraftOwner?: string;
readonly aircraftType?: string;
readonly registration?: string;
readonly codeShares?: ReadonlyArray<FlightNumber>;
readonly children?: ReadonlyArray<ConnectionsTableChildItem>;
}

interface ConnectionsTableParentItem extends ConnectionsTableBaseItem {
readonly flightNumber: undefined;
readonly aircraftOwner: undefined;
readonly aircraftType: undefined;
readonly registration: undefined;
readonly codeShares: undefined;
readonly children: ReadonlyArray<ConnectionsTableChildItem>;
}

interface ConnectionsTableChildItem extends ConnectionsTableBaseItem {
readonly flightNumber: FlightNumber;
readonly aircraftOwner: string;
readonly aircraftType: string;
readonly registration?: string;
readonly codeShares: ReadonlyArray<FlightNumber>;
readonly children: undefined;
}

type ConnectionsTableItem = ConnectionsTableParentItem | ConnectionsTableChildItem;

function connectionsToTableItems(connections: ReadonlyArray<Connection>, flights: Record<string, Flight>): ReadonlyArray<ConnectionsTableItem> {
const result: Array<ConnectionsTableItem> = [];

for (const expanded of expandConnections(connections, flights)) {
if (expanded.length === 1) {
result.push(expanded[0]);
} else if (expanded.length > 1) {
const first = expanded[0];
const last = expanded[expanded.length - 1];

result.push({
flightNumber: undefined,
departureTime: first.departureTime,
departureAirport: first.departureAirport,
arrivalTime: last.arrivalTime,
arrivalAirport: last.arrivalAirport,
aircraftOwner: undefined,
aircraftType: undefined,
registration: undefined,
codeShares: undefined,
children: expanded,
} satisfies ConnectionsTableParentItem);
}
}

return result;
}

function expandConnections(conns: ReadonlyArray<Connection>, flights: Record<string, Flight>): ReadonlyArray<ReadonlyArray<ConnectionsTableChildItem>> {
const result: Array<ReadonlyArray<ConnectionsTableChildItem>> = [];
for (const conn of conns) {
const flight = flights[conn.flightId];
const departureTime = DateTime.fromISO(flight.departureTime, { setZone: true });
const arrivalTime = DateTime.fromISO(flight.arrivalTime, { setZone: true });
if (!departureTime.isValid || !arrivalTime.isValid) {
throw new Error(`invalid departureTime/arrivalTime: ${flight.departureTime} / ${flight.arrivalTime}`);
}

const item = {
flightNumber: flight.flightNumber,
departureTime: departureTime,
departureAirport: flight.departureAirport,
arrivalTime: arrivalTime,
arrivalAirport: flight.arrivalAirport,
aircraftOwner: flight.aircraftOwner,
aircraftType: flight.aircraftType,
registration: flight.registration,
codeShares: flight.codeShares,
children: undefined,
} satisfies ConnectionsTableChildItem;

if (conn.outgoing.length > 0) {
for (const expanded of expandConnections(conn.outgoing, flights)) {
result.push([
{ ...item } satisfies ConnectionsTableChildItem,
...expanded,
]);
}
} else {
result.push([item]);
}
}

return result;
}

function ConnectionsTable({ connections }: { connections?: Connections }) {
const rawItems = useMemo(() => {
if (connections === undefined) {
return [];
}

return connectionsToTableItems(connections.connections, connections.flights);
}, [connections]);
const { items, collectionProps } = useCollection(rawItems, { sorting: {} });
const [expandedItems, setExpandedItems] = useState<ReadonlyArray<ConnectionsTableItem>>([]);

return (
<Table
{...collectionProps}
items={items}
columnDefinitions={[
{
id: 'flight_number',
header: 'Flight Number',
cell: (v) => v.flightNumber !== undefined ? flightNumberToString(v.flightNumber) : '',
},
{
id: 'departure_time',
header: 'Departure Time',
cell: (v) => v.departureTime.toLocaleString(DateTime.DATETIME_FULL),
sortingField: 'departureTime',
},
{
id: 'departure_airport',
header: 'Departure Airport',
cell: (v) => v.departureAirport,
sortingField: 'departureAirport',
},
{
id: 'arrival_time',
header: 'Arrival Time',
cell: (v) => v.arrivalTime.toLocaleString(DateTime.DATETIME_FULL),
sortingField: 'arrivalTime',
},
{
id: 'arrival_airport',
header: 'Arrival Airport',
cell: (v) => v.arrivalAirport,
sortingField: 'arrivalAirport',
},
{
id: 'duration',
header: 'Duration',
cell: (v) => v.arrivalTime.diff(v.departureTime).rescale().toHuman({ unitDisplay: 'short' }),
},
{
id: 'aircraft_owner',
header: 'Aircraft Owner',
cell: (v) => v.aircraftOwner,
},
{
id: 'aircraft_type',
header: 'Aircraft Type',
cell: (v) => v.aircraftType,
},
{
id: 'registration',
header: 'Aircraft Registration',
cell: (v) => v.registration,
},
{
id: 'code_shares',
header: 'Code Shares',
cell: (v) => v.codeShares !== undefined ? v.codeShares.map(flightNumberToString).join(', ') : '',
},
]}
expandableRows={{
getItemChildren: (item) => item.children ?? [],
isItemExpandable: (item) => item.children !== undefined,
expandedItems: expandedItems,
onExpandableItemToggle: (e) => {
const item = e.detail.item;
const expand = e.detail.expanded;

setExpandedItems((prev) => {
if (expand) {
return [...prev, item];
}

const index = prev.indexOf(item);
if (index === -1) {
return prev;
}

return prev.toSpliced(index, 1);
});
},
}}
variant={'stacked'}
/>
);
}

0 comments on commit a4a367b

Please sign in to comment.