Implement Subscription History and fix Interest Status Update flow

This commit is contained in:
mageshwaran committed 2026-04-28 15:41:54 +05:30
1 parent 4ba4ce1e1b
commit b3d33aca9c
42 files changed
+3950 -2677

No files matched your search

+13 -17
View File
@@ -73,35 +73,31 @@ const EducationalDetailsForm = ({
dispatch(updateEducationalDetails(updates));
setLocalErrors((prev) => ({ ...prev, [field]: "" }));
if (!isEditMode) {
dispatch(clearAllStepsFrom(3));
}
};
const validateForm = () => {
const newErrors = {};
if (!data.study_field) newErrors.study_field = "Required";
if (!data.education) newErrors.education = "Required";
if (!data.education_detail) newErrors.education_detail = "Required";
if (!data.employee_type) newErrors.employee_type = "Required";
if (!data.study_field) newErrors.study_field = "Field of study is required";
if (!data.education) newErrors.education = "Highest qualification is required";
if (!data.education_detail) newErrors.education_detail = "Education detail is required";
if (!data.employee_type) newErrors.employee_type = "Employee type is required";
if (!isUnemployed) {
if (!data.occupation) newErrors.occupation = "Required";
if (!data.occupation_detail) newErrors.occupation_detail = "Required";
if (!data.income_currency) newErrors.income_currency = "Required";
if (!data.annual_income) newErrors.annual_income = "Required";
if (!data.work_country) newErrors.work_country = "Required";
if (!data.occupation) newErrors.occupation = "Occupation is required";
if (!data.occupation_detail) newErrors.occupation_detail = "Occupation detail is required";
if (!data.income_currency) newErrors.income_currency = "Income currency is required";
if (!data.annual_income) newErrors.annual_income = "Annual income is required";
if (!data.work_country) newErrors.work_country = "Work country is required";
if (isIndia) {
if (!data.work_state) newErrors.work_state = "Required";
if (!data.work_district) newErrors.work_district = "Required";
if (!data.work_state) newErrors.work_state = "Work state is required";
if (!data.work_district) newErrors.work_district = "Work city is required";
} else {
if (!data.work_city) newErrors.work_city = "Required";
if (!data.work_city) newErrors.work_city = "Work city is required";
}
}
if (!data.address) newErrors.address = "Required";
if (!data.address) newErrors.address = "Work address is required";
setLocalErrors(newErrors);
return newErrors;
-8
View File
@@ -119,10 +119,6 @@ const FamilyDetailsForm = ({ onSubmitStep, onSkipStep, errors, onFieldChange, is
dispatch(updateFamilyDetails(updates));
if (onFieldChange) onFieldChange(fieldsToClear);
if (!isEditMode) {
dispatch(clearAllStepsFrom(4));
}
};
const handleSiblingChange = (type, index, field, value) => {
@@ -131,10 +127,6 @@ const FamilyDetailsForm = ({ onSubmitStep, onSkipStep, errors, onFieldChange, is
list[index] = { ...list[index], [field]: value };
dispatch(updateFamilyDetails({ [type]: list }));
if (onFieldChange) onFieldChange(type);
if (!isEditMode) {
dispatch(clearAllStepsFrom(4));
}
};
const scrollToError = (errorMap) => {
+234 -149
View File
@@ -18,11 +18,11 @@ import {
DialogActions,
Tooltip,
} from "@mui/material";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { TimePicker } from "@mui/x-date-pickers/TimePicker";
import { LocalizationProvider, DatePicker, TimePicker } from "@mui/x-date-pickers";
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
import { format } from "date-fns";
import { useLifestyleMasters } from "../hooks/useMasters";
import { useStarMasters, usePathamMasters } from "../hooks/useDependentMasters";
import horoscopeImg from "../assets/images/horoscopeimg.png";
const LifestyleDetailsForm = ({
@@ -34,30 +34,47 @@ const LifestyleDetailsForm = ({
}) => {
const dispatch = useDispatch();
const data = useSelector((state) => state.registerform.lifestyleDetails);
if (!data) return null;
const inputRef = useRef(null);
const requiredMark = <span style={{ color: "#d32f2f" }}> *</span>;
const { data: lifestyleMasters, isLoading: isLifestyleMastersLoading } =
useLifestyleMasters();
const dietOptions = useMemo(() => {
const raasiOptions = useMemo(() => {
const raw = lifestyleMasters;
if (!raw) return [];
if (Array.isArray(raw)) return raw;
return raw.diet || raw.diets || [];
return raw.raasis || raw.raasi || raw.rasi || [];
}, [lifestyleMasters]);
const hobbyOptions = useMemo(() => {
const panjangamOptions = useMemo(() => {
const raw = lifestyleMasters;
if (!raw) return [];
if (Array.isArray(raw)) return raw;
return raw.hobbies || raw.hobby || [];
return raw.panjangam_types || raw.panjangamTypes || [];
}, [lifestyleMasters]);
const { data: starData } = useStarMasters(data.raasi);
const starOptions = useMemo(() => {
if (!starData) return [];
return starData.stars || starData.star || starData.data || (Array.isArray(starData) ? starData : []);
}, [starData]);
const { data: pathamData } = usePathamMasters(data.star);
const pathamOptions = useMemo(() => {
if (!pathamData) return [];
return (
pathamData.pathams ||
pathamData.patham ||
pathamData.data ||
(Array.isArray(pathamData) ? pathamData : [])
);
}, [pathamData]);
const grahaOptions = useMemo(() => {
const raw = lifestyleMasters;
if (!raw) return [];
if (Array.isArray(raw)) return raw;
return raw.grahas || raw.graha || [];
}, [lifestyleMasters]);
@@ -74,13 +91,21 @@ const LifestyleDetailsForm = ({
inputRef.current?.focus();
}, []);
useEffect(() => {
if (data.dob) {
const date = new Date(data.dob);
if (!isNaN(date.getTime())) {
const day = format(date, "EEEE");
if (data.dayOfBirth !== day) {
handleChange("dayOfBirth", day);
}
}
}
}, [data.dob]);
const handleChange = (field, value) => {
dispatch(updateLifestyleDetails({ [field]: value }));
if (onFieldChange) onFieldChange(field);
if (!isEditMode) {
dispatch(clearAllStepsFrom(5));
}
};
const handleMultiChange = (field, value) => {
@@ -189,7 +214,7 @@ const LifestyleDetailsForm = ({
return `${h}:${m}`;
};
const renderChartCell = (label, value, onChange) => (
const renderChartCell = (label, value = [], onChange) => (
<Tooltip title={value && value.length > 0 ? value.join(", ") : ""} arrow placement="top">
<div className="bg-white border border-gray-300 rounded-lg p-2 flex flex-col items-center justify-center min-h-[70px]">
<span className="text-[10px] font-medium text-gray-700 text-center">
@@ -221,7 +246,7 @@ const LifestyleDetailsForm = ({
>
{grahaOptions.map((opt) => (
<MenuItem key={opt} value={opt}>
<Checkbox checked={value.indexOf(opt) > -1} />
<Checkbox checked={Array.isArray(value) && value.indexOf(opt) > -1} />
<ListItemText primary={opt} />
</MenuItem>
))}
@@ -267,120 +292,13 @@ const LifestyleDetailsForm = ({
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">
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Diet{requiredMark}
</label>
<FormControl fullWidth variant="outlined" error={Boolean(errors.diets)}>
<InputLabel id="diets-label">Select Diet</InputLabel>
<Select
labelId="diets-label"
label="Select Diet"
name="diets"
value={data.diets}
onChange={(e) => handleChange("diets", e.target.value)}
inputRef={inputRef}
disabled={isLifestyleMastersLoading}
sx={{
"& .MuiSelect-select.Mui-disabled": {
cursor: "not-allowed",
},
}}
>
{dietOptions.map((opt) => {
const value = opt.id ?? opt;
const label = opt.diet_name || opt.name || String(opt);
return (
<MenuItem key={value} value={value}>
{label}
</MenuItem>
);
})}
</Select>
{errors.diets && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors.diets}
</p>
)}
</FormControl>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Hobbies & Interests (Multi-select){requiredMark}
</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.hobbies)}
>
<InputLabel id="hobbies-label">
Select Hobbies & Interests
</InputLabel>
<Select
labelId="hobbies-label"
label="Select Hobbies & Interests"
name="hobbies"
multiple
value={data.hobbies}
onChange={(e) => handleMultiChange("hobbies", e.target.value)}
disabled={isLifestyleMastersLoading}
renderValue={(selected) =>
selected
.map((id) => {
const item = hobbyOptions.find(
(opt) => (opt.id ?? opt) === id
);
return item?.hobby_name || item?.name || id;
})
.join(", ")
}
sx={{
"& .MuiSelect-select.Mui-disabled": {
cursor: "not-allowed",
},
}}
>
{hobbyOptions.map((opt) => {
const value = opt.id ?? opt;
const label = opt.hobby_name || opt.name || String(opt);
return (
<MenuItem key={value} value={value}>
<Checkbox checked={data.hobbies.indexOf(value) > -1} />
<ListItemText primary={label} />
</MenuItem>
);
})}
</Select>
{errors.hobbies && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors.hobbies}
</p>
)}
</FormControl>
</div>
</div>
<div className="text-center py-4">
<h2 className="text-[18px] font-semibold text-gray-800">
Astrology / Horoscope
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 w-full max-w-[900px] mx-auto">
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 w-full max-w-[1200px] mx-auto">
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Date of Birth{requiredMark}
@@ -403,6 +321,19 @@ const LifestyleDetailsForm = ({
</LocalizationProvider>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Day of Birth</label>
<TextField
fullWidth
name="dayOfBirth"
value={data.dayOfBirth}
readOnly
variant="outlined"
placeholder="Day of Week"
InputProps={{ readOnly: true }}
/>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Time of Birth{requiredMark}
@@ -424,7 +355,7 @@ const LifestyleDetailsForm = ({
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Place of Birth</label>
<label className="text-gray-900 text-[15px]">Place of Birth{requiredMark}</label>
<TextField
fullWidth
name="placeOfBirth"
@@ -438,32 +369,186 @@ const LifestyleDetailsForm = ({
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 w-full max-w-[950px] mx-auto mt-6">
<div>
<div className="py-4">
<h3 className="text-base font-semibold text-gray-800 mb-3">
Add Rasi
</h3>
{renderChartGrid("graha")}
{errors.graha_duplicate && (
<p style={{ color: "#d32f2f", fontSize: "0.75rem", marginTop: "4px" }}>
{errors.graha_duplicate}
</p>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 w-full max-w-[1200px] mx-auto mt-6">
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Select Raasi{requiredMark}</label>
<FormControl fullWidth variant="outlined" error={Boolean(errors.raasi)} id="raasi">
<InputLabel id="raasi-label">Select Raasi</InputLabel>
<Select
labelId="raasi-label"
label="Select Raasi"
name="raasi"
value={data.raasi}
onChange={(e) => {
handleChange("raasi", e.target.value);
handleChange("star", "");
handleChange("patham", "");
}}
>
{raasiOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>
{opt.raasi_name || opt.rasi_name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div>
<div className="py-4">
<h3 className="text-base font-semibold text-gray-800 mb-3">
Add Navamsam
</h3>
{renderChartGrid("amsam")}
{errors.amsam_duplicate && (
<p style={{ color: "#d32f2f", fontSize: "0.75rem", marginTop: "4px" }}>
{errors.amsam_duplicate}
</p>
)}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Select Birth Star{requiredMark}</label>
<FormControl fullWidth variant="outlined" error={Boolean(errors.star)} id="star">
<InputLabel id="star-label">Select Birth Star</InputLabel>
<Select
labelId="star-label"
label="Select Birth Star"
name="star"
value={data.star}
onChange={(e) => {
handleChange("star", e.target.value);
handleChange("patham", "");
}}
>
{starOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>
{opt.star_name || opt.name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Select Padham</label>
<FormControl fullWidth variant="outlined" id="patham">
<InputLabel id="patham-label">Select Padham</InputLabel>
<Select
labelId="patham-label"
label="Select Padham"
name="patham"
value={data.patham}
onChange={(e) => handleChange("patham", e.target.value)}
>
{pathamOptions.map((opt) => (
<MenuItem key={opt} value={opt}>
{opt}
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Select Lagnam</label>
<FormControl fullWidth variant="outlined" id="lagnam">
<InputLabel id="lagnam-label">Select Lagnam</InputLabel>
<Select
labelId="lagnam-label"
label="Select Lagnam"
name="lagnam"
value={data.lagnam}
onChange={(e) => handleChange("lagnam", e.target.value)}
>
{raasiOptions.map((opt) => (
<MenuItem key={opt.id} value={opt.id}>
{opt.raasi_name || opt.rasi_name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Panjangam Type</label>
<FormControl fullWidth variant="outlined" id="panjangam_type">
<InputLabel id="panjangam-label">Select Panjangam Type</InputLabel>
<Select
labelId="panjangam-label"
label="Select Panjangam Type"
name="panjangam_type"
value={data.panjangam_type}
onChange={(e) => handleChange("panjangam_type", e.target.value)}
>
{panjangamOptions.map((opt) => (
<MenuItem key={opt} value={opt}>
{opt}
</MenuItem>
))}
</Select>
</FormControl>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 w-full max-w-[1200px] mx-auto mt-6">
<div className="flex flex-col gap-2">
<h3 className="text-base font-semibold text-gray-800 mb-3">Add Rasi</h3>
{renderChartGrid("graha")}
</div>
<div className="flex flex-col gap-2">
<h3 className="text-base font-semibold text-gray-800 mb-3">Add Navamsam</h3>
{renderChartGrid("amsam")}
</div>
</div>
<div className="mt-8 w-full max-w-[1200px] mx-auto p-6 border border-gray-200 rounded-lg bg-gray-50">
<h3 className="text-base font-semibold text-gray-800 mb-4">Dasa Details</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Dasa Type</label>
<FormControl fullWidth variant="outlined" id="dasa_balance">
<InputLabel id="dasa-label">Select Dasa Type</InputLabel>
<Select
labelId="dasa-label"
label="Select Dasa Type"
name="dasa_balance"
value={data.dasa_balance}
onChange={(e) => handleChange("dasa_balance", e.target.value)}
>
{grahaOptions.map((opt) => (
<MenuItem key={opt} value={opt}>
{opt}
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Dasa Year</label>
<TextField
id="dasa_years"
fullWidth
name="dasa_years"
value={data.dasa_years}
onChange={(e) => handleChange("dasa_years", e.target.value)}
placeholder="Enter Year"
variant="outlined"
type="number"
/>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Dasa Month</label>
<TextField
id="dasa_months"
fullWidth
name="dasa_months"
value={data.dasa_months}
onChange={(e) => handleChange("dasa_months", e.target.value)}
placeholder="Enter Month"
variant="outlined"
type="number"
/>
</div>
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Dasa Day</label>
<TextField
id="dasa_days"
fullWidth
name="dasa_days"
value={data.dasa_days}
onChange={(e) => handleChange("dasa_days", e.target.value)}
placeholder="Enter Day"
variant="outlined"
type="number"
/>
</div>
</div>
</div>
+270 -156
View File
@@ -10,6 +10,7 @@ import {
Button,
Checkbox,
ListItemText,
TextField,
} from "@mui/material";
import { usePartnerPreferenceMasters } from "../hooks/useMasters";
import { useCityMasters, useSubCasteMasters } from "../hooks/useDependentMasters";
@@ -30,11 +31,30 @@ const PartnerPreferencesForm = ({
const subCasteQuery = useSubCasteMasters(data.castes);
const cityQuery = useCityMasters(data.states);
const ageRangeOptions = useMemo(() => {
const ageOptions = useMemo(() => Array.from({ length: 53 }, (_, i) => i + 18), []);
const heightOptions = useMemo(() => {
const raw = masters;
if (!raw) return [];
if (Array.isArray(raw)) return raw;
return raw.ageRange || raw.age_range || [];
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(() => {
@@ -72,6 +92,8 @@ const PartnerPreferencesForm = ({
return raw.annual_income || raw.annualIncome || [];
}, [masters]);
const currencyOptions = useMemo(() => ["INR", "USD"], []);
const stateOptions = useMemo(() => {
const raw = masters;
if (!raw) return [];
@@ -96,8 +118,15 @@ const PartnerPreferencesForm = ({
const cityOptions = useMemo(() => {
const raw = cityQuery.data;
if (!raw) return [];
if (Array.isArray(raw)) return raw;
return raw.subCaste || raw.district || raw.data || [];
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(() => {
@@ -107,12 +136,15 @@ const PartnerPreferencesForm = ({
const handleChange = (field, value) => {
const arrayFields = new Set([
"castes",
"subCastes",
"sub_castes",
"occupations",
"educations",
"hobbies",
"states",
"districts",
"marital_statuses",
"birth_stars",
"employee_types",
"currencies",
]);
const nextValue =
arrayFields.has(field) && typeof value === "string"
@@ -122,8 +154,8 @@ const PartnerPreferencesForm = ({
const fieldsToClear = [field];
if (field === "castes") {
updates.subCastes = [];
fieldsToClear.push("subCastes");
updates.sub_castes = [];
fieldsToClear.push("sub_castes");
}
if (field === "states") {
updates.districts = [];
@@ -201,215 +233,297 @@ const PartnerPreferencesForm = ({
<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 */}
{/* 1. 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",
}}
<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)}
>
{errors.ageRange}
</p>
)}
</FormControl>
{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>
{/* Caste */}
{/* 2. Height Range */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Caste{requiredMark}
</label>
<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 || String(opt),
getValue: (opt) => opt.id ?? opt,
getLabel: (opt) => opt.caste_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* Sub Caste */}
{/* 6. Sub-Sect */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Sub Caste{requiredMark}
</label>
<label className="text-gray-900 text-[15px]">Sub-Sect</label>
{renderMultiSelect({
name: "subCastes",
label: "Sub Caste",
name: "sub_castes",
label: "Sub-Sect",
options: subCasteOptions,
value: data.subCastes,
getLabel: (opt) =>
opt.sub_caste_name || opt.subCaste_name || opt.name || String(opt),
getValue: (opt) => opt.id ?? opt,
value: data.sub_castes,
getLabel: (opt) => opt.sub_caste_name || opt.name,
getValue: (opt) => opt.id,
disabled: data.castes.length === 0 || subCasteQuery.isLoading,
})}
</div>
{/* Occupation */}
{/* 7. Qualification */}
<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>
<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 || String(opt),
getValue: (opt) => opt.id ?? opt,
getLabel: (opt) => opt.education_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* Lifestyle and Hobbies */}
{/* 8. Occupation */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Lifestyle & Hobbies{requiredMark}
</label>
<label className="text-gray-900 text-[15px]">Occupation</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,
name: "occupations",
label: "Occupation",
options: occupationOptions,
value: data.occupations,
getLabel: (opt) => opt.occupation_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* Annual Income */}
{/* 9. Employee Type */}
<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>
<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>
{/* State */}
{/* 10. Currency Type */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
State{requiredMark}
</label>
<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 || String(opt),
getValue: (opt) => opt.id ?? opt,
getLabel: (opt) => opt.state_name || opt.name,
getValue: (opt) => opt.id,
disabled: isPartnerMastersLoading,
})}
</div>
{/* City */}
{/* 13. City */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
City{requiredMark}
</label>
<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 || String(opt),
getValue: (opt) => opt.id ?? opt,
getLabel: (opt) => opt.district_name || opt.city_name || opt.name,
getValue: (opt) => opt.id,
disabled: data.states.length === 0 || cityQuery.isLoading,
})}
</div>
</div>
<Grid
item
xs={12}
size={12}
style={{
marginTop: 40,
display: "flex",
+58 -37
View File
@@ -126,13 +126,20 @@ const PersonalDetailsForm = ({ onSubmitStep, errors: externalErrors, onFieldChan
dispatch(updatePersonalDetails(updates));
if (onFieldChange) onFieldChange(fieldsToClear);
if (!isEditMode) {
dispatch(clearAllStepsFrom(2));
}
if (localErrors[field]) {
setLocalErrors(prev => ({ ...prev, [field]: "" }));
}
// Real-time password match validation
if (field === "confirmPassword" || field === "password") {
const p = field === "password" ? value : data.password;
const cp = field === "confirmPassword" ? value : data.confirmPassword;
if (p && cp && p !== cp) {
setLocalErrors(prev => ({ ...prev, confirmPassword: "Passwords do not match" }));
} else if (p && cp && p === cp) {
setLocalErrors(prev => ({ ...prev, confirmPassword: "" }));
}
}
};
const handleMobileSubmit = async () => {
@@ -198,21 +205,27 @@ const PersonalDetailsForm = ({ onSubmitStep, errors: externalErrors, onFieldChan
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";
if (!data.profile_for) newErrors.profile_for = "Profile created for is required";
if (!data.gender) newErrors.gender = "Gender is required";
if (!data.name) newErrors.name = "Name is required";
if (!data.mobile) newErrors.mobile = "Mobile number is required";
if (!data.email) newErrors.email = "Email is required";
if (!isEditMode && !data.password) newErrors.password = "Password is required";
if (!isEditMode && !data.confirmPassword) newErrors.confirmPassword = "Confirm Password is required";
if (!isEditMode && data.password && data.confirmPassword && data.password !== data.confirmPassword) {
newErrors.confirmPassword = "Passwords do not match";
}
if (!data.caste) newErrors.caste = "Caste is required";
if (!data.sub_caste) newErrors.sub_caste = "Sub-Sect is required";
if (!data.marital_status) newErrors.marital_status = "Marital status is required";
if (!data.height) newErrors.height = "Height is required";
if (!data.complexion) newErrors.complexion = "Complexion is required";
if (!data.physical_status) newErrors.physical_status = "Physical status is required";
if (!data.mother_language) newErrors.mother_language = "Mother tongue is required";
if (!data.do_you_speak_telugu && data.do_you_speak_telugu !== 0) newErrors.do_you_speak_telugu = "Telugu speaking status is required";
if (!data.inter_caste_parents && data.inter_caste_parents !== 0) newErrors.inter_caste_parents = "Inter-caste parents status is required";
if (data.known_languages.length === 0) newErrors.known_languages = "Known languages are required";
setLocalErrors(newErrors);
return Object.keys(newErrors).length === 0;
@@ -260,21 +273,27 @@ const PersonalDetailsForm = ({ onSubmitStep, errors: externalErrors, onFieldChan
// 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";
if (!data.profile_for) newErrors.profile_for = "Profile created for is required";
if (!data.gender) newErrors.gender = "Gender is required";
if (!data.name) newErrors.name = "Name is required";
if (!data.mobile) newErrors.mobile = "Mobile number is required";
if (!data.email) newErrors.email = "Email is required";
if (!isEditMode && !data.password) newErrors.password = "Password is required";
if (!isEditMode && !data.confirmPassword) newErrors.confirmPassword = "Confirm Password is required";
if (!isEditMode && data.password && data.confirmPassword && data.password !== data.confirmPassword) {
newErrors.confirmPassword = "Passwords do not match";
}
if (!data.caste) newErrors.caste = "Caste is required";
if (!data.sub_caste) newErrors.sub_caste = "Sub-Sect is required";
if (!data.marital_status) newErrors.marital_status = "Marital status is required";
if (!data.height) newErrors.height = "Height is required";
if (!data.complexion) newErrors.complexion = "Complexion is required";
if (!data.physical_status) newErrors.physical_status = "Physical status is required";
if (!data.mother_language) newErrors.mother_language = "Mother tongue is required";
if (!data.do_you_speak_telugu && data.do_you_speak_telugu !== 0) newErrors.do_you_speak_telugu = "Telugu speaking status is required";
if (!data.inter_caste_parents && data.inter_caste_parents !== 0) newErrors.inter_caste_parents = "Inter-caste parents status is required";
if (data.known_languages.length === 0) newErrors.known_languages = "Known languages are required";
toast.error("Please fill all mandatory fields");
scrollToError(newErrors);
@@ -581,8 +600,8 @@ const PersonalDetailsForm = ({ onSubmitStep, errors: externalErrors, onFieldChan
{/* 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}>
<label className="text-gray-900 text-[15px]">Caste / Community{requiredMark}</label>
<FormControl fullWidth disabled={casteQuery.isLoading} error={Boolean(errors.caste)} id="caste">
<InputLabel>Select Caste</InputLabel>
<Select
value={data.caste}
@@ -593,13 +612,14 @@ const PersonalDetailsForm = ({ onSubmitStep, errors: externalErrors, onFieldChan
<MenuItem key={opt.id} value={opt.id}>{opt.caste_name}</MenuItem>
))}
</Select>
{errors.caste && <FormHelperText>{errors.caste}</FormHelperText>}
</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">
<FormControl fullWidth disabled={!data.caste || subCasteQuery.isLoading} error={Boolean(errors.sub_caste)} id="sub_caste">
<InputLabel>Select Sub-Sect</InputLabel>
<Select
value={data.sub_caste}
@@ -610,6 +630,7 @@ const PersonalDetailsForm = ({ onSubmitStep, errors: externalErrors, onFieldChan
<MenuItem key={opt.id} value={opt.id}>{opt.sub_caste_name}</MenuItem>
))}
</Select>
{errors.sub_caste && <FormHelperText>{errors.sub_caste}</FormHelperText>}
</FormControl>
</div>
View File
Whitespace-only changes.
+311 -401
View File
@@ -5,7 +5,7 @@ import { Navigation, Pagination } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import { Edit2,Info } from 'lucide-react';
import { Edit2, Info } from 'lucide-react';
import {
Card,
CardContent,
@@ -25,6 +25,7 @@ import {
} from '@mui/material';
import { usePreviewDetails } from "../hooks/usePreview";
import { isAuthenticated } from "../utills/auth";
import { usePersonalMasters, usePartnerPreferenceMasters } from "../hooks/useMasters";
import horoscopeImg from "../assets/images/horoscopeimg.png";
import { updatePersonalDetails } from "../redux/registrationFormSlice";
@@ -42,8 +43,7 @@ const PreviewScreen = ({ onEdit, onSubmit }) => {
if (pd) {
const images = pd.images || pd.profile_images;
if (images && images.length > 0) {
// Map images to ensure they have a consistent format for Redux
const formattedImages = images.map(img =>
const formattedImages = images.map(img =>
typeof img === 'string' ? { url: img, preview: img } : img
);
dispatch(updatePersonalDetails({ profiles: formattedImages }));
@@ -66,124 +66,50 @@ const PreviewScreen = ({ onEdit, onSubmit }) => {
setOpenConfirmDialog(false);
};
const sections = previewData?.personal_details
? [
{
title: "Personal Details",
step: 1,
data: previewData.personal_details,
},
{
title: "Educational & Professional Details",
step: 2,
data: previewData.educational_details,
},
{
title: "Family Details",
step: 3,
data: previewData.family_details,
},
{
title: "Lifestyle & Habits",
step: 4,
data: previewData.lifestyle_details,
},
{
title: "Partner Preferences",
step: 5,
data: previewData.partner_preferences,
},
]
: [
{
title: 'Personal Details',
step: 1,
data: formData.personalDetails,
},
{
title: 'Educational & Professional Details',
step: 2,
data: formData.educationalDetails,
},
{
title: 'Family Details',
step: 3,
data: formData.familyDetails,
},
{
title: 'Lifestyle & Habits',
step: 4,
data: formData.lifestyleDetails,
},
{
title: 'Partner Preferences',
step: 5,
data: formData.partnerPreferences,
},
];
const renderValue = (key, value) => {
if (key === "profiles" || key === "profile" || key === "profile_images" || key === "images") {
const list = Array.isArray(value) ? value : (value ? [value] : []);
return (
<Box display="flex" gap={1} flexWrap="wrap">
{list.length > 0 ? (
list.map((imgObj, index) => {
const src =
typeof imgObj === "string"
? imgObj
: imgObj?.preview ||
imgObj?.url ||
(imgObj?.file ? URL.createObjectURL(imgObj.file) : null);
if (!src) return null;
return (
<img
key={index}
src={src}
alt="Profile"
style={{
width: "90px",
height: "90px",
objectFit: "cover",
borderRadius: "50%",
border: "1px solid #ccc",
// marginLeft: "-20px",
}}
/>
);
})
) : (
<Box display="flex" alignItems="center" gap={1} sx={{ color: "#888" }}>
<Info size={16} />
<Typography>No photos uploaded</Typography>
</Box>
)}
</Box>
);
}
const renderValue = (value) => {
if (value === null || value === undefined || value === "") return "-";
if (Array.isArray(value)) {
if (value.length === 0) return "-";
if (typeof value[0] === "object") {
return value.map((item, idx) => (
<div key={idx}>{JSON.stringify(item)}</div>
));
}
return value.join(", ");
}
if (value && typeof value === "object") {
return "-";
if (typeof value === "boolean" || value === 1 || value === "1" || value === 0 || value === "0") {
if (value === true || value === 1 || value === "1") return "Yes";
if (value === false || value === 0 || value === "0") return "No";
}
return value || "-";
return value;
};
const DetailRow = ({ label, value }) => (
<Box display="grid" gridTemplateColumns="45% 55%" gap={1} mb={0.8} alignItems="start">
<Typography color="text.secondary" sx={{ fontWeight: 500, fontSize: '0.875rem' }}>
{label}:
</Typography>
<Typography variant="body2" fontWeight={600} sx={{ wordBreak: 'break-word' }}>
{renderValue(value)}
</Typography>
</Box>
);
const SectionTitle = ({ title, mt = 2 }) => (
<Typography
variant="subtitle1"
sx={{
fontWeight: 'bold',
fontSize: '18px',
color: '#111827',
mt: mt,
mb: 2
}}
>
{title}
</Typography>
);
const renderChartGrid = (getDataForCell, title) => {
const renderCell = (i) => {
const items = getDataForCell(i);
const label = Array.isArray(items) ? items.join(', ') : '';
return (
<Tooltip key={i} title={label} arrow placement="top" disableHoverListener={!items || items.length === 0}>
<Box
@@ -204,12 +130,12 @@ const PreviewScreen = ({ onEdit, onSubmit }) => {
cursor: items && items.length > 0 ? 'help' : 'default'
}}
>
{items && items.length > 2 ? (
<Box>
{items.slice(0, 2).join(', ')}
<Box component="span" sx={{ color: 'primary.main', display: 'block', fontSize:'0.6rem' }}>+{items.length - 2}</Box>
</Box>
) : label}
{items && items.length > 2 ? (
<Box>
{items.slice(0, 2).join(', ')}
<Box component="span" sx={{ color: 'primary.main', display: 'block', fontSize: '0.6rem' }}>+{items.length - 2}</Box>
</Box>
) : label}
</Box>
</Tooltip>
);
@@ -217,315 +143,299 @@ const PreviewScreen = ({ onEdit, onSubmit }) => {
return (
<Box>
<Typography variant="subtitle2" align="center" gutterBottom sx={{ fontWeight: 600 }}>{title}</Typography>
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0.5, maxWidth: '300px', mx: 'auto', p: 1, bgcolor: '#fff3e0', borderRadius: 2 }}>
{renderCell(1)} {renderCell(2)} {renderCell(3)} {renderCell(4)}
{renderCell(5)}
<Box sx={{ gridColumn: 'span 2', gridRow: 'span 2', bgcolor: '#fff', border: '1px solid #eee', display:'flex', alignItems:'center', justifyContent:'center', fontSize:'0.7rem', fontWeight:'bold', color:'#aaa', overflow: 'hidden' }}>
<img src={horoscopeImg} alt={title} style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
</Box>
{renderCell(6)}
{renderCell(7)} {renderCell(8)}
{renderCell(9)} {renderCell(10)} {renderCell(11)} {renderCell(12)}
<Typography variant="subtitle2" align="center" gutterBottom sx={{ fontWeight: 600, color: '#CC1F1F' }}>{title}</Typography>
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0.5, maxWidth: '300px', mx: 'auto', p: 1, bgcolor: '#fff3e0', borderRadius: 2 }}>
{renderCell(1)} {renderCell(2)} {renderCell(3)} {renderCell(4)}
{renderCell(5)}
<Box sx={{ gridColumn: 'span 2', gridRow: 'span 2', bgcolor: '#fff', border: '1px solid #eee', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '0.7rem', fontWeight: 'bold', color: '#aaa', overflow: 'hidden' }}>
<img src={horoscopeImg} alt={title} style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
</Box>
{renderCell(6)}
{renderCell(7)} {renderCell(8)}
{renderCell(9)} {renderCell(10)} {renderCell(11)} {renderCell(12)}
</Box>
</Box>
);
};
return (
<Box p={3} sx={{maxWidth:"1400px", width:"100%"}} mx="auto" >
<div className='grid grid-cols-1 md:grid-cols-2 gap-4 my-10'>
const { data: pm } = usePersonalMasters();
const { data: ppm } = usePartnerPreferenceMasters();
const getNamesFromIds = (ids, options, labelKey = 'name', valueKey = 'id') => {
if (!ids || (Array.isArray(ids) && ids.length === 0)) return null;
if (!options) return Array.isArray(ids) ? ids.join(", ") : ids;
const idArray = Array.isArray(ids) ? ids : String(ids).split(",").map(v => v.trim());
if (idArray.length === 0) return null;
// Check if the first element is already a name
if (idArray[0] && typeof idArray[0] === 'string' && isNaN(Number(idArray[0]))) {
return idArray.join(", ");
}
const names = idArray.map(id => {
const found = options.find(opt => String(opt[valueKey]) === String(id));
if (found) return found[labelKey];
return id;
});
return names.filter(Boolean).join(", ");
};
const pInfo = previewData?.personal_details || formData.personalDetails;
const eInfo = previewData?.educational_details || formData.educationalDetails;
const fInfo = previewData?.family_details || formData.familyDetails;
const lInfo = previewData?.lifestyle_details || formData.lifestyleDetails;
// Helper to check if partner preferences from API are actually empty
const isPartnerApiDataEmpty = (pp) => {
if (!pp) return true;
// Check for common fields that should have values if data was saved
const hasData = pp.preferred_age_from || pp.age_from || pp.preferred_marital_status_ids?.length > 0 || pp.marital_statuses?.length > 0 || pp.preferred_castes_ids?.length > 0 || pp.castes?.length > 0;
return !hasData;
};
const prInfo = (!isPartnerApiDataEmpty(previewData?.partner_preferences)) ? previewData.partner_preferences :
(!isPartnerApiDataEmpty(previewData?.preferred_details)) ? previewData.preferred_details :
(!isPartnerApiDataEmpty(previewData?.partner_details)) ? previewData.partner_details :
(formData.partnerPreferences || {});
return (
<Box p={3} sx={{ maxWidth: "1400px", width: "100%" }} mx="auto">
<div className='grid grid-cols-1 md:grid-cols-2 gap-6 my-10'>
{isLoading && (
<Box
py={4}
display="flex"
justifyContent="center"
sx={{ color: "#666" }}
>
<Box py={4} display="flex" justifyContent="center" sx={{ color: "#666", gridColumn: 'span 2' }}>
Loading preview...
</Box>
)}
{isError && (
<Box
py={4}
display="flex"
justifyContent="center"
sx={{ color: "#d32f2f" }}
>
<Box py={4} display="flex" justifyContent="center" sx={{ color: "#d32f2f", gridColumn: 'span 2' }}>
Failed to load preview.
</Box>
)}
{!isLoading &&
sections.map((section) => (
<Card key={section.title} variant="outlined"
sx={{ borderRadius: 2,
// background:"#fff5ed"
}}>
{!isLoading && (
<>
{/* 1. Personal Details */}
<Card variant="outlined" sx={{ borderRadius: 2 }}>
<CardHeader
title={
<Typography variant="h6" fontWeight="bold">
{section.title}
</Typography>
}
action={
<IconButton
aria-label="edit"
color="primary"
onClick={() => onEdit(section.step)}
size="large"
>
<Edit2 size={20} />
</IconButton>
}
sx={{padding:"15px 15px", background:"#f5fbff" }}
title={<Typography variant="h6" fontWeight="bold">Personal Details</Typography>}
action={<IconButton color="primary" onClick={() => onEdit(1)} size="large"><Edit2 size={20} /></IconButton>}
sx={{ padding: "15px 15px", background: "#f5fbff" }}
/>
{/* <Divider /> */}
<CardContent sx={{ pt: 1, }}>
{Object.entries(section.data || {}).map(([key, value]) => {
// Filter out ID fields
const hiddenFields = [
"id",
"created_at",
"updated_at",
"phone_number_visibility",
"chat_alert_notification",
"chat_protection",
"profile_photo_protect",
"call_protection",
"match_alert_preference",
"who_can_message",
"who_can_message_categories",
"user_status",
];
if (hiddenFields.includes(key) || key.endsWith('_id') || key.endsWith('Id') || key.endsWith('_ids') || key.endsWith('Ids')) {
return null;
}
// Handle Horoscope Chart (Server Data)
if (key === 'horoscope' && value && typeof value === 'object') {
return (
<Box key={key} sx={{ width: '100%', mt: 2, mb: 2, borderTop: '1px solid #e0e0e0', pt: 2, gridColumn: '1 / -1' }}>
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>Horoscope Details</Typography>
<Grid container spacing={4} justifyContent="center">
<Grid item xs={12} md={6}>
{renderChartGrid((i) => {
const val = value[`graha_${i}`];
return val ? val.split(',') : [];
}, 'Rasi')}
</Grid>
<Grid item xs={12} md={6}>
{renderChartGrid((i) => {
const val = value[`amsam_${i}`];
return val ? val.split(',') : [];
}, 'Navamsam')}
</Grid>
</Grid>
</Box>
);
}
// Handle Horoscope Chart (Redux Data)
if (key === 'graha' && value) {
return (
<Box key={key} sx={{ width: '100%', mt: 2, mb: 2, gridColumn: '1 / -1' }}>
{renderChartGrid((i) => value[i] || [], 'Rasi')}
</Box>
);
}
if (key === 'amsam' && value) {
return (
<Box key={key} sx={{ width: '100%', mt: 2, mb: 2, gridColumn: '1 / -1' }}>
{renderChartGrid((i) => value[i] || [], 'Navamsam')}
</Box>
);
}
// Handle brothers and sisters arrays specifically
if ((key === 'brothers' || key === 'sisters') && Array.isArray(value) && value.length > 0) {
return (
<Box key={key} sx={{ py: 2, borderBottom: "1px solid #e0e0e0" }}>
<Typography color="text.secondary" sx={{ fontWeight: 600, mb: 1 }}>
{key === 'brothers' ? 'Brothers Details' : 'Sisters Details'}
</Typography>
<style>
{`
.custom-swiper-${key} .swiper-button-next,
.custom-swiper-${key} .swiper-button-prev {
color: #d32f2f;
width: 25px;
height: 25px;
overflow: 'hidden';
padding:5px;
display: 'flex';
align-items: 'center';
justify-content: 'center';
background: rgba(255, 255, 255, 0.9);
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.custom-swiper-${key} .swiper-button-next::after,
.custom-swiper-${key} .swiper-button-prev::after {
font-size: 8px;
font-weight: bold;
width:20px;
}
.custom-swiper-${key} .swiper-pagination-bullet-active {
background-color: #d32f2f;
}
`}
</style>
<Swiper
className={`custom-swiper-${key}`}
modules={[Navigation, Pagination]}
spaceBetween={15}
slidesPerView={1}
navigation
pagination={{ clickable: true }}
style={{ padding: "4px 4px 30px 4px" }}
>
{value.map((sibling, idx) => (
<SwiperSlide key={idx}>
<Card variant="outlined"
sx={{ bgcolor: '#fff', height: '100%' }}>
<CardContent sx={{ p: 2, '&:last-child': { pb: 2 } }}>
{Object.entries(sibling).map(([sKey, sVal]) => {
if (sKey === 'id' || sKey.endsWith('_id') || sKey.endsWith('Id') || sKey === 'created_at' || sKey === 'updated_at') return null;
let displayValue = sVal;
if (sKey === 'haveChildrens' || sKey === 'have_childrens' || sKey === 'hasChildren' || sKey === 'has_children') {
if (sVal === true || sVal === 1 || sVal === '1' || sVal === 'Yes') {
displayValue = 'Yes';
} else if (sVal === false || sVal === 0 || sVal === '0' || sVal === 'No') {
displayValue = 'No';
}
}
return (
<Box key={sKey} display="grid" gridTemplateColumns="45% 55%" gap={1} mb={0.8} alignItems="start">
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 500 }}>
{sKey
.replace(/([A-Z])/g, ' $1')
.replace(/_/g, ' ')
.replace(/\b\w/g, (l) => l.toUpperCase())
.trim()}
</Typography>
<Typography variant="body2" fontWeight={600} sx={{ wordBreak: 'break-word' }}>
{displayValue ?? '-'}
</Typography>
</Box>
)})}
</CardContent>
</Card>
</SwiperSlide>
))}
</Swiper>
</Box>
);
}
// Convert camelCase or camel_Snake_case to readable words
const formattedKey = key
.replace(/([A-Z])/g, ' $1')
.replace(/_/g, ' ')
.replace(/\b\w/g, (l) => l.toUpperCase())
.trim();
const content = renderValue(key, value);
return (
<Box
key={key}
py={0.7}
// borderBottom="1px solid #e0e0e0"
sx={{
display: "grid",
gridTemplateColumns: {
xs: "1fr",
sm: "1fr 1fr",
},
gap: "10px",
alignItems: "center",
}}
>
<Typography color="text.secondary" sx={{ fontWeight: 500 }}>
{formattedKey}:
</Typography>
{/* Special Case: Profiles Image Preview */}
{key === "profiles" || key === "profile" || key === "profile_images" || key === "images" ? (
content
) : value ? (
<Typography
sx={{
fontWeight: 600,
wordBreak: "break-word",
textAlign: "left",
}}
>
{content}
</Typography>
) : (
<Box
display="flex"
alignItems="center"
gap={1}
sx={{ color: "#888", fontStyle: "italic" }}
>
<Info size={16} />
<Typography>No data available</Typography>
</Box>
)}
</Box>
);
})}
<CardContent>
<Box mb={2}>
{/* Photos Preview */}
<Typography color="text.secondary" sx={{ fontWeight: 600, mb: 1, fontSize: '0.75rem' }}>Profile Photos:</Typography>
<Box display="flex" gap={1} flexWrap="wrap">
{(pInfo.images || pInfo.profile_images || pInfo.profiles || []).map((imgObj, index) => {
const src = typeof imgObj === "string" ? imgObj : imgObj?.preview || imgObj?.url;
return src ? (
<img key={index} src={src} alt="Profile" style={{ width: "80px", height: "80px", objectFit: "cover", borderRadius: "50%", border: "1px solid #ccc" }} />
) : null;
})}
{!(pInfo.images || pInfo.profile_images || pInfo.profiles || []).length && <Typography variant="caption">No photos</Typography>}
</Box>
</Box>
<DetailRow label="Profile Created For" value={pInfo.profile_for} />
<DetailRow label="Gender" value={pInfo.gender} />
<DetailRow label="Name" value={pInfo.name} />
<DetailRow label="Mobile Number" value={pInfo.mobile || pInfo.mobileNumber || pInfo.mobile_number} />
<DetailRow label="Email Id" value={pInfo.email || pInfo.emailId || pInfo.email_id} />
<DetailRow label="Marital Status" value={getNamesFromIds(pInfo.marital_status, pm?.marital_status, 'marital_status_name')} />
<DetailRow label="Height" value={getNamesFromIds(pInfo.height, pm?.heights, 'height_text')} />
<DetailRow label="Weight" value={pInfo.weight} />
<DetailRow label="Complexion" value={getNamesFromIds(pInfo.complexion, pm?.complexion, 'complexion_name')} />
<DetailRow label="Physical Status" value={getNamesFromIds(pInfo.physical_status, pm?.physicalStatus, 'physical_status_name')} />
<DetailRow label="Religion" value={getNamesFromIds(pInfo.religion, pm?.religion, 'religion_name')} />
<DetailRow label="Caste / Community" value={getNamesFromIds(pInfo.caste, pm?.caste, 'caste_name')} />
<DetailRow label="Sub-Sect" value={getNamesFromIds(pInfo.sub_caste, pm?.subCaste, 'sub_caste_name')} />
<DetailRow label="Willing to marry from same sub-sect" value={pInfo.willing_to_marry} />
<DetailRow label="Inter-Caste Parents" value={pInfo.inter_caste_parents === 1 || pInfo.inter_caste_parents === "1" ? "Yes" : "No"} />
{ (pInfo.inter_caste_parents === 1 || pInfo.inter_caste_parents === "1") && <DetailRow label="Inter-Caste Parents Details" value={pInfo.inter_caste_parents_details} />}
<DetailRow label="Gothram" value={pInfo.gothram} />
<DetailRow label="Do you speak Telugu" value={pInfo.do_you_speak_telugu === 1 || pInfo.do_you_speak_telugu === "1" ? "Yes" : "No"} />
<DetailRow label="About" value={pInfo.about_us} />
<DetailRow label="Language Spoken" value={pInfo.known_languages} />
<DetailRow label="Mother Tongue" value={getNamesFromIds(pInfo.mother_language, pm?.languages, 'language')} />
</CardContent>
</Card>
))}
</div>
{/* 2. Educational & Professional Details */}
<Card variant="outlined" sx={{ borderRadius: 2 }}>
<CardHeader
title={<Typography variant="h6" fontWeight="bold">Educational & Professional Details</Typography>}
action={<IconButton color="primary" onClick={() => onEdit(2)} size="large"><Edit2 size={20} /></IconButton>}
sx={{ padding: "15px 15px", background: "#f5fbff" }}
/>
<CardContent>
<DetailRow label="Highest Qualification" value={getNamesFromIds(eInfo.education, ppm?.education || ppm?.educations, 'education_name')} />
<DetailRow label="Field of Study" value={getNamesFromIds(eInfo.study_field, ppm?.study_fields || ppm?.studyFields, 'study_field_name')} />
<DetailRow label="About" value={eInfo.education_detail || eInfo.educationDetail || eInfo.about} />
<DetailRow label="College Name" value={eInfo.college_name} />
<DetailRow label="Employee type" value={getNamesFromIds(eInfo.employee_type, ppm?.employeeTypes || ppm?.employee_type, 'employee_type_name')} />
<DetailRow label="Occupation" value={getNamesFromIds(eInfo.occupation, ppm?.occupation || ppm?.occupations, 'occupation_name')} />
<DetailRow label="Occupation Details" value={eInfo.occupation_detail || eInfo.occupationDetail} />
<DetailRow label="Organization Name" value={eInfo.company_name || eInfo.organizationName || eInfo.companyName} />
<DetailRow label="Income Currency Type" value={eInfo.income_currency} />
<DetailRow label="Annual Income" value={eInfo.annual_income} />
<SectionTitle title="Work Location" />
<DetailRow label="Country" value={eInfo.work_country_name || eInfo.work_country} />
<DetailRow label="State" value={eInfo.work_state_name || eInfo.work_state} />
<DetailRow label="City" value={eInfo.work_district_name || eInfo.work_city} />
<DetailRow label="Address" value={eInfo.work_location || eInfo.address} />
</CardContent>
</Card>
<Grid item xs={12} sx={{display:"flex", justifyContent:"center"}}>
<Button
variant="contained"
color="success"
size="large"
onClick={handleConfirmSubmit}
sx={{ borderRadius: 2 }}
>
Submit full Completed Data
</Button>
</Grid>
{/* 3. Family Details */}
<Card variant="outlined" sx={{ borderRadius: 2 }}>
<CardHeader
title={<Typography variant="h6" fontWeight="bold">Family Details</Typography>}
action={<IconButton color="primary" onClick={() => onEdit(3)} size="large"><Edit2 size={20} /></IconButton>}
sx={{ padding: "15px 15px", background: "#f5fbff" }}
/>
<CardContent>
<DetailRow label="Father Name" value={fInfo.father_name || fInfo.fatherName} />
<DetailRow label="Father Occupation" value={fInfo.father_occupation || fInfo.father_occupational || fInfo.fatherOccupation || fInfo.fatherOccupational || fInfo.father_occupation_name} />
<DetailRow label="Mother Name" value={fInfo.mother_name || fInfo.motherName} />
<DetailRow label="Mother Occupation" value={fInfo.mother_occupation || fInfo.mother_occupational || fInfo.motherOccupation || fInfo.motherOccupational || fInfo.mother_occupation_name} />
<Dialog
open={openConfirmDialog}
onClose={handleCancelSubmit}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
{/* Siblings */}
{['brothers', 'sisters'].map(type => (
(fInfo[type] && fInfo[type].length > 0) && (
<Box key={type} mt={2} mb={2}>
<Typography color="text.secondary" sx={{ fontWeight: 600, mb: 1, textTransform: 'capitalize' }}>{type} Details:</Typography>
<Swiper modules={[Navigation, Pagination]} spaceBetween={10} slidesPerView={1} navigation pagination={{ clickable: true }} style={{ paddingBottom: '30px' }}>
{fInfo[type].map((sib, i) => (
<SwiperSlide key={i}>
<Card variant="outlined" sx={{ p: 2, bgcolor: '#f9f9f9' }}>
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 'bold' }}>{type.slice(0, -1)} {i + 1}</Typography>
<DetailRow label="Name" value={sib.name} />
<DetailRow label={type === 'brothers' ? 'Brother Type' : 'Sister Type'} value={sib.type} />
<DetailRow label="Occupation" value={sib.occupation || sib.occupational || sib.occupation_name} />
<DetailRow label="Marital Status" value={sib.marital_status || sib.maritalStatus || sib.marital_status_id} />
<DetailRow label="Has Children" value={sib.has_children || sib.hasChildren || sib.thereHaveChildren} />
<DetailRow label="Additional Details" value={sib.details || sib.additionalDetails || sib.additional_details} />
</Card>
</SwiperSlide>
))}
</Swiper>
</Box>
)
))}
<DetailRow label="Family Status" value={fInfo.family_status || fInfo.familyStatus} />
<DetailRow label="Native Place" value={fInfo.native_place || fInfo.nativePlace} />
<SectionTitle title="Location Living" />
<DetailRow label="Country Living" value={fInfo.family_country_name || fInfo.living_country_name} />
<DetailRow label="Residing State" value={fInfo.family_state_name || fInfo.living_state_name} />
<DetailRow label="Residing City" value={fInfo.family_district_name || fInfo.family_city || fInfo.living_city_name} />
<DetailRow label="Address" value={fInfo.address} />
<DetailRow label="Expectations / Requirements Details" value={fInfo.expectation_details || fInfo.expectationDetails} />
<DetailRow label="Willing to go abroad" value={fInfo.willing_to_go_abroad || fInfo.willingToGoAbroad} />
</CardContent>
</Card>
{/* 4. Lifestyle & Horoscope */}
<Card variant="outlined" sx={{ borderRadius: 2 }}>
<CardHeader
title={<Typography variant="h6" fontWeight="bold">Lifestyle & Horoscope</Typography>}
action={<IconButton color="primary" onClick={() => onEdit(4)} size="large"><Edit2 size={20} /></IconButton>}
sx={{ padding: "15px 15px", background: "#f5fbff" }}
/>
<CardContent>
<DetailRow label="Time of Birth" value={lInfo.time_of_birth_formated || lInfo.tob} />
<DetailRow label="Place of Birth" value={lInfo.place_of_birth || lInfo.placeOfBirth} />
<DetailRow label="Birth Star" value={lInfo.star_name || lInfo.star} />
<DetailRow label="Padham" value={lInfo.patham} />
<DetailRow label="Rasi" value={lInfo.raasi_name || lInfo.raasi} />
<DetailRow label="Lagnam" value={lInfo.lagnam_name || lInfo.lagnam} />
<DetailRow label="Panjangam" value={lInfo.panjangam_type} />
<DetailRow label="Date of Birth" value={lInfo.date_of_birth_formated || lInfo.dob} />
<DetailRow label="Dasa Balance" value={lInfo.dasa_balance} />
<DetailRow label="Dasa Duration" value={(lInfo.dasa_years || lInfo.dasa_months || lInfo.dasa_days) ? `${lInfo.dasa_years || 0} Years, ${lInfo.dasa_months || 0} Months, ${lInfo.dasa_days || 0} Days` : "-"} />
{/* Horoscope Charts */}
{lInfo.horoscope && (
<Box mt={3}>
<Grid container spacing={2}>
<Grid size={{ xs: 12, sm: 6 }}>
{renderChartGrid((i) => lInfo.horoscope[`graha_${i}`]?.split(',') || (lInfo.graha && lInfo.graha[i]) || [], 'Rasi')}
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
{renderChartGrid((i) => lInfo.horoscope[`amsam_${i}`]?.split(',') || (lInfo.amsam && lInfo.amsam[i]) || [], 'Navamsam')}
</Grid>
</Grid>
</Box>
)}
</CardContent>
</Card>
{/* 5. What am I looking for in a Partner */}
<Card variant="outlined" sx={{ borderRadius: 2 }}>
<CardHeader
title={<Typography variant="h6" fontWeight="bold">What am I looking for in a Partner</Typography>}
action={<IconButton color="primary" onClick={() => onEdit(5)} size="large"><Edit2 size={20} /></IconButton>}
sx={{ padding: "15px 15px", background: "#f5fbff" }}
/>
<CardContent>
<DetailRow label="Age Range" value={(prInfo.preferred_age_from || prInfo.age_from) && (prInfo.preferred_age_to || prInfo.age_to) ? `${prInfo.preferred_age_from || prInfo.age_from} - ${prInfo.preferred_age_to || prInfo.age_to}` : "-"} />
<DetailRow label="Height" value={(getNamesFromIds(prInfo.preferred_height_from_id || prInfo.height_from, ppm?.heights, 'height_text') && getNamesFromIds(prInfo.preferred_height_to_id || prInfo.height_to, ppm?.heights, 'height_text')) ? `${getNamesFromIds(prInfo.preferred_height_from_id || prInfo.height_from, ppm?.heights, 'height_text')} - ${getNamesFromIds(prInfo.preferred_height_to_id || prInfo.height_to, ppm?.heights, 'height_text')}` : "-"} />
<DetailRow label="Marital Status" value={getNamesFromIds(prInfo.preferred_marital_statuses || prInfo.marital_statuses || prInfo.preferred_marital_status || prInfo.marital_status, ppm?.maritalStatus || ppm?.marital_status, 'marital_status_name')} />
<DetailRow label="Birth Star" value={getNamesFromIds(prInfo.preferred_birth_stars || prInfo.birth_stars || prInfo.preferred_birth_star || prInfo.birth_star, ppm?.stars || ppm?.star, 'star_name')} />
<DetailRow label="Caste" value={getNamesFromIds(prInfo.preferred_castes || prInfo.castes || prInfo.preferred_caste || prInfo.caste, ppm?.caste || ppm?.castes, 'caste_name')} />
<DetailRow label="Sub - Sect" value={getNamesFromIds(prInfo.preferred_sub_castes || prInfo.sub_castes || prInfo.preferred_sub_caste || prInfo.sub_caste, ppm?.sub_caste || ppm?.sub_castes, 'sub_caste_name')} />
<DetailRow label="Qualification" value={getNamesFromIds(prInfo.preferred_educations || prInfo.educations || prInfo.preferred_education || prInfo.education, ppm?.education || ppm?.educations, 'education_name')} />
<DetailRow label="Occupation" value={getNamesFromIds(prInfo.preferred_occupations || prInfo.occupations || prInfo.preferred_occupation || prInfo.occupation, ppm?.occupation || ppm?.occupations, 'occupation_name')} />
<DetailRow label="Employee Type" value={getNamesFromIds(prInfo.preferred_employee_types || prInfo.employee_types || prInfo.preferred_employee_type || prInfo.employee_type, ppm?.employeeTypes || ppm?.employee_type, 'employee_type_name')} />
<SectionTitle title="Prefer Annual Income" />
{(prInfo.preferred_currencies || prInfo.currencies || []).includes("INR") && (
<DetailRow label="Prefer Annual Income (INR)" value={(prInfo.preferred_inr_from || prInfo.inr_from || prInfo.preferred_inr_range) ? `From : ${prInfo.preferred_inr_from || prInfo.inr_from || ""} \nTo : ${prInfo.preferred_inr_to || prInfo.inr_to || ""}` : "-"} />
)}
{(prInfo.preferred_currencies || prInfo.currencies || []).includes("USD") && (
<DetailRow label="Prefer Annual Income (USD)" value={(prInfo.preferred_usd_from || prInfo.usd_from || prInfo.preferred_usd_range) ? `From : ${prInfo.preferred_usd_from || prInfo.usd_from || ""} \nTo : ${prInfo.preferred_usd_to || prInfo.usd_to || ""}` : "-"} />
)}
<DetailRow label="State" value={getNamesFromIds(prInfo.preferred_states || prInfo.states || prInfo.preferred_state || prInfo.state, ppm?.states, 'state_name')} />
<DetailRow label="City" value={getNamesFromIds(prInfo.preferred_districts || prInfo.districts || prInfo.preferred_district || prInfo.district, ppm?.districts, 'district_name')} />
</CardContent>
</Card>
</>
)}
</div>
<Grid size={12} sx={{ display: "flex", justifyContent: "center" }}>
<Button
variant="contained"
color="success"
size="large"
onClick={handleConfirmSubmit}
sx={{ borderRadius: 2 }}
>
<DialogTitle id="alert-dialog-title">
{"Confirm Submission"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Once you submit your details, you will not be able to edit the following fields: Place of Birth, Date of Birth, Rasi and Navamsam.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCancelSubmit}>Cancel</Button>
<Button onClick={handleProceedSubmit} autoFocus>
OK
</Button>
</DialogActions>
</Dialog>
Submit Full Completed Data
</Button>
</Grid>
<Dialog
open={openConfirmDialog}
onClose={handleCancelSubmit}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Confirm Submission"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Once you submit your details, you will not be able to edit the following fields: Place of Birth, Date of Birth, Rasi and Navamsam.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCancelSubmit}>Cancel</Button>
<Button onClick={handleProceedSubmit} autoFocus>OK</Button>
</DialogActions>
</Dialog>
</Box>
);
};
File diff suppressed because it is too large. Load diff
+411 -190
View File
@@ -84,15 +84,24 @@ const STEP_FIELD_ORDER = {
"familyStatus",
"nativePlace",
],
4: ["diets", "hobbies", "dob", "tob", "placeOfBirth"],
4: ["dob", "tob", "placeOfBirth", "raasi", "star", "patham", "lagnam", "panjangam_type", "dasa_balance", "dasa_years", "dasa_months", "dasa_days"],
5: [
"ageRange",
"age_from",
"age_to",
"height_from",
"height_to",
"marital_statuses",
"birth_stars",
"castes",
"subCastes",
"occupations",
"sub_castes",
"educations",
"hobbies",
"annualIncome",
"occupations",
"employee_types",
"currencies",
"inr_from",
"inr_to",
"usd_from",
"usd_to",
"states",
"districts",
],
@@ -392,18 +401,23 @@ const [completedSteps, setCompletedSteps] = useState([]);
if (!firstKey) return;
setTimeout(() => {
const element = document.getElementById(firstKey) ||
document.querySelector(`[name="${firstKey}"]`) ||
document.querySelector(`[aria-labelledby~="${firstKey}-label"]`);
const element =
document.getElementById(firstKey) ||
document.querySelector(`[name="${firstKey}"]`) ||
document.querySelector(`[id^="${firstKey}"]`) ||
document.querySelector(`[name^="${firstKey}"]`) ||
document.querySelector(`[aria-labelledby~="${firstKey}-label"]`);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "center" });
// Try to find a focusable element within the container
const focusable = element.querySelector('input:not([type="hidden"]), select, textarea, [role="combobox"], [role="button"]') || element;
const focusable =
element.querySelector('input:not([type="hidden"]), select, textarea, [role="combobox"], [role="button"]') ||
element;
if (focusable && typeof focusable.focus === "function") {
focusable.focus();
setTimeout(() => focusable.focus(), 300);
}
}
}, 100);
@@ -460,32 +474,36 @@ const [completedSteps, setCompletedSteps] = useState([]);
useEffect(() => {
const processData = async () => {
if (personalDetailsData?.status === "success" && personalDetailsData?.personal_details) {
const isSuccess = personalDetailsData?.status === "success" || personalDetailsData?.success === true;
if (isSuccess && personalDetailsData?.personal_details) {
const pd = personalDetailsData.personal_details;
setIsStep1Update(true);
const rawImages = pd.profile_images || pd.images || [];
const rawImages = pd.profile_images || pd.profiles || pd.images || [];
const mappedImages = await Promise.all(
rawImages.map(async (url, index) => {
const imageUrl = typeof url === "string" ? url : url?.url;
rawImages.map(async (img, index) => {
const originalUrl = typeof img === "string" ? img : (img.url || img.preview || img.image_url);
if (!originalUrl) return null;
// Rewrite URL to use proxy in development to avoid CORS
const imageUrl = (import.meta.env.DEV && originalUrl.startsWith('https://www.thirukalyanam.amrithaa.net/backend'))
? originalUrl.replace('https://www.thirukalyanam.amrithaa.net/backend', '/backend')
: originalUrl;
const fileName = (typeof img === "object" && img.name) || `image-${index}.jpg`;
let file = null;
let mimeType = "image/jpeg";
let fileName = `image-${index}.jpg`;
try {
if (imageUrl) {
const response = await fetch(imageUrl);
const blob = await response.blob();
if (blob.type) mimeType = blob.type;
const ext = mimeType.split("/")[1] || "jpg";
fileName = `image-${index}.${ext}`;
file = new File([blob], fileName, { type: mimeType });
}
const response = await fetch(imageUrl);
const blob = await response.blob();
if (blob.type) mimeType = blob.type;
file = new File([blob], fileName, { type: mimeType });
} catch (error) {
console.error("Error converting image URL to File:", error);
}
return {
id: `server-${index}`,
id: (typeof img === "object" && img.id) || `server-${index}`,
preview: imageUrl,
imageUrl: imageUrl,
file: file,
@@ -494,42 +512,61 @@ const [completedSteps, setCompletedSteps] = useState([]);
valid: true,
};
})
);
).then(res => res.filter(Boolean));
const formattedDob = pd.dob ? pd.dob.split("T")[0] : "";
const dobVal = pd.dob || pd.date_of_birth || "";
const formattedDob = dobVal ? dobVal.split("T")[0] : "";
dispatch(
updatePersonalDetails({
name: pd.name || "",
mobile: pd.mobile || "",
email: pd.email || "",
gender: pd.gender || "",
dob: formattedDob,
height: pd.height_id || "",
weight: pd.weight || "",
marital_status: pd.marital_status_id || "",
religion: pd.religion_id || "",
profile_for: pd.profile_for_id || "",
caste: pd.caste_id || "",
sub_caste: pd.sub_caste_id || "",
willing_to_marry: pd.willing_to_marry || "",
inter_caste_parents: pd.inter_caste_parents || 0,
inter_caste_parents_details: pd.inter_caste_parents_details || "",
gothram: pd.gothram || "",
do_you_speak_telugu: pd.do_you_speak_telugu || 0,
about_us: pd.about_us || "",
known_languages: pd.known_language_ids || [],
mother_language: pd.mother_language_id || "",
complexion: pd.complexion_id || "",
physical_status: pd.physical_status_id || "",
raasi: pd.raasi_id || "",
star: pd.star_id || "",
state: pd.state_id || "",
city: pd.district_id || "",
pincode: pd.pincode || "",
profiles: mappedImages,
})
);
const updates = {};
if (pd.name) updates.name = pd.name;
const mobile = pd.mobile || pd.mobileNumber || pd.mobile_number;
if (mobile) updates.mobile = mobile;
const email = pd.email || pd.emailId || pd.email_id;
if (email) updates.email = email;
if (pd.gender) updates.gender = pd.gender;
if (formattedDob) updates.dob = formattedDob;
const height = pd.height_id || pd.height;
if (height) updates.height = height;
if (pd.weight) updates.weight = pd.weight;
const marital_status = pd.marital_status_id || pd.marital_status;
if (marital_status) updates.marital_status = marital_status;
const religion = pd.religion_id || pd.religion;
if (religion) updates.religion = religion;
const profile_for = pd.profile_for_id || pd.profile_for;
if (profile_for) updates.profile_for = profile_for;
const caste = pd.caste_id || pd.caste;
if (caste) updates.caste = caste;
const sub_caste = pd.sub_caste_id || pd.sub_caste;
if (sub_caste) updates.sub_caste = sub_caste;
if (pd.willing_to_marry) updates.willing_to_marry = pd.willing_to_marry;
if (pd.inter_caste_parents !== undefined) updates.inter_caste_parents = pd.inter_caste_parents ?? 0;
if (pd.inter_caste_parents_details) updates.inter_caste_parents_details = pd.inter_caste_parents_details;
const gothram = pd.gothram || pd.gothram_id;
if (gothram) updates.gothram = gothram;
if (pd.do_you_speak_telugu !== undefined) updates.do_you_speak_telugu = pd.do_you_speak_telugu ?? 0;
if (pd.about_us) updates.about_us = pd.about_us;
const known_languages = pd.known_language_ids || pd.known_languages;
if (known_languages) updates.known_languages = known_languages;
const mother_language = pd.mother_language_id || pd.mother_language;
if (mother_language) updates.mother_language = mother_language;
const complexion = pd.complexion_id || pd.complexion;
if (complexion) updates.complexion = complexion;
const physical_status = pd.physical_status_id || pd.physical_status;
if (physical_status) updates.physical_status = physical_status;
const raasi = pd.raasi_id || pd.raasi;
if (raasi) updates.raasi = raasi;
const star = pd.star_id || pd.star;
if (star) updates.star = star;
const state = pd.state_id || pd.state;
if (state) updates.state = state;
const city = pd.district_id || pd.city;
if (city) updates.city = city;
if (pd.pincode) updates.pincode = pd.pincode;
if (mappedImages && mappedImages.length > 0) updates.profiles = mappedImages;
if (Object.keys(updates).length > 0) {
dispatch(updatePersonalDetails(updates));
}
}
};
processData();
@@ -546,31 +583,64 @@ const [completedSteps, setCompletedSteps] = useState([]);
enabled: isAuth || shouldHideStepper,
retry: false,
refetchOnWindowFocus: false,
refetchOnMount: true,
});
useEffect(() => {
if (educationalData?.status === "success" && educationalData?.educational_details) {
useEffect(() => {
const isSuccess = educationalData?.status === "success" || educationalData?.success === true;
if (isSuccess && educationalData?.educational_details) {
const ed = educationalData.educational_details;
dispatch(
updateEducationalDetails({
study_field: ed.study_field_id || "",
education: ed.education_id || "",
education_detail: ed.education_detail || "",
college_name: ed.college_name || "",
employee_type: ed.employee_type_id || "",
occupation: ed.occupation_id || "",
occupation_detail: ed.occupation_detail || "",
company_name: ed.company_name || "",
income_currency: ed.income_currency || "INR",
annual_income: ed.annual_income || "",
work_country: ed.work_country_id || "",
work_state: ed.work_state_id || "",
work_district: ed.work_district_id || "",
work_city: ed.work_city || "",
address: ed.address || ed.work_location || "",
})
);
const updates = {};
const study_field = ed.study_field_id || ed.study_field || ed.education_id;
if (study_field) updates.study_field = study_field;
const education = ed.education_id || ed.education || ed.qualification_id;
if (education) updates.education = education;
const education_detail = ed.education_detail || ed.education_details || ed.educationDetail;
if (education_detail) updates.education_detail = education_detail;
const college_name = ed.college_name || ed.collegeName || ed.college;
if (college_name) updates.college_name = college_name;
const employee_type = ed.employee_type_id || ed.employee_type || ed.employeeType;
if (employee_type) updates.employee_type = employee_type;
const occupation = ed.occupation_id || ed.occupation || ed.occupational;
if (occupation) updates.occupation = occupation;
const occupation_detail = ed.occupation_detail || ed.occupationDetail || ed.occupation_details;
if (occupation_detail) updates.occupation_detail = occupation_detail;
const company_name = ed.company_name || ed.companyName || ed.organization_name;
if (company_name) updates.company_name = company_name;
const income_currency = ed.income_currency || ed.currency || "INR";
if (income_currency) updates.income_currency = income_currency;
const annual_income = ed.annual_income || ed.annual_income_id || ed.income;
if (annual_income) updates.annual_income = annual_income;
const work_country = ed.work_country_id || ed.work_country || ed.country_id;
if (work_country) updates.work_country = work_country;
const work_state = ed.work_state_id || ed.work_state || ed.state_id;
if (work_state) updates.work_state = work_state;
const work_district = ed.work_district_id || ed.work_district || ed.district_id;
if (work_district) updates.work_district = work_district;
const work_city = ed.work_city || ed.city || ed.work_location;
if (work_city) updates.work_city = work_city;
const address = ed.address || ed.work_location || "";
if (address) updates.address = address;
if (Object.keys(updates).length > 0) {
dispatch(updateEducationalDetails(updates));
}
}
}, [educationalData, dispatch]);
@@ -586,49 +656,59 @@ const {data:familyData} = useQuery({
enabled: isAuth || shouldHideStepper,
retry:false,
refetchOnWindowFocus:false,
refetchOnMount: true,
});
useEffect(() => {
if (familyData?.status === "success" && familyData?.family_details) {
const fd = familyData.family_details;
dispatch(
updateFamilyDetails({
fatherName: fd.father_name || "",
fatherOccupation: fd.father_occupation || "",
motherName: fd.mother_name || "",
motherOccupation: fd.mother_occupation || "",
familyStatus: fd.family_status_id || "",
nativePlace: fd.native_place || "",
familyCountry: fd.family_country_id || "",
familyState: fd.family_state_id || "",
familyDistrict: fd.family_district_id || "",
familyCity: fd.family_city || "",
address: fd.address || "",
expectationDetails: fd.expectation_details || "",
willingToGoAbroad: fd.willing_to_go_abroad || "",
brotherCount: fd.brother_count || 0,
sisterCount: fd.sister_count || 0,
brothers: (fd.brothers || []).map((b) => ({
const isSuccess = familyData?.status === "success" || familyData?.success === true;
if (isSuccess && familyData?.family_details) {
const fd = familyData.family_details;
const updates = {};
if (fd.father_name || fd.fatherName) updates.fatherName = fd.father_name || fd.fatherName || "";
if (fd.father_occupation || fd.fatherOccupation || fd.father_occupation_id) updates.fatherOccupation = fd.father_occupation ?? fd.fatherOccupation ?? fd.father_occupation_id ?? "";
if (fd.mother_name || fd.motherName) updates.motherName = fd.mother_name || fd.motherName || "";
if (fd.mother_occupation || fd.motherOccupation || fd.mother_occupation_id) updates.motherOccupation = fd.mother_occupation ?? fd.motherOccupation ?? fd.mother_occupation_id ?? "";
if (fd.family_status_id || fd.familyStatus || fd.family_status) updates.familyStatus = fd.family_status_id || fd.familyStatus || fd.family_status || "";
if (fd.native_place || fd.nativePlace) updates.nativePlace = fd.native_place || fd.nativePlace || "";
if (fd.brother_count !== undefined) updates.brotherCount = fd.brother_count || 0;
if (fd.sister_count !== undefined) updates.sisterCount = fd.sister_count || 0;
if (fd.family_country_id || fd.family_country_name || fd.familyCountry) updates.familyCountry = fd.family_country_id || fd.familyCountry || "";
if (fd.family_state_id || fd.family_state_name || fd.familyState) updates.familyState = fd.family_state_id || fd.familyState || "";
if (fd.family_district_id || fd.family_district_name || fd.familyDistrict) updates.familyDistrict = fd.family_district_id || fd.familyDistrict || "";
if (fd.family_city || fd.familyCity) updates.familyCity = fd.family_city || fd.familyCity || "";
if (fd.address) updates.address = fd.address || "";
if (fd.expectation_details || fd.expectationDetails) updates.expectationDetails = fd.expectation_details || fd.expectationDetails || "";
if (fd.willing_to_go_abroad !== undefined) updates.willingToGoAbroad = fd.willing_to_go_abroad || "";
if (fd.brothers || fd.brother_details) {
updates.brothers = (fd.brothers || fd.brother_details || []).map((b) => ({
name: b.name || "",
occupation: b.occupation_name || "",
maritalStatus: b.marital_status || "",
occupation: b.occupation || b.occupational || b.occupation_name || "",
maritalStatus: b.marital_status || b.maritalStatus || b.marital_status_id || "",
type: b.type || "",
hasChildren: b.has_children || "",
details: b.additional_details || ""
})),
sisters: (fd.sisters || []).map((s) => ({
hasChildren: b.has_children || b.hasChildren || b.thereHaveChildren || "",
details: b.details || b.additional_details || b.additionalDetails || "",
}));
}
if (fd.sisters || fd.sister_details) {
updates.sisters = (fd.sisters || fd.sister_details || []).map((s) => ({
name: s.name || "",
occupation: s.occupation_name || "",
maritalStatus: s.marital_status || "",
occupation: s.occupation || s.occupational || s.occupation_name || "",
maritalStatus: s.marital_status || s.maritalStatus || s.marital_status_id || "",
type: s.type || "",
hasChildren: s.has_children || "",
details: s.additional_details || ""
})),
})
);
}
}, [familyData, dispatch]);
hasChildren: s.has_children || s.hasChildren || s.thereHaveChildren || "",
details: s.details || s.additional_details || s.additionalDetails || "",
}));
}
if (Object.keys(updates).length > 0) {
dispatch(updateFamilyDetails(updates));
}
}
}, [familyData, dispatch]);
// Fetch Lifestyle Details
const { data: lifestyleData } = useQuery({
@@ -644,7 +724,8 @@ useEffect(() => {
});
useEffect(() => {
if (lifestyleData?.status === "success" && lifestyleData?.lifestyle_details) {
const isSuccess = lifestyleData?.status === "success" || lifestyleData?.success === true;
if (isSuccess && lifestyleData?.lifestyle_details) {
const ld = lifestyleData.lifestyle_details;
const horoscope = ld.horoscope || {};
@@ -653,18 +734,35 @@ useEffect(() => {
for (let i = 1; i <= 12; i++) {
const key = `${prefix}_${i}`;
const val = horoscope[key];
chart[i] = val ? val.split(",") : [];
chart[i] = val ? (Array.isArray(val) ? val : val.split(",")) : [];
}
return chart;
};
const dobVal = ld.dob || ld.date_of_birth || "";
const formattedDob = dobVal ? dobVal.split("T")[0] : "";
let formattedTob = ld.tob || ld.time_of_birth || "";
if (!formattedTob && ld.time_of_birth_formated) {
if (ld.time_of_birth_formated.includes(":")) {
formattedTob = ld.time_of_birth_formated.substring(0, 5);
}
}
dispatch(
updateLifestyleDetails({
diets: ld.diet_id || "",
hobbies: ld.hobbies_ids || [],
dob: ld.time_of_birth ? ld.time_of_birth.split("T")[0] : "",
tob: ld.time_of_birth_formated ? ld.time_of_birth_formated.substring(0, 5) : "",
placeOfBirth: ld.place_of_birth || "",
dob: formattedDob,
tob: formattedTob,
placeOfBirth: ld.place_of_birth || ld.placeOfBirth || "",
raasi: ld.raasi_id || horoscope.raasi_id || ld.raasi || "",
star: ld.star_id || horoscope.star_id || ld.star || "",
patham: ld.patham || horoscope.patham || "",
lagnam: ld.lagnam_id || ld.lagnam || horoscope.lagnam || "",
panjangam_type: ld.panjangam_type || horoscope.panjangam_type || "",
dasa_balance: ld.dasa_balance || horoscope.dasa_balance || "",
dasa_years: ld.dasa_years || horoscope.dasa_years || "",
dasa_months: ld.dasa_months || horoscope.dasa_months || "",
dasa_days: ld.dasa_days || horoscope.dasa_days || "",
graha: mapChart("graha"),
amsam: mapChart("amsam"),
})
@@ -686,21 +784,86 @@ useEffect(() => {
});
useEffect(() => {
if (partnerData?.status === "success" && partnerData?.partner_preferences) {
const isSuccess = partnerData?.status === "success" || partnerData?.success === true;
if (isSuccess && partnerData?.partner_preferences) {
const pp = partnerData.partner_preferences;
dispatch(
updatePartnerPreferences({
ageRange: pp.preferred_age_range_id || "",
annualIncome: pp.preferred_annual_income_id || "",
castes: pp.preferred_castes_ids || [],
subCastes: pp.preferred_sub_castes_ids || [],
occupations: pp.preferred_occupations_ids || [],
educations: pp.preferred_educations_ids || [],
hobbies: pp.preferred_hobbies_ids || [],
states: pp.preferred_states_ids || [],
districts: pp.preferred_districts_ids || [],
})
);
const updates = {};
const age_from = pp.preferred_age_from ?? pp.age_from ?? pp.preferred_age_from_id ?? pp.age_from_id;
if (age_from !== undefined && age_from !== null) updates.age_from = age_from;
const age_to = pp.preferred_age_to ?? pp.age_to ?? pp.preferred_age_to_id ?? pp.age_to_id;
if (age_to !== undefined && age_to !== null) updates.age_to = age_to;
const height_from = pp.preferred_height_from_id ?? pp.height_from_id ?? pp.preferred_height_from ?? pp.height_from;
if (height_from !== undefined && height_from !== null) updates.height_from = height_from;
const height_to = pp.preferred_height_to_id ?? pp.height_to_id ?? pp.preferred_height_to ?? pp.height_to;
if (height_to !== undefined && height_to !== null) updates.height_to = height_to;
const mapMulti = (val) => {
if (!val) return [];
if (Array.isArray(val)) return val;
return String(val).split(",").map(v => v.trim()).filter(Boolean);
};
if (pp.preferred_marital_status_ids || pp.marital_status_ids || pp.preferred_marital_statuses || pp.marital_status) {
const val = mapMulti(pp.preferred_marital_status_ids || pp.marital_status_ids || pp.preferred_marital_statuses || pp.marital_status);
if (val.length > 0) updates.marital_statuses = val;
}
if (pp.preferred_birth_star_ids || pp.birth_star_ids || pp.preferred_birth_stars || pp.birth_star) {
const val = mapMulti(pp.preferred_birth_star_ids || pp.birth_star_ids || pp.preferred_birth_stars || pp.birth_star);
if (val.length > 0) updates.birth_stars = val;
}
if (pp.preferred_castes_ids || pp.castes_ids || pp.preferred_castes || pp.caste) {
const val = mapMulti(pp.preferred_castes_ids || pp.castes_ids || pp.preferred_castes || pp.caste);
if (val.length > 0) updates.castes = val;
}
if (pp.preferred_sub_castes_ids || pp.sub_castes_ids || pp.preferred_sub_castes || pp.sub_caste) {
const val = mapMulti(pp.preferred_sub_castes_ids || pp.sub_castes_ids || pp.preferred_sub_castes || pp.sub_caste);
if (val.length > 0) updates.sub_castes = val;
}
if (pp.preferred_occupations_ids || pp.occupations_ids || pp.preferred_occupations || pp.occupation) {
const val = mapMulti(pp.preferred_occupations_ids || pp.occupations_ids || pp.preferred_occupations || pp.occupation);
if (val.length > 0) updates.occupations = val;
}
if (pp.preferred_educations_ids || pp.educations_ids || pp.preferred_educations || pp.education) {
const val = mapMulti(pp.preferred_educations_ids || pp.educations_ids || pp.preferred_educations || pp.education);
if (val.length > 0) updates.educations = val;
}
if (pp.preferred_employee_type_ids || pp.employee_type_ids || pp.preferred_employee_types || pp.employee_type) {
const val = mapMulti(pp.preferred_employee_type_ids || pp.employee_type_ids || pp.preferred_employee_types || pp.employee_type);
if (val.length > 0) updates.employee_types = val;
}
if (pp.preferred_currencies || pp.currencies) {
const val = mapMulti(pp.preferred_currencies || pp.currencies);
if (val.length > 0) updates.currencies = val;
}
const inr_from = pp.preferred_inr_from ?? pp.inr_from ?? pp.preferred_inr_from_id;
if (inr_from !== undefined && inr_from !== null) updates.inr_from = inr_from;
const inr_to = pp.preferred_inr_to ?? pp.inr_to ?? pp.preferred_inr_to_id;
if (inr_to !== undefined && inr_to !== null) updates.inr_to = inr_to;
const usd_from = pp.preferred_usd_from ?? pp.usd_from ?? pp.preferred_usd_from_id;
if (usd_from !== undefined && usd_from !== null) updates.usd_from = usd_from;
const usd_to = pp.preferred_usd_to ?? pp.usd_to ?? pp.preferred_usd_to_id;
if (usd_to !== undefined && usd_to !== null) updates.usd_to = usd_to;
if (pp.preferred_states_ids || pp.states_ids || pp.preferred_states || pp.state) {
const val = mapMulti(pp.preferred_states_ids || pp.states_ids || pp.preferred_states || pp.state);
if (val.length > 0) updates.states = val;
}
if (pp.preferred_districts_ids || pp.districts_ids || pp.preferred_districts || pp.district) {
const val = mapMulti(pp.preferred_districts_ids || pp.districts_ids || pp.preferred_districts || pp.district);
if (val.length > 0) updates.districts = val;
}
if (Object.keys(updates).length > 0) {
dispatch(updatePartnerPreferences(updates));
}
}
}, [partnerData, dispatch]);
@@ -722,6 +885,7 @@ useEffect(() => {
"marital_status",
"profile_for",
"caste",
"sub_caste",
"email",
"mother_language",
"complexion",
@@ -795,7 +959,7 @@ useEffect(() => {
];
required.forEach((field) => {
if (!familyDetails[field]) {
// newErrors[field] = "This field is required";
// newErrors[field] = `${label} is required`;
const label = field
.replace(/([A-Z])/g, " $1")
.replace(/^./, (str) => str.toUpperCase());
@@ -810,7 +974,7 @@ useEffect(() => {
newErrors.motherName = "Mother Name must contain only alphabets";
}
} else if (step === 4) {
const required = ["diets", "hobbies", "dob", "tob"];
const required = ["dob", "tob", "placeOfBirth", "raasi", "star"];
required.forEach((field) => {
const value = lifestyleDetails[field];
if (Array.isArray(value)) {
@@ -818,7 +982,10 @@ useEffect(() => {
if (field === "hobbies") {
newErrors[field] = "Hobbies and Interests is required";
} else {
newErrors[field] = "This field is required";
const label = field
.replace(/([A-Z])/g, " $1")
.replace(/^./, (str) => str.toUpperCase());
newErrors[field] = `${label} is required`;
}
}
@@ -830,8 +997,11 @@ useEffect(() => {
else if (field === "tob") {
newErrors[field] = "Time of Birth is required";
}
else if (field === "diets") {
newErrors[field] = "Diet is required";
else if (field === "raasi") {
newErrors[field] = "Raasi is required";
}
else if (field === "star") {
newErrors[field] = "Birth Star is required";
}
else {
@@ -843,33 +1013,53 @@ useEffect(() => {
}
});
} else if (step === 5) {
const required = [
"ageRange",
"castes",
"subCastes",
"occupations",
"educations",
"hobbies",
"annualIncome",
"states",
"districts",
];
// Range Validations
if (partnerPreferences.age_from && partnerPreferences.age_to) {
if (Number(partnerPreferences.age_from) > Number(partnerPreferences.age_to)) {
newErrors.age_from = "From Age cannot be greater than To Age";
newErrors.age_to = "To Age cannot be less than From Age";
}
}
if (partnerPreferences.height_from && partnerPreferences.height_to) {
if (Number(partnerPreferences.height_from) > Number(partnerPreferences.height_to)) {
newErrors.height_from = "From Height cannot be greater than To Height";
newErrors.height_to = "To Height cannot be less than From Height";
}
}
if (partnerPreferences.currencies.includes("INR")) {
const from = parseFloat(partnerPreferences.inr_from);
const to = parseFloat(partnerPreferences.inr_to);
if (!isNaN(from) && !isNaN(to) && from > to) {
newErrors.inr_from = "From Income cannot be greater than To Income";
newErrors.inr_to = "To Income cannot be less than From Income";
}
}
if (partnerPreferences.currencies.includes("USD")) {
const from = parseFloat(partnerPreferences.usd_from);
const to = parseFloat(partnerPreferences.usd_to);
if (!isNaN(from) && !isNaN(to) && from > to) {
newErrors.usd_from = "From Income cannot be greater than To Income";
newErrors.usd_to = "To Income cannot be less than From Income";
}
}
// Required fields
const required = ["age_from", "age_to", "height_from", "height_to", "castes", "educations", "states"];
required.forEach((field) => {
const value = partnerPreferences[field];
let isMissing = false;
if (Array.isArray(value)) {
if (value.length === 0) {
// newErrors[field] = "This field is required";
const label = field
.replace(/([A-Z])/g, " $1")
.replace(/^./, (str) => str.toUpperCase());
newErrors[field] = `${label} is required`;
}
return;
isMissing = value.length === 0;
} else {
isMissing = !value && value !== 0;
}
if (!value) {
// newErrors[field] = "This field is required";
if (isMissing) {
const label = field
.replace(/([A-Z])/g, " $1")
.replace(/_/g, " ")
.replace(/^./, (str) => str.toUpperCase());
newErrors[field] = `${label} is required`;
}
@@ -928,7 +1118,13 @@ useEffect(() => {
if (!isValidFile && item.preview && typeof item.preview === "string") {
try {
const response = await fetch(item.preview);
// Rewrite URL to use proxy in development to avoid CORS
const finalUrl = (import.meta.env.DEV && item.preview.startsWith('https://www.thirukalyanam.amrithaa.net/backend'))
? item.preview.replace('https://www.thirukalyanam.amrithaa.net/backend', '/backend')
: item.preview;
const response = await fetch(finalUrl);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const blob = await response.blob();
const mimeType = blob.type || "image/jpeg";
const ext = mimeType.split("/")[1] || "jpg";
@@ -983,12 +1179,16 @@ useEffect(() => {
formData.append("mother_name", familyDetails.motherName);
formData.append("mother_occupation", familyDetails.motherOccupation || "");
formData.append("family_status", familyDetails.familyStatus);
formData.append("family_status_id", familyDetails.familyStatus);
formData.append("native_place", familyDetails.nativePlace || "");
formData.append("brother_count", familyDetails.brotherCount || 0);
formData.append("sister_count", familyDetails.sisterCount || 0);
formData.append("family_country", familyDetails.familyCountry || "");
formData.append("family_country_id", familyDetails.familyCountry || "");
formData.append("family_state", familyDetails.familyState || "");
formData.append("family_state_id", familyDetails.familyState || "");
formData.append("family_district", familyDetails.familyDistrict || "");
formData.append("family_district_id", familyDetails.familyDistrict || "");
formData.append("family_city", familyDetails.familyCity || "");
formData.append("address", familyDetails.address || "");
formData.append("expectation_details", familyDetails.expectationDetails || "");
@@ -1001,6 +1201,7 @@ useEffect(() => {
formData.append(`brothers[${index}][type]`, brother?.type || "");
formData.append(`brothers[${index}][has_children]`, brother?.hasChildren || "");
formData.append(`brothers[${index}][details]`, brother?.details || "");
formData.append(`brothers[${index}][additional_details]`, brother?.details || "");
});
(familyDetails.sisters || []).forEach((sister, index) => {
@@ -1010,6 +1211,7 @@ useEffect(() => {
formData.append(`sisters[${index}][type]`, sister?.type || "");
formData.append(`sisters[${index}][has_children]`, sister?.hasChildren || "");
formData.append(`sisters[${index}][details]`, sister?.details || "");
formData.append(`sisters[${index}][additional_details]`, sister?.details || "");
});
return formData;
@@ -1020,12 +1222,17 @@ useEffect(() => {
formData.append("dob", lifestyleDetails.dob || "");
formData.append("tob", lifestyleDetails.tob || "");
formData.append("place_of_birth", lifestyleDetails.placeOfBirth || "");
formData.append("diets", lifestyleDetails.diets || "");
(lifestyleDetails.hobbies || []).forEach((id, index) => {
formData.append(`hobbies[${index}]`, id);
});
formData.append("raasi", lifestyleDetails.raasi || "");
formData.append("raasi_id", lifestyleDetails.raasi || "");
formData.append("star", lifestyleDetails.star || "");
formData.append("star_id", lifestyleDetails.star || "");
formData.append("patham", lifestyleDetails.patham || "");
formData.append("lagnam", lifestyleDetails.lagnam || "");
formData.append("panjangam_type", lifestyleDetails.panjangam_type || "");
formData.append("dasa_balance", lifestyleDetails.dasa_balance || "");
formData.append("dasa_years", lifestyleDetails.dasa_years || "");
formData.append("dasa_months", lifestyleDetails.dasa_months || "");
formData.append("dasa_days", lifestyleDetails.dasa_days || "");
const graha = lifestyleDetails.graha || {};
Object.keys(graha).forEach((house) => {
@@ -1048,35 +1255,54 @@ useEffect(() => {
const buildRegisterStep5Payload = () => {
const formData = new FormData();
formData.append("age_range", partnerPreferences.ageRange || "");
formData.append("annual_income", partnerPreferences.annualIncome || "");
formData.append("preferred_age_from", partnerPreferences.age_from || "");
formData.append("preferred_age_to", partnerPreferences.age_to || "");
formData.append("preferred_height_from", partnerPreferences.height_from || "");
formData.append("preferred_height_to", partnerPreferences.height_to || "");
formData.append("preferred_inr_from", partnerPreferences.inr_from || "");
formData.append("preferred_inr_to", partnerPreferences.inr_to || "");
formData.append("preferred_usd_from", partnerPreferences.usd_from || "");
formData.append("preferred_usd_to", partnerPreferences.usd_to || "");
(partnerPreferences.castes || []).forEach((id, index) => {
formData.append(`castes[${index}]`, id);
(partnerPreferences.marital_statuses || []).forEach((id, index) => {
formData.append(`preferred_marital_status_ids[${index}]`, id);
});
(partnerPreferences.subCastes || []).forEach((id, index) => {
formData.append(`sub_castes[${index}]`, id);
(partnerPreferences.employee_types || []).forEach((id, index) => {
formData.append(`preferred_employee_type_ids[${index}]`, id);
});
(partnerPreferences.birth_stars || []).forEach((id, index) => {
formData.append(`preferred_birth_star_ids[${index}]`, id);
});
(partnerPreferences.currencies || []).forEach((curr, index) => {
formData.append(`preferred_currencies[${index}]`, curr);
});
(partnerPreferences.castes || []).forEach((id, index) => {
formData.append(`preferred_castes_ids[${index}]`, id);
});
(partnerPreferences.sub_castes || []).forEach((id, index) => {
formData.append(`preferred_sub_castes_ids[${index}]`, id);
});
(partnerPreferences.occupations || []).forEach((id, index) => {
formData.append(`occupations[${index}]`, id);
formData.append(`preferred_occupations_ids[${index}]`, id);
});
(partnerPreferences.educations || []).forEach((id, index) => {
formData.append(`educations[${index}]`, id);
});
(partnerPreferences.hobbies || []).forEach((id, index) => {
formData.append(`hobbies[${index}]`, id);
formData.append(`preferred_educations_ids[${index}]`, id);
});
(partnerPreferences.states || []).forEach((id, index) => {
formData.append(`states[${index}]`, id);
formData.append(`preferred_states_ids[${index}]`, id);
});
(partnerPreferences.districts || []).forEach((id, index) => {
formData.append(`districts[${index}]`, id);
formData.append(`preferred_districts_ids[${index}]`, id);
});
return formData;
@@ -1256,11 +1482,6 @@ useEffect(() => {
if (!enabledSteps.includes(step)) return;
// If moving backwards during registration, clear the unsaved data of the step we are leaving
if (step < currentStep && !shouldHideStepper) {
dispatch(clearAllStepsFrom(currentStep));
}
setCurrentStep(step);
setErrors({});
window.scrollTo(0, 0);
@@ -1378,7 +1599,7 @@ useEffect(() => {
return required.every(field => familyDetails[field]);
}
if (stepNum === 4) {
const required = ["diets", "hobbies", "dob", "tob"];
const required = ["dob", "tob", "placeOfBirth", "raasi", "star"];
return required.every(field => {
const val = lifestyleDetails[field];
return Array.isArray(val) ? val.length > 0 : !!val;