form update
This commit is contained in:
parent
135f6bba48
commit
8f6ddbcb2c
BIN
src/assets/images/horoscopeimg.png
Normal file
BIN
src/assets/images/horoscopeimg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 788 KiB |
@ -447,11 +447,13 @@ const EducationalDetailsForm = ({
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Button variant="outlined" onClick={onSkipStep}>
|
||||
Next
|
||||
</Button>
|
||||
{onSkipStep && (
|
||||
<Button variant="outlined" onClick={onSkipStep}>
|
||||
Skip
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="contained" color="primary" onClick={handleSubmit}>
|
||||
Submit
|
||||
{onSkipStep ? "Next" : "Update"}
|
||||
</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
|
||||
@ -415,11 +415,13 @@ const FamilyDetailsForm = ({ onSubmitStep, onSkipStep, errors, onFieldChange })
|
||||
xs={12}
|
||||
sx={{ marginTop: 10, display: "flex", gap: 4, justifyContent: "center" }}
|
||||
>
|
||||
<Button variant="outlined" onClick={onSkipStep}>
|
||||
Skip
|
||||
</Button>
|
||||
{onSkipStep && (
|
||||
<Button variant="outlined" onClick={onSkipStep}>
|
||||
Skip
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="contained" color="primary" onClick={handleSubmit}>
|
||||
Submit
|
||||
{onSkipStep ? "Next" : "Update"}
|
||||
</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
|
||||
@ -186,7 +186,7 @@ const LifestyleDetailsForm = ({
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-20 gap-y-10 mb-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-gray-900 text-[15px]">
|
||||
Diet (Multi-select){requiredMark}
|
||||
Diet{requiredMark}
|
||||
</label>
|
||||
<FormControl fullWidth variant="outlined" error={Boolean(errors.diets)}>
|
||||
<InputLabel id="diets-label">Select Diet</InputLabel>
|
||||
@ -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 (
|
||||
<MenuItem key={value} value={value}>
|
||||
<Checkbox checked={data.diets.indexOf(value) > -1} />
|
||||
<ListItemText primary={label} />
|
||||
{label}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
@ -406,11 +394,13 @@ const LifestyleDetailsForm = ({
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Button variant="outlined" onClick={onSkipStep}>
|
||||
Skip
|
||||
</Button>
|
||||
{onSkipStep && (
|
||||
<Button variant="outlined" onClick={onSkipStep}>
|
||||
Skip
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="contained" color="primary" onClick={handleSubmit}>
|
||||
Next
|
||||
{onSkipStep ? "Next" : "Update"}
|
||||
</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
|
||||
@ -417,11 +417,13 @@ const PartnerPreferencesForm = ({
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Button variant="outlined" onClick={onSkipStep}>
|
||||
Skip
|
||||
</Button>
|
||||
{onSkipStep && (
|
||||
<Button variant="outlined" onClick={onSkipStep}>
|
||||
Skip
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="contained" color="primary" onClick={handleSubmit}>
|
||||
Next
|
||||
{onSkipStep ? "Next" : "Update"}
|
||||
</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
|
||||
@ -1290,7 +1290,7 @@ const PersonalDetailsForm = ({ onSubmitStep, errors, onFieldChange, isStep1Updat
|
||||
onClick={handleSubmit}
|
||||
// disabled={!mobileOtpVerified}
|
||||
>
|
||||
Submit
|
||||
Next
|
||||
</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
|
||||
@ -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) => (
|
||||
<Box
|
||||
py={0.7}
|
||||
borderBottom="1px solid #e0e0e0"
|
||||
// borderBottom="1px solid #e0e0e0"
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: {
|
||||
@ -102,8 +164,119 @@ const ProfilePreviewPage = () => {
|
||||
</Box>
|
||||
);
|
||||
|
||||
const renderChartGrid = (getDataForCell, title) => {
|
||||
const renderCell = (i) => {
|
||||
const items = getDataForCell(i);
|
||||
const label = Array.isArray(items) ? items.join(", ") : "";
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
key={i}
|
||||
title={label}
|
||||
arrow
|
||||
placement="top"
|
||||
disableHoverListener={!items || items.length === 0}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
border: "1px solid #ccc",
|
||||
bgcolor: "#fff",
|
||||
height: "60px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
fontSize: "0.65rem",
|
||||
fontWeight: "bold",
|
||||
overflow: "hidden",
|
||||
p: 0.5,
|
||||
wordBreak: "break-word",
|
||||
lineHeight: 1.1,
|
||||
cursor: items && items.length > 0 ? "help" : "default",
|
||||
}}
|
||||
>
|
||||
{items && items.length > 2 ? (
|
||||
<Box>
|
||||
{items.slice(0, 2).join(", ")}
|
||||
<Box
|
||||
component="span"
|
||||
sx={{ color: "primary.main", display: "block", fontSize: "0.6rem" }}
|
||||
>
|
||||
+{items.length - 2}
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
label
|
||||
)}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
align="center"
|
||||
gutterBottom
|
||||
sx={{ fontWeight: 600 }}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(4, 1fr)",
|
||||
gap: 0.5,
|
||||
maxWidth: "300px",
|
||||
mx: "auto",
|
||||
p: 1,
|
||||
bgcolor: "#fff3e0",
|
||||
borderRadius: 2,
|
||||
}}
|
||||
>
|
||||
{renderCell(1)} {renderCell(2)} {renderCell(3)} {renderCell(4)}
|
||||
{renderCell(5)}
|
||||
<Box
|
||||
sx={{
|
||||
gridColumn: "span 2",
|
||||
gridRow: "span 2",
|
||||
bgcolor: "#fff",
|
||||
border: "1px solid #eee",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: "0.7rem",
|
||||
fontWeight: "bold",
|
||||
color: "#aaa",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="img"
|
||||
src={horoscopeImg}
|
||||
alt={title}
|
||||
sx={{
|
||||
width: "70%",
|
||||
height: "70%",
|
||||
objectFit: "cover",
|
||||
animation: "spin 20s linear infinite",
|
||||
"@keyframes spin": {
|
||||
"0%": { transform: "rotate(0deg)" },
|
||||
"100%": { transform: "rotate(360deg)" },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{renderCell(6)}
|
||||
{renderCell(7)} {renderCell(8)}
|
||||
{renderCell(9)} {renderCell(10)} {renderCell(11)} {renderCell(12)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const renderPersonalSection = () => (
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#fff5ed" }}>
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#ffff" }}>
|
||||
<CardHeader
|
||||
title={
|
||||
<Typography variant="h6" fontWeight="bold">
|
||||
@ -120,9 +293,9 @@ const ProfilePreviewPage = () => {
|
||||
<Edit2 size={20} />
|
||||
</IconButton>
|
||||
}
|
||||
sx={{ padding: "15px 15px", background: "#ffff" }}
|
||||
sx={{ padding: "15px 15px", background: "#f2f2f2" }}
|
||||
/>
|
||||
<Divider />
|
||||
{/* <Divider /> */}
|
||||
|
||||
<CardContent sx={{ pt: 1 }}>
|
||||
{renderField("Name", personalDetails.name, 1)}
|
||||
@ -142,7 +315,7 @@ const ProfilePreviewPage = () => {
|
||||
{/* Profile Images */}
|
||||
<Box
|
||||
py={0.7}
|
||||
borderBottom="1px solid #e0e0e0"
|
||||
// borderBottom="1px solid #e0e0e0"
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
|
||||
@ -210,7 +383,7 @@ const ProfilePreviewPage = () => {
|
||||
);
|
||||
|
||||
const renderEducationalSection = () => (
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#fff5ed" }}>
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#ffff" }}>
|
||||
<CardHeader
|
||||
title={
|
||||
<Typography variant="h6" fontWeight="bold">
|
||||
@ -227,9 +400,9 @@ const ProfilePreviewPage = () => {
|
||||
<Edit2 size={20} />
|
||||
</IconButton>
|
||||
}
|
||||
sx={{ padding: "15px 15px", background: "#f5fbff" }}
|
||||
sx={{ padding: "15px 15px", background: "#f2f2f2" }}
|
||||
/>
|
||||
<Divider />
|
||||
{/* <Divider /> */}
|
||||
|
||||
<CardContent sx={{ pt: 1 }}>
|
||||
{renderField("Qualification", educationalDetails.qualification, 2)}
|
||||
@ -245,7 +418,7 @@ const ProfilePreviewPage = () => {
|
||||
);
|
||||
|
||||
const renderFamilySection = () => (
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#fff5ed" }}>
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#ffff" }}>
|
||||
<CardHeader
|
||||
title={
|
||||
<Typography variant="h6" fontWeight="bold">
|
||||
@ -256,15 +429,15 @@ const ProfilePreviewPage = () => {
|
||||
<IconButton
|
||||
aria-label="edit"
|
||||
color="primary"
|
||||
onClick={() => handleEditSection(2)}
|
||||
onClick={() => handleEditSection(3)}
|
||||
size="large"
|
||||
>
|
||||
<Edit2 size={20} />
|
||||
</IconButton>
|
||||
}
|
||||
sx={{ padding: "15px 15px", background: "#f5fbff" }}
|
||||
sx={{ padding: "15px 15px", background: "#f2f2f2" }}
|
||||
/>
|
||||
<Divider />
|
||||
{/* <Divider /> */}
|
||||
|
||||
<CardContent sx={{ pt: 1 }}>
|
||||
{renderField("Father Name", familyDetails.fatherName)}
|
||||
@ -280,7 +453,7 @@ const ProfilePreviewPage = () => {
|
||||
);
|
||||
|
||||
const renderLifestyleSection = () => (
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#fff5ed" }}>
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#ffff" }}>
|
||||
<CardHeader
|
||||
title={
|
||||
<Typography variant="h6" fontWeight="bold">
|
||||
@ -291,15 +464,15 @@ const ProfilePreviewPage = () => {
|
||||
<IconButton
|
||||
aria-label="edit"
|
||||
color="primary"
|
||||
onClick={() => handleEditSection(2)}
|
||||
onClick={() => handleEditSection(4)}
|
||||
size="large"
|
||||
>
|
||||
<Edit2 size={20} />
|
||||
</IconButton>
|
||||
}
|
||||
sx={{ padding: "15px 15px", background: "#f5fbff" }}
|
||||
sx={{ padding: "15px 15px", background: "#f2f2f2" }}
|
||||
/>
|
||||
<Divider />
|
||||
{/* <Divider /> */}
|
||||
|
||||
<CardContent sx={{ pt: 1 }}>
|
||||
{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 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
mt: 2,
|
||||
mb: 2,
|
||||
borderTop: "1px solid #e0e0e0",
|
||||
pt: 2,
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
Horoscope Details
|
||||
</Typography>
|
||||
<Grid container spacing={4} justifyContent="center">
|
||||
<Grid item xs={12} md={6}>
|
||||
{renderChartGrid((i) => {
|
||||
const val = lifestyleDetails.horoscope[`graha_${i}`];
|
||||
return val ? val.split(",") : [];
|
||||
}, "Rasi")}
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
{renderChartGrid((i) => {
|
||||
const val = lifestyleDetails.horoscope[`amsam_${i}`];
|
||||
return val ? val.split(",") : [];
|
||||
}, "Navamsam")}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
|
||||
const renderPreferenceSection = () => (
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#fff5ed" }}>
|
||||
<Card variant="outlined" sx={{ borderRadius: 2, background: "#ffff" }}>
|
||||
<CardHeader
|
||||
title={
|
||||
<Typography variant="h6" fontWeight="bold">
|
||||
Lifestyle Details
|
||||
Partner Preferences
|
||||
</Typography>
|
||||
}
|
||||
action={
|
||||
<IconButton
|
||||
aria-label="edit"
|
||||
color="primary"
|
||||
onClick={() => handleEditSection(2)}
|
||||
onClick={() => handleEditSection(5)}
|
||||
size="large"
|
||||
>
|
||||
<Edit2 size={20} />
|
||||
</IconButton>
|
||||
}
|
||||
sx={{ padding: "15px 15px", background: "#f5fbff" }}
|
||||
sx={{ padding: "15px 15px", background: "#f2f2f2" }}
|
||||
/>
|
||||
<Divider />
|
||||
{/* <Divider /> */}
|
||||
|
||||
<CardContent sx={{ pt: 1 }}>
|
||||
{renderField("Age Range", partnerPreferences.ageRange)}
|
||||
@ -394,6 +596,14 @@ const ProfilePreviewPage = () => {
|
||||
</Card>
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Box display="flex" justifyContent="center" alignItems="center" minHeight="50vh">
|
||||
<Typography>Loading Profile...</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="custom-swiper-hero flex items-center justify-center p-4">
|
||||
|
||||
@ -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 (
|
||||
// <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 }) => {
|
||||
|
||||
const steps = [
|
||||
{ num: 1, label: "Personal" },
|
||||
@ -133,31 +183,45 @@ const Stepper = ({ currentStep, onStepClick }) => {
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between px-4 py-6">
|
||||
{steps.map((step, index) => (
|
||||
<React.Fragment key={step.num}>
|
||||
<div
|
||||
className="flex flex-col items-center cursor-pointer"
|
||||
onClick={() => onStepClick(step.num)}
|
||||
>
|
||||
{steps.map((step, index) => {
|
||||
const isEnabled = enabledSteps.includes(step.num);
|
||||
const isCompleted = completedSteps.includes(step.num);
|
||||
|
||||
return (
|
||||
<React.Fragment key={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"
|
||||
className={`flex flex-col items-center ${
|
||||
isEnabled ? "cursor-pointer" : "cursor-not-allowed opacity-50"
|
||||
}`}
|
||||
onClick={() => isEnabled && onStepClick(step.num)}
|
||||
>
|
||||
{step.num}
|
||||
<div
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold ${
|
||||
isCompleted
|
||||
? "bg-green-600 text-white"
|
||||
: currentStep === step.num
|
||||
? "bg-red-600 text-white"
|
||||
: "bg-gray-300 text-gray-600"
|
||||
}`}
|
||||
>
|
||||
{isCompleted ? <Check size={16} /> : step.num}
|
||||
</div>
|
||||
|
||||
<span className="text-xs mt-1">{step.label}</span>
|
||||
</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>
|
||||
))}
|
||||
|
||||
{index < steps.length - 1 && (
|
||||
<div
|
||||
className={`flex-1 h-0.5 mx-1 ${
|
||||
completedSteps.includes(step.num)
|
||||
? "bg-green-600"
|
||||
: "bg-gray-300"
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -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 (
|
||||
<EducationalDetailsForm
|
||||
onSubmitStep={handleStepSubmit}
|
||||
onSkipStep={handleSkip}
|
||||
onSkipStep={shouldHideStepper ? null : handleSkip}
|
||||
errors={errors}
|
||||
onFieldChange={clearFieldErrors}
|
||||
/>
|
||||
@ -1004,7 +1135,7 @@ useEffect(()=>{
|
||||
return (
|
||||
<FamilyDetailsForm
|
||||
onSubmitStep={handleStepSubmit}
|
||||
onSkipStep={handleSkip}
|
||||
onSkipStep={shouldHideStepper ? null : handleSkip}
|
||||
errors={errors}
|
||||
onFieldChange={clearFieldErrors}
|
||||
/>
|
||||
@ -1013,7 +1144,7 @@ useEffect(()=>{
|
||||
return (
|
||||
<LifestyleDetailsForm
|
||||
onSubmitStep={handleStepSubmit}
|
||||
onSkipStep={handleSkip}
|
||||
onSkipStep={shouldHideStepper ? null : handleSkip}
|
||||
errors={errors}
|
||||
onFieldChange={clearFieldErrors}
|
||||
/>
|
||||
@ -1022,7 +1153,7 @@ useEffect(()=>{
|
||||
return (
|
||||
<PartnerPreferencesForm
|
||||
onSubmitStep={handleStepSubmit}
|
||||
onSkipStep={handleSkip}
|
||||
onSkipStep={shouldHideStepper ? null : handleSkip}
|
||||
errors={errors}
|
||||
onFieldChange={clearFieldErrors}
|
||||
/>
|
||||
@ -1051,8 +1182,13 @@ useEffect(()=>{
|
||||
<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">
|
||||
|
||||
<Stepper currentStep={currentStep} onStepClick={handleStepClick} />
|
||||
{!shouldHideStepper && (
|
||||
<Stepper
|
||||
currentStep={currentStep}
|
||||
onStepClick={handleStepClick}
|
||||
enabledSteps={enabledSteps}
|
||||
completedSteps={completedSteps}
|
||||
/> )}
|
||||
<div className="flex items-center p-4 justify-center">
|
||||
{currentStep > 1 && currentStep < 6 && (
|
||||
<button
|
||||
@ -1062,7 +1198,7 @@ useEffect(()=>{
|
||||
<ChevronLeft size={24} />
|
||||
</button>
|
||||
)}
|
||||
<h1 className="text-[24px] font-semibold text-center uppercase bg-[#fff2f2] py-2 px-3 rounded-5">{getTitle()}</h1>
|
||||
<h1 className="text-[24px] font-semibold text-center uppercase py-2 px-3 rounded-5">{getTitle()}</h1>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user