@openmrs/esm-patient-programs-app 3.2.1-pre.97 → 4.0.1-pre.221
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 +34 -35
- package/dist/109.js +1 -0
- package/dist/13.js +1 -1
- package/dist/145.js +1 -1
- package/dist/154.js +2 -0
- package/dist/154.js.LICENSE.txt +53 -0
- package/dist/238.js +2 -0
- package/dist/{707.js.LICENSE.txt → 238.js.LICENSE.txt} +0 -0
- package/dist/340.js +1 -0
- package/dist/435.js +1 -0
- package/dist/461.js +1 -0
- package/dist/574.js +1 -1
- package/dist/709.js +1 -0
- package/dist/784.js +1 -1
- package/dist/784.js.LICENSE.txt +2 -7
- package/dist/858.js +1 -1
- package/dist/858.js.LICENSE.txt +2 -0
- package/dist/main.js +1 -0
- package/dist/openmrs-esm-patient-programs-app.js +1 -1
- package/dist/openmrs-esm-patient-programs-app.js.buildmanifest.json +116 -187
- package/dist/openmrs-esm-patient-programs-app.old +1 -1
- package/package.json +7 -11
- package/src/config-schema.ts +12 -0
- package/src/dashboard.meta.tsx +1 -1
- package/src/index.ts +3 -2
- package/src/programs/program-action-button/program-action-button.component.tsx +47 -0
- package/src/programs/programs-detailed-summary.component.tsx +68 -37
- package/src/programs/programs-detailed-summary.scss +1 -4
- package/src/programs/programs-detailed-summary.test.tsx +26 -7
- package/src/programs/programs-form.component.tsx +102 -46
- package/src/programs/programs-form.scss +1 -9
- package/src/programs/programs-form.test.tsx +113 -110
- package/src/programs/programs-overview.component.tsx +59 -45
- package/src/programs/programs-overview.scss +1 -3
- package/src/programs/programs-overview.test.tsx +6 -7
- package/src/programs/programs.resource.tsx +63 -8
- package/src/types/index.ts +16 -2
- package/translations/en.json +8 -2
- package/dist/142.js +0 -1
- package/dist/186.js +0 -2
- package/dist/186.js.LICENSE.txt +0 -23
- package/dist/252.js +0 -2
- package/dist/252.js.LICENSE.txt +0 -23
- package/dist/336.js +0 -2
- package/dist/336.js.LICENSE.txt +0 -20
- package/dist/373.js +0 -1
- package/dist/402.js +0 -1
- package/dist/635.js +0 -2
- package/dist/635.js.LICENSE.txt +0 -16
- package/dist/692.js +0 -1
- package/dist/701.js +0 -1
- package/dist/707.js +0 -2
- package/dist/76.js +0 -1
- package/dist/932.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-patient-programs-app",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1-pre.221",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "Patient programs microfrontend for the OpenMRS SPA",
|
|
6
6
|
"browser": "dist/openmrs-esm-patient-programs-app.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"debug": "npm run serve",
|
|
13
13
|
"build": "webpack --mode production",
|
|
14
14
|
"analyze": "webpack --mode=production --env analyze=true",
|
|
15
|
-
"lint": "eslint src --ext tsx,ts",
|
|
15
|
+
"lint": "eslint src --ext tsx,ts --fix --max-warnings=0",
|
|
16
16
|
"typescript": "tsc",
|
|
17
17
|
"extract-translations": "i18next 'src/**/*.component.tsx' --config ../../tools/i18next-parser.config.js"
|
|
18
18
|
},
|
|
@@ -34,23 +34,19 @@
|
|
|
34
34
|
"url": "https://github.com/openmrs/openmrs-esm-patient-chart/issues"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@carbon/
|
|
38
|
-
"@
|
|
39
|
-
"@openmrs/esm-patient-common-lib": "^3.2.1-pre.97",
|
|
40
|
-
"carbon-components-react": "^7.25.0",
|
|
37
|
+
"@carbon/react": "^1.8.0",
|
|
38
|
+
"@openmrs/esm-patient-common-lib": "^4.0.1-pre.221",
|
|
41
39
|
"d3": "^7.0.3",
|
|
42
40
|
"lodash-es": "^4.17.15"
|
|
43
41
|
},
|
|
44
42
|
"peerDependencies": {
|
|
45
43
|
"@openmrs/esm-framework": "3.x",
|
|
46
44
|
"@openmrs/esm-patient-common-lib": "3.x",
|
|
47
|
-
"carbon-components": "10.x",
|
|
48
|
-
"carbon-icons": "7.x",
|
|
49
45
|
"dayjs": "1.x",
|
|
50
|
-
"react": "
|
|
46
|
+
"react": "^18.2.0",
|
|
51
47
|
"react-i18next": "11.x",
|
|
52
|
-
"react-router-dom": "
|
|
48
|
+
"react-router-dom": "6.x",
|
|
53
49
|
"rxjs": "6.x"
|
|
54
50
|
},
|
|
55
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "4823a7e6eec21a1a4491a545f1e54ea34b64c2c9"
|
|
56
52
|
}
|
package/src/dashboard.meta.tsx
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle } from '@openmrs/esm-framework';
|
|
2
2
|
import { createDashboardLink } from '@openmrs/esm-patient-common-lib';
|
|
3
|
+
import { configSchema } from './config-schema';
|
|
3
4
|
import { dashboardMeta } from './dashboard.meta';
|
|
4
5
|
|
|
5
6
|
const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
@@ -16,7 +17,7 @@ function setupOpenMRS() {
|
|
|
16
17
|
moduleName,
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
defineConfigSchema(moduleName,
|
|
20
|
+
defineConfigSchema(moduleName, configSchema);
|
|
20
21
|
|
|
21
22
|
return {
|
|
22
23
|
extensions: [
|
|
@@ -48,7 +49,7 @@ function setupOpenMRS() {
|
|
|
48
49
|
name: 'programs-form-workspace',
|
|
49
50
|
load: getAsyncLifecycle(() => import('./programs/programs-form.component'), options),
|
|
50
51
|
meta: {
|
|
51
|
-
title: 'Record
|
|
52
|
+
title: 'Record program enrollment',
|
|
52
53
|
},
|
|
53
54
|
},
|
|
54
55
|
],
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button } from '@carbon/react';
|
|
4
|
+
import { TaskAdd } from '@carbon/react/icons';
|
|
5
|
+
import { formEntrySub, launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
|
|
6
|
+
import { ConfigurableProgram } from '../../types';
|
|
7
|
+
|
|
8
|
+
interface ProgramActionButton {
|
|
9
|
+
enrollment: ConfigurableProgram;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ProgramActionButton: React.FC<ProgramActionButton> = ({ enrollment }) => {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
const formUuid =
|
|
15
|
+
enrollment.enrollmentStatus === 'eligible' ? enrollment.enrollmentFormUuid : enrollment.discontinuationFormUuid;
|
|
16
|
+
const launchEnrollmentForm = (enrollmentStatus: string) => {
|
|
17
|
+
formEntrySub.next({ formUuid, encounterUuid: '' });
|
|
18
|
+
launchPatientWorkspace('patient-form-entry-workspace', {
|
|
19
|
+
workspaceTitle: `${enrollment?.display} ${enrollmentStatus} Form`,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (enrollment.enrollmentStatus === 'eligible') {
|
|
24
|
+
return (
|
|
25
|
+
<Button
|
|
26
|
+
onClick={() => launchEnrollmentForm(t('enrollment', 'Enrollment'))}
|
|
27
|
+
renderIcon={(props) => <TaskAdd size={20} {...props} />}
|
|
28
|
+
kind="tertiary"
|
|
29
|
+
size="small"
|
|
30
|
+
>
|
|
31
|
+
{t('enroll', 'Enroll')}
|
|
32
|
+
</Button>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return (
|
|
36
|
+
<Button
|
|
37
|
+
onClick={() => launchEnrollmentForm(t('discontinue', 'Discontinue'))}
|
|
38
|
+
renderIcon={(props) => <TaskAdd size={20} {...props} />}
|
|
39
|
+
kind="danger--ghost"
|
|
40
|
+
size="small"
|
|
41
|
+
>
|
|
42
|
+
{t('discontinue', 'Discontinue')}
|
|
43
|
+
</Button>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default ProgramActionButton;
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import Add16 from '@carbon/icons-react/es/add/16';
|
|
3
|
-
import filter from 'lodash-es/filter';
|
|
4
|
-
import includes from 'lodash-es/includes';
|
|
5
|
-
import map from 'lodash-es/map';
|
|
6
|
-
import styles from './programs-detailed-summary.scss';
|
|
7
|
-
import { CardHeader, EmptyState, ErrorState, launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
|
|
8
2
|
import { useTranslation } from 'react-i18next';
|
|
9
|
-
import
|
|
3
|
+
import capitalize from 'lodash-es/capitalize';
|
|
10
4
|
import {
|
|
11
5
|
Button,
|
|
12
6
|
DataTable,
|
|
7
|
+
DataTableHeader,
|
|
8
|
+
DataTableRow,
|
|
13
9
|
DataTableSkeleton,
|
|
14
10
|
InlineLoading,
|
|
11
|
+
InlineNotification,
|
|
15
12
|
Table,
|
|
13
|
+
TableBody,
|
|
16
14
|
TableCell,
|
|
17
15
|
TableContainer,
|
|
18
|
-
TableBody,
|
|
19
16
|
TableHead,
|
|
20
17
|
TableHeader,
|
|
21
18
|
TableRow,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from '
|
|
26
|
-
import {
|
|
19
|
+
} from '@carbon/react';
|
|
20
|
+
import { Add, Edit } from '@carbon/react/icons';
|
|
21
|
+
import { formatDate, formatDatetime, useConfig, usePagination, ConfigObject } from '@openmrs/esm-framework';
|
|
22
|
+
import { CardHeader, EmptyState, ErrorState, launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
|
|
23
|
+
import { useAvailablePrograms, useEnrollments, usePrograms } from './programs.resource';
|
|
24
|
+
import { ConfigurableProgram } from '../types';
|
|
25
|
+
import ProgramActionButton from './program-action-button/program-action-button.component';
|
|
26
|
+
import styles from './programs-detailed-summary.scss';
|
|
27
27
|
|
|
28
28
|
interface ProgramsDetailedSummaryProps {
|
|
29
29
|
patientUuid: string;
|
|
@@ -33,13 +33,18 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = ({ patie
|
|
|
33
33
|
const { t } = useTranslation();
|
|
34
34
|
const displayText = t('programEnrollments', 'Program enrollments');
|
|
35
35
|
const headerTitle = t('carePrograms', 'Care Programs');
|
|
36
|
+
const config = useConfig() as ConfigObject;
|
|
37
|
+
const programsCount = 5;
|
|
38
|
+
const isConfigurable = config.customUrl ? true : false;
|
|
36
39
|
|
|
37
|
-
const {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
const { enrollments, isLoading, isError, isValidating, availablePrograms, eligiblePrograms, configurablePrograms } =
|
|
41
|
+
usePrograms(patientUuid);
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
results: paginatedEnrollments,
|
|
45
|
+
goTo,
|
|
46
|
+
currentPage,
|
|
47
|
+
} = usePagination(isConfigurable ? configurablePrograms : enrollments ?? [], programsCount);
|
|
43
48
|
|
|
44
49
|
const tableHeaders: Array<DataTableHeader> = React.useMemo(
|
|
45
50
|
() => [
|
|
@@ -59,11 +64,15 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = ({ patie
|
|
|
59
64
|
key: 'status',
|
|
60
65
|
header: t('status', 'Status'),
|
|
61
66
|
},
|
|
67
|
+
{
|
|
68
|
+
key: 'actions',
|
|
69
|
+
header: t('actions', 'Actions'),
|
|
70
|
+
},
|
|
62
71
|
],
|
|
63
72
|
[t],
|
|
64
73
|
);
|
|
65
74
|
|
|
66
|
-
const tableRows
|
|
75
|
+
const tableRows = React.useMemo(() => {
|
|
67
76
|
return enrollments?.map((program) => {
|
|
68
77
|
return {
|
|
69
78
|
id: program.uuid,
|
|
@@ -73,6 +82,7 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = ({ patie
|
|
|
73
82
|
status: program.dateCompleted
|
|
74
83
|
? `${t('completedOn', 'Completed On')} ${formatDate(new Date(program.dateCompleted))}`
|
|
75
84
|
: t('active', 'Active'),
|
|
85
|
+
actions: <ProgramEditButton programEnrollmentId={program.uuid} />,
|
|
76
86
|
};
|
|
77
87
|
});
|
|
78
88
|
}, [enrollments, t]);
|
|
@@ -88,7 +98,7 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = ({ patie
|
|
|
88
98
|
<span>{isValidating ? <InlineLoading /> : null}</span>
|
|
89
99
|
<Button
|
|
90
100
|
kind="ghost"
|
|
91
|
-
renderIcon={
|
|
101
|
+
renderIcon={(props) => <Add size={16} {...props} />}
|
|
92
102
|
iconDescription="Add programs"
|
|
93
103
|
onClick={launchProgramsForm}
|
|
94
104
|
disabled={availablePrograms?.length && eligiblePrograms?.length === 0}
|
|
@@ -96,19 +106,19 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = ({ patie
|
|
|
96
106
|
{t('add', 'Add')}
|
|
97
107
|
</Button>
|
|
98
108
|
</CardHeader>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<Table {...getTableProps()}
|
|
109
|
+
{availablePrograms?.length && eligiblePrograms?.length === 0 ? (
|
|
110
|
+
<InlineNotification
|
|
111
|
+
style={{ minWidth: '100%', margin: '0rem', padding: '0rem' }}
|
|
112
|
+
kind={'info'}
|
|
113
|
+
lowContrast
|
|
114
|
+
subtitle={t('noEligibleEnrollments', 'There are no more programs left to enroll this patient in')}
|
|
115
|
+
title={t('fullyEnrolled', 'Enrolled in all programs')}
|
|
116
|
+
/>
|
|
117
|
+
) : null}
|
|
118
|
+
<DataTable rows={tableRows} headers={tableHeaders} isSortable size="xs" useZebraStyles>
|
|
119
|
+
{({ rows, headers, getHeaderProps, getTableProps }) => (
|
|
120
|
+
<TableContainer>
|
|
121
|
+
<Table {...getTableProps()}>
|
|
112
122
|
<TableHead>
|
|
113
123
|
<TableRow>
|
|
114
124
|
{headers.map((header) => (
|
|
@@ -134,13 +144,34 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = ({ patie
|
|
|
134
144
|
))}
|
|
135
145
|
</TableBody>
|
|
136
146
|
</Table>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
</
|
|
147
|
+
</TableContainer>
|
|
148
|
+
)}
|
|
149
|
+
</DataTable>
|
|
140
150
|
</div>
|
|
141
151
|
);
|
|
142
152
|
}
|
|
143
153
|
return <EmptyState displayText={displayText} headerTitle={headerTitle} launchForm={launchProgramsForm} />;
|
|
144
154
|
};
|
|
145
155
|
|
|
156
|
+
interface ProgramEditButtonProps {
|
|
157
|
+
programEnrollmentId: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function ProgramEditButton({ programEnrollmentId }: ProgramEditButtonProps) {
|
|
161
|
+
const launchEditProgramsForm = React.useCallback(
|
|
162
|
+
() => launchPatientWorkspace('programs-form-workspace', { programEnrollmentId }),
|
|
163
|
+
[programEnrollmentId],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<Button
|
|
168
|
+
kind="ghost"
|
|
169
|
+
renderIcon={(props) => <Edit size={16} {...props} />}
|
|
170
|
+
iconDescription="Edit Program"
|
|
171
|
+
onClick={launchEditProgramsForm}
|
|
172
|
+
hasIconOnly
|
|
173
|
+
/>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
146
177
|
export default ProgramsDetailedSummary;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { screen } from '@testing-library/react';
|
|
2
|
+
import { screen, within, waitFor } from '@testing-library/react';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { openmrsFetch, usePagination } from '@openmrs/esm-framework';
|
|
5
|
+
import { launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
|
|
6
|
+
import { mockPatient } from '../../../../__mocks__/patient.mock';
|
|
5
7
|
import { mockEnrolledProgramsResponse } from '../../../../__mocks__/programs.mock';
|
|
6
8
|
import { renderWithSwr, waitForLoadingToFinish } from '../../../../tools/test-helpers';
|
|
7
|
-
import { launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
|
|
8
9
|
import ProgramsDetailedSummary from './programs-detailed-summary.component';
|
|
9
10
|
|
|
11
|
+
jest.setTimeout(20000);
|
|
12
|
+
|
|
10
13
|
const mockOpenmrsFetch = openmrsFetch as jest.Mock;
|
|
11
14
|
const mockUsePagination = usePagination as jest.Mock;
|
|
12
15
|
|
|
@@ -70,6 +73,8 @@ describe('ProgramsDetailedSummary ', () => {
|
|
|
70
73
|
});
|
|
71
74
|
|
|
72
75
|
it("renders a detailed tabular summary of the patient's program enrollments", async () => {
|
|
76
|
+
const user = userEvent.setup();
|
|
77
|
+
|
|
73
78
|
mockOpenmrsFetch.mockReturnValueOnce({ data: { results: mockEnrolledProgramsResponse } });
|
|
74
79
|
mockUsePagination.mockImplementation(() => ({
|
|
75
80
|
currentPage: 1,
|
|
@@ -89,16 +94,30 @@ describe('ProgramsDetailedSummary ', () => {
|
|
|
89
94
|
|
|
90
95
|
const addButton = screen.getByRole('button', { name: /Add/ });
|
|
91
96
|
expect(addButton).toBeInTheDocument();
|
|
92
|
-
|
|
93
|
-
expect(
|
|
94
|
-
expect(
|
|
97
|
+
const row = screen.getByRole('row', { name: /hiv care and treatment/i });
|
|
98
|
+
expect(row).toBeInTheDocument();
|
|
99
|
+
expect(within(row).getByRole('cell', { name: /16-Jan-2020/i })).toBeInTheDocument();
|
|
100
|
+
expect(within(row).getByRole('cell', { name: /active$/i })).toBeInTheDocument();
|
|
101
|
+
const editButton = within(row).getByRole('button', { name: /Edit Program$/i });
|
|
102
|
+
expect(editButton).toBeInTheDocument();
|
|
95
103
|
|
|
96
104
|
// Clicking "Add" launches the programs form in a workspace
|
|
97
|
-
|
|
105
|
+
await waitFor(() => user.click(addButton));
|
|
106
|
+
|
|
98
107
|
expect(launchPatientWorkspace).toHaveBeenCalledWith('programs-form-workspace');
|
|
108
|
+
|
|
109
|
+
// Clicking the edit button launches the edit form in a workspace
|
|
110
|
+
await waitFor(() => user.click(editButton));
|
|
111
|
+
|
|
112
|
+
expect(launchPatientWorkspace).toHaveBeenCalledWith('programs-form-workspace', {
|
|
113
|
+
programEnrollmentId: mockEnrolledProgramsResponse[0].uuid,
|
|
114
|
+
});
|
|
99
115
|
});
|
|
100
116
|
});
|
|
101
117
|
|
|
102
118
|
function renderProgramsOverview() {
|
|
103
|
-
|
|
119
|
+
const testProps = {
|
|
120
|
+
patientUuid: mockPatient.id,
|
|
121
|
+
};
|
|
122
|
+
renderWithSwr(<ProgramsDetailedSummary {...testProps} />);
|
|
104
123
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React, { SyntheticEvent } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
2
3
|
import dayjs from 'dayjs';
|
|
3
4
|
import filter from 'lodash-es/filter';
|
|
4
5
|
import includes from 'lodash-es/includes';
|
|
5
6
|
import map from 'lodash-es/map';
|
|
6
7
|
import { useSWRConfig } from 'swr';
|
|
7
|
-
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import {
|
|
9
9
|
createErrorHandler,
|
|
10
10
|
showNotification,
|
|
@@ -12,27 +12,34 @@ import {
|
|
|
12
12
|
useSession,
|
|
13
13
|
useLocations,
|
|
14
14
|
useLayoutType,
|
|
15
|
+
parseDate,
|
|
15
16
|
} from '@openmrs/esm-framework';
|
|
16
17
|
import {
|
|
17
18
|
Button,
|
|
19
|
+
ButtonSet,
|
|
18
20
|
DatePicker,
|
|
19
21
|
DatePickerInput,
|
|
20
|
-
Select,
|
|
21
|
-
SelectItem,
|
|
22
22
|
Form,
|
|
23
23
|
FormGroup,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Select,
|
|
25
|
+
SelectItem,
|
|
26
|
+
Stack,
|
|
27
|
+
} from '@carbon/react';
|
|
26
28
|
import {
|
|
27
29
|
createProgramEnrollment,
|
|
28
30
|
useAvailablePrograms,
|
|
29
31
|
useEnrollments,
|
|
30
32
|
customRepresentation,
|
|
33
|
+
updateProgramEnrollment,
|
|
31
34
|
} from './programs.resource';
|
|
32
35
|
import { DefaultWorkspaceProps } from '@openmrs/esm-patient-common-lib';
|
|
33
36
|
import styles from './programs-form.scss';
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
interface ProgramsFormProps extends DefaultWorkspaceProps {
|
|
39
|
+
programEnrollmentId?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const ProgramsForm: React.FC<ProgramsFormProps> = ({ closeWorkspace, patientUuid, programEnrollmentId }) => {
|
|
36
43
|
const { t } = useTranslation();
|
|
37
44
|
const isTablet = useLayoutType() === 'tablet';
|
|
38
45
|
const session = useSession();
|
|
@@ -41,16 +48,25 @@ const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patient
|
|
|
41
48
|
|
|
42
49
|
const { data: availablePrograms } = useAvailablePrograms();
|
|
43
50
|
const { data: enrollments } = useEnrollments(patientUuid);
|
|
51
|
+
const currentEnrollment = programEnrollmentId && enrollments.filter((e) => e.uuid == programEnrollmentId)[0];
|
|
52
|
+
const currentProgram = currentEnrollment
|
|
53
|
+
? {
|
|
54
|
+
display: currentEnrollment.program.name,
|
|
55
|
+
...currentEnrollment.program,
|
|
56
|
+
}
|
|
57
|
+
: null;
|
|
44
58
|
|
|
45
|
-
const eligiblePrograms =
|
|
46
|
-
|
|
47
|
-
(program) => !includes(map(enrollments, 'program.uuid'), program.uuid)
|
|
59
|
+
const eligiblePrograms = currentProgram
|
|
60
|
+
? [currentProgram]
|
|
61
|
+
: filter(availablePrograms, (program) => !includes(map(enrollments, 'program.uuid'), program.uuid));
|
|
62
|
+
const [completionDate, setCompletionDate] = React.useState<Date>(
|
|
63
|
+
currentEnrollment?.dateCompleted ? parseDate(currentEnrollment.dateCompleted) : null,
|
|
48
64
|
);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const [selectedProgram, setSelectedProgram] = React.useState<string>('');
|
|
53
|
-
const [userLocation, setUserLocation] = React.useState('');
|
|
65
|
+
const [enrollmentDate, setEnrollmentDate] = React.useState<Date>(
|
|
66
|
+
currentEnrollment?.dateEnrolled ? parseDate(currentEnrollment.dateEnrolled) : new Date(),
|
|
67
|
+
);
|
|
68
|
+
const [selectedProgram, setSelectedProgram] = React.useState<string>(currentEnrollment?.program.uuid ?? '');
|
|
69
|
+
const [userLocation, setUserLocation] = React.useState<string>(currentEnrollment?.location.uuid ?? '');
|
|
54
70
|
|
|
55
71
|
if (!userLocation && session?.sessionLocation?.uuid) {
|
|
56
72
|
setUserLocation(session?.sessionLocation?.uuid);
|
|
@@ -71,43 +87,83 @@ const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patient
|
|
|
71
87
|
};
|
|
72
88
|
|
|
73
89
|
const abortController = new AbortController();
|
|
74
|
-
const sub =
|
|
75
|
-
(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
const sub = currentEnrollment
|
|
91
|
+
? updateProgramEnrollment(currentEnrollment.uuid, payload, abortController).subscribe(
|
|
92
|
+
(response) => {
|
|
93
|
+
if (response.status === 200) {
|
|
94
|
+
closeWorkspace();
|
|
95
|
+
|
|
96
|
+
showToast({
|
|
97
|
+
critical: true,
|
|
98
|
+
kind: 'success',
|
|
99
|
+
description: t(
|
|
100
|
+
'enrollmentUpdatesNowVisible',
|
|
101
|
+
'Changes to the program are now visible in the Programs table',
|
|
102
|
+
),
|
|
103
|
+
title: t('enrollmentUpdated', 'Program enrollment updated'),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
mutate(`/ws/rest/v1/programenrollment?patient=${patientUuid}&v=${customRepresentation}`);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
(err) => {
|
|
110
|
+
createErrorHandler();
|
|
111
|
+
|
|
112
|
+
showNotification({
|
|
113
|
+
title: t('programEnrollmentSaveError', 'Error saving program enrollment'),
|
|
114
|
+
kind: 'error',
|
|
115
|
+
critical: true,
|
|
116
|
+
description: err?.message,
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
: createProgramEnrollment(payload, abortController).subscribe(
|
|
121
|
+
(response) => {
|
|
122
|
+
if (response.status === 201) {
|
|
123
|
+
closeWorkspace();
|
|
124
|
+
|
|
125
|
+
showToast({
|
|
126
|
+
critical: true,
|
|
127
|
+
kind: 'success',
|
|
128
|
+
description: t('enrollmentNowVisible', 'It is now visible in the Programs table'),
|
|
129
|
+
title: t('enrollmentSaved', 'Program enrollment saved'),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
mutate(`/ws/rest/v1/programenrollment?patient=${patientUuid}&v=${customRepresentation}`);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
(err) => {
|
|
136
|
+
createErrorHandler();
|
|
137
|
+
|
|
138
|
+
showNotification({
|
|
139
|
+
title: t('programEnrollmentSaveError', 'Error saving program enrollment'),
|
|
140
|
+
kind: 'error',
|
|
141
|
+
critical: true,
|
|
142
|
+
description: err?.message,
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
);
|
|
100
146
|
return () => {
|
|
101
147
|
sub.unsubscribe();
|
|
102
148
|
};
|
|
103
149
|
},
|
|
104
|
-
[
|
|
150
|
+
[
|
|
151
|
+
closeWorkspace,
|
|
152
|
+
completionDate,
|
|
153
|
+
enrollmentDate,
|
|
154
|
+
mutate,
|
|
155
|
+
patientUuid,
|
|
156
|
+
selectedProgram,
|
|
157
|
+
t,
|
|
158
|
+
userLocation,
|
|
159
|
+
currentEnrollment,
|
|
160
|
+
],
|
|
105
161
|
);
|
|
106
162
|
|
|
107
163
|
return (
|
|
108
164
|
<Form className={styles.form} onSubmit={handleSubmit}>
|
|
109
|
-
<
|
|
110
|
-
<FormGroup style={{
|
|
165
|
+
<Stack className={styles.formContainer} gap={7}>
|
|
166
|
+
<FormGroup style={{ maxWidth: '50%' }} legendText={t('programName', 'Program name')}>
|
|
111
167
|
<div className={styles.selectContainer}>
|
|
112
168
|
<Select
|
|
113
169
|
id="program"
|
|
@@ -126,7 +182,7 @@ const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patient
|
|
|
126
182
|
</Select>
|
|
127
183
|
</div>
|
|
128
184
|
</FormGroup>
|
|
129
|
-
<FormGroup legendText={t('dateEnrolled', 'Date enrolled')}>
|
|
185
|
+
<FormGroup style={{ maxWidth: '50%' }} legendText={t('dateEnrolled', 'Date enrolled')}>
|
|
130
186
|
<DatePicker
|
|
131
187
|
id="enrollmentDate"
|
|
132
188
|
datePickerType="single"
|
|
@@ -140,7 +196,7 @@ const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patient
|
|
|
140
196
|
<DatePickerInput id="enrollmentDateInput" labelText="" />
|
|
141
197
|
</DatePicker>
|
|
142
198
|
</FormGroup>
|
|
143
|
-
<FormGroup legendText={t('dateCompleted', 'Date completed')}>
|
|
199
|
+
<FormGroup style={{ width: '50%' }} legendText={t('dateCompleted', 'Date completed')}>
|
|
144
200
|
<DatePicker
|
|
145
201
|
id="completionDate"
|
|
146
202
|
datePickerType="single"
|
|
@@ -173,7 +229,7 @@ const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patient
|
|
|
173
229
|
))}
|
|
174
230
|
</Select>
|
|
175
231
|
</FormGroup>
|
|
176
|
-
</
|
|
232
|
+
</Stack>
|
|
177
233
|
<ButtonSet className={isTablet ? styles.tablet : styles.desktop}>
|
|
178
234
|
<Button className={styles.button} kind="secondary" onClick={() => closeWorkspace()}>
|
|
179
235
|
{t('cancel', 'Cancel')}
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
@import
|
|
2
|
-
@import "~carbon-components/src/globals/scss/vars";
|
|
3
|
-
@import "~carbon-components/src/globals/scss/mixins";
|
|
4
|
-
|
|
5
|
-
.selectContainer {
|
|
6
|
-
display: flex;
|
|
7
|
-
align-items: center;
|
|
8
|
-
}
|
|
1
|
+
@import '~@openmrs/esm-styleguide/src/vars';
|
|
9
2
|
|
|
10
3
|
.loading {
|
|
11
4
|
margin-left: 1rem;
|
|
@@ -13,7 +6,6 @@
|
|
|
13
6
|
min-height: 2.5rem;
|
|
14
7
|
}
|
|
15
8
|
|
|
16
|
-
|
|
17
9
|
.button {
|
|
18
10
|
height: 4rem;
|
|
19
11
|
display: flex;
|