diff --git a/src/api/apiEndpoints.js b/src/api/apiEndpoints.js index b31f80e..96b848e 100644 --- a/src/api/apiEndpoints.js +++ b/src/api/apiEndpoints.js @@ -27,4 +27,12 @@ REGISTER_STEP5:"update_preferred_details", // partner preference details updat PREVIEW_DETAILS: "get_preview_details", REVIEWS: "reviews", +// edit api's autopapulated + +EDIT_PERSONAL_DETAILS: "get_personal_details", +EDIT_EDUCATION_DETAILS: "get_educational_details", +EDIT_FAMILY_DETAILS: "get_family_details", +EDIT_LIFESTYLE_DETAILS: "get_lifestyle_details", +EDIT_PREFERED_PARTNER_DETAILS: "get_preferred_details", + }; diff --git a/src/feature/AdvancedDropzone.jsx b/src/feature/AdvancedDropzone.jsx index 5c644b0..789a551 100644 --- a/src/feature/AdvancedDropzone.jsx +++ b/src/feature/AdvancedDropzone.jsx @@ -91,8 +91,6 @@ const AdvancedDropzone = ({ value, onChange }) => { actionButtons={{ position: "after", abortButton: {}, - deleteButton: {}, - uploadButton: {}, }} > {extFiles.map((file, index) => ( diff --git a/src/feature/EducationalDetailsForm.jsx b/src/feature/EducationalDetailsForm.jsx index 081ca5a..56758da 100644 --- a/src/feature/EducationalDetailsForm.jsx +++ b/src/feature/EducationalDetailsForm.jsx @@ -106,7 +106,7 @@ const EducationalDetailsForm = ({ return ( <> -
+
{/* Field of Study */} diff --git a/src/feature/FamilyDetailsForm.jsx b/src/feature/FamilyDetailsForm.jsx index bc59430..d8b14c6 100644 --- a/src/feature/FamilyDetailsForm.jsx +++ b/src/feature/FamilyDetailsForm.jsx @@ -209,7 +209,7 @@ const FamilyDetailsForm = ({ onSubmitStep, onSkipStep, errors, onFieldChange }) }; return ( -
+
diff --git a/src/feature/LifestyleDetailsForm.jsx b/src/feature/LifestyleDetailsForm.jsx index 037ce19..71e6166 100644 --- a/src/feature/LifestyleDetailsForm.jsx +++ b/src/feature/LifestyleDetailsForm.jsx @@ -181,7 +181,7 @@ const LifestyleDetailsForm = ({ }; return ( -
+
@@ -316,6 +316,7 @@ const LifestyleDetailsForm = ({ handleChange("dob", formatDate(value))} slotProps={{ diff --git a/src/feature/PartnerPreferencesForm.jsx b/src/feature/PartnerPreferencesForm.jsx index ce5cd32..7c3757a 100644 --- a/src/feature/PartnerPreferencesForm.jsx +++ b/src/feature/PartnerPreferencesForm.jsx @@ -198,7 +198,7 @@ const PartnerPreferencesForm = ({ ); return ( -
+
{/* Age Range */} diff --git a/src/feature/PersonalDetailsForm.jsx b/src/feature/PersonalDetailsForm.jsx index 0ae8fca..8ef768a 100644 --- a/src/feature/PersonalDetailsForm.jsx +++ b/src/feature/PersonalDetailsForm.jsx @@ -433,7 +433,7 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat const parseDobValue = data.dob ? new Date(data.dob) : null; return ( <> -
+
{/* Name */} @@ -648,6 +648,7 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat { let formatted = ""; diff --git a/src/feature/PreviewScreen.jsx b/src/feature/PreviewScreen.jsx index 47fa9d2..53925e5 100644 --- a/src/feature/PreviewScreen.jsx +++ b/src/feature/PreviewScreen.jsx @@ -16,12 +16,17 @@ import { IconButton, Button, Grid, + Tooltip, } from '@mui/material'; import { usePreviewDetails } from "../hooks/usePreview"; +import { isAuthenticated } from "../utills/auth"; const PreviewScreen = ({ onEdit, onSubmit }) => { const formData = useSelector((state) => state.registerform); - const { data: previewData, isLoading, isError } = usePreviewDetails(); + const isAuth = isAuthenticated(); + const { data: previewData, isLoading: apiLoading, isError: apiError } = usePreviewDetails(isAuth); + const isLoading = isAuth && apiLoading; + const isError = isAuth && apiError; const sections = previewData?.personal_details ? [ @@ -136,6 +141,59 @@ const PreviewScreen = ({ onEdit, onSubmit }) => { return value || "-"; }; + 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)} + + {title.toUpperCase()} + + {renderCell(6)} + {renderCell(7)} {renderCell(8)} + {renderCell(9)} {renderCell(10)} {renderCell(11)} {renderCell(12)} + + + ); + }; + return ( @@ -188,6 +246,50 @@ const PreviewScreen = ({ onEdit, onSubmit }) => { {Object.entries(section.data || {}).map(([key, value]) => { + // Filter out ID fields + if (key === 'id' || key.endsWith('_id') || key.endsWith('Id') || key.endsWith('_ids') || key.endsWith('Ids') || key === 'created_at' || key === 'updated_at') { + return null; + } + + // Handle Horoscope Chart (Server Data) + if (key === 'horoscope' && value && typeof value === 'object') { + return ( + + Horoscope Details + + + {renderChartGrid((i) => { + const val = value[`graha_${i}`]; + return val ? val.split(',') : []; + }, 'Rasi')} + + + {renderChartGrid((i) => { + const val = value[`amsam_${i}`]; + return val ? val.split(',') : []; + }, 'Navamsam')} + + + + ); + } + + // Handle Horoscope Chart (Redux Data) + if (key === 'graha' && value) { + return ( + + {renderChartGrid((i) => value[i] || [], 'Rasi')} + + ); + } + if (key === 'amsam' && value) { + return ( + + {renderChartGrid((i) => value[i] || [], 'Navamsam')} + + ); + } + // Handle brothers and sisters arrays specifically if ((key === 'brothers' || key === 'sisters') && Array.isArray(value) && value.length > 0) { return ( @@ -229,7 +331,17 @@ const PreviewScreen = ({ onEdit, onSubmit }) => { - {Object.entries(sibling).map(([sKey, sVal]) => ( + {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') { + if (sVal === true || sVal === 1 || sVal === '1') { + displayValue = 'Yes'; + } else if (sVal === false || sVal === 0 || sVal === '0') { + displayValue = 'No'; + } + } + return ( {sKey @@ -239,10 +351,10 @@ const PreviewScreen = ({ onEdit, onSubmit }) => { .trim()} - {sVal || '-'} + {displayValue ?? '-'} - ))} + )})} diff --git a/src/feature/StepperForm.jsx b/src/feature/StepperForm.jsx index 7c65f25..38593b5 100644 --- a/src/feature/StepperForm.jsx +++ b/src/feature/StepperForm.jsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from "react"; +import { useQuery } from "@tanstack/react-query"; import { ChevronLeft } from "lucide-react"; import { useDispatch, useSelector } from "react-redux"; import { @@ -15,7 +16,7 @@ import FamilyDetailsForm from "./FamilyDetailsForm"; import LifestyleDetailsForm from "./LifestyleDetailsForm"; import PartnerPreferencesForm from "./PartnerPreferencesForm"; import PreviewScreen from "./PreviewScreen"; -import { useLocation } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import { useRegisterStep1, useRegisterStep2, @@ -23,8 +24,10 @@ import { useRegisterStep4, useRegisterStep5, } from "../hooks/useRegister"; -import axiosInstance, { setAccessToken } from "../api/axiosInstance"; +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: [ @@ -162,6 +165,7 @@ const Stepper = ({ currentStep, onStepClick }) => { const StepperForm = () => { const dispatch = useDispatch(); const location = useLocation(); + const navigate = useNavigate(); const personalDetails = useSelector( (state) => state.registerform.personalDetails @@ -177,11 +181,17 @@ const StepperForm = () => { (state) => state.registerform.partnerPreferences ); - const initialStep = location.state?.step || 1; + 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 [currentStep, setCurrentStep] = useState(initialStep); const [isStep1Update, setIsStep1Update] = useState(false); const [errors, setErrors] = useState({}); + const isAuth = isAuthenticated(); const registerStep1 = useRegisterStep1(); const registerStep2 = useRegisterStep2(); @@ -189,6 +199,10 @@ const StepperForm = () => { 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(); @@ -331,19 +345,45 @@ const StepperForm = () => { }, [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]); + + useEffect(() => { + if (!isAuth) return; const fetchPersonalDetails = async () => { try { - const response = await axiosInstance.get("/get_personal_details"); + const response = await axiosInstance.get(API_ENDPOINTS.EDIT_PERSONAL_DETAILS); const data = response.data; if (data.status === "success" && data.personal_details) { const pd = data.personal_details; setIsStep1Update(true); - const mappedImages = (pd.images || []).map((url, index) => ({ - id: `server-${index}`, - preview: url, - name: `image-${index}.png`, - })); + 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; + try { + const response = await fetch(imageUrl); + const blob = await response.blob(); + file = new File([blob], `image-${index}.png`, { type: "image/png" }); + } catch (error) { + console.error("Error converting image URL to File:", error); + } + return { + id: `server-${index}`, + preview: imageUrl, + imageUrl: imageUrl, + file: file, + name: `image-${index}.png`, + type: "image/png", + valid: true, + }; + }) + ); const formattedDob = pd.dob ? pd.dob.split("T")[0] : ""; @@ -376,7 +416,167 @@ const StepperForm = () => { } }; fetchPersonalDetails(); - }, [dispatch]); + }, [dispatch, isAuth]); + + +// 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, + retry: false, + refetchOnWindowFocus: false, + }); + + +useEffect(() => { + if (educationalData?.status === "success" && educationalData?.educational_details) { + const ed = educationalData.educational_details; + dispatch( + updateEducationalDetails({ + fieldOfStudy: ed.study_field_id || "", + qualification: ed.education_id || "", + collegeName: ed.college_name || "", + occupation: ed.occupation_id || "", + organization: ed.company_name || "", + employeeType: ed.employee_type_id || "", + income: ed.annual_income_id || "", + workLocation: 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, + retry:false, + refetchOnWindowFocus:false, +}); + + +useEffect(()=>{ + if (familyData?.status === "success" && familyData?.family_details) { + const fd = familyData.family_details; + + const mappedBrothers = (fd.brothers || []).map((b) => ({ + name: b.name || "", + occupation: b.occupation_name || "", + maritalStatus: b.marital_status || "", + haveChildrens: b.have_childrens === true ? 1 : (b.have_childrens === false ? 0 : b.have_childrens), + })); + + const mappedSisters = (fd.sisters || []).map((s) => ({ + name: s.name || "", + occupation: s.occupation_name || "", + maritalStatus: s.marital_status || "", + haveChildrens: s.have_childrens === true ? 1 : (s.have_childrens === false ? 0 : s.have_childrens), + })); + + 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 || "", + brotherCount: fd.brother_count || 0, + sisterCount: fd.sister_count || 0, + brothers: mappedBrothers, + sisters: mappedSisters, + }) + ); + } + +},[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, + retry: false, + refetchOnWindowFocus: false, + }); + + 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 ? [ld.diet_id] : [], + hobbies: ld.hobbies_ids || [], + dob: ld.date_of_birth || "", + tob: ld.time_of_birth ? ld.time_of_birth.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, + 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]); + + + + @@ -528,29 +728,34 @@ const StepperForm = () => { formData.append("web_fcm_token", localStorage.getItem("fcm_token") || ""); if (personalDetails.profiles && Array.isArray(personalDetails.profiles)) { - for (const [index, item] of personalDetails.profiles.entries()) { - // If the file object is intact (e.g., just added), use it directly. - if (item.file && (item.file instanceof Blob || (typeof item.file === 'object' && item.file !== null && item.file.name))) { - formData.append(`profile_images[${index}]`, item.file); - } - // If the file object was lost due to Redux serialization, - // try to recover it from the blob URL preview. - else if ( - item.preview && - typeof item.preview === "string" - ) { - try { - const response = await fetch(item.preview); - const blob = await response.blob(); - const fileName = item.name || `profile_image_${index}.jpg`; - const fileType = item.type || "image/jpeg"; - const recoveredFile = new File([blob], fileName, { type: fileType }); - formData.append(`profile_images[${index}]`, recoveredFile); - } catch (e) { - console.error(`Could not recover file from blob URL: ${item.preview}`, e); + 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 fileName = item.name || `profile_image_${index}.jpg`; + const fileType = item.type || "image/jpeg"; + 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); + } + }) + ); } return formData; }; @@ -686,7 +891,7 @@ const StepperForm = () => { const payload = await buildRegisterStep1Payload(); let res; if (isStep1Update) { - res = await axiosInstance.post("/update_personal_details", payload); + res = await apiForFiles.post("/update_personal_details", payload); } else { res = await registerStep1.mutateAsync(payload); } @@ -769,6 +974,8 @@ const StepperForm = () => { // 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" }); } @@ -844,7 +1051,7 @@ const StepperForm = () => {
{/* Header */} -
+