Implement inline editing and image upload/delete/edit
This commit is contained in:
170
web/app/ui/display/editable/aircraft-log-item.tsx
Normal file
170
web/app/ui/display/editable/aircraft-log-item.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
import AircraftForm from "@/ui/form/aircraft-form";
|
||||
import { useApi } from "@/util/api";
|
||||
import { useAircraft, usePatchFlight } from "@/util/hooks";
|
||||
import { AircraftFormSchema, AircraftSchema } from "@/util/types";
|
||||
import {
|
||||
ActionIcon,
|
||||
Group,
|
||||
Tooltip,
|
||||
Text,
|
||||
Select,
|
||||
Modal,
|
||||
Card,
|
||||
Stack,
|
||||
Button,
|
||||
Loader,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { IconPlus, IconPencil, IconX } from "@tabler/icons-react";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
export function AircraftLogItem({
|
||||
label,
|
||||
content,
|
||||
id = "",
|
||||
field = "",
|
||||
}: {
|
||||
label: string;
|
||||
content: string | null;
|
||||
id?: string;
|
||||
field?: string;
|
||||
}) {
|
||||
const [editValue, setEditValue] = useState<string>(content ?? "");
|
||||
const [editError, setEditError] = useState("");
|
||||
|
||||
const [editOpened, { open: openEdit, close: closeEdit }] =
|
||||
useDisclosure(false);
|
||||
|
||||
const [aircraftOpened, { open: openAircraft, close: closeAircraft }] =
|
||||
useDisclosure(false);
|
||||
|
||||
const client = useApi();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const addAircraft = useMutation({
|
||||
mutationFn: async (values: AircraftFormSchema) => {
|
||||
const newAircraft = values;
|
||||
if (newAircraft) {
|
||||
const res = await client.post("/aircraft", newAircraft);
|
||||
return res.data;
|
||||
}
|
||||
throw new Error("Aircraft creation failed");
|
||||
},
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({ queryKey: ["aircraft-list"] });
|
||||
close();
|
||||
},
|
||||
});
|
||||
|
||||
const getAircraft = useAircraft();
|
||||
|
||||
const updateValue = usePatchFlight(id, field, closeEdit);
|
||||
|
||||
if (content === null) content = "";
|
||||
const editForm = (
|
||||
<Select
|
||||
label={
|
||||
<Group gap="0">
|
||||
<Text size="sm" fw={700} span>
|
||||
Aircraft
|
||||
</Text>
|
||||
|
||||
<Tooltip label="Add Aircraft">
|
||||
<ActionIcon variant="transparent" onClick={openAircraft}>
|
||||
<IconPlus size="1rem" />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
}
|
||||
data={
|
||||
getAircraft.isFetched
|
||||
? getAircraft.data?.map((item: AircraftSchema) => ({
|
||||
value: item.tail_no,
|
||||
label: item.tail_no,
|
||||
}))
|
||||
: content
|
||||
? [
|
||||
{
|
||||
value: content,
|
||||
label: content,
|
||||
},
|
||||
]
|
||||
: null
|
||||
}
|
||||
allowDeselect={false}
|
||||
value={editValue}
|
||||
onChange={(_value, option) => {
|
||||
setEditError("");
|
||||
setEditValue(option.label);
|
||||
}}
|
||||
error={editError}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
opened={aircraftOpened}
|
||||
onClose={closeAircraft}
|
||||
title="New Aircraft"
|
||||
centered
|
||||
>
|
||||
<AircraftForm
|
||||
onSubmit={addAircraft.mutate}
|
||||
isError={addAircraft.isError}
|
||||
error={addAircraft.error}
|
||||
isPending={addAircraft.isPending}
|
||||
submitButtonLabel="Add"
|
||||
withCancelButton
|
||||
cancelFunc={closeAircraft}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
opened={editOpened}
|
||||
onClose={closeEdit}
|
||||
title={`Edit ${label}`}
|
||||
centered
|
||||
>
|
||||
<Stack>
|
||||
{editForm}
|
||||
<Group justify="flex-end">
|
||||
{updateValue.isPending ? <Loader /> : null}
|
||||
{updateValue.isError ? (
|
||||
<Text c="red">{updateValue.error?.message}</Text>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (editValue.length === 0) {
|
||||
setEditError("Please select an aircraft");
|
||||
} else {
|
||||
updateValue.mutate(editValue);
|
||||
}
|
||||
}}
|
||||
leftSection={<IconPencil />}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
<Card shadow="sm" withBorder h="100%">
|
||||
<Stack gap="xs" align="center" h="100%">
|
||||
<Text c="dimmed">{label}</Text>
|
||||
<Tooltip label={`Edit ${label}`}>
|
||||
<UnstyledButton onClick={openEdit}>
|
||||
<Text
|
||||
size="lg"
|
||||
style={{ textAlign: "center" }}
|
||||
c={content === "" ? "dimmed" : ""}
|
||||
>
|
||||
{content === "" ? <IconX /> : content}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
105
web/app/ui/display/editable/date-log-item.tsx
Normal file
105
web/app/ui/display/editable/date-log-item.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import { usePatchFlight } from "@/util/hooks";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
Loader,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { IconPencil, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc.js";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
export function DateLogItem({
|
||||
label,
|
||||
content,
|
||||
id = "",
|
||||
field = "",
|
||||
}: {
|
||||
label: string;
|
||||
content: Date | string | null;
|
||||
id?: string;
|
||||
field?: string;
|
||||
}) {
|
||||
const [editValue, setEditValue] = useState<Date | null>(
|
||||
content ? new Date(content as string) : null
|
||||
);
|
||||
const [editError, setEditError] = useState("");
|
||||
|
||||
const [editOpened, { open: openEdit, close: closeEdit }] =
|
||||
useDisclosure(false);
|
||||
|
||||
const updateValue = usePatchFlight(id, field, closeEdit);
|
||||
|
||||
content = (content as string).split("T")[0];
|
||||
const editForm = (
|
||||
<DatePickerInput
|
||||
label={label}
|
||||
value={editValue}
|
||||
onChange={setEditValue}
|
||||
error={editError}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
opened={editOpened}
|
||||
onClose={closeEdit}
|
||||
title={`Edit ${label}`}
|
||||
centered
|
||||
>
|
||||
<Stack>
|
||||
{editForm}
|
||||
<Group justify="flex-end">
|
||||
{updateValue.isPending ? <Loader /> : null}
|
||||
{updateValue.isError ? (
|
||||
<Text c="red">{updateValue.error?.message}</Text>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (editValue === null) {
|
||||
setEditError("Please select a date");
|
||||
} else {
|
||||
updateValue.mutate(
|
||||
dayjs(editValue).utc().startOf("day").toISOString()
|
||||
);
|
||||
}
|
||||
}}
|
||||
leftSection={<IconPencil />}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
<Card shadow="sm" withBorder h="100%">
|
||||
<Stack gap="xs" align="center" h="100%">
|
||||
<Text c="dimmed" style={{ textalign: "center" }}>
|
||||
{label}
|
||||
</Text>
|
||||
<Tooltip label={`Edit ${label}`}>
|
||||
<UnstyledButton onClick={openEdit}>
|
||||
<Text
|
||||
size="lg"
|
||||
style={{ textAlign: "center" }}
|
||||
c={content === "" ? "dimmed" : ""}
|
||||
>
|
||||
{content === "" ? <IconX /> : content}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
99
web/app/ui/display/editable/hour-log-item.tsx
Normal file
99
web/app/ui/display/editable/hour-log-item.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
Loader,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { IconPencil, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import { ZeroHourInput } from "@/ui/input/hour-input";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { usePatchFlight } from "@/util/hooks";
|
||||
|
||||
export function HourLogItem({
|
||||
label,
|
||||
content,
|
||||
id = "",
|
||||
field = "",
|
||||
}: {
|
||||
label: string;
|
||||
content: number | string | null;
|
||||
id?: string;
|
||||
field?: string;
|
||||
}) {
|
||||
content = Number(content);
|
||||
|
||||
const [editValue, setEditValue] = useState<number | null>(content);
|
||||
|
||||
const [editError, setEditError] = useState("");
|
||||
|
||||
const [editOpened, { open: openEdit, close: closeEdit }] =
|
||||
useDisclosure(false);
|
||||
|
||||
const updateValue = usePatchFlight(id, field, closeEdit);
|
||||
|
||||
const editForm = (
|
||||
<ZeroHourInput
|
||||
label=""
|
||||
value={editValue}
|
||||
setValue={setEditValue}
|
||||
error={editError}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
opened={editOpened}
|
||||
onClose={closeEdit}
|
||||
title={`Edit ${label}`}
|
||||
centered
|
||||
>
|
||||
<Stack>
|
||||
{editForm}
|
||||
<Group justify="flex-end">
|
||||
{updateValue.isPending ? <Loader /> : null}
|
||||
{updateValue.isError ? (
|
||||
<Text c="red">{updateValue.error?.message}</Text>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (editValue === null || editValue < 0) {
|
||||
setEditError("Please enter a valid hour number");
|
||||
} else {
|
||||
updateValue.mutate(editValue);
|
||||
}
|
||||
}}
|
||||
leftSection={<IconPencil />}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
<Card shadow="sm" withBorder h="100%">
|
||||
<Stack gap="xs" align="center" h="100%">
|
||||
<Text c="dimmed" style={{ textalign: "center" }}>
|
||||
{label}
|
||||
</Text>
|
||||
<Tooltip label={`Edit ${label}`}>
|
||||
<UnstyledButton onClick={openEdit}>
|
||||
<Text
|
||||
size="lg"
|
||||
style={{ textAlign: "center" }}
|
||||
c={content === null ? "dimmed" : ""}
|
||||
>
|
||||
{content === null ? <IconX /> : content}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
99
web/app/ui/display/editable/int-log-item.tsx
Normal file
99
web/app/ui/display/editable/int-log-item.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
Loader,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { IconPencil, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { usePatchFlight } from "@/util/hooks";
|
||||
import { ZeroIntInput } from "@/ui/input/int-input";
|
||||
|
||||
export function IntLogItem({
|
||||
label,
|
||||
content,
|
||||
id = "",
|
||||
field = "",
|
||||
}: {
|
||||
label: string;
|
||||
content: number | string | null;
|
||||
id?: string;
|
||||
field?: string;
|
||||
}) {
|
||||
content = Number(content);
|
||||
|
||||
const [editValue, setEditValue] = useState<number>(content);
|
||||
|
||||
const [editError, setEditError] = useState("");
|
||||
|
||||
const [editOpened, { open: openEdit, close: closeEdit }] =
|
||||
useDisclosure(false);
|
||||
|
||||
const updateValue = usePatchFlight(id, field, closeEdit);
|
||||
|
||||
const editForm = (
|
||||
<ZeroIntInput
|
||||
label=""
|
||||
value={editValue}
|
||||
setValue={setEditValue}
|
||||
error={editError}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
opened={editOpened}
|
||||
onClose={closeEdit}
|
||||
title={`Edit ${label}`}
|
||||
centered
|
||||
>
|
||||
<Stack>
|
||||
{editForm}
|
||||
<Group justify="flex-end">
|
||||
{updateValue.isPending ? <Loader /> : null}
|
||||
{updateValue.isError ? (
|
||||
<Text c="red">{updateValue.error?.message}</Text>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (editValue === null || editValue < 0) {
|
||||
setEditError("Please enter a valid number");
|
||||
} else {
|
||||
updateValue.mutate(editValue);
|
||||
}
|
||||
}}
|
||||
leftSection={<IconPencil />}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
<Card shadow="sm" withBorder h="100%">
|
||||
<Stack gap="xs" align="center" h="100%">
|
||||
<Text c="dimmed" style={{ textalign: "center" }}>
|
||||
{label}
|
||||
</Text>
|
||||
<Tooltip label={`Edit ${label}`}>
|
||||
<UnstyledButton onClick={openEdit}>
|
||||
<Text
|
||||
size="lg"
|
||||
style={{ textAlign: "center" }}
|
||||
c={content === null ? "dimmed" : ""}
|
||||
>
|
||||
{content === null ? <IconX /> : content}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
115
web/app/ui/display/editable/list-log-item.tsx
Normal file
115
web/app/ui/display/editable/list-log-item.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
Loader,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { randomId, useDisclosure } from "@mantine/hooks";
|
||||
import { IconPencil, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import ListInput from "@/ui/input/list-input";
|
||||
import { usePatchFlight } from "@/util/hooks";
|
||||
|
||||
export function LogItem({
|
||||
label,
|
||||
content,
|
||||
}: {
|
||||
label: string;
|
||||
content: string | null;
|
||||
}) {
|
||||
if (content === null) content = "";
|
||||
|
||||
return (
|
||||
<Group justify="space-between" px="sm">
|
||||
<Text>{label}</Text>
|
||||
<Text>{content}</Text>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
export function ListLogItem({
|
||||
label,
|
||||
content,
|
||||
listColor = "",
|
||||
id = "",
|
||||
field = "",
|
||||
}: {
|
||||
label: string;
|
||||
content: string | string[] | null;
|
||||
listColor?: string;
|
||||
id?: string;
|
||||
field?: string;
|
||||
}) {
|
||||
if (content === null) content = [];
|
||||
if (content instanceof String) content = [content as string];
|
||||
|
||||
const [editValue, setEditValue] = useState<string[]>(content as string[]);
|
||||
|
||||
const [editOpened, { open: openEdit, close: closeEdit }] =
|
||||
useDisclosure(false);
|
||||
|
||||
const updateValue = usePatchFlight(id, field, closeEdit);
|
||||
|
||||
const editForm = (
|
||||
<ListInput label={label} value={editValue} setValue={setEditValue} />
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
opened={editOpened}
|
||||
onClose={closeEdit}
|
||||
title={`Edit ${label}`}
|
||||
centered
|
||||
>
|
||||
<Stack>
|
||||
{editForm}
|
||||
<Group justify="flex-end">
|
||||
{updateValue.isPending ? <Loader /> : null}
|
||||
{updateValue.isError ? (
|
||||
<Text c="red">{updateValue.error?.message}</Text>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
updateValue.mutate(editValue);
|
||||
}}
|
||||
leftSection={<IconPencil />}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
<Card shadow="sm" withBorder h="100%">
|
||||
<Stack gap="xs" align="center" h="100%">
|
||||
<Text c="dimmed" style={{ textalign: "center" }}>
|
||||
{label}
|
||||
</Text>
|
||||
<Tooltip label={`Edit ${label}`}>
|
||||
<UnstyledButton onClick={openEdit}>
|
||||
{(content as string[]).length > 0 ? (
|
||||
<Text size="lg">
|
||||
{(content as string[]).map((item) => (
|
||||
<Badge key={randomId()} size="lg" mx="xs" color={listColor}>
|
||||
{item}
|
||||
</Badge>
|
||||
))}
|
||||
</Text>
|
||||
) : (
|
||||
<Text size="lg" style={{ textAlign: "center" }} c="dimmed">
|
||||
<IconX />
|
||||
</Text>
|
||||
)}
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
88
web/app/ui/display/editable/text-log-item.tsx
Normal file
88
web/app/ui/display/editable/text-log-item.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
Loader,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
Textarea,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { IconPencil, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { usePatchFlight } from "@/util/hooks";
|
||||
|
||||
export function TextLogItem({
|
||||
label,
|
||||
content,
|
||||
id = "",
|
||||
field = "",
|
||||
}: {
|
||||
label: string;
|
||||
content: string | null;
|
||||
id?: string;
|
||||
field?: string;
|
||||
}) {
|
||||
const [editValue, setEditValue] = useState<string>(content ?? "");
|
||||
|
||||
const [editOpened, { open: openEdit, close: closeEdit }] =
|
||||
useDisclosure(false);
|
||||
|
||||
const updateValue = usePatchFlight(id, field, closeEdit);
|
||||
|
||||
const editForm = (
|
||||
<Textarea
|
||||
label=""
|
||||
value={editValue}
|
||||
onChange={(event) => setEditValue(event.currentTarget.value)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
opened={editOpened}
|
||||
onClose={closeEdit}
|
||||
title={`Edit ${label}`}
|
||||
centered
|
||||
>
|
||||
<Stack>
|
||||
{editForm}
|
||||
<Group justify="flex-end">
|
||||
{updateValue.isPending ? <Loader /> : null}
|
||||
{updateValue.isError ? (
|
||||
<Text c="red">{updateValue.error?.message}</Text>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => updateValue.mutate(editValue)}
|
||||
leftSection={<IconPencil />}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
<Card shadow="sm" withBorder h="100%">
|
||||
<Stack gap="xs" align="center" h="100%">
|
||||
<Text c="dimmed" style={{ textalign: "center" }}>
|
||||
{label}
|
||||
</Text>
|
||||
<Tooltip label={`Edit ${label}`}>
|
||||
<UnstyledButton onClick={openEdit}>
|
||||
<Text
|
||||
size="lg"
|
||||
style={{ textAlign: "center" }}
|
||||
c={content === null ? "dimmed" : ""}
|
||||
>
|
||||
{content === null ? <IconX /> : content}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
120
web/app/ui/display/editable/time-log-item.tsx
Normal file
120
web/app/ui/display/editable/time-log-item.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
Loader,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { IconPencil, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import TimeInput from "@/ui/input/time-input";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import dayjs from "dayjs";
|
||||
import { usePatchFlight } from "@/util/hooks";
|
||||
import utc from "dayjs/plugin/utc.js";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
export function TimeLogItem({
|
||||
label,
|
||||
content,
|
||||
date,
|
||||
id = "",
|
||||
field = "",
|
||||
}: {
|
||||
label: string;
|
||||
content: string | null;
|
||||
date: dayjs.Dayjs | string;
|
||||
id?: string;
|
||||
field?: string;
|
||||
}) {
|
||||
if (date instanceof String) date = dayjs(date);
|
||||
|
||||
const time = (content as string).split("T")[1].split(":");
|
||||
const [editValue, setEditValue] = useState<number | string | undefined>(
|
||||
Number(`${time[0]}${time[1]}`)
|
||||
);
|
||||
|
||||
const [editError, setEditError] = useState("");
|
||||
|
||||
const [editOpened, { open: openEdit, close: closeEdit }] =
|
||||
useDisclosure(false);
|
||||
|
||||
const updateValue = usePatchFlight(id, field, closeEdit);
|
||||
|
||||
content = `${time[0]}:${time[1]}`;
|
||||
const editForm = (
|
||||
<TimeInput
|
||||
label={label}
|
||||
value={editValue}
|
||||
setValue={setEditValue}
|
||||
error={editError}
|
||||
allowLeadingZeros
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
opened={editOpened}
|
||||
onClose={closeEdit}
|
||||
title={`Edit ${label}`}
|
||||
centered
|
||||
>
|
||||
<Stack>
|
||||
{editForm}
|
||||
<Group justify="flex-end">
|
||||
{updateValue.isPending ? <Loader /> : null}
|
||||
{updateValue.isError ? (
|
||||
<Text c="red">{updateValue.error?.message}</Text>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (Number(editValue) > 2359)
|
||||
setEditError("Time must be between 0000 and 2359");
|
||||
else if (Number(editValue) % 100 > 59)
|
||||
setEditError("Minutes must not exceed 59");
|
||||
else {
|
||||
updateValue.mutate(
|
||||
dayjs(date)
|
||||
.utc()
|
||||
.hour(Math.floor((Number(editValue) ?? 0) / 100))
|
||||
.minute(Math.floor((Number(editValue) ?? 0) % 100))
|
||||
.second(0)
|
||||
.millisecond(0)
|
||||
.toISOString()
|
||||
);
|
||||
}
|
||||
}}
|
||||
leftSection={<IconPencil />}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
<Card shadow="sm" withBorder h="100%">
|
||||
<Stack gap="xs" align="center" h="100%">
|
||||
<Text c="dimmed" style={{ textalign: "center" }}>
|
||||
{label}
|
||||
</Text>
|
||||
<Tooltip label={`Edit ${label}`}>
|
||||
<UnstyledButton onClick={openEdit}>
|
||||
<Text
|
||||
size="lg"
|
||||
style={{ textAlign: "center" }}
|
||||
c={content === null ? "dimmed" : ""}
|
||||
>
|
||||
{content === null ? <IconX /> : content}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user