| import React, { useState, useRef, useEffect, useMemo } from "react"; | 
| import { useLocation } from 'react-router-dom'; | 
| import { | 
|     Box, | 
|     CircularProgress, | 
|     Typography, | 
|     Button, | 
|     TextField, | 
|     Stack, | 
|     Autocomplete, | 
|     InputAdornment, | 
|     IconButton, | 
| } from '@mui/material'; | 
| import { | 
|     useTranslate, | 
|     useLogin, | 
|     useNotify, | 
|     email as validEmail, | 
| } from 'react-admin'; | 
| import { useForm, Controller } from 'react-hook-form'; | 
| import ProviderChoices from "./ProviderChoices"; | 
| import Visibility from '@mui/icons-material/Visibility'; | 
| import VisibilityOff from '@mui/icons-material/VisibilityOff'; | 
| import { sendEmailCode, register } from '@/api/auth'; | 
|   | 
| const Register = (props) => { | 
|     const translate = useTranslate(); | 
|     const notify = useNotify(); | 
|     const login = useLogin(); | 
|     const location = useLocation(); | 
|     const { systemInfo } = props; | 
|   | 
|     const { control, watch, handleSubmit, setValue, setError, clearErrors } = useForm(); | 
|   | 
|     const email = watch('email'); | 
|     const username = watch('username'); | 
|     const password = watch('password'); | 
|     const confirmPassword = watch('confirmPassword'); | 
|     const code = watch('code'); | 
|   | 
|     const [loading, setLoading] = useState(false); | 
|     const [codeLoading, setCodeLoading] = useState(false); | 
|     const [showPassword, setShowPassword] = useState(true); | 
|     const [isCounting, setIsCounting] = useState(false); | 
|     const [countdown, setCountdown] = useState(60); | 
|   | 
|     // send code | 
|     const handleSendCode = async () => { | 
|         if (!email) { | 
|             setError("email", { | 
|                 message: translate('ra.validation.required') | 
|             }) | 
|             return; | 
|         } | 
|         const emailError = validEmail()(email); | 
|         if (emailError) { | 
|             setError("email", { | 
|                 message: translate("ra.validation.email") | 
|             }) | 
|             return; | 
|         } | 
|         clearErrors("email"); | 
|         setCodeLoading(true); | 
|         sendEmailCode({ email }).then(res => { | 
|             setCodeLoading(false); | 
|             const { code, msg, data } = res; | 
|             if (code === 200) { | 
|                 notify(msg, { type: 'success', messageArgs: { _: msg } }); | 
|   | 
|                 const timestamp = Math.floor(Date.now() / 1000); | 
|                 const expirationTime = timestamp + 60; | 
|                 localStorage.setItem('codeExpirationTime', expirationTime); | 
|   | 
|                 setIsCounting(true); | 
|                 setCountdown(60); | 
|             } else if (code === 10005 || code === 10006) { | 
|                 setError('email', { | 
|                     message: msg | 
|                 }) | 
|             } else { | 
|                 notify(msg, { type: 'error', messageArgs: { _: msg } }); | 
|             } | 
|         }).catch((error) => { | 
|             setCodeLoading(false); | 
|             notify(error.message, { type: 'error', messageArgs: { _: error.message } }); | 
|             console.error(error); | 
|         }) | 
|     }; | 
|   | 
|     // countdown | 
|     useEffect(() => { | 
|         const codeExpirationTime = localStorage.getItem('codeExpirationTime'); | 
|         if (codeExpirationTime) { | 
|             const currentTimestamp = Math.floor(Date.now() / 1000); | 
|             const remainingTime = codeExpirationTime - currentTimestamp; | 
|             if (remainingTime > 0) { | 
|                 setCountdown(remainingTime); | 
|                 setIsCounting(true); | 
|             } | 
|         } | 
|   | 
|         const interval = setInterval(() => { | 
|             if (isCounting && countdown > 0) { | 
|                 setCountdown(prev => prev - 1); | 
|             } else if (countdown <= 0) { | 
|                 clearInterval(interval); | 
|                 setIsCounting(false); | 
|                 localStorage.removeItem('codeExpirationTime'); | 
|             } | 
|         }, 1000); | 
|   | 
|         return () => clearInterval(interval); | 
|     }, [countdown, isCounting]); | 
|   | 
|     // register | 
|     const onSubmit = (params) => { | 
|         setLoading(true); | 
|         register(params).then(res => { | 
|             setLoading(false); | 
|             const { code, msg, data } = res; | 
|             if (code === 200) { | 
|                 notify(msg, { type: 'success', messageArgs: { _: msg } }); | 
|                 // to login | 
|                 login( | 
|                     params, | 
|                     location.state ? (location.state).nextPathname : '/' | 
|                 ).catch(({ code, msg }) => { | 
|                     setLoading(false); | 
|                     notify(msg, { type: 'error', messageArgs: { _: msg } }); | 
|                 }); | 
|             } else if (code === 10002) { | 
|                 setError("username", { | 
|                     message: msg | 
|                 }) | 
|             } else { | 
|                 notify(msg, { type: 'error', messageArgs: { _: msg } }); | 
|             } | 
|         }).catch((error) => { | 
|             setLoading(false); | 
|             notify(error.message, { type: 'error', messageArgs: { _: error.message } }); | 
|             console.error(error); | 
|         }) | 
|     }; | 
|   | 
|     return ( | 
|         <> | 
|             <Box | 
|                 p={2} | 
|                 display="flex" | 
|                 flexDirection='column' | 
|                 component="form" onSubmit={handleSubmit(onSubmit)} noValidate | 
|             > | 
|                 <Stack spacing={2}> | 
|                     <Controller | 
|                         name="username" | 
|                         control={control} | 
|                         defaultValue="" | 
|                         rules={{ required: true }} | 
|                         render={({ field, fieldState: { error } }) => ( | 
|                             <TextField | 
|                                 {...field} | 
|                                 label={translate("page.login.username")} | 
|                                 variant="standard" | 
|                                 disabled={loading} | 
|                                 autoFocus | 
|                                 autoComplete="off" | 
|                                 error={!!error} | 
|                                 helperText={error?.message || ""} | 
|                             /> | 
|                         )} | 
|                     /> | 
|   | 
|                     <Controller | 
|                         name="password" | 
|                         control={control} | 
|                         defaultValue="" | 
|                         rules={{ | 
|                             required: translate('ra.validation.required'), | 
|                             pattern: { | 
|                                 value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d\.]{6,13}$/, | 
|                                 message: translate('page.settings.resetPwd.tip.pwdInputLimit'), | 
|                             }, | 
|                         }} | 
|                         render={({ field, fieldState: { error } }) => ( | 
|                             <TextField | 
|                                 {...field} | 
|                                 label={translate("page.login.password")} | 
|                                 type={showPassword ? 'text' : 'password'} | 
|                                 variant="standard" | 
|                                 disabled={loading} | 
|                                 autoComplete="off" | 
|                                 error={!!error} | 
|                                 helperText={error?.message || ""} | 
|                                 InputProps={{ | 
|                                     endAdornment: ( | 
|                                         <InputAdornment position="end"> | 
|                                             <IconButton | 
|                                                 aria-label="toggle password visibility" | 
|                                                 onClick={() => setShowPassword((show) => !show)} | 
|                                                 onMouseDown={(event) => { event.preventDefault() }} | 
|                                                 edge="end" | 
|                                             > | 
|                                                 {showPassword ? <VisibilityOff /> : <Visibility />} | 
|                                             </IconButton> | 
|                                         </InputAdornment> | 
|                                     ), | 
|                                 }} | 
|                             /> | 
|                         )} | 
|                     /> | 
|   | 
|                     <Controller | 
|                         name="confirmPassword" | 
|                         control={control} | 
|                         defaultValue="" | 
|                         rules={{ | 
|                             required: translate('ra.validation.required'), | 
|                             validate: value => | 
|                                 value === password || translate('page.settings.resetPwd.tip.pwdNotMatch'), | 
|                         }} | 
|                         render={({ field, fieldState: { error } }) => ( | 
|                             <TextField | 
|                                 {...field} | 
|                                 label={translate('page.login.confirmPwd')} | 
|                                 type={showPassword ? 'text' : 'password'} | 
|                                 variant="standard" | 
|                                 disabled={loading} | 
|                                 autoComplete="off" | 
|                                 error={!!error} | 
|                                 helperText={error?.message || ""} | 
|                                 InputProps={{ | 
|                                     endAdornment: ( | 
|                                         <InputAdornment position="end"> | 
|                                             <IconButton | 
|                                                 aria-label="toggle password visibility" | 
|                                                 onClick={() => setShowPassword((show) => !show)} | 
|                                                 onMouseDown={(event) => { event.preventDefault() }} | 
|                                                 edge="end" | 
|                                             > | 
|                                                 {showPassword ? <VisibilityOff /> : <Visibility />} | 
|                                             </IconButton> | 
|                                         </InputAdornment> | 
|                                     ), | 
|                                 }} | 
|                             /> | 
|                         )} | 
|                     /> | 
|   | 
|                     <Controller | 
|                         name="email" | 
|                         control={control} | 
|                         defaultValue="" | 
|                         rules={{ | 
|                             required: translate('ra.validation.required'), | 
|                             pattern: { | 
|                                 value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, | 
|                                 message: translate("ra.validation.email"), | 
|                             }, | 
|                         }} | 
|                         render={({ field, fieldState: { error } }) => ( | 
|                             <TextField | 
|                                 {...field} | 
|                                 label={translate('page.login.email')} | 
|                                 variant="standard" | 
|                                 disabled={loading} | 
|                                 // autoComplete="off" | 
|                                 error={!!error} | 
|                                 helperText={error ? error.message : ""} | 
|                             /> | 
|                         )} | 
|                     /> | 
|   | 
|                     <Box display="flex" alignItems="center" justifyContent='center' width="100%"> | 
|                         <Controller | 
|                             name="code" | 
|                             control={control} | 
|                             defaultValue="" | 
|                             rules={{ | 
|                                 required: translate('ra.validation.required'), | 
|                             }} | 
|                             render={({ field, fieldState: { error } }) => ( | 
|                                 <TextField | 
|                                     {...field} | 
|                                     label={translate('page.login.code')} | 
|                                     variant="standard" | 
|                                     disabled={loading} | 
|                                     autoComplete="off" | 
|                                     error={!!error} | 
|                                     helperText={error ? error.message : ""} | 
|                                     sx={{ | 
|                                         width: '65%', | 
|                                         mr: 2, | 
|                                     }} | 
|                                 /> | 
|                             )} | 
|                         /> | 
|   | 
|                         <Button | 
|                             variant="outlined" | 
|                             onClick={handleSendCode} | 
|                             disabled={codeLoading || isCounting} | 
|                             sx={{ | 
|                                 width: '35%', | 
|                                 mt: 1, | 
|                                 whiteSpace: 'nowrap', | 
|                             }} | 
|                         > | 
|                             {codeLoading ? ( | 
|                                 <CircularProgress size={20} color="primary" sx={{ marginRight: 1 }} /> | 
|                             ) : | 
|                                 isCounting ? ( | 
|                                     `${countdown}s` | 
|                                 ) : ( | 
|                                     translate('page.login.button.code') | 
|                                 ) | 
|                             } | 
|                         </Button> | 
|                     </Box> | 
|   | 
|                     <Box /> | 
|   | 
|                     <Button | 
|                         type="submit" | 
|                         variant="contained" | 
|                         disabled={loading || !(email && username && password && confirmPassword && code)} | 
|                         sx={{ | 
|                             backgroundColor: "#3D4BA7" | 
|                         }} | 
|                     > | 
|                         {loading && <CircularProgress size={25} thickness={2} />} | 
|                         {translate('page.login.button.register')} | 
|                     </Button> | 
|   | 
|                 </Stack> | 
|                 <Box mt={1} mb={1} sx={{ textAlign: 'center' }}>or</Box> | 
|   | 
|                 <ProviderChoices type="REGISTER" /> | 
|             </Box > | 
|         </> | 
|     ) | 
| } | 
|   | 
| export default Register; |