Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

Commit

Permalink
feat(labs): adds ability to request and view labs
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmeyer committed Apr 3, 2020
1 parent a9c4100 commit efba834
Show file tree
Hide file tree
Showing 14 changed files with 573 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/HospitalRun.tsx
Expand Up @@ -100,7 +100,7 @@ const HospitalRun = () => {
path="/appointments/:id"
component={ViewAppointment}
/>
<PrivateRoute isAuthenticated exact path="/labs" component={Labs} />
<PrivateRoute isAuthenticated path="/labs" component={Labs} />
</Switch>
</div>
<Toaster autoClose={5000} hideProgressBar draggable />
Expand Down
11 changes: 11 additions & 0 deletions src/clients/db/LabRepository.ts
@@ -0,0 +1,11 @@
import Lab from 'model/Lab'
import Repository from './Repository'
import { labs } from '../../config/pouchdb'

export class LabRepository extends Repository<Lab> {
constructor() {
super(labs)
}
}

export default new LabRepository()
21 changes: 21 additions & 0 deletions src/components/Navbar.tsx
Expand Up @@ -70,6 +70,27 @@ const Navbar = () => {
},
],
},
{
type: 'link-list',
label: t('labs.label'),
className: 'labs-link-list',
children: [
{
type: 'link',
label: t('labs.label'),
onClick: () => {
history.push('/labs')
},
},
{
type: 'link',
label: t('labs.requests.new'),
onClick: () => {
history.push('/labs/new')
},
},
],
},
{
type: 'search',
placeholderText: t('actions.search'),
Expand Down
1 change: 1 addition & 0 deletions src/config/pouchdb.ts
Expand Up @@ -28,3 +28,4 @@ function createDb(name: string) {

export const patients = createDb('patients')
export const appointments = createDb('appointments')
export const labs = createDb('labs')
46 changes: 40 additions & 6 deletions src/labs/Labs.tsx
@@ -1,12 +1,46 @@
import React from 'react'
import PrivateRoute from 'components/PrivateRoute'
import { Switch } from 'react-router'
import LabRequests from './requests/LabRequests'
import useAddBreadcrumbs from 'breadcrumbs/useAddBreadcrumbs'
import { useSelector } from 'react-redux'
import Permissions from 'model/Permissions'
import LabRequests from './ViewLabs'
import NewLabRequest from './requests/NewLabRequest'
import ViewLab from './ViewLab'
import { RootState } from '../store'

const Labs = () => (
<Switch>
<PrivateRoute isAuthenticated exact path="/labs" component={LabRequests} />
</Switch>
)
const Labs = () => {
const { permissions } = useSelector((state: RootState) => state.user)
const breadcrumbs = [
{
i18nKey: 'labs.label',
location: `/labs`,
},
]
useAddBreadcrumbs(breadcrumbs, true)

return (
<Switch>
<PrivateRoute
isAuthenticated={permissions.includes(Permissions.ViewLabs)}
exact
path="/labs"
component={LabRequests}
/>
<PrivateRoute
isAuthenticated={permissions.includes(Permissions.RequestLab)}
exact
path="/labs/new"
component={NewLabRequest}
/>
<PrivateRoute
isAuthenticated={permissions.includes(Permissions.ViewLab)}
exact
path="/labs/:id"
component={ViewLab}
/>
</Switch>
)
}

export default Labs
241 changes: 241 additions & 0 deletions src/labs/ViewLab.tsx
@@ -0,0 +1,241 @@
import React, { useEffect, useState } from 'react'
import { useParams, useHistory } from 'react-router'
import format from 'date-fns/format'
import LabRepository from 'clients/db/LabRepository'
import Lab from 'model/Lab'
import Patient from 'model/Patient'
import PatientRepository from 'clients/db/PatientRepository'
import useTitle from 'page-header/useTitle'
import { useTranslation } from 'react-i18next'
import { Row, Column, Badge, Button, Alert } from '@hospitalrun/components'
import TextFieldWithLabelFormGroup from 'components/input/TextFieldWithLabelFormGroup'
import { useButtonToolbarSetter } from 'page-header/ButtonBarProvider'
import useAddBreadcrumbs from 'breadcrumbs/useAddBreadcrumbs'
import { useSelector } from 'react-redux'
import Permissions from 'model/Permissions'
import { RootState } from '../store'

const getTitle = (patient: Patient | undefined, lab: Lab | undefined) =>
patient && lab ? `${lab.type} for ${patient.fullName}` : ''

const ViewLab = () => {
const { id } = useParams()
const { t } = useTranslation()
const history = useHistory()
const setButtons = useButtonToolbarSetter()

const { permissions } = useSelector((state: RootState) => state.user)
const [patient, setPatient] = useState<Patient | undefined>()
const [lab, setLab] = useState<Lab | undefined>()
const [isEditable, setIsEditable] = useState<boolean>(true)
const [isResultInvalid, setIsResultInvalid] = useState<boolean>(false)
const [resultFeedback, setResultFeedback] = useState()
const [errorMessage, setErrorMessage] = useState()

useTitle(getTitle(patient, lab))

const breadcrumbs = [
{
i18nKey: 'labs.requests.view',
location: `/labs/${lab?.id}`,
},
]
useAddBreadcrumbs(breadcrumbs)

useEffect(() => {
const fetchLab = async () => {
if (id) {
const fetchedLab = await LabRepository.find(id)
setLab(fetchedLab)
setIsEditable(fetchedLab.status === 'requested')
}
}
fetchLab()
}, [id])

useEffect(() => {
const fetchPatient = async () => {
if (lab) {
const fetchedPatient = await PatientRepository.find(lab.patientId)
setPatient(fetchedPatient)
}
}

fetchPatient()
}, [lab])

const onResultChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const result = event.currentTarget.value
const newLab = lab as Lab
setLab({ ...newLab, result })
}

const onNotesChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const notes = event.currentTarget.value
const newLab = lab as Lab
setLab({ ...newLab, notes })
}

const onUpdate = async () => {
await LabRepository.saveOrUpdate(lab as Lab)
history.push('/labs')
}

const onComplete = async () => {
const newLab = lab as Lab

if (!newLab.result) {
setIsResultInvalid(true)
setResultFeedback(t('labs.requests.error.resultRequiredToComplete'))
setErrorMessage(t('labs.requests.error.unableToComplete'))
return
}

await LabRepository.saveOrUpdate({
...newLab,
completedOn: new Date().toISOString(),
status: 'completed',
})
history.push('/labs')
}

const onCancel = async () => {
const newLab = lab as Lab
await LabRepository.saveOrUpdate({
...newLab,
canceledOn: new Date().toISOString(),
status: 'canceled',
})
history.push('/labs')
}

const getButtons = () => {
const buttons: React.ReactNode[] = []
if (lab?.status === 'completed' || lab?.status === 'canceled') {
return buttons
}

if (permissions.includes(Permissions.CompleteLab)) {
buttons.push(
<Button onClick={onComplete} color="primary">
{t('labs.requests.complete')}
</Button>,
)
}

if (permissions.includes(Permissions.CancelLab)) {
buttons.push(
<Button onClick={onCancel} color="danger">
{t('labs.requests.cancel')}
</Button>,
)
}

return buttons
}

setButtons(getButtons())

if (lab && patient) {
const getBadgeColor = () => {
if (lab.status === 'completed') {
return 'primary'
}
if (lab.status === 'canceled') {
return 'danger'
}
return 'warning'
}

const getCanceledOnOrCompletedOnDate = () => {
if (lab.status === 'completed') {
return (
<Column>
<div className="form-group">
<h4>{t('labs.lab.completedOn')}</h4>
<h5>{format(new Date(lab.completedOn), 'yyyy-MM-dd hh:mm a')}</h5>
</div>
</Column>
)
}
if (lab.status === 'canceled') {
return (
<Column>
<div className="form-group">
<h4>{t('labs.lab.canceledOn')}</h4>
<h5>{format(new Date(lab.completedOn), 'yyyy-MM-dd hh:mm a')}</h5>
</div>
</Column>
)
}
return <></>
}

return (
<>
{isResultInvalid && (
<Alert color="danger" title={t('states.error')} message={errorMessage} />
)}
<Row>
<Column>
<div className="form-group">
<h4>{t('labs.lab.status')}</h4>
<Badge color={getBadgeColor()}>
<h5>{lab.status}</h5>
</Badge>
</div>
</Column>
<Column>
<div className="form-group">
<h4>{t('labs.lab.for')}</h4>
<h5>{patient.fullName}</h5>
</div>
</Column>
<Column>
<div className="form-group">
<h4>{t('labs.lab.type')}</h4>
<h5>{lab.type}</h5>
</div>
</Column>
<Column>
<div className="form-group">
<h4>{t('labs.lab.requestedOn')}</h4>
<h5>{format(new Date(lab.requestedOn), 'yyyy/mm/dd hh:mm a')}</h5>
</div>
</Column>
{getCanceledOnOrCompletedOnDate()}
</Row>
<div className="border-bottom" />
<form>
<TextFieldWithLabelFormGroup
name="result"
label={t('labs.lab.result')}
isEditable={isEditable}
isInvalid={isResultInvalid}
feedback={resultFeedback}
onChange={onResultChange}
/>
<TextFieldWithLabelFormGroup
name="notes"
label={t('labs.lab.notes')}
value={lab.notes}
isEditable={isEditable}
onChange={onNotesChange}
/>
{isEditable && (
<div className="row float-right">
<div className="btn-group btn-group-lg mt-3">
<Button className="mr-2" color="success" onClick={onUpdate}>
{t('actions.update')}
</Button>
</div>
</div>
)}
</form>
</>
)
}
return <h1>Loading...</h1>
}

export default ViewLab

0 comments on commit efba834

Please sign in to comment.