mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-15 19:50:37 +03:00
Add a login page (not been used yet)
This commit is contained in:
parent
11f6acbb63
commit
c661ac8833
@ -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
179
ui/src/layout/Login.js
Normal 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
3
ui/src/layout/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
import Login from './Login'
|
||||
|
||||
export { Login }
|
26
ui/src/layout/themes.js
Normal file
26
ui/src/layout/themes.js
Normal 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)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user