Untitled
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