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'
};

30
frontend/client/.gitignore vendored Normal file
View file

@ -0,0 +1,30 @@
# 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*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

2486
frontend/client/README.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^3.0.1",
"@material-ui/icons": "^3.0.1",
"react": "^16.4.2",
"react-dom": "^16.4.2",
"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,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no minimum-scale=1">
<meta name="theme-color" content="#000000">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
<!--
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</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); }
}

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 exact 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,86 @@
import React, { Component } from "react";
import {Link} from "react-router-dom";
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Toolbar from '@material-ui/core/Toolbar';
import AppBar from '@material-ui/core/AppBar';
import MenuItem from '@material-ui/core/MenuItem';
import Menu from '@material-ui/core/Menu';
import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import AccountCircle from '@material-ui/icons/AccountCircle';
const styles = {
root: {
flexGrow: 1,
},
title: {
textDecoration: 'none',
flexGrow: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
};
class Navigation extends Component {
state = {
anchorEl: null,
};
handleMenu = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: null });
};
render() {
const { classes } = this.props;
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
return (
<AppBar position="static">
<Toolbar>
<Typography component={Link} to="/" variant="title" color="inherit" className={classes.title}>Webvirtcloud</Typography>
<div>
<IconButton
aria-owns={open ? 'menu-appbar' : null}
aria-haspopup="true"
onClick={this.handleMenu}
color="inherit"
>
<AccountCircle />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={open}
onClose={this.handleClose}
>
<MenuItem component={Link} to="/profile" onClick={this.handleClose}>Profile</MenuItem>
<MenuItem component={Link} to="/auth/signin" onClick={this.handleClose}>Log out</MenuItem>
</Menu>
</div>
</Toolbar>
</AppBar>
)
}
}
Navigation.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Navigation);

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,36 @@
import React from "react";
import {Route, Switch} from "react-router-dom";
import IndexPage from "../pages/Index";
import ProfilePage from "../pages/Profile";
import Navigation from "../components/Navigation";
import withStyles from '@material-ui/core/styles/withStyles';
import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline";
const styles = theme => ({
content: {
width: '100%',
maxWidth: theme.breakpoints.values.md,
margin: `${theme.spacing.unit * 4}px auto`,
padding: `0 ${theme.spacing.unit}px`,
}
});
function Dashboard(props) {
const { classes } = props;
return (
<React.Fragment>
<CssBaseline />
<div className="dashboard-layout">
<Navigation/>
<main className={classes.content}>
<Switch>
<Route exact path="/" component={IndexPage}/>
<Route path="/profile" component={ProfilePage}/>
</Switch>
</main>
</div>
</React.Fragment>
)
}
export default withStyles(styles)(Dashboard);

View file

@ -0,0 +1,80 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
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 Paper from '@material-ui/core/Paper';
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 => ({
header: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: theme.spacing.unit * 4,
},
paper: {
width: '100%',
overflowX: 'auto',
},
});
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>
<div className={classes.header}>
<Typography variant="headline">Dashboard</Typography>
<Button color="primary" variant="contained">Add host</Button>
</div>
<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,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,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();
});
}
}

7600
frontend/client/yarn.lock Normal file

File diff suppressed because it is too large Load diff