diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx
index e047db7e..5000d4b2 100644
--- a/src/components/Auth/Login.jsx
+++ b/src/components/Auth/Login.jsx
@@ -11,6 +11,7 @@ const Login = props => {
const [username, _setUsername] = useState('');
const [password, _setPassword] = useState('');
const handleClose = () => setMessage(null);
+ document.title = 'EHR | Patient Portal';
const onSubmit = useCallback(() => {
if (username && password) {
diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx
index c5304a3a..95aae834 100644
--- a/src/components/Dashboard/Dashboard.jsx
+++ b/src/components/Dashboard/Dashboard.jsx
@@ -1,8 +1,5 @@
import React, { memo, useState, useEffect } from 'react';
import useStyles from './styles';
-import DashboardElement from './DashboardElement';
-import FormControlLabel from '@mui/material/FormControlLabel';
-import Checkbox from '@mui/material/Checkbox';
import Box from '@mui/material/Box';
import Drawer from '@mui/material/Drawer';
import CssBaseline from '@mui/material/CssBaseline';
@@ -10,6 +7,7 @@ import Toolbar from '@mui/material/Toolbar';
import List from '@mui/material/List';
import Divider from '@mui/material/Divider';
import AssignmentIcon from '@mui/icons-material/Assignment';
+import ListAltIcon from '@mui/icons-material/ListAlt';
import MedicationIcon from '@mui/icons-material/Medication';
import BiotechIcon from '@mui/icons-material/Biotech';
import LogoutIcon from '@mui/icons-material/Logout';
@@ -21,34 +19,28 @@ import NotificationsIcon from '@mui/icons-material/Notifications';
import AlarmIcon from '@mui/icons-material/Alarm';
import SettingsIcon from '@mui/icons-material/Settings';
import MedicalInformationIcon from '@mui/icons-material/MedicalInformation';
-import { Paper } from '@mui/material';
+import FormsSection from './ListSelections/FormsSection';
+import EmptySection from './ListSelections/EmptySection';
+import PatientTaskSection from './ListSelections/PatientTaskSection';
const Dashboard = props => {
const classes = useStyles();
- const [resources, setResources] = useState([]);
- const [message, setMessage] = useState('Loading...');
- const [checked, setChecked] = useState(true);
- const drawerWidth = '340px';
- const handleChange = event => {
- setChecked(event.target.checked);
- };
+ const [selectedIndex, setSelectedIndex] = useState(3);
- const addResources = bundle => {
- if (bundle.entry) {
- bundle.entry.forEach(e => {
- const resource = e.resource;
- setResources(resources => [...resources, resource]);
- });
- }
+ const handleListItemClick = (event, index) => {
+ setSelectedIndex(index);
};
+
+ const drawerWidth = '340px';
+
const createIcons = () => {
const icons = [];
const style = { fontSize: '40px' };
const itemStyle = { height: '80px' };
- const qStyle = { height: '80px', backgroundColor: '#f5f5fa' };
icons.push(['Notifications', , itemStyle]);
icons.push(['Appointments', , itemStyle]);
- icons.push(['Questionnaire Forms', , qStyle]);
+ icons.push(['Tasks', , itemStyle]);
+ icons.push(['Questionnaire Forms', , itemStyle]);
icons.push(['Health Data', , itemStyle]);
icons.push(['Medications', , itemStyle]);
icons.push(['Tests and Results', , itemStyle]);
@@ -57,32 +49,25 @@ const Dashboard = props => {
return icons;
};
+
useEffect(() => {
- if (props.client.patient.id) {
- props.client.patient
- .request('QuestionnaireResponse', { pageLimit: 0, onPage: addResources })
- .then(() => {
- setMessage(
- 'No QuestionnaireResponses Found for user with patientId: ' + props.client.patient.id
- );
- });
- } else {
- setMessage('Invalid patient: No patientId provided');
+ if (selectedIndex === 8) {
+ // logout - set client to null to display login page
+ props.logout();
}
- }, [props.client.patient]);
+ }, [selectedIndex]);
- const renderElements = () => {
- let resourcesToRender = [];
- if (checked) {
- resourcesToRender = resources.filter(e => {
- return e.status === 'in-progress';
- });
- } else {
- resourcesToRender = resources;
+ const renderBody = () => {
+ switch (selectedIndex) {
+ case 2:
+ return ;
+ case 3:
+ return ;
+ default:
+ return ;
}
- resourcesToRender.reverse();
- return resourcesToRender;
};
+
return (
@@ -102,8 +87,13 @@ const Dashboard = props => {
{createIcons().map((option, index) => (
-
-
+
+ handleListItemClick(event, index)}>
{option[1]}
{
-
-
Available Forms
-
}
- label="Only show in-progress forms"
- />
- {resources.length > 0 ? (
- renderElements().map(e => {
- return (
-
- );
- })
- ) : (
-
{message}
- )}
-
+ {renderBody()}
diff --git a/src/components/Dashboard/ListSelections/EmptySection.jsx b/src/components/Dashboard/ListSelections/EmptySection.jsx
new file mode 100644
index 00000000..b9dd66ca
--- /dev/null
+++ b/src/components/Dashboard/ListSelections/EmptySection.jsx
@@ -0,0 +1,13 @@
+import React, { memo } from 'react';
+import useStyles from '../styles';
+
+const EmptySection = () => {
+ const classes = useStyles();
+ return (
+
+
Not available
+
+ );
+};
+
+export default memo(EmptySection);
diff --git a/src/components/Dashboard/ListSelections/FormsSection.jsx b/src/components/Dashboard/ListSelections/FormsSection.jsx
new file mode 100644
index 00000000..34491555
--- /dev/null
+++ b/src/components/Dashboard/ListSelections/FormsSection.jsx
@@ -0,0 +1,75 @@
+import React, { memo, useState, useEffect } from 'react';
+import { Paper } from '@mui/material';
+import DashboardElement from '../DashboardElement';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import Checkbox from '@mui/material/Checkbox';
+import useStyles from '../styles';
+
+const FormsSection = props => {
+ const classes = useStyles();
+ const [resources, setResources] = useState([]);
+ const [message, setMessage] = useState('Loading...');
+ const [checked, setChecked] = useState(true);
+
+ useEffect(() => {
+ if (props.client.patient.id) {
+ props.client.patient
+ .request('QuestionnaireResponse', { pageLimit: 0, onPage: addResources })
+ .then(() => {
+ setMessage(
+ 'No QuestionnaireResponses Found for user with patientId: ' + props.client.patient.id
+ );
+ });
+ } else {
+ setMessage('Invalid patient: No patientId provided');
+ }
+ }, [props.client.patient]);
+
+ const handleChange = event => {
+ setChecked(event.target.checked);
+ };
+
+ const addResources = bundle => {
+ if (bundle.entry) {
+ bundle.entry.forEach(e => {
+ const resource = e.resource;
+ setResources(resources => [...resources, resource]);
+ });
+ }
+ };
+
+ const renderElements = () => {
+ let resourcesToRender = [];
+ if (checked) {
+ resourcesToRender = resources.filter(e => {
+ return e.status === 'in-progress';
+ });
+ } else {
+ resourcesToRender = resources;
+ }
+ resourcesToRender.reverse();
+ return resourcesToRender;
+ };
+
+ return (
+
+
Available Forms
+
}
+ label="Only show in-progress forms"
+ />
+ {resources.length > 0 ? (
+ renderElements().map(e => {
+ return (
+
+ );
+ })
+ ) : (
+
{message}
+ )}
+
+ );
+};
+
+export default memo(FormsSection);
diff --git a/src/components/Dashboard/ListSelections/PatientTaskSection.jsx b/src/components/Dashboard/ListSelections/PatientTaskSection.jsx
new file mode 100644
index 00000000..6900f73f
--- /dev/null
+++ b/src/components/Dashboard/ListSelections/PatientTaskSection.jsx
@@ -0,0 +1,205 @@
+import React, { memo, useState, useEffect, Fragment } from 'react';
+import useStyles from '../styles';
+import { Button, Grid, Stack, Modal, Box } from '@mui/material';
+import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
+import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
+import AssignmentIndIcon from '@mui/icons-material/AssignmentInd';
+import { Info, Refresh } from '@mui/icons-material';
+import PersonIcon from '@mui/icons-material/Person';
+import PersonAddIcon from '@mui/icons-material/PersonAdd';
+import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
+import DeleteIcon from '@mui/icons-material/Delete';
+
+const PatientTaskSection = props => {
+ const classes = useStyles();
+ const [tasks, setTasks] = useState([]);
+ const [taskToDelete, setTaskToDelete] = useState('');
+ const [open, setOpen] = useState(false);
+ const currUser = props.client.patient.id;
+
+ const tryDelete = task => {
+ setTaskToDelete(task);
+ setOpen(true);
+ };
+ const handleClose = () => {
+ setOpen(false);
+ };
+ const fetchTasks = () => {
+ let identifier = 'Task';
+ if (props.client.patient && props.client.patient.id) {
+ identifier = `Task?patient=${props.client.patient.id}`;
+ }
+ props.client.request(identifier, { resolveReferences: ['for', 'owner'] }).then(request => {
+ console.log(request);
+ if (request && request.entry) {
+ const allTasks = request.entry.map(e => e.resource);
+ const myTasks = allTasks.filter(t => t.owner?.id === currUser);
+ setTasks(myTasks);
+ } else {
+ setTasks([]);
+ }
+ });
+ };
+
+ useEffect(() => {
+ fetchTasks();
+ }, []);
+
+ const deleteTask = () => {
+ if (taskToDelete) {
+ props.client.delete(`${taskToDelete.resourceType}/${taskToDelete.id}`).then(e => {
+ console.log('Deleted Task');
+ fetchTasks();
+ });
+ setOpen(false);
+ setTaskToDelete('');
+ }
+ };
+
+ 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() === 'patient') {
+ const patient = task.owner;
+ if (patient.name) {
+ taskOwnerName = `${patient.name[0].given[0]} ${patient.name[0].family}`;
+ } else {
+ taskOwnerName = task.owner.id;
+ }
+ }
+ let ownerText = (
+
+
+ Unassigned
+
+ );
+ if (task.owner) {
+ ownerText = (
+
+
+ {`Assigned to ${taskOwnerName}`}
+
+ );
+ }
+ return (
+
+
+
+
+ {`Task ID: ${task.id}`}
+
+
+ {`Timestamp: ${task.authoredOn}`}
+
+
+ {`Beneficiary: ${task.for ? task.for.id : 'None'}`}
+
+
+ {`STATUS: ${task.status.toUpperCase()}`}
+
+
+ {task.description}
+
+
+
+ {taskForName}
+
+
+
+ {ownerText}
+
+
+ }>
+ View Resource
+
+
+
+ {/*spacer*/}
+
+
+ }
+ onClick={() => {
+ tryDelete(task);
+ }}
+ >
+ Delete
+
+
+
+ }>
+ Process Task
+
+
+
+
+
+ );
+ };
+
+ return (
+
+
Tasks
+ {renderTasks(tasks)}
+
+
+
+
+ {taskToDelete ? `Are you sure you want to delete Task ${taskToDelete.id}` : ''}
+
+
+ {/*spacer*/}
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default memo(PatientTaskSection);
diff --git a/src/components/Dashboard/styles.jsx b/src/components/Dashboard/styles.jsx
index e2a2437f..14d02edf 100644
--- a/src/components/Dashboard/styles.jsx
+++ b/src/components/Dashboard/styles.jsx
@@ -47,7 +47,64 @@ export default makeStyles(
borderRadius: '12px',
float: 'right'
},
- spacer: {}
+ spacer: {},
+ 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: 'Dashboard', index: 1 }
diff --git a/src/components/RequestDashboard/TasksSection.jsx b/src/components/RequestDashboard/TasksSection.jsx
index 431a09ec..e8c424d5 100644
--- a/src/components/RequestDashboard/TasksSection.jsx
+++ b/src/components/RequestDashboard/TasksSection.jsx
@@ -1,5 +1,16 @@
import React, { memo, useState, useEffect, Fragment } from 'react';
-import { Button, Box, Modal, Grid, Tabs, Tab, Stack } from '@mui/material';
+import {
+ Button,
+ Box,
+ Modal,
+ Grid,
+ Tabs,
+ Tab,
+ Stack,
+ Select,
+ FormControl,
+ InputLabel
+} from '@mui/material';
import AssignmentIcon from '@mui/icons-material/Assignment';
import PersonIcon from '@mui/icons-material/Person';
import DeleteIcon from '@mui/icons-material/Delete';
@@ -13,6 +24,7 @@ import useStyles from './styles';
import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider';
import { MemoizedTabPanel } from './TabPanel';
import { Info, Refresh } from '@mui/icons-material';
+import MenuItem from '@mui/material/MenuItem';
const TasksSection = props => {
const classes = useStyles();
@@ -21,6 +33,15 @@ const TasksSection = props => {
const [value, setValue] = useState(0);
const [open, setOpen] = useState(false);
const [taskToDelete, setTaskToDelete] = useState('');
+
+ const handleChangeAssign = (event, task) => {
+ if (event.target.value === 'me') {
+ assignTaskToMe(task);
+ } else {
+ assignTaskToPatient(task);
+ }
+ };
+
const handleChange = (event, newValue) => {
setValue(newValue);
};
@@ -32,8 +53,7 @@ const TasksSection = props => {
setTaskToDelete(task);
setOpen(true);
};
- const assignTask = task => {
- // TODO: should allow assigning to anybody, not just self
+ const assignTaskToMe = task => {
if (task) {
let user = props.client.user.id;
if (!user) {
@@ -53,6 +73,24 @@ const TasksSection = props => {
});
}
};
+ const assignTaskToPatient = task => {
+ if (task) {
+ let patientId = task.for.id;
+ let user = `Patient/${patientId}`;
+ 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 => {
@@ -103,13 +141,13 @@ const TasksSection = props => {
let taskForName = 'N/A';
let taskOwnerName = 'N/A';
- if (task.for.resourceType.toLowerCase() === 'patient') {
+ 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') {
+ 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}`;
@@ -117,6 +155,14 @@ const TasksSection = props => {
taskOwnerName = task.owner.id;
}
}
+ if (task.owner && task.owner?.resourceType?.toLowerCase() === 'patient') {
+ const patient = task.owner;
+ if (patient.name) {
+ taskOwnerName = `${patient.name[0].given[0]} ${patient.name[0].family}`;
+ } else {
+ taskOwnerName = task.owner.id;
+ }
+ }
let ownerText = (
@@ -164,15 +210,22 @@ const TasksSection = props => {
- }
- onClick={() => {
- assignTask(task);
- }}
+
- Assign
-
+ Assign
+
+
{/*spacer*/}
diff --git a/src/containers/PatientPortal.jsx b/src/containers/PatientPortal.jsx
index d8bf2bc1..fe61fc0e 100644
--- a/src/containers/PatientPortal.jsx
+++ b/src/containers/PatientPortal.jsx
@@ -30,9 +30,15 @@ const PatientPortal = () => {
setPatientName(getName(patient));
});
setClient(client);
+ document.title = 'EHR | Patient Portal';
}
}, [token]);
+ const logout = () => {
+ setClient(null);
+ setPatientName(null);
+ };
+
const getName = patient => {
const name = [];
if (patient.name) {
@@ -60,7 +66,7 @@ const PatientPortal = () => {
{token && client ? (
-
+
) : (
)}