diff --git a/src/pages/Common/SignUp/PrivacyAndPolicyModal.jsx b/src/pages/Common/SignUp/PrivacyAndPolicyModal.jsx
index 2989c6d..63f6e7d 100644
--- a/src/pages/Common/SignUp/PrivacyAndPolicyModal.jsx
+++ b/src/pages/Common/SignUp/PrivacyAndPolicyModal.jsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React from "react";
import { GlobalContext } from "@/globalContext";
import { callCustomAPI } from "@/utils/callCustomAPI";
import { Dialog, Transition } from "@headlessui/react";
@@ -8,19 +8,28 @@ import { useState } from "react";
import { Fragment } from "react";
import MkdSDK from "@/utils/MkdSDK";
-const PrivacyAndPolicyModal = ({ isOpen, closeModal }) => {
+const PrivacyAndPolicyModal = ({ isOpen, closeModal, onReadToEnd }) => {
const [privacy, setPrivacy] = useState("");
const { dispatch: globalDispatch } = useContext(GlobalContext);
+ const [hasReadToEnd, setHasReadToEnd] = useState(false);
async function fetchPrivacyPolicy() {
globalDispatch({ type: "START_LOADING" });
const sdk = new MkdSDK();
sdk.setTable("cms");
try {
- const result = await callCustomAPI("cms", "post", { payload: { content_key: "privacy_policy" }, limit: 1000, page: 1 }, "PAGINATE");
+ const result = await callCustomAPI(
+ "cms",
+ "post",
+ { payload: { content_key: "privacy_policy" }, limit: 1000, page: 1 },
+ "PAGINATE"
+ );
if (Array.isArray(result.list) && result.list.length > 0) {
- setPrivacy(result.list.find((stg) => stg.content_key == "privacy_policy")?.content_value);
+ setPrivacy(
+ result.list.find((stg) => stg.content_key == "privacy_policy")
+ ?.content_value
+ );
}
} catch (err) {
globalDispatch({
@@ -36,74 +45,79 @@ const PrivacyAndPolicyModal = ({ isOpen, closeModal }) => {
useEffect(() => {
fetchPrivacyPolicy();
- }, []);
+ setHasReadToEnd(false);
+ }, [isOpen]);
+
+ function handleScroll(e) {
+ const { scrollTop, scrollHeight, clientHeight } = e.target;
+ if (!hasReadToEnd && scrollTop + clientHeight >= scrollHeight - 10) {
+ setHasReadToEnd(true);
+ if (onReadToEnd) onReadToEnd();
+ }
+ }
return (
<>
-
+
-
-
+
+ >
+ );
+};
-export default PrivacyAndPolicyModal
\ No newline at end of file
+export default PrivacyAndPolicyModal;
diff --git a/src/pages/Common/SignUp/SignUpDetailsForm.jsx b/src/pages/Common/SignUp/SignUpDetailsForm.jsx
index 5b264c9..16394a5 100644
--- a/src/pages/Common/SignUp/SignUpDetailsForm.jsx
+++ b/src/pages/Common/SignUp/SignUpDetailsForm.jsx
@@ -16,6 +16,7 @@ import TermsAndConditionsModal from "./TermsAndConditionsModal";
import DatePickerV2 from "@/components/frontend/DatePickerV2";
import { LoadingButton } from "@/components/frontend";
import PrivacyAndPolicyModal from "./PrivacyAndPolicyModal";
+import commonPasswords from "@/assets/json/common-passwords.json";
export default function SignUpDetailsForm() {
const navigate = useNavigate();
@@ -28,6 +29,9 @@ export default function SignUpDetailsForm() {
const [modalOpen, setModalOpen] = React.useState(false);
const [privacyOpen, setPrivacyModalOpen] = React.useState(false);
const initialDate = useRef(new Date());
+ const [agreedToTerms, setAgreedToTerms] = React.useState(false);
+ const [privacyRead, setPrivacyRead] = React.useState(false);
+ const [passwordErrors, setPasswordErrors] = React.useState([]);
function closeModal() {
setModalOpen(false);
@@ -36,11 +40,67 @@ export default function SignUpDetailsForm() {
setPrivacyModalOpen(false);
}
+ // --- DOB must be at least 18 years ---
+ function isAtLeast18YearsOld(date) {
+ if (!date) return false;
+ const now = new Date();
+ const dob = new Date(date);
+ let age = now.getFullYear() - dob.getFullYear();
+ const m = now.getMonth() - dob.getMonth();
+ if (m < 0 || (m === 0 && now.getDate() < dob.getDate())) {
+ age--;
+ }
+ return age >= 18;
+ }
+
+ // --- Password validation ---
+ function validatePassword(password, firstName, lastName, dob) {
+ const errors = [];
+ if (!password) errors.push("Password is required.");
+ if (password && password.length < 10)
+ errors.push("Password must be at least 10 characters long.");
+ if (password && !/[a-z]/.test(password))
+ errors.push("Password must contain at least one lowercase letter.");
+ if (password && !/[A-Z]/.test(password))
+ errors.push("Password must contain at least one uppercase letter.");
+ if (password && !/[0-9]/.test(password))
+ errors.push("Password must contain at least one digit.");
+ if (password && !/[^A-Za-z0-9]/.test(password))
+ errors.push("Password must contain at least one symbol.");
+ if (
+ password &&
+ firstName &&
+ password.toLowerCase().includes(firstName.toLowerCase())
+ )
+ errors.push("Password must not contain your first name.");
+ if (
+ password &&
+ lastName &&
+ password.toLowerCase().includes(lastName.toLowerCase())
+ )
+ errors.push("Password must not contain your last name.");
+ if (password && dob) {
+ const dobStr = new Date(dob).toISOString().slice(0, 10).replace(/-/g, "");
+ if (dobStr && password.includes(dobStr))
+ errors.push("Password must not contain your date of birth.");
+ }
+ if (
+ password &&
+ commonPasswords.some((w) => password.toLowerCase().includes(w))
+ )
+ errors.push("Password must not contain common password words.");
+ return errors;
+ }
+
+ // --- Validation schema ---
const schema = yup.object({
- firstName: yup.string(),
- lastName: yup.string(),
- dob: yup.date(),
- password: yup.string()
+ firstName: yup.string().required("First name is required."),
+ lastName: yup.string().required("Last name is required."),
+ dob: yup
+ .date()
+ .required("Date of birth is required.")
+ .test("age", "You must be at least 18 years old.", isAtLeast18YearsOld),
+ password: yup.string().required("Password is required."),
});
const {
@@ -65,6 +125,34 @@ export default function SignUpDetailsForm() {
const data = watch();
+ // --- Password error display ---
+ React.useEffect(() => {
+ setPasswordErrors(
+ validatePassword(data.password, data.firstName, data.lastName, data.dob)
+ );
+ }, [data.password, data.firstName, data.lastName, data.dob]);
+
+ // --- Terms and Privacy Modal handlers ---
+ function handleAgreeTerms() {
+ setAgreedToTerms(true);
+ setModalOpen(false);
+ }
+ function handlePrivacyRead() {
+ setPrivacyRead(true);
+ setPrivacyModalOpen(false);
+ }
+
+ // --- Disable submit logic ---
+ const canSubmit =
+ data.firstName &&
+ data.lastName &&
+ data.dob &&
+ isAtLeast18YearsOld(data.dob) &&
+ data.password &&
+ passwordErrors.length === 0 &&
+ agreedToTerms &&
+ privacyRead;
+
async function onSubmit() {
setLoading(true);
try {
@@ -74,7 +162,15 @@ export default function SignUpDetailsForm() {
// register device
sdk.setTable("device");
- await sdk.callRestAPI({ active: 1, user_id: result.user_id, last_login_time: new Date().toISOString().split("T")[0], uid: localStorage.getItem("device-uid") }, "POST");
+ await sdk.callRestAPI(
+ {
+ active: 1,
+ user_id: result.user_id,
+ last_login_time: new Date().toISOString().split("T")[0],
+ uid: localStorage.getItem("device-uid"),
+ },
+ "POST"
+ );
await callCustomAPI(
"edit-self",
@@ -85,11 +181,13 @@ export default function SignUpDetailsForm() {
last_name: data.lastName,
},
profile: {
- dob: isSameDay(data.dob, initialDate.current) ? undefined : moment(data.dob).format("yyyy-MM-DD"),
+ dob: isSameDay(data.dob, initialDate.current)
+ ? undefined
+ : moment(data.dob).format("yyyy-MM-DD"),
},
},
"",
- result.token,
+ result.token
);
localStorage.removeItem("token");
@@ -97,7 +195,7 @@ export default function SignUpDetailsForm() {
authDispatch({ type: "ALLOW_CHECK_VERIFICATION" });
navigate("/check-verification");
localStorage.setItem("first_login", result.user_id);
- setLoading(false);
+ setLoading(false);
} else {
setLoading(false);
if (result.validation) {
@@ -108,11 +206,9 @@ export default function SignUpDetailsForm() {
type: "manual",
message: result.validation[field],
});
-
}
}
}
-
} catch (err) {
setLoading(false);
setError("firstName", {
@@ -126,122 +222,160 @@ export default function SignUpDetailsForm() {
return (
<>
-