thirukalyanamweb/src/feature/PartnerPreferencesForm.jsx

549 lines
18 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,
TextField,
} 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 ageOptions = useMemo(() => Array.from({ length: 53 }, (_, i) => i + 18), []);
const heightOptions = useMemo(() => {
const raw = masters;
if (!raw) return [];
return raw.heights || raw.height || [];
}, [masters]);
const maritalStatusOptions = useMemo(() => {
const raw = masters;
if (!raw) return [];
return raw.maritalStatus || raw.marital_status || [];
}, [masters]);
const starOptions = useMemo(() => {
const raw = masters;
if (!raw) return [];
return raw.stars || raw.star || [];
}, [masters]);
const employeeTypeOptions = useMemo(() => {
const raw = masters;
if (!raw) return [];
return raw.employeeTypes || raw.employee_type || [];
}, [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 currencyOptions = useMemo(() => ["INR", "USD"], []);
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)) {
const merged = raw.flatMap((entry) => {
if (!entry) return [];
if (Array.isArray(entry)) return entry;
return entry.district || entry.districts || entry.data || [];
});
return merged;
}
return raw.district || raw.districts || raw.data || [];
}, [cityQuery.data]);
useEffect(() => {
inputRef.current?.focus();
}, []);
const handleChange = (field, value) => {
const arrayFields = new Set([
"castes",
"sub_castes",
"occupations",
"educations",
"states",
"districts",
"marital_statuses",
"birth_stars",
"employee_types",
"currencies",
]);
const nextValue =
arrayFields.has(field) && typeof value === "string"
? value.split(",").filter(Boolean)
: value;
const updates = { [field]: nextValue };
const fieldsToClear = [field];
if (field === "castes") {
updates.sub_castes = [];
fieldsToClear.push("sub_castes");
}
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">
{/* 1. Age Range */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Age Range (From - To)</label>
<div className="flex gap-2">
<FormControl fullWidth variant="outlined" error={Boolean(errors.age_from)}>
<InputLabel>From Age</InputLabel>
<Select
id="age_from"
name="age_from"
value={data.age_from}
label="From Age"
inputRef={inputRef}
onChange={(e) => handleChange("age_from", e.target.value)}
>
{ageOptions.map((age) => (
<MenuItem key={age} value={age}>{age}</MenuItem>
))}
</Select>
{errors.age_from && (
<p style={{ color: "#d32f2f", margin: "3px 14px 0 14px", fontSize: "0.75rem" }}>
{errors.age_from}
</p>
)}
</FormControl>
<FormControl fullWidth variant="outlined" error={Boolean(errors.age_to)}>
<InputLabel>To Age</InputLabel>
<Select
id="age_to"
name="age_to"
value={data.age_to}
label="To Age"
onChange={(e) => handleChange("age_to", e.target.value)}
>
{ageOptions.map((age) => (
<MenuItem key={age} value={age}>{age}</MenuItem>
))}
</Select>
{errors.age_to && (
<p style={{ color: "#d32f2f", margin: "3px 14px 0 14px", fontSize: "0.75rem" }}>
{errors.age_to}
</p>
)}
</FormControl>
</div>
</div>
{/* 2. Height Range */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Height Range (From - To)</label>
<div className="flex gap-2">
<FormControl fullWidth variant="outlined" error={Boolean(errors.height_from)}>
<InputLabel>From Height</InputLabel>
<Select
id="height_from"
name="height_from"
value={data.height_from}
label="From Height"
onChange={(e) => handleChange("height_from", e.target.value)}
>
{heightOptions.map((h) => (
<MenuItem key={h.id} value={h.id}>{h.height_text}</MenuItem>
))}
</Select>
{errors.height_from && (
<p style={{ color: "#d32f2f", margin: "3px 14px 0 14px", fontSize: "0.75rem" }}>
{errors.height_from}
</p>
)}
</FormControl>
<FormControl fullWidth variant="outlined" error={Boolean(errors.height_to)}>
<InputLabel>To Height</InputLabel>
<Select
id="height_to"
name="height_to"
value={data.height_to}
label="To Height"
onChange={(e) => handleChange("height_to", e.target.value)}
>
{heightOptions.map((h) => (
<MenuItem key={h.id} value={h.id}>{h.height_text}</MenuItem>
))}
</Select>
{errors.height_to && (
<p style={{ color: "#d32f2f", margin: "3px 14px 0 14px", fontSize: "0.75rem" }}>
{errors.height_to}
</p>
)}
</FormControl>
</div>
</div>
{/* 3. Marital Status */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Marital Status</label>
{renderMultiSelect({
name: "marital_statuses",
label: "Marital Status",
options: maritalStatusOptions,
value: data.marital_statuses,
getLabel: (opt) => opt.marital_status_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* 4. Birth Star */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Birth Star</label>
{renderMultiSelect({
name: "birth_stars",
label: "Birth Star",
options: starOptions,
value: data.birth_stars,
getLabel: (opt) => opt.star_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* 5. Caste */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Caste</label>
{renderMultiSelect({
name: "castes",
label: "Caste",
options: casteOptions,
value: data.castes,
getLabel: (opt) => opt.caste_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* 6. Sub-Sect */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Sub-Sect</label>
{renderMultiSelect({
name: "sub_castes",
label: "Sub-Sect",
options: subCasteOptions,
value: data.sub_castes,
getLabel: (opt) => opt.sub_caste_name || opt.name,
getValue: (opt) => opt.id,
disabled: data.castes.length === 0 || subCasteQuery.isLoading,
})}
</div>
{/* 7. Qualification */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Qualification</label>
{renderMultiSelect({
name: "educations",
label: "Qualification",
options: educationOptions,
value: data.educations,
getLabel: (opt) => opt.education_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* 8. Occupation */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Occupation</label>
{renderMultiSelect({
name: "occupations",
label: "Occupation",
options: occupationOptions,
value: data.occupations,
getLabel: (opt) => opt.occupation_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* 9. Employee Type */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Employee Type</label>
{renderMultiSelect({
name: "employee_types",
label: "Employee Type",
options: employeeTypeOptions,
value: data.employee_types,
getLabel: (opt) => opt.employee_type_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* 10. Currency Type */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Income Currency Type</label>
{renderMultiSelect({
name: "currencies",
label: "Currency",
options: currencyOptions,
value: data.currencies,
getLabel: (opt) => opt,
getValue: (opt) => opt,
disabled: isPartnerMastersLoading,
})}
</div>
{/* 11. Income Range */}
{data.currencies.includes("INR") && (
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Annual Income (INR Range)</label>
<div className="flex gap-2">
<TextField
id="inr_from"
name="inr_from"
fullWidth
label="From (INR)"
value={data.inr_from}
error={Boolean(errors.inr_from)}
helperText={errors.inr_from}
onChange={(e) => handleChange("inr_from", e.target.value)}
/>
<TextField
id="inr_to"
name="inr_to"
fullWidth
label="To (INR)"
value={data.inr_to}
error={Boolean(errors.inr_to)}
helperText={errors.inr_to}
onChange={(e) => handleChange("inr_to", e.target.value)}
/>
</div>
</div>
)}
{data.currencies.includes("USD") && (
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Annual Income (USD Range)</label>
<div className="flex gap-2">
<TextField
id="usd_from"
name="usd_from"
fullWidth
label="From (USD)"
value={data.usd_from}
error={Boolean(errors.usd_from)}
helperText={errors.usd_from}
onChange={(e) => handleChange("usd_from", e.target.value)}
/>
<TextField
id="usd_to"
name="usd_to"
fullWidth
label="To (USD)"
value={data.usd_to}
error={Boolean(errors.usd_to)}
helperText={errors.usd_to}
onChange={(e) => handleChange("usd_to", e.target.value)}
/>
</div>
</div>
)}
{/* 12. State */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">State</label>
{renderMultiSelect({
name: "states",
label: "State",
options: stateOptions,
value: data.states,
getLabel: (opt) => opt.state_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* 13. City */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">City</label>
{renderMultiSelect({
name: "districts",
label: "City",
options: cityOptions,
value: data.districts,
getLabel: (opt) => opt.district_name || opt.city_name || opt.name,
getValue: (opt) => opt.id,
disabled: data.states.length === 0 || cityQuery.isLoading,
})}
</div>
</div>
<Grid
size={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;