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
+
- {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 (
+
+ );
+ }
+ })}
+ {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}
+
+
+ }>
+ View Resource
+
+
+
+ }
+ onClick={() => {
+ assignTask(task);
+ }}
+ >
+ Assign
+
+
+
+ {/*spacer*/}
+
+
+ }
+ onClick={() => {
+ tryDelete(task);
+ }}
+ >
+ Delete
+
+
+
+ }>
+ Process Task
+
+
+
+
+
+ );
+ };
+
+ 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})`} />
+
+
+
+ }
+ onClick={() => {
+ fetchTasks();
+ }}
+ >
+ Refresh
+
+
+
+
+ {/* 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 ?
- } onClick={() => this.setState({ showMedications: false })}>Close Medications
- :
-
- } disabled={options.length === 0} onClick={() => {
- this.updatePatient(patient);
- this.setState({ showMedications: true, showQuestionnaires: false });
- }}>Request New Medication
-
- }
- { this.state.showQuestionnaires ?
- } onClick={() => this.setState({ showQuestionnaires: false })}>Close In Progress Forms
- :
-
-
- } disabled={this.state.numInProgressForms === 0} onClick={() => {
- this.updatePatient(patient);
- this.setState({ showQuestionnaires: true, showMedications: false });
- }}>{this.state.numInProgressForms} Form(s) In Progress
-
-
- }
-
+ {this.state.showMedications ? (
+ }
+ onClick={() => this.setState({ showMedications: false })}
+ >
+ Close Medications
+
+ ) : (
+
+
+ }
+ disabled={options.length === 0}
+ onClick={() => {
+ this.updatePatient(patient);
+ this.setState({ showMedications: true, showQuestionnaires: false });
+ }}
+ >
+ Request New Medication
+
+
+
+ )}
+ {this.state.showQuestionnaires ? (
+ }
+ onClick={() => this.setState({ showQuestionnaires: false })}
+ >
+ Close In Progress Forms
+
+ ) : (
+
+
+ }
+ disabled={this.state.numInProgressForms === 0}
+ onClick={() => {
+ this.updatePatient(patient);
+ this.setState({ showQuestionnaires: true, showMedications: false });
+ }}
+ >
+ {this.state.numInProgressForms} Form(s) In Progress
+
+
+
+ )}
+
- { 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 (
-
- );
- }
- })}
- {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
+};