Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) O3-3773 Support for Patient Program Summary in Patient Chart #2203

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import React, { useCallback, useMemo, useState } from 'react';
import { navigate, showModal, showSnackbar, type Visit } from '@openmrs/esm-framework';
import { EmptyState } from '@openmrs/esm-patient-common-lib';
import { useTranslation } from 'react-i18next';
import { EncounterListDataTable } from './table.component';
import { Button, Link, OverflowMenu, OverflowMenuItem, DataTableSkeleton, Pagination } from '@carbon/react';
import { Add } from '@carbon/react/icons';
import { AddIcon, navigate, showModal, showSnackbar, type Visit } from '@openmrs/esm-framework';
import { EmptyState } from '@openmrs/esm-patient-common-lib';
import { EncounterListDataTable } from './table.component';
import { launchEncounterForm } from '../utils/helpers';
import { deleteEncounter } from '../encounter-list.resource';
import { deleteEncounter } from '../utils/encounter-list.resource';
import { useEncounterRows, useFormsJson } from '../hooks';

import type { TableRow, Encounter, Mode, ColumnValue, FormattedColumn } from '../types';
import styles from './encounter-list.scss';
import { type TableRow, type Encounter, type Mode, type ColumnValue } from '../types';
import { type FormattedColumn } from '../utils/encounter-list-config-builder';


export interface EncounterListColumn {
key: string;
Expand Down Expand Up @@ -243,7 +241,7 @@ export const EncounterList: React.FC<EncounterListProps> = ({
return (
<Button
kind="ghost"
renderIcon={Add}
renderIcon={AddIcon}
iconDescription="Add"
onClick={(e) => {
e.preventDefault();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const renderTag = (
statusColorMappings: Record<string, string>,
config: ConfigConcepts,
) => {
const columnStatus = getObsFromEncounter(encounter, concept, false, false, undefined, undefined, undefined, config);
const columnStatus = getObsFromEncounter({ encounter: encounter, obsConcept: concept, config: config });
const columnStatusObs = findObs(encounter, concept);

if (columnStatus == '--') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { memo, useMemo } from 'react';

import { useTranslation } from 'react-i18next';
import { useConfig } from '@openmrs/esm-framework';
import { getEncounterTileColumns } from '../utils';
import { EncounterTile } from './encounter-tile.component';
import { type MenuCardProps } from '../types';

interface OverviewListProps {
patientUuid: string;
}

const ClinicalViewsSummary: React.FC<OverviewListProps> = memo(({ patientUuid }) => {
const { tileDefinitions, trueConceptUuid, falseConceptUuid, otherConceptUuid } = useConfig();

const { t } = useTranslation();

const tilesData = useMemo(() => {
const configConcepts = {
trueConceptUuid,
falseConceptUuid,
otherConceptUuid,
};

return tileDefinitions?.map((tile: MenuCardProps) => ({
title: t(tile.tileHeader),
columns: getEncounterTileColumns(tile, configConcepts),
}));
}, [tileDefinitions, t, trueConceptUuid, falseConceptUuid, otherConceptUuid]);

return tilesData?.length > 0 ? (
<>
{tilesData.map((tile, index) => (
<EncounterTile key={index} patientUuid={patientUuid} columns={tile.columns} headerTitle={tile.title} />
))}
</>
) : null;
});

export default ClinicalViewsSummary;
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { memo, useMemo } from 'react';
import { useLayoutType } from '@openmrs/esm-framework';
import { useTranslation } from 'react-i18next';
import { CodeSnippetSkeleton, Tile, Column, Layer } from '@carbon/react';
import { groupColumnsByEncounterType } from '../utils/helpers';
import { useLastEncounter } from '../hooks';
import type { EncounterTileColumn, EncounterTileProps } from '../types';
import styles from './tile.scss';

export const EncounterTile = memo(({ patientUuid, columns, headerTitle }: EncounterTileProps) => {
const columnsByEncounterType = useMemo(() => groupColumnsByEncounterType(columns), [columns]);
const isTablet = useLayoutType() === 'tablet';

return (
<Layer className={styles.layer}>
<Tile className={styles.tile}>
<div className={isTablet ? styles.tabletHeading : styles.desktopHeading}>
<h4 className={styles.title}>{headerTitle}</h4>
</div>
<Column className={styles.columnContainer}>
{Object.entries(columnsByEncounterType).map(([encounterTypeUuid, columns]) => (
<EncounterData
key={encounterTypeUuid}
patientUuid={patientUuid}
encounterType={encounterTypeUuid}
columns={columns}
/>
))}
</Column>
</Tile>
</Layer>
);
});

const EncounterData: React.FC<{
patientUuid: string;
encounterType: string;
columns: EncounterTileColumn[];
}> = ({ patientUuid, encounterType, columns }) => {
const { lastEncounter, isLoading, error, isValidating } = useLastEncounter(patientUuid, encounterType);
const { t } = useTranslation();

if (isLoading || isValidating) {
return <CodeSnippetSkeleton type="multi" role="progressbar" />;
}

if (error || lastEncounter == undefined) {
return (
<div className={styles.tileBox}>
{columns.map((column, ind) => (
<div key={ind}>
<span className={styles.tileTitle}>{t(column.title)}</span>
<span className={styles.tileValue}>{error?.message}</span>
</div>
))}
</div>
);
}

return (
<div className={styles.tileBox}>
{columns.map((column, ind) => {
return (
<div key={ind}>
<span className={styles.tileTitle}>{column.header}</span>
<span className={styles.tileValue}>
<p>{column.getObsValue(lastEncounter)}</p>
</span>
{column.hasSummary && (
<>
<span className={styles.tileValue}>
<p>{column.getSummaryObsValue(lastEncounter)}</p>
</span>
<span className={styles.tileValue}>
<p>{column.getObsValue(lastEncounter)}</p>
</span>
</>
)}
</div>
);
})}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
@use '@carbon/layout';
@use '@carbon/type';
@use '@openmrs/esm-styleguide/src/vars' as *;

.title {
@include type.type-style('productive-heading-03');
}

.title:after {
content: '';
display: block;
width: layout.$spacing-07;
padding-top: layout.$spacing-01;
border-bottom: layout.$spacing-03 solid $brand-teal-01;
}

.tileTitle {
@include type.type-style('body-long-01');
display: block;
padding-top: layout.$spacing-05;
}

.columnContainer {
display: flex;
flex-direction: row;
}

.tileSubTitle {
@include type.type-style('body-compact-01');
}

.tileValue {
@include type.type-style('label-01');
margin: layout.$spacing-04 0 layout.$spacing-04;
display: block;
}

.tileBox {
display: flex;
gap: layout.$spacing-05;
}

.desktopHeading {
h4 {
@include type.type-style('heading-compact-02');
color: $text-02;
}
}

.tabletHeading {
h4 {
@include type.type-style('heading-03');
color: $text-02;
}
}

.desktopHeading,
.tabletHeading {
text-align: left;
text-transform: capitalize;
margin-bottom: layout.$spacing-05;

h4:after {
content: '';
display: block;
width: layout.$spacing-07;
padding-top: layout.$spacing-01;
border-bottom: 0.375rem solid var(--brand-03);
}
}

.heading:after {
content: '';
display: block;
width: layout.$spacing-07;
padding-top: layout.$spacing-01;
border-bottom: 0.375rem solid var(--brand-03);
}

.tile {
border: 1px solid $ui-03;
margin-bottom: layout.$spacing-03;
}

[data-extension-id='clinical-views-summary'] {
height: auto;
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './useEncounterRows';
export * from './useFormsJson';
export * from './useLastEncounter';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { openmrsFetch } from '@openmrs/esm-framework';
import { type Encounter } from '../types';
import useSWR from 'swr';

const encounterRepresentation =
'custom:(uuid,encounterDatetime,encounterType,location:(uuid,name),' +
'patient:(uuid,display,age,identifiers,person),encounterProviders:(uuid,provider:(uuid,name)),' +
'obs:(uuid,obsDatetime,voided,groupMembers,concept:(uuid,display,name:(uuid,name)),value:(uuid,name:(uuid,name,display),' +
'names:(uuid,conceptNameType,name,display))),form:(uuid,name))';

export function useLastEncounter(patientUuid: string, encounterType: string) {
const query = `encounterType=${encounterType}&patient=${patientUuid}`;
const endpointUrl =
patientUuid && encounterType
? `/ws/rest/v1/encounter?${query}&v=${encounterRepresentation}&limit=1&order=desc&startIndex=0`
: null;

const { data, error, isLoading, isValidating } = useSWR<{ data: { results: Array<Encounter> } }, Error>(
endpointUrl,
openmrsFetch,
);

return {
lastEncounter: data?.data.results?.length > 0 ? data?.data.results[0] : null,
error,
isLoading,
isValidating,
};
}
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy