Converted to bootstrap, ready for initial testing
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -65,3 +65,4 @@ typings/
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
dist/*
|
||||
|
||||
BIN
app/build/POCloud.png
Normal file
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
@@ -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>
|
||||
</html>
|
||||
|
||||
@@ -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 {
|
||||
export const emailChanged = text => ({
|
||||
type: EMAIL_CHANGED,
|
||||
payload: text,
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
export const passwordChanged = (text) => {
|
||||
return {
|
||||
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);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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
BIN
app/src/assets/POCloud.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
@@ -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>
|
||||
<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 waves-effect waves-light"
|
||||
className="btn btn-danger"
|
||||
onClick={this.onLogoutButtonClicked.bind(this)}
|
||||
style={{ marginRight: 15 }}
|
||||
>
|
||||
Log Out
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/main">Main</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav>
|
||||
<div className="nav-wrapper">
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/login">Login</Link>
|
||||
<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>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,34 +25,42 @@ 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="container">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<h1>Login</h1>
|
||||
<form className="col s12">
|
||||
<div className="input-field col s12">
|
||||
<form>
|
||||
<div className="form-group">
|
||||
<label htmlFor="email">Email Address</label>
|
||||
<input
|
||||
placeholder="email@email.com"
|
||||
id="email"
|
||||
type="email"
|
||||
className="form-control"
|
||||
id="email"
|
||||
aria-describedby="emailHelp"
|
||||
placeholder="email@website.com"
|
||||
value={email}
|
||||
onChange={this.onEmailChange.bind(this)}
|
||||
/>
|
||||
<label htmlFor="email">Email Address</label>
|
||||
<small
|
||||
id="emailHelp"
|
||||
className="form-text text-muted"
|
||||
>
|
||||
Enter your POCloud login information.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div className="input-field col s12">
|
||||
<div className="form-group">
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
className="form-control"
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={this.onPasswordChange.bind(this)}
|
||||
/>
|
||||
<label htmlFor="password">Password</label>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@@ -55,11 +69,14 @@ class Login extends Component {
|
||||
name="action"
|
||||
onClick={this.onButtonPress.bind(this)}
|
||||
>
|
||||
Submit <i className="material-icons right">send</i>
|
||||
Login
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
state = {
|
||||
companyShowingDetails: 0
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.getData();
|
||||
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,
|
||||
gateways,
|
||||
devicetypes,
|
||||
allDevices,
|
||||
companies,
|
||||
devices,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import AuthReducer from './authReducer';
|
||||
import MeshifyReducer from './meshifyReducer';
|
||||
|
||||
export default combineReducers({
|
||||
auth: AuthReducer,
|
||||
meshify: MeshifyReducer,
|
||||
});
|
||||
|
||||
45
app/src/reducers/meshifyReducer.js
Normal file
45
app/src/reducers/meshifyReducer.js
Normal 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
853
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user