435 lines
13 KiB
JavaScript
435 lines
13 KiB
JavaScript
import React, { useEffect, useMemo, useRef } from "react";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import { updatePartnerPreferences } from "../redux/registrationFormSlice";
|
|
import {
|
|
Grid,
|
|
FormControl,
|
|
InputLabel,
|
|
Select,
|
|
MenuItem,
|
|
Button,
|
|
Checkbox,
|
|
ListItemText,
|
|
} from "@mui/material";
|
|
import { usePartnerPreferenceMasters } from "../hooks/useMasters";
|
|
import { useCityMasters, useSubCasteMasters } from "../hooks/useDependentMasters";
|
|
|
|
const PartnerPreferencesForm = ({
|
|
onSubmitStep,
|
|
onSkipStep,
|
|
errors,
|
|
onFieldChange,
|
|
}) => {
|
|
const dispatch = useDispatch();
|
|
const data = useSelector((state) => state.registerform.partnerPreferences);
|
|
const inputRef = useRef(null);
|
|
const requiredMark = <span style={{ color: "#d32f2f" }}> *</span>;
|
|
|
|
const { data: masters, isLoading: isPartnerMastersLoading } =
|
|
usePartnerPreferenceMasters();
|
|
const subCasteQuery = useSubCasteMasters(data.castes);
|
|
const cityQuery = useCityMasters(data.states);
|
|
|
|
const ageRangeOptions = useMemo(() => {
|
|
const raw = masters;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) return raw;
|
|
return raw.ageRange || raw.age_range || [];
|
|
}, [masters]);
|
|
|
|
const casteOptions = useMemo(() => {
|
|
const raw = masters;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) return raw;
|
|
return raw.caste || raw.castes || [];
|
|
}, [masters]);
|
|
|
|
const occupationOptions = useMemo(() => {
|
|
const raw = masters;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) return raw;
|
|
return raw.occupation || raw.occupations || [];
|
|
}, [masters]);
|
|
|
|
const educationOptions = useMemo(() => {
|
|
const raw = masters;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) return raw;
|
|
return raw.education || raw.educations || [];
|
|
}, [masters]);
|
|
|
|
const hobbyOptions = useMemo(() => {
|
|
const raw = masters;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) return raw;
|
|
return raw.hobbies || raw.hobby || [];
|
|
}, [masters]);
|
|
|
|
const annualIncomeOptions = useMemo(() => {
|
|
const raw = masters;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) return raw;
|
|
return raw.annual_income || raw.annualIncome || [];
|
|
}, [masters]);
|
|
|
|
const stateOptions = useMemo(() => {
|
|
const raw = masters;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) return raw;
|
|
return raw.state || raw.states || [];
|
|
}, [masters]);
|
|
|
|
const subCasteOptions = useMemo(() => {
|
|
const raw = subCasteQuery.data;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) {
|
|
const merged = raw.flatMap((entry) => {
|
|
if (!entry) return [];
|
|
if (Array.isArray(entry)) return entry;
|
|
return entry.sub_caste || entry.subCaste || entry.data || [];
|
|
});
|
|
return merged;
|
|
}
|
|
return raw.sub_caste || raw.subCaste || raw.data || [];
|
|
}, [subCasteQuery.data]);
|
|
|
|
const cityOptions = useMemo(() => {
|
|
const raw = cityQuery.data;
|
|
if (!raw) return [];
|
|
if (Array.isArray(raw)) return raw;
|
|
return raw.subCaste || raw.district || raw.data || [];
|
|
}, [cityQuery.data]);
|
|
|
|
useEffect(() => {
|
|
inputRef.current?.focus();
|
|
}, []);
|
|
|
|
const handleChange = (field, value) => {
|
|
const arrayFields = new Set([
|
|
"castes",
|
|
"subCastes",
|
|
"occupations",
|
|
"educations",
|
|
"hobbies",
|
|
"states",
|
|
"districts",
|
|
]);
|
|
const nextValue =
|
|
arrayFields.has(field) && typeof value === "string"
|
|
? value.split(",").filter(Boolean)
|
|
: value;
|
|
const updates = { [field]: nextValue };
|
|
const fieldsToClear = [field];
|
|
|
|
if (field === "castes") {
|
|
updates.subCastes = [];
|
|
fieldsToClear.push("subCastes");
|
|
}
|
|
if (field === "states") {
|
|
updates.districts = [];
|
|
fieldsToClear.push("districts");
|
|
}
|
|
|
|
dispatch(updatePartnerPreferences(updates));
|
|
if (onFieldChange) onFieldChange(fieldsToClear);
|
|
};
|
|
|
|
const handleSubmit = () => {
|
|
console.log("Submitting partner preferences:", data);
|
|
onSubmitStep();
|
|
};
|
|
|
|
const renderMultiSelect = ({
|
|
name,
|
|
label,
|
|
options,
|
|
value,
|
|
getLabel,
|
|
getValue,
|
|
disabled,
|
|
}) => (
|
|
<FormControl fullWidth variant="outlined" error={Boolean(errors[name])}>
|
|
<InputLabel id={`${name}-label`}>Select {label}</InputLabel>
|
|
<Select
|
|
labelId={`${name}-label`}
|
|
label={`Select ${label}`}
|
|
name={name}
|
|
multiple
|
|
value={value}
|
|
onChange={(e) => handleChange(name, e.target.value)}
|
|
disabled={disabled}
|
|
renderValue={(selected) =>
|
|
selected
|
|
.map((id) => {
|
|
const item = options.find((opt) => getValue(opt) === id);
|
|
return item ? getLabel(item) : id;
|
|
})
|
|
.join(", ")
|
|
}
|
|
sx={{
|
|
"& .MuiSelect-select.Mui-disabled": {
|
|
cursor: "not-allowed",
|
|
},
|
|
}}
|
|
>
|
|
{options.map((opt) => {
|
|
const optValue = getValue(opt);
|
|
const optLabel = getLabel(opt);
|
|
return (
|
|
<MenuItem key={optValue} value={optValue}>
|
|
<Checkbox checked={value.indexOf(optValue) > -1} />
|
|
<ListItemText primary={optLabel} />
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Select>
|
|
{errors[name] && (
|
|
<p
|
|
style={{
|
|
color: "#d32f2f",
|
|
margin: "3px 14px 0 14px",
|
|
fontSize: "0.75rem",
|
|
}}
|
|
>
|
|
{errors[name]}
|
|
</p>
|
|
)}
|
|
</FormControl>
|
|
);
|
|
|
|
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">
|
|
{/* Age Range */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
Age Range{requiredMark}
|
|
</label>
|
|
<FormControl
|
|
fullWidth
|
|
variant="outlined"
|
|
error={Boolean(errors.ageRange)}
|
|
>
|
|
<InputLabel id="ageRange-label">Select Age Range</InputLabel>
|
|
<Select
|
|
labelId="ageRange-label"
|
|
label="Select Age Range"
|
|
name="ageRange"
|
|
value={data.ageRange}
|
|
onChange={(e) => handleChange("ageRange", e.target.value)}
|
|
inputRef={inputRef}
|
|
disabled={isPartnerMastersLoading}
|
|
sx={{
|
|
"& .MuiSelect-select.Mui-disabled": {
|
|
cursor: "not-allowed",
|
|
},
|
|
}}
|
|
>
|
|
{ageRangeOptions.map((opt) => (
|
|
<MenuItem key={opt.id ?? opt} value={opt.id ?? opt}>
|
|
{opt.name || opt}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
{errors.ageRange && (
|
|
<p
|
|
style={{
|
|
color: "#d32f2f",
|
|
margin: "3px 14px 0 14px",
|
|
fontSize: "0.75rem",
|
|
}}
|
|
>
|
|
{errors.ageRange}
|
|
</p>
|
|
)}
|
|
</FormControl>
|
|
</div>
|
|
|
|
{/* Caste */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
Caste{requiredMark}
|
|
</label>
|
|
{renderMultiSelect({
|
|
name: "castes",
|
|
label: "Caste",
|
|
options: casteOptions,
|
|
value: data.castes,
|
|
getLabel: (opt) => opt.caste_name || opt.name || String(opt),
|
|
getValue: (opt) => opt.id ?? opt,
|
|
disabled: isPartnerMastersLoading,
|
|
})}
|
|
</div>
|
|
|
|
{/* Sub Caste */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
Sub Caste{requiredMark}
|
|
</label>
|
|
{renderMultiSelect({
|
|
name: "subCastes",
|
|
label: "Sub Caste",
|
|
options: subCasteOptions,
|
|
value: data.subCastes,
|
|
getLabel: (opt) =>
|
|
opt.sub_caste_name || opt.subCaste_name || opt.name || String(opt),
|
|
getValue: (opt) => opt.id ?? opt,
|
|
disabled: data.castes.length === 0 || subCasteQuery.isLoading,
|
|
})}
|
|
</div>
|
|
|
|
{/* Occupation */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
Occupation{requiredMark}
|
|
</label>
|
|
{renderMultiSelect({
|
|
name: "occupations",
|
|
label: "Occupation",
|
|
options: occupationOptions,
|
|
value: data.occupations,
|
|
getLabel: (opt) => opt.occupation_name || opt.name || String(opt),
|
|
getValue: (opt) => opt.id ?? opt,
|
|
disabled: isPartnerMastersLoading,
|
|
})}
|
|
</div>
|
|
|
|
{/* Qualification */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
Qualification{requiredMark}
|
|
</label>
|
|
{renderMultiSelect({
|
|
name: "educations",
|
|
label: "Qualification",
|
|
options: educationOptions,
|
|
value: data.educations,
|
|
getLabel: (opt) => opt.education_name || opt.name || String(opt),
|
|
getValue: (opt) => opt.id ?? opt,
|
|
disabled: isPartnerMastersLoading,
|
|
})}
|
|
</div>
|
|
|
|
{/* Lifestyle and Hobbies */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
Lifestyle & Hobbies{requiredMark}
|
|
</label>
|
|
{renderMultiSelect({
|
|
name: "hobbies",
|
|
label: "Lifestyle & Hobbies",
|
|
options: hobbyOptions,
|
|
value: data.hobbies,
|
|
getLabel: (opt) => opt.hobby_name || opt.name || String(opt),
|
|
getValue: (opt) => opt.id ?? opt,
|
|
disabled: isPartnerMastersLoading,
|
|
})}
|
|
</div>
|
|
|
|
{/* Annual Income */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
Annual Income{requiredMark}
|
|
</label>
|
|
<FormControl
|
|
fullWidth
|
|
variant="outlined"
|
|
error={Boolean(errors.annualIncome)}
|
|
>
|
|
<InputLabel id="annualIncome-label">
|
|
Select Annual Income
|
|
</InputLabel>
|
|
<Select
|
|
labelId="annualIncome-label"
|
|
label="Select Annual Income"
|
|
name="annualIncome"
|
|
value={data.annualIncome}
|
|
onChange={(e) => handleChange("annualIncome", e.target.value)}
|
|
disabled={isPartnerMastersLoading}
|
|
sx={{
|
|
"& .MuiSelect-select.Mui-disabled": {
|
|
cursor: "not-allowed",
|
|
},
|
|
}}
|
|
>
|
|
{annualIncomeOptions.map((opt) => (
|
|
<MenuItem key={opt.id ?? opt} value={opt.id ?? opt}>
|
|
{opt.annual_income_name || opt.name || opt}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
{errors.annualIncome && (
|
|
<p
|
|
style={{
|
|
color: "#d32f2f",
|
|
margin: "3px 14px 0 14px",
|
|
fontSize: "0.75rem",
|
|
}}
|
|
>
|
|
{errors.annualIncome}
|
|
</p>
|
|
)}
|
|
</FormControl>
|
|
</div>
|
|
|
|
{/* State */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
State{requiredMark}
|
|
</label>
|
|
{renderMultiSelect({
|
|
name: "states",
|
|
label: "State",
|
|
options: stateOptions,
|
|
value: data.states,
|
|
getLabel: (opt) => opt.state_name || opt.name || String(opt),
|
|
getValue: (opt) => opt.id ?? opt,
|
|
disabled: isPartnerMastersLoading,
|
|
})}
|
|
</div>
|
|
|
|
{/* City */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-gray-900 text-[15px]">
|
|
City{requiredMark}
|
|
</label>
|
|
{renderMultiSelect({
|
|
name: "districts",
|
|
label: "City",
|
|
options: cityOptions,
|
|
value: data.districts,
|
|
getLabel: (opt) =>
|
|
opt.district_name || opt.city_name || opt.name || String(opt),
|
|
getValue: (opt) => opt.id ?? opt,
|
|
disabled: data.states.length === 0 || cityQuery.isLoading,
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
<Grid
|
|
item
|
|
xs={12}
|
|
style={{
|
|
marginTop: 40,
|
|
display: "flex",
|
|
gap: 16,
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
{onSkipStep && (
|
|
<Button variant="outlined" onClick={onSkipStep}>
|
|
Skip
|
|
</Button>
|
|
)}
|
|
<Button variant="contained" color="primary" onClick={handleSubmit}>
|
|
{onSkipStep ? "Next" : "Update"}
|
|
</Button>
|
|
</Grid>
|
|
</form>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default PartnerPreferencesForm;
|