Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/components/MedicationStatus/MedicationStatus.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import axios from 'axios';
import { MedicationStatusButton } from './MedicationStatusButton.jsx';
import { MedicationStatusModal } from './MedicationStatusModal.jsx';
import { useState, useEffect } from 'react';
import { Card } from '@mui/material';

export const MedicationStatus = props => {
const { ehrUrl, request } = props;
const [medicationDispense, setMedicationDispense] = useState(null);
const [showMedicationStatus, setShowMedicationStatus] = useState(false);
const [lastCheckedMedicationTime, setLastCheckedMedicationTime] = useState(null);

useEffect(() => getMedicationStatus(), [request.id]);

const getMedicationStatus = () => {
setLastCheckedMedicationTime(Date.now());

axios.get(`${ehrUrl}/MedicationDispense?prescription=${request.id}`).then(
response => {
const bundle = response.data;
setMedicationDispense(bundle.entry?.[0].resource);
},
error => {
console.log('Was not able to get medication status', error);
}
);
};

const handleCloseMedicationStatus = () => {
setShowMedicationStatus(false);
};

const handleOpenMedicationStatus = () => {
setShowMedicationStatus(true);
};

return (
<Card variant="outlined" sx={{ padding: 2 }}>
<MedicationStatusButton
baseColor={getStatusColor(medicationDispense?.status)}
medicationDispense={medicationDispense}
handleOpenMedicationStatus={handleOpenMedicationStatus}
lastCheckedMedicationTime={lastCheckedMedicationTime}
/>
<MedicationStatusModal
callback={getMedicationStatus}
medicationDispense={medicationDispense}
update={showMedicationStatus}
onClose={handleCloseMedicationStatus}
/>
</Card>
);
};

export const getStatusColor = status => {
switch (status) {
case 'completed':
return 'green';
case 'preparation':
case 'in-progress':
case 'cancelled':
case 'on-hold':
case 'entered-in-error':
case 'stopped':
case 'declined':
case 'unknown':
default:
return '#0c0c0c';
}
};

export const getStatusText = status => {
switch (status) {
case 'completed':
return 'Picked Up';
case 'unknown':
return 'Not Started';
case 'preparation':
case 'in-progress':
case 'cancelled':
case 'on-hold':
case 'entered-in-error':
case 'stopped':
case 'declined':
default:
return 'N/A';
}
};
16 changes: 16 additions & 0 deletions src/components/MedicationStatus/MedicationStatusButton.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.etasuButtonText {
margin-bottom: 0px;
font-weight: bold;
font-size: 14px;
}

.etasuButtonTimestamp {
margin: 0 auto;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 0.8rem;
}

.timestampString {
font-size: 0.7rem;
opacity: 0.8;
}
65 changes: 65 additions & 0 deletions src/components/MedicationStatus/MedicationStatusButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Button, Grid, Typography } from '@mui/material';
import LocalPharmacyIcon from '@mui/icons-material/LocalPharmacy';
import './MedicationStatusButton.css';
import { getStatusText } from './MedicationStatus';

export const MedicationStatusButton = props => {
const { baseColor, medicationDispense, handleOpenMedicationStatus, lastCheckedMedicationTime } =
props;
return (
<Grid item container flexDirection="column" alignItems="center">
<Button sx={buttonSx(baseColor)} variant="contained" onClick={handleOpenMedicationStatus}>
<LocalPharmacyIcon fontSize="large" />
<Typography className="etasuButtonText" component="p">
Medication:
</Typography>
<Typography component="p">{getStatusText(medicationDispense?.status)}</Typography>
</Button>
{renderTimestamp(lastCheckedMedicationTime)}
</Grid>
);
};

const buttonSx = baseColor => ({
backgroundColor: baseColor,
':hover': { filter: 'brightness(110%)', backgroundColor: baseColor },
flexDirection: 'column'
});

const renderTimestamp = checkedTime => {
return checkedTime ? (
<>
<Typography component="p" className="etasuButtonTimestamp">
{convertTimeDifference(checkedTime)}
</Typography>
<Typography component="p" className="etasuButtonTimestamp timestampString">
{new Date(checkedTime).toLocaleString()}
</Typography>
</>
) : (
<Typography>No medication selected</Typography>
);
};

const convertTimeDifference = start => {
const end = Date.now();
const difference = end - start;
const diffMin = difference / 60000;
let prefix = 'a long time';
if (diffMin < 1) {
prefix = 'a few seconds';
} else if (diffMin > 1 && diffMin < 2) {
prefix = 'a minute';
} else if (diffMin > 2 && diffMin < 60) {
prefix = `${Math.round(diffMin)} minutes`;
} else if (diffMin > 60 && diffMin < 120) {
prefix = 'an hour';
} else if (diffMin > 120 && diffMin < 1440) {
prefix = `${Math.round(diffMin / 60)} hours`;
} else if (diffMin > 1440 && diffMin < 2880) {
prefix = 'a day';
} else if (diffMin > 2880) {
prefix = `${Math.round(diffMin / 1440)} days`;
}
return `Last checked ${prefix} ago`;
};
33 changes: 33 additions & 0 deletions src/components/MedicationStatus/MedicationStatusModal.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.renew-icon{
cursor: pointer;
margin-left: 15px;
margin-right: 15px;
}

.refresh{
cursor: pointer;
margin-left: 15px;
margin-right: 15px;
animation-name: spin;
animation-duration: 500ms;
animation-iteration-count: 2;
animation-timing-function: ease-in-out;
}

.status-icon{
width: 100%;
height:5px;
}

.bundle-entry{
margin: 5px;
}

@keyframes spin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(360deg);
}
}
62 changes: 62 additions & 0 deletions src/components/MedicationStatus/MedicationStatusModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Box, Grid, IconButton, Modal, Tooltip } from '@mui/material';
import AutorenewIcon from '@mui/icons-material/Autorenew';
import { useState, useEffect } from 'react';
import { getStatusColor, getStatusText } from './MedicationStatus';
import './MedicationStatusModal.css';

const getIdText = medicationDispense => medicationDispense?.id || 'N/A';

export const MedicationStatusModal = props => {
const { callback, onClose, medicationDispense, update } = props;
const [spin, setSpin] = useState(false);
const color = getStatusColor(medicationDispense?.status);
const status = getStatusText(medicationDispense?.status);

useEffect(() => {
if (update) {
setSpin(true);
callback();
}
}, [update]);

return (
<Modal open={update} onClose={onClose}>
<Box sx={modalStyle}>
<div>
<h1>Medication Status</h1>
<div className="status-icon" style={{ backgroundColor: color }}></div>
<Grid container>
<Grid item xs={10}>
<div className="bundle-entry">ID: {getIdText(medicationDispense)}</div>
<div className="bundle-entry">Status: {status}</div>
</Grid>
<Grid item xs={2}>
<div className="bundle-entry">
<Tooltip title="Refresh">
<IconButton onClick={callback}>
<AutorenewIcon
className={spin ? 'refresh' : 'renew-icon'}
onAnimationEnd={() => setSpin(false)}
/>
</IconButton>
</Tooltip>
</div>
</Grid>
</Grid>
</div>
</Box>
</Modal>
);
};

const modalStyle = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
border: '1px solid #000',
boxShadow: 24,
p: 4
};
Loading