diff --git a/src/pages/authentication/BusRegister.js b/src/pages/authentication/BusRegister.js index dace00d..e224cb0 100644 --- a/src/pages/authentication/BusRegister.js +++ b/src/pages/authentication/BusRegister.js @@ -1,5 +1,5 @@ // import { Link } from 'react-router-dom'; -import React, { useState } from 'react'; +import React, { useState, useRef } from 'react'; // material-ui import { @@ -31,6 +31,7 @@ const BusRegister = () => { const [activeStep, setActiveStep] = useState(0); const [completed, setCompleted] = useState([false]); const [updateValid, setUpdateValid] = useState(false); + const step0GuardRef = useRef(null); const [username, setUsername] = useState("") const [base64Url, setBase64Url] = useState("") const [checkCode, setCheckCode] = useState("") @@ -97,6 +98,12 @@ const BusRegister = () => { } const handleNext = async () => { + if (activeStep === 0 && step0GuardRef.current) { + const step0Ok = await Promise.resolve(step0GuardRef.current()); + if (!step0Ok) { + return; + } + } const captchaTest = await handleCaptcha(); if (!captchaTest) { notifyActionError(intl.formatMessage({id: 'validVerify'})) @@ -193,6 +200,7 @@ const BusRegister = () => { { const [activeStep, setActiveStep] = useState(0); const [completed, setCompleted] = useState([false]); const [updateValid, setUpdateValid] = useState(false); + const step0GuardRef = useRef(null); const [base64Url, setBase64Url] = useState("") const [checkCode, setCheckCode] = useState("") const [idNo, setIdNo] = useState(""); @@ -102,6 +103,12 @@ const Register = () => { notifyActionError(intl.formatMessage({id: 'iAmSmartNoIdNoMsg'})) return; } + if (activeStep === 0 && step0GuardRef.current) { + const step0Ok = await Promise.resolve(step0GuardRef.current()); + if (!step0Ok) { + return; + } + } const captchaTest = await handleCaptcha(); if (!captchaTest) { notifyActionError(intl.formatMessage({id: 'validVerify'})) @@ -184,6 +191,7 @@ const Register = () => { { const [activeStep, setActiveStep] = useState(0); const [completed, setCompleted] = useState([false]); const [updateValid, setUpdateValid] = useState(false); + const step0GuardRef = useRef(null); const [username, setUsername] = useState(""); const [base64Url, setBase64Url] = useState("") const [checkCode, setCheckCode] = useState("") @@ -101,6 +102,12 @@ const Register = () => { const handleNext = async () => { + if (activeStep === 0 && step0GuardRef.current) { + const step0Ok = await Promise.resolve(step0GuardRef.current()); + if (!step0Ok) { + return; + } + } const captchaTest = await handleCaptcha(); if (!captchaTest) { @@ -198,6 +205,7 @@ const Register = () => { { } }, [checkDistrictBlur]) - const handleCheckDistrict = async () => { + const isDistrictSelectionValid = () => { + if (selectedAddress5?.type !== "hongKong") { + return true; + } + return !(selectedAddress4 == null || selectedAddress4 === ""); + }; + + const handleCheckDistrict = () => { setDistrictErrStr(""); if (selectedAddress5?.type === "hongKong") { if (selectedAddress4 == null || selectedAddress4 == "" || selectedAddress4 == {}){ @@ -229,9 +236,15 @@ const BusCustomFormWizard = (props) => { }else { setCheckDistrict(false) } + } else { + setCheckDistrict(false); } } + useEffect(() => { + handleCheckDistrict(); + }, [selectedAddress4, selectedAddress5]); + function getRequiredErrStr(fieldname){ return displayErrorMsg(intl.formatMessage({ id: 'require'},{fieldname:fieldname?intl.formatMessage({ id: fieldname}):""})); } @@ -253,43 +266,39 @@ const BusCustomFormWizard = (props) => { }); } + const computeStep0Valid = (data) => ( + handleCaptcha(data.captchaField) && + data.username !== "" && + data.password !== "" && + data.confirmPassword !== "" && + data.password === data.confirmPassword && + (data.chCompanyName !== "" || data.enCompanyName !== "") && + data.enName !== "" && + data.chName !== "" && + data.address1 !== "" && + data.email !== "" && + data.emailConfirm !== "" && + data.email === data.emailConfirm && + data.phone !== "" && + data.phoneCountryCode !== "" && + termsAndConAccept === true && + fileList.length !== 0 && + data.brNo !== "" && + data.brExpiryDate !== "" && + handlePassword(data.password) && + handleEmail(data.email) && + handlePhone(data.phone) && + handleUserName(data.username) && + handleBrNo(data.brNo) && + isDistrictSelectionValid() && + !checkUsername&& + !checkEmail + ); + const checkDataField = (data) => { - // console.log(data.brExpiryDate) - if ( - handleCaptcha(data.captchaField) && - data.username !== "" && - data.password !== "" && - data.confirmPassword !== "" && - data.password === data.confirmPassword && - (data.chCompanyName !== "" || data.enCompanyName !== "") && - data.enName !== "" && - data.chName !== "" && - data.address1 !== "" && - data.email !== "" && - data.emailConfirm !== "" && - data.email === data.emailConfirm && - data.phone !== "" && - data.phoneCountryCode !== "" && - termsAndConAccept === true && - fileList.length !== 0 && - data.brNo !== "" && - data.brExpiryDate !== "" && - handlePassword(data.password) && - handleEmail(data.email) && - handlePhone(data.phone) && - handleUserName(data.username) && - handleBrNo(data.brNo) && - handleCheckDistrict()&& - !checkUsername&& - !checkEmail&& - !checkDistrict - ) { - setisValid(true) - return isValid - } else { - setisValid(false) - return isValid - } + const can = computeStep0Valid(data); + setisValid(can); + return can; }; const handleCheckBoxChange = (event) => { @@ -641,6 +650,21 @@ const BusCustomFormWizard = (props) => { }; const { values } = formik + + useEffect(() => { + if (!props.step0GuardRef) return; + props.step0GuardRef.current = () => { + const can = computeStep0Valid(values); + setisValid(can); + if (!can && !isDistrictSelectionValid()) { + setDistrictErrStr(getRequiredErrStr("district")); + setCheckDistrict(true); + notifyActionError(intl.formatMessage({ id: 'require' }, { fieldname: intl.formatMessage({ id: 'district' }) })); + } + return can; + }; + }, [values, selectedAddress4, selectedAddress5, termsAndConAccept, fileList, checkUsername, checkEmail]); + useEffect(() => { checkDataField(values) }, [values]) diff --git a/src/pages/authentication/auth-forms/CustomFormWizard.js b/src/pages/authentication/auth-forms/CustomFormWizard.js index 36d3d8a..99f18e3 100644 --- a/src/pages/authentication/auth-forms/CustomFormWizard.js +++ b/src/pages/authentication/auth-forms/CustomFormWizard.js @@ -230,7 +230,14 @@ const CustomFormWizard = (props) => { } } - const handleCheckDistrict = async () => { + const isDistrictSelectionValid = () => { + if (selectedAddress5?.type !== "hongKong") { + return true; + } + return !(selectedAddress4 == null || selectedAddress4 === ""); + }; + + const handleCheckDistrict = () => { setDistrictErrStr(""); if (selectedAddress5?.type === "hongKong") { if (selectedAddress4 == null || selectedAddress4 == "" || selectedAddress4 == {}){ @@ -239,9 +246,15 @@ const CustomFormWizard = (props) => { }else { setCheckDistrict(false) } + } else { + setCheckDistrict(false); } } + useEffect(() => { + handleCheckDistrict(); + }, [selectedAddress4, selectedAddress5]); + useEffect(() => { if (username) { username.addEventListener("blur", function () { @@ -345,45 +358,38 @@ const CustomFormWizard = (props) => { }); } + const computeStep0Valid = (data) => ( + handleCaptcha(data.captchaField) && + data.username !== "" && + data.password !== "" && + data.confirmPassword !== "" && + data.password == data.confirmPassword && + selectedIdDocType.type !== "" && + data.idNo !== "" && + handleName(data.enName, data.chName) && + data.address1 !== "" && + data.email !== "" && + data.emailConfirm !== "" && + data.email == data.emailConfirm && + data.phone !== "" && + data.phoneCountryCode !== "" && + termsAndConAccept == true && + fileList.length !== 0 && + handlePassword(data.password) && + handleEmail(data.email) && + handleIdNo(data.idNo, selectedIdDocType.type, data.checkDigit) && + handlePhone(data.phone) && + handleUsername(data.username) && + isDistrictSelectionValid() && + !checkUsername && + !checkEmail && + !checkIdDocNumber + ); + const checkDataField = (data) => { - // console.log(data) - if ( - handleCaptcha(data.captchaField) && - data.username !== "" && - data.password !== "" && - data.confirmPassword !== "" && - data.password == data.confirmPassword && - selectedIdDocType.type !== "" && - data.idNo !== "" && - // (data.enName !== "" || selectedIdDocType.type === "CNID") && - // data.chName !== "" && - handleName(data.enName, data.chName) && - data.address1 !== "" && - data.email !== "" && - data.emailConfirm !== "" && - data.email == data.emailConfirm && - data.phone !== "" && - data.phoneCountryCode !== "" && - termsAndConAccept == true && - fileList.length !== 0 && - // data.captchaField && - handlePassword(data.password) && - handleEmail(data.email) && - handleIdNo(data.idNo, selectedIdDocType.type, data.checkDigit) && - handlePhone(data.phone) && - handleUsername(data.username) && - handleCheckDistrict()&& - !checkUsername && - !checkEmail && - !checkIdDocNumber&& - !checkDistrict - ) { - setisValid(true) - return isValid - } else { - setisValid(false) - return isValid - } + const can = computeStep0Valid(data); + setisValid(can); + return can; }; const handleCheckBoxChange = (event) => { @@ -462,10 +468,6 @@ const CustomFormWizard = (props) => { props.setUpdateValid(isValid) }, [isValid]) - useEffect(() => { - props.setUpdateValid(isValid) - }, [isValid]) - useEffect(() => { checkDataField(values) }, [ @@ -871,6 +873,20 @@ const CustomFormWizard = (props) => { const { values } = formik + useEffect(() => { + if (!props.step0GuardRef) return; + props.step0GuardRef.current = () => { + const can = computeStep0Valid(values); + setisValid(can); + if (!can && !isDistrictSelectionValid()) { + setDistrictErrStr(getRequiredErrStr("district")); + setCheckDistrict(true); + notifyActionError(intl.formatMessage({ id: 'require' }, { fieldname: intl.formatMessage({ id: 'district' }) })); + } + return can; + }; + }, [values, selectedAddress4, selectedAddress5, termsAndConAccept, fileList, checkUsername, checkEmail, checkIdDocNumber, selectedIdDocType]); + useEffect(() => { checkDataField(values) }, [values]) diff --git a/src/pages/authentication/auth-forms/IAmSmartFormWizard.js b/src/pages/authentication/auth-forms/IAmSmartFormWizard.js index 5852636..a7da4eb 100644 --- a/src/pages/authentication/auth-forms/IAmSmartFormWizard.js +++ b/src/pages/authentication/auth-forms/IAmSmartFormWizard.js @@ -151,7 +151,15 @@ const CustomFormWizard = (props) => { } }, [checkDistrictBlur]) - const handleCheckDistrict = async () => { + /** Synchronous HK district rule — must match enable logic for Continue (async handleCheckDistrict() was always truthy). */ + const isDistrictSelectionValid = () => { + if (selectedAddress5?.type !== "hongKong") { + return true; + } + return !(selectedAddress4 == null || selectedAddress4 === ""); + }; + + const handleCheckDistrict = () => { setDistrictErrStr(""); if (selectedAddress5?.type === "hongKong") { if (selectedAddress4 == null || selectedAddress4 == "" || selectedAddress4 == {}){ @@ -160,9 +168,15 @@ const CustomFormWizard = (props) => { }else { setCheckDistrict(false) } + } else { + setCheckDistrict(false); } } + useEffect(() => { + handleCheckDistrict(); + }, [selectedAddress4, selectedAddress5]); + function getRequiredErrStr(fieldname) { return displayErrorMsg(intl.formatMessage({ id: 'require' }, { fieldname: fieldname ? intl.formatMessage({ id: fieldname }) : "" })); } @@ -240,15 +254,25 @@ const CustomFormWizard = (props) => { return add; } + /** POST /user/checkE1 — returns true if email is already registered */ + const checkEmailRegistered = async (emailStr) => { + const em = emailStr?.trim(); + if (!em) return false; + const response = await axios.post(`${POST_USER_EMAIL}`, { e1: em }); + return Number(response.data[0]) > 0; + }; + const handleCheckEmail = async () => { - if (values?.email) { - const response = await axios.post(`${POST_USER_EMAIL}`, { - e1: values.email, - }) - setCheckEmail((Number(response.data[0]) > 0)) - return Number(response.data[0]) > 0 + if (!values?.email) return false; + try { + const taken = await checkEmailRegistered(values.email); + setCheckEmail(taken); + return taken; + } catch (error) { + notifyActionError(intl.formatMessage({ id: 'connectionError' })); + return false; } - } + }; useEffect(() => { if (email) { @@ -295,28 +319,28 @@ const CustomFormWizard = (props) => { } + /** All step-0 checks except duplicate-email result from /checkE1 (see checkEmail state). */ + const computeStep0ValidSync = (data) => ( + data.address1 !== "" && + data.email !== "" && + data.emailConfirm !== "" && + data.email == data.emailConfirm && + data.phone !== "" && + data.phoneCountryCode !== "" && + termsAndConAccept == true && + data.captchaField && + handleEmail(data.email) && + handlePhone(data.phone) && + handleCaptcha(data.captchaField) && + isDistrictSelectionValid() + ); + + const computeStep0Valid = (data) => computeStep0ValidSync(data) && !checkEmail; + const checkDataField = (data) => { - if (data.address1 !== "" && - data.email !== "" && - data.emailConfirm !== "" && - data.email == data.emailConfirm && - data.phone !== "" && - data.phoneCountryCode !== "" && - termsAndConAccept == true && - data.captchaField && - handleEmail(data.email) && - handlePhone(data.phone) && - handleCaptcha(data.captchaField) && - handleCheckDistrict()&& - !checkEmail&& - !checkDistrict - ) { - setisValid(true) - return isValid - } else { - setisValid(false) - return isValid - } + const can = computeStep0Valid(data); + setisValid(can); + return can; }; const handleCheckBoxChange = (event) => { @@ -478,6 +502,37 @@ const CustomFormWizard = (props) => { }; const { values } = formik + + useEffect(() => { + if (!props.step0GuardRef) return; + props.step0GuardRef.current = async () => { + const syncOk = computeStep0ValidSync(values); + if (!syncOk) { + setisValid(false); + if (!isDistrictSelectionValid()) { + setDistrictErrStr(getRequiredErrStr("district")); + setCheckDistrict(true); + notifyActionError(intl.formatMessage({ id: 'require' }, { fieldname: intl.formatMessage({ id: 'district' }) })); + } + return false; + } + try { + const taken = await checkEmailRegistered(values.email); + setCheckEmail(taken); + if (taken) { + setisValid(false); + notifyActionError(intl.formatMessage({ id: 'emailUsed' })); + return false; + } + } catch (error) { + notifyActionError(intl.formatMessage({ id: 'connectionError' })); + return false; + } + setisValid(true); + return true; + }; + }, [values, selectedAddress4, selectedAddress5, termsAndConAccept, checkEmail]); + useEffect(() => { checkDataField(values) }, [values])