import {
ButtonContent,
DestructiveButton,
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
Icon,
Input,
Toaster,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
import React, { useEffect, useState } from "react";
import { toast } from "sonner";
import wretch from "wretch";
import { useRouter } from "next/router";
import { useGetVesselFromIMO } from "@/hooks";
const formSchema = z.object({
imo: z.string().max(7),
cargo_type: z.string().optional(),
cargo_sub_type: z.string().optional(),
mmsi: z.string().max(9).optional(),
vessel_name: z.string().optional(),
year_of_build: z.string().optional(),
flag: z.string().optional(),
grt: z.string().optional(),
dwt: z.string().optional(),
overall_length: z.string().optional(),
beam: z.string().optional(),
maximum_draft: z.string().optional(),
});
type FormFields = z.infer<typeof formSchema>;
const fieldsNameHelper: Record<
keyof FormFields,
{ name: string; description: string }
> = {
imo: {
name: "IMO",
description: "International Maritime Organization identifier",
},
cargo_type: {
name: "Cargo Type",
description: "The type of cargo the vessel carries",
},
cargo_sub_type: {
name: "Cargo Sub Type",
description: "The subtype of cargo the vessel carries",
},
mmsi: { name: "MMSI", description: "Maritime Mobile Service Identity" },
vessel_name: { name: "Vessel Name", description: "The name of the vessel" },
year_of_build: {
name: "Year of Build",
description: "The year the vessel was built",
},
flag: { name: "Flag", description: "The flag country code" },
grt: { name: "GRT", description: "Gross Registered Tonnage" },
dwt: { name: "DWT", description: "Dead Weight Tonnage" },
overall_length: {
name: "Overall Length",
description: "The overall length of the vessel",
},
beam: { name: "Beam", description: "The beam of the vessel" },
maximum_draft: {
name: "Maximum Draft",
description:
"The deepest point of the vessel below the waterline when fully loaded",
},
};
interface VesselInfoFormProps {
mode: "add" | "edit";
initialValues?: FormFields;
onModeChange?: (mode: "add" | "edit") => void;
}
export function VesselInfoForm({
mode,
initialValues,
onModeChange,
}: VesselInfoFormProps) {
const [imoChecked, setImoChecked] = useState(false);
const [isEditMode, setIsEditMode] = useState(mode === "edit");
const [updateError, setUpdateError] = useState(null);
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: initialValues || {},
});
const router = useRouter();
const imoValue = form.watch("imo");
const {
data: vesselData,
isError,
isLoading,
} = useGetVesselFromIMO(imoValue);
useEffect(() => {
if (mode === "edit" && initialValues) {
form.reset(initialValues);
}
}, [initialValues, mode, form]);
useEffect(() => {
if (vesselData && vesselData.length > 0 && mode === "add") {
setIsEditMode(true);
if (onModeChange) onModeChange("edit");
} else {
setIsEditMode(false);
if (onModeChange) onModeChange("add");
}
}, [vesselData, mode, onModeChange]);
async function onCheckIMO() {
if (vesselData && vesselData.length > 0) {
router.push(
`/secure/manager/manageVessel/editRegularVessel?imo=${imoValue}`,
);
} else {
setImoChecked(true);
}
}
async function onSubmit(values: FormFields) {
const updatedVesselInfo = Object.entries(values).reduce(
(acc: Record<string, string>, [key, value]) => {
const vesselInfoKey = fieldsNameHelper[key as keyof FormFields].name;
acc[vesselInfoKey] = (value || "").toString().toLowerCase();
return acc;
},
{},
);
setUpdateError(null);
try {
const apiUrl = isEditMode
? "/api/form/updateVessel"
: "/api/form/insertVessel";
const response = await wretch(apiUrl).post(updatedVesselInfo).res();
if (response.ok) {
toast.success("Success!", {
description: isEditMode
? "Vessel details updated."
: "Vessel details added.",
});
form.reset();
setImoChecked(false);
} else {
throw new Error("Error submitting form");
}
} catch (error) {
setUpdateError(error);
}
}
return (
<div className={"mt-4"}>
<Form {...form}>
<form className="space-y-4">
<FormField
control={form.control}
name="imo"
render={({ field }) => (
<FormItem className={"flex flex-row items-center"}>
<FormLabel className={"w-64 text-lg font-light"}>
{fieldsNameHelper.imo.name}
</FormLabel>
<div className={"mr-2"}>
<TooltipProvider>
<Tooltip>
<TooltipTrigger className="text-black hover:text-black/50 dark:text-white dark:hover:text-white/50">
<Icon name="unknown" style="h-5 w-5" />
</TooltipTrigger>
<TooltipContent>
<p>{fieldsNameHelper.imo.description}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<FormControl className={"w-full"}>
<Input
{...field}
className={"text-md font-light"}
type="text"
value={field.value ?? ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{imoChecked &&
(!vesselData || vesselData.length === 0) &&
(Object.keys(formSchema.shape) as Array<keyof FormFields>).map(
(key) =>
key !== "imo" && (
<FormField
key={key}
control={form.control}
name={key}
render={({ field }) => (
<FormItem className={"flex flex-row items-center"}>
<FormLabel className={"w-64 text-lg font-light"}>
{fieldsNameHelper[key].name}
</FormLabel>
<div className={"mr-2"}>
<TooltipProvider>
<Tooltip>
<TooltipTrigger className="text-black hover:text-black/50 dark:text-white dark:hover:text-white/50">
<Icon name="unknown" style="h-5 w-5" />
</TooltipTrigger>
<TooltipContent>
<p>{fieldsNameHelper[key].description}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<FormControl className={"w-full"}>
<Input
{...field}
className={"text-md font-light"}
type="text"
value={field.value ?? ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
),
)}
<div className="flex flex-row justify-end">
<DestructiveButton
onClick={imoChecked ? form.handleSubmit(onSubmit) : onCheckIMO}
className="mt-4"
>
<ButtonContent>
{isEditMode
? "Save Changes"
: imoChecked
? "Add Vessel"
: "Check IMO"}
</ButtonContent>
</DestructiveButton>
</div>
<Toaster />
</form>
</Form>
</div>
);
}