diff --git a/web/src/scans/ManualScanDialog.js b/web/src/scans/ManualScanDialog.js new file mode 100644 index 00000000..a84b3cbf --- /dev/null +++ b/web/src/scans/ManualScanDialog.js @@ -0,0 +1,132 @@ +/* + 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 PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import { + Dialog, + DialogTitle, + DialogActions, + Button, + DialogContent, + TextField, +} from '@material-ui/core'; + +const styles = { + dialog: { + width: 320, + marginLeft: 'auto', + marginRight: 'auto', + marginTop: 50, + height: 'fit-content', + }, +}; + +const initialState = { + cardNumber: undefined, + validation: { + cardNumber: undefined, + }, +}; + +class ManualScanDialog extends Component { + state = initialState; + + componentWillReceiveProps = (nextProps) => { + if (nextProps.open && !this.props.open) { + this.setState({ + ...initialState + }); + } + } + + handleCancelClick = () => { + this.props.onClose(undefined); + } + + handleEnterClick = () => { + this.validate().then(result => { + if (result.isValid) { + this.props.onClose(this.state.cardNumber); + } + }); + } + + handleChange = (event) => { + this.setState({ + cardNumber: event.target.value, + validation: { cardNumber: undefined }, + }); + } + + validate = () => { + let result = { ...initialState.validation }; + + if (this.state.cardNumber === undefined) { + result.cardNumber = 'A Card Number is required.'; + } + + if (isNaN(this.state.cardNumber)) { + result.cardNumber = 'The Card Number must contain only numbers.'; + } + + return new Promise(resolve => { + this.setState({ validation: result }, () => { + result.isValid = JSON.stringify(result) === JSON.stringify(initialState.validation); + resolve(result); + }); + }) + } + + render() { + let { classes, open } = this.props; + let { validation } = this.state; + + return ( + + Enter Card Number + + this.handleChange(event)} + /> + + + + + + + ); + } +} + +ManualScanDialog.propTypes = { + classes: PropTypes.object.isRequired, + open: PropTypes.bool.isRequired, +}; + +export default withStyles(styles)(ManualScanDialog); \ No newline at end of file diff --git a/web/src/scans/Scanner.js b/web/src/scans/Scanner.js index b1b6697d..2ec35dfa 100644 --- a/web/src/scans/Scanner.js +++ b/web/src/scans/Scanner.js @@ -19,6 +19,7 @@ import ScannerMenu from './ScannerMenu'; import ScannerHistoryDialog from './ScannerHistoryDialog'; import { getScanResult } from './scannerUtil'; +import ManualScanDialog from './ManualScanDialog'; const historyLimit = 5; @@ -45,6 +46,17 @@ const styles = { marginRight: 'auto', marginTop: 68, }, + scanSpinner: { + position: 'fixed', + top: 0, + bottom: 0, + left: 0, + right: 0, + marginTop: 'auto', + marginBottom: 'auto', + marginLeft: 'auto', + marginRight: 'auto', + }, displayBox: { display: 'flex', justifyContent: 'center', @@ -65,6 +77,10 @@ const initialState = { isExecuting: false, isErrored: false, }, + scanApi: { + isExecuting: false, + isErrored: false, + }, scanner: { event: undefined, service: undefined, @@ -79,6 +95,9 @@ const initialState = { historyDialog: { open: false, }, + scanDialog: { + open: false, + }, } class Scanner extends Component { @@ -91,14 +110,26 @@ class Scanner extends Component { } handleBarcodeScanned = (barcode) => { + if (barcode === undefined) return; + let { event, service } = this.state.scanner; let scan = { eventId: event && event.id, serviceId: service && service.id, cardNumber: barcode }; - api.put('/v1/scans', scan) - .then(response => { - this.handleScanResponse(barcode, response); - }, error => { - this.handleScanResponse(barcode, error.response); + this.setState({ + scan: initialState.scan, + scanDialog: { open: false }, + scanApi: { ...this.state.scanApi, isExecuting: true } + }, () => { + api.put('/v1/scans', scan) + .then(response => { + this.setState({ scanApi: { isExecuting: false, isErrored: false }}, () => { + this.handleScanResponse(barcode, response); + }); + }, error => { + this.setState({ scanApi: { isExecuting: false, isErrored: true }}, () => { + this.handleScanResponse(barcode, error.response); + }); + }); }); } @@ -107,10 +138,18 @@ class Scanner extends Component { initiateMobileScan(); } else { - // TODO: manual input of barcode + this.setState({ scanDialog: { open: true }}); } } + handleScanDialogClose = (result) => { + this.setState({ scanDialog: { open: false }}, () => { + if (result !== undefined) { + this.handleBarcodeScanned(result); + } + }); + } + handleScanResponse = (cardNumber, response) => { let scan = { cardNumber: cardNumber, status: response.status, response: response.data }; @@ -225,7 +264,7 @@ class Scanner extends Component { render() { let classes = this.props.classes; - let { loadApi, refreshApi, scanner, scan, events, services, history, historyDialog } = this.state; + let { loadApi, refreshApi, scanApi, scanner, scan, events, services, history, historyDialog, scanDialog } = this.state; let title = this.getTitle(scanner); let display = this.getScanDisplay(scan); @@ -259,31 +298,32 @@ class Scanner extends Component { viewHistory={() => this.setState({ historyDialog: { open: true }})} /> - {refreshApi.isExecuting ? - : -
- {!eventSelected && - } - onItemClick={this.handleEventItemClick} - /> - } - {!serviceSelected && eventSelected && - } - onItemClick={this.handleServiceItemClick} - /> - } - {serviceSelected && eventSelected && -
- {!scan.status ? : - display - } -
- } -
+ {scanApi.isExecuting ? : + refreshApi.isExecuting ? + : +
+ {!eventSelected && + } + onItemClick={this.handleEventItemClick} + /> + } + {!serviceSelected && eventSelected && + } + onItemClick={this.handleServiceItemClick} + /> + } + {serviceSelected && eventSelected && +
+ {!scan.status ? : + display + } +
+ } +
} @@ -295,6 +335,10 @@ class Scanner extends Component { > } + {this.props.title} @@ -96,4 +109,4 @@ ConfirmDialog.propTypes = { suppressCloseOnConfirm: PropTypes.bool, }; -export default ConfirmDialog; \ No newline at end of file +export default withStyles(styles)(ConfirmDialog); \ No newline at end of file