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) => (
+ //
+
+ {/* */}
+
+ {/* */}
+ }
+ onClick={() =>
+ form.setFieldValue(
+ field,
+ (form.getTransformedValues()[field_key] as string[]).filter(
+ (i) => i !== id
+ )
+ )
+ }
+ >
+ Remove
+
+
+ //
+ ))}
+
+ >
+ //
+ //
+ //
+ // {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) => (
+
+
+ }
+ onClick={() => setImageIds(imageIds.filter((i) => i !== id))}
+ >
+ Remove
+
+
+ ))}
+
+
+ >
+ );
+}
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) {