From 2395bb10bf7b0e4a267dfe7f5da212d013a0d686 Mon Sep 17 00:00:00 2001 From: april Date: Mon, 15 Jan 2024 11:52:29 -0600 Subject: [PATCH] Add image display to flight logs --- web/app/routes/logbook.dashboard/route.tsx | 1 - web/app/routes/logbook.flights.$id/route.tsx | 3 ++ web/app/routes/logbook.flights.new/route.tsx | 26 ++++++++++ web/app/ui/display/secure-img.tsx | 10 +++- web/app/ui/form/flight-form.tsx | 18 ++++--- web/app/ui/form/image-upload.tsx | 51 ++++++++++++++++++++ web/app/util/api.tsx | 4 ++ web/app/util/types.ts | 2 + 8 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 web/app/ui/form/image-upload.tsx diff --git a/web/app/routes/logbook.dashboard/route.tsx b/web/app/routes/logbook.dashboard/route.tsx index 96f0591..0183c54 100644 --- a/web/app/routes/logbook.dashboard/route.tsx +++ b/web/app/routes/logbook.dashboard/route.tsx @@ -31,7 +31,6 @@ export default function Dashboard() { useEffect(() => { if (totals.isFetched && !!totals.data) { - console.log(totals.data); setTotalsData(totals.data); } }, [totals.data]); diff --git a/web/app/routes/logbook.flights.$id/route.tsx b/web/app/routes/logbook.flights.$id/route.tsx index 9f3bf79..247f40a 100644 --- a/web/app/routes/logbook.flights.$id/route.tsx +++ b/web/app/routes/logbook.flights.$id/route.tsx @@ -77,6 +77,7 @@ export default function Flight() { ) : null} + {deleteFlight.isPending ? : null} @@ -131,6 +132,7 @@ export default function Flight() { {imageIds.length > 0 ? ( diff --git a/web/app/routes/logbook.flights.new/route.tsx b/web/app/routes/logbook.flights.new/route.tsx index 94aba45..8428b28 100644 --- a/web/app/routes/logbook.flights.new/route.tsx +++ b/web/app/routes/logbook.flights.new/route.tsx @@ -16,6 +16,32 @@ export default function NewFlight() { const newFlight = flightCreateHelper(values); if (newFlight) { const res = await client.post("/flights", newFlight); + + const id = res.data.id; + if (!id) throw new Error("Flight creation failed"); + + console.log(values.images); + + const imageForm = new FormData(); + + // Upload images + for (const img of values.images) { + imageForm.append("images", img); + } + + console.log(imageForm); + + const img_id = await client.post( + `/flights/${id}/add_images`, + imageForm, + { headers: { "Content-Type": "multipart/form-data" } } + ); + + if (!img_id) { + await queryClient.invalidateQueries({ queryKey: ["flights-list"] }); + throw new Error("Image upload failed"); + } + return res.data; } throw new Error("Flight creation failed"); diff --git a/web/app/ui/display/secure-img.tsx b/web/app/ui/display/secure-img.tsx index 30e4b1b..f7f0315 100644 --- a/web/app/ui/display/secure-img.tsx +++ b/web/app/ui/display/secure-img.tsx @@ -45,10 +45,12 @@ function useFetchImageAsBase64( export default function SecureImage({ id, radius = "sm", + h = "", clickable = true, }: { id: string; radius?: string; + h?: string; clickable?: boolean; }) { const { isLoading, error, data } = useFetchImageAsBase64(id); @@ -57,7 +59,7 @@ export default function SecureImage({ if (isLoading) return ( -
+
); @@ -80,6 +82,12 @@ export default function SecureImage({ { if (clickable) { open(); diff --git a/web/app/ui/form/flight-form.tsx b/web/app/ui/form/flight-form.tsx index ae7856e..32930b0 100644 --- a/web/app/ui/form/flight-form.tsx +++ b/web/app/ui/form/flight-form.tsx @@ -10,6 +10,7 @@ import { Container, Fieldset, Group, + Loader, Modal, NumberInput, ScrollArea, @@ -34,6 +35,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useApi } from "@/util/api"; import { useAircraft } from "@/util/hooks"; import { useEffect, useState } from "react"; +import ImageUpload from "./image-upload"; export default function FlightForm({ onSubmit, @@ -101,6 +103,8 @@ export default function FlightForm({ crew: [], comments: "", + + images: [], }, }); @@ -492,20 +496,22 @@ export default function FlightForm({ minRows={4} {...form.getInputProps("comments")} /> + {isPending ? ( - - Loading... - + ) : isError ? ( - {error instanceof AxiosError - ? error.response?.data.detail - : error?.message} + {error?.message} ) : null} {withCancelButton ? ( diff --git a/web/app/ui/form/image-upload.tsx b/web/app/ui/form/image-upload.tsx new file mode 100644 index 0000000..5eb22dc --- /dev/null +++ b/web/app/ui/form/image-upload.tsx @@ -0,0 +1,51 @@ +import { FlightFormSchema } from "@/util/types"; +import { FileInput, FileInputProps, Pill } from "@mantine/core"; +import { UseFormReturnType } from "@mantine/form"; +import { randomId } from "@mantine/hooks"; +import { IconPhoto } from "@tabler/icons-react"; + +export default function ImageUpload({ + form, + field, + label = "", + placeholder = "", +}: { + form: UseFormReturnType< + FlightFormSchema, + (values: FlightFormSchema) => FlightFormSchema + >; + field: string; + label?: string; + placeholder?: string; +}) { + const ValueComponent: FileInputProps["valueComponent"] = ({ value }) => { + if (value === null) { + return null; + } + + if (Array.isArray(value)) { + return ( + + {value.map((file) => ( + {file.name} + ))} + + ); + } + + return {value.name}; + }; + + return ( + } + {...form.getInputProps(field)} + /> + ); +} diff --git a/web/app/util/api.tsx b/web/app/util/api.tsx index a17df2c..0b38b55 100644 --- a/web/app/util/api.tsx +++ b/web/app/util/api.tsx @@ -58,3 +58,7 @@ function useProvideApi(apiUrl: string) { return client; } + +export function createClient() { + return axios.create({}); +} diff --git a/web/app/util/types.ts b/web/app/util/types.ts index d1f5aed..f27824e 100644 --- a/web/app/util/types.ts +++ b/web/app/util/types.ts @@ -50,6 +50,8 @@ type FlightFormSchema = FlightBaseSchema & { time_off: number | null; time_down: number | null; time_stop: number | null; + + images: File[]; }; type FlightCreateSchema = FlightBaseSchema & {