diff --git a/src/auth/utils.js b/src/auth/utils.js index 1699eb4..93a772d 100644 --- a/src/auth/utils.js +++ b/src/auth/utils.js @@ -14,6 +14,53 @@ export const apiPath = window.location.hostname.match("localhost")?`${hostPath}/ //export const apiPath = `/api`; export const paymentPath = `https://pnspsdev.gld.gov.hk/payment`; export const iAmSmartPath = `https://`; +export const clientId = "cf61fa7c121e4869966f69c8694b1cd2"; + +export const iAmSmartCallbackPath = () => { + let hostname = window.location.hostname; + if (hostname.match("localhost")) { + hostname = "pnspsuat.gld.gov.hk"; + } + return hostname; +}; + +export const getNonce = () => { + let hostname = window.location.hostname; + if (hostname.match("localhost")) { + hostname = "pnspsuat.gld.gov.hk"; + } + return hostname; +}; + +export const getBowerType = () => { + console.log(navigator.userAgent) + // const regex = /Mobi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Mi|huawei|Opera Mini|SAMSUNG|Samsung|SGH-[I|N|T]|GT-[I|N]|SM-[A|N|P|T|Z]|SHV-E|SCH-[I|J|R|S]|SPH-L/i; + // if(!regex.test(navigator.userAgent)) + + if (navigator.userAgent.indexOf("Edg") != -1) { + if (navigator.userAgent.match(/Android/i)) return "Android_Edge" + if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Edge" + return "PC_Browser" + } else if (navigator.userAgent.indexOf("Chrome") != -1) { + if (navigator.userAgent.match(/Android/i)) return "Android_Chrome" + if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Chrome" + return "PC_Browser" + } else if (navigator.userAgent.indexOf("Safari") != -1) { + if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Safari" + return "PC_Browser" + } else if (navigator.userAgent.indexOf("Firefox") != -1) { + if (navigator.userAgent.match(/Android/i)) return "Android_Firefox" + if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Firefox" + return "PC_Browser" + } else if (navigator.userAgent.match(/SAMSUNG|Samsung|SGH-[I|N|T]|GT-[I|N]|SM-[A|N|P|T|Z]|SHV-E|SCH-[I|J|R|S]|SPH-L/i)) { + return "Android_Samsung" + } else if (navigator.userAgent.match(/huawei/i)) { + return "Android_Huawei" + } else if (navigator.userAgent.match(/Mi/i)) { + return "Android_Xiaomi" + } + return "PC_Browser"; +} export const isUserLoggedIn = () => { diff --git a/src/pages/authentication/RegisterCustom.js b/src/pages/authentication/RegisterCustom.js index b478d0c..2ed113a 100644 --- a/src/pages/authentication/RegisterCustom.js +++ b/src/pages/authentication/RegisterCustom.js @@ -1,76 +1,148 @@ // material-ui -import {Link, Button, Card , Box, Grid } from '@mui/material'; +import { + Dialog, DialogTitle, DialogContent, + Link, Button, Card, Box, Grid +} from '@mui/material'; import Typography from '@mui/material/Typography'; import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png'; import banner from 'assets/images/bg_ml.jpg'; import { Stack } from '../../../node_modules/@mui/material/index'; +import { iAmSmartPath, clientId, getBowerType , iAmSmartCallbackPath} from 'auth/utils' + +import * as React from 'react'; // ================================|| LOGIN ||================================ // -const RegisterCustom = () => ( - - - banner -
- *': { - flexGrow: 1, - flexBasis: '50%' - }, - backgroundColor: "secondary" - }} - - > - - - - 立即成為憲報刊登公告用戶 - 只需4-5分鐘 - - - - - - 個人用戶 - - - - - 你可點擊「智方便」按鈕,系統會自動輸入個人資料,或自行輸入個人資料,以即時啟動憲報刊登公告帳戶。 -
如欲使用「智方便」提供個人資料,請先下載「智方便」流動應用程式並登記成為「智方便」用戶。 -
- 了解更多 -
+const RegisterCustom = () => { + + const [isPopUp, setIsPopUp] = React.useState(false); + + + const registerWithIAmSmart = () => { + setIsPopUp(true); + } + + const getQRWithIAmSmart = () => { + let callbackUrl = "https://"+iAmSmartCallbackPath()+"/iamsmart/authcallback"; + let url = iAmSmartPath + "/api/v1/auth/getQR" + + "?clientID=" + clientId + + "&responseType=code" + +"&source=" + getBowerType() + +"&redirectURI="+encodeURIComponent(callbackUrl) + +"&scope="+encodeURIComponent("eidapi_auth eidapi_profiles") + +"&lang=zh-HK"//en-US, zh-HK, or zh-CN + //+"&state=" + +"&brokerPage=false" + window.location.assign(url); + } + - + return ( + + banner +
+ *': { + flexGrow: 1, + flexBasis: '50%' + }, + backgroundColor: "secondary" + }} - + > + + + + 立即成為憲報刊登公告用戶 + 只需4-5分鐘 + + + + + + 個人用戶 + + + + + 你可點擊「智方便」按鈕,系統會自動輸入個人資料,或自行輸入個人資料,以即時啟動憲報刊登公告帳戶。 +
如欲使用「智方便」提供個人資料,請先下載「智方便」流動應用程式並登記成為「智方便」用戶。 +
+ 了解更多 +
+ + + + - - 需上載身份證明文件數碼檔案以進行網上申請。 -
如:香港身份證; 護照; 中國內地身份證; 專業執業証書等 + + 需上載身份證明文件數碼檔案以進行網上申請。 +
如:香港身份證; 護照; 中國內地身份證; 專業執業証書等 +
+
+ + 機構/公司用戶 + + + 需上載以下任何一份證明文件以進行網上申請。 +
如:商業登記證;專業執業證書 +
+
+
+
+
+
+
+ setIsPopUp(false)} > + + + + 授權「智方便」提供個人資料 + + + 為完成開戶並建立與「智方便」的連接,請授權「智方便」提供以下個人資料: + + + + + + + - 中文姓名 + + + - 英文姓名 + + + - 身份證號碼 - - 機構/公司用戶 - - - 需上載以下任何一份證明文件以進行網上申請。 -
如:商業登記證;專業執業證書 + + + - 電郵地址 + + + - 手機號碼 + + + - 住宅地址
-
-
-
-
-
+ + + +
+ 了解更多 +
+ + + ); -); +}; export default RegisterCustom; diff --git a/src/pages/iAmSmart/AuthCallback/index.js b/src/pages/iAmSmart/AuthCallback/index.js new file mode 100644 index 0000000..b626731 --- /dev/null +++ b/src/pages/iAmSmart/AuthCallback/index.js @@ -0,0 +1,185 @@ +// material-ui +import { + Grid, + Typography, + Stack, + Card, + FormHelperText, + InputLabel, OutlinedInput, +} from '@mui/material'; +import * as React from "react"; +import { useFormik, FormikProvider } from 'formik'; +import * as yup from 'yup'; +import { useParams } from "react-router-dom"; +//import { iAmSmartPath, clientId, getBowerType , iAmSmartCallbackPath} from 'auth/utils' + +import Loadable from 'components/Loadable'; +const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); + +import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' +const BackgroundHead = { + backgroundImage: `url(${titleBackgroundImg})`, + width: '100%', + height: '100%', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: '#0C489E', + backgroundPosition: 'right' +} + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + +const Index = () => { + + const params = useParams(); + const [onReady, setOnReady] = React.useState(false); + const [checkUsername, setCheckUsername] = React.useState(false); + const [props, setProps] = React.useState({}); + + React.useEffect(() => { + if(params.code){ + setOnReady(true); + setProps({}); + } + }, []); + + // function loadIAmSmartProfile(){ + + // } + + function displayErrorMsg(errorMsg) { + return {errorMsg} + } + + const formik = useFormik({ + initialValues: ({ + username: '', + enName: '', + email: '', + address1: '', + address2: '', + address3: '', + password: '', + phone: '', + phoneCountryCode: '852', + }), + + validationSchema: yup.object().shape({ + username: yup.string().min(6, displayErrorMsg('用戶名稱最少6位')).required(displayErrorMsg('請輸入用戶名稱')) + .matches(/^[aA-zZ0-9\s]+$/, { message: displayErrorMsg("用戶名稱不包含特殊字符") }) + .matches(/^\S*$/, { message: displayErrorMsg('用戶名稱不包含空格') }), + enName: yup.string().max(255).required(displayErrorMsg('請輸入英文姓名')), + chName: yup.string().max(255).required(displayErrorMsg('請輸入中文姓名')), + address1: yup.string().max(255).required(displayErrorMsg('請輸入第一行地址')), + address2: yup.string().max(255).required(displayErrorMsg('請輸入第二行地址')), + address3: yup.string().max(255).required(displayErrorMsg('請輸入第三行地址')), + email: yup.string().email(displayErrorMsg('請輸入電郵格式')).max(255).required(displayErrorMsg('請輸入電郵')), + phoneCountryCode: yup.string().min(2, displayErrorMsg('請輸入最少2位數字')).required(displayErrorMsg('請輸入國際區號')), + phone: yup.string().min(8, displayErrorMsg('請輸入最少8位數字')).required(displayErrorMsg('請輸入聯絡電話')), + }, ['username']), + + }); + + + return ( + !onReady ? + + : + + + +
+ + iAmSmart 登記 + +
+
+ {/*row 1*/} + + + *': { + flexGrow: 1, + flexBasis: '50%' + }, + backgroundColor: "secondary", + p:8, + pl:16, + pr:16 + }} + > + + + +
+ 成為個人用戶 +
+ 註有*的項目必須輸入資料 + 用戶資料 + {/* + Already have an account? + */} +
+
+ + + + + + + 用戶登入名稱 + * + + + { + setCheckUsername(false) + props.username = e.target.value + formik.handleChange(e) + }} + placeholder="用戶登入名稱" + fullWidth + error={Boolean((formik.touched.username && formik.errors.username) || checkUsername)} + onBlur={formik.handleBlur} + inputProps={{ + onKeyDown: (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + } + }, + }} + /> + {formik.touched.username && formik.errors.username && ( + + {formik.errors.username} + + )} + {checkUsername && ( + + 此用戶登入名稱已被注冊,請使用其他用戶登入名稱 + + )} + + + + + +
+
+
+
+ {/*row 2*/} +
+
+ + ); +}; + +export default Index; \ No newline at end of file diff --git a/src/pages/iAmSmart/FailCallback/index.js b/src/pages/iAmSmart/FailCallback/index.js new file mode 100644 index 0000000..e908b83 --- /dev/null +++ b/src/pages/iAmSmart/FailCallback/index.js @@ -0,0 +1,66 @@ +// material-ui +import { + Grid, + Typography, + Stack, +} from '@mui/material'; +import * as React from "react"; + +import Loadable from 'components/Loadable'; +const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); + +import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' +const BackgroundHead = { + backgroundImage: `url(${titleBackgroundImg})`, + width: '100%', + height: '100%', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: '#0C489E', + backgroundPosition: 'right' +} + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + +const Index = () => { + + const [onReady, setOnReady] = React.useState(false); + + React.useEffect(() => { + setOnReady(true); + }, []); + + return ( + !onReady ? + + : + + +
+ + iAmSmart 登入失敗 + +
+
+ {/*row 1*/} + + +
+ + + + 連接 iAM Smart 時出現錯誤,請重試。 + + + +
+
+
+ {/*row 2*/} +
+ + + ); +}; + +export default Index; \ No newline at end of file diff --git a/src/pages/iAmSmart/SuccessCallback/index.js b/src/pages/iAmSmart/SuccessCallback/index.js new file mode 100644 index 0000000..15ff2e3 --- /dev/null +++ b/src/pages/iAmSmart/SuccessCallback/index.js @@ -0,0 +1,66 @@ +// material-ui +import { + Grid, + Typography, + Stack, +} from '@mui/material'; +import * as React from "react"; + +import Loadable from 'components/Loadable'; +const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); + +import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' +const BackgroundHead = { + backgroundImage: `url(${titleBackgroundImg})`, + width: '100%', + height: '100%', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: '#0C489E', + backgroundPosition: 'right' +} + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + +const Index = () => { + + const [onReady, setOnReady] = React.useState(false); + + React.useEffect(() => { + setOnReady(true); + }, []); + + return ( + !onReady ? + + : + + +
+ + iAmSmart 成功登入 + +
+
+ {/*row 1*/} + + +
+ + + + iAmSmart 成功登入 + + + +
+
+
+ {/*row 2*/} +
+ + + ); +}; + +export default Index; diff --git a/src/routes/LoginRoutes.js b/src/routes/LoginRoutes.js index 30052b9..d0adeb1 100644 --- a/src/routes/LoginRoutes.js +++ b/src/routes/LoginRoutes.js @@ -11,6 +11,11 @@ const AuthRegister = Loadable(lazy(() => import('pages/authentication/RegisterCu const RegisterForm = Loadable(lazy(() => import('pages/authentication/Register'))); const BusRegisterForm = Loadable(lazy(() => import('pages/authentication/BusRegister'))); const ErrorPage = Loadable(lazy(() => import('pages/extra-pages/ErrorPage'))); +const IAmSmart_FailCallback = Loadable(lazy(() => import('pages/iAmSmart/FailCallback'))); +const IAmSmart_SuccessCallback = Loadable(lazy(() => import('pages/iAmSmart/SuccessCallback'))); +const IAmSmart_AuthCallback = Loadable(lazy(() => import('pages/iAmSmart/AuthCallback'))); + +//TODO: this page for testing only, please remove at prod const TestMailPage = Loadable(lazy(() => import('pages/pnspsNotifyTest'))); const VerifyPage = Loadable(lazy(() => import('pages/authentication/Verify'))); @@ -20,6 +25,10 @@ const LoginRoutes = { path: '/', element: , children: [ + {//TODO: this page for testing only, please remove at prod + path: 'testMailPage', + element: + }, { path: 'login', element: @@ -40,6 +49,18 @@ const LoginRoutes = { path: 'error', element: }, + { + path: 'iamsmart/authcallback', + element: + }, + { + path: 'iamsmart/loginfallback', + element: + }, + { + path: 'iamsmart/logincallback', + element: + }, { path: 'testMailPage', element: