@ -0,0 +1,396 @@ import { ButtonContent, DestructiveButton, Form, FormControl, FormField, FormItem, FormLabel, FormMessage, Icon, If, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, 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"; export interface VesselInfo { [key: string]: string | undefined; } interface RegularVesselFormProps { vesselInfo: VesselInfo; vesselId?: string; imo?: string; } export const formSchema = z.object({ imo: z.string().optional(), cargoType: z.string().optional(), cargoSubType: z.string().optional(), mmsi: z.string().optional(), vesselName: z.string().optional(), yearOfBuild: z.string().optional(), flag: z.string().optional(), dwt: z.string().optional(), maximumDraft: z.string().optional(), geared: z.string().optional(), gearDetails: z.string().optional(), }); type FieldsNameHelper = { [key: string]: { name: string; description: string }; }; export const fieldsNameHelper: FieldsNameHelper = { imo: { name: "IMO", description: "International Maritime Organization identifier", }, cargoType: { name: "Cargo Type", description: "The type of cargo the vessel carries", }, cargoSubType: { name: "Cargo Sub Type", description: "The subtype of cargo the vessel carries", }, mmsi: { name: "MMSI", description: "Maritime Mobile Service Identity" }, vesselName: { name: "Vessel Name", description: "The name of the vessel" }, yearOfBuild: { name: "Year of Build", description: "The year the vessel was built", }, flag: { name: "Flag", description: "The flag country code" }, dwt: { name: "DWT", description: "Dead Weight Tonnage" }, maximumDraft: { name: "Maximum Draft", description: "The deepest point of the vessel below the waterline when fully loaded", }, geared: { name: "Geared", description: "Indicates if the vessel is geared (yes or no)", }, gearDetails: { name: "Gear Details", description: "Details about the vessel's gear", }, }; export const numberFields: string[] = [ "dwt", "yearOfBuild", "maximumDraft", "mmsi", "imo", ]; export const uppercaseFields: string[] = ["imo", "mmsi"]; export function toTitleCase(str: string): string { if (uppercaseFields.includes(str)) { return str.toUpperCase().replace("_", " "); } else { str = str.replace("_", " "); return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); } } export const RegularVesselInfoForm = ({ vesselInfo, vesselId, imo, }: RegularVesselFormProps) => { const disabledFields: string[] = ["vesselId", "imo"]; const [selectedType, setSelectedType] = useState<string | null>( vesselInfo?.cargoType ?? null, ); const defaultFieldsValues = Object.fromEntries( Object.keys(fieldsNameHelper).map((key) => [ key, vesselInfo ? vesselInfo[fieldsNameHelper[key].name as string] ?? "" : "", ]), ); const form = useForm<z.infer<typeof formSchema>>({ resolver: zodResolver(formSchema), defaultValues: defaultFieldsValues, }); useEffect(() => { if (vesselInfo) { form.reset(defaultFieldsValues); } }, [vesselInfo]); async function onSubmit(values: z.infer<typeof formSchema>) { const updatedVesselInfo: VesselInfo = Object.entries(values).reduce( (acc: VesselInfo, [key, value]) => { const vesselInfoKey: string = fieldsNameHelper[key].name; acc[vesselInfoKey] = (value || "").toString().toLowerCase(); return acc; }, {}, ); try { const response = await wretch("/api/form/regularVessel") .post(updatedVesselInfo) .res(); if (response.ok) { await wretch(`/api/form/regularVessel?vessel_id=${vesselId}&imo=${imo}`) .get() .json(); toast.success("Success!", { description: "Vessel details updated.", }); } else { toast.error("Error submitting form"); } } catch (error) { toast.error("Something went wrong", { description: (error as any)?.message, }); } } return ( <div className="mt-6 flex w-full flex-col gap-2 p-2 sm:w-1/2"> <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> {Object.keys(formSchema.shape).map((key, index) => ( <React.Fragment key={key}> <If condition={Object.keys(vesselInfo || {}).includes( fieldsNameHelper[key].name, )} > <If condition={ key !== "type" && key !== "subType" && key !== "buildYear" } > <FormField key={key} control={form.control} name={key as any} render={({ field }) => ( <FormItem className={"flex flex-row items-center"}> <FormLabel className={"w-64 text-lg font-light"}> {toTitleCase(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={ numberFields.includes(key) ? "number" : "text" } value={field.value ?? ""} disabled={disabledFields.includes(key)} /> </FormControl> <FormMessage /> </FormItem> )} /> </If> <If condition={key === "type"}> <FormField control={form.control} name="type" render={({ field }) => ( <FormItem className={"flex flex-row items-center"}> <FormLabel className={"w-64 text-lg font-light"}> Type </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> <Select onValueChange={(value: any) => { field.onChange(value); setSelectedType(value); }} value={field.value ?? ""} > <FormControl className={"text-md w-full font-light"}> <SelectTrigger> <SelectValue placeholder="Select a type" /> </SelectTrigger> </FormControl> <SelectContent> <SelectItem value={"cargo vessel"}> Cargo Vessel </SelectItem> <SelectItem value={"tanker"}>Tanker</SelectItem> <SelectItem value={"passenger vessel"}> Passenger Vessel </SelectItem> <SelectItem value={"other"}>Other</SelectItem> </SelectContent> </Select> </FormItem> )} /> </If> <If condition={key === "subType"}> <FormField control={form.control} name="subType" render={({ field }) => ( <FormItem className={"flex flex-row items-center"}> <FormLabel className={"w-64 text-lg font-light"}> Sub type </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> <Select onValueChange={field.onChange} value={field.value ?? ""} > <FormControl className={"text-md w-full font-light"}> <SelectTrigger> <SelectValue placeholder="Select a sub type" /> </SelectTrigger> </FormControl> {selectedType && selectedType === "cargo vessel" && ( <SelectContent> <SelectItem value={"bulk carrier"}> Bulk Carrier </SelectItem> <SelectItem value={"container ship"}> Container Ship </SelectItem> <SelectItem value={"general cargo ship"}> General Cargo Ship </SelectItem> </SelectContent> )} {selectedType && selectedType === "tanker" && ( <SelectContent> <SelectItem value={"oil tanker"}> Oil Tanker </SelectItem> <SelectItem value={"chemical tanker"}> Chemical Tanker </SelectItem> <SelectItem value={"gas carrier"}> Gas Carrier </SelectItem> </SelectContent> )} {selectedType && selectedType === "passenger vessel" && ( <SelectContent> <SelectItem value={"cruise ship"}> Cruise Ship </SelectItem> <SelectItem value={"ferry"}>Ferry</SelectItem> </SelectContent> )} </Select> </FormItem> )} /> </If> <If condition={key === "buildYear"}> <FormField control={form.control} name="buildYear" render={({ field }) => ( <FormItem className={"flex flex-row items-center"}> <FormLabel className={"w-64 text-lg font-light"}> Build year </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="number" min={1900} max={2100} /> </FormControl> <FormMessage /> </FormItem> )} /> </If> </If> </React.Fragment> ))} <If condition={vesselInfo && Object.keys(formSchema.shape).length > 0} > <div className="flex flex-row justify-end"> <DestructiveButton type="submit" className="mt-4"> <ButtonContent>Update</ButtonContent> </DestructiveButton> </div> </If> <Toaster /> </form> </Form> </div> ); };
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter