Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

397 Zeilen
14 KiB

  1. import React, {
  2. useEffect,
  3. useState,
  4. lazy,
  5. // useContext
  6. } from 'react';
  7. import { Link as RouterLink } from 'react-router-dom';
  8. import { useNavigate } from 'react-router-dom';
  9. import { useForm, } from 'react-hook-form'
  10. import { iAmSmartPath, clientId, getBowserType, isAppBowser, iAmSmartCallbackPath } from 'auth/utils'
  11. //iAmSmartAppPath
  12. // material-ui
  13. import {
  14. Button,
  15. //Checkbox,
  16. //Divider,
  17. //FormControlLabel,
  18. FormHelperText,
  19. Grid,
  20. Link,
  21. IconButton,
  22. InputAdornment,
  23. InputLabel,
  24. OutlinedInput,
  25. Stack,
  26. Typography
  27. } from '@mui/material';
  28. // third party
  29. import * as yup from 'yup';
  30. import { useFormik, FormikProvider } from 'formik';
  31. // project import
  32. //import FirebaseSocial from './FirebaseSocial';
  33. import AnimateButton from 'components/@extended/AnimateButton';
  34. import Loadable from 'components/Loadable';
  35. const PasswordAlertDialog = Loadable(lazy(() => import('./PasswordAlertDialog')));
  36. // assets
  37. import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
  38. // import axios from "axios";
  39. import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png';
  40. import { useDispatch } from "react-redux";
  41. import { handleLogin } from "auth/index";
  42. import useJwt from "auth/jwt/useJwt";
  43. import { handleLogoutFunction } from 'auth/index';
  44. import {FormattedMessage, useIntl} from "react-intl";
  45. // import LocaleContext from "components/I18nProvider";
  46. // ============================|| FIREBASE - LOGIN ||============================ //
  47. const AuthLoginCustom = () => {
  48. const dispatch = useDispatch()
  49. const navigate = useNavigate()
  50. const intl = useIntl();
  51. // const { setLocaleFromLogin } = useContext(LocaleContext);
  52. const [showPassword, setShowPassword] = useState(false);
  53. const handleClickShowPassword = () => {
  54. setShowPassword(!showPassword);
  55. };
  56. // let [posts, setPosts] = useState([]);
  57. const [isValid, setisValid] = useState(false);
  58. const [open, setOpen] = React.useState(false);
  59. const [isButtonDisabled, setIsButtonDisabled] = useState(true);
  60. const [errorMassage, setErrorMassage] = useState('');
  61. const handleMouseDownPassword = (event) => {
  62. event.preventDefault();
  63. };
  64. const tryLogin = () => {
  65. if (isValid) {
  66. dispatch(handleLogoutFunction());
  67. // setSumitting(true)
  68. useJwt
  69. .login({ username: values.username, password: values.password })
  70. .then((response) => {
  71. // console.log(response)
  72. const userData = {
  73. id: response.data.id,
  74. fullenName: response.data.name,
  75. fullchName: response.data.chName,
  76. email: response.data.email,
  77. type: response.data.type,
  78. role: response.data.role,
  79. abilities: response.data.abilities,
  80. creditor: response.data.creditor,
  81. locale: response.data.preferLocale,
  82. //avatar: require('src/assets/images/users/avatar-3.png').default,
  83. }
  84. const data = { ...userData, accessToken: response.data.accessToken, refreshToken: response.data.refreshToken }
  85. // setSuccess(true)
  86. // console.log(response.data);
  87. if(response.data.type === "GLD"){
  88. // setLocale("en");
  89. localStorage.setItem('locale','en');
  90. }else{
  91. if (response.data.preferLocale ==="zh_HK"){
  92. // setLocale("zh-HK");
  93. localStorage.setItem('locale','zh-HK');
  94. }
  95. if (response.data.preferLocale ==="zh-CN"){
  96. // setLocale("zh-CN");
  97. localStorage.setItem('locale','zh-CN');
  98. }
  99. }
  100. dispatch(handleLogin(data))
  101. navigate('/dashboard');
  102. location.reload()
  103. // setSumitting(false)
  104. })
  105. .catch((error) => {
  106. // setSuccess(false)
  107. console.error(error)
  108. console.error(error.response.data.error)
  109. setErrorMassage(error.response.data.error)
  110. setOpen(true)
  111. });
  112. } else {
  113. setOpen(true)
  114. }
  115. }
  116. const formik = useFormik({
  117. initialValues: ({
  118. username: '',
  119. password: '',
  120. submit: null
  121. }),
  122. validationSchema: yup.object().shape({
  123. // username: yup.string().min(6,'用戶名稱最少6位').required('請輸入用戶名稱'),
  124. username: yup.string().required(intl.formatMessage({id: 'requireUsername'})),
  125. password: yup.string().min(8, intl.formatMessage({id: 'atLeast8CharPassword'})).required(intl.formatMessage({id: 'requirePassword'}))
  126. .matches(/^(?=.*[a-z])/, intl.formatMessage({id: 'atLeastOneSmallLetter'}))
  127. .matches(/^(?=.*[A-Z])/, intl.formatMessage({id: 'atLeastOneCapLetter'}))
  128. .matches(/^(?=.*[0-9])/, intl.formatMessage({id: 'atLeast1Number'}))
  129. .matches(/^(?=.*[!@#%&])/, intl.formatMessage({id: 'atLeast1SpecialChar'})),
  130. }),
  131. });
  132. const checkDataField = (data) => {
  133. if (data.username !== "" &&
  134. data.password !== "" &&
  135. handlePassword(data.password)
  136. // &&handle6Digi(data.username)
  137. ) {
  138. setisValid(true)
  139. setIsButtonDisabled(false);
  140. return isValid
  141. } else {
  142. setisValid(false)
  143. setIsButtonDisabled(true);
  144. return isValid
  145. }
  146. };
  147. function handlePassword(password) {
  148. let new_pass = password;
  149. // regular expressions to validate password
  150. var lowerCase = /[a-z]/g;
  151. var upperCase = /[A-Z]/g;
  152. var numbers = /[0-9]/g;
  153. var symbol = /^(?=.*[!@#%&])/;
  154. if (!new_pass.match(lowerCase)) {
  155. return false;
  156. } else if (!new_pass.match(upperCase)) {
  157. return false;
  158. } else if (!new_pass.match(numbers)) {
  159. return false;
  160. } else if (!new_pass.match(symbol)) {
  161. return false;
  162. } else if (new_pass.length < 8) {
  163. return false;
  164. } else {
  165. return true;
  166. }
  167. }
  168. const handleClose = () => {
  169. setOpen(false);
  170. };
  171. const { values } = formik
  172. useEffect(() => {
  173. checkDataField(values)
  174. }, [values])
  175. const { handleSubmit } = useForm({})
  176. const getQRWithIAmSmart = () => {
  177. if (isAppBowser()) {
  178. openApp();
  179. } else {
  180. openQR();
  181. }
  182. }
  183. const openQR = () => {
  184. let callbackUrl = "https://" + iAmSmartCallbackPath() + "/iamsmart/authcallback";
  185. let url = iAmSmartPath + "/api/v1/auth/getQR"
  186. + "?clientID=" + clientId
  187. + "&responseType=code"
  188. + "&source=" + getBowserType()
  189. + "&redirectURI=" + encodeURIComponent(callbackUrl)
  190. + "&scope=" + encodeURIComponent("eidapi_auth eidapi_profiles")
  191. + "&lang=zh-HK"//en-US, zh-HK, or zh-CN
  192. //+"&state="
  193. + "&brokerPage=false"
  194. window.location=url;
  195. }
  196. const openApp = () => {
  197. // setTimeout(function () {
  198. // openQR();
  199. // }, 1000);
  200. // let callbackUrl = "https://" + iAmSmartCallbackPath() + "/iamsmart/authcallback";
  201. // let source = getBowserType()
  202. // console.log(source)
  203. // let url = iAmSmartAppPath + "auth"
  204. // + "?clientID=" + clientId
  205. // + "&responseType=code"
  206. // + "&source=" + getBowserType()
  207. // + "&redirectURI=" + encodeURIComponent(callbackUrl)
  208. // + "&scope=" + encodeURIComponent("eidapi_auth eidapi_profiles")
  209. // + "&lang=zh-HK"//en-US, zh-HK, or zh-CN
  210. // //+"&state="
  211. // + "&brokerPage=true"
  212. // window.location=url;
  213. let callbackUrl = "https://" + iAmSmartCallbackPath() + "/iamsmart/authcallback";
  214. let url = iAmSmartPath + "/api/v1/auth/getQR"
  215. + "?clientID=" + clientId
  216. + "&responseType=code"
  217. + "&source=" + getBowserType()
  218. + "&redirectURI=" + encodeURIComponent(callbackUrl)
  219. + "&scope=" + encodeURIComponent("eidapi_auth eidapi_profiles")
  220. + "&lang=zh-HK"//en-US, zh-HK, or zh-CN
  221. //+"&state="
  222. + "&brokerPage=true"
  223. window.location=url;
  224. }
  225. return (
  226. <FormikProvider value={formik}>
  227. <form onSubmit={handleSubmit(tryLogin)}>
  228. <Grid container spacing={3}>
  229. <Grid item xs={12}>
  230. <Stack spacing={1}>
  231. <InputLabel htmlFor="email-login">
  232. <Typography variant="h5">
  233. <FormattedMessage id="userLoginName"/>
  234. </Typography>
  235. </InputLabel>
  236. <OutlinedInput
  237. id="username"
  238. name="username"
  239. onChange={formik.handleChange}
  240. placeholder=""
  241. fullWidth
  242. value={formik.values.username}
  243. error={Boolean(formik.touched.username && formik.errors.username)}
  244. onBlur={formik.handleBlur}
  245. inputProps={{
  246. maxLength: 50,
  247. onKeyDown: (e) => {
  248. if (e.key === 'Enter') {
  249. e.preventDefault();
  250. }
  251. },
  252. }}
  253. />
  254. {formik.touched.username && formik.errors.username && (
  255. <FormHelperText error id="standard-weight-helper-text-username-login">
  256. {formik.errors.username}
  257. </FormHelperText>
  258. )}
  259. </Stack>
  260. </Grid>
  261. <Grid item xs={12}>
  262. <Stack spacing={1}>
  263. <InputLabel htmlFor="password-login"><Typography variant="h5">
  264. <FormattedMessage id="userPassword"/>
  265. </Typography></InputLabel>
  266. <OutlinedInput
  267. fullWidth
  268. id="password-login"
  269. type={showPassword ? 'text' : 'password'}
  270. name="password"
  271. value={formik.values.password}
  272. onChange={formik.handleChange}
  273. onBlur={formik.handleBlur}
  274. error={Boolean(formik.touched.password && formik.errors.password)}
  275. endAdornment={
  276. <InputAdornment position="end">
  277. <IconButton
  278. aria-label="toggle password visibility"
  279. onClick={handleClickShowPassword}
  280. onMouseDown={handleMouseDownPassword}
  281. edge="end"
  282. size="large"
  283. >
  284. {showPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />}
  285. </IconButton>
  286. </InputAdornment>
  287. }
  288. placeholder=""
  289. />
  290. {formik.touched.password && formik.errors.password && (
  291. <FormHelperText error id="standard-weight-helper-text-password-login">
  292. {formik.errors.password}
  293. </FormHelperText>
  294. )}
  295. </Stack>
  296. </Grid>
  297. <Grid item xs={12}>
  298. <Grid container>
  299. <Grid item xs={12}>
  300. <AnimateButton>
  301. <Button disableElevation disabled={isButtonDisabled}
  302. fullWidth size="large" type="submit" variant="contained" color="primary"
  303. sx={{
  304. "&.Mui-disabled": {
  305. background: "#bbdefb",
  306. color: "#fff",
  307. border: "2px solid",
  308. borderColor: "#e7e7e7"
  309. }
  310. }}>
  311. <Typography variant="h5">
  312. <FormattedMessage id="login"/>
  313. </Typography>
  314. </Button>
  315. </AnimateButton>
  316. </Grid>
  317. <Grid item xs={12}>
  318. <Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={1}>
  319. <Link component={RouterLink} to="/forgot/password" color="primary" sx={{textDecoration:"none"}}>
  320. <Typography align="center" variant="h7">
  321. <FormattedMessage id="forgotUserPassword"/>?
  322. </Typography>
  323. </Link>
  324. <Typography align="center" variant="h7">
  325. |
  326. </Typography>
  327. <Link component={RouterLink} to="/forgot/username" color="primary" sx={{textDecoration:"none"}}>
  328. <Typography align="center" variant="h7">
  329. <FormattedMessage id="forgotUsername"/>?
  330. </Typography>
  331. </Link>
  332. </Stack>
  333. </Grid>
  334. </Grid>
  335. </Grid>
  336. <Grid item xs={12}>
  337. <Grid container>
  338. <Grid item xs={12}>
  339. <Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
  340. <Button onClick={() => getQRWithIAmSmart()} sx={{textTransform: 'none'}} color="iAmSmart" fullWidth size="large" variant="outlined" startIcon={<img src={iAmSmartICon} alt="iAM Smart" width="30" />}>
  341. <Typography variant="h5">
  342. <FormattedMessage id="iAmSmartLogin"/>
  343. </Typography>
  344. </Button>
  345. </Stack>
  346. </Grid>
  347. <Grid item xs={12}>
  348. <Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
  349. <Link href="https://www.iamsmart.gov.hk/tc/" color="primary" sx={{textDecoration:"none"}}>
  350. <Typography align="center" variant="h7">
  351. { intl.formatMessage({id: 'learnMore'})+" >"}
  352. </Typography>
  353. </Link>
  354. </Stack>
  355. </Grid>
  356. </Grid>
  357. </Grid>
  358. <Grid item xs={12}>
  359. <Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
  360. <Button fullWidth size="large" variant="outlined" href="/register" ><Typography variant="h5">
  361. <FormattedMessage id="createOrReActivate"/>
  362. </Typography></Button>
  363. </Stack>
  364. </Grid>
  365. </Grid>
  366. <PasswordAlertDialog open={open} handleClose={handleClose} errorMassage={errorMassage} />
  367. </form>
  368. </FormikProvider>
  369. );
  370. };
  371. export default AuthLoginCustom;