-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
KHP3-4780: (feat) add mch clinical views for anc, labour and delivery…
…, and postn… (#97) * (feat) add mch clinical views for anc, labour and delivery, and postnatal care * fix lint issues * (fix)add content switcher for MCH mother services. Add expandable table renderer for more encounter information * (fix) format fixes * remove unused code
1 parent
4a61147
commit 84a8efa
Showing
21 changed files
with
1,418 additions
and
4 deletions.
There are no files selected for viewing
26 changes: 25 additions & 1 deletion
26
packages/esm-patient-clinical-view-app/src/config-schema.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,25 @@ | ||
export const configSchema = {}; | ||
import { Type } from '@openmrs/esm-framework'; | ||
|
||
export const configSchema = { | ||
encounterTypes: { | ||
_type: Type.Object, | ||
_description: 'List of encounter type UUIDs', | ||
_default: { | ||
mchMotherConsultation: 'c6d09e05-1f25-4164-8860-9f32c5a02df0', | ||
}, | ||
}, | ||
formsList: { | ||
_type: Type.Object, | ||
_description: 'List of form UUIDs', | ||
_default: { | ||
antenatal: 'e8f98494-af35-4bb8-9fc7-c409c8fed843', | ||
postNatal: '72aa78e0-ee4b-47c3-9073-26f3b9ecc4a7', | ||
labourAndDelivery: '496c7cc3-0eea-4e84-a04c-2292949e2f7f', | ||
}, | ||
}, | ||
}; | ||
|
||
export interface ConfigObject { | ||
encounterTypes: { mchMotherConsultation: string }; | ||
formsList: { labourAndDelivery: string; antenatal: string; postnatal: string }; | ||
} |
65 changes: 65 additions & 0 deletions
65
packages/esm-patient-clinical-view-app/src/data-table/o-table.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { | ||
DataTable, | ||
Table, | ||
TableCell, | ||
TableContainer, | ||
TableBody, | ||
TableHead, | ||
TableHeader, | ||
TableRow, | ||
TableExpandHeader, | ||
TableExpandRow, | ||
TableExpandedRow, | ||
} from '@carbon/react'; | ||
import styles from './o-table.scss'; | ||
import EncounterObservations from '../encounter-observation/encounter-observation.component'; | ||
|
||
interface TableProps { | ||
tableHeaders: any; | ||
tableRows: any; | ||
} | ||
|
||
export const OTable: React.FC<TableProps> = ({ tableHeaders, tableRows }) => { | ||
return ( | ||
<TableContainer> | ||
<DataTable rows={tableRows} headers={tableHeaders} isSortable={true} size="short"> | ||
{({ rows, headers, getHeaderProps, getTableProps, getRowProps }) => ( | ||
<Table {...getTableProps()}> | ||
<TableHead> | ||
<TableRow> | ||
<TableExpandHeader enableToggle={false} /> | ||
{headers.map((header) => ( | ||
<TableHeader | ||
className={`${styles.productiveHeading01} ${styles.text02}`} | ||
{...getHeaderProps({ | ||
header, | ||
isSortable: header.isSortable, | ||
})}> | ||
{header.header?.content ?? header.header} | ||
</TableHeader> | ||
))} | ||
</TableRow> | ||
</TableHead> | ||
<TableBody> | ||
{rows.map((row, index) => { | ||
return ( | ||
<React.Fragment key={row.id}> | ||
<TableExpandRow {...getRowProps({ row })}> | ||
{row.cells.map((cell) => ( | ||
<TableCell key={cell.id}>{cell.value}</TableCell> | ||
))} | ||
</TableExpandRow> | ||
<TableExpandedRow className={styles.hiddenRow} colSpan={headers.length + 2}> | ||
<EncounterObservations observations={tableRows?.[index]?.obs ?? []} /> | ||
</TableExpandedRow> | ||
</React.Fragment> | ||
); | ||
})} | ||
</TableBody> | ||
</Table> | ||
)} | ||
</DataTable> | ||
</TableContainer> | ||
); | ||
}; |
Empty file.
67 changes: 67 additions & 0 deletions
67
packages/esm-patient-clinical-view-app/src/encounter-list/encounter-list-utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { formatDate, parseDate } from '@openmrs/esm-framework'; | ||
|
||
export function getEncounterValues(encounter, param: string, isDate?: Boolean) { | ||
if (isDate) { | ||
return formatDate(parseDate(encounter[param])); | ||
} else { | ||
return encounter[param] ? encounter[param] : '--'; | ||
} | ||
} | ||
|
||
export function formatDateTime(dateString: string): any { | ||
const format = 'YYYY-MM-DDTHH:mm:ss'; | ||
if (dateString.includes('.')) { | ||
dateString = dateString.split('.')[0]; | ||
} | ||
return formatDate(parseDate(dateString)); | ||
} | ||
|
||
export function obsArrayDateComparator(left, right) { | ||
return formatDateTime(right.obsDatetime) - formatDateTime(left.obsDatetime); | ||
} | ||
|
||
export function findObs(encounter, obsConcept): Record<string, any> { | ||
const allObs = encounter?.obs?.filter((observation) => observation.concept.uuid === obsConcept) || []; | ||
return allObs?.length == 1 ? allObs[0] : allObs?.sort(obsArrayDateComparator)[0]; | ||
} | ||
|
||
export function getObsFromEncounters(encounters, obsConcept) { | ||
const filteredEnc = encounters?.find((enc) => enc.obs.find((obs) => obs.concept.uuid === obsConcept)); | ||
return getObsFromEncounter(filteredEnc, obsConcept); | ||
} | ||
|
||
export function getMultipleObsFromEncounter(encounter, obsConcepts: Array<string>) { | ||
let observations = []; | ||
obsConcepts.map((concept) => { | ||
const obs = getObsFromEncounter(encounter, concept); | ||
if (obs !== '--') { | ||
observations.push(obs); | ||
} | ||
}); | ||
|
||
return observations.length ? observations.join(', ') : '--'; | ||
} | ||
|
||
export function getObsFromEncounter(encounter, obsConcept, isDate?: Boolean, isTrueFalseConcept?: Boolean) { | ||
const obs = findObs(encounter, obsConcept); | ||
|
||
if (isTrueFalseConcept) { | ||
if (obs.value.uuid == 'cf82933b-3f3f-45e7-a5ab-5d31aaee3da3') { | ||
return 'Yes'; | ||
} else { | ||
return 'No'; | ||
} | ||
} | ||
if (!obs) { | ||
return '--'; | ||
} | ||
if (isDate) { | ||
return formatDate(parseDate(obs.value), { mode: 'wide' }); | ||
} | ||
if (typeof obs.value === 'object' && obs.value?.names) { | ||
return ( | ||
obs.value?.names?.find((conceptName) => conceptName.conceptNameType === 'SHORT')?.name || obs.value.name.name | ||
); | ||
} | ||
return obs.value; | ||
} |
235 changes: 235 additions & 0 deletions
235
packages/esm-patient-clinical-view-app/src/encounter-list/encounter-list.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
import { navigate } from '@openmrs/esm-framework'; | ||
import React, { useCallback, useEffect, useMemo, useState } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import styles from './encounter-list.scss'; | ||
import { OTable } from '../data-table/o-table.component'; | ||
import { | ||
Button, | ||
Link, | ||
OverflowMenu, | ||
OverflowMenuItem, | ||
Pagination, | ||
DataTableSkeleton, | ||
Layer, | ||
Tile, | ||
} from '@carbon/react'; | ||
import { Add } from '@carbon/react/icons'; | ||
import { useEncounterRows } from '../../src/hooks/useEncounterRows'; | ||
import { OpenmrsEncounter } from '../type/types'; | ||
import { EmptyDataIllustration, EmptyState } from '@openmrs/esm-patient-common-lib'; | ||
|
||
export interface O3FormSchema { | ||
name: string; | ||
pages: Array<any>; | ||
processor: string; | ||
uuid: string; | ||
referencedForms: []; | ||
encounterType: string; | ||
encounter?: string | OpenmrsEncounter; | ||
allowUnspecifiedAll?: boolean; | ||
defaultPage?: string; | ||
readonly?: string | boolean; | ||
inlineRendering?: 'single-line' | 'multiline' | 'automatic'; | ||
markdown?: any; | ||
postSubmissionActions?: Array<{ actionId: string; config?: Record<string, any> }>; | ||
formOptions?: { | ||
usePreviousValueDisabled: boolean; | ||
}; | ||
version?: string; | ||
} | ||
export interface EncounterListColumn { | ||
key: string; | ||
header: string; | ||
getValue: (encounter: any) => string; | ||
link?: any; | ||
} | ||
|
||
export interface EncounterListProps { | ||
patientUuid: string; | ||
encounterType: string; | ||
columns: Array<any>; | ||
headerTitle: string; | ||
description: string; | ||
formList?: Array<{ | ||
name: string; | ||
excludedIntents?: Array<string>; | ||
fixedIntent?: string; | ||
isDefault?: boolean; | ||
}>; | ||
launchOptions: { | ||
moduleName: string; | ||
hideFormLauncher?: boolean; | ||
displayText?: string; | ||
workspaceWindowSize?: 'minimized' | 'maximized'; | ||
}; | ||
filter?: (encounter: any) => boolean; | ||
} | ||
|
||
export const EncounterList: React.FC<EncounterListProps> = ({ | ||
patientUuid, | ||
encounterType, | ||
columns, | ||
headerTitle, | ||
description, | ||
formList, | ||
filter, | ||
launchOptions, | ||
}) => { | ||
const { t } = useTranslation(); | ||
const [paginatedRows, setPaginatedRows] = useState([]); | ||
const [forms, setForms] = useState<O3FormSchema[]>([]); | ||
const [currentPage, setCurrentPage] = useState(1); | ||
const [pageSize, setPageSize] = useState(10); | ||
const formNames = useMemo(() => formList.map((form) => form.name), []); | ||
const { | ||
encounters, | ||
isLoading: isLoadingEncounters, | ||
onFormSave, | ||
} = useEncounterRows(patientUuid, encounterType, filter); | ||
const { moduleName, workspaceWindowSize, displayText, hideFormLauncher } = launchOptions; | ||
|
||
const defaultActions = useMemo( | ||
() => [ | ||
{ | ||
label: t('viewEncounter', 'View'), | ||
form: { | ||
name: forms[0]?.name, | ||
}, | ||
mode: 'view', | ||
intent: '*', | ||
}, | ||
{ | ||
label: t('editEncounter', 'Edit'), | ||
form: { | ||
name: forms[0]?.name, | ||
}, | ||
mode: 'view', | ||
intent: '*', | ||
}, | ||
], | ||
[forms, t], | ||
); | ||
|
||
const headers = useMemo(() => { | ||
if (columns) { | ||
return columns.map((column) => { | ||
return { key: column.key, header: column.header }; | ||
}); | ||
} | ||
return []; | ||
}, [columns]); | ||
|
||
const expandedHeaderProps = useMemo(() => { | ||
if (columns) { | ||
return columns.map((column) => { | ||
return { key: column.key, header: column.header }; | ||
}); | ||
} | ||
return []; | ||
}, [columns]); | ||
|
||
const constructPaginatedTableRows = useCallback( | ||
(encounters: OpenmrsEncounter[], currentPage: number, pageSize: number) => { | ||
const startIndex = (currentPage - 1) * pageSize; | ||
const paginatedEncounters = []; | ||
for (let i = startIndex; i < startIndex + pageSize; i++) { | ||
if (i < encounters.length) { | ||
paginatedEncounters.push(encounters[i]); | ||
} | ||
} | ||
const rows = paginatedEncounters.map((encounter) => { | ||
const tableRow: { id: string; actions: any; obs: any } = { | ||
id: encounter.uuid, | ||
actions: null, | ||
obs: encounter.obs, | ||
}; | ||
// inject launch actions | ||
encounter['launchFormActions'] = { | ||
editEncounter: () => {}, | ||
viewEncounter: () => {}, | ||
}; | ||
// process columns | ||
columns.forEach((column) => { | ||
let val = column.getValue(encounter); | ||
if (column.link) { | ||
val = ( | ||
<Link | ||
onClick={(e) => { | ||
e.preventDefault(); | ||
if (column.link.handleNavigate) { | ||
column.link.handleNavigate(encounter); | ||
} else { | ||
column.link?.getUrl && navigate({ to: column.link.getUrl() }); | ||
} | ||
}}> | ||
{val} | ||
</Link> | ||
); | ||
} | ||
tableRow[column.key] = val; | ||
}); | ||
// If custom config is available, generate actions accordingly; otherwise, fallback to the default actions. | ||
const actions = tableRow.actions?.length ? tableRow.actions : defaultActions; | ||
tableRow['actions'] = ( | ||
<OverflowMenu flipped className={styles.flippedOverflowMenu}> | ||
{actions.map((actionItem, index) => ( | ||
<OverflowMenuItem | ||
itemText={actionItem.label} | ||
onClick={(e) => { | ||
e.preventDefault(); | ||
}} | ||
/> | ||
))} | ||
</OverflowMenu> | ||
); | ||
return tableRow; | ||
}); | ||
setPaginatedRows(rows); | ||
}, | ||
[columns, defaultActions, forms, moduleName, workspaceWindowSize], | ||
); | ||
|
||
useEffect(() => { | ||
if (encounters?.length) { | ||
constructPaginatedTableRows(encounters, currentPage, pageSize); | ||
} | ||
}, [encounters, pageSize, constructPaginatedTableRows, currentPage]); | ||
|
||
return ( | ||
<> | ||
{isLoadingEncounters ? ( | ||
<DataTableSkeleton rowCount={5} /> | ||
) : encounters.length > 0 ? ( | ||
<> | ||
<div className={styles.widgetContainer}> | ||
<div className={styles.widgetHeaderContainer}> | ||
{!hideFormLauncher && <div className={styles.toggleButtons}>{}</div>} | ||
</div> | ||
<OTable tableHeaders={headers} tableRows={paginatedRows} /> | ||
<Pagination | ||
page={currentPage} | ||
pageSize={pageSize} | ||
pageSizes={[10, 20, 30, 40, 50]} | ||
totalItems={encounters.length} | ||
onChange={({ page, pageSize }) => { | ||
setCurrentPage(page); | ||
setPageSize(pageSize); | ||
}} | ||
/> | ||
</div> | ||
</> | ||
) : ( | ||
<div className={styles.widgetContainer}> | ||
<Layer className={styles.emptyStateContainer}> | ||
<Tile className={styles.tile}> | ||
<div> | ||
<EmptyDataIllustration /> | ||
</div> | ||
<p className={styles.content}>There are no encounters to display</p> | ||
</Tile> | ||
</Layer> | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; |
53 changes: 53 additions & 0 deletions
53
packages/esm-patient-clinical-view-app/src/encounter-list/encounter-list.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
@use '@carbon/styles/scss/spacing'; | ||
@use '@carbon/styles/scss/type'; | ||
@import "../../src/root.scss"; | ||
|
||
|
||
.widgetContainer { | ||
background-color: $ui-background; | ||
border: 1px solid #e0e0e0; | ||
margin-bottom: 1rem; | ||
} | ||
|
||
.widgetHeaderContainer { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
padding: spacing.$spacing-04 0 spacing.$spacing-04 spacing.$spacing-05; | ||
} | ||
|
||
.widgetHeaderContainer > h4:after { | ||
content: ""; | ||
display: block; | ||
width: 2rem; | ||
padding-top: 0.188rem; | ||
border-bottom: 0.375rem solid var(--brand-03); | ||
} | ||
|
||
.newServiceEnrolmentBtn { | ||
margin-bottom: 0.5em; | ||
text-align: right; | ||
} | ||
|
||
.newServiceEnrolmentBtn > button { | ||
background-color: $ui-background; | ||
} | ||
|
||
.widgetContainer :global(.cds--data-table) thead th button span { | ||
height: unset !important; | ||
} | ||
|
||
.tile { | ||
text-align: center; | ||
} | ||
|
||
.emptyStateContainer { | ||
margin: 2rem 0; | ||
} | ||
|
||
.content { | ||
@extend .productiveHeading01; | ||
color: $text-02; | ||
margin-top: spacing.$spacing-05; | ||
margin-bottom: spacing.$spacing-03; | ||
} |
65 changes: 65 additions & 0 deletions
65
packages/esm-patient-clinical-view-app/src/encounter-list/encounter.resource.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
export function mapEncounters(encounters: Array<any>): MappedEncounter[] { | ||
return encounters?.map((enc) => ({ | ||
id: enc?.uuid, | ||
datetime: enc?.encounterDatetime, | ||
obs: enc?.obs, | ||
provider: enc?.encounterProviders?.length > 0 ? enc?.encounterProviders[0].provider?.person?.display : '--', | ||
})); | ||
} | ||
|
||
export interface MappedEncounter { | ||
id: string; | ||
datetime: string; | ||
obs: Array<Observation>; | ||
provider: string; | ||
} | ||
|
||
export interface Encounter { | ||
uuid: string; | ||
encounterDatetime: string; | ||
encounterProviders: Array<EncounterProvider>; | ||
obs: Array<Observation>; | ||
} | ||
|
||
export interface EncounterProvider { | ||
uuid: string; | ||
display: string; | ||
encounterRole: { | ||
uuid: string; | ||
display: string; | ||
}; | ||
provider: { | ||
uuid: string; | ||
person: { | ||
uuid: string; | ||
display: string; | ||
}; | ||
}; | ||
} | ||
|
||
export interface Observation { | ||
uuid: string; | ||
concept: { | ||
uuid: string; | ||
display: string; | ||
conceptClass: { | ||
uuid: string; | ||
display: string; | ||
}; | ||
}; | ||
display: string; | ||
groupMembers: null | Array<{ | ||
uuid: string; | ||
concept: { | ||
uuid: string; | ||
display: string; | ||
}; | ||
value: { | ||
uuid: string; | ||
display: string; | ||
}; | ||
display: string; | ||
}>; | ||
value: any; | ||
obsDatetime?: string; | ||
} |
20 changes: 20 additions & 0 deletions
20
.../esm-patient-clinical-view-app/src/encounter-observation/encounter-observation-table.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
@use '@carbon/styles/scss/spacing'; | ||
|
||
.observation { | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
grid-gap: spacing.$spacing-03; | ||
margin: spacing.$spacing-05 spacing.$spacing-05 spacing.$spacing-05 0; | ||
} | ||
|
||
.observation > span { | ||
align-self: center; | ||
} | ||
|
||
.parentConcept { | ||
font-weight: bold; | ||
} | ||
|
||
.childConcept { | ||
padding-left: 0.8rem; | ||
} |
96 changes: 96 additions & 0 deletions
96
...m-patient-clinical-view-app/src/encounter-observation/encounter-observation.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import React from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { SkeletonText } from '@carbon/react'; | ||
import { useConfig } from '@openmrs/esm-framework'; | ||
import { Observation } from '../../src/type/types'; | ||
import styles from './encounter-observation-table.scss'; | ||
|
||
interface EncounterObservationsProps { | ||
observations: Array<Observation>; | ||
} | ||
|
||
const EncounterObservations: React.FC<EncounterObservationsProps> = ({ observations }) => { | ||
const { t } = useTranslation(); | ||
const { obsConceptUuidsToHide = [] } = useConfig(); | ||
const conceptMap = new Map([ | ||
['1658AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Adherence rating'], | ||
['164848AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Patient received viral load result'], | ||
[ | ||
'163310AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', | ||
'Was the Viral load result suppressed (less than 1000) or unsuppressed (greater than 1000) ', | ||
], | ||
['160632AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Way forward'], | ||
['160110AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Have any dosses been missed?'], | ||
['1898AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', "Patient's condition improved since last visit"], | ||
['95f73a05-7c52-4a8d-b3b1-f632a41d065d', 'Will the patient benefit from a home visit?'], | ||
['164891AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Date of first session'], | ||
['162846AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Pill count adherence % (from pill count)'], | ||
['1272AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Has the patient been referred to other services?'], | ||
]); | ||
|
||
function getAnswerFromDisplay(display: string): string { | ||
if (display == undefined) { | ||
return ''; | ||
} | ||
const colonIndex = display.indexOf(':'); | ||
if (colonIndex === -1) { | ||
return ''; | ||
} else { | ||
return display.substring(colonIndex + 1).trim(); | ||
} | ||
} | ||
|
||
function matchFormDisplay(conceptUuid: string): string { | ||
let theDisplay = conceptMap.get(conceptUuid) ? conceptMap.get(conceptUuid) : ''; | ||
return theDisplay; | ||
} | ||
|
||
if (!observations) { | ||
return <SkeletonText />; | ||
} | ||
|
||
if (observations) { | ||
const filteredObservations = !!obsConceptUuidsToHide.length | ||
? observations?.filter((obs) => { | ||
return !obsConceptUuidsToHide.includes(obs?.concept?.uuid); | ||
}) | ||
: observations; | ||
return ( | ||
<div className={styles.observation}> | ||
{filteredObservations?.map((obs, index) => { | ||
if (obs.groupMembers) { | ||
return ( | ||
<React.Fragment key={index}> | ||
<span className={styles.parentConcept}>{obs.concept.display}</span> | ||
<span /> | ||
{obs.groupMembers.map((member) => ( | ||
<React.Fragment key={index}> | ||
<span className={styles.childConcept}>{member.concept.display}</span> | ||
<span>{getAnswerFromDisplay(member.display)}</span> | ||
</React.Fragment> | ||
))} | ||
</React.Fragment> | ||
); | ||
} else { | ||
return ( | ||
<React.Fragment key={index}> | ||
<span> | ||
{matchFormDisplay(obs.concept.uuid) ? matchFormDisplay(obs.concept.uuid) : obs.concept.display} | ||
</span> | ||
<span>{getAnswerFromDisplay(obs.display)}</span> | ||
</React.Fragment> | ||
); | ||
} | ||
})} | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<div className={styles.observation}> | ||
<p>{t('noObservationsFound', 'No observations found')}</p> | ||
</div> | ||
); | ||
}; | ||
|
||
export default EncounterObservations; |
76 changes: 76 additions & 0 deletions
76
packages/esm-patient-clinical-view-app/src/esm-mch-app/constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
export const vLResultsConcept = '1305AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
|
||
export const ancVisitNumberConcept = '1425AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
|
||
//Labour & Delivery | ||
export const infantStatusAtBirthConcept = '159917AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const deliveryOutcomeConcept = '159949AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const ancHivResultConcept = 'c5f74c86-62cd-4d22-9260-4238f1e45fe0'; | ||
export const hivStatusAtDeliveryConcept = 'c5f74c86-62cd-4d22-9260-4238f1e45fe0'; | ||
export const artInitiationConcept = '6e62bf7e-2107-4d09-b485-6e60cbbb2d08'; | ||
export const birthCountConcept = '1568AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const hivTestStatus = '164401AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const recenctViralLoad = '163310AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const motherStatusConcept = '1856AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const hivTestAtMaternityResults = '1396AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const placeOfDeliveryConcept = '1572AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
|
||
export const infantDeliveryGroupingConcept = '1c70c490-cafa-4c95-9fdd-a30b62bb78b8'; | ||
export const infantPTrackerIdConcept = '164803AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const infantDateOfBirth = '164802AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
|
||
//Infant Postnatal | ||
export const infantPostnatalEncounterType = 'af1f1b24-d2e8-4282-b308-0bf79b365584'; | ||
export const nextVisitDate = '5096AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const ChildPTrackerId = '6c45421e-2566-47cb-bbb3-07586fffbfe2'; | ||
export const childDateOfBirth = '163260AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const artProphylaxisStatus = '1148AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const linkedToArt = 'a40d8bc4-56b8-4f28-a1dd-412da5cf20ed'; | ||
export const breastfeedingStatus = '1151AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const outcomeStatus = '160433AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const infantVisitDate = '159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const finalTestResults = '164460AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
|
||
//Antenatal | ||
export const antenatalEncounterType = '2549af50-75c8-4aeb-87ca-4bb2cef6c69a'; | ||
export const visitDateConcept = '163260AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const hivTestResultConcept = '159427AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const partnerHivStatus = '1436AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const artNoConcept = '164402AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const followUpDateConcept = '5096AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const eDDConcept = '5596AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const nextVisitDateConcept = '5096AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const artStartDate = '159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const ancVisitsConcept = '1425AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const testTypeConcept = 'ee8c0292-47f8-4c01-8b60-8ba13a560e1a'; | ||
export const infantExposureStatus = '6027869c-5d7e-4a82-b22f-6d9c57d61a4d'; | ||
|
||
//Mother Postnatal | ||
export const motherPostnatalEncounterType = '269bcc7f-04f8-4ddc-883d-7a3a0d569aad'; | ||
export const MothervisitDate = '163260AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const MotherHivStatus = '159427AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const MotherViralLoadDate = '163281AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const MotherViralLoadResult = '1305AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const MotherNextVisitDate = '5096AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const artUniqueNoConcept = '164402AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const motherGeneralConditionConcept = '1856AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const motherBreastConditionConcept = '159780AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const pphConditionConcept = '230AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const uterusConditionConcept = '163742AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
|
||
//Reports | ||
export const ancVisitsReport = '5299521a-7fad-47bb-8280-14c99d04c790'; | ||
export const eddReport = 'aac635c9-0a77-4489-8b0d-866b3ad22f73'; | ||
export const motherHivStatusReport = 'ed50a889-dd5b-4759-861c-b54e3c686fe7'; | ||
|
||
//MCH Summary | ||
export const mchEncounterType = '12de5bc5-352e-4faf-9961-a2125085a75c'; | ||
export const antenatalVisitType = '164181AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; | ||
export const mchVisitType = '140084BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'; | ||
export const mchVisitsTypes = ['Antenatal', 'Labor and Delivery', 'Mother Postnatal']; | ||
|
||
export const encounterRepresentation = | ||
'custom:(uuid,encounterDatetime,encounterType,location:(uuid,name),' + | ||
'patient:(uuid,display),encounterProviders:(uuid,provider:(uuid,name)),' + | ||
'obs:(uuid,obsDatetime,voided,groupMembers,concept:(uuid,name:(uuid,name)),value:(uuid,name:(uuid,name),' + | ||
'names:(uuid,conceptNameType,name))),form:(uuid,name))'; |
39 changes: 39 additions & 0 deletions
39
packages/esm-patient-clinical-view-app/src/esm-mch-app/maternal-health-component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
@use '@carbon/styles/scss/spacing'; | ||
@import "~@openmrs/esm-styleguide/src/vars"; | ||
|
||
.widgetContainer { | ||
background-color: $ui-background; | ||
} | ||
|
||
.widgetHeaderContainer { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
padding: spacing.$spacing-04 0 spacing.$spacing-04 spacing.$spacing-05; | ||
} | ||
|
||
.widgetHeaderContainer > h4:after { | ||
content: ''; | ||
display: block; | ||
width: 2rem; | ||
padding-top: 0.188rem; | ||
border-bottom: 0.375rem solid var(--brand-01); | ||
} | ||
|
||
.toggleButtons { | ||
width: fit-content; | ||
margin: 0 spacing.$spacing-03; | ||
} | ||
|
||
.tabContainer div[role='tabpanel'] { | ||
padding: 0 !important; | ||
} | ||
|
||
.tabContainer li button { | ||
width: 100% !important; | ||
} | ||
|
||
|
||
.hivStatusTag { | ||
min-width: 80px; | ||
} |
48 changes: 48 additions & 0 deletions
48
packages/esm-patient-clinical-view-app/src/esm-mch-app/mch-clinical-view.meta.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
export const mchFolderMeta = { | ||
title: 'Maternal & Child Health', | ||
slotName: 'mch-slot', | ||
isExpanded: false, | ||
patientExpression: 'calculateAge(patient.birthDate) <= 10 || patient.gender === "female"', | ||
}; | ||
|
||
export const mchSummaryDashboardMeta = { | ||
name: 'mch-summary', | ||
slot: 'mch-summary-slot', | ||
columns: 1, | ||
title: 'MNCH Summary', | ||
path: 'mnch-summary', | ||
layoutMode: 'anchored', | ||
}; | ||
|
||
export const maternalVisitsDashboardMeta = { | ||
slot: 'maternal-visits-summary-slot', | ||
columns: 1, | ||
title: 'Maternal Visits', | ||
path: 'maternal-visits', | ||
layoutMode: 'anchored', | ||
patientExpression: 'calculateAge(patient.birthDate) > 10', | ||
}; | ||
|
||
export const childVisitsDashboardMeta = { | ||
slot: 'child-visits-summary-slot', | ||
columns: 1, | ||
title: 'Child Visits', | ||
path: 'child-visits', | ||
layoutMode: 'anchored', | ||
patientExpression: 'calculateAge(patient.birthDate) <= 10', | ||
}; | ||
|
||
// Clinical Dashboard | ||
export const motherChildDashboardMeta = { | ||
name: 'mother-child-health', | ||
slot: 'mother-child-health-dashboard-slot', | ||
config: { | ||
columns: 1, | ||
type: 'grid', | ||
programme: 'pmtct', | ||
dashboardTitle: 'Mother Child Health Home Page', | ||
icon: '', | ||
}, | ||
isLink: true, | ||
title: 'Maternal & Child Health', | ||
}; |
43 changes: 43 additions & 0 deletions
43
...ent-clinical-view-app/src/esm-mch-app/views/maternal-health/maternal-health.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import React, { useState } from 'react'; | ||
import { Tabs, Tab, TabList, TabPanels, TabPanel, ContentSwitcher, Switch, SwitcherItem } from '@carbon/react'; | ||
import styles from '../../maternal-health-component.scss'; | ||
import { useTranslation } from 'react-i18next'; | ||
import AntenatalCareList from './tabs/antenatal-care.component'; | ||
import LabourDeliveryList from './tabs/labour-delivery.component'; | ||
import PostnatalCareList from './tabs/postnatal-care.component'; | ||
import { CardHeader } from '@openmrs/esm-patient-common-lib'; | ||
|
||
interface OverviewListProps { | ||
patientUuid: string; | ||
} | ||
|
||
type SwitcherItem = { | ||
index: number; | ||
name?: string; | ||
text?: string; | ||
}; | ||
const MaternalHealthList: React.FC<OverviewListProps> = ({ patientUuid }) => { | ||
const { t } = useTranslation(); | ||
const [switchItem, setSwitcherItem] = useState<SwitcherItem>({ index: 0 }); | ||
|
||
return ( | ||
<div className={styles.widgetCard}> | ||
<CardHeader title={t('carePanel', 'MCH Clinical View')}> | ||
<div className={styles.contextSwitcherContainer}> | ||
<ContentSwitcher selectedIndex={switchItem?.index} onChange={setSwitcherItem}> | ||
<Switch name={'antenatal'} text="Antenatal Care" /> | ||
<Switch name={'labourAndDelivery'} text="Labour and Delivery" /> | ||
<Switch name={'postnatal'} text="Postnatal Care" /> | ||
</ContentSwitcher> | ||
</div> | ||
</CardHeader> | ||
<div style={{ width: '100%', minHeight: '20rem' }}> | ||
{<>{switchItem.index == 0 && <AntenatalCareList patientUuid={patientUuid} />}</>} | ||
{<>{switchItem.index == 1 && <LabourDeliveryList patientUuid={patientUuid} />}</>} | ||
{<>{switchItem.index == 2 && <PostnatalCareList patientUuid={patientUuid} />}</>} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default MaternalHealthList; |
113 changes: 113 additions & 0 deletions
113
...clinical-view-app/src/esm-mch-app/views/maternal-health/tabs/antenatal-care.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import React, { useMemo } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { EncounterList, EncounterListColumn } from '../../../../encounter-list/encounter-list.component'; | ||
import { getObsFromEncounter } from '../../../../encounter-list/encounter-list-utils'; | ||
import { | ||
artInitiationConcept, | ||
eDDConcept, | ||
followUpDateConcept, | ||
hivTestResultConcept, | ||
ancVisitNumberConcept, | ||
visitDateConcept, | ||
vLResultsConcept, | ||
partnerHivStatus, | ||
} from '../../../constants'; | ||
import { useConfig, formatDate, parseDate } from '@openmrs/esm-framework'; | ||
import { ConfigObject } from '../../../../config-schema'; | ||
|
||
interface AntenatalCareListProps { | ||
patientUuid: string; | ||
} | ||
|
||
const AntenatalCareList: React.FC<AntenatalCareListProps> = ({ patientUuid }) => { | ||
const { t } = useTranslation(); | ||
const headerTitle = t('antenatalCare', 'Antenatal Care'); | ||
const { | ||
encounterTypes: { mchMotherConsultation }, | ||
formsList: { antenatal }, | ||
} = useConfig<ConfigObject>(); | ||
|
||
const ANCEncounterTypeUUID = mchMotherConsultation; | ||
const ANCEncounterFormUUID = antenatal; | ||
|
||
const columns: EncounterListColumn[] = useMemo( | ||
() => [ | ||
{ | ||
key: 'visitDate', | ||
header: t('visitDate', 'Visit Date'), | ||
getValue: (encounter) => { | ||
return formatDate(parseDate(encounter.encounterDatetime)); | ||
}, | ||
}, | ||
{ | ||
key: 'ancVisitNumber', | ||
header: t('ancVisitNumber', 'ANC Visit Number'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, ancVisitNumberConcept); | ||
}, | ||
}, | ||
{ | ||
key: 'hivTestResults', | ||
header: t('hivTestResults', 'HIV Test Results'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, hivTestResultConcept); | ||
}, | ||
}, | ||
{ | ||
key: 'partnerStatus', | ||
header: t('partnerStatus', 'HIV status of partner)'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, partnerHivStatus); | ||
}, | ||
}, | ||
{ | ||
key: 'followUpDate', | ||
header: t('followUpDate', 'Next follow-up date'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, followUpDateConcept, true); | ||
}, | ||
}, | ||
{ | ||
key: 'facility', | ||
header: t('facility', 'Facility'), | ||
getValue: (encounter) => { | ||
return encounter.location.name; | ||
}, | ||
}, | ||
{ | ||
key: 'actions', | ||
header: t('actions', 'Actions'), | ||
getValue: (encounter) => [ | ||
{ | ||
form: { name: 'Antenatal Form', package: 'maternal_health' }, | ||
encounterUuid: encounter.uuid, | ||
intent: '*', | ||
label: t('editForm', 'Edit Form'), | ||
mode: 'edit', | ||
}, | ||
], | ||
}, | ||
], | ||
[t], | ||
); | ||
|
||
return ( | ||
<EncounterList | ||
patientUuid={patientUuid} | ||
encounterType={ANCEncounterTypeUUID} | ||
formList={[{ name: 'Antenatal Form' }]} | ||
columns={columns} | ||
description={headerTitle} | ||
headerTitle={headerTitle} | ||
launchOptions={{ | ||
displayText: t('add', 'Add'), | ||
moduleName: 'MCH Clinical View', | ||
}} | ||
filter={(encounter) => { | ||
return encounter.form.uuid == ANCEncounterFormUUID; | ||
}} | ||
/> | ||
); | ||
}; | ||
|
||
export default AntenatalCareList; |
106 changes: 106 additions & 0 deletions
106
...linical-view-app/src/esm-mch-app/views/maternal-health/tabs/labour-delivery.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import React, { useMemo } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { EncounterList, EncounterListColumn } from '../../../../encounter-list/encounter-list.component'; | ||
import { getObsFromEncounter } from '../../../../encounter-list/encounter-list-utils'; | ||
import { | ||
ancHivResultConcept, | ||
artInitiationConcept, | ||
birthCountConcept, | ||
deliveryOutcomeConcept, | ||
hivTestAtMaternityResults, | ||
hivTestResultConcept, | ||
placeOfDeliveryConcept, | ||
visitDateConcept, | ||
} from '../../../constants'; | ||
import { useConfig, formatDate, parseDate } from '@openmrs/esm-framework'; | ||
import { ConfigObject } from '../../../../config-schema'; | ||
|
||
interface LabourDeliveryListProps { | ||
patientUuid: string; | ||
} | ||
|
||
const LabourDeliveryList: React.FC<LabourDeliveryListProps> = ({ patientUuid }) => { | ||
const { t } = useTranslation(); | ||
const headerTitle = t('labourAndDelivery', 'Labour and Delivery'); | ||
|
||
const { | ||
encounterTypes: { mchMotherConsultation }, | ||
formsList: { labourAndDelivery }, | ||
} = useConfig<ConfigObject>(); | ||
|
||
const LNDEncounterTypeUUID = mchMotherConsultation; | ||
const LNDEncounterFormUUID = labourAndDelivery; | ||
const columns: EncounterListColumn[] = useMemo( | ||
() => [ | ||
{ | ||
key: 'deliveryDate', | ||
header: t('deliveryDate', 'Delivery Date'), | ||
getValue: (encounter) => { | ||
return formatDate(parseDate(encounter.encounterDatetime)); | ||
}, | ||
}, | ||
{ | ||
key: 'deliveryOutcome', | ||
header: t('deliveryOutcome', 'Delivery Outcome'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, deliveryOutcomeConcept); | ||
}, | ||
}, | ||
{ | ||
key: 'hivTestResults', | ||
header: t('hivTestResults', 'HIV Test Results'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, hivTestResultConcept); | ||
}, | ||
}, | ||
{ | ||
key: 'testingAtMaternity', | ||
header: t('testingAtMaternity', 'HIV test at maternity'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, hivTestAtMaternityResults); | ||
}, | ||
}, | ||
{ | ||
key: 'placeOfDelivery', | ||
header: t('placeOfDelivery', 'Place of Delivery'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, placeOfDeliveryConcept); | ||
}, | ||
}, | ||
{ | ||
key: 'actions', | ||
header: t('actions', 'Actions'), | ||
getValue: (encounter) => [ | ||
{ | ||
form: { name: 'Labour & Delivery Form', package: 'maternal_health' }, | ||
encounterUuid: encounter.uuid, | ||
intent: '*', | ||
label: t('editForm', 'Edit Form'), | ||
mode: 'edit', | ||
}, | ||
], | ||
}, | ||
], | ||
[t], | ||
); | ||
|
||
return ( | ||
<EncounterList | ||
patientUuid={patientUuid} | ||
encounterType={LNDEncounterTypeUUID} | ||
formList={[{ name: 'Labour & Delivery Form' }]} | ||
columns={columns} | ||
description={headerTitle} | ||
headerTitle={headerTitle} | ||
launchOptions={{ | ||
displayText: t('add', 'Add'), | ||
moduleName: 'MCH Clinical View', | ||
}} | ||
filter={(encounter) => { | ||
return encounter.form.uuid == LNDEncounterFormUUID; | ||
}} | ||
/> | ||
); | ||
}; | ||
|
||
export default LabourDeliveryList; |
119 changes: 119 additions & 0 deletions
119
...clinical-view-app/src/esm-mch-app/views/maternal-health/tabs/postnatal-care.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import React, { useMemo } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { EncounterList, EncounterListColumn } from '../../../../encounter-list/encounter-list.component'; | ||
import { getObsFromEncounter } from '../../../../encounter-list/encounter-list-utils'; | ||
import { | ||
hivTestResultConcept, | ||
artUniqueNoConcept, | ||
hivTestStatus, | ||
MotherHivStatus, | ||
MotherNextVisitDate, | ||
motherPostnatalEncounterType, | ||
MotherViralLoadDate, | ||
MotherViralLoadResult, | ||
MothervisitDate, | ||
ancVisitNumberConcept, | ||
recenctViralLoad, | ||
motherGeneralConditionConcept, | ||
pphConditionConcept, | ||
} from '../../../constants'; | ||
import { useConfig, formatDate, parseDate } from '@openmrs/esm-framework'; | ||
import { ConfigObject } from '../../../../config-schema'; | ||
|
||
interface PostnatalCareListProps { | ||
patientUuid: string; | ||
} | ||
|
||
const PostnatalCareList: React.FC<PostnatalCareListProps> = ({ patientUuid }) => { | ||
const { t } = useTranslation(); | ||
const headerTitle = t('postnatalCare', 'Postnatal Care'); | ||
|
||
const { | ||
encounterTypes: { mchMotherConsultation }, | ||
formsList: { postnatal }, | ||
} = useConfig<ConfigObject>(); | ||
|
||
const MotherPNCEncounterTypeUUID = mchMotherConsultation; | ||
const MotherPNCEncounterFormUUID = postnatal; | ||
|
||
const columns: EncounterListColumn[] = useMemo( | ||
() => [ | ||
{ | ||
key: 'visitDate', | ||
header: t('visitDate', 'Visit Date'), | ||
getValue: (encounter) => { | ||
return formatDate(parseDate(encounter.encounterDatetime)); | ||
}, | ||
}, | ||
{ | ||
key: 'hivTestResults', | ||
header: t('hivTestResults', 'HIV Status'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, hivTestResultConcept); | ||
}, | ||
}, | ||
{ | ||
key: 'motherGeneralCondition', | ||
header: t('motherGeneralCondition', 'General condition'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, motherGeneralConditionConcept, true); | ||
}, | ||
}, | ||
{ | ||
key: 'pphCondition', | ||
header: t('pphCondition', 'PPH present'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, pphConditionConcept); | ||
}, | ||
}, | ||
{ | ||
key: 'uterusCondition', | ||
header: t('uterusCondition', 'PPH Condition of uterus'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, pphConditionConcept); | ||
}, | ||
}, | ||
{ | ||
key: 'nextVisitDate', | ||
header: t('nextVisitDate', 'Next visit date'), | ||
getValue: (encounter) => { | ||
return getObsFromEncounter(encounter, MotherNextVisitDate, true); | ||
}, | ||
}, | ||
{ | ||
key: 'actions', | ||
header: t('actions', 'Actions'), | ||
getValue: (encounter) => [ | ||
{ | ||
form: { name: 'Mother - Postnatal Form', package: 'maternal_health' }, | ||
encounterUuid: encounter.uuid, | ||
intent: '*', | ||
label: t('editForm', 'Edit Form'), | ||
mode: 'edit', | ||
}, | ||
], | ||
}, | ||
], | ||
[t], | ||
); | ||
|
||
return ( | ||
<EncounterList | ||
patientUuid={patientUuid} | ||
encounterType={MotherPNCEncounterTypeUUID} | ||
formList={[{ name: 'Mother - Postnatal Form' }]} | ||
columns={columns} | ||
description={headerTitle} | ||
headerTitle={headerTitle} | ||
launchOptions={{ | ||
displayText: t('add', 'Add'), | ||
moduleName: 'MCH Clinical View', | ||
}} | ||
filter={(encounter) => { | ||
return encounter.form.uuid == MotherPNCEncounterFormUUID; | ||
}} | ||
/> | ||
); | ||
}; | ||
|
||
export default PostnatalCareList; |
61 changes: 61 additions & 0 deletions
61
packages/esm-patient-clinical-view-app/src/hooks/useEncounterRows.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import useSWRImmutable, { mutate } from 'swr'; | ||
import { useCallback, useEffect, useMemo, useState } from 'react'; | ||
import { openmrsFetch } from '@openmrs/esm-framework'; | ||
import { encounterRepresentation } from '../esm-mch-app/constants'; | ||
|
||
export interface OpenmrsResource { | ||
uuid: string; | ||
display?: string; | ||
[anythingElse: string]: any; | ||
} | ||
|
||
export interface OpenmrsEncounter extends OpenmrsResource { | ||
encounterDatetime: Date; | ||
encounterType: string; | ||
patient: string; | ||
location: string; | ||
encounterProviders?: Array<{ encounterRole: string; provider: string }>; | ||
obs: Array<OpenmrsResource>; | ||
form?: string; | ||
visit?: string; | ||
} | ||
|
||
export function useEncounterRows(patientUuid: string, encounterType: string, encounterFilter: (encounter) => boolean) { | ||
const [encounters, setEncounters] = useState([]); | ||
const url = useMemo( | ||
() => `/ws/rest/v1/encounter?encounterType=${encounterType}&patient=${patientUuid}&v=${encounterRepresentation}`, | ||
[encounterType, patientUuid], | ||
); | ||
|
||
const { | ||
data: response, | ||
error, | ||
isLoading, | ||
} = useSWRImmutable<{ data: { results: OpenmrsEncounter[] } }, Error>(url, openmrsFetch); | ||
|
||
useEffect(() => { | ||
if (response) { | ||
// sort the encounters | ||
response.data.results.sort( | ||
(a, b) => new Date(b.encounterDatetime).getTime() - new Date(a.encounterDatetime).getTime(), | ||
); | ||
// apply filter | ||
if (encounterFilter) { | ||
setEncounters(response.data.results.filter((encounter) => encounterFilter(encounter))); | ||
} else { | ||
setEncounters([...response.data.results]); | ||
} | ||
} | ||
}, [encounterFilter, response]); | ||
|
||
const onFormSave = useCallback(() => { | ||
mutate(url); | ||
}, [url]); | ||
|
||
return { | ||
encounters, | ||
isLoading, | ||
error, | ||
onFormSave, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
@use '@carbon/styles/scss/spacing'; | ||
@use '@carbon/styles/scss/type'; | ||
@import "~@openmrs/esm-styleguide/src/vars"; | ||
|
||
$ui-01: #f4f4f4; | ||
$ui-02: #ffffff; | ||
$ui-03: #e0e0e0; | ||
$ui-05: #161616; | ||
$ui-background: #ffffff; | ||
$color-gray-70: #525252; | ||
$color-blue-60-2: #0f62fe; | ||
$color-yellow-50: #feecae; | ||
$inverse-support-03: #f1c21b; | ||
$warning-background: #fff8e1; | ||
$openmrs-background-grey: #f4f4f4; | ||
$danger: #da1e28; | ||
$interactive-01: #0f62fe; | ||
$brand-teal-01: #3197D9; | ||
$ohri-input-width: 22.313rem; | ||
$ohri-home-background: #ededed; | ||
$button-primary: #0078A6; | ||
|
||
.productiveHeading01 { | ||
@include type.type-style("heading-compact-01"); | ||
} | ||
|
||
.productiveHeading02 { | ||
@include type.type-style("heading-compact-02"); | ||
} | ||
|
||
.productiveHeading03 { | ||
@include type.type-style("heading-03"); | ||
} | ||
|
||
.productiveHeading04 { | ||
@include type.type-style("heading-04"); | ||
} | ||
|
||
.productiveHeading05 { | ||
@include type.type-style("heading-05"); | ||
} | ||
|
||
.productiveHeading06 { | ||
@include type.type-style("heading-06"); | ||
} | ||
|
||
.bodyShort01 { | ||
@include type.type-style("body-compact-01"); | ||
} | ||
|
||
.helperText01 { | ||
@include type.type-style("helper-text-01"); | ||
} | ||
|
||
.bodyShort02 { | ||
@include type.type-style("body-compact-02"); | ||
} | ||
|
||
.bodyLong01 { | ||
@include type.type-style("body-01"); | ||
} | ||
|
||
.bodyLong02 { | ||
@include type.type-style("body-02"); | ||
} | ||
|
||
.label01 { | ||
@include type.type-style("label-01"); | ||
} | ||
|
||
.text02 { | ||
color: $text-02; | ||
} | ||
|
||
aside { | ||
background-color: $ui-02 !important; | ||
} | ||
|
||
// Login Overrides | ||
|
||
div[class*='-esm-login__styles__center'] > img { | ||
width: 140px; // design has 120px | ||
} | ||
|
||
:global(.tab-12rem) > button { | ||
width: 12rem !important; | ||
} | ||
|
||
:global(.tab-14rem) > button { | ||
width: 14rem !important; | ||
} | ||
|
||
:global(.tab-16rem) > button { | ||
width: 16rem !important; | ||
} | ||
|
||
:global(.cds--overflow-menu) > div { | ||
width: 15rem !important; | ||
} | ||
|
||
nav :global(.cds--accordion__title) { | ||
color: #525252; | ||
font-weight: 600 !important; | ||
} | ||
|
||
nav :global(.cds--accordion__content) { | ||
padding-bottom: 0 !important; | ||
padding-top: 0 !important; | ||
} | ||
|
||
nav :global(.cds--accordion__content)>a { | ||
background-color: #cecece !important; | ||
color: #161616 !important; | ||
border-left-color: var(--brand-01) !important; | ||
font: bolder; | ||
} | ||
|
||
:global(.cds--tab--list) button { | ||
max-width: 16rem !important; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { OpenmrsResource } from '@openmrs/esm-framework'; | ||
|
||
export interface LocationData { | ||
display: string; | ||
uuid: string; | ||
} | ||
|
||
type Links = Array<{ | ||
rel: string; | ||
uri: string; | ||
}>; | ||
|
||
export interface OpenmrsEncounter extends OpenmrsResource { | ||
encounterDatetime: Date; | ||
encounterType: string; | ||
patient: string; | ||
location: string; | ||
encounterProviders?: Array<{ encounterRole: string; provider: string }>; | ||
obs: Array<OpenmrsResource>; | ||
form?: string; | ||
visit?: string; | ||
} | ||
|
||
export interface Concept { | ||
uuid: string; | ||
display: string; | ||
answers?: Concept[]; | ||
} | ||
|
||
export interface Observation { | ||
uuid: string; | ||
concept: { | ||
uuid: string; | ||
display: string; | ||
conceptClass: { | ||
uuid: string; | ||
display: string; | ||
}; | ||
}; | ||
display: string; | ||
groupMembers: null | Array<{ | ||
uuid: string; | ||
concept: { | ||
uuid: string; | ||
display: string; | ||
}; | ||
value: { | ||
uuid: string; | ||
display: string; | ||
}; | ||
display: string; | ||
}>; | ||
value: any; | ||
obsDatetime?: string; | ||
} |