Untitled

mail@pastecode.io avatar
unknown
plain_text
7 days ago
21 kB
1
Indexable
Never
import React, { useEffect, useState } from "react";
import Breadcrumbs from "../../../stories/components/molecules/Breadcrumbs/Breadcrumbs";
import Text from "../../../stories/components/ions/Text/Text";
import Input from "../../../stories/components/atoms/Input/Input";
import Select from "../../../stories/components/atoms/Select/Select";
import { RadioInput } from "../../../stories/components/atoms/Radio/Radio";
import { FormErrors } from "./interfaces";
import InputImage from "../../../stories/components/organisms/InputFile/InputFile";
import Button from "../../../stories/components/atoms/Button/Button";
import { modelService } from "../../../services/model/model.service";
import { useNavigate, useParams } from "react-router-dom";
import { z } from "zod";
import { AxiosError, AxiosProgressEvent } from "axios";
import "react-toastify/dist/ReactToastify.css";
import { boardService } from "../../../services/board/board.service";
import { IBoardDetails } from "../../../services/board/board.types";
import { IClient, clientService } from "../../../services/client";
import { IModel } from "../../../services/model";
import { UseToast } from "../../../contexts/ToastContext";
import "react-toastify/dist/ReactToastify.css";
import { RadioInputCustom } from "../../../stories/components/atoms/Radio/RadioCustom";
import ProgressBarLine from "../../../stories/components/atoms/ProgressBarLine/ProgressBarLine";

const BoardCreate: React.FC = () => {
  const navigate = useNavigate();
  const [loading, setLoading] = useState<boolean>(false);
  const [uploadProgress, setUploadProgress] = useState<number>(0);

  const { id } = useParams();

  const [sideOptions, setSideOptions] = React.useState<
    { label: string; value: string }[]
  >([
    { label: "Bottom (Lado S)", value: "Bottom" },
    { label: "Top (Lado C)", value: "Top" },
  ]);

  const [isNewRegister] = useState<boolean>(!id);

  const [file, setFile] = useState<File | null>(null);
  const [models, setModels] = useState<IModel[]>([]);
  const [clients, setClients] = useState<IClient[]>([]);
  const [clientIdSelected, setClientIdSelected] = useState<string>("");
  const [formData, setFormData] = useState({
    ClientId: "",
    ModelId: "",
    ModelBoardName: "",
    ProgramName: "",
    QRCode: "",
    Side: "Bottom",
    EstimatedLifetime: 0,
    TotalThermocouples: 0,
    TotalReservedThermocouples: 0,
    Nitrogen: false,
  });

  const [validationErrors, setValidationErrors] = useState({
    file: false,
    ClientId: false,
    ModelId: false,
    ModelBoardName: false,
    ProgramName: false,
    QRCode: false,
    EstimatedLifetime: false,
    TotalThermocouples: false,
    TotalReservedThermocouples: false,
  });

  const [errors, setErrors] = useState<FormErrors>({});
  const [boardData, setBoardData] = useState<IBoardDetails>();

  const labelStyles: string = "mb-1";

  const boardCreationSchema = z.object({
    ClientId: z.string().min(1, "Seleção obrigatória"),
    ModelId: z.string().min(1, "Seleção obrigatória"),
    ModelBoardName: z.string().min(1, "Seleção obrigatória"),
    QRCode: z.string().min(1, "Campo obrigatório"),
    ProgramName: z.string().min(1, "Campo obrigatório"),
    Side: z.enum(["Bottom", "Top"]),
    EstimatedLifetime: z.number().min(1, "Campo obrigatório"),
    TotalThermocouples: z.number().min(1, "Campo obrigatório"),
    TotalReservedThermocouples: z.number().min(1, "Campo obrigatório"),
    Nitrogen: z.boolean(),
  });

  const { showToast } = UseToast();

  useEffect(() => {
    if (id) {
      const fetchBoardData = async () => {
        const { data } = await boardService.getBoardDetails(id);

        const updatedData = {
          ...data,
          qrCode: data.qrCode.length > 0 && isNewRegister ? data.qrCode : "",
        };

        setBoardData(updatedData);

        setFormData({
          ClientId: data.clientId,
          EstimatedLifetime: data.lifeTime,
          ProgramName: data.programName,
          QRCode: isNewRegister ? data.qrCode : "",
          Side: data.side,
          Nitrogen: data.nitrogen,
          ModelId: data.modelId,
          ModelBoardName: data.modelBoardName,
          TotalReservedThermocouples: data.totalReservedThermocouples,
          TotalThermocouples: data.totalThermocouples,
        });
        setSideOptions([
          {
            label: `${data.side === "Bottom" ? "Bottom (Lado S)" : "Top (Lado S)"}`,
            value: data.side,
          },
        ]);
      };

      fetchBoardData();
    }
  }, [id, isNewRegister]);

  useEffect(() => {
    const fetchClients = async () => {
      const response = await clientService.listClients();
      setClients(response.data);
    };

    if (clientIdSelected) {
      clearModelsList();
      const fetchModelsByClientId = async () => {
        const response =
          await modelService.listModelsByClient(clientIdSelected);
        setModels(response.data);
      };
      fetchModelsByClientId();
    }
    fetchClients();
  }, [clientIdSelected]);

  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setLoading(true);
    try {
      const validatedData = boardCreationSchema.parse(formData);

      if (file || id) {
        const formData = new FormData();

        if (file) {
          formData.append("Image", file);
        }

        formData.append("ClientId", validatedData.ClientId);
        formData.append("UserId", "1");
        formData.append("ModelId", validatedData.ModelId);
        formData.append("ModelBoardName", validatedData.ModelBoardName);
        formData.append("ProgramName", validatedData.ProgramName);
        formData.append("QRCode", validatedData.QRCode);
        formData.append("Side", validatedData.Side);
        formData.append("Nitrogen", validatedData.Nitrogen.toString());
        formData.append(
          "EstimatedLifetime",
          validatedData.EstimatedLifetime.toString()
        );
        formData.append(
          "TotalThermocouples",
          validatedData.TotalThermocouples.toString()
        );
        formData.append(
          "TotalReservedThermocouples",
          validatedData.TotalReservedThermocouples.toString()
        );

        if (id) {
          await boardService.resubscribeBoard(
            { data: formData, id },
            (progressEvent: AxiosProgressEvent) => {
              const percentCompleted = Math.round(
                (progressEvent.loaded * 100) / progressEvent.total!
              );
              setUploadProgress(percentCompleted);
            }
          );
        } else {
          await boardService.subscribeBoard(
            formData,
            (progressEvent: AxiosProgressEvent) => {
              const percentCompleted = Math.round(
                (progressEvent.loaded * 100) / progressEvent.total!
              );
              setUploadProgress(percentCompleted);
            }
          );
        }

        navigate("/boards", {
          state: showToast(
            `Placa instrumentalizada ${isNewRegister ? "cadastrada" : "recadastrada"} com sucesso!`,
            {
              type: "success",
            }
          ),
        });
      } else {
        setLoading(false);
        showToast(`Por favor, anexe uma imagem da placa antes de submeter!`, {
          type: "warning",
        });
      }
    } catch (error) {
      setLoading(false);
      if (error instanceof z.ZodError) {
        const newErrors: FormErrors = {};
        error.errors.forEach((e) => {
          if (e.path[0]) {
            newErrors[e.path[0] as keyof FormErrors] = e.message;
          }
        });
        setErrors(newErrors);
      }

      if (error instanceof AxiosError) {
        showToast(error.response?.data.message, { type: "error" });
      }
    } finally {
      setLoading(false);
    }
  };

  const handleChange = ({
    field,
    value,
  }: {
    field: string;
    value: string | number | boolean;
  }) => {
    if (field === "ClientId" && clientIdSelected !== value) {
      setClientIdSelected(String(value));
    }

    setFormData((prev) => ({ ...prev, [field]: value }));
  };

  const clearModelsList = async () => {
    setModels([]);
    setFormData((prev) => ({ ...prev, ModelId: "" }));
  };

  const clearFileInParent = () => {
    setFile(null);
  };

  const modelSelectIsDisabled = () => {
    if (boardData?.boardId || !clientIdSelected) return true;
    return false;
  };

  return (
    <>
      {loading && uploadProgress > 0 && (
        <ProgressBarLine progress={uploadProgress} />
      )}
      <div className="container">
        <header className=" items-center">
          <Breadcrumbs
            title={
              isNewRegister
                ? "Cadastrar Placa Instrumentalizada"
                : "Recadastrar Placa Instrumentalizada"
            }
            path={[
              {
                link: "#",
                title: "Placas",
              },
              {
                link: "/boards",
                title: "Lista de Placas Instrumentalizadas",
              },
              {
                link: "/board-create",
                title: id ? "Recadastrar Placa Instrumentalizadas" : "Cadastrar Placa Instrumentalizadas",
              },
            ]}
          />

          <Text type="span" fontSize="16px" color="#00000073">
            Preencha as informações da Placa Instrumentalizada e faça o upload
            da foto da placa. Todos os campos são obrigatórios
          </Text>
        </header>

        {loading && (
          <div className="w-full h-screen z-20 bg-white absolute bg-opacity-50"></div>
        )}
        <form className="justify-between mt-5 " onSubmit={handleSubmit}>
          <div id="line1" className="flex ">
            <section
              id="column1"
              className="w-1/2 mr-10 h-full  justify-between"
            >
              <div>
                <Select
                  controlLabel="Cliente"
                  options={clients.map((item) => ({
                    label: item.clientName,
                    value: String(item.id),
                  }))}
                  onChange={(value) => {
                    handleChange({ field: "ClientId", value: String(value) });
                    setValidationErrors((prev) => ({
                      ...prev,
                      ClientId: !!value,
                    }));
                  }}
                  classNameLabel={labelStyles}
                  disabled={boardData?.boardId ? true : false}
                  initialValue={isNewRegister ? "" : String(boardData?.client)}
                  testId="clientSelect"
                  error={
                    errors.ClientId && !validationErrors.ClientId
                      ? "Seleção obrigatória"
                      : ""
                  }
                />
                <div className="flex justify-between mt-4">
                  <article className="w-[25%]">
                    <Select
                      controlLabel="Código do Modelo"
                      options={models.map((item) => ({
                        label: item.modelName,
                        value: String(item.id),
                      }))}
                      onChange={(value) => {
                        handleChange({ field: "ModelId", value: String(value) });
                        setValidationErrors((prev) => ({
                          ...prev,
                          ModelId: !!value,
                        }));
                      }}
                      classNameLabel={labelStyles}
                      disabled={modelSelectIsDisabled()}
                      initialValue={
                        isNewRegister ? "" : String(boardData?.modelCode)
                      }
                      testId="modelSelect"
                      error={
                        errors.ModelId && !validationErrors.ModelId
                          ? "Seleção obrigatória"
                          : ""
                      }
                    />
                  </article>
                  <article className="w-2/3 mt-1">
                    <Input
                      label="Nome do Modelo"
                      placeholder="Insira o Nome da Placa"
                      type="text"
                      classNameLabel={labelStyles}
                      onChange={({ target: { value } }) => {
                        handleChange({ field: "ModelBoardName", value });
                        setValidationErrors((prev) => ({
                          ...prev,
                          QRCode: !!value,
                        }));
                      }}
                      initialValue={isNewRegister ? "" : String(boardData?.modelBoardName)}
                      testId="NomedoProgramaInput"
                      error={
                        errors.QRCode && !validationErrors.QRCode
                          ? "Seleção obrigatória"
                          : ""
                      }
                    />
                  </article>
                </div>
                <Input
                  label="Nome do Programa"
                  placeholder="Insira o nome do Programa"
                  type="text"
                  classNameLabel={labelStyles}
                  onChange={({ target: { value } }) => {
                    handleChange({ field: "ProgramName", value });
                    setValidationErrors((prev) => ({
                      ...prev,
                      QRCode: !!value,
                    }));
                  }}
                  initialValue={isNewRegister ? "" : String(boardData?.programName)}
                  testId="NomedoProgramaInput"
                  error={
                    errors.QRCode && !validationErrors.QRCode
                      ? "Seleção obrigatória"
                      : ""
                  }
                />

                <Input
                  label="QR Code de identificação da Placa"
                  placeholder="Leia aqui o QR Code ou digite manualmente"
                  type="text"
                  classNameLabel={labelStyles}
                  onChange={({ target: { value } }) => {
                    handleChange({ field: "QRCode", value });
                    setValidationErrors((prev) => ({
                      ...prev,
                      QRCode: !!value,
                    }));
                  }}
                  initialValue={isNewRegister ? "" : String(boardData?.qrCode)}
                  testId="QrCodeInput"
                  error={
                    errors.QRCode && !validationErrors.QRCode
                      ? "Seleção obrigatória"
                      : ""
                  }
                />

                <div className="flex justify-between">
                  <article className="w-[45%]">
                    <RadioInput
                      label="Lado"
                      onChange={(value) =>
                        handleChange({ field: "Side", value })
                      }
                      options={sideOptions}
                      selectedValue={formData.Side}
                      classNameLabel={labelStyles}
                      testId="sideInputRadio"
                    />
                  </article>

                  <article className="w-2/3">
                    <Input
                      label="Vida Útil estimada (Ciclos)"
                      placeholder="Ex: 100"
                      type="number"
                      classNameLabel={labelStyles}
                      onChange={({ target: { value } }) => {
                        handleChange({
                          field: "EstimatedLifetime",
                          value: Number(value),
                        });
                        setValidationErrors((prev) => ({
                          ...prev,
                          EstimatedLifetime: !!value,
                        }));
                      }}
                      initialValue={
                        isNewRegister ? "" : String(boardData?.lifeTime)
                      }
                      testId="LifeEstimatedInput"
                      error={
                        errors.EstimatedLifetime &&
                        !validationErrors.EstimatedLifetime
                          ? "Seleção obrigatória"
                          : ""
                      }
                    />
                  </article>
                </div>

                <div className="flex justify-between">
                  <div className="w-[45%]">
                    <Input
                      label="Qtd. Total de Termopares"
                      placeholder="Insira um número"
                      type="number"
                      classNameLabel={labelStyles}
                      onChange={({ target: { value } }) => {
                        handleChange({
                          field: "TotalThermocouples",
                          value: Number(value),
                        });

                        setValidationErrors((prev) => ({
                          ...prev,
                          TotalThermocouples: !!value,
                        }));
                      }}
                      initialValue={
                        isNewRegister
                          ? ""
                          : String(boardData?.totalThermocouples)
                      }
                      testId="QtdThermocouplesInput"
                      error={
                        errors.TotalThermocouples &&
                        !validationErrors.TotalThermocouples
                          ? "Seleção obrigatória"
                          : ""
                      }
                    />
                  </div>

                  <div className="w-1/2">
                    <Input
                      label="Qtd. de Termopares Reserva"
                      placeholder="Insira um número"
                      type="number"
                      classNameLabel={labelStyles}
                      onChange={({ target: { value } }) => {
                        handleChange({
                          field: "TotalReservedThermocouples",
                          value: Number(value),
                        });
                        setValidationErrors((prev) => ({
                          ...prev,
                          TotalReservedThermocouples: !!value,
                        }));
                      }}
                      initialValue={
                        isNewRegister
                          ? ""
                          : String(boardData?.totalReservedThermocouples)
                      }
                      testId="QtdReservedThermocouplesInput"
                      error={
                        errors.TotalReservedThermocouples &&
                        !validationErrors.TotalReservedThermocouples
                          ? "Seleção obrigatória"
                          : ""
                      }
                    />
                  </div>
                </div>

                <div className="flex">
                  <RadioInputCustom
                    label="Nitrogênio"
                    onChange={(value) =>
                      handleChange({
                        field: "Nitrogen",
                        value: value === String(true) ? true : false,
                      })
                    }
                    options={[
                      { label: "Não utiliza", value: String(false) },
                      { label: "Utiliza", value: String(true) },
                    ]}
                    selectedValue={String(formData.Nitrogen)}
                    classNameLabel={labelStyles}
                    testId="nitrogenInputRadio"
                    id={id}
                  />
                </div>
              </div>
            </section>

            <section id="column2" className="w-1/2">
              <InputImage
                onUpload={(file) => {
                  setFile(file);
                }}
                onFileCleared={clearFileInParent}
                labelText="Foto da Placa Instrumentalizada"
                initialFile={isNewRegister ? null : boardData?.image}
                testId="inputImage"
              />
            </section>
          </div>

          <footer className="mt-10 flex justify-between relative z-20">
            <Button
              label="Cancelar"
              className="py-2 px-4"
              type="button"
              onClick={() => navigate("/boards")}
              testId="cancelButton"
              disabled={loading}
            />
            <Button
              label={isNewRegister ? "Cadastrar" : "Recadastrar"}
              className="py-2 px-4"
              type="submit"
              primary
              testId="confirmButton"
              loading={loading}
              disabled={loading}
            />
          </footer>
        </form>
      </div>
    </>
  );
};

export default BoardCreate;
Leave a Comment