From 0979c9d38c7c67a5d328df2dcbb8cbb3ea3093e3 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Mon, 8 Oct 2018 13:59:28 -0500 Subject: [PATCH 01/56] Log errors in api.js --- web/src/api.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/web/src/api.js b/web/src/api.js index 8e8e5ee8..a2585ac8 100644 --- a/web/src/api.js +++ b/web/src/api.js @@ -30,6 +30,7 @@ api.interceptors.response.use(config => { let creds = getCredentials(); if (error.response.status === 401 && creds && creds.refreshToken) { + let originalError = error; let data = JSON.stringify(creds.refreshToken); let headers = { headers: { 'Content-Type': 'application/json' }}; @@ -43,13 +44,28 @@ api.interceptors.response.use(config => { }, error => { deleteCredentials(); window.location.reload(true); + logError(originalError); return Promise.reject(error); } ); } else { + logError(error); return Promise.reject(error); } }) +const logError = (error) => { + if (error.response) { + console.log(error.response.data); + console.log(error.response.status); + console.log(error.response.headers); + } else if (error.request) { + console.log(error.request); + } else { + console.log('Error: ', error.message); + } + console.log(error.config); +} + export default api; \ No newline at end of file From efd5be4ddcc708be517e880b32dca6d8c0f6cdb0 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Mon, 8 Oct 2018 14:04:11 -0500 Subject: [PATCH 02/56] Use api instead of axios for LoginForm --- web/src/security/LoginForm.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/src/security/LoginForm.js b/web/src/security/LoginForm.js index c2034db7..69dc83ba 100644 --- a/web/src/security/LoginForm.js +++ b/web/src/security/LoginForm.js @@ -4,9 +4,8 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import axios from 'axios'; +import api from '../api'; -import { getEnvironment } from '../util'; import { withStyles } from '@material-ui/core/styles'; import { Card, CardContent, CardActions, CircularProgress } from '@material-ui/core'; @@ -99,7 +98,7 @@ class LoginForm extends Component { api: { isExecuting: true }, }, () => { - axios.post(getEnvironment().apiRoot + '/v1/security/login', this.state) + api.post('/v1/security/login', this.state) .then( response => { this.props.onLogin(response.data, this.state.rememberMe); From 80f3713d785509f78f65134de26233e1b371d1a0 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Mon, 8 Oct 2018 14:05:07 -0500 Subject: [PATCH 03/56] Remove duplicate logging --- web/src/security/LoginForm.js | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/security/LoginForm.js b/web/src/security/LoginForm.js index 69dc83ba..007c598c 100644 --- a/web/src/security/LoginForm.js +++ b/web/src/security/LoginForm.js @@ -118,7 +118,6 @@ class LoginForm extends Component { }); } else { - console.log(error) this.setState({ snackbar: { message: 'Error: ' + error.message, From 5549276888be476343967710cd09c2504b15902f Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Mon, 8 Oct 2018 14:30:36 -0500 Subject: [PATCH 04/56] Properly log errors after refresh --- web/src/api.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/src/api.js b/web/src/api.js index a2585ac8..01244117 100644 --- a/web/src/api.js +++ b/web/src/api.js @@ -30,21 +30,19 @@ api.interceptors.response.use(config => { let creds = getCredentials(); if (error.response.status === 401 && creds && creds.refreshToken) { - let originalError = error; let data = JSON.stringify(creds.refreshToken); let headers = { headers: { 'Content-Type': 'application/json' }}; // use 'axios' here instead of the 'api' instance we created to bypass our interceptors - // and avoid an endless loop should either of these two calls result in a 401. + // and avoid an endless loop should this call result in a 401. return axios.post('/v1/security/refresh', data, headers) .then(response => { updateCredentials(response.data); request.headers.Authorization = response.data.tokenType + ' ' + response.data.accessToken; - return axios(request); + return api(request); }, error => { deleteCredentials(); window.location.reload(true); - logError(originalError); return Promise.reject(error); } ); From 026aabec86f35c42ae1238781427851e6ad2b8f5 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Mon, 8 Oct 2018 19:55:50 -0500 Subject: [PATCH 05/56] include card number in scan 404 --- api/QCVOC.Api/Scans/Controller/ScansController.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/QCVOC.Api/Scans/Controller/ScansController.cs b/api/QCVOC.Api/Scans/Controller/ScansController.cs index dac5497a..9ad15256 100644 --- a/api/QCVOC.Api/Scans/Controller/ScansController.cs +++ b/api/QCVOC.Api/Scans/Controller/ScansController.cs @@ -10,7 +10,6 @@ namespace QCVOC.Api.Scans.Controller using System.Linq; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.ModelBinding; using QCVOC.Api.Common; using QCVOC.Api.Common.Data.Repository; using QCVOC.Api.Events.Data.Model; @@ -108,7 +107,7 @@ public IActionResult Scan([FromBody]ScanRequest scan) if (veteran == default(Veteran)) { - return StatusCode(404, "The specified Card Id doesn't match an enrolled Veteran."); + return StatusCode(404, $"Card Number {scan.CardNumber} doesn't match an enrolled Veteran."); } var scanRecord = new Scan() From 7fb8e0b33d07f8867598e25d3dfd9c391463fe40 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Mon, 8 Oct 2018 20:40:59 -0500 Subject: [PATCH 06/56] add css for menu --- web/src/security/style.css | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 web/src/security/style.css diff --git a/web/src/security/style.css b/web/src/security/style.css new file mode 100644 index 00000000..5456b12b --- /dev/null +++ b/web/src/security/style.css @@ -0,0 +1,14 @@ +/* + Copyright (c) QC Coders (JP Dillingham, Nick Acosta, Will Burklund, et. al.). All rights reserved. Licensed under the GPLv3 license. See LICENSE file + in the project root for full license information. +*/ + +.menu { + margin-top: 40px; +} + +@media screen and (max-width: 599px) { + .menu { + margin-top: 32px; + } +} \ No newline at end of file From 41f7b9e53ecde156810b1f3ae143243f29dce158 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Mon, 8 Oct 2018 20:41:16 -0500 Subject: [PATCH 07/56] swap inline css for imported --- web/src/security/SecurityMenu.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/security/SecurityMenu.js b/web/src/security/SecurityMenu.js index efad2f90..a5661687 100644 --- a/web/src/security/SecurityMenu.js +++ b/web/src/security/SecurityMenu.js @@ -2,10 +2,13 @@ Copyright (c) QC Coders (JP Dillingham, Nick Acosta, Will Burklund, et. al.). All rights reserved. Licensed under the GPLv3 license. See LICENSE file in the project root for full license information. */ + import React, { Component } from 'react'; import PropTypes from 'proptypes'; import api from '../api'; +import './style.css'; + import IconButton from '@material-ui/core/IconButton'; import Typography from '@material-ui/core/Typography'; import { LockOpen, ExitToApp, AccountCircle } from '@material-ui/icons'; @@ -25,9 +28,6 @@ const styles = { icon: { fontSize: 29, }, - menu: { - marginTop: 40, - }, } const initialState = { @@ -121,7 +121,7 @@ class SecurityMenu extends Component { open={menu.open} anchorEl={menu.anchorEl} onClose={this.handleMenuClose} - style={styles.menu} + className={'menu'} > From 9c784d5190ad5312870101aface7b94fb7e9a586 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 07:25:36 -0500 Subject: [PATCH 08/56] remove timed scan clear --- web/src/scans/Scanner.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 2ec35dfa..2ca9ea9a 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -160,10 +160,6 @@ class Scanner extends Component { this.setState({ scan: scan, history: history, - }, () => { - setTimeout(() => { - this.setState({ scan: initialState.scan }); - }, 2500); }); } From ae26ea3ca8b77066051b2955944de2a0d05d7122 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 07:26:14 -0500 Subject: [PATCH 09/56] add clearLastScan() --- web/src/scans/Scanner.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 2ca9ea9a..f0a80902 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -170,6 +170,10 @@ class Scanner extends Component { }); } + clearLastScan = () => { + this.setState({ scan: initialState.scan }); + } + fetchEvents = (apiType) => { let start = moment().startOf('day').format(); let end = moment().endOf('day').format(); From 6218565821c803eef91ff70798273fc92bf87574 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 07:27:16 -0500 Subject: [PATCH 10/56] pass clearLastScan to ScannerMenu --- web/src/scans/Scanner.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index f0a80902..7ebf0af4 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -294,6 +294,7 @@ class Scanner extends Component { this.setState({ historyDialog: { open: true }})} /> From c9719c1662a5db0c9610c35779bc3793402faf90 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 07:39:09 -0500 Subject: [PATCH 11/56] add Clear Last Scan menu item --- web/src/scans/ScannerMenu.js | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/web/src/scans/ScannerMenu.js b/web/src/scans/ScannerMenu.js index ff13f52a..4ee47571 100644 --- a/web/src/scans/ScannerMenu.js +++ b/web/src/scans/ScannerMenu.js @@ -57,6 +57,12 @@ class ScannerMenu extends Component { }); } + handleClearLastScanClick = () => { + this.setState({ menu: { open: false }}, () => { + this.props.clearLastScan(); + }) + } + resetScanner = () => { return new Promise((resolve) => { this.props.resetScanner(resolve); @@ -82,14 +88,24 @@ class ScannerMenu extends Component { onClose={this.handleMenuClose} > {configured && - - - - - - View Scan History - - +
+ + + + + + Clear Last Scan + + + + + + + + View Scan History + + +
} From 99d5bde82c51c559c12cd33464d0988880b533d7 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 08:03:25 -0500 Subject: [PATCH 12/56] conditionally show clear if scan exists --- web/src/scans/Scanner.js | 4 +++- web/src/scans/ScannerMenu.js | 16 +++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 7ebf0af4..71e40294 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -86,8 +86,9 @@ const initialState = { service: undefined, }, scan: { + cardNumber: undefined, status: undefined, - data: undefined, + response: undefined, }, events: [], services: [], @@ -294,6 +295,7 @@ class Scanner extends Component { this.setState({ historyDialog: { open: true }})} diff --git a/web/src/scans/ScannerMenu.js b/web/src/scans/ScannerMenu.js index 4ee47571..8e96e5aa 100644 --- a/web/src/scans/ScannerMenu.js +++ b/web/src/scans/ScannerMenu.js @@ -8,7 +8,7 @@ import PropTypes from 'proptypes'; import { withStyles } from '@material-ui/core/styles'; import IconButton from '@material-ui/core/IconButton'; -import { MoreVert, Replay, History } from '@material-ui/icons'; +import { MoreVert, Replay, History, ArrowBack } from '@material-ui/icons'; import ConfirmDialog from '../shared/ConfirmDialog'; import { Menu, MenuItem, ListItemIcon, ListItemText } from '@material-ui/core'; @@ -70,7 +70,7 @@ class ScannerMenu extends Component { } render() { - let { classes, visible, configured } = this.props; + let { classes, visible, configured, lastScan } = this.props; let { menu } = this.state; return ( @@ -88,15 +88,15 @@ class ScannerMenu extends Component { onClose={this.handleMenuClose} > {configured && -
- +
+ {lastScan && lastScan.status && - + Clear Last Scan - + } @@ -109,7 +109,7 @@ class ScannerMenu extends Component { } - + Reset Scanner @@ -134,6 +134,8 @@ ScannerMenu.propTypes = { visible: PropTypes.bool, configured: PropTypes.bool, resetScanner: PropTypes.func.isRequired, + lastScan: PropTypes.object, + clearLastScan: PropTypes.func.isRequired, viewHistory: PropTypes.func.isRequired, } From 71dce1bcb3f19dfadb54a72e4576060876e9b8e6 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 08:07:38 -0500 Subject: [PATCH 13/56] add divider --- web/src/scans/ScannerMenu.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/scans/ScannerMenu.js b/web/src/scans/ScannerMenu.js index 8e96e5aa..187d7383 100644 --- a/web/src/scans/ScannerMenu.js +++ b/web/src/scans/ScannerMenu.js @@ -10,7 +10,7 @@ import { withStyles } from '@material-ui/core/styles'; import IconButton from '@material-ui/core/IconButton'; import { MoreVert, Replay, History, ArrowBack } from '@material-ui/icons'; import ConfirmDialog from '../shared/ConfirmDialog'; -import { Menu, MenuItem, ListItemIcon, ListItemText } from '@material-ui/core'; +import { Menu, MenuItem, ListItemIcon, ListItemText, Divider } from '@material-ui/core'; const styles = { container: { @@ -107,6 +107,7 @@ class ScannerMenu extends Component {
} + From 5805ccb33cb536c342a644f669c75e6a3895ac1f Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 08:17:18 -0500 Subject: [PATCH 14/56] simplify handling of menu close --- web/src/scans/ScannerMenu.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/web/src/scans/ScannerMenu.js b/web/src/scans/ScannerMenu.js index 187d7383..3aad005b 100644 --- a/web/src/scans/ScannerMenu.js +++ b/web/src/scans/ScannerMenu.js @@ -40,7 +40,7 @@ class ScannerMenu extends Component { } handleMenuClose = () => { - this.setState({ menu: { open: false }}); + this.close(); } handleConfirmDialogClose = (result) => { @@ -48,19 +48,15 @@ class ScannerMenu extends Component { } handleResetScannerClick = () => { - this.setState({ menu: { open: false }, confirmDialog: { open: true }}); + this.close().then(() => this.setState({ confirmDialog: { open: true }})); } handleHistoryClick = () => { - this.setState({ menu: { open: false }}, () => { - this.props.viewHistory(); - }); + this.close().then(() => this.props.viewHistory()); } handleClearLastScanClick = () => { - this.setState({ menu: { open: false }}, () => { - this.props.clearLastScan(); - }) + this.close().then(() => this.props.clearLastScan()); } resetScanner = () => { @@ -69,6 +65,12 @@ class ScannerMenu extends Component { }); } + close = () => { + return new Promise(resolve => { + this.setState({ menu: { open: false }}, () => resolve()); + }) + } + render() { let { classes, visible, configured, lastScan } = this.props; let { menu } = this.state; From da085fc9692d83c5e8f18b9ac04a11818d6d04a0 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 14:48:32 -0500 Subject: [PATCH 15/56] Move role check from EventList to Events --- web/src/events/EventList.js | 4 ++-- web/src/events/Events.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/src/events/EventList.js b/web/src/events/EventList.js index 9ea0816d..a550ee7f 100644 --- a/web/src/events/EventList.js +++ b/web/src/events/EventList.js @@ -36,8 +36,8 @@ const EventList = (props) => { {events.sort(sortByProp('startDate')).map((e, index) => onItemClick(e) : () => {}} + button={onItemClick !== undefined} + onClick={onItemClick !== undefined ? () => onItemClick(e) : () => {}} > {icon} diff --git a/web/src/events/Events.js b/web/src/events/Events.js index 03114c45..adcc1df0 100644 --- a/web/src/events/Events.js +++ b/web/src/events/Events.js @@ -156,19 +156,19 @@ class Events extends Component { } - onItemClick={this.handleEditClick} + onItemClick={userCanView() ? this.handleEditClick : undefined} /> Upcoming } - onItemClick={this.handleEditClick} + onItemClick={userCanView() ? this.handleEditClick : undefined} /> Past } - onItemClick={this.handleEditClick} + onItemClick={userCanView() ? this.handleEditClick : undefined} />
} From 5c5ae11bb49654bab7f5b32a8f705604d818be9b Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 14:58:53 -0500 Subject: [PATCH 16/56] Remove unused userCanView import --- web/src/events/EventList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/events/EventList.js b/web/src/events/EventList.js index a550ee7f..575e85c7 100644 --- a/web/src/events/EventList.js +++ b/web/src/events/EventList.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import moment from 'moment'; -import { sortByProp, userCanView } from '../util'; +import { sortByProp } from '../util'; import { List, ListItem, ListItemIcon, ListItemText } from '@material-ui/core'; From a61e6a90079b9b5bfc14b2a71acdb0cae790d0ab Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Mon, 8 Oct 2018 22:14:30 -0500 Subject: [PATCH 17/56] Implement React context --- web/src/shared/ServiceContext.js | 79 ++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 web/src/shared/ServiceContext.js diff --git a/web/src/shared/ServiceContext.js b/web/src/shared/ServiceContext.js new file mode 100644 index 00000000..32a90417 --- /dev/null +++ b/web/src/shared/ServiceContext.js @@ -0,0 +1,79 @@ +import React, { Component } from 'react'; +import Snackbar from '@material-ui/core/Snackbar'; + +const ServiceContext = React.createContext(); + +const initialState = { + snackbar: { + message: '', + open: false, + } +} + +class ServiceProvider extends Component { + state = initialState; + + showMessage = (message) => { + this.setState({ snackbar: { open: true, message: message }}) + } + + showErrorMessage = (error) => { + if (error.response && error.response.data) { + if (error.response.status === 500) { + this.showMessage(error.response.data.Message); + } else { + this.showMessage(error.response.data); + } + } + } + + apiCall = (command, ...args) => { + return new Promise ((resolve, reject) => + command(...args) + .then( + response => { + resolve(response); + }, + error => { + this.showErrorMessage(error); + reject(error); + } + ) + ); + } + + handleSnackbarClose = () => { + this.setState({ snackbar: { message: '', open: false }}); + } + + render() { + return ( + +
+ {this.props.children} + {this.state.snackbar.message}} + /> +
+
+ ); + } +} + +export const withContext = (Component) => { + return (props) => { + return ( + + {context => ()} + + ); + }; +} + +export default ServiceProvider; From b86b8874b1c22c411dfcb6e7f4080a02ff56c008 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Mon, 8 Oct 2018 22:15:49 -0500 Subject: [PATCH 18/56] Add context provider --- web/src/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/index.js b/web/src/index.js index 9ccef0fc..e578ce47 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -7,6 +7,7 @@ import ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; import MuiPickersUtilsProvider from 'material-ui-pickers/utils/MuiPickersUtilsProvider'; import MomentUtils from 'material-ui-pickers/utils/moment-utils'; +import ServiceProvider from './shared/ServiceContext'; import './index.css'; import App from './app/App'; @@ -14,7 +15,9 @@ import App from './app/App'; ReactDOM.render( - + + + , document.getElementById('root') From e7df0340cd6fe7751f44e9e48fdfc26e1a6b6035 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Mon, 8 Oct 2018 22:24:34 -0500 Subject: [PATCH 19/56] Migrate AccountDialog to context --- web/src/accounts/AccountDialog.js | 37 ++++--------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/web/src/accounts/AccountDialog.js b/web/src/accounts/AccountDialog.js index 0ba9b1f1..52277977 100644 --- a/web/src/accounts/AccountDialog.js +++ b/web/src/accounts/AccountDialog.js @@ -6,6 +6,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import { withStyles } from '@material-ui/core/styles'; import { @@ -22,7 +23,6 @@ import { } from '@material-ui/core'; import CircularProgress from '@material-ui/core/CircularProgress'; -import Snackbar from '@material-ui/core/Snackbar'; import ConfirmDialog from '../shared/ConfirmDialog'; @@ -70,10 +70,6 @@ const initialState = { password: undefined, password2: undefined, }, - snackbar: { - message: '', - open: false, - }, confirmDialog: { open: false, }, @@ -152,14 +148,10 @@ class AccountDialog extends Component { this.setState({ confirmDialog: { open: false }}); } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - execute = (action, api, successMessage) => { return new Promise((resolve, reject) => { this.setState({ [api]: { isExecuting: true }}, () => { - action(this.state.account) + this.props.context.apiCall(action, this.state.account) .then(response => { this.setState({ [api]: { isExecuting: false, isErrored: false } @@ -168,22 +160,8 @@ class AccountDialog extends Component { resolve(response); }) }, error => { - var body = error && error.response && error.response.data ? error.response.data : error; - - if (typeof(body) !== 'string') { - var keys = Object.keys(body); - - if (keys.length > 0) { - body = body[keys[0]]; - } - } - this.setState({ - [api]: { isExecuting: false, isErrored: true }, - snackbar: { - message: body, - open: true, - }, + [api]: { isExecuting: false, isErrored: true } }, () => reject(error)); }) }) @@ -337,13 +315,6 @@ class AccountDialog extends Component { >

Are you sure you want to delete account '{this.state.account.name}'?

- {this.state.snackbar.message}} - /> ); } @@ -357,4 +328,4 @@ AccountDialog.propTypes = { account: PropTypes.object, }; -export default withStyles(styles)(AccountDialog); \ No newline at end of file +export default withContext(withStyles(styles)(AccountDialog)); \ No newline at end of file From cb94a6e222cfa3176e7270fabf797aaa208e33a0 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 18:50:17 -0500 Subject: [PATCH 20/56] Fix merge error --- web/src/accounts/AccountDialog.js | 11 ++++----- web/src/accounts/Accounts.js | 29 ++++++----------------- web/src/index.js | 6 ++--- web/src/shared/ServiceContext.js | 39 ++++++++++++++++++++++--------- 4 files changed, 43 insertions(+), 42 deletions(-) diff --git a/web/src/accounts/AccountDialog.js b/web/src/accounts/AccountDialog.js index 52277977..9cef2131 100644 --- a/web/src/accounts/AccountDialog.js +++ b/web/src/accounts/AccountDialog.js @@ -5,7 +5,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import api from '../api'; import { withContext } from '../shared/ServiceContext'; import { withStyles } from '@material-ui/core/styles'; @@ -114,14 +113,14 @@ class AccountDialog extends Component { if (result.isValid) { if (this.props.intent === 'add') { this.execute( - () => api.post('/v1/security/accounts', account), + () => this.props.context.apiPost('/v1/security/accounts', account), 'addApi', 'Account \'' + account.name + '\' successfully created.' ) } else { this.execute( - () => api.patch('/v1/security/accounts/' + account.id, account), + () => this.props.context.apiPatch('/v1/security/accounts/' + account.id, account), 'updateApi', 'Account \'' + account.name + '\' successfully updated.' ); @@ -138,7 +137,7 @@ class AccountDialog extends Component { let account = this.state.account; return this.execute( - () => api.delete('/v1/security/accounts/' + account.id), + () => this.props.context.apiDelete('/v1/security/accounts/' + account.id), 'deleteApi', 'Account \'' + account.name + '\' successfully deleted.' ); @@ -151,7 +150,7 @@ class AccountDialog extends Component { execute = (action, api, successMessage) => { return new Promise((resolve, reject) => { this.setState({ [api]: { isExecuting: true }}, () => { - this.props.context.apiCall(action, this.state.account) + action(this.state.account) .then(response => { this.setState({ [api]: { isExecuting: false, isErrored: false } @@ -328,4 +327,4 @@ AccountDialog.propTypes = { account: PropTypes.object, }; -export default withContext(withStyles(styles)(AccountDialog)); \ No newline at end of file +export default withStyles(styles)(withContext(AccountDialog)); \ No newline at end of file diff --git a/web/src/accounts/Accounts.js b/web/src/accounts/Accounts.js index 1c74b62c..e9953289 100644 --- a/web/src/accounts/Accounts.js +++ b/web/src/accounts/Accounts.js @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import AccountList from './AccountList'; import ContentWrapper from '../shared/ContentWrapper'; @@ -21,7 +21,6 @@ import { } from '@material-ui/core'; import { Add } from '@material-ui/icons' -import Snackbar from '@material-ui/core/Snackbar'; import CircularProgress from '@material-ui/core/CircularProgress'; const styles = { @@ -69,10 +68,6 @@ class Accounts extends Component { open: false, account: undefined, }, - snackbar: { - message: '', - open: false, - }, }; componentWillMount = () => { @@ -81,7 +76,7 @@ class Accounts extends Component { refresh = (apiType) => { this.setState({ ...this.state, [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - api.get('/v1/security/accounts') + this.props.context.apiGet('/v1/security/accounts') .then(response => { this.setState({ accounts: response.data, @@ -91,7 +86,6 @@ class Accounts extends Component { this.setState({ ...this.state, [apiType]: { isExecuting: false, isErrored: true }, - snackbar: { message: error.response.data, open: true }, }); }); }) @@ -133,7 +127,8 @@ class Accounts extends Component { } }, () => { if (result) { - this.setState({ snackbar: { message: result, open: true }}, () => this.refresh('refreshApi')); + this.props.context.showMessage(result); + this.refresh('refreshApi'); } }) } @@ -146,15 +141,12 @@ class Accounts extends Component { } }, () => { if (result) { - this.setState({ snackbar: { message: result, open: true }}, () => this.props.onPasswordReset()); + this.props.context.showMessage(result); + this.props.onPasswordReset(); } }) } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - render() { let { accounts, loadApi, refreshApi, accountDialog, passwordResetDialog } = this.state; let { classes } = this.props; @@ -197,13 +189,6 @@ class Accounts extends Component { onClose={this.handlePasswordResetDialogClose} /> - {this.state.snackbar.message}} - /> ); } @@ -213,4 +198,4 @@ Accounts.propTypes = { classes: PropTypes.object.isRequired, }; -export default withStyles(styles)(Accounts); \ No newline at end of file +export default withStyles(styles)(withContext(Accounts)); \ No newline at end of file diff --git a/web/src/index.js b/web/src/index.js index e578ce47..7b8a5804 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -7,7 +7,7 @@ import ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; import MuiPickersUtilsProvider from 'material-ui-pickers/utils/MuiPickersUtilsProvider'; import MomentUtils from 'material-ui-pickers/utils/moment-utils'; -import ServiceProvider from './shared/ServiceContext'; +import ContextProvider from './shared/ServiceContext'; import './index.css'; import App from './app/App'; @@ -15,9 +15,9 @@ import App from './app/App'; ReactDOM.render( - + - + , document.getElementById('root') diff --git a/web/src/shared/ServiceContext.js b/web/src/shared/ServiceContext.js index 32a90417..3616d829 100644 --- a/web/src/shared/ServiceContext.js +++ b/web/src/shared/ServiceContext.js @@ -1,5 +1,11 @@ +/* + Copyright (c) QC Coders. All rights reserved. Licensed under the GPLv3 license. See LICENSE file + in the project root for full license information. +*/ + import React, { Component } from 'react'; import Snackbar from '@material-ui/core/Snackbar'; +import api from '../api'; const ServiceContext = React.createContext(); @@ -10,7 +16,7 @@ const initialState = { } } -class ServiceProvider extends Component { +class ContextProvider extends Component { state = initialState; showMessage = (message) => { @@ -42,6 +48,12 @@ class ServiceProvider extends Component { ); } + apiDelete = (...args) => { return this.apiCall(api.delete, ...args); } + apiGet = (...args) => { return this.apiCall(api.get, ...args); } + apiPatch = (...args) => { return this.apiCall(api.patch, ...args); } + apiPost = (...args) => { return this.apiCall(api.post, ...args); } + apiPut = (...args) => { return this.apiCall(api.put, ...args); } + handleSnackbarClose = () => { this.setState({ snackbar: { message: '', open: false }}); } @@ -50,18 +62,23 @@ class ServiceProvider extends Component { return ( + apiDelete: this.apiDelete, + apiGet: this.apiGet, + apiPatch: this.apiPatch, + apiPost: this.apiPost, + apiPut: this.apiPut}} + >
- {this.props.children} + {this.props.children} {this.state.snackbar.message}} - /> + anchorOrigin={{ vertical: 'bottom', horizontal: 'center'}} + open={this.state.snackbar.open} + onClose={this.handleSnackbarClose} + autoHideDuration={3000} + message={{this.state.snackbar.message}} + />
-
+ ); } } @@ -76,4 +93,4 @@ export const withContext = (Component) => { }; } -export default ServiceProvider; +export default ContextProvider; From 95c99e2c627dc2e6c04d16262ed73dd3488bbc0f Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 18:58:08 -0500 Subject: [PATCH 21/56] Migrate App to context --- web/src/app/App.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/app/App.js b/web/src/app/App.js index de927ac6..54514544 100644 --- a/web/src/app/App.js +++ b/web/src/app/App.js @@ -7,7 +7,7 @@ import React, { Component } from 'react'; import { Route, Switch, withRouter } from 'react-router-dom'; import PropTypes from 'prop-types'; import { getCredentials, saveLocalCredentials, saveSessionCredentials, deleteCredentials, updateCredentials } from '../credentialStore'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext' import { withStyles } from '@material-ui/core/styles'; import { People, VerifiedUser, Assignment, InsertInvitation, SpeakerPhone } from '@material-ui/icons'; @@ -73,7 +73,7 @@ class App extends Component { componentDidMount = () => { if (getCredentials()) { this.setState({ api: { ...this.state.api, isExecuting: true }}, () => { - api.get('/v1/security').then(() => { + this.props.context.apiGet('/v1/security').then(() => { this.setState({ api: { isExecuting: false, isErrored: false }, credentials: getCredentials() @@ -182,4 +182,4 @@ App.propTypes = { classes: PropTypes.object.isRequired, }; -export default withRouter(withStyles(styles)(App)); \ No newline at end of file +export default withRouter(withStyles(styles)(withContext(App))); \ No newline at end of file From f831ec60edebed4bccb2296eadbf21b904d7c543 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 18:59:41 -0500 Subject: [PATCH 22/56] Add trailing semicolon --- web/src/app/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/app/App.js b/web/src/app/App.js index 54514544..35fdbb11 100644 --- a/web/src/app/App.js +++ b/web/src/app/App.js @@ -7,7 +7,7 @@ import React, { Component } from 'react'; import { Route, Switch, withRouter } from 'react-router-dom'; import PropTypes from 'prop-types'; import { getCredentials, saveLocalCredentials, saveSessionCredentials, deleteCredentials, updateCredentials } from '../credentialStore'; -import { withContext } from '../shared/ServiceContext' +import { withContext } from '../shared/ServiceContext'; import { withStyles } from '@material-ui/core/styles'; import { People, VerifiedUser, Assignment, InsertInvitation, SpeakerPhone } from '@material-ui/icons'; From fac9818ba284bb166aa32c6e0844403a58392a5a Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 19:04:02 -0500 Subject: [PATCH 23/56] Migrate EventDialog to context --- web/src/events/EventDialog.js | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/web/src/events/EventDialog.js b/web/src/events/EventDialog.js index e0566226..5a0ddf20 100644 --- a/web/src/events/EventDialog.js +++ b/web/src/events/EventDialog.js @@ -17,10 +17,9 @@ import { TextField, } from '@material-ui/core'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import CircularProgress from '@material-ui/core/CircularProgress'; -import Snackbar from '@material-ui/core/Snackbar'; import ConfirmDialog from '../shared/ConfirmDialog'; import DateTimePicker from 'material-ui-pickers/DateTimePicker'; @@ -63,10 +62,6 @@ const initialState = { startDate: undefined, endDate: undefined, }, - snackbar: { - message: '', - open: false, - }, confirmDialog: { open: false, }, @@ -98,14 +93,14 @@ class EventDialog extends Component { if (result.isValid) { if (this.props.intent === 'add') { this.execute( - () => api.post('/v1/events', event), + () => this.props.context.apiPost('/v1/events', event), 'addApi', 'Event \'' + event.name + '\' successfully created.' ); } else { this.execute( - () => api.put('/v1/events/' + event.id, event), + () => this.props.context.apiPut('/v1/events/' + event.id, event), 'updateApi', 'Event \'' + event.name + '\' successfully updated.' ); @@ -124,7 +119,7 @@ class EventDialog extends Component { handleDeleteConfirmClick = () => { return this.execute( - () => api.delete('/v1/events/' + this.state.event.id), + () => this.props.context.apiDelete('/v1/events/' + this.state.event.id), 'deleteApi', 'Event \'' + this.state.event.name + '\' successfully deleted.' ); @@ -147,10 +142,6 @@ class EventDialog extends Component { this.setState({ confirmDialog: { open: false }}); } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - execute = (action, api, successMessage) => { return new Promise((resolve, reject) => { this.setState({ [api]: { isExecuting: true }}, () => { @@ -174,11 +165,7 @@ class EventDialog extends Component { } this.setState({ - [api]: { isExecuting: false, isErrored: true }, - snackbar: { - message: body, - open: true, - }, + [api]: { isExecuting: false, isErrored: true } }, () => reject(error)); }) }) @@ -308,13 +295,6 @@ class EventDialog extends Component { >

Are you sure you want to delete Event '{name + ' starting ' + moment(startDate).format('dddd, MMMM Do [at] LT')}?

- {this.state.snackbar.message}} - /> ); } @@ -328,4 +308,4 @@ EventDialog.propTypes = { event: PropTypes.object, }; -export default withStyles(styles)(EventDialog); \ No newline at end of file +export default withStyles(styles)(withContext(EventDialog)); \ No newline at end of file From 339b67657e716a1d077ce77f36b0ce854508f873 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 19:08:09 -0500 Subject: [PATCH 24/56] Migrate Events to context --- web/src/events/Events.js | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/web/src/events/Events.js b/web/src/events/Events.js index adcc1df0..9686ee31 100644 --- a/web/src/events/Events.js +++ b/web/src/events/Events.js @@ -5,11 +5,10 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import { withStyles } from '@material-ui/core/styles'; import ContentWrapper from '../shared/ContentWrapper'; -import Snackbar from '@material-ui/core/Snackbar'; import { Card, CardContent, Typography, CircularProgress, ListSubheader, Button } from '@material-ui/core'; import { Add, EventAvailable, Event, Today } from '@material-ui/icons'; import EventList from './EventList'; @@ -59,10 +58,6 @@ class Events extends Component { intent: 'add', event: undefined, }, - snackbar: { - message: '', - open: false, - }, show: showCount } @@ -102,34 +97,28 @@ class Events extends Component { } }, () => { if (!result) return; - this.setState({ snackbar: { message: result, open: true }}, () => this.refresh('refreshApi')) + this.props.context.showMessage(result); + this.refresh('refreshApi'); }) } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - refresh = (apiType) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - api.get('/v1/events?offset=0&limit=100&orderBy=ASC') + this.props.context.apiGet('/v1/events?offset=0&limit=100&orderBy=ASC') .then(response => { this.setState({ events: response.data, [apiType]: { isExecuting: false, isErrored: false }, }); }, error => { - this.setState({ - [apiType]: { isExecuting: false, isErrored: true }, - snackbar: { message: error.response.data.Message, open: true }, - }); + this.setState({ [apiType]: { isExecuting: false, isErrored: true } }); }); }) } render() { let classes = this.props.classes; - let { events, loadApi, refreshApi, snackbar, show, eventDialog } = this.state; + let { events, loadApi, refreshApi, show, eventDialog } = this.state; events = events.map(e => ({ ...e, startDate: new Date(e.startDate).getTime(), endDate: new Date(e.endDate).getTime() })) @@ -192,13 +181,6 @@ class Events extends Component { event={eventDialog.event} /> - {snackbar.message}} - /> ); } @@ -208,4 +190,4 @@ Events.propTypes = { classes: PropTypes.object.isRequired, }; -export default withStyles(styles)(Events); \ No newline at end of file +export default withStyles(styles)(withContext(Events)); \ No newline at end of file From f811518b9d90154bc2dfd58e455cdcb220dbefb9 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 19:23:08 -0500 Subject: [PATCH 25/56] Migrate Scanner to context --- web/src/scans/Scanner.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 71e40294..9de269ac 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import moment from 'moment'; import { withStyles } from '@material-ui/core/styles'; @@ -121,7 +121,7 @@ class Scanner extends Component { scanDialog: { open: false }, scanApi: { ...this.state.scanApi, isExecuting: true } }, () => { - api.put('/v1/scans', scan) + this.props.context.apiPut('/v1/scans', scan) .then(response => { this.setState({ scanApi: { isExecuting: false, isErrored: false }}, () => { this.handleScanResponse(barcode, response); @@ -181,7 +181,7 @@ class Scanner extends Component { return new Promise((resolve, reject) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - api.get('/v1/events?dateStart=' + start + '&dateEnd=' + end) + this.props.context.apiGet('/v1/events?dateStart=' + start + '&dateEnd=' + end) .then(response => { this.setState({ events: response.data, @@ -197,7 +197,7 @@ class Scanner extends Component { fetchServices = (apiType) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - api.get('/v1/services') + this.props.context.apiGet('/v1/services') .then(response => { this.setState({ services: response.data, @@ -240,7 +240,7 @@ class Scanner extends Component { this.setState({ refreshApi: { ...this.state.refreshApi, isExecuting: true } }, () => { - api.post('/v1/events', event) + this.props.context.apiPost('/v1/events', event) .then(response => { resolve(response.data); }) @@ -357,4 +357,4 @@ Scanner.propTypes = { classes: PropTypes.object.isRequired, }; -export default withStyles(styles)(Scanner); \ No newline at end of file +export default withStyles(styles)(withContext(Scanner)); \ No newline at end of file From 8b1996cd2d2be2f727b851534289b00ca858f60f Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 19:28:02 -0500 Subject: [PATCH 26/56] Migrate LoginForm to context --- web/src/security/LoginForm.js | 39 +++++------------------------------ 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/web/src/security/LoginForm.js b/web/src/security/LoginForm.js index 007c598c..07ec4173 100644 --- a/web/src/security/LoginForm.js +++ b/web/src/security/LoginForm.js @@ -4,7 +4,7 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import { withStyles } from '@material-ui/core/styles'; @@ -13,7 +13,6 @@ import TextField from '@material-ui/core/TextField'; import Button from '@material-ui/core/Button'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Checkbox from '@material-ui/core/Checkbox'; -import Snackbar from '@material-ui/core/Snackbar'; import logo from '../assets/qcvo.png'; @@ -65,10 +64,6 @@ const initialState = { isExecuting: false, isErrored: false, }, - snackbar: { - message: '', - open: false, - }, } class LoginForm extends Component { @@ -89,16 +84,12 @@ class LoginForm extends Component { }); } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - handleLoginClick = () => { this.setState({ api: { isExecuting: true }, }, () => { - api.post('/v1/security/login', this.state) + this.props.context.apiPost('/v1/security/login', this.state) .then( response => { this.props.onLogin(response.data, this.state.rememberMe); @@ -107,24 +98,11 @@ class LoginForm extends Component { this.setState({ api: { isExecuting: false, isErrored: true }}, () => { if (error.response && (error.response.status === 400 || error.response.status === 401)) { - this.setState({ - password: '', - snackbar: { - message: 'Login failed.', - open: true, - } - }, () => { + this.setState({ password: '', }, () => { + this.props.context.showMessage('Login failed.'); this.passwordInput.focus(); }); } - else { - this.setState({ - snackbar: { - message: 'Error: ' + error.message, - open: true - } - }); - } }); } ); @@ -195,13 +173,6 @@ class LoginForm extends Component { - {this.state.snackbar.message}} - /> ); } @@ -212,4 +183,4 @@ LoginForm.propTypes = { classes: PropTypes.object.isRequired, }; -export default withStyles(styles)(LoginForm); \ No newline at end of file +export default withStyles(styles)(withContext(LoginForm)); \ No newline at end of file From c6b88403828896e6517bfd64d650c362ff78837f Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 19:37:49 -0500 Subject: [PATCH 27/56] Migrate PasswordResetDialog to context --- web/src/security/PasswordResetDialog.js | 36 +++---------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/web/src/security/PasswordResetDialog.js b/web/src/security/PasswordResetDialog.js index f79a1a9e..43d3b03e 100644 --- a/web/src/security/PasswordResetDialog.js +++ b/web/src/security/PasswordResetDialog.js @@ -4,7 +4,7 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import { withStyles } from '@material-ui/core/styles'; import { @@ -17,7 +17,6 @@ import { } from '@material-ui/core'; import CircularProgress from '@material-ui/core/CircularProgress'; -import Snackbar from '@material-ui/core/Snackbar'; import ConfirmDialog from '../shared/ConfirmDialog'; import { getCredentials } from '../credentialStore'; @@ -49,10 +48,6 @@ const initialState = { password: undefined, password2: undefined, }, - snackbar: { - message: '', - open: false, - }, confirmDialog: { open: false, }, @@ -122,10 +117,6 @@ class PasswordResetDialog extends Component { return this.updatePassword(); } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - handleDialogClose = () => { this.setState({ confirmDialog: { open: false }}); } @@ -139,7 +130,7 @@ class PasswordResetDialog extends Component { delete account.name; delete account.password2; - api.patch('/v1/security/accounts/' + account.id, account) + this.props.context.apiPatch('/v1/security/accounts/' + account.id, account) .then(response => { this.setState({ updateApi: { isExecuting: false, isErrored: false } @@ -148,22 +139,8 @@ class PasswordResetDialog extends Component { resolve(response); }) }, error => { - var body = error && error.response && error.response.data ? error.response.data : error; - - if (typeof(body) !== 'string') { - var keys = Object.keys(body); - - if (keys.length > 0) { - body = body[keys[0]]; - } - } - this.setState({ updateApi: { isExecuting: false, isErrored: true }, - snackbar: { - message: body, - open: true, - }, }, () => reject(error)); }) }) @@ -262,13 +239,6 @@ class PasswordResetDialog extends Component {

The user will be prompted to change their password at the next log in.

- {this.state.snackbar.message}} - /> ); } @@ -281,4 +251,4 @@ PasswordResetDialog.propTypes = { account: PropTypes.object, }; -export default withStyles(styles)(PasswordResetDialog); \ No newline at end of file +export default withStyles(styles)(withContext(PasswordResetDialog)); \ No newline at end of file From 5f8225b105028e707c5b2e874d364fc0007934c9 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 19:49:58 -0500 Subject: [PATCH 28/56] Migrate SecurityMenu to context --- web/src/security/SecurityMenu.js | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/web/src/security/SecurityMenu.js b/web/src/security/SecurityMenu.js index a5661687..d713af94 100644 --- a/web/src/security/SecurityMenu.js +++ b/web/src/security/SecurityMenu.js @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'proptypes'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import './style.css'; @@ -13,7 +13,7 @@ import IconButton from '@material-ui/core/IconButton'; import Typography from '@material-ui/core/Typography'; import { LockOpen, ExitToApp, AccountCircle } from '@material-ui/icons'; import ConfirmDialog from '../shared/ConfirmDialog'; -import { Badge, Menu, MenuItem, Snackbar, ListItemIcon, ListItemText } from '@material-ui/core'; +import { Badge, Menu, MenuItem, ListItemIcon, ListItemText } from '@material-ui/core'; import PasswordResetDialog from '../security/PasswordResetDialog'; const styles = { @@ -42,17 +42,13 @@ const initialState = { anchorEl: undefined, open: false, }, - snackbar: { - open: false, - message: undefined, - }, }; class SecurityMenu extends Component { state = initialState; logout = () => { - return api.post('v1/security/logout') + return this.props.context.apiPost('v1/security/logout') .then(() => this.props.onLogout()); } @@ -85,16 +81,13 @@ class SecurityMenu extends Component { this.setState({ confirmDialog: { open: false }}); } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - handlePasswordResetDialogClose = (result) => { this.setState({ passwordResetDialog: { open: false }, }, () => { if (result) { - this.setState({ snackbar: { message: result, open: true }}, () => this.props.onPasswordReset()); + this.props.context.showMessage(result); + this.props.onPasswordReset(); } }); } @@ -160,13 +153,6 @@ class SecurityMenu extends Component { account={passwordResetDialog.account} onClose={this.handlePasswordResetDialogClose} /> - {this.state.snackbar.message}} - /> ); } @@ -178,4 +164,4 @@ SecurityMenu.propTypes = { onPasswordReset: PropTypes.func.isRequired, } -export default SecurityMenu; \ No newline at end of file +export default withContext(SecurityMenu); \ No newline at end of file From 670f88b99bc992ccc4e99d2bafc4f1425ae4eb57 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 19:54:50 -0500 Subject: [PATCH 29/56] Migrate ServiceDialog to context --- web/src/services/ServiceDialog.js | 42 ++++++------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/web/src/services/ServiceDialog.js b/web/src/services/ServiceDialog.js index 92985b35..492afbba 100644 --- a/web/src/services/ServiceDialog.js +++ b/web/src/services/ServiceDialog.js @@ -16,10 +16,9 @@ import { TextField, } from '@material-ui/core'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import CircularProgress from '@material-ui/core/CircularProgress'; -import Snackbar from '@material-ui/core/Snackbar'; import ConfirmDialog from '../shared/ConfirmDialog'; const styles = { @@ -59,10 +58,6 @@ const initialState = { name: undefined, description: undefined, }, - snackbar: { - message: '', - open: false, - }, confirmDialog: { open: false, }, @@ -90,14 +85,14 @@ class ServiceDialog extends Component { if (result.isValid) { if (this.props.intent === 'add') { this.execute( - () => api.post('/v1/services', service), + () => this.props.context.apiPost('/v1/services', service), 'addApi', 'Service \'' + service.name + '\' successfully created.' ) } else { this.execute( - () => api.put('/v1/services/' + service.id, service), + () => this.props.context.apiPut('/v1/services/' + service.id, service), 'updateApi', 'Service \'' + service.name + '\' successfully updated.' ); @@ -116,7 +111,7 @@ class ServiceDialog extends Component { handleDeleteConfirmClick = () => { return this.execute( - () => api.delete('/v1/services/' + this.state.service.id), + () => this.props.context.apiDelete('/v1/services/' + this.state.service.id), 'deleteApi', 'Service \'' + this.state.service.name + '\' successfully deleted.' ); @@ -136,7 +131,7 @@ class ServiceDialog extends Component { } handleDialogClose = (result) => { - this.setState({ snackbar: { open: false }}); + this.setState({ confirmDialog: { open: false }}); } execute = (action, api, successMessage) => { @@ -151,23 +146,7 @@ class ServiceDialog extends Component { resolve(response); }) }, error => { - var body = error && error.response && error.response.data ? error.response.data : error; - - if (typeof(body) !== 'string') { - var keys = Object.keys(body); - - if (keys.length > 0) { - body = body[keys[0]]; - } - } - - this.setState({ - [api]: { isExecuting: false, isErrored: true }, - snackbar: { - message: body, - open: true, - }, - }, () => reject(error)); + this.setState({ [api]: { isExecuting: false, isErrored: true } }, () => reject(error)); }) }) }) @@ -272,13 +251,6 @@ class ServiceDialog extends Component { >

Are you sure you want to delete Service '{name}'?

- {this.state.snackbar.message}} - /> ); } @@ -292,4 +264,4 @@ ServiceDialog.propTypes = { service: PropTypes.object, }; -export default withStyles(styles)(ServiceDialog); \ No newline at end of file +export default withStyles(styles)(withContext(ServiceDialog)); \ No newline at end of file From 9874b69871e1b57bb71c1bd39ff883fda430c65d Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 20:03:03 -0500 Subject: [PATCH 30/56] Migrate Services to context --- web/src/services/Services.js | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/web/src/services/Services.js b/web/src/services/Services.js index 6edc7feb..bda0390e 100644 --- a/web/src/services/Services.js +++ b/web/src/services/Services.js @@ -4,12 +4,11 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import { withStyles } from '@material-ui/core/styles'; import ContentWrapper from '../shared/ContentWrapper'; -import Snackbar from '@material-ui/core/Snackbar'; import { Card, CardContent, Typography, CircularProgress, Button, ListSubheader } from '@material-ui/core'; import { Add } from '@material-ui/icons'; import ServiceList from './ServiceList'; @@ -57,10 +56,6 @@ class Services extends Component { intent: 'add', service: undefined, }, - snackbar: { - message: '', - open: false, - }, } componentWillMount = () => { @@ -95,17 +90,14 @@ class Services extends Component { } }, () => { if (!result) return; - this.setState({ snackbar: { message: result, open: true }}, () => this.refresh('refreshApi')) + this.props.context.showMessage(result); + this.refresh('refreshApi'); }) } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - refresh = (apiType) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true}}, () => { - api.get('/v1/services?offset=0&limit=5000&orderBy=ASC') + this.props.context.apiGet('/v1/services?offset=0&limit=5000&orderBy=ASC') .then(response => { this.setState({ services: response.data, @@ -113,8 +105,7 @@ class Services extends Component { }); }, error => { this.setState({ - [apiType]: { isExecuting: false, isErrored: true }, - snackbar: { message: error.response.data.Message, open: true }, + [apiType]: { isExecuting: false, isErrored: true } }); }); }) @@ -122,7 +113,7 @@ class Services extends Component { render() { let { classes } = this.props; - let { services, loadApi, refreshApi, snackbar, serviceDialog } = this.state; + let { services, loadApi, refreshApi, serviceDialog } = this.state; let userDefined = services.filter(s => s.id !== '00000000-0000-0000-0000-000000000000'); let systemDefined = services.filter(s => s.id === '00000000-0000-0000-0000-000000000000'); @@ -168,13 +159,6 @@ class Services extends Component { service={serviceDialog.service} /> - {snackbar.message}} - /> ); } @@ -184,4 +168,4 @@ Services.propTypes = { classes: PropTypes.object.isRequired, }; -export default withStyles(styles)(Services); \ No newline at end of file +export default withStyles(styles)(withContext(Services)); \ No newline at end of file From 92ce5309591d1b9bb59c905106dd770cae7dc2e2 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 20:06:54 -0500 Subject: [PATCH 31/56] Migrate VeteranDialog to context --- web/src/veterans/VeteranDialog.js | 42 +++++-------------------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/web/src/veterans/VeteranDialog.js b/web/src/veterans/VeteranDialog.js index 72f22fdd..c59f92df 100644 --- a/web/src/veterans/VeteranDialog.js +++ b/web/src/veterans/VeteranDialog.js @@ -21,10 +21,9 @@ import { } from '@material-ui/core'; import { validateEmail, validatePhoneNumber, userCanView } from '../util'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import CircularProgress from '@material-ui/core/CircularProgress'; -import Snackbar from '@material-ui/core/Snackbar'; import ConfirmDialog from '../shared/ConfirmDialog'; const styles = { @@ -77,10 +76,6 @@ const initialState = { email: undefined, verificationMethod: undefined, }, - snackbar: { - message: '', - open: false, - }, confirmDialog: undefined, } @@ -110,7 +105,7 @@ class VeteranDialog extends Component { if (result.isValid) { if (this.props.intent === 'add') { this.execute( - () => api.post('/v1/veterans', veteran), + () => this.props.context.apiPost('/v1/veterans', veteran), 'addApi', 'Veteran \'' + fullName + '\' successfully enrolled.' ) @@ -121,7 +116,7 @@ class VeteranDialog extends Component { this.setState({ confirmDialog: 'changeCardNumber' }); } else { this.execute( - () => api.put('/v1/veterans/' + veteran.id, veteran), + () => this.props.context.apiPut('/v1/veterans/' + veteran.id, veteran), 'updateApi', 'Veteran \'' + fullName + '\' successfully updated.' ); @@ -141,7 +136,7 @@ class VeteranDialog extends Component { handleDeleteConfirmClick = () => { return this.execute( - () => api.delete('/v1/veterans/' + this.state.veteran.id), + () => this.props.context.apiDelete('/v1/veterans/' + this.state.veteran.id), 'deleteApi', 'Veteran \'' + this.state.veteran.firstName + ' ' + this.state.veteran.lastName + '\' successfully deleted.' ); @@ -152,7 +147,7 @@ class VeteranDialog extends Component { let fullName = veteran.firstName + ' ' + veteran.lastName; return this.execute( - () => api.put('/v1/veterans/' + veteran.id, veteran), + () => this.props.context.apiPut('/v1/veterans/' + veteran.id, veteran), 'updateApi', 'Veteran \'' + fullName + '\' successfully updated.' ); @@ -175,10 +170,6 @@ class VeteranDialog extends Component { this.setState({ confirmDialog: undefined}); } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - execute = (action, api, successMessage) => { return new Promise((resolve, reject) => { this.setState({ [api]: { isExecuting: true }}, () => { @@ -191,22 +182,8 @@ class VeteranDialog extends Component { resolve(response); }) }, error => { - var body = error && error.response && error.response.data ? error.response.data : error; - - if (typeof(body) !== 'string') { - var keys = Object.keys(body); - - if (keys.length > 0) { - body = body[keys[0]]; - } - } - this.setState({ [api]: { isExecuting: false, isErrored: true }, - snackbar: { - message: body, - open: true, - }, }, () => reject(error)); }) }) @@ -402,13 +379,6 @@ class VeteranDialog extends Component { >

Are you sure you want to change this Veteran's card number to {cardNumber}? The previous card, {oldCardNumber}, will no longer function.

- {this.state.snackbar.message}} - /> ); } @@ -422,4 +392,4 @@ VeteranDialog.propTypes = { veteran: PropTypes.object }; -export default withStyles(styles)(VeteranDialog); \ No newline at end of file +export default withStyles(styles)(withContext(VeteranDialog)); \ No newline at end of file From 73a2a62dcb9e0a4277ba496933fc8956a6d7da0b Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 20:11:42 -0500 Subject: [PATCH 32/56] Migrate Veterans to context --- web/src/veterans/Veterans.js | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/web/src/veterans/Veterans.js b/web/src/veterans/Veterans.js index bd84b778..cee2ce9a 100644 --- a/web/src/veterans/Veterans.js +++ b/web/src/veterans/Veterans.js @@ -4,12 +4,11 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import api from '../api'; +import { withContext } from '../shared/ServiceContext'; import { withStyles } from '@material-ui/core/styles'; import ContentWrapper from '../shared/ContentWrapper'; -import Snackbar from '@material-ui/core/Snackbar'; import { Card, CardContent, Typography, CircularProgress, Button, TextField, InputAdornment } from '@material-ui/core'; import { Add, Search } from '@material-ui/icons'; import VeteranList from './VeteranList'; @@ -67,10 +66,6 @@ class Veterans extends Component { intent: 'add', veteran: undefined, }, - snackbar: { - message: '', - open: false, - }, filter: '', show: showCount, } @@ -118,17 +113,14 @@ class Veterans extends Component { } }, () => { if (!result) return; - this.setState({ snackbar: { message: result, open: true }}, () => this.refresh('refreshApi')) + this.props.context.showMessage(result); + this.refresh('refreshApi'); }) } - handleSnackbarClose = () => { - this.setState({ snackbar: { open: false }}); - } - refresh = (apiType) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - api.get('/v1/veterans?offset=0&limit=5000&orderBy=ASC') + this.props.context.apiGet('/v1/veterans?offset=0&limit=5000&orderBy=ASC') .then(response => { this.setState({ veterans: response.data.map(p => ({ ...p, cardNumber: p.cardNumber || '' })), @@ -137,7 +129,6 @@ class Veterans extends Component { }, error => { this.setState({ [apiType]: { isExecuting: false, isErrored: true }, - snackbar: { message: error.response.data.Message, open: true }, }); }); }) @@ -145,7 +136,7 @@ class Veterans extends Component { render() { let { classes } = this.props; - let { veterans, loadApi, refreshApi, snackbar, show, veteranDialog } = this.state; + let { veterans, loadApi, refreshApi, show, veteranDialog } = this.state; let searchById = this.state.filter !== undefined && this.state.filter !== '' && !isNaN(this.state.filter); @@ -203,13 +194,6 @@ class Veterans extends Component { veteran={veteranDialog.veteran} /> - {snackbar.message}} - /> ); } @@ -219,4 +203,4 @@ Veterans.propTypes = { classes: PropTypes.object.isRequired, }; -export default withStyles(styles)(Veterans); \ No newline at end of file +export default withStyles(styles)(withContext(Veterans)); \ No newline at end of file From ff8c1836a238fa3f8fb9a4c936b067d7c8eec46a Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 22:10:18 -0500 Subject: [PATCH 33/56] Move login failure message to backend --- api/QCVOC.Api/Security/Controller/SecurityController.cs | 4 ++-- web/src/security/LoginForm.js | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/api/QCVOC.Api/Security/Controller/SecurityController.cs b/api/QCVOC.Api/Security/Controller/SecurityController.cs index f3fed6da..11bccbfb 100644 --- a/api/QCVOC.Api/Security/Controller/SecurityController.cs +++ b/api/QCVOC.Api/Security/Controller/SecurityController.cs @@ -77,7 +77,7 @@ public IActionResult CheckCredentials() [AllowAnonymous] [ProducesResponseType(typeof(TokenResponse), 200)] [ProducesResponseType(typeof(string), 400)] - [ProducesResponseType(401)] + [ProducesResponseType(typeof(string), 401)] [ProducesResponseType(typeof(Exception), 500)] public IActionResult Login([FromBody]TokenRequest credentials) { @@ -93,7 +93,7 @@ public IActionResult Login([FromBody]TokenRequest credentials) if (accountRecord == default(Account)) { - return Unauthorized(); + return StatusCode(401, "Login failed."); } PurgeExpiredRefreshTokensFor(accountRecord.Id); diff --git a/web/src/security/LoginForm.js b/web/src/security/LoginForm.js index 07ec4173..5e9ccaaf 100644 --- a/web/src/security/LoginForm.js +++ b/web/src/security/LoginForm.js @@ -99,7 +99,6 @@ class LoginForm extends Component { if (error.response && (error.response.status === 400 || error.response.status === 401)) { this.setState({ password: '', }, () => { - this.props.context.showMessage('Login failed.'); this.passwordInput.focus(); }); } From f779206d54ecad9141f63882df9d6e210602cda7 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 23:52:02 -0500 Subject: [PATCH 34/56] Rename ServiceContext to ContextProvider --- web/src/accounts/AccountDialog.js | 2 +- web/src/accounts/Accounts.js | 2 +- web/src/app/App.js | 2 +- web/src/events/EventDialog.js | 2 +- web/src/events/Events.js | 2 +- web/src/index.js | 2 +- web/src/scans/Scanner.js | 2 +- web/src/security/LoginForm.js | 2 +- web/src/security/PasswordResetDialog.js | 2 +- web/src/security/SecurityMenu.js | 2 +- web/src/services/ServiceDialog.js | 2 +- web/src/services/Services.js | 2 +- web/src/shared/{ServiceContext.js => ContextProvider.js} | 0 web/src/veterans/VeteranDialog.js | 2 +- web/src/veterans/Veterans.js | 2 +- 15 files changed, 14 insertions(+), 14 deletions(-) rename web/src/shared/{ServiceContext.js => ContextProvider.js} (100%) diff --git a/web/src/accounts/AccountDialog.js b/web/src/accounts/AccountDialog.js index 9cef2131..1d19a70d 100644 --- a/web/src/accounts/AccountDialog.js +++ b/web/src/accounts/AccountDialog.js @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import { withStyles } from '@material-ui/core/styles'; import { diff --git a/web/src/accounts/Accounts.js b/web/src/accounts/Accounts.js index e9953289..2b157452 100644 --- a/web/src/accounts/Accounts.js +++ b/web/src/accounts/Accounts.js @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import AccountList from './AccountList'; import ContentWrapper from '../shared/ContentWrapper'; diff --git a/web/src/app/App.js b/web/src/app/App.js index 35fdbb11..4244ad9e 100644 --- a/web/src/app/App.js +++ b/web/src/app/App.js @@ -7,7 +7,7 @@ import React, { Component } from 'react'; import { Route, Switch, withRouter } from 'react-router-dom'; import PropTypes from 'prop-types'; import { getCredentials, saveLocalCredentials, saveSessionCredentials, deleteCredentials, updateCredentials } from '../credentialStore'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import { withStyles } from '@material-ui/core/styles'; import { People, VerifiedUser, Assignment, InsertInvitation, SpeakerPhone } from '@material-ui/icons'; diff --git a/web/src/events/EventDialog.js b/web/src/events/EventDialog.js index 5a0ddf20..6bb46b9b 100644 --- a/web/src/events/EventDialog.js +++ b/web/src/events/EventDialog.js @@ -17,7 +17,7 @@ import { TextField, } from '@material-ui/core'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import CircularProgress from '@material-ui/core/CircularProgress'; import ConfirmDialog from '../shared/ConfirmDialog'; diff --git a/web/src/events/Events.js b/web/src/events/Events.js index 9686ee31..47271607 100644 --- a/web/src/events/Events.js +++ b/web/src/events/Events.js @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import { withStyles } from '@material-ui/core/styles'; import ContentWrapper from '../shared/ContentWrapper'; diff --git a/web/src/index.js b/web/src/index.js index 7b8a5804..0ce58608 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -7,7 +7,7 @@ import ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; import MuiPickersUtilsProvider from 'material-ui-pickers/utils/MuiPickersUtilsProvider'; import MomentUtils from 'material-ui-pickers/utils/moment-utils'; -import ContextProvider from './shared/ServiceContext'; +import ContextProvider from './shared/ContextProvider'; import './index.css'; import App from './app/App'; diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 9de269ac..f0f9fad3 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import moment from 'moment'; import { withStyles } from '@material-ui/core/styles'; diff --git a/web/src/security/LoginForm.js b/web/src/security/LoginForm.js index 5e9ccaaf..3b1370a1 100644 --- a/web/src/security/LoginForm.js +++ b/web/src/security/LoginForm.js @@ -4,7 +4,7 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import { withStyles } from '@material-ui/core/styles'; diff --git a/web/src/security/PasswordResetDialog.js b/web/src/security/PasswordResetDialog.js index 43d3b03e..789ca47e 100644 --- a/web/src/security/PasswordResetDialog.js +++ b/web/src/security/PasswordResetDialog.js @@ -4,7 +4,7 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import { withStyles } from '@material-ui/core/styles'; import { diff --git a/web/src/security/SecurityMenu.js b/web/src/security/SecurityMenu.js index d713af94..d4e9ba65 100644 --- a/web/src/security/SecurityMenu.js +++ b/web/src/security/SecurityMenu.js @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'proptypes'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import './style.css'; diff --git a/web/src/services/ServiceDialog.js b/web/src/services/ServiceDialog.js index 492afbba..23bfc911 100644 --- a/web/src/services/ServiceDialog.js +++ b/web/src/services/ServiceDialog.js @@ -16,7 +16,7 @@ import { TextField, } from '@material-ui/core'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import CircularProgress from '@material-ui/core/CircularProgress'; import ConfirmDialog from '../shared/ConfirmDialog'; diff --git a/web/src/services/Services.js b/web/src/services/Services.js index bda0390e..f5d090dd 100644 --- a/web/src/services/Services.js +++ b/web/src/services/Services.js @@ -4,7 +4,7 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import { withStyles } from '@material-ui/core/styles'; diff --git a/web/src/shared/ServiceContext.js b/web/src/shared/ContextProvider.js similarity index 100% rename from web/src/shared/ServiceContext.js rename to web/src/shared/ContextProvider.js diff --git a/web/src/veterans/VeteranDialog.js b/web/src/veterans/VeteranDialog.js index c59f92df..9acc0316 100644 --- a/web/src/veterans/VeteranDialog.js +++ b/web/src/veterans/VeteranDialog.js @@ -21,7 +21,7 @@ import { } from '@material-ui/core'; import { validateEmail, validatePhoneNumber, userCanView } from '../util'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import CircularProgress from '@material-ui/core/CircularProgress'; import ConfirmDialog from '../shared/ConfirmDialog'; diff --git a/web/src/veterans/Veterans.js b/web/src/veterans/Veterans.js index cee2ce9a..9860b350 100644 --- a/web/src/veterans/Veterans.js +++ b/web/src/veterans/Veterans.js @@ -4,7 +4,7 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { withContext } from '../shared/ServiceContext'; +import { withContext } from '../shared/ContextProvider'; import { withStyles } from '@material-ui/core/styles'; From 1ace2bc6fe1b171539f1473c7b9d7618a9430c12 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Tue, 9 Oct 2018 23:53:32 -0500 Subject: [PATCH 35/56] Rename ServiceContext to Context --- web/src/shared/ContextProvider.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/src/shared/ContextProvider.js b/web/src/shared/ContextProvider.js index 3616d829..0f3d2617 100644 --- a/web/src/shared/ContextProvider.js +++ b/web/src/shared/ContextProvider.js @@ -7,7 +7,7 @@ import React, { Component } from 'react'; import Snackbar from '@material-ui/core/Snackbar'; import api from '../api'; -const ServiceContext = React.createContext(); +const Context = React.createContext(); const initialState = { snackbar: { @@ -60,7 +60,7 @@ class ContextProvider extends Component { render() { return ( - {this.state.snackbar.message}} /> - + ); } } @@ -86,9 +86,9 @@ class ContextProvider extends Component { export const withContext = (Component) => { return (props) => { return ( - + {context => ()} - + ); }; } From 85844e9e1d999f9283a8a0d2677f6e325cfa3fa4 Mon Sep 17 00:00:00 2001 From: Will Burklund Date: Wed, 10 Oct 2018 00:05:41 -0500 Subject: [PATCH 36/56] Condense api* methods into object --- web/src/accounts/AccountDialog.js | 6 +++--- web/src/accounts/Accounts.js | 2 +- web/src/app/App.js | 2 +- web/src/events/EventDialog.js | 6 +++--- web/src/events/Events.js | 2 +- web/src/scans/Scanner.js | 8 ++++---- web/src/security/LoginForm.js | 2 +- web/src/security/PasswordResetDialog.js | 2 +- web/src/security/SecurityMenu.js | 2 +- web/src/services/ServiceDialog.js | 6 +++--- web/src/services/Services.js | 2 +- web/src/shared/ContextProvider.js | 18 ++++++++---------- web/src/veterans/VeteranDialog.js | 8 ++++---- web/src/veterans/Veterans.js | 2 +- 14 files changed, 33 insertions(+), 35 deletions(-) diff --git a/web/src/accounts/AccountDialog.js b/web/src/accounts/AccountDialog.js index 1d19a70d..8006e369 100644 --- a/web/src/accounts/AccountDialog.js +++ b/web/src/accounts/AccountDialog.js @@ -113,14 +113,14 @@ class AccountDialog extends Component { if (result.isValid) { if (this.props.intent === 'add') { this.execute( - () => this.props.context.apiPost('/v1/security/accounts', account), + () => this.props.context.api.post('/v1/security/accounts', account), 'addApi', 'Account \'' + account.name + '\' successfully created.' ) } else { this.execute( - () => this.props.context.apiPatch('/v1/security/accounts/' + account.id, account), + () => this.props.context.api.patch('/v1/security/accounts/' + account.id, account), 'updateApi', 'Account \'' + account.name + '\' successfully updated.' ); @@ -137,7 +137,7 @@ class AccountDialog extends Component { let account = this.state.account; return this.execute( - () => this.props.context.apiDelete('/v1/security/accounts/' + account.id), + () => this.props.context.api.delete('/v1/security/accounts/' + account.id), 'deleteApi', 'Account \'' + account.name + '\' successfully deleted.' ); diff --git a/web/src/accounts/Accounts.js b/web/src/accounts/Accounts.js index 2b157452..2adda4eb 100644 --- a/web/src/accounts/Accounts.js +++ b/web/src/accounts/Accounts.js @@ -76,7 +76,7 @@ class Accounts extends Component { refresh = (apiType) => { this.setState({ ...this.state, [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - this.props.context.apiGet('/v1/security/accounts') + this.props.context.api.get('/v1/security/accounts') .then(response => { this.setState({ accounts: response.data, diff --git a/web/src/app/App.js b/web/src/app/App.js index 4244ad9e..8bf79e5e 100644 --- a/web/src/app/App.js +++ b/web/src/app/App.js @@ -73,7 +73,7 @@ class App extends Component { componentDidMount = () => { if (getCredentials()) { this.setState({ api: { ...this.state.api, isExecuting: true }}, () => { - this.props.context.apiGet('/v1/security').then(() => { + this.props.context.api.get('/v1/security').then(() => { this.setState({ api: { isExecuting: false, isErrored: false }, credentials: getCredentials() diff --git a/web/src/events/EventDialog.js b/web/src/events/EventDialog.js index 6bb46b9b..63a41886 100644 --- a/web/src/events/EventDialog.js +++ b/web/src/events/EventDialog.js @@ -93,14 +93,14 @@ class EventDialog extends Component { if (result.isValid) { if (this.props.intent === 'add') { this.execute( - () => this.props.context.apiPost('/v1/events', event), + () => this.props.context.api.post('/v1/events', event), 'addApi', 'Event \'' + event.name + '\' successfully created.' ); } else { this.execute( - () => this.props.context.apiPut('/v1/events/' + event.id, event), + () => this.props.context.api.put('/v1/events/' + event.id, event), 'updateApi', 'Event \'' + event.name + '\' successfully updated.' ); @@ -119,7 +119,7 @@ class EventDialog extends Component { handleDeleteConfirmClick = () => { return this.execute( - () => this.props.context.apiDelete('/v1/events/' + this.state.event.id), + () => this.props.context.api.delete('/v1/events/' + this.state.event.id), 'deleteApi', 'Event \'' + this.state.event.name + '\' successfully deleted.' ); diff --git a/web/src/events/Events.js b/web/src/events/Events.js index 47271607..ecfce74f 100644 --- a/web/src/events/Events.js +++ b/web/src/events/Events.js @@ -104,7 +104,7 @@ class Events extends Component { refresh = (apiType) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - this.props.context.apiGet('/v1/events?offset=0&limit=100&orderBy=ASC') + this.props.context.api.get('/v1/events?offset=0&limit=100&orderBy=ASC') .then(response => { this.setState({ events: response.data, diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index f0f9fad3..da16e680 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -121,7 +121,7 @@ class Scanner extends Component { scanDialog: { open: false }, scanApi: { ...this.state.scanApi, isExecuting: true } }, () => { - this.props.context.apiPut('/v1/scans', scan) + this.props.context.api.put('/v1/scans', scan) .then(response => { this.setState({ scanApi: { isExecuting: false, isErrored: false }}, () => { this.handleScanResponse(barcode, response); @@ -181,7 +181,7 @@ class Scanner extends Component { return new Promise((resolve, reject) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - this.props.context.apiGet('/v1/events?dateStart=' + start + '&dateEnd=' + end) + this.props.context.api.get('/v1/events?dateStart=' + start + '&dateEnd=' + end) .then(response => { this.setState({ events: response.data, @@ -197,7 +197,7 @@ class Scanner extends Component { fetchServices = (apiType) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - this.props.context.apiGet('/v1/services') + this.props.context.api.get('/v1/services') .then(response => { this.setState({ services: response.data, @@ -240,7 +240,7 @@ class Scanner extends Component { this.setState({ refreshApi: { ...this.state.refreshApi, isExecuting: true } }, () => { - this.props.context.apiPost('/v1/events', event) + this.props.context.api.post('/v1/events', event) .then(response => { resolve(response.data); }) diff --git a/web/src/security/LoginForm.js b/web/src/security/LoginForm.js index 3b1370a1..4d089a7a 100644 --- a/web/src/security/LoginForm.js +++ b/web/src/security/LoginForm.js @@ -89,7 +89,7 @@ class LoginForm extends Component { api: { isExecuting: true }, }, () => { - this.props.context.apiPost('/v1/security/login', this.state) + this.props.context.api.post('/v1/security/login', this.state) .then( response => { this.props.onLogin(response.data, this.state.rememberMe); diff --git a/web/src/security/PasswordResetDialog.js b/web/src/security/PasswordResetDialog.js index 789ca47e..98d1880c 100644 --- a/web/src/security/PasswordResetDialog.js +++ b/web/src/security/PasswordResetDialog.js @@ -130,7 +130,7 @@ class PasswordResetDialog extends Component { delete account.name; delete account.password2; - this.props.context.apiPatch('/v1/security/accounts/' + account.id, account) + this.props.context.api.patch('/v1/security/accounts/' + account.id, account) .then(response => { this.setState({ updateApi: { isExecuting: false, isErrored: false } diff --git a/web/src/security/SecurityMenu.js b/web/src/security/SecurityMenu.js index d4e9ba65..400b12ad 100644 --- a/web/src/security/SecurityMenu.js +++ b/web/src/security/SecurityMenu.js @@ -48,7 +48,7 @@ class SecurityMenu extends Component { state = initialState; logout = () => { - return this.props.context.apiPost('v1/security/logout') + return this.props.context.api.post('v1/security/logout') .then(() => this.props.onLogout()); } diff --git a/web/src/services/ServiceDialog.js b/web/src/services/ServiceDialog.js index 23bfc911..7b8d8448 100644 --- a/web/src/services/ServiceDialog.js +++ b/web/src/services/ServiceDialog.js @@ -85,14 +85,14 @@ class ServiceDialog extends Component { if (result.isValid) { if (this.props.intent === 'add') { this.execute( - () => this.props.context.apiPost('/v1/services', service), + () => this.props.context.api.post('/v1/services', service), 'addApi', 'Service \'' + service.name + '\' successfully created.' ) } else { this.execute( - () => this.props.context.apiPut('/v1/services/' + service.id, service), + () => this.props.context.api.put('/v1/services/' + service.id, service), 'updateApi', 'Service \'' + service.name + '\' successfully updated.' ); @@ -111,7 +111,7 @@ class ServiceDialog extends Component { handleDeleteConfirmClick = () => { return this.execute( - () => this.props.context.apiDelete('/v1/services/' + this.state.service.id), + () => this.props.context.api.delete('/v1/services/' + this.state.service.id), 'deleteApi', 'Service \'' + this.state.service.name + '\' successfully deleted.' ); diff --git a/web/src/services/Services.js b/web/src/services/Services.js index f5d090dd..796e87f2 100644 --- a/web/src/services/Services.js +++ b/web/src/services/Services.js @@ -97,7 +97,7 @@ class Services extends Component { refresh = (apiType) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true}}, () => { - this.props.context.apiGet('/v1/services?offset=0&limit=5000&orderBy=ASC') + this.props.context.api.get('/v1/services?offset=0&limit=5000&orderBy=ASC') .then(response => { this.setState({ services: response.data, diff --git a/web/src/shared/ContextProvider.js b/web/src/shared/ContextProvider.js index 0f3d2617..3c63ef93 100644 --- a/web/src/shared/ContextProvider.js +++ b/web/src/shared/ContextProvider.js @@ -48,11 +48,13 @@ class ContextProvider extends Component { ); } - apiDelete = (...args) => { return this.apiCall(api.delete, ...args); } - apiGet = (...args) => { return this.apiCall(api.get, ...args); } - apiPatch = (...args) => { return this.apiCall(api.patch, ...args); } - apiPost = (...args) => { return this.apiCall(api.post, ...args); } - apiPut = (...args) => { return this.apiCall(api.put, ...args); } + api = { + delete: (...args) => { return this.apiCall(api.delete, ...args); }, + get: (...args) => { return this.apiCall(api.get, ...args); }, + patch: (...args) => { return this.apiCall(api.patch, ...args); }, + post: (...args) => { return this.apiCall(api.post, ...args); }, + put: (...args) => { return this.apiCall(api.put, ...args); } + } handleSnackbarClose = () => { this.setState({ snackbar: { message: '', open: false }}); @@ -62,11 +64,7 @@ class ContextProvider extends Component { return (
{this.props.children} diff --git a/web/src/veterans/VeteranDialog.js b/web/src/veterans/VeteranDialog.js index 9acc0316..56497056 100644 --- a/web/src/veterans/VeteranDialog.js +++ b/web/src/veterans/VeteranDialog.js @@ -105,7 +105,7 @@ class VeteranDialog extends Component { if (result.isValid) { if (this.props.intent === 'add') { this.execute( - () => this.props.context.apiPost('/v1/veterans', veteran), + () => this.props.context.api.post('/v1/veterans', veteran), 'addApi', 'Veteran \'' + fullName + '\' successfully enrolled.' ) @@ -116,7 +116,7 @@ class VeteranDialog extends Component { this.setState({ confirmDialog: 'changeCardNumber' }); } else { this.execute( - () => this.props.context.apiPut('/v1/veterans/' + veteran.id, veteran), + () => this.props.context.api.put('/v1/veterans/' + veteran.id, veteran), 'updateApi', 'Veteran \'' + fullName + '\' successfully updated.' ); @@ -136,7 +136,7 @@ class VeteranDialog extends Component { handleDeleteConfirmClick = () => { return this.execute( - () => this.props.context.apiDelete('/v1/veterans/' + this.state.veteran.id), + () => this.props.context.api.delete('/v1/veterans/' + this.state.veteran.id), 'deleteApi', 'Veteran \'' + this.state.veteran.firstName + ' ' + this.state.veteran.lastName + '\' successfully deleted.' ); @@ -147,7 +147,7 @@ class VeteranDialog extends Component { let fullName = veteran.firstName + ' ' + veteran.lastName; return this.execute( - () => this.props.context.apiPut('/v1/veterans/' + veteran.id, veteran), + () => this.props.context.api.put('/v1/veterans/' + veteran.id, veteran), 'updateApi', 'Veteran \'' + fullName + '\' successfully updated.' ); diff --git a/web/src/veterans/Veterans.js b/web/src/veterans/Veterans.js index 9860b350..7b209e61 100644 --- a/web/src/veterans/Veterans.js +++ b/web/src/veterans/Veterans.js @@ -120,7 +120,7 @@ class Veterans extends Component { refresh = (apiType) => { this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => { - this.props.context.apiGet('/v1/veterans?offset=0&limit=5000&orderBy=ASC') + this.props.context.api.get('/v1/veterans?offset=0&limit=5000&orderBy=ASC') .then(response => { this.setState({ veterans: response.data.map(p => ({ ...p, cardNumber: p.cardNumber || '' })), From 261154ea1f57ea133a468c7eaf30720520c403ba Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 18:03:52 -0500 Subject: [PATCH 37/56] adjust height and spinner position in Events --- web/src/events/Events.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/events/Events.js b/web/src/events/Events.js index ecfce74f..a32d99ba 100644 --- a/web/src/events/Events.js +++ b/web/src/events/Events.js @@ -26,7 +26,7 @@ const styles = { zIndex: 1000 }, card: { - minHeight: 272, + minHeight: 273, maxWidth: 800, margin: 'auto', }, @@ -36,7 +36,7 @@ const styles = { right: 0, marginLeft: 'auto', marginRight: 'auto', - marginTop: 68, + marginTop: 80, }, }; @@ -159,9 +159,9 @@ class Events extends Component { icon={} onItemClick={userCanView() ? this.handleEditClick : undefined} /> + {past.length > show && }
} - {past.length > show && } { userCanView() && From 71b3b3850bf02e5005944c7302be78d3ffc38d96 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Tue, 9 Oct 2018 18:06:57 -0500 Subject: [PATCH 38/56] hide show button while refreshing Veterans --- web/src/veterans/Veterans.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/web/src/veterans/Veterans.js b/web/src/veterans/Veterans.js index 7b209e61..d4018d8b 100644 --- a/web/src/veterans/Veterans.js +++ b/web/src/veterans/Veterans.js @@ -170,13 +170,15 @@ class Veterans extends Component { /> {refreshApi.isExecuting ? : - +
+ + {list.length > show && } +
} - {list.length > show && } : + {refreshApi.isExecuting ? + : +
+ {!eventSelected && + } + onItemClick={this.handleEventItemClick} + /> + } + {!serviceSelected && eventSelected && + } + onItemClick={this.handleServiceItemClick} + /> + } + {serviceSelected && eventSelected && +
+ {scanApi.isExecuting ? : + !scan.status ? : display - } -
- } -
+ } + + } + } From 6588a21d85751dec0036833d118f325bf94fff89 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Wed, 10 Oct 2018 21:46:24 -0500 Subject: [PATCH 51/56] set icon size --- web/src/scans/Scanner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index bfe6f131..5cb8b6af 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -215,7 +215,7 @@ class Scanner extends Component { let result = getScanResult(scan); - return
{result.icon}
; + return
{React.cloneElement(result.icon, { style: { fontSize: 72 }})}
; } getDailyEvent = () => { From 83dd81b83f9dda1e2b88ea9d04c0e2587d9559c6 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Wed, 10 Oct 2018 21:51:59 -0500 Subject: [PATCH 52/56] remove unused imports --- web/src/scans/Scanner.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 5cb8b6af..2da41556 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -17,8 +17,6 @@ import EventList from '../events/EventList'; import ServiceList from '../services/ServiceList'; import ScannerMenu from './ScannerMenu'; -import { Done, Clear, Pause } from '@material-ui/icons'; - import ScannerHistoryDialog from './ScannerHistoryDialog'; import { getScanResult } from './scannerUtil'; import ManualScanDialog from './ManualScanDialog'; From ae35788bf92bf2fde5740e8d509967328b7c6eb4 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Thu, 11 Oct 2018 07:54:47 -0500 Subject: [PATCH 53/56] add veteran and message to scan result --- web/src/scans/Scanner.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 2da41556..544e3d9b 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -211,9 +211,19 @@ class Scanner extends Component { getScanDisplay = (scan) => { if (scan === undefined || scan.status === undefined) return; - let result = getScanResult(scan); + let { veteran, plusOne } = scan.response; + let { message, icon } = getScanResult(scan); + icon = React.cloneElement(icon, { style: { fontSize: 72 }}); - return
{React.cloneElement(result.icon, { style: { fontSize: 72 }})}
; + console.log(scan); + + return ( +
+
{veteran}
+ {icon} +
{message}
+
+ ); } getDailyEvent = () => { From 3e294832d0371e36f03f8e31d757fb671e6b1a25 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Thu, 11 Oct 2018 07:55:12 -0500 Subject: [PATCH 54/56] set text-align for displayBox --- web/src/scans/Scanner.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 544e3d9b..83088bb7 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -50,6 +50,7 @@ const styles = { display: 'flex', justifyContent: 'center', alignItems: 'center', + textAlign: 'center', height: 'calc(100vh - 188px)', width: '100%', }, From 958693bf17d1d9643b666afcb19098055f672ab2 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Thu, 11 Oct 2018 08:12:40 -0500 Subject: [PATCH 55/56] remove test code --- web/src/scans/Scanner.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 83088bb7..6881bd2c 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -214,13 +214,13 @@ class Scanner extends Component { let { veteran, plusOne } = scan.response; let { message, icon } = getScanResult(scan); - icon = React.cloneElement(icon, { style: { fontSize: 72 }}); - console.log(scan); + icon = React.cloneElement(icon, { style: { fontSize: 72 }}); + let title = veteran ? veteran + (plusOne ? ' + 1': '') : scan.cardNumber; return (
-
{veteran}
+
{title}
{icon}
{message}
From ff851327ed48297a5fd28fffaefb96fab4e6b074 Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Thu, 11 Oct 2018 21:03:51 -0500 Subject: [PATCH 56/56] adjust typography on scanner --- web/src/scans/Scanner.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index 6881bd2c..3dccfc04 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -216,13 +216,14 @@ class Scanner extends Component { let { message, icon } = getScanResult(scan); icon = React.cloneElement(icon, { style: { fontSize: 72 }}); - let title = veteran ? veteran + (plusOne ? ' + 1': '') : scan.cardNumber; + let title = veteran ? veteran : scan.cardNumber; return (
-
{title}
+ {title} + {plusOne && +1} {icon} -
{message}
+ {message}
); }