| @@ -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 * 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 HttpUtils from "utils/HttpUtils"; | ||||
| import * as UrlUtils from "utils/ApiPathConst"; | 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'; | import Loadable from 'components/Loadable'; | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | 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 ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const Index = () => { | 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(() => { | React.useEffect(() => { | ||||
| if(params.code){ | |||||
| setOnReady(true); | |||||
| setProps({}); | |||||
| getPrfile(); | |||||
| } | |||||
| getPrfile(); | |||||
| }, []); | }, []); | ||||
| function 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 ( | return ( | ||||
| !onReady ? | |||||
| <LoadingComponent /> | <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 AuthRegister = Loadable(lazy(() => import('pages/authentication/RegisterCustom'))); | ||||
| const RegisterForm = Loadable(lazy(() => import('pages/authentication/Register'))); | const RegisterForm = Loadable(lazy(() => import('pages/authentication/Register'))); | ||||
| const BusRegisterForm = Loadable(lazy(() => import('pages/authentication/BusRegister'))); | 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 ErrorPage = Loadable(lazy(() => import('pages/extra-pages/ErrorPage'))); | ||||
| const IAmSmart_FailCallback = Loadable(lazy(() => import('pages/iAmSmart/FailCallback'))); | const IAmSmart_FailCallback = Loadable(lazy(() => import('pages/iAmSmart/FailCallback'))); | ||||
| const IAmSmart_SuccessCallback = Loadable(lazy(() => import('pages/iAmSmart/SuccessCallback'))); | const IAmSmart_SuccessCallback = Loadable(lazy(() => import('pages/iAmSmart/SuccessCallback'))); | ||||
| @@ -45,6 +46,10 @@ const LoginRoutes = { | |||||
| path: 'registerFromOrganization', | path: 'registerFromOrganization', | ||||
| element: <BusRegisterForm /> | element: <BusRegisterForm /> | ||||
| }, | }, | ||||
| { | |||||
| path: 'iAmSmartRegisterFrom', | |||||
| element: <IAmSmartRegister /> | |||||
| }, | |||||
| { | { | ||||
| path: 'error', | path: 'error', | ||||
| element: <ErrorPage/> | element: <ErrorPage/> | ||||