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()}