vesselInfoForm.tsx (17.06.2024)

PHOTO EMBED

Sun Jun 16 2024 23:08:07 GMT+0000 (Coordinated Universal Time)

Saved by @rafal_rydz

@ -0,0 +1,261 @@
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>
  );
}
content_copyCOPY