Move aircraft form to separate module

This commit is contained in:
april 2024-01-10 14:10:29 -06:00
parent 1958f6dc5f
commit d6d03c9027
3 changed files with 185 additions and 125 deletions

View File

@ -1,4 +1,5 @@
import ErrorDisplay from "@/ui/error-display"; import ErrorDisplay from "@/ui/error-display";
import AircraftForm from "@/ui/form/aircraft-form";
import { useApi } from "@/util/api"; import { useApi } from "@/util/api";
import { AircraftFormSchema, AircraftSchema } from "@/util/types"; import { AircraftFormSchema, AircraftSchema } from "@/util/types";
import { import {
@ -125,54 +126,9 @@ function NewAircraftModal({
opened: boolean; opened: boolean;
close: () => void; close: () => void;
}) { }) {
const newForm = useForm<AircraftFormSchema>({
initialValues: {
tail_no: "",
make: "",
model: "",
aircraft_category: "",
aircraft_class: "",
hobbs: 0.0,
},
validate: {
tail_no: (value) =>
value === null || value.trim() === ""
? "Please enter a tail number"
: null,
make: (value) =>
value === null || value.trim() === "" ? "Please enter a make" : null,
model: (value) =>
value === null || value.trim() === "" ? "Please enter a model" : null,
aircraft_category: (value) =>
value === null || value.trim() === ""
? "Please select a category"
: null,
aircraft_class: (value) =>
value === null || value.trim() === "" ? "Please select a class" : null,
},
});
const client = useApi(); const client = useApi();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const categories = useQuery({
queryKey: ["categories"],
queryFn: async () =>
await client.get(`/aircraft/categories`).then((res) => res.data),
});
const [category, setCategory] = useState("");
const [classSelection, setClassSelection] = useState("");
const classes = useQuery({
queryKey: ["classes", category],
queryFn: async () =>
await client
.get(`/aircraft/class?category=${category}`)
.then((res) => res.data),
enabled: !!category,
});
const addAircraft = useMutation({ const addAircraft = useMutation({
mutationFn: async (values: AircraftFormSchema) => { mutationFn: async (values: AircraftFormSchema) => {
const newAircraft = values; const newAircraft = values;
@ -190,84 +146,13 @@ function NewAircraftModal({
return ( return (
<Modal opened={opened} onClose={close} title="New Aircraft" centered> <Modal opened={opened} onClose={close} title="New Aircraft" centered>
<form onSubmit={newForm.onSubmit((values) => addAircraft.mutate(values))}> <AircraftForm
<Container> onSubmit={addAircraft.mutate}
<Stack> isError={addAircraft.isError}
<TextInput error={addAircraft.error}
label="Tail Number" isPending={addAircraft.isPending}
withAsterisk submitButtonLabel="Add"
{...newForm.getInputProps("tail_no")} />
/>
<TextInput
label="Make"
{...newForm.getInputProps("make")}
withAsterisk
/>
<TextInput
label="Model"
{...newForm.getInputProps("model")}
withAsterisk
/>
<Select
{...newForm.getInputProps("aircraft_category")}
label="Category"
placeholder="Pick a value"
name="aircraft_category"
withAsterisk
data={
categories.isFetched && !categories.isError
? categories.data.categories
: []
}
onChange={(_value, option) => {
setCategory(option.value);
setClassSelection("");
queryClient.invalidateQueries({
queryKey: ["classes", option.value],
});
newForm.setFieldValue("aircraft_category", option.value);
}}
/>
<Select
label="Class"
placeholder="Pick a value"
withAsterisk
data={
classes.isFetched && !classes.isError && classes.data
? classes.data.classes
: []
}
value={classSelection}
{...newForm.getInputProps("aircraft_class")}
/>
<NumberInput
label="Hobbs"
min={0.0}
suffix=" hrs"
decimalScale={1}
fixedDecimalScale
{...newForm.getInputProps("hobbs")}
/>
<Group justify="flex-end">
{addAircraft.isError ? (
<Text c="red">
{(addAircraft.error as AxiosError)?.response?.data?.detail ??
"Error adding aircraft"}
</Text>
) : addAircraft.isPending ? (
<Text c="yellow">Adding aircraft...</Text>
) : null}
<Button
type="submit"
leftSection={<IconPencil />}
onClick={() => null}
>
Create
</Button>
</Group>
</Stack>
</Container>
</form>
</Modal> </Modal>
); );
} }

View File

@ -0,0 +1,173 @@
import { useApi } from "@/util/api";
import { AircraftFormSchema } from "@/util/types";
import {
Button,
Container,
Group,
NumberInput,
Select,
Stack,
Text,
TextInput,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import { IconPencil, IconX } from "@tabler/icons-react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { useState } from "react";
export default function AircraftForm({
onSubmit,
isError,
error,
isPending,
initialValues,
submitButtonLabel,
withCancelButton,
cancelFunc,
}: {
onSubmit: (values: AircraftFormSchema) => void;
isError: boolean;
error: Error | null;
isPending: boolean;
initialValues?: AircraftFormSchema | null;
mah?: string;
submitButtonLabel?: string;
withCancelButton?: boolean;
cancelFunc?: () => void;
}) {
const newForm = useForm<AircraftFormSchema>({
initialValues: initialValues ?? {
tail_no: "",
make: "",
model: "",
aircraft_category: "",
aircraft_class: "",
hobbs: 0.0,
},
validate: {
tail_no: (value) =>
value === null || value.trim() === ""
? "Please enter a tail number"
: null,
make: (value) =>
value === null || value.trim() === "" ? "Please enter a make" : null,
model: (value) =>
value === null || value.trim() === "" ? "Please enter a model" : null,
aircraft_category: (value) =>
value === null || value.trim() === ""
? "Please select a category"
: null,
aircraft_class: (value) =>
value === null || value.trim() === "" ? "Please select a class" : null,
},
});
const client = useApi();
const queryClient = useQueryClient();
const categories = useQuery({
queryKey: ["categories"],
queryFn: async () =>
await client.get(`/aircraft/categories`).then((res) => res.data),
});
const [category, setCategory] = useState("");
const [classSelection, setClassSelection] = useState("");
const classes = useQuery({
queryKey: ["classes", category],
queryFn: async () =>
await client
.get(`/aircraft/class?category=${category}`)
.then((res) => res.data),
enabled: !!category,
});
return (
<form onSubmit={newForm.onSubmit((values) => onSubmit(values))}>
<Container>
<Stack>
<TextInput
label="Tail Number"
withAsterisk
{...newForm.getInputProps("tail_no")}
/>
<TextInput
label="Make"
{...newForm.getInputProps("make")}
withAsterisk
/>
<TextInput
label="Model"
{...newForm.getInputProps("model")}
withAsterisk
/>
<Select
{...newForm.getInputProps("aircraft_category")}
label="Category"
placeholder="Pick a value"
name="aircraft_category"
withAsterisk
data={
categories.isFetched && !categories.isError
? categories.data.categories
: []
}
onChange={(_value, option) => {
setCategory(option.value);
setClassSelection("");
queryClient.invalidateQueries({
queryKey: ["classes", option.value],
});
newForm.setFieldValue("aircraft_category", option.value);
}}
/>
<Select
label="Class"
placeholder="Pick a value"
withAsterisk
data={
classes.isFetched && !classes.isError && classes.data
? classes.data.classes
: []
}
value={classSelection}
{...newForm.getInputProps("aircraft_class")}
/>
<NumberInput
label="Hobbs"
min={0.0}
suffix=" hrs"
decimalScale={1}
fixedDecimalScale
{...newForm.getInputProps("hobbs")}
/>
<Group justify="flex-end">
{isError ? (
<Text c="red">
{error instanceof AxiosError
? error?.response?.data?.detail ?? "Error adding aircraft"
: error?.message}
</Text>
) : isPending ? (
<Text c="yellow">Adding aircraft...</Text>
) : null}
{withCancelButton ? (
<Button leftSection={<IconX />} color="gray" onClick={cancelFunc}>
Cancel
</Button>
) : null}
<Button
type="submit"
leftSection={<IconPencil />}
onClick={() => null}
>
{submitButtonLabel ?? "Submit"}
</Button>
</Group>
</Stack>
</Container>
</form>
);
}

View File

@ -33,7 +33,7 @@ export default function FlightForm({
}: { }: {
onSubmit: (values: FlightFormSchema) => void; onSubmit: (values: FlightFormSchema) => void;
isError: boolean; isError: boolean;
error: AxiosError | null; error: Error | null;
initialValues?: FlightFormSchema | null; initialValues?: FlightFormSchema | null;
mah?: string; mah?: string;
submitButtonLabel?: string; submitButtonLabel?: string;
@ -262,7 +262,9 @@ export default function FlightForm({
<Group justify="flex-end" mt="md"> <Group justify="flex-end" mt="md">
{isError ? ( {isError ? (
<Text c="red" fw={700}> <Text c="red" fw={700}>
{error?.message} {error instanceof AxiosError
? error.response?.data.detail
: error?.message}
</Text> </Text>
) : null} ) : null}
{withCancelButton ? ( {withCancelButton ? (