You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

2288 line
138 KiB

  1. import { useEffect, useState, useRef } from 'react';
  2. import {
  3. Box,
  4. Button, Checkbox
  5. // MenuItem
  6. , FormControl, FormGroup, FormHelperText,
  7. Grid, IconButton,
  8. InputAdornment,
  9. InputLabel, OutlinedInput,
  10. Stack, TextField, Typography
  11. } from '@mui/material';
  12. import Autocomplete from "@mui/material/Autocomplete";
  13. import { useForm } from 'react-hook-form';
  14. // third party
  15. import { FormikProvider, useFormik } from 'formik';
  16. import * as yup from 'yup';
  17. // import axios from "axios";
  18. // project import
  19. // import AnimateButton from 'components/@extended/AnimateButton';
  20. import { strengthColorChi, strengthIndicator } from 'utils/password-strength';
  21. // import {apiPath} from "auth/utils";
  22. import axios from "axios";
  23. import { POST_USERNAME, POST_USER_EMAIL, POST_CAPTCHA, POST_PUBLIC_USER_REGISTER, POST_IDNO, POST_CAPTCHA_AUDIO } from "utils/ApiPathConst";
  24. // import * as HttpUtils from 'utils/HttpUtils';
  25. import * as ComboData from "utils/ComboData";
  26. import Loadable from 'components/Loadable';
  27. import { lazy } from 'react';
  28. const UploadFileTable = Loadable(lazy(() => import('./UploadFileTable')));
  29. const PreviewUploadFileTable = Loadable(lazy(() => import('./PreviewUploadFileTable')));
  30. const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent')));
  31. // import UploadFileTable from './UploadFileTable';
  32. // import LoadingComponent from "../../extra-pages/LoadingComponent";
  33. // assets
  34. import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons';
  35. // import { Paper } from '../../../../node_modules/@mui/material/index';
  36. import { ThemeProvider } from "@emotion/react";
  37. import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';
  38. import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
  39. import LoopIcon from '@mui/icons-material/Loop';
  40. import { useTheme } from '@mui/material/styles';
  41. import { FormattedMessage, useIntl } from "react-intl";
  42. import { Link } from 'react-router-dom';
  43. import { PNSPS_LONG_BUTTON_THEME } from "../../../themes/buttonConst";
  44. import * as HttpUtils from "../../../utils/HttpUtils";
  45. import { notifyActionError } from 'utils/CommonFunction';
  46. // ============================|| FIREBASE - REGISTER ||============================ //
  47. const CustomFormWizard = (props) => {
  48. const intl = useIntl();
  49. const { locale } = intl;
  50. const theme = useTheme()
  51. const [level, setLevel] = useState();
  52. const [showPassword, setShowPassword] = useState(false);
  53. const [showConfirmPassword, setshowConfirmPassword] = useState(false);
  54. const [showId, setshowId] = useState(false);
  55. const [fileList, setFileList] = useState([]);
  56. const [fileListData, setFileListData] = useState([]);
  57. const [checkUpload, setCheckUpload] = useState(false);
  58. const [isLoading, setLoding] = useState(true);
  59. const [updateRows, setUpdateRows] = useState([]);
  60. const [captchaImg, setCaptchaImage] = useState("");
  61. const [base64Url, setBase64Url] = useState("")
  62. const [checkCode, setCheckCode] = useState("")
  63. const handleClickShowPassword = () => {
  64. setShowPassword(!showPassword);
  65. };
  66. const handleClickShowConfirmPassword = () => {
  67. setshowConfirmPassword(!showConfirmPassword);
  68. };
  69. const handleMouseDownPassword = (event) => {
  70. event.preventDefault();
  71. };
  72. const handleClickShowId = () => {
  73. setshowId(!showId);
  74. };
  75. const handleMouseDownId = (event) => {
  76. event.preventDefault();
  77. };
  78. const changePassword = (value) => {
  79. const temp = strengthIndicator(value);
  80. setLevel(strengthColorChi(temp));
  81. };
  82. const [selectedIdDocType, setSelectedIdDocType] = useState({});
  83. const [selectedIdDocInputType, setSelectedIdDocInputType] = useState();
  84. const [selectedAddress4, setSelectedAddress4] = useState(null);
  85. const [selectedAddress5, setSelectedAddress5] = useState(ComboData.country[0]);
  86. const [termsAndConAccept, setTermsAndConAccept] = useState(false);
  87. const [termsAndConNotAccept, setTermsAndConNotAccept] = useState(false);
  88. const [isValid, setisValid] = useState(false);
  89. const [checkCountry, setCheckCountry] = useState(false);
  90. // eslint-disable-next-line no-unused-vars -- used as ref for hidden file input (ref={fileInputRef}) and in click handlers
  91. const fileInputRef = useRef(null);
  92. const username = document.getElementById("username-login")
  93. const [checkUsername, setCheckUsername] = useState(false);
  94. const [checkUsernameBlur, setCheckUsernameBlur] = useState(false)
  95. const idDocNumber = document.getElementById("idNo-login")
  96. const [checkIdDocNumber, setCheckIdDocNumber] = useState(false)
  97. const [checkIdDocNumberBlur, setCheckIdDocNumberBlur] = useState(false)
  98. const [idDocHKIDNumber, setIdDocHKIDNumber] = useState(document.getElementById("idNo-hkid-login"))
  99. const [checkIdDocHKIDNumberBlur, setCheckIdDocHKIDNumberBlur] = useState(false)
  100. const [checkDigit, setCheckDigit] = useState(document.getElementById("checkDigit-login"))
  101. const [checkCheckDigitBlur, setCheckCheckDigitBlur] = useState(false)
  102. const [checkHKIdDocWithCheckDigit, setCheckHKIdDocWithCheckDigit] = useState(false)
  103. const email = document.getElementById("email-login")
  104. const [checkEmail, setCheckEmail] = useState(false)
  105. const [checkEmailBlur, setCheckEmailBlur] = useState(false)
  106. const district = document.getElementById("address4-combo")
  107. const [checkDistrict, setCheckDistrict] = useState(false)
  108. const [checkDistrictBlur, setCheckDistrictBlur] = useState(false)
  109. const [districtErrStr, setDistrictErrStr] = useState("")
  110. const idDocTypeComboList = ComboData.idDocType;
  111. const refType = "identification";
  112. useEffect(() => {
  113. changePassword('');
  114. if (captchaImg == ""){
  115. onCaptchaChange();
  116. }
  117. }, []);
  118. const playCaptchaAudio = async () => {
  119. try {
  120. const resp = await axios.post(
  121. `${POST_CAPTCHA_AUDIO}`,
  122. { base64Url, lang: intl.locale },
  123. { responseType: "arraybuffer" }
  124. );
  125. const blob = new Blob([resp.data], { type: "audio/wav" });
  126. const url = URL.createObjectURL(blob);
  127. const audio = new Audio(url);
  128. await audio.play();
  129. } catch (error) {
  130. let message = intl.formatMessage({ id: "captchaAudioError" });
  131. if (error.response) {
  132. if (error.response.status === 404) {
  133. message = intl.formatMessage({ id: "captchaExpired" });
  134. } else if (error.response.status === 500) {
  135. message = intl.formatMessage({ id: "captchaAudioServerError" });
  136. }
  137. } else if (error.request) {
  138. message = intl.formatMessage({ id: "connectionError" });
  139. }
  140. notifyActionError(message);
  141. }
  142. };
  143. useEffect(() => {
  144. if (selectedIdDocType.type === "HKID"){
  145. setIdDocHKIDNumber(document.getElementById("idNo-hkid-login"))
  146. setCheckDigit(document.getElementById("checkDigit-login"))
  147. // setTimeout(() => {
  148. // }, 1000);
  149. }
  150. }, [selectedIdDocType]);
  151. const handleCheckUsername = async () => {
  152. if (values?.username) {
  153. if (handleUsername(values.username)){
  154. const response = await axios.post(`${POST_USERNAME}`, {
  155. u1: values.username,
  156. })
  157. setCheckUsername((Number(response.data[0]) > 0))
  158. return Number(response.data[0]) > 0
  159. }
  160. }
  161. }
  162. const handleCheckIdDocNumber = async () => {
  163. if (values?.idNo&&values?.checkDigit !="") {
  164. if (handleIdNo(values.idNo, selectedIdDocType.type, values.checkDigit)){
  165. const response = await axios.post(`${POST_IDNO}`, {
  166. i1: values.idNo,
  167. })
  168. // console.log(response.data.Vaild)
  169. setCheckIdDocNumber(response.data.Vaild === false)
  170. return response.data.Vaild === false
  171. }
  172. }
  173. }
  174. const handleCheckIdDocNumberbyCheckDigit = async () => {
  175. if (values?.checkDigit) {
  176. if (handleIdNo(values.idNo, selectedIdDocType.type, values.checkDigit)){
  177. const response = await axios.post(`${POST_IDNO}`, {
  178. i1: values.idNo,
  179. })
  180. // console.log(response.data.Vaild)
  181. setCheckIdDocNumber(response.data.Vaild === false)
  182. return response.data.Vaild === false
  183. }
  184. }
  185. }
  186. /** POST /user/checkE1 — returns true if email is already registered */
  187. const checkEmailRegistered = async (emailStr) => {
  188. const em = emailStr?.trim();
  189. if (!em) return false;
  190. const response = await axios.post(`${POST_USER_EMAIL}`, { e1: em });
  191. return Number(response.data[0]) > 0;
  192. };
  193. const handleCheckEmail = async () => {
  194. if (!values?.email) return false;
  195. if (!handleEmail(values.email)) return false;
  196. try {
  197. const taken = await checkEmailRegistered(values.email);
  198. setCheckEmail(taken);
  199. return taken;
  200. } catch (error) {
  201. notifyActionError(intl.formatMessage({ id: 'connectionError' }));
  202. return false;
  203. }
  204. };
  205. const isDistrictSelectionValid = () => {
  206. if (selectedAddress5?.type !== "hongKong") {
  207. return true;
  208. }
  209. return !(selectedAddress4 == null || selectedAddress4 === "");
  210. };
  211. const handleCheckDistrict = () => {
  212. setDistrictErrStr("");
  213. if (selectedAddress5?.type === "hongKong") {
  214. if (selectedAddress4 == null || selectedAddress4 == "" || selectedAddress4 == {}){
  215. setCheckDistrict(true)
  216. setDistrictErrStr(getRequiredErrStr("district"))
  217. }else {
  218. setCheckDistrict(false)
  219. }
  220. } else {
  221. setCheckDistrict(false);
  222. }
  223. }
  224. useEffect(() => {
  225. handleCheckDistrict();
  226. }, [selectedAddress4, selectedAddress5]);
  227. useEffect(() => {
  228. if (username) {
  229. username.addEventListener("blur", function () {
  230. setCheckUsernameBlur(true)
  231. })
  232. }
  233. }, [username])
  234. useEffect(() => {
  235. if (checkUsernameBlur) {
  236. handleCheckUsername()
  237. setCheckUsernameBlur(false)
  238. }
  239. }, [checkUsernameBlur])
  240. useEffect(() => {
  241. if (idDocNumber) {
  242. idDocNumber.addEventListener("blur", function () {
  243. setCheckIdDocNumberBlur(true)
  244. })
  245. }
  246. }, [idDocNumber])
  247. useEffect(() => {
  248. if (checkIdDocNumberBlur) {
  249. handleCheckIdDocNumber()
  250. setCheckIdDocNumberBlur(false)
  251. }
  252. }, [checkIdDocNumberBlur])
  253. useEffect(() => {
  254. if (idDocHKIDNumber) {
  255. idDocHKIDNumber.addEventListener("blur", function () {
  256. setCheckIdDocHKIDNumberBlur(true)
  257. })
  258. }
  259. }, [idDocHKIDNumber])
  260. useEffect(() => {
  261. if (checkIdDocHKIDNumberBlur) {
  262. handleCheckIdDocNumber()
  263. setCheckIdDocHKIDNumberBlur(false)
  264. }
  265. }, [checkIdDocHKIDNumberBlur])
  266. useEffect(() => {
  267. if (checkDigit) {
  268. checkDigit.addEventListener("blur", function () {
  269. setCheckCheckDigitBlur(true)
  270. })
  271. }
  272. }, [checkDigit])
  273. useEffect(() => {
  274. if (checkCheckDigitBlur) {
  275. handleCheckIdDocNumberbyCheckDigit()
  276. setCheckCheckDigitBlur(false)
  277. }
  278. }, [checkCheckDigitBlur])
  279. useEffect(() => {
  280. if (email) {
  281. email.addEventListener("blur", function () {
  282. setCheckEmailBlur(true)
  283. })
  284. }
  285. }, [email])
  286. useEffect(() => {
  287. if (checkEmailBlur) {
  288. handleCheckEmail()
  289. setCheckEmailBlur(false)
  290. }
  291. }, [checkEmailBlur])
  292. useEffect(() => {
  293. if (district) {
  294. district.addEventListener("blur", function () {
  295. setCheckDistrictBlur(true)
  296. })
  297. }
  298. }, [district])
  299. useEffect(() => {
  300. if (checkDistrictBlur) {
  301. handleCheckDistrict()
  302. setCheckDistrictBlur(false)
  303. }
  304. }, [checkDistrictBlur])
  305. const onCaptchaChange = () => {
  306. HttpUtils.post({
  307. url: POST_CAPTCHA,
  308. params: { width: 130, height: 40, captcha: captchaImg },
  309. onSuccess: (responseData) => {
  310. props.setBase64Url(responseData.base64Url)
  311. setBase64Url(responseData.base64Url)
  312. localStorage.setItem("base64Url", responseData.base64Url);
  313. setCaptchaImage(localStorage.getItem('base64Url'));
  314. }
  315. });
  316. }
  317. /** Step-0 validity excluding duplicate-email result from /checkE1 (see checkEmail state). */
  318. const computeStep0ValidSync = (data) => (
  319. handleCaptcha(data.captchaField) &&
  320. data.username !== "" &&
  321. data.password !== "" &&
  322. data.confirmPassword !== "" &&
  323. data.password == data.confirmPassword &&
  324. selectedIdDocType.type !== "" &&
  325. data.idNo !== "" &&
  326. handleName(data.enName, data.chName) &&
  327. data.address1 !== "" &&
  328. data.email !== "" &&
  329. data.emailConfirm !== "" &&
  330. data.email == data.emailConfirm &&
  331. data.phone !== "" &&
  332. data.phoneCountryCode !== "" &&
  333. termsAndConAccept == true &&
  334. fileList.length !== 0 &&
  335. handlePassword(data.password) &&
  336. handleEmail(data.email) &&
  337. handleIdNo(data.idNo, selectedIdDocType.type, data.checkDigit) &&
  338. handlePhone(data.phone) &&
  339. handleUsername(data.username) &&
  340. isDistrictSelectionValid() &&
  341. !checkUsername &&
  342. !checkIdDocNumber
  343. );
  344. const computeStep0Valid = (data) => computeStep0ValidSync(data) && !checkEmail;
  345. const checkDataField = (data) => {
  346. const can = computeStep0Valid(data);
  347. setisValid(can);
  348. return can;
  349. };
  350. const handleCheckBoxChange = (event) => {
  351. if (event.target.name == 'termsAndConAccept') {
  352. setTermsAndConAccept(event.target.checked)
  353. setTermsAndConNotAccept(!event.target.checked)
  354. }
  355. if (event.target.name == 'termsAndConNotAccept') {
  356. setTermsAndConNotAccept(event.target.checked)
  357. setTermsAndConAccept(!event.target.checked)
  358. }
  359. };
  360. useEffect(() => {
  361. let updateRowList = new DataTransfer();
  362. var updateRowsIndex = updateRows.length;
  363. const saveFileList = [];
  364. if (updateRowsIndex != null) {
  365. for (let i = 0; i < updateRowsIndex; i++) {
  366. const file = updateRows[i]
  367. file.id = i;
  368. updateRowList.items.add(file);
  369. saveFileList.push(file);
  370. }
  371. let updatedFileList = updateRowList.files;
  372. setFileList(updatedFileList);
  373. setFileListData(saveFileList)
  374. }
  375. if (fileInputRef.current) {
  376. fileInputRef.current.value = '';
  377. }
  378. }, [updateRows]);
  379. const handleFileUpload = (event) => {
  380. let updateList = new DataTransfer();
  381. let currentFileList = fileListData;
  382. const uploadFileList = event.target.files;
  383. const saveFileList = [];
  384. var currentIndex = 0;
  385. if (currentFileList.length != null) {
  386. currentIndex = currentFileList.length;
  387. for (let i = 0; i < currentIndex; i++) {
  388. const file = currentFileList[i]
  389. // file.id = currentIndex;
  390. updateList.items.add(file);
  391. saveFileList.push(file);
  392. }
  393. }
  394. for (let i = 0; i < uploadFileList.length && currentIndex < 5; i++) {
  395. const file = event.target.files[i]
  396. let isDuplicate = false;
  397. // Check if the file name already exists in saveFileList
  398. for (let j = 0; j < saveFileList.length; j++) {
  399. if (saveFileList[j].name === file.name) {
  400. isDuplicate = true;
  401. break;
  402. }
  403. }
  404. if (!isDuplicate && i + currentIndex < 5) {
  405. file.id = currentIndex + i
  406. saveFileList.push(file)
  407. updateList.items.add(file);
  408. }
  409. }
  410. let updatedFileList = updateList.files;
  411. setFileListData(saveFileList)
  412. setFileList(updatedFileList);
  413. setUpdateRows(saveFileList);
  414. event.target.value = '';
  415. };
  416. useEffect(() => {
  417. props.setUpdateValid(isValid)
  418. }, [isValid])
  419. useEffect(() => {
  420. checkDataField(values)
  421. }, [
  422. selectedIdDocType,
  423. selectedAddress4, selectedAddress5,
  424. termsAndConAccept, termsAndConNotAccept, fileList])
  425. // useEffect(() => {
  426. // setDistrictErrStr("");
  427. // if (selectedAddress5?.type === "hongKong") {
  428. // if (selectedAddress4 == null || selectedAddress4 == "" || selectedAddress4 == {})
  429. // setDistrictErrStr(getRequiredErrStr("district"))
  430. // }
  431. // }, [selectedAddress4, selectedAddress5])
  432. useEffect(() => {
  433. props.step == 2 ? _onSubmit() : null;
  434. if (captchaImg == "")
  435. onCaptchaChange();
  436. checkDataField(values)
  437. }, [props.step])
  438. const { handleSubmit } = useForm({})
  439. const _onSubmit = () => {
  440. setLoding(true);
  441. values.idDocType = selectedIdDocType.type
  442. values.address4 = selectedAddress4 == null ? "" : selectedAddress4.type
  443. values.address5 = selectedAddress5.type
  444. // console.log(values)
  445. const userAddress = {
  446. "addressLine1": "",
  447. "addressLine2": "",
  448. "addressLine3": "",
  449. "district": "",
  450. "country": ""
  451. };
  452. userAddress.addressLine1 = values.address1
  453. userAddress.addressLine2 = values.address2
  454. userAddress.addressLine3 = values.address3
  455. userAddress.district = values.address4
  456. userAddress.country = values.address5
  457. const userFaxNo = {
  458. "countryCode": values.faxCountryCode,
  459. "faxNumber": values.fax,
  460. };
  461. const userMobileNumber = {
  462. "countryCode": values.phoneCountryCode,
  463. "phoneNumber": values.phone,
  464. };
  465. let tncFlag = false;
  466. if (termsAndConAccept) {
  467. tncFlag = true
  468. }
  469. if (termsAndConNotAccept) {
  470. tncFlag = false
  471. }
  472. const preferLocale = locale === 'en' ? 'en': locale === 'zh-HK' ? 'zh_HK': 'zh-CN'
  473. const user = {
  474. username: values.username,
  475. password: values.password,
  476. name: values.username,
  477. enName: values.enName,
  478. chName: values.chName,
  479. emailAddress: values.email,
  480. idDocType: values.idDocType,
  481. identification: values.idNo,
  482. checkDigit: values.checkDigit,
  483. tncFlag: tncFlag,
  484. type: "IND",
  485. captcha: base64Url,
  486. checkCode: checkCode,
  487. preferLocale: preferLocale
  488. };
  489. var formData = new FormData();
  490. for (let i = 0; i < fileListData.length; i++) {
  491. const file = fileListData[i]
  492. formData.append("multipartFileList", file);
  493. }
  494. formData.append("refType", refType);
  495. for (const [key, value] of Object.entries(user)) {
  496. formData.append(key, value);
  497. }
  498. formData.append("userFaxNo", JSON.stringify(userFaxNo));
  499. formData.append("userMobileNumber", JSON.stringify(userMobileNumber));
  500. formData.append("userAddress", JSON.stringify(userAddress));
  501. // formData.append("preferLocale", "en");
  502. // if(refCode){
  503. // formData.append("refCode", refCode);
  504. // }
  505. if (isValid) {
  506. axios.post(POST_PUBLIC_USER_REGISTER, formData, {
  507. headers: {
  508. "Content-Type": "multipart/form-data"
  509. }
  510. })
  511. .then((
  512. // response
  513. ) => {
  514. // console.log(response)
  515. setCheckUpload(true)
  516. setLoding(false);
  517. })
  518. .catch(error => {
  519. console.error(error);
  520. setLoding(false);
  521. });
  522. } else {
  523. setLoding(false);
  524. }
  525. }
  526. function handlePhone(phone) {
  527. if (phone.length < 8) {
  528. return false;
  529. } else {
  530. // console.log("Phone true")
  531. return true;
  532. }
  533. }
  534. function handleUsername(username) {
  535. var symbol = /^(?=.*\W)/;
  536. var space = /\s/;
  537. if (username.length < 6) {
  538. return false;
  539. } else if (username.match(symbol)) {
  540. return false;
  541. } else if (username.match(space)) {
  542. return false;
  543. } else {
  544. return true;
  545. }
  546. }
  547. function handleCaptcha(captchaField) {
  548. // console.log(captchaField.length)
  549. if (captchaField.length == 5 ){
  550. return true
  551. } else {
  552. return false
  553. }
  554. }
  555. function handleIdNo(idNo, selectedIdDocType, checkDigit) {
  556. // var pattern = /^[A-Z][0-9]*$/;
  557. var pattern_HKIDv1 = /^[A-Z]{1}[0-9]{6}$/;
  558. var pattern_HKIDv2 = /^[A-Z]{2}[0-9]{6}$/;
  559. var pattern_passport = /^[A-Z]{1}[0-9]{8}$/;
  560. var pattern_CHID = /^[0-9]{6}(20|19)[0-9]{2}(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])[0-9]{3}[0-9X]{1}/;
  561. var pattern_otherCert = /^[A-Z]{1}[0-9]{5,}/;
  562. // var space = /\s/;
  563. // if (!idNo.match(pattern)) {
  564. // return false;
  565. // } else if (selectedIdDocType=="HKID"&&checkDigit==""){
  566. // return false;
  567. // } else if (idNo.match(space)) {
  568. // return false;
  569. // } else if (idNo.length < 6) {
  570. // return false;
  571. // } else {
  572. // // console.log("IdNo true")
  573. // return true;
  574. // }
  575. switch (selectedIdDocType) {
  576. case "HKID":
  577. if (checkDigit === ""){
  578. setCheckHKIdDocWithCheckDigit(true)
  579. return false
  580. } else{
  581. setCheckHKIdDocWithCheckDigit(false)
  582. }
  583. if (idNo.match(pattern_HKIDv1)) {
  584. return true
  585. } else if (idNo.match(pattern_HKIDv2)) {
  586. return true
  587. } else {
  588. return false
  589. }
  590. case "passport":
  591. if (idNo.match(pattern_passport)) {
  592. return true
  593. } else {
  594. return false
  595. }
  596. case "CNID":
  597. if (idNo.match(pattern_CHID)) {
  598. const subStr_year = idNo.substring(6, 10)
  599. const subStr_month = idNo.substring(10, 12)
  600. const subStr_date = idNo.substring(12, 14)
  601. const today = new Date()
  602. const inputDate = new Date(`${subStr_year}-${subStr_month}-${subStr_date}`)
  603. if (inputDate > today || inputDate === "Invalid Date" || inputDate.getFullYear().toString() !== subStr_year || (inputDate.getMonth() + 1).toString().padStart(2, "0") !== subStr_month || inputDate.getDate().toString().padStart(2, "0") !== subStr_date) {
  604. return false
  605. } else {
  606. return true
  607. }
  608. } else {
  609. return false
  610. }
  611. case "otherCert":
  612. if (idNo.match(pattern_otherCert)) {
  613. return true
  614. } else {
  615. return false
  616. }
  617. default:
  618. break;
  619. }
  620. }
  621. function handleName(enName, chName) {
  622. if (enName == "" && chName !== ""){
  623. return true
  624. } else if (enName !== "" && chName == ""){
  625. return true
  626. } else if (enName !== "" && chName !== "") {
  627. return true
  628. } else {
  629. return false
  630. }
  631. }
  632. function handlePassword(password) {
  633. let new_pass = password;
  634. // regular expressions to validate password
  635. var lowerCase = /[a-z]/g;
  636. var upperCase = /[A-Z]/g;
  637. var numbers = /[0-9]/g;
  638. var symbol = /^(?=.*\W)/;
  639. var space = /\s/;
  640. if (!new_pass.match(lowerCase)) {
  641. return false;
  642. } else if (!new_pass.match(upperCase)) {
  643. return false;
  644. } else if (!new_pass.match(numbers)) {
  645. return false;
  646. } else if (!new_pass.match(symbol)) {
  647. return false;
  648. } else if (new_pass.length < 8) {
  649. return false;
  650. }
  651. else if (new_pass.match(space)) {
  652. return false;
  653. } else {
  654. // console.log("password true")
  655. return true;
  656. }
  657. }
  658. function handleEmail(email) {
  659. var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
  660. if (!email.match(validRegex)) {
  661. return false;
  662. } else {
  663. return true;
  664. }
  665. }
  666. function displayErrorMsg(errorMsg) {
  667. return <Typography variant="errorMessage1">{errorMsg}</Typography>
  668. }
  669. function getMaxErrStr(num, fieldname) {
  670. return displayErrorMsg(intl.formatMessage({ id: 'noMoreThenNWords' }, { num: num, fieldname: fieldname ? intl.formatMessage({ id: fieldname }) + ": " : "" }));
  671. }
  672. function getRequiredErrStr(fieldname) {
  673. return displayErrorMsg(intl.formatMessage({ id: 'require' }, { fieldname: fieldname ? intl.formatMessage({ id: fieldname }) : "" }));
  674. }
  675. const formik = useFormik({
  676. initialValues: ({
  677. username: '',
  678. enName: '',
  679. chName: '',
  680. email: '',
  681. emailConfirm: '',
  682. address1: '',
  683. address2: '',
  684. address3: '',
  685. password: '',
  686. confirmPassword: '',
  687. phone: '',
  688. phoneCountryCode: '852',
  689. idNo: '',
  690. checkDigit: '',
  691. submit: null,
  692. fax: '',
  693. faxCountryCode: '852',
  694. idDocType: '',
  695. captchaField: ''
  696. }),
  697. validationSchema: yup.object().shape({
  698. username: yup.string().min(6, displayErrorMsg(intl.formatMessage({ id: 'atLeast6CharAccount' }))).max(30, getMaxErrStr(30)).required(displayErrorMsg(intl.formatMessage({ id: 'requireUsername' })))
  699. .matches(/^[aA-zZ0-9\s]+$/, { message: displayErrorMsg(intl.formatMessage({ id: 'noSpecialCharAccount' })) })
  700. .matches(/^\S*$/, { message: displayErrorMsg(intl.formatMessage({ id: 'noSpaceAccount' })) }),
  701. password: yup.string().min(8, displayErrorMsg(intl.formatMessage({ id: 'atLeast8CharPassword' }))).max(60, getMaxErrStr(60)).required(displayErrorMsg(intl.formatMessage({ id: 'requirePassword' })))
  702. .matches(/^\S*$/, { message: displayErrorMsg(intl.formatMessage({ id: 'noSpacePassword' })) })
  703. .matches(/^(?=.*[a-z])/, { message: displayErrorMsg(intl.formatMessage({ id: 'atLeastOneSmallLetter' })) })
  704. .matches(/^(?=.*[A-Z])/, { message: displayErrorMsg(intl.formatMessage({ id: 'atLeastOneCapLetter' })) })
  705. .matches(/^(?=.*[0-9])/, { message: displayErrorMsg(intl.formatMessage({ id: 'atLeast1Number' })) })
  706. .matches(/^(?=.*\W)/, { message: displayErrorMsg(intl.formatMessage({ id: 'atLeast1SpecialChar' })) }),
  707. confirmPassword: yup.string().min(8, displayErrorMsg(intl.formatMessage({ id: 'atLeast8CharPassword' }))).required(displayErrorMsg(intl.formatMessage({ id: 'pleaseConfirmPassword' }))).oneOf([yup.ref('password'), null], displayErrorMsg(intl.formatMessage({ id: 'samePassword' }))),
  708. chName: yup.string().max(6, getMaxErrStr(6)),
  709. enName: yup.string().max(40, getMaxErrStr(40)).when('chName', {
  710. is: (chName) => chName?false:true,
  711. then: yup.string().required(displayErrorMsg(intl.formatMessage({ id: 'registerNameLabel' })))
  712. }),
  713. address1: yup.string().max(40, getMaxErrStr(40, "addressLine1")).required(displayErrorMsg(intl.formatMessage({ id: 'validateAddressLine1' }))),
  714. address2: yup.string().max(40, getMaxErrStr(40, "addressLine2")),
  715. address3: yup.string().max(40, getMaxErrStr(40, "addressLine3")),
  716. email: yup.string().email(displayErrorMsg(intl.formatMessage({ id: 'validEmailFormat' }))).max(128, getMaxErrStr(128)).required(displayErrorMsg(intl.formatMessage({ id: 'requireEmail' }))),
  717. 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' }))),
  718. idNo: yup.string().required(getRequiredErrStr('number'))
  719. .matches(/^[aA-zZ0-9\s]+$/, { message: displayErrorMsg(`${selectedIdDocInputType}${intl.formatMessage({ id: 'noSpecialCharacter' })}`) })
  720. .matches(/^\S*$/, { message: displayErrorMsg(`${selectedIdDocInputType}${intl.formatMessage({ id: 'noSpace' })}`) })
  721. .test('checkIDCardFormat', displayErrorMsg(`${intl.formatMessage({ id: 'requiredValid' })}${selectedIdDocInputType==undefined?"":selectedIdDocInputType}${intl.formatMessage({ id: 'number' })}`), function (value) {
  722. const idDocType = selectedIdDocType.type;
  723. var pattern_HKIDv1 = /^[A-Z]{1}[0-9]{6}$/;
  724. var pattern_HKIDv2 = /^[A-Z]{2}[0-9]{6}$/;
  725. var pattern_passport = /^[A-Z]{1}[0-9]{8}$/;
  726. var pattern_CHID = /^[0-9]{6}(20|19)[0-9]{2}(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])[0-9]{3}[0-9X]{1}/;
  727. var pattern_otherCert = /^[A-Z]{1}[0-9]{5,}/;
  728. if (value !== undefined) {
  729. switch (idDocType) {
  730. case "HKID":
  731. if (value.match(pattern_HKIDv1)) {
  732. return true
  733. } else if (value.match(pattern_HKIDv2)) {
  734. return true
  735. } else {
  736. return false
  737. }
  738. case "passport":
  739. if (value.match(pattern_passport)) {
  740. return true
  741. } else {
  742. return false
  743. }
  744. case "CNID":
  745. if (value.match(pattern_CHID)) {
  746. const subStr_year = value.substring(6, 10)
  747. const subStr_month = value.substring(10, 12)
  748. const subStr_date = value.substring(12, 14)
  749. const today = new Date()
  750. const inputDate = new Date(`${subStr_year}-${subStr_month}-${subStr_date}`)
  751. if (inputDate > today || inputDate === "Invalid Date" || inputDate.getFullYear().toString() !== subStr_year || (inputDate.getMonth() + 1).toString().padStart(2, "0") !== subStr_month || inputDate.getDate().toString().padStart(2, "0") !== subStr_date) {
  752. return false
  753. } else {
  754. return true
  755. }
  756. } else {
  757. return false
  758. }
  759. case "otherCert":
  760. if (value.match(pattern_otherCert)) {
  761. return true
  762. } else {
  763. return false
  764. }
  765. default:
  766. break;
  767. }
  768. }
  769. }),
  770. checkDigit: yup.string().max(1, getMaxErrStr(1)).required(displayErrorMsg(intl.formatMessage({ id: 'requiredNumberInQuote' }))),
  771. idDocType: yup.string().max(255, getMaxErrStr(255)).required(displayErrorMsg(intl.formatMessage({ id: 'requireIdDocType' }))),
  772. phoneCountryCode: yup.string().min(2, displayErrorMsg(intl.formatMessage({ id: 'requireAtLeast2Number' }))).required(displayErrorMsg(intl.formatMessage({ id: 'requireDialingCode' }))),
  773. // faxCountryCode: yup.string().min(3,'請輸入3位數字'),
  774. phone: yup.string().min(8, displayErrorMsg(intl.formatMessage({ id: 'requireAtLeast8Number' }))).required(displayErrorMsg(intl.formatMessage({ id: 'requireContactNumber' }))),
  775. // fax: yup.string().min(8,'請輸入8位數字'),
  776. captchaField: yup.string().max(5, getMaxErrStr(5)).required(displayErrorMsg(intl.formatMessage({ id: 'requireVerify' }))).min(5, displayErrorMsg(intl.formatMessage({ id: 'requireVerify' }))),//.oneOf([captcha], displayErrorMsg('請輸入有效驗證')),
  777. }),
  778. });
  779. const handleReset = (resetForm) => {
  780. resetForm();
  781. setSelectedAddress4("")
  782. setSelectedAddress5(ComboData.country[0])
  783. setCheckCountry(false)
  784. setSelectedIdDocType({})
  785. setSelectedIdDocInputType("");
  786. setFileList([])
  787. setFileListData([])
  788. onCaptchaChange()
  789. // setSelectedIdDocLabel("")
  790. };
  791. const handleCCPChange = (e) => {
  792. e.preventDefault();
  793. };
  794. const { values } = formik
  795. useEffect(() => {
  796. if (!props.step0GuardRef) return;
  797. props.step0GuardRef.current = async () => {
  798. const syncOk = computeStep0ValidSync(values);
  799. if (!syncOk) {
  800. setisValid(false);
  801. if (!isDistrictSelectionValid()) {
  802. setDistrictErrStr(getRequiredErrStr("district"));
  803. setCheckDistrict(true);
  804. notifyActionError(intl.formatMessage({ id: 'require' }, { fieldname: intl.formatMessage({ id: 'district' }) }));
  805. }
  806. return false;
  807. }
  808. try {
  809. const taken = await checkEmailRegistered(values.email);
  810. setCheckEmail(taken);
  811. if (taken) {
  812. setisValid(false);
  813. notifyActionError(intl.formatMessage({ id: 'emailUsed' }));
  814. return false;
  815. }
  816. } catch (error) {
  817. notifyActionError(intl.formatMessage({ id: 'connectionError' }));
  818. return false;
  819. }
  820. setisValid(true);
  821. return true;
  822. };
  823. }, [values, selectedAddress4, selectedAddress5, termsAndConAccept, fileList, checkUsername, checkEmail, checkIdDocNumber, selectedIdDocType]);
  824. useEffect(() => {
  825. checkDataField(values)
  826. }, [values])
  827. return (
  828. <FormikProvider value={formik}>
  829. <form onSubmit={handleSubmit(_onSubmit)}>
  830. {/* Input Form */}
  831. <FormGroup id={"inputForm"} sx={{ display: props.step === 0 ? "" : "none" }}>
  832. <Grid container spacing={3}>
  833. <Grid item xs={12} md={12}>
  834. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  835. <Button
  836. variant="outlined"
  837. type="reset"
  838. onClick={handleReset.bind(null, formik.resetForm)}
  839. sx={{
  840. height: '40px',
  841. borderColor: '#616161',
  842. color: '#424242',
  843. '&:hover': { borderColor: '#424242', backgroundColor: 'rgba(97,97,97,0.08)' },
  844. }}
  845. >
  846. <Typography variant="pnspsFormHeader">
  847. <FormattedMessage id="reset" />
  848. </Typography>
  849. </Button>
  850. <div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}>
  851. <Typography display="inline" variant="h3" sx={{ color: '#1A4399' }}>
  852. <FormattedMessage id="becomeNewPersonalUser" />
  853. </Typography>
  854. </div>
  855. <Typography mt={0.25} variant="h6" sx={{ color: '#B00020' }}>
  856. <FormattedMessage id="requireString" />
  857. </Typography>
  858. <Typography mt={0.25} variant="h4" sx={{ color: 'primary.primary' }}>
  859. <FormattedMessage id="yourLoginInformation" />
  860. </Typography>
  861. </Stack>
  862. </Grid>
  863. <Grid item xs={12} md={12}>
  864. <Grid container spacing={1}>
  865. <Grid item xs={12} md={12} >
  866. <Stack spacing={1}>
  867. <InputLabel htmlFor="username-signup">
  868. <Typography variant="pnspsFormHeader">
  869. <FormattedMessage id="userLoginName" />
  870. <span style={{ color: '#B00020' }}>*</span>
  871. {/*<Button*/}
  872. {/* variant="contained"*/}
  873. {/* onClick={handleCheckUsername}*/}
  874. {/* sx={{ ml: 2, height: "40px" }}>*/}
  875. {/* <Typography variant="h6">檢查是否重覆</Typography>*/}
  876. {/*</Button> **/}
  877. </Typography>
  878. </InputLabel>
  879. <OutlinedInput
  880. id="username-signup"
  881. type="text"
  882. value={formik.values.username.trim()}
  883. name="username"
  884. onChange={(e) => {
  885. setCheckUsername(false)
  886. props.setUsername(e.target.value)
  887. formik.handleChange(e)
  888. }}
  889. placeholder={intl.formatMessage({ id: 'userLoginName' })}
  890. fullWidth
  891. autoFocus
  892. error={Boolean((formik.touched.username && formik.errors.username) || checkUsername)}
  893. onBlur={formik.handleBlur}
  894. inputProps={{
  895. "aria-label": intl.formatMessage({ id: "userLoginName" }),
  896. "aria-describedby": 'helper-text-username-signup',
  897. onKeyDown: (e) => {
  898. if (e.key === 'Enter') {
  899. e.preventDefault();
  900. }
  901. },
  902. }}
  903. />
  904. {formik.touched.username && formik.errors.username && (
  905. <FormHelperText error id="helper-text-username-signup">
  906. {formik.errors.username}
  907. </FormHelperText>
  908. )}
  909. {checkUsername && (
  910. <FormHelperText error id="helper-text-username-signup">
  911. <FormattedMessage id="usernameTaken" />
  912. </FormHelperText>
  913. )}
  914. </Stack>
  915. </Grid>
  916. <Grid item xs={12} md={12}>
  917. <Grid container>
  918. <Grid item xs={12} md={6} >
  919. <Stack spacing={1} sx={{ mr: { md: 1 }, mb: 1 }}>
  920. <Stack direction="row" justifyContent="space-between">
  921. <InputLabel htmlFor="password-signup">
  922. <Typography variant="pnspsFormHeader">
  923. <FormattedMessage id="userPassword" />
  924. <span style={{ color: '#B00020' }}>*</span>
  925. </Typography>
  926. </InputLabel>
  927. </Stack>
  928. <OutlinedInput
  929. fullWidth
  930. error={Boolean(formik.touched.password && formik.errors.password)}
  931. id="password-signup"
  932. type={showPassword ? 'text' : 'password'}
  933. value={formik.values.password.trim()}
  934. name="password"
  935. onChange={(e) => {
  936. formik.handleChange(e);
  937. changePassword(e.target.value);
  938. }}
  939. endAdornment={
  940. <InputAdornment position="end">
  941. <IconButton
  942. role="button"
  943. aria-label={intl.formatMessage({
  944. id: showPassword ? "ariaHidePassword" : "ariaShowPassword"
  945. })}
  946. onClick={handleClickShowPassword}
  947. onMouseDown={handleMouseDownPassword}
  948. edge="end"
  949. size="large"
  950. >
  951. {showPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />}
  952. </IconButton>
  953. </InputAdornment>
  954. }
  955. placeholder={intl.formatMessage({ id: 'userPassword' })}
  956. onBlur={formik.handleBlur}
  957. inputProps={{
  958. "aria-label": intl.formatMessage({ id: "userPassword" }),
  959. "aria-describedby": 'helper-text-password-signup',
  960. onKeyDown: (e) => {
  961. if (e.key === 'Enter') {
  962. e.preventDefault();
  963. }
  964. },
  965. }}
  966. />
  967. {formik.touched.password && formik.errors.password && (
  968. <FormHelperText error id="helper-text-password-signup">
  969. {formik.errors.password}
  970. </FormHelperText>
  971. )}
  972. </Stack>
  973. <FormControl fullWidth sx={{ mt: 2 }}>
  974. <Grid container spacing={2} alignItems="center">
  975. <Grid item>
  976. <Box sx={{ bgcolor: level?.color, width: 85, height: 8, borderRadius: '7px' }} />
  977. </Grid>
  978. <Grid item>
  979. <Typography variant="subtitle1">
  980. <FormattedMessage id={level ? level?.label : "pwWeak"} />
  981. </Typography>
  982. </Grid>
  983. </Grid>
  984. </FormControl>
  985. </Grid>
  986. <Grid item xs={12} md={6} >
  987. <Stack spacing={1}>
  988. <InputLabel htmlFor="confirmPassword-signup">
  989. <Typography variant="pnspsFormHeader">
  990. <FormattedMessage id="confirmPassword" />
  991. <span style={{ color: '#B00020' }}>*</span>
  992. </Typography>
  993. </InputLabel>
  994. <OutlinedInput
  995. id="confirmPassword-signup"
  996. type={showConfirmPassword ? 'text' : 'password'}
  997. value={formik.values.confirmPassword.trim()}
  998. name="confirmPassword"
  999. onBlur={formik.handleBlur}
  1000. onCut={handleCCPChange}
  1001. onCopy={handleCCPChange}
  1002. onPaste={handleCCPChange}
  1003. onChange={(e) => {
  1004. formik.handleChange(e);
  1005. // changePassword(e.target.value);
  1006. }}
  1007. inputProps={{
  1008. "aria-label": intl.formatMessage({ id: "confirmPassword" }),
  1009. "aria-describedby": 'helper-text-confirmPassword-signup',
  1010. onKeyDown: (e) => {
  1011. if (e.key === 'Enter') {
  1012. e.preventDefault();
  1013. }
  1014. },
  1015. }}
  1016. endAdornment={
  1017. <InputAdornment position="end">
  1018. <IconButton
  1019. role="button"
  1020. aria-label={intl.formatMessage({
  1021. id: showConfirmPassword ? "ariaHidePassword" : "ariaShowPassword"
  1022. })}
  1023. onClick={handleClickShowConfirmPassword}
  1024. onMouseDown={handleMouseDownPassword}
  1025. edge="end"
  1026. size="large"
  1027. >
  1028. {showConfirmPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />}
  1029. </IconButton>
  1030. </InputAdornment>
  1031. }
  1032. placeholder={intl.formatMessage({ id: 'confirmPassword' })}
  1033. fullWidth
  1034. error={Boolean(formik.touched.confirmPassword && formik.errors.confirmPassword)}
  1035. />
  1036. {formik.touched.confirmPassword && formik.errors.confirmPassword && (
  1037. <FormHelperText error id="helper-text-confirmPassword-signup">
  1038. {formik.errors.confirmPassword}
  1039. </FormHelperText>
  1040. )}
  1041. </Stack>
  1042. <Grid container spacing={2} alignItems="center">
  1043. <Grid item sx={{ mt: 1 }}>
  1044. <Typography variant="subtitle1">
  1045. •<FormattedMessage id="pwRemark1" /><br />
  1046. •<FormattedMessage id="pwRemark2" /><br />
  1047. •<FormattedMessage id="pwRemark3" /><br />
  1048. •<FormattedMessage id="pwRemark4" /><br />
  1049. •<FormattedMessage id="pwRemark5"/><br />
  1050. </Typography>
  1051. </Grid>
  1052. </Grid>
  1053. </Grid>
  1054. </Grid>
  1055. </Grid>
  1056. <Grid item xs={12} mt={1} mb={1}>
  1057. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  1058. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  1059. <FormattedMessage id="yourPersonalInformation" />
  1060. </Typography>
  1061. {/* <Typography component={Link} to="/login" variant="body1" sx={{ textDecoration: 'none' }} color="primary">
  1062. Already have an account?
  1063. </Typography> */}
  1064. </Stack>
  1065. </Grid>
  1066. <Grid item xs={12} md={12} >
  1067. <Grid container sx={{ mb: 1 }}>
  1068. <Stack spacing={1}>
  1069. <InputLabel htmlFor="idDocType-signup">
  1070. <Typography variant="pnspsFormHeader">
  1071. <FormattedMessage id="userIdDoc" />
  1072. <span style={{ color: '#B00020' }}>*</span>
  1073. </Typography>
  1074. </InputLabel>
  1075. {/* {formik.touched.enName && formik.errors.enName && (
  1076. <FormHelperText error id="helper-text-enName-signup">
  1077. {formik.errors.enName}
  1078. </FormHelperText>
  1079. )} */}
  1080. </Stack>
  1081. </Grid>
  1082. <Grid container>
  1083. <Grid item xs={12} md={6} >
  1084. <Stack spacing={1} sx={{ mr: { md: 1 } }}>
  1085. <Autocomplete
  1086. disablePortal
  1087. id="idDocType"
  1088. label={intl.formatMessage({ id: "idDocType" })}
  1089. //value={selectedIdDocType}
  1090. size="small"
  1091. options={idDocTypeComboList}
  1092. onBlur={formik.handleBlur}
  1093. filterOptions={(options) => options}
  1094. inputValue={selectedIdDocInputType}
  1095. getOptionLabel={(option) => option.label ? intl.formatMessage({ id: option.label }) : ""}
  1096. onChange={(event, newValue) => {
  1097. if (newValue != null) {
  1098. setSelectedIdDocInputType(intl.formatMessage({ id: newValue.label }));
  1099. setSelectedIdDocType(newValue);
  1100. if (newValue.type !== "HKID") {
  1101. formik.setFieldValue("checkDigit", "")
  1102. }
  1103. } else {
  1104. setSelectedIdDocInputType("");
  1105. setSelectedIdDocType({});
  1106. }
  1107. }}
  1108. sx={{
  1109. '& .MuiInputBase-root': { alignItems: 'center' },
  1110. '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
  1111. '& .MuiOutlinedInput-root': { height: 40 }
  1112. }}
  1113. renderInput={(params) => <TextField
  1114. {...params}
  1115. error={formik.touched.idDocType && (selectedIdDocType === null || selectedIdDocType?.type == null)}
  1116. placeholder={intl.formatMessage({ id: 'idDocType' })}
  1117. />}
  1118. clearText={intl.formatMessage({ id: "muiClear" })}
  1119. closeText={intl.formatMessage({ id: "muiClose" })}
  1120. openText={intl.formatMessage({ id: "muiOpen" })}
  1121. noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
  1122. />
  1123. {formik.touched.idDocType && (
  1124. selectedIdDocType === null || selectedIdDocType?.type == null ?
  1125. <FormHelperText error id="helper-text-idDocType-signup" sx={{ fontSize: 16, fontWeight: 'bold', }}>
  1126. <FormattedMessage id="requireIdDocType" />
  1127. </FormHelperText> : ''
  1128. )}
  1129. </Stack>
  1130. </Grid>
  1131. {selectedIdDocType.type === "HKID" ?
  1132. <>
  1133. <Grid item xs={9} md={5}>
  1134. <Stack spacing={1} sx={{ mr: { md: 1 } }}>
  1135. <OutlinedInput
  1136. id="idNo-hkid-login"
  1137. type="text"
  1138. name="idNo"
  1139. value={formik.values.idNo}
  1140. onChange={async (e) => {
  1141. const ele = document.getElementById('idNo-hkid-login')
  1142. const startPos = ele.selectionStart
  1143. if (e.type === "change") {
  1144. if (!(e.target.value.match(/\s/g))) {
  1145. const newValue = await e.target.value.toUpperCase()
  1146. await formik.setFieldValue("idNo", newValue)
  1147. ele.setSelectionRange(startPos, startPos)
  1148. } else {
  1149. await formik.setFieldValue("idNo", formik.values.idNo)
  1150. ele.setSelectionRange(startPos - 1, startPos - 1)
  1151. }
  1152. }
  1153. }}
  1154. placeholder={intl.formatMessage({ id: 'idDocNumber' })}
  1155. fullWidth
  1156. sx={{ mr: 1 }}
  1157. error={Boolean(formik.touched.idNo && formik.errors.idNo || checkIdDocNumber)}
  1158. onBlur={formik.handleBlur}
  1159. inputProps={{
  1160. "aria-label": intl.formatMessage({ id: "idDocNumber" }),
  1161. "aria-describedby": 'helper-text-idNo-signup',
  1162. maxLength: selectedIdDocType.type === 'HKID' ? 8 : 18,
  1163. onKeyDown: (e) => {
  1164. // console.log(e)
  1165. if (e.key === 'Enter') {
  1166. e.preventDefault();
  1167. }
  1168. },
  1169. }}
  1170. />
  1171. {formik.touched.idNo && formik.errors.idNo && (
  1172. <FormHelperText error id="helper-text-idNo-signup">
  1173. {formik.errors.idNo}
  1174. </FormHelperText>
  1175. )}
  1176. {formik.touched.checkDigit && formik.errors.checkDigit && (
  1177. <FormHelperText error id="helper-text-checkDigit-signup">
  1178. {formik.errors.checkDigit}
  1179. </FormHelperText>
  1180. )}
  1181. {checkIdDocNumber && (
  1182. <FormHelperText error id="helper-text-idNo-signup">
  1183. <FormattedMessage id="idNoTaken" />
  1184. </FormHelperText>
  1185. )}
  1186. {checkHKIdDocWithCheckDigit && (
  1187. <FormHelperText error id="helper-text-idNo-checkdigit-signup">
  1188. <FormattedMessage id="requiredNumberInQuote" />
  1189. </FormHelperText>
  1190. )}
  1191. </Stack>
  1192. </Grid>
  1193. <Grid item xs={3} md={1}>
  1194. <Stack spacing={1}>
  1195. <OutlinedInput
  1196. id="checkDigit-login"
  1197. type="text"
  1198. value={formik.values.checkDigit.trim()}
  1199. name="checkDigit"
  1200. onChange={formik.handleChange}
  1201. //placeholder="( )"
  1202. // sx={{height:"53px"}}
  1203. startAdornment={<InputAdornment position="start">(</InputAdornment>}
  1204. endAdornment={<InputAdornment position="end">)</InputAdornment>}
  1205. sx={{
  1206. '& .MuiOutlinedInput-input': {
  1207. padding: '5px 2px 5px 2px', // Set the desired padding inline
  1208. },
  1209. }}
  1210. inputProps={{
  1211. maxLength: 1,
  1212. onKeyDown: (e) => {
  1213. if (e.key === 'Enter') {
  1214. e.preventDefault();
  1215. }
  1216. },
  1217. }}
  1218. fullWidth
  1219. error={Boolean(formik.touched.checkDigit && formik.errors.checkDigit || checkIdDocNumber)}
  1220. onBlur={formik.handleBlur}
  1221. />
  1222. </Stack>
  1223. </Grid>
  1224. </> :
  1225. <Grid item xs={12} md={6}>
  1226. <Stack spacing={1}>
  1227. <OutlinedInput
  1228. id="idNo-login"
  1229. type="text"
  1230. value={formik.values.idNo}
  1231. name="idNo"
  1232. onChange={async (e) => {
  1233. const ele = document.getElementById('idNo-login')
  1234. const startPos = ele.selectionStart
  1235. if (e.type === "change") {
  1236. if (!(e.target.value.match(/\s/g))) {
  1237. const newValue = await e.target.value.toUpperCase()
  1238. await formik.setFieldValue("idNo", newValue)
  1239. ele.setSelectionRange(startPos, startPos)
  1240. } else {
  1241. await formik.setFieldValue("idNo", formik.values.idNo)
  1242. ele.setSelectionRange(startPos - 1, startPos - 1)
  1243. }
  1244. }
  1245. }}
  1246. placeholder={intl.formatMessage({ id: 'idDocNumber' })}
  1247. fullWidth
  1248. sx={{ mr: 1 }}
  1249. error={Boolean(formik.touched.idNo && formik.errors.idNo || checkIdDocNumber)}
  1250. onBlur={formik.handleBlur}
  1251. inputProps={{
  1252. "aria-label": intl.formatMessage({ id: "idDocNumber" }),
  1253. "aria-describedby": 'helper-text-idNo-signup',
  1254. maxLength: 18,
  1255. onKeyDown: (e) => {
  1256. if (e.key === 'Enter') {
  1257. e.preventDefault();
  1258. }
  1259. },
  1260. }}
  1261. />
  1262. {formik.touched.idNo && formik.errors.idNo && (
  1263. <FormHelperText error id="helper-text-idNo-signup">
  1264. {formik.errors.idNo}
  1265. </FormHelperText>
  1266. )}
  1267. {checkIdDocNumber && (
  1268. <FormHelperText error id="helper-text-idNo-signup">
  1269. <FormattedMessage id="idNoTaken" />
  1270. </FormHelperText>
  1271. )}
  1272. </Stack>
  1273. </Grid>
  1274. }
  1275. </Grid>
  1276. </Grid>
  1277. <Grid item xs={12} md={12}>
  1278. <Typography variant="subtitle1">
  1279. <FormattedMessage id="registerNameLabel" />
  1280. </Typography>
  1281. </Grid>
  1282. <Grid item xs={12} md={6}>
  1283. <Stack spacing={1}>
  1284. <InputLabel htmlFor="enName-signup">
  1285. <Typography variant="pnspsFormHeader">
  1286. <FormattedMessage id="userEnglishName" />
  1287. {selectedIdDocType.type === "CNID" ? "" : <span style={{ color: '#B00020' }}></span>}
  1288. </Typography>
  1289. </InputLabel>
  1290. <OutlinedInput
  1291. id="enName-signup"
  1292. type="enName"
  1293. value={formik.values.enName}
  1294. name="enName"
  1295. onChange={formik.handleChange}
  1296. placeholder={intl.formatMessage({ id: 'sameAsYourIdDoc' })}
  1297. fullWidth
  1298. error={Boolean(formik.touched.enName && formik.errors.enName && selectedIdDocType.type !== "CNID")}
  1299. onBlur={formik.handleBlur}
  1300. inputProps={{
  1301. "aria-label": intl.formatMessage({ id: "userEnglishName" }),
  1302. "aria-describedby": 'helper-text-enName-signup',
  1303. onKeyDown: (e) => {
  1304. if (e.key === 'Enter') {
  1305. e.preventDefault();
  1306. }
  1307. },
  1308. }}
  1309. />
  1310. {formik.touched.enName && formik.errors.enName && selectedIdDocType.type !== "CNID" && (
  1311. <FormHelperText error id="helper-text-enName-signup">
  1312. {formik.errors.enName}
  1313. </FormHelperText>
  1314. )}
  1315. </Stack>
  1316. </Grid>
  1317. <Grid item xs={12} md={6}>
  1318. <Stack spacing={1}>
  1319. <InputLabel htmlFor="chName-signup">
  1320. <Typography variant="pnspsFormHeader">
  1321. <FormattedMessage id="userChineseName" />
  1322. <span style={{ color: '#B00020' }}></span>
  1323. </Typography>
  1324. </InputLabel>
  1325. <OutlinedInput
  1326. fullWidth
  1327. error={Boolean(formik.touched.chName && formik.errors.chName)}
  1328. id="chName-signup"
  1329. type="text"
  1330. value={formik.values.chName.trim()}
  1331. name="chName"
  1332. onChange={formik.handleChange}
  1333. placeholder={intl.formatMessage({ id: 'sameAsYourIdDoc' })}
  1334. onBlur={formik.handleBlur}
  1335. inputProps={{
  1336. "aria-label": intl.formatMessage({ id: "userChineseName" }),
  1337. "aria-describedby": 'helper-text-chName-signup',
  1338. maxLength: 6,
  1339. onKeyDown: (e) => {
  1340. if (e.key === 'Enter') {
  1341. e.preventDefault();
  1342. }
  1343. },
  1344. }}
  1345. />
  1346. {formik.touched.chName && formik.errors.chName && (
  1347. <FormHelperText error id="helper-text-chName-signup">
  1348. {formik.errors.chName}
  1349. </FormHelperText>
  1350. )}
  1351. </Stack>
  1352. </Grid>
  1353. <Grid item xs={12}>
  1354. <Stack spacing={1}>
  1355. <InputLabel htmlFor="address1-signup">
  1356. <Typography variant="pnspsFormHeader">
  1357. <FormattedMessage id="formAddress" />
  1358. <span style={{ color: '#B00020' }}>*</span>
  1359. </Typography>
  1360. </InputLabel>
  1361. <OutlinedInput
  1362. fullWidth
  1363. error={Boolean(formik.touched.address1 && formik.errors.address1)}
  1364. id="address1-signup"
  1365. value={formik.values.address1}
  1366. name="address1"
  1367. onChange={formik.handleChange}
  1368. placeholder={intl.formatMessage({ id: 'addressLine1' })}
  1369. onBlur={formik.handleBlur}
  1370. inputProps={{
  1371. "aria-label": intl.formatMessage({ id: "addressLine1" }),
  1372. onKeyDown: (e) => {
  1373. if (e.key === 'Enter') {
  1374. e.preventDefault();
  1375. }
  1376. },
  1377. }}
  1378. />
  1379. <OutlinedInput
  1380. fullWidth
  1381. error={Boolean(formik.touched.address2 && formik.errors.address2)}
  1382. id="address2-signup"
  1383. value={formik.values.address2}
  1384. name="address2"
  1385. onChange={formik.handleChange}
  1386. onBlur={formik.handleBlur}
  1387. placeholder={intl.formatMessage({ id: 'addressLine2' })}
  1388. inputProps={{
  1389. "aria-label": intl.formatMessage({ id: "addressLine2" }),
  1390. onKeyDown: (e) => {
  1391. if (e.key === 'Enter') {
  1392. e.preventDefault();
  1393. }
  1394. },
  1395. }}
  1396. />
  1397. <OutlinedInput
  1398. fullWidth
  1399. error={Boolean(formik.touched.address3 && formik.errors.address3)}
  1400. id="address3-signup"
  1401. value={formik.values.address3}
  1402. name="address3"
  1403. onChange={formik.handleChange}
  1404. onBlur={formik.handleBlur}
  1405. placeholder={intl.formatMessage({ id: 'addressLine3' })}
  1406. inputProps={{
  1407. "aria-label": intl.formatMessage({ id: "addressLine3" }),
  1408. onKeyDown: (e) => {
  1409. if (e.key === 'Enter') {
  1410. e.preventDefault();
  1411. }
  1412. },
  1413. }}
  1414. />
  1415. <Autocomplete
  1416. disablePortal
  1417. id="address4-combo"
  1418. value={selectedAddress4}
  1419. options={ComboData.district}
  1420. disabled={checkCountry}
  1421. // error={Boolean(districtErrStr != "")}
  1422. // onBlur={formik.handleBlur}
  1423. getOptionLabel={(option) => option.type ? intl.formatMessage({ id: option.type }) : ""}
  1424. onChange={(event, newValue) => {
  1425. setSelectedAddress4(newValue);
  1426. }}
  1427. sx={{
  1428. '& .MuiInputBase-root': { alignItems: 'center' },
  1429. '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
  1430. '& .MuiOutlinedInput-root': { height: 40 }
  1431. }}
  1432. renderInput={(params) => <TextField error={checkDistrict} {...params} placeholder={intl.formatMessage({ id: 'region' })}
  1433. />}
  1434. clearText={intl.formatMessage({ id: "muiClear" })}
  1435. closeText={intl.formatMessage({ id: "muiClose" })}
  1436. openText={intl.formatMessage({ id: "muiOpen" })}
  1437. noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
  1438. />
  1439. <Autocomplete
  1440. disablePortal
  1441. id="address5-combo"
  1442. value={selectedAddress5}
  1443. options={ComboData.country}
  1444. disabled= {true}
  1445. getOptionLabel={(option) => option.type ? intl.formatMessage({ id: option.type }) : ""}
  1446. onChange={(event, newValue) => {
  1447. if (newValue !== null) {
  1448. setSelectedAddress5(newValue);
  1449. if (newValue.type === 'hongKong') {
  1450. setCheckCountry(false)
  1451. if(formik.values.phone==""){
  1452. formik.values.phoneCountryCode = "852";
  1453. }
  1454. if(formik.values.fax==""){
  1455. formik.values.faxCountryCode = "852";
  1456. }
  1457. } else {
  1458. if (newValue.type === 'mainland'){
  1459. if(formik.values.phone==""){
  1460. formik.values.phoneCountryCode = "86";
  1461. }
  1462. if(formik.values.fax==""){
  1463. formik.values.faxCountryCode = "86";
  1464. }
  1465. }else if(newValue.type === 'macau'){
  1466. if(formik.values.phone==""){
  1467. formik.values.phoneCountryCode = "853";
  1468. }
  1469. if(formik.values.fax==""){
  1470. formik.values.faxCountryCode = "853";
  1471. }
  1472. }
  1473. setSelectedAddress4("");
  1474. setCheckCountry(true)
  1475. }
  1476. } else {
  1477. setSelectedAddress4("");
  1478. setCheckCountry(true)
  1479. }
  1480. }}
  1481. sx={{
  1482. '& .MuiInputBase-root': { alignItems: 'center' },
  1483. '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
  1484. '& .MuiOutlinedInput-root': { height: 40 }
  1485. }}
  1486. renderInput={(params) => <TextField {...params} placeholder={intl.formatMessage({ id: 'regionOrCountry' })} />}
  1487. clearText={intl.formatMessage({ id: "muiClear" })}
  1488. closeText={intl.formatMessage({ id: "muiClose" })}
  1489. openText={intl.formatMessage({ id: "muiOpen" })}
  1490. noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
  1491. />
  1492. {formik.touched.address1 && formik.errors.address1 && (
  1493. <FormHelperText error id="helper-text-address1-signup">
  1494. {formik.errors.address1}
  1495. </FormHelperText>
  1496. )}
  1497. {formik.touched.address2 && formik.errors.address2 && (
  1498. <FormHelperText error id="helper-text-address2-signup">
  1499. {formik.errors.address2}
  1500. </FormHelperText>
  1501. )}
  1502. {formik.touched.address3 && formik.errors.address3 && (
  1503. <FormHelperText error id="helper-text-address3-signup">
  1504. {formik.errors.address3}
  1505. </FormHelperText>
  1506. )}
  1507. {checkDistrict && (
  1508. <FormHelperText error >
  1509. {districtErrStr}
  1510. </FormHelperText>
  1511. )}
  1512. </Stack>
  1513. </Grid>
  1514. <Grid item xs={12} mt={1} mb={1}>
  1515. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  1516. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  1517. <FormattedMessage id="yourContact" />
  1518. </Typography>
  1519. </Stack>
  1520. </Grid>
  1521. <Grid item xs={12} md={12}>
  1522. <Grid container>
  1523. <Grid item xs={12} md={6}>
  1524. <Stack spacing={1} sx={{ mr: { md: 1 }, mb: 1 }}>
  1525. <InputLabel htmlFor="email-login">
  1526. <Typography variant="pnspsFormHeader">
  1527. <FormattedMessage id="userContactEmail" />
  1528. <span style={{ color: '#B00020' }}>*</span>
  1529. </Typography>
  1530. </InputLabel>
  1531. <OutlinedInput
  1532. fullWidth
  1533. error={Boolean((formik.touched.email && formik.errors.email) || checkEmail)}
  1534. id="email-login"
  1535. type="email"
  1536. value={formik.values.email.trim()}
  1537. name="email"
  1538. onChange={formik.handleChange}
  1539. placeholder={intl.formatMessage({ id: 'userContactEmail' })}
  1540. onBlur={formik.handleBlur}
  1541. inputProps={{
  1542. "aria-label": intl.formatMessage({ id: "userContactEmail" }),
  1543. "aria-describedby": "helper-text-email-signup",
  1544. onKeyDown: (e) => {
  1545. if (e.key === 'Enter') {
  1546. e.preventDefault();
  1547. }
  1548. },
  1549. }}
  1550. />
  1551. {formik.touched.email && formik.errors.email && (
  1552. <FormHelperText error id="helper-text-email-signup">
  1553. {formik.errors.email}
  1554. </FormHelperText>
  1555. )}
  1556. {checkEmail && (
  1557. <FormHelperText error id="helper-text-email-signup">
  1558. <FormattedMessage id="emailUsed" />
  1559. </FormHelperText>
  1560. )}
  1561. </Stack>
  1562. </Grid>
  1563. <Grid item xs={12} md={6}>
  1564. <Stack spacing={1} >
  1565. <InputLabel htmlFor="emailConfirm-login">
  1566. <Typography variant="pnspsFormHeader">
  1567. <FormattedMessage id="confirmEmail" />
  1568. <span style={{ color: '#B00020' }}>*</span>
  1569. </Typography>
  1570. </InputLabel>
  1571. <OutlinedInput
  1572. fullWidth
  1573. error={Boolean(formik.touched.emailConfirm && formik.errors.emailConfirm)}
  1574. id="emailConfirm-login"
  1575. type="email"
  1576. value={formik.values.emailConfirm.trim()}
  1577. name="emailConfirm"
  1578. // onBlur={formik.handleBlur}
  1579. onChange={formik.handleChange}
  1580. placeholder={intl.formatMessage({ id: 'confirmEmail' })}
  1581. onBlur={formik.handleBlur}
  1582. onCut={handleCCPChange}
  1583. onCopy={handleCCPChange}
  1584. onPaste={handleCCPChange}
  1585. inputProps={{
  1586. "aria-label": intl.formatMessage({ id: "confirmEmail" }),
  1587. "aria-describedby": "helper-text-emailConfirm-signup",
  1588. onKeyDown: (e) => {
  1589. if (e.key === 'Enter') {
  1590. e.preventDefault();
  1591. }
  1592. },
  1593. }}
  1594. />
  1595. {formik.touched.emailConfirm && formik.errors.emailConfirm && (
  1596. <FormHelperText error id="helper-text-emailConfirm-signup">
  1597. {formik.errors.emailConfirm}
  1598. </FormHelperText>
  1599. )}
  1600. </Stack>
  1601. </Grid>
  1602. </Grid>
  1603. </Grid>
  1604. <Grid item xs={12} md={12}>
  1605. <Grid container>
  1606. <Grid item xs={12} md={6}>
  1607. <Grid container>
  1608. <Grid item xs={12} md={12}>
  1609. <Stack direction="column" spacing={1} sx={{ mr: { md: 1 }, mb: 1 }}>
  1610. <InputLabel htmlFor="phone-signup">
  1611. <Typography variant="pnspsFormHeader">
  1612. <FormattedMessage id="userContactNumber" />
  1613. <span style={{ color: '#B00020' }}>*</span>
  1614. </Typography>
  1615. </InputLabel>
  1616. <Stack direction="row">
  1617. <OutlinedInput
  1618. id="phoneCountryCode-signup"
  1619. type="phoneCountryCode"
  1620. value={formik.values.phoneCountryCode.trim()}
  1621. name="phoneCountryCode"
  1622. // onBlur={formik.handleBlur}
  1623. // onChange={formik.handleChange}
  1624. onChange={(event) => {
  1625. const value = event.target.value;
  1626. if (value.match(/[^0-9]/)) {
  1627. return event.preventDefault();
  1628. }
  1629. formik.setFieldValue("phoneCountryCode", value);
  1630. }}
  1631. placeholder={intl.formatMessage({ id: 'dialingCode' })}
  1632. error={Boolean(formik.touched.phone && formik.errors.phone)}
  1633. onBlur={formik.handleBlur}
  1634. endAdornment={<InputAdornment position="end">-</InputAdornment>}
  1635. inputProps={{
  1636. "aria-label": intl.formatMessage({ id: "dialingCode" }),
  1637. maxLength: 3,
  1638. onKeyDown: (e) => {
  1639. if (e.key === 'Enter') {
  1640. e.preventDefault();
  1641. }
  1642. },
  1643. }}
  1644. sx={{ width: '33%', mr: 1 }}
  1645. />
  1646. <OutlinedInput
  1647. id="phone-signup"
  1648. type="phone"
  1649. value={formik.values.phone.trim()}
  1650. name="phone"
  1651. // onBlur={formik.handleBlur}
  1652. // onChange={formik.handleChange}
  1653. onChange={(event) => {
  1654. const value = event.target.value;
  1655. if (value.match(/[^0-9]/)) {
  1656. return event.preventDefault();
  1657. }
  1658. formik.setFieldValue("phone", value);
  1659. }}
  1660. placeholder={intl.formatMessage({ id: 'userContactNumber' })}
  1661. error={Boolean(formik.touched.phone && formik.errors.phone)}
  1662. onBlur={formik.handleBlur}
  1663. inputProps={{
  1664. "aria-label": intl.formatMessage({ id: "userContactNumber" }),
  1665. "aria-describedby": 'helper-text-phone-signup',
  1666. maxLength: 11,
  1667. onKeyDown: (e) => {
  1668. if (e.key === 'Enter') {
  1669. e.preventDefault();
  1670. }
  1671. },
  1672. }}
  1673. sx={{ width: '66%' }}
  1674. />
  1675. </Stack>
  1676. {formik.touched.phone && formik.errors.phone && (
  1677. <FormHelperText error id="helper-text-phone-signup">
  1678. {formik.errors.phone}
  1679. </FormHelperText>
  1680. )}
  1681. </Stack>
  1682. </Grid>
  1683. </Grid>
  1684. </Grid>
  1685. <Grid item xs={12} md={6}>
  1686. <Grid container>
  1687. <Grid item xs={12} md={12}>
  1688. <Stack spacing={1} direction="column">
  1689. <InputLabel htmlFor="fax-signup">
  1690. <Typography variant="pnspsFormHeader">
  1691. <FormattedMessage id="userFaxNumber" />
  1692. </Typography>
  1693. </InputLabel>
  1694. <Stack direction="row">
  1695. <OutlinedInput
  1696. error={Boolean(formik.touched.fax && formik.errors.fax)}
  1697. id="faxCountryCode-signup"
  1698. type="faxCountryCode"
  1699. value={formik.values.faxCountryCode.trim()}
  1700. name="faxCountryCode"
  1701. // onChange={formik.handleChange}
  1702. onChange={(event) => {
  1703. const value = event.target.value;
  1704. if (value.match(/[^0-9]/)) {
  1705. return event.preventDefault();
  1706. }
  1707. formik.setFieldValue("faxCountryCode", value);
  1708. }}
  1709. placeholder={intl.formatMessage({ id: 'dialingCode' })}
  1710. onBlur={formik.handleBlur}
  1711. endAdornment={<InputAdornment position="end">-</InputAdornment>}
  1712. inputProps={{
  1713. "aria-label": intl.formatMessage({ id: "faxCountryCode" }),
  1714. maxLength: 3,
  1715. onKeyDown: (e) => {
  1716. if (e.key === 'Enter') {
  1717. e.preventDefault();
  1718. }
  1719. },
  1720. }}
  1721. sx={{ width: '33%', mr: 1 }}
  1722. />
  1723. <OutlinedInput
  1724. id="fax-signup"
  1725. type="fax"
  1726. value={formik.values.fax.trim()}
  1727. name="fax"
  1728. onBlur={formik.handleBlur}
  1729. // onChange={formik.handleChange}
  1730. onChange={(event) => {
  1731. const value = event.target.value;
  1732. if (value.match(/[^0-9]/)) {
  1733. return event.preventDefault();
  1734. }
  1735. formik.setFieldValue("fax", value);
  1736. }}
  1737. placeholder={intl.formatMessage({ id: 'userFaxNumber' })}
  1738. inputProps={{
  1739. "aria-label": intl.formatMessage({ id: "userFaxNumber" }),
  1740. maxLength: 8,
  1741. onKeyDown: (e) => {
  1742. if (e.key === 'Enter') {
  1743. e.preventDefault();
  1744. }
  1745. },
  1746. }}
  1747. sx={{ width: '66%' }}
  1748. />
  1749. </Stack>
  1750. </Stack>
  1751. </Grid>
  1752. </Grid>
  1753. </Grid>
  1754. </Grid>
  1755. </Grid>
  1756. <Grid item xs={12} md={12} mt={1} mb={1}>
  1757. <Grid container>
  1758. <Grid item xs={12} md={12}>
  1759. <Stack spacing={1} direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  1760. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  1761. <FormattedMessage id="userIdDoc" />
  1762. <span style={{ color: '#B00020' }}>*</span></Typography>
  1763. <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}>
  1764. <FormattedMessage id="pleaseUploadIdDoc" />
  1765. </Typography>
  1766. <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}>
  1767. <FormattedMessage id="pleaseUploadIdDocSubTitle" />
  1768. </Typography>
  1769. <Stack mt={1} direction="row" justifyContent="flex-start" alignItems="center" spacing={2}>
  1770. <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}>
  1771. <Button
  1772. variant="contained"
  1773. sx={{ height: '40px' }}
  1774. type="button"
  1775. onClick={() => {
  1776. if (fileInputRef.current) {
  1777. fileInputRef.current.click();
  1778. }
  1779. }}
  1780. onKeyDown={(event) => {
  1781. if (event.key === 'Enter' || event.key === ' ') {
  1782. event.preventDefault();
  1783. if (fileInputRef.current) {
  1784. fileInputRef.current.click();
  1785. }
  1786. }
  1787. }}
  1788. >
  1789. <FormattedMessage id="uploadIdDoc" />
  1790. </Button>
  1791. <input
  1792. accept="image/png, .jpg, .bmp, .pdf"
  1793. //className={classes.input}
  1794. id="contained-button-file"
  1795. multiple
  1796. type="file"
  1797. onChange={handleFileUpload}
  1798. ref={fileInputRef}
  1799. style={{ display: 'none' }}
  1800. />
  1801. </ThemeProvider>
  1802. {/*<Typography xs={12} sm={9} md={3} display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}>如: 香港身份證; 護照; 中國內地身份證等</Typography>*/}
  1803. </Stack>
  1804. {fileList != null && props.step === 0 ?
  1805. <UploadFileTable key="uploadTable" recordList={fileListData} setUpdateRows={setUpdateRows} /> : null}
  1806. {/* <Stack mt={1} direction="row" justifyContent="flex-start" alignItems="center" spacing={2}>
  1807. <Button variant="contained" type="submit" sx={{ fontSize: 12,height:'25px'}}>Submit</Button>
  1808. <Button disabled={!formik.isValid} variant="contained" type="submit" sx={{ fontSize: 12,height:'25px'}}>Submit</Button>
  1809. </Stack> */}
  1810. </Stack>
  1811. </Grid>
  1812. </Grid>
  1813. </Grid>
  1814. </Grid>
  1815. <Grid item xs={12} md={12}>
  1816. <Grid container>
  1817. <Grid item xs={12} md={12}>
  1818. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  1819. <FormattedMessage id="termsAndCondition" />
  1820. <span style={{ color: '#B00020' }}>*</span>
  1821. </Typography>
  1822. </Grid>
  1823. <Grid item xs={12} md={12}>
  1824. <Grid container>
  1825. <Grid item xs={12} md={12}>
  1826. <Typography variant="h6" height="100%" sx={{ textAlign: "left", /*overflow: "scroll",*/ borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0C489E" }}>
  1827. <div style={{padding: 12}} dangerouslySetInnerHTML={{__html: intl.formatMessage({id: "termsAndCon"})}} />
  1828. </Typography>
  1829. </Grid>
  1830. </Grid>
  1831. <Grid item xs={12} s={12} md={12} lg={12}>
  1832. <Grid container>
  1833. <Grid item xs={6} sm={6} md={11} lg={11}>
  1834. <Grid container>
  1835. <Grid item sx={{ display: 'flex', alignItems: 'center' }}>
  1836. <Checkbox
  1837. checked={termsAndConAccept}
  1838. onChange={handleCheckBoxChange}
  1839. name="termsAndConAccept"
  1840. color="primary"
  1841. size="small"
  1842. inputProps={{
  1843. "aria-label": intl.formatMessage({ id: "iConfirm" })
  1844. }}
  1845. />
  1846. <Typography variant="pnspsFormHeader">
  1847. <FormattedMessage id="iConfirm" />
  1848. </Typography>
  1849. </Grid>
  1850. </Grid>
  1851. </Grid>
  1852. <Grid item xs={6} s={6} md={3} lg={3}>
  1853. <Grid container style={{ display: "none" }}>
  1854. <Grid item sx={{ display: 'flex', alignItems: 'center' }}>
  1855. <Checkbox
  1856. checked={termsAndConNotAccept}
  1857. onChange={handleCheckBoxChange}
  1858. name="termsAndConNotAccept"
  1859. color="primary"
  1860. size="small"
  1861. inputProps={{
  1862. "aria-label": intl.formatMessage({ id: "rejectTerms" })
  1863. }}
  1864. />
  1865. <Typography variant="pnspsFormHeader">
  1866. <FormattedMessage id="rejectTerms" />
  1867. </Typography>
  1868. </Grid>
  1869. </Grid>
  1870. </Grid>
  1871. </Grid>
  1872. </Grid>
  1873. </Grid>
  1874. </Grid>
  1875. </Grid>
  1876. <Grid item xs={12} lg={12}>
  1877. <Grid container>
  1878. <Stack direction="column">
  1879. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  1880. <FormattedMessage id="verify" />
  1881. <span style={{ color: '#B00020' }}>*</span>
  1882. </Typography>
  1883. <Stack spacing={1} direction="row">
  1884. <Grid item xs={5} lg={5} style={{ "border": "1px solid black" }}>
  1885. <img src={captchaImg} alt="" />
  1886. </Grid>
  1887. <Grid item xs={1} lg={1} style={{ "border": "0px solid black" }}>
  1888. <IconButton role="button" aria-label={intl.formatMessage({ id: 'ariaRefreshCaptcha' })} size="large" onClick={() => { onCaptchaChange() }}>
  1889. <LoopIcon fontSize="inherit" />
  1890. </IconButton>
  1891. </Grid>
  1892. <Grid item xs={6} lg={6}>
  1893. <OutlinedInput
  1894. fullWidth
  1895. id="captchaField"
  1896. type="text"
  1897. value={formik.values.captchaField.trim()}
  1898. onBlur={formik.handleBlur}
  1899. error={Boolean(formik.touched.captchaField && formik.errors.captchaField)}
  1900. name="captchaField"
  1901. onChange={(event) => {
  1902. const value = event.target.value;
  1903. props.setCheckCode(event.target.value);
  1904. setCheckCode(event.target.value);
  1905. formik.setFieldValue("captchaField", value);
  1906. }}
  1907. sx={{ width: '75%' }}
  1908. inputProps={{
  1909. "aria-label": intl.formatMessage({ id: "verify" }),
  1910. "aria-describedby": 'helper-text-captcha-signup'
  1911. }}
  1912. />
  1913. </Grid>
  1914. </Stack>
  1915. {formik.touched.captchaField && formik.errors.captchaField && (
  1916. <FormHelperText error id="helper-text-captcha-signup">
  1917. {formik.errors.captchaField}
  1918. </FormHelperText>
  1919. )}
  1920. <Stack spacing={1} direction="row" sx={{ mt: 0.25 }}>
  1921. <Button
  1922. variant="contained"
  1923. onClick={playCaptchaAudio}
  1924. aria-label={intl.formatMessage({ id: "captchaPlayAudio" })}
  1925. sx={{
  1926. backgroundColor: '#0C489E',
  1927. color: '#FFFFFF',
  1928. '&:hover': { backgroundColor: '#093A7A' },
  1929. }}
  1930. >
  1931. <FormattedMessage id="captchaPlayAudio" />
  1932. </Button>
  1933. </Stack>
  1934. </Stack>
  1935. </Grid>
  1936. </Grid>
  1937. </Grid>
  1938. </Grid>
  1939. </FormGroup>
  1940. {/* Preview Form */}
  1941. <FormGroup id={"previewForm"} sx={{ display: props.step === 1 ? "" : "none" }}>
  1942. <Grid container spacing={3}>
  1943. <Grid item xs={12} md={12}>
  1944. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  1945. <div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}>
  1946. <Typography display="inline" variant="h3" sx={{ color: '#1A4399' }}>
  1947. <FormattedMessage id="becomeNewPersonalUser" />
  1948. </Typography>
  1949. </div>
  1950. {/* <Typography mt={0.25} variant="h6" sx={{ fontSize: 12,color: '#B00020'}}>註有*的項目必須輸入資料</Typography> */}
  1951. <Typography mt={0.25} variant="h4" sx={{ color: 'primary.primary' }}>
  1952. <FormattedMessage id="yourLoginInformation" />
  1953. </Typography>
  1954. {/* <Typography component={Link} to="/login" variant="body1" sx={{ textDecoration: 'none' }} color="primary">
  1955. Already have an account?
  1956. </Typography> */}
  1957. </Stack>
  1958. </Grid>
  1959. <Grid item xs={12} md={12}>
  1960. <Grid container spacing={2}>
  1961. <Grid item xs={12} >
  1962. <Stack spacing={2} direction="row">
  1963. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  1964. <FormattedMessage id="userLoginName" />:
  1965. </Typography>
  1966. <Typography variant="pnspsFormHeader" id="preview-username-login">
  1967. {formik.values.username}
  1968. </Typography>
  1969. </Stack>
  1970. </Grid>
  1971. <Grid item xs={12} mt={1} mb={1}>
  1972. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  1973. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  1974. <FormattedMessage id="yourPersonalInformation" />
  1975. </Typography>
  1976. {/* <Typography component={Link} to="/login" variant="body1" sx={{ textDecoration: 'none' }} color="primary">
  1977. Already have an account?
  1978. </Typography> */}
  1979. </Stack>
  1980. </Grid>
  1981. {/* <Grid item xs={12} md={12} >
  1982. <Stack spacing={1}>
  1983. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  1984. <FormattedMessage id="userIdDoc" />
  1985. </Typography>
  1986. </Stack>
  1987. </Grid> */}
  1988. <Grid item xs={12} md={6} >
  1989. <Stack spacing={1} direction="row" >
  1990. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  1991. <FormattedMessage id="idDocType" />:
  1992. </Typography>
  1993. <Typography variant="pnspsFormHeader" name="preview-idDocType">
  1994. {selectedIdDocType?.label? intl.formatMessage({ id: selectedIdDocType.label}): " "}
  1995. </Typography>
  1996. </Stack>
  1997. </Grid>
  1998. <Grid item xs={12} md={6}>
  1999. <Stack direction="row" >
  2000. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]} sx={{mr:1}}>
  2001. <FormattedMessage id="idDocNumber" />:
  2002. </Typography>
  2003. <Typography variant="pnspsFormHeader" id="idNo-f4-login">
  2004. {formik.values.idNo.slice(0, 4)}
  2005. </Typography>
  2006. <Typography variant="pnspsFormHeader" id="idNo-exf4-login"
  2007. type={showId ? "text" : "password"}
  2008. >
  2009. {showId ?formik.values.idNo.slice(4):"****"}{showId ?selectedIdDocType.type == "HKID" ? '(' + formik.values.checkDigit + ')' : null:null}
  2010. </Typography>
  2011. <IconButton
  2012. role="button"
  2013. aria-label={intl.formatMessage({ id: 'ariaToggleIdVisibility' })}
  2014. onClick={handleClickShowId}
  2015. onMouseDown={handleMouseDownId}
  2016. edge="end"
  2017. size="medium"
  2018. >
  2019. {showId ? <EyeOutlined /> : <EyeInvisibleOutlined />}
  2020. </IconButton>
  2021. </Stack>
  2022. </Grid>
  2023. <Grid item xs={12} md={6}>
  2024. <Stack spacing={1} direction="row">
  2025. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  2026. <FormattedMessage id="userEnglishName" />:
  2027. </Typography>
  2028. <Typography variant="pnspsFormHeader" id="preview-enName-signup">
  2029. {formik.values.enName}
  2030. </Typography>
  2031. </Stack>
  2032. </Grid>
  2033. <Grid item xs={12} md={6}>
  2034. <Stack spacing={1} direction="row">
  2035. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  2036. <FormattedMessage id="userChineseName" />:
  2037. </Typography>
  2038. <Typography variant="pnspsFormHeader" id="preview-chName-signup">
  2039. {formik.values.chName}
  2040. </Typography>
  2041. </Stack>
  2042. </Grid>
  2043. <Grid item xs={12}>
  2044. <Stack spacing={1} direction="column">
  2045. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  2046. <FormattedMessage id="formAddress" />:
  2047. </Typography>
  2048. <Stack spacing={1} direction="column">
  2049. <Typography variant="pnspsFormHeader" id="preview-address1-signup">
  2050. {formik.values.address1}
  2051. </Typography>
  2052. {formik.values.address2 != null ?
  2053. <Typography variant="pnspsFormHeader" id="preview-address2-signup">
  2054. {formik.values.address2}
  2055. </Typography>
  2056. : null}
  2057. {formik.values.address3 != null ?
  2058. <Typography variant="pnspsFormHeader" id="preview-address3-signup">
  2059. {formik.values.address3}
  2060. </Typography>
  2061. : null}
  2062. {selectedAddress5.type === "hongKong" ?
  2063. <Stack direction="column">
  2064. {/* <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]} id="preview-address4-signup">
  2065. <FormattedMessage id="region" />:
  2066. </Typography> */}
  2067. <Typography variant="pnspsFormHeader">
  2068. {!selectedAddress4 ? "" : intl.formatMessage({ id: selectedAddress4.type })}
  2069. </Typography>
  2070. </Stack>
  2071. : null}
  2072. <Stack direction="column">
  2073. {/* <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]} id="preview-address5-signup">
  2074. <FormattedMessage id="regionOrCountry" />:
  2075. </Typography> */}
  2076. <Typography variant="pnspsFormHeader">
  2077. {intl.formatMessage({ id: selectedAddress5.type })}
  2078. </Typography>
  2079. </Stack>
  2080. </Stack>
  2081. </Stack>
  2082. </Grid>
  2083. <Grid item xs={12} mt={1} mb={1}>
  2084. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  2085. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  2086. <FormattedMessage id="yourContact" />
  2087. </Typography>
  2088. </Stack>
  2089. </Grid>
  2090. <Grid item xs={12} md={12}>
  2091. <Stack spacing={1} direction="row">
  2092. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  2093. <FormattedMessage id="userContactEmail" />:
  2094. </Typography>
  2095. <Typography variant="pnspsFormHeader" id="preview-email-signup">
  2096. {formik.values.email}
  2097. </Typography>
  2098. </Stack>
  2099. </Grid>
  2100. <Grid item xs={12} md={6}>
  2101. <Stack spacing={1} direction="row">
  2102. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  2103. <FormattedMessage id="userContactNumber" />:
  2104. </Typography>
  2105. <Typography variant="pnspsFormHeader" id="preview-phone-signup">
  2106. +{formik.values.phoneCountryCode} {formik.values.phone}
  2107. </Typography>
  2108. </Stack>
  2109. </Grid>
  2110. {formik.values.faxCountryCode != "" && formik.values.fax != "" ?
  2111. <Grid item xs={12} md={6}>
  2112. <Stack spacing={1} direction="row">
  2113. <Typography variant="pnspsFormHeader" color={theme.palette.grey[600]}>
  2114. <FormattedMessage id="userFaxNumber" />:
  2115. </Typography>
  2116. <Typography variant="pnspsFormHeader" id="preview-fax-signup">
  2117. +{formik.values.faxCountryCode} {formik.values.fax}
  2118. </Typography>
  2119. </Stack>
  2120. </Grid>
  2121. : null}
  2122. <Grid item xs={12} md={12} mt={1} mb={1}>
  2123. <Grid container>
  2124. <Grid item xs={12} md={12}>
  2125. <Stack spacing={1} direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  2126. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  2127. <FormattedMessage id="userIdDoc" />
  2128. </Typography>
  2129. {fileList != null && props.step === 1 ?
  2130. <PreviewUploadFileTable key="previewTable" recordList={fileListData} /> : null}
  2131. </Stack>
  2132. </Grid>
  2133. </Grid>
  2134. </Grid>
  2135. </Grid>
  2136. </Grid>
  2137. </Grid>
  2138. </FormGroup>
  2139. {/* Submit page */}
  2140. <FormGroup id={"submitForm"} sx={{ display: props.step === 2 ? "" : "none" }}>
  2141. <Grid container spacing={3}>
  2142. {isLoading ?
  2143. <LoadingComponent /> :
  2144. <Grid item xs={12}>
  2145. {checkUpload ?
  2146. // SUCCESS page
  2147. <Stack mt={1} direction="column" justifyContent="flex-start" alignItems="center" spacing={2}>
  2148. <CheckCircleOutlineIcon color="success" sx={{ width: "200px", height: "200px" }} />
  2149. <Typography display="inline" variant="h4">
  2150. <FormattedMessage id="registerSubmitted" />
  2151. </Typography>
  2152. <Typography display="inline" variant="h4">
  2153. <FormattedMessage id="emailSent" />
  2154. </Typography>
  2155. <Button variant="outlined" component={Link} to="/login" ><Typography variant="pnspsFormHeader">
  2156. <FormattedMessage id="backToLogin" />
  2157. </Typography></Button>
  2158. </Stack>
  2159. :
  2160. // ERROR page
  2161. <Stack mt={1} direction="column" justifyContent="flex-start" alignItems="center" spacing={2}>
  2162. {/* <Button disabled={true} hidden={true} variant="contained" type="submit" sx={{ fontSize: 12,height:'25px'}}>Submit</Button> */}
  2163. <CancelOutlinedIcon color="error" sx={{ width: "200px", height: "200px" }} />
  2164. <Typography display="inline" variant="h4">
  2165. <FormattedMessage id="registerFail" />
  2166. </Typography>
  2167. <Button color="error" variant="outlined" component={Link} to="/login" ><Typography variant="pnspsFormHeader">
  2168. <FormattedMessage id="backToLogin" />
  2169. </Typography></Button>
  2170. </Stack>
  2171. }
  2172. </Grid>
  2173. }
  2174. </Grid>
  2175. </FormGroup>
  2176. </form>
  2177. </FormikProvider>
  2178. );
  2179. }
  2180. export default CustomFormWizard;