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

378 regels
13 KiB

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