Updated api endpoint structure, fixed issues, worked on admin page
This commit is contained in:
@@ -1,22 +1,21 @@
|
||||
import { AirportOrderField, Bounds, GetAirportResponse, GetAirportsResponse } from './airport.types';
|
||||
import { getRequest, deleteRequest } from '.';
|
||||
import { Airport, AirportOrderField, Bounds, GetAirportResponse, GetAirportsResponse } from './airport.types';
|
||||
import { getRequest, deleteRequest, postRequest, putRequest } from '.';
|
||||
|
||||
interface GetAirportProps {
|
||||
icao: string;
|
||||
}
|
||||
|
||||
export async function getAirport({ icao }: GetAirportProps): Promise<GetAirportResponse> {
|
||||
const response = await getRequest(`airports/search/${icao}`);
|
||||
const response = await getRequest(`airports/${icao}`);
|
||||
return response?.json() || { data: undefined };
|
||||
}
|
||||
|
||||
interface GetAirportsProps {
|
||||
bounds?: Bounds;
|
||||
category?: string;
|
||||
name?: string;
|
||||
search?: string;
|
||||
order_field?: AirportOrderField;
|
||||
order_by?: 'asc' | 'desc';
|
||||
icao?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
@@ -24,20 +23,18 @@ interface GetAirportsProps {
|
||||
export async function getAirports({
|
||||
bounds,
|
||||
category,
|
||||
name,
|
||||
icao,
|
||||
search,
|
||||
order_field,
|
||||
order_by,
|
||||
limit = 10,
|
||||
page = 1
|
||||
}: GetAirportsProps): Promise<GetAirportsResponse> {
|
||||
const response = await getRequest('airports/search', {
|
||||
const response = await getRequest('airports', {
|
||||
bounds: bounds
|
||||
? `${bounds?.northEast.lat},${bounds?.northEast.lon},${bounds?.southWest.lat},${bounds?.southWest.lon}`
|
||||
: undefined,
|
||||
category: category ?? undefined,
|
||||
name: name ?? undefined,
|
||||
icao: icao ?? undefined,
|
||||
search: search ?? undefined,
|
||||
order_field: order_field ?? undefined,
|
||||
order_by: order_by ?? undefined,
|
||||
limit,
|
||||
@@ -49,14 +46,24 @@ export async function getAirports({
|
||||
export async function removeAirport({ icao }: { icao?: string }): Promise<any> {
|
||||
let response
|
||||
if (icao) {
|
||||
response = await deleteRequest(`airports/remove/${icao}`);
|
||||
response = await deleteRequest(`airports/${icao}`);
|
||||
} else {
|
||||
response = await deleteRequest('airports/remove');
|
||||
response = await deleteRequest('airports');
|
||||
}
|
||||
return response.status == 204;
|
||||
}
|
||||
|
||||
export async function importAirports(): Promise<any> {
|
||||
const response = await getRequest('airports/import');
|
||||
export async function createAirport({ airport }: { airport: Airport }): Promise<any> {
|
||||
const response = await postRequest(`airports`, airport);
|
||||
return response?.json() || { data: undefined };
|
||||
}
|
||||
|
||||
export async function updateAirport({ airport }: { airport: Airport }): Promise<any> {
|
||||
const response = await putRequest(`airports`, airport);
|
||||
return response?.json() || { data: undefined };
|
||||
}
|
||||
|
||||
export async function importAirports(): Promise<any> {
|
||||
const response = await postRequest('airports/import');
|
||||
return response?.json() || { data: undefined };
|
||||
}
|
||||
|
||||
@@ -41,6 +41,28 @@ export async function postRequest(endpoint: string, body?: any, options?: PostOp
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function putRequest(endpoint: string, body?: any, options?: PostOptions): Promise<Response> {
|
||||
const url = `${baseURL}/${endpoint}`;
|
||||
let response;
|
||||
if (body && (!options?.type || options.type === 'json')) {
|
||||
response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
} else {
|
||||
response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
credentials: 'include',
|
||||
body
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function deleteRequest(endpoint: string): Promise<Response> {
|
||||
const url = `${baseURL}/${endpoint}`;
|
||||
const response = await fetch(url, {
|
||||
|
||||
@@ -10,6 +10,6 @@ export async function getMetars(icaos: string[]): Promise<GetMetarsResponse> {
|
||||
return { data: [] };
|
||||
}
|
||||
const stationICAOs: string = icaos.map((icao) => icao).join(',');
|
||||
const response = await getRequest(`metars/${stationICAOs}`, {});
|
||||
const response = await getRequest(`metars`, { icaos: stationICAOs });
|
||||
return response?.json() || { data: [] };
|
||||
}
|
||||
|
||||
@@ -3,26 +3,31 @@
|
||||
import { Airport } from "@/api/airport.types";
|
||||
import AirportTablePanel from "@/components/Admin/AirportTablePanel";
|
||||
import CreateAirportPanel from "@/components/Admin/CreateAirportPanel";
|
||||
import { Container, Grid } from "@mantine/core";
|
||||
import UpdateAirportModal from "@/components/Admin/UpdateAirportModal";
|
||||
import { Container, Grid, SimpleGrid } from "@mantine/core";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function Page() {
|
||||
const [airport, setAirport] = useState<Airport | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(airport);
|
||||
}, [airport]);
|
||||
|
||||
return <Container fluid>
|
||||
<Grid p={'lg'}>
|
||||
<Grid.Col span={12}>
|
||||
<AirportTablePanel setAirport={setAirport} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={12}>
|
||||
<CreateAirportPanel airport={airport} setAirport={setAirport} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Container>;
|
||||
return (
|
||||
<Container fluid>
|
||||
<SimpleGrid cols={{ base: 1, xs: 1 }} spacing={'md'}>
|
||||
<Grid p={'lg'}>
|
||||
<Grid.Col span={12}>
|
||||
<AirportTablePanel setAirport={setAirport} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={12}>
|
||||
<CreateAirportPanel />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</SimpleGrid>
|
||||
<UpdateAirportModal airport={airport} setAirport={setAirport} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getAirports, importAirports, removeAirport } from "@/api/airport";
|
||||
import { Airport, AirportCategory, AirportOrderField, airportCategoryToText } from "@/api/airport.types";
|
||||
import { Text, Button, Card, Group, Pagination, ScrollArea, Table, TextInput, rem, UnstyledButton, Center } from "@mantine/core";
|
||||
import { Airport, airportCategoryToText } from "@/api/airport.types";
|
||||
import { Text, Button, Card, Group, Pagination, Table, TextInput, rem, UnstyledButton, Center, Flex, Container, Grid, Space } from "@mantine/core";
|
||||
import { HiChevronUp, HiChevronDown, HiSelector } from "react-icons/hi";
|
||||
import { useEffect, useState } from "react";
|
||||
import { CiSearch } from "react-icons/ci";
|
||||
@@ -14,6 +14,7 @@ export default function AirportTablePanel({ setAirport }: { setAirport: (airport
|
||||
|
||||
async function getAirportData() {
|
||||
const response = await getAirports({
|
||||
search,
|
||||
page,
|
||||
limit: 100
|
||||
});
|
||||
@@ -45,14 +46,12 @@ export default function AirportTablePanel({ setAirport }: { setAirport: (airport
|
||||
<Table.Td>{airport.gps_code}</Table.Td>
|
||||
<Table.Td>{airport.iata_code}</Table.Td>
|
||||
<Table.Td>{airport.local_code}</Table.Td>
|
||||
<Table.Td>{airport.point.x}</Table.Td>
|
||||
<Table.Td>{airport.point.y}</Table.Td>
|
||||
</Table.Tr>
|
||||
))
|
||||
|
||||
return <Card shadow={'sm'} padding={'lg'} radius={'md'} withBorder>
|
||||
<TextInput
|
||||
placeholder="Search by ICAO"
|
||||
placeholder="Search..."
|
||||
mb="md"
|
||||
leftSection={<CiSearch style={{ width: rem(16), height: rem(16) }} />}
|
||||
value={search}
|
||||
@@ -72,28 +71,36 @@ export default function AirportTablePanel({ setAirport }: { setAirport: (airport
|
||||
<Table.Th>GPS Code</Table.Th>
|
||||
<Table.Th>IATA Code</Table.Th>
|
||||
<Table.Th>Local Code</Table.Th>
|
||||
<Table.Th>Latitude</Table.Th>
|
||||
<Table.Th>Longitude</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>{rows}</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
<Group>
|
||||
<Pagination value={page} total={totalPages} onChange={setPage} />
|
||||
<PanelButton onClick={async () => {
|
||||
await importAirports();
|
||||
await getAirportData();
|
||||
}}>
|
||||
Import
|
||||
</PanelButton>
|
||||
<PanelButton color={'red'} onClick={async () => {
|
||||
await removeAirport({});
|
||||
await getAirportData();
|
||||
}}>
|
||||
Remove All
|
||||
</PanelButton>
|
||||
</Group>
|
||||
<Grid mt={'md'} justify={'space-between'}>
|
||||
<Grid.Col span={10}>
|
||||
<Pagination value={page} total={totalPages} onChange={setPage} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<Flex justify={'end'}>
|
||||
<Space mr={'sm'}>
|
||||
<PanelButton onClick={async () => {
|
||||
await importAirports();
|
||||
await getAirportData();
|
||||
}}>
|
||||
Import
|
||||
</PanelButton>
|
||||
</Space>
|
||||
<Space>
|
||||
<PanelButton color={'red'} onClick={async () => {
|
||||
await removeAirport({});
|
||||
await getAirportData();
|
||||
}}>
|
||||
Remove All
|
||||
</PanelButton>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Card>
|
||||
}
|
||||
|
||||
@@ -103,7 +110,6 @@ function PanelButton({ children, color = 'blue', onClick }: {children: any, colo
|
||||
loading={loading}
|
||||
variant='light'
|
||||
color={color}
|
||||
mt={'md'}
|
||||
radius={'md'}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { createAirport } from "@/api/airport";
|
||||
import { Airport, AirportCategory } from "@/api/airport.types";
|
||||
import { Card, TextInput, Select, Group, Flex, Space, Button } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function CreateAirportPanel({ airport, setAirport } : { airport?: Airport, setAirport: (airport: Airport | undefined) => void }) {
|
||||
export default function CreateAirportPanel() {
|
||||
const form = useForm<Airport>({
|
||||
initialValues: {
|
||||
icao: '',
|
||||
@@ -25,20 +26,12 @@ export default function CreateAirportPanel({ airport, setAirport } : { airport?:
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log(airport);
|
||||
if (airport) {
|
||||
form.setValues(airport);
|
||||
}
|
||||
}, [airport]);
|
||||
|
||||
return <Card shadow={'sm'} padding={'lg'} radius={'md'} withBorder>
|
||||
Create Airport
|
||||
<form onSubmit={form.onSubmit((values) => {
|
||||
if (airport) {
|
||||
console.log('update');
|
||||
} else {
|
||||
console.log('create');
|
||||
<form onSubmit={form.onSubmit(async (values) => {
|
||||
const response = await createAirport({ airport: values });
|
||||
if (response.success) {
|
||||
form.reset();
|
||||
}
|
||||
})}>
|
||||
<TextInput
|
||||
@@ -136,7 +129,7 @@ export default function CreateAirportPanel({ airport, setAirport } : { airport?:
|
||||
color='blue'
|
||||
radius={'md'}
|
||||
>
|
||||
{airport ? 'Update' : 'Create'}
|
||||
Create
|
||||
</Button>
|
||||
</Space>
|
||||
<Space>
|
||||
@@ -145,10 +138,7 @@ export default function CreateAirportPanel({ airport, setAirport } : { airport?:
|
||||
variant='light'
|
||||
color='red'
|
||||
radius={'md'}
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
setAirport(undefined);
|
||||
}}
|
||||
onClick={() => form.reset()}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
148
ui/src/components/Admin/UpdateAirportModal.tsx
Normal file
148
ui/src/components/Admin/UpdateAirportModal.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import { removeAirport, updateAirport } from "@/api/airport";
|
||||
import { Airport, AirportCategory } from "@/api/airport.types";
|
||||
import { Button, Container, Flex, Group, Modal, Paper, Select, TextInput, Title } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function UpdateAirportModal({ airport, setAirport }: { airport: Airport | undefined, setAirport: (airport: Airport | undefined) => void}) {
|
||||
const form = useForm<Airport>({
|
||||
initialValues: {
|
||||
icao: airport?.icao || '',
|
||||
category: airport?.category || AirportCategory.SMALL,
|
||||
full_name: airport?.full_name || '',
|
||||
elevation_ft: airport?.elevation_ft || 0,
|
||||
continent: airport?.continent || '',
|
||||
iso_country: airport?.iso_country || '',
|
||||
iso_region: airport?.iso_region || '',
|
||||
municipality: airport?.municipality || '',
|
||||
gps_code: airport?.gps_code || '',
|
||||
iata_code: airport?.iata_code || '',
|
||||
local_code: airport?.local_code || '',
|
||||
point: {
|
||||
x: airport?.point.x || 0,
|
||||
y: airport?.point.y || 0,
|
||||
srid: airport?.point.srid || 4326
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (airport) {
|
||||
form.setValues(airport);
|
||||
}
|
||||
}, [airport]);
|
||||
|
||||
return (
|
||||
<Modal opened={airport !== undefined} onClose={() => setAirport(undefined)} withCloseButton={false} size={'50%'}>
|
||||
<Container>
|
||||
<Title ta='center'>Update Airport</Title>
|
||||
<Paper withBorder p={30} mt={30} radius={'md'} shadow={'sm'}>
|
||||
<form onSubmit={form.onSubmit(async (values) => {
|
||||
const response = await updateAirport({ airport: values });
|
||||
if (response.success) {
|
||||
setAirport(undefined);
|
||||
}
|
||||
})}>
|
||||
<TextInput
|
||||
required
|
||||
label='ICAO'
|
||||
placeholder='KHEF'
|
||||
{...form.getInputProps('icao')}
|
||||
/>
|
||||
<Select
|
||||
required
|
||||
label='Category'
|
||||
placeholder='Select category'
|
||||
data={[
|
||||
{ value: AirportCategory.SMALL, label: 'Small' },
|
||||
{ value: AirportCategory.MEDIUM, label: 'Medium' },
|
||||
{ value: AirportCategory.LARGE, label: 'Large' },
|
||||
]}
|
||||
{...form.getInputProps('category')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Full Name'
|
||||
placeholder='Manassas Regional Airport/Harry P. Davis Field'
|
||||
{...form.getInputProps('full_name')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Elevation (ft)'
|
||||
placeholder='192'
|
||||
{...form.getInputProps('elevation_ft')}
|
||||
/>
|
||||
<Group>
|
||||
<TextInput
|
||||
required
|
||||
label='Continent'
|
||||
placeholder='NA'
|
||||
{...form.getInputProps('continent')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='ISO Country'
|
||||
placeholder='US'
|
||||
{...form.getInputProps('iso_country')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='ISO Region'
|
||||
placeholder='US-VA'
|
||||
{...form.getInputProps('iso_region')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Municipality'
|
||||
placeholder='Manassas'
|
||||
{...form.getInputProps('municipality')}
|
||||
/>
|
||||
</Group>
|
||||
<Group>
|
||||
<TextInput
|
||||
required
|
||||
label='GPS Code'
|
||||
placeholder='KHEF'
|
||||
{...form.getInputProps('gps_code')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='IATA Code'
|
||||
placeholder='HEF'
|
||||
{...form.getInputProps('iata_code')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Local Code'
|
||||
placeholder='HEF'
|
||||
{...form.getInputProps('local_code')}
|
||||
/>
|
||||
</Group>
|
||||
<Group>
|
||||
<TextInput
|
||||
required
|
||||
label='Latitude'
|
||||
placeholder='38.72140121'
|
||||
{...form.getInputProps('point.x')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Longitude'
|
||||
placeholder='-77.51540375'
|
||||
{...form.getInputProps('point.y')}
|
||||
/>
|
||||
</Group>
|
||||
<Flex justify={'end'} mt={'sm'}>
|
||||
<Button type='submit' variant='light'>Update Airport</Button>
|
||||
{airport && <Button variant='light' color='red' ml={10} onClick={async () => {
|
||||
if (await removeAirport({icao: airport.icao})) {
|
||||
setAirport(undefined);
|
||||
}
|
||||
}}>Delete</Button>}
|
||||
</Flex>
|
||||
</form>
|
||||
</Paper>
|
||||
</Container>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export default function Header() {
|
||||
|
||||
async function onChange(value: string) {
|
||||
setSearchValue(value);
|
||||
const airportData = await getAirports({ name: value, icao: value });
|
||||
const airportData = await getAirports({ search: value });
|
||||
setAirports(
|
||||
airportData.data.map((airport) => ({
|
||||
key: airport.icao,
|
||||
|
||||
Reference in New Issue
Block a user