@@ -74,10 +74,10 @@ function Header(props) { | |||
isGLDLoggedIn() ? | |||
<div id="adminContent"> | |||
<li> | |||
<Link className="dashboard" to='/dashboard'><Typography variant={"headerTitle1"} sx={{ml:2}}>Dashboard</Typography></Link> | |||
<Link className="dashboard" to='/dashboard'><Typography variant={"headerTitle1"} sx={{ml:1}} >Dashboard</Typography></Link> | |||
</li> | |||
<li> | |||
<Link className="application" to='/application/search'><Typography variant={"headerTitle1"} sx={{ml:2}}>Application</Typography></Link> | |||
<Link className="application" to='/application/search'><Typography variant={"headerTitle1"} sx={{ml:1}}>Application</Typography></Link> | |||
</li> | |||
<li> | |||
<Link className="proof" to='/proof/search'><Typography variant={"headerTitle1"} sx={{ml:2}}>Proof</Typography></Link> | |||
@@ -115,13 +115,13 @@ function Header(props) { | |||
<Link className="dashboard" to='/dashboard'><Typography variant={"headerTitle1"} sx={{ml:2}}>主頁</Typography></Link> | |||
</li> | |||
<li> | |||
<Link className="myDocumet" to='/publicNotice'><Typography variant={"headerTitle1"} sx={{ml:2}}>我的公共啟事</Typography></Link> | |||
<Link className="myDocumet" to='/publicNotice'><Typography variant={"headerTitle1"} sx={{ml:0.5}}>我的公共啟事</Typography></Link> | |||
</li> | |||
<li> | |||
<Link className="documentRecord" to='/proof/search'><Typography variant={"headerTitle1"} sx={{ml:2}}>校對記錄</Typography></Link> | |||
<Link className="documentRecord" to='/proof/search'><Typography variant={"headerTitle1"} sx={{ml:1.5}}>校對記錄</Typography></Link> | |||
</li> | |||
<li> | |||
<Link className="paymentRecord" to='/paymentPage/search'><Typography variant={"headerTitle1"} sx={{ml:2}}>付款記錄</Typography></Link> | |||
<Link className="paymentRecord" to='/paymentPage/search'><Typography variant={"headerTitle1"} sx={{ml:1.5}}>付款記錄</Typography></Link> | |||
</li> | |||
<li> | |||
<Link className="userSetting" to='/dashboard'><Typography variant={"headerTitle1"} sx={{ml:2}}>設定</Typography><KeyboardArrowDownIcon /></Link> | |||
@@ -165,7 +165,7 @@ const UserSearchForm = ({ applySearch }) => { | |||
textTransform: 'capitalize', | |||
alignItems: 'end' | |||
}}> | |||
<AddCircleOutlineIcon sx={{mb:0.5}}/> | |||
<AddCircleOutlineIcon sx={{ mb: 0.5 }} /> | |||
<Typography sx={{ ml: 1 }} variant="h5">New User</Typography> | |||
</Button> | |||
</Grid> | |||
@@ -17,8 +17,11 @@ import VisibilityIcon from '@mui/icons-material/Visibility'; | |||
// project import | |||
import Loadable from 'components/Loadable'; | |||
import { lazy } from 'react'; | |||
import { notifyActionError } from 'utils/CommonFunction'; | |||
const CustomFormWizard = Loadable(lazy(() => import('./auth-forms/BusCustomFormWizard'))); | |||
const AuthWrapper = Loadable(lazy(() => import('./AuthWrapperCustom'))); | |||
import axios from "axios"; | |||
import { GET_USERNAME } from "utils/ApiPathConst"; | |||
// import CustomFormWizard from './auth-forms/BusCustomFormWizard'; | |||
// import AuthWrapper from './AuthWrapperCustom'; | |||
@@ -53,6 +56,7 @@ const BusRegister = () => { | |||
const [activeStep, setActiveStep] = useState(0); | |||
const [completed, setCompleted] = useState([false]); | |||
const [updateValid, setUpdateValid] = useState(false); | |||
const [username, setUsername] = useState("") | |||
const totalSteps = () => { | |||
return steps.length; | |||
@@ -70,15 +74,29 @@ const BusRegister = () => { | |||
return completedSteps() === totalSteps(); | |||
}; | |||
const handleNext = () => { | |||
const newActiveStep = | |||
isLastStep() && !allStepsCompleted() | |||
? // It's the last step, but not all steps have been completed, | |||
// find the first step that has been completed | |||
steps.findIndex((step, i) => !(i in completed)) | |||
: activeStep + 1; | |||
setActiveStep(newActiveStep); | |||
scrollToTop(); | |||
const handleCheckUsername = async () => { | |||
const response = await axios.get(`${GET_USERNAME}`, { | |||
params: { | |||
username: username, | |||
} | |||
}) | |||
return Number(response.data[0]) === 1 | |||
} | |||
const handleNext = async () => { | |||
const test = await handleCheckUsername() | |||
if (test) { | |||
notifyActionError("此用戶登入名稱已被注冊,請使用其他用戶登入名稱") | |||
} else { | |||
const newActiveStep = | |||
isLastStep() && !allStepsCompleted() | |||
? // It's the last step, but not all steps have been completed, | |||
// find the first step that has been completed | |||
steps.findIndex((step, i) => !(i in completed)) | |||
: activeStep + 1; | |||
setActiveStep(newActiveStep); | |||
scrollToTop(); | |||
} | |||
}; | |||
const handleBack = () => { | |||
@@ -133,7 +151,7 @@ const BusRegister = () => { | |||
) : ( | |||
<React.Fragment> | |||
<AuthWrapper> | |||
<CustomFormWizard setUpdateValid={setUpdateValid} step={activeStep} /> | |||
<CustomFormWizard setUpdateValid={setUpdateValid} step={activeStep} setUsername={setUsername} /> | |||
{/* <CustomFormWizard step={activeStep} /> */} | |||
</AuthWrapper> | |||
<Stack direction="row" sx={{ pb: 2 }}> | |||
@@ -15,10 +15,13 @@ import { | |||
Button, | |||
} from '@mui/material'; | |||
import VisibilityIcon from '@mui/icons-material/Visibility'; | |||
import { GET_USERNAME } from "utils/ApiPathConst"; | |||
// project import | |||
import Loadable from 'components/Loadable'; | |||
import { lazy } from 'react'; | |||
import { notifyActionError } from 'utils/CommonFunction'; | |||
import axios from "axios"; | |||
const CustomFormWizard = Loadable(lazy(() => import('./auth-forms/CustomFormWizard'))); | |||
const AuthWrapper = Loadable(lazy(() => import('./AuthWrapperCustom'))); | |||
// ================================|| REGISTER ||================================ // | |||
@@ -52,6 +55,7 @@ const Register = () => { | |||
const [activeStep, setActiveStep] = useState(0); | |||
const [completed, setCompleted] = useState([false]); | |||
const [updateValid, setUpdateValid] = useState(false); | |||
const [username, setUsername] = useState(""); | |||
const totalSteps = () => { | |||
return steps.length; | |||
@@ -69,15 +73,29 @@ const Register = () => { | |||
return completedSteps() === totalSteps(); | |||
}; | |||
const handleNext = () => { | |||
const newActiveStep = | |||
isLastStep() && !allStepsCompleted() | |||
? // It's the last step, but not all steps have been completed, | |||
// find the first step that has been completed | |||
steps.findIndex((step, i) => !(i in completed)) | |||
: activeStep + 1; | |||
setActiveStep(newActiveStep); | |||
scrollToTop(); | |||
const handleCheckUsername = async () => { | |||
const response = await axios.get(`${GET_USERNAME}`, { | |||
params: { | |||
username: username, | |||
} | |||
}) | |||
return Number(response.data[0]) === 1 | |||
} | |||
const handleNext = async () => { | |||
const test = await handleCheckUsername() | |||
if (test) { | |||
notifyActionError("此用戶登入名稱已被注冊,請使用其他用戶登入名稱") | |||
} else { | |||
const newActiveStep = | |||
isLastStep() && !allStepsCompleted() | |||
? // It's the last step, but not all steps have been completed, | |||
// find the first step that has been completed | |||
steps.findIndex((step, i) => !(i in completed)) | |||
: activeStep + 1; | |||
setActiveStep(newActiveStep); | |||
scrollToTop(); | |||
} | |||
}; | |||
const handleBack = () => { | |||
@@ -132,7 +150,7 @@ const Register = () => { | |||
) : ( | |||
<React.Fragment> | |||
<AuthWrapper> | |||
<CustomFormWizard setUpdateValid={setUpdateValid} step={activeStep} /> | |||
<CustomFormWizard setUpdateValid={setUpdateValid} step={activeStep} setUsername={setUsername}/> | |||
{/* <CustomFormWizard step={activeStep} /> */} | |||
</AuthWrapper> | |||
<Stack direction="row" sx={{ pb: 2 }}> | |||
@@ -92,6 +92,7 @@ const BusCustomFormWizard = (props) => { | |||
const [termsAndConNotAccept, setTermsAndConNotAccept] = useState(false); | |||
const [isValid, setisValid] = useState(false); | |||
const [checkCountry, setCheckCountry] = useState(false); | |||
const [checkUsername, setCheckUsername] = useState(false); | |||
const address4ComboList = ComboData.district; | |||
const address5ComboList = ComboData.country; | |||
@@ -129,6 +130,18 @@ const BusCustomFormWizard = (props) => { | |||
}); | |||
}, []); | |||
const handleCheckUsername = async () => { | |||
if (values?.username) { | |||
const response = await axios.get(`${GET_USERNAME}`, { | |||
params: { | |||
username: values.username, | |||
} | |||
}) | |||
setCheckUsername((Number(response.data[0]) === 1)) | |||
return Number(response.data[0]) === 1 | |||
} | |||
} | |||
const onCaptchaChange = () => { | |||
HttpUtils.post({ | |||
url: POST_CAPTCHA, | |||
@@ -167,7 +180,8 @@ const BusCustomFormWizard = (props) => { | |||
handleEmail(data.email) && | |||
handlePhone(data.phone) && | |||
handleUserName(data.username) && | |||
handleCaptcha(data.captchaField) | |||
handleCaptcha(data.captchaField) && | |||
!checkUsername | |||
) { | |||
setisValid(true) | |||
return isValid | |||
@@ -528,6 +542,12 @@ const BusCustomFormWizard = (props) => { | |||
<Typography variant="h5"> | |||
用戶登入名稱 | |||
<span style={{ color: '#f10000' }}>*</span> | |||
<Button | |||
variant="contained" | |||
onClick={handleCheckUsername} | |||
sx={{ ml: 2, height: "40px" }}> | |||
<Typography variant="h6">檢查是否重覆</Typography> | |||
</Button> | |||
</Typography> | |||
</InputLabel> | |||
<OutlinedInput | |||
@@ -535,7 +555,11 @@ const BusCustomFormWizard = (props) => { | |||
type="text" | |||
value={formik.values.username.trim()} | |||
name="username" | |||
onChange={formik.handleChange} | |||
onChange={(e) => { | |||
setCheckUsername(false) | |||
props.setUsername(e.target.value) | |||
formik.handleChange(e) | |||
}} | |||
placeholder="用戶登入名稱" | |||
fullWidth | |||
error={Boolean(formik.touched.username && formik.errors.username)} | |||
@@ -553,6 +577,11 @@ const BusCustomFormWizard = (props) => { | |||
{formik.errors.username} | |||
</FormHelperText> | |||
)} | |||
{checkUsername && ( | |||
<FormHelperText error id="helper-text-username-signup"> | |||
此用戶登入名稱已被注冊,請使用其他用戶登入名稱 | |||
</FormHelperText> | |||
)} | |||
</Stack> | |||
</Grid> | |||
<Grid item xs={12} md={12}> | |||
@@ -64,7 +64,6 @@ const CustomFormWizard = (props) => { | |||
const [checkUpload, setCheckUpload] = useState(false); | |||
const [isLoading, setLoding] = useState(true); | |||
const [updateRows, setUpdateRows] = useState([]); | |||
const [userNameList, setUserNameList] = useState([]); | |||
const [captcha, setCaptcha] = useState([]); | |||
const [captchaImg, setCaptchaImage] = useState([]); | |||
@@ -94,6 +93,7 @@ const CustomFormWizard = (props) => { | |||
const [termsAndConNotAccept, setTermsAndConNotAccept] = useState(false); | |||
const [isValid, setisValid] = useState(false); | |||
const [checkCountry, setCheckCountry] = useState(false); | |||
const [checkUsername, setCheckUsername] = useState(false); | |||
const idDocTypeComboList = ComboData.idDocType; | |||
const address4ComboList = ComboData.district; | |||
@@ -111,18 +111,19 @@ const CustomFormWizard = (props) => { | |||
useEffect(() => { | |||
changePassword(''); | |||
onCaptchaChange(); | |||
axios.get(`${GET_USERNAME}`) | |||
.then((response) => { | |||
if (response.status === 200) { | |||
setUserNameList(response.data); | |||
} | |||
}, []); | |||
const handleCheckUsername = async () => { | |||
if (values?.username) { | |||
const response = await axios.get(`${GET_USERNAME}`, { | |||
params: { | |||
username: values.username, | |||
} | |||
}) | |||
.catch(error => { | |||
console.log(error); | |||
return false; | |||
}); | |||
}, []); | |||
setCheckUsername((Number(response.data[0]) === 1)) | |||
return Number(response.data[0]) === 1 | |||
} | |||
} | |||
const onCaptchaChange = () => { | |||
HttpUtils.post({ | |||
@@ -137,7 +138,6 @@ const CustomFormWizard = (props) => { | |||
}); | |||
} | |||
const checkDataField = (data) => { | |||
if (data.username !== "" && | |||
data.password !== "" && | |||
@@ -161,7 +161,8 @@ const CustomFormWizard = (props) => { | |||
handleIdNo(data.idNo, selectedIdDocType.type, data.checkDigit) && | |||
handlePhone(data.phone) && | |||
handleUsername(data.username) && | |||
handleCaptcha(data.captchaField) | |||
handleCaptcha(data.captchaField) && | |||
!checkUsername | |||
) { | |||
setisValid(true) | |||
return isValid | |||
@@ -502,25 +503,15 @@ const CustomFormWizard = (props) => { | |||
captchaField: '' | |||
}), | |||
validationSchema: yup.object().shape({ | |||
username: yup.string().min(6, displayErrorMsg('用戶名稱最少6位')).required(displayErrorMsg("請輸入用戶登入名稱")).test( | |||
"checkUserNameUsed", | |||
displayErrorMsg("此用戶登入名稱已被注冊,請使用其他用戶登入名稱"), | |||
function (value) { | |||
if (userNameList.some(item => item.username === value)) { | |||
return false | |||
} else { | |||
return true | |||
} | |||
} | |||
) | |||
.matches(/^[aA-zZ0-9\s]+$/, {message: displayErrorMsg("用戶名稱不包含特殊字符")}) | |||
.matches(/^\S*$/, {message: displayErrorMsg("用戶名稱不包含空格")}), | |||
username: yup.string().min(6, displayErrorMsg('用戶名稱最少6位')).required(displayErrorMsg("請輸入用戶登入名稱")) | |||
.matches(/^[aA-zZ0-9\s]+$/, { message: displayErrorMsg("用戶名稱不包含特殊字符") }) | |||
.matches(/^\S*$/, { message: displayErrorMsg("用戶名稱不包含空格") }), | |||
password: yup.string().min(8, displayErrorMsg('請輸入最少8位密碼')).required(displayErrorMsg('請輸入密碼')) | |||
.matches(/^\S*$/, {message: displayErrorMsg('密碼不包含空格')}) | |||
.matches(/^(?=.*[a-z])/, {message: displayErrorMsg('請包括最少1個小寫字母')}) | |||
.matches(/^(?=.*[A-Z])/, {message: displayErrorMsg('請包括最少1個大寫字母')}) | |||
.matches(/^(?=.*[0-9])/, {message: displayErrorMsg('請包括最少1個數字')}) | |||
.matches(/^(?=.*[!@#%&])/, {message: displayErrorMsg('請包括最少1個特殊字符')}), | |||
.matches(/^\S*$/, { message: displayErrorMsg('密碼不包含空格') }) | |||
.matches(/^(?=.*[a-z])/, { message: displayErrorMsg('請包括最少1個小寫字母') }) | |||
.matches(/^(?=.*[A-Z])/, { message: displayErrorMsg('請包括最少1個大寫字母') }) | |||
.matches(/^(?=.*[0-9])/, { message: displayErrorMsg('請包括最少1個數字') }) | |||
.matches(/^(?=.*[!@#%&])/, { message: displayErrorMsg('請包括最少1個特殊字符') }), | |||
confirmPassword: yup.string().min(8, displayErrorMsg('請最少輸入8位密碼')).required(displayErrorMsg('請確認密碼')).oneOf([yup.ref('password'), null], displayErrorMsg('請輸入相同密碼')), | |||
enName: yup.string().max(255).required(displayErrorMsg('請輸入英文姓名')), | |||
chName: yup.string().max(6).required(displayErrorMsg('請輸入中文姓名')), | |||
@@ -530,8 +521,8 @@ const CustomFormWizard = (props) => { | |||
email: yup.string().email(displayErrorMsg('請輸入電郵格式')).max(255).required(displayErrorMsg('請輸入電郵')), | |||
emailConfirm: yup.string().email(displayErrorMsg('請輸入電郵格式')).max(255).required(displayErrorMsg('請輸入電郵')).oneOf([yup.ref('email'), null], displayErrorMsg('請輸入相同電郵')), | |||
idNo: yup.string().required(displayErrorMsg(`請輸入${selectedIdDocInputType}號碼`)) | |||
.matches(/^[aA-zZ0-9\s]+$/, {message: displayErrorMsg(`${selectedIdDocInputType}號碼不包含特殊字符`)}) | |||
.matches(/^\S*$/, {message: displayErrorMsg(`${selectedIdDocInputType}號碼不包含空格`)}) | |||
.matches(/^[aA-zZ0-9\s]+$/, { message: displayErrorMsg(`${selectedIdDocInputType}號碼不包含特殊字符`) }) | |||
.matches(/^\S*$/, { message: displayErrorMsg(`${selectedIdDocInputType}號碼不包含空格`) }) | |||
.test('checkIDCardFormat', displayErrorMsg(`請輸入有效的${selectedIdDocInputType}號碼`), function (value) { | |||
const idDocType = selectedIdDocType.type; | |||
var pattern_HKIDv1 = /^[A-Z]{1}[0-9]{6}$/; | |||
@@ -607,6 +598,7 @@ const CustomFormWizard = (props) => { | |||
}; | |||
const { values } = formik | |||
useEffect(() => { | |||
checkDataField(values) | |||
}, [values]) | |||
@@ -635,6 +627,12 @@ const CustomFormWizard = (props) => { | |||
<Typography variant="h5"> | |||
用戶登入名稱 | |||
<span style={{ color: '#f10000' }}>*</span> | |||
<Button | |||
variant="contained" | |||
onClick={handleCheckUsername} | |||
sx={{ ml: 2, height: "40px" }}> | |||
<Typography variant="h6">檢查是否重覆</Typography> | |||
</Button> | |||
</Typography> | |||
</InputLabel> | |||
<OutlinedInput | |||
@@ -642,7 +640,11 @@ const CustomFormWizard = (props) => { | |||
type="text" | |||
value={formik.values.username.trim()} | |||
name="username" | |||
onChange={formik.handleChange} | |||
onChange={(e) => { | |||
setCheckUsername(false) | |||
props.setUsername(e.target.value) | |||
formik.handleChange(e) | |||
}} | |||
placeholder="用戶登入名稱" | |||
fullWidth | |||
error={Boolean(formik.touched.username && formik.errors.username)} | |||
@@ -660,6 +662,11 @@ const CustomFormWizard = (props) => { | |||
{formik.errors.username} | |||
</FormHelperText> | |||
)} | |||
{checkUsername && ( | |||
<FormHelperText error id="helper-text-username-signup"> | |||
此用戶登入名稱已被注冊,請使用其他用戶登入名稱 | |||
</FormHelperText> | |||
)} | |||
</Stack> | |||
</Grid> | |||
<Grid item xs={12} md={12}> | |||
@@ -0,0 +1,20 @@ | |||
// ==============================|| OVERRIDES - FormHelperText ||============================== // | |||
export default function FormHelperText() { | |||
const fontSize = "1rem" | |||
const fontWeight = 600 | |||
const lineHeight = 1 | |||
return { | |||
MuiFormHelperText: { | |||
styleOverrides: { | |||
root: { | |||
fontSize: fontSize, | |||
fontWeight: fontWeight, | |||
lineHeight: lineHeight, | |||
}, | |||
} | |||
} | |||
}; | |||
} |
@@ -20,6 +20,7 @@ import Typography from './Typography'; | |||
import Textfield from './Textfield'; | |||
import DataGrid from './DataGrid'; | |||
import FormControlLabel from './FormControlLabel'; | |||
import FormHelperText from './FormHelperText'; | |||
// ==============================|| OVERRIDES - MAIN ||============================== // | |||
@@ -32,6 +33,7 @@ export default function ComponentsOverrides(theme) { | |||
Chip(theme), | |||
DataGrid(), | |||
FormControlLabel(), | |||
FormHelperText(), | |||
IconButton(theme), | |||
InputLabel(theme), | |||
LinearProgress(), | |||
@@ -162,6 +162,18 @@ export const notifyActionSuccess = (actionMsg) => { | |||
theme: "light", | |||
})}; | |||
export const notifyActionError = (actionMsg) => { | |||
toast.error(`${actionMsg}`, { | |||
position: "bottom-right", | |||
autoClose: 5000, | |||
hideProgressBar: false, | |||
closeOnClick: true, | |||
pauseOnHover: true, | |||
draggable: true, | |||
progress: undefined, | |||
theme: "light", | |||
})}; | |||
export function prettyJson(json){ | |||
console.log(json); | |||
console.log(JSON.stringify(json, null, 2)); | |||