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> ); }