Implement flight deletion

This commit is contained in:
april 2024-01-05 14:54:44 -06:00
parent 40108d1070
commit 654393bab8
6 changed files with 356 additions and 275 deletions

View File

@ -37,6 +37,7 @@ import {
Stack,
Title,
} from "@mantine/core";
import { ModalsProvider } from "@mantine/modals";
import { IconRocket } from "@tabler/icons-react";
import { AuthProvider } from "./util/auth";
@ -159,12 +160,14 @@ export default function App() {
<HydrationBoundary state={dehydratedState}>
<ApiProvider apiUrl={data.ENV.TAILFIN_API_URL}>
<MantineProvider theme={{ primaryColor: "violet" }}>
<ModalsProvider>
<AuthProvider>
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
</AuthProvider>
</ModalsProvider>
</MantineProvider>
</ApiProvider>
<ReactQueryDevtools initialIsOpen={false} />

View File

@ -2,21 +2,31 @@ import { VerticalLogItem } from "@/ui/display/log-item";
import ErrorDisplay from "@/ui/error-display";
import { useApi } from "@/util/api";
import {
ActionIcon,
Center,
Container,
Grid,
Group,
Loader,
ScrollArea,
Stack,
Title,
Tooltip,
Text,
Modal,
Button,
} from "@mantine/core";
import { useParams } from "@remix-run/react";
import { useQuery } from "@tanstack/react-query";
import { useDisclosure } from "@mantine/hooks";
import { modals } from "@mantine/modals";
import { useNavigate, useParams } from "@remix-run/react";
import { IconPencil, IconTrash } from "@tabler/icons-react";
import { useMutation, useQuery } from "@tanstack/react-query";
export default function Flight() {
const params = useParams();
const client = useApi();
const navigate = useNavigate();
const flight = useQuery({
queryKey: [params.id],
@ -24,10 +34,48 @@ export default function Flight() {
await client.get(`/flights/${params.id}`).then((res) => res.data),
});
const [deleteOpened, { open: openDelete, close: closeDelete }] =
useDisclosure(false);
const deleteFlight = useMutation({
mutationFn: async () =>
await client.delete(`/flights/${params.id}`).then((res) => res.data),
onSuccess: () => {
navigate("/logbook/flights");
},
});
const log = flight.data;
return (
// <Container>
<>
<Modal
opened={deleteOpened}
onClose={closeDelete}
title="Delete Flight?"
centered
>
<Stack>
<Text>
Are you sure you want to delete this flight? This action cannot be
undone.
</Text>
{deleteFlight.isError ? (
<Text c="red" fw={700}>
{deleteFlight.error.message}
</Text>
) : null}
<Group justify="flex-end">
<Button color="red" onClick={() => deleteFlight.mutate()}>
Delete
</Button>
<Button color="gray" onClick={closeDelete}>
Cancel
</Button>
</Group>
</Stack>
</Modal>
<Container>
<Stack h="calc(100vh-95px)">
{flight.isError ? (
<Center h="calc(100vh - 95px)">
@ -39,9 +87,28 @@ export default function Flight() {
</Center>
) : flight.data ? (
<>
<Title order={3} py="lg" style={{ textAlign: "center" }}>
<Group justify="space-between" px="xl">
<Title order={2} py="lg" style={{ textAlign: "center" }}>
Flight Log
</Title>
<Group>
<Tooltip label="Edit Flight">
<ActionIcon variant="subtle" size="lg">
<IconPencil />
</ActionIcon>
</Tooltip>
<Tooltip label="Delete Flight">
<ActionIcon
variant="subtle"
size="lg"
color="red"
onClick={openDelete}
>
<IconTrash />
</ActionIcon>
</Tooltip>
</Group>
</Group>
<ScrollArea h="calc(100vh - 95px - 110px)" m="0" p="0">
<Container h="100%">
<Grid justify="center">
@ -49,7 +116,10 @@ export default function Flight() {
<VerticalLogItem label="Date" content={log.date} date />
</Grid.Col>
<Grid.Col span={6}>
<VerticalLogItem label="Aircraft" content={log.aircraft} />
<VerticalLogItem
label="Aircraft"
content={log.aircraft}
/>
</Grid.Col>
{log.waypoint_from || log.waypoint_to ? (
<>
@ -259,6 +329,7 @@ export default function Flight() {
</Center>
)}
</Stack>
// </Container>
</Container>
</>
);
}

View File

@ -5,8 +5,9 @@ import {
Fieldset,
Group,
NumberInput,
ScrollAreaAutosize,
ScrollArea,
Stack,
Text,
TextInput,
Textarea,
Title,
@ -23,7 +24,6 @@ import ListInput from "@/ui/form/list-input";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useApi } from "@/util/api";
import { useNavigate } from "@remix-run/react";
import { useAuth } from "@/util/auth";
import { AxiosError } from "axios";
export default function NewFlight() {
@ -82,24 +82,18 @@ export default function NewFlight() {
const client = useApi();
const { clearUser } = useAuth();
const createFlight = useMutation({
mutationFn: async (values: FlightFormSchema) => {
const newFlight = flightCreateHelper(values);
if (newFlight) {
const res = await client.post("/flights", newFlight);
return res.data;
}
throw new Error("Flight creation failed");
},
retry: (failureCount, error: AxiosError) => {
return !error || error.response?.status !== 401;
},
onError: (error: AxiosError) => {
console.log(error);
if (error.response?.status === 401) {
clearUser();
navigate("/login");
}
},
onSuccess: async (data: { id: string }) => {
await queryClient.invalidateQueries({ queryKey: ["flights-list"] });
navigate(`/logbook/flights/${data.id}`);
@ -112,7 +106,7 @@ export default function NewFlight() {
<Title order={2}>New Flight</Title>
<form onSubmit={form.onSubmit((values) => createFlight.mutate(values))}>
<ScrollAreaAutosize mah="calc(100vh - 95px - 110px)">
<ScrollArea.Autosize mah="calc(100vh - 95px - 110px)">
<Container>
{/* Date and Aircraft */}
@ -347,9 +341,14 @@ export default function NewFlight() {
/>
</Fieldset>
</Container>
</ScrollAreaAutosize>
</ScrollArea.Autosize>
<Group justify="flex-end" mt="md">
{createFlight.isError ? (
<Text c="red" fw={700}>
{createFlight.error.message}
</Text>
) : null}
<Button type="submit" leftSection={<IconPencil />}>
Create
</Button>

View File

@ -37,7 +37,7 @@ export default function ListInput({
};
return (
<PillsInput label={label}>
<PillsInput label={label} description="Press enter or comma to add item">
<Pill.Group>
{(form.getTransformedValues()[field_key] as string[]).map(
(item: string) => (

View File

@ -88,37 +88,40 @@ type FlightConciseSchema = {
comments: string;
};
const flightCreateHelper = (values: FlightFormSchema): FlightCreateSchema => {
console.log(values.date.utc().startOf("date").toISOString());
return {
const flightCreateHelper = (
values: FlightFormSchema
): FlightCreateSchema | void => {
const date = dayjs(values.date);
try {
const newFlight = {
...values,
date: values.date.utc().startOf("day").toISOString(),
hobbs_start: Number(values.hobbs_start),
hobbs_end: Number(values.hobbs_end),
tach_start: Number(values.tach_start),
tach_end: Number(values.tach_end),
time_start: values.date
date: date.utc().startOf("day").toISOString(),
hobbs_start: values.hobbs_start ? Number(values.hobbs_start) : null,
hobbs_end: values.hobbs_end ? Number(values.hobbs_end) : null,
tach_start: values.tach_start ? Number(values.tach_start) : null,
tach_end: values.tach_end ? Number(values.tach_end) : null,
time_start: date
.utc()
.hour(Math.floor((values.time_start ?? 0) / 100))
.minute(Math.floor((values.time_start ?? 0) % 100))
.second(0)
.millisecond(0)
.toISOString(),
time_off: values.date
time_off: date
.utc()
.hour(Math.floor((values.time_off ?? 0) / 100))
.minute(Math.floor((values.time_off ?? 0) % 100))
.second(0)
.millisecond(0)
.toISOString(),
time_down: values.date
time_down: date
.utc()
.hour(Math.floor((values.time_down ?? 0) / 100))
.minute(Math.floor((values.time_down ?? 0) % 100))
.second(0)
.millisecond(0)
.toISOString(),
time_stop: values.date
time_stop: date
.utc()
.hour(Math.floor((values.time_stop ?? 0) / 100))
.minute(Math.floor((values.time_stop ?? 0) % 100))
@ -126,6 +129,10 @@ const flightCreateHelper = (values: FlightFormSchema): FlightCreateSchema => {
.millisecond(0)
.toISOString(),
};
return newFlight;
} catch (err) {
console.log(err);
}
};
export {

1
web/package-lock.json generated
View File

@ -4,6 +4,7 @@
"requires": true,
"packages": {
"": {
"name": "tailfin-web",
"dependencies": {
"@mantine/core": "^7.4.0",
"@mantine/dates": "^7.4.0",