thirukalyanamweb/src/feature/PersonalDetailsForm.jsx

774 lines
31 KiB
JavaScript

import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updatePersonalDetails, clearAllStepsFrom } from "../redux/registrationFormSlice";
import {
Grid,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Button,
Box,
Typography,
InputAdornment,
IconButton,
FormHelperText,
CircularProgress,
Autocomplete,
} from "@mui/material";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import Visibility from "@mui/icons-material/Visibility";
import VisibilityOff from "@mui/icons-material/VisibilityOff";
import AdvancedDropzone from "./AdvancedDropzone";
import toast from "react-hot-toast";
import {
usePersonalDetailsMasters,
useCasteMasters,
useSubCasteMasters,
} from "../hooks/useDependentMasters";
import { useSendOtp, useVerifyOtp } from "../hooks/useAuth";
const OTP_LENGTH = 4;
const OTP_TIMER_SEC = 60;
const PersonalDetailsForm = ({ onSubmitStep, errors: externalErrors, onFieldChange, isEditMode }) => {
const dispatch = useDispatch();
const data = useSelector((state) => state.registerform.personalDetails);
const nameInputRef = useRef(null);
const mobileInputRef = useRef(null);
const requiredMark = <span style={{ color: "#d32f2f" }}> *</span>;
const [showOtp, setShowOtp] = useState(false);
const [otp, setOtp] = useState(new Array(OTP_LENGTH).fill(""));
const [otpTimer, setOtpTimer] = useState(0);
const [otpError, setOtpError] = useState("");
const [mobileOtpVerified, setMobileOtpVerified] = useState(false);
const [mobileNumberError, setMobileNumberError] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [localErrors, setLocalErrors] = useState({});
const sendOtp = useSendOtp();
const verifyOtp = useVerifyOtp();
const errors = useMemo(() => ({ ...externalErrors, ...localErrors }), [externalErrors, localErrors]);
const { data: personalMasters, isLoading: isPersonalMastersLoading } = usePersonalDetailsMasters();
const casteQuery = useCasteMasters(data.religion);
const subCasteQuery = useSubCasteMasters(data.caste);
const genderOptions = useMemo(() => personalMasters?.gender ?? ["Male", "Female"], [personalMasters]);
const maritalStatusOptions = useMemo(() => personalMasters?.marital_status ?? [], [personalMasters]);
const religionOptions = useMemo(() => personalMasters?.religion ?? [], [personalMasters]);
const profileForOptions = useMemo(() => personalMasters?.profileCreatedFor ?? [], [personalMasters]);
const languageOptions = useMemo(() => personalMasters?.languages ?? [], [personalMasters]);
const heightOptions = useMemo(() => personalMasters?.heights ?? [], [personalMasters]);
const complexionOptions = useMemo(() => personalMasters?.complextions ?? [], [personalMasters]);
const physicalStatusOptions = useMemo(() => personalMasters?.physicalStatus ?? [], [personalMasters]);
const casteOptions = useMemo(() => {
const raw = casteQuery.data;
if (!raw) return [];
return Array.isArray(raw) ? raw : raw.caste || raw.data || [];
}, [casteQuery.data]);
const subCasteOptions = useMemo(() => {
const raw = subCasteQuery.data;
if (!raw) return [];
return Array.isArray(raw) ? raw : raw.sub_caste || raw.subCaste || raw.data || [];
}, [subCasteQuery.data]);
useEffect(() => {
if (!isEditMode) {
if (!data.religion) dispatch(updatePersonalDetails({ religion: 1 }));
if (!data.caste) dispatch(updatePersonalDetails({ caste: 1 }));
}
}, [isEditMode, data.religion, data.caste, dispatch]);
useEffect(() => {
if (nameInputRef.current) {
nameInputRef.current.focus();
}
}, []);
const handleChange = (field, value) => {
const updates = { [field]: value };
const fieldsToClear = [field];
if (field === "name" && !/^[a-zA-Z\s]*$/.test(value)) return;
if (field === "mobile") {
setMobileNumberError("");
if (data.verifiedMobileNumber && value === data.verifiedMobileNumber) {
setMobileOtpVerified(true);
setShowOtp(false);
} else {
setMobileOtpVerified(false);
setShowOtp(false);
setOtp(new Array(OTP_LENGTH).fill(""));
setOtpError("");
setOtpTimer(0);
}
}
if (field === "religion") {
updates.caste = "";
updates.sub_caste = "";
fieldsToClear.push("caste", "sub_caste");
}
if (field === "caste") {
updates.sub_caste = "";
fieldsToClear.push("sub_caste");
}
dispatch(updatePersonalDetails(updates));
if (onFieldChange) onFieldChange(fieldsToClear);
if (!isEditMode) {
dispatch(clearAllStepsFrom(2));
}
if (localErrors[field]) {
setLocalErrors(prev => ({ ...prev, [field]: "" }));
}
};
const handleMobileSubmit = async () => {
if (!data.mobile || data.mobile.length !== 10) {
setMobileNumberError("Please enter a valid 10-digit mobile number");
return;
}
try {
setMobileNumberError("");
const res = await sendOtp.mutateAsync(data.mobile);
toast.success(res?.message || "OTP sent successfully");
setShowOtp(true);
setOtpTimer(OTP_TIMER_SEC);
setOtp(new Array(OTP_LENGTH).fill(""));
setMobileOtpVerified(false);
} catch (error) {
const msg = error?.response?.data?.message || "Failed to send OTP";
setMobileNumberError(msg);
toast.error(msg);
}
};
const handleOtpChange = (index, value) => {
if (!/^\d*$/.test(value)) return;
const newOtp = [...otp];
newOtp[index] = value.slice(-1);
setOtp(newOtp);
if (value && index < OTP_LENGTH - 1) {
document.getElementById(`otp-${index + 1}`)?.focus();
}
};
const handleOtpSubmit = async () => {
if (otp.some(d => d === "")) {
setOtpError("Complete OTP is required");
return;
}
try {
const res = await verifyOtp.mutateAsync({
mobile: data.mobile,
otp: otp.join(""),
});
toast.success(res?.message || "OTP verified successfully");
setMobileOtpVerified(true);
dispatch(updatePersonalDetails({ verifiedMobileNumber: data.mobile }));
setOtpError("");
} catch (error) {
setOtpError("Invalid or expired OTP");
}
};
useEffect(() => {
if (otpTimer <= 0) {
if (showOtp && !mobileOtpVerified) {
setOtpError("OTP expired, please resend");
setOtp(new Array(OTP_LENGTH).fill(""));
}
return;
}
const timerId = setInterval(() => setOtpTimer(t => t - 1), 1000);
return () => clearInterval(timerId);
}, [otpTimer, showOtp, mobileOtpVerified]);
const validateForm = () => {
const newErrors = {};
if (!data.profile_for) newErrors.profile_for = "Required";
if (!data.gender) newErrors.gender = "Required";
if (!data.name) newErrors.name = "Required";
if (!data.mobile) newErrors.mobile = "Required";
if (!data.email) newErrors.email = "Required";
if (!isEditMode && !data.password) newErrors.password = "Required";
if (!isEditMode && data.password !== data.confirmPassword) newErrors.confirmPassword = "Passwords do not match";
if (!data.marital_status) newErrors.marital_status = "Required";
if (!data.height) newErrors.height = "Required";
if (!data.complexion) newErrors.complexion = "Required";
if (!data.physical_status) newErrors.physical_status = "Required";
if (!data.mother_language) newErrors.mother_language = "Required";
if (!data.do_you_speak_telugu && data.do_you_speak_telugu !== 0) newErrors.do_you_speak_telugu = "Required";
if (!data.inter_caste_parents && data.inter_caste_parents !== 0) newErrors.inter_caste_parents = "Required";
if (data.known_languages.length === 0) newErrors.known_languages = "Required";
setLocalErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const scrollToError = (errorMap) => {
const errorFields = Object.keys(errorMap);
if (errorFields.length > 0) {
const fieldId = errorFields[0];
const element = document.getElementById(fieldId);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "center" });
// Use a timeout to allow the smooth scroll to complete
setTimeout(() => {
// For MUI Select, the focusable element is often the div with role="combobox" or the button
const focusable = element.querySelector('[role="combobox"]') ||
element.querySelector('[role="button"]') ||
element.querySelector("input") ||
element.querySelector("select") ||
element;
if (focusable && typeof focusable.focus === "function") {
focusable.focus();
}
}, 500);
}
}
};
const handleSubmit = async () => {
// 1. Prioritize OTP verification if showOtp is active
if (!mobileOtpVerified && !isEditMode && showOtp) {
setOtpError("Please enter and verify OTP");
const firstOtp = document.getElementById("otp-0");
if (firstOtp) {
firstOtp.scrollIntoView({ behavior: "smooth", block: "center" });
setTimeout(() => {
const input = firstOtp.querySelector("input") || firstOtp;
if (input && typeof input.focus === "function") input.focus();
}, 500);
}
return;
}
// 2. Perform general form validation
if (!validateForm()) {
const newErrors = {};
if (!data.profile_for) newErrors.profile_for = "Required";
if (!data.gender) newErrors.gender = "Required";
if (!data.name) newErrors.name = "Required";
if (!data.mobile) newErrors.mobile = "Required";
if (!data.email) newErrors.email = "Required";
if (!isEditMode && !data.password) newErrors.password = "Required";
if (!isEditMode && data.password !== data.confirmPassword) newErrors.confirmPassword = "Passwords do not match";
if (!data.marital_status) newErrors.marital_status = "Required";
if (!data.height) newErrors.height = "Required";
if (!data.complexion) newErrors.complexion = "Required";
if (!data.physical_status) newErrors.physical_status = "Required";
if (!data.mother_language) newErrors.mother_language = "Required";
if (!data.do_you_speak_telugu && data.do_you_speak_telugu !== 0) newErrors.do_you_speak_telugu = "Required";
if (!data.inter_caste_parents && data.inter_caste_parents !== 0) newErrors.inter_caste_parents = "Required";
if (data.known_languages.length === 0) newErrors.known_languages = "Required";
toast.error("Please fill all mandatory fields");
scrollToError(newErrors);
return;
}
// 3. Final mobile verification check if showOtp was not yet active
if (!mobileOtpVerified && !isEditMode) {
setMobileNumberError("Please verify your mobile number");
const mobileField = document.getElementById("mobile");
if (mobileField) {
mobileField.scrollIntoView({ behavior: "smooth", block: "center" });
setTimeout(() => {
const input = mobileField.querySelector("input") || mobileField;
if (input && typeof input.focus === "function") input.focus();
}, 500);
}
return;
}
onSubmitStep();
};
return (
<div className="w-full max-w-[1200px] mx-auto py-6 md:px-2 rounded-8">
<form noValidate autoComplete="off" style={{ padding: 16 }}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-20 gap-y-10 mb-6">
{/* 1. Profile Created for */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Profile Created for{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.profile_for)} variant="outlined" id="profile_for">
<InputLabel>Select Profile Created For</InputLabel>
<Select
value={data.profile_for}
label="Select Profile Created For"
onChange={(e) => handleChange("profile_for", e.target.value)}
>
{profileForOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.profile_for_name}</MenuItem>
))}
</Select>
{errors.profile_for && <FormHelperText>{errors.profile_for}</FormHelperText>}
</FormControl>
</div>
{/* 2. Gender */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Gender{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.gender)} variant="outlined" id="gender">
<InputLabel>Select Gender</InputLabel>
<Select
value={data.gender}
label="Select Gender"
onChange={(e) => handleChange("gender", e.target.value)}
>
{genderOptions.map((opt) => (
<MenuItem key={opt} value={opt}>{opt}</MenuItem>
))}
</Select>
{errors.gender && <FormHelperText>{errors.gender}</FormHelperText>}
</FormControl>
</div>
{/* 3. Name */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Name{requiredMark}</label>
<TextField
id="name"
fullWidth
inputRef={nameInputRef}
label="Enter Name"
value={data.name}
onChange={(e) => handleChange("name", e.target.value)}
error={Boolean(errors.name)}
helperText={errors.name}
variant="outlined"
/>
</div>
{/* 4. Mobile Number */}
<div className="flex flex-col gap-2" id="mobile">
<label className="text-gray-900 text-[15px]">Mobile Number{requiredMark}</label>
<Box sx={{ display: "flex", gap: 2, flexDirection: { xs: "column", sm: "row" } }}>
<TextField
fullWidth
inputRef={mobileInputRef}
label="Enter Mobile Number"
value={data.mobile}
onChange={(e) => handleChange("mobile", e.target.value)}
error={Boolean(errors.mobile) || Boolean(mobileNumberError)}
helperText={mobileNumberError || errors.mobile}
inputProps={{ maxLength: 10 }}
disabled={isEditMode}
InputProps={{
endAdornment: mobileOtpVerified && (
<InputAdornment position="end">
<CheckCircleIcon color="success" />
</InputAdornment>
),
}}
/>
{!isEditMode && !mobileOtpVerified && !showOtp && (
<Button variant="contained" onClick={handleMobileSubmit} sx={{ height: 56, minWidth: 120 }}>
Send OTP
</Button>
)}
</Box>
{showOtp && !mobileOtpVerified && (
<Box sx={{ mt: 2, p: 2, border: "1px solid #ddd", borderRadius: 1 }}>
<Typography variant="subtitle2" sx={{ mb: 1 }}>Enter OTP</Typography>
<Box sx={{ display: "flex", gap: 1 }}>
{otp.map((digit, i) => (
<TextField
key={i}
id={`otp-${i}`}
value={digit}
onChange={(e) => handleOtpChange(i, e.target.value)}
inputProps={{ maxLength: 1, style: { textAlign: "center", width: 40 } }}
/>
))}
<Button variant="contained" onClick={handleOtpSubmit} disabled={otp.some(d => !d)}>
Verify
</Button>
</Box>
{otpError && <Typography variant="caption" color="error">{otpError}</Typography>}
<Typography variant="caption" display="block" sx={{ mt: 1 }}>
{otpTimer > 0 ? `Resend in ${otpTimer}s` : (
<Button size="small" onClick={handleMobileSubmit}>Resend OTP</Button>
)}
</Typography>
</Box>
)}
</div>
{/* 5. Email Id */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Email Id{requiredMark}</label>
<TextField
id="email"
fullWidth
label="Enter Email"
value={data.email}
onChange={(e) => handleChange("email", e.target.value)}
error={Boolean(errors.email)}
helperText={errors.email}
variant="outlined"
/>
</div>
{!isEditMode && (
<>
{/* 6. Create Password */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Create Password{requiredMark}</label>
<TextField
id="password"
fullWidth
type={showPassword ? "text" : "password"}
label="Enter Password"
value={data.password}
onChange={(e) => handleChange("password", e.target.value)}
error={Boolean(errors.password)}
helperText={errors.password}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => setShowPassword(!showPassword)} edge="end">
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
/>
</div>
{/* 7. Confirm Password */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Confirm Password{requiredMark}</label>
<TextField
id="confirmPassword"
fullWidth
type={showConfirmPassword ? "text" : "password"}
label="Confirm Password"
value={data.confirmPassword}
onChange={(e) => handleChange("confirmPassword", e.target.value)}
error={Boolean(errors.confirmPassword)}
helperText={errors.confirmPassword}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => setShowConfirmPassword(!showConfirmPassword)} edge="end">
{showConfirmPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
/>
</div>
</>
)}
{/* 8. Marital Status */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Marital Status{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.marital_status)} id="marital_status">
<InputLabel>Select Marital Status</InputLabel>
<Select
value={data.marital_status}
label="Select Marital Status"
onChange={(e) => handleChange("marital_status", e.target.value)}
>
{maritalStatusOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.marital_status_name}</MenuItem>
))}
</Select>
{errors.marital_status && <FormHelperText>{errors.marital_status}</FormHelperText>}
</FormControl>
</div>
{/* 9. Height */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Height{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.height)} id="height">
<InputLabel>Select Height</InputLabel>
<Select
value={data.height}
label="Select Height"
onChange={(e) => handleChange("height", e.target.value)}
>
{heightOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.height_text}</MenuItem>
))}
</Select>
{errors.height && <FormHelperText>{errors.height}</FormHelperText>}
</FormControl>
</div>
{/* 10. Weight */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Weight (kg)</label>
<TextField
id="weight"
fullWidth
type="number"
label="Enter Weight"
value={data.weight}
onChange={(e) => handleChange("weight", e.target.value)}
/>
</div>
{/* 11. Select Complexion */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Select Complexion{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.complexion)} id="complexion">
<InputLabel>Select Complexion</InputLabel>
<Select
value={data.complexion}
label="Select Complexion"
onChange={(e) => handleChange("complexion", e.target.value)}
>
{complexionOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.complexion_name}</MenuItem>
))}
</Select>
{errors.complexion && <FormHelperText>{errors.complexion}</FormHelperText>}
</FormControl>
</div>
{/* 12. Select Physical Status */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Select Physical Status{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.physical_status)} id="physical_status">
<InputLabel>Select Physical Status</InputLabel>
<Select
value={data.physical_status}
label="Select Physical Status"
onChange={(e) => handleChange("physical_status", e.target.value)}
>
{physicalStatusOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.physical_status_name}</MenuItem>
))}
</Select>
{errors.physical_status && <FormHelperText>{errors.physical_status}</FormHelperText>}
</FormControl>
</div>
{/* 13. Religion */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Religion</label>
<FormControl fullWidth>
<InputLabel>Select Religion</InputLabel>
<Select
value={data.religion}
label="Select Religion"
onChange={(e) => handleChange("religion", e.target.value)}
>
{religionOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.religion_name}</MenuItem>
))}
</Select>
</FormControl>
</div>
{/* 14. Caste / Community */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Caste / Community</label>
<FormControl fullWidth disabled={casteQuery.isLoading}>
<InputLabel>Select Caste</InputLabel>
<Select
value={data.caste}
label="Select Caste"
onChange={(e) => handleChange("caste", e.target.value)}
>
{casteOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.caste_name}</MenuItem>
))}
</Select>
</FormControl>
</div>
{/* 15. Sub-Sect */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Sub-Sect{requiredMark}</label>
<FormControl fullWidth disabled={!data.caste || subCasteQuery.isLoading} id="sub_caste">
<InputLabel>Select Sub-Sect</InputLabel>
<Select
value={data.sub_caste}
label="Select Sub-Sect"
onChange={(e) => handleChange("sub_caste", e.target.value)}
>
{subCasteOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.sub_caste_name}</MenuItem>
))}
</Select>
</FormControl>
</div>
{/* 16. Willing to marry from the same sub sect */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Willing to marry from the same sub sect</label>
<FormControl fullWidth>
<InputLabel>Select Option</InputLabel>
<Select
value={data.willing_to_marry}
label="Select Option"
onChange={(e) => handleChange("willing_to_marry", e.target.value)}
>
<MenuItem value="Yes">Yes</MenuItem>
<MenuItem value="No">No</MenuItem>
<MenuItem value="Any">Any</MenuItem>
</Select>
</FormControl>
</div>
{/* 17. Inter-Caste Parents */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Inter-Caste Parents{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.inter_caste_parents)} id="inter_caste_parents">
<InputLabel>Select Option</InputLabel>
<Select
value={data.inter_caste_parents}
label="Select Option"
onChange={(e) => handleChange("inter_caste_parents", e.target.value)}
>
<MenuItem value={1}>Yes</MenuItem>
<MenuItem value={0}>No</MenuItem>
</Select>
{errors.inter_caste_parents && <FormHelperText>{errors.inter_caste_parents}</FormHelperText>}
</FormControl>
</div>
{/* 18. Parents Details */}
<div className="flex flex-col gap-2 md:col-span-2">
<label className="text-gray-900 text-[15px]">Parents Details</label>
<TextField
id="inter_caste_parents_details"
fullWidth
multiline
rows={3}
label="Enter Details"
value={data.inter_caste_parents_details}
onChange={(e) => handleChange("inter_caste_parents_details", e.target.value)}
/>
</div>
{/* 19. Gothram (optional) */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Gothram (optional)</label>
<TextField
id="gothram"
fullWidth
label="Enter Gothram"
value={data.gothram}
onChange={(e) => handleChange("gothram", e.target.value)}
/>
</div>
{/* 20. Do you speak Telugu */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Do you speak Telugu?{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.do_you_speak_telugu)} id="do_you_speak_telugu">
<InputLabel>Select Option</InputLabel>
<Select
value={data.do_you_speak_telugu}
label="Select Option"
onChange={(e) => handleChange("do_you_speak_telugu", e.target.value)}
>
<MenuItem value={1}>Yes</MenuItem>
<MenuItem value={0}>No</MenuItem>
</Select>
{errors.do_you_speak_telugu && <FormHelperText>{errors.do_you_speak_telugu}</FormHelperText>}
</FormControl>
</div>
{/* 21. About you */}
<div className="flex flex-col gap-2 md:col-span-2">
<label className="text-gray-900 text-[15px]">About you</label>
<TextField
id="about_us"
fullWidth
multiline
rows={4}
label="Describe yourself"
value={data.about_us}
onChange={(e) => handleChange("about_us", e.target.value)}
/>
</div>
{/* 22. Select Languages Spoken */}
<div className="flex flex-col gap-2 md:col-span-2">
<label className="text-gray-900 text-[15px]">Select Languages Spoken{requiredMark}</label>
<Autocomplete
id="known_languages"
multiple
options={languageOptions}
getOptionLabel={(opt) => opt.language || ""}
value={languageOptions.filter(opt => data.known_languages.includes(opt.id))}
onChange={(_, newValue) => handleChange("known_languages", newValue.map(v => v.id))}
renderInput={(params) => (
<TextField
{...params}
label="Select Languages"
error={Boolean(errors.known_languages)}
helperText={errors.known_languages}
/>
)}
/>
</div>
{/* 23. Select Mother Tongue */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Select Mother Tongue{requiredMark}</label>
<FormControl fullWidth error={Boolean(errors.mother_language)} id="mother_language">
<InputLabel>Select Mother Tongue</InputLabel>
<Select
value={data.mother_language}
label="Select Mother Tongue"
onChange={(e) => handleChange("mother_language", e.target.value)}
>
{languageOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>{opt.language}</MenuItem>
))}
</Select>
{errors.mother_language && <FormHelperText>{errors.mother_language}</FormHelperText>}
</FormControl>
</div>
{/* 24. Upload Profile */}
<div className="flex flex-col gap-2 md:col-span-2">
<label className="text-gray-900 text-[15px]">Upload Profile ( Max 3 image with 10 MB ){requiredMark}</label>
<AdvancedDropzone
id="profiles"
value={data.profiles || []}
onChange={(files) => handleChange("profiles", files)}
/>
</div>
</div>
<div className="flex justify-center mt-10">
<Button
id="save-button"
variant="contained"
size="large"
onClick={handleSubmit}
sx={{ px: 8, py: 1.5, borderRadius: 2 }}
>
{isEditMode ? "Save Changes" : "Save & Continue"}
</Button>
</div>
</form>
</div>
);
};
export default PersonalDetailsForm;