Refactor forms to use a generic form component

Replaced the contact form with a generic form component to support multiple form configurations. Moved validation schema and form data to a new structure, allowing dynamic form rendering. Updated the App component to utilize this generic approach for handling different forms.
This commit is contained in:
2024-08-06 21:07:20 +02:00
parent 2eef14ba2e
commit d2f15d2627
7 changed files with 125 additions and 72 deletions
+124
View File
@@ -0,0 +1,124 @@
import React, {useState} from "react";
import './GenericForm.css';
import {useNavigate} from 'react-router-dom'
import {ErrorMessage, Field, Form, Formik, FormikValues} from "formik";
import {Step, UserInput, FormData} from './formInterfaces';
import * as Yup from "yup";
const GenericForm = (formData: FormData) => {
const steps: Step[] = formData.steps;
const backend: string = formData.backend;
const userInput: UserInput = formData.userInput;
const spec: Yup.Schema<any> = formData.spec;
const navigate = useNavigate();
const [currentStep, setCurrentStep] = useState<number>(0);
const handleSubmit = async (e: FormikValues) => {
try {
const response = await fetch(backend, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(e)
})
if (!response.ok) {
let json: string = JSON.stringify(steps);
const blob = new Blob([json], {type: "application/json"});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'form_data.json';
link.click();
URL.revokeObjectURL(url);
//TODO clean up
alert("Your form submission was denied by the server, or the server was unable to process it, if you didn't mess with the data please contact the administrator at [email protected]");
} else {
navigate('/verify-email', {
state: {
email: e['email']
}
});
}
} catch (e) {
alert("Your form submission was denied by the server, if you didn't mess with the data please contact the administrator at [email protected]")
}
};
const next = () => {
setCurrentStep(current => current + 1)
}
const prev = () => {
setCurrentStep(current => Math.max(current - 1, 0))
}
const [prevLength, setPrevLength] = useState(0);
return (
<div className="container">
<div>
<h1>Contact Form</h1>
</div>
<div>
<Formik
initialValues={userInput}
validationSchema={spec}
onSubmit={(values: FormikValues) => {
handleSubmit(values);
}}
>
{({
touched,
errors,
isValid,
handleChange,
values,
setFieldTouched
}) => (
<Form>
<div>
<label>
{steps[currentStep].label}
<Field
type={steps[currentStep].type}
name={steps[currentStep].name}
required={steps[currentStep].required}
min={steps[currentStep].min_length}
max={steps[currentStep].max_length}
as={(steps[currentStep].type === "textarea") ? "textarea" : "input"}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
handleChange(e);
if (prevLength !== values[steps[currentStep].name].length) {
setFieldTouched(steps[currentStep].name);
setPrevLength(values[steps[currentStep].name].length);
}
}}
/>
<ErrorMessage name={steps[currentStep].name} component="div"/>
</label>
</div>
<button type="button" onClick={prev} disabled={currentStep === 0}>
Previous
</button>
<button type="button" onClick={next}
hidden={currentStep === (steps.length - 1)}
disabled={(!touched[steps[currentStep].name] || !!errors[steps[currentStep].name]) || currentStep === (steps.length - 1)}>
Next
</button>
<input type="submit" value="Submit"
hidden={currentStep !== (steps.length - 1)}
disabled={!isValid || currentStep !== (steps.length - 1)}
/>
</Form>
)}
</Formik>
</div>
</div>
);
};
export default GenericForm;