Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 

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