@openmrs/esm-patient-forms-app 11.3.1-pre.9452 → 11.3.1-pre.9455
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 +14 -15
- package/dist/1197.js +1 -1
- package/dist/1197.js.map +1 -0
- package/dist/2608.js +1 -0
- package/dist/2608.js.map +1 -0
- package/dist/3578.js +1 -0
- package/dist/4341.js +1 -0
- package/dist/4341.js.map +1 -0
- package/dist/5277.js +1 -0
- package/dist/5277.js.map +1 -0
- package/dist/5696.js +1 -1
- package/dist/5696.js.map +1 -1
- package/dist/5764.js +1 -0
- package/dist/5764.js.map +1 -0
- package/dist/5792.js +2 -0
- package/dist/{7906.js.LICENSE.txt → 5792.js.LICENSE.txt} +10 -0
- package/dist/5792.js.map +1 -0
- package/dist/7003.js +1 -1
- package/dist/7003.js.map +1 -1
- package/dist/7007.js +1 -1
- package/dist/7007.js.map +1 -1
- package/dist/707.js +1 -1
- package/dist/707.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-forms-app.js +1 -1
- package/dist/openmrs-esm-patient-forms-app.js.buildmanifest.json +180 -137
- package/dist/openmrs-esm-patient-forms-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/clinical-form-action-button.component.tsx +13 -40
- package/src/clinical-form-action-button.test.tsx +23 -22
- package/src/config-schema.ts +1 -1
- package/src/forms/exported-form-entry.workspace.tsx +37 -0
- package/src/forms/exported-forms-dashboard.workspace.tsx +43 -0
- package/src/forms/form-entry.component.tsx +138 -0
- package/src/forms/form-entry.resources.ts +37 -0
- package/src/forms/form-entry.test.tsx +14 -28
- package/src/forms/form-entry.workspace.tsx +25 -99
- package/src/forms/forms-dashboard.component.tsx +13 -58
- package/src/forms/forms-dashboard.test.tsx +3 -15
- package/src/forms/forms-dashboard.workspace.tsx +34 -10
- package/src/forms/forms-list.component.tsx +20 -41
- package/src/forms/forms-list.test.tsx +2 -2
- package/src/forms/forms-table.component.tsx +1 -1
- package/src/hooks/use-forms.ts +3 -4
- package/src/index.ts +6 -8
- package/src/offline-forms/use-offline-form-encounters.ts +2 -2
- package/src/offline.ts +5 -7
- package/src/routes.json +13 -49
- package/dist/1123.js +0 -2
- package/dist/1123.js.LICENSE.txt +0 -9
- package/dist/1123.js.map +0 -1
- package/dist/2773.js +0 -1
- package/dist/2773.js.map +0 -1
- package/dist/4078.js +0 -1
- package/dist/4078.js.map +0 -1
- package/dist/7906.js +0 -2
- package/dist/7906.js.map +0 -1
- package/dist/8803.js +0 -1
- package/dist/8803.js.map +0 -1
- package/src/htmlformentry/html-form-entry.workspace.tsx +0 -54
|
@@ -1,54 +1,27 @@
|
|
|
1
1
|
import React, { type ComponentProps } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
clinicalFormsWorkspace,
|
|
6
|
-
formEntryWorkspace,
|
|
7
|
-
htmlFormEntryWorkspace,
|
|
8
|
-
useLaunchWorkspaceRequiringVisit,
|
|
9
|
-
} from '@openmrs/esm-patient-common-lib';
|
|
3
|
+
import { ActionMenuButton2, DocumentIcon } from '@openmrs/esm-framework';
|
|
4
|
+
import { type PatientChartWorkspaceActionButtonProps, useStartVisitIfNeeded } from '@openmrs/esm-patient-common-lib';
|
|
10
5
|
|
|
11
6
|
/**
|
|
12
7
|
* This button uses the patient chart store and MUST only be used
|
|
13
8
|
* within the patient chart
|
|
14
9
|
*/
|
|
15
|
-
const ClinicalFormActionButton: React.FC<
|
|
10
|
+
const ClinicalFormActionButton: React.FC<PatientChartWorkspaceActionButtonProps> = ({
|
|
11
|
+
groupProps: { patientUuid },
|
|
12
|
+
}) => {
|
|
16
13
|
const { t } = useTranslation();
|
|
17
|
-
const
|
|
18
|
-
const launchFormsWorkspace = useLaunchWorkspaceRequiringVisit(patientUuid, clinicalFormsWorkspace);
|
|
19
|
-
|
|
20
|
-
const formEntryWorkspaces = workspaces.filter((w) => w.name === formEntryWorkspace);
|
|
21
|
-
const recentlyOpenedForm = formEntryWorkspaces[0];
|
|
22
|
-
|
|
23
|
-
const htmlFormEntryWorkspaces = workspaces.filter((w) => w.name === htmlFormEntryWorkspace);
|
|
24
|
-
const recentlyOpenedHtmlForm = htmlFormEntryWorkspaces[0];
|
|
25
|
-
|
|
26
|
-
const isFormOpen = formEntryWorkspaces?.length >= 1;
|
|
27
|
-
const isHtmlFormOpen = htmlFormEntryWorkspaces?.length >= 1;
|
|
28
|
-
|
|
29
|
-
const launchPatientWorkspaceCb = () => {
|
|
30
|
-
if (isFormOpen) {
|
|
31
|
-
launchWorkspace(formEntryWorkspace, {
|
|
32
|
-
workspaceTitle: recentlyOpenedForm?.additionalProps?.['workspaceTitle'],
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
// we aren't currently supporting keeping HTML Form workspaces open, but just in case
|
|
36
|
-
else if (isHtmlFormOpen) {
|
|
37
|
-
launchWorkspace(htmlFormEntryWorkspace, {
|
|
38
|
-
workspaceTitle: recentlyOpenedHtmlForm?.additionalProps?.['workspaceTitle'],
|
|
39
|
-
});
|
|
40
|
-
} else {
|
|
41
|
-
launchFormsWorkspace();
|
|
42
|
-
}
|
|
43
|
-
};
|
|
14
|
+
const startVisitIfNeeded = useStartVisitIfNeeded(patientUuid);
|
|
44
15
|
|
|
45
16
|
return (
|
|
46
|
-
<
|
|
47
|
-
|
|
17
|
+
<ActionMenuButton2
|
|
18
|
+
icon={(props: ComponentProps<typeof DocumentIcon>) => <DocumentIcon {...props} />}
|
|
48
19
|
label={t('clinicalForms', 'Clinical forms')}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
20
|
+
workspaceToLaunch={{
|
|
21
|
+
workspaceName: 'clinical-forms-workspace',
|
|
22
|
+
workspaceProps: {},
|
|
23
|
+
}}
|
|
24
|
+
onBeforeWorkspaceLaunch={startVisitIfNeeded}
|
|
52
25
|
/>
|
|
53
26
|
);
|
|
54
27
|
};
|
|
@@ -1,46 +1,47 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import {
|
|
3
|
+
import { ActionMenuButton2, useLayoutType } from '@openmrs/esm-framework';
|
|
4
4
|
import ClinicalFormActionButton from './clinical-form-action-button.component';
|
|
5
|
+
import { mockPatient } from 'tools';
|
|
5
6
|
|
|
7
|
+
const mockActionMenuButton = jest.mocked(ActionMenuButton2);
|
|
6
8
|
const mockUseLayoutType = jest.mocked(useLayoutType);
|
|
7
|
-
const mockUseWorkspaces = useWorkspaces as jest.Mock;
|
|
8
|
-
const mockActionMenuButton = jest.mocked(ActionMenuButton);
|
|
9
9
|
|
|
10
|
-
mockActionMenuButton.mockImplementation(({
|
|
11
|
-
<button
|
|
10
|
+
mockActionMenuButton.mockImplementation(({ label, tagContent }) => (
|
|
11
|
+
<button>
|
|
12
12
|
{tagContent} {label}
|
|
13
13
|
</button>
|
|
14
14
|
));
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
preferredWindowSize: 'normal',
|
|
25
|
-
type: 'form',
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
workspaceWindowState: 'normal',
|
|
29
|
-
prompt: null,
|
|
30
|
-
}));
|
|
16
|
+
jest.mock('@openmrs/esm-patient-common-lib', () => {
|
|
17
|
+
const originalModule = jest.requireActual('@openmrs/esm-patient-common-lib');
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
...originalModule,
|
|
21
|
+
useStartVisitIfNeeded: jest.fn(() => () => Promise.resolve(true)),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
31
24
|
|
|
32
25
|
describe('<ClinicalFormActionButton>', () => {
|
|
33
26
|
test('should display clinical form action button on tablet view', () => {
|
|
34
27
|
mockUseLayoutType.mockReturnValue('tablet');
|
|
35
28
|
|
|
36
|
-
render(
|
|
29
|
+
render(
|
|
30
|
+
<ClinicalFormActionButton
|
|
31
|
+
groupProps={{ patientUuid: mockPatient.id, patient: mockPatient, visitContext: null, mutateVisitContext: null }}
|
|
32
|
+
/>,
|
|
33
|
+
);
|
|
37
34
|
expect(screen.getByRole('button', { name: /Clinical forms/i })).toBeInTheDocument();
|
|
38
35
|
});
|
|
39
36
|
|
|
40
37
|
test('should display clinical form action button on desktop view', () => {
|
|
41
38
|
mockUseLayoutType.mockReturnValue('small-desktop');
|
|
42
39
|
|
|
43
|
-
render(
|
|
40
|
+
render(
|
|
41
|
+
<ClinicalFormActionButton
|
|
42
|
+
groupProps={{ patientUuid: mockPatient.id, patient: mockPatient, visitContext: null, mutateVisitContext: null }}
|
|
43
|
+
/>,
|
|
44
|
+
);
|
|
44
45
|
const clinicalActionButton = screen.getByRole('button', { name: /Form/i });
|
|
45
46
|
expect(clinicalActionButton).toBeInTheDocument();
|
|
46
47
|
});
|
package/src/config-schema.ts
CHANGED
|
@@ -123,7 +123,7 @@ export interface FormsSection {
|
|
|
123
123
|
forms: Array<string>;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
export interface
|
|
126
|
+
export interface FormEntryConfigSchema {
|
|
127
127
|
htmlFormEntryForms: Array<HtmlFormEntryForm>;
|
|
128
128
|
formSections: Array<FormsSection>;
|
|
129
129
|
customFormsUrl: string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type Workspace2DefinitionProps } from '@openmrs/esm-framework';
|
|
3
|
+
import { type Form } from '@openmrs/esm-patient-common-lib';
|
|
4
|
+
import FormEntry from './form-entry.component';
|
|
5
|
+
import { type ExportedClinicalFormsWindowProps } from './exported-forms-dashboard.workspace';
|
|
6
|
+
|
|
7
|
+
interface FormEntryWorkspaceProps {
|
|
8
|
+
form: Form;
|
|
9
|
+
encounterUuid: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This workspace is meant for use outside the patient chart.
|
|
14
|
+
*
|
|
15
|
+
* @see form-entry.workspace.tsx
|
|
16
|
+
*/
|
|
17
|
+
const ExportedFormEntryWorkspace: React.FC<
|
|
18
|
+
Workspace2DefinitionProps<FormEntryWorkspaceProps, ExportedClinicalFormsWindowProps, {}>
|
|
19
|
+
> = ({
|
|
20
|
+
closeWorkspace,
|
|
21
|
+
workspaceProps: { form, encounterUuid },
|
|
22
|
+
windowProps: { patient, patientUuid, visitContext, mutateVisitContext },
|
|
23
|
+
}) => {
|
|
24
|
+
return (
|
|
25
|
+
<FormEntry
|
|
26
|
+
form={form}
|
|
27
|
+
encounterUuid={encounterUuid}
|
|
28
|
+
patient={patient}
|
|
29
|
+
patientUuid={patientUuid}
|
|
30
|
+
visitContext={visitContext}
|
|
31
|
+
mutateVisitContext={mutateVisitContext}
|
|
32
|
+
closeWorkspace={closeWorkspace}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default ExportedFormEntryWorkspace;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import FormsDashboard from './forms-dashboard.component';
|
|
4
|
+
import styles from './forms-dashboard-workspace.scss';
|
|
5
|
+
import { type Form } from '@openmrs/esm-patient-common-lib';
|
|
6
|
+
import { type Visit, Workspace2, type Workspace2DefinitionProps } from '@openmrs/esm-framework';
|
|
7
|
+
|
|
8
|
+
export interface ExportedClinicalFormsWindowProps {
|
|
9
|
+
formEntryWorkspaceName: string;
|
|
10
|
+
patient: fhir.Patient;
|
|
11
|
+
patientUuid: string;
|
|
12
|
+
visitContext: Visit;
|
|
13
|
+
mutateVisitContext: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* This workspace is meant for use outside the patient chart.
|
|
18
|
+
* @see forms-dashboard.workspace.tsx
|
|
19
|
+
*/
|
|
20
|
+
const ExportedFormsDashboardWorkspace: React.FC<
|
|
21
|
+
Workspace2DefinitionProps<{}, ExportedClinicalFormsWindowProps, {}>
|
|
22
|
+
> = ({ launchChildWorkspace, windowProps: { formEntryWorkspaceName, patient, visitContext } }) => {
|
|
23
|
+
const { t } = useTranslation();
|
|
24
|
+
const handleFormOpen = useCallback(
|
|
25
|
+
(form: Form, encounterUuid: string) => {
|
|
26
|
+
launchChildWorkspace(formEntryWorkspaceName, {
|
|
27
|
+
form,
|
|
28
|
+
encounterUuid,
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
[launchChildWorkspace, formEntryWorkspaceName],
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Workspace2 title={t('clinicalForms', 'Clinical forms')} hasUnsavedChanges={false}>
|
|
36
|
+
<div className={styles.container}>
|
|
37
|
+
<FormsDashboard {...{ patient, visitContext, handleFormOpen }} />
|
|
38
|
+
</div>
|
|
39
|
+
</Workspace2>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default ExportedFormsDashboardWorkspace;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import { useSWRConfig } from 'swr';
|
|
3
|
+
import {
|
|
4
|
+
ExtensionSlot,
|
|
5
|
+
useConfig,
|
|
6
|
+
useConnectivity,
|
|
7
|
+
Workspace2,
|
|
8
|
+
type Workspace2DefinitionProps,
|
|
9
|
+
} from '@openmrs/esm-framework';
|
|
10
|
+
import { type Form, type FormRendererProps, invalidateVisitAndEncounterData } from '@openmrs/esm-patient-common-lib';
|
|
11
|
+
import { useTranslation } from 'react-i18next';
|
|
12
|
+
import { type FormEntryConfigSchema } from '../config-schema';
|
|
13
|
+
import { toHtmlForm } from './form-entry.resources';
|
|
14
|
+
import HtmlFormEntryWrapper from '../htmlformentry/html-form-entry-wrapper.component';
|
|
15
|
+
|
|
16
|
+
export interface FormEntryProps {
|
|
17
|
+
form: Form;
|
|
18
|
+
encounterUuid: string;
|
|
19
|
+
patientUuid;
|
|
20
|
+
patient;
|
|
21
|
+
visitContext;
|
|
22
|
+
mutateVisitContext;
|
|
23
|
+
closeWorkspace: Workspace2DefinitionProps['closeWorkspace'];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const FormEntry: React.FC<FormEntryProps> = ({
|
|
27
|
+
form,
|
|
28
|
+
encounterUuid,
|
|
29
|
+
patientUuid,
|
|
30
|
+
patient,
|
|
31
|
+
visitContext,
|
|
32
|
+
mutateVisitContext,
|
|
33
|
+
closeWorkspace,
|
|
34
|
+
}) => {
|
|
35
|
+
const formUuid = form.uuid;
|
|
36
|
+
const visitStartDatetime = visitContext?.startDatetime;
|
|
37
|
+
const visitStopDatetime = visitContext?.stopDatetime;
|
|
38
|
+
const visitTypeUuid = visitContext?.visitType.uuid;
|
|
39
|
+
const visitUuid = visitContext?.uuid;
|
|
40
|
+
const { htmlFormEntryForms } = useConfig<FormEntryConfigSchema>();
|
|
41
|
+
const htmlForm = toHtmlForm(form, htmlFormEntryForms);
|
|
42
|
+
const isHtmlForm = htmlForm != null;
|
|
43
|
+
const isOnline = useConnectivity();
|
|
44
|
+
const { mutate: globalMutate } = useSWRConfig();
|
|
45
|
+
const { t } = useTranslation();
|
|
46
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
47
|
+
|
|
48
|
+
const state = useMemo(
|
|
49
|
+
() => ({
|
|
50
|
+
view: 'form',
|
|
51
|
+
formUuid: formUuid ?? null,
|
|
52
|
+
visitUuid: visitUuid ?? visitContext?.uuid ?? null,
|
|
53
|
+
visitTypeUuid: visitTypeUuid ?? visitContext?.visitType?.uuid ?? null,
|
|
54
|
+
visitStartDatetime: visitStartDatetime ?? visitContext?.startDatetime ?? null,
|
|
55
|
+
visitStopDatetime: visitStopDatetime ?? visitContext?.stopDatetime ?? null,
|
|
56
|
+
isOffline: !isOnline,
|
|
57
|
+
patientUuid: patientUuid ?? null,
|
|
58
|
+
patient,
|
|
59
|
+
encounterUuid: encounterUuid ?? null,
|
|
60
|
+
closeWorkspace: () => {
|
|
61
|
+
return closeWorkspace();
|
|
62
|
+
},
|
|
63
|
+
closeWorkspaceWithSavedChanges: () => {
|
|
64
|
+
// Update current visit data for critical components
|
|
65
|
+
mutateVisitContext?.();
|
|
66
|
+
|
|
67
|
+
// Also invalidate visit history and encounter tables since form submission may create/update encounters
|
|
68
|
+
invalidateVisitAndEncounterData(globalMutate, patientUuid);
|
|
69
|
+
|
|
70
|
+
return closeWorkspace({ discardUnsavedChanges: true, closeWindow: true });
|
|
71
|
+
},
|
|
72
|
+
promptBeforeClosing: (func) => setHasUnsavedChanges(func()),
|
|
73
|
+
}),
|
|
74
|
+
[
|
|
75
|
+
closeWorkspace,
|
|
76
|
+
visitContext?.startDatetime,
|
|
77
|
+
visitContext?.stopDatetime,
|
|
78
|
+
visitContext?.uuid,
|
|
79
|
+
visitContext?.visitType?.uuid,
|
|
80
|
+
encounterUuid,
|
|
81
|
+
formUuid,
|
|
82
|
+
globalMutate,
|
|
83
|
+
isOnline,
|
|
84
|
+
mutateVisitContext,
|
|
85
|
+
patient,
|
|
86
|
+
patientUuid,
|
|
87
|
+
setHasUnsavedChanges,
|
|
88
|
+
visitStartDatetime,
|
|
89
|
+
visitStopDatetime,
|
|
90
|
+
visitTypeUuid,
|
|
91
|
+
visitUuid,
|
|
92
|
+
],
|
|
93
|
+
) satisfies FormRendererProps;
|
|
94
|
+
|
|
95
|
+
const htmlFormEntryUrl = useMemo(() => {
|
|
96
|
+
if (!htmlForm) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const uiPage = encounterUuid ? htmlForm.formEditUiPage : htmlForm.formUiPage;
|
|
100
|
+
const url = `${window.openmrsBase}/htmlformentryui/htmlform/${uiPage}.page?`;
|
|
101
|
+
const searchParams = new URLSearchParams();
|
|
102
|
+
searchParams.append('patientId', patientUuid);
|
|
103
|
+
if (visitUuid) {
|
|
104
|
+
searchParams.append('visitId', visitUuid);
|
|
105
|
+
}
|
|
106
|
+
if (encounterUuid) {
|
|
107
|
+
searchParams.append('encounterId', encounterUuid);
|
|
108
|
+
}
|
|
109
|
+
if (htmlForm.formUiResource) {
|
|
110
|
+
searchParams.append('definitionUiResource', htmlForm.formUiResource);
|
|
111
|
+
} else {
|
|
112
|
+
searchParams.append('formUuid', htmlForm.formUuid);
|
|
113
|
+
}
|
|
114
|
+
searchParams.append('returnUrl', 'post-message:close-workspace');
|
|
115
|
+
return url + searchParams;
|
|
116
|
+
}, [encounterUuid, htmlForm, patientUuid, visitUuid]);
|
|
117
|
+
|
|
118
|
+
const showFormAndLoadedData = form && patientUuid;
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<Workspace2 title={form.display ?? t('clinicalForm', 'Clinical form')} hasUnsavedChanges={hasUnsavedChanges}>
|
|
122
|
+
<div>
|
|
123
|
+
<ExtensionSlot name="visit-context-header-slot" state={{ patientUuid }} />
|
|
124
|
+
{showFormAndLoadedData &&
|
|
125
|
+
(isHtmlForm ? (
|
|
126
|
+
<HtmlFormEntryWrapper
|
|
127
|
+
src={htmlFormEntryUrl}
|
|
128
|
+
closeWorkspaceWithSavedChanges={() => closeWorkspace({ discardUnsavedChanges: true })}
|
|
129
|
+
/>
|
|
130
|
+
) : (
|
|
131
|
+
<ExtensionSlot key={state.formUuid} name="form-widget-slot" state={state} />
|
|
132
|
+
))}
|
|
133
|
+
</div>
|
|
134
|
+
</Workspace2>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export default FormEntry;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type HtmlFormEntryForm } from '@openmrs/esm-patient-common-lib';
|
|
2
|
+
import { type Form } from '../types';
|
|
3
|
+
|
|
4
|
+
const formEngineResourceName = 'formEngine';
|
|
5
|
+
const htmlformentryFormEngine = 'htmlformentry';
|
|
6
|
+
const uiStyleResourceName = 'uiStyle';
|
|
7
|
+
const uiStyleSimple = 'simple';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* For a given form , check if it is an HTML form. If it is, return the HtmlFormEntryForm object,
|
|
11
|
+
* otherwise return null
|
|
12
|
+
* @param form
|
|
13
|
+
* @param htmlFormEntryForms A list of HTML forms configured in @esm-patient-forms-app's config
|
|
14
|
+
*
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
17
|
+
export function toHtmlForm(form: Form, htmlFormEntryForms: Array<HtmlFormEntryForm>): HtmlFormEntryForm {
|
|
18
|
+
const isHtmlForm =
|
|
19
|
+
htmlFormEntryForms?.some((hfeForm) => hfeForm.formUuid === form.uuid) ||
|
|
20
|
+
form.resources?.some((resource) => {
|
|
21
|
+
return resource.name === formEngineResourceName && resource.valueReference === htmlformentryFormEngine;
|
|
22
|
+
});
|
|
23
|
+
if (isHtmlForm) {
|
|
24
|
+
const hfeForm = htmlFormEntryForms?.find((f) => f.formUuid === form.uuid);
|
|
25
|
+
const simple = form.resources?.some((r) => r.name === uiStyleResourceName && r.valueReference === uiStyleSimple);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
formUuid: form.uuid,
|
|
29
|
+
formName: hfeForm?.formName ?? form.display ?? form.name,
|
|
30
|
+
formUiResource: hfeForm?.formUiResource,
|
|
31
|
+
formUiPage: hfeForm?.formUiPage ?? (simple ? 'enterHtmlFormWithSimpleUi' : 'enterHtmlFormWithStandardUi'),
|
|
32
|
+
formEditUiPage: hfeForm?.formEditUiPage ?? (simple ? 'editHtmlFormWithSimpleUi' : 'editHtmlFormWithStandardUi'),
|
|
33
|
+
};
|
|
34
|
+
} else {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,39 +1,25 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
3
|
import { BehaviorSubject } from 'rxjs';
|
|
4
|
-
import { ExtensionSlot, useConnectivity
|
|
4
|
+
import { ExtensionSlot, useConnectivity } from '@openmrs/esm-framework';
|
|
5
5
|
import { mockPatient } from 'tools';
|
|
6
|
-
import FormEntry from './form-entry.
|
|
6
|
+
import FormEntry, { type FormEntryProps } from './form-entry.component';
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
const defaultProps: FormEntryProps = {
|
|
9
|
+
form: {
|
|
10
|
+
uuid: 'some-form-uuid',
|
|
11
|
+
name: '',
|
|
12
|
+
version: '',
|
|
13
|
+
published: false,
|
|
14
|
+
retired: false,
|
|
15
|
+
resources: [],
|
|
15
16
|
},
|
|
16
|
-
|
|
17
|
-
startDatetime: '2021-03-16T08:16:00.000+0000',
|
|
18
|
-
stopDatetime: null,
|
|
19
|
-
location: {
|
|
20
|
-
uuid: '6351fcf4-e311-4a19-90f9-35667d99a8af',
|
|
21
|
-
name: 'Registration Desk',
|
|
22
|
-
display: 'Registration Desk',
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const testProps = {
|
|
27
|
-
closeWorkspace: jest.fn(),
|
|
28
|
-
closeWorkspaceWithSavedChanges: jest.fn(),
|
|
29
|
-
promptBeforeClosing: jest.fn(),
|
|
17
|
+
encounterUuid: 'some-encounter-uuid',
|
|
30
18
|
patientUuid: mockPatient.id,
|
|
31
19
|
patient: mockPatient,
|
|
32
|
-
|
|
33
|
-
mutateForm: jest.fn(),
|
|
34
|
-
setTitle: jest.fn(),
|
|
35
|
-
visitContext: mockCurrentVisit,
|
|
20
|
+
visitContext: null,
|
|
36
21
|
mutateVisitContext: null,
|
|
22
|
+
closeWorkspace: jest.fn(),
|
|
37
23
|
};
|
|
38
24
|
|
|
39
25
|
const mockFormEntrySub = jest.fn();
|
|
@@ -49,7 +35,7 @@ describe('FormEntry', () => {
|
|
|
49
35
|
);
|
|
50
36
|
mockExtensionSlot.mockImplementation((ext) => ext.name as any);
|
|
51
37
|
|
|
52
|
-
render(<FormEntry {...
|
|
38
|
+
render(<FormEntry {...defaultProps} />);
|
|
53
39
|
|
|
54
40
|
await screen.findByText(/form-widget-slot/);
|
|
55
41
|
expect(screen.getByText(/form-widget-slot/)).toBeInTheDocument();
|
|
@@ -1,108 +1,34 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
clinicalFormsWorkspace,
|
|
6
|
-
invalidateVisitAndEncounterData,
|
|
7
|
-
type DefaultPatientWorkspaceProps,
|
|
8
|
-
type FormEntryProps,
|
|
9
|
-
} from '@openmrs/esm-patient-common-lib';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type Form, type PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib';
|
|
3
|
+
import FormEntry from './form-entry.component';
|
|
10
4
|
|
|
11
|
-
interface
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
clinicalFormsWorkspaceName?: string;
|
|
5
|
+
interface FormEntryWorkspaceProps {
|
|
6
|
+
form: Form;
|
|
7
|
+
encounterUuid: string;
|
|
15
8
|
}
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
/**
|
|
11
|
+
* This workspace renders a React or HTML form to be filled out for a given patient.
|
|
12
|
+
*
|
|
13
|
+
* This workspace must only be used within the patient chart.
|
|
14
|
+
* @see exported-form-entry.workspace.tsx
|
|
15
|
+
*/
|
|
16
|
+
const FormEntryWorkspace: React.FC<PatientWorkspace2DefinitionProps<FormEntryWorkspaceProps, {}>> = ({
|
|
21
17
|
closeWorkspace,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
mutateForm,
|
|
25
|
-
formInfo,
|
|
26
|
-
visitContext,
|
|
27
|
-
mutateVisitContext,
|
|
18
|
+
workspaceProps: { form, encounterUuid },
|
|
19
|
+
groupProps: { patientUuid, patient, visitContext, mutateVisitContext },
|
|
28
20
|
}) => {
|
|
29
|
-
const { encounterUuid, formUuid, visitStartDatetime, visitStopDatetime, visitTypeUuid, visitUuid, additionalProps } =
|
|
30
|
-
formInfo || {};
|
|
31
|
-
const [showForm, setShowForm] = useState(true);
|
|
32
|
-
const isOnline = useConnectivity();
|
|
33
|
-
const { mutate: globalMutate } = useSWRConfig();
|
|
34
|
-
|
|
35
|
-
const state = useMemo(
|
|
36
|
-
() => ({
|
|
37
|
-
view: 'form',
|
|
38
|
-
formUuid: formUuid ?? null,
|
|
39
|
-
visitUuid: visitUuid ?? visitContext?.uuid ?? null,
|
|
40
|
-
visitTypeUuid: visitTypeUuid ?? visitContext?.visitType?.uuid ?? null,
|
|
41
|
-
visitStartDatetime: visitStartDatetime ?? visitContext?.startDatetime ?? null,
|
|
42
|
-
visitStopDatetime: visitStopDatetime ?? visitContext?.stopDatetime ?? null,
|
|
43
|
-
isOffline: !isOnline,
|
|
44
|
-
patientUuid: patientUuid ?? null,
|
|
45
|
-
patient,
|
|
46
|
-
encounterUuid: encounterUuid ?? null,
|
|
47
|
-
closeWorkspace: () => {
|
|
48
|
-
typeof mutateForm === 'function' && mutateForm();
|
|
49
|
-
closeWorkspace();
|
|
50
|
-
},
|
|
51
|
-
closeWorkspaceWithSavedChanges: () => {
|
|
52
|
-
typeof mutateForm === 'function' && mutateForm();
|
|
53
|
-
// Update current visit data for critical components
|
|
54
|
-
mutateVisitContext?.();
|
|
55
|
-
|
|
56
|
-
// Also invalidate visit history and encounter tables since form submission may create/update encounters
|
|
57
|
-
invalidateVisitAndEncounterData(globalMutate, patientUuid);
|
|
58
|
-
|
|
59
|
-
closeWorkspaceWithSavedChanges();
|
|
60
|
-
},
|
|
61
|
-
promptBeforeClosing,
|
|
62
|
-
additionalProps,
|
|
63
|
-
clinicalFormsWorkspaceName,
|
|
64
|
-
}),
|
|
65
|
-
[
|
|
66
|
-
additionalProps,
|
|
67
|
-
clinicalFormsWorkspaceName,
|
|
68
|
-
closeWorkspace,
|
|
69
|
-
closeWorkspaceWithSavedChanges,
|
|
70
|
-
visitContext?.startDatetime,
|
|
71
|
-
visitContext?.stopDatetime,
|
|
72
|
-
visitContext?.uuid,
|
|
73
|
-
visitContext?.visitType?.uuid,
|
|
74
|
-
encounterUuid,
|
|
75
|
-
formUuid,
|
|
76
|
-
globalMutate,
|
|
77
|
-
isOnline,
|
|
78
|
-
mutateVisitContext,
|
|
79
|
-
mutateForm,
|
|
80
|
-
patient,
|
|
81
|
-
patientUuid,
|
|
82
|
-
promptBeforeClosing,
|
|
83
|
-
visitStartDatetime,
|
|
84
|
-
visitStopDatetime,
|
|
85
|
-
visitTypeUuid,
|
|
86
|
-
visitUuid,
|
|
87
|
-
],
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
// FIXME: This logic triggers a reload of the form when the formUuid changes. It's a workaround for the fact that the form doesn't reload when the formUuid changes.
|
|
91
|
-
useEffect(() => {
|
|
92
|
-
if (state.formUuid) {
|
|
93
|
-
setShowForm(false);
|
|
94
|
-
setTimeout(() => {
|
|
95
|
-
setShowForm(true);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}, [state]);
|
|
99
|
-
|
|
100
21
|
return (
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
22
|
+
<FormEntry
|
|
23
|
+
form={form}
|
|
24
|
+
encounterUuid={encounterUuid}
|
|
25
|
+
patient={patient}
|
|
26
|
+
patientUuid={patientUuid}
|
|
27
|
+
visitContext={visitContext}
|
|
28
|
+
mutateVisitContext={mutateVisitContext}
|
|
29
|
+
closeWorkspace={closeWorkspace}
|
|
30
|
+
/>
|
|
105
31
|
);
|
|
106
32
|
};
|
|
107
33
|
|
|
108
|
-
export default
|
|
34
|
+
export default FormEntryWorkspace;
|