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()
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/mobile-new/app/build.gradle b/mobile-new/app/build.gradle
index 494930c8..3b352102 100644
--- a/mobile-new/app/build.gradle
+++ b/mobile-new/app/build.gradle
@@ -12,7 +12,7 @@ android {
}
buildTypes {
release {
- minifyEnabled false
+ minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
diff --git a/mobile-new/app/proguard-rules.pro b/mobile-new/app/proguard-rules.pro
index f1b42451..1849a114 100644
--- a/mobile-new/app/proguard-rules.pro
+++ b/mobile-new/app/proguard-rules.pro
@@ -8,9 +8,9 @@
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
+-keepclassmembers class org.qccoders.qcvoc.MainActivity {
+ public *;
+}
# Uncomment this to preserve the line number information for
# debugging stack traces.
diff --git a/mobile-new/app/src/main/ic_launcher-web.png b/mobile-new/app/src/main/ic_launcher-web.png
new file mode 100644
index 00000000..587fbadf
Binary files /dev/null and b/mobile-new/app/src/main/ic_launcher-web.png differ
diff --git a/mobile-new/app/src/main/res/drawable/ic_launcher_background.xml b/mobile-new/app/src/main/res/drawable/ic_launcher_background.xml
index d5fccc53..2408e30d 100644
--- a/mobile-new/app/src/main/res/drawable/ic_launcher_background.xml
+++ b/mobile-new/app/src/main/res/drawable/ic_launcher_background.xml
@@ -1,170 +1,74 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:viewportWidth="108"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile-new/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/mobile-new/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index eca70cfe..c4a603d4 100644
--- a/mobile-new/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/mobile-new/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/mobile-new/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/mobile-new/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index eca70cfe..c4a603d4 100644
--- a/mobile-new/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/mobile-new/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher.png b/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher.png
index a2f59082..2ca7b1f4 100644
Binary files a/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..aa974af0
Binary files /dev/null and b/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
index 1b523998..430b9e24 100644
Binary files a/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/mobile-new/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher.png b/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher.png
index ff10afd6..02b002b2 100644
Binary files a/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..df0914d0
Binary files /dev/null and b/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
index 115a4c76..bd6f32fd 100644
Binary files a/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/mobile-new/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index dcd3cd80..cdf714fa 100644
Binary files a/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..30db38eb
Binary files /dev/null and b/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
index 459ca609..f4bad472 100644
Binary files a/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/mobile-new/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 8ca12fe0..f52bbdf4 100644
Binary files a/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..d5c7e0e0
Binary files /dev/null and b/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
index 8e19b410..363df5db 100644
Binary files a/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/mobile-new/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index b824ebdd..55e8f5ae 100644
Binary files a/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..db579a67
Binary files /dev/null and b/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
index 4c19a13c..9e2fcd92 100644
Binary files a/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/mobile-new/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/web/src/accounts/AccountDialog.js b/web/src/accounts/AccountDialog.js
index 0ba9b1f1..8006e369 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 api from '../api';
+import { withContext } from '../shared/ContextProvider';
import { withStyles } from '@material-ui/core/styles';
import {
@@ -22,7 +22,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 +69,6 @@ const initialState = {
password: undefined,
password2: undefined,
},
- snackbar: {
- message: '',
- open: false,
- },
confirmDialog: {
open: false,
},
@@ -118,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.api.post('/v1/security/accounts', account),
'addApi',
'Account \'' + account.name + '\' successfully created.'
)
}
else {
this.execute(
- () => api.patch('/v1/security/accounts/' + account.id, account),
+ () => this.props.context.api.patch('/v1/security/accounts/' + account.id, account),
'updateApi',
'Account \'' + account.name + '\' successfully updated.'
);
@@ -142,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.api.delete('/v1/security/accounts/' + account.id),
'deleteApi',
'Account \'' + account.name + '\' successfully deleted.'
);
@@ -152,10 +147,6 @@ 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 }}, () => {
@@ -168,22 +159,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 +314,6 @@ class AccountDialog extends Component {
>
Are you sure you want to delete account '{this.state.account.name}'?
- {this.state.snackbar.message}}
- />
);
}
@@ -357,4 +327,4 @@ AccountDialog.propTypes = {
account: PropTypes.object,
};
-export default 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..2adda4eb 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/ContextProvider';
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.api.get('/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/api.js b/web/src/api.js
index 8e8e5ee8..01244117 100644
--- a/web/src/api.js
+++ b/web/src/api.js
@@ -34,12 +34,12 @@ api.interceptors.response.use(config => {
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);
@@ -48,8 +48,22 @@ api.interceptors.response.use(config => {
);
}
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
diff --git a/web/src/app/App.js b/web/src/app/App.js
index de927ac6..ae1a3cac 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/ContextProvider';
import { withStyles } from '@material-ui/core/styles';
import { People, VerifiedUser, Assignment, InsertInvitation, SpeakerPhone } from '@material-ui/icons';
@@ -26,7 +26,7 @@ import LoginForm from '../security/LoginForm';
import LinkList from './LinkList';
import Scanner from '../scans/Scanner';
import { CircularProgress, ListSubheader } from '@material-ui/core';
-import { getEnvironment } from '../util';
+import { getEnvironment, userCanView } from '../util';
const styles = {
root: {
@@ -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.api.get('/v1/security').then(() => {
this.setState({
api: { isExecuting: false, isErrored: false },
credentials: getCredentials()
@@ -123,7 +123,7 @@ class App extends Component {
render() {
let classes = this.props.classes;
let { isExecuting, isErrored } = this.state.api;
- let { accessToken, role } = this.state.credentials;
+ let { accessToken } = this.state.credentials;
let env = getEnvironment();
@@ -153,7 +153,7 @@ class App extends Component {
}>Veterans
}>Events
}>Scanner
- {(role === 'Administrator' || role === 'Supervisor') &&
+ {userCanView() &&
Administration
}>Services
@@ -164,11 +164,15 @@ class App extends Component {
- }/>
-
+ {userCanView() &&
+
+ }
:
@@ -182,4 +186,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
diff --git a/web/src/events/EventDialog.js b/web/src/events/EventDialog.js
index e0566226..63a41886 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/ContextProvider';
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.api.post('/v1/events', event),
'addApi',
'Event \'' + event.name + '\' successfully created.'
);
}
else {
this.execute(
- () => api.put('/v1/events/' + event.id, event),
+ () => this.props.context.api.put('/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.api.delete('/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
diff --git a/web/src/events/EventList.js b/web/src/events/EventList.js
index 9ea0816d..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';
@@ -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..a32d99ba 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/ContextProvider';
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';
@@ -27,7 +26,7 @@ const styles = {
zIndex: 1000
},
card: {
- minHeight: 272,
+ minHeight: 273,
maxWidth: 800,
margin: 'auto',
},
@@ -37,7 +36,7 @@ const styles = {
right: 0,
marginLeft: 'auto',
marginRight: 'auto',
- marginTop: 68,
+ marginTop: 80,
},
};
@@ -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.api.get('/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() }))
@@ -156,23 +145,23 @@ 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}
/>
+ {past.length > show && }
}
- {past.length > show && }
{ userCanView() &&
@@ -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
diff --git a/web/src/index.js b/web/src/index.js
index 9ccef0fc..0ce58608 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 ContextProvider from './shared/ContextProvider';
import './index.css';
import App from './app/App';
@@ -14,7 +15,9 @@ import App from './app/App';
ReactDOM.render(
-
+
+
+
,
document.getElementById('root')
diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js
index 2ec35dfa..3dccfc04 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/ContextProvider';
import moment from 'moment';
import { withStyles } from '@material-ui/core/styles';
@@ -44,24 +44,15 @@ const styles = {
right: 0,
marginLeft: 'auto',
marginRight: 'auto',
- marginTop: 68,
- },
- scanSpinner: {
- position: 'fixed',
- top: 0,
- bottom: 0,
- left: 0,
- right: 0,
- marginTop: 'auto',
- marginBottom: 'auto',
- marginLeft: 'auto',
- marginRight: 'auto',
+ marginTop: 27,
},
displayBox: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
+ textAlign: 'center',
height: 'calc(100vh - 188px)',
+ width: '100%',
},
title: {
display: 'inline',
@@ -86,8 +77,9 @@ const initialState = {
service: undefined,
},
scan: {
+ cardNumber: undefined,
status: undefined,
- data: undefined,
+ response: undefined,
},
events: [],
services: [],
@@ -120,7 +112,7 @@ class Scanner extends Component {
scanDialog: { open: false },
scanApi: { ...this.state.scanApi, isExecuting: true }
}, () => {
- api.put('/v1/scans', scan)
+ this.props.context.api.put('/v1/scans', scan)
.then(response => {
this.setState({ scanApi: { isExecuting: false, isErrored: false }}, () => {
this.handleScanResponse(barcode, response);
@@ -160,10 +152,6 @@ class Scanner extends Component {
this.setState({
scan: scan,
history: history,
- }, () => {
- setTimeout(() => {
- this.setState({ scan: initialState.scan });
- }, 2500);
});
}
@@ -174,13 +162,17 @@ class Scanner extends Component {
});
}
+ clearLastScan = () => {
+ this.setState({ scan: initialState.scan });
+ }
+
fetchEvents = (apiType) => {
let start = moment().startOf('day').format();
let end = moment().endOf('day').format();
return new Promise((resolve, reject) => {
this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => {
- api.get('/v1/events?dateStart=' + start + '&dateEnd=' + end)
+ this.props.context.api.get('/v1/events?dateStart=' + start + '&dateEnd=' + end)
.then(response => {
this.setState({
events: response.data,
@@ -196,7 +188,7 @@ class Scanner extends Component {
fetchServices = (apiType) => {
this.setState({ [apiType]: { ...this.state[apiType], isExecuting: true }}, () => {
- api.get('/v1/services')
+ this.props.context.api.get('/v1/services')
.then(response => {
this.setState({
services: response.data,
@@ -218,7 +210,22 @@ class Scanner extends Component {
}
getScanDisplay = (scan) => {
- return {JSON.stringify(scan, null, 2)}
;
+ if (scan === undefined || scan.status === undefined) return;
+
+ let { veteran, plusOne } = scan.response;
+ let { message, icon } = getScanResult(scan);
+
+ icon = React.cloneElement(icon, { style: { fontSize: 72 }});
+ let title = veteran ? veteran : scan.cardNumber;
+
+ return (
+
+ {title}
+ {plusOne && +1}
+ {icon}
+ {message}
+
+ );
}
getDailyEvent = () => {
@@ -239,7 +246,7 @@ class Scanner extends Component {
this.setState({
refreshApi: { ...this.state.refreshApi, isExecuting: true }
}, () => {
- api.post('/v1/events', event)
+ this.props.context.api.post('/v1/events', event)
.then(response => {
resolve(response.data);
})
@@ -294,36 +301,38 @@ class Scanner extends Component {
this.setState({ historyDialog: { open: true }})}
/>
- {scanApi.isExecuting ? :
- refreshApi.isExecuting ?
- :
-
- {!eventSelected &&
-
}
- onItemClick={this.handleEventItemClick}
- />
- }
- {!serviceSelected && eventSelected &&
- }
- onItemClick={this.handleServiceItemClick}
- />
- }
- {serviceSelected && eventSelected &&
-
- {!scan.status ?
:
+ {refreshApi.isExecuting ?
+
:
+
+ {!eventSelected &&
+
}
+ onItemClick={this.handleEventItemClick}
+ />
+ }
+ {!serviceSelected && eventSelected &&
+ }
+ onItemClick={this.handleServiceItemClick}
+ />
+ }
+ {serviceSelected && eventSelected &&
+
+ {scanApi.isExecuting ? :
+ !scan.status ? :
display
- }
-
- }
-
+ }
+
+ }
+
}
@@ -354,4 +363,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
diff --git a/web/src/scans/ScannerMenu.js b/web/src/scans/ScannerMenu.js
index ff13f52a..3aad005b 100644
--- a/web/src/scans/ScannerMenu.js
+++ b/web/src/scans/ScannerMenu.js
@@ -8,9 +8,9 @@ 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';
+import { Menu, MenuItem, ListItemIcon, ListItemText, Divider } from '@material-ui/core';
const styles = {
container: {
@@ -40,7 +40,7 @@ class ScannerMenu extends Component {
}
handleMenuClose = () => {
- this.setState({ menu: { open: false }});
+ this.close();
}
handleConfirmDialogClose = (result) => {
@@ -48,13 +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.close().then(() => this.props.clearLastScan());
}
resetScanner = () => {
@@ -63,8 +65,14 @@ class ScannerMenu extends Component {
});
}
+ close = () => {
+ return new Promise(resolve => {
+ this.setState({ menu: { open: false }}, () => resolve());
+ })
+ }
+
render() {
- let { classes, visible, configured } = this.props;
+ let { classes, visible, configured, lastScan } = this.props;
let { menu } = this.state;
return (
@@ -82,18 +90,29 @@ class ScannerMenu extends Component {
onClose={this.handleMenuClose}
>
{configured &&
-
+
+ {lastScan && lastScan.status && }
+
+
}
+