Skip to content

Commit

Permalink
(Fix) Fix the Encounters pagination by leveraging the backend (#1926)
Browse files Browse the repository at this point in the history
* paginate encounters on the backend

* fix CI

* fix the tests

* remove unused import
  • Loading branch information
hadijahkyampeire authored Sep 23, 2024
1 parent 6bea80e commit 72c37cc
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 63 deletions.
2 changes: 1 addition & 1 deletion packages/esm-commons-lib/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export interface Location {

export interface OpenmrsEncounter extends OpenmrsResource {
encounterDatetime: Date;
encounterType: string;
encounterType: { name: string; uuid: string };
patient: string;
location: string;
encounterProviders?: Array<{ encounterRole: string; provider: string }>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { EmptyState } from '../empty-state/empty-state.component';
import { FormLauncherWithIntent } from '../ohri-form-launcher/ohri-form-launcher.component';
import styles from './encounter-list.scss';
import { OTable } from '../data-table/o-table.component';
import { Button, Link, OverflowMenu, OverflowMenuItem, Pagination, DataTableSkeleton } from '@carbon/react';
import { Add } from '@carbon/react/icons';
Expand All @@ -14,6 +13,8 @@ import { OpenmrsEncounter } from '../../api/types';
import { useFormsJson } from '../../hooks/useFormsJson';
import { usePatientDeathStatus } from '../../hooks/usePatientDeathStatus';

import styles from './encounter-list.scss';

export interface EncounterListColumn {
key: string;
header: string;
Expand Down Expand Up @@ -64,12 +65,15 @@ export const EncounterList: React.FC<EncounterListProps> = ({
const { isDead } = usePatientDeathStatus(patientUuid);
const formNames = useMemo(() => formList.map((form) => form.name), []);
const { formsJson, isLoading: isLoadingFormsJson } = useFormsJson(formNames);
const { encounters, isLoading, onFormSave } = useEncounterRows(
const { encounters, total, isLoading, onFormSave } = useEncounterRows(
patientUuid,
encounterType,
filter,
afterFormSaveAction,
pageSize,
currentPage,
);

const { moduleName, workspaceWindowSize, displayText, hideFormLauncher } = launchOptions;

const defaultActions = useMemo(
Expand Down Expand Up @@ -102,38 +106,41 @@ export const EncounterList: React.FC<EncounterListProps> = ({
[forms, t],
);

const handleDeleteEncounter = useCallback((encounterUuid, encounterTypeName) => {
const close = showModal('delete-encounter-modal', {
close: () => close(),
encounterTypeName: encounterTypeName || '',
onConfirmation: () => {
const abortController = new AbortController();
deleteEncounter(encounterUuid, abortController)
.then(() => {
onFormSave();
showSnackbar({
isLowContrast: true,
title: t('encounterDeleted', 'Encounter deleted'),
subtitle: `Encounter ${t('successfullyDeleted', 'successfully deleted')}`,
kind: 'success',
const handleDeleteEncounter = useCallback(
(encounterUuid, encounterTypeName) => {
const close = showModal('delete-encounter-modal', {
close: () => close(),
encounterTypeName: encounterTypeName || '',
onConfirmation: () => {
const abortController = new AbortController();
deleteEncounter(encounterUuid, abortController)
.then(() => {
onFormSave();
showSnackbar({
isLowContrast: true,
title: t('encounterDeleted', 'Encounter deleted'),
subtitle: `Encounter ${t('successfullyDeleted', 'successfully deleted')}`,
kind: 'success',
});
})
.catch(() => {
showSnackbar({
isLowContrast: false,
title: t('error', 'Error'),
subtitle: `Encounter ${t('failedDeleting', "couldn't be deleted")}`,
kind: 'error',
});
});
})
.catch(() => {
showSnackbar({
isLowContrast: false,
title: t('error', 'Error'),
subtitle: `Encounter ${t('failedDeleting', "couldn't be deleted")}`,
kind: 'error',
});
});

// Update encounters after deletion
const updatedEncounters = encounters.filter((enc) => enc.uuid !== encounterUuid);
constructPaginatedTableRows(updatedEncounters, currentPage, pageSize);
close();
},
});
}, []);
// Update encounters after deletion
const updatedEncounters = encounters.filter((enc) => enc.uuid !== encounterUuid);
constructTableRows(updatedEncounters);
close();
},
});
},
[t, onFormSave, encounters],
);

useEffect(() => {
if (!isLoadingFormsJson) {
Expand Down Expand Up @@ -167,18 +174,11 @@ export const EncounterList: React.FC<EncounterListProps> = ({
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 constructTableRows = useCallback(
(encounters: OpenmrsEncounter[]) => {
const rows = encounters.map((encounter) => {
const tableRow: { id: string; actions: any } = { id: encounter.uuid, actions: null };
// inject launch actions

encounter['launchFormActions'] = {
editEncounter: () =>
launchEncounterForm(
Expand All @@ -205,7 +205,7 @@ export const EncounterList: React.FC<EncounterListProps> = ({
patientUuid,
),
};
// process columns

columns.forEach((column) => {
let val = column?.getValue(encounter);
if (column.link) {
Expand All @@ -225,7 +225,7 @@ export const EncounterList: React.FC<EncounterListProps> = ({
}
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} data-testid="actions-id">
Expand Down Expand Up @@ -262,9 +262,9 @@ export const EncounterList: React.FC<EncounterListProps> = ({

useEffect(() => {
if (encounters?.length) {
constructPaginatedTableRows(encounters, currentPage, pageSize);
constructTableRows(encounters);
}
}, [encounters, pageSize, constructPaginatedTableRows, currentPage]);
}, [encounters, pageSize, constructTableRows, currentPage]);

const formLauncher = useMemo(() => {
if (forms.length == 1 && !forms[0]['availableIntents']?.length) {
Expand Down Expand Up @@ -317,7 +317,7 @@ export const EncounterList: React.FC<EncounterListProps> = ({
}, [forms, hideFormLauncher, isDead, displayText, moduleName, workspaceWindowSize, onFormSave, patientUuid]);

if (isLoading === true || isLoadingForms === true || isLoadingFormsJson === true) {
return <DataTableSkeleton rowCount={5} />;
return <DataTableSkeleton rowCount={10} />;
}

return (
Expand All @@ -335,7 +335,7 @@ export const EncounterList: React.FC<EncounterListProps> = ({
page={currentPage}
pageSize={pageSize}
pageSizes={[10, 20, 30, 40, 50]}
totalItems={encounters.length}
totalItems={total}
onChange={({ page, pageSize }) => {
setCurrentPage(page);
setPageSize(pageSize);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('EncounterList', () => {
test('renders an loading state if data is loading', () => {
jest
.spyOn(encounterRowsHook, 'useEncounterRows')
.mockReturnValue({ encounters: [], isLoading: true, error: null, onFormSave: () => {} });
.mockReturnValue({ encounters: [], total: 0, isLoading: true, error: null, onFormSave: () => {} });

jest.spyOn(formsJsonHook, 'useFormsJson').mockReturnValue({ formsJson: [], isLoading: true });

Expand All @@ -75,7 +75,7 @@ describe('EncounterList', () => {
test('renders an empty state if data is null', () => {
jest
.spyOn(encounterRowsHook, 'useEncounterRows')
.mockReturnValue({ encounters: [], isLoading: false, error: null, onFormSave: () => {} });
.mockReturnValue({ encounters: [], total: 0, isLoading: false, error: null, onFormSave: () => {} });

jest.spyOn(formsJsonHook, 'useFormsJson').mockReturnValue({ formsJson: [], isLoading: false });

Expand All @@ -90,6 +90,7 @@ describe('EncounterList', () => {
test('should render the encounter list component', () => {
jest.spyOn(encounterRowsHook, 'useEncounterRows').mockReturnValue({
encounters: mockEncounter,
total: 1,
isLoading: false,
error: null,
onFormSave: () => {},
Expand Down
24 changes: 14 additions & 10 deletions packages/esm-commons-lib/src/hooks/useEncounterRows.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
import useSWR from 'swr';
import { OpenmrsEncounter } from '../api/types';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { openmrsFetch } from '@openmrs/esm-framework';
import { encounterRepresentation } from '../constants';

interface EncounterResponse {
results: OpenmrsEncounter[];
totalCount?: number;
}

export function useEncounterRows(
patientUuid: string,
encounterType: string,
encounterFilter: (encounter) => boolean,
afterFormSaveAction: () => void,
pageSize?: number,
pageNumber?: number,
) {
const [encounters, setEncounters] = useState([]);
const url = `/ws/rest/v1/encounter?encounterType=${encounterType}&patient=${patientUuid}&v=${encounterRepresentation}`;
const startIndex = (pageNumber - 1) * pageSize;

const {
data: response,
error,
isLoading,
mutate,
} = useSWR<{ data: { results: OpenmrsEncounter[] } }, Error>(url, openmrsFetch);
const url = `/ws/rest/v1/encounter?encounterType=${encounterType}&patient=${patientUuid}&v=${encounterRepresentation}&totalCount=true&limit=${pageSize}&startIndex=${startIndex}`;

const { data: response, error, isLoading, mutate } = useSWR<{ data: EncounterResponse }, 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 {
Expand All @@ -42,6 +45,7 @@ export function useEncounterRows(

return {
encounters,
total: response?.data?.totalCount,
isLoading,
error,
onFormSave,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const HtsOverviewList: React.FC<HtsOverviewListProps> = ({ patientUuid }) => {
key: 'location',
header: t('location', 'Location'),
getValue: (encounter) => {
return encounter.location.name;
return encounter?.location?.name;
},
},
{
Expand Down

0 comments on commit 72c37cc

Please sign in to comment.