Condensed airport update/create forms into modal
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import { createAirport, removeAirport, updateAirport } from "@/api/airport";
|
||||
import { Airport } from "@/api/airport.types";
|
||||
import AirportForm from "@/components/Admin/AirportForm";
|
||||
import AirportTablePanel from "@/components/Admin/AirportTablePanel";
|
||||
import CreateAirportPanel from "@/components/Admin/CreateAirportPanel";
|
||||
import UpdateAirportModal from "@/components/Admin/UpdateAirportModal";
|
||||
import { isAdminState } from "@/state/auth";
|
||||
import { Container, Grid, SimpleGrid } from "@mantine/core";
|
||||
import { Container, Grid, Modal, SimpleGrid } from "@mantine/core";
|
||||
import { useState } from "react";
|
||||
import { useRecoilValue } from "recoil";
|
||||
|
||||
export default function Page() {
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [airport, setAirport] = useState<Airport | undefined>(undefined);
|
||||
const isAdmin = useRecoilValue(isAdminState);
|
||||
|
||||
@@ -20,14 +21,32 @@ export default function Page() {
|
||||
<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 />
|
||||
<AirportTablePanel setShowModal={setShowModal} setAirport={setAirport} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</SimpleGrid>
|
||||
<UpdateAirportModal airport={airport} setAirport={setAirport} />
|
||||
<Modal size={'xl'} opened={showModal} onClose={() => {
|
||||
setAirport(undefined);
|
||||
setShowModal(false);
|
||||
}}>
|
||||
<AirportForm
|
||||
title={airport ? 'Update Airport' : 'Create Airport'}
|
||||
submitText={airport ? 'Update' : 'Create'}
|
||||
airport={airport}
|
||||
onDelete={airport ? async () => {
|
||||
const response = await removeAirport({ icao: airport.icao });
|
||||
setShowModal(false);
|
||||
} : undefined}
|
||||
onSubmit={async (value) => {
|
||||
if (airport) {
|
||||
const response = await updateAirport({ airport: value });
|
||||
} else {
|
||||
const response = await createAirport({ airport: value });
|
||||
}
|
||||
setShowModal(false);
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</Container>
|
||||
)}
|
||||
</>
|
||||
|
||||
159
ui/src/components/Admin/AirportForm.tsx
Normal file
159
ui/src/components/Admin/AirportForm.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
import { Airport, AirportCategory } from '@/api/airport.types';
|
||||
import { Button, Checkbox, Container, Flex, Group, NumberInput, Paper, Select, TextInput, Title } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
|
||||
interface AirportFormProps {
|
||||
title: string;
|
||||
airport?: Airport;
|
||||
submitText: string;
|
||||
onSubmit: (airport: Airport) => Promise<void>;
|
||||
onDelete?: () => Promise<void>;
|
||||
}
|
||||
|
||||
export default function AirportForm({ title, airport, submitText, onSubmit, onDelete }: AirportFormProps) {
|
||||
const form = useForm<Airport>({
|
||||
initialValues: {
|
||||
icao: airport?.icao || '',
|
||||
category: airport?.category || AirportCategory.SMALL,
|
||||
name: airport?.name || '',
|
||||
elevation_ft: airport?.elevation_ft || 0,
|
||||
iso_country: airport?.iso_country || '',
|
||||
iso_region: airport?.iso_region || '',
|
||||
municipality: airport?.municipality || '',
|
||||
iata: airport?.iata || '',
|
||||
local: airport?.local || '',
|
||||
latitude: airport?.latitude || 0,
|
||||
longitude: airport?.longitude || 0,
|
||||
has_tower: airport?.has_tower || false,
|
||||
has_beacon: airport?.has_beacon || false,
|
||||
runways: airport?.runways || [],
|
||||
frequencies: airport?.frequencies || [],
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Container fluid>
|
||||
<Title ta='center'>{title}</Title>
|
||||
<Paper p={30} radius={'md'}>
|
||||
<form onSubmit={form.onSubmit(async (values) => {
|
||||
await onSubmit(values);
|
||||
form.reset();
|
||||
})}>
|
||||
<Group>
|
||||
<TextInput
|
||||
required
|
||||
label='ICAO'
|
||||
placeholder='KHEF'
|
||||
{...form.getInputProps('icao')}
|
||||
/>
|
||||
<TextInput
|
||||
label='IATA Code'
|
||||
placeholder='HEF'
|
||||
{...form.getInputProps('iata')}
|
||||
/>
|
||||
<TextInput
|
||||
label='Local Code'
|
||||
placeholder='HEF'
|
||||
{...form.getInputProps('local')}
|
||||
/>
|
||||
</Group>
|
||||
<TextInput
|
||||
required
|
||||
label='Name'
|
||||
placeholder='Manassas Regional Airport/Harry P. Davis Field'
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Select
|
||||
required
|
||||
label='Category'
|
||||
placeholder='Select category'
|
||||
data={[
|
||||
{ value: AirportCategory.SMALL, label: 'Small' },
|
||||
{ value: AirportCategory.MEDIUM, label: 'Medium' },
|
||||
{ value: AirportCategory.LARGE, label: 'Large' },
|
||||
{ value: AirportCategory.HELIPORT, label: 'Heliport' },
|
||||
{ value: AirportCategory.CLOSED, label: 'Closed' },
|
||||
{ value: AirportCategory.SEAPLANE, label: 'Seaplane Base' },
|
||||
{ value: AirportCategory.BALLOONPORT, label: 'Balloonport' },
|
||||
{ value: AirportCategory.UNKNOWN, label: 'Unknown'}
|
||||
]}
|
||||
{...form.getInputProps('category')}
|
||||
/>
|
||||
<Group>
|
||||
<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>
|
||||
<Checkbox
|
||||
mt={'xs'}
|
||||
label='Has Tower'
|
||||
defaultChecked={form.values.has_tower}
|
||||
{...form.getInputProps('has_tower')}
|
||||
/>
|
||||
<Checkbox
|
||||
mt={'xs'}
|
||||
label='Has Beacon'
|
||||
defaultChecked={form.values.has_beacon}
|
||||
{...form.getInputProps('has_beacon')}
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
hideControls
|
||||
allowNegative={false}
|
||||
decimalScale={1}
|
||||
label='Elevation (ft)'
|
||||
placeholder='192.2'
|
||||
{...form.getInputProps('elevation_ft')}
|
||||
/>
|
||||
<Group>
|
||||
<NumberInput
|
||||
required
|
||||
hideControls
|
||||
decimalScale={8}
|
||||
label='Latitude'
|
||||
placeholder='38.72140121'
|
||||
{...form.getInputProps('latitude')}
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
hideControls
|
||||
decimalScale={8}
|
||||
label='Longitude'
|
||||
placeholder='-77.51540375'
|
||||
{...form.getInputProps('longitude')}
|
||||
/>
|
||||
</Group>
|
||||
<Flex justify={'end'} mt={'sm'}>
|
||||
<Button type='submit'>{submitText}</Button>
|
||||
<Button color='red' ml={'sm'} onClick={() => form.reset()}>Reset</Button>
|
||||
{onDelete && (
|
||||
<Button
|
||||
variant='light'
|
||||
color='red'
|
||||
ml={'sm'}
|
||||
onClick={async () => await onDelete()}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</form>
|
||||
</Paper>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { CiSearch } from "react-icons/ci";
|
||||
import { notifications } from '@mantine/notifications';
|
||||
|
||||
|
||||
export default function AirportTablePanel({ setAirport }: { setAirport: (airport: Airport) => void }) {
|
||||
export default function AirportTablePanel({ setShowModal, setAirport }: { setShowModal: (value: boolean) => void, setAirport: (airport: Airport | undefined) => void }) {
|
||||
const [search, setSearch] = useState('');
|
||||
const [page, setPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
@@ -35,7 +35,10 @@ export default function AirportTablePanel({ setAirport }: { setAirport: (airport
|
||||
const rows = airports.map((airport) => (
|
||||
<Table.Tr
|
||||
key={airport.icao}
|
||||
onClick={() => setAirport(airport)}
|
||||
onClick={() => {
|
||||
setAirport(airport);
|
||||
setShowModal(true);
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<Table.Td>{airport.icao}</Table.Td>
|
||||
@@ -80,6 +83,14 @@ export default function AirportTablePanel({ setAirport }: { setAirport: (airport
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<Flex justify={'end'}>
|
||||
<Space mr={'sm'}>
|
||||
<PanelButton color={'green'} onClick={async () => {
|
||||
setAirport(undefined);
|
||||
setShowModal(true);
|
||||
}}>
|
||||
Create New
|
||||
</PanelButton>
|
||||
</Space>
|
||||
<Space mr={'sm'}>
|
||||
<PanelFileButton accept={'.json'} onChange={async (payload) => {
|
||||
if (payload instanceof File) {
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
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";
|
||||
|
||||
export default function CreateAirportPanel() {
|
||||
const form = useForm<Airport>({
|
||||
initialValues: {
|
||||
icao: '',
|
||||
category: AirportCategory.SMALL,
|
||||
name: '',
|
||||
elevation_ft: 0,
|
||||
iso_country: '',
|
||||
iso_region: '',
|
||||
municipality: '',
|
||||
iata: '',
|
||||
local: '',
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
has_tower: false,
|
||||
has_beacon: false,
|
||||
runways: [],
|
||||
frequencies: [],
|
||||
}
|
||||
});
|
||||
|
||||
return <Card shadow={'sm'} padding={'lg'} radius={'md'} withBorder>
|
||||
Create Airport
|
||||
<form onSubmit={form.onSubmit(async (values) => {
|
||||
const response = await createAirport({ airport: values });
|
||||
if (response.success) {
|
||||
form.reset();
|
||||
}
|
||||
})}>
|
||||
<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' },
|
||||
{ value: AirportCategory.HELIPORT, label: 'Heliport' },
|
||||
{ value: AirportCategory.CLOSED, label: 'Closed' },
|
||||
{ value: AirportCategory.SEAPLANE, label: 'Seaplane Base' },
|
||||
{ value: AirportCategory.BALLOONPORT, label: 'Balloonport' },
|
||||
{ value: AirportCategory.UNKNOWN, label: 'Unknown'}
|
||||
]}
|
||||
{...form.getInputProps('category')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Full Name'
|
||||
placeholder='Manassas Regional Airport/Harry P. Davis Field'
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Elevation (ft)'
|
||||
placeholder='192'
|
||||
{...form.getInputProps('elevation_ft')}
|
||||
/>
|
||||
<Group>
|
||||
<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
|
||||
label='IATA Code'
|
||||
placeholder='MNZ'
|
||||
{...form.getInputProps('iata')}
|
||||
/>
|
||||
<TextInput
|
||||
label='Local Code'
|
||||
placeholder='HEF'
|
||||
{...form.getInputProps('local')}
|
||||
/>
|
||||
</Group>
|
||||
<Group>
|
||||
<TextInput
|
||||
required
|
||||
label='Latitude'
|
||||
placeholder='38.72140121'
|
||||
{...form.getInputProps('latitude')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Longitude'
|
||||
placeholder='-77.51540375'
|
||||
{...form.getInputProps('longitude')}
|
||||
/>
|
||||
</Group>
|
||||
<Flex justify={'end'} mt={'sm'}>
|
||||
<Space mr={'sm'}>
|
||||
<Button
|
||||
type='submit'
|
||||
variant='light'
|
||||
color='blue'
|
||||
radius={'md'}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
type='button'
|
||||
variant='light'
|
||||
color='red'
|
||||
radius={'md'}
|
||||
onClick={() => form.reset()}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</Space>
|
||||
</Flex>
|
||||
</form>
|
||||
</Card>
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
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,
|
||||
name: airport?.name || '',
|
||||
elevation_ft: airport?.elevation_ft || 0,
|
||||
iso_country: airport?.iso_country || '',
|
||||
iso_region: airport?.iso_region || '',
|
||||
municipality: airport?.municipality || '',
|
||||
iata: airport?.iata || '',
|
||||
local: airport?.local || '',
|
||||
latitude: airport?.latitude || 0,
|
||||
longitude: airport?.longitude || 0,
|
||||
has_tower: airport?.has_tower || false,
|
||||
has_beacon: airport?.has_beacon || false,
|
||||
runways: airport?.runways || [],
|
||||
frequencies: airport?.frequencies || [],
|
||||
}
|
||||
});
|
||||
|
||||
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' },
|
||||
{ value: AirportCategory.HELIPORT, label: 'Heliport' },
|
||||
{ value: AirportCategory.CLOSED, label: 'Closed' },
|
||||
{ value: AirportCategory.SEAPLANE, label: 'Seaplane Base' },
|
||||
{ value: AirportCategory.BALLOONPORT, label: 'Balloonport' },
|
||||
{ value: AirportCategory.UNKNOWN, label: 'Unknown'}
|
||||
]}
|
||||
{...form.getInputProps('category')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Full Name'
|
||||
placeholder='Manassas Regional Airport/Harry P. Davis Field'
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Elevation (ft)'
|
||||
placeholder='192'
|
||||
{...form.getInputProps('elevation_ft')}
|
||||
/>
|
||||
<Group>
|
||||
<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='IATA Code'
|
||||
placeholder='HEF'
|
||||
{...form.getInputProps('iata')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Local Code'
|
||||
placeholder='HEF'
|
||||
{...form.getInputProps('local')}
|
||||
/>
|
||||
</Group>
|
||||
<Group>
|
||||
<TextInput
|
||||
required
|
||||
label='Latitude'
|
||||
placeholder='38.72140121'
|
||||
{...form.getInputProps('latitude')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label='Longitude'
|
||||
placeholder='-77.51540375'
|
||||
{...form.getInputProps('longitude')}
|
||||
/>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user