From 5392a4211e1afeed377ac9fc93e16df21a95d26d Mon Sep 17 00:00:00 2001 From: Meenadeveloper Date: Mon, 9 Mar 2026 18:16:38 +0530 Subject: [PATCH] bug fixing --- src/components/common/ProfileHeader.jsx | 15 ++++- src/feature/AdvancedDropzone.jsx | 18 +++--- src/feature/PersonalDetailsForm.jsx | 50 +++++++++------ src/feature/PreviewScreen.jsx | 21 +++++- src/feature/ProfilePreviewPage.jsx | 18 +++++- src/feature/StepperForm.jsx | 85 +++++++++++++++++++++---- src/pages/ContactUsPage.jsx | 9 ++- src/routes/PublicRoutes.jsx | 7 +- 8 files changed, 171 insertions(+), 52 deletions(-) diff --git a/src/components/common/ProfileHeader.jsx b/src/components/common/ProfileHeader.jsx index 9d1a5fa..adcfddb 100644 --- a/src/components/common/ProfileHeader.jsx +++ b/src/components/common/ProfileHeader.jsx @@ -28,12 +28,13 @@ import axiosInstance, { logoutAPI } from "../../api/axiosInstance"; import toast from "react-hot-toast"; import { API_ENDPOINTS } from "../../api/apiEndpoints"; import { useMutation, useQuery } from "@tanstack/react-query"; +import { useSelector } from "react-redux"; const NAV_LINKS = [ // { label: "Home", path: "/" }, { label: "Matches", path: "/matches" }, // { label: "ProfileCard", path: "/profile-card" }, { label: "Interest", path: "/interest" }, - { label: "Horoscope", path: "/horoscoper-generate" }, + // { label: "Horoscope", path: "/horoscoper-generate" }, { label: "Messages", path: "/chat" }, { label: "Search", path: "/matches" }, { label: "Notifications", path: "/notifications" } @@ -159,6 +160,14 @@ const ProfileHeader = () => { const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [logoutModalOpen, setLogoutModalOpen] = useState(false); + const { personalDetails } = useSelector((state) => state.registerform); + + const profileImage = + personalDetails?.profiles?.[0]?.preview || + personalDetails?.profiles?.[0]?.url || + personalDetails?.profiles?.[0] || + userimg; + const theme = useTheme(); const isDesktop = useMediaQuery(theme.breakpoints.up("md")); @@ -200,7 +209,7 @@ const ProfileHeader = () => { const deleteAccountMutation = useMutation({ mutationFn: async () => { - return await axiosInstance.delete(API_ENDPOINTS.DELETE_ACCOUNT); + return await axiosInstance.post(API_ENDPOINTS.DELETE_ACCOUNT); }, onSuccess: (response) => { toast.success(response?.data?.message || "Account deleted successfully"); @@ -372,7 +381,7 @@ const ProfileHeader = () => { - + diff --git a/src/feature/AdvancedDropzone.jsx b/src/feature/AdvancedDropzone.jsx index 789a551..02f1e35 100644 --- a/src/feature/AdvancedDropzone.jsx +++ b/src/feature/AdvancedDropzone.jsx @@ -75,12 +75,12 @@ const AdvancedDropzone = ({ value, onChange }) => { <> Drag'n drop up to 3 images (max 10 MB each)} uploadConfig={{ url: BASE_URL + "/file", cleanOnUpload: true, @@ -107,14 +107,14 @@ const AdvancedDropzone = ({ value, onChange }) => { {hoveredId === file.id && (
{ }} aria-label="Move left" > - + - { title={file.name} > {file.name} - + */}
)} diff --git a/src/feature/PersonalDetailsForm.jsx b/src/feature/PersonalDetailsForm.jsx index cd49dc6..50e77f8 100644 --- a/src/feature/PersonalDetailsForm.jsx +++ b/src/feature/PersonalDetailsForm.jsx @@ -33,7 +33,7 @@ import { } from "../hooks/useDependentMasters"; import { useSendOtp, useVerifyOtp } from "../hooks/useAuth"; const OTP_LENGTH = 4; -const OTP_TIMER_SEC = 120; // 2 minutes +const OTP_TIMER_SEC = 60; // 1 minute const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Update }) => { const dispatch = useDispatch(); @@ -51,25 +51,19 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat const [showConfirmPassword, setShowConfirmPassword] = useState(false); const sendOtp = useSendOtp(); const verifyOtp = useVerifyOtp(); - const [verifiedMobileNumber, setVerifiedMobileNumber] = useState( - () => localStorage.getItem("registration_verified_mobile") || "" - ); - const initializedVerifiedNumber = useRef(false); useEffect(() => { - if (isStep1Update && data.mobileNumber && !initializedVerifiedNumber.current) { - setVerifiedMobileNumber(data.mobileNumber); - localStorage.setItem("registration_verified_mobile", data.mobileNumber); + if (isStep1Update && data.mobileNumber && !data.verifiedMobileNumber) { + dispatch(updatePersonalDetails({ verifiedMobileNumber: data.mobileNumber })); setMobileOtpVerified(true); - initializedVerifiedNumber.current = true; } - }, [isStep1Update, data.mobileNumber]); + }, [isStep1Update, data.mobileNumber, data.verifiedMobileNumber, dispatch]); useEffect(() => { - if (verifiedMobileNumber && data.mobileNumber === verifiedMobileNumber) { + if (data.verifiedMobileNumber && data.mobileNumber === data.verifiedMobileNumber) { setMobileOtpVerified(true); } - }, [verifiedMobileNumber, data.mobileNumber]); + }, [data.verifiedMobileNumber, data.mobileNumber]); const { data: personalMasters, isLoading: isPersonalMastersLoading } = usePersonalDetailsMasters(); @@ -282,11 +276,15 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat }; const handleChange = (field, value) => { + if (field === "name" && !/^[a-zA-Z\s]*$/.test(value)) { + return; + } + const updates = { [field]: value }; const fieldsToClear = [field]; if (field === "mobileNumber") { setMobileNumberError(""); - if (verifiedMobileNumber && value === verifiedMobileNumber) { + if (data.verifiedMobileNumber && value === data.verifiedMobileNumber) { setMobileOtpVerified(true); setShowOtp(false); setOtpError(""); @@ -373,8 +371,7 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat "OTP verified successfully"; toast.success(successMessage, { position: "top-right" }); setMobileOtpVerified(true); - setVerifiedMobileNumber(data.mobileNumber); - localStorage.setItem("registration_verified_mobile", data.mobileNumber); + dispatch(updatePersonalDetails({ verifiedMobileNumber: data.mobileNumber })); setMobileNumberError(""); setOtpError(""); } catch (error) { @@ -387,6 +384,13 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat } }; + useEffect(() => { + if (showOtp && !mobileOtpVerified && isOtpComplete) { + handleOtpSubmit(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [otp]); + // file upload @@ -583,7 +587,7 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat {/* OTP Inputs */} -
+
{showOtp && !mobileOtpVerified && ( <> @@ -595,7 +599,8 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat maxLength: 1, style: { textAlign: "center", - width: 40, + width: 20, + height:20, fontSize: 20, borderRadius: 4, backgroundColor: digit @@ -628,14 +633,14 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat {otpTimer > 0 ? ( ` ${formatTimer(otpTimer) } Seconds` ) : ( - 0} > Resend OTP - + )} @@ -1290,6 +1295,11 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat dispatch(updatePersonalDetails({ profiles: files })); }} /> + {errors.profiles && ( +

+ {errors.profiles} +

+ )}
diff --git a/src/feature/PreviewScreen.jsx b/src/feature/PreviewScreen.jsx index 7b30f24..b633b72 100644 --- a/src/feature/PreviewScreen.jsx +++ b/src/feature/PreviewScreen.jsx @@ -1,5 +1,5 @@ -import React, { useState } from "react"; -import { useSelector } from 'react-redux'; +import React, { useState, useEffect } from "react"; +import { useSelector, useDispatch } from 'react-redux'; import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, Pagination } from "swiper/modules"; import "swiper/css"; @@ -26,6 +26,7 @@ import { import { usePreviewDetails } from "../hooks/usePreview"; import { isAuthenticated } from "../utills/auth"; import horoscopeImg from "../assets/images/horoscopeimg.png"; +import { updatePersonalDetails } from "../redux/registrationFormSlice"; const PreviewScreen = ({ onEdit, onSubmit }) => { const formData = useSelector((state) => state.registerform); @@ -34,6 +35,22 @@ const PreviewScreen = ({ onEdit, onSubmit }) => { const isLoading = isAuth && apiLoading; const isError = isAuth && apiError; + const dispatch = useDispatch(); + + useEffect(() => { + const pd = previewData?.personal_details; + 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 => + typeof img === 'string' ? { url: img, preview: img } : img + ); + dispatch(updatePersonalDetails({ profiles: formattedImages })); + } + } + }, [previewData, dispatch]); + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); const handleConfirmSubmit = () => { diff --git a/src/feature/ProfilePreviewPage.jsx b/src/feature/ProfilePreviewPage.jsx index 9446ef7..e42f207 100644 --- a/src/feature/ProfilePreviewPage.jsx +++ b/src/feature/ProfilePreviewPage.jsx @@ -1,8 +1,9 @@ import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, Autoplay } from "swiper/modules"; import { ChevronLeft, ChevronRight, Edit2 } from "lucide-react"; - +import { useEffect } from "react"; import { useNavigate } from "react-router-dom"; +import { useDispatch } from "react-redux"; import "swiper/css"; import "swiper/css/navigation"; import LazyImage from "../components/common/LazyImage"; @@ -28,6 +29,7 @@ import { } from "@mui/material"; import { Info } from "@mui/icons-material"; import { usePreviewDetails } from "../hooks/usePreview"; +import { updatePersonalDetails } from "../redux/registrationFormSlice"; const images = [ weddingpromo1, // bride in saree @@ -44,8 +46,22 @@ const images = [ const ProfilePreviewPage = () => { const navigate = useNavigate(); + const dispatch = useDispatch(); const { data, isLoading } = usePreviewDetails(); + useEffect(() => { + if (data?.personal_details) { + const pd = data.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 })); + } + } + }, [data, dispatch]); + const personalDetails = data?.personal_details ? { name: data.personal_details.name, diff --git a/src/feature/StepperForm.jsx b/src/feature/StepperForm.jsx index dd9c162..3f4b727 100644 --- a/src/feature/StepperForm.jsx +++ b/src/feature/StepperForm.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { useQuery } from "@tanstack/react-query"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; import { ChevronLeft } from "lucide-react"; import { useDispatch, useSelector } from "react-redux"; import { @@ -28,6 +28,7 @@ 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: [ "name", @@ -228,6 +229,7 @@ const Stepper = ({ currentStep, enabledSteps, completedSteps, onStepClick }) => const StepperForm = () => { const dispatch = useDispatch(); + const queryClient = useQueryClient(); const location = useLocation(); const navigate = useNavigate(); const hideStepperRoutes = ["/profile-edit"]; @@ -434,10 +436,16 @@ const [completedSteps, setCompletedSteps] = useState([]); 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 { const response = await fetch(imageUrl); const blob = await response.blob(); - file = new File([blob], `image-${index}.png`, { type: "image/png" }); + 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); } @@ -446,8 +454,8 @@ const [completedSteps, setCompletedSteps] = useState([]); preview: imageUrl, imageUrl: imageUrl, file: file, - name: `image-${index}.png`, - type: "image/png", + name: fileName, + type: mimeType, valid: true, }; }) @@ -669,13 +677,16 @@ useEffect(()=>{ required.forEach((field) => { if (!personalDetails[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 (!isStep1Update) { - if (!personalDetails.password) newErrors.password = "This field is required"; - if (!personalDetails.confirmPassword) newErrors.confirmPassword = "This field is required"; + if (!personalDetails.password) newErrors.password = "Password is required"; + if (!personalDetails.confirmPassword) newErrors.confirmPassword = "Confirm Password is required"; } if ( @@ -733,19 +744,44 @@ useEffect(()=>{ 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) newErrors[field] = "This field is required"; + if (value.length === 0) { + if (field === "hobbies") { + newErrors[field] = "Hobbies and Interests is required"; + } else { + newErrors[field] = "This field is required"; + } + } + } else if (!value) { - // newErrors[field] = "This field is required"; + + 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) { @@ -764,7 +800,11 @@ useEffect(()=>{ const value = partnerPreferences[field]; if (Array.isArray(value)) { if (value.length === 0) { - 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`; } return; } @@ -823,8 +863,10 @@ useEffect(()=>{ 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 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); @@ -977,6 +1019,23 @@ useEffect(()=>{ if (isStep1Update) { res = await apiForFiles.post("/update_personal_details", payload); + + try { + const previewData = await getPreviewDetails(); + 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: ["previewDetails"] }); + } catch (error) { + console.error("Error refreshing preview details:", error); + } } else { res = await registerStep1.mutateAsync(payload); } diff --git a/src/pages/ContactUsPage.jsx b/src/pages/ContactUsPage.jsx index f1a7ce8..4d049f8 100644 --- a/src/pages/ContactUsPage.jsx +++ b/src/pages/ContactUsPage.jsx @@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'; import { getContactUs } from '../api/contact.api'; import LazyImage from '../components/common/LazyImage'; import InstagramIcon from '@mui/icons-material/Instagram'; +import WhatsAppIcon from '@mui/icons-material/WhatsApp'; import FacebookIcon from '@mui/icons-material/Facebook'; import SvgIcon from '@mui/material/SvgIcon'; import { Phone, Mail, ChevronRight } from 'lucide-react'; @@ -39,7 +40,13 @@ const ContactUsPage = () => { icon: , color: "from-gray-800 to-black", url: contact.x_url - } + }, + { + name: "WhatsApp", + icon: , + color: "from-green-500 to-green-600", + url: contact.whatsapp_mobile ? `https://wa.me/${contact.whatsapp_mobile}` : null + }, ]; if (isLoading) { diff --git a/src/routes/PublicRoutes.jsx b/src/routes/PublicRoutes.jsx index 10cd86a..227bb13 100644 --- a/src/routes/PublicRoutes.jsx +++ b/src/routes/PublicRoutes.jsx @@ -19,9 +19,10 @@ const PublicRoutes = () => { <> }> } /> - }> - } /> - + {/* }> + */} + } /> + }>