Add a login page (not been used yet)

This commit is contained in:
Deluan 2020-01-19 22:59:33 -05:00
parent 11f6acbb63
commit c661ac8833
4 changed files with 210 additions and 1 deletions

View File

@ -2,11 +2,12 @@
import React from 'react'
import { Admin, Resource } from 'react-admin'
import jsonServerProvider from 'ra-data-json-server'
import { Login } from './layout'
import user from './user'
const dataProvider = jsonServerProvider('/app/api')
const App = () => (
<Admin dataProvider={dataProvider}>
<Admin dataProvider={dataProvider} loginPage={Login}>
<Resource name="user" {...user} />
</Admin>
)

179
ui/src/layout/Login.js Normal file
View File

@ -0,0 +1,179 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { Field, Form } from 'react-final-form'
import Avatar from '@material-ui/core/Avatar'
import Button from '@material-ui/core/Button'
import Card from '@material-ui/core/Card'
import CardActions from '@material-ui/core/CardActions'
import CircularProgress from '@material-ui/core/CircularProgress'
import TextField from '@material-ui/core/TextField'
import { createMuiTheme, makeStyles } from '@material-ui/core/styles'
import { ThemeProvider } from '@material-ui/styles'
import LockIcon from '@material-ui/icons/Lock'
import { Notification, useLogin, useNotify, useTranslate } from 'react-admin'
import { lightTheme } from './themes'
const useStyles = makeStyles((theme) => ({
main: {
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'flex-start',
background: 'url(https://source.unsplash.com/random/1600x900?music)',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
backgroundPosition: 'center'
},
card: {
minWidth: 300,
marginTop: '6em'
},
avatar: {
margin: '1em',
display: 'flex',
justifyContent: 'center'
},
icon: {
backgroundColor: theme.palette.secondary.main
},
systemName: {
marginTop: '1em',
display: 'flex',
justifyContent: 'center',
color: 'blue' //theme.palette.grey[500]
},
form: {
padding: '0 1em 1em 1em'
},
input: {
marginTop: '1em'
},
actions: {
padding: '0 1em 1em 1em'
}
}))
const renderInput = ({
meta: { touched, error } = {},
input: { ...inputProps },
...props
}) => (
<TextField
error={!!(touched && error)}
helperText={touched && error}
{...inputProps}
{...props}
fullWidth
/>
)
const Login = ({ location }) => {
const [loading, setLoading] = useState(false)
const translate = useTranslate()
const classes = useStyles()
const notify = useNotify()
const login = useLogin()
const handleSubmit = (auth) => {
setLoading(true)
login(auth, location.state ? location.state.nextPathname : '/').catch(
(error) => {
setLoading(false)
notify(
typeof error === 'string'
? error
: typeof error === 'undefined' || !error.message
? 'ra.auth.sign_in_error'
: error.message,
'warning'
)
}
)
}
const validate = (values) => {
const errors = {}
if (!values.username) {
errors.username = translate('ra.validation.required')
}
if (!values.password) {
errors.password = translate('ra.validation.required')
}
return errors
}
return (
<Form
onSubmit={handleSubmit}
validate={validate}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit} noValidate>
<div className={classes.main}>
<Card className={classes.card}>
<div className={classes.avatar}>
<Avatar className={classes.icon}>
<LockIcon />
</Avatar>
</div>
<div className={classes.systemName}>CloudSonic</div>
<div className={classes.form}>
<div className={classes.input}>
<Field
autoFocus
name="username"
component={renderInput}
label={translate('ra.auth.username')}
disabled={loading}
/>
</div>
<div className={classes.input}>
<Field
name="password"
component={renderInput}
label={translate('ra.auth.password')}
type="password"
disabled={loading}
/>
</div>
</div>
<CardActions className={classes.actions}>
<Button
variant="contained"
type="submit"
color="primary"
disabled={loading}
className={classes.button}
fullWidth
>
{loading && <CircularProgress size={25} thickness={2} />}
{translate('ra.auth.sign_in')}
</Button>
</CardActions>
</Card>
<Notification />
</div>
</form>
)}
/>
)
}
Login.propTypes = {
authProvider: PropTypes.func,
previousRoute: PropTypes.string
}
// We need to put the ThemeProvider decoration in another component
// Because otherwise the useStyles() hook used in Login won't get
// the right theme
const LoginWithTheme = (props) => (
<ThemeProvider theme={createMuiTheme(lightTheme)}>
<Login {...props} />
</ThemeProvider>
)
export default LoginWithTheme

3
ui/src/layout/index.js Normal file
View File

@ -0,0 +1,3 @@
import Login from './Login'
export { Login }

26
ui/src/layout/themes.js Normal file
View File

@ -0,0 +1,26 @@
export const darkTheme = {
palette: {
type: 'dark' // Switching the dark mode on is a single property value change.
}
}
export const lightTheme = {
palette: {
secondary: {
light: '#5f5fc4',
main: '#283593',
dark: '#001064',
contrastText: '#fff'
}
},
overrides: {
MuiFilledInput: {
root: {
backgroundColor: 'rgba(0, 0, 0, 0.04)',
'&$disabled': {
backgroundColor: 'rgba(0, 0, 0, 0.04)'
}
}
}
}
}