1449 lines
48 KiB
JavaScript
1449 lines
48 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import { ChevronLeft } from "lucide-react";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import {
|
|
updatePersonalDetails,
|
|
updateEducationalDetails,
|
|
updateFamilyDetails,
|
|
updateLifestyleDetails,
|
|
updatePartnerPreferences,
|
|
clearAllStepsFrom,
|
|
submitForm,
|
|
} from "../redux/registrationFormSlice";
|
|
import PersonalDetailsForm from "./PersonalDetailsForm";
|
|
import EducationalDetailsForm from "./EducationalDetailsForm";
|
|
import FamilyDetailsForm from "./FamilyDetailsForm";
|
|
import LifestyleDetailsForm from "./LifestyleDetailsForm";
|
|
import PartnerPreferencesForm from "./PartnerPreferencesForm";
|
|
import PreviewScreen from "./PreviewScreen";
|
|
import { useLocation, useNavigate } from "react-router-dom";
|
|
import {
|
|
useRegisterStep1,
|
|
useRegisterStep2,
|
|
useRegisterStep3,
|
|
useRegisterStep4,
|
|
useRegisterStep5,
|
|
} from "../hooks/useRegister";
|
|
import axiosInstance, { apiForFiles, setAccessToken } from "../api/axiosInstance";
|
|
import toast from "react-hot-toast";
|
|
import { API_ENDPOINTS } from "../api/apiEndpoints";
|
|
import { isAuthenticated } from "../utills/auth";
|
|
import { getPreviewDetails } from "../api/preview.api";
|
|
const STEP_FIELD_ORDER = {
|
|
1: [
|
|
"profile_for",
|
|
"gender",
|
|
"name",
|
|
"mobile",
|
|
"email",
|
|
"password",
|
|
"confirmPassword",
|
|
"marital_status",
|
|
"height",
|
|
"weight",
|
|
"complexion",
|
|
"physical_status",
|
|
"religion",
|
|
"caste",
|
|
"sub_caste",
|
|
"willing_to_marry",
|
|
"inter_caste_parents",
|
|
"inter_caste_parents_details",
|
|
"gothram",
|
|
"do_you_speak_telugu",
|
|
"about_us",
|
|
"known_languages",
|
|
"mother_language",
|
|
"profiles",
|
|
],
|
|
2: [
|
|
"study_field",
|
|
"education",
|
|
"education_detail",
|
|
"college_name",
|
|
"employee_type",
|
|
"occupation",
|
|
"occupation_detail",
|
|
"company_name",
|
|
"income_currency",
|
|
"annual_income",
|
|
"work_country",
|
|
"work_city",
|
|
"work_state",
|
|
"work_district",
|
|
"address",
|
|
],
|
|
3: [
|
|
"fatherName",
|
|
"fatherOccupation",
|
|
"motherName",
|
|
"motherOccupation",
|
|
"brotherCount",
|
|
"sisterCount",
|
|
"familyStatus",
|
|
"nativePlace",
|
|
],
|
|
4: ["diets", "hobbies", "dob", "tob", "placeOfBirth"],
|
|
5: [
|
|
"ageRange",
|
|
"castes",
|
|
"subCastes",
|
|
"occupations",
|
|
"educations",
|
|
"hobbies",
|
|
"annualIncome",
|
|
"states",
|
|
"districts",
|
|
],
|
|
};
|
|
|
|
const STEP1_SERVER_FIELD_MAP = {
|
|
name: "name",
|
|
mobile: "mobile",
|
|
email: "email",
|
|
gender: "gender",
|
|
height: "height",
|
|
weight: "weight",
|
|
marital_status: "marital_status",
|
|
religion: "religion",
|
|
profile_for: "profile_for",
|
|
caste: "caste",
|
|
sub_caste: "sub_caste",
|
|
willing_to_marry: "willing_to_marry",
|
|
inter_caste_parents: "inter_caste_parents",
|
|
inter_caste_parents_details: "inter_caste_parents_details",
|
|
gothram: "gothram",
|
|
do_you_speak_telugu: "do_you_speak_telugu",
|
|
about_us: "about_us",
|
|
known_languages: "known_languages",
|
|
mother_language: "mother_language",
|
|
complexion: "complexion",
|
|
physical_status: "physical_status",
|
|
password: "password",
|
|
confirm_password: "confirmPassword",
|
|
profiles: "profiles",
|
|
};
|
|
|
|
|
|
// const Stepper = ({ currentStep, enabledSteps, onStepClick,completedSteps }) => {
|
|
// const steps = [
|
|
// { num: 1, label: "Personal" },
|
|
// { num: 2, label: "Educational" },
|
|
// { num: 3, label: "Family" },
|
|
// { num: 4, label: "Lifestyle" },
|
|
// { num: 5, label: "Partner" },
|
|
// { num: 6, label: "Preview" },
|
|
// ];
|
|
|
|
// return (
|
|
// <div className="flex items-center justify-between px-4 py-6">
|
|
// {steps.map((step, index) => {
|
|
// const isEnabled = enabledSteps.includes(step.num);
|
|
|
|
// return (
|
|
// <React.Fragment key={step.num}>
|
|
// <div
|
|
// className={`flex flex-col items-center ${
|
|
// isEnabled ? "cursor-pointer" : "cursor-not-allowed opacity-50"
|
|
// }`}
|
|
// onClick={() => isEnabled && onStepClick(step.num)}
|
|
// >
|
|
// <div
|
|
// className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold ${
|
|
// currentStep >= step.num
|
|
// ? "bg-red-600 text-white"
|
|
// : "bg-gray-300 text-gray-600"
|
|
// }`}
|
|
// >
|
|
// {step.num}
|
|
// </div>
|
|
// </div>
|
|
|
|
// {index < steps.length - 1 && (
|
|
// <div
|
|
// className={`flex-1 h-0.5 mx-1 ${
|
|
// currentStep > step.num ? "bg-red-600" : "bg-gray-300"
|
|
// }`}
|
|
// />
|
|
// )}
|
|
// </React.Fragment>
|
|
// );
|
|
// })}
|
|
// </div>
|
|
// );
|
|
// };
|
|
|
|
import { Check } from "lucide-react";
|
|
|
|
const Stepper = ({ currentStep, enabledSteps, completedSteps, onStepClick, checkStepValidity }) => {
|
|
const steps = [
|
|
{ num: 1, label: "Personal" },
|
|
{ num: 2, label: "Educational" },
|
|
{ num: 3, label: "Family" },
|
|
{ num: 4, label: "Lifestyle" },
|
|
{ num: 5, label: "Partner" },
|
|
{ num: 6, label: "Preview" },
|
|
];
|
|
|
|
return (
|
|
<div className="flex items-center justify-between px-4 py-6 overflow-x-auto">
|
|
{steps.map((step, index) => {
|
|
const isSkippable = currentStep > 1 && currentStep < 6 && step.num === currentStep + 1;
|
|
const isEnabled = enabledSteps.includes(step.num) || isSkippable;
|
|
const isActive = currentStep === step.num;
|
|
|
|
// Dynamic completion check: Green tick if all mandatory fields are filled
|
|
const isValid = checkStepValidity && checkStepValidity(step.num);
|
|
const isCompleted = completedSteps.includes(step.num) || isValid;
|
|
|
|
// A step is Blue (Skipped) ONLY if it's a previous step and NOT completed
|
|
const isSkipped = isEnabled && !isCompleted && !isActive && step.num < currentStep;
|
|
|
|
return (
|
|
<React.Fragment key={step.num}>
|
|
<div
|
|
className={`flex flex-col items-center min-w-[70px] ${
|
|
isEnabled ? "cursor-pointer" : "cursor-not-allowed opacity-60"
|
|
}`}
|
|
onClick={() => isEnabled && onStepClick(step.num)}
|
|
>
|
|
<div
|
|
className={`w-9 h-9 rounded-full flex items-center justify-center text-sm font-bold transition-all duration-300 ${
|
|
isCompleted
|
|
? "bg-green-600 text-white shadow-md"
|
|
: isActive
|
|
? "bg-red-600 text-white ring-4 ring-red-100 shadow-md"
|
|
: isSkipped
|
|
? "bg-blue-500 text-white shadow-md"
|
|
: "bg-gray-200 text-gray-500"
|
|
}`}
|
|
>
|
|
{isCompleted ? <Check size={18} /> : step.num}
|
|
</div>
|
|
<span
|
|
className={`text-[10px] mt-2 font-medium text-center uppercase tracking-wider ${
|
|
isActive ? "text-red-600 font-bold" : isCompleted ? "text-green-600" : isSkipped ? "text-blue-500" : "text-gray-400"
|
|
}`}
|
|
>
|
|
{step.label}
|
|
</span>
|
|
</div>
|
|
|
|
{index < steps.length - 1 && (
|
|
<div
|
|
className={`flex-1 h-[2px] mx-1 mb-6 transition-colors duration-500 ${
|
|
(completedSteps.includes(step.num) || (checkStepValidity && checkStepValidity(step.num)))
|
|
? "bg-green-600"
|
|
: (enabledSteps.includes(step.num) && enabledSteps.includes(step.num + 1))
|
|
? "bg-blue-300"
|
|
: "bg-gray-200"
|
|
}`}
|
|
/>
|
|
)}
|
|
</React.Fragment>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const StepperForm = () => {
|
|
const dispatch = useDispatch();
|
|
const queryClient = useQueryClient();
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
const hideStepperRoutes = ["/profile-edit"];
|
|
const shouldHideStepper = hideStepperRoutes.some((route) => location.pathname.startsWith(route));
|
|
const personalDetails = useSelector(
|
|
(state) => state.registerform.personalDetails
|
|
);
|
|
const educationalDetails = useSelector(
|
|
(state) => state.registerform.educationalDetails
|
|
);
|
|
const familyDetails = useSelector((state) => state.registerform.familyDetails);
|
|
const lifestyleDetails = useSelector(
|
|
(state) => state.registerform.lifestyleDetails
|
|
);
|
|
const partnerPreferences = useSelector(
|
|
(state) => state.registerform.partnerPreferences
|
|
);
|
|
|
|
const [currentStep, setCurrentStep] = useState(() => {
|
|
if (location.state?.step) {
|
|
return location.state.step;
|
|
}
|
|
const savedStep = localStorage.getItem("registration_current_step");
|
|
return savedStep ? Number(savedStep) : 1;
|
|
});
|
|
const [enabledSteps, setEnabledSteps] = useState(() => {
|
|
if (location.state?.step) {
|
|
return Array.from({ length: 6 }, (_, i) => i + 1); // Enable all if coming from edit/preview
|
|
}
|
|
const savedStep = localStorage.getItem("registration_current_step");
|
|
const step = savedStep ? Number(savedStep) : 1;
|
|
return Array.from({ length: step }, (_, i) => i + 1);
|
|
});
|
|
const [completedSteps, setCompletedSteps] = useState([]);
|
|
const [isStep1Update, setIsStep1Update] = useState(false);
|
|
const [errors, setErrors] = useState({});
|
|
const isAuth = isAuthenticated();
|
|
|
|
const registerStep1 = useRegisterStep1();
|
|
const registerStep2 = useRegisterStep2();
|
|
const registerStep3 = useRegisterStep3();
|
|
const registerStep4 = useRegisterStep4();
|
|
const registerStep5 = useRegisterStep5();
|
|
|
|
useEffect(() => {
|
|
localStorage.setItem("registration_current_step", currentStep);
|
|
}, [currentStep]);
|
|
|
|
const normalizeStep1Field = (key) => {
|
|
if (!key) return key;
|
|
const trimmed = String(key).trim();
|
|
if (!trimmed) return trimmed;
|
|
|
|
// Map backend profile_images errors (e.g., profile_images.0) to the profiles field
|
|
if (trimmed.startsWith("profile_images")) {
|
|
return "profiles";
|
|
}
|
|
|
|
return (
|
|
STEP1_SERVER_FIELD_MAP[trimmed] ||
|
|
STEP1_SERVER_FIELD_MAP[trimmed.toLowerCase()] ||
|
|
trimmed
|
|
);
|
|
};
|
|
|
|
const coerceErrorMessage = (value) => {
|
|
if (Array.isArray(value)) {
|
|
return value.filter(Boolean).join(" ");
|
|
}
|
|
if (typeof value === "string") return value;
|
|
if (value && typeof value === "object") {
|
|
return (
|
|
value.message ||
|
|
value.msg ||
|
|
value.error ||
|
|
value.detail ||
|
|
""
|
|
);
|
|
}
|
|
if (value === null || value === undefined) return "";
|
|
return String(value);
|
|
};
|
|
|
|
const mapServerErrors = (error) => {
|
|
const data = error?.response?.data ?? error?.data ?? error;
|
|
if (!data) return {};
|
|
const payload = data.errors ?? data.error ?? data.data ?? data;
|
|
|
|
if (typeof payload === "string") {
|
|
return { _form: payload };
|
|
}
|
|
|
|
if (Array.isArray(payload)) {
|
|
const out = {};
|
|
payload.forEach((item) => {
|
|
if (!item) return;
|
|
if (typeof item === "string") {
|
|
out._form = out._form ? `${out._form} ${item}` : item;
|
|
return;
|
|
}
|
|
const key = item.field || item.name || item.param || item.key;
|
|
const message =
|
|
item.message || item.msg || item.error || item.detail || "";
|
|
if (key) {
|
|
out[normalizeStep1Field(key)] =
|
|
String(message || "Invalid value");
|
|
}
|
|
});
|
|
return out;
|
|
}
|
|
|
|
if (typeof payload === "object") {
|
|
const out = {};
|
|
Object.entries(payload).forEach(([key, value]) => {
|
|
if (
|
|
(key === "message" || key === "error" || key === "detail") &&
|
|
typeof value === "string"
|
|
) {
|
|
out._form = out._form ? `${out._form} ${value}` : value;
|
|
return;
|
|
}
|
|
const normalizedKey = normalizeStep1Field(key);
|
|
const message = coerceErrorMessage(value);
|
|
if (!normalizedKey) return;
|
|
out[normalizedKey] = message || "Invalid value";
|
|
});
|
|
return out;
|
|
}
|
|
|
|
return {};
|
|
};
|
|
|
|
const focusFirstError = (errorMap, fieldOrder = []) => {
|
|
if (!errorMap || Object.keys(errorMap).length === 0) return;
|
|
const order = fieldOrder.filter((key) => errorMap[key]);
|
|
const firstKey =
|
|
order[0] ||
|
|
Object.keys(errorMap).find((key) => key !== "_form");
|
|
if (!firstKey) return;
|
|
|
|
setTimeout(() => {
|
|
const element = document.getElementById(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;
|
|
|
|
if (focusable && typeof focusable.focus === "function") {
|
|
focusable.focus();
|
|
}
|
|
}
|
|
}, 100);
|
|
};
|
|
|
|
const clearFieldErrors = (fields) => {
|
|
if (!fields) return;
|
|
const list = Array.isArray(fields) ? fields : [fields];
|
|
setErrors((prev) => {
|
|
if (!prev || Object.keys(prev).length === 0) return prev;
|
|
let changed = false;
|
|
const next = { ...prev };
|
|
list.forEach((field) => {
|
|
if (field in next) {
|
|
delete next[field];
|
|
changed = true;
|
|
}
|
|
});
|
|
if (next._form) {
|
|
delete next._form;
|
|
changed = true;
|
|
}
|
|
return changed ? next : prev;
|
|
});
|
|
};
|
|
|
|
|
|
useEffect(() => {
|
|
// in case user comes again with a different step
|
|
if (location.state?.step) {
|
|
setCurrentStep(location.state.step);
|
|
setEnabledSteps([1, 2, 3, 4, 5, 6]);
|
|
window.scrollTo(0, 0);
|
|
}
|
|
}, [location.state?.step]);
|
|
|
|
useEffect(() => {
|
|
// Clear the state so it doesn't persist on refresh
|
|
if (location.state?.step) {
|
|
navigate(location.pathname, { replace: true, state: {} });
|
|
}
|
|
}, [location.state, navigate, location.pathname]);
|
|
|
|
const { data: personalDetailsData } = useQuery({
|
|
queryKey: ["personalDetails"],
|
|
queryFn: async () => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.EDIT_PERSONAL_DETAILS);
|
|
return response.data;
|
|
},
|
|
enabled: isAuth || shouldHideStepper,
|
|
retry: false,
|
|
refetchOnWindowFocus: false,
|
|
});
|
|
|
|
useEffect(() => {
|
|
const processData = async () => {
|
|
if (personalDetailsData?.status === "success" && personalDetailsData?.personal_details) {
|
|
const pd = personalDetailsData.personal_details;
|
|
setIsStep1Update(true);
|
|
|
|
const rawImages = pd.profile_images || pd.images || [];
|
|
const mappedImages = await Promise.all(
|
|
rawImages.map(async (url, index) => {
|
|
const imageUrl = typeof url === "string" ? url : url?.url;
|
|
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 });
|
|
}
|
|
} catch (error) {
|
|
console.error("Error converting image URL to File:", error);
|
|
}
|
|
return {
|
|
id: `server-${index}`,
|
|
preview: imageUrl,
|
|
imageUrl: imageUrl,
|
|
file: file,
|
|
name: fileName,
|
|
type: mimeType,
|
|
valid: true,
|
|
};
|
|
})
|
|
);
|
|
|
|
const formattedDob = pd.dob ? pd.dob.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,
|
|
})
|
|
);
|
|
}
|
|
};
|
|
processData();
|
|
}, [personalDetailsData, dispatch]);
|
|
|
|
|
|
// Fetch Educational Details
|
|
const { data: educationalData } = useQuery({
|
|
queryKey: ["educationalDetails"],
|
|
queryFn: async () => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.EDIT_EDUCATION_DETAILS);
|
|
return response.data;
|
|
},
|
|
enabled: isAuth || shouldHideStepper,
|
|
retry: false,
|
|
refetchOnWindowFocus: false,
|
|
});
|
|
|
|
|
|
useEffect(() => {
|
|
if (educationalData?.status === "success" && 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 || "",
|
|
})
|
|
);
|
|
}
|
|
}, [educationalData, dispatch]);
|
|
|
|
|
|
|
|
// Fetch family details
|
|
const {data:familyData} = useQuery({
|
|
queryKey:["familyDetails"],
|
|
queryFn: async()=>{
|
|
const response = await axiosInstance.get(API_ENDPOINTS.EDIT_FAMILY_DETAILS);
|
|
return response.data;
|
|
},
|
|
enabled: isAuth || shouldHideStepper,
|
|
retry:false,
|
|
refetchOnWindowFocus:false,
|
|
});
|
|
|
|
|
|
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) => ({
|
|
name: b.name || "",
|
|
occupation: b.occupation_name || "",
|
|
maritalStatus: b.marital_status || "",
|
|
type: b.type || "",
|
|
hasChildren: b.has_children || "",
|
|
details: b.additional_details || ""
|
|
})),
|
|
sisters: (fd.sisters || []).map((s) => ({
|
|
name: s.name || "",
|
|
occupation: s.occupation_name || "",
|
|
maritalStatus: s.marital_status || "",
|
|
type: s.type || "",
|
|
hasChildren: s.has_children || "",
|
|
details: s.additional_details || ""
|
|
})),
|
|
})
|
|
);
|
|
}
|
|
}, [familyData, dispatch]);
|
|
|
|
// Fetch Lifestyle Details
|
|
const { data: lifestyleData } = useQuery({
|
|
queryKey: ["lifestyleDetails"],
|
|
queryFn: async () => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.EDIT_LIFESTYLE_DETAILS);
|
|
return response.data;
|
|
},
|
|
enabled: isAuth || shouldHideStepper,
|
|
retry: false,
|
|
refetchOnWindowFocus: false,
|
|
refetchOnMount: true,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (lifestyleData?.status === "success" && lifestyleData?.lifestyle_details) {
|
|
const ld = lifestyleData.lifestyle_details;
|
|
const horoscope = ld.horoscope || {};
|
|
|
|
const mapChart = (prefix) => {
|
|
const chart = {};
|
|
for (let i = 1; i <= 12; i++) {
|
|
const key = `${prefix}_${i}`;
|
|
const val = horoscope[key];
|
|
chart[i] = val ? val.split(",") : [];
|
|
}
|
|
return chart;
|
|
};
|
|
|
|
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 || "",
|
|
graha: mapChart("graha"),
|
|
amsam: mapChart("amsam"),
|
|
})
|
|
);
|
|
}
|
|
}, [lifestyleData, dispatch]);
|
|
|
|
|
|
// Fetch Partner Preferences
|
|
const { data: partnerData } = useQuery({
|
|
queryKey: ["partnerPreferences"],
|
|
queryFn: async () => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.EDIT_PREFERED_PARTNER_DETAILS);
|
|
return response.data;
|
|
},
|
|
enabled: isAuth || shouldHideStepper,
|
|
retry: false,
|
|
refetchOnWindowFocus: false,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (partnerData?.status === "success" && 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 || [],
|
|
})
|
|
);
|
|
}
|
|
}, [partnerData, dispatch]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const validateStep = (step) => {
|
|
const newErrors = {};
|
|
|
|
if (step === 1) {
|
|
const required = [
|
|
"name",
|
|
"mobile",
|
|
"gender",
|
|
"height",
|
|
"marital_status",
|
|
"profile_for",
|
|
"caste",
|
|
"email",
|
|
"mother_language",
|
|
"complexion",
|
|
"physical_status",
|
|
"inter_caste_parents",
|
|
"do_you_speak_telugu",
|
|
];
|
|
|
|
required.forEach((field) => {
|
|
if (!personalDetails[field] && personalDetails[field] !== 0) {
|
|
const label = field
|
|
.replace(/_/g, " ")
|
|
.replace(/^./, (str) => str.toUpperCase());
|
|
newErrors[field] = `${label} is required`;
|
|
}
|
|
});
|
|
|
|
if (!isStep1Update) {
|
|
if (!personalDetails.password) newErrors.password = "Password is required";
|
|
if (!personalDetails.confirmPassword) newErrors.confirmPassword = "Confirm Password is required";
|
|
}
|
|
|
|
if (
|
|
personalDetails.email &&
|
|
!/\S+@\S+\.\S+/.test(personalDetails.email)
|
|
) {
|
|
newErrors.email = "Invalid email format";
|
|
}
|
|
if (
|
|
personalDetails.mobile &&
|
|
personalDetails.mobile.length !== 10
|
|
) {
|
|
newErrors.mobile = "Mobile number must be 10 digits";
|
|
}
|
|
if (
|
|
personalDetails.password &&
|
|
personalDetails.confirmPassword &&
|
|
personalDetails.password !== personalDetails.confirmPassword
|
|
) {
|
|
newErrors.confirmPassword = "Passwords do not match";
|
|
}
|
|
} else if (step === 2) {
|
|
const isUnemployed = educationalDetails.employee_type === 11;
|
|
const isIndia = educationalDetails.work_country === 1;
|
|
const required = ["study_field", "education", "education_detail", "employee_type"];
|
|
|
|
if (!isUnemployed) {
|
|
required.push("occupation", "occupation_detail", "income_currency", "annual_income", "work_country");
|
|
if (isIndia) {
|
|
required.push("work_state", "work_district");
|
|
} else {
|
|
required.push("work_city");
|
|
}
|
|
}
|
|
required.push("address");
|
|
|
|
required.forEach((field) => {
|
|
if (!educationalDetails[field]) {
|
|
const label = field
|
|
.replace(/_/g, " ")
|
|
.replace(/^./, (str) => str.toUpperCase());
|
|
newErrors[field] = `${label} is required`;
|
|
}
|
|
});
|
|
} else if (step === 3) {
|
|
const required = [
|
|
"fatherName",
|
|
"motherName",
|
|
"familyStatus",
|
|
"nativePlace",
|
|
];
|
|
required.forEach((field) => {
|
|
if (!familyDetails[field]) {
|
|
// newErrors[field] = "This field is required";
|
|
const label = field
|
|
.replace(/([A-Z])/g, " $1")
|
|
.replace(/^./, (str) => str.toUpperCase());
|
|
newErrors[field] = `${label} is required`;
|
|
}
|
|
});
|
|
|
|
if (familyDetails.fatherName && !/^[a-zA-Z\s]*$/.test(familyDetails.fatherName)) {
|
|
newErrors.fatherName = "Father Name must contain only alphabets";
|
|
}
|
|
if (familyDetails.motherName && !/^[a-zA-Z\s]*$/.test(familyDetails.motherName)) {
|
|
newErrors.motherName = "Mother Name must contain only alphabets";
|
|
}
|
|
} else if (step === 4) {
|
|
const required = ["diets", "hobbies", "dob", "tob"];
|
|
required.forEach((field) => {
|
|
const value = lifestyleDetails[field];
|
|
if (Array.isArray(value)) {
|
|
if (value.length === 0) {
|
|
if (field === "hobbies") {
|
|
newErrors[field] = "Hobbies and Interests is required";
|
|
} else {
|
|
newErrors[field] = "This field is required";
|
|
}
|
|
}
|
|
|
|
} else if (!value) {
|
|
|
|
if (field === "dob") {
|
|
newErrors[field] = "Date of Birth is required";
|
|
}
|
|
else if (field === "tob") {
|
|
newErrors[field] = "Time of Birth is required";
|
|
}
|
|
else if (field === "diets") {
|
|
newErrors[field] = "Diet is required";
|
|
}
|
|
|
|
else {
|
|
const label = field
|
|
.replace(/([A-Z])/g, " $1")
|
|
.replace(/^./, (str) => str.toUpperCase());
|
|
newErrors[field] = `${label} is required`;
|
|
}
|
|
}
|
|
});
|
|
} else if (step === 5) {
|
|
const required = [
|
|
"ageRange",
|
|
"castes",
|
|
"subCastes",
|
|
"occupations",
|
|
"educations",
|
|
"hobbies",
|
|
"annualIncome",
|
|
"states",
|
|
"districts",
|
|
];
|
|
required.forEach((field) => {
|
|
const value = partnerPreferences[field];
|
|
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;
|
|
}
|
|
if (!value) {
|
|
// newErrors[field] = "This field is required";
|
|
const label = field
|
|
.replace(/([A-Z])/g, " $1")
|
|
.replace(/^./, (str) => str.toUpperCase());
|
|
newErrors[field] = `${label} is required`;
|
|
}
|
|
});
|
|
}
|
|
|
|
setErrors(newErrors);
|
|
|
|
if (Object.keys(newErrors).length > 0) {
|
|
focusFirstError(newErrors, STEP_FIELD_ORDER[step] || []);
|
|
}
|
|
|
|
return Object.keys(newErrors).length === 0;
|
|
};
|
|
|
|
const buildRegisterStep1Payload = async () => {
|
|
const formData = new FormData();
|
|
formData.append("name", personalDetails.name);
|
|
formData.append("mobile", personalDetails.mobile);
|
|
formData.append("email", personalDetails.email);
|
|
formData.append("gender", personalDetails.gender);
|
|
formData.append("height", personalDetails.height || "");
|
|
formData.append("weight", personalDetails.weight || "");
|
|
formData.append("marital_status", personalDetails.marital_status);
|
|
formData.append("religion", personalDetails.religion);
|
|
formData.append("profile_for", personalDetails.profile_for || "");
|
|
formData.append("caste", personalDetails.caste);
|
|
formData.append("sub_caste", personalDetails.sub_caste || "");
|
|
formData.append("willing_to_marry", personalDetails.willing_to_marry || "");
|
|
formData.append("inter_caste_parents", personalDetails.inter_caste_parents);
|
|
formData.append("inter_caste_parents_details", personalDetails.inter_caste_parents_details || "");
|
|
formData.append("gothram", personalDetails.gothram || "");
|
|
formData.append("do_you_speak_telugu", personalDetails.do_you_speak_telugu);
|
|
formData.append("about_us", personalDetails.about_us || "");
|
|
formData.append("mother_language", personalDetails.mother_language || "");
|
|
formData.append("complexion", personalDetails.complexion || "");
|
|
formData.append("physical_status", personalDetails.physical_status || "");
|
|
|
|
(personalDetails.known_languages || []).forEach((id, index) => {
|
|
formData.append(`known_languages[${index}]`, id);
|
|
});
|
|
|
|
if (!isStep1Update) {
|
|
formData.append("password", personalDetails.password || "");
|
|
}
|
|
formData.append("web_fcm_token", localStorage.getItem("fcm_token") || "");
|
|
|
|
if (personalDetails.profiles && Array.isArray(personalDetails.profiles)) {
|
|
await Promise.all(
|
|
personalDetails.profiles.map(async (item, index) => {
|
|
let fileToAppend = item.file;
|
|
// Check if it's a valid File/Blob. Redux might turn it into {}
|
|
const isValidFile =
|
|
fileToAppend &&
|
|
(fileToAppend instanceof Blob || (typeof fileToAppend === "object" && fileToAppend.name));
|
|
|
|
if (!isValidFile && item.preview && typeof item.preview === "string") {
|
|
try {
|
|
const response = await fetch(item.preview);
|
|
const blob = await response.blob();
|
|
const mimeType = blob.type || "image/jpeg";
|
|
const ext = mimeType.split("/")[1] || "jpg";
|
|
const fileName = item.name || `profile_image_${index}.${ext}`;
|
|
const fileType = item.type || mimeType;
|
|
fileToAppend = new File([blob], fileName, { type: fileType });
|
|
} catch (e) {
|
|
console.error(`Could not recover file from URL: ${item.preview}`, e);
|
|
fileToAppend = null;
|
|
}
|
|
} else if (!isValidFile) {
|
|
fileToAppend = null;
|
|
}
|
|
|
|
if (fileToAppend) {
|
|
formData.append(`profile_images[${index}]`, fileToAppend);
|
|
} else if (item.preview && typeof item.preview === "string") {
|
|
formData.append(`profile_images[${index}]`, item.preview);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
return formData;
|
|
};
|
|
|
|
const buildRegisterStep2Payload = () => {
|
|
const formData = new FormData();
|
|
formData.append("study_field", educationalDetails.study_field || "");
|
|
formData.append("education", educationalDetails.education || "");
|
|
formData.append("education_detail", educationalDetails.education_detail || "");
|
|
formData.append("college_name", educationalDetails.college_name || "");
|
|
formData.append("employee_type", educationalDetails.employee_type || "");
|
|
formData.append("occupation", educationalDetails.occupation || "");
|
|
formData.append("occupation_detail", educationalDetails.occupation_detail || "");
|
|
formData.append("company_name", educationalDetails.company_name || "");
|
|
formData.append("income_currency", educationalDetails.income_currency || "INR");
|
|
formData.append("annual_income", educationalDetails.annual_income || "");
|
|
formData.append("work_country", educationalDetails.work_country || "");
|
|
formData.append("work_city", educationalDetails.work_city || "");
|
|
formData.append("work_state", educationalDetails.work_state || "");
|
|
formData.append("work_district", educationalDetails.work_district || "");
|
|
formData.append("address", educationalDetails.address || "");
|
|
// Also append work_location as some APIs might use it for address/city
|
|
formData.append("work_location", educationalDetails.address || "");
|
|
return formData;
|
|
};
|
|
|
|
const buildRegisterStep3Payload = () => {
|
|
const formData = new FormData();
|
|
formData.append("father_name", familyDetails.fatherName);
|
|
formData.append("father_occupation", familyDetails.fatherOccupation || "");
|
|
formData.append("mother_name", familyDetails.motherName);
|
|
formData.append("mother_occupation", familyDetails.motherOccupation || "");
|
|
formData.append("family_status", 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_state", familyDetails.familyState || "");
|
|
formData.append("family_district", familyDetails.familyDistrict || "");
|
|
formData.append("family_city", familyDetails.familyCity || "");
|
|
formData.append("address", familyDetails.address || "");
|
|
formData.append("expectation_details", familyDetails.expectationDetails || "");
|
|
formData.append("willing_to_go_abroad", familyDetails.willingToGoAbroad || "");
|
|
|
|
(familyDetails.brothers || []).forEach((brother, index) => {
|
|
formData.append(`brothers[${index}][name]`, brother?.name || "");
|
|
formData.append(`brothers[${index}][occupation]`, brother?.occupation || "");
|
|
formData.append(`brothers[${index}][marital_status]`, brother?.maritalStatus || "");
|
|
formData.append(`brothers[${index}][type]`, brother?.type || "");
|
|
formData.append(`brothers[${index}][has_children]`, brother?.hasChildren || "");
|
|
formData.append(`brothers[${index}][details]`, brother?.details || "");
|
|
});
|
|
|
|
(familyDetails.sisters || []).forEach((sister, index) => {
|
|
formData.append(`sisters[${index}][name]`, sister?.name || "");
|
|
formData.append(`sisters[${index}][occupation]`, sister?.occupation || "");
|
|
formData.append(`sisters[${index}][marital_status]`, sister?.maritalStatus || "");
|
|
formData.append(`sisters[${index}][type]`, sister?.type || "");
|
|
formData.append(`sisters[${index}][has_children]`, sister?.hasChildren || "");
|
|
formData.append(`sisters[${index}][details]`, sister?.details || "");
|
|
});
|
|
|
|
return formData;
|
|
};
|
|
|
|
const buildRegisterStep4Payload = () => {
|
|
const formData = new FormData();
|
|
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);
|
|
});
|
|
|
|
const graha = lifestyleDetails.graha || {};
|
|
Object.keys(graha).forEach((house) => {
|
|
const values = graha[house] || [];
|
|
values.forEach((value, index) => {
|
|
formData.append(`graha_${house}[${index}]`, value);
|
|
});
|
|
});
|
|
|
|
const amsam = lifestyleDetails.amsam || {};
|
|
Object.keys(amsam).forEach((house) => {
|
|
const values = amsam[house] || [];
|
|
values.forEach((value, index) => {
|
|
formData.append(`amsam_${house}[${index}]`, value);
|
|
});
|
|
});
|
|
|
|
return formData;
|
|
};
|
|
|
|
const buildRegisterStep5Payload = () => {
|
|
const formData = new FormData();
|
|
formData.append("age_range", partnerPreferences.ageRange || "");
|
|
formData.append("annual_income", partnerPreferences.annualIncome || "");
|
|
|
|
(partnerPreferences.castes || []).forEach((id, index) => {
|
|
formData.append(`castes[${index}]`, id);
|
|
});
|
|
|
|
(partnerPreferences.subCastes || []).forEach((id, index) => {
|
|
formData.append(`sub_castes[${index}]`, id);
|
|
});
|
|
|
|
(partnerPreferences.occupations || []).forEach((id, index) => {
|
|
formData.append(`occupations[${index}]`, id);
|
|
});
|
|
|
|
(partnerPreferences.educations || []).forEach((id, index) => {
|
|
formData.append(`educations[${index}]`, id);
|
|
});
|
|
|
|
(partnerPreferences.hobbies || []).forEach((id, index) => {
|
|
formData.append(`hobbies[${index}]`, id);
|
|
});
|
|
|
|
(partnerPreferences.states || []).forEach((id, index) => {
|
|
formData.append(`states[${index}]`, id);
|
|
});
|
|
|
|
(partnerPreferences.districts || []).forEach((id, index) => {
|
|
formData.append(`districts[${index}]`, id);
|
|
});
|
|
|
|
return formData;
|
|
};
|
|
|
|
const extractAccessToken = (res) =>
|
|
res?.access_token ||
|
|
res?.accessToken ||
|
|
res?.token ||
|
|
res?.data?.access_token ||
|
|
res?.data?.accessToken ||
|
|
res?.data?.token ||
|
|
res?.result?.access_token ||
|
|
res?.result?.accessToken ||
|
|
res?.result?.token ||
|
|
null;
|
|
|
|
const handleStepSubmit = async () => {
|
|
const isValid = validateStep(currentStep);
|
|
if (!isValid) return;
|
|
|
|
try {
|
|
let payload;
|
|
let res;
|
|
|
|
switch (currentStep) {
|
|
case 1:
|
|
payload = await buildRegisterStep1Payload();
|
|
|
|
if (isStep1Update) {
|
|
res = await apiForFiles.post("/update_personal_details", payload);
|
|
|
|
try {
|
|
const previewData = await getPreviewDetails();
|
|
queryClient.invalidateQueries({
|
|
queryKey: ["preview-details"]
|
|
});
|
|
if (previewData?.personal_details) {
|
|
const pd = previewData.personal_details;
|
|
const images = pd.images || pd.profile_images;
|
|
if (images && images.length > 0) {
|
|
const formattedImages = images.map((img) =>
|
|
typeof img === "string" ? { url: img, preview: img } : img
|
|
);
|
|
dispatch(updatePersonalDetails({ profiles: formattedImages }));
|
|
}
|
|
}
|
|
queryClient.invalidateQueries({ queryKey: ["personalDetails"] });
|
|
} catch (error) {
|
|
console.error("Error refreshing preview details:", error);
|
|
}
|
|
} else {
|
|
res = await registerStep1.mutateAsync(payload);
|
|
}
|
|
|
|
const token = extractAccessToken(res.data || res);
|
|
if (token) setAccessToken(token);
|
|
|
|
// Store profile_id and user_id for WebSocket channels
|
|
const profileId = res.data?.profile_id || res?.profile_id || res.data?.data?.profile_id;
|
|
const userId = res.data?.user_id || res?.user_id || res.data?.data?.user_id;
|
|
if (profileId) localStorage.setItem("profile_id", profileId);
|
|
if (userId) localStorage.setItem("user_id", userId);
|
|
break;
|
|
|
|
case 2:
|
|
payload = buildRegisterStep2Payload();
|
|
await registerStep2.mutateAsync(payload);
|
|
queryClient.invalidateQueries({ queryKey: ["educationalDetails"] });
|
|
queryClient.invalidateQueries({ queryKey: ["preview-details"] });
|
|
break;
|
|
|
|
case 3:
|
|
payload = buildRegisterStep3Payload();
|
|
await registerStep3.mutateAsync(payload);
|
|
queryClient.invalidateQueries({ queryKey: ["familyDetails"] })
|
|
queryClient.invalidateQueries({ queryKey: ["preview-details"] });
|
|
break;
|
|
|
|
case 4:
|
|
payload = buildRegisterStep4Payload();
|
|
await registerStep4.mutateAsync(payload);
|
|
queryClient.invalidateQueries({ queryKey: ["lifestyleDetails"] });
|
|
queryClient.invalidateQueries({ queryKey: ["preview-details"] });
|
|
break;
|
|
|
|
case 5:
|
|
payload = buildRegisterStep5Payload();
|
|
await registerStep5.mutateAsync(payload);
|
|
queryClient.invalidateQueries({ queryKey: ["partnerPreferences"] });
|
|
queryClient.invalidateQueries({ queryKey: ["preview-details"] });
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (shouldHideStepper) {
|
|
toast.success("Updated successfully", { position: "top-right" });
|
|
navigate("/profile");
|
|
return;
|
|
}
|
|
|
|
// Mark step completed (tick icon)
|
|
setCompletedSteps((prev) =>
|
|
prev.includes(currentStep) ? prev : [...prev, currentStep]
|
|
);
|
|
|
|
const nextStep = Math.min(currentStep + 1, 6);
|
|
|
|
// Enable next step navigation
|
|
setEnabledSteps((prev) =>
|
|
prev.includes(nextStep) ? prev : [...prev, nextStep]
|
|
);
|
|
|
|
setErrors({});
|
|
setCurrentStep(nextStep);
|
|
window.scrollTo(0, 0);
|
|
|
|
} catch (e) {
|
|
const serverErrors = mapServerErrors(e);
|
|
let handled = false;
|
|
|
|
if (Object.keys(serverErrors).length > 0) {
|
|
const hasFieldErrors = Object.keys(serverErrors).some(
|
|
(key) => key !== "_form"
|
|
);
|
|
|
|
if (hasFieldErrors) {
|
|
setErrors(serverErrors);
|
|
focusFirstError(serverErrors, STEP_FIELD_ORDER[currentStep]);
|
|
handled = true;
|
|
}
|
|
|
|
if (serverErrors._form) {
|
|
toast.error(serverErrors._form, { position: "top-right" });
|
|
handled = true;
|
|
}
|
|
}
|
|
|
|
if (!handled) {
|
|
const msg =
|
|
e?.response?.data?.message ||
|
|
e?.message ||
|
|
"Failed to submit step. Please try again.";
|
|
|
|
toast.error(msg, { position: "top-right" });
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleSkip = () => {
|
|
setErrors({});
|
|
|
|
// Remove from completed if skipping (matching Flutter logic)
|
|
setCompletedSteps((prev) => prev.filter(s => s !== currentStep));
|
|
|
|
setCurrentStep((prev) => {
|
|
const nextStep = Math.min(prev + 1, 6);
|
|
|
|
setEnabledSteps((steps) =>
|
|
steps.includes(nextStep) ? steps : [...steps, nextStep]
|
|
);
|
|
|
|
return nextStep;
|
|
});
|
|
|
|
window.scrollTo(0, 0);
|
|
};
|
|
|
|
const handleStepClick = (step) => {
|
|
// If clicking next step and current is skippable (not Step 1), trigger handleSkip
|
|
if (step === currentStep + 1 && currentStep > 1 && currentStep < 6) {
|
|
handleSkip();
|
|
return;
|
|
}
|
|
|
|
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);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (currentStep === 6) {
|
|
setEnabledSteps([1, 2, 3, 4, 5, 6]);
|
|
}
|
|
}, [currentStep]);
|
|
|
|
const handleEdit = (step) => {
|
|
setCurrentStep(step);
|
|
setErrors({});
|
|
window.scrollTo(0, 0);
|
|
};
|
|
|
|
const handleFinalSubmit = async () => {
|
|
const ok = validateStep(1);
|
|
if (!ok) {
|
|
setCurrentStep(1);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// final combined API call - replace with your final API
|
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
toast.success("Form submitted successfully!", { position: "top-right" });
|
|
localStorage.removeItem("registration_current_step");
|
|
navigate("/dashboard-home");
|
|
} catch (e) {
|
|
toast.error("Failed to submit form.", { position: "top-right" });
|
|
}
|
|
};
|
|
|
|
const renderStepContent = () => {
|
|
switch (currentStep) {
|
|
case 1:
|
|
return (
|
|
<PersonalDetailsForm
|
|
onSubmitStep={handleStepSubmit}
|
|
errors={errors}
|
|
onFieldChange={clearFieldErrors}
|
|
isEditMode={isStep1Update || shouldHideStepper}
|
|
/>
|
|
);
|
|
case 2:
|
|
return (
|
|
<EducationalDetailsForm
|
|
onSubmitStep={handleStepSubmit}
|
|
onSkipStep={shouldHideStepper ? null : handleSkip}
|
|
errors={errors}
|
|
onFieldChange={clearFieldErrors}
|
|
isEditMode={shouldHideStepper}
|
|
/>
|
|
);
|
|
case 3:
|
|
return (
|
|
<FamilyDetailsForm
|
|
onSubmitStep={handleStepSubmit}
|
|
onSkipStep={shouldHideStepper ? null : handleSkip}
|
|
errors={errors}
|
|
onFieldChange={clearFieldErrors}
|
|
isEditMode={shouldHideStepper}
|
|
/>
|
|
);
|
|
case 4:
|
|
return (
|
|
<LifestyleDetailsForm
|
|
onSubmitStep={handleStepSubmit}
|
|
onSkipStep={shouldHideStepper ? null : handleSkip}
|
|
errors={errors}
|
|
onFieldChange={clearFieldErrors}
|
|
isEditMode={shouldHideStepper}
|
|
/>
|
|
);
|
|
case 5:
|
|
return (
|
|
<PartnerPreferencesForm
|
|
onSubmitStep={handleStepSubmit}
|
|
onSkipStep={shouldHideStepper ? null : handleSkip}
|
|
errors={errors}
|
|
onFieldChange={clearFieldErrors}
|
|
isEditMode={shouldHideStepper}
|
|
/>
|
|
);
|
|
case 6:
|
|
return <PreviewScreen onEdit={handleEdit} onSubmit={handleFinalSubmit} />;
|
|
default:
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const checkStepValidity = (stepNum) => {
|
|
if (stepNum === 1) {
|
|
const required = ["name", "mobile", "gender", "height", "marital_status", "profile_for", "caste", "email", "mother_language", "complexion", "physical_status"];
|
|
return required.every(field => personalDetails[field] || personalDetails[field] === 0);
|
|
}
|
|
if (stepNum === 2) {
|
|
const required = ["study_field", "education", "education_detail", "employee_type", "address"];
|
|
if (!educationalDetails.study_field || !educationalDetails.education || !educationalDetails.education_detail || !educationalDetails.employee_type || !educationalDetails.address) return false;
|
|
if (educationalDetails.employee_type !== 11) {
|
|
const workReq = ["occupation", "occupation_detail", "income_currency", "annual_income", "work_country"];
|
|
if (!workReq.every(f => educationalDetails[f])) return false;
|
|
if (educationalDetails.work_country === 1) {
|
|
if (!educationalDetails.work_state || !educationalDetails.work_district) return false;
|
|
} else {
|
|
if (!educationalDetails.work_city) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
if (stepNum === 3) {
|
|
const required = ["fatherName", "motherName", "familyStatus", "nativePlace"];
|
|
return required.every(field => familyDetails[field]);
|
|
}
|
|
if (stepNum === 4) {
|
|
const required = ["diets", "hobbies", "dob", "tob"];
|
|
return required.every(field => {
|
|
const val = lifestyleDetails[field];
|
|
return Array.isArray(val) ? val.length > 0 : !!val;
|
|
});
|
|
}
|
|
if (stepNum === 5) {
|
|
const required = ["ageRange", "castes", "subCastes", "occupations", "educations", "hobbies", "annualIncome", "states", "districts"];
|
|
return required.every(field => {
|
|
const val = partnerPreferences[field];
|
|
return Array.isArray(val) ? val.length > 0 : !!val;
|
|
});
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const getTitle = () => {
|
|
const titles = {
|
|
1: "Personal Details",
|
|
2: "Educational & Professional Details",
|
|
3: "Family Details",
|
|
4: "Lifestyle & Habits",
|
|
5: "Partner Preferences",
|
|
6: "Details Preview",
|
|
};
|
|
return titles[currentStep] || "";
|
|
};
|
|
|
|
return (
|
|
<div className="">
|
|
<div className="max-w-[1400px] mx-auto bg-white ">
|
|
{/* Header */}
|
|
<div className="my-4 rounded-[10px] py-4 w-full max-w-[1200px] mx-auto">
|
|
{!shouldHideStepper && (
|
|
<Stepper
|
|
currentStep={currentStep}
|
|
onStepClick={handleStepClick}
|
|
enabledSteps={enabledSteps}
|
|
completedSteps={completedSteps}
|
|
checkStepValidity={checkStepValidity}
|
|
/> )}
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center p-4 justify-center">
|
|
{/* {currentStep > 1 && currentStep < 6 && (
|
|
<button
|
|
onClick={() => setCurrentStep((prev) => prev - 1)}
|
|
className="mr-3"
|
|
>
|
|
<ChevronLeft size={24} />
|
|
</button>
|
|
)} */}
|
|
<h1 className="text-[24px] font-semibold text-center uppercase py-2 px-3 rounded-5">{getTitle()}</h1>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="pb-6">{renderStepContent()}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default StepperForm;
|