@openmrs/esm-patient-chart-app 11.3.1-pre.9294 → 11.3.1-pre.9304
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +9 -9
- package/dist/1119.js +1 -1
- package/dist/1197.js +1 -1
- package/dist/2146.js +1 -1
- package/dist/2690.js +1 -1
- package/dist/276.js +1 -1
- package/dist/276.js.map +1 -1
- package/dist/2761.js +1 -1
- package/dist/2761.js.map +1 -1
- package/dist/2859.js +1 -1
- package/dist/2859.js.map +1 -1
- package/dist/3099.js +1 -1
- package/dist/3119.js +1 -1
- package/dist/3119.js.map +1 -1
- package/dist/3584.js +1 -1
- package/dist/4055.js +1 -1
- package/dist/4132.js +1 -1
- package/dist/4300.js +1 -1
- package/dist/4335.js +1 -1
- package/dist/4618.js +1 -1
- package/dist/4652.js +1 -1
- package/dist/4727.js +1 -1
- package/dist/4727.js.map +1 -1
- package/dist/4944.js +1 -1
- package/dist/5048.js +1 -1
- package/dist/5048.js.map +1 -1
- package/dist/5173.js +1 -1
- package/dist/5241.js +1 -1
- package/dist/5442.js +1 -1
- package/dist/5661.js +1 -1
- package/dist/6022.js +1 -1
- package/dist/6468.js +1 -1
- package/dist/6568.js +1 -0
- package/dist/6568.js.map +1 -0
- package/dist/6679.js +1 -1
- package/dist/6840.js +1 -1
- package/dist/6859.js +1 -1
- package/dist/7097.js +1 -1
- package/dist/7159.js +1 -1
- package/dist/723.js +1 -1
- package/dist/7617.js +1 -1
- package/dist/795.js +1 -1
- package/dist/8163.js +1 -1
- package/dist/8260.js +1 -0
- package/dist/8260.js.map +1 -0
- package/dist/8349.js +1 -1
- package/dist/8454.js +1 -1
- package/dist/8454.js.map +1 -1
- package/dist/8618.js +1 -1
- package/dist/890.js +1 -1
- package/dist/9214.js +1 -1
- package/dist/9329.js +1 -0
- package/dist/9329.js.map +1 -0
- package/dist/9538.js +1 -1
- package/dist/9569.js +1 -1
- package/dist/986.js +1 -1
- package/dist/9879.js +1 -1
- package/dist/9895.js +1 -1
- package/dist/9900.js +1 -1
- package/dist/9913.js +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-chart-app.js +1 -1
- package/dist/openmrs-esm-patient-chart-app.js.buildmanifest.json +214 -214
- package/dist/openmrs-esm-patient-chart-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/actions-buttons/delete-visit.component.tsx +8 -3
- package/src/actions-buttons/stop-visit.component.tsx +1 -1
- package/src/clinical-views/encounter-list/{encounter-list-tabs.component.tsx → encounter-list-tabs.extension.tsx} +10 -6
- package/src/index.ts +4 -4
- package/src/mark-patient-deceased/mark-patient-deceased-form.test.tsx +2 -0
- package/src/patient-banner-tags/{visit-attribute-tags.component.tsx → visit-attribute-tags.extension.tsx} +6 -4
- package/src/patient-chart/patient-chart.component.tsx +39 -65
- package/src/patient-chart/patient-chart.resources.ts +108 -0
- package/src/visit/hooks/useDeleteVisit.test.tsx +39 -42
- package/src/visit/hooks/useDeleteVisit.tsx +33 -17
- package/src/visit/visit-form/visit-form.test.tsx +3 -9
- package/src/visit/visit-form/visit-form.workspace.tsx +17 -7
- package/src/visit/visit-prompt/delete-visit-dialog.component.tsx +10 -4
- package/src/visit/visit-prompt/delete-visit-dialog.test.tsx +20 -2
- package/src/visit/visit-prompt/end-visit-dialog.component.tsx +7 -1
- package/src/visit/visit-prompt/end-visit-dialog.test.tsx +19 -0
- package/src/visit/visits-widget/active-visit-buttons/active-visit-buttons.tsx +7 -6
- package/src/visit/visits-widget/{current-visit-summary.component.tsx → current-visit-summary.extension.tsx} +13 -20
- package/src/visit/visits-widget/current-visit-summary.test.tsx +45 -25
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.component.tsx +4 -3
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.resource.ts +0 -1
- package/src/visit/visits-widget/visit-context/retrospective-data-date-time-picker/retrospective-date-time-picker.component.tsx +6 -8
- package/src/visit/visits-widget/visit-context/{visit-context-header.component.tsx → visit-context-header.extension.tsx} +17 -15
- package/src/visit/visits-widget/visit-context/visit-context-header.test.tsx +35 -29
- package/src/visit/visits-widget/visit-context/visit-context-switcher.modal.tsx +13 -11
- package/src/visit/visits-widget/visit-context/visit-context-switcher.test.tsx +50 -30
- package/src/visit/visits-widget/visit.resource.tsx +1 -1
- package/translations/am.json +1 -3
- package/translations/ar.json +1 -3
- package/translations/ar_SY.json +1 -3
- package/translations/bn.json +1 -3
- package/translations/de.json +1 -3
- package/translations/en.json +1 -3
- package/translations/en_US.json +1 -3
- package/translations/es.json +1 -3
- package/translations/es_MX.json +1 -3
- package/translations/fr.json +1 -3
- package/translations/he.json +1 -3
- package/translations/hi.json +1 -3
- package/translations/hi_IN.json +1 -3
- package/translations/id.json +1 -3
- package/translations/it.json +1 -3
- package/translations/ka.json +1 -3
- package/translations/km.json +1 -3
- package/translations/ku.json +1 -3
- package/translations/ky.json +1 -3
- package/translations/lg.json +1 -3
- package/translations/ne.json +1 -3
- package/translations/pl.json +1 -3
- package/translations/pt.json +1 -3
- package/translations/pt_BR.json +1 -3
- package/translations/qu.json +1 -3
- package/translations/ro_RO.json +1 -3
- package/translations/ru_RU.json +1 -3
- package/translations/si.json +1 -3
- package/translations/sw.json +1 -3
- package/translations/sw_KE.json +1 -3
- package/translations/tr.json +1 -3
- package/translations/tr_TR.json +1 -3
- package/translations/uk.json +1 -3
- package/translations/uz.json +1 -3
- package/translations/uz@Latn.json +1 -3
- package/translations/uz_UZ.json +1 -3
- package/translations/vi.json +1 -3
- package/translations/zh.json +1 -3
- package/translations/zh_CN.json +1 -3
- package/dist/2442.js +0 -1
- package/dist/2442.js.map +0 -1
- package/dist/3042.js +0 -1
- package/dist/3042.js.map +0 -1
- package/dist/4713.js +0 -1
- package/dist/4713.js.map +0 -1
|
@@ -1,34 +1,52 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import {
|
|
4
|
-
import { waitForLoadingToFinish } from 'tools';
|
|
5
|
-
import
|
|
3
|
+
import { getConfig } from '@openmrs/esm-framework';
|
|
4
|
+
import { mockPatient, waitForLoadingToFinish } from 'tools';
|
|
5
|
+
import { usePatientChartStore } from '@openmrs/esm-patient-common-lib';
|
|
6
|
+
import CurrentVisitSummary from './current-visit-summary.extension';
|
|
6
7
|
|
|
7
8
|
const mockGetConfig = jest.mocked(getConfig);
|
|
8
|
-
const
|
|
9
|
+
const mockUsePatientChartStore = jest.mocked(usePatientChartStore);
|
|
10
|
+
|
|
11
|
+
jest.mock('@openmrs/esm-patient-common-lib', () => ({
|
|
12
|
+
...jest.requireActual('@openmrs/esm-patient-common-lib'),
|
|
13
|
+
usePatientChartStore: jest.fn(),
|
|
14
|
+
}));
|
|
9
15
|
|
|
10
16
|
describe('CurrentVisitSummary', () => {
|
|
11
17
|
test('renders an empty state when there is no active visit', () => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
mutate: jest.fn(),
|
|
18
|
+
mockUsePatientChartStore.mockReturnValue({
|
|
19
|
+
patientUuid: mockPatient.id,
|
|
20
|
+
patient: mockPatient,
|
|
21
|
+
visitContext: null,
|
|
22
|
+
mutateVisitContext: null,
|
|
23
|
+
setPatient: jest.fn(),
|
|
24
|
+
setVisitContext: jest.fn(),
|
|
20
25
|
});
|
|
21
|
-
|
|
22
|
-
render(<CurrentVisitSummary patientUuid="some-uuid" />);
|
|
26
|
+
render(<CurrentVisitSummary patientUuid={mockPatient.id} />);
|
|
23
27
|
expect(screen.getByText(/current visit/i)).toBeInTheDocument();
|
|
24
|
-
expect(screen.getByText('There are no active
|
|
28
|
+
expect(screen.getByText('There are no active visits to display for this patient')).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('returns null when patientUuid does not match store patientUuid', () => {
|
|
32
|
+
mockUsePatientChartStore.mockReturnValue({
|
|
33
|
+
patientUuid: 'different-patient-id',
|
|
34
|
+
patient: mockPatient,
|
|
35
|
+
visitContext: null,
|
|
36
|
+
mutateVisitContext: null,
|
|
37
|
+
setPatient: jest.fn(),
|
|
38
|
+
setVisitContext: jest.fn(),
|
|
39
|
+
});
|
|
40
|
+
render(<CurrentVisitSummary patientUuid={mockPatient.id} />);
|
|
41
|
+
expect(screen.queryByText(/current visit/i)).not.toBeInTheDocument();
|
|
25
42
|
});
|
|
26
43
|
|
|
27
|
-
test('renders a visit summary when
|
|
44
|
+
test('renders a visit summary when visit context exists', async () => {
|
|
28
45
|
mockGetConfig.mockResolvedValue({ htmlFormEntryForms: [] });
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
mockUsePatientChartStore.mockReturnValue({
|
|
47
|
+
patientUuid: mockPatient.id,
|
|
48
|
+
patient: mockPatient,
|
|
49
|
+
visitContext: {
|
|
32
50
|
uuid: 'some-uuid',
|
|
33
51
|
display: 'Visit 1',
|
|
34
52
|
startDatetime: '2021-03-23T10:00:00.000+0300',
|
|
@@ -42,15 +60,17 @@ describe('CurrentVisitSummary', () => {
|
|
|
42
60
|
display: 'Visit Type 1',
|
|
43
61
|
},
|
|
44
62
|
encounters: [],
|
|
63
|
+
patient: {
|
|
64
|
+
uuid: mockPatient.id,
|
|
65
|
+
display: 'Test Patient',
|
|
66
|
+
},
|
|
45
67
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
isValidating: false,
|
|
50
|
-
mutate: jest.fn(),
|
|
68
|
+
mutateVisitContext: null,
|
|
69
|
+
setPatient: jest.fn(),
|
|
70
|
+
setVisitContext: jest.fn(),
|
|
51
71
|
});
|
|
52
72
|
|
|
53
|
-
render(<CurrentVisitSummary patientUuid=
|
|
73
|
+
render(<CurrentVisitSummary patientUuid={mockPatient.id} />);
|
|
54
74
|
|
|
55
75
|
await waitForLoadingToFinish();
|
|
56
76
|
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
type HtmlFormEntryForm,
|
|
45
45
|
launchFormEntryOrHtmlForms,
|
|
46
46
|
invalidateVisitAndEncounterData,
|
|
47
|
+
usePatientChartStore,
|
|
47
48
|
} from '@openmrs/esm-patient-common-lib';
|
|
48
49
|
import { jsonSchemaResourceName } from '../../../../constants';
|
|
49
50
|
import {
|
|
@@ -80,7 +81,7 @@ const EncountersTable: React.FC<EncountersTableProps> = ({
|
|
|
80
81
|
const pageSizes = [10, 20, 30, 40, 50];
|
|
81
82
|
const desktopLayout = isDesktop(useLayoutType());
|
|
82
83
|
const session = useSession();
|
|
83
|
-
const {
|
|
84
|
+
const { mutateVisitContext } = usePatientChartStore(patientUuid);
|
|
84
85
|
const { mutate } = useSWRConfig();
|
|
85
86
|
const responsiveSize = desktopLayout ? 'sm' : 'lg';
|
|
86
87
|
const { data: encounterTypes, isLoading: isLoadingEncounterTypes } = useEncounterTypes();
|
|
@@ -133,7 +134,7 @@ const EncountersTable: React.FC<EncountersTableProps> = ({
|
|
|
133
134
|
deleteEncounter(encounterUuid, abortController)
|
|
134
135
|
.then(() => {
|
|
135
136
|
// Update current visit data for critical components
|
|
136
|
-
|
|
137
|
+
mutateVisitContext?.();
|
|
137
138
|
|
|
138
139
|
// Also invalidate visit history and encounter tables since the encounter was deleted
|
|
139
140
|
invalidateVisitAndEncounterData(mutate, patientUuid);
|
|
@@ -160,7 +161,7 @@ const EncountersTable: React.FC<EncountersTableProps> = ({
|
|
|
160
161
|
},
|
|
161
162
|
});
|
|
162
163
|
},
|
|
163
|
-
[mutate,
|
|
164
|
+
[mutate, mutateVisitContext, patientUuid, t],
|
|
164
165
|
);
|
|
165
166
|
|
|
166
167
|
if (isLoadingEncounterTypes || isLoading) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { SelectItem, TimePickerSelect, TimePicker, Checkbox } from '@carbon/react';
|
|
2
|
-
import { OpenmrsDatePicker, ResponsiveWrapper, useFeatureFlag,
|
|
2
|
+
import { OpenmrsDatePicker, ResponsiveWrapper, useFeatureFlag, type Visit } from '@openmrs/esm-framework';
|
|
3
3
|
import React, { useEffect, useState } from 'react';
|
|
4
4
|
import { type Control, Controller, useForm } from 'react-hook-form';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import styles from './restrospective-date-time-picker.scss';
|
|
7
|
-
import { useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
|
|
8
7
|
|
|
9
8
|
type FormValues = {
|
|
10
9
|
retrospectiveDate: Date;
|
|
@@ -13,23 +12,22 @@ type FormValues = {
|
|
|
13
12
|
};
|
|
14
13
|
|
|
15
14
|
type RetrospectiveDateTimePickerProps = {
|
|
16
|
-
|
|
15
|
+
visitContext: Visit;
|
|
17
16
|
control?: Control<FormValues>;
|
|
18
17
|
onChange?: (data: FormValues) => void;
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
const RetrospectiveDateTimePicker = ({
|
|
22
|
-
|
|
21
|
+
visitContext,
|
|
23
22
|
control: propControl,
|
|
24
23
|
onChange,
|
|
25
24
|
}: RetrospectiveDateTimePickerProps) => {
|
|
26
25
|
const { t } = useTranslation();
|
|
27
26
|
const isRdeEnabled = useFeatureFlag('rde');
|
|
28
27
|
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const minDate = currentVisit?.startDatetime;
|
|
28
|
+
const isActiveVisit = !Boolean(visitContext && visitContext.stopDatetime);
|
|
29
|
+
const maxDate = visitContext?.stopDatetime;
|
|
30
|
+
const minDate = visitContext?.startDatetime;
|
|
33
31
|
|
|
34
32
|
const [manuallyEnableDateTimePicker, setManuallyEnableDateTimePicker] = useState<boolean>(false);
|
|
35
33
|
|
|
@@ -1,24 +1,33 @@
|
|
|
1
|
-
import { Button
|
|
2
|
-
import { showModal, useFeatureFlag
|
|
1
|
+
import { Button } from '@carbon/react';
|
|
2
|
+
import { showModal, useFeatureFlag } from '@openmrs/esm-framework';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import styles from './visit-context-header.scss';
|
|
7
7
|
import VisitContextInfo from './visit-context-info.component';
|
|
8
|
-
import { useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
|
|
8
|
+
import { usePatientChartStore, useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
|
|
9
9
|
|
|
10
10
|
interface VisitContextHeaderProps {
|
|
11
11
|
patientUuid: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The visit context header displays the currently select visit context in the patient chart store.
|
|
16
|
+
* When creating encounters (ex: visit notes, order entry, vitals), they are associated
|
|
17
|
+
* with the visit in context.
|
|
18
|
+
*
|
|
19
|
+
* This extension uses the patient chart store and should only be shown within the patient chart.
|
|
20
|
+
* It does not render when mounted outside the patient chart.
|
|
21
|
+
*/
|
|
14
22
|
const VisitContextHeader: React.FC<VisitContextHeaderProps> = ({ patientUuid }) => {
|
|
15
23
|
const { t } = useTranslation();
|
|
16
24
|
const { systemVisitEnabled } = useSystemVisitSetting();
|
|
17
25
|
const isRdeEnabled = useFeatureFlag('rde');
|
|
18
|
-
const showVisitContextHeader = systemVisitEnabled && isRdeEnabled;
|
|
19
26
|
|
|
20
|
-
const {
|
|
21
|
-
const isActiveVisit =
|
|
27
|
+
const { visitContext } = usePatientChartStore(patientUuid);
|
|
28
|
+
const isActiveVisit = Boolean(visitContext && !visitContext.stopDatetime);
|
|
29
|
+
|
|
30
|
+
const showVisitContextHeader = systemVisitEnabled && isRdeEnabled && visitContext;
|
|
22
31
|
|
|
23
32
|
const openVisitSwitcherModal = () => {
|
|
24
33
|
const dispose = showModal('visit-context-switcher', {
|
|
@@ -31,26 +40,19 @@ const VisitContextHeader: React.FC<VisitContextHeaderProps> = ({ patientUuid })
|
|
|
31
40
|
if (!showVisitContextHeader) {
|
|
32
41
|
return null;
|
|
33
42
|
}
|
|
34
|
-
if (isLoading) {
|
|
35
|
-
return (
|
|
36
|
-
<div className={styles.visitContextHeader}>
|
|
37
|
-
<Loading small />
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
43
|
return (
|
|
42
44
|
<div
|
|
43
45
|
className={classNames(styles.visitContextHeader, isActiveVisit ? styles.activeVisit : styles.retroactiveVisit)}
|
|
44
46
|
>
|
|
45
47
|
<div className={styles.addingTo}>{t('addingToVisit', 'Adding to:')}</div>
|
|
46
|
-
<div className={styles.visitType}>{
|
|
48
|
+
<div className={styles.visitType}>{visitContext.visitType?.display}</div>
|
|
47
49
|
<div className={styles.changeVisitButton}>
|
|
48
50
|
<Button kind="ghost" size="sm" onClick={openVisitSwitcherModal}>
|
|
49
51
|
{t('change', 'Change')}
|
|
50
52
|
</Button>
|
|
51
53
|
</div>
|
|
52
54
|
<div className={styles.visitInfo}>
|
|
53
|
-
<VisitContextInfo visit={
|
|
55
|
+
<VisitContextInfo visit={visitContext} />
|
|
54
56
|
</div>
|
|
55
57
|
</div>
|
|
56
58
|
);
|
|
@@ -1,51 +1,57 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
|
|
1
|
+
import { usePatientChartStore, useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
|
|
3
2
|
import { render, screen } from '@testing-library/react';
|
|
4
3
|
import { mockCurrentVisit } from '__mocks__';
|
|
5
4
|
import React from 'react';
|
|
6
|
-
import VisitContextHeader from './visit-context-header.
|
|
5
|
+
import VisitContextHeader from './visit-context-header.extension';
|
|
6
|
+
import { mockPatient } from 'tools';
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
errorFetchingSystemVisitSetting: null,
|
|
11
|
-
isLoadingSystemVisitSetting: false,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
jest.mocked(useVisitContextStore).mockReturnValue({
|
|
15
|
-
manuallySetVisitUuid: null,
|
|
16
|
-
mutateVisitCallbacks: {},
|
|
17
|
-
patientUuid: null,
|
|
18
|
-
setVisitContext: jest.fn(),
|
|
19
|
-
mutateVisit: jest.fn(),
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
jest.mocked(useVisit).mockReturnValue({
|
|
23
|
-
currentVisit: mockCurrentVisit,
|
|
24
|
-
error: null,
|
|
25
|
-
mutate: jest.fn(),
|
|
26
|
-
isValidating: false,
|
|
27
|
-
activeVisit: null,
|
|
28
|
-
currentVisitIsRetrospective: false,
|
|
29
|
-
isLoading: false,
|
|
30
|
-
});
|
|
8
|
+
const mockUsePatientChartStore = jest.mocked(usePatientChartStore);
|
|
9
|
+
const mockUseSystemVisitSetting = jest.mocked(useSystemVisitSetting);
|
|
31
10
|
|
|
32
|
-
jest.mock('@openmrs/esm-patient-common-lib
|
|
33
|
-
|
|
11
|
+
jest.mock('@openmrs/esm-patient-common-lib', () => ({
|
|
12
|
+
usePatientChartStore: jest.fn(),
|
|
13
|
+
useSystemVisitSetting: jest.fn(),
|
|
34
14
|
}));
|
|
35
15
|
|
|
36
16
|
describe('VisitContextHeader', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
mockUseSystemVisitSetting.mockReturnValue({
|
|
19
|
+
systemVisitEnabled: true,
|
|
20
|
+
errorFetchingSystemVisitSetting: null,
|
|
21
|
+
isLoadingSystemVisitSetting: false,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
37
25
|
it('should not show header if system does not support visits', () => {
|
|
38
26
|
mockUseSystemVisitSetting.mockReturnValueOnce({
|
|
39
27
|
systemVisitEnabled: false,
|
|
40
28
|
errorFetchingSystemVisitSetting: null,
|
|
41
29
|
isLoadingSystemVisitSetting: false,
|
|
42
30
|
});
|
|
31
|
+
mockUsePatientChartStore.mockReturnValue({
|
|
32
|
+
patientUuid: mockPatient.id,
|
|
33
|
+
patient: mockPatient,
|
|
34
|
+
visitContext: null,
|
|
35
|
+
mutateVisitContext: null,
|
|
36
|
+
setPatient: jest.fn(),
|
|
37
|
+
setVisitContext: jest.fn(),
|
|
38
|
+
});
|
|
39
|
+
|
|
43
40
|
render(<VisitContextHeader patientUuid="some-uuid" />);
|
|
44
41
|
expect(screen.queryByText('Adding to')).not.toBeInTheDocument();
|
|
45
42
|
});
|
|
46
43
|
|
|
47
44
|
it('should show the current visit', () => {
|
|
48
|
-
|
|
45
|
+
mockUsePatientChartStore.mockReturnValue({
|
|
46
|
+
patientUuid: mockPatient.id,
|
|
47
|
+
patient: mockPatient,
|
|
48
|
+
visitContext: mockCurrentVisit,
|
|
49
|
+
mutateVisitContext: null,
|
|
50
|
+
setPatient: jest.fn(),
|
|
51
|
+
setVisitContext: jest.fn(),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
render(<VisitContextHeader patientUuid={mockPatient.id} />);
|
|
49
55
|
expect(screen.getByText(/Adding to/i)).toBeInTheDocument();
|
|
50
56
|
expect(screen.getByText(mockCurrentVisit.visitType.display)).toBeInTheDocument();
|
|
51
57
|
});
|
|
@@ -9,12 +9,13 @@ import {
|
|
|
9
9
|
OpenmrsDatePicker,
|
|
10
10
|
useDebounce,
|
|
11
11
|
useOnVisible,
|
|
12
|
-
useVisitContextStore,
|
|
13
12
|
type Visit,
|
|
14
13
|
} from '@openmrs/esm-framework';
|
|
14
|
+
import { invalidateVisitByUuid, usePatientChartStore } from '@openmrs/esm-patient-common-lib';
|
|
15
15
|
import { useInfiniteVisits } from '../visit.resource';
|
|
16
16
|
import VisitContextInfo from './visit-context-info.component';
|
|
17
17
|
import styles from './visit-context-switcher.scss';
|
|
18
|
+
import { useSWRConfig } from 'swr';
|
|
18
19
|
|
|
19
20
|
interface VisitContextSwitcherProps {
|
|
20
21
|
patientUuid: string;
|
|
@@ -37,10 +38,9 @@ const VisitContextSwitcherModal: React.FC<VisitContextSwitcherProps> = ({
|
|
|
37
38
|
{ toStartDate: dayjs(maxStartDateDebounced).endOf('day').toISOString() },
|
|
38
39
|
rep,
|
|
39
40
|
);
|
|
40
|
-
const {
|
|
41
|
-
const [
|
|
42
|
-
|
|
43
|
-
);
|
|
41
|
+
const { visitContext, setVisitContext } = usePatientChartStore(patientUuid);
|
|
42
|
+
const [selectedVisitUuid, setSelectedVisitUuid] = useState<string>(visitContext?.uuid ?? null);
|
|
43
|
+
const { mutate: globalMutate } = useSWRConfig();
|
|
44
44
|
|
|
45
45
|
const onScrollToEnd = useCallback(() => {
|
|
46
46
|
if (hasMore) {
|
|
@@ -71,7 +71,7 @@ const VisitContextSwitcherModal: React.FC<VisitContextSwitcherProps> = ({
|
|
|
71
71
|
<ModalBody>
|
|
72
72
|
{error ? (
|
|
73
73
|
<ErrorState headerTitle={t('visits', 'visits')} error={error} />
|
|
74
|
-
) : visits?.length
|
|
74
|
+
) : visits?.length === 0 ? (
|
|
75
75
|
<Tile className={styles.tile}>
|
|
76
76
|
<div className={styles.tileContent}>
|
|
77
77
|
<p className={styles.content}>{t('noVisitsToDisplay', 'No visits to display')}</p>
|
|
@@ -85,8 +85,8 @@ const VisitContextSwitcherModal: React.FC<VisitContextSwitcherProps> = ({
|
|
|
85
85
|
<VisitCardRow
|
|
86
86
|
key={visit.uuid}
|
|
87
87
|
visit={visit}
|
|
88
|
-
setSelectedVisit={
|
|
89
|
-
isSelected={
|
|
88
|
+
setSelectedVisit={setSelectedVisitUuid}
|
|
89
|
+
isSelected={selectedVisitUuid === visit.uuid}
|
|
90
90
|
/>
|
|
91
91
|
);
|
|
92
92
|
})}
|
|
@@ -104,9 +104,11 @@ const VisitContextSwitcherModal: React.FC<VisitContextSwitcherProps> = ({
|
|
|
104
104
|
{t('cancel', 'Cancel')}
|
|
105
105
|
</Button>
|
|
106
106
|
<Button
|
|
107
|
-
disabled={
|
|
107
|
+
disabled={selectedVisitUuid === null || isLoading}
|
|
108
108
|
onClick={() => {
|
|
109
|
-
|
|
109
|
+
const selectedVisit = visits.find((v) => v.uuid === selectedVisitUuid);
|
|
110
|
+
const mutateVisitContext = () => invalidateVisitByUuid(globalMutate, selectedVisit.uuid);
|
|
111
|
+
setVisitContext(selectedVisit, mutateVisitContext);
|
|
110
112
|
onAfterVisitSelected?.();
|
|
111
113
|
closeModal();
|
|
112
114
|
}}
|
|
@@ -125,7 +127,7 @@ interface VisitCardRowProps {
|
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
/**
|
|
128
|
-
* A clickable row within the
|
|
130
|
+
* A clickable row within the visit context switcher to select a visit. This
|
|
129
131
|
* has slightly different UX than a regular radio button, as the entire card
|
|
130
132
|
* (not just the radio button and the label) is clickable
|
|
131
133
|
*/
|
|
@@ -1,46 +1,49 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
|
|
5
5
|
import { mockCurrentVisit, mockVisit2, mockVisit3 } from '__mocks__';
|
|
6
6
|
import { useInfiniteVisits } from '../visit.resource';
|
|
7
7
|
import VisitContextSwitcherModal from './visit-context-switcher.modal';
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
errorFetchingSystemVisitSetting: null,
|
|
11
|
-
isLoadingSystemVisitSetting: false,
|
|
12
|
-
systemVisitEnabled: true,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
jest.mocked(useVisitContextStore).mockReturnValue({
|
|
16
|
-
manuallySetVisitUuid: null,
|
|
17
|
-
mutateVisitCallbacks: {},
|
|
18
|
-
patientUuid: null,
|
|
19
|
-
setVisitContext: jest.fn(),
|
|
20
|
-
mutateVisit: jest.fn(),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const mockUseInfiniteVisits = jest.fn(useInfiniteVisits).mockReturnValue({
|
|
24
|
-
visits: [mockCurrentVisit, mockVisit2, mockVisit3],
|
|
25
|
-
error: null,
|
|
26
|
-
mutate: jest.fn(),
|
|
27
|
-
isValidating: false,
|
|
28
|
-
isLoading: false,
|
|
29
|
-
totalCount: 3,
|
|
30
|
-
hasMore: false,
|
|
31
|
-
loadMore: jest.fn(),
|
|
32
|
-
nextUri: '',
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
jest.mock('@openmrs/esm-patient-common-lib/src/useSystemVisitSetting', () => ({
|
|
36
|
-
useSystemVisitSetting: () => mockUseSystemVisitSetting(),
|
|
37
|
-
}));
|
|
9
|
+
const mockUseInfiniteVisits = jest.fn(useInfiniteVisits);
|
|
38
10
|
|
|
39
11
|
jest.mock('../visit.resource', () => ({
|
|
40
12
|
useInfiniteVisits: () => mockUseInfiniteVisits('some-uuid'),
|
|
41
13
|
}));
|
|
42
14
|
|
|
15
|
+
const mockSetVisitContext = jest.fn();
|
|
16
|
+
const mockUseSystemVisitSetting = jest.fn(useSystemVisitSetting);
|
|
17
|
+
|
|
18
|
+
jest.mock('@openmrs/esm-patient-common-lib', () => ({
|
|
19
|
+
...jest.requireActual('@openmrs/esm-patient-common-lib'),
|
|
20
|
+
useSystemVisitSetting: jest.fn(),
|
|
21
|
+
usePatientChartStore: jest.fn(() => ({
|
|
22
|
+
visitContext: null,
|
|
23
|
+
setVisitContext: mockSetVisitContext,
|
|
24
|
+
})),
|
|
25
|
+
}));
|
|
26
|
+
|
|
43
27
|
describe('VisitContextSwitcherModal', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
mockUseInfiniteVisits.mockReturnValue({
|
|
30
|
+
visits: [mockCurrentVisit, mockVisit2, mockVisit3],
|
|
31
|
+
error: null,
|
|
32
|
+
mutate: jest.fn(),
|
|
33
|
+
isValidating: false,
|
|
34
|
+
isLoading: false,
|
|
35
|
+
totalCount: 3,
|
|
36
|
+
hasMore: false,
|
|
37
|
+
loadMore: jest.fn(),
|
|
38
|
+
nextUri: '',
|
|
39
|
+
});
|
|
40
|
+
mockUseSystemVisitSetting.mockReturnValue({
|
|
41
|
+
errorFetchingSystemVisitSetting: null,
|
|
42
|
+
isLoadingSystemVisitSetting: false,
|
|
43
|
+
systemVisitEnabled: true,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
44
47
|
it('should display a list of past visits', () => {
|
|
45
48
|
mockUseSystemVisitSetting.mockReturnValueOnce({
|
|
46
49
|
systemVisitEnabled: false,
|
|
@@ -53,6 +56,23 @@ describe('VisitContextSwitcherModal', () => {
|
|
|
53
56
|
// visit type - only check the visitType div elements
|
|
54
57
|
expect(screen.getAllByText('Facility Visit', { selector: '.visitType' })).toHaveLength(3);
|
|
55
58
|
});
|
|
59
|
+
|
|
60
|
+
it('should call setVisitContext when continue button is clicked with selected visit', async () => {
|
|
61
|
+
const user = userEvent.setup();
|
|
62
|
+
|
|
63
|
+
renderVisitContextSwitcherModal();
|
|
64
|
+
|
|
65
|
+
// Select a visit by clicking on the first visit card
|
|
66
|
+
const firstVisitRadio = screen.getAllByRole('radio', { name: /Facility Visit/ })[0];
|
|
67
|
+
await user.click(firstVisitRadio);
|
|
68
|
+
|
|
69
|
+
// Click the continue button
|
|
70
|
+
const continueButton = screen.getByRole('button', { name: /continue/i });
|
|
71
|
+
await user.click(continueButton);
|
|
72
|
+
|
|
73
|
+
// Verify setVisitContext was called with the selected visit
|
|
74
|
+
expect(mockSetVisitContext).toHaveBeenCalledWith(mockCurrentVisit, expect.any(Function));
|
|
75
|
+
});
|
|
56
76
|
});
|
|
57
77
|
|
|
58
78
|
function renderVisitContextSwitcherModal() {
|
|
@@ -53,7 +53,7 @@ export function deleteVisit(visitUuid: string) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export function restoreVisit(visitUuid: string) {
|
|
56
|
-
return openmrsFetch(`${restBaseUrl}/visit/${visitUuid}`, {
|
|
56
|
+
return openmrsFetch<Visit>(`${restBaseUrl}/visit/${visitUuid}`, {
|
|
57
57
|
headers: {
|
|
58
58
|
'content-type': 'application/json',
|
|
59
59
|
},
|
package/translations/am.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actions": "Actions",
|
|
3
|
+
"activeVisits__lower": "active visits",
|
|
3
4
|
"addingToVisit": "Adding to:",
|
|
4
5
|
"additionalVisitInformationUpdatedSuccessfully": "Additional visit information updated successfully",
|
|
5
6
|
"addVisit": "Add visit",
|
|
@@ -75,7 +76,6 @@
|
|
|
75
76
|
"errorUpdatingVisitAttribute": "Could not update {{attributeName}} attribute",
|
|
76
77
|
"errorUpdatingVisitDetails": "Error updating visit details",
|
|
77
78
|
"errorWhenRestoringVisit": "Error occurred when restoring {{visit}}",
|
|
78
|
-
"failedToLoadCurrentVisit": "Failed loading current visit",
|
|
79
79
|
"fieldRequired": "This field is required",
|
|
80
80
|
"filterByEncounterType": "Filter by encounter type",
|
|
81
81
|
"form": "Form name",
|
|
@@ -85,7 +85,6 @@
|
|
|
85
85
|
"indication": "Indication",
|
|
86
86
|
"inThePast": "In the past",
|
|
87
87
|
"loading": "Loading",
|
|
88
|
-
"loadingVisit": "Loading current visit...",
|
|
89
88
|
"markAliveSuccessfully": "Patient marked alive successfully",
|
|
90
89
|
"markDeceasedWarning": "Marking the patient as deceased will end any active visits for this patient",
|
|
91
90
|
"markPatientAlive": "Mark patient alive",
|
|
@@ -99,7 +98,6 @@
|
|
|
99
98
|
"nextPage": "Next page",
|
|
100
99
|
"no": "No",
|
|
101
100
|
"noActiveVisit": "No active visit",
|
|
102
|
-
"noActiveVisitMessage": "active visit",
|
|
103
101
|
"noActiveVisitNoRDEText": "You can't add data to the patient chart without an active visit. Would you like to start a new visit?",
|
|
104
102
|
"noDiagnosesFound": "No diagnoses found",
|
|
105
103
|
"noEncountersToDisplay": "No encounters to display",
|
package/translations/ar.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actions": "Actions",
|
|
3
|
+
"activeVisits__lower": "active visits",
|
|
3
4
|
"addingToVisit": "Adding to:",
|
|
4
5
|
"additionalVisitInformationUpdatedSuccessfully": "Additional visit information updated successfully",
|
|
5
6
|
"addVisit": "Add visit",
|
|
@@ -75,7 +76,6 @@
|
|
|
75
76
|
"errorUpdatingVisitAttribute": "Could not update {{attributeName}} attribute",
|
|
76
77
|
"errorUpdatingVisitDetails": "Error updating visit details",
|
|
77
78
|
"errorWhenRestoringVisit": "Error occurred when restoring {{visit}}",
|
|
78
|
-
"failedToLoadCurrentVisit": "فشل في تحميل الزيارة الحالية",
|
|
79
79
|
"fieldRequired": "This field is required",
|
|
80
80
|
"filterByEncounterType": "تصفية حسب نوع اللقاء",
|
|
81
81
|
"form": "Form name",
|
|
@@ -85,7 +85,6 @@
|
|
|
85
85
|
"indication": "دلالة",
|
|
86
86
|
"inThePast": "In the past",
|
|
87
87
|
"loading": "جار التحميل",
|
|
88
|
-
"loadingVisit": "جار تحميل الزيارة الحالية...",
|
|
89
88
|
"markAliveSuccessfully": "Patient marked alive successfully",
|
|
90
89
|
"markDeceasedWarning": "Marking the patient as deceased will end any active visits for this patient",
|
|
91
90
|
"markPatientAlive": "Mark patient alive",
|
|
@@ -99,7 +98,6 @@
|
|
|
99
98
|
"nextPage": "Next page",
|
|
100
99
|
"no": "لا",
|
|
101
100
|
"noActiveVisit": "لا يوجد زيارة نشطة",
|
|
102
|
-
"noActiveVisitMessage": "زيارة نشطة",
|
|
103
101
|
"noActiveVisitNoRDEText": "لا يمكنك إضافة بيانات إلى السجل الطبي للمريض بدون زيارة نشطة. هل ترغب في بدء زيارة جديدة؟",
|
|
104
102
|
"noDiagnosesFound": "لم يتم العثور على تشخيصات",
|
|
105
103
|
"noEncountersToDisplay": "لا يوجد لقاءات لعرضها",
|
package/translations/ar_SY.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actions": "Actions",
|
|
3
|
+
"activeVisits__lower": "active visits",
|
|
3
4
|
"addingToVisit": "Adding to:",
|
|
4
5
|
"additionalVisitInformationUpdatedSuccessfully": "Additional visit information updated successfully",
|
|
5
6
|
"addVisit": "Add visit",
|
|
@@ -75,7 +76,6 @@
|
|
|
75
76
|
"errorUpdatingVisitAttribute": "Could not update {{attributeName}} attribute",
|
|
76
77
|
"errorUpdatingVisitDetails": "Error updating visit details",
|
|
77
78
|
"errorWhenRestoringVisit": "Error occurred when restoring {{visit}}",
|
|
78
|
-
"failedToLoadCurrentVisit": "Failed loading current visit",
|
|
79
79
|
"fieldRequired": "This field is required",
|
|
80
80
|
"filterByEncounterType": "Filter by encounter type",
|
|
81
81
|
"form": "Form name",
|
|
@@ -85,7 +85,6 @@
|
|
|
85
85
|
"indication": "Indication",
|
|
86
86
|
"inThePast": "In the past",
|
|
87
87
|
"loading": "Loading",
|
|
88
|
-
"loadingVisit": "Loading current visit...",
|
|
89
88
|
"markAliveSuccessfully": "Patient marked alive successfully",
|
|
90
89
|
"markDeceasedWarning": "Marking the patient as deceased will end any active visits for this patient",
|
|
91
90
|
"markPatientAlive": "Mark patient alive",
|
|
@@ -99,7 +98,6 @@
|
|
|
99
98
|
"nextPage": "Next page",
|
|
100
99
|
"no": "No",
|
|
101
100
|
"noActiveVisit": "No Active Visit",
|
|
102
|
-
"noActiveVisitMessage": "active visit",
|
|
103
101
|
"noActiveVisitNoRDEText": "You can't add data to the patient chart without an active visit. Would you like to start a new visit?",
|
|
104
102
|
"noDiagnosesFound": "No diagnoses found",
|
|
105
103
|
"noEncountersToDisplay": "No encounters to display",
|
package/translations/bn.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actions": "Actions",
|
|
3
|
+
"activeVisits__lower": "active visits",
|
|
3
4
|
"addingToVisit": "Adding to:",
|
|
4
5
|
"additionalVisitInformationUpdatedSuccessfully": "Additional visit information updated successfully",
|
|
5
6
|
"addVisit": "Add visit",
|
|
@@ -75,7 +76,6 @@
|
|
|
75
76
|
"errorUpdatingVisitAttribute": "Could not update {{attributeName}} attribute",
|
|
76
77
|
"errorUpdatingVisitDetails": "Error updating visit details",
|
|
77
78
|
"errorWhenRestoringVisit": "Error occurred when restoring {{visit}}",
|
|
78
|
-
"failedToLoadCurrentVisit": "Failed loading current visit",
|
|
79
79
|
"fieldRequired": "This field is required",
|
|
80
80
|
"filterByEncounterType": "Filter by encounter type",
|
|
81
81
|
"form": "Form name",
|
|
@@ -85,7 +85,6 @@
|
|
|
85
85
|
"indication": "Indication",
|
|
86
86
|
"inThePast": "In the past",
|
|
87
87
|
"loading": "Loading",
|
|
88
|
-
"loadingVisit": "Loading current visit...",
|
|
89
88
|
"markAliveSuccessfully": "Patient marked alive successfully",
|
|
90
89
|
"markDeceasedWarning": "Marking the patient as deceased will end any active visits for this patient",
|
|
91
90
|
"markPatientAlive": "Mark patient alive",
|
|
@@ -99,7 +98,6 @@
|
|
|
99
98
|
"nextPage": "Next page",
|
|
100
99
|
"no": "No",
|
|
101
100
|
"noActiveVisit": "No Active Visit",
|
|
102
|
-
"noActiveVisitMessage": "active visit",
|
|
103
101
|
"noActiveVisitNoRDEText": "You can't add data to the patient chart without an active visit. Would you like to start a new visit?",
|
|
104
102
|
"noDiagnosesFound": "No diagnoses found",
|
|
105
103
|
"noEncountersToDisplay": "No encounters to display",
|