Browse Source

Merge branch 'master' into iAmSmart

# Conflicts:
#	src/routes/LoginRoutes.js
master
Anna Ho 1 year ago
parent
commit
3da91d4232
21 changed files with 252 additions and 103 deletions
  1. +5
    -5
      src/assets/style/navbarStyles.css
  2. +15
    -2
      src/assets/style/styles.css
  3. +30
    -1
      src/auth/utils.js
  4. +1
    -0
      src/index.js
  5. +2
    -2
      src/layout/MainLayout/Header/HeaderContent/Profile/index.js
  6. +14
    -10
      src/pages/Proof/Create_FromApp/ProofForm.js
  7. +5
    -4
      src/pages/Proof/Reply_Public/ApplicationDetails.js
  8. +3
    -3
      src/pages/Proof/Search_GLD/SearchForm.js
  9. +18
    -18
      src/pages/Proof/Search_Public/DataGrid.js
  10. +2
    -2
      src/pages/Proof/Search_Public/SearchForm.js
  11. +1
    -1
      src/pages/PublicNotice/ApplyForm/PublicNoticeApplyForm.js
  12. +8
    -5
      src/pages/PublicNotice/Details_Public/ApplicationDetailCard.js
  13. +1
    -1
      src/pages/User/SearchPage/UserSearchForm.js
  14. +1
    -1
      src/pages/User/SearchPage_Individual/UserSearchForm_Individual.js
  15. +2
    -2
      src/pages/authentication/AuthWrapper.js
  16. +24
    -14
      src/pages/authentication/RegisterCustom.js
  17. +74
    -0
      src/pages/authentication/Verify.js
  18. +35
    -31
      src/pages/authentication/auth-forms/PasswordAlertDialog.js
  19. +9
    -0
      src/routes/LoginRoutes.js
  20. +1
    -1
      src/themes/index.js
  21. +1
    -0
      src/utils/ApiPathConst.js

+ 5
- 5
src/assets/style/navbarStyles.css View File

@@ -26,7 +26,7 @@
text-decoration: none; text-decoration: none;
font-size: 1.2rem; font-size: 1.2rem;
font-weight: 600; font-weight: 600;
font-family: 微軟正黑體;
/* font-family: 微軟正黑體; */
color: black; color: black;
transition: 0.3s ease-in-out; transition: 0.3s ease-in-out;
} }
@@ -76,7 +76,7 @@
font-weight: 600; font-weight: 600;
color: #0C489E; color: #0C489E;
transition: 0.3s ease-in-out; transition: 0.3s ease-in-out;
font-family: 微軟正黑體;
/* font-family: 微軟正黑體; */
text-align: center; text-align: center;
} }
#mobileTitle{ #mobileTitle{
@@ -85,13 +85,13 @@
font-weight: 600; font-weight: 600;
color: #0C489E; color: #0C489E;
transition: 0.3s ease-in-out; transition: 0.3s ease-in-out;
font-family: 微軟正黑體;
/* font-family: 微軟正黑體; */
text-align: center; text-align: center;
} }
#sidebar{ #sidebar{
font-size: 1.3rem; font-size: 1.3rem;
font-weight: 600; font-weight: 600;
font-family: 微軟正黑體;
/* font-family: 微軟正黑體; */
} }
#sidebartop{ #sidebartop{
align-items: center; align-items: center;
@@ -115,7 +115,7 @@
text-decoration: none; text-decoration: none;
font-size: 1.3rem; font-size: 1.3rem;
font-weight: 600; font-weight: 600;
font-family: 微軟正黑體;
/* font-family: 微軟正黑體; */
color: black; color: black;
transition: 0.3s ease-in-out; transition: 0.3s ease-in-out;
} }


+ 15
- 2
src/assets/style/styles.css View File

@@ -1,7 +1,20 @@
body{
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+HK|Noto+Sans+SC&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap');

html,
body,
#root,
.app,
.content {
height: 100%;
width: 100%;
font-family: "Noto Sans HK", "Noto Sans SC";
}

/* body{
padding-top: 53px; padding-top: 53px;
font-family: 微軟正黑體; font-family: 微軟正黑體;
}
} */
/* main{ /* main{
padding-top: 43px; padding-top: 43px;
} */ } */

+ 30
- 1
src/auth/utils.js View File

@@ -13,9 +13,38 @@ export const hostPath = `http://${hostname}:${hostPort}`;
export const apiPath = `${hostPath}/api`; export const apiPath = `${hostPath}/api`;
// export const apiPath = `/api`; // export const apiPath = `/api`;
export const paymentPath = `http://pnspsdev.gld.gov.hk/payment`; export const paymentPath = `http://pnspsdev.gld.gov.hk/payment`;

export const iAmSmartPath = `https://apigw-isit.staging-eid.gov.hk`; export const iAmSmartPath = `https://apigw-isit.staging-eid.gov.hk`;
export const clientId = "cf61fa7c121e4869966f69c8694b1cd2";
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))


// clientId : cf61fa7c121e4869966f69c8694b1cd2
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";
}








+ 1
- 0
src/index.js View File

@@ -1,6 +1,7 @@
import { StrictMode } from 'react'; import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
import "./assets/style/styles.css"


// scroll bar // scroll bar
import 'simplebar/src/simplebar.css'; import 'simplebar/src/simplebar.css';


+ 2
- 2
src/layout/MainLayout/Header/HeaderContent/Profile/index.js View File

@@ -109,7 +109,7 @@ const Profile = () => {
> >
<Stack direction="row" alignItems="center" sx={{ p: 0.5 }}> <Stack direction="row" alignItems="center" sx={{ p: 0.5 }}>
<AccountCircleIcon style={{ color: 'black' }} alt="profile user" src={avatar1} sx={{ width: 32, height: 32 }} /> <AccountCircleIcon style={{ color: 'black' }} alt="profile user" src={avatar1} sx={{ width: 32, height: 32 }} />
<Typography style={{ color: 'black',fontFamily: "微軟正黑體", fontSize: "1.2rem" }} variant="subtitle1">{userData == null ? "" : (userData.fullenName?userData.fullenName: userData.fullchName)}</Typography>
<Typography style={{ color: 'black', fontSize: "1.2rem" }} variant="subtitle1">{userData == null ? "" : (userData.fullenName?userData.fullenName: userData.fullchName)}</Typography>
</Stack> </Stack>
</ButtonBase> </ButtonBase>
<Popper <Popper
@@ -152,7 +152,7 @@ const Profile = () => {
<Stack direction="row" spacing={1.25} alignItems="center"> <Stack direction="row" spacing={1.25} alignItems="center">
<AccountCircleIcon style={{ color: 'black' }} alt="profile user" src={avatar1} sx={{ width: 32, height: 32 }} /> <AccountCircleIcon style={{ color: 'black' }} alt="profile user" src={avatar1} sx={{ width: 32, height: 32 }} />
<Stack> <Stack>
<Typography style={{ color: 'black',fontFamily: "微軟正黑體", fontSize: "1.2rem" }} variant="subtitle1">{userData == null ? "" : (userData.fullenName?userData.fullenName: userData.fullchName)}</Typography>
<Typography style={{ color: 'black', fontSize: "1.2rem" }} variant="subtitle1">{userData == null ? "" : (userData.fullenName?userData.fullenName: userData.fullchName)}</Typography>
{/* <Typography variant="body2" color="textSecondary"> {/* <Typography variant="body2" color="textSecondary">
{userData == null ? "" : userData.fullenName} {userData == null ? "" : userData.fullenName}
</Typography> */} </Typography> */}


+ 14
- 10
src/pages/Proof/Create_FromApp/ProofForm.js View File

@@ -10,6 +10,7 @@ import {
FormLabel, FormLabel,
Button Button
} from '@mui/material'; } from '@mui/material';

import * as UrlUtils from "utils/ApiPathConst"; import * as UrlUtils from "utils/ApiPathConst";
import * as HttpUtils from "utils/HttpUtils"; import * as HttpUtils from "utils/HttpUtils";
import MainCard from "components/MainCard"; import MainCard from "components/MainCard";
@@ -20,8 +21,6 @@ import { useNavigate } from "react-router-dom";
import Loadable from 'components/Loadable'; import Loadable from 'components/Loadable';
import { notifySaveSuccess } from 'utils/CommonFunction'; import { notifySaveSuccess } from 'utils/CommonFunction';
const UploadFileTable = Loadable(React.lazy(() => import('./UploadFileTable'))); const UploadFileTable = Loadable(React.lazy(() => import('./UploadFileTable')));
// ==============================|| DASHBOARD - DEFAULT ||============================== //



const FormPanel = ({ formData }) => { const FormPanel = ({ formData }) => {


@@ -239,12 +238,13 @@ const FormPanel = ({ formData }) => {
size="small" size="small"
type="text" type="text"
onChange={(event) => { onChange={(event) => {
const value = event.target.value;
formik.setFieldValue("length", value);
formik.setFieldValue("fee", 6552 * value);
const re = /^[0-9\b]+$/;
if (event.target.value === '' || re.test(event.target.value)) {
const value = event.target.value;
formik.setFieldValue("length", value);
formik.setFieldValue("fee", 6552 * value);
}
}} }}
name="noOfPages"
value={formik.values["noOfPages"]}
variant="outlined" variant="outlined"
sx={ sx={
{ {
@@ -275,9 +275,13 @@ const FormPanel = ({ formData }) => {
size="small" size="small"
type="text" type="text"
onChange={(event) => { onChange={(event) => {
const value = event.target.value;
formik.setFieldValue("length", value);
formik.setFieldValue("fee", columnPrice.value * value);
const re = /^[0-9\b]+$/;
if (event.target.value === '' || re.test(event.target.value)) {
const value = event.target.value;
formik.setFieldValue("length", value);
formik.setFieldValue("fee", columnPrice.value * value);
}
}} }}
name="length" name="length"
value={formik.values["length"]} value={formik.values["length"]}


+ 5
- 4
src/pages/Proof/Reply_Public/ApplicationDetails.js View File

@@ -204,19 +204,20 @@ const ApplicationDetailCard = ({ formData, }) => {
<Grid container alignItems={"center"}> <Grid container alignItems={"center"}>
<Grid item xs={12} md={12} lg={12} <Grid item xs={12} md={12} lg={12}
sx={{ display: 'flex', alignItems: 'center' }}> sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="h5">繳費及返稿最後限期:</Typography>
<Typography variant="h5" display="inline"><Typography variant="h5" style={{color:'red'}} display="inline">繳費及</Typography>返稿最後限期:</Typography>
</Grid> </Grid>
<Grid item xs={12} md={12} lg={12} sx={{ mb: 4, display: 'flex', alignItems: 'center' }}> <Grid item xs={12} md={12} lg={12} sx={{ mb: 4, display: 'flex', alignItems: 'center' }}>
<Typography variant="h5">{DateUtils.dateStr_Cht(data.returnBeforeDate)} 下午 2:00前</Typography>
<Typography variant="h5">&emsp;{DateUtils.dateStr_Cht(data.returnBeforeDate)} 下午 2:00前</Typography>
</Grid> </Grid>
<Grid item xs={12} md={3} lg={3} <Grid item xs={12} md={3} lg={3}
sx={{ mb: 1, display: 'flex', alignItems: 'center' }}> sx={{ mb: 1, display: 'flex', alignItems: 'center' }}>
<Typography variant="h5">應繳費用:</Typography> <Typography variant="h5">應繳費用:</Typography>
</Grid> </Grid>
<Grid item xs={12} md={9} lg={9} sx={{ mb: 1, display: 'flex', alignItems: 'center' }}> <Grid item xs={12} md={9} lg={9} sx={{ mb: 1, display: 'flex', alignItems: 'center' }}>
<Typography variant="h5" style={{ color: "blue", fontWeight: "bold", }}>{FormatUtils.currencyFormat(data.fee)}</Typography>
<Typography variant="h2" style={{ color: "#0049B8", fontWeight: "bold", }}>{FormatUtils.currencyFormat(data.fee)}</Typography>
</Grid> </Grid>
<Grid item xs={12} md={12} lg={12} sx={{ mb: 4, display: 'flex', alignItems: 'center' }}>
<Grid item xs={0} md={3} lg={3} sx={{ mb: 4, display: 'flex', alignItems: 'center' }}></Grid>
<Grid item xs={12} md={9} lg={9} sx={{ mb: 4, display: 'flex', alignItems: 'center' }}>
{ {
formik.values.groupType == "A" formik.values.groupType == "A"
? ?


+ 3
- 3
src/pages/Proof/Search_GLD/SearchForm.js View File

@@ -91,7 +91,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
> >


<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<Grid container sx={{ backgroundColor: '#ffffff', ml: 2, mt: 3, mb: 3 }} width="98%">
<Grid container sx={{ backgroundColor: '#ffffff', ml: 2 }} width="98%">


{/*row 1*/} {/*row 1*/}
<CardContent sx={{ px: 2.5, pt: 3 }}> <CardContent sx={{ px: 2.5, pt: 3 }}>
@@ -282,7 +282,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
{/*last row*/} {/*last row*/}
<Grid container maxWidth justifyContent="flex-end"> <Grid container maxWidth justifyContent="flex-end">


<Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}>
<Grid item sx={{ ml: 3, mb: 3, mt: 3 }}>
<Button <Button
size="large" size="large"
variant="contained" variant="contained"
@@ -295,7 +295,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
</Button> </Button>
</Grid> </Grid>


<Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}>
<Grid item sx={{ ml: 3, mb: 3, mt: 3 }}>
<Button <Button
size="large" size="large"
variant="contained" variant="contained"


+ 18
- 18
src/pages/Proof/Search_Public/DataGrid.js View File

@@ -37,7 +37,8 @@ export default function SearchPublicNoticeTable({ recordList }) {
navigate('/proof/reply/' + params.row.id); navigate('/proof/reply/' + params.row.id);
}; };


const getGroupTitle = (title) => {
/*eslint no-irregular-whitespace: ["error", { "skipComments": true }]*/
/*const getGroupTitle = (title) => {
switch (title) { switch (title) {
case 'Private Bill': case 'Private Bill':
return "私人帳單"; return "私人帳單";
@@ -52,9 +53,8 @@ export default function SearchPublicNoticeTable({ recordList }) {
default: default:
return title; return title;
} }
}


}*/
const columns = [ const columns = [
{ {
field: 'actions', field: 'actions',
@@ -68,17 +68,17 @@ export default function SearchPublicNoticeTable({ recordList }) {
{ {
id: 'appId', id: 'appId',
field: 'appId', field: 'appId',
headerName: '申請編號 / 憲報編號 / 憲報期數',
headerName: '申請編號 / 憲報期數',
flex: 1, flex: 1,
renderCell: (params) => { renderCell: (params) => {
let appNo = params.row.appNo; let appNo = params.row.appNo;
let code = params.row.groupNo;
// let code = params.row.groupNo;
let isssue = params.row.issueYear let isssue = params.row.issueYear
+ " Vol. " + FormatUtils.zeroPad(params.row.issueVolume, 3) + " Vol. " + FormatUtils.zeroPad(params.row.issueVolume, 3)
+ ", No. " + FormatUtils.zeroPad(params.row.issueNo, 2) + ", No. " + FormatUtils.zeroPad(params.row.issueNo, 2)
+ ", " + DateUtils.dateFormat(params.row.issueDate, "D MMM YYYY (ddd)"); + ", " + DateUtils.dateFormat(params.row.issueDate, "D MMM YYYY (ddd)");


return <div style={{ margin: 4 }}>{appNo}<br />{code}<br />{isssue}</div>
return <div style={{ margin: 4 }}>{appNo}<br />{isssue}</div>
}, },
}, },
{ {
@@ -110,15 +110,15 @@ export default function SearchPublicNoticeTable({ recordList }) {
return params?.value ? DateUtils.datetimeStr(params?.value) : ""; return params?.value ? DateUtils.datetimeStr(params?.value) : "";
} }
}, },
{
id: 'groupTitle',
field: 'groupTitle',
headerName: '憲報類型',
flex: 1,
valueGetter: (params) => {
return getGroupTitle(params?.value);
}
},
// {
// id: 'groupTitle',
// field: 'groupTitle',
// headerName: '憲報類型',
// flex: 1,
// valueGetter: (params) => {
// return getGroupTitle(params?.value);
// }
// },
{ {
id: 'fee', id: 'fee',
field: 'fee', field: 'fee',
@@ -147,9 +147,9 @@ export default function SearchPublicNoticeTable({ recordList }) {
paginationModel: { page: 0, pageSize: 10 }, paginationModel: { page: 0, pageSize: 10 },
}, },
}} }}
getRowHeight={()=>"auto"}
getRowHeight={() => "auto"}
onRowDoubleClick={handleRowDoubleClick} onRowDoubleClick={handleRowDoubleClick}
/> />
</div> </div>
); );
}
}

+ 2
- 2
src/pages/Proof/Search_Public/SearchForm.js View File

@@ -154,7 +154,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData
/> />
</Grid> </Grid>


<Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
{/* <Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
<Autocomplete <Autocomplete
{...register("gazettGroup")} {...register("gazettGroup")}
disablePortal disablePortal
@@ -175,7 +175,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData
/> />
)} )}
/> />
</Grid>
</Grid> */}


<Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> <Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
<TextField <TextField


+ 1
- 1
src/pages/PublicNotice/ApplyForm/PublicNoticeApplyForm.js View File

@@ -90,7 +90,7 @@ const PublicNoticeApplyForm = ({ loadedData, selections }) => {
}, },
files: [attachment], files: [attachment],
onSuccess: function () { onSuccess: function () {
notifyActionSuccess('申請成功!')
notifyActionSuccess('申請提交成功!')
navigate("/publicNotice"); navigate("/publicNotice");
// location.reload(); // location.reload();
} }


+ 8
- 5
src/pages/PublicNotice/Details_Public/ApplicationDetailCard.js View File

@@ -137,12 +137,13 @@ const ApplicationDetailCard = (
variant="contained" variant="contained"
onClick={cancelledClick()} onClick={cancelledClick()}
disabled={currentApplicationDetailData.status !== "submitted"} disabled={currentApplicationDetailData.status !== "submitted"}
title={"取消"}
sx={{ sx={{
textTransform: 'capitalize', textTransform: 'capitalize',
alignItems: 'end', alignItems: 'end',
backgroundColor: '#ffa733' backgroundColor: '#ffa733'
}}> }}>
<CloseIcon />
<CloseIcon sx={{mb:0.5}}/>
<Typography ml={1} variant="h5"> 取消</Typography> <Typography ml={1} variant="h5"> 取消</Typography>
</Button> </Button>
</Stack> </Stack>
@@ -480,7 +481,7 @@ const ApplicationDetailCard = (
</Grid> </Grid>
<Grid item xs={12} md={9} lg={9} sx={{ display: 'flex', alignItems: 'center' }}> <Grid item xs={12} md={9} lg={9} sx={{ display: 'flex', alignItems: 'center' }}>
<Grid container direction="row" justifyContent="flex-start"> <Grid container direction="row" justifyContent="flex-start">
<Grid item xs={12} md={5} lg={5} sx={{ display: 'flex', alignItems: 'center' }}>
<Grid item xs={12} md={8} lg={8} sx={{ display: 'flex', alignItems: 'center' }}>
<FormControl variant="outlined" fullWidth > <FormControl variant="outlined" fullWidth >
<Typography <Typography
fullWidth fullWidth
@@ -491,16 +492,18 @@ const ApplicationDetailCard = (
</Typography> </Typography>
</FormControl> </FormControl>
</Grid> </Grid>
<Grid item md={2} lg={2}>
<Grid item md={4} lg={4}>
<Button <Button
size="small"
variant="contained" variant="contained"
onClick={onDownloadClick()} onClick={onDownloadClick()}
title="下載"
style={{display:"flex-right"}}
sx={{ sx={{
textTransform: 'capitalize', textTransform: 'capitalize',
alignItems: 'end', alignItems: 'end',
}}> }}>
<DownloadIcon />
<DownloadIcon sx={{mb: 0.5}}/>
<Typography sx={{ml: 1}} variant="h5">下載</Typography>
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>


+ 1
- 1
src/pages/User/SearchPage/UserSearchForm.js View File

@@ -41,7 +41,7 @@ const UserSearchForm = ({ applySearch }) => {


const temp = { const temp = {
username: data.userName, username: data.userName,
fullenName: data.fullenName,
enName: data.fullenName,
post: data.post, post: data.post,
email: data.email, email: data.email,
phone: data.phone, phone: data.phone,


+ 1
- 1
src/pages/User/SearchPage_Individual/UserSearchForm_Individual.js View File

@@ -29,7 +29,7 @@ const UserSearchForm_Individual = ({ applySearch }) => {


const temp = { const temp = {
username: data.userName, username: data.userName,
fullenName: data.fullenName,
fullName: data.fullenName,
email: data.email, email: data.email,
phone: data.phone, phone: data.phone,
accountFilter: accountFilter, accountFilter: accountFilter,


+ 2
- 2
src/pages/authentication/AuthWrapper.js View File

@@ -44,8 +44,8 @@ const AuthWrapper = ({ children }) => (
alignItems="center" alignItems="center"
spacing={2}> spacing={2}>
<Grid item xs={12} md={8} lg={8} xl={9} sx={{ ml: 4, mt: 3 ,display: { xs: 'none', sm: 'block' }}}> <Grid item xs={12} md={8} lg={8} xl={9} sx={{ ml: 4, mt: 3 ,display: { xs: 'none', sm: 'block' }}}>
<Typography style={{textAlign: "center",fontFamily: "微軟正黑體",fontSize: "1.8rem"}}>香港特別行政區政府</Typography>
<Typography style={{textAlign: "center",fontFamily: "微軟正黑體",fontSize: "1.8rem",fontWeight:"bold"}}>憲報</Typography>
<Typography style={{textAlign: "center",fontSize: "1.8rem"}}>香港特別行政區政府</Typography>
<Typography style={{textAlign: "center",fontSize: "1.8rem",fontWeight:"bold"}}>憲報</Typography>
</Grid> </Grid>
<Grid <Grid
item item


+ 24
- 14
src/pages/authentication/RegisterCustom.js View File

@@ -2,13 +2,14 @@


// material-ui // material-ui
import { import {
Dialog, DialogTitle, DialogContent, DialogActions,
Dialog, DialogTitle, DialogContent,
Link, Button, Card, Box, Grid Link, Button, Card, Box, Grid
} from '@mui/material'; } from '@mui/material';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png'; import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png';
import banner from 'assets/images/bg_ml.jpg'; import banner from 'assets/images/bg_ml.jpg';
import { Stack } from '../../../node_modules/@mui/material/index'; import { Stack } from '../../../node_modules/@mui/material/index';
import { iAmSmartPath, clientId, getBowerType } from 'auth/utils'


import * as React from 'react'; import * as React from 'react';


@@ -23,6 +24,19 @@ const RegisterCustom = () => {
setIsPopUp(true); setIsPopUp(true);
} }


const getQRWithIAmSmart = () => {
let url = iAmSmartPath + "/api/v1/auth/getQR"
+ "?clientID=" + clientId
+ "&responseType=code"
+"&source=" + getBowerType()
+"&redirectURI="
+"&scope=eidapi_formFilling"
+"&lang=zh-HK"//en-US, zh-HK, or zh-CN
//+"&state="
+"&brokerPage=false"
window.location.assign(url);
}



return ( return (
<Stack justifyContent="center" sx={{ minHeight: '100vh', bgcolor: 'backgroundColor.default' }}> <Stack justifyContent="center" sx={{ minHeight: '100vh', bgcolor: 'backgroundColor.default' }}>
@@ -58,7 +72,7 @@ const RegisterCustom = () => {
你可點擊「智方便」按鈕,系統會自動輸入個人資料,或自行輸入個人資料,以即時啟動憲報刊登公告帳戶。 你可點擊「智方便」按鈕,系統會自動輸入個人資料,或自行輸入個人資料,以即時啟動憲報刊登公告帳戶。
<br />如欲使用「智方便」提供個人資料,請先下載「智方便」流動應用程式並登記成為「智方便」用戶。 <br />如欲使用「智方便」提供個人資料,請先下載「智方便」流動應用程式並登記成為「智方便」用戶。
</Typography> </Typography>
<Link href="#">了解更多</Link>
<Link href="https://www.iamsmart.gov.hk/tc/">了解更多</Link>
</Box> </Box>


<Typography m={5}>或</Typography> <Typography m={5}>或</Typography>
@@ -105,29 +119,25 @@ const RegisterCustom = () => {
<Typography style={{ padding: '4px' }}> <Typography style={{ padding: '4px' }}>
- 身份證號碼 - 身份證號碼
</Typography> </Typography>
<Typography style={{ padding: '4px' }}>
- 性別
</Typography>
</Grid> </Grid>
<Grid item xs={6} > <Grid item xs={6} >
<Typography style={{ padding: '4px' }}> <Typography style={{ padding: '4px' }}>
- 中文姓名
</Typography>
<Typography style={{ padding: '4px' }}>
- 英文姓名
- 電郵地址
</Typography> </Typography>
<Typography style={{ padding: '4px' }}> <Typography style={{ padding: '4px' }}>
- 身份證號碼
- 手機號碼
</Typography> </Typography>
<Typography style={{ padding: '4px' }}> <Typography style={{ padding: '4px' }}>
- 性別
- 住宅地址
</Typography> </Typography>
</Grid> </Grid>
</Grid> </Grid>
</DialogContent> </DialogContent>
<DialogActions>
<Button onClick={() => setIsPopUp(false)}>OK</Button>
</DialogActions>
<DialogContent align="right">
<Button variant="outlined" onClick={getQRWithIAmSmart} startIcon={<img src={iAmSmartICon} alt="iAM Smart" width="30" />}><Typography variant="h5">使用「智方便」自動填表</Typography></Button>
<br />
<Link href="https://www.iamsmart.gov.hk/tc/">了解更多</Link>
</DialogContent>
</Dialog> </Dialog>
</Stack> </Stack>
); );


+ 74
- 0
src/pages/authentication/Verify.js View File

@@ -0,0 +1,74 @@
// material-ui
import { Button, Grid } from '@mui/material';
import { Link } from 'react-router-dom';
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 { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
import { GET_VERIFY_USER_ACCOUNT } from 'utils/ApiPathConst';
import Loadable from 'components/Loadable';
import { lazy } from 'react';

const LoadingComponent = Loadable(lazy(() => import('../extra-pages/LoadingComponent')));
const AuthWrapper = Loadable(lazy(() => import('./AuthWrapperCustom')));
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';

export default function Verify() {

const [isLoading, setIsLoading] = useState(true)
const [verifyState, setVerifyState] = useState(null)

const params = useParams()
const handleVerify = async () => {
const response = await axios.get(GET_VERIFY_USER_ACCOUNT, {
params: {
email: decodeURIComponent(params.email),
emailVerifyHash: decodeURIComponent(params.verifyCode)
}
})
if (response.status === 200 && response.data) {
setVerifyState(true)
} else {
setVerifyState(false)
}
setIsLoading(false)
}

let enterUseEffect = false
useEffect(() => {
if (enterUseEffect) handleVerify()
enterUseEffect = true
}, [])

return (
<Stack sx={{ width: '100%', fontSize: '2rem', paddingTop: '65px' }} alignItems="center">
<AuthWrapper>
{isLoading || verifyState == null ?
<LoadingComponent /> :
<Grid item xs={12}>
{verifyState ?
// SUCCESS page
<Stack mt={1} direction="column" justifyContent="flex-start" alignItems="center" spacing={2}>
<CheckCircleOutlineIcon color="success" sx={{ width: "200px", height: "200px" }} />
<Typography display="inline" variant="h4">帳戶已成功驗證。</Typography>
<Button variant="outlined" component={Link} to="/login" sx={{ fontSize: 20, height: '60px' }}><Typography variant="h5">返回登入頁面</Typography></Button>
</Stack>
:
// ERROR page
<Stack mt={1} direction="column" justifyContent="flex-start" alignItems="center" spacing={2}>
{/* <Button disabled={true} hidden={true} variant="contained" type="submit" sx={{ fontSize: 12,height:'25px'}}>Submit</Button> */}
<CancelOutlinedIcon color="error" sx={{ width: "200px", height: "200px" }} />
<Typography display="inline" variant="h4">驗證失敗,請聯絡相關的系統管理員協助。</Typography>
<Button color="error" variant="outlined" component={Link} to="/login" sx={{ fontSize: 20, height: '60px' }}><Typography variant="h5">返回登入頁面</Typography></Button>
</Stack>
}
</Grid>
}
</AuthWrapper>
</Stack>
)
}

+ 35
- 31
src/pages/authentication/auth-forms/PasswordAlertDialog.js View File

@@ -19,41 +19,45 @@ import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';


const PasswordAlertDialog = (props) => { const PasswordAlertDialog = (props) => {
return ( return (
<Dialog
<Dialog
open={props.open} open={props.open}
onClose={props.handleClose} onClose={props.handleClose}
aria-labelledby="alert-dialog-title" aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description" aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
<Stack mt={1} mr={4} direction="row" justifyContent="flex-start" alignItems="center" spacing={2}>
<CancelOutlinedIcon color="error" sx={{width:"35px",height:"40px"}}/>
{props.errorMassage==='ACCOUNT_LOCKED_ERROR'?
<Stack direction="column">
<Typography display="inline">
帳戶將被封鎖
</Typography>
<Typography display="inline">
帳戶連續五次登入錯誤,請與系統管理員聯絡
</Typography>
</Stack>:
<Typography display="inline">
用戶名或密碼錯誤
</Typography>
}
</Stack>
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{""}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button variant="contained" color="error" onClick={props.handleClose} autoFocus>
關閉
</Button>
</DialogActions>
</Dialog>
>
<DialogTitle id="alert-dialog-title">
<Stack mt={1} mr={4} direction="row" justifyContent="flex-start" alignItems="center" spacing={2}>
<CancelOutlinedIcon color="error" sx={{ width: "35px", height: "40px" }} />
{props.errorMassage === 'ACCOUNT_LOCKED_ERROR' ?
<Stack direction="column">
<Typography display="inline">
帳戶將被封鎖
</Typography>
<Typography display="inline">
帳戶連續五次登入錯誤,請與系統管理員聯絡
</Typography>
</Stack> :
props.errorMassage === 'ACCOUNT_VERIFIED_ERROR' ?
<Typography display="inline">
帳戶尚未驗證
</Typography> :
<Typography display="inline">
用戶名或密碼錯誤
</Typography>
}
</Stack>
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{""}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button variant="contained" color="error" onClick={props.handleClose} autoFocus>
關閉
</Button>
</DialogActions>
</Dialog>
); );
}; };




+ 9
- 0
src/routes/LoginRoutes.js View File

@@ -16,6 +16,7 @@ const IAmSmart_SuccessCallback = Loadable(lazy(() => import('pages/iAmSmart/Succ


//TODO: this page for testing only, please remove at prod //TODO: this page for testing only, please remove at prod
const TestMailPage = Loadable(lazy(() => import('pages/pnspsNotifyTest'))); const TestMailPage = Loadable(lazy(() => import('pages/pnspsNotifyTest')));
const VerifyPage = Loadable(lazy(() => import('pages/authentication/Verify')));


// ==============================|| AUTH ROUTING ||============================== // // ==============================|| AUTH ROUTING ||============================== //


@@ -54,6 +55,14 @@ const LoginRoutes = {
{ {
path: 'iamsmart/logincallback', path: 'iamsmart/logincallback',
element: <IAmSmart_SuccessCallback/> element: <IAmSmart_SuccessCallback/>
},
{
path: 'testMailPage',
element: <TestMailPage/>
},
{
path: 'verify/:verifyCode/:email',
element: <VerifyPage/>
} }
] ]
}; };


+ 1
- 1
src/themes/index.js View File

@@ -17,7 +17,7 @@ export default function ThemeCustomization({ children }) {
const theme = Palette('light', 'default'); const theme = Palette('light', 'default');


// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
const themeTypography = Typography(`微軟正黑體`);
const themeTypography = Typography(``);
const themeCustomShadows = useMemo(() => CustomShadows(theme), [theme]); const themeCustomShadows = useMemo(() => CustomShadows(theme), [theme]);


const themeOptions = useMemo( const themeOptions = useMemo(


+ 1
- 0
src/utils/ApiPathConst.js View File

@@ -45,6 +45,7 @@ export const POST_CAPTCHA = apiPath+'/captcha';
export const POST_PUBLIC_USER_REGISTER = apiPath+'/user/register'; export const POST_PUBLIC_USER_REGISTER = apiPath+'/user/register';
export const GET_USERNAME = apiPath+'/user/username'; export const GET_USERNAME = apiPath+'/user/username';
export const GET_USER_EMAIL = apiPath+'/user/email'; export const GET_USER_EMAIL = apiPath+'/user/email';
export const GET_VERIFY_USER_ACCOUNT = apiPath+'/user/verifyEmail';


//Public //Public
export const GET_PUBLIC_ORG_USER_LIST = apiPath+'/user/listOrg'; export const GET_PUBLIC_ORG_USER_LIST = apiPath+'/user/listOrg';


Loading…
Cancel
Save