From a7188050fc133a7db0ba15776f0c38a275a52a4e Mon Sep 17 00:00:00 2001 From: Spamhurts Date: Thu, 6 Aug 2020 15:58:30 -0500 Subject: [PATCH 01/10] New dev for animal ID chip reader --- .gitignore | 11 +- .../src/client/ChipReader/ChipReader.jsx | 160 ++++++++++++++++++ snprc_ehr/src/client/ChipReader/api/api.js | 23 +++ snprc_ehr/src/client/ChipReader/app.jsx | 9 + .../components/CancelChangeModal.jsx | 27 +++ .../ChipReader/components/ChipDataPanel.jsx | 75 ++++++++ .../components/DemographicsPanel.jsx | 37 ++++ .../ChipReader/components/SummaryGridItem.jsx | 38 +++++ .../components/SummaryGridPanel.jsx | 38 +++++ .../ChipReader/constants/chipReaderState.js | 16 ++ .../src/client/ChipReader/constants/index.js | 6 + snprc_ehr/src/client/ChipReader/dev.jsx | 20 +++ .../ChipReader/services/bluetoothService.js | 17 ++ .../client/ChipReader/styles/chipReader.scss | 8 + .../ChipReader/tests/__mocks__/moment.js | 5 + snprc_ehr/webpack/entryPoints.js | 11 +- 16 files changed, 494 insertions(+), 7 deletions(-) create mode 100644 snprc_ehr/src/client/ChipReader/ChipReader.jsx create mode 100644 snprc_ehr/src/client/ChipReader/api/api.js create mode 100644 snprc_ehr/src/client/ChipReader/app.jsx create mode 100644 snprc_ehr/src/client/ChipReader/components/CancelChangeModal.jsx create mode 100644 snprc_ehr/src/client/ChipReader/components/ChipDataPanel.jsx create mode 100644 snprc_ehr/src/client/ChipReader/components/DemographicsPanel.jsx create mode 100644 snprc_ehr/src/client/ChipReader/components/SummaryGridItem.jsx create mode 100644 snprc_ehr/src/client/ChipReader/components/SummaryGridPanel.jsx create mode 100644 snprc_ehr/src/client/ChipReader/constants/chipReaderState.js create mode 100644 snprc_ehr/src/client/ChipReader/constants/index.js create mode 100644 snprc_ehr/src/client/ChipReader/dev.jsx create mode 100644 snprc_ehr/src/client/ChipReader/services/bluetoothService.js create mode 100644 snprc_ehr/src/client/ChipReader/styles/chipReader.scss create mode 100644 snprc_ehr/src/client/ChipReader/tests/__mocks__/moment.js diff --git a/.gitignore b/.gitignore index a14aeae56..33cc8e869 100644 --- a/.gitignore +++ b/.gitignore @@ -16,12 +16,15 @@ snprc_ehr/resources/views/HelloApp*.* snprc_ehr/resources/views/helloWorld*.* snprc_ehr/resources/views/NewAnimalPage*.* snprc_ehr/resources/views/BirthRecordReport*.* +snprc_ehr/resources/views/ChipReader*.* snprc_ehr/cmd-here.exe snprc_ehr/resources/referenceStudy/*.xlsx snprc_ehr/resources/web/snprc_ehr/snprcReports.js.gz snprc_ehr/resources/web/snprc_ehr/gen/* snprc_mobile -/snprc_ehr.2020*.zip -/snprc_ehr.2020*.7z -/snprc_ehr/.editorconfig -/snprc_ehr/cmd-here.exe +snprc_ehr.2020*.zip +snprc_ehr.2020*.7z +snprc_ehr/.editorconfig +snprc_ehr/cmd-here.exe +snprc_ehr/resources/referenceStudy/LoadTaqmanData +snprc_ehr/resources/referenceStudy/datasetImport/LoadTaqmanData diff --git a/snprc_ehr/src/client/ChipReader/ChipReader.jsx b/snprc_ehr/src/client/ChipReader/ChipReader.jsx new file mode 100644 index 000000000..172d828f0 --- /dev/null +++ b/snprc_ehr/src/client/ChipReader/ChipReader.jsx @@ -0,0 +1,160 @@ +/* eslint-disable no-alert */ + +import React from 'react' +import { LoadingSpinner } from '@labkey/components' +import './styles/chipReader.scss' +import ChipReaderState from './constants/chipReaderState' +import constants from './constants/index' +import InfoPanel from '../Shared/components/InfoPanel' +import ChipDataPanel from './components/ChipDataPanel' +import SummaryGridPanel from './components/SummaryGridPanel' +import CancelChangeModal from './components/CancelChangeModal' + +export default class ChipReader extends React.Component { + state = new ChipReaderState(); + notSupportedMessage = constants.notSupportedMessage + debug = constants.debug; + isSerialSupported = ('serial' in navigator) + + + componentDidMount() { + // prevent user from navigating away from page + window.addEventListener('beforeunload', this.beforeunload.bind(this)) + + this.setState(prevState => ( + { + ...prevState, + isLoading: false + } + )) + } + + componentWillUnmount() { + window.removeEventListener('beforeunload', this.beforeunload.bind(this)) + } + + beforeunload(e) { + if (this.state.isDirty) { + e.preventDefault() + e.returnValue = true + } + } + + handleSetPort = (port) => { + this.setState(prevState => ( + { + ...prevState, + port + } + )) + + } + handleError = value => { + this.setState(prevState => ( + { + ...prevState, + hasError: value + } + )) + } + + // Cancel button callback + handleCancel = () => { + if (this.state.isDirty) { + this.setState(prevState => ( + { + ...prevState, + showCancelModal: true + } + )) + } else { + window.history.back() + } + } + + // reset app + onCancelClick = () => { + this.setState(prevState => ( + { + ...prevState, + isDirty: false, + showCancelModal: false + } + )) + window.history.back() + } + + // dismiss modals + onCloseClick = () => { + this.setState(prevState => ( + { + ...prevState, + showCancelModal: false, + showSaveModal: false, + showSpeciesChangeModal: false + } + )) + } + + render() { + // allow debug mode to be triggered for running test suite + this.debug = this.props.debug !== undefined ? this.props.debug : constants.debug + + const { isLoading } = this.state + + if (isLoading) { + return ( + + ) + } + + return ( +
+ { this.isSerialSupported && +
+
+
+

Chip id

+
+
+ +
+ +
+ +
+
+
+

Animals Seen

+
+
+ +
+
+
+
+ } + +
+ + {/* Cancel Modal */ } + +
+
+ ) + } +} diff --git a/snprc_ehr/src/client/ChipReader/api/api.js b/snprc_ehr/src/client/ChipReader/api/api.js new file mode 100644 index 000000000..bb6afa4d6 --- /dev/null +++ b/snprc_ehr/src/client/ChipReader/api/api.js @@ -0,0 +1,23 @@ +import { Query } from '@labkey/api' + +export const request = ({ schemaName, queryName, viewName = '', sort = '', columns = [], filterArray = [] }) => { + return new Promise((resolve, reject) => { + const success = response => { + resolve(response) + } + const failure = error => { + reject(error) + } + Query.selectRows({ + requiredVersion: 19.2, + schemaName, + queryName, + viewName, + sort, + columns, + success, + failure, + filterArray + }) + }) +} diff --git a/snprc_ehr/src/client/ChipReader/app.jsx b/snprc_ehr/src/client/ChipReader/app.jsx new file mode 100644 index 000000000..9b0354eb3 --- /dev/null +++ b/snprc_ehr/src/client/ChipReader/app.jsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom' + +import ChipReader from './ChipReader' + +// Need to wait for container element to be available in labkey wrapper before render +window.addEventListener('DOMContentLoaded', () => { + ReactDOM.render(, document.getElementById('app')) +}) diff --git a/snprc_ehr/src/client/ChipReader/components/CancelChangeModal.jsx b/snprc_ehr/src/client/ChipReader/components/CancelChangeModal.jsx new file mode 100644 index 000000000..c95505fbc --- /dev/null +++ b/snprc_ehr/src/client/ChipReader/components/CancelChangeModal.jsx @@ -0,0 +1,27 @@ +import React from 'react' +import { Modal, Button } from 'react-bootstrap' + +export default class CancelChangeModal extends React.PureComponent { + render() { + return ( + + + {this.props.title} + + + +

{this.props.message}

+
+ + + + + +
+ ) + } +} diff --git a/snprc_ehr/src/client/ChipReader/components/ChipDataPanel.jsx b/snprc_ehr/src/client/ChipReader/components/ChipDataPanel.jsx new file mode 100644 index 000000000..bc6aae651 --- /dev/null +++ b/snprc_ehr/src/client/ChipReader/components/ChipDataPanel.jsx @@ -0,0 +1,75 @@ +import React from 'react' +import Select from 'react-select' +import InfoPanel from '../../Shared/components/InfoPanel' +import { requestPort, connect } from '../services/serialService' + +export default class ChipDataPanel extends React.Component { + dateErrorMessageText = 'Birthdate must occur on or before the acquisition date.' + + state = { + errorMessage: undefined + } + + componentDidMount = () => { + console.log('CurrentAnimalPanel mounted') + } + + logger = (args) => { + let line = Array.prototype.slice.call(args).map(function (arg) { + return typeof arg === 'string' ? arg : JSON.stringify(arg); + }).join(''); + + document.querySelector('#log').textContent += line + '\n'; + } + + onButtonClick = () => { + + requestPort().then( (serialPort) => + connect(serialPort, 9600).then(( port ) => { + console.log(port) + this.props.handleSetPort(port) + }) + + ) + } + render() { + let { port } = !this.props.port ? 'Not selected' : this.props.port + + return ( + <> +
+
+
+
+

Port:

+
+

Scanned chip goes here

+
+
+
+ +
+

Serial Output

+
+
+ +
+
+
+

+                            
+ +
+ +
+
+ + + + ) + } +} diff --git a/snprc_ehr/src/client/ChipReader/components/DemographicsPanel.jsx b/snprc_ehr/src/client/ChipReader/components/DemographicsPanel.jsx new file mode 100644 index 000000000..5dfdda1b5 --- /dev/null +++ b/snprc_ehr/src/client/ChipReader/components/DemographicsPanel.jsx @@ -0,0 +1,37 @@ +import React from 'react' +import Select from 'react-select' +import moment from 'moment' +import WrappedDatePicker from '../../Shared/components/WrappedDatePicker' +import InfoPanel from '../../Shared/components/InfoPanel' + +export default class DemographicsPanel extends React.Component { + + state = { + errorMessage: undefined + } + + componentDidMount = () => { + console.log('DemographicsPanel mounted') + } + + render() { + return ( + <> +
+
+
+

Demographic Data Here

+ +
+
+ +
+ + + ) + } +} diff --git a/snprc_ehr/src/client/ChipReader/components/SummaryGridItem.jsx b/snprc_ehr/src/client/ChipReader/components/SummaryGridItem.jsx new file mode 100644 index 000000000..07324ab52 --- /dev/null +++ b/snprc_ehr/src/client/ChipReader/components/SummaryGridItem.jsx @@ -0,0 +1,38 @@ +import React from 'react' +import moment from 'moment' + +export default class SummaryGridItem extends React.PureComponent { + onClickHandler = () => { + const { id } = this.props.row + this.props.print(id) + } + + render() { + const { id, acqDate, species, selectedOption } = this.props.row + return ( + + + {id} + {selectedOption === 'Birth' + && ( +