|  |  | 
 |  |  | import * as React from 'react'; | 
 |  |  | import { useState } from 'react'; | 
 |  |  | import React, { useState, useRef, useEffect, useMemo } from "react"; | 
 |  |  | import { useLocation } from 'react-router-dom'; | 
 |  |  | import { | 
 |  |  |     Avatar, | 
 |  |  |     Box, | 
 |  |  |     Button, | 
 |  |  |     Card, | 
 |  |  |     CardActions, | 
 |  |  |     CircularProgress, | 
 |  |  |     Typography, | 
 |  |  |     Button, | 
 |  |  |     TextField, | 
 |  |  |     Stack, | 
 |  |  |     Autocomplete, | 
 |  |  |     InputAdornment, | 
 |  |  |     IconButton, | 
 |  |  | } from '@mui/material'; | 
 |  |  | import LockIcon from '@mui/icons-material/Lock'; | 
 |  |  | import { | 
 |  |  |     Form, | 
 |  |  |     required, | 
 |  |  |     TextInput, | 
 |  |  |     useTranslate, | 
 |  |  |     useLogin, | 
 |  |  |     useNotify, | 
 |  |  | } from 'react-admin'; | 
 |  |  | import { LOGIN_BACKGROUND } from '@/config/setting'; | 
 |  |  | import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form"; | 
 |  |  | import ProviderChoices from "./ProviderChoices"; | 
 |  |  | import Visibility from '@mui/icons-material/Visibility'; | 
 |  |  | import VisibilityOff from '@mui/icons-material/VisibilityOff'; | 
 |  |  |  | 
 |  |  | const Login = () => { | 
 |  |  |     const [loading, setLoading] = useState(false); | 
 |  |  | const Login = (props) => { | 
 |  |  |     const translate = useTranslate(); | 
 |  |  |  | 
 |  |  |     const notify = useNotify(); | 
 |  |  |     const login = useLogin(); | 
 |  |  |     const location = useLocation(); | 
 |  |  |     const { systemInfo: { mode }, tenantList } = props; | 
 |  |  |  | 
 |  |  |     const handleSubmit = (auth) => { | 
 |  |  |     const { control, handleSubmit, watch, setValue, getValues, setError, clearErrors } = useForm(); | 
 |  |  |  | 
 |  |  |     const [loading, setLoading] = useState(false); | 
 |  |  |     const [showPassword, setShowPassword] = useState(false); | 
 |  |  |  | 
 |  |  |     const username = watch('username'); | 
 |  |  |     const password = watch('password'); | 
 |  |  |     const tenantId = watch('tenantId'); | 
 |  |  |  | 
 |  |  |     useEffect(() => { | 
 |  |  |         if (tenantList.length > 0 && !tenantId) { | 
 |  |  |             const rememberTenantId = localStorage.getItem('remember_tenantId'); | 
 |  |  |             if (rememberTenantId && tenantList.some(t => t.id === Number(rememberTenantId))) { | 
 |  |  |                 setValue('tenantId', Number(rememberTenantId)); | 
 |  |  |             } else { | 
 |  |  |                 setValue('tenantId', tenantList[0].id); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |     }, [tenantList, setValue]); | 
 |  |  |  | 
 |  |  |     const onSubmit = (data) => { | 
 |  |  |         setLoading(true); | 
 |  |  |         // js native confirm && root | 
 |  |  |         login( | 
 |  |  |             auth, | 
 |  |  |             data, | 
 |  |  |             location.state ? (location.state).nextPathname : '/' | 
 |  |  |         ).catch((error) => { | 
 |  |  |         ).catch((res) => { | 
 |  |  |             setLoading(false); | 
 |  |  |             notify( | 
 |  |  |                 typeof error === 'string' | 
 |  |  |                     ? error | 
 |  |  |                     : typeof error === 'undefined' || !error.message | 
 |  |  |                         ? 'ra.auth.sign_in_error' | 
 |  |  |                         : error.message, | 
 |  |  |                 { | 
 |  |  |                     type: 'error', | 
 |  |  |                     messageArgs: { | 
 |  |  |                         _: | 
 |  |  |                             typeof error === 'string' | 
 |  |  |                                 ? error | 
 |  |  |                                 : error && error.message | 
 |  |  |                                     ? error.message | 
 |  |  |                                     : undefined, | 
 |  |  |                     }, | 
 |  |  |                 } | 
 |  |  |             ); | 
 |  |  |             const { code, msg, data } = res; | 
 |  |  |             if (code === 10003) { | 
 |  |  |                 setError('username', { | 
 |  |  |                     message: msg | 
 |  |  |                 }) | 
 |  |  |             } else if (code === 10004) { | 
 |  |  |                 setError('username', { | 
 |  |  |                     message: msg | 
 |  |  |                 }) | 
 |  |  |             } else if (code === 10001) { | 
 |  |  |                 setError('password', { | 
 |  |  |                     message: msg | 
 |  |  |                 }) | 
 |  |  |             } else { | 
 |  |  |                 notify(msg, { type: 'error', messageArgs: { _: msg } }); | 
 |  |  |             } | 
 |  |  |         }); | 
 |  |  |     }; | 
 |  |  |  | 
 |  |  |     return ( | 
 |  |  |         <Form onSubmit={handleSubmit} noValidate> | 
 |  |  |             {/* https://unsplash.com/ */} | 
 |  |  |         <> | 
 |  |  |             <Box | 
 |  |  |                 sx={{ | 
 |  |  |                     display: 'flex', | 
 |  |  |                     flexDirection: 'column', | 
 |  |  |                     minHeight: '100vh', | 
 |  |  |                     alignItems: 'center', | 
 |  |  |                     justifyContent: 'flex-start', | 
 |  |  |                     // justifyContent: 'center', | 
 |  |  |                     background: `url(/login_bg.jpg)`, | 
 |  |  |                     backgroundRepeat: 'no-repeat', | 
 |  |  |                     backgroundSize: 'cover', | 
 |  |  |                 }} | 
 |  |  |                 p={2} | 
 |  |  |                 display="flex" | 
 |  |  |                 flexDirection='column' | 
 |  |  |                 component="form" onSubmit={handleSubmit(onSubmit)} noValidate | 
 |  |  |             > | 
 |  |  |                 <video | 
 |  |  |                     autoPlay | 
 |  |  |                     loop | 
 |  |  |                     muted | 
 |  |  |                     style={{ | 
 |  |  |                         position: 'fixed', | 
 |  |  |                         top: 0, | 
 |  |  |                         left: 0, | 
 |  |  |                         width: '100%', | 
 |  |  |                         height: '100%', | 
 |  |  |                         // objectFit: 'cover', | 
 |  |  |                         // objectFit: 'contain', | 
 |  |  |                         objectFit: 'fill', | 
 |  |  |                         // objectFit: 'scale-down', | 
 |  |  |                         zIndex: 0, | 
 |  |  |                     }} | 
 |  |  |                 > | 
 |  |  |                     {LOGIN_BACKGROUND === 'media' && ( | 
 |  |  |                         <source src="/login_bg.mp4" type="video/mp4" /> | 
 |  |  |                 <Stack spacing={2}> | 
 |  |  |                     {mode === 'OFFLINE' && ( | 
 |  |  |                         <Controller | 
 |  |  |                             name="tenantId" | 
 |  |  |                             control={control} | 
 |  |  |                             rules={{ required: true }} | 
 |  |  |                             defaultValue={tenantList.length > 0 ? tenantList[0].id : ''} | 
 |  |  |                             render={({ field: { onChange, value, ref } }) => { | 
 |  |  |                                 const selectedTenant = tenantList.find(tenant => tenant.id === value) || null; | 
 |  |  |                                 return ( | 
 |  |  |                                     <Autocomplete | 
 |  |  |                                         options={tenantList} | 
 |  |  |                                         getOptionLabel={(option) => option.name} | 
 |  |  |                                         value={selectedTenant} | 
 |  |  |                                         onChange={(_, newValue) => { | 
 |  |  |                                             const newTenantId = newValue ? newValue.id : ''; | 
 |  |  |                                             onChange(newTenantId); | 
 |  |  |                                             localStorage.setItem('remember_tenantId', newTenantId); | 
 |  |  |                                         }} | 
 |  |  |                                         renderInput={(params) => ( | 
 |  |  |                                             <TextField | 
 |  |  |                                                 {...params} | 
 |  |  |                                                 label={translate("page.login.tenant")} | 
 |  |  |                                                 variant="standard" | 
 |  |  |                                                 inputRef={ref} | 
 |  |  |                                             /> | 
 |  |  |                                         )} | 
 |  |  |                                     /> | 
 |  |  |                                 ); | 
 |  |  |                             }} | 
 |  |  |                         /> | 
 |  |  |                     )} | 
 |  |  |                 </video> | 
 |  |  |  | 
 |  |  |                 <Card sx={{ | 
 |  |  |                     minWidth: 300, | 
 |  |  |                     marginTop: '6em', | 
 |  |  |                     zIndex: 1 | 
 |  |  |                 }}> | 
 |  |  |                     <Box | 
 |  |  |                         sx={{ | 
 |  |  |                             margin: '1em', | 
 |  |  |                             display: 'flex', | 
 |  |  |                             justifyContent: 'center', | 
 |  |  |                         }} | 
 |  |  |                     > | 
 |  |  |                         <Avatar sx={{ bgcolor: 'secondary.main' }}> | 
 |  |  |                             <LockIcon /> | 
 |  |  |                         </Avatar> | 
 |  |  |                     </Box> | 
 |  |  |                     <Box | 
 |  |  |                         sx={{ | 
 |  |  |                             marginTop: '1em', | 
 |  |  |                             display: 'flex', | 
 |  |  |                             justifyContent: 'center', | 
 |  |  |                             color: theme => theme.palette.grey[500], | 
 |  |  |                         }} | 
 |  |  |                     > | 
 |  |  |                         Hint: root / 123456 | 
 |  |  |                     </Box> | 
 |  |  |                     <Box sx={{ padding: '0 1em 1em 1em' }}> | 
 |  |  |                         <Box sx={{ marginTop: '1em' }}> | 
 |  |  |                             <TextInput | 
 |  |  |                     <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 | 
 |  |  |                                 source="username" | 
 |  |  |                                 label={translate('ra.auth.username')} | 
 |  |  |                                 disabled={loading} | 
 |  |  |                                 validate={required()} | 
 |  |  |                                 autoComplete="off" | 
 |  |  |                                 error={!!error} | 
 |  |  |                                 helperText={error?.message || ""} | 
 |  |  |                             /> | 
 |  |  |                         </Box> | 
 |  |  |                         <Box sx={{ marginTop: '1em' }}> | 
 |  |  |                             <TextInput | 
 |  |  |                                 source="password" | 
 |  |  |                                 label={translate('ra.auth.password')} | 
 |  |  |                                 type="password" | 
 |  |  |                                 disabled={loading} | 
 |  |  |                                 validate={required()} | 
 |  |  |                             /> | 
 |  |  |                         </Box> | 
 |  |  |                     </Box> | 
 |  |  |                     <CardActions sx={{ padding: '0 1em 1em 1em' }}> | 
 |  |  |                         <Button | 
 |  |  |                             variant="contained" | 
 |  |  |                             type="submit" | 
 |  |  |                             color="primary" | 
 |  |  |                             disabled={loading} | 
 |  |  |                             fullWidth | 
 |  |  |                         > | 
 |  |  |                             {loading && ( | 
 |  |  |                                 <CircularProgress size={25} thickness={2} /> | 
 |  |  |                             )} | 
 |  |  |                             {translate('ra.auth.sign_in')} | 
 |  |  |                         </Button> | 
 |  |  |                     </CardActions> | 
 |  |  |                 </Card> | 
 |  |  |             </Box> | 
 |  |  |         </Form> | 
 |  |  |     ); | 
 |  |  | }; | 
 |  |  |                         )} | 
 |  |  |                     /> | 
 |  |  |  | 
 |  |  | export default Login; | 
 |  |  |                     <Controller | 
 |  |  |                         name="password" | 
 |  |  |                         control={control} | 
 |  |  |                         defaultValue="" | 
 |  |  |                         rules={{ required: true }} | 
 |  |  |                         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> | 
 |  |  |                                     ), | 
 |  |  |                                 }} | 
 |  |  |                             /> | 
 |  |  |                         )} | 
 |  |  |                     /> | 
 |  |  |  | 
 |  |  |                     <Box /> | 
 |  |  |  | 
 |  |  |                     <Button | 
 |  |  |                         type="submit" | 
 |  |  |                         variant="contained" | 
 |  |  |                         disabled={loading || !((mode === 'OFFLINE' ? tenantId : true) && username && password)} | 
 |  |  |                     > | 
 |  |  |                         {loading && <CircularProgress size={25} thickness={2} />} | 
 |  |  |                         {translate('page.login.button.login')} | 
 |  |  |                     </Button> | 
 |  |  |  | 
 |  |  |                 </Stack> | 
 |  |  |                 <Box mt={1} mb={1} sx={{ textAlign: 'center' }}>or</Box> | 
 |  |  |  | 
 |  |  |                 <ProviderChoices type="LOG IN" /> | 
 |  |  |             </Box > | 
 |  |  |         </> | 
 |  |  |     ) | 
 |  |  | } | 
 |  |  |  | 
 |  |  | export default Login; |