import { useContext, useEffect } from "react"; import { FormEditionContext } from "./FormContext"; import uuid from "uuid/v4"; import { testQuestionTextSchema, testDescriptionTextSchema, selectOptionsTesting, testSubformSchema, selectOptionTextTesting, testTextValidation, } from "../components/fieldsDisplayForm/utils/schemas"; import { pushTitle, pushQuestion, pushSelect, pushRadio, pushCheckbox, pushSubform, } from "../components/fieldsDisplayForm/utils/FormComposition"; import api from "../api"; import createBackendForm from "../components/fieldsDisplayForm/utils/BackendTranslation"; import { createFrontendForm } from "../components/fieldsDisplayForm/utils/FrontEndTranslation"; const useForm = () => { const { formState, idValue } = useContext(FormEditionContext); const [form, setForm] = formState; const routeId = idValue; /** Function that adds the title object into the form array. * Its parameters are used on the translation of the backend data. * @param title - the title; * @param description - the description of the form; * @param id - optional parameter, it is used when editing a form. */ function addToFormTitle(title, description, id) { setForm(pushTitle(form, title, description, id)); } /** Function that adds a 'question' object into the form array. * Its parameters are used on the translation of the backend data. * @param question - the question; * @param description - the description of the question; * @param validation - optional validation the question may have. */ function addToFormQuestion(question, description, validation) { pushQuestion(form, question, description, validation); setForm([...form]); } /** Function that adds a 'select' object into the form array. * Its parameters are used on the translation of the backend data. * @param question - the question; * @param description - the description of the question; * @param options - the options array; * @param validation - optional validation the question may have. */ function addToFormSelect(question, description, options, validation) { pushSelect(form, question, description, options, validation); setForm([...form]); } /** Function that adds a 'radio' object into the form array. * Its parameters are used on the translation of the backend data. * @param question - the question; * @param description - the description of the question; * @param options - the options array; * @param validation - optional validation the question may have. */ function addToFormRadio(question, description, options, validation) { pushRadio(form, question, description, options, validation); setForm([...form]); } /** Function that adds a 'checkbox' object into the form array. * Its parameters are used on the translation of the backend data. * @param question - the question; * @param description - the description of the question; * @param options - the options array; * @param validation - optional validation the question may have. */ function addToFormCheckbox(question, description, options, validation) { pushCheckbox(form, question, description, options, validation); setForm([...form]); } /** Function that adds a 'select' object into the form array. * Its parameters are used on the translation of the backend data. * @param question - the question; * @param description - the description of the question; * @param subformId - id of the form being used as a subform; * @param validation - optional validation the question may have. */ function addToFormSubform(question, description, subformId, validation) { pushSubform(form, question, description, subformId, validation); setForm([...form]); } /** Function used on FormFieldRadio, FormFieldCheckbox and FormFieldSelect, it adds a new option field for those types of questions. * @param index - the position on the array that the operation needs to be done. */ async function addSelectOption(index) { form[index].options.push(""); form[index].error.errorMsg.options.push("Por favor, preencha esta opção"); await selectOptionsTesting(form, index); setForm([...form]); } /** Function used on FormFieldRadio, FormFieldCheckbox and FormFieldSelect, it removes an option field for those types of questions. * @param index - the position on the array that the operation needs to be done. */ async function removeSelectOption(index, idopt) { form[index].options.splice(idopt, 1); form[index].error.errorMsg.options.splice(idopt, 1); await selectOptionsTesting(form, index); setForm([...form]); } /** Function used on every FormField, it deletes the question from the array. * @param index - the position on the array that the operation needs to be done. */ function deleteFromForm(index) { form.splice(index, 1); setForm([...form]); } /** Function used on FormFieldQuestion, it handles the validation the user chooses. * Currently, only handle max and min number of characters. * @param index - the position on the array that the operation needs to be done. */ function addValidation(index) { form[index].validation.push({ type: "", value: "" }); form[index].error.errorMsg.validationValue = "Por favor, digite um número"; setForm([...form]); } /** Function used on every FormField, it updates the value of the question property on the form array. * @param value - the value being typed by the user; * @param index - the position on the array that the operation needs to be done. */ async function setQuestionField(value, index) { form[index].question = value; await testQuestionTextSchema(form, value, index); setForm([...form]); } /** Function used on every FormField, it updates the value of the description property on the array. * @param value - the value being typed by the user; * @param index - the position on the array that the operation needs to be done. */ async function setDescriptionField(value, index) { form[index].description = value; await testDescriptionTextSchema(form, value, index); setForm([...form]); } /** Function used on every FormField, it updates the value of the oprion property of the object on the array. * @param value - the value being typed by the user; * @param index - the position on the array that the operation needs to be done; * @param idopt - the id of the options being changed, inside the form[index]. */ async function setSelectOption(value, index, idopt) { form[index].options[idopt] = value; await selectOptionTextTesting(form, value, index, idopt); setForm([...form]); } /** Function used on every FormField, it updates the value of the required property of a question. * @param index - the position on the array that the operation needs to be done. */ function setRequiredField(index) { form[index].validation[0].value = !form[index].validation[0].value; setForm([...form]); } /** Function to store the selected subform Id on it's corresponding object. * @param value - the id of the selected form; * @param index - the position on the array that the operation needs to be done. */ async function setSubformId(value, index) { form[index].subformId = value; await testSubformSchema(form, index); setForm([...form]); } /** Function used on FormFieldText to set the chosen validation type, currently min and max char. * @param value - the type of the chosen validation; * @param index - the position on the array that the operation needs to be done. */ async function setValidationType(value, index) { form[index].validation[1].type = value; setForm([...form]); } /** Function used on FormFieldText to set the value of the validation * @param value - the value for the chosen validation; * @param index - the position on the array that the operation needs to be done. */ async function setValidationValue(value, index) { form[index].validation[1].value = value; await testTextValidation(form, index, value); setForm([...form]); } /** Function used on FormFieldText to remove the validaiton. * @param index - the position on the array that the operation needs to be done. */ function removeValidation(index) { form[index].validation.splice(-1, 1); form[index].error.errorMsg.validationValue = ""; setForm([...form]); } /** Reordering the form array based on the place the question is being dragged over. * @param result - an composed object bringing info about the drag event. */ function onDragEnd(result) { if (!result.destination) return; const { source, destination } = result; const copiedForm = [...form]; const [removed] = copiedForm.splice(source.index, 1); copiedForm.splice(destination.index, 0, removed); setForm(copiedForm); } /** Verify if the validation was changed on the edition. * @param backForm - form that came from the backend; * @param form - form being edited. */ function differentValidation(backForm, form) { for (let i = 0; i < form.validation.length; i++) { if ( JSON.stringify(form.validation[i]) !== JSON.stringify(backForm.validation[i]) ) { return true; } } return false; } /** Comparing the edited form with the 'original' form that is on the backend. * If some property of the input was changed in the edition, its id becomes null. */ async function setId() { const fetchData = async () => { await api.get(`/form/${routeId}`).then(async function (res) { let backForm = createFrontendForm(res.data); for (let i = 1; i < backForm.length; i++) { for (let j = 1; j < form.length; j++) { if (backForm[i].inputId === form[j].inputId) { if ( JSON.stringify(backForm[i], [ "question", "description", "options", "subformId", ]) !== JSON.stringify(form[j], [ "question", "description", "options", "subformId", ]) || differentValidation(backForm[i], form[j]) ) { form[j].inputId = null; } } } } }); }; await fetchData(); } /** The submit function. It's triggered when the submit button is pressed on the interface. * Its api call may be to create or to edit a form. */ async function submit(validToSend) { if (!validToSend) return; if (form[0].id) { await setId(); let data = await createBackendForm(form, routeId); const post_response = await api .put(`/form/${routeId}`, data, { headers: { authorization: `bearer ${window.sessionStorage.getItem("token")}`, }, }) .then(function (error) { if (!error.response) alert("Seu formulário foi atualizado com sucesso."); }) .catch(function (error) { if (error.response.data.error === "User dont own this form.") alert("Você não tem permissão para alterar este formulário"); else if (error.response.data.error === "Found a subform loop") alert( "Foi encontrada uma recursão de subformulários. Por favor, altere o subformulário selecionado ou o exclua." ); else alert("Um erro ocorreu."); }); } else { const post_response = await api .post(`/form`, await createBackendForm(form), { headers: { authorization: `bearer ${window.sessionStorage.getItem("token")}`, }, }) .then(function (error) { if (!error.response) alert("Seu formulário foi criado com sucesso."); else console.log("ERROR NO POST_RESPONSE", error); }) .catch(function (error) { console.log("ERROR NO POST RESPONSE", error.response); alert("Um erro ocorreu."); }); } } return { addToFormTitle, addToFormQuestion, addToFormSelect, addToFormRadio, addToFormCheckbox, addToFormSubform, addSelectOption, removeSelectOption, deleteFromForm, addValidation, setQuestionField, setDescriptionField, setSelectOption, setRequiredField, setSubformId, setValidationType, setValidationValue, removeValidation, onDragEnd, submit, }; }; export default useForm;