| @@ -10,7 +10,7 @@ import { | |||
| Stack, | |||
| Typography, | |||
| Button, StepLabel, | |||
| CircularProgress, | |||
| } from '@mui/material'; | |||
| import VisibilityIcon from '@mui/icons-material/Visibility'; | |||
| @@ -32,9 +32,11 @@ const BusRegister = () => { | |||
| const [completed, setCompleted] = useState([false]); | |||
| const [updateValid, setUpdateValid] = useState(false); | |||
| const step0GuardRef = useRef(null); | |||
| const nextBusyRef = useRef(false); | |||
| const [username, setUsername] = useState("") | |||
| const [base64Url, setBase64Url] = useState("") | |||
| const [checkCode, setCheckCode] = useState("") | |||
| const [isNextBusy, setIsNextBusy] = useState(false); | |||
| const intl = useIntl(); | |||
| const steps = [ | |||
| intl.formatMessage({id: 'personalInformation'}), | |||
| @@ -98,30 +100,38 @@ const BusRegister = () => { | |||
| } | |||
| const handleNext = async () => { | |||
| if (activeStep === 0 && step0GuardRef.current) { | |||
| const step0Ok = await Promise.resolve(step0GuardRef.current()); | |||
| if (!step0Ok) { | |||
| if (nextBusyRef.current) return; | |||
| nextBusyRef.current = true; | |||
| setIsNextBusy(true); | |||
| try { | |||
| if (activeStep === 0 && step0GuardRef.current) { | |||
| const step0Ok = await Promise.resolve(step0GuardRef.current()); | |||
| if (!step0Ok) { | |||
| return; | |||
| } | |||
| } | |||
| const captchaTest = await handleCaptcha(); | |||
| if (!captchaTest) { | |||
| notifyActionError(intl.formatMessage({id: 'validVerify'})) | |||
| return; | |||
| } | |||
| } | |||
| const captchaTest = await handleCaptcha(); | |||
| if (!captchaTest) { | |||
| notifyActionError(intl.formatMessage({id: 'validVerify'})) | |||
| return; | |||
| } | |||
| const test = await handleCheckUsername() | |||
| if (test) { | |||
| notifyActionError(intl.formatMessage({id: 'usernameTaken'})) | |||
| } 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 test = await handleCheckUsername() | |||
| if (test) { | |||
| notifyActionError(intl.formatMessage({id: 'usernameTaken'})) | |||
| } 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(); | |||
| } | |||
| } finally { | |||
| nextBusyRef.current = false; | |||
| setIsNextBusy(false); | |||
| } | |||
| }; | |||
| @@ -224,7 +234,7 @@ const BusRegister = () => { | |||
| ) : ( | |||
| <Button | |||
| color="inherit" | |||
| disabled={activeStep === 0} | |||
| disabled={activeStep === 0 || isNextBusy} | |||
| onClick={handleBack} | |||
| sx={{ mr: 1 }} | |||
| variant="h5" | |||
| @@ -238,10 +248,20 @@ const BusRegister = () => { | |||
| <Stack sx={{ flex: '1 1 auto' }} /> | |||
| {activeStep === totalSteps() - 2 ? | |||
| ( | |||
| <Button variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| <Button | |||
| variant="outlined" | |||
| disabled={isNextBusy} | |||
| onClick={handleNext} | |||
| aria-busy={isNextBusy} | |||
| sx={{ mr: 1, minWidth: 120 }} | |||
| > | |||
| {isNextBusy ? ( | |||
| <CircularProgress size={22} color="inherit" aria-hidden /> | |||
| ) : ( | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| )} | |||
| </Button> | |||
| ) : (activeStep === totalSteps() - 1 ? | |||
| ( | |||
| @@ -254,7 +274,7 @@ const BusRegister = () => { | |||
| ) : | |||
| ( | |||
| // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Button disabled={!updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Button disabled={!updateValid || isNextBusy} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="continue"/> | |||
| </Typography> | |||
| @@ -13,6 +13,7 @@ import { | |||
| Stack, | |||
| Typography, | |||
| Button, | |||
| CircularProgress, | |||
| } from '@mui/material'; | |||
| import VisibilityIcon from '@mui/icons-material/Visibility'; | |||
| import { GET_ID, POST_VERIFY_CAPTCHA } from "utils/ApiPathConst"; | |||
| @@ -55,9 +56,11 @@ const Register = () => { | |||
| const [completed, setCompleted] = useState([false]); | |||
| const [updateValid, setUpdateValid] = useState(false); | |||
| const step0GuardRef = useRef(null); | |||
| const nextBusyRef = useRef(false); | |||
| const [base64Url, setBase64Url] = useState("") | |||
| const [checkCode, setCheckCode] = useState("") | |||
| const [idNo, setIdNo] = useState(""); | |||
| const [isNextBusy, setIsNextBusy] = useState(false); | |||
| const intl = useIntl(); | |||
| const steps = [ | |||
| intl.formatMessage({id: 'personalInformation'}), | |||
| @@ -103,29 +106,37 @@ const Register = () => { | |||
| notifyActionError(intl.formatMessage({id: 'iAmSmartNoIdNoMsg'})) | |||
| return; | |||
| } | |||
| if (activeStep === 0 && step0GuardRef.current) { | |||
| const step0Ok = await Promise.resolve(step0GuardRef.current()); | |||
| if (!step0Ok) { | |||
| if (nextBusyRef.current) return; | |||
| nextBusyRef.current = true; | |||
| setIsNextBusy(true); | |||
| try { | |||
| if (activeStep === 0 && step0GuardRef.current) { | |||
| const step0Ok = await Promise.resolve(step0GuardRef.current()); | |||
| if (!step0Ok) { | |||
| return; | |||
| } | |||
| } | |||
| const captchaTest = await handleCaptcha(); | |||
| if (!captchaTest) { | |||
| notifyActionError(intl.formatMessage({id: 'validVerify'})) | |||
| return; | |||
| } | |||
| } | |||
| const captchaTest = await handleCaptcha(); | |||
| if (!captchaTest) { | |||
| notifyActionError(intl.formatMessage({id: 'validVerify'})) | |||
| return; | |||
| } | |||
| const test = await handleCheckID() | |||
| if (test) { | |||
| notifyActionError(intl.formatMessage({id: 'userRegistered'})) | |||
| } 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 test = await handleCheckID() | |||
| if (test) { | |||
| notifyActionError(intl.formatMessage({id: 'userRegistered'})) | |||
| } 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(); | |||
| } | |||
| } finally { | |||
| nextBusyRef.current = false; | |||
| setIsNextBusy(false); | |||
| } | |||
| }; | |||
| @@ -214,7 +225,7 @@ const Register = () => { | |||
| ) : ( | |||
| <Button | |||
| color="inherit" | |||
| disabled={activeStep === 0} | |||
| disabled={activeStep === 0 || isNextBusy} | |||
| onClick={handleBack} | |||
| sx={{ mr: 1 }} | |||
| > | |||
| @@ -227,10 +238,20 @@ const Register = () => { | |||
| <Stack sx={{ flex: '1 1 auto' }} /> | |||
| {activeStep === totalSteps() - 2 ? | |||
| ( | |||
| <Button variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| <Button | |||
| variant="outlined" | |||
| disabled={isNextBusy} | |||
| onClick={handleNext} | |||
| aria-busy={isNextBusy} | |||
| sx={{ mr: 1, minWidth: 120 }} | |||
| > | |||
| {isNextBusy ? ( | |||
| <CircularProgress size={22} color="inherit" aria-hidden /> | |||
| ) : ( | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| )} | |||
| </Button> | |||
| ) : (activeStep === totalSteps() - 1 ? | |||
| ( | |||
| @@ -243,7 +264,7 @@ const Register = () => { | |||
| ) : | |||
| ( | |||
| // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Button disabled={!updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Button disabled={!updateValid || isNextBusy} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="continue"/> | |||
| </Typography> | |||
| @@ -13,6 +13,7 @@ import { | |||
| Stack, | |||
| Typography, | |||
| Button, StepLabel, | |||
| CircularProgress, | |||
| } from '@mui/material'; | |||
| import VisibilityIcon from '@mui/icons-material/Visibility'; | |||
| import { POST_USERNAME, POST_VERIFY_CAPTCHA } from "utils/ApiPathConst"; | |||
| @@ -36,6 +37,8 @@ const Register = () => { | |||
| const [username, setUsername] = useState(""); | |||
| const [base64Url, setBase64Url] = useState("") | |||
| const [checkCode, setCheckCode] = useState("") | |||
| const [isNextBusy, setIsNextBusy] = useState(false); | |||
| const nextBusyRef = useRef(false); | |||
| const intl = useIntl(); | |||
| // Localized document title/meta for register flow | |||
| usePageTitle("register"); | |||
| @@ -102,31 +105,39 @@ const Register = () => { | |||
| const handleNext = async () => { | |||
| if (activeStep === 0 && step0GuardRef.current) { | |||
| const step0Ok = await Promise.resolve(step0GuardRef.current()); | |||
| if (!step0Ok) { | |||
| return; | |||
| if (nextBusyRef.current) return; | |||
| nextBusyRef.current = true; | |||
| setIsNextBusy(true); | |||
| try { | |||
| if (activeStep === 0 && step0GuardRef.current) { | |||
| const step0Ok = await Promise.resolve(step0GuardRef.current()); | |||
| if (!step0Ok) { | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| const captchaTest = await handleCaptcha(); | |||
| if (!captchaTest) { | |||
| notifyActionError(intl.formatMessage({id: 'validVerify'})) | |||
| return; | |||
| } | |||
| const captchaTest = await handleCaptcha(); | |||
| if (!captchaTest) { | |||
| notifyActionError(intl.formatMessage({id: 'validVerify'})) | |||
| return; | |||
| } | |||
| const test = await handleCheckUsername() | |||
| if (test) { | |||
| notifyActionError(intl.formatMessage({id: 'usernameTaken'})) | |||
| } 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 test = await handleCheckUsername() | |||
| if (test) { | |||
| notifyActionError(intl.formatMessage({id: 'usernameTaken'})) | |||
| } 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(); | |||
| } | |||
| } finally { | |||
| nextBusyRef.current = false; | |||
| setIsNextBusy(false); | |||
| } | |||
| }; | |||
| @@ -229,7 +240,7 @@ const Register = () => { | |||
| ) : ( | |||
| <Button | |||
| color="inherit" | |||
| disabled={activeStep === 0} | |||
| disabled={activeStep === 0 || isNextBusy} | |||
| onClick={handleBack} | |||
| sx={{ mr: 1 }} | |||
| variant="h5" | |||
| @@ -243,10 +254,20 @@ const Register = () => { | |||
| <Stack sx={{ flex: '1 1 auto' }} /> | |||
| {activeStep === totalSteps() - 2 ? | |||
| ( | |||
| <Button variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| <Button | |||
| variant="outlined" | |||
| disabled={isNextBusy} | |||
| onClick={handleNext} | |||
| aria-busy={isNextBusy} | |||
| sx={{ mr: 1, minWidth: 120 }} | |||
| > | |||
| {isNextBusy ? ( | |||
| <CircularProgress size={22} color="inherit" aria-hidden /> | |||
| ) : ( | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| )} | |||
| </Button> | |||
| ) : (activeStep === totalSteps() - 1 ? | |||
| ( | |||
| @@ -259,7 +280,7 @@ const Register = () => { | |||
| ) : | |||
| ( | |||
| // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Button disabled={!updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Button disabled={!updateValid || isNextBusy} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | |||
| <Typography variant="h5"> | |||
| <FormattedMessage id="continue"/> | |||
| </Typography> | |||