diff --git a/src/assets/images/horoscopeimg.png b/src/assets/images/horoscopeimg.png new file mode 100644 index 0000000..a5d9b82 Binary files /dev/null and b/src/assets/images/horoscopeimg.png differ diff --git a/src/feature/EducationalDetailsForm.jsx b/src/feature/EducationalDetailsForm.jsx index ab7669a..d60edeb 100644 --- a/src/feature/EducationalDetailsForm.jsx +++ b/src/feature/EducationalDetailsForm.jsx @@ -447,11 +447,13 @@ const EducationalDetailsForm = ({ justifyContent: "center", }} > - + {onSkipStep && ( + + )} diff --git a/src/feature/FamilyDetailsForm.jsx b/src/feature/FamilyDetailsForm.jsx index d8b14c6..168d1ae 100644 --- a/src/feature/FamilyDetailsForm.jsx +++ b/src/feature/FamilyDetailsForm.jsx @@ -415,11 +415,13 @@ const FamilyDetailsForm = ({ onSubmitStep, onSkipStep, errors, onFieldChange }) xs={12} sx={{ marginTop: 10, display: "flex", gap: 4, justifyContent: "center" }} > - + {onSkipStep && ( + + )} diff --git a/src/feature/LifestyleDetailsForm.jsx b/src/feature/LifestyleDetailsForm.jsx index 71e6166..a3ccddf 100644 --- a/src/feature/LifestyleDetailsForm.jsx +++ b/src/feature/LifestyleDetailsForm.jsx @@ -186,7 +186,7 @@ const LifestyleDetailsForm = ({
Select Diet @@ -194,21 +194,10 @@ const LifestyleDetailsForm = ({ labelId="diets-label" label="Select Diet" name="diets" - multiple value={data.diets} - onChange={(e) => handleMultiChange("diets", e.target.value)} + onChange={(e) => handleChange("diets", e.target.value)} inputRef={inputRef} disabled={isLifestyleMastersLoading} - renderValue={(selected) => - selected - .map((id) => { - const item = dietOptions.find( - (opt) => (opt.id ?? opt) === id - ); - return item?.diet_name || item?.name || id; - }) - .join(", ") - } sx={{ "& .MuiSelect-select.Mui-disabled": { cursor: "not-allowed", @@ -220,8 +209,7 @@ const LifestyleDetailsForm = ({ const label = opt.diet_name || opt.name || String(opt); return ( - -1} /> - + {label} ); })} @@ -406,11 +394,13 @@ const LifestyleDetailsForm = ({ justifyContent: "center", }} > - + {onSkipStep && ( + + )} diff --git a/src/feature/PartnerPreferencesForm.jsx b/src/feature/PartnerPreferencesForm.jsx index 7c3757a..55ceb23 100644 --- a/src/feature/PartnerPreferencesForm.jsx +++ b/src/feature/PartnerPreferencesForm.jsx @@ -417,11 +417,13 @@ const PartnerPreferencesForm = ({ justifyContent: "center", }} > - + {onSkipStep && ( + + )} diff --git a/src/feature/PersonalDetailsForm.jsx b/src/feature/PersonalDetailsForm.jsx index 08c83da..2f9648c 100644 --- a/src/feature/PersonalDetailsForm.jsx +++ b/src/feature/PersonalDetailsForm.jsx @@ -1290,7 +1290,7 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat onClick={handleSubmit} // disabled={!mobileOtpVerified} > - Submit + Next diff --git a/src/feature/ProfilePreviewPage.jsx b/src/feature/ProfilePreviewPage.jsx index cee037c..9446ef7 100644 --- a/src/feature/ProfilePreviewPage.jsx +++ b/src/feature/ProfilePreviewPage.jsx @@ -2,7 +2,6 @@ import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, Autoplay } from "swiper/modules"; import { ChevronLeft, ChevronRight, Edit2 } from "lucide-react"; -import { useSelector, useDispatch } from "react-redux"; import { useNavigate } from "react-router-dom"; import "swiper/css"; import "swiper/css/navigation"; @@ -13,9 +12,8 @@ import weddingpromo2 from "../assets/images/weddingpromo2.jpg"; import weddingpromo3 from "../assets/images/weddingpromo3.jpg"; import weddingpromo4 from "../assets/images/weddingpromo4.jpg"; +import horoscopeImg from "../assets/images/horoscopeimg.png"; import ProfileCompletion from "../components/profiledashboard/ProfileCompletion"; -import { preloadDummyProfile } from "../redux/registrationFormSlice"; -import { useEffect } from "react"; import { Box, Button, @@ -25,8 +23,11 @@ import { Divider, IconButton, Typography, + Grid, + Tooltip, } from "@mui/material"; import { Info } from "@mui/icons-material"; +import { usePreviewDetails } from "../hooks/usePreview"; const images = [ weddingpromo1, // bride in saree @@ -42,20 +43,81 @@ const images = [ ]; const ProfilePreviewPage = () => { - const dispatch = useDispatch(); const navigate = useNavigate(); - const { - personalDetails, - educationalDetails, - familyDetails, - lifestyleDetails, - partnerPreferences, - } = useSelector((state) => state.registerform); + const { data, isLoading } = usePreviewDetails(); - // For dummy data on first visit - useEffect(() => { - dispatch(preloadDummyProfile()); - }, [dispatch]); + const personalDetails = data?.personal_details + ? { + name: data.personal_details.name, + mobileNumber: data.personal_details.mobile, + gender: data.personal_details.gender, + dob: data.personal_details.dob + ? data.personal_details.dob.split("T")[0] + : "", + height: data.personal_details.height, + weight: data.personal_details.weight, + maritalStatus: data.personal_details.marital_status, + religion: data.personal_details.religion, + caste: data.personal_details.caste, + email: data.personal_details.email, + state: data.personal_details.state, + city: data.personal_details.district, + pincode: data.personal_details.pincode, + profiles: + data.personal_details.images?.map((url) => ({ preview: url })) || [], + } + : {}; + + const educationalDetails = data?.educational_details + ? { + qualification: data.educational_details.education, + fieldOfStudy: data.educational_details.study_field, + collegeName: data.educational_details.college_name, + occupation: data.educational_details.occupation, + organization: data.educational_details.company_name, + employeeType: data.educational_details.employee_type, + income: data.educational_details.annual_income, + workLocation: data.educational_details.work_location, + } + : {}; + + const familyDetails = data?.family_details + ? { + fatherName: data.family_details.father_name, + fatherOccupation: data.family_details.father_occupation, + motherName: data.family_details.mother_name, + motherOccupation: data.family_details.mother_occupation, + brotherCount: data.family_details.brother_count, + sisterCount: data.family_details.sister_count, + familyStatus: data.family_details.family_status, + nativePlace: data.family_details.native_place, + } + : {}; + + const lifestyleDetails = data?.lifestyle_details + ? { + diets: data.lifestyle_details.diet, + hobbies: data.lifestyle_details.hobbies, + dob: data.lifestyle_details.date_of_birth_formated, + tob: data.lifestyle_details.time_of_birth_formated, + placeOfBirth: data.lifestyle_details.place_of_birth, + horoscope: data.lifestyle_details.horoscope, + } + : {}; + + const partnerPreferences = data?.partner_preferences + ? { + ageRange: data.partner_preferences.preferred_age_range, + castes: data.partner_preferences.preferred_castes, + subCastes: data.partner_preferences.preferred_sub_castes, + occupations: data.partner_preferences.preferred_occupations, + educations: data.partner_preferences.preferred_educations, + hobbies: data.partner_preferences.preferred_hobbies, + annualIncome: data.partner_preferences.preferred_annual_income, + states: data.partner_preferences.preferred_states, + districts: data.partner_preferences.preferred_districts, + } + : {}; const handleEditSection = (stepNum) => { navigate("/profile-edit", { state: { step: stepNum } }); @@ -64,7 +126,7 @@ const ProfilePreviewPage = () => { const renderField = (label, value, stepNum = 1) => ( { ); + const renderChartGrid = (getDataForCell, title) => { + const renderCell = (i) => { + const items = getDataForCell(i); + const label = Array.isArray(items) ? items.join(", ") : ""; + + return ( + + 0 ? "help" : "default", + }} + > + {items && items.length > 2 ? ( + + {items.slice(0, 2).join(", ")} + + +{items.length - 2} + + + ) : ( + label + )} + + + ); + }; + + return ( + + + {title} + + + {renderCell(1)} {renderCell(2)} {renderCell(3)} {renderCell(4)} + {renderCell(5)} + + + + {renderCell(6)} + {renderCell(7)} {renderCell(8)} + {renderCell(9)} {renderCell(10)} {renderCell(11)} {renderCell(12)} + + + ); + }; + const renderPersonalSection = () => ( - + @@ -120,9 +293,9 @@ const ProfilePreviewPage = () => { } - sx={{ padding: "15px 15px", background: "#ffff" }} + sx={{ padding: "15px 15px", background: "#f2f2f2" }} /> - + {/* */} {renderField("Name", personalDetails.name, 1)} @@ -142,7 +315,7 @@ const ProfilePreviewPage = () => { {/* Profile Images */} { ); const renderEducationalSection = () => ( - + @@ -227,9 +400,9 @@ const ProfilePreviewPage = () => { } - sx={{ padding: "15px 15px", background: "#f5fbff" }} + sx={{ padding: "15px 15px", background: "#f2f2f2" }} /> - + {/* */} {renderField("Qualification", educationalDetails.qualification, 2)} @@ -245,7 +418,7 @@ const ProfilePreviewPage = () => { ); const renderFamilySection = () => ( - + @@ -256,15 +429,15 @@ const ProfilePreviewPage = () => { handleEditSection(2)} + onClick={() => handleEditSection(3)} size="large" > } - sx={{ padding: "15px 15px", background: "#f5fbff" }} + sx={{ padding: "15px 15px", background: "#f2f2f2" }} /> - + {/* */} {renderField("Father Name", familyDetails.fatherName)} @@ -280,7 +453,7 @@ const ProfilePreviewPage = () => { ); const renderLifestyleSection = () => ( - + @@ -291,15 +464,15 @@ const ProfilePreviewPage = () => { handleEditSection(2)} + onClick={() => handleEditSection(4)} size="large" > } - sx={{ padding: "15px 15px", background: "#f5fbff" }} + sx={{ padding: "15px 15px", background: "#f2f2f2" }} /> - + {/* */} {renderField( @@ -317,32 +490,61 @@ const ProfilePreviewPage = () => { {renderField("Date of Birth", lifestyleDetails.dob)} {renderField("Time of Birth", lifestyleDetails.tob)} {renderField("Place of Birth", lifestyleDetails.placeOfBirth)} + {lifestyleDetails.horoscope && ( + + + Horoscope Details + + + + {renderChartGrid((i) => { + const val = lifestyleDetails.horoscope[`graha_${i}`]; + return val ? val.split(",") : []; + }, "Rasi")} + + + {renderChartGrid((i) => { + const val = lifestyleDetails.horoscope[`amsam_${i}`]; + return val ? val.split(",") : []; + }, "Navamsam")} + + + + )} ); const renderPreferenceSection = () => ( - + - Lifestyle Details + Partner Preferences } action={ handleEditSection(2)} + onClick={() => handleEditSection(5)} size="large" > } - sx={{ padding: "15px 15px", background: "#f5fbff" }} + sx={{ padding: "15px 15px", background: "#f2f2f2" }} /> - + {/* */} {renderField("Age Range", partnerPreferences.ageRange)} @@ -394,6 +596,14 @@ const ProfilePreviewPage = () => { ); + if (isLoading) { + return ( + + Loading Profile... + + ); + } + return ( <>
diff --git a/src/feature/StepperForm.jsx b/src/feature/StepperForm.jsx index f80c0f4..1b97cb3 100644 --- a/src/feature/StepperForm.jsx +++ b/src/feature/StepperForm.jsx @@ -28,7 +28,6 @@ import axiosInstance, { apiForFiles, setAccessToken } from "../api/axiosInstance import toast from "react-hot-toast"; import { API_ENDPOINTS } from "../api/apiEndpoints"; import { isAuthenticated } from "../utills/auth"; - const STEP_FIELD_ORDER = { 1: [ "name", @@ -119,8 +118,59 @@ const STEP1_SERVER_FIELD_MAP = { profiles: "profiles", profile_images: "profiles", }; -const Stepper = ({ currentStep, onStepClick }) => { - + + +// 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 ( +//
+// {steps.map((step, index) => { +// const isEnabled = enabledSteps.includes(step.num); + +// return ( +// +//
isEnabled && onStepClick(step.num)} +// > +//
= step.num +// ? "bg-red-600 text-white" +// : "bg-gray-300 text-gray-600" +// }`} +// > +// {step.num} +//
+//
+ +// {index < steps.length - 1 && ( +//
step.num ? "bg-red-600" : "bg-gray-300" +// }`} +// /> +// )} +// +// ); +// })} +//
+// ); +// }; + +import { Check } from "lucide-react"; + +const Stepper = ({ currentStep, enabledSteps, completedSteps, onStepClick }) => { const steps = [ { num: 1, label: "Personal" }, @@ -133,31 +183,45 @@ const Stepper = ({ currentStep, onStepClick }) => { return (
- {steps.map((step, index) => ( - -
onStepClick(step.num)} - > + {steps.map((step, index) => { + const isEnabled = enabledSteps.includes(step.num); + const isCompleted = completedSteps.includes(step.num); + + return ( +
= step.num - ? "bg-red-600 text-white" - : "bg-gray-300 text-gray-600" + className={`flex flex-col items-center ${ + isEnabled ? "cursor-pointer" : "cursor-not-allowed opacity-50" }`} + onClick={() => isEnabled && onStepClick(step.num)} > - {step.num} +
+ {isCompleted ? : step.num} +
+ + {step.label}
-
- {index < steps.length - 1 && ( -
step.num ? "bg-red-600" : "bg-gray-300" - }`} - /> - )} - - ))} + + {index < steps.length - 1 && ( +
+ )} + + ); + })}
); }; @@ -166,7 +230,9 @@ const StepperForm = () => { const dispatch = useDispatch(); const location = useLocation(); const navigate = useNavigate(); +const hideStepperRoutes = ["/profile-edit"]; +const shouldHideStepper = hideStepperRoutes.includes(location.pathname); const personalDetails = useSelector( (state) => state.registerform.personalDetails ); @@ -188,7 +254,8 @@ const StepperForm = () => { const savedStep = localStorage.getItem("registration_current_step"); return savedStep ? Number(savedStep) : 1; }); - +const [enabledSteps, setEnabledSteps] = useState([1]); +const [completedSteps, setCompletedSteps] = useState([]); const [isStep1Update, setIsStep1Update] = useState(false); const [errors, setErrors] = useState({}); const isAuth = isAuthenticated(); @@ -340,6 +407,7 @@ const StepperForm = () => { // 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]); @@ -530,7 +598,7 @@ useEffect(()=>{ dispatch( updateLifestyleDetails({ - diets: ld.diet_id ? [ld.diet_id] : [], + diets: ld.diet_id || "", hobbies: ld.hobbies_ids || [], dob: ld.date_of_birth || "", tob: ld.time_of_birth ? ld.time_of_birth.substring(0, 5) : "", @@ -644,7 +712,10 @@ useEffect(()=>{ ]; required.forEach((field) => { if (!educationalDetails[field]) { - newErrors[field] = "This field is required"; + const label = field + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); + newErrors[field] = `${label} is required`; } }); } else if (step === 3) { @@ -655,7 +726,11 @@ useEffect(()=>{ ]; required.forEach((field) => { if (!familyDetails[field]) { - newErrors[field] = "This field is required"; + // newErrors[field] = "This field is required"; + const label = field + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); + newErrors[field] = `${label} is required`; } }); } else if (step === 4) { @@ -665,7 +740,12 @@ useEffect(()=>{ if (Array.isArray(value)) { if (value.length === 0) newErrors[field] = "This field is required"; } else if (!value) { - newErrors[field] = "This field is required"; + // newErrors[field] = "This field is required"; + const label = field + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); + newErrors[field] = `${label} is required`; + } }); } else if (step === 5) { @@ -689,7 +769,11 @@ useEffect(()=>{ return; } if (!value) { - newErrors[field] = "This field is required"; + // newErrors[field] = "This field is required"; + const label = field + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); + newErrors[field] = `${label} is required`; } }); } @@ -806,9 +890,7 @@ useEffect(()=>{ formData.append("tob", lifestyleDetails.tob || ""); formData.append("place_of_birth", lifestyleDetails.placeOfBirth || ""); - (lifestyleDetails.diets || []).forEach((id, index) => { - formData.append(`diets[${index}]`, id); - }); + formData.append("diet", lifestyleDetails.diets || ""); (lifestyleDetails.hobbies || []).forEach((id, index) => { formData.append(`hobbies[${index}]`, id); @@ -882,77 +964,128 @@ useEffect(()=>{ null; const handleStepSubmit = async () => { - const isValid = validateStep(currentStep); - if (!isValid) return; + const isValid = validateStep(currentStep); + if (!isValid) return; + + try { + let payload; + let res; + + switch (currentStep) { + case 1: + payload = await buildRegisterStep1Payload(); - try { - if (currentStep === 1) { - const payload = await buildRegisterStep1Payload(); - let res; if (isStep1Update) { res = await apiForFiles.post("/update_personal_details", payload); } else { res = await registerStep1.mutateAsync(payload); } - + const token = extractAccessToken(res.data || res); - if (token) { - setAccessToken(token); - } - } else if (currentStep === 2) { - const payload = buildRegisterStep2Payload(); + if (token) setAccessToken(token); + break; + + case 2: + payload = buildRegisterStep2Payload(); await registerStep2.mutateAsync(payload); - } else if (currentStep === 3) { - const payload = buildRegisterStep3Payload(); + break; + + case 3: + payload = buildRegisterStep3Payload(); await registerStep3.mutateAsync(payload); - } else if (currentStep === 4) { - const payload = buildRegisterStep4Payload(); + break; + + case 4: + payload = buildRegisterStep4Payload(); await registerStep4.mutateAsync(payload); - } else if (currentStep === 5) { - const payload = buildRegisterStep5Payload(); + break; + + case 5: + payload = buildRegisterStep5Payload(); await registerStep5.mutateAsync(payload); - } - setErrors({}); - setCurrentStep((prev) => Math.min(prev + 1, 6)); - window.scrollTo(0, 0); - } catch (e) { - const serverErrors = mapServerErrors(e); - let handled = false; + break; - 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]); - // For step 1, we know mapping is correct so field errors are sufficient feedback - if (currentStep === 1) handled = true; - } - if (serverErrors._form) { - toast.error(serverErrors._form, { position: "top-right" }); - handled = true; - } + default: + break; + } + + // 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 (!handled) { - const msg = e?.response?.data?.message || e?.message || "Failed to submit step. Please try again."; - toast.error(msg, { position: "top-right" }); + if (serverErrors._form) { + toast.error(serverErrors._form, { position: "top-right" }); + handled = true; } } - }; - const handleSkip = () => { - setErrors({}); - setCurrentStep((prev) => Math.min(prev + 1, 6)); - window.scrollTo(0, 0); - }; + if (!handled) { + const msg = + e?.response?.data?.message || + e?.message || + "Failed to submit step. Please try again."; - const handleStepClick = (step) => { - setCurrentStep(step); - setErrors({}); - window.scrollTo(0, 0); - }; + toast.error(msg, { position: "top-right" }); + } + } +}; + + const handleSkip = () => { + setErrors({}); + + 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 (!enabledSteps.includes(step)) return; + + setCurrentStep(step); + setErrors({}); + window.scrollTo(0, 0); +}; + +useEffect(() => { + if (currentStep === 6) { + setEnabledSteps([1, 2, 3, 4, 5, 6]); + } +}, [currentStep]); const handleEdit = (step) => { setCurrentStep(step); @@ -961,12 +1094,10 @@ useEffect(()=>{ }; const handleFinalSubmit = async () => { - for (let i = 1; i <= 5; i++) { - const ok = validateStep(i); - if (!ok) { - setCurrentStep(i); - return; - } + const ok = validateStep(1); + if (!ok) { + setCurrentStep(1); + return; } try { @@ -995,7 +1126,7 @@ useEffect(()=>{ return ( @@ -1004,7 +1135,7 @@ useEffect(()=>{ return ( @@ -1013,7 +1144,7 @@ useEffect(()=>{ return ( @@ -1022,7 +1153,7 @@ useEffect(()=>{ return ( @@ -1051,8 +1182,13 @@ useEffect(()=>{
{/* Header */}
- - +{!shouldHideStepper && ( + )}
{currentStep > 1 && currentStep < 6 && ( )} -

{getTitle()}

+

{getTitle()}