您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 

1032 行
62 KiB

  1. import { useEffect, useState, } from 'react';
  2. // material-ui
  3. import {
  4. Button,
  5. FormHelperText,
  6. Grid, IconButton,
  7. InputLabel, OutlinedInput,
  8. Stack,
  9. Typography,
  10. FormGroup,
  11. TextField,
  12. Checkbox
  13. // MenuItem
  14. } from '@mui/material';
  15. import { useForm, } from 'react-hook-form'
  16. import Autocomplete from "@mui/material/Autocomplete";
  17. // third party
  18. import { useFormik, FormikProvider } from 'formik';
  19. import * as yup from 'yup';
  20. import axios from "axios";
  21. import { POST_IAMSMART_USER_REGISTER, POST_CAPTCHA, GET_USER_EMAIL } from "utils/ApiPathConst";
  22. import * as ComboData from "utils/ComboData";
  23. import Loadable from 'components/Loadable';
  24. import { lazy } from 'react';
  25. const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent')));
  26. import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
  27. import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';
  28. import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png';
  29. import { Link } from 'react-router-dom';
  30. import * as HttpUtils from "../../../utils/HttpUtils";
  31. import LoopIcon from '@mui/icons-material/Loop';
  32. import { useTheme } from '@mui/material/styles';
  33. import { useLocation } from "react-router-dom";
  34. // ============================|| FIREBASE - REGISTER ||============================ //
  35. const CustomFormWizard = (props) => {
  36. const location = useLocation();
  37. const theme = useTheme();
  38. const [iAmSmartData, setIAmSmartData] = useState({});
  39. const [checkUpload, setCheckUpload] = useState(false);
  40. const [isLoading, setLoding] = useState(true);
  41. const [captcha, setCaptcha] = useState([]);
  42. const [captchaImg, setCaptchaImage] = useState([]);
  43. const [selectedAddress4, setSelectedAddress4] = useState(null);
  44. const [selectedAddress5, setSelectedAddress5] = useState(ComboData.country[0]);
  45. const [termsAndConAccept, setTermsAndConAccept] = useState(false);
  46. const [termsAndConNotAccept, setTermsAndConNotAccept] = useState(false);
  47. const [isValid, setisValid] = useState(false);
  48. const [checkCountry, setCheckCountry] = useState(false);
  49. const email = document.getElementById("email-login")
  50. const [checkEmail, setCheckEmail] = useState(false)
  51. const [checkEmailBlur, setCheckEmailBlur] = useState(false)
  52. const address4ComboList = ComboData.district;
  53. const address5ComboList = ComboData.country;
  54. const termsAndCon = "此網址由香港特別行政區政府物流服務署製作及管理。本署會盡力確保網址上的資料無誤,\n"
  55. + "但有絕對酌情權隨時刪除、暫停登載或編輯各項資料而無須給予任何理由。\n由於任何與網址"
  56. + "內資料有關的理由或原因,而導致出現申索、損失或損害,本署概不負責。\n使用者須自行評"
  57. + "估本網址所載或與本網址有關連的各項資料,並應在根據該等資料行事前,參照印行的香港"
  58. + "特別行政區憲報以核實該等資料,以及徵詢獨立意見。\n版權公告本網頁的內容,包括但不限"
  59. + "於所有文本、平面圖像、圖畫、圖片、照片以及數據或其他資料的匯編,均受版權保障。\n香"
  60. + "港特別行政區政府是本網頁內所有版權作品的擁有人,除非預先得到政府物流服務署的書面"
  61. + "授權,否則嚴禁複製、改編、分發、發布或向公眾提供該等版權作品。"
  62. useEffect(() => {
  63. location.state?.responseData ?? {}
  64. onCaptchaChange();
  65. responseToData();
  66. }, []);
  67. const responseToData = () => {
  68. //let rd = JSON.parse("{\"emailAddress\":\"[email protected]\",\"postalAddress\":{\"EngPremisesAddress\":{\"EngDistrict\":{\"DcDistrict\":\"KC\",\"Sub-district\":\"TSING YI\"},\"EngEstate\":{\"EstateName\":\"Cheung Hang Estate\",\"EngPhase\":{\"PhaseName\":\"N/A\"}},\"BuildingName\":\"Hang Lai House\",\"EngBlock\":{\"BlockDescriptor\":\"Block\",\"BlockNo\":\"2\"},\"Region\":\"NT\",\"EngStreet\":{\"StreetName\":\"Liu To Road\",\"BuildingNoFrom\":\"6\"},\"Eng3dAddress\":{\"EngFloor\":{\"FloorNum\":\"33\"},\"EngUnit\":{\"UnitDescriptor\":\"Room\",\"UnitNo\":\"3301\"}}}},\"mobileNumber\":{\"CountryCode\":\"852\",\"SubscriberNumber\":\"99999999\"},\"residentialAddress\":{\"ChiPremisesAddress\":{\"Chi3dAddress\":{\"ChiUnit\":{\"UnitDescriptor\":\"室\",\"UnitNo\":\"1010\"},\"ChiFloor\":{\"FloorNum\":\"10\"}},\"ChiBlock\":{\"BlockDescriptor\":\"座\",\"BlockNo\":\"2\"},\"BuildingName\":\"亨麗樓(第2座)\",\"ChiDistrict\":{\"DcDistrict\":\"KC\",\"Sub-district\":\"青衣\"},\"Region\":\"新界\",\"ChiEstate\":{\"EstateName\":\"長亨邨\"},\"ChiStreet\":{\"StreetName\":\"寮肚路\",\"BuildingNoFrom\":\"6\"}}},\"enName\":{\"UnstructuredName\":\"Testing Co One\"},\"idNo\":{\"Identification\":\"G561107\",\"CheckDigit\":\"4\"},\"chName\":{\"ChineseName\":\"測試商一\"}}");
  69. let rd = props.state.responseData;
  70. let data = {
  71. "enName": rd.enName?.UnstructuredName ?? "",
  72. "chName": rd.chName?.ChineseName ?? "",
  73. "idNo": rd.idNo?.Identification ?? "",
  74. "checkDigit": rd.idNo?.CheckDigit ?? "",
  75. "email": rd.emailAddress ?? "",
  76. "phone": rd.mobileNumber?.SubscriberNumber ?? "",
  77. "phoneCountryCode": rd.mobileNumber?.CountryCode ?? "",
  78. };
  79. if (rd.postalAddress) {
  80. if (rd.postalAddress.EngPremisesAddress) {
  81. data["address1"] = getAddressEng(rd.postalAddress.EngPremisesAddress);
  82. } else if (rd.postalAddress.ChiPremisesAddress) {
  83. data["address1"] = getAddressChi(rd.postalAddress.ChiPremisesAddress);
  84. }
  85. } else if (rd.residentialAddress) {
  86. if (rd.residentialAddress.EngPremisesAddress) {
  87. data["address1"] = getAddressEng(rd.residentialAddress.EngPremisesAddress);
  88. } else if (rd.residentialAddress.ChiPremisesAddress) {
  89. data["address1"] = getAddressChi(rd.residentialAddress.ChiPremisesAddress);
  90. }
  91. }
  92. setIAmSmartData(data);
  93. }
  94. const getAddressEng = (pAdd) => {
  95. let unit = (pAdd.Eng3dAddress?.EngUnit?.UnitDescriptor ?? "") + " " + (pAdd.Eng3dAddress?.EngUnit?.UnitNo ?? "");
  96. let block = (pAdd.EngBlock?.BlockDescriptor ?? "") + " " + (pAdd.EngBlock?.BlockNo ?? "");
  97. let floor = pAdd.Eng3dAddress?.EngFloor?.FloorNum ? pAdd.Eng3dAddress?.EngFloor?.FloorNum + "/F " : "";
  98. let street = (pAdd.EngStreet?.EngUnit?.UnitDescriptor ?? "") + " " + (pAdd.Eng3dAddress?.EngUnit?.UnitNo ?? "");
  99. //let region = pAdd.Region ?? "";
  100. let buildingName = pAdd.BuildingName ?? "";
  101. let estate = pAdd.EngEstate?.EstateName ?? "";
  102. let district = pAdd.EngDistrict["Sub - district"] ?? "";
  103. return getAddressStr([unit, block, floor, buildingName, estate, street, district]);
  104. }
  105. const getAddressChi = (pAdd) => {
  106. let unit = (pAdd.Chi3dAddress?.ChiUnit?.UnitDescriptor ?? "") + " " + (pAdd.Chi3dAddress?.ChiUnit?.UnitNo ?? "");
  107. let block = (pAdd.ChiBlock?.BlockDescriptor ?? "") + " " + (pAdd.ChiBlock?.BlockNo ?? "");
  108. let floor = pAdd.Chi3dAddress?.ChiFloor?.FloorNum ? pAdd.Chi3dAddress?.ChiFloor?.FloorNum + "樓 " : "";
  109. let street = (pAdd.ChiStreet?.ChiUnit?.UnitDescriptor ?? "") + " " + (pAdd.Chi3dAddress?.ChiUnit?.UnitNo ?? "");
  110. //let region = pAdd.Region ?? "";
  111. let buildingName = pAdd.BuildingName ?? "";
  112. let estate = pAdd.ChiEstate?.EstateName ?? "";
  113. let district = pAdd.ChiDistrict["Sub - district"] ?? "";
  114. return getAddressStr([district, street, estate, buildingName, street, floor, block, unit ]);
  115. }
  116. const getAddressStr = (strs) => {
  117. let add = ""
  118. strs.forEach(str => {
  119. add += str ? str + ", " : "";
  120. });
  121. add = add.trim();
  122. if (add.slice(- 1) == ",") {
  123. add = add.substring(0, add.length - 1);
  124. }
  125. return add;
  126. }
  127. const handleCheckEmail = async () => {
  128. if (values?.email) {
  129. const response = await axios.get(`${GET_USER_EMAIL}`, {
  130. params: {
  131. email: values.email,
  132. }
  133. })
  134. setCheckEmail((Number(response.data[0]) === 1))
  135. return Number(response.data[0]) === 1
  136. }
  137. }
  138. useEffect(() => {
  139. if (email) {
  140. email.addEventListener("blur", function () {
  141. setCheckEmailBlur(true)
  142. })
  143. }
  144. }, [email])
  145. useEffect(() => {
  146. if (checkEmailBlur) {
  147. handleCheckEmail()
  148. setCheckEmailBlur(false)
  149. }
  150. }, [checkEmailBlur])
  151. useEffect(() => {
  152. if (iAmSmartData) {
  153. formik.setFieldValue("enName", iAmSmartData.enName??"");
  154. formik.setFieldValue("chName", iAmSmartData.chName??"");
  155. formik.setFieldValue("idNo", iAmSmartData.idNo??"");
  156. formik.setFieldValue("checkDigit", iAmSmartData.checkDigit??"");
  157. formik.setFieldValue("email", iAmSmartData.email??"");
  158. formik.setFieldValue("phone", iAmSmartData.phone??"");
  159. formik.setFieldValue("phoneCountryCode", iAmSmartData.phoneCountryCode??"");
  160. formik.setFieldValue("address1", iAmSmartData.address1??"");
  161. props.setIdNo(iAmSmartData.idNo??"");
  162. }
  163. }, [iAmSmartData])
  164. const onCaptchaChange = () => {
  165. HttpUtils.post({
  166. url: POST_CAPTCHA,
  167. params: { width: 130, height: 40 },
  168. onSuccess: (responseData) => {
  169. localStorage.setItem("checkCode", responseData.checkCode);
  170. localStorage.setItem("base64Url", responseData.base64Url);
  171. setCaptcha(localStorage.getItem('checkCode'));
  172. setCaptchaImage(localStorage.getItem('base64Url'));
  173. }
  174. });
  175. }
  176. const checkDataField = (data) => {
  177. if (data.address1 !== "" &&
  178. data.email !== "" &&
  179. data.emailConfirm !== "" &&
  180. data.email == data.emailConfirm &&
  181. data.phone !== "" &&
  182. data.phoneCountryCode !== "" &&
  183. termsAndConAccept == true &&
  184. data.captchaField &&
  185. handleEmail(data.email) &&
  186. handlePhone(data.phone) &&
  187. handleCaptcha(data.captchaField) &&
  188. !checkEmail
  189. ) {
  190. setisValid(true)
  191. return isValid
  192. } else {
  193. setisValid(false)
  194. return isValid
  195. }
  196. };
  197. const handleCheckBoxChange = (event) => {
  198. if (event.target.name == 'termsAndConAccept') {
  199. setTermsAndConAccept(event.target.checked)
  200. setTermsAndConNotAccept(!event.target.checked)
  201. }
  202. if (event.target.name == 'termsAndConNotAccept') {
  203. setTermsAndConNotAccept(event.target.checked)
  204. setTermsAndConAccept(!event.target.checked)
  205. }
  206. };
  207. useEffect(() => {
  208. props.setUpdateValid(isValid)
  209. }, [isValid])
  210. useEffect(() => {
  211. checkDataField(values)
  212. }, [
  213. selectedAddress4, selectedAddress5,
  214. termsAndConAccept, termsAndConNotAccept])
  215. useEffect(() => {
  216. props.step == 2 ? _onSubmit() : null;
  217. onCaptchaChange();
  218. checkDataField(values)
  219. }, [props.step])
  220. const { handleSubmit } = useForm({})
  221. const _onSubmit = () => {
  222. setLoding(true);
  223. values.address4 = selectedAddress4
  224. values.address5 = selectedAddress5
  225. const userAddress = {
  226. "addressLine1": "",
  227. "addressLine2": "",
  228. "addressLine3": "",
  229. "district": "",
  230. "country": ""
  231. };
  232. userAddress.addressLine1 = values.address1
  233. userAddress.addressLine2 = values.address2
  234. userAddress.addressLine3 = values.address3
  235. userAddress.district = values.address4
  236. userAddress.country = values.address5
  237. const userFaxNo = {
  238. "countryCode": values.faxCountryCode,
  239. "faxNumber": values.fax,
  240. };
  241. const userMobileNumber = {
  242. "countryCode": values.phoneCountryCode,
  243. "phoneNumber": values.phone,
  244. };
  245. let tncFlag = false;
  246. if (termsAndConAccept) {
  247. tncFlag = true
  248. }
  249. if (termsAndConNotAccept) {
  250. tncFlag = false
  251. }
  252. const formData = {
  253. enName: iAmSmartData.enName,
  254. chName: iAmSmartData.chName,
  255. emailAddress: values.email,
  256. idDocType: "HKID",
  257. identification: iAmSmartData.idNo,
  258. checkDigit: iAmSmartData.checkDigit,
  259. tncFlag: tncFlag,
  260. type: "IND",
  261. userFaxNo: JSON.stringify(userFaxNo),
  262. userMobileNumber: JSON.stringify(userMobileNumber),
  263. userAddress: JSON.stringify(userAddress)
  264. };
  265. if (isValid) {
  266. axios.post(POST_IAMSMART_USER_REGISTER, formData, {
  267. headers: {
  268. "Content-Type": "multipart/form-data"
  269. }
  270. })
  271. .then((response) => {
  272. console.log(response)
  273. setCheckUpload(true)
  274. setLoding(false);
  275. })
  276. .catch(error => {
  277. console.error(error);
  278. setLoding(false);
  279. });
  280. } else {
  281. setLoding(false);
  282. }
  283. }
  284. function handlePhone(phone) {
  285. if (phone.length < 8) {
  286. return false;
  287. } else {
  288. return true;
  289. }
  290. }
  291. function handleCaptcha(captchaField) {
  292. return captchaField == captcha;
  293. }
  294. function handleEmail(email) {
  295. var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
  296. if (!email.match(validRegex)) {
  297. return false;
  298. } else {
  299. return true;
  300. }
  301. }
  302. function displayErrorMsg(errorMsg) {
  303. return <Typography variant="errorMessage1">{errorMsg}</Typography>
  304. }
  305. const formik = useFormik({
  306. initialValues: ({
  307. email: iAmSmartData.email??"",
  308. emailConfirm: iAmSmartData.email??"",
  309. address1: iAmSmartData.address1??"",
  310. address2: '',
  311. address3: '',
  312. phone: iAmSmartData.phone??"",
  313. phoneCountryCode: iAmSmartData.phoneCountryCode??"852",
  314. submit: null,
  315. fax: '',
  316. faxCountryCode: '852',
  317. captchaField: ''
  318. }),
  319. validationSchema: yup.object().shape({
  320. address1: yup.string().max(255).required(displayErrorMsg('請輸入第一行地址')),
  321. address2: yup.string().max(255).required(displayErrorMsg('請輸入第二行地址')),
  322. address3: yup.string().max(255).required(displayErrorMsg('請輸入第三行地址')),
  323. email: yup.string().email(displayErrorMsg('請輸入電郵格式')).max(255).required(displayErrorMsg('請輸入電郵')),
  324. emailConfirm: yup.string().email(displayErrorMsg('請輸入電郵格式')).max(255).required(displayErrorMsg('請輸入電郵')).oneOf([yup.ref('email'), null], displayErrorMsg('請輸入相同電郵')),
  325. phoneCountryCode: yup.string().min(2, displayErrorMsg('請輸入最少2位數字')).required(displayErrorMsg('請輸入國際區號')),
  326. phone: yup.string().min(8, displayErrorMsg('請輸入最少8位數字')).required(displayErrorMsg('請輸入聯絡電話')),
  327. captchaField: yup.string().required(displayErrorMsg('請輸入驗證')).oneOf([captcha], displayErrorMsg('請輸入有效驗證')),
  328. }),
  329. });
  330. const { values } = formik
  331. useEffect(() => {
  332. checkDataField(values)
  333. }, [values])
  334. return (
  335. <FormikProvider value={formik}>
  336. <form onSubmit={handleSubmit(_onSubmit)}>
  337. {/* Input Form */}
  338. <FormGroup id={"inputForm"} sx={{ display: props.step === 0 ? "" : "none" }}>
  339. <Grid container spacing={3}>
  340. <Grid item xs={12} md={12}>
  341. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  342. <div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}>
  343. <Typography display="inline" variant="h3" sx={{ color: '#1A4399' }}>成為新的個人用戶</Typography><img src={iAmSmartICon} alt="iAM Smart" width="50" />
  344. </div>
  345. <Typography mt={0.25} variant="h6" sx={{ color: '#f10000' }}>註有*的項目必須輸入資料。</Typography>
  346. <Stack mt={1} direction="row" style={{ alignItems: "center" }}><img src={iAmSmartICon} alt="iAM Smart" width="25" /><Typography mt={0.25} variant="h6" >: 表示該項由「智方便」提供。</Typography></Stack>
  347. </Stack>
  348. </Grid>
  349. <Grid item xs={12} md={12}>
  350. <Grid container spacing={1}>
  351. <Grid item xs={12} mt={1} mb={1}>
  352. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  353. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>你的個人資料</Typography>
  354. </Stack>
  355. </Grid>
  356. <Grid item xs={12} md={12} >
  357. <Grid container sx={{ mb: 1 }}>
  358. <Stack spacing={1}>
  359. <InputLabel htmlFor="idDocType-signup">
  360. <Typography variant="h5">
  361. 香港身份證: {iAmSmartData.idNo+"("+iAmSmartData.checkDigit+")"}
  362. </Typography>
  363. </InputLabel>
  364. </Stack>
  365. </Grid>
  366. </Grid>
  367. <Grid item xs={12} md={6}>
  368. <Stack spacing={1}>
  369. <InputLabel htmlFor="enName-signup">
  370. <Typography variant="h5">
  371. 英文姓名: {iAmSmartData.enName}
  372. </Typography>
  373. </InputLabel>
  374. </Stack>
  375. </Grid>
  376. <Grid item xs={12} md={6}>
  377. <Stack spacing={1}>
  378. <InputLabel htmlFor="chName-signup">
  379. <Typography variant="h5">
  380. 中文姓名: {iAmSmartData.chName}
  381. </Typography>
  382. </InputLabel>
  383. </Stack>
  384. </Grid>
  385. <Grid item xs={12}>
  386. <Stack spacing={1}>
  387. <InputLabel htmlFor="address1-signup">
  388. <Typography variant="h5">
  389. 地址
  390. <span style={{ color: '#f10000' }}>*</span>
  391. {iAmSmartData.address1?<img src={iAmSmartICon} alt="iAM Smart" width="25" />:null}
  392. </Typography>
  393. </InputLabel>
  394. <OutlinedInput
  395. fullWidth
  396. error={Boolean(formik.touched.address1 && formik.errors.address1)}
  397. id="address1-signup"
  398. value={formik.values.address1}
  399. name="address1"
  400. onChange={formik.handleChange}
  401. placeholder="第一行"
  402. onBlur={formik.handleBlur}
  403. inputProps={{
  404. onKeyDown: (e) => {
  405. if (e.key === 'Enter') {
  406. e.preventDefault();
  407. }
  408. },
  409. }}
  410. />
  411. <OutlinedInput
  412. fullWidth
  413. error={Boolean(formik.touched.address2 && formik.errors.address2)}
  414. id="address2-signup"
  415. value={formik.values.address2}
  416. name="address2"
  417. onChange={formik.handleChange}
  418. placeholder="第二行"
  419. inputProps={{
  420. onKeyDown: (e) => {
  421. if (e.key === 'Enter') {
  422. e.preventDefault();
  423. }
  424. },
  425. }}
  426. />
  427. <OutlinedInput
  428. fullWidth
  429. error={Boolean(formik.touched.address3 && formik.errors.address3)}
  430. id="address3-signup"
  431. value={formik.values.address3}
  432. name="address3"
  433. onChange={formik.handleChange}
  434. placeholder="第三行"
  435. inputProps={{
  436. onKeyDown: (e) => {
  437. if (e.key === 'Enter') {
  438. e.preventDefault();
  439. }
  440. },
  441. }}
  442. />
  443. <Autocomplete
  444. disablePortal
  445. id="address4-combo"
  446. value={selectedAddress4}
  447. options={address4ComboList}
  448. disabled={checkCountry}
  449. onChange={(event, newValue) => {
  450. setSelectedAddress4(newValue);
  451. }}
  452. sx={{ "& .MuiInputBase-root": { height: "41px" }, "#address4-combo": { padding: "0px 0px 0px 0px" }, "& .MuiAutocomplete-endAdornment": { top: "auto" }, }}
  453. renderInput={(params) => <TextField {...params} placeholder="區域 (只適用於香港)" />}
  454. />
  455. <Autocomplete
  456. disablePortal
  457. id="address5-combo"
  458. value={selectedAddress5}
  459. options={address5ComboList}
  460. onChange={(event, newValue) => {
  461. if (newValue !== null) {
  462. setSelectedAddress5(newValue);
  463. if (newValue == '香港') {
  464. setCheckCountry(false)
  465. } else {
  466. setSelectedAddress4("");
  467. setCheckCountry(true)
  468. }
  469. } else {
  470. setSelectedAddress4("");
  471. setCheckCountry(true)
  472. }
  473. }}
  474. sx={{ "& .MuiInputBase-root": { height: "41px" }, "#address5-combo": { padding: "0px 0px 0px 0px" }, "& .MuiAutocomplete-endAdornment": { top: "auto" }, }}
  475. renderInput={(params) => <TextField {...params} placeholder="國家/地區" />}
  476. />
  477. {formik.touched.address1 && formik.errors.address1 && (
  478. <FormHelperText error id="helper-text-address1-signup">
  479. {formik.errors.address1}
  480. </FormHelperText>
  481. )}
  482. {formik.touched.address2 && formik.errors.address2 && (
  483. <FormHelperText error id="helper-text-address2-signup">
  484. {formik.errors.address2}
  485. </FormHelperText>
  486. )}
  487. {formik.touched.address3 && formik.errors.address3 && (
  488. <FormHelperText error id="helper-text-address3-signup">
  489. {formik.errors.address3}
  490. </FormHelperText>
  491. )}
  492. </Stack>
  493. </Grid>
  494. <Grid item xs={12} mt={1} mb={1}>
  495. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  496. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>你的聯絡資料</Typography>
  497. </Stack>
  498. </Grid>
  499. <Grid item xs={12} md={12}>
  500. <Grid container>
  501. <Grid item xs={12} md={6}>
  502. <Stack spacing={1} sx={{ mr: { md: 1 }, mb: 1 }}>
  503. <InputLabel htmlFor="email-signup">
  504. <Typography variant="h5">
  505. 電郵
  506. <span style={{ color: '#f10000' }}>*</span>
  507. {iAmSmartData.email?<img src={iAmSmartICon} alt="iAM Smart" width="25" />:null}
  508. </Typography>
  509. </InputLabel>
  510. <OutlinedInput
  511. fullWidth
  512. error={Boolean((formik.touched.email && formik.errors.email) || checkEmail)}
  513. id="email-login"
  514. type="email"
  515. value={formik.values.email.trim()}
  516. name="email"
  517. onChange={formik.handleChange}
  518. placeholder="電郵"
  519. onBlur={formik.handleBlur}
  520. inputProps={{
  521. onKeyDown: (e) => {
  522. if (e.key === 'Enter') {
  523. e.preventDefault();
  524. }
  525. },
  526. }}
  527. />
  528. {formik.touched.email && formik.errors.email && (
  529. <FormHelperText error id="helper-text-email-signup">
  530. {formik.errors.email}
  531. </FormHelperText>
  532. )}
  533. {checkEmail && (
  534. <FormHelperText error id="helper-text-email-signup">
  535. 此電郵已被注冊,請使用其他電郵
  536. </FormHelperText>
  537. )}
  538. </Stack>
  539. </Grid>
  540. <Grid item xs={12} md={6}>
  541. <Stack spacing={1} >
  542. <InputLabel htmlFor="emailConfirm-signup">
  543. <Typography variant="h5">
  544. 確認電郵
  545. <span style={{ color: '#f10000' }}>*</span>
  546. </Typography>
  547. </InputLabel>
  548. <OutlinedInput
  549. fullWidth
  550. error={Boolean(formik.touched.emailConfirm && formik.errors.emailConfirm)}
  551. id="emailConfirm-login"
  552. type="email"
  553. value={formik.values.emailConfirm.trim()}
  554. name="emailConfirm"
  555. // onBlur={formik.handleBlur}
  556. onChange={formik.handleChange}
  557. placeholder="確認電郵"
  558. onBlur={formik.handleBlur}
  559. inputProps={{
  560. onKeyDown: (e) => {
  561. if (e.key === 'Enter') {
  562. e.preventDefault();
  563. }
  564. },
  565. }}
  566. />
  567. {formik.touched.emailConfirm && formik.errors.emailConfirm && (
  568. <FormHelperText error id="helper-text-emailConfirm-signup">
  569. {formik.errors.emailConfirm}
  570. </FormHelperText>
  571. )}
  572. </Stack>
  573. </Grid>
  574. </Grid>
  575. </Grid>
  576. <Grid item xs={12} md={12}>
  577. <Grid container>
  578. <Grid item xs={12} md={6}>
  579. <Grid container>
  580. <Grid item xs={12} md={12}>
  581. <Stack direction="column" spacing={1} sx={{ mr: { md: 1 }, mb: 1 }}>
  582. <InputLabel htmlFor="phone-signup">
  583. <Typography variant="h5">
  584. 聯絡電話
  585. <span style={{ color: '#f10000' }}>*</span>
  586. {iAmSmartData.phone?<img src={iAmSmartICon} alt="iAM Smart" width="25" />:null}
  587. </Typography>
  588. </InputLabel>
  589. <Stack direction="row">
  590. <OutlinedInput
  591. id="phoneCountryCode-login"
  592. type="phoneCountryCode"
  593. value={formik.values.phoneCountryCode.trim()}
  594. name="phoneCountryCode"
  595. // onBlur={formik.handleBlur}
  596. // onChange={formik.handleChange}
  597. onChange={(event) => {
  598. const value = event.target.value;
  599. if (value.match(/[^0-9]/)) {
  600. return event.preventDefault();
  601. }
  602. formik.setFieldValue("phoneCountryCode", value);
  603. }}
  604. placeholder="國際區號"
  605. error={Boolean(formik.touched.phone && formik.errors.phone)}
  606. onBlur={formik.handleBlur}
  607. inputProps={{
  608. maxLength: 3,
  609. onKeyDown: (e) => {
  610. if (e.key === 'Enter') {
  611. e.preventDefault();
  612. }
  613. },
  614. }}
  615. sx={{ width: '25%' }}
  616. />
  617. <OutlinedInput
  618. id="phone-login"
  619. type="phone"
  620. value={formik.values.phone.trim()}
  621. name="phone"
  622. // onBlur={formik.handleBlur}
  623. // onChange={formik.handleChange}
  624. onChange={(event) => {
  625. const value = event.target.value;
  626. if (value.match(/[^0-9]/)) {
  627. return event.preventDefault();
  628. }
  629. formik.setFieldValue("phone", value);
  630. }}
  631. placeholder="聯絡電話"
  632. error={Boolean(formik.touched.phone && formik.errors.phone)}
  633. onBlur={formik.handleBlur}
  634. inputProps={{
  635. maxLength: 11,
  636. onKeyDown: (e) => {
  637. if (e.key === 'Enter') {
  638. e.preventDefault();
  639. }
  640. },
  641. }}
  642. sx={{ width: '75%' }}
  643. />
  644. </Stack>
  645. {formik.touched.phone && formik.errors.phone && (
  646. <FormHelperText error id="helper-text-phone-signup">
  647. {formik.errors.phone}
  648. </FormHelperText>
  649. )}
  650. </Stack>
  651. </Grid>
  652. </Grid>
  653. </Grid>
  654. <Grid item xs={12} md={6}>
  655. <Grid container>
  656. <Grid item xs={12} md={12}>
  657. <Stack spacing={1} direction="column">
  658. <InputLabel htmlFor="fax-signup">
  659. <Typography variant="h5">
  660. 傳真號碼
  661. </Typography>
  662. </InputLabel>
  663. <Stack direction="row">
  664. <OutlinedInput
  665. error={Boolean(formik.touched.fax && formik.errors.fax)}
  666. id="faxCountryCode-login"
  667. type="faxCountryCode"
  668. value={formik.values.faxCountryCode.trim()}
  669. name="faxCountryCode"
  670. // onChange={formik.handleChange}
  671. onChange={(event) => {
  672. const value = event.target.value;
  673. if (value.match(/[^0-9]/)) {
  674. return event.preventDefault();
  675. }
  676. formik.setFieldValue("faxCountryCode", value);
  677. }}
  678. placeholder="國際區號"
  679. onBlur={formik.handleBlur}
  680. inputProps={{
  681. maxLength: 3,
  682. onKeyDown: (e) => {
  683. if (e.key === 'Enter') {
  684. e.preventDefault();
  685. }
  686. },
  687. }}
  688. sx={{ width: '25%' }}
  689. />
  690. <OutlinedInput
  691. id="fax-login"
  692. type="fax"
  693. value={formik.values.fax.trim()}
  694. name="fax"
  695. onBlur={formik.handleBlur}
  696. // onChange={formik.handleChange}
  697. onChange={(event) => {
  698. const value = event.target.value;
  699. if (value.match(/[^0-9]/)) {
  700. return event.preventDefault();
  701. }
  702. formik.setFieldValue("fax", value);
  703. }}
  704. placeholder="傳真號碼"
  705. inputProps={{
  706. maxLength: 8,
  707. onKeyDown: (e) => {
  708. if (e.key === 'Enter') {
  709. e.preventDefault();
  710. }
  711. },
  712. }}
  713. sx={{ width: '75%' }}
  714. />
  715. </Stack>
  716. </Stack>
  717. </Grid>
  718. </Grid>
  719. </Grid>
  720. </Grid>
  721. </Grid>
  722. </Grid>
  723. <Grid item xs={12} md={12}>
  724. <Grid container>
  725. <Grid item xs={12} md={12}>
  726. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  727. 條款和條件
  728. <span style={{ color: '#f10000' }}>*</span>
  729. </Typography>
  730. </Grid>
  731. <Grid item xs={12} md={12}>
  732. <Grid container>
  733. <Grid item xs={12} md={12}>
  734. <Typography variant="h5" height="80%" sx={{ textAlign: "left", overflow: "scroll", borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0C489E" }}>
  735. {termsAndCon}
  736. </Typography>
  737. </Grid>
  738. </Grid>
  739. <Grid item xs={12} s={12} md={12} lg={12}>
  740. <Grid container>
  741. <Grid item xs={6} s={6} md={2} lg={2}>
  742. <Grid container>
  743. <Grid item sx={{ display: 'flex', alignItems: 'center' }}>
  744. <Checkbox
  745. checked={termsAndConAccept}
  746. onChange={handleCheckBoxChange}
  747. name="termsAndConAccept"
  748. color="primary"
  749. size="small"
  750. />
  751. <Typography variant="h5">我接受</Typography>
  752. </Grid>
  753. </Grid>
  754. </Grid>
  755. <Grid item xs={6} s={6} md={2} lg={2}>
  756. <Grid container>
  757. <Grid item sx={{ display: 'flex', alignItems: 'center' }}>
  758. <Checkbox
  759. checked={termsAndConNotAccept}
  760. onChange={handleCheckBoxChange}
  761. name="termsAndConNotAccept"
  762. color="primary"
  763. size="small"
  764. />
  765. <Typography variant="h5">我不接受</Typography>
  766. </Grid>
  767. </Grid>
  768. </Grid>
  769. </Grid>
  770. </Grid>
  771. </Grid>
  772. </Grid>
  773. </Grid>
  774. <Grid item xs={12} lg={12}>
  775. <Grid container>
  776. <Stack direction="column">
  777. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
  778. 驗證
  779. <span style={{ color: '#f10000' }}>*</span>
  780. </Typography>
  781. <Stack spacing={1} direction="row">
  782. <Grid item xs={5} lg={5} style={{ "border": "1px solid black" }}>
  783. <img src={captchaImg} alt="" />
  784. </Grid>
  785. <Grid item xs={1} lg={1} style={{ "border": "0px solid black" }}>
  786. <IconButton aria-label="refrashCaptcha" size="large" onClick={() => { onCaptchaChange() }}>
  787. <LoopIcon fontSize="inherit" />
  788. </IconButton>
  789. </Grid>
  790. <Grid item xs={6} lg={6}>
  791. <OutlinedInput
  792. fullWidth
  793. id="captchaField"
  794. type="text"
  795. value={formik.values.captchaField.trim()}
  796. onBlur={formik.handleBlur}
  797. error={Boolean(formik.touched.captchaField && formik.errors.captchaField)}
  798. name="captchaField"
  799. onChange={(event) => {
  800. const value = event.target.value;
  801. formik.setFieldValue("captchaField", value);
  802. }}
  803. sx={{ width: '75%' }}
  804. />
  805. </Grid>
  806. </Stack>
  807. {formik.touched.captchaField && formik.errors.captchaField && (
  808. <FormHelperText error id="helper-text-captcha-signup">
  809. {formik.errors.captchaField}
  810. </FormHelperText>
  811. )}
  812. </Stack>
  813. </Grid>
  814. </Grid>
  815. </Grid>
  816. </Grid>
  817. </FormGroup>
  818. {/* Preview Form */}
  819. <FormGroup id={"previewForm"} sx={{ display: props.step === 1 ? "" : "none" }}>
  820. <Grid container spacing={3}>
  821. <Grid item xs={12} md={12}>
  822. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  823. <div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}>
  824. <Typography display="inline" variant="h3" sx={{ color: '#1A4399' }}>成為新的個人用戶</Typography>
  825. </div>
  826. </Stack>
  827. </Grid>
  828. <Grid item xs={12} md={12}>
  829. <Grid container spacing={2}>
  830. <Grid item xs={12} mt={1} mb={1}>
  831. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  832. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>你的個人資料</Typography>
  833. {/* <Typography component={Link} to="/login" variant="body1" sx={{ textDecoration: 'none' }} color="primary">
  834. Already have an account?
  835. </Typography> */}
  836. </Stack>
  837. </Grid>
  838. <Grid item xs={12} md={12} >
  839. <Stack spacing={1}>
  840. <Typography variant="h5" color={theme.palette.grey[600]}>
  841. 身份證明文件
  842. </Typography>
  843. <Typography variant="h5" name="preview-idDocType">
  844. {formik.values.idNo+"("+formik.values.checkDigit+")"}
  845. </Typography>
  846. </Stack>
  847. </Grid>
  848. <Grid item xs={12} md={6}>
  849. <Stack spacing={1} direction="row">
  850. <Typography variant="h5" color={theme.palette.grey[600]}>
  851. 英文姓名:
  852. </Typography>
  853. <Typography variant="h5" id="preview-enName-signup">
  854. {formik.values.enName}
  855. </Typography>
  856. </Stack>
  857. </Grid>
  858. <Grid item xs={12} md={6}>
  859. <Stack spacing={1} direction="row">
  860. <Typography variant="h5" color={theme.palette.grey[600]}>
  861. 中文姓名:
  862. </Typography>
  863. <Typography variant="h5" id="preview-chName-signup">
  864. {formik.values.chName}
  865. </Typography>
  866. </Stack>
  867. </Grid>
  868. <Grid item xs={12}>
  869. <Stack spacing={1} direction="row">
  870. <Typography variant="h5" color={theme.palette.grey[600]}>
  871. 地址:
  872. </Typography>
  873. <Stack spacing={1} direction="column">
  874. <Typography variant="h5" id="preview-address1-signup">
  875. {formik.values.address1}
  876. </Typography>
  877. {formik.values.address2 != null ?
  878. <Typography variant="h5" id="preview-address2-signup">
  879. {formik.values.address2}
  880. </Typography>
  881. : null}
  882. {formik.values.address3 != null ?
  883. <Typography variant="h5" id="preview-address3-signup">
  884. {formik.values.address3}
  885. </Typography>
  886. : null}
  887. {selectedAddress5 == ("香港") ?
  888. <Stack direction="row">
  889. <Typography variant="h5" color={theme.palette.grey[600]} id="preview-address4-signup">
  890. 區域 (只適用於香港):
  891. </Typography>
  892. <Typography variant="h5">{selectedAddress4}</Typography>
  893. </Stack>
  894. : null}
  895. <Stack direction="row">
  896. <Typography variant="h5" color={theme.palette.grey[600]} id="preview-address5-signup">
  897. 國家/地區:
  898. </Typography>
  899. <Typography variant="h5">{selectedAddress5}</Typography>
  900. </Stack>
  901. </Stack>
  902. </Stack>
  903. </Grid>
  904. <Grid item xs={12} mt={1} mb={1}>
  905. <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
  906. <Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>你的聯絡資料</Typography>
  907. </Stack>
  908. </Grid>
  909. <Grid item xs={12} md={12}>
  910. <Stack spacing={1} direction="row">
  911. <Typography variant="h5" color={theme.palette.grey[600]}>
  912. 電郵:
  913. </Typography>
  914. <Typography variant="h5" id="preview-email-signup">
  915. {formik.values.email}
  916. </Typography>
  917. </Stack>
  918. </Grid>
  919. <Grid item xs={12} md={6}>
  920. <Stack spacing={1} direction="row">
  921. <Typography variant="h5" color={theme.palette.grey[600]}>
  922. 聯絡電話:
  923. </Typography>
  924. <Typography variant="h5" id="preview-phone-signup">
  925. +{formik.values.phoneCountryCode} {formik.values.phone}
  926. </Typography>
  927. </Stack>
  928. </Grid>
  929. {formik.values.faxCountryCode != "" && formik.values.fax != "" ?
  930. <Grid item xs={12} md={6}>
  931. <Stack spacing={1} direction="row">
  932. <Typography variant="h5" color={theme.palette.grey[600]}>
  933. 傳真號碼:
  934. </Typography>
  935. <Typography variant="h5" id="preview-fax-signup">
  936. +{formik.values.faxCountryCode} {formik.values.fax}
  937. </Typography>
  938. </Stack>
  939. </Grid>
  940. : null}
  941. </Grid>
  942. </Grid>
  943. </Grid>
  944. </FormGroup>
  945. {/* Submit page */}
  946. <FormGroup id={"submitForm"} sx={{ display: props.step === 2 ? "" : "none" }}>
  947. <Grid container spacing={3}>
  948. {isLoading ?
  949. <LoadingComponent /> :
  950. <Grid item xs={12}>
  951. {checkUpload ?
  952. // SUCCESS page
  953. <Stack mt={1} direction="column" justifyContent="flex-start" alignItems="center" spacing={2}>
  954. <CheckCircleOutlineIcon color="success" sx={{ width: "200px", height: "200px" }} />
  955. <Typography display="inline" variant="h4">帳戶申請已成功提交。</Typography>
  956. <Typography display="inline" variant="h4">驗證電郵將發送到你的電郵地址,請依指示完成驗證及登入系統。</Typography>
  957. <Button variant="outlined" component={Link} to="/login" sx={{ fontSize: 20, height: '60px' }}><Typography variant="h5">返回登入頁面</Typography></Button>
  958. </Stack>
  959. :
  960. // ERROR page
  961. <Stack mt={1} direction="column" justifyContent="flex-start" alignItems="center" spacing={2}>
  962. {/* <Button disabled={true} hidden={true} variant="contained" type="submit" sx={{ fontSize: 12,height:'25px'}}>Submit</Button> */}
  963. <CancelOutlinedIcon color="error" sx={{ width: "200px", height: "200px" }} />
  964. <Typography display="inline" variant="h4">申請失敗,請稍後嘗試</Typography>
  965. <Button color="error" variant="outlined" component={Link} to="/login" sx={{ fontSize: 20, height: '60px' }}><Typography variant="h5">返回登入頁面</Typography></Button>
  966. </Stack>
  967. }
  968. </Grid>
  969. }
  970. </Grid>
  971. </FormGroup>
  972. </form>
  973. </FormikProvider>
  974. );
  975. }
  976. export default CustomFormWizard;