Add image display to flight logs

This commit is contained in:
april 2024-01-15 11:52:29 -06:00
parent 4b80593aa3
commit 2395bb10bf
8 changed files with 107 additions and 8 deletions

View File

@ -31,7 +31,6 @@ export default function Dashboard() {
useEffect(() => { useEffect(() => {
if (totals.isFetched && !!totals.data) { if (totals.isFetched && !!totals.data) {
console.log(totals.data);
setTotalsData(totals.data); setTotalsData(totals.data);
} }
}, [totals.data]); }, [totals.data]);

View File

@ -77,6 +77,7 @@ export default function Flight() {
</Text> </Text>
) : null} ) : null}
<Group justify="flex-end"> <Group justify="flex-end">
{deleteFlight.isPending ? <Loader /> : null}
<Button color="red" onClick={() => deleteFlight.mutate()}> <Button color="red" onClick={() => deleteFlight.mutate()}>
Delete Delete
</Button> </Button>
@ -131,6 +132,7 @@ export default function Flight() {
{imageIds.length > 0 ? ( {imageIds.length > 0 ? (
<CollapsibleFieldset legend="Images" mt="sm" w="100%"> <CollapsibleFieldset legend="Images" mt="sm" w="100%">
<Carousel <Carousel
style={{ maxHeight: "700px" }}
withIndicators withIndicators
slideGap="sm" slideGap="sm"
slideSize={{ base: "100%", sm: "80%" }} slideSize={{ base: "100%", sm: "80%" }}
@ -140,6 +142,7 @@ export default function Flight() {
<SecureImage <SecureImage
key={randomId()} key={randomId()}
id={img} id={img}
h="700px"
radius="lg" radius="lg"
/> />
</Carousel.Slide> </Carousel.Slide>

View File

@ -16,6 +16,32 @@ export default function NewFlight() {
const newFlight = flightCreateHelper(values); const newFlight = flightCreateHelper(values);
if (newFlight) { if (newFlight) {
const res = await client.post("/flights", 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; return res.data;
} }
throw new Error("Flight creation failed"); throw new Error("Flight creation failed");

View File

@ -45,10 +45,12 @@ function useFetchImageAsBase64(
export default function SecureImage({ export default function SecureImage({
id, id,
radius = "sm", radius = "sm",
h = "",
clickable = true, clickable = true,
}: { }: {
id: string; id: string;
radius?: string; radius?: string;
h?: string;
clickable?: boolean; clickable?: boolean;
}) { }) {
const { isLoading, error, data } = useFetchImageAsBase64(id); const { isLoading, error, data } = useFetchImageAsBase64(id);
@ -57,7 +59,7 @@ export default function SecureImage({
if (isLoading) if (isLoading)
return ( return (
<Center h="100%"> <Center h="500px">
<Loader /> <Loader />
</Center> </Center>
); );
@ -80,6 +82,12 @@ export default function SecureImage({
<Image <Image
src={`data:${data?.type};base64,${data?.blob}`} src={`data:${data?.type};base64,${data?.blob}`}
radius={radius} radius={radius}
w="auto"
maw="100%"
h="100%"
m="auto"
fit="contain"
style={{ maxHeight: h ?? "" }}
onClick={() => { onClick={() => {
if (clickable) { if (clickable) {
open(); open();

View File

@ -10,6 +10,7 @@ import {
Container, Container,
Fieldset, Fieldset,
Group, Group,
Loader,
Modal, Modal,
NumberInput, NumberInput,
ScrollArea, ScrollArea,
@ -34,6 +35,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useApi } from "@/util/api"; import { useApi } from "@/util/api";
import { useAircraft } from "@/util/hooks"; import { useAircraft } from "@/util/hooks";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import ImageUpload from "./image-upload";
export default function FlightForm({ export default function FlightForm({
onSubmit, onSubmit,
@ -101,6 +103,8 @@ export default function FlightForm({
crew: [], crew: [],
comments: "", comments: "",
images: [],
}, },
}); });
@ -492,20 +496,22 @@ export default function FlightForm({
minRows={4} minRows={4}
{...form.getInputProps("comments")} {...form.getInputProps("comments")}
/> />
<ImageUpload
form={form}
field="images"
label="Images"
placeholder="Upload Images"
/>
</Fieldset> </Fieldset>
</Container> </Container>
</ScrollArea.Autosize> </ScrollArea.Autosize>
<Group justify="flex-end" mt="md"> <Group justify="flex-end" mt="md">
{isPending ? ( {isPending ? (
<Text c="yellow" fw={700}> <Loader />
Loading...
</Text>
) : isError ? ( ) : isError ? (
<Text c="red" fw={700}> <Text c="red" fw={700}>
{error instanceof AxiosError {error?.message}
? error.response?.data.detail
: error?.message}
</Text> </Text>
) : null} ) : null}
{withCancelButton ? ( {withCancelButton ? (

View File

@ -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 (
<Pill.Group>
{value.map((file) => (
<Pill key={randomId()}>{file.name}</Pill>
))}
</Pill.Group>
);
}
return <Pill>{value.name}</Pill>;
};
return (
<FileInput
label={label}
placeholder={placeholder}
multiple
accept="image/*"
valueComponent={ValueComponent}
rightSectionPointerEvents="none"
rightSection={<IconPhoto />}
{...form.getInputProps(field)}
/>
);
}

View File

@ -58,3 +58,7 @@ function useProvideApi(apiUrl: string) {
return client; return client;
} }
export function createClient() {
return axios.create({});
}

View File

@ -50,6 +50,8 @@ type FlightFormSchema = FlightBaseSchema & {
time_off: number | null; time_off: number | null;
time_down: number | null; time_down: number | null;
time_stop: number | null; time_stop: number | null;
images: File[];
}; };
type FlightCreateSchema = FlightBaseSchema & { type FlightCreateSchema = FlightBaseSchema & {