diff --git a/ui/src/App.js b/ui/src/App.js index 8c70c6f35..a8c819b97 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -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 = () => ( - + ) diff --git a/ui/src/layout/Login.js b/ui/src/layout/Login.js new file mode 100644 index 000000000..01d5c282c --- /dev/null +++ b/ui/src/layout/Login.js @@ -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 +}) => ( + +) + +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 ( +
( + +
+ +
+ + + +
+
CloudSonic
+
+
+ +
+
+ +
+
+ + + +
+ +
+
+ )} + /> + ) +} + +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) => ( + + + +) + +export default LoginWithTheme diff --git a/ui/src/layout/index.js b/ui/src/layout/index.js new file mode 100644 index 000000000..5e18ae6cc --- /dev/null +++ b/ui/src/layout/index.js @@ -0,0 +1,3 @@ +import Login from './Login' + +export { Login } diff --git a/ui/src/layout/themes.js b/ui/src/layout/themes.js new file mode 100644 index 000000000..6aa7dc808 --- /dev/null +++ b/ui/src/layout/themes.js @@ -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)' + } + } + } + } +}