From 5a4cf53aa9dcc96a881b43865c53f63b1eeac429 Mon Sep 17 00:00:00 2001 From: Alex Cheung Date: Fri, 30 Jan 2026 01:59:12 +0800 Subject: [PATCH] outstanding fix update --- src/pages/GFMIS/DataGrid.js | 34 +- src/pages/GFMIS/TransactionDataGrid.js | 4 +- src/pages/GFMIS/index.js | 9 +- .../auth-forms/IAmSmartFormWizard.js | 2244 ++++++++--------- src/utils/HttpUtils.js | 12 +- 5 files changed, 1129 insertions(+), 1174 deletions(-) diff --git a/src/pages/GFMIS/DataGrid.js b/src/pages/GFMIS/DataGrid.js index a9c2bfd..76618c4 100644 --- a/src/pages/GFMIS/DataGrid.js +++ b/src/pages/GFMIS/DataGrid.js @@ -7,8 +7,8 @@ import { FiDataGrid } from "components/FiDataGrid"; // ==============================|| EVENT TABLE ||============================== // -export default function SearchTable({ previewSearchCriteria, onPreviewGridOnReady,selectedIds = []}) { - const [_searchCriteria, set_searchCriteria] = React.useState(previewSearchCriteria); +export default function SearchTable({ previewSearchCriteria, onPreviewGridOnReady,selectedIds = [], previewToken }) { + // const [_searchCriteria, set_searchCriteria] = React.useState(previewSearchCriteria); const navigate = useNavigate() // const [rows, setRows] = React.useState([]); @@ -28,9 +28,21 @@ export default function SearchTable({ previewSearchCriteria, onPreviewGridOnRead } } - React.useEffect(() => { - set_searchCriteria(previewSearchCriteria); - }, [previewSearchCriteria]); + const doLoad = React.useMemo(() => { + if (!selectedIds?.length) return undefined; + return { + url: GFIMIS_LIST, + params: { + ...previewSearchCriteria, + paymentId: selectedIds.join(',') + } + }; + }, [previewSearchCriteria, selectedIds, previewToken]); + + + // React.useEffect(() => { + // set_searchCriteria(previewSearchCriteria); + // }, [previewSearchCriteria]); const handleEditClick = (params) => () => { navigate('/paymentPage/details/' + params.row.id); @@ -61,23 +73,15 @@ export default function SearchTable({ previewSearchCriteria, onPreviewGridOnRead return (
- { - if (!selectedIds?.length) { - return undefined; // ⬅️ will keep grid empty - } - return { - url: GFIMIS_LIST, - params: { ..._searchCriteria, paymentId: selectedIds.join(',') }, - }; - }, [_searchCriteria, selectedIds])} + doLoad={doLoad} />
); diff --git a/src/pages/GFMIS/TransactionDataGrid.js b/src/pages/GFMIS/TransactionDataGrid.js index 8c527d7..489cc7d 100644 --- a/src/pages/GFMIS/TransactionDataGrid.js +++ b/src/pages/GFMIS/TransactionDataGrid.js @@ -117,10 +117,10 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a callback: (responseData) => { const newIds = responseData.records.map(r => r.id); if (selectedIds.length === 0) { - onSelectionChange(newIds); + onSelectionChange?.(newIds); } else { const stillValid = selectedIds.filter(id => newIds.includes(id)); - onSelectionChange(stillValid); + onSelectionChange?.(stillValid); } } }), [_searchCriteria])} diff --git a/src/pages/GFMIS/index.js b/src/pages/GFMIS/index.js index 11fe8d8..6380305 100644 --- a/src/pages/GFMIS/index.js +++ b/src/pages/GFMIS/index.js @@ -57,6 +57,7 @@ const Index = () => { const [inputDate, setInputDate] = React.useState(searchCriteria.dateTo); const [inputDateValue, setInputDateValue] = React.useState("dd / mm / yyyy"); + const [previewToken, setPreviewToken] = React.useState(0); React.useEffect(() => { setInputDateValue(inputDate); @@ -145,11 +146,14 @@ const Index = () => { } function previewSearch() { - // trigger reload even if criteria object is identical - const withToken = { ...searchCriteria, __ts: Date.now() }; + if (selectedIds.length === 0) return; setIsPopUp(false); setIsPreviewLoading(true); + + const withToken = { ...searchCriteria, __ts: Date.now() }; setPreviewSearchCriteria(withToken); + + setPreviewToken(t => t + 1); } function onPreviewGridOnReady(isLoading) { @@ -228,6 +232,7 @@ const Index = () => { previewSearchCriteria={previewSearchCriteria} onPreviewGridOnReady={onPreviewGridOnReady} selectedIds={selectedIds} + previewToken={previewToken} /> diff --git a/src/pages/authentication/auth-forms/IAmSmartFormWizard.js b/src/pages/authentication/auth-forms/IAmSmartFormWizard.js index 20ff7e9..4306516 100644 --- a/src/pages/authentication/auth-forms/IAmSmartFormWizard.js +++ b/src/pages/authentication/auth-forms/IAmSmartFormWizard.js @@ -1,19 +1,19 @@ -import { useEffect, useState, } from 'react'; +import { useEffect, useState, lazy } from 'react'; // material-ui import { - Button, - FormHelperText, - Grid, IconButton, - InputLabel, OutlinedInput, - Stack, - Typography, - FormGroup, - TextField, - Checkbox - // MenuItem + Button, + FormHelperText, + Grid, + IconButton, + InputLabel, + OutlinedInput, + Stack, + Typography, + FormGroup, + TextField, + Checkbox } from '@mui/material'; -import { useForm, } from 'react-hook-form' import Autocomplete from "@mui/material/Autocomplete"; // third party @@ -26,7 +26,6 @@ import { POST_IAMSMART_USER_REGISTER, POST_CAPTCHA, POST_USER_EMAIL } from "util import * as ComboData from "utils/ComboData"; import Loadable from 'components/Loadable'; -import { lazy } from 'react'; const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent'))); import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; @@ -34,1184 +33,1131 @@ import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined'; import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png'; import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons'; -import { Link } from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import * as HttpUtils from "../../../utils/HttpUtils"; import LoopIcon from '@mui/icons-material/Loop'; import { useTheme } from '@mui/material/styles'; -import { useLocation } from "react-router-dom"; import { FormattedMessage, useIntl } from "react-intl"; -// ============================|| FIREBASE - REGISTER ||============================ // - -const CustomFormWizard = (props) => { - const location = useLocation(); - const theme = useTheme(); - const intl = useIntl(); - const { locale } = intl; - - const [iAmSmartData, setIAmSmartData] = useState({}); - - const [checkUpload, setCheckUpload] = useState(false); - const [isLoading, setLoding] = useState(true); - const [isLoadingData, setLodingData] = useState(true); - - const [captchaImg, setCaptchaImage] = useState(""); - - const [selectedAddress4, setSelectedAddress4] = useState(null); - const [selectedAddress5, setSelectedAddress5] = useState(ComboData.country[0]); - const [termsAndConAccept, setTermsAndConAccept] = useState(false); - const [termsAndConNotAccept, setTermsAndConNotAccept] = useState(false); - const [isValid, setisValid] = useState(false); - const [checkCountry, setCheckCountry] = useState(false); - const email = document.getElementById("email-login") - const [checkEmail, setCheckEmail] = useState(false) - const [checkEmailBlur, setCheckEmailBlur] = useState(false) - const district = document.getElementById("address4-combo") - const [checkDistrict, setCheckDistrict] = useState(false) - const [checkDistrictBlur, setCheckDistrictBlur] = useState(false) - const [districtErrStr, setDistrictErrStr] = useState("") - - const address4ComboList = ComboData.district; - const address5ComboList = ComboData.country; - const [showId, setshowId] = useState(false); - const [showComId, setshowComId] = useState(false); - - const [base64Url, setBase64Url] = useState("") - const [checkCode, setCheckCode] = useState("") - - useEffect(() => { - location.state?.responseData ?? window.location.assign("/login"); - if (captchaImg == "") - onCaptchaChange(); - responseToData(); - }, []); - - const handleClickShowId = () => { - setshowId(!showId); - }; +const IAmSmartFormWizard = (props) => { + const location = useLocation(); + const theme = useTheme(); + const intl = useIntl(); + const { locale } = intl; - const handleMouseDownId = (event) => { - event.preventDefault(); - }; + const [iAmSmartData, setIAmSmartData] = useState({}); - const handleClickShowComId = () => { - setshowComId(!showId); - }; + const [checkUpload, setCheckUpload] = useState(false); + const [isLoading, setLoding] = useState(true); + const [isLoadingData, setLodingData] = useState(true); - const handleMouseDownComId = (event) => { - event.preventDefault(); - }; + const [captchaImg, setCaptchaImage] = useState(""); + const [base64Url, setBase64Url] = useState(""); + const [checkCode, setCheckCode] = useState(""); - useEffect(() => { - if (district) { - district.addEventListener("blur", function () { - setCheckDistrictBlur(true) - }) - } - }, [district]) - - useEffect(() => { - if (checkDistrictBlur) { - handleCheckDistrict() - setCheckDistrictBlur(false) - } - }, [checkDistrictBlur]) - - const handleCheckDistrict = async () => { - setDistrictErrStr(""); - if (selectedAddress5?.type === "hongKong") { - if (selectedAddress4 == null || selectedAddress4 == "" || selectedAddress4 == {}){ - setCheckDistrict(true) - setDistrictErrStr(getRequiredErrStr("district")) - }else { - setCheckDistrict(false) - } - } - } + const [selectedAddress4, setSelectedAddress4] = useState(null); + const [selectedAddress5, setSelectedAddress5] = useState(ComboData.country[0]); // usually hongKong + const [termsAndConAccept, setTermsAndConAccept] = useState(false); + const [termsAndConNotAccept, setTermsAndConNotAccept] = useState(false); - function getRequiredErrStr(fieldname) { - return displayErrorMsg(intl.formatMessage({ id: 'require' }, { fieldname: fieldname ? intl.formatMessage({ id: fieldname }) : "" })); - } + const [isValid, setisValid] = useState(false); + + const [checkCountry, setCheckCountry] = useState(false); + + // email duplication + const [checkEmail, setCheckEmail] = useState(false); + + // district validation UI + const [checkDistrict, setCheckDistrict] = useState(false); + const [districtErrStr, setDistrictErrStr] = useState(""); + + const [showId, setshowId] = useState(false); + const [showComId, setshowComId] = useState(false); + + const address4ComboList = ComboData.district; + const address5ComboList = ComboData.country; + + const handleClickShowId = () => setshowId(!showId); + const handleMouseDownId = (event) => event.preventDefault(); + + const handleClickShowComId = () => setshowComId(!showComId); + const handleMouseDownComId = (event) => event.preventDefault(); + + // ===== Helpers ===== + function displayErrorMsg(errorMsg) { + return {errorMsg}; + } - function getMaxErrStr(num, fieldname) { - return displayErrorMsg(intl.formatMessage({ id: 'noMoreThenNWords' }, { num: num, fieldname: fieldname ? intl.formatMessage({ id: fieldname }) + ": " : "" })); + function getRequiredErrStr(fieldname) { + return displayErrorMsg( + intl.formatMessage( + { id: 'require' }, + { fieldname: fieldname ? intl.formatMessage({ id: fieldname }) : "" } + ) + ); + } + + function getMaxErrStr(num, fieldname) { + return displayErrorMsg( + intl.formatMessage( + { id: 'noMoreThenNWords' }, + { num: num, fieldname: fieldname ? intl.formatMessage({ id: fieldname }) + ": " : "" } + ) + ); + } + + function handlePhone(phone) { + return (phone || "").length >= 8; + } + + function handleCaptcha(captchaField) { + return (captchaField || "").length === 5; + } + + function handleEmail(email) { + const validRegex = + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; + return !!email?.match(validRegex); + } + + // ===== District validation (FIX #1) ===== + const isDistrictRequired = () => selectedAddress5?.type === "hongKong"; + + const validateDistrict = () => { + setDistrictErrStr(""); + + if (!isDistrictRequired()) { + setCheckDistrict(false); + return true; } + const ok = !!selectedAddress4?.type; + setCheckDistrict(!ok); + setDistrictErrStr(ok ? "" : getRequiredErrStr("district")); + return ok; + }; + + // Run district validation whenever district/country changes + useEffect(() => { + validateDistrict(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedAddress4, selectedAddress5]); - const responseToData = () => { - //let rd = JSON.parse("{\"emailAddress\":\"test@gmail.com\",\"postalAddress\":{\"EngPremisesAddress\":{\"EngDistrict\":{\"DcDistrict\":\"KC\",\"Sub-district\":\"TSING YI\"},\"EngEstate\":{\"EstateName\":\"Cheung Hang Estate\",\"EngPhase\":{\"PhaseName\":\"N/A\"}},\"BuildingName\":\"Hang Lai House\",\"EngBlock\":{\"BlockDescriptor\":\"Block\",\"BlockNo\":\"2\"},\"Region\":\"NT\",\"EngStreet\":{\"StreetName\":\"Liu To Road\",\"BuildingNoFrom\":\"6\"},\"Eng3dAddress\":{\"EngFloor\":{\"FloorNum\":\"33\"},\"EngUnit\":{\"UnitDescriptor\":\"Room\",\"UnitNo\":\"3301\"}}}},\"mobileNumber\":{\"CountryCode\":\"852\",\"SubscriberNumber\":\"99999999\"},\"residentialAddress\":{\"ChiPremisesAddress\":{\"Chi3dAddress\":{\"ChiUnit\":{\"UnitDescriptor\":\"室\",\"UnitNo\":\"1010\"},\"ChiFloor\":{\"FloorNum\":\"10\"}},\"ChiBlock\":{\"BlockDescriptor\":\"座\",\"BlockNo\":\"2\"},\"BuildingName\":\"亨麗樓(第2座)\",\"ChiDistrict\":{\"DcDistrict\":\"KC\",\"Sub-district\":\"青衣\"},\"Region\":\"新界\",\"ChiEstate\":{\"EstateName\":\"長亨邨\"},\"ChiStreet\":{\"StreetName\":\"寮肚路\",\"BuildingNoFrom\":\"6\"}}},\"enName\":{\"UnstructuredName\":\"Testing Co One\"},\"idNo\":{\"Identification\":\"G561107\",\"CheckDigit\":\"4\"},\"chName\":{\"ChineseName\":\"測試商一\"}}"); - let rd = JSON.parse(location.state?.responseData.data); - let data = { - "enName": rd?.enName?.UnstructuredName ?? "", - "chName": rd?.chName?.ChineseName ?? "", - "idNo": rd?.idNo?.Identification ?? "", - "checkDigit": rd?.idNo?.CheckDigit ?? "", - "email": rd?.emailAddress ?? "", - "phone": rd?.mobileNumber?.SubscriberNumber ?? "", - "phoneCountryCode": rd?.mobileNumber?.CountryCode ?? "", - }; - - if (rd?.postalAddress) { - if (rd?.postalAddress?.EngPremisesAddress) { - data["address1"] = getAddressEng(rd?.postalAddress?.EngPremisesAddress); - } else if (rd.postalAddress.ChiPremisesAddress) { - data["address1"] = getAddressChi(rd?.postalAddress?.ChiPremisesAddress); - } - } else if (rd?.residentialAddress) { - if (rd?.residentialAddress?.EngPremisesAddress) { - data["address1"] = getAddressEng(rd?.residentialAddress?.EngPremisesAddress); - } else if (rd?.residentialAddress?.ChiPremisesAddress) { - data["address1"] = getAddressChi(rd?.residentialAddress?.ChiPremisesAddress); - } - } - - setIAmSmartData(data); + // ===== Email duplication check (FIX #2) ===== + const checkEmailDuplicate = async (email) => { + const value = (email || "").trim(); + if (!value) return false; + if (!handleEmail(value)) return false; + try { + const response = await axios.post(`${POST_USER_EMAIL}`, { e1: value }); + return Number(response.data?.[0]) === 1; + } catch (e) { + // choose: do not block if API fails (keeps old behavior) + return false; } + }; + + // ===== Captcha ===== + const onCaptchaChange = () => { + HttpUtils.post({ + url: POST_CAPTCHA, + params: { width: 130, height: 40, captcha: captchaImg }, + onSuccess: (responseData) => { + props.setBase64Url(responseData.base64Url); + setBase64Url(responseData.base64Url); + localStorage.setItem("base64Url", responseData.base64Url); + setCaptchaImage(localStorage.getItem('base64Url')); + } + }); + }; + + // ===== iAM Smart data mapping ===== + const getAddressStr = (strs) => { + let add = ""; + strs.forEach(str => { + add += str?.trim() ? str.trim() + ", " : ""; + }); + add = add.trim(); + if (add?.slice(-1) === ",") add = add.substring(0, add.length - 1); + return add; + }; + + const getAddressEng = (pAdd) => { + let unit = (pAdd.Eng3dAddress?.EngUnit?.UnitDescriptor ?? "") + " " + (pAdd.Eng3dAddress?.EngUnit?.UnitNo ?? ""); + let block = (pAdd.EngBlock?.BlockDescriptor ?? "") + " " + (pAdd.EngBlock?.BlockNo ?? ""); + let floor = pAdd.Eng3dAddress?.EngFloor?.FloorNum ? pAdd.Eng3dAddress?.EngFloor?.FloorNum + "/F " : ""; + let street = (pAdd.EngStreet?.StreetName ?? "") + " " + (pAdd.EngStreet?.BuildingNoFrom ?? ""); + let buildingName = pAdd.BuildingName ?? ""; + let estate = pAdd.EngEstate?.EstateName ?? ""; + // NOTE: your original key had weird spacing; keep safe access + let district = pAdd.EngDistrict?.["Sub - district"] ?? pAdd.EngDistrict?.["Sub-district"] ?? ""; + return getAddressStr([unit, block, floor, buildingName, estate, street, district]); + }; + const getAddressChi = (pAdd) => { + let unit = (pAdd.Chi3dAddress?.ChiUnit?.UnitDescriptor ?? "") + " " + (pAdd.Chi3dAddress?.ChiUnit?.UnitNo ?? ""); + let block = (pAdd.ChiBlock?.BlockDescriptor ?? "") + " " + (pAdd.ChiBlock?.BlockNo ?? ""); + let floor = pAdd.Chi3dAddress?.ChiFloor?.FloorNum ? pAdd.Chi3dAddress?.ChiFloor?.FloorNum + "樓 " : ""; + let street = (pAdd.ChiStreet?.StreetName ?? "") + " " + (pAdd.ChiStreet?.BuildingNoFrom ?? ""); + let buildingName = pAdd.BuildingName ?? ""; + let estate = pAdd.ChiEstate?.EstateName ?? ""; + let district = pAdd.ChiDistrict?.["Sub - district"] ?? pAdd.ChiDistrict?.["Sub-district"] ?? ""; + return getAddressStr([district, street, estate, buildingName, floor, block, unit]); + }; - const getAddressEng = (pAdd) => { - let unit = (pAdd.Eng3dAddress?.EngUnit?.UnitDescriptor ?? "") + " " + (pAdd.Eng3dAddress?.EngUnit?.UnitNo ?? ""); - let block = (pAdd.EngBlock?.BlockDescriptor ?? "") + " " + (pAdd.EngBlock?.BlockNo ?? ""); - let floor = pAdd.Eng3dAddress?.EngFloor?.FloorNum ? pAdd.Eng3dAddress?.EngFloor?.FloorNum + "/F " : ""; - let street = (pAdd.EngStreet?.EngUnit?.UnitDescriptor ?? "") + " " + (pAdd.Eng3dAddress?.EngUnit?.UnitNo ?? ""); - //let region = pAdd.Region ?? ""; - let buildingName = pAdd.BuildingName ?? ""; - let estate = pAdd.EngEstate?.EstateName ?? ""; - let district = pAdd.EngDistrict["Sub - district"] ?? ""; - return getAddressStr([unit, block, floor, buildingName, estate, street, district]); + const responseToData = () => { + const raw = location.state?.responseData?.data; + if (!raw) return; + + let rd; + try { + rd = JSON.parse(raw); + } catch (e) { + return; } - const getAddressChi = (pAdd) => { - let unit = (pAdd.Chi3dAddress?.ChiUnit?.UnitDescriptor ?? "") + " " + (pAdd.Chi3dAddress?.ChiUnit?.UnitNo ?? ""); - let block = (pAdd.ChiBlock?.BlockDescriptor ?? "") + " " + (pAdd.ChiBlock?.BlockNo ?? ""); - let floor = pAdd.Chi3dAddress?.ChiFloor?.FloorNum ? pAdd.Chi3dAddress?.ChiFloor?.FloorNum + "樓 " : ""; - let street = (pAdd.ChiStreet?.ChiUnit?.UnitDescriptor ?? "") + " " + (pAdd.Chi3dAddress?.ChiUnit?.UnitNo ?? ""); - //let region = pAdd.Region ?? ""; - let buildingName = pAdd.BuildingName ?? ""; - let estate = pAdd.ChiEstate?.EstateName ?? ""; - let district = pAdd.ChiDistrict["Sub - district"] ?? ""; - return getAddressStr([district, street, estate, buildingName, street, floor, block, unit]); + let data = { + enName: rd?.enName?.UnstructuredName ?? "", + chName: rd?.chName?.ChineseName ?? "", + idNo: rd?.idNo?.Identification ?? "", + checkDigit: rd?.idNo?.CheckDigit ?? "", + email: rd?.emailAddress ?? "", + phone: rd?.mobileNumber?.SubscriberNumber ?? "", + phoneCountryCode: rd?.mobileNumber?.CountryCode ?? "", + address1: "" + }; + + if (rd?.postalAddress) { + if (rd?.postalAddress?.EngPremisesAddress) { + data.address1 = getAddressEng(rd?.postalAddress?.EngPremisesAddress); + } else if (rd?.postalAddress?.ChiPremisesAddress) { + data.address1 = getAddressChi(rd?.postalAddress?.ChiPremisesAddress); + } + } else if (rd?.residentialAddress) { + if (rd?.residentialAddress?.EngPremisesAddress) { + data.address1 = getAddressEng(rd?.residentialAddress?.EngPremisesAddress); + } else if (rd?.residentialAddress?.ChiPremisesAddress) { + data.address1 = getAddressChi(rd?.residentialAddress?.ChiPremisesAddress); + } } - const getAddressStr = (strs) => { - let add = "" - strs.forEach(str => { - add += str.trim() ? str.trim() + ", " : ""; - }); - add = add.trim(); - if (add?.slice(- 1) == ",") { - add = add.substring(0, add.length - 1); - } - return add; + setIAmSmartData(data); + }; + + useEffect(() => { + // redirect if no iAM Smart response + location.state?.responseData ?? window.location.assign("/login"); + + if (!captchaImg) onCaptchaChange(); + responseToData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // ===== Formik ===== + const formik = useFormik({ + initialValues: { + email: iAmSmartData.email ?? "", + emailConfirm: iAmSmartData.email ?? "", + address1: iAmSmartData.address1 ?? "", + address2: '', + address3: '', + phone: iAmSmartData.phone ?? "", + phoneCountryCode: iAmSmartData.phoneCountryCode ?? "852", + submit: null, + fax: '', + faxCountryCode: '852', + captchaField: '' + }, + validationSchema: yup.object().shape({ + address1: yup.string().max(40, getMaxErrStr(40)).required(displayErrorMsg(intl.formatMessage({ id: 'validateAddressLine1' }))), + address2: yup.string().max(40), + address3: yup.string().max(40), + email: yup.string().email(displayErrorMsg(intl.formatMessage({ id: 'validEmailFormat' }))).max(128, getMaxErrStr(128)).required(displayErrorMsg(intl.formatMessage({ id: 'requireEmail' }))), + emailConfirm: yup.string().email(displayErrorMsg(intl.formatMessage({ id: 'validEmailFormat' }))).max(128, getMaxErrStr(128)) + .required(displayErrorMsg(intl.formatMessage({ id: 'requireEmail' }))) + .oneOf([yup.ref('email'), null], displayErrorMsg(intl.formatMessage({ id: 'validSameEmail' }))), + phoneCountryCode: yup.string().min(2, displayErrorMsg(intl.formatMessage({ id: 'requireAtLeast2Number' }))).required(displayErrorMsg(intl.formatMessage({ id: 'requireDialingCode' }))), + phone: yup.string().min(8, displayErrorMsg(intl.formatMessage({ id: 'requireAtLeast8Number' }))).required(displayErrorMsg(intl.formatMessage({ id: 'requireContactNumber' }))), + captchaField: yup.string().max(5, getMaxErrStr(5)).required(displayErrorMsg(intl.formatMessage({ id: 'requireVerify' }))).min(5, displayErrorMsg(intl.formatMessage({ id: 'requireVerify' }))) + }) + }); + + const { values } = formik; + + // Apply iAM Smart data to form + useEffect(() => { + if (iAmSmartData) { + formik.setFieldValue("enName", iAmSmartData.enName ?? ""); + formik.setFieldValue("chName", iAmSmartData.chName ?? ""); + formik.setFieldValue("idNo", iAmSmartData.idNo ?? ""); + formik.setFieldValue("checkDigit", iAmSmartData.checkDigit ?? ""); + formik.setFieldValue("email", iAmSmartData.email ?? ""); + formik.setFieldValue("emailConfirm", iAmSmartData.email ?? ""); + formik.setFieldValue("phone", iAmSmartData.phone ?? ""); + formik.setFieldValue("phoneCountryCode", iAmSmartData.phoneCountryCode ?? ""); + formik.setFieldValue("address1", iAmSmartData.address1 ?? ""); + + props.setIdNo(iAmSmartData.idNo ?? ""); + setLodingData(false); } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [iAmSmartData]); + + // ===== Validity check (FIX #3: no stale return) ===== + const checkDataField = (data) => { + const ok = + data.address1 !== "" && + data.email !== "" && + data.emailConfirm !== "" && + data.email === data.emailConfirm && + data.phone !== "" && + data.phoneCountryCode !== "" && + termsAndConAccept === true && + handleEmail(data.email) && + handlePhone(data.phone) && + handleCaptcha(data.captchaField) && + validateDistrict() && + !checkEmail && + !checkDistrict; + + setisValid(ok); + return ok; + }; + + // keep parent updated + useEffect(() => { + props.setUpdateValid(isValid); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isValid]); - const handleCheckEmail = async () => { - if (values?.email) { - const response = await axios.post(`${POST_USER_EMAIL}`, { - e1: values.email, - }) - setCheckEmail((Number(response.data[0]) === 1)) - return Number(response.data[0]) === 1 - } + // re-check on relevant changes + useEffect(() => { + checkDataField(values); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [values, selectedAddress4, selectedAddress5, termsAndConAccept, termsAndConNotAccept, checkEmail, checkDistrict]); + + const handleCheckBoxChange = (event) => { + if (event.target.name === 'termsAndConAccept') { + setTermsAndConAccept(event.target.checked); + setTermsAndConNotAccept(!event.target.checked); + } + if (event.target.name === 'termsAndConNotAccept') { + setTermsAndConNotAccept(event.target.checked); + setTermsAndConAccept(!event.target.checked); } + }; + + // ===== Submit (FIX #4: force email + district validation before posting) ===== + const _onSubmit = async () => { + setLoding(true); + + // force sync validations + const districtOk = validateDistrict(); - useEffect(() => { - if (email) { - email.addEventListener("blur", function () { - setCheckEmailBlur(true) - }) - } - }, [email]) - - useEffect(() => { - if (checkEmailBlur) { - handleCheckEmail() - setCheckEmailBlur(false) - } - }, [checkEmailBlur]) - - useEffect(() => { - if (iAmSmartData) { - formik.setFieldValue("enName", iAmSmartData.enName ?? ""); - formik.setFieldValue("chName", iAmSmartData.chName ?? ""); - formik.setFieldValue("idNo", iAmSmartData.idNo ?? ""); - formik.setFieldValue("checkDigit", iAmSmartData.checkDigit ?? ""); - formik.setFieldValue("email", iAmSmartData.email ?? ""); - formik.setFieldValue("emailConfirm", iAmSmartData.email ?? ""); - formik.setFieldValue("phone", iAmSmartData.phone ?? ""); - formik.setFieldValue("phoneCountryCode", iAmSmartData.phoneCountryCode ?? ""); - formik.setFieldValue("address1", iAmSmartData.address1 ?? ""); - props.setIdNo(iAmSmartData.idNo ?? ""); - setLodingData(false) - } - }, [iAmSmartData]) - - const onCaptchaChange = () => { - HttpUtils.post({ - url: POST_CAPTCHA, - params: { width: 130, height: 40, captcha: captchaImg }, - onSuccess: (responseData) => { - props.setBase64Url(responseData.base64Url) - setBase64Url(responseData.base64Url) - localStorage.setItem("base64Url", responseData.base64Url); - setCaptchaImage(localStorage.getItem('base64Url')); - } - }); + // force email dup check even if user never blurred + const duplicated = await checkEmailDuplicate(values.email); + setCheckEmail(duplicated); + + // recompute validity using latest flags + const okNow = checkDataField(values) && districtOk && !duplicated; + + if (!okNow) { + setLoding(false); + return; } + const userAddress = { + addressLine1: values.address1, + addressLine2: values.address2, + addressLine3: values.address3, + district: selectedAddress4?.type ? selectedAddress4.type : "", + country: selectedAddress5?.type ? selectedAddress5.type : "" + }; - 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 userFaxNo = { + countryCode: values.faxCountryCode, + faxNumber: values.fax }; - const handleCheckBoxChange = (event) => { - if (event.target.name == 'termsAndConAccept') { - setTermsAndConAccept(event.target.checked) - setTermsAndConNotAccept(!event.target.checked) - } - if (event.target.name == 'termsAndConNotAccept') { - setTermsAndConNotAccept(event.target.checked) - setTermsAndConAccept(!event.target.checked) - } + const userMobileNumber = { + countryCode: values.phoneCountryCode, + phoneNumber: values.phone }; - useEffect(() => { - props.setUpdateValid(isValid) - }, [isValid]) - - useEffect(() => { - checkDataField(values) - }, [ - selectedAddress4, selectedAddress5, - termsAndConAccept, termsAndConNotAccept]) - - useEffect(() => { - props.step == 2 ? _onSubmit() : null; - if (captchaImg == "") - onCaptchaChange(); - checkDataField(values) - }, [props.step]) - - const { handleSubmit } = useForm({}) - const _onSubmit = () => { - setLoding(true); - - const userAddress = { - "addressLine1": "", - "addressLine2": "", - "addressLine3": "", - "district": "", - "country": "" - }; - userAddress.addressLine1 = values.address1 - userAddress.addressLine2 = values.address2 - userAddress.addressLine3 = values.address3 - userAddress.district = selectedAddress4 == null ? "" : selectedAddress4.type - userAddress.country = selectedAddress5.type - - const userFaxNo = { - "countryCode": values.faxCountryCode, - "faxNumber": values.fax, - }; - const userMobileNumber = { - "countryCode": values.phoneCountryCode, - "phoneNumber": values.phone, - }; - let tncFlag = false; - if (termsAndConAccept) { - tncFlag = true - } - if (termsAndConNotAccept) { - tncFlag = false - } - - const preferLocale = locale === 'en' ? 'en' : locale === 'zh-HK' ? 'zh_HK' : 'zh-CN' - const formData = { - enName: iAmSmartData.enName, - chName: iAmSmartData.chName, - emailAddress: values.email, - idDocType: "HKID", - identification: iAmSmartData.idNo, - checkDigit: iAmSmartData.checkDigit, - tncFlag: tncFlag, - type: "IND", - userFaxNo: JSON.stringify(userFaxNo), - userMobileNumber: JSON.stringify(userMobileNumber), - userAddress: JSON.stringify(userAddress), - captcha: base64Url, - checkCode: checkCode, - preferLocale: preferLocale - }; - - if (isValid) { - axios.post(POST_IAMSMART_USER_REGISTER, formData, { - headers: { - "Content-Type": "multipart/form-data" - } - }) - .then((response) => { - console.log(response) - setCheckUpload(true) - setLoding(false); - }) - .catch(error => { - console.error(error); - setLoding(false); - }); - } else { - setLoding(false); - } - } + const tncFlag = termsAndConAccept ? true : false; - function handlePhone(phone) { - if (phone.length < 8) { - return false; - } else { - return true; - } - } + const preferLocale = locale === 'en' ? 'en' : locale === 'zh-HK' ? 'zh_HK' : 'zh-CN'; - function handleCaptcha(captchaField) { - if (captchaField.length == 5) { - return true - } else { - return false - } - } + const formData = { + enName: iAmSmartData.enName, + chName: iAmSmartData.chName, + emailAddress: values.email, + idDocType: "HKID", + identification: iAmSmartData.idNo, + checkDigit: iAmSmartData.checkDigit, + tncFlag: tncFlag, + type: "IND", + userFaxNo: JSON.stringify(userFaxNo), + userMobileNumber: JSON.stringify(userMobileNumber), + userAddress: JSON.stringify(userAddress), + captcha: base64Url, + checkCode: checkCode, + preferLocale: preferLocale + }; - function handleEmail(email) { - var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; - if (!email.match(validRegex)) { - return false; - } else { - return true; - } + try { + await axios.post(POST_IAMSMART_USER_REGISTER, formData, { + headers: { "Content-Type": "multipart/form-data" } + }); + setCheckUpload(true); + } catch (error) { + console.error(error); + setCheckUpload(false); + } finally { + setLoding(false); } + }; - function displayErrorMsg(errorMsg) { - return {errorMsg} + // Auto-submit when step reaches 2 (existing behavior) + useEffect(() => { + if (props.step === 2) { + _onSubmit(); } + if (!captchaImg) onCaptchaChange(); + checkDataField(values); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.step]); - const formik = useFormik({ - initialValues: ({ - email: iAmSmartData.email ?? "", - emailConfirm: iAmSmartData.email ?? "", - address1: iAmSmartData.address1 ?? "", - address2: '', - address3: '', - phone: iAmSmartData.phone ?? "", - phoneCountryCode: iAmSmartData.phoneCountryCode ?? "852", - submit: null, - fax: '', - faxCountryCode: '852', - captchaField: '' - }), - validationSchema: yup.object().shape({ - address1: yup.string().max(40, getMaxErrStr(40)).required(displayErrorMsg(intl.formatMessage({ id: 'validateAddressLine1' }))), - address2: yup.string().max(40), - address3: yup.string().max(40), - email: yup.string().email(displayErrorMsg(intl.formatMessage({ id: 'validEmailFormat' }))).max(128, getMaxErrStr(128)).required(displayErrorMsg(intl.formatMessage({ id: 'requireEmail' }))), - emailConfirm: yup.string().email(displayErrorMsg(intl.formatMessage({ id: 'validEmailFormat' }))).max(128, getMaxErrStr(128)).required(displayErrorMsg(intl.formatMessage({ id: 'requireEmail' }))).oneOf([yup.ref('email'), null], displayErrorMsg(intl.formatMessage({ id: 'validSameEmail' }))), - phoneCountryCode: yup.string().min(2, displayErrorMsg(intl.formatMessage({ id: 'requireAtLeast2Number' }))).required(displayErrorMsg(intl.formatMessage({ id: 'requireDialingCode' }))), - phone: yup.string().min(8, displayErrorMsg(intl.formatMessage({ id: 'requireAtLeast8Number' }))).required(displayErrorMsg(intl.formatMessage({ id: 'requireContactNumber' }))), - captchaField: yup.string().max(5, getMaxErrStr(5)).required(displayErrorMsg(intl.formatMessage({ id: 'requireVerify' }))).min(5, displayErrorMsg(intl.formatMessage({ id: 'requireVerify' }))),//.oneOf([captcha], displayErrorMsg('請輸入有效驗證')), - }), - }); + const handleCCPChange = (e) => e.preventDefault(); - const handleCCPChange = (e) => { - e.preventDefault(); - }; + return ( + isLoadingData ? ( + + ) : ( + +
e.preventDefault()}> + {/* Input Form */} + + + + +
+ + + +
- const { values } = formik - useEffect(() => { - checkDataField(values) - }, [values]) - - return ( - isLoadingData ? - : - - - {/* Input Form */} - - - - - - -
- - - -
- - - 。 - - iAM Smart: - -
-
- - - - - - - - - - - - - - - : {iAmSmartData.idNo ? iAM Smart : <>} - {/* {iAmSmartData.idNo + "(" + iAmSmartData.checkDigit + ")"} */} - - - - {iAmSmartData?.idNo?.slice(0, 4)}{showId ? iAmSmartData?.idNo?.slice(4) : "****"}{showId ? '(' + iAmSmartData.checkDigit + ')' : null} - - - {showId ? : } - - - - - - - - - - - : {iAmSmartData.enName}{iAmSmartData.enName ? iAM Smart : <>} - - - - - - - - - - {intl.formatMessage({ id: 'userChineseName' })}: {iAmSmartData.chName}{iAmSmartData.chName ? iAM Smart : <>} - - - - - - - - - - * - - - - - - { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - /> - {iAmSmartData.address1 != "" && iAmSmartData.address1 == formik.values.address1 ? iAM Smart : null} - - - - { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - /> - {iAmSmartData.address2 != "" && iAmSmartData.address2 == formik.values.address2 ? iAM Smart : null} - - - - - { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - /> - {iAmSmartData.address3 != "" && iAmSmartData.address3 == formik.values.address3 ? iAM Smart : null} - - - - option.type ? intl.formatMessage({ id: option.type }) : ""} - onChange={(event, newValue) => { - setSelectedAddress4(newValue); - }} - sx={{ - '& .MuiInputBase-root': { alignItems: 'center' }, - '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, - '& .MuiOutlinedInput-root': { height: 40 } - }} - renderInput={(params) => } - /> - option.type ? intl.formatMessage({ id: option.type }) : ""} - onChange={(event, newValue) => { - if (newValue !== null) { - setSelectedAddress5(newValue); - if (newValue.type === 'hongKong') { - setCheckCountry(false) - } else { - setSelectedAddress4(""); - setCheckCountry(true) - } - } else { - setSelectedAddress4(""); - setCheckCountry(true) - } - }} - sx={{ - '& .MuiInputBase-root': { alignItems: 'center' }, - '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, - '& .MuiOutlinedInput-root': { height: 40 } - }} - renderInput={(params) => } - /> - {formik.touched.address1 && formik.errors.address1 && ( - - {formik.errors.address1} - - )} - {formik.touched.address2 && formik.errors.address2 && ( - - {formik.errors.address2} - - )} - {formik.touched.address3 && formik.errors.address3 && ( - - {formik.errors.address3} - - )} - {checkDistrict && ( - - {districtErrStr} - - )} - - - - - - - - - - - - - - - - - * - - - - - - { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - /> - {iAmSmartData.email && iAmSmartData.email == formik.values.email ? iAM Smart : null} - - - {formik.touched.email && formik.errors.email && ( - - {formik.errors.email} - - )} - {checkEmail && ( - - - - )} - - - - - - - - * - - - - { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - /> - {iAmSmartData.email && iAmSmartData.email == formik.values.emailConfirm ? iAM Smart : null} - - - {formik.touched.emailConfirm && formik.errors.emailConfirm && ( - - {formik.errors.emailConfirm} - - )} - - - - - - - - - - - - - - * - - - - - { - const value = event.target.value; - if (value.match(/[^0-9]/)) { - return event.preventDefault(); - } - formik.setFieldValue("phoneCountryCode", value); - }} - placeholder={intl.formatMessage({ id: 'dialingCode' })} - error={Boolean(formik.touched.phone && formik.errors.phone)} - onBlur={formik.handleBlur} - inputProps={{ - maxLength: 3, - onKeyDown: (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - sx={{ width: '25%' }} - /> - { - const value = event.target.value; - if (value.match(/[^0-9]/)) { - return event.preventDefault(); - } - formik.setFieldValue("phone", value); - }} - placeholder={intl.formatMessage({ id: 'userContactNumber' })} - error={Boolean(formik.touched.phone && formik.errors.phone)} - onBlur={formik.handleBlur} - inputProps={{ - maxLength: 11, - onKeyDown: (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - fullWidth - /> - {iAmSmartData.phone && iAmSmartData.phone == formik.values.phone && iAmSmartData.phoneCountryCode == formik.values.phoneCountryCode ? iAM Smart : null} - - {formik.touched.phone && formik.errors.phone && ( - - {formik.errors.phone} - - )} - - - - - - - - - - - - - - - { - const value = event.target.value; - if (value.match(/[^0-9]/)) { - return event.preventDefault(); - } - formik.setFieldValue("faxCountryCode", value); - }} - placeholder={intl.formatMessage({ id: 'dialingCode' })} - onBlur={formik.handleBlur} - inputProps={{ - maxLength: 3, - onKeyDown: (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - sx={{ width: '25%' }} - /> - { - const value = event.target.value; - if (value.match(/[^0-9]/)) { - return event.preventDefault(); - } - formik.setFieldValue("fax", value); - }} - placeholder={intl.formatMessage({ id: 'userFaxNumber' })} - inputProps={{ - maxLength: 8, - onKeyDown: (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - } - }, - }} - sx={{ width: '75%' }} - /> - - - - - - - - - - - - - - * - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * - - - - - - - { onCaptchaChange() }}> - - - - - { - const value = event.target.value; - props.setCheckCode(event.target.value); - setCheckCode(event.target.value); - formik.setFieldValue("captchaField", value); - }} - sx={{ width: '75%' }} - /> - - - {formik.touched.captchaField && formik.errors.captchaField && ( - - {formik.errors.captchaField} - - )} - - - - - - - {/* Preview Form */} - - - - -
- - - -
-
-
- - - - - - - - {/* - Already have an account? - */} - - - - - - - - - {formik?.values?.idNo?.slice(0, 4)} - {/* {formik.values.idNo + "(" + formik.values.checkDigit + ")"} */} - - - {showComId ? formik?.values?.idNo?.slice(4) : "****"}{showComId ? '(' + formik.values.checkDigit + ')' : null} - {/* {formik.values.idNo + "(" + formik.values.checkDigit + ")"} */} - - - {showComId ? : } - - - - - - - : - - - {formik.values.enName} - - {iAmSmartData.enName ? iAM Smart : <>} - - - - - - : - - - {formik.values.chName} - - - - - - - : - - - - {formik.values.address1} - - {formik.values.address2 != null ? - - {formik.values.address2} - - : null} - {formik.values.address3 != null ? - - {formik.values.address3} - - : null} - {selectedAddress5.type === "hongKong" ? - - {/* - : - */} - - {!selectedAddress4 ? "" : intl.formatMessage({ id: selectedAddress4.type })} - - - : null} - - {/* - : - */} - - {intl.formatMessage({ id: selectedAddress5.type })} - - - - - - - - - - - - - - - - : - - - {formik.values.email} - - - - - - - : - - - +{formik.values.phoneCountryCode} {formik.values.phone} - - - - {formik.values.faxCountryCode != "" && formik.values.fax != "" ? - - - - : - - - +{formik.values.faxCountryCode} {formik.values.fax} - - - - : null} - - -
-
- {/* Submit page */} - - - {isLoading ? - : - - {checkUpload ? - // SUCCESS page - - - - - - - - - - - : - // ERROR page - - {/* */} - - - - - - - } - + + 。 + + + iAM Smart + + : + + + + + + + + + + + + + + + + + + + + :{" "} + {iAmSmartData.idNo ? iAM Smart : null} + + + + + {iAmSmartData?.idNo?.slice(0, 4)} + {showId ? iAmSmartData?.idNo?.slice(4) : "****"} + {showId ? '(' + iAmSmartData.checkDigit + ')' : null} + + + {showId ? : } + + + + + + + + + + + : {iAmSmartData.enName} + {iAmSmartData.enName ? iAM Smart : null} + + + + + + + + + + {intl.formatMessage({ id: 'userChineseName' })}: {iAmSmartData.chName} + {iAmSmartData.chName ? iAM Smart : null} + + + + + + + + + + + * + + + + + { + if (e.key === 'Enter') e.preventDefault(); } + }} + /> + {iAmSmartData.address1 !== "" && iAmSmartData.address1 === formik.values.address1 + ? iAM Smart + : null} + + + + { + if (e.key === 'Enter') e.preventDefault(); + } + }} + /> + + + + { + if (e.key === 'Enter') e.preventDefault(); + } + }} + /> + + + {/* District */} + option.type ? intl.formatMessage({ id: option.type }) : ""} + onChange={(event, newValue) => { + setSelectedAddress4(newValue); + }} + onBlur={() => validateDistrict()} + sx={{ + '& .MuiInputBase-root': { alignItems: 'center' }, + '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, + '& .MuiOutlinedInput-root': { height: 40 } + }} + renderInput={(params) => ( + + )} + /> + + {/* Country */} + option.type ? intl.formatMessage({ id: option.type }) : ""} + onChange={(event, newValue) => { + if (newValue !== null) { + setSelectedAddress5(newValue); + if (newValue.type === 'hongKong') { + setCheckCountry(false); + } else { + setSelectedAddress4(null); + setCheckCountry(true); + } + } else { + setSelectedAddress4(null); + setCheckCountry(true); + } + }} + sx={{ + '& .MuiInputBase-root': { alignItems: 'center' }, + '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, + '& .MuiOutlinedInput-root': { height: 40 } + }} + renderInput={(params) => ( + + )} + /> + + {formik.touched.address1 && formik.errors.address1 && ( + + {formik.errors.address1} + + )} + {formik.touched.address2 && formik.errors.address2 && ( + + {formik.errors.address2} + + )} + {formik.touched.address3 && formik.errors.address3 && ( + + {formik.errors.address3} + + )} + {checkDistrict && ( + + {districtErrStr} + + )} + + + + {/* Contact */} + + + + + + + + + + + + + + + + * + + + + + { + setCheckEmail(false); + formik.handleChange(e); + }} + placeholder={intl.formatMessage({ id: 'userContactEmail' })} + onBlur={async (e) => { + formik.handleBlur(e); + const dup = await checkEmailDuplicate(formik.values.email); + setCheckEmail(dup); + }} + inputProps={{ + onKeyDown: (e) => { + if (e.key === 'Enter') e.preventDefault(); + } + }} + /> + {iAmSmartData.email && iAmSmartData.email === formik.values.email + ? iAM Smart + : null} + + + {formik.touched.email && formik.errors.email && ( + + {formik.errors.email} + + )} + {checkEmail && ( + + + + )} + + + + + + + + + * + + + + + { + if (e.key === 'Enter') e.preventDefault(); + } + }} + /> + {iAmSmartData.email && iAmSmartData.email === formik.values.emailConfirm + ? iAM Smart + : null} + + + {formik.touched.emailConfirm && formik.errors.emailConfirm && ( + + {formik.errors.emailConfirm} + + )} + + + + + + {/* Phone/Fax */} + + + + + + + + * + + + + + { + const value = event.target.value; + if (value.match(/[^0-9]/)) return event.preventDefault(); + formik.setFieldValue("phoneCountryCode", value); + }} + placeholder={intl.formatMessage({ id: 'dialingCode' })} + error={Boolean(formik.touched.phone && formik.errors.phone)} + onBlur={formik.handleBlur} + inputProps={{ + maxLength: 3, + onKeyDown: (e) => { + if (e.key === 'Enter') e.preventDefault(); + } + }} + sx={{ width: '25%' }} + /> + { + const value = event.target.value; + if (value.match(/[^0-9]/)) return event.preventDefault(); + formik.setFieldValue("phone", value); + }} + placeholder={intl.formatMessage({ id: 'userContactNumber' })} + error={Boolean(formik.touched.phone && formik.errors.phone)} + onBlur={formik.handleBlur} + inputProps={{ + maxLength: 11, + onKeyDown: (e) => { + if (e.key === 'Enter') e.preventDefault(); + } + }} + fullWidth + /> + {iAmSmartData.phone && iAmSmartData.phone === formik.values.phone && iAmSmartData.phoneCountryCode === formik.values.phoneCountryCode + ? iAM Smart + : null} + + + {formik.touched.phone && formik.errors.phone && ( + + {formik.errors.phone} + + )} + + + + + + + + + + + + + { + const value = event.target.value; + if (value.match(/[^0-9]/)) return event.preventDefault(); + formik.setFieldValue("faxCountryCode", value); + }} + placeholder={intl.formatMessage({ id: 'dialingCode' })} + onBlur={formik.handleBlur} + inputProps={{ + maxLength: 3, + onKeyDown: (e) => { + if (e.key === 'Enter') e.preventDefault(); + } + }} + sx={{ width: '25%' }} + /> + { + const value = event.target.value; + if (value.match(/[^0-9]/)) return event.preventDefault(); + formik.setFieldValue("fax", value); + }} + placeholder={intl.formatMessage({ id: 'userFaxNumber' })} + inputProps={{ + maxLength: 8, + onKeyDown: (e) => { + if (e.key === 'Enter') e.preventDefault(); + } + }} + sx={{ width: '75%' }} + /> + + + + + + + {/* Terms */} + + + + + + * + + + + + +
+ + + + + + + + + + + - - - - ); -} + + + + + {/* Captcha */} + + + + + + * + + + + + + + + { onCaptchaChange(); }}> + + + + + { + const value = event.target.value; + props.setCheckCode(value); + setCheckCode(value); + formik.setFieldValue("captchaField", value); + }} + sx={{ width: '75%' }} + /> + + + + {formik.touched.captchaField && formik.errors.captchaField && ( + + {formik.errors.captchaField} + + )} + + + + + + + + + {/* Preview Form */} + + + + +
+ + + +
+
+
+ + + + + + + + + + {iAmSmartData?.idNo?.slice(0, 4)} + + + {showComId ? iAmSmartData?.idNo?.slice(4) : "****"} + {showComId ? '(' + iAmSmartData.checkDigit + ')' : null} + + + + {showComId ? : } + + + + + + + + : + + + {iAmSmartData.enName} + + {iAmSmartData.enName ? iAM Smart : null} + + + + + + + : + + + {iAmSmartData.chName} + + + + + + + + : + + + {formik.values.address1} + {formik.values.address2 ? {formik.values.address2} : null} + {formik.values.address3 ? {formik.values.address3} : null} + + {selectedAddress5.type === "hongKong" ? ( + + {!selectedAddress4 ? "" : intl.formatMessage({ id: selectedAddress4.type })} + + ) : null} + + + {intl.formatMessage({ id: selectedAddress5.type })} + + + + + + + + + + + + + + + + + : + + {formik.values.email} + + + + + + + : + + + +{formik.values.phoneCountryCode} {formik.values.phone} + + + + + {formik.values.faxCountryCode && formik.values.fax ? ( + + + + : + + + +{formik.values.faxCountryCode} {formik.values.fax} + + + + ) : null} + + +
+
+ + {/* Submit page */} + + + {isLoading ? ( + + ) : ( + + {checkUpload ? ( + + + + + + + + + + + ) : ( + + + + + + + + )} + + )} + + + + + ) + ); +}; -export default CustomFormWizard; +export default IAmSmartFormWizard; diff --git a/src/utils/HttpUtils.js b/src/utils/HttpUtils.js index 6680b2e..9b89f3f 100644 --- a/src/utils/HttpUtils.js +++ b/src/utils/HttpUtils.js @@ -106,19 +106,19 @@ export const fileDownload = ({ url, fileId, skey, params, method, onResponse, on }; const fileDownloadResponse = (response, onResponse) => { - const fn = response.headers.get("content-disposition")?.split("filename=")[1]?.split('"')[1]?.trim() ?? filename; + const cd = response.headers?.['content-disposition']; + const fn = cd?.split('filename=')[1]?.replaceAll('"','')?.trim() || 'export.xlsx'; + const url = URL.createObjectURL(response.data); const a = document.createElement('a'); a.href = url; - a.setAttribute("download", fn); + a.download = fn; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); - if (onResponse) { - onResponse(); - } -} + onResponse?.(); +}; export const fileUpload = ({ refType, refId, files, refCode, onSuccess, onFail, onError }) => { postWithFiles({