Preview:
@ -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>
  );
};
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