1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-03-23 11:04:49 +00:00

Added V2 from scratch

This commit is contained in:
retspen 2018-09-23 13:17:48 +03:00
parent 5c2232f4e8
commit 6c2925a35d
478 changed files with 21437 additions and 134206 deletions

View file

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View file

@ -0,0 +1,3 @@
module.exports = {
extends: 'react-app'
};

21
frontend/admin/.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

26
frontend/admin/README.md Normal file
View file

@ -0,0 +1,26 @@
# Webvirtcloud Frontend Admin
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br>
See the section about [running tests](#running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br>
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!

View file

@ -0,0 +1,19 @@
{
"name": "admin",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^3.0.3",
"@material-ui/icons": "^3.0.1",
"react": "^16.5.1",
"react-dom": "^16.5.1",
"react-router-dom": "^4.3.1",
"react-scripts": "1.1.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Webvirtcloud Admin</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View file

@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View file

@ -0,0 +1,28 @@
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

19
frontend/admin/src/App.js Normal file
View file

@ -0,0 +1,19 @@
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Dashboard from './layouts/Dashboard';
import Auth from './layouts/Auth';
import './App.css';
export default class App extends Component {
render() {
return (
<Router>
<Switch>
<Route path="/" component={Dashboard} />
<Route path="/profile" component={Dashboard} />
<Route path="/auth" component={Auth} />
</Switch>
</Router>
);
}
}

View file

@ -0,0 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

View file

@ -0,0 +1,64 @@
import React from 'react';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import ListSubheader from '@material-ui/core/ListSubheader';
import DashboardIcon from '@material-ui/icons/Dashboard';
import PeopleIcon from '@material-ui/icons/People';
import BarChartIcon from '@material-ui/icons/BarChart';
import LayersIcon from '@material-ui/icons/Layers';
import AssignmentIcon from '@material-ui/icons/Assignment';
import {Link} from 'react-router-dom';
export const mainListItems = (
<div>
<ListItem button component={Link} to="/">
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button component={Link} to="/users">
<ListItemIcon>
<PeopleIcon />
</ListItemIcon>
<ListItemText primary="Users" />
</ListItem>
<ListItem button>
<ListItemIcon>
<BarChartIcon />
</ListItemIcon>
<ListItemText primary="Reports" />
</ListItem>
<ListItem button>
<ListItemIcon>
<LayersIcon />
</ListItemIcon>
<ListItemText primary="Compute" />
</ListItem>
</div>
);
export const secondaryListItems = (
<div>
<ListSubheader inset>Reports</ListSubheader>
<ListItem button>
<ListItemIcon>
<AssignmentIcon />
</ListItemIcon>
<ListItemText primary="Current month" />
</ListItem>
<ListItem button>
<ListItemIcon>
<AssignmentIcon />
</ListItemIcon>
<ListItemText primary="Last quarter" />
</ListItem>
<ListItem button>
<ListItemIcon>
<AssignmentIcon />
</ListItemIcon>
<ListItemText primary="Year-end sale" />
</ListItem>
</div>
);

View file

@ -0,0 +1,5 @@
body {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}

View file

@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './css/main.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render((
<App />
), document.getElementById('root')
);
registerServiceWorker();

View file

@ -0,0 +1,23 @@
import React from "react";
import {Route, Switch} from "react-router-dom";
import SignInPage from "../pages/auth/SignIn";
import SignUpPage from "../pages/auth/SignUp";
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
function Auth(props) {
return (
<React.Fragment>
<CssBaseline />
<div className="dashboard-layout">
<main>
<Switch>
<Route exact path="/auth/signin" component={SignInPage} />
<Route exact path="/auth/signup" component={SignUpPage} />
</Switch>
</main>
</div>
</React.Fragment>
)
}
export default Auth;

View file

@ -0,0 +1,217 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {withStyles} from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import Drawer from '@material-ui/core/Drawer';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import List from '@material-ui/core/List';
import Typography from '@material-ui/core/Typography';
import Divider from '@material-ui/core/Divider';
import IconButton from '@material-ui/core/IconButton';
import Badge from '@material-ui/core/Badge';
import MenuIcon from '@material-ui/icons/Menu';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import NotificationsIcon from '@material-ui/icons/Notifications';
import {Link, Route, Switch} from "react-router-dom";
import IndexPage from "../pages/Index";
import UsersPage from "../pages/Users";
import {mainListItems, secondaryListItems} from '../components/listItems';
import AccountCircle from '@material-ui/icons/AccountCircle';
import Menu from "@material-ui/core/Menu/Menu";
import MenuItem from "@material-ui/core/MenuItem/MenuItem";
const drawerWidth = 240;
const styles = theme => ({
root: {
display: 'flex',
},
toolbar: {
paddingRight: 24, // keep right padding when drawer closed
},
toolbarIcon: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar,
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
menuButton: {
marginLeft: 12,
marginRight: 36,
},
menuButtonHidden: {
display: 'none',
},
title: {
flexGrow: 1,
},
drawerPaper: {
position: 'relative',
whiteSpace: 'nowrap',
width: drawerWidth,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawerPaperClose: {
overflowX: 'hidden',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: theme.spacing.unit * 7,
[theme.breakpoints.up('sm')]: {
width: theme.spacing.unit * 9,
},
},
appBarSpacer: theme.mixins.toolbar,
content: {
flexGrow: 1,
padding: theme.spacing.unit * 3,
height: '100vh',
overflow: 'auto',
},
chartContainer: {
marginLeft: -22,
},
tableContainer: {
height: 320,
},
});
class Dashboard extends React.Component {
state = {
open: true,
anchorEl: null,
};
handleDrawerOpen = () => {
this.setState({open: true});
};
handleDrawerClose = () => {
this.setState({open: false});
};
handleUserMenu = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleUserMenuClose = () => {
this.setState({ anchorEl: null });
};
render() {
const {classes} = this.props;
const { anchorEl } = this.state;
const userMenuIsOpen = Boolean(anchorEl);
return (
<React.Fragment>
<CssBaseline/>
<div className={classes.root}>
<AppBar
position="absolute"
className={classNames(classes.appBar, this.state.open && classes.appBarShift)}
>
<Toolbar disableGutters={!this.state.open} className={classes.toolbar}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerOpen}
className={classNames(
classes.menuButton,
this.state.open && classes.menuButtonHidden,
)}
>
<MenuIcon/>
</IconButton>
<Typography variant="title" color="inherit" noWrap className={classes.title}>
Dashboard
</Typography>
<IconButton color="inherit">
<Badge badgeContent={4} color="secondary">
<NotificationsIcon/>
</Badge>
</IconButton>
<IconButton
aria-owns={userMenuIsOpen ? 'menu-appbar' : null}
aria-haspopup="true"
onClick={this.handleUserMenu}
color="inherit"
>
<AccountCircle />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={userMenuIsOpen}
onClose={this.handleClose}
>
<MenuItem component={Link} to="/profile" onClick={this.handleUserMenu}>Profile</MenuItem>
<MenuItem component={Link} to="/auth/signin" onClick={this.handleUserMenu}>Log out</MenuItem>
</Menu>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
classes={{
paper: classNames(classes.drawerPaper, !this.state.open && classes.drawerPaperClose),
}}
open={this.state.open}
>
<div className={classes.toolbarIcon}>
<IconButton onClick={this.handleDrawerClose}>
<ChevronLeftIcon/>
</IconButton>
</div>
<Divider/>
<List>{mainListItems}</List>
<Divider/>
{/*<List>{secondaryListItems}</List>*/}
</Drawer>
<main className={classes.content}>
<div className={classes.appBarSpacer}/>
<Switch>
<Route exact path="/" component={IndexPage}/>
<Route exact path="/users" component={UsersPage}/>
</Switch>
</main>
</div>
</React.Fragment>
);
}
}
Dashboard.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Dashboard);

View file

@ -0,0 +1,45 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
import withStyles from "@material-ui/core/styles/withStyles";
import Grid from '@material-ui/core/Grid';
const styles = theme => ({
title: {
marginBottom: theme.spacing.unit * 3,
},
root: {
flexGrow: 1,
},
paper: {
width: '100%',
overflowX: 'auto',
padding: theme.spacing.unit * 4,
},
});
function Index(props) {
const { classes } = props;
return (
<React.Fragment>
<CssBaseline />
<div>
<Typography className={classes.title} variant="headline">Cluster stats</Typography>
<Grid container spacing={24}>
<Grid item xs={12} md={4}>
<Paper className={classes.paper}>Some graph here</Paper>
</Grid>
<Grid item xs={12} md={4}>
<Paper className={classes.paper}>Some graph here</Paper>
</Grid>
<Grid item xs={12} md={4}>
<Paper className={classes.paper}>Some graph here</Paper>
</Grid>
</Grid>
</div>
</React.Fragment>
)
}
export default withStyles(styles)(Index);

View file

@ -0,0 +1,117 @@
import React from "react";
import Paper from "@material-ui/core/Paper";
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';
import TextField from '@material-ui/core/TextField';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import Button from '@material-ui/core/Button';
import withStyles from "@material-ui/core/styles/withStyles";
const styles = theme => ({
layout: {
width: 'auto',
display: 'block',
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(600 + theme.spacing.unit * 3 * 2)]: {
width: 600,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
padding: theme.spacing.unit * 4,
},
});
function Profile(props) {
const { classes } = props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.layout}>
<Paper className={classes.paper}>
<Typography variant="headline">Profile</Typography>
<Grid container spacing={24}>
<Grid item xs={12} sm={6}>
<TextField
required
id="firstName"
name="firstName"
label="First name"
fullWidth
autoComplete="fname"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="lastName"
name="lastName"
label="Last name"
fullWidth
autoComplete="lname"
/>
</Grid>
<Grid item xs={12}>
<TextField
required
id="address1"
name="address1"
label="Address line 1"
fullWidth
autoComplete="billing address-line1"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="city"
name="city"
label="City"
fullWidth
autoComplete="billing address-level2"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField id="state" name="state" label="State/Province/Region" fullWidth />
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="zip"
name="zip"
label="Zip / Postal code"
fullWidth
autoComplete="billing postal-code"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="country"
name="country"
label="Country"
fullWidth
autoComplete="billing country"
/>
</Grid>
<Grid item xs={12}>
<FormControlLabel
control={<Checkbox color="secondary" name="saveAddress" value="yes" />}
label="Use this address for payment details"
/>
</Grid>
<Grid item xs={12}>
<Button fullWidth variant="contained" color="primary">Update profile</Button>
</Grid>
</Grid>
</Paper>
</div>
</React.Fragment>
)
}
export default withStyles(styles)(Profile);

View file

@ -0,0 +1,74 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
import withStyles from "@material-ui/core/styles/withStyles";
const styles = theme => ({
title: {
marginBottom: theme.spacing.unit * 2,
},
paper: {
width: '100%',
overflowX: 'auto',
padding: theme.spacing.unit * 4,
},
});
let id = 0;
function createData(name, status) {
id += 1;
return { id, name, status };
}
const rows = [
createData('cloud-test', 'connected'),
createData('cloud-ubuntu-fra3', 'disconnected'),
];
function Index(props) {
const { classes } = props;
return (
<React.Fragment>
<CssBaseline />
<div>
<Typography className={classes.title} variant="headline">Users</Typography>
<Paper className={classes.paper}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Status</TableCell>
<TableCell>Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => {
return (
<TableRow key={row.id}>
<TableCell component="th" scope="row">{row.name}</TableCell>
<TableCell>{row.status}</TableCell>
<TableCell>
<IconButton aria-label="Delete">
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</Paper>
</div>
</React.Fragment>
)
}
export default withStyles(styles)(Index);

View file

@ -0,0 +1,135 @@
import React from 'react';
import PropTypes from 'prop-types';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import LockIcon from '@material-ui/icons/LockOutlined';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import withStyles from '@material-ui/core/styles/withStyles';
import {Link} from "react-router-dom";
const styles = theme => ({
layout: {
width: 'auto',
display: 'block',
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%',
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
footer: {
marginTop: theme.spacing.unit * 3,
textAlign: 'center',
},
});
class SignIn extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
name: '',
email: '',
password: '',
}
};
}
/**
* Change the user object.
*
* @param {object} event - the JavaScript event object
*/
handleChange(event) {
const field = event.target.name;
const user = this.state.user;
user[field] = event.target.value;
this.setState({ user });
}
login() {
return fetch('http://localhost:8000/api/v1/rest-auth/registration/', {
method: 'POST',
body: JSON.stringify({
email: this.state.user.email,
name: this.state.user.name,
password: this.state.user.password,
}),
});
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockIcon />
</Avatar>
<Typography variant="headline">Sign In</Typography>
<form className={classes.form}>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="email">Email Address</InputLabel>
<Input id="email" name="email" autoComplete="email" autoFocus />
</FormControl>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="password">Password</InputLabel>
<Input
name="password"
type="password"
id="password"
autoComplete="current-password"
/>
</FormControl>
<Button
type="submit"
fullWidth
variant="raised"
color="primary"
className={classes.submit}
onClick={this.login.bind(this)}
>
Sign In
</Button>
</form>
</Paper>
<footer className={classes.footer}>
<Button size="small" component={Link} to="/auth/signup">Don't have an account? Sign Up</Button>
</footer>
</div>
</React.Fragment>
)
}
}
SignIn.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SignIn);

View file

@ -0,0 +1,150 @@
import React from 'react';
import {Link} from 'react-router-dom';
import PropTypes from 'prop-types';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import LockIcon from '@material-ui/icons/LockOutlined';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import withStyles from '@material-ui/core/styles/withStyles';
const styles = theme => ({
layout: {
width: 'auto',
display: 'block',
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing.unit * 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`,
},
avatar: {
margin: theme.spacing.unit,
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%',
marginTop: theme.spacing.unit,
},
submit: {
marginTop: theme.spacing.unit * 3,
},
footer: {
marginTop: theme.spacing.unit * 3,
textAlign: 'center',
},
});
class SignIn extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
email: '',
password: '',
}
};
this.submitForm = this.submitForm.bind(this);
this.handleChange = this.handleChange.bind(this);
}
/**
* Change the user object.
*
* @param {object} event - the JavaScript event object
*/
handleChange(event) {
const field = event.target.name;
const user = this.state.user;
user[field] = event.target.value;
this.setState({ user });
}
/**
* Process the form.
*
* @param {object} event - the JavaScript event object
*/
submitForm(event) {
// prevent default action. in this case, action is the form submission event
event.preventDefault();
fetch('http://localhost:8000/api/v1/rest-auth/registration/', {
credentials: 'same-origin',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify(this.state.user),
})
.then(response=> response.json())
.then((response) => {
console.log(response);
});
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<LockIcon />
</Avatar>
<Typography variant="headline">Sign Up</Typography>
<form action="/" className={classes.form} onSubmit={this.submitForm}>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="email">Email Address</InputLabel>
<Input onChange={this.handleChange} id="email" name="email" autoComplete="email" autoFocus />
</FormControl>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="password">Password</InputLabel>
<Input
onChange={this.handleChange}
name="password"
type="password"
id="password"
autoComplete="current-password"
/>
</FormControl>
<Button
type="submit"
fullWidth
variant="raised"
color="primary"
className={classes.submit}
>
Sign Up
</Button>
</form>
</Paper>
<footer className={classes.footer}>
<Button size="small" component={Link} to="/auth/signin">Already have an account? Sign In</Button>
</footer>
</div>
</React.Fragment>
)
}
}
SignIn.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SignIn);

View file

@ -0,0 +1,117 @@
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}

7472
frontend/admin/yarn.lock Normal file

File diff suppressed because it is too large Load diff