Skip to content

Commit 73e4092

Browse files
committed
OHRI-2295 Update OHRI endpoints to use SWR
1 parent 41f1f66 commit 73e4092

11 files changed

+268
-373
lines changed

packages/esm-commons-lib/src/api.resource.ts

+26-33
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
import { openmrsFetch } from '@openmrs/esm-framework';
22
import dayjs from 'dayjs';
3-
import {
4-
finalHIVCodeConcept,
5-
finalPositiveHIVValueConcept,
6-
computedHIV_StatusConcept,
7-
encounterRepresentation,
8-
covidOutcomesCohortUUID,
9-
} from './constants';
3+
import { BASE_FHIR_API_URL, BASE_WS_API_URL, encounterRepresentation } from './constants';
104
import useSWR from 'swr';
11-
12-
const BASE_WS_API_URL = '/ws/rest/v1/';
13-
const BASE_FHIR_API_URL = '/ws/fhir2/R4/';
5+
import { configSchema } from './config-schema';
146

157
export function fetchLastVisit(uuid: string) {
168
return openmrsFetch(`/ws/fhir2/R4/Encounter?patient=${uuid}&_sort=-date&_count=1`);
@@ -145,7 +137,7 @@ export async function getPatientListsForPatient(patientUuid: string) {
145137

146138
export function fetchPatientsFinalHIVStatus(patientUUID: string) {
147139
return openmrsFetch(
148-
`/ws/fhir2/R4/Observation?code=${finalHIVCodeConcept}&value-concept=${finalPositiveHIVValueConcept}&patient=${patientUUID}&_sort=-date&_count=1`,
140+
`/ws/fhir2/R4/Observation?code=${configSchema.obsConcepts._default.finalHIVCodeConcept}&value-concept=${configSchema.obsConcepts._default.finalPositiveHIVValueConcept}&patient=${patientUUID}&_sort=-date&_count=1`,
149141
).then(({ data }) => {
150142
if (data.entry?.length) {
151143
return data.entry[0].resource.valueCodeableConcept.coding[0].display;
@@ -166,7 +158,7 @@ export function fetchPatientObservationFromEncounter(
166158

167159
export function fetchPatientComputedConcept_HIV_Status(patientUUID: string) {
168160
return openmrsFetch(
169-
`/ws/fhir2/R4/Observation?code=${computedHIV_StatusConcept}&value-concept=${computedHIV_StatusConcept}&patient=${patientUUID}&_sort=-date&_count=1`,
161+
`/ws/fhir2/R4/Observation?code=${configSchema.obsConcepts._default.computedHIV_StatusConcept}&value-concept=${configSchema.obsConcepts._default.computedHIV_StatusConcept}&patient=${patientUUID}&_sort=-date&_count=1`,
170162
).then(({ data }) => {
171163
if (data.entry?.length) {
172164
return data.entry[0].resource.valueCodeableConcept.coding[0].display;
@@ -191,21 +183,23 @@ export function fetchPatientLastEncounter(patientUuid: string, encounterType) {
191183
}
192184

193185
export function fetchPatientCovidOutcome() {
194-
return openmrsFetch(`/ws/rest/v1/reportingrest/cohort/${covidOutcomesCohortUUID}`).then(({ data }) => {
195-
if (data.members?.length) {
196-
let patientRefs = data.members.map((member) => {
197-
return member.uuid;
198-
});
199-
patientRefs = new Set([...patientRefs]);
200-
patientRefs = Array.from(patientRefs);
201-
return Promise.all(
202-
patientRefs.map((ref) => {
203-
return openmrsFetch(BASE_FHIR_API_URL + '/Person/' + ref);
204-
}),
205-
);
206-
}
207-
return [];
208-
});
186+
return openmrsFetch(`/ws/rest/v1/reportingrest/cohort/${configSchema.obsConcepts._default.covidOutcomesCohortUUID}`).then(
187+
({ data }) => {
188+
if (data.members?.length) {
189+
let patientRefs = data.members.map((member) => {
190+
return member.uuid;
191+
});
192+
patientRefs = new Set([...patientRefs]);
193+
patientRefs = Array.from(patientRefs);
194+
return Promise.all(
195+
patientRefs.map((ref) => {
196+
return openmrsFetch(BASE_FHIR_API_URL + '/Person/' + ref);
197+
}),
198+
);
199+
}
200+
return [];
201+
},
202+
);
209203
}
210204

211205
export function fetchConceptNameByUuid(conceptUuid: string) {
@@ -358,12 +352,11 @@ export async function getCohortList(
358352
`/ws/rest/v1/encounter?encounterType=${encounterType}&patient=${member.patient.uuid}&v=${encounterRepresentation}`,
359353
).then(({ data }) => {
360354
if (data.results.length) {
361-
const sortedEncounters = data.results
362-
.sort(
363-
(firstEncounter, secondEncounter) =>
364-
new Date(secondEncounter.encounterDatetime).getTime() -
365-
new Date(firstEncounter.encounterDatetime).getTime(),
366-
);
355+
const sortedEncounters = data.results.sort(
356+
(firstEncounter, secondEncounter) =>
357+
new Date(secondEncounter.encounterDatetime).getTime() -
358+
new Date(firstEncounter.encounterDatetime).getTime(),
359+
);
367360
return sortedEncounters[0];
368361
}
369362
return null;

packages/esm-commons-lib/src/components/banner-tags/.patient-status-tag.test.tsx

-36
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React from 'react';
22
import { Tag } from '@carbon/react';
33
import { useTranslation } from 'react-i18next';
4-
import { isPatientHivPositive } from './patientHivStatus';
4+
import { usePatientsFinalHIVStatus } from './usePatientHivStatus';
5+
import { useConfig } from '@openmrs/esm-framework';
56

67
export function PatientStatusBannerTag({ patientUuid }) {
78
const { t } = useTranslation();
8-
const [hivPositive, setHivPositive] = useState(false);
9+
const { obsConcepts } = useConfig();
10+
const { isLoading, hivStatus, error } = usePatientsFinalHIVStatus(
11+
patientUuid,
12+
obsConcepts.finalHIVCodeConcept,
13+
obsConcepts.finalPositiveHIVValueConcept,
14+
);
915

10-
useEffect(() => {
11-
isPatientHivPositive(patientUuid).then((result) => setHivPositive(result));
12-
}, [hivPositive, patientUuid]);
16+
if (isLoading) {
17+
return <p>{t('loading', 'Loading...')}</p>;
18+
}
1319

14-
//TODO: Improve refresh time
15-
// forceRerender();
20+
if (error) {
21+
return <p>{t('error', 'Error...')}</p>;
22+
}
1623

17-
return <>{hivPositive && <Tag type="red">{t('hivPositive', 'HIV Positive')}</Tag>}</>;
24+
return <>{hivStatus && <Tag type="red">{t('hivPositive', 'HIV Positive')}</Tag>}</>;
1825
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
4+
import { PatientStatusBannerTag } from './patient-status-tag.component';
5+
import { usePatientsFinalHIVStatus } from './usePatientHivStatus';
6+
7+
const mockedUsePatientsFinalHIVStatus = jest.mocked(usePatientsFinalHIVStatus);
8+
9+
jest.mock('./usePatientHivStatus', () => {
10+
const originalModule = jest.requireActual('./usePatientHivStatus');
11+
12+
return {
13+
...originalModule,
14+
usePatientsFinalHIVStatus: jest.fn().mockImplementation(() => ({
15+
hivStatus: true,
16+
isLoading: false,
17+
})),
18+
};
19+
});
20+
21+
describe('PatientStatusBannerTag', () => {
22+
beforeEach(() => {
23+
jest.clearAllMocks();
24+
});
25+
26+
const samplePatientUuid = '703AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
27+
28+
it('renders red tag when patient is HIV positive', async () => {
29+
render(<PatientStatusBannerTag patientUuid={samplePatientUuid} />);
30+
expect(screen.getByText(/HIV Positive/i)).toBeInTheDocument();
31+
});
32+
33+
it('does not render red tag when patient is not HIV positive', async () => {
34+
mockedUsePatientsFinalHIVStatus.mockReturnValue({ hivStatus: false, isLoading: false, error: null });
35+
render(<PatientStatusBannerTag patientUuid={samplePatientUuid} />);
36+
expect(screen.queryByText('HIV Positive')).not.toBeInTheDocument();
37+
});
38+
});

packages/esm-commons-lib/src/components/banner-tags/patientHivStatus.ts

-44
This file was deleted.

packages/esm-commons-lib/src/components/banner-tags/patientStatus.test.ts

-16
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { fhirBaseUrl, openmrsFetch } from '@openmrs/esm-framework';
2+
import useSWR from 'swr';
3+
4+
export function usePatientsFinalHIVStatus(
5+
patientUuid: string,
6+
finalHIVCodeConcept: string,
7+
finalPositiveHIVValueConcept: string,
8+
) {
9+
const url = `${fhirBaseUrl}/Observation?code=${finalHIVCodeConcept}&value-concept=${finalPositiveHIVValueConcept}&patient=${patientUuid}&_sort=-date&_count=1`;
10+
const { data, error, isLoading, mutate } = useSWR<{ data: any }, Error>(url, openmrsFetch);
11+
12+
const hivStatusResult = data?.data?.entry[0].resource.valueCodeableConcept.coding[0].display;
13+
const hivStatus = hivStatusResult.toLowerCase().includes('positive') ? true : false;
14+
15+
return { hivStatus: hivStatus, isLoading: isLoading, error: error };
16+
}

packages/esm-commons-lib/src/components/cohort-patient-list/helpers.tsx

+27-35
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import dayjs from 'dayjs';
55
import localizedFormat from 'dayjs/plugin/localizedFormat';
66
import relativeTime from 'dayjs/plugin/relativeTime';
77
import { AddPatientToListOverflowMenuItem } from '../modals/add-patient-to-list-modal.component';
8-
import { fetchPatientLastEncounter } from '../../api.resource';
98
import { launchForm } from '../../utils/ohri-forms-commons';
109
import { navigate, WorkspaceContainer } from '@openmrs/esm-framework';
10+
import { useLastEncounter } from '../../hooks/useLastEncounter';
1111

1212
interface PatientMetaConfig {
1313
location: { name: string };
@@ -38,23 +38,25 @@ export const LaunchableFormMenuItem = ({
3838
}) => {
3939
const [actionText, setActionText] = useState(launchableForm.actionText);
4040
const [encounterUuid, setEncounterUuid] = useState(null);
41-
const [isLoading, setIsLoading] = useState(false);
4241
const continueEncounterActionText = launchableForm.actionText || 'Continue encounter ';
42+
const { lastEncounter, isLoading } = useLastEncounter(patientUuid, encounterType);
4343

4444
useEffect(() => {
4545
if (launchableForm.editLatestEncounter && encounterType && !encounterUuid) {
46-
setIsLoading(true);
47-
fetchPatientLastEncounter(patientUuid, encounterType).then((latestEncounter) => {
48-
if (latestEncounter) {
49-
setActionText(continueEncounterActionText);
50-
setEncounterUuid(latestEncounter.uuid);
51-
}
52-
setIsLoading(false);
53-
});
54-
} else {
55-
setIsLoading(false);
46+
if (!isLoading && lastEncounter) {
47+
setActionText(continueEncounterActionText);
48+
setEncounterUuid(lastEncounter.uuid);
49+
}
5650
}
57-
}, [continueEncounterActionText, encounterType, encounterUuid, launchableForm.editLatestEncounter, patientUuid]);
51+
}, [
52+
continueEncounterActionText,
53+
encounterType,
54+
encounterUuid,
55+
launchableForm.editLatestEncounter,
56+
patientUuid,
57+
lastEncounter,
58+
isLoading,
59+
]);
5860

5961
return (
6062
<>
@@ -79,21 +81,15 @@ export const LaunchableFormMenuItem = ({
7981
export const ViewSummaryMenuItem = ({ patientUuid, ViewSummary, encounterType }) => {
8082
const [actionText, setActionText] = useState(ViewSummary.actionText);
8183
const [encounterUuid, setEncounterUuid] = useState(null);
82-
const [isLoading, setIsLoading] = useState(false);
8384
const viewSummaryActionText = ViewSummary.actionText || 'View Summary ';
85+
const { lastEncounter, isLoading } = useLastEncounter(patientUuid, encounterType);
8486

8587
useEffect(() => {
8688
if (ViewSummary.editLatestEncounter && encounterType && !encounterUuid) {
87-
setIsLoading(true);
88-
fetchPatientLastEncounter(patientUuid, encounterType).then((latestEncounter) => {
89-
if (latestEncounter) {
90-
setActionText(viewSummaryActionText);
91-
setEncounterUuid(latestEncounter.uuid);
92-
}
93-
setIsLoading(false);
94-
});
95-
} else {
96-
setIsLoading(false);
89+
if (!isLoading && lastEncounter) {
90+
setActionText(viewSummaryActionText);
91+
setEncounterUuid(lastEncounter.uuid);
92+
}
9793
}
9894
}, [ViewSummary.editLatestEncounter, encounterType, encounterUuid, patientUuid, viewSummaryActionText]);
9995

@@ -114,24 +110,19 @@ export const ViewSummaryMenuItem = ({ patientUuid, ViewSummary, encounterType })
114110
</>
115111
);
116112
};
113+
117114
export const ViewTptSummaryMenuItem = ({ patientUuid, ViewTptSummary, encounterType }) => {
118115
const [actionText, setActionText] = useState(ViewTptSummary.actionText);
119116
const [encounterUuid, setEncounterUuid] = useState(null);
120-
const [isLoading, setIsLoading] = useState(false);
121117
const viewTptSummaryActionText = ViewTptSummary.actionText || 'View Summary ';
118+
const { lastEncounter, isLoading } = useLastEncounter(patientUuid, encounterType);
122119

123120
useEffect(() => {
124121
if (ViewTptSummary.editLatestEncounter && encounterType && !encounterUuid) {
125-
setIsLoading(true);
126-
fetchPatientLastEncounter(patientUuid, encounterType).then((latestEncounter) => {
127-
if (latestEncounter) {
128-
setActionText(viewTptSummaryActionText);
129-
setEncounterUuid(latestEncounter.uuid);
130-
}
131-
setIsLoading(false);
132-
});
133-
} else {
134-
setIsLoading(false);
122+
if (!isLoading && lastEncounter) {
123+
setActionText(viewTptSummaryActionText);
124+
setEncounterUuid(lastEncounter.uuid);
125+
}
135126
}
136127
}, [ViewTptSummary.editLatestEncounter, encounterType, patientUuid, encounterUuid, viewTptSummaryActionText]);
137128

@@ -152,6 +143,7 @@ export const ViewTptSummaryMenuItem = ({ patientUuid, ViewTptSummary, encounterT
152143
</>
153144
);
154145
};
146+
155147
export function consolidatatePatientMeta(rawPatientMeta, form, config: PatientMetaConfig) {
156148
const {
157149
isDynamicCohort,

0 commit comments

Comments
 (0)