From cefa780aa74d67b067897d189d4f8840f3047c14 Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Thu, 1 Feb 2024 17:10:37 -0500 Subject: [PATCH 01/13] fix unrelated warning --- src/components/SMARTBox/PatientBox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SMARTBox/PatientBox.js b/src/components/SMARTBox/PatientBox.js index 22f54f5b..13926cbb 100644 --- a/src/components/SMARTBox/PatientBox.js +++ b/src/components/SMARTBox/PatientBox.js @@ -465,7 +465,7 @@ export default class PatientBox extends Component { - + {columns.map((column) => ( Date: Thu, 1 Feb 2024 17:11:23 -0500 Subject: [PATCH 02/13] remove unnecessary key --- src/components/SMARTBox/PatientBox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SMARTBox/PatientBox.js b/src/components/SMARTBox/PatientBox.js index 13926cbb..22f54f5b 100644 --- a/src/components/SMARTBox/PatientBox.js +++ b/src/components/SMARTBox/PatientBox.js @@ -465,7 +465,7 @@ export default class PatientBox extends Component {
- + {columns.map((column) => ( Date: Thu, 15 Feb 2024 15:17:33 -0500 Subject: [PATCH 03/13] add task ui and update other ui --- src/components/DisplayBox/DisplayBox.js | 2 +- src/components/DisplayBox/card-list.css | 2 +- .../PatientSearchBar/PatientSearchBar.js | 11 +- .../PatientSearchBarStyle.css | 1 + src/components/RequestBox/RequestBox.js | 12 +- src/components/RequestBox/request.css | 5 - src/components/RequestDashboard/Home.jsx | 115 +++++++ .../RequestDashboard/PatientSection.jsx | 15 + .../RequestDashboard/SettingsSection.jsx | 215 +++++++++++++ src/components/RequestDashboard/TabPanel.jsx | 24 ++ .../RequestDashboard/TasksSection.jsx | 248 +++++++++++++++ src/components/RequestDashboard/styles.jsx | 151 ++++++++++ src/components/SettingsBox/SettingsBox.css | 46 --- src/components/SettingsBox/SettingsBox.js | 285 ------------------ .../ContextProvider/SettingsProvider.js | 17 ++ src/containers/ContextProvider/reducer.js | 31 ++ src/containers/Index.jsx | 7 +- src/containers/RequestBuilder.js | 74 +++-- 18 files changed, 885 insertions(+), 376 deletions(-) create mode 100644 src/components/RequestDashboard/Home.jsx create mode 100644 src/components/RequestDashboard/PatientSection.jsx create mode 100644 src/components/RequestDashboard/SettingsSection.jsx create mode 100644 src/components/RequestDashboard/TabPanel.jsx create mode 100644 src/components/RequestDashboard/TasksSection.jsx create mode 100644 src/components/RequestDashboard/styles.jsx delete mode 100644 src/components/SettingsBox/SettingsBox.css delete mode 100644 src/components/SettingsBox/SettingsBox.js create mode 100644 src/containers/ContextProvider/SettingsProvider.js create mode 100644 src/containers/ContextProvider/reducer.js diff --git a/src/components/DisplayBox/DisplayBox.js b/src/components/DisplayBox/DisplayBox.js index cd8c1374..4a018084 100644 --- a/src/components/DisplayBox/DisplayBox.js +++ b/src/components/DisplayBox/DisplayBox.js @@ -392,7 +392,7 @@ export default class DisplayBox extends Component { } return (
- Notification Cards ({renderedCards.length})
{renderedCards}
+
{renderedCards}
); } else { 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/PatientSearchBar/PatientSearchBar.js b/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js index 881d1bc7..8974331f 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,13 @@ 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..2c8577cf 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..1b46899b --- /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..26c17810 --- /dev/null +++ b/src/components/RequestDashboard/PatientSection.jsx @@ -0,0 +1,15 @@ +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 so + // we can get rid of this hacky shim + return
; +}; + +export default memo(PatientSection); diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx new file mode 100644 index 00000000..7883dd20 --- /dev/null +++ b/src/components/RequestDashboard/SettingsSection.jsx @@ -0,0 +1,215 @@ +import React, { memo, useState, useEffect } from 'react'; +import { Button, Box, FormControlLabel, Grid, Checkbox, TextField } from '@mui/material'; + +import useStyles from './styles'; +import { headerDefinitions } from '../../util/data'; +import { stateActions } 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]); + } + } + }); + }, []); + const updateSetting = (key, value) => { + dispatch({ + type: stateActions.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 }) => + _event => { + 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 }) => + _event => { + 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 }) => + _event => { + 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 resetHeaderDefinitions = [ + { + display: 'Clear In-Progress Forms', + key: 'clearQuestionnaireResponses', + reset: clearQuestionnaireResponses + }, + { + display: 'Reset PIMS Database', + key: 'resetPims', + reset: resetPims + }, + { + display: 'Reset REMS-Admin Database', + key: 'resetRemsAdmin', + reset: resetRemsAdmin + } + ]; + + + 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 }) => { + 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..413badf7 --- /dev/null +++ b/src/components/RequestDashboard/TabPanel.jsx @@ -0,0 +1,24 @@ +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); + \ No newline at end of file diff --git a/src/components/RequestDashboard/TasksSection.jsx b/src/components/RequestDashboard/TasksSection.jsx new file mode 100644 index 00000000..d8b4a4e5 --- /dev/null +++ b/src/components/RequestDashboard/TasksSection.jsx @@ -0,0 +1,248 @@ +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..02c033b1 --- /dev/null +++ b/src/components/RequestDashboard/styles.jsx @@ -0,0 +1,151 @@ +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' + }, + mainButton: { + '&.MuiButtonBase-root': { + // transition: 'all 250ms ease-in 0ms', + } + }, + 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', + // margin: '0 15px 0 15px', // must be same as tabDivView + 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': { + // specificty + 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': { + // padding: '0 15px 0 15px', + 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', + borderRadius: '5px', + padding: '8px', + background: 'linear-gradient(to right bottom, #F5F5F7, #eaeaef)', + '&:hover': { + background: 'linear-gradient(to right bottom, #FFFFFF, #efefff)', + } + }, + taskTabHeader: { + fontSize: '8px', + 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/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..56eaeb58 --- /dev/null +++ b/src/containers/ContextProvider/SettingsProvider.js @@ -0,0 +1,17 @@ +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} + + ); +}; \ No newline at end of file diff --git a/src/containers/ContextProvider/reducer.js b/src/containers/ContextProvider/reducer.js new file mode 100644 index 00000000..77f3e014 --- /dev/null +++ b/src/containers/ContextProvider/reducer.js @@ -0,0 +1,31 @@ +import { headerDefinitions } from '../../util/data'; +export const stateActions = Object.freeze({ + updatePatient: 'update_patient', + updateSetting: 'update_setting', // {type, settingId, value} +}); +// todo: add an enum that defines possible settings +export const reducer = (state, action) => { + switch (action.type) { + case stateActions.updateSetting: + return { + ...state, + [action.settingId]: action.value + }; + case stateActions.updatePatient: + return { + ...state, + 'patient': action.value + }; + default: + return state; + } + }; + +const initialState = { + patient: null +}; +Object.keys(headerDefinitions).forEach((e) => { + initialState[e] = headerDefinitions[e].default; // fill default settings values +}); + +export { initialState }; \ No newline at end of file diff --git a/src/containers/Index.jsx b/src/containers/Index.jsx index 3b74544b..f69d8245 100644 --- a/src/containers/Index.jsx +++ b/src/containers/Index.jsx @@ -1,7 +1,8 @@ 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'; +import { SettingsProvider } from './ContextProvider/SettingsProvider'; const Index = props => { const [client, setClient] = useState(null); @@ -15,7 +16,9 @@ const Index = props => { return (
{client ? ( - + + + ) : (

Getting Client...

diff --git a/src/containers/RequestBuilder.js b/src/containers/RequestBuilder.js index e4845d42..b392b26a 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 { stateActions } 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, @@ -78,7 +79,7 @@ export default class RequestBuilder extends Component { reconnectEhr() { FHIR.oauth2.authorize({ clientId: env.get('REACT_APP_CLIENT').asString(), - iss: this.state.baseUrl, + iss: this.props.globalState.baseUrl, redirectUri: this.props.redirect, scope: env.get('REACT_APP_CLIENT_SCOPES').asString() }); @@ -96,7 +97,14 @@ export default class RequestBuilder extends Component { } updateStateElement = (elementName, text) => { - this.setState({ [elementName]: text }); + if(elementName === 'patient') { + this.props.dispatch({ + type: stateActions.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 +124,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,8 +205,9 @@ export default class RequestBuilder extends Component { } getPatients = () => { - this.props.client - .request(this.state.patientFhirQuery, { flat: true }) + if(this.props.globalState.patientFhirQuery) { + this.props.client + .request(this.props.globalState.patientFhirQuery, { flat: true }) .then(result => { this.setState({ patientList: result @@ -209,6 +218,7 @@ export default class RequestBuilder extends Component { patientList: e }); }); + } }; updateStateList = (elementName, text) => { @@ -309,15 +319,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,29 +345,29 @@ export default class RequestBuilder extends Component { {displayRequestBox && ( )} @@ -372,7 +382,7 @@ export default class RequestBuilder extends Component { From 37561aae298858d8170736348e03d3d2ddf88857 Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Thu, 15 Feb 2024 15:35:29 -0500 Subject: [PATCH 04/13] prettier --- src/components/DisplayBox/DisplayBox.js | 1 - src/components/RequestDashboard/Home.jsx | 186 +++---- .../RequestDashboard/PatientSection.jsx | 8 +- .../RequestDashboard/SettingsSection.jsx | 245 ++++----- src/components/RequestDashboard/TabPanel.jsx | 39 +- .../RequestDashboard/TasksSection.jsx | 473 ++++++++++-------- src/components/RequestDashboard/styles.jsx | 92 ++-- .../ContextProvider/SettingsProvider.js | 14 +- src/containers/ContextProvider/reducer.js | 42 +- 9 files changed, 568 insertions(+), 532 deletions(-) diff --git a/src/components/DisplayBox/DisplayBox.js b/src/components/DisplayBox/DisplayBox.js index 4a018084..7a510481 100644 --- a/src/components/DisplayBox/DisplayBox.js +++ b/src/components/DisplayBox/DisplayBox.js @@ -399,5 +399,4 @@ export default class DisplayBox extends Component { return
; } } - } diff --git a/src/components/RequestDashboard/Home.jsx b/src/components/RequestDashboard/Home.jsx index 1b46899b..43206752 100644 --- a/src/components/RequestDashboard/Home.jsx +++ b/src/components/RequestDashboard/Home.jsx @@ -11,105 +11,105 @@ 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 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); - }; + 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; + // 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 */} +
+ ); + }; - return (
-
-
-
-
-
-
+ // render content of each view, makes other content invisible so it doesn't rerender every time + const renderSectionView = () => { + let renderSection =
Loading...
; -
); - } else { - return ''; - } - }; + if (section) { + let patientRenderClass = section === patientButton ? '' : classes.disappear; + let taskRenderClass = section === taskButton ? '' : classes.disappear; + let settingsRenderClass = section === settingsButton ? '' : classes.disappear; - return ( -
- {renderMainView()} - {renderSectionView()} + return ( +
+
+ +
+
+ +
+
+ +
- ); + ); + } else { + return ''; + } + }; + + return ( +
+ {renderMainView()} + {renderSectionView()} +
+ ); }; export default memo(Home); diff --git a/src/components/RequestDashboard/PatientSection.jsx b/src/components/RequestDashboard/PatientSection.jsx index 26c17810..5443462b 100644 --- a/src/components/RequestDashboard/PatientSection.jsx +++ b/src/components/RequestDashboard/PatientSection.jsx @@ -6,10 +6,14 @@ import { SettingsContext } from '../../containers/ContextProvider/SettingsProvid const PatientSection = props => { const classes = useStyles(); - const [ state, dispatch ] = React.useContext(SettingsContext); + const [state, dispatch] = React.useContext(SettingsContext); // TODO: Make request builder use react-hooks so // we can get rid of this hacky shim - return
; + return ( +
+ +
+ ); }; export default memo(PatientSection); diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx index 7883dd20..f175ebaa 100644 --- a/src/components/RequestDashboard/SettingsSection.jsx +++ b/src/components/RequestDashboard/SettingsSection.jsx @@ -8,112 +8,113 @@ import { SettingsContext } from '../../containers/ContextProvider/SettingsProvid const SettingsSection = props => { const classes = useStyles(); - const [ state, dispatch ] = React.useContext(SettingsContext); + const [state, dispatch] = React.useContext(SettingsContext); const [headers, setHeaders] = useState([]); const [originalValues, setOriginalValues] = useState([]); - - useEffect(() => { + 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) - ); + .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]])); + 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]); - } + 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]); } + } }); }, []); const updateSetting = (key, value) => { dispatch({ - type: stateActions.updateSetting, - settingId: key, - value: value + type: stateActions.updateSetting, + settingId: key, + value: value }); }; const saveSettings = () => { - const headers = Object.keys(headerDefinitions).map(key => ([key, state[key]])); + const headers = Object.keys(headerDefinitions).map(key => [key, state[key]]); localStorage.setItem('reqgenSettings', JSON.stringify(headers)); }; const resetSettings = () => { - originalValues.forEach((e) => { - try{ + originalValues.forEach(e => { + try { updateSetting(e[0], e[1]); } catch { console.log('Failed to reset setting value'); } }); }; - const clearQuestionnaireResponses = ({ defaultUser }) => - _event => { - 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); - }); + const clearQuestionnaireResponses = + ({ defaultUser }) => + _event => { + 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); }); - }) - .catch(e => { - console.log('Failed to retrieve list of QuestionnaireResponses'); - console.log(e); - }); - }; + }; - const resetPims = ({ pimsUrl }) => - _event => { - let url = new URL(pimsUrl); - const resetUrl = url.origin + '/doctorOrders/api/deleteAll'; - console.log('reset pims: ' + resetUrl); + const resetPims = + ({ pimsUrl }) => + _event => { + 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); + fetch(resetUrl, { + method: 'DELETE' }) - .catch(error => { - console.log('Reset pims error: '); - console.log(error); - }); - }; + .then(response => { + console.log('Reset pims: '); + console.log(response); + }) + .catch(error => { + console.log('Reset pims error: '); + console.log(error); + }); + }; const resetRemsAdmin = - ({ cdsUrl }) => - _event => { - let url = new URL(cdsUrl); - const resetUrl = url.origin + '/etasu/reset'; + ({ cdsUrl }) => + _event => { + 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); + fetch(resetUrl, { + method: 'POST' }) - .catch(error => { - console.log('Reset rems admin error: '); - console.log(error); - }); - }; + .then(response => { + console.log('Reset rems admin etasu: '); + console.log(response); + }) + .catch(error => { + console.log('Reset rems admin error: '); + console.log(error); + }); + }; const resetHeaderDefinitions = [ { display: 'Clear In-Progress Forms', @@ -131,33 +132,33 @@ const SettingsSection = props => { reset: resetRemsAdmin } ]; - 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%'}} - /> -
+ +
+ { + updateSetting(key, event.target.value); + }} + sx={{ width: '100%' }} + /> +
); case 'check': - if(firstCheckbox) { + if (firstCheckbox) { firstCheckbox = false; showBreak = true; } else { @@ -165,14 +166,18 @@ const SettingsSection = props => { } return ( - {showBreak ? :''} + {showBreak ? : ''} - {updateSetting(key, event.target.checked);}}/> + { + updateSetting(key, event.target.checked); + }} + /> } - label = {display} + label={display} /> @@ -185,30 +190,36 @@ const SettingsSection = props => { ); } })} - {resetHeaderDefinitions.map(({ key, display, reset }) => { + {resetHeaderDefinitions.map(({ key, display, reset }) => { return ( - - - +
); - })} - {/* spacer */} -
- - - - - - - - + })} + {/* spacer */} +
+ + + + + + + -
-
+
+
+
); }; diff --git a/src/components/RequestDashboard/TabPanel.jsx b/src/components/RequestDashboard/TabPanel.jsx index 413badf7..5857a316 100644 --- a/src/components/RequestDashboard/TabPanel.jsx +++ b/src/components/RequestDashboard/TabPanel.jsx @@ -2,23 +2,22 @@ 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); - \ No newline at end of file + 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 index d8b4a4e5..431a09ec 100644 --- a/src/components/RequestDashboard/TasksSection.jsx +++ b/src/components/RequestDashboard/TasksSection.jsx @@ -15,234 +15,263 @@ 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(); - }, []); + 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); + }; - useEffect(() => { + 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(); - }, [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'; - } + }); + } + }; + 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(); + }, []); - 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*/} - - - - - - - - - - - ); - }; + 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'; + } - const unassignedTasks = tasks.filter((t) => !t.owner); - const assignedTasks = tasks.filter((t) => t.owner?.id === state.defaultUser); // should check current user, not default + 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 ( - <> - - - - - {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)} - -
- + + + + + {`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 index 02c033b1..a4bc9998 100644 --- a/src/components/RequestDashboard/styles.jsx +++ b/src/components/RequestDashboard/styles.jsx @@ -2,7 +2,7 @@ import { makeStyles } from '@mui/styles'; export default makeStyles( theme => ({ disappear: { - display: 'none' + display: 'none' }, spacer: { height: '50px', // must be same as buttons @@ -45,16 +45,16 @@ export default makeStyles( height: 'auto', // margin: '0 15px 0 15px', // must be same as tabDivView borderLeft: '1px solid black', - borderRight: '1px solid black', + borderRight: '1px solid black' }, noTasks: { - backgroundColor: '#e4e4e4', - padding: '10px', - fontSize: '18px' + backgroundColor: '#e4e4e4', + padding: '10px', + fontSize: '18px' }, // DO NOT ALPHABETIZE // if you must alphabetize this file to have classes - // sorted, rename tabButtonView and selectedTabView such + // sorted, rename tabButtonView and selectedTabView such // that tabButtonView occurs earlier in the list. // Otherwise, the styles will override incorrectly. tabButtonView: { @@ -65,8 +65,7 @@ export default makeStyles( fontSize: '1.5rem', border: '1px solid black', boxShadow: 'none', - borderRadius: '0', - + borderRadius: '0' }, '& > *': { // generic child selector @@ -77,32 +76,32 @@ export default makeStyles( } }, selectedTabView: { - '&.MuiButtonBase-root': { - color: 'black', - borderBottom: 'none', + '&.MuiButtonBase-root': { + color: 'black', + borderBottom: 'none', + backgroundColor: '#F5F5F7', + '&:hover': { backgroundColor: '#F5F5F7', - '&:hover': { - backgroundColor: '#F5F5F7', - boxShadow: 'none' - }, + boxShadow: 'none' } + } }, taskDeleteHeader: { - padding: '15px 0 15px 0', + padding: '15px 0 15px 0' }, taskDeleteModal: { - border: '1px solid black', - width: '400px', + 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' + 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': { @@ -113,39 +112,38 @@ export default makeStyles( } }, taskHeaderTabs: { - margin: '15px 15px 5px 15px', - backgroundColor: '#F5F5F7', + margin: '15px 15px 5px 15px', + backgroundColor: '#F5F5F7' }, taskRefreshButton: { - padding: '35px 0 0 0' + padding: '35px 0 0 0' }, taskTabButton: { - padding: '10px 0px 5px 0px' + padding: '10px 0px 5px 0px' }, taskTabMain: { - border: '0px solid black', - boxShadow: '2px 2px', - borderRadius: '5px', - padding: '8px', - background: 'linear-gradient(to right bottom, #F5F5F7, #eaeaef)', - '&:hover': { - background: 'linear-gradient(to right bottom, #FFFFFF, #efefff)', - } + border: '0px solid black', + boxShadow: '2px 2px', + borderRadius: '5px', + padding: '8px', + background: 'linear-gradient(to right bottom, #F5F5F7, #eaeaef)', + '&:hover': { + background: 'linear-gradient(to right bottom, #FFFFFF, #efefff)' + } }, taskTabHeader: { - fontSize: '8px', - color: '#777', - borderBottom: '1px solid #e3e3ef', + fontSize: '8px', + color: '#777', + borderBottom: '1px solid #e3e3ef' }, taskTabDescription: { - fontSize: '18px', - padding: '8px 0px 10px 2px', + fontSize: '18px', + padding: '8px 0px 10px 2px' }, taskTabOwner: { - color: '#777' - }, + color: '#777' + } }), - { name: 'RequestDashboard', index: 1 } ); diff --git a/src/containers/ContextProvider/SettingsProvider.js b/src/containers/ContextProvider/SettingsProvider.js index 56eaeb58..4350a5ff 100644 --- a/src/containers/ContextProvider/SettingsProvider.js +++ b/src/containers/ContextProvider/SettingsProvider.js @@ -2,16 +2,12 @@ import React from 'react'; import { reducer, initialState } from './reducer'; export const SettingsContext = React.createContext({ - state: initialState, - dispatch: () => null + state: initialState, + dispatch: () => null }); export const SettingsProvider = ({ children }) => { - const [state, dispatch] = React.useReducer(reducer, initialState); + const [state, dispatch] = React.useReducer(reducer, initialState); - return ( - - {children} - - ); -}; \ No newline at end of file + return {children}; +}; diff --git a/src/containers/ContextProvider/reducer.js b/src/containers/ContextProvider/reducer.js index 77f3e014..7fc15a16 100644 --- a/src/containers/ContextProvider/reducer.js +++ b/src/containers/ContextProvider/reducer.js @@ -1,31 +1,31 @@ import { headerDefinitions } from '../../util/data'; export const stateActions = Object.freeze({ - updatePatient: 'update_patient', - updateSetting: 'update_setting', // {type, settingId, value} + updatePatient: 'update_patient', + updateSetting: 'update_setting' // {type, settingId, value} }); // todo: add an enum that defines possible settings export const reducer = (state, action) => { - switch (action.type) { - case stateActions.updateSetting: - return { - ...state, - [action.settingId]: action.value - }; - case stateActions.updatePatient: - return { - ...state, - 'patient': action.value - }; - default: - return state; - } - }; + switch (action.type) { + case stateActions.updateSetting: + return { + ...state, + [action.settingId]: action.value + }; + case stateActions.updatePatient: + return { + ...state, + patient: action.value + }; + default: + return state; + } +}; const initialState = { - patient: null + patient: null }; -Object.keys(headerDefinitions).forEach((e) => { - initialState[e] = headerDefinitions[e].default; // fill default settings values +Object.keys(headerDefinitions).forEach(e => { + initialState[e] = headerDefinitions[e].default; // fill default settings values }); -export { initialState }; \ No newline at end of file +export { initialState }; From 4233b75ecc0d46ccca374f39815dd6755bfc0206 Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Tue, 20 Feb 2024 02:43:08 -0500 Subject: [PATCH 05/13] minor merge fixes --- src/containers/RequestBuilder.js | 42 +++----------------------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/src/containers/RequestBuilder.js b/src/containers/RequestBuilder.js index b392b26a..fee487c6 100644 --- a/src/containers/RequestBuilder.js +++ b/src/containers/RequestBuilder.js @@ -254,47 +254,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 }); - }} - > -
- -
-
-
@@ -373,7 +337,7 @@ export default class RequestBuilder extends Component { )} {!disableGetMedicationStatus && ( - + )} @@ -382,7 +346,7 @@ export default class RequestBuilder extends Component { From 5d341a2c62b74b09af277f1b22d6cd683bdd4cb9 Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Tue, 20 Feb 2024 03:08:49 -0500 Subject: [PATCH 06/13] fix settings related errors --- src/components/RequestDashboard/PatientSection.jsx | 4 +++- src/components/RequestDashboard/SettingsSection.jsx | 4 ++++ src/containers/ContextProvider/reducer.js | 12 ++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/components/RequestDashboard/PatientSection.jsx b/src/components/RequestDashboard/PatientSection.jsx index 5443462b..97ef1fa3 100644 --- a/src/components/RequestDashboard/PatientSection.jsx +++ b/src/components/RequestDashboard/PatientSection.jsx @@ -11,7 +11,9 @@ const PatientSection = props => { // we can get rid of this hacky shim return (
- + {state.startup ? + : + <>Loading...}
); }; diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx index f175ebaa..b599c597 100644 --- a/src/components/RequestDashboard/SettingsSection.jsx +++ b/src/components/RequestDashboard/SettingsSection.jsx @@ -31,6 +31,10 @@ const SettingsSection = props => { console.log('Could not load setting:' + element[0]); } } + // indicate to the rest of the app that the settings have been loaded + dispatch({ + type: stateActions.flagStartup + }); }); }, []); const updateSetting = (key, value) => { diff --git a/src/containers/ContextProvider/reducer.js b/src/containers/ContextProvider/reducer.js index 7fc15a16..1cde8e68 100644 --- a/src/containers/ContextProvider/reducer.js +++ b/src/containers/ContextProvider/reducer.js @@ -1,7 +1,9 @@ import { headerDefinitions } from '../../util/data'; export const stateActions = Object.freeze({ updatePatient: 'update_patient', - updateSetting: 'update_setting' // {type, settingId, value} + updateSetting: 'update_setting', // {type, settingId, value} + flagStartup: 'flag_startup' + }); // todo: add an enum that defines possible settings export const reducer = (state, action) => { @@ -16,13 +18,19 @@ export const reducer = (state, action) => { ...state, patient: action.value }; + case stateActions.flagStartup: + return { + ...state, + startup: true + }; default: return state; } }; const initialState = { - patient: null + patient: null, + startup: false }; Object.keys(headerDefinitions).forEach(e => { initialState[e] = headerDefinitions[e].default; // fill default settings values From 4afc32e27ce92f2efc4c5dbd1142a70b965f3bfb Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Tue, 20 Feb 2024 14:03:18 -0500 Subject: [PATCH 07/13] restore button in settings --- .../RequestDashboard/SettingsSection.jsx | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx index b599c597..c4f9bb4b 100644 --- a/src/components/RequestDashboard/SettingsSection.jsx +++ b/src/components/RequestDashboard/SettingsSection.jsx @@ -2,7 +2,9 @@ import React, { memo, useState, useEffect } from 'react'; import { Button, Box, FormControlLabel, Grid, Checkbox, TextField } from '@mui/material'; import useStyles from './styles'; -import { headerDefinitions } from '../../util/data'; +import FHIR from 'fhirclient'; + +import { headerDefinitions, types } from '../../util/data'; import { stateActions } from '../../containers/ContextProvider/reducer'; import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider'; @@ -60,7 +62,7 @@ const SettingsSection = props => { }; const clearQuestionnaireResponses = ({ defaultUser }) => - _event => { + () => { props.client .request('QuestionnaireResponse?author=' + defaultUser, { flat: true }) .then(result => { @@ -84,7 +86,7 @@ const SettingsSection = props => { const resetPims = ({ pimsUrl }) => - _event => { + () => { let url = new URL(pimsUrl); const resetUrl = url.origin + '/doctorOrders/api/deleteAll'; console.log('reset pims: ' + resetUrl); @@ -103,7 +105,7 @@ const SettingsSection = props => { }; const resetRemsAdmin = ({ cdsUrl }) => - _event => { + () => { let url = new URL(cdsUrl); const resetUrl = url.origin + '/etasu/reset'; @@ -119,6 +121,40 @@ const SettingsSection = props => { console.log(error); }); }; + 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 resetHeaderDefinitions = [ { display: 'Clear In-Progress Forms', @@ -134,6 +170,11 @@ const SettingsSection = props => { display: 'Reset REMS-Admin Database', key: 'resetRemsAdmin', reset: resetRemsAdmin + }, + { + display: 'Clear EHR MedicationDispenses', + key: 'clearMedicationDispenses', + reset: clearMedicationDispenses } ]; @@ -196,7 +237,7 @@ const SettingsSection = props => { })} {resetHeaderDefinitions.map(({ key, display, reset }) => { return ( - + From b8a33a2f1889afe398f62ebb9ba598c050b6166e Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Tue, 20 Feb 2024 14:03:32 -0500 Subject: [PATCH 08/13] prettier --- .../InProgressFormBox/InProgressFormBox.js | 56 ++--- .../PatientSearchBar/PatientSearchBar.js | 10 +- src/components/RequestBox/RequestBox.js | 12 +- .../RequestDashboard/PatientSection.jsx | 8 +- src/components/SMARTBox/PatientBox.js | 196 +++++++++++------- src/containers/ContextProvider/reducer.js | 9 +- src/containers/RequestBuilder.js | 29 +-- src/util/buildScript.2022071.js | 3 +- src/util/data.js | 6 +- src/util/fhir.js | 29 ++- 10 files changed, 209 insertions(+), 149 deletions(-) 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 8974331f..34aa1e43 100644 --- a/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js +++ b/src/components/RequestBox/PatientSearchBar/PatientSearchBar.js @@ -58,13 +58,9 @@ export default function PatientSearchBar(props) { Showing {getFilteredLength(input, listOfPatients)} of {props.searchablePatients.length}{' '} records

- props.getPatients()} - size="large" - > - - + props.getPatients()} size="large"> + + {displayFilteredPatientList(input, listOfPatients[0])} diff --git a/src/components/RequestBox/RequestBox.js b/src/components/RequestBox/RequestBox.js index 2c8577cf..36ecab22 100644 --- a/src/components/RequestBox/RequestBox.js +++ b/src/components/RequestBox/RequestBox.js @@ -383,13 +383,13 @@ export default class RequestBox extends Component {
- - {this.renderPatientInfo()} - - - {this.renderPrefetchedResources()} - + + {this.renderPatientInfo()} + + {this.renderPrefetchedResources()} + +
diff --git a/src/components/RequestDashboard/PatientSection.jsx b/src/components/RequestDashboard/PatientSection.jsx index 97ef1fa3..2ba78ee7 100644 --- a/src/components/RequestDashboard/PatientSection.jsx +++ b/src/components/RequestDashboard/PatientSection.jsx @@ -11,9 +11,11 @@ const PatientSection = props => { // we can get rid of this hacky shim return (
- {state.startup ? - : - <>Loading...} + {state.startup ? ( + + ) : ( + <>Loading... + )}
); }; diff --git a/src/components/SMARTBox/PatientBox.js b/src/components/SMARTBox/PatientBox.js index 22f54f5b..e7b189dd 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 recieve 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 }); }); } @@ -400,9 +397,9 @@ export default class PatientBox extends Component { /** * Launch In progress From - */ + */ - 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} @@ -580,17 +581,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 +607,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/containers/ContextProvider/reducer.js b/src/containers/ContextProvider/reducer.js index 1cde8e68..38f0ded1 100644 --- a/src/containers/ContextProvider/reducer.js +++ b/src/containers/ContextProvider/reducer.js @@ -3,7 +3,6 @@ export const stateActions = Object.freeze({ updatePatient: 'update_patient', updateSetting: 'update_setting', // {type, settingId, value} flagStartup: 'flag_startup' - }); // todo: add an enum that defines possible settings export const reducer = (state, action) => { @@ -19,10 +18,10 @@ export const reducer = (state, action) => { patient: action.value }; case stateActions.flagStartup: - return { - ...state, - startup: true - }; + return { + ...state, + startup: true + }; default: return state; } diff --git a/src/containers/RequestBuilder.js b/src/containers/RequestBuilder.js index fee487c6..c2f5b46b 100644 --- a/src/containers/RequestBuilder.js +++ b/src/containers/RequestBuilder.js @@ -97,7 +97,7 @@ export default class RequestBuilder extends Component { } updateStateElement = (elementName, text) => { - if(elementName === 'patient') { + if (elementName === 'patient') { this.props.dispatch({ type: stateActions.updatePatient, value: text @@ -205,19 +205,19 @@ export default class RequestBuilder extends Component { } getPatients = () => { - if(this.props.globalState.patientFhirQuery) { + 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 + .request(this.props.globalState.patientFhirQuery, { flat: true }) + .then(result => { + this.setState({ + patientList: result + }); + }) + .catch(e => { + this.setState({ + patientList: e + }); }); - }); } }; @@ -337,7 +337,10 @@ export default class RequestBuilder extends Component { )} {!disableGetMedicationStatus && ( - + )} 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 +}; From d3743c2bacb92f9db9c78adedb5855c8ae5dc9a6 Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Wed, 21 Feb 2024 16:24:34 -0500 Subject: [PATCH 09/13] fix bugs and reintroduce reconnect ehr button --- src/components/App.js | 13 +- .../RequestDashboard/SettingsSection.jsx | 193 +++++++++--------- src/components/SMARTBox/PatientBox.js | 3 + src/containers/ContextProvider/reducer.js | 3 +- src/containers/Index.jsx | 5 +- src/index.js | 8 +- 6 files changed, 127 insertions(+), 98 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index dd43cadf..1f9e44cd 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 { stateActions } 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: stateActions.updateSetting, + settingId: 'redirect', + value: redirect + }); + }, []); return ( diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx index c4f9bb4b..c2640422 100644 --- a/src/components/RequestDashboard/SettingsSection.jsx +++ b/src/components/RequestDashboard/SettingsSection.jsx @@ -2,6 +2,7 @@ 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'; @@ -122,7 +123,7 @@ const SettingsSection = props => { }); }; const clearMedicationDispenses = - ({ ehrUrl, access_token }, consoleLog) => + ({ ehrUrl, access_token }) => () => { console.log('Clear MedicationDispenses from the EHR: ' + ehrUrl); const client = FHIR.client({ @@ -138,10 +139,6 @@ const SettingsSection = props => { client .delete('MedicationDispense/' + resource.id) .then(result => { - consoleLog( - 'Successfully deleted MedicationDispense ' + resource.id + ' from EHR', - types.info - ); console.log(result); }) .catch(e => { @@ -155,17 +152,27 @@ const SettingsSection = props => { 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: 'Clear In-Progress Forms', - key: 'clearQuestionnaireResponses', - reset: clearQuestionnaireResponses - }, { display: 'Reset PIMS Database', key: 'resetPims', reset: resetPims }, + { + display: 'Clear In-Progress Forms', + key: 'clearQuestionnaireResponses', + reset: clearQuestionnaireResponses + }, { display: 'Reset REMS-Admin Database', key: 'resetRemsAdmin', @@ -175,96 +182,100 @@ const SettingsSection = props => { 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}

+ + + {headers.map(({ key, type, display }) => { + switch (type) { + case 'input': + return ( + +
+ { + updateSetting(key, event.target.value); + }} + sx={{ width: '100%' }} + />
- ); - } - })} - {resetHeaderDefinitions.map(({ key, display, reset }) => { - return ( - - - - ); - })} - {/* spacer */} -
- + + ); + 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 */} +
+ - - - - - - + + + + + -
-
+
+
); }; diff --git a/src/components/SMARTBox/PatientBox.js b/src/components/SMARTBox/PatientBox.js index e7b189dd..4a567107 100644 --- a/src/components/SMARTBox/PatientBox.js +++ b/src/components/SMARTBox/PatientBox.js @@ -540,6 +540,9 @@ export default class PatientBox extends Component { render() { const patient = this.props.patient; + if (!patient) { + return <>; + } let name = ''; let fullName = ''; let formatBirthdate = ''; diff --git a/src/containers/ContextProvider/reducer.js b/src/containers/ContextProvider/reducer.js index 38f0ded1..a475f6c1 100644 --- a/src/containers/ContextProvider/reducer.js +++ b/src/containers/ContextProvider/reducer.js @@ -29,7 +29,8 @@ export const reducer = (state, action) => { const initialState = { patient: null, - startup: false + startup: false, + redirect: '' }; Object.keys(headerDefinitions).forEach(e => { initialState[e] = headerDefinitions[e].default; // fill default settings values diff --git a/src/containers/Index.jsx b/src/containers/Index.jsx index f69d8245..0bf71379 100644 --- a/src/containers/Index.jsx +++ b/src/containers/Index.jsx @@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react'; import FHIR from 'fhirclient'; import env from 'env-var'; import Home from '../components/RequestDashboard/Home'; -import { SettingsProvider } from './ContextProvider/SettingsProvider'; const Index = props => { const [client, setClient] = useState(null); @@ -16,9 +15,7 @@ const Index = props => { return (
{client ? ( - - - + ) : (

Getting Client...

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') +); From fe17131599acdf43dc27c255679c8bdc52ac398c Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Thu, 22 Feb 2024 15:23:38 -0500 Subject: [PATCH 10/13] code cleanup and spelling --- src/components/RequestDashboard/Home.jsx | 2 +- .../RequestDashboard/PatientSection.jsx | 3 +- src/components/RequestDashboard/styles.jsx | 9 +-- src/components/SMARTBox/PatientBox.js | 2 +- src/containers/RequestBuilder.js | 56 ++++--------------- 5 files changed, 14 insertions(+), 58 deletions(-) diff --git a/src/components/RequestDashboard/Home.jsx b/src/components/RequestDashboard/Home.jsx index 43206752..a1b0a231 100644 --- a/src/components/RequestDashboard/Home.jsx +++ b/src/components/RequestDashboard/Home.jsx @@ -71,7 +71,7 @@ const Home = props => { ) : ( - )}{' '} + )} {/* spacer */} ); diff --git a/src/components/RequestDashboard/PatientSection.jsx b/src/components/RequestDashboard/PatientSection.jsx index 2ba78ee7..a871bb69 100644 --- a/src/components/RequestDashboard/PatientSection.jsx +++ b/src/components/RequestDashboard/PatientSection.jsx @@ -7,8 +7,7 @@ import { SettingsContext } from '../../containers/ContextProvider/SettingsProvid const PatientSection = props => { const classes = useStyles(); const [state, dispatch] = React.useContext(SettingsContext); - // TODO: Make request builder use react-hooks so - // we can get rid of this hacky shim + // TODO: Make request builder use react-hooks return (
{state.startup ? ( diff --git a/src/components/RequestDashboard/styles.jsx b/src/components/RequestDashboard/styles.jsx index a4bc9998..cb3da16b 100644 --- a/src/components/RequestDashboard/styles.jsx +++ b/src/components/RequestDashboard/styles.jsx @@ -10,11 +10,6 @@ export default makeStyles( flexGrow: 1, backgroundColor: '#005B94' }, - mainButton: { - '&.MuiButtonBase-root': { - // transition: 'all 250ms ease-in 0ms', - } - }, mainButtonView: { '&.MuiButtonBase-root': { width: '600px', @@ -43,7 +38,6 @@ export default makeStyles( mainSectionView: { width: 'auto', height: 'auto', - // margin: '0 15px 0 15px', // must be same as tabDivView borderLeft: '1px solid black', borderRight: '1px solid black' }, @@ -70,7 +64,7 @@ export default makeStyles( '& > *': { // generic child selector '&.MuiButton-iconSizeMedium': { - // specificty + // specificity marginRight: 0 } } @@ -105,7 +99,6 @@ export default makeStyles( }, tabDivView: { '&.MuiGrid-root': { - // padding: '0 15px 0 15px', marginTop: '0vh', alignItems: 'flex-start', justifyContent: 'flex-start' diff --git a/src/components/SMARTBox/PatientBox.js b/src/components/SMARTBox/PatientBox.js index 4a567107..9471e30c 100644 --- a/src/components/SMARTBox/PatientBox.js +++ b/src/components/SMARTBox/PatientBox.js @@ -396,7 +396,7 @@ export default class PatientBox extends Component { } /** - * Launch In progress From + * Launch In progress Form */ relaunch = data => { diff --git a/src/containers/RequestBuilder.js b/src/containers/RequestBuilder.js index c2f5b46b..5014e1a0 100644 --- a/src/containers/RequestBuilder.js +++ b/src/containers/RequestBuilder.js @@ -46,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: stateActions.updateSetting, + settingId: 'baseUrl', + value: this.state.client.state.serverUrl + }); + this.props.dispatch({ + type: stateActions.updateSetting, + settingId: 'ehrUrl', + value: this.state.client.state.serverUrl + }); } } - reconnectEhr() { - FHIR.oauth2.authorize({ - clientId: env.get('REACT_APP_CLIENT').asString(), - iss: this.props.globalState.baseUrl, - redirectUri: this.props.redirect, - scope: env.get('REACT_APP_CLIENT_SCOPES').asString() - }); - } - consoleLog(content, type) { console.log(content); let jsonContent = { @@ -359,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' - } -}; From a3ffa6bf6850dd959bd13c26d0dbf57d81f59cfc Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Thu, 22 Feb 2024 15:33:41 -0500 Subject: [PATCH 11/13] spelling --- src/components/SMARTBox/PatientBox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SMARTBox/PatientBox.js b/src/components/SMARTBox/PatientBox.js index 9471e30c..11a71519 100644 --- a/src/components/SMARTBox/PatientBox.js +++ b/src/components/SMARTBox/PatientBox.js @@ -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) { From 95d81314795c8b80d7602186ec068db0de149e5f Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Thu, 22 Feb 2024 15:53:36 -0500 Subject: [PATCH 12/13] update reducer --- src/components/App.js | 4 ++-- src/components/RequestDashboard/SettingsSection.jsx | 6 +++--- src/containers/ContextProvider/reducer.js | 12 ++++++------ src/containers/RequestBuilder.js | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 1f9e44cd..f22c2a08 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -8,7 +8,7 @@ import PatientPortal from '../containers/PatientPortal'; import RegisterPage from '../containers/register/RegisterPage'; import theme from '../containers/styles/theme'; import { SettingsContext } from '../containers/ContextProvider/SettingsProvider'; -import { stateActions } from '../containers/ContextProvider/reducer'; +import { actionTypes } from '../containers/ContextProvider/reducer'; const isGhPages = process.env.REACT_APP_GH_PAGES === 'true'; const Router = isGhPages ? HashRouter : BrowserRouter; @@ -17,7 +17,7 @@ const App = () => { const [state, dispatch] = React.useContext(SettingsContext); useEffect(() => { dispatch({ - type: stateActions.updateSetting, + type: actionTypes.updateSetting, settingId: 'redirect', value: redirect }); diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx index c2640422..ec5a97fd 100644 --- a/src/components/RequestDashboard/SettingsSection.jsx +++ b/src/components/RequestDashboard/SettingsSection.jsx @@ -6,7 +6,7 @@ import env from 'env-var'; import FHIR from 'fhirclient'; import { headerDefinitions, types } from '../../util/data'; -import { stateActions } from '../../containers/ContextProvider/reducer'; +import { actionTypes } from '../../containers/ContextProvider/reducer'; import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider'; const SettingsSection = props => { @@ -36,13 +36,13 @@ const SettingsSection = props => { } // indicate to the rest of the app that the settings have been loaded dispatch({ - type: stateActions.flagStartup + type: actionTypes.flagStartup }); }); }, []); const updateSetting = (key, value) => { dispatch({ - type: stateActions.updateSetting, + type: actionTypes.updateSetting, settingId: key, value: value }); diff --git a/src/containers/ContextProvider/reducer.js b/src/containers/ContextProvider/reducer.js index a475f6c1..5c856693 100644 --- a/src/containers/ContextProvider/reducer.js +++ b/src/containers/ContextProvider/reducer.js @@ -1,23 +1,23 @@ import { headerDefinitions } from '../../util/data'; -export const stateActions = Object.freeze({ - updatePatient: 'update_patient', +export const actionTypes = Object.freeze({ + updatePatient: 'update_patient', // {type, value} updateSetting: 'update_setting', // {type, settingId, value} - flagStartup: 'flag_startup' + flagStartup: 'flag_startup' // {type} }); // todo: add an enum that defines possible settings export const reducer = (state, action) => { switch (action.type) { - case stateActions.updateSetting: + case actionTypes.updateSetting: return { ...state, [action.settingId]: action.value }; - case stateActions.updatePatient: + case actionTypes.updatePatient: return { ...state, patient: action.value }; - case stateActions.flagStartup: + case actionTypes.flagStartup: return { ...state, startup: true diff --git a/src/containers/RequestBuilder.js b/src/containers/RequestBuilder.js index 5014e1a0..7d4c3d5c 100644 --- a/src/containers/RequestBuilder.js +++ b/src/containers/RequestBuilder.js @@ -19,7 +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 { stateActions } from './ContextProvider/reducer.js'; +import { actionTypes } from './ContextProvider/reducer.js'; export default class RequestBuilder extends Component { constructor(props) { @@ -56,12 +56,12 @@ export default class RequestBuilder extends Component { // Call patients on load of page this.getPatients(); this.props.dispatch({ - type: stateActions.updateSetting, + type: actionTypes.updateSetting, settingId: 'baseUrl', value: this.state.client.state.serverUrl }); this.props.dispatch({ - type: stateActions.updateSetting, + type: actionTypes.updateSetting, settingId: 'ehrUrl', value: this.state.client.state.serverUrl }); @@ -82,7 +82,7 @@ export default class RequestBuilder extends Component { updateStateElement = (elementName, text) => { if (elementName === 'patient') { this.props.dispatch({ - type: stateActions.updatePatient, + type: actionTypes.updatePatient, value: text }); } else { From 772615f4ec117464c7649761c2579aae55bddc99 Mon Sep 17 00:00:00 2001 From: KeeyanGhoreshi Date: Thu, 22 Feb 2024 16:45:47 -0500 Subject: [PATCH 13/13] increase task card clarity --- src/components/RequestDashboard/styles.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/RequestDashboard/styles.jsx b/src/components/RequestDashboard/styles.jsx index cb3da16b..00c52a63 100644 --- a/src/components/RequestDashboard/styles.jsx +++ b/src/components/RequestDashboard/styles.jsx @@ -116,7 +116,7 @@ export default makeStyles( }, taskTabMain: { border: '0px solid black', - boxShadow: '2px 2px', + boxShadow: '-2px -2px 3px 1px', borderRadius: '5px', padding: '8px', background: 'linear-gradient(to right bottom, #F5F5F7, #eaeaef)', @@ -125,7 +125,7 @@ export default makeStyles( } }, taskTabHeader: { - fontSize: '8px', + fontSize: '9px', color: '#777', borderBottom: '1px solid #e3e3ef' },