Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 

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