Converted to bootstrap, ready for initial testing

This commit is contained in:
Patrick McDonagh
2018-06-05 10:58:53 -05:00
parent 9df0b5775d
commit 9eef3052ff
16 changed files with 1068 additions and 231 deletions

View File

@@ -1,3 +1,3 @@
{
"extends": "airbnb"
"extends": "rallycoding"
}

3
.gitignore vendored
View File

@@ -64,4 +64,5 @@ typings/
.vuepress/dist
# Serverless directories
.serverless
.serverless
dist/*

BIN
app/build/POCloud.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

File diff suppressed because one or more lines are too long

View File

@@ -3,18 +3,11 @@
<head>
<meta charset="UTF-8">
<title>POCloud Admin</title>
<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<div id="root"></div>
</body>
<!-- Compiled and minified JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
<script src="./build/bundle.js"></script>
<script src="./build/bundle.js"></script>
</html>

View File

@@ -8,24 +8,19 @@ import {
LOGIN_USER_SUCCESS,
LOGIN_USER_FAIL,
LOGOUT,
COMPANY_DATA_RECEIVED,
} from './types';
import baseURL from '../Meshify';
export const emailChanged = (text) => {
return {
type: EMAIL_CHANGED,
payload: text,
};
};
export const emailChanged = text => ({
type: EMAIL_CHANGED,
payload: text,
});
export const passwordChanged = (text) => {
return {
type: PASSWORD_CHANGED,
payload: text,
};
};
export const passwordChanged = text => ({
type: PASSWORD_CHANGED,
payload: text,
});
const loginUserSuccess = (dispatch, user, authToken) => {
dispatch({
@@ -49,15 +44,6 @@ export const loginUser = ({ email, password }) => {
loginUserSuccess(dispatch, response.data, authToken);
})
.catch(() => loginUserFail(dispatch));
axios.get(`${baseURL}/companies`, { headers: { Authorization: authToken } })
.then((response) => {
dispatch({ type: COMPANY_DATA_RECEIVED, payload: response.data });
})
.catch((err) => {
console.log('AUTH', authToken);
console.log(err);
});
};
};

View File

@@ -14,11 +14,11 @@ export const getData = ({ authToken }) => {
}
return (dispatch) => {
axios.get(`${baseURL}/companies`, { headers: { Authorization: authToken } })
.then((response) => {
dispatch({ type: COMPANY_DATA_RECEIVED, payload: response.data });
.then((companiesResponse) => {
dispatch({ type: COMPANY_DATA_RECEIVED, payload: companiesResponse.data });
axios.get(`${baseURL}/devicetypes`, { headers: { Authorization: authToken } })
.then((response) => {
dispatch({ type: DEVICETYPE_DATA_RECEIVED, payload: response.data });
.then((devicetypesResponse) => {
dispatch({ type: DEVICETYPE_DATA_RECEIVED, payload: devicetypesResponse.data });
axios.get(`${baseURL}/gateways`, { headers: { Authorization: authToken } })
.then((gtwResponse) => {
dispatch({ type: GATEWAY_DATA_RECEIVED, payload: gtwResponse.data });
@@ -32,3 +32,5 @@ export const getData = ({ authToken }) => {
};
};
export const dummy = '';

BIN
app/src/assets/POCloud.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

View File

@@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import pocloudLogo from '../assets/POCloud.png';
import { logoutUser } from '../actions';
@@ -15,38 +16,38 @@ class Header extends Component {
const { authToken } = this.props.auth;
if (authToken) {
return (
<nav>
<div className="nav-wrapper">
<ul className="right">
<li>
<button
className="btn waves-effect waves-light"
onClick={this.onLogoutButtonClicked.bind(this)}
style={{ marginRight: 15 }}
>
Log Out
</button>
</li>
</ul>
<ul>
<li>
<Link to="/main">Main</Link>
</li>
</ul>
</div>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<span className="navbar-brand mb-0 h1">
<img src={pocloudLogo} className="img-fluid" alt="POCloud" style={{ maxWidth: 200 }} />
</span>
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<Link to="/main" className="nav-link">Main</Link>
</li>
</ul>
<button
className="btn btn-danger"
onClick={this.onLogoutButtonClicked.bind(this)}
>
Log Out
</button>
</nav>
);
}
return (
<nav>
<div className="nav-wrapper">
<ul>
<li>
<Link to="/login">Login</Link>
</li>
</ul>
</div>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<span className="navbar-brand mb-0 h1">
<img src={pocloudLogo} className="img-fluid" alt="POCloud" style={{ maxWidth: 200 }} />
</span>
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<Link to="/login" className="nav-link">Login</Link>
</li>
</ul>
</nav>
);
}

View File

@@ -4,6 +4,12 @@ import { connect } from 'react-redux';
import { emailChanged, passwordChanged, loginUser } from '../actions';
class Login extends Component {
componentWillMount() {
if (this.props.auth.authToken) {
this.props.history.push('/main');
}
}
onEmailChange(event) {
this.props.emailChanged(event.target.value);
}
@@ -19,46 +25,57 @@ class Login extends Component {
}
render() {
const { email, password, authToken } = this.props.auth;
if (authToken) {
this.props.history.push('/main');
}
const { email, password } = this.props.auth;
return (
<div className="row">
<h1>Login</h1>
<form className="col s12">
<div className="input-field col s12">
<input
placeholder="email@email.com"
id="email"
type="email"
value={email}
onChange={this.onEmailChange.bind(this)}
/>
<label htmlFor="email">Email Address</label>
<div className="container">
<div className="row">
<div className="col">
<h1>Login</h1>
<form>
<div className="form-group">
<label htmlFor="email">Email Address</label>
<input
type="email"
className="form-control"
id="email"
aria-describedby="emailHelp"
placeholder="email@website.com"
value={email}
onChange={this.onEmailChange.bind(this)}
/>
<small
id="emailHelp"
className="form-text text-muted"
>
Enter your POCloud login information.
</small>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
id="password"
value={password}
onChange={this.onPasswordChange.bind(this)}
/>
</div>
<button
className="btn waves-effect waves-light"
type="submit"
name="action"
onClick={this.onButtonPress.bind(this)}
>
Login
</button>
</form>
</div>
<div className="input-field col s12">
<input
id="password"
type="password"
value={password}
onChange={this.onPasswordChange.bind(this)}
/>
<label htmlFor="password">Password</label>
</div>
<button
className="btn waves-effect waves-light"
type="submit"
name="action"
onClick={this.onButtonPress.bind(this)}
>
Submit <i className="material-icons right">send</i>
</button>
</form>
</div>
</div>
);
}

View File

@@ -1,30 +1,143 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import _ from 'lodash';
import { getData } from '../actions';
class Main extends Component {
componentWillMount() {
this.props.getData();
state = {
companyShowingDetails: 0
}
componentWillMount() {
const { authToken } = this.props.auth;
this.props.getData({ authToken });
}
getGatewaysForCompany({ companyId }) {
return _.filter(this.props.gateways, g => g.companyId === companyId);
}
getDevicesForGateway({ gatewayId }) {
return _.filter(this.props.allDevices, g => g.gatewayId === gatewayId);
}
companySelected({ companyId }) {
if (companyId === this.state.companyShowingDetails) {
this.setState({ companyShowingDetails: 0 });
} else {
this.setState({ companyShowingDetails: companyId });
}
}
renderGatewayList({ gatewayId }) {
const devices = this.getDevicesForGateway({ gatewayId });
return (
<ul>
{_.map(devices, d => (
<li key={d.id}>{d.vanityName} - {this.props.devicetypes[d.deviceTypeId].vanityName}</li>
))}
</ul>
);
}
renderCompanyGatewayList({ gatewayList }) {
return _.map(gatewayList, g => (
<tr>
<th>{g.name}</th>
<th>{g.macAddress}</th>
</tr>
));
}
renderCompanyTable({ companyId }) {
const gateways = this.getGatewaysForCompany({ companyId });
let gatewayList;
if (companyId === this.state.companyShowingDetails) {
gatewayList = (
<table className="table">
<thead>
<tr>
<th>Gateway</th>
<th>MAC Address</th>
</tr>
</thead>
<tbody>
{this.renderCompanyGatewayList({ gatewayList: gateways })}
</tbody>
</table>
);
}
if (gateways.length > 0) {
return (
<li
key={companyId}
className="list-group-item flex-column align-items-center"
onClick={() => this.companySelected({ companyId })}
>
<div className="d-flex justify-content-between">
{this.props.companies[companyId].name}
<span className="badge badge-primary badge-pill">{gateways.length}</span>
</div>
{gatewayList}
</li>
);
}
}
render() {
return (
<h1>Meshify Devices</h1>
<table>
<thead>
<div className="container">
<div className="row">
<div className="col">
<h1>Companies with Devices</h1>
<ul className="list-group">
{_.map(this.props.companies, c => this.renderCompanyTable({ companyId: c.id }))}
</ul>
</div>
</div>
</thead>
</table>
<div className="row">
<div className="col">
<h1>Companies with NO Devices</h1>
<ul className="list-group" style={{ marginBottom: 20 }}>
{_.map(this.props.companies, c => {
if (this.getGatewaysForCompany({ companyId: c.id }).length === 0) {
return (
<li className="list-group-item">
{this.props.companies[c.id].name}
</li>
);
}
})}
</ul>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
console.log(state.meshify);
const {
gateways,
devicetypes,
allDevices,
companies,
devices,
} = state.meshify;
return {
auth: state.auth,
auth: state.auth,
gateways,
devicetypes,
allDevices,
companies,
devices,
};
};

View File

@@ -1,4 +1,3 @@
import _ from 'lodash';
import {
EMAIL_CHANGED,
PASSWORD_CHANGED,
@@ -6,7 +5,6 @@ import {
LOGIN_USER_SUCCESS,
LOGIN_USER_FAIL,
LOGOUT,
COMPANY_DATA_RECEIVED,
} from '../actions/types';
const INITIAL_STATE = {
@@ -53,15 +51,6 @@ export default (state = INITIAL_STATE, action) => {
case LOGOUT:
return INITIAL_STATE;
case COMPANY_DATA_RECEIVED:
return {
...state,
user: {
...state.user,
companyName: _.keyBy(action.payload, c => c.id)[state.user.companyId].name,
},
};
default:
return state;
}

View File

@@ -1,6 +1,8 @@
import { combineReducers } from 'redux';
import AuthReducer from './authReducer';
import MeshifyReducer from './meshifyReducer';
export default combineReducers({
auth: AuthReducer,
meshify: MeshifyReducer,
});

View File

@@ -0,0 +1,45 @@
import _ from 'lodash';
import {
GATEWAY_DATA_RECEIVED,
DEVICETYPE_DATA_RECEIVED,
DEVICE_DATA_RECEIVED,
COMPANY_DATA_RECEIVED,
} from '../actions/types';
const INITIAL_STATE = {
gateways: {},
devicetypes: {},
allDevices: {},
companies: {},
devices: {},
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GATEWAY_DATA_RECEIVED:
return { ...state, gateways: _.keyBy(action.payload, g => g.id) };
case COMPANY_DATA_RECEIVED:
return { ...state, companies: _.keyBy(action.payload, c => c.id) };
case DEVICETYPE_DATA_RECEIVED:
return {
...state,
devicetypes: _.keyBy(action.payload, d => d.id),
};
case DEVICE_DATA_RECEIVED:
return {
...state,
devices: _.groupBy(action.payload, (device) => {
if (state.devicetypes[device.deviceTypeId]) {
return state.devicetypes[device.deviceTypeId].id;
}
}),
allDevices: _.keyBy(action.payload, d => d.id),
};
default:
return state;
}
};

853
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@
},
"author": "Patric McDonagh",
"devDependencies": {
"eslint-config-airbnb": "^16.1.0",
"electron": "^2.0.2",
"npm-run-all": "^4.1.3",
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.2",
@@ -33,8 +33,8 @@
"dependencies": {
"axios": "^0.18.0",
"base-64": "^0.1.0",
"electron": "^2.0.2",
"electron-localshortcut": "^1.1.1",
"eslint-config-rallycoding": "^3.2.0",
"lodash": "^4.17.10",
"moment": "^2.18.1",
"moment-duration-format": "^1.3.0",