diff --git a/src/components/App.js b/src/components/App.js index dd43cadf..f22c2a08 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,5 +1,5 @@ import { ThemeProvider } from '@mui/styles'; -import React from 'react'; +import React, { useEffect } from 'react'; import { BrowserRouter, HashRouter, Route, Routes } from 'react-router-dom'; import Gateway from '../containers/Gateway/Gateway'; import Index from '../containers/Index'; @@ -7,10 +7,21 @@ import Launch from '../containers/Launch'; import PatientPortal from '../containers/PatientPortal'; import RegisterPage from '../containers/register/RegisterPage'; import theme from '../containers/styles/theme'; +import { SettingsContext } from '../containers/ContextProvider/SettingsProvider'; +import { actionTypes } from '../containers/ContextProvider/reducer'; + const isGhPages = process.env.REACT_APP_GH_PAGES === 'true'; const Router = isGhPages ? HashRouter : BrowserRouter; const redirect = isGhPages ? '/request-generator/#/index' : '/index'; const App = () => { + const [state, dispatch] = React.useContext(SettingsContext); + useEffect(() => { + dispatch({ + type: actionTypes.updateSetting, + settingId: 'redirect', + value: redirect + }); + }, []); return ( diff --git a/src/components/DisplayBox/DisplayBox.js b/src/components/DisplayBox/DisplayBox.js index cd8c1374..7a510481 100644 --- a/src/components/DisplayBox/DisplayBox.js +++ b/src/components/DisplayBox/DisplayBox.js @@ -392,12 +392,11 @@ export default class DisplayBox extends Component { } return (
- Notification Cards ({renderedCards.length})
{renderedCards}
+
{renderedCards}
); } else { return
; } } - } diff --git a/src/components/DisplayBox/card-list.css b/src/components/DisplayBox/card-list.css index 229f5a8c..3adcffe7 100644 --- a/src/components/DisplayBox/card-list.css +++ b/src/components/DisplayBox/card-list.css @@ -1,6 +1,6 @@ .decision-card { padding: 15px; - margin: 10px; + margin: 0 0 10px 10px; background: #fcfcfc; border: 1px solid rgba(0, 0, 0, 0.12); border-radius: 4px; diff --git a/src/components/RequestBox/InProgressFormBox/InProgressFormBox.js b/src/components/RequestBox/InProgressFormBox/InProgressFormBox.js index a9d90891..aba49800 100644 --- a/src/components/RequestBox/InProgressFormBox/InProgressFormBox.js +++ b/src/components/RequestBox/InProgressFormBox/InProgressFormBox.js @@ -3,27 +3,35 @@ import React from 'react'; import './InProgressFormBoxStyle.css'; export default function InProgressFormBox(props) { - return ( - props.qrResponse?.questionnaire ? ( - - - In Progress Form - - - Practitioner: {props.qrResponse.author ? props.qrResponse.author.reference : 'empty'} - - - Last Edited: {props.qrResponse.authored ? props.qrResponse.authored : 'empty'} - - - Form Link: {props.qrResponse.questionnaire ? props.qrResponse.questionnaire : 'empty'} - - - - - - ) : '' - ); -} \ No newline at end of file + return props.qrResponse?.questionnaire ? ( + + + In Progress Form + + + Practitioner: + {props.qrResponse.author ? props.qrResponse.author.reference : 'empty'} + + + Last Edited: {' '} + {props.qrResponse.authored ? props.qrResponse.authored : 'empty'} + + + Form Link: + {props.qrResponse.questionnaire ? props.qrResponse.questionnaire : 'empty'} + + + + + + ) : ( + '' + ); +} diff --git a/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js b/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js index 881d1bc7..34aa1e43 100644 --- a/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js +++ b/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js @@ -1,7 +1,9 @@ -import { Autocomplete, Box, TextField } from '@mui/material'; +import { Autocomplete, Box, TextField, IconButton } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { PrefetchTemplate } from '../../../PrefetchTemplate'; import { defaultValues } from '../../../util/data'; +import RefreshIcon from '@mui/icons-material/Refresh'; + import PatientBox from '../../SMARTBox/PatientBox'; import './PatientSearchBarStyle.css'; @@ -56,6 +58,9 @@ export default function PatientSearchBar(props) { Showing {getFilteredLength(input, listOfPatients)} of {props.searchablePatients.length}{' '} records

+ props.getPatients()} size="large"> + + {displayFilteredPatientList(input, listOfPatients[0])} diff --git a/src/components/RequestBox/PatientSearchBar/PatientSearchBarStyle.css b/src/components/RequestBox/PatientSearchBar/PatientSearchBarStyle.css index 4908c658..010c5dd5 100644 --- a/src/components/RequestBox/PatientSearchBar/PatientSearchBarStyle.css +++ b/src/components/RequestBox/PatientSearchBar/PatientSearchBarStyle.css @@ -17,5 +17,6 @@ .search-header { display: flex; + padding: 12px; align-items: center; } \ No newline at end of file diff --git a/src/components/RequestBox/RequestBox.js b/src/components/RequestBox/RequestBox.js index 85dcd7e9..36ecab22 100644 --- a/src/components/RequestBox/RequestBox.js +++ b/src/components/RequestBox/RequestBox.js @@ -1,4 +1,4 @@ -import { Button, ButtonGroup } from '@mui/material'; +import { Button, ButtonGroup, Grid } from '@mui/material'; import _ from 'lodash'; import React, { Component } from 'react'; import buildNewRxRequest from '../../util/buildScript.2017071.js'; @@ -382,8 +382,14 @@ export default class RequestBox extends Component { Patient ID: {this.props.patient.id}
- {this.renderPatientInfo()} - {this.renderPrefetchedResources()} + + + {this.renderPatientInfo()} + + + {this.renderPrefetchedResources()} + +
diff --git a/src/components/RequestBox/request.css b/src/components/RequestBox/request.css index 14b6aedd..be3c7e79 100644 --- a/src/components/RequestBox/request.css +++ b/src/components/RequestBox/request.css @@ -12,7 +12,6 @@ .request { border: 1px solid black; - height: auto; padding: 10px; border-radius: 5px; background-color: rgb(248, 248, 248); @@ -32,8 +31,6 @@ } .demographics { - width: 50%; - float: left; padding:10px 10px 10px 0px; } @@ -54,8 +51,6 @@ } .prefetched { - width: 50%; - float:left; padding:10px 10px 10px 0px; margin-top: 5px; } diff --git a/src/components/RequestDashboard/Home.jsx b/src/components/RequestDashboard/Home.jsx new file mode 100644 index 00000000..a1b0a231 --- /dev/null +++ b/src/components/RequestDashboard/Home.jsx @@ -0,0 +1,115 @@ +import React, { memo, useState } from 'react'; +import { Button, Grid, Tooltip } from '@mui/material'; +import PersonIcon from '@mui/icons-material/Person'; +import AssignmentIcon from '@mui/icons-material/Assignment'; +import SettingsIcon from '@mui/icons-material/Settings'; + +import useStyles from './styles'; +import PatientSection from './PatientSection'; +import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider'; +import SettingsSection from './SettingsSection'; +import TasksSection from './TasksSection'; + +const Home = props => { + const classes = useStyles(); + const patientButton = 'Select a Patient'; + const taskButton = 'View Tasks'; + const settingsButton = 'Settings'; + const [section, setSection] = useState(''); + const [state, dispatch] = React.useContext(SettingsContext); + + const openSection = buttonId => { + setSection(buttonId); + }; + + // renders top menu tab buttons + const renderMainButton = (buttonId, icon) => { + let buttonClass = `${classes.mainButton} ${classes.mainButtonView}`; + let gridWidth = 2; + let tooltip = ''; + if (section) { + // section active, switch button view + buttonClass = `${classes.mainButton} ${classes.tabButtonView}`; + if (buttonId === section) { + buttonClass += ` ${classes.selectedTabView}`; + } + gridWidth = 0; + tooltip =
{buttonId}
; + } + return ( + + + + + + ); + }; + // render view depending on which tab button is selected + const renderMainView = () => { + let gridClass = `${classes.mainDiv} ${classes.mainDivView}`; + if (section) { + gridClass = `${classes.mainDiv} ${classes.tabDivView}`; + } + return ( + + {section ? '' : } {/* spacer */} + {renderMainButton(patientButton, )} + {renderMainButton(taskButton, )} + {renderMainButton(settingsButton, )} + {section ? ( + +
+
+ ) : ( + + )} + {/* spacer */} +
+ ); + }; + + // render content of each view, makes other content invisible so it doesn't rerender every time + const renderSectionView = () => { + let renderSection =
Loading...
; + + if (section) { + let patientRenderClass = section === patientButton ? '' : classes.disappear; + let taskRenderClass = section === taskButton ? '' : classes.disappear; + let settingsRenderClass = section === settingsButton ? '' : classes.disappear; + + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); + } else { + return ''; + } + }; + + return ( +
+ {renderMainView()} + {renderSectionView()} +
+ ); +}; + +export default memo(Home); diff --git a/src/components/RequestDashboard/PatientSection.jsx b/src/components/RequestDashboard/PatientSection.jsx new file mode 100644 index 00000000..a871bb69 --- /dev/null +++ b/src/components/RequestDashboard/PatientSection.jsx @@ -0,0 +1,22 @@ +import React, { memo } from 'react'; + +import useStyles from './styles'; +import RequestBuilder from '../../containers/RequestBuilder'; +import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider'; + +const PatientSection = props => { + const classes = useStyles(); + const [state, dispatch] = React.useContext(SettingsContext); + // TODO: Make request builder use react-hooks + return ( +
+ {state.startup ? ( + + ) : ( + <>Loading... + )} +
+ ); +}; + +export default memo(PatientSection); diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx new file mode 100644 index 00000000..ec5a97fd --- /dev/null +++ b/src/components/RequestDashboard/SettingsSection.jsx @@ -0,0 +1,282 @@ +import React, { memo, useState, useEffect } from 'react'; +import { Button, Box, FormControlLabel, Grid, Checkbox, TextField } from '@mui/material'; + +import useStyles from './styles'; +import env from 'env-var'; +import FHIR from 'fhirclient'; + +import { headerDefinitions, types } from '../../util/data'; +import { actionTypes } from '../../containers/ContextProvider/reducer'; +import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider'; + +const SettingsSection = props => { + const classes = useStyles(); + const [state, dispatch] = React.useContext(SettingsContext); + const [headers, setHeaders] = useState([]); + const [originalValues, setOriginalValues] = useState([]); + + useEffect(() => { + const headers = Object.keys(headerDefinitions) + .map(key => ({ ...headerDefinitions[key], key })) + // Display the fields in descending order of type. If two fields are the same type, then sort by ascending order of display text. + .sort( + (self, other) => + -self.type.localeCompare(other.type) || self.display.localeCompare(other.display) + ); + setHeaders(headers); + const originals = Object.keys(headerDefinitions).map(key => [key, state[key]]); + setOriginalValues(originals); + JSON.parse(localStorage.getItem('reqgenSettings') || '[]').forEach(element => { + try { + updateSetting(element[0], element[1]); + } catch { + if (element[0]) { + console.log('Could not load setting:' + element[0]); + } + } + // indicate to the rest of the app that the settings have been loaded + dispatch({ + type: actionTypes.flagStartup + }); + }); + }, []); + const updateSetting = (key, value) => { + dispatch({ + type: actionTypes.updateSetting, + settingId: key, + value: value + }); + }; + const saveSettings = () => { + const headers = Object.keys(headerDefinitions).map(key => [key, state[key]]); + localStorage.setItem('reqgenSettings', JSON.stringify(headers)); + }; + + const resetSettings = () => { + originalValues.forEach(e => { + try { + updateSetting(e[0], e[1]); + } catch { + console.log('Failed to reset setting value'); + } + }); + }; + const clearQuestionnaireResponses = + ({ defaultUser }) => + () => { + props.client + .request('QuestionnaireResponse?author=' + defaultUser, { flat: true }) + .then(result => { + result.forEach(resource => { + props.client + .delete('QuestionnaireResponse/' + resource.id) + .then(result => { + console.log(result); + }) + .catch(e => { + console.log('Failed to delete QuestionnaireResponse ' + resource.id); + console.log(e); + }); + }); + }) + .catch(e => { + console.log('Failed to retrieve list of QuestionnaireResponses'); + console.log(e); + }); + }; + + const resetPims = + ({ pimsUrl }) => + () => { + let url = new URL(pimsUrl); + const resetUrl = url.origin + '/doctorOrders/api/deleteAll'; + console.log('reset pims: ' + resetUrl); + + fetch(resetUrl, { + method: 'DELETE' + }) + .then(response => { + console.log('Reset pims: '); + console.log(response); + }) + .catch(error => { + console.log('Reset pims error: '); + console.log(error); + }); + }; + const resetRemsAdmin = + ({ cdsUrl }) => + () => { + let url = new URL(cdsUrl); + const resetUrl = url.origin + '/etasu/reset'; + + fetch(resetUrl, { + method: 'POST' + }) + .then(response => { + console.log('Reset rems admin etasu: '); + console.log(response); + }) + .catch(error => { + console.log('Reset rems admin error: '); + console.log(error); + }); + }; + const clearMedicationDispenses = + ({ ehrUrl, access_token }) => + () => { + console.log('Clear MedicationDispenses from the EHR: ' + ehrUrl); + const client = FHIR.client({ + serverUrl: ehrUrl, + ...(access_token ? { tokenResponse: access_token } : {}) + }); + client + .request('MedicationDispense', { flat: true }) + .then(result => { + console.log(result); + result.forEach(resource => { + console.log(resource.id); + client + .delete('MedicationDispense/' + resource.id) + .then(result => { + console.log(result); + }) + .catch(e => { + console.log('Failed to delete MedicationDispense ' + resource.id); + console.log(e); + }); + }); + }) + .catch(e => { + console.log('Failed to retrieve list of MedicationDispense'); + console.log(e); + }); + }; + const reconnectEhr = + ({ baseUrl, redirect }) => + () => { + FHIR.oauth2.authorize({ + clientId: env.get('REACT_APP_CLIENT').asString(), + iss: baseUrl, + redirectUri: redirect, + scope: env.get('REACT_APP_CLIENT_SCOPES').asString() + }); + }; + const resetHeaderDefinitions = [ + { + display: 'Reset PIMS Database', + key: 'resetPims', + reset: resetPims + }, + { + display: 'Clear In-Progress Forms', + key: 'clearQuestionnaireResponses', + reset: clearQuestionnaireResponses + }, + { + display: 'Reset REMS-Admin Database', + key: 'resetRemsAdmin', + reset: resetRemsAdmin + }, + { + display: 'Clear EHR MedicationDispenses', + key: 'clearMedicationDispenses', + reset: clearMedicationDispenses + }, + { + display: 'Reconnect EHR', + key: 'reconnectEHR', + reset: reconnectEhr, + variant: 'contained' + } + ]; + + let firstCheckbox = true; + let showBreak = true; + return ( + + + {headers.map(({ key, type, display }) => { + switch (type) { + case 'input': + return ( + +
+ { + updateSetting(key, event.target.value); + }} + sx={{ width: '100%' }} + /> +
+
+ ); + case 'check': + if (firstCheckbox) { + firstCheckbox = false; + showBreak = true; + } else { + showBreak = false; + } + return ( + + {showBreak ? : ''} + + { + updateSetting(key, event.target.checked); + }} + /> + } + label={display} + /> + + + ); + default: + return ( +
+

{display}

+
+ ); + } + })} + {resetHeaderDefinitions.map(({ key, display, reset, variant }) => { + return ( + + + + ); + })} + {/* spacer */} +
+ + + + + + + + + +
+ ); +}; + +export default memo(SettingsSection); diff --git a/src/components/RequestDashboard/TabPanel.jsx b/src/components/RequestDashboard/TabPanel.jsx new file mode 100644 index 00000000..5857a316 --- /dev/null +++ b/src/components/RequestDashboard/TabPanel.jsx @@ -0,0 +1,23 @@ +import { Box } from '@mui/material'; +import React from 'react'; + +function TabPanel(props) { + const { children, value, index, name, ...other } = props; + + return ( + + ); +} + +export const MemoizedTabPanel = React.memo(TabPanel); diff --git a/src/components/RequestDashboard/TasksSection.jsx b/src/components/RequestDashboard/TasksSection.jsx new file mode 100644 index 00000000..431a09ec --- /dev/null +++ b/src/components/RequestDashboard/TasksSection.jsx @@ -0,0 +1,277 @@ +import React, { memo, useState, useEffect, Fragment } from 'react'; +import { Button, Box, Modal, Grid, Tabs, Tab, Stack } from '@mui/material'; +import AssignmentIcon from '@mui/icons-material/Assignment'; +import PersonIcon from '@mui/icons-material/Person'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditNoteIcon from '@mui/icons-material/EditNote'; +import AssignmentLateIcon from '@mui/icons-material/AssignmentLate'; +import AssignmentIndIcon from '@mui/icons-material/AssignmentInd'; +import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn'; +import PersonAddIcon from '@mui/icons-material/PersonAdd'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import useStyles from './styles'; +import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider'; +import { MemoizedTabPanel } from './TabPanel'; +import { Info, Refresh } from '@mui/icons-material'; + +const TasksSection = props => { + const classes = useStyles(); + const [tasks, setTasks] = useState([]); + const [state, dispatch] = React.useContext(SettingsContext); + const [value, setValue] = useState(0); + const [open, setOpen] = useState(false); + const [taskToDelete, setTaskToDelete] = useState(''); + const handleChange = (event, newValue) => { + setValue(newValue); + }; + const handleClose = () => { + setOpen(false); + }; + + const tryDelete = task => { + setTaskToDelete(task); + setOpen(true); + }; + const assignTask = task => { + // TODO: should allow assigning to anybody, not just self + if (task) { + let user = props.client.user.id; + if (!user) { + user = `Practitioner/${state.defaultUser}`; + } + task.owner = { + reference: user + }; + if (task.for) { + // reset 'for' element before updating + task.for = { + reference: `${task.for.resourceType}/${task.for.id}` + }; + } + props.client.update(task).then(e => { + fetchTasks(); + }); + } + }; + const deleteTask = () => { + if (taskToDelete) { + props.client.delete(`${taskToDelete.resourceType}/${taskToDelete.id}`).then(e => { + console.log('Deleted Task'); + fetchTasks(); + }); + setOpen(false); + setTaskToDelete(''); + } + }; + const fetchTasks = () => { + let identifier = 'Task'; + if (state.patient && state.patient.id) { + identifier = `Task?patient=${state.patient.id}`; + } + props.client.request(identifier, { resolveReferences: ['for', 'owner'] }).then(request => { + console.log(request); + if (request && request.entry) { + setTasks(request.entry.map(e => e.resource)); + } else { + setTasks([]); + } + }); + }; + useEffect(() => { + fetchTasks(); + }, []); + + useEffect(() => { + fetchTasks(); + }, [state.patient]); + const renderTasks = taskSubset => { + if (taskSubset.length > 0) { + return ( + + {taskSubset.map(t => renderTask(t))} + + ); + } else { + return

No Tasks Found

; + } + }; + const renderTask = task => { + let statusColor = '#555'; + if (task.status.toLowerCase() === 'ready') { + statusColor = '#198754'; + } + + let taskForName = 'N/A'; + let taskOwnerName = 'N/A'; + if (task.for.resourceType.toLowerCase() === 'patient') { + const patient = task.for; + if (patient.name) { + taskForName = `${patient.name[0].given[0]} ${patient.name[0].family}`; + } + } + if (task.owner && task.owner.resourceType.toLowerCase() === 'practitioner') { + const practitioner = task.owner; + if (practitioner.name) { + taskOwnerName = `${practitioner.name[0].given[0]} ${practitioner.name[0].family}`; + } else { + taskOwnerName = task.owner.id; + } + } + let ownerText = ( + + + Unassigned + + ); + if (task.owner) { + ownerText = ( + + + {`Assigned to ${taskOwnerName}`} + + ); + } + return ( + + + + + {`Task ID: ${task.id}`} + + + {`Timestamp: ${task.authoredOn}`} + + + {`Beneficiary: ${task.for ? task.for.id : 'None'}`} + + + {`STATUS: ${task.status.toUpperCase()}`} + + + {task.description} + + + + {taskForName} + + + + {ownerText} + + + + + + + + + {/*spacer*/} + + + + + + + + + + + ); + }; + + const unassignedTasks = tasks.filter(t => !t.owner); + const assignedTasks = tasks.filter(t => t.owner?.id === state.defaultUser); // should check current user, not default + return ( + <> + + + + + {taskToDelete ? `Are you sure you want to delete Task ${taskToDelete.id}` : ''} + + + {/*spacer*/} + + + + + + + + + + +
+ + + + } label={`ALL TASKS (${tasks.length})`} /> + } label={`MY TASKS (${assignedTasks.length})`} /> + } label={`UNASSIGNED TASKS (${unassignedTasks.length})`} /> + + + + + + + + {/* all tasks */} + {renderTasks(tasks)} + + + {/* my tasks */} + {renderTasks(assignedTasks)} + + + {/* unassigned tasks */} + {renderTasks(unassignedTasks)} + +
+ + ); +}; + +export default memo(TasksSection); diff --git a/src/components/RequestDashboard/styles.jsx b/src/components/RequestDashboard/styles.jsx new file mode 100644 index 00000000..00c52a63 --- /dev/null +++ b/src/components/RequestDashboard/styles.jsx @@ -0,0 +1,142 @@ +import { makeStyles } from '@mui/styles'; +export default makeStyles( + theme => ({ + disappear: { + display: 'none' + }, + spacer: { + height: '50px', // must be same as buttons + borderBottom: '1px solid black', + flexGrow: 1, + backgroundColor: '#005B94' + }, + mainButtonView: { + '&.MuiButtonBase-root': { + width: '600px', + maxWidth: '90%', + height: '150px', + backgroundColor: '#0d6efd', + opacity: '75%', + color: '#fcfcfc', + fontSize: '1.5rem' + } + }, + mainDiv: {}, + mainDivView: { + '&.MuiGrid-root': { + padding: '0 50px 0 50px', + marginTop: '35vh' + } + }, + mainIcon: { + '&.MuiSvgIcon-root': { + '&.MuiSvgIcon-fontSizeMedium': { + fontSize: '3rem' + } + } + }, + mainSectionView: { + width: 'auto', + height: 'auto', + borderLeft: '1px solid black', + borderRight: '1px solid black' + }, + noTasks: { + backgroundColor: '#e4e4e4', + padding: '10px', + fontSize: '18px' + }, + // DO NOT ALPHABETIZE + // if you must alphabetize this file to have classes + // sorted, rename tabButtonView and selectedTabView such + // that tabButtonView occurs earlier in the list. + // Otherwise, the styles will override incorrectly. + tabButtonView: { + '&.MuiButtonBase-root': { + width: '75px', + height: '50px', + opacity: '75%', + fontSize: '1.5rem', + border: '1px solid black', + boxShadow: 'none', + borderRadius: '0' + }, + '& > *': { + // generic child selector + '&.MuiButton-iconSizeMedium': { + // specificity + marginRight: 0 + } + } + }, + selectedTabView: { + '&.MuiButtonBase-root': { + color: 'black', + borderBottom: 'none', + backgroundColor: '#F5F5F7', + '&:hover': { + backgroundColor: '#F5F5F7', + boxShadow: 'none' + } + } + }, + taskDeleteHeader: { + padding: '15px 0 15px 0' + }, + taskDeleteModal: { + border: '1px solid black', + width: '400px', + + backgroundColor: 'white', + position: 'fixed', + top: '50%', + left: '50%', + padding: '15px', + transform: 'translate(-50%, -50%)', + overflowY: 'auto', + fontSize: '18px', + boxShadow: '10px 10px 20px black' + }, + tabDivView: { + '&.MuiGrid-root': { + marginTop: '0vh', + alignItems: 'flex-start', + justifyContent: 'flex-start' + } + }, + taskHeaderTabs: { + margin: '15px 15px 5px 15px', + backgroundColor: '#F5F5F7' + }, + taskRefreshButton: { + padding: '35px 0 0 0' + }, + taskTabButton: { + padding: '10px 0px 5px 0px' + }, + taskTabMain: { + border: '0px solid black', + boxShadow: '-2px -2px 3px 1px', + borderRadius: '5px', + padding: '8px', + background: 'linear-gradient(to right bottom, #F5F5F7, #eaeaef)', + '&:hover': { + background: 'linear-gradient(to right bottom, #FFFFFF, #efefff)' + } + }, + taskTabHeader: { + fontSize: '9px', + color: '#777', + borderBottom: '1px solid #e3e3ef' + }, + taskTabDescription: { + fontSize: '18px', + padding: '8px 0px 10px 2px' + }, + taskTabOwner: { + color: '#777' + } + }), + + { name: 'RequestDashboard', index: 1 } +); diff --git a/src/components/SMARTBox/PatientBox.js b/src/components/SMARTBox/PatientBox.js index 22f54f5b..11a71519 100644 --- a/src/components/SMARTBox/PatientBox.js +++ b/src/components/SMARTBox/PatientBox.js @@ -29,7 +29,7 @@ export default class PatientBox extends Component { questionnaireTitles: {}, showMedications: false, showQuestionnaires: false, - numInProgressForms: 0, + numInProgressForms: 0 }; this.handleRequestChange = this.handleRequestChange.bind(this); @@ -49,7 +49,7 @@ export default class PatientBox extends Component { componentDidMount() { // get requests and responses on open of patients this.getRequests(); - this.getResponses(); // TODO: PatientBox should not be rendering itself, needs to recieve its state from parent + this.getResponses(); // TODO: PatientBox should not be rendering itself, needs to receive its state from parent } getCoding(resource) { @@ -86,7 +86,6 @@ export default class PatientBox extends Component { return code; } - makeOption(request, options) { const code = this.getCoding(request); @@ -232,7 +231,6 @@ export default class PatientBox extends Component { flat: true }) .then(result => { - // add the medicationReference as a contained resource result?.data.forEach(e => { if (e?.medicationReference) { @@ -265,7 +263,6 @@ export default class PatientBox extends Component { } }); - this.setState({ medicationRequests: result }); }); } @@ -399,10 +396,10 @@ export default class PatientBox extends Component { } /** - * Launch In progress From - */ + * Launch In progress Form + */ - relaunch = (data) => { + relaunch = data => { this.handleResponseChange(data); this.props.callback('expanded', false); this.buildLaunchLink(data).then(link => { @@ -455,34 +452,38 @@ export default class PatientBox extends Component { let linkCopy = Object.assign({}, link); - const result = await retrieveLaunchContext(linkCopy, this.props.patient.id, this.props.client.state); + const result = await retrieveLaunchContext( + linkCopy, + this.props.patient.id, + this.props.client.state + ); linkCopy = result; return linkCopy; } makeResponseTable(columns, options, type, patient) { return ( - + - + - {columns.map((column) => ( - + {columns.map(column => ( + {column.label} ))} - {options.map((row) => ( - + {options.map(row => ( + this.handleRequestChange(row.value, patient)} > @@ -500,29 +501,29 @@ export default class PatientBox extends Component { makeQuestionnaireTable(columns, options, type, patient) { return ( - +
- + - {columns.map((column) => ( - + {columns.map(column => ( + {column.label} ))} - {options.map((row) => ( - + {options.map(row => ( + this.relaunch(row.value)} - className='hover-row' + className="hover-row" > {row.text} @@ -539,6 +540,9 @@ export default class PatientBox extends Component { render() { const patient = this.props.patient; + if (!patient) { + return <>; + } let name = ''; let fullName = ''; let formatBirthdate = ''; @@ -580,17 +584,19 @@ export default class PatientBox extends Component { } const medicationColumns = [ - { id: 'name', label: 'Medication'}, - { id: 'code', label: 'Request #'}, + { id: 'name', label: 'Medication' }, + { id: 'code', label: 'Request #' } ]; const questionnaireColumns = [ - { id: 'name', label: 'Title'}, - { id: 'time', label: 'Created'} + { id: 'name', label: 'Title' }, + { id: 'time', label: 'Created' } ]; - const medicationTooltip = options.length === 0 ? 'No medications found.' : `${options.length} medications available`; - const formTooltip = this.state.numInProgressForms === 0 ? 'No In-Progress Forms' : 'Open In-Progress Forms'; + const medicationTooltip = + options.length === 0 ? 'No medications found.' : `${options.length} medications available`; + const formTooltip = + this.state.numInProgressForms === 0 ? 'No In-Progress Forms' : 'Open In-Progress Forms'; return (
@@ -604,54 +610,97 @@ export default class PatientBox extends Component { Full Name: {fullName}
- Gender: {patient.gender.charAt(0).toUpperCase() + patient.gender.slice(1)} + Gender:{' '} + {patient.gender.charAt(0).toUpperCase() + patient.gender.slice(1)}
- DoB/Age: {formatBirthdate} ({getAge(patient.birthDate)} years old) + DoB/Age: {formatBirthdate} ( + {getAge(patient.birthDate)} years old)
- { this.state.showMedications ? - - : - - - - } - { this.state.showQuestionnaires ? - - : - - - - - - } - + {this.state.showMedications ? ( + + ) : ( + + + + + + )} + {this.state.showQuestionnaires ? ( + + ) : ( + + + + + + )} +
- { this.state.showMedications ? -
- { this.makeResponseTable(medicationColumns, options, 'medication', patient) } + {this.state.showMedications ? ( +
+ {this.makeResponseTable(medicationColumns, options, 'medication', patient)}
- : } - { this.state.showQuestionnaires ? -
- { this.makeQuestionnaireTable(questionnaireColumns, responseOptions, 'questionnaire', patient) } + ) : ( + + )} + {this.state.showQuestionnaires ? ( +
+ {this.makeQuestionnaireTable( + questionnaireColumns, + responseOptions, + 'questionnaire', + patient + )}
- : } + ) : ( + + )}
); } diff --git a/src/components/SettingsBox/SettingsBox.css b/src/components/SettingsBox/SettingsBox.css deleted file mode 100644 index 9f473389..00000000 --- a/src/components/SettingsBox/SettingsBox.css +++ /dev/null @@ -1,46 +0,0 @@ - -.setting-input { - margin: 1px 0 10px 0; - border-color: #999; - border-width: 1px; - border-style:solid; - height: 25px; -} - - -.setting-checkbox { - padding: 2px 4px; -} - -.setting-inner-checkbox { - margin-left: 1px; - margin-right: 1px; -} - -.setting-header{ - font-weight:bold; - margin:0; - margin-bottom: 10px; - padding:15px; - color:white; - background-color:#005B94; -} - -.setting-btn { - margin-top: 6px; - } - - -.setting-btn:hover{ - border-width:1px 3px 1px 1px; - margin-left:2px; - margin-top:8px; -} - -.setting-btn:disabled, -.setting-btn[disabled] { - border: 1px solid #999999; - background-color: #333232; - color: #858282; -} - diff --git a/src/components/SettingsBox/SettingsBox.js b/src/components/SettingsBox/SettingsBox.js deleted file mode 100644 index 684188ec..00000000 --- a/src/components/SettingsBox/SettingsBox.js +++ /dev/null @@ -1,285 +0,0 @@ -import React, { Component } from 'react'; -import './SettingsBox.css'; -import Checkbox from '@mui/material/Checkbox'; - -import { headerDefinitions, types } from '../../util/data'; -import FHIR from 'fhirclient'; -import { Box, Button, FormControlLabel, Grid, TextField } from '@mui/material'; - -const clearMedicationDispenses = - ({ ehrUrl, access_token }, consoleLog) => - () => { - console.log('Clear MedicationDispenses from the EHR: ' + ehrUrl); - const client = FHIR.client({ - serverUrl: ehrUrl, - ...(access_token ? { tokenResponse: access_token } : {}) - }); - client - .request('MedicationDispense', { flat: true }) - .then(result => { - console.log(result); - result.forEach(resource => { - console.log(resource.id); - client - .delete('MedicationDispense/' + resource.id) - .then(result => { - consoleLog( - 'Successfully deleted MedicationDispense ' + resource.id + ' from EHR', - types.info - ); - console.log(result); - }) - .catch(e => { - console.log('Failed to delete MedicationDispense ' + resource.id); - console.log(e); - }); - }); - }) - .catch(e => { - console.log('Failed to retrieve list of MedicationDispense'); - console.log(e); - }); - }; - -const clearQuestionnaireResponses = - ({ ehrUrl, defaultUser, access_token }, consoleLog) => - () => { - console.log( - 'Clear QuestionnaireResponses from the EHR: ' + ehrUrl + ' for author ' + defaultUser - ); - const client = FHIR.client({ - serverUrl: ehrUrl, - ...(access_token ? { tokenResponse: access_token } : {}) - }); - client - .request('QuestionnaireResponse?author=' + defaultUser, { flat: true }) - .then(result => { - console.log(result); - result.forEach(resource => { - console.log(resource.id); - client - .delete('QuestionnaireResponse/' + resource.id) - .then(result => { - consoleLog( - 'Successfully deleted QuestionnaireResponse ' + resource.id + ' from EHR', - types.info - ); - console.log(result); - }) - .catch(e => { - console.log('Failed to delete QuestionnaireResponse ' + resource.id); - console.log(e); - }); - }); - }) - .catch(e => { - console.log('Failed to retrieve list of QuestionnaireResponses'); - console.log(e); - }); - }; - -const resetPims = - ({ pimsUrl }, consoleLog) => - () => { - let url = new URL(pimsUrl); - const resetUrl = url.origin + '/doctorOrders/api/deleteAll'; - console.log('reset pims: ' + resetUrl); - - fetch(resetUrl, { - method: 'DELETE' - }) - .then(response => { - console.log('Reset pims: '); - console.log(response); - consoleLog('Successfully reset pims database', types.info); - }) - .catch(error => { - console.log('Reset pims error: '); - consoleLog('Server returned error when resetting pims: ', types.error); - consoleLog(error.message); - console.log(error); - }); - }; - -const resetRemsAdmin = - ({ cdsUrl }, consoleLog) => - () => { - let url = new URL(cdsUrl); - const resetUrl = url.origin + '/etasu/reset'; - - fetch(resetUrl, { - method: 'POST' - }) - .then(response => { - console.log('Reset rems admin etasu: '); - console.log(response); - consoleLog('Successfully reset rems admin etasu', types.info); - }) - .catch(error => { - console.log('Reset rems admin error: '); - consoleLog('Server returned error when resetting rems admin etasu: ', types.error); - consoleLog(error.message); - console.log(error); - }); - }; - -const resetHeaderDefinitions = [ - { - display: 'Clear In-Progress Forms', - key: 'clearQuestionnaireResponses', - reset: clearQuestionnaireResponses - }, - { - display: 'Clear EHR MedicationDispenses', - key: 'clearMedicationDispenses', - reset: clearMedicationDispenses - }, - { - display: 'Reset PIMS Database', - key: 'resetPims', - reset: resetPims - }, - { - display: 'Reset REMS-Admin Database', - key: 'resetRemsAdmin', - reset: resetRemsAdmin - } -]; - -export default class SettingsBox extends Component { - constructor(props) { - super(props); - this.state = { - originalValues: [] - }; - this.cancelSettings = this.cancelSettings.bind(this); - this.closeSettings = this.closeSettings.bind(this); - this.saveSettings = this.saveSettings.bind(this); - } - - componentDidMount() { - const headers = Object.keys(headerDefinitions).map(key => [key, this.props.state[key]]); - this.setState({ originalValues: headers }); - } - - closeSettings() { - this.props.updateCB('showSettings', false); - } - saveSettings() { - const headers = Object.keys(headerDefinitions).map(key => [key, this.props.state[key]]); - console.log(headers); - localStorage.setItem('reqgenSettings', JSON.stringify(headers)); - this.closeSettings(); - } - - cancelSettings() { - this.state.originalValues.forEach(e => { - try { - this.props.updateCB(e[0], e[1]); - } catch { - console.log('Failed to reset setting value'); - } - }); - this.closeSettings(); - } - - render() { - const { state, consoleLog, updateCB } = this.props; - let firstCheckbox = true; - let showBreak = true; - - const headers = Object.keys(headerDefinitions) - .map(key => ({ ...headerDefinitions[key], key })) - // Display the fields in descending order of type. If two fields are the same type, then sort by ascending order of display text. - .sort( - (self, other) => - -self.type.localeCompare(other.type) || self.display.localeCompare(other.display) - ); - - return ( - -

Settings

- - {headers.map(({ key, type, display }) => { - switch (type) { - case 'input': - return ( - -
- { - updateCB(key, event.target.value); - }} - sx={{ width: '100%' }} - /> -
-
- ); - case 'check': - if (firstCheckbox) { - firstCheckbox = false; - showBreak = true; - } else { - showBreak = false; - } - return ( - - {showBreak ? : ''} - - { - updateCB(key, event.target.checked); - }} - /> - } - label={display} - /> - - - ); - default: - return ( -
-

{display}

-
- ); - } - })} - {resetHeaderDefinitions.map(({ key, display, reset }) => { - return ( - - - - ); - })} - {/* spacer */} -
- - - - - - - - - -
- ); - } -} diff --git a/src/containers/ContextProvider/SettingsProvider.js b/src/containers/ContextProvider/SettingsProvider.js new file mode 100644 index 00000000..4350a5ff --- /dev/null +++ b/src/containers/ContextProvider/SettingsProvider.js @@ -0,0 +1,13 @@ +import React from 'react'; +import { reducer, initialState } from './reducer'; + +export const SettingsContext = React.createContext({ + state: initialState, + dispatch: () => null +}); + +export const SettingsProvider = ({ children }) => { + const [state, dispatch] = React.useReducer(reducer, initialState); + + return {children}; +}; diff --git a/src/containers/ContextProvider/reducer.js b/src/containers/ContextProvider/reducer.js new file mode 100644 index 00000000..5c856693 --- /dev/null +++ b/src/containers/ContextProvider/reducer.js @@ -0,0 +1,39 @@ +import { headerDefinitions } from '../../util/data'; +export const actionTypes = Object.freeze({ + updatePatient: 'update_patient', // {type, value} + updateSetting: 'update_setting', // {type, settingId, value} + flagStartup: 'flag_startup' // {type} +}); +// todo: add an enum that defines possible settings +export const reducer = (state, action) => { + switch (action.type) { + case actionTypes.updateSetting: + return { + ...state, + [action.settingId]: action.value + }; + case actionTypes.updatePatient: + return { + ...state, + patient: action.value + }; + case actionTypes.flagStartup: + return { + ...state, + startup: true + }; + default: + return state; + } +}; + +const initialState = { + patient: null, + startup: false, + redirect: '' +}; +Object.keys(headerDefinitions).forEach(e => { + initialState[e] = headerDefinitions[e].default; // fill default settings values +}); + +export { initialState }; diff --git a/src/containers/Index.jsx b/src/containers/Index.jsx index 3b74544b..0bf71379 100644 --- a/src/containers/Index.jsx +++ b/src/containers/Index.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import FHIR from 'fhirclient'; import env from 'env-var'; -import RequestBuilder from '../containers/RequestBuilder'; +import Home from '../components/RequestDashboard/Home'; const Index = props => { const [client, setClient] = useState(null); @@ -15,7 +15,7 @@ const Index = props => { return (
{client ? ( - + ) : (

Getting Client...

diff --git a/src/containers/RequestBuilder.js b/src/containers/RequestBuilder.js index e4845d42..7d4c3d5c 100644 --- a/src/containers/RequestBuilder.js +++ b/src/containers/RequestBuilder.js @@ -2,13 +2,13 @@ import React, { Component } from 'react'; import { Button, Box, Grid, IconButton, Modal, DialogTitle } from '@mui/material'; import PersonIcon from '@mui/icons-material/Person'; import RefreshIcon from '@mui/icons-material/Refresh'; +import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import DisplayBox from '../components/DisplayBox/DisplayBox'; import '../index.css'; -import SettingsBox from '../components/SettingsBox/SettingsBox'; import RequestBox from '../components/RequestBox/RequestBox'; import buildRequest from '../util/buildRequest.js'; import { types, defaultValues, headerDefinitions } from '../util/data.js'; -import { createJwt, setupKeys } from '../util/auth'; +import { createJwt } from '../util/auth'; import env from 'env-var'; import FHIR from 'fhirclient'; @@ -19,6 +19,7 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import SettingsIcon from '@mui/icons-material/Settings'; import PatientSearchBar from '../components/RequestBox/PatientSearchBar/PatientSearchBar'; import { MedicationStatus } from '../components/MedicationStatus/MedicationStatus.jsx'; +import { actionTypes } from './ContextProvider/reducer.js'; export default class RequestBuilder extends Component { constructor(props) { @@ -27,7 +28,7 @@ export default class RequestBuilder extends Component { loading: false, logs: [], patient: {}, - expanded: false, + expanded: true, patientList: [], response: {}, code: null, @@ -45,45 +46,28 @@ export default class RequestBuilder extends Component { this.submit_info = this.submit_info.bind(this); this.consoleLog = this.consoleLog.bind(this); this.takeSuggestion = this.takeSuggestion.bind(this); - this.reconnectEhr = this.reconnectEhr.bind(this); this.requestBox = React.createRef(); } componentDidMount() { - // init settings - Object.keys(headerDefinitions).map(key => { - this.setState({ [key]: headerDefinitions[key].default }); - }); - // load settings - JSON.parse(localStorage.getItem('reqgenSettings') || '[]').forEach(element => { - try { - this.updateStateElement(element[0], element[1]); - } catch { - if (element[0]) { - console.log('Could not load setting:' + element[0]); - } - } - }); - 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 }); + this.props.dispatch({ + type: actionTypes.updateSetting, + settingId: 'baseUrl', + value: this.state.client.state.serverUrl + }); + this.props.dispatch({ + type: actionTypes.updateSetting, + settingId: 'ehrUrl', + value: this.state.client.state.serverUrl + }); } } - reconnectEhr() { - FHIR.oauth2.authorize({ - clientId: env.get('REACT_APP_CLIENT').asString(), - iss: this.state.baseUrl, - redirectUri: this.props.redirect, - scope: env.get('REACT_APP_CLIENT_SCOPES').asString() - }); - } - consoleLog(content, type) { console.log(content); let jsonContent = { @@ -96,7 +80,14 @@ export default class RequestBuilder extends Component { } updateStateElement = (elementName, text) => { - this.setState({ [elementName]: text }); + if (elementName === 'patient') { + this.props.dispatch({ + type: actionTypes.updatePatient, + value: text + }); + } else { + this.setState({ [elementName]: text }); + } // if the patientFhirQuery is updated, make a call to get the patients if (elementName === 'patientFhirQuery') { setTimeout(() => { @@ -116,39 +107,39 @@ export default class RequestBuilder extends Component { this.consoleLog('Initiating form submission', types.info); this.setState({ patient }); const hookConfig = { - includeConfig: this.state.includeConfig, - alternativeTherapy: this.state.alternativeTherapy + includeConfig: this.props.globalState.includeConfig, + alternativeTherapy: this.props.globalState.alternativeTherapy }; - let user = this.state.defaultUser; + let user = this.props.globalState.defaultUser; let json_request = buildRequest( request, user, patient, - this.state.ehrUrlSentToRemsAdminForPreFetch, + this.props.globalState.ehrUrlSentToRemsAdminForPreFetch, this.state.client.state.tokenResponse, prefetch, - this.state.sendPrefetch, + this.props.globalState.sendPrefetch, hook, hookConfig ); - let cdsUrl = this.state.cdsUrl; + let cdsUrl = this.props.globalState.cdsUrl; if (hook === 'order-sign') { - cdsUrl = cdsUrl + '/' + this.state.orderSign; + cdsUrl = cdsUrl + '/' + this.props.globalState.orderSign; } else if (hook === 'order-select') { - cdsUrl = cdsUrl + '/' + this.state.orderSelect; + cdsUrl = cdsUrl + '/' + this.props.globalState.orderSelect; } else if (hook === 'patient-view') { - cdsUrl = cdsUrl + '/' + this.state.patientView; + cdsUrl = cdsUrl + '/' + this.props.globalState.patientView; } else { this.consoleLog("ERROR: unknown hook type: '", hook, "'"); return; } - let baseUrl = this.state.baseUrl; + let baseUrl = this.props.globalState.baseUrl; const headers = { 'Content-Type': 'application/json' }; - if (this.state.generateJsonToken) { + if (this.props.globalState.generateJsonToken) { const jwt = 'Bearer ' + createJwt(baseUrl, cdsUrl); headers.authorization = jwt; } @@ -197,18 +188,20 @@ export default class RequestBuilder extends Component { } getPatients = () => { - this.props.client - .request(this.state.patientFhirQuery, { flat: true }) - .then(result => { - this.setState({ - patientList: result - }); - }) - .catch(e => { - this.setState({ - patientList: e + if (this.props.globalState.patientFhirQuery) { + this.props.client + .request(this.props.globalState.patientFhirQuery, { flat: true }) + .then(result => { + this.setState({ + patientList: result + }); + }) + .catch(e => { + this.setState({ + patientList: e + }); }); - }); + } }; updateStateList = (elementName, text) => { @@ -244,47 +237,11 @@ export default class RequestBuilder extends Component { } render() { - const displayRequestBox = !!this.state.patient.id; + const displayRequestBox = !!this.props.globalState.patient?.id; const disableGetMedicationStatus = this.isOrderNotSelected() || this.state.loading; return ( <> - - - - { - this.setState({ showSettings: false }); - }} - > -
- -
-
-
@@ -309,15 +266,15 @@ export default class RequestBuilder extends Component { searchablePatients={this.state.patientList} client={this.props.client} request={this.state.request} - launchUrl={this.state.launchUrl} + launchUrl={this.props.globalState.launchUrl} callback={this.updateStateElement} callbackList={this.updateStateList} callbackMap={this.updateStateMap} // updatePrefetchCallback={PrefetchTemplate.generateQueries} clearCallback={this.clearState} options={this.state.codeValues} - responseExpirationDays={this.state.responseExpirationDays} - defaultUser={this.state.defaultUser} + responseExpirationDays={this.props.globalState.responseExpirationDays} + defaultUser={this.props.globalState.defaultUser} /> )} @@ -335,35 +292,38 @@ export default class RequestBuilder extends Component { {displayRequestBox && ( )} {!disableGetMedicationStatus && ( - + )} @@ -372,7 +332,7 @@ export default class RequestBuilder extends Component { @@ -382,22 +342,3 @@ export default class RequestBuilder extends Component { ); } } - -const navigationBarButtonStyle = { - backgroundColor: 'white', - color: 'black', - borderColor: 'black', - display: 'flex', - marginX: 2, - marginY: 1, - '&:hover': { - color: 'white', - backgroundColor: 'black', - borderColor: 'black' - }, - '&:active': { - color: 'white', - backgroundColor: 'black', - borderColor: 'black' - } -}; diff --git a/src/index.js b/src/index.js index 4c4b3f6c..2eea30c3 100644 --- a/src/index.js +++ b/src/index.js @@ -2,5 +2,11 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './components/App'; +import { SettingsProvider } from './containers/ContextProvider/SettingsProvider'; -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render( + + + , + document.getElementById('root') +); diff --git a/src/util/buildScript.2022071.js b/src/util/buildScript.2022071.js index 58bb9016..85e1a250 100644 --- a/src/util/buildScript.2022071.js +++ b/src/util/buildScript.2022071.js @@ -217,7 +217,8 @@ function buildNewRxMedication(doc, medicationRequestResource) { var drugCoded = doc.createElement('DrugCoded'); // loop through the coding values and find the ndc code and the rxnorm code - let medicationCodingList = getDrugCodeableConceptFromMedicationRequest(medicationRequestResource)?.coding; + let medicationCodingList = + getDrugCodeableConceptFromMedicationRequest(medicationRequestResource)?.coding; for (let i = 0; i < medicationCodingList.length; i++) { const coding = medicationCodingList[i]; const system = coding.system.toLowerCase(); diff --git a/src/util/data.js b/src/util/data.js index f135947b..b1f0b2e2 100644 --- a/src/util/data.js +++ b/src/util/data.js @@ -29,9 +29,7 @@ const headerDefinitions = { ehrUrlSentToRemsAdminForPreFetch: { display: 'EHR Server Sent to REMS Admin for Prefetch', type: 'input', - default: env - .get('REACT_APP_EHR_SERVER_TO_BE_SENT_TO_REMS_ADMIN_FOR_PREFETCH') - .asString() + default: env.get('REACT_APP_EHR_SERVER_TO_BE_SENT_TO_REMS_ADMIN_FOR_PREFETCH').asString() }, generateJsonToken: { display: 'Generate JSON Web Token', @@ -86,7 +84,7 @@ const headerDefinitions = { smartAppUrl: { display: 'SMART App', type: 'input', - default: env.get('REACT_APP_SMART_LAUNCH_URL').asString() + default: env.get('REACT_APP_SMART_LAUNCH_URL').asString() } }; diff --git a/src/util/fhir.js b/src/util/fhir.js index 6c2ae3c9..0cbb845e 100644 --- a/src/util/fhir.js +++ b/src/util/fhir.js @@ -1,4 +1,3 @@ - function fhir(resource, ehrUrl, patient, auth) { const headers = { 'Content-Type': 'application/json' @@ -29,9 +28,9 @@ function getAge(dateString) { } /* -* Retrieve the CodeableConcept for the medication from the medicationCodeableConcept if available. -* Read CodeableConcept from contained Medication matching the medicationReference otherwise. -*/ + * Retrieve the CodeableConcept for the medication from the medicationCodeableConcept if available. + * Read CodeableConcept from contained Medication matching the medicationReference otherwise. + */ function getDrugCodeableConceptFromMedicationRequest(medicationRequest) { if (medicationRequest) { if (medicationRequest?.medicationCodeableConcept) { @@ -48,20 +47,20 @@ function getDrugCodeableConceptFromMedicationRequest(medicationRequest) { } } }); - return coding; + return coding; } } return undefined; - } - - /* +} + +/* * Retrieve the coding for the medication from the medicationCodeableConcept if available. * Read coding from contained Medication matching the medicationReference otherwise. */ function getDrugCodeFromMedicationRequest(medicationRequest) { const codeableConcept = getDrugCodeableConceptFromMedicationRequest(medicationRequest); return codeableConcept?.coding?.[0]; - } +} function createMedicationDispenseFromMedicationRequest(medicationRequest) { console.log('createMedicationDispenseFromMedicationRequest'); @@ -75,8 +74,16 @@ function createMedicationDispenseFromMedicationRequest(medicationRequest) { medicationDispense.medicationReference = medicationRequest.medicationReference; } medicationDispense.subject = medicationRequest.subject; - medicationDispense.authorizingPrescription = [ { 'reference': 'MedicationRequest/' + medicationRequest.id } ]; + medicationDispense.authorizingPrescription = [ + { reference: 'MedicationRequest/' + medicationRequest.id } + ]; return medicationDispense; } -export { fhir, getAge, getDrugCodeableConceptFromMedicationRequest, getDrugCodeFromMedicationRequest, createMedicationDispenseFromMedicationRequest }; +export { + fhir, + getAge, + getDrugCodeableConceptFromMedicationRequest, + getDrugCodeFromMedicationRequest, + createMedicationDispenseFromMedicationRequest +};