diff --git a/web/app/routes/logbook.flights.$id/route.tsx b/web/app/routes/logbook.flights.$id/route.tsx index 23b39a4..11ad499 100644 --- a/web/app/routes/logbook.flights.$id/route.tsx +++ b/web/app/routes/logbook.flights.$id/route.tsx @@ -1,6 +1,5 @@ import CollapsibleFieldset from "@/ui/display/collapsible-fieldset"; import { VerticalLogItem } from "@/ui/display/log-item"; -import SecureImage from "@/ui/display/secure-img"; import ErrorDisplay from "@/ui/error-display"; import { useApi } from "@/util/api"; import { @@ -18,8 +17,7 @@ import { Modal, Button, } from "@mantine/core"; -import { Carousel } from "@mantine/carousel"; -import { randomId, useDisclosure } from "@mantine/hooks"; +import { useDisclosure } from "@mantine/hooks"; import { useNavigate, useParams } from "@remix-run/react"; import { IconPencil, IconTrash } from "@tabler/icons-react"; import { useMutation, useQuery } from "@tanstack/react-query"; @@ -142,7 +140,7 @@ export default function Flight() { diff --git a/web/app/routes/logbook.flights.new/route.tsx b/web/app/routes/logbook.flights.new/route.tsx index d48faaf..518684a 100644 --- a/web/app/routes/logbook.flights.new/route.tsx +++ b/web/app/routes/logbook.flights.new/route.tsx @@ -23,19 +23,21 @@ export default function NewFlight() { const imageForm = new FormData(); // Upload images - for (const img of values.images) { - imageForm.append("images", img); - } + if (values.images.length > 0) { + for (const img of values.images) { + imageForm.append("images", img); + } - const img_id = await client.post( - `/flights/${id}/add_images`, - imageForm, - { headers: { "Content-Type": "multipart/form-data" } } - ); + 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"); + if (!img_id) { + await queryClient.invalidateQueries({ queryKey: ["flights-list"] }); + throw new Error("Image upload failed"); + } } return res.data; diff --git a/web/app/ui/display/editable/img-log-item.tsx b/web/app/ui/display/editable/img-log-item.tsx index eab5e7f..35ecab5 100644 --- a/web/app/ui/display/editable/img-log-item.tsx +++ b/web/app/ui/display/editable/img-log-item.tsx @@ -13,9 +13,9 @@ import { Text, } from "@mantine/core"; import { IconPencil } from "@tabler/icons-react"; -import ListInput from "@/ui/input/list-input"; +import ImageListInput from "@/ui/input/image-list-input"; import ImageUpload from "@/ui/input/image-upload"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useApi } from "@/util/api"; import { useMutation, useQueryClient } from "@tanstack/react-query"; @@ -66,18 +66,23 @@ export default function ImageLogItem({ ); if (!img_id) { + await queryClient.invalidateQueries({ queryKey: [id] }); await queryClient.invalidateQueries({ queryKey: ["flights-list"] }); throw new Error("Image upload failed"); } } }, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: [id] }); - queryClient.invalidateQueries({ queryKey: ["flights-list"] }); + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: [id] }); + await queryClient.invalidateQueries({ queryKey: ["flights-list"] }); closeEdit(); }, }); + useEffect(() => { + setExistingImages(imageIds); + }, [imageIds]); + return ( <> - + {updateValue.isPending ? : null} diff --git a/web/app/ui/form/flight-form.tsx b/web/app/ui/form/flight-form.tsx index ac2e2e2..d35bdc8 100644 --- a/web/app/ui/form/flight-form.tsx +++ b/web/app/ui/form/flight-form.tsx @@ -35,6 +35,7 @@ import { useApi } from "@/util/api"; import { useAircraft } from "@/util/hooks"; import { useEffect, useState } from "react"; import ImageUpload from "./image-upload"; +import ImageListInput from "./image-list-input"; export default function FlightForm({ onSubmit, @@ -59,7 +60,8 @@ export default function FlightForm({ cancelFunc?: () => void; autofillHobbs?: boolean; }) { - const validate_time = (value) => { + const validate_time = (value: number | null) => { + if (value === null) return; if (value > 2359) return "Time must be between 0000 and 2359"; if (value % 100 > 59) return "Minutes must not exceed 59"; }; @@ -163,6 +165,7 @@ export default function FlightForm({ getHobbs.data.hobbs ?? form.getTransformedValues()["hobbs_start"] ); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [getHobbs.data]); return ( @@ -332,7 +335,9 @@ export default function FlightForm({ style={{ display: ["", null].indexOf( - form.getTransformedValues()["hobbs_start"] + form.getTransformedValues()["hobbs_start"] as + | string + | null ) > -1 ? "none" : undefined, @@ -509,12 +514,12 @@ export default function FlightForm({ {...form.getInputProps("comments")} /> {initialValues?.existing_images?.length ?? 0 > 0 ? ( - ) : null} FlightFormSchema + >; + field: string; + mt?: string; + w?: string; + h?: string; +}) { + const field_key = field as keyof typeof form.getTransformedValues; + + return ( + <> + {/* */} + + {label} + + + {(form.getTransformedValues()[field_key] as string[]).map((id) => ( + // + + {/* */} + + {/* */} + + + // + ))} + + + // + // + // + // {imageIds.map((id: string) => ( + // setImageIds(imageIds.filter((i) => i !== id))} + // > + // + // + // ))} + // + // + ); +} diff --git a/web/app/ui/input/image-list-input.tsx b/web/app/ui/input/image-list-input.tsx new file mode 100644 index 0000000..938fc65 --- /dev/null +++ b/web/app/ui/input/image-list-input.tsx @@ -0,0 +1,65 @@ +import { + ActionIcon, + Button, + Card, + Collapse, + Group, + Text, + Tooltip, +} from "@mantine/core"; +import { Dispatch, SetStateAction, useState } from "react"; +import SecureImage from "../display/secure-img"; +import { randomId } from "@mantine/hooks"; +import { IconMinus, IconPlus, IconTrash } from "@tabler/icons-react"; + +export default function ImageListInput({ + label, + imageIds, + setImageIds, + collapsible = false, + startCollapsed = true, +}: { + label: string; + imageIds: string[]; + setImageIds: Dispatch>; + collapsible?: boolean; + startCollapsed?: boolean; +}) { + const [collapsed, setCollapsed] = useState(startCollapsed); + return ( + <> + + + {label} + + + {collapsible ? ( + + setCollapsed(!collapsed)} + > + {collapsed ? : } + + + ) : null} + + + + {imageIds.map((id) => ( + + + + + ))} + + + + ); +} diff --git a/web/app/util/types.ts b/web/app/util/types.ts index 096865f..e65525b 100644 --- a/web/app/util/types.ts +++ b/web/app/util/types.ts @@ -58,10 +58,10 @@ type FlightFormSchema = FlightBaseSchema & { type FlightCreateSchema = FlightBaseSchema & { date: string; - time_start: string; - time_off: string; - time_down: string; - time_stop: string; + time_start: string | null; + time_off: string | null; + time_down: string | null; + time_stop: string | null; }; type FlightDisplaySchema = FlightBaseSchema & { @@ -101,34 +101,42 @@ const flightCreateHelper = ( 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, - 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: 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: 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: date - .utc() - .hour(Math.floor((values.time_stop ?? 0) / 100)) - .minute(Math.floor((values.time_stop ?? 0) % 100)) - .second(0) - .millisecond(0) - .toISOString(), + time_start: values.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() + : null, + time_off: values.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() + : null, + time_down: values.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() + : null, + time_stop: values.time_stop + ? date + .utc() + .hour(Math.floor((values.time_stop ?? 0) / 100)) + .minute(Math.floor((values.time_stop ?? 0) % 100)) + .second(0) + .millisecond(0) + .toISOString() + : null, }; return newFlight; } catch (err) {