@@ -0,0 +1,217 @@ | |||
// import { Link } from 'react-router-dom'; | |||
import React, { | |||
useState | |||
// ,useEffect | |||
} from 'react'; | |||
// material-ui | |||
import { | |||
Stepper, | |||
Step, | |||
StepButton, | |||
// Grid, | |||
Stack, | |||
Typography, | |||
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/IAmSmartFormWizard'))); | |||
const AuthWrapper = Loadable(lazy(() => import('./AuthWrapperCustom'))); | |||
// ================================|| REGISTER ||================================ // | |||
const stepStyle = { | |||
width: "40%", | |||
boxShadow: 1, | |||
backgroundColor: "#FFFFFF", | |||
padding: 2, | |||
"& .Mui-active": { | |||
"&.MuiStepIcon-root": { | |||
color: "warning.main", | |||
fontSize: "2rem", | |||
}, | |||
"& .MuiStepConnector-line": { | |||
borderColor: "warning.main" | |||
} | |||
}, | |||
"& .Mui-completed": { | |||
"&.MuiStepIcon-root": { | |||
color: "secondary.main", | |||
fontSize: "2rem", | |||
}, | |||
"& .MuiStepConnector-line": { | |||
borderColor: "secondary.main" | |||
} | |||
} | |||
} | |||
const steps = ['個人資料', '預覽', '完成提交']; | |||
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; | |||
}; | |||
const completedSteps = () => { | |||
return Object.keys(completed).length; | |||
}; | |||
const isLastStep = () => { | |||
return activeStep === totalSteps() - 1; | |||
}; | |||
const allStepsCompleted = () => { | |||
return completedSteps() === totalSteps(); | |||
}; | |||
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 = () => { | |||
scrollToTop(); | |||
setActiveStep((prevActiveStep) => prevActiveStep - 1); | |||
}; | |||
const scrollToTop = () => { | |||
window.scrollTo(0, 0); | |||
}; | |||
const handleReset = () => { | |||
setActiveStep(0); | |||
setCompleted({}); | |||
}; | |||
return ( | |||
// <AuthWrapper> | |||
<Stack sx={{ width: '100%', fontSize: '2rem', paddingTop: '65px', bgcolor: 'backgroundColor.default' }} alignItems="center"> | |||
<Stepper activeStep={activeStep} sx={stepStyle}> | |||
{steps.map((label, index) => ( | |||
<Step key={label} completed={completed[index]} readOnly={true}> | |||
{ | |||
index < 2 ? | |||
(<StepButton | |||
// onClick={handleStep(index)} | |||
> | |||
<Typography variant="step1">{label}</Typography> | |||
</StepButton>) : | |||
(<StepButton | |||
sx={activeStep === 2 ? { "& .MuiSvgIcon-root": { color: "warning.main", fontSize: "2rem" } } : allStepsCompleted() ? { "& .MuiSvgIcon-root": { color: "secondary.main", fontSize: "2rem" } } : { color: "rgba(0, 0, 0, 0.38)" }} | |||
icon={<VisibilityIcon />} | |||
// onClick={handleStep(index)} | |||
> | |||
<Typography variant="step1">{label}</Typography> | |||
</StepButton>) | |||
} | |||
</Step> | |||
))} | |||
</Stepper> | |||
{allStepsCompleted() ? ( | |||
<React.Fragment> | |||
<Typography variant="h4" sx={{ mt: 2, mb: 1 }}> | |||
All steps completed - you're finished | |||
</Typography> | |||
<Stack direction="row" sx={{ pt: 2 }}> | |||
<Stack sx={{ flex: '1 1 auto' }} /> | |||
<Button onClick={handleReset}><Typography variant="h5">Reset</Typography></Button> | |||
</Stack> | |||
</React.Fragment> | |||
) : ( | |||
<React.Fragment> | |||
<AuthWrapper> | |||
<CustomFormWizard setUpdateValid={setUpdateValid} step={activeStep} setUsername={setUsername}/> | |||
{/* <CustomFormWizard step={activeStep} /> */} | |||
</AuthWrapper> | |||
<Stack direction="row" sx={{ pb: 2 }}> | |||
{activeStep === 2 || activeStep === 0 ? ( | |||
<Button | |||
color="inherit" | |||
disabled={true} | |||
onClick={handleBack} | |||
sx={{ mr: 1 }} | |||
> | |||
<Typography variant="h5">返回</Typography> | |||
</Button> | |||
) : ( | |||
<Button | |||
color="inherit" | |||
disabled={activeStep === 0} | |||
onClick={handleBack} | |||
sx={{ mr: 1 }} | |||
> | |||
<Typography variant="h5">返回</Typography> | |||
</Button> | |||
) | |||
} | |||
<Stack sx={{ flex: '1 1 auto' }} /> | |||
{activeStep === totalSteps() - 2 ? | |||
( | |||
<Button variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
<Typography variant="h5">提交</Typography> | |||
</Button> | |||
) : (activeStep === totalSteps() - 1 ? | |||
( | |||
<Button variant="outlined" color="inherit" | |||
disabled={true} sx={{ mr: 1 }}> | |||
<Typography variant="h5">提交</Typography> | |||
</Button> | |||
) : | |||
( | |||
// <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
<Button disabled={!updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
<Typography variant="h5">繼續</Typography> | |||
</Button> | |||
) | |||
)} | |||
{/* {activeStep !== steps.length && | |||
(completed[activeStep] ? ( | |||
<Typography variant="caption" sx={{ display: 'inline-block' }}> | |||
Step {activeStep + 1} already completed | |||
</Typography> | |||
) : ( | |||
<Button onClick={handleComplete}> | |||
{completedSteps() === totalSteps() - 1 | |||
? 'Finish' | |||
: 'Complete Step'} | |||
</Button> | |||
))} */} | |||
</Stack> | |||
</React.Fragment> | |||
)} | |||
</Stack > | |||
// </AuthWrapper> | |||
); | |||
}; | |||
export default Register; |
@@ -1,195 +1,42 @@ | |||
// 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 * as HttpUtils from "utils/HttpUtils"; | |||
import * as UrlUtils from "utils/ApiPathConst"; | |||
//import { iAmSmartPath, clientId, getBowerType , iAmSmartCallbackPath} from 'auth/utils' | |||
import { useNavigate } from "react-router-dom"; | |||
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({}); | |||
const navigate = useNavigate() | |||
React.useEffect(() => { | |||
if(params.code){ | |||
setOnReady(true); | |||
setProps({}); | |||
getPrfile(); | |||
} | |||
getPrfile(); | |||
}, []); | |||
function getPrfile(){ | |||
HttpUtils.post({ | |||
url: UrlUtils.GET_SMART_PROFILE, | |||
params:{ | |||
code: params.code | |||
}, | |||
onSuccess: () => { | |||
} | |||
}); | |||
} | |||
function displayErrorMsg(errorMsg) { | |||
return <Typography variant="errorMessage1">{errorMsg}</Typography> | |||
let params = new URLSearchParams(window.location.search) | |||
if(params.get("code")){ | |||
HttpUtils.post({ | |||
url: UrlUtils.GET_SMART_PROFILE, | |||
params:{ | |||
code: params.get("code") | |||
}, | |||
onSuccess: (responseData) => { | |||
navigate('/iAmSmartRegisterFrom', { state: { responseData: responseData } }); | |||
} | |||
}); | |||
} | |||
} | |||
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 ? | |||
<LoadingComponent /> | |||
: | |||
<FormikProvider value={formik}> | |||
<Grid container sx={{ minHeight: '110vh', backgroundColor: '#fff' }} direction="column" justifyContent="flex-start" alignItems="center" style={{ backgroundColor: "#F2F2F2" }} > | |||
<Grid item xs={12} width="100%"> | |||
<div style={BackgroundHead} width="100%"> | |||
<Stack direction="row" height='70px'> | |||
<Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>iAmSmart 登記</Typography> | |||
</Stack> | |||
</div> | |||
</Grid> | |||
{/*row 1*/} | |||
<Grid item xs={12} md={12}> | |||
<Grid container justifyContent="flex-start" alignItems="center" > | |||
<Card | |||
sx={{ | |||
maxWidth: { xs: 1, lg: 1000 }, | |||
margin: { xs: 2.5, md: 3 }, | |||
'& > *': { | |||
flexGrow: 1, | |||
flexBasis: '50%' | |||
}, | |||
backgroundColor: "secondary", | |||
p:8, | |||
pl:16, | |||
pr:16 | |||
}} | |||
> | |||
<Grid container spacing={3} > | |||
<Grid item xs={12} md={12}> | |||
<Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}> | |||
<div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}> | |||
<Typography display="inline" variant="h3" sx={{ color: '#1A4399' }}>成為個人用戶</Typography> | |||
</div> | |||
<Typography mt={0.25} variant="h6" sx={{ color: '#f10000' }}>註有*的項目必須輸入資料</Typography> | |||
<Typography mt={0.25} variant="h4" sx={{ color: 'primary.primary' }}>用戶資料</Typography> | |||
{/* <Typography component={Link} to="/login" variant="body1" sx={{ textDecoration: 'none' }} color="primary"> | |||
Already have an account? | |||
</Typography> */} | |||
</Stack> | |||
</Grid> | |||
<Grid item xs={12} md={12}> | |||
<Grid container spacing={1}> | |||
<Grid item xs={12} md={12} > | |||
<Stack spacing={1}> | |||
<InputLabel htmlFor="username-signup"> | |||
<Typography variant="h5"> | |||
用戶登入名稱 | |||
<span style={{ color: '#f10000' }}>*</span> | |||
</Typography> | |||
</InputLabel> | |||
<OutlinedInput | |||
id="username-login" | |||
type="text" | |||
value={formik.values.username.trim()} | |||
name="username" | |||
onChange={(e) => { | |||
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 && ( | |||
<FormHelperText error id="helper-text-username-signup"> | |||
{formik.errors.username} | |||
</FormHelperText> | |||
)} | |||
{checkUsername && ( | |||
<FormHelperText error id="helper-text-username-signup"> | |||
此用戶登入名稱已被注冊,請使用其他用戶登入名稱 | |||
</FormHelperText> | |||
)} | |||
</Stack> | |||
</Grid> | |||
</Grid> | |||
</Grid> | |||
</Grid> | |||
</Card> | |||
</Grid> | |||
</Grid> | |||
{/*row 2*/} | |||
</Grid > | |||
</FormikProvider> | |||
); | |||
}; | |||
@@ -10,6 +10,7 @@ const AuthLogin = Loadable(lazy(() => import('pages/authentication/Login'))); | |||
const AuthRegister = Loadable(lazy(() => import('pages/authentication/RegisterCustom'))); | |||
const RegisterForm = Loadable(lazy(() => import('pages/authentication/Register'))); | |||
const BusRegisterForm = Loadable(lazy(() => import('pages/authentication/BusRegister'))); | |||
const IAmSmartRegister = Loadable(lazy(() => import('pages/authentication/IAmSmartRegister'))); | |||
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'))); | |||
@@ -45,6 +46,10 @@ const LoginRoutes = { | |||
path: 'registerFromOrganization', | |||
element: <BusRegisterForm /> | |||
}, | |||
{ | |||
path: 'iAmSmartRegisterFrom', | |||
element: <IAmSmartRegister /> | |||
}, | |||
{ | |||
path: 'error', | |||
element: <ErrorPage/> | |||