diff --git a/src/components/ConsoleBox/ConsoleBox.js b/src/components/ConsoleBox/ConsoleBox.js deleted file mode 100644 index 056f7226..00000000 --- a/src/components/ConsoleBox/ConsoleBox.js +++ /dev/null @@ -1,69 +0,0 @@ -import { Button } from '@mui/material'; -import React, { Component } from 'react'; - -export default class ConsoleBox extends Component { - constructor(props) { - super(props); - this.state = { - showStatus: 'hideConsole', - headerStatus: 'collapseHeader' - }; - - this.toggleConsole = this.toggleConsole.bind(this); - } - - handleAddition = (e, { value }) => { - this.setState({ - options: [{ text: value, value }, ...this.state.options] - }); - }; - - handleChange = (e, { value }) => { - this.props.updateCB(this.props.elementName, value); - this.setState({ currentValue: value }); - }; - - toggleConsole() { - if (this.state.showStatus === 'showConsole') { - this.setState({ showStatus: 'hideConsole' }); - this.setState({ headerStatus: 'collapseHeader' }); - } else { - this.setState({ showStatus: 'showConsole' }); - this.setState({ headerStatus: 'showHeader' }); - } - } - - render() { - try { - var objDiv = document.getElementById('your_div'); - if (objDiv) { - objDiv.scrollTop = objDiv.scrollHeight; - } - } catch (error) { - console.log('Encountered error', error); - } - let i = 0; - return ( -
- -
- {this.props.logs.map(element => { - i++; - return ( -
- {' '} - {element.content} -
- ); - })} -
-
- ); - } -} diff --git a/src/components/ConsoleBox/consoleBox.css b/src/components/ConsoleBox/consoleBox.css deleted file mode 100644 index 69359024..00000000 --- a/src/components/ConsoleBox/consoleBox.css +++ /dev/null @@ -1,78 +0,0 @@ -.consoleMain{ - border-width:1px 5px 3px 5px; - border-style: solid solid solid solid; - border-color: black; - background-color: #333333; - color:white; - overflow-y:scroll; - overflow-x: wrap; - word-break:break-all; - font-family: 'Courier New', Courier, monospace; - transition: all .2s ease; - /* transition-duration: .2s; */ - transition-delay: .25s; -} -.resize{ - resize: vertical; - transition-duration:0s; - transition-delay:0s; -} -.showConsole{ - height:200px; - - -} -.hideConsole{ - resize:none; - height:0px !important; - border-width: 0px; - transition-property: all; - transition-duration: .2s; - transition-delay: 0s; -} - -.consoleHeader{ - border: 3px solid black; - background-color: black; - color: white; - margin-top: 30px; - font-family: 'Courier New', Courier, monospace; - display:block; - font-size: 18px; - text-align: center; - transition-property: all; - transition-duration: .2s; - transition-delay: 0s; -} - -.showHeader{ - width:100%; -} - -.collapseHeader{ - width: 250px; - color: black; - text-align:center; - border-width: 1px 1px 1px 1px; - background-color: white; - transition-property: all; - transition-duration: .2s; - transition-delay: .25s; -} - -.showHeader:hover ~ .resize{ - transition-delay:.25s; - transition-duration:.4s; -} - -.errorClass{ - color:#dc3545; -} - -.warningClass{ - color: #ffc107; -} - -.infoClass{ - color: #17a2b8; -} \ No newline at end of file diff --git a/src/components/RequestBox/InProgressFormBox/InProgressFormBox.js b/src/components/RequestBox/InProgressFormBox/InProgressFormBox.js index 7cfe2573..293003e8 100644 --- a/src/components/RequestBox/InProgressFormBox/InProgressFormBox.js +++ b/src/components/RequestBox/InProgressFormBox/InProgressFormBox.js @@ -1,4 +1,4 @@ -import { Box, Button, Paper, Typography, ButtonGroup } from '@mui/material'; +import { Box, Button, Typography, ButtonGroup } from '@mui/material'; import React from 'react'; import './InProgressFormBoxStyle.css'; diff --git a/src/components/RequestBox/InProgressFormBox/InProgressFormBoxStyle.css b/src/components/RequestBox/InProgressFormBox/InProgressFormBoxStyle.css index 4e8b3eea..ceea9016 100644 --- a/src/components/RequestBox/InProgressFormBox/InProgressFormBoxStyle.css +++ b/src/components/RequestBox/InProgressFormBox/InProgressFormBoxStyle.css @@ -4,7 +4,4 @@ border-radius: 5px; padding:20px; margin:20px 0 20px 0; - - /* This should be inherited, need to change */ - width:48.5vw; } \ No newline at end of file diff --git a/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js b/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js index 123ed142..250428f4 100644 --- a/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js +++ b/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js @@ -24,20 +24,37 @@ export default function PatientSearchBar(props) { } return ''; } + + function getFilteredLength(searchstring, listOfPatients) { + const filteredListOfPatients = listOfPatients[0].filter((element) => { + if (searchstring === '') { + return element; + } + else { + return element.name.toLowerCase().includes(searchstring); + } + }); + + return filteredListOfPatients.length; + } function patientSearchBar() { return ( - { - setInput(newInputValue.toLowerCase()); - }} - options={listOfPatients[0].map(item => item.name)} - renderInput={(params) => } /> + +

Filter patient list

+ { + setInput(newInputValue.toLowerCase()); + }} + options={listOfPatients[0].map(item => item.name)} + renderInput={(params) => } /> +

Showing {getFilteredLength(input, listOfPatients)} of {props.searchablePatients.length} records

+
{displayFilteredPatientList(input, listOfPatients[0])}
); @@ -52,12 +69,11 @@ export default function PatientSearchBar(props) { return element.name.toLowerCase().includes(searchstring); } }); - return ( {filteredListOfPatients.map(patient => { return ( -
+ item.id === patient.id)} @@ -72,7 +88,7 @@ export default function PatientSearchBar(props) { responseExpirationDays={props.responseExpirationDays} defaultUser={props.defaultUser} /> -
+ ); })}
@@ -80,8 +96,8 @@ export default function PatientSearchBar(props) { } return ( -
+ {listOfPatients[0] ? patientSearchBar() : 'loading...'} -
+ ); } \ No newline at end of file diff --git a/src/components/RequestBox/PatientSearchBar/PatientSearchBarStyle.css b/src/components/RequestBox/PatientSearchBar/PatientSearchBarStyle.css index bcd1cac6..4908c658 100644 --- a/src/components/RequestBox/PatientSearchBar/PatientSearchBarStyle.css +++ b/src/components/RequestBox/PatientSearchBar/PatientSearchBarStyle.css @@ -3,10 +3,8 @@ } .search-box { - top: -10; - width: 100%; - margin: 10px auto; - margin-bottom: 50px; + width: 75%; + margin: 0px 10px 25px 20px; } .search-box-container { @@ -15,4 +13,9 @@ display: flex; flex-direction: column; justify-content: space-evenly +} + +.search-header { + display: flex; + align-items: center; } \ No newline at end of file diff --git a/src/components/RequestBox/RequestBox.js b/src/components/RequestBox/RequestBox.js index 723e9a9b..b71ab0bd 100644 --- a/src/components/RequestBox/RequestBox.js +++ b/src/components/RequestBox/RequestBox.js @@ -1,48 +1,20 @@ -import PersonIcon from '@mui/icons-material/Person'; -import { Box, Button, ButtonGroup, Modal } from '@mui/material'; -import MuiAlert from '@mui/material/Alert'; -import Snackbar from '@mui/material/Snackbar'; +import { Button, ButtonGroup } from '@mui/material'; import _ from 'lodash'; import React, { Component } from 'react'; import buildNewRxRequest from '../../util/buildScript.2017071.js'; +import PersonIcon from '@mui/icons-material/Person'; +import MuiAlert from '@mui/material/Alert'; +import Snackbar from '@mui/material/Snackbar'; import { defaultValues, shortNameMap } from '../../util/data'; import { getAge } from '../../util/fhir'; import { retrieveLaunchContext } from '../../util/util'; import InProgressFormBox from './InProgressFormBox/InProgressFormBox.js'; import './request.css'; -import PatientSearchBar from './PatientSearchBar/PatientSearchBar.js'; - -const style = { - position: 'absolute', - top: '50%', - left: '50%', - flexDirection: 'column', - width: '80%', - height: '70%', - overflowY: 'scroll', - transform: 'translate(-50%, -50%)', - display: 'flex', - bgcolor: 'background.paper', - border: '2px solid #000', - borderBottom: '2px solid black', - boxShadow: 24, - p: 4, - padding: '50px' -}; export default class RequestBox extends Component { constructor(props) { super(props); this.state = { - openPatient: false, - patientList: [], - patient: {}, - prefetchedResources: new Map(), - codeValues: defaultValues, - code: null, - codeSystem: null, - display: null, - request: {}, gatherCount: 0, response: {}, open: false @@ -59,27 +31,22 @@ export default class RequestBox extends Component { // TODO - see how to submit response for alternative therapy replaceRequestAndSubmit(request) { - this.setState({ request: request }); - // Submit the cds hook request. + this.props.callback(request,request); // Submit the cds hook request. this.submitOrderSign(request); } componentDidMount() { } - exitSmart = () => { - this.setState({ openPatient: false }); - }; - prepPrefetch() { const preppedResources = new Map(); - Object.keys(this.state.prefetchedResources).forEach(resourceKey => { + Object.keys(this.props.prefetchedResources).forEach(resourceKey => { let resourceList = []; - if (Array.isArray(this.state.prefetchedResources[resourceKey])) { - resourceList = this.state.prefetchedResources[resourceKey].map(resource => { + if (Array.isArray(this.props.prefetchedResources[resourceKey])) { + resourceList = this.props.prefetchedResources[resourceKey].map(resource => { return resource; }); } else { - resourceList = this.state.prefetchedResources[resourceKey]; + resourceList = this.props.prefetchedResources[resourceKey]; } preppedResources.set(resourceKey, resourceList); @@ -88,27 +55,27 @@ export default class RequestBox extends Component { } submitPatientView = () => { - this.props.submitInfo(this.prepPrefetch(), null, this.state.patient, 'patient-view'); + this.props.submitInfo(this.prepPrefetch(), null, this.props.patient, 'patient-view'); }; submitOrderSelect = () => { - if (!_.isEmpty(this.state.request)) { + if (!_.isEmpty(this.props.request)) { this.props.submitInfo( this.prepPrefetch(), - this.state.request, - this.state.patient, + this.props.request, + this.props.patient, 'order-select' ); } }; submitOrderSign = request => { - this.props.submitInfo(this.prepPrefetch(), request, this.state.patient, 'order-sign'); + this.props.submitInfo(this.prepPrefetch(), request, this.props.patient, 'order-sign'); }; submit = () => { - if (!_.isEmpty(this.state.request)) { - this.submitOrderSign(this.state.request); + if (!_.isEmpty(this.props.request)) { + this.submitOrderSign(this.props.request); } }; @@ -119,12 +86,12 @@ export default class RequestBox extends Component { this.state.prefetchCompleted ) { // if the prefetch contains a medicationRequests bundle - if (this.state.prefetchedResources.medicationRequests) { + if (this.props.prefetchedResources.medicationRequests) { this.submitPatientView(); } // we could use this in the future to send order-select //// if the prefetch contains a request - //if (this.state.prefetchedResources.request) { + //if (this.props.prefetchedResources.request) { // this.submitOrderSelect(); //} } @@ -134,52 +101,10 @@ export default class RequestBox extends Component { this.setState({ [elementName]: text }); }; - updateStateList = (elementName, text) => { - this.setState(prevState => { - return { [elementName]: [...prevState[elementName], text] }; - }); - }; - - updateStateMap = (elementName, key, text) => { - this.setState(prevState => { - if (!prevState[elementName][key]) { - prevState[elementName][key] = []; - } - return { [elementName]: { ...prevState[elementName], [key]: text } }; - }); - }; - - clearState = () => { - this.setState({ - prefetchedResources: new Map(), - practitioner: {}, - coverage: {}, - response: {} - }); - }; - - getPatients = () => { - - - this.props.client - .request(this.props.patientFhirQuery, { flat: true }) - .then(result => { - this.setState({ - patientList: result, - openPatient: true - }); - }) - .catch(e => { - this.setState({ - patientList: e - }); - }); - }; - emptyField = (empty); renderPatientInfo() { - const patient = this.state.patient; + const patient = this.props.patient; if (Object.keys(patient).length === 0) { return
; } @@ -216,20 +141,20 @@ export default class RequestBox extends Component { Coding
- Code: {this.state.code ? this.state.code : this.emptyField} + Code: {this.props.code ? this.props.code : this.emptyField}
- System: {this.state.codeSystem ? shortNameMap[this.state.codeSystem] : this.emptyField} + System: {this.props.codeSystem ? shortNameMap[this.props.codeSystem] : this.emptyField}
- Display: {this.state.display ? this.state.display : this.emptyField} + Display: {this.props.display ? this.props.display : this.emptyField}
); } renderPrefetchedResources() { - const prefetchMap = new Map(Object.entries(this.state.prefetchedResources)); + const prefetchMap = new Map(Object.entries(this.props.prefetchedResources)); if (prefetchMap.size > 0) { return this.renderRequestResources(prefetchMap); } @@ -305,7 +230,7 @@ export default class RequestBox extends Component { launchSmartOnFhirApp = () => { console.log('Launch SMART on FHIR App'); - let userId = this.state.prefetchedResources?.practitioner?.id; + let userId = this.props.prefetchedResources?.practitioner?.id; if (!userId) { console.log( 'Practitioner not populated from prefetch, using default from config: ' + @@ -315,12 +240,12 @@ export default class RequestBox extends Component { } let link = { - appContext: 'user=' + userId + '&patient=' + this.state.patient.id, + appContext: 'user=' + userId + '&patient=' + this.props.patient.id, type: 'smart', url: this.props.smartAppUrl }; - retrieveLaunchContext(link, this.state.patient.id, this.props.client.state).then(result => { + retrieveLaunchContext(link, this.props.patient.id, this.props.client.state).then(result => { link = result; console.log(link); // launch the application in a new window @@ -346,10 +271,10 @@ export default class RequestBox extends Component { response = undefined; if (!this.isOrderNotSelected()) { - if (Object.keys(this.state.request).length > 0) { - order = `${this.state.request.resourceType}/${this.state.request.id}`; - if (this.state.request.insurance && this.state.request.insurance.length > 0) { - coverage = `${this.state.request.insurance[0].reference}`; + if (Object.keys(this.props.request).length > 0) { + order = `${this.props.request.resourceType}/${this.props.request.id}`; + if (this.props.request.insurance && this.props.request.insurance.length > 0) { + coverage = `${this.props.request.insurance[0].reference}`; } } } @@ -362,8 +287,8 @@ export default class RequestBox extends Component { } } - if (Object.keys(this.state.response).length > 0) { - response = `QuestionnaireResponse/${this.state.response.id}`; + if (Object.keys(this.props.response).length > 0) { + response = `QuestionnaireResponse/${this.props.response.id}`; } if (order && response) { @@ -380,7 +305,7 @@ export default class RequestBox extends Component { let linkCopy = Object.assign({}, link); - return retrieveLaunchContext(linkCopy, this.state.patient.id, this.props.client.state).then( + return retrieveLaunchContext(linkCopy, this.props.patient.id, this.props.client.state).then( result => { linkCopy = result; return linkCopy; @@ -396,9 +321,9 @@ export default class RequestBox extends Component { // build the NewRx Message var newRx = buildNewRxRequest( - this.state.prefetchedResources.patient, - this.state.prefetchedResources.practitioner, - this.state.request + this.props.prefetchedResources.patient, + this.props.prefetchedResources.practitioner, + this.props.request ); console.log('Prepared NewRx:'); @@ -428,11 +353,11 @@ export default class RequestBox extends Component { }; isOrderNotSelected() { - return Object.keys(this.state.request).length === 0; + return Object.keys(this.props.request).length === 0; } isPatientNotSelected() { - return Object.keys(this.state.patient).length === 0; + return Object.keys(this.props.patient).length === 0; } // SnackBar @@ -448,69 +373,39 @@ export default class RequestBox extends Component { const { open } = this.state; return (
-
- - - {/* Patient selection pop up and search */} - - {this.state.patientList instanceof Error - ? this.renderError() - : } - - -
- -
- {this.state.patient.id ? ( - Patient ID: {this.state.patient.id} - ) : ( - No patient selected - )} -
-
- {this.renderPatientInfo()} - {this.renderPrefetchedResources()} -
+ { this.props.patient.id ? ( +
+
+
+ Patient ID: {this.props.patient.id} +
+
+ {this.renderPatientInfo()} + {this.renderPrefetchedResources()} +
+
+
+ + + + + + +
-
- {this.state.patient.id ? ( -
- - - - - - - + )} + -
- ) : ( - - )}
); } diff --git a/src/components/RequestBox/request.css b/src/components/RequestBox/request.css index 2da628c9..14b6aedd 100644 --- a/src/components/RequestBox/request.css +++ b/src/components/RequestBox/request.css @@ -12,10 +12,10 @@ .request { border: 1px solid black; - height:375px; + height: auto; padding: 10px; border-radius: 5px; - background-color: rgb(248, 248, 248) + background-color: rgb(248, 248, 248); } .select-button{ @@ -123,4 +123,8 @@ .empty-field { color: dimgrey; font-style: italic; +} + +.patient-info { + margin: 5px; } \ No newline at end of file diff --git a/src/components/SMARTBox/PatientBox.js b/src/components/SMARTBox/PatientBox.js index af8001ff..ea337028 100644 --- a/src/components/SMARTBox/PatientBox.js +++ b/src/components/SMARTBox/PatientBox.js @@ -118,7 +118,7 @@ export default class PatientBox extends Component { updateValues(patient) { this.props.callback('patient', patient); - this.props.callback('openPatient', false); + this.props.callback('expanded', false); this.props.clearCallback(); if (this.state.request) { const request = JSON.parse(this.state.request); @@ -134,6 +134,10 @@ export default class PatientBox extends Component { } } else { this.updatePrefetchRequest(null, patient, this.props.defaultUser); + this.props.callback('request', {}); + this.props.callback('code', null); + this.props.callback('codeSystem', null); + this.props.callback('display', null); } if (this.state.response) { @@ -323,7 +327,6 @@ export default class PatientBox extends Component { } getRequests() { - console.log(this.props.client); const patientId = this.props.patient.id; this.getDeviceRequest(patientId); this.getServiceRequest(patientId); diff --git a/src/components/SMARTBox/smart.css b/src/components/SMARTBox/smart.css index 69043330..243de77e 100644 --- a/src/components/SMARTBox/smart.css +++ b/src/components/SMARTBox/smart.css @@ -86,6 +86,7 @@ html{ .select-btn { height: 40px; align-self: center; + margin-top: 25px !important; } .emptyForm { diff --git a/src/containers/RequestBuilder.js b/src/containers/RequestBuilder.js index 777538cd..f6eed7b8 100644 --- a/src/containers/RequestBuilder.js +++ b/src/containers/RequestBuilder.js @@ -1,16 +1,23 @@ import React, { Component } from 'react'; - +import { Button, Box, IconButton } from '@mui/material'; +import PersonIcon from '@mui/icons-material/Person'; +import RefreshIcon from '@mui/icons-material/Refresh'; import DisplayBox from '../components/DisplayBox/DisplayBox'; -import ConsoleBox from '../components/ConsoleBox/ConsoleBox'; import '../index.css'; -import '../components/ConsoleBox/consoleBox.css'; import SettingsBox from '../components/SettingsBox/SettingsBox'; import RequestBox from '../components/RequestBox/RequestBox'; import buildRequest from '../util/buildRequest.js'; -import { types } from '../util/data.js'; -import { createJwt } from '../util/auth'; +import { types, defaultValues } from '../util/data.js'; +import { createJwt, setupKeys } from '../util/auth'; + import env from 'env-var'; import FHIR from 'fhirclient'; +import Accordion from '@mui/material/Accordion'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; + +import PatientSearchBar from '../components/RequestBox/PatientSearchBar/PatientSearchBar'; export default class RequestBuilder extends Component { constructor(props) { @@ -18,11 +25,19 @@ export default class RequestBuilder extends Component { this.state = { loading: false, logs: [], - patient: {}, + patient: {}, + expanded: false, + patientList: [], response: null, + code: null, + codeSystem: null, + display: null, + prefetchedResources: new Map(), + request: {}, showSettings: false, token: null, client: this.props.client, + codeValues: defaultValues, // Configurable values alternativeTherapy: env.get('REACT_APP_ALT_DRUG').asBool(), baseUrl: env.get('REACT_APP_EHR_BASE').asString(), @@ -57,6 +72,8 @@ export default class RequestBuilder extends Component { if (!this.state.client) { this.reconnectEhr(); } else { + // Call patients on load of page + this.getPatients(); this.setState({ baseUrl: this.state.client.state.serverUrl }); this.setState({ ehrUrl: this.state.client.state.serverUrl }); } @@ -84,8 +101,15 @@ export default class RequestBuilder extends Component { updateStateElement = (elementName, text) => { this.setState({ [elementName]: text }); + // if the patientFhirQuery is updated, make a call to get the patients + if (elementName === 'patientFhirQuery') { + setTimeout(() => { + this.getPatients(); + }, 1000); + } }; + timeout = time => { let controller = new AbortController(); setTimeout(() => controller.abort(), time * 1000); @@ -177,6 +201,48 @@ export default class RequestBuilder extends Component { this.requestBox.current.replaceRequestAndSubmit(resource); } + getPatients = () => { + this.props.client + .request(this.state.patientFhirQuery, { flat: true }) + .then(result => { + this.setState({ + patientList: result, + }); + }) + .catch(e => { + this.setState({ + patientList: e + }); + }); + }; + + updateStateList = (elementName, text) => { + this.setState(prevState => { + return { [elementName]: [...prevState[elementName], text] }; + }); + }; + + updateStateMap = (elementName, key, text) => { + this.setState(prevState => { + if (!prevState[elementName][key]) { + prevState[elementName][key] = []; + } + return { [elementName]: { ...prevState[elementName], [key]: text } }; + }); + }; + + clearState = () => { + this.setState({ + prefetchedResources: new Map(), + practitioner: {}, + coverage: {}, + response: {} + }); + }; + handleChange = () => (event, isExpanded) => { + this.setState({ expanded: isExpanded ? true: false}); + }; + render() { return (
@@ -198,15 +264,66 @@ export default class RequestBuilder extends Component { Reconnect EHR
-
-
+
+ {/*
*/} {this.state.showSettings && ( - - )} +
+ +
+ )} +
+
+ + } + aria-controls="panel1a-content" + id="panel1a-header" + style={{marginLeft: '45%'}} + > + + + + {this.state.patientList.length > 0 && this.state.expanded ? +
+ + {this.state.patientList instanceof Error + ? this.renderError() + : } + +
+ : + } + +
+
+ this.getPatients()} + size="large" + > + + +
+
{/*for the ehr launch */}
-
- -
-
-
-
diff --git a/src/index.css b/src/index.css index 5ab4fed4..19229868 100644 --- a/src/index.css +++ b/src/index.css @@ -15,8 +15,6 @@ body { box-shadow: none; } - - .floating-label { position: absolute; pointer-events: none; @@ -307,11 +305,12 @@ input:not(:focus):not([value=""]):valid ~ .floating-label{ } .nav-header{ - margin-bottom: 10px; - height: 55px; - padding:10px; - border-bottom: 1px solid black; - background-color: #005B94; + margin-bottom: 10px; + display: flow; + height: 55px; + padding:10px; + border-bottom: 1px solid black; + background-color: #005B94; } .loading{ @@ -325,4 +324,9 @@ input:not(:focus):not([value=""]):valid ~ .floating-label{ .title { margin-bottom: 65px; +} + +.settings-box { + width: 50%; + margin-left: 20px; } \ No newline at end of file