Form With Validation - Copy this React, Tailwind Component to your project
import React, { useState, useCallback ,useRef} from "react"; import Grid from "@mui/material/Grid2"; import { Box, FormLabel } from "@mui/material"; import * as Yup from "yup"; import { useNavigate } from "react-router-dom"; import { LeadsModel } from "../../models/leads-model"; import { CompreoBasePanel, CompreoDropdown, CompreoInput, CompreoFileUploadButton, CompreoCancelButton, CompreoPrimaryButton, CompreoHeading, CompreoToggleButton, CompreoTextArea } from "../../../../components"; import useLeads from "../leads/hooks/use-leads"; import leadsService from "../../services/leads-service"; const validationSchema = Yup.object({ enquiryDate: Yup.date().required("Enquiry Date is required"), responseLastDate: Yup.date().min(Yup.ref('enquiryDate'), "Response Last Date must be after Enquiry Date.").nullable(), enquiryType: Yup.string().required("Enquiry Type is required"), customerType: Yup.string().required("Customer Type is required"), projectType: Yup.string().required("Project Type is required"), clientName: Yup.string().required("Client Name is required"), projectName: Yup.string().required("Project Name is required"), projectDetails: Yup.string().required("Project Details are required"), projectLocation: Yup.string().required("Project Location is required"), leadGeneratedBy: Yup.string(), leadStatus: Yup.string(), }); const INITIAL_FORM_STATE: Partial<LeadsModel> = { enquiryStatus: true, leadStatus: "Draft", enquiryDate: new Date(), }; const CreateLead: React.FC = () => { const [formData, setFormData] = useState<Partial<LeadsModel>>(INITIAL_FORM_STATE); const [errors, setErrors] = useState<{ [key: string]: string }>({}); const { enquiryTypes, existingClients, projectTypes, employeesList, leadStatus } = useLeads(); const navigate = useNavigate(); const fieldRefs = { enquiryType: useRef<HTMLSelectElement>(null), enquiryDate: useRef<HTMLInputElement>(null), customerType: useRef<HTMLSelectElement>(null), clientNameDropdown: useRef<HTMLSelectElement>(null), clientNameInput: useRef<HTMLInputElement>(null), projectType: useRef<HTMLSelectElement>(null), projectName: useRef<HTMLInputElement>(null), projectLocation: useRef<HTMLInputElement>(null), projectDetails: useRef<HTMLTextAreaElement>(null), }; const handleInputChange = useCallback( (field: string, value: any) => { const updatedValue = field === "enquiryDate" || field === "responseLastDate" ? new Date(value) : value; setFormData((prevData) => ({ ...prevData, [field]: updatedValue })); validationSchema .validateAt(field, { ...formData, [field]: updatedValue }) .then(() => setErrors((prev) => ({ ...prev, [field]: "" }))) .catch((err: any) => setErrors((prev) => ({ ...prev, [field]: err.message }))); }, [formData] ); const extractClientCode = (clientName: string | undefined): string => { if (!clientName) return ""; const startIdx = clientName.indexOf("(") + 1; const endIdx = clientName.indexOf(")"); return startIdx > 0 && endIdx > startIdx ? clientName.slice(startIdx, endIdx) : ""; }; const scrollToFirstError = (validationErrors: Record<string, string>) => { const firstErrorField = Object.keys(validationErrors)[0]; const ref = fieldRefs[firstErrorField as keyof typeof fieldRefs]; if (ref && ref.current) { ref.current.scrollIntoView({ behavior: "smooth", block: "center" }); // Focus input/select/textarea within the field, if applicable const customFocusable = ref.current.querySelector("input, select, textarea") || ref.current; if (customFocusable) { console.log("hi",customFocusable); (customFocusable as HTMLElement).focus(); } } }; const handleSubmit = async () => { try { await validationSchema.validate(formData, { abortEarly: false }); const clientCode = extractClientCode(formData.clientName); const updatedFormData = { ...formData, clientCode }; console.log("Lead successfully created:", updatedFormData); await leadsService.createLead(updatedFormData as LeadsModel); navigate(-1); } catch (err: any) { if (err.name === "ValidationError") { const validationErrors: Record<string, string> = {}; err.inner.forEach((error: any) => { if (error.path) validationErrors[error.path] = error.message; }); setErrors(validationErrors); scrollToFirstError(validationErrors); } else { console.error("Unexpected error during form submission:", err); } } }; const handleFileUpload = (files: File[]) => { if (files && files.length > 0) { //setFormData((prevData) => ({ ...prevData, enquiryDetailsAttachment: files[0] })); } }; const handleClose = () => { navigate(-1) }; return ( <CompreoBasePanel links={[ { name: "Home", url: "/dashboard" }, { name: "Leads", url: "/leads" }, { name: "Create Lead", url: `/leads/create-lead` }, ]} > <CompreoHeading text="Enquiry Details" divider={true} /> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 6 }}> <CompreoDropdown ref={fieldRefs.enquiryType} label="Enquiry Type *" options={enquiryTypes} placeholder="Select Enquiry Type" error={Boolean(errors.enquiryType)} helperText={errors.enquiryType} value={formData.enquiryType} onChange={(event) => handleInputChange("enquiryType", event.target.value)} // ref={fieldRefs.enquiryType} /> </Grid> <Grid size={{ xs: 12, md: 6 }}> <CompreoInput ref={fieldRefs.enquiryDate} label="Tender/Enquiry Date *" type="date" error={Boolean(errors.enquiryDate)} helperText={errors.enquiryDate} value={formData.enquiryDate ? new Date(formData.enquiryDate).toLocaleDateString('en-CA') : ''} onChange={(event) => handleInputChange( "enquiryDate", new Date(event.target.value) ) } /> </Grid> </Grid> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 6 }} sx={{ marginTop: 4 }}> <FormLabel sx={{ mr: 1.5 }}>{"Enquiry Status *"}</FormLabel> <CompreoToggleButton checked={true} onChange={(event) => handleInputChange("enquiryStatus", event.target.checked) } /> </Grid> <Grid size={{ xs: 12, md: 6 }}> <CompreoInput label="Response Last Date" type="date" error={Boolean(errors.responseLastDate)} helperText={errors.responseLastDate} value={formData.responseLastDate ? new Date(formData.responseLastDate).toLocaleDateString('en-CA') : ''} onChange={(event) => handleInputChange( "responseLastDate", new Date(event.target.value) ) } /> </Grid> </Grid> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> </Grid> <CompreoHeading text="Client Details" divider={true} sx={{ marginTop: 2 }} /> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 6 }}> <CompreoDropdown ref={fieldRefs.customerType} label="Customer Type *" options={[ { value: "Existing", label: "Existing" }, { value: "New Client", label: "New Client" }, ]} placeholder="Select a Customer Type" error={formData.customerType === ""} helperText={(formData.customerType === "" ? "Customer Type is required" : undefined)} value={formData.customerType} onChange={(event) => { const value = event.target.value; setFormData((prevData) => { let updatedData = { ...prevData, customerType: String(value)}; if (value === "New Client" || value === "Existing" || value === "") { updatedData.clientName = ""; } return updatedData; }); } } /> </Grid> <Grid size={{ xs: 12, md: 6 }}> {formData.customerType === "New Client" ? ( <CompreoInput label={"Client Name *"} ref={fieldRefs.clientNameInput} placeholder="Select Client Name" error={Boolean(errors.clientName)} helperText={errors.clientName} disabled={!formData.customerType} value={formData.clientName} onChange={(event) => handleInputChange("clientName", event.target.value) } />) : ( <CompreoDropdown label="Client Name *" ref={fieldRefs.clientNameDropdown} options={existingClients} placeholder="Select Client Name" error={Boolean(errors.clientName)} disabled={!formData.customerType} helperText={errors.clientName} value={formData.clientName} onChange={(event) => handleInputChange("clientName", event.target.value) } />) } </Grid> </Grid> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 6 }}> <CompreoInput label="Client Project ID" type="text" error={false} onChange={(event) => handleInputChange("projectId", event.target.value) } /> </Grid> </Grid> <CompreoHeading text="Project Details" divider={true} sx={{ marginTop: 2 }} /> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 6 }}> <CompreoDropdown label="Project Type *" ref={fieldRefs.projectType} options={projectTypes} placeholder="Select Project Type" error={Boolean(errors.projectType)} helperText={errors.projectType} value={formData.projectType} onChange={(event) => handleInputChange("projectType", event.target.value) } /> </Grid> <Grid size={{ xs: 12, md: 6 }}> <CompreoInput label="Project Name *" ref={fieldRefs.projectName} type="text" error={Boolean(errors.projectName)} helperText={errors.projectName} onChange={(event) => handleInputChange("projectName", event.target.value) } /> </Grid> </Grid> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 6 }} > <CompreoInput label="Project Location *" ref={fieldRefs.projectLocation} type="text" error={Boolean(errors.projectLocation)} helperText={errors.projectLocation} onChange={(event) => handleInputChange("projectLocation", event.target.value) } /> </Grid> </Grid> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 12 }}> <CompreoTextArea label="Project Details *" ref={fieldRefs.projectDetails} error={Boolean(errors.projectDetails)} helperText={errors.projectDetails} onChange={(event) => handleInputChange("projectDetails", event.target.value) } /> </Grid> </Grid> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 12 }}> <CompreoFileUploadButton label="Document Upload" buttonText="Choose File" accept=".pdf, .doc, .docx, .xls, .xlsx" helperText="Please upload files in PDF, Word, or Excel format only." error={false} // initialFileName={formData.enquiryDetailsAttachment && Array.isArray(formData.enquiryDetailsAttachment) // ? formData.enquiryDetailsAttachment // : []} onFileUpload={handleFileUpload} /> </Grid> </Grid> <CompreoHeading text="Supporting Details" divider={true} sx={{ marginTop: 2 }} /> <Grid container spacing={{ xs: 0, md: 6, lg: 6, xl: 6 }}> <Grid size={{ xs: 12, md: 6 }}> <CompreoDropdown label="Lead Generated by" options={employeesList} placeholder="Select an Employee" error={false} helperText={errors.leadGeneratedBy} value={formData.leadGeneratedBy} onChange={(event) => handleInputChange("leadGeneratedBy", event.target.value) } /> </Grid> <Grid size={{ xs: 12, md: 6 }}> <CompreoDropdown label="Lead Status" options={leadStatus} placeholder="Select Status" error={Boolean(errors.leadStatus)} value={formData.leadStatus} onChange={(event) => handleInputChange("leadStatus", event.target.value) } disabled={true} /> </Grid> </Grid> <Box sx={{ display: "flex", justifyContent: "end", marginTop: 1.5 }}> <CompreoCancelButton name={"Cancel"} sx={{ mr: 3 }} onClick={handleClose} /> <CompreoPrimaryButton name={"Create Lead"} onClick={handleSubmit} /> </Box> </CompreoBasePanel> ); }; export default CreateLead; when i click create lead button .the cursor nott move to the error field can you please fix it