Skip to content

Commit

Permalink
(fix) Various fixes for the visit notes form
Browse files Browse the repository at this point in the history
  • Loading branch information
denniskigen committed Mar 19, 2024
1 parent 984087d commit 2615e8c
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import {
DatePickerInput,
Form,
FormGroup,
InlineNotification,
Layer,
Row,
Search,
SkeletonText,
Stack,
Tag,
TextArea,
Tile,
Layer,
} from '@carbon/react';
import { Add, Edit, WarningFilled } from '@carbon/react/icons';
import {
Expand All @@ -40,7 +41,7 @@ import { type DefaultWorkspaceProps } from '@openmrs/esm-patient-common-lib';
import type { ConfigObject } from '../config-schema';
import type { Concept, Diagnosis, DiagnosisPayload, VisitNotePayload } from '../types';
import {
fetchConceptDiagnosisByName,
fetchDiagnosisConceptsByName,
savePatientDiagnosis,
saveVisitNote,
useInfiniteVisits,
Expand Down Expand Up @@ -95,6 +96,7 @@ const VisitNotesForm: React.FC<DefaultWorkspaceProps> = ({
const [searchSecondaryResults, setSearchSecondaryResults] = useState<Array<Concept>>([]);
const [combinedDiagnoses, setCombinedDiagnoses] = useState<Array<Diagnosis>>([]);
const [rows, setRows] = useState<number>();
const [error, setError] = useState<Error>(null);

const { control, handleSubmit, watch, getValues, setValue, formState } = useForm<VisitNotesFormData>({
mode: 'onSubmit',
Expand Down Expand Up @@ -135,21 +137,21 @@ const VisitNotesForm: React.FC<DefaultWorkspaceProps> = ({
} else if (fieldName === 'secondaryDiagnosisSearch') {
setLoadingSecondary(true);
}
const sub = fetchConceptDiagnosisByName(fieldQuery, config.diagnosisConceptClass).subscribe(
(matchingConceptDiagnoses: Array<Concept>) => {

fetchDiagnosisConceptsByName(fieldQuery, config.diagnosisConceptClass)
.then((matchingConceptDiagnoses: Array<Concept>) => {
if (fieldName == 'primaryDiagnosisSearch') {
setSearchPrimaryResults(matchingConceptDiagnoses);
setLoadingPrimary(false);
} else if (fieldName == 'secondaryDiagnosisSearch') {
setSearchSecondaryResults(matchingConceptDiagnoses);
setLoadingSecondary(false);
}
},
() => createErrorHandler(),
);
return () => {
sub.unsubscribe();
};
})
.catch((e) => {
setError(e);
createErrorHandler();
});
}
}, searchTimeoutInMs),
[],
Expand Down Expand Up @@ -423,7 +425,18 @@ const VisitNotesForm: React.FC<DefaultWorkspaceProps> = ({
<div>
{(() => {
if (!getValues('primaryDiagnosisSearch')) return null;
if (loadingPrimary)
if (error) {
return (
<InlineNotification
className={styles.errorNotification}
lowContrast
title={t('error', 'Error')}
subtitle={t('errorFetchingConcepts', 'There was a problem fetching concepts') + '.'}
onClose={() => setError(null)}
/>
);
}
if (loadingPrimary) {
return (
<>
<SkeletonText className={styles.skeleton} />
Expand All @@ -433,6 +446,7 @@ const VisitNotesForm: React.FC<DefaultWorkspaceProps> = ({
<SkeletonText className={styles.skeleton} />
</>
);
}
if (!loadingPrimary && searchPrimaryResults && searchPrimaryResults.length > 0) {
return (
<ul className={styles.diagnosisList}>
Expand Down Expand Up @@ -495,7 +509,18 @@ const VisitNotesForm: React.FC<DefaultWorkspaceProps> = ({
<div>
{(() => {
if (!getValues('secondaryDiagnosisSearch')) return null;
if (loadingSecondary)
if (error) {
return (
<InlineNotification
className={styles.errorNotification}
lowContrast
title={t('error', 'Error')}
subtitle={t('errorFetchingConcepts', 'There was a problem fetching concepts') + '.'}
onClose={() => setError(null)}
/>
);
}
if (loadingSecondary) {
return (
<>
<SkeletonText className={styles.skeleton} />
Expand All @@ -505,7 +530,8 @@ const VisitNotesForm: React.FC<DefaultWorkspaceProps> = ({
<SkeletonText className={styles.skeleton} />
</>
);
if (!loadingSecondary && searchSecondaryResults && searchSecondaryResults.length > 0)
}
if (!loadingSecondary && searchSecondaryResults && searchSecondaryResults.length > 0) {
return (
<ul className={styles.diagnosisList}>
{searchSecondaryResults.map((diagnosis, index) => {
Expand All @@ -524,6 +550,7 @@ const VisitNotesForm: React.FC<DefaultWorkspaceProps> = ({
})}
</ul>
);
}
return (
<ResponsiveWrapper>
<Tile className={styles.emptyResults}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,7 @@
height: 2.5rem;
}
}

.errorNotification {
margin-top: 0.25rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
import { screen, render } from '@testing-library/react';
import { of } from 'rxjs/internal/observable/of';
import { showSnackbar, useConfig, useSession } from '@openmrs/esm-framework';
import { fetchConceptDiagnosisByName, saveVisitNote } from './visit-notes.resource';
import { fetchDiagnosisConceptsByName, saveVisitNote } from './visit-notes.resource';
import {
ConfigMock,
diagnosisSearchResponse,
Expand All @@ -21,7 +21,7 @@ const testProps = {
promptBeforeClosing: jest.fn(),
};

const mockFetchConceptDiagnosisByName = fetchConceptDiagnosisByName as jest.Mock;
const mockFetchDiagnosisConceptsByName = fetchDiagnosisConceptsByName as jest.Mock;
const mockSaveVisitNote = saveVisitNote as jest.Mock;
const mockedShowSnackbar = jest.mocked(showSnackbar);
const mockUseConfig = useConfig as jest.Mock;
Expand All @@ -42,7 +42,7 @@ jest.mock('@openmrs/esm-framework', () => {
});

jest.mock('./visit-notes.resource', () => ({
fetchConceptDiagnosisByName: jest.fn(),
fetchDiagnosisConceptsByName: jest.fn(),
useLocationUuid: jest.fn().mockImplementation(() => ({
data: mockFetchLocationByUuidResponse.data.uuid,
})),
Expand All @@ -59,7 +59,7 @@ jest.mock('./visit-notes.resource', () => ({
}));

test('renders the visit notes form with all the relevant fields and values', () => {
mockFetchConceptDiagnosisByName.mockReturnValue(of([]));
mockFetchDiagnosisConceptsByName.mockResolvedValue([]);

renderVisitNotesForm();

Expand All @@ -74,7 +74,7 @@ test('renders the visit notes form with all the relevant fields and values', ()
});

test.only('typing in the diagnosis search input triggers a search', async () => {
mockFetchConceptDiagnosisByName.mockReturnValue(of(diagnosisSearchResponse.results));
mockFetchDiagnosisConceptsByName.mockResolvedValue(diagnosisSearchResponse.results);

renderVisitNotesForm();

Expand All @@ -99,7 +99,7 @@ test.only('typing in the diagnosis search input triggers a search', async () =>

test('renders an error message when no matching diagnoses are found', async () => {
const user = userEvent.setup();
mockFetchConceptDiagnosisByName.mockReturnValue(of([]));
mockFetchDiagnosisConceptsByName.mockResolvedValue([]);

renderVisitNotesForm();

Expand Down Expand Up @@ -139,7 +139,7 @@ test('renders a success snackbar upon successfully recording a visit note', asyn
};

mockSaveVisitNote.mockResolvedValueOnce({ status: 201, body: 'Condition created' });
mockFetchConceptDiagnosisByName.mockReturnValue(of(diagnosisSearchResponse.results));
mockFetchDiagnosisConceptsByName.mockResolvedValue(diagnosisSearchResponse.results);

renderVisitNotesForm();

Expand Down Expand Up @@ -173,7 +173,7 @@ test('renders an error snackbar if there was a problem recording a condition', a
};

mockSaveVisitNote.mockRejectedValueOnce(error);
mockFetchConceptDiagnosisByName.mockReturnValue(of(diagnosisSearchResponse.results));
mockFetchDiagnosisConceptsByName.mockResolvedValue(diagnosisSearchResponse.results);

renderVisitNotesForm();

Expand Down
17 changes: 12 additions & 5 deletions packages/esm-patient-notes-app/src/notes/visit-notes.resource.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import useSWR from 'swr';
import useSWRInfinite from 'swr/infinite';
import { map } from 'rxjs/operators';
import { openmrsFetch, openmrsObservableFetch, restBaseUrl, useConfig } from '@openmrs/esm-framework';
import {
openmrsFetch,
openmrsObservableFetch,
restBaseUrl,
useAbortController,
useConfig,
} from '@openmrs/esm-framework';
import { type ConfigObject } from '../config-schema';
import {
type EncountersFetchResponse,
Expand Down Expand Up @@ -104,10 +110,11 @@ export function useInfiniteVisits(patientUuid: string) {
};
}

export function fetchConceptDiagnosisByName(searchTerm: string, diagnosisConceptClass: string) {
return openmrsObservableFetch<Array<Concept>>(
`${restBaseUrl}/concept?name=${searchTerm}&searchType=fuzzy&class=${diagnosisConceptClass}&name=&v=custom:(uuid,display)`,
).pipe(map(({ data }) => data['results']));
export function fetchDiagnosisConceptsByName(searchTerm: string, diagnosisConceptClass: string) {
const customRepresentation = 'custom:(uuid,display)';
const url = `${restBaseUrl}/concept?name=${searchTerm}&searchType=fuzzy&class=${diagnosisConceptClass}&v=${customRepresentation}`;

return openmrsFetch<Array<Concept>>(url).then(({ data }) => Promise.resolve(data['results']));
}

export function saveVisitNote(abortController: AbortController, payload: VisitNotePayload) {
Expand Down
2 changes: 2 additions & 0 deletions packages/esm-patient-notes-app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"emptyDiagnosisText": "No diagnosis selected — Enter a diagnosis below",
"enterPrimaryDiagnoses": "Enter Primary diagnoses",
"enterSecondaryDiagnoses": "Enter Secondary diagnoses",
"error": "Error",
"errorFetchingConcepts": "There was a problem fetching concepts",
"image": "Image",
"imageUploadHelperText": "Upload an image or use this device's camera to capture an image",
"noMatchingDiagnoses": "No diagnoses found matching",
Expand Down

0 comments on commit 2615e8c

Please sign in to comment.