From c79df7722b7ddcaa347f7f44cb113e05d044a1c0 Mon Sep 17 00:00:00 2001 From: John Lyon-Smith Date: Tue, 27 Feb 2018 14:44:37 -0800 Subject: [PATCH] Login component working --- website/src/{helpers/Api.js => API.js} | 131 +++---------------------- website/src/App.js | 5 +- website/src/Auth/ConfirmEmail.js | 2 +- website/src/Auth/ForgotPassword.js | 2 +- website/src/Auth/Login.js | 17 ++-- website/src/Auth/Logout.js | 2 +- website/src/Auth/ProtectedRoute.js | 2 +- website/src/Auth/ResetPassword.js | 2 +- website/src/Profile/Profile.js | 28 +----- website/src/Users/UserForm.js | 6 +- website/src/Users/UserList.js | 11 +-- website/src/Users/Users.js | 2 +- website/src/helpers/Constants.js | 75 -------------- website/src/helpers/index.js | 2 - website/src/ui/BoundButton.js | 32 +++--- website/src/ui/BoundCheckbox.js | 5 +- website/src/ui/BoundInput.js | 23 +++-- website/src/ui/Button.js | 13 ++- website/src/ui/Button.style.js | 9 +- website/src/ui/Checkbox.js | 28 ++++-- website/src/ui/Dropdown.js | 19 ---- website/src/ui/Dropdown.style.js | 2 - website/src/ui/Input.js | 13 ++- website/src/ui/index.js | 1 - website/src/ui/style.js | 1 + 25 files changed, 112 insertions(+), 321 deletions(-) rename website/src/{helpers/Api.js => API.js} (68%) delete mode 100644 website/src/helpers/Constants.js delete mode 100644 website/src/helpers/index.js delete mode 100644 website/src/ui/Dropdown.js delete mode 100644 website/src/ui/Dropdown.style.js diff --git a/website/src/helpers/Api.js b/website/src/API.js similarity index 68% rename from website/src/helpers/Api.js rename to website/src/API.js index 41a0bce..9f87df7 100644 --- a/website/src/helpers/Api.js +++ b/website/src/API.js @@ -1,7 +1,7 @@ import EventEmitter from 'eventemitter3' import io from 'socket.io-client' -const authToken = 'deightonAuthToken' +const authTokenName = 'AuthToken' class NetworkError extends Error { constructor(message) { @@ -15,7 +15,7 @@ class NetworkError extends Error { } } -class ApiError extends Error { +class APIError extends Error { constructor(status, message) { super(message || '') this.status = status || 500 @@ -28,12 +28,12 @@ class ApiError extends Error { } } -class Api extends EventEmitter { +class API extends EventEmitter { constructor() { super() this.user = null - let token = localStorage.getItem(authToken) || sessionStorage.getItem(authToken) + let token = localStorage.getItem(authTokenName) || sessionStorage.getItem(authTokenName) if (token) { this.token = token @@ -46,8 +46,8 @@ class Api extends EventEmitter { this.emit('login') }) .catch(() => { - localStorage.removeItem(authToken) - sessionStorage.removeItem(authToken) + localStorage.removeItem(authTokenName) + sessionStorage.removeItem(authTokenName) this.token = null this.user = null this.socket = null @@ -153,7 +153,7 @@ class Api extends EventEmitter { resolve(responseBody) } } else { - reject(new ApiError(res.status, responseBody.message)) + reject(new APIError(res.status, responseBody.message)) } }).catch((error) => { reject(new NetworkError(error.message)) @@ -183,13 +183,13 @@ class Api extends EventEmitter { const [ scheme, token ] = authValue.split(' ') if (scheme !== 'Bearer' || !token) { - reject(new ApiError('Unexpected Authorization scheme or token')) + reject(new APIError('Unexpected Authorization scheme or token')) } if (remember) { - localStorage.setItem(authToken, token) + localStorage.setItem(authTokenName, token) } else { - sessionStorage.setItem(authToken, token) + sessionStorage.setItem(authTokenName, token) } this.token = token this.user = response.body @@ -204,8 +204,8 @@ class Api extends EventEmitter { logout() { let cb = () => { // Regardless of response, always logout in the client - localStorage.removeItem(authToken) - sessionStorage.removeItem(authToken) + localStorage.removeItem(authTokenName) + sessionStorage.removeItem(authTokenName) this.token = null this.user = null this.disconnectSocket() @@ -271,111 +271,6 @@ class Api extends EventEmitter { return this.put('/users/leave-room') } - getItemsForPathname(pathname) { - return this.get('/fingerprint' + pathname) - } - - getCorporation(_id, params) { - return this.get('/corporations/' + _id + Api.makeParams(params)) - } - listCorporations(params) { - return this.get('/corporations' + Api.makeParams(params)) - } - createCorporation(corporation) { - return this.post('/corporations', corporation) - } - updateCorporation(corporation) { - return this.put('/corporations', corporation) - } - deleteCorporation(_id) { - return this.delete('/corporations/' + _id) - } - setCorporationImage(details) { - // _id: corporation id - // imageId: image asset id - // size: desired size of image { width, height } - return this.put('/corporations/set-image', details) - } - - getBranch(_id, params) { - return this.get('/branches/' + _id + Api.makeParams(params)) - } - listBranches(params) { - return this.get('/branches' + Api.makeParams(params)) - } - createBranch(branch) { - return this.post('/branches', branch) - } - updateBranch(branch) { - return this.put('/branches', branch) - } - deleteBranch(_id) { - return this.delete('/branches/' + _id) - } - - getProject(projectId, params) { - return this.get('/projects/' + projectId) - } - getPopulatedProject(projectId, params) { - return this.get('/projects/' + projectId + '/populated') - } - getProjectBrokerClientData(projectId, brokerId) { - return this.get('/projects/' + projectId + '/broker/' + brokerId) - } - signOffProjectBrokerClientData(projectId, brokerId) { - return this.post('/projects/' + projectId + '/broker/' + brokerId + '/sign-off', {}) - } - createProjectPackages(projectId) { - return this.post('/projects/' + projectId + '/create-packages', {}) - } - resetProjectPackages(projectId) { - return this.post('/projects/' + projectId + '/reset-packages', {}) - } - buildProjectPDFs(projectId) { - return this.post('/projects/' + projectId + '/build-pdfs', {}) - } - listProjects(params) { - return this.get('/projects' + Api.makeParams(params)) - } - listDashboardProjects() { - return this.get('/projects/dashboard') - } - listBrokerProjects(brokerId) { - return this.get('/projects/broker/' + brokerId) - } - createProject(project) { - return this.post('/projects', project) - } - importProjectClientData(importData) { - return this.post('/projects/import-client-data', importData) - } - updateProject(project) { - return this.put('/projects', project) - } - deleteProject(_id) { - return this.delete('/projects/' + _id) - } - - getPackage(_id, params) { - return this.get('/packages/' + _id + Api.makeParams(params)) - } - listPackages(params) { // Example: listPackages({ projectId: '59c2faa32d27b9d10bd764b3' }) - return this.get('/packages' + Api.makeParams(params)) - } - - getFormSet(formSetId) { - return this.get('/formsets/' + formSetId) - } - getForm(formSetId, formId) { - return this.get('/formsets/' + formSetId + '/form/' + formId) - } - listFormSets(params) { - return this.get('/formsets' + Api.makeParams(params)) - } - setFormPDF(formSetId, formId, pdfAssetId) { - return this.post('/formsets/' + formSetId + '/form/' + formId, { pdfAssetId }) - } - upload(file, progressCallback) { return new Promise((resolve, reject) => { const chunkSize = 32 * 1024 @@ -423,4 +318,4 @@ class Api extends EventEmitter { } } -export let api = new Api() +export let api = new API() diff --git a/website/src/App.js b/website/src/App.js index ff9685f..f79549e 100644 --- a/website/src/App.js +++ b/website/src/App.js @@ -8,6 +8,7 @@ import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' import logoImage from 'images/logo.png' import { versionInfo } from './version' import { sizeInfo } from 'ui/style' +import { api } from 'src/API' export class App extends React.Component { render() { @@ -25,8 +26,8 @@ export class App extends React.Component {   - - + { api.loggedInUser && } + { api.loggedInUser && } diff --git a/website/src/Auth/ConfirmEmail.js b/website/src/Auth/ConfirmEmail.js index 59b0827..0babc50 100644 --- a/website/src/Auth/ConfirmEmail.js +++ b/website/src/Auth/ConfirmEmail.js @@ -1,5 +1,5 @@ import React from 'react' -import { api } from '../helpers' +import { api } from 'src/API' import PropTypes from 'prop-types' import { MessageModal, WaitModal } from '../Modal' diff --git a/website/src/Auth/ForgotPassword.js b/website/src/Auth/ForgotPassword.js index a00d12c..a471719 100644 --- a/website/src/Auth/ForgotPassword.js +++ b/website/src/Auth/ForgotPassword.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import { regExpPattern } from 'regexp-pattern' import { Text, Column, BoundInput, BoundButton } from 'ui' import { MessageModal, WaitModal } from '../Modal' -import { api } from '../helpers' +import { api } from 'src/API' import { FormBinder } from 'react-form-binder' export class ForgotPassword extends React.Component { diff --git a/website/src/Auth/Login.js b/website/src/Auth/Login.js index dc8d9c9..8ed6456 100644 --- a/website/src/Auth/Login.js +++ b/website/src/Auth/Login.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import { regExpPattern } from 'regexp-pattern' -import { api } from '../helpers' +import { api } from 'src/API' import { WaitModal, MessageModal } from '../Modal' import { Image, Link, Text, Row, Column, BoundInput, BoundCheckbox, BoundButton } from 'ui' import headerLogo from 'images/deighton.png' @@ -54,7 +54,7 @@ export class Login extends React.Component { return } - let obj = this.state.binder.getValues() + let obj = this.state.binder.getModifiedFieldValues() if (obj) { this.setState({ waitModal: true }) @@ -75,7 +75,7 @@ export class Login extends React.Component { elems[0].focus() } this.setState({ - binder: new FormBinder({ email: this.state.binder.getField('email').value }, Login.bindings), + binder: new FormBinder({ email: this.state.binder.getFieldValue('email').value }, Login.bindings), waitModal: false, messageModal: { title: 'Login Error...', message: `Unable to login. ${error.message}` } }) @@ -119,23 +119,22 @@ export class Login extends React.Component { - + - + + -
- +
+ -
Please contact {versionInfo.supportEmail} to request login credentials.
diff --git a/website/src/Auth/Logout.js b/website/src/Auth/Logout.js index c2bceb7..4c600a1 100644 --- a/website/src/Auth/Logout.js +++ b/website/src/Auth/Logout.js @@ -1,6 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' -import { api } from '../helpers' +import { api } from 'src/API' export class Logout extends React.Component { static propTypes = { diff --git a/website/src/Auth/ProtectedRoute.js b/website/src/Auth/ProtectedRoute.js index 7f173c2..49a8d72 100644 --- a/website/src/Auth/ProtectedRoute.js +++ b/website/src/Auth/ProtectedRoute.js @@ -1,7 +1,7 @@ import React from 'react' import { Route, Redirect } from 'react-router-dom' import { PropTypes } from 'prop-types' -import { api } from '../helpers' +import { api } from 'src/API' export class ProtectedRoute extends React.Component { static propTypes = { diff --git a/website/src/Auth/ResetPassword.js b/website/src/Auth/ResetPassword.js index dc4e74d..4032d53 100644 --- a/website/src/Auth/ResetPassword.js +++ b/website/src/Auth/ResetPassword.js @@ -2,7 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import { Text, Column, BoundInput, BoundButton } from 'ui' import { MessageModal, WaitModal } from '../Modal' -import { api } from '../helpers' +import { api } from 'src/API' import { FormBinder } from 'react-form-binder' export class ResetPassword extends React.Component { diff --git a/website/src/Profile/Profile.js b/website/src/Profile/Profile.js index 76ccf45..54c3f45 100644 --- a/website/src/Profile/Profile.js +++ b/website/src/Profile/Profile.js @@ -1,6 +1,6 @@ import React from 'react' import { ProfileForm } from './ProfileForm' -import { Constants, api } from '../helpers' +import { api } from 'src/API' import { WaitModal, MessageModal, ChangePasswordModal, ChangeEmailModal } from '../Modal' import { autoBind } from 'auto-bind2' @@ -17,8 +17,7 @@ export class Profile extends React.Component { changeEmailModal: null, progressModal: null, uploadPercent: 0, - user, - userImageUrl: api.makeImageUrl(user.imageId, Constants.bigUserImageSize) + user } } @@ -30,10 +29,6 @@ export class Profile extends React.Component { api.removeListener('newProfileImage', this.handleNewProfileImage) } - handleNewProfileImage(data) { - this.setState({ userImageUrl: api.makeImageUrl(data.imageId, Constants.bigUserImageSize) }) - } - handleSaved(user) { this.setState({ waitModal: { message: 'Updating Profile' } }) api.updateUser(user).then((updatedUser) => { @@ -57,25 +52,6 @@ export class Profile extends React.Component { this.setState({ changePasswordModal: true }) } - handleSelectImage(file) { - this.setState({ progressModal: { message: `Uploading image '${file.name}'...`, file }, uploadPercent: 0 }) - api.upload(file, this.handleProgress).then((uploadData) => { - this.setState({ progressModal: null }) - return api.setUserImage({ - _id: api.loggedInUser._id, - imageId: uploadData.assetId, - bigSize: Profile.bigUserImageSize, - smallSize: Constants.smallUserImageSize - }) - }).catch((error) => { - // TODO: if the upload succeeds but the setUserImage fails, delete the uploaded image - this.setState({ - progressModal: null, - messageModal: { title: 'Upload Error...', message: `Unable to upload the file. ${error.message}` } - }) - }) - } - handleProgress(uploadData) { if (this.state.progressModal) { this.setState({ uploadPercent: Math.round(uploadData.uploadedChunks / uploadData.numberOfChunks * 100) }) diff --git a/website/src/Users/UserForm.js b/website/src/Users/UserForm.js index 919b8c3..2c89459 100644 --- a/website/src/Users/UserForm.js +++ b/website/src/Users/UserForm.js @@ -2,7 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import { reactAutoBind } from 'auto-bind2' import { regExpPattern } from 'regexp-pattern' -import { Constants, api } from '../helpers' +import { api } from 'src/API' import { Column, BoundInput, BoundButton, BoundCheckbox, BoundEmailIcon } from 'ui' import { FormBinder } from 'react-form-binder' @@ -103,9 +103,7 @@ export class UserForm extends React.Component {
- + - - + { this.state.users ? this.state.users.map((user, index) => ( - + { user._id ? user.firstName + ' ' + user.lastName : '[New User]' } { user === this.props.selectedUser && this.props.selectionModified ? - - - - ) - } else { - return null - } + const { name, width, content, submit, onClick } = this.props + const { visible, disabled } = this.state + + return ( + + ) } } diff --git a/website/src/ui/BoundCheckbox.js b/website/src/ui/BoundCheckbox.js index d091f6d..c947251 100644 --- a/website/src/ui/BoundCheckbox.js +++ b/website/src/ui/BoundCheckbox.js @@ -32,8 +32,11 @@ export default class BoundCheckbox extends React.Component { } render() { + const { name, label } = this.props + const { visible, disabled, value } = this.state + return ( - + ) } } diff --git a/website/src/ui/BoundInput.js b/website/src/ui/BoundInput.js index eb7745f..152fc34 100644 --- a/website/src/ui/BoundInput.js +++ b/website/src/ui/BoundInput.js @@ -19,12 +19,12 @@ export default class BoundInput extends React.Component { this.state = props.binder.getFieldState(props.name) } - handleChange(e, data) { + handleChange(e) { const { binder, name } = this.props const state = binder.getFieldState(name) if (!state.readOnly && !state.disabled) { - this.setState(binder.updateFieldValue(name, data.value)) + this.setState(binder.updateFieldValue(name, e.target.value)) } } @@ -35,17 +35,22 @@ export default class BoundInput extends React.Component { } render() { + const { label, password, name, placeholder, message } = this.props + const { visible, disabled, value, valid } = this.state + return ( -
- {this.props.label} +
+ {label}
- + placeholder={placeholder} />
- {this.props.message} + {valid ? ' ' : message}
) } diff --git a/website/src/ui/Button.js b/website/src/ui/Button.js index f24b26e..87d1f97 100644 --- a/website/src/ui/Button.js +++ b/website/src/ui/Button.js @@ -7,13 +7,20 @@ class Button extends Component { static propTypes = { submit: PropTypes.bool, children: PropTypes.node, - width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]) + width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), + visible: PropTypes.bool, + disabled: PropTypes.bool, + name: PropTypes.name, } render() { - const { children, submit, width } = this.props + const { name, children, submit, width, visible, disabled } = this.props + return ( - + ) } } diff --git a/website/src/ui/Button.style.js b/website/src/ui/Button.style.js index f247567..9298d40 100644 --- a/website/src/ui/Button.style.js +++ b/website/src/ui/Button.style.js @@ -2,17 +2,20 @@ import { colorInfo, fontInfo } from './style' export default { base: { + height: '100%', borderRadius: '10px', fontFamily: fontInfo.family, color: '#FFFFFF', - fontSize: fontInfo.size['large'], + fontSize: fontInfo.size.large, background: colorInfo.buttonBackgroundHover, verticalAlign: 'middle', - padding: '8px 15px 8px 15px', + padding: '0 15px 0 15px', outline: 'none', - ':hover': { background: colorInfo.buttonBackground, + }, + ':disabled': { + background: colorInfo.disabledButtonBackground, } } } diff --git a/website/src/ui/Checkbox.js b/website/src/ui/Checkbox.js index 1d3d72b..347f400 100644 --- a/website/src/ui/Checkbox.js +++ b/website/src/ui/Checkbox.js @@ -9,6 +9,9 @@ class Checkbox extends Component { static propTypes = { value: PropTypes.bool, label: PropTypes.string, + visible: PropTypes.bool, + // disabled: PropTypes.bool, + name: PropTypes.name, } constructor(props) { @@ -24,18 +27,27 @@ class Checkbox extends Component { } render() { + const { visible, name, label } = this.props + return ( -
+
- {this.props.label} + { /* TODO: Move this into .style.js */ } + { /* TODO: Implement disabled */ } + { /* TODO: Add checkbox input element back in */ } + { /* TODO: Use an actual label element with a for (shortid) */ } + + {label} +
) } diff --git a/website/src/ui/Dropdown.js b/website/src/ui/Dropdown.js deleted file mode 100644 index aa8c293..0000000 --- a/website/src/ui/Dropdown.js +++ /dev/null @@ -1,19 +0,0 @@ -import Radium from 'radium' -import PropTypes from 'prop-types' -import React, { Component } from 'react' -import style from './Input.style' - -class Dropdown extends Component { - static propTypes = { - password: PropTypes.bool, - children: PropTypes.node - } - - render() { - return ( - {this.props.children} - ) - } -} - -export default Radium(Dropdown) diff --git a/website/src/ui/Dropdown.style.js b/website/src/ui/Dropdown.style.js deleted file mode 100644 index efba7fa..0000000 --- a/website/src/ui/Dropdown.style.js +++ /dev/null @@ -1,2 +0,0 @@ -export default { -} diff --git a/website/src/ui/Input.js b/website/src/ui/Input.js index c11232f..ca662f6 100644 --- a/website/src/ui/Input.js +++ b/website/src/ui/Input.js @@ -6,17 +6,22 @@ import style from './Input.style' class Input extends Component { static propTypes = { password: PropTypes.bool, - children: PropTypes.node, - width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]) + placeholder: PropTypes.string, + width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), + onChange: PropTypes.func, + visible: PropTypes.bool, + disabled: PropTypes.bool, + name: PropTypes.string, } render() { - let { children, width } = this.props + let { name, width, password, placeholder, onChange, visible, disabled } = this.props width = width || '100%' return ( - {children} + ) } } diff --git a/website/src/ui/index.js b/website/src/ui/index.js index c464e39..78e8545 100644 --- a/website/src/ui/index.js +++ b/website/src/ui/index.js @@ -8,7 +8,6 @@ export { default as Text } from './Text' export { default as Link } from './Link' export { default as Icon } from './Icon' export { default as List } from './List' -export { default as Dropdown } from './Dropdown' export { default as Modal } from './Modal' export { default as Dimmer } from './Dimmer' export { default as Loader } from './Loader' diff --git a/website/src/ui/style.js b/website/src/ui/style.js index f0f993a..13e5280 100644 --- a/website/src/ui/style.js +++ b/website/src/ui/style.js @@ -6,6 +6,7 @@ export const colorInfo = { buttonBackgroundHover: '#3CB0FD', headerButtonBackground: '#FAFAFA', headerButtonBackgroundHover: '#DADADA', + disabledButtonBackground: '#A0A0A0', } export const sizeInfo = {