@openmrs/esm-patient-programs-app 3.2.1-pre.97 → 4.0.0
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 +33 -35
- package/dist/238.js +2 -0
- package/dist/{707.js.LICENSE.txt → 238.js.LICENSE.txt} +0 -0
- package/dist/402.js +1 -1
- package/dist/435.js +1 -0
- package/dist/461.js +1 -0
- package/dist/574.js +1 -1
- package/dist/641.js +1 -0
- package/dist/709.js +1 -0
- package/dist/733.js +1 -0
- package/dist/734.js +2 -0
- package/dist/734.js.LICENSE.txt +53 -0
- package/dist/784.js +1 -1
- package/dist/784.js.LICENSE.txt +2 -7
- package/dist/821.js +1 -0
- package/dist/858.js +1 -1
- 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 +134 -205
- package/dist/openmrs-esm-patient-programs-app.old +1 -1
- package/package.json +12 -14
- package/src/config-schema.ts +12 -0
- package/src/dashboard.meta.tsx +1 -1
- package/src/index.ts +8 -3
- package/src/programs/program-action-button/program-action-button.component.tsx +50 -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 +246 -109
- package/src/programs/programs-form.scss +1 -9
- package/src/programs/programs-form.test.tsx +113 -110
- package/src/programs/programs-overview.component.tsx +60 -46
- 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 +10 -2
- package/dist/13.js +0 -1
- package/dist/142.js +0 -1
- package/dist/145.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/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
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import React, { SyntheticEvent } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { useSWRConfig } from 'swr';
|
|
2
4
|
import dayjs from 'dayjs';
|
|
3
5
|
import filter from 'lodash-es/filter';
|
|
4
6
|
import includes from 'lodash-es/includes';
|
|
5
7
|
import map from 'lodash-es/map';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
+
import {
|
|
9
|
+
Button,
|
|
10
|
+
ButtonSet,
|
|
11
|
+
DatePicker,
|
|
12
|
+
DatePickerInput,
|
|
13
|
+
Form,
|
|
14
|
+
FormGroup,
|
|
15
|
+
InlineNotification,
|
|
16
|
+
Layer,
|
|
17
|
+
Select,
|
|
18
|
+
SelectItem,
|
|
19
|
+
Stack,
|
|
20
|
+
} from '@carbon/react';
|
|
8
21
|
import {
|
|
9
22
|
createErrorHandler,
|
|
10
23
|
showNotification,
|
|
@@ -12,27 +25,23 @@ import {
|
|
|
12
25
|
useSession,
|
|
13
26
|
useLocations,
|
|
14
27
|
useLayoutType,
|
|
28
|
+
parseDate,
|
|
15
29
|
} from '@openmrs/esm-framework';
|
|
16
|
-
import {
|
|
17
|
-
Button,
|
|
18
|
-
DatePicker,
|
|
19
|
-
DatePickerInput,
|
|
20
|
-
Select,
|
|
21
|
-
SelectItem,
|
|
22
|
-
Form,
|
|
23
|
-
FormGroup,
|
|
24
|
-
ButtonSet,
|
|
25
|
-
} from 'carbon-components-react';
|
|
30
|
+
import { DefaultWorkspaceProps } from '@openmrs/esm-patient-common-lib';
|
|
26
31
|
import {
|
|
27
32
|
createProgramEnrollment,
|
|
28
33
|
useAvailablePrograms,
|
|
29
34
|
useEnrollments,
|
|
30
35
|
customRepresentation,
|
|
36
|
+
updateProgramEnrollment,
|
|
31
37
|
} from './programs.resource';
|
|
32
|
-
import { DefaultWorkspaceProps } from '@openmrs/esm-patient-common-lib';
|
|
33
38
|
import styles from './programs-form.scss';
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
interface ProgramsFormProps extends DefaultWorkspaceProps {
|
|
41
|
+
programEnrollmentId?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ProgramsForm: React.FC<ProgramsFormProps> = ({ closeWorkspace, patientUuid, programEnrollmentId }) => {
|
|
36
45
|
const { t } = useTranslation();
|
|
37
46
|
const isTablet = useLayoutType() === 'tablet';
|
|
38
47
|
const session = useSession();
|
|
@@ -42,15 +51,26 @@ const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patient
|
|
|
42
51
|
const { data: availablePrograms } = useAvailablePrograms();
|
|
43
52
|
const { data: enrollments } = useEnrollments(patientUuid);
|
|
44
53
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
const currentEnrollment = programEnrollmentId && enrollments.filter((e) => e.uuid == programEnrollmentId)[0];
|
|
55
|
+
const currentProgram = currentEnrollment
|
|
56
|
+
? {
|
|
57
|
+
display: currentEnrollment.program.name,
|
|
58
|
+
...currentEnrollment.program,
|
|
59
|
+
}
|
|
60
|
+
: null;
|
|
61
|
+
|
|
62
|
+
const eligiblePrograms = currentProgram
|
|
63
|
+
? [currentProgram]
|
|
64
|
+
: filter(availablePrograms, (program) => !includes(map(enrollments, 'program.uuid'), program.uuid));
|
|
49
65
|
|
|
50
|
-
const [completionDate, setCompletionDate] = React.useState(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const [
|
|
66
|
+
const [completionDate, setCompletionDate] = React.useState<Date>(
|
|
67
|
+
currentEnrollment?.dateCompleted ? parseDate(currentEnrollment.dateCompleted) : null,
|
|
68
|
+
);
|
|
69
|
+
const [enrollmentDate, setEnrollmentDate] = React.useState<Date>(
|
|
70
|
+
currentEnrollment?.dateEnrolled ? parseDate(currentEnrollment.dateEnrolled) : new Date(),
|
|
71
|
+
);
|
|
72
|
+
const [selectedProgram, setSelectedProgram] = React.useState<string>(currentEnrollment?.program.uuid ?? '');
|
|
73
|
+
const [userLocation, setUserLocation] = React.useState<string>(currentEnrollment?.location.uuid ?? '');
|
|
54
74
|
|
|
55
75
|
if (!userLocation && session?.sessionLocation?.uuid) {
|
|
56
76
|
setUserLocation(session?.sessionLocation?.uuid);
|
|
@@ -71,109 +91,226 @@ const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patient
|
|
|
71
91
|
};
|
|
72
92
|
|
|
73
93
|
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
|
-
|
|
94
|
+
const sub = currentEnrollment
|
|
95
|
+
? updateProgramEnrollment(currentEnrollment.uuid, payload, abortController).subscribe(
|
|
96
|
+
(response) => {
|
|
97
|
+
if (response.status === 200) {
|
|
98
|
+
closeWorkspace();
|
|
99
|
+
|
|
100
|
+
showToast({
|
|
101
|
+
critical: true,
|
|
102
|
+
kind: 'success',
|
|
103
|
+
description: t(
|
|
104
|
+
'enrollmentUpdatesNowVisible',
|
|
105
|
+
'Changes to the program are now visible in the Programs table',
|
|
106
|
+
),
|
|
107
|
+
title: t('enrollmentUpdated', 'Program enrollment updated'),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
mutate(`/ws/rest/v1/programenrollment?patient=${patientUuid}&v=${customRepresentation}`);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
(err) => {
|
|
114
|
+
createErrorHandler();
|
|
115
|
+
|
|
116
|
+
showNotification({
|
|
117
|
+
title: t('programEnrollmentSaveError', 'Error saving program enrollment'),
|
|
118
|
+
kind: 'error',
|
|
119
|
+
critical: true,
|
|
120
|
+
description: err?.message,
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
: createProgramEnrollment(payload, abortController).subscribe(
|
|
125
|
+
(response) => {
|
|
126
|
+
if (response.status === 201) {
|
|
127
|
+
closeWorkspace();
|
|
128
|
+
|
|
129
|
+
showToast({
|
|
130
|
+
critical: true,
|
|
131
|
+
kind: 'success',
|
|
132
|
+
description: t('enrollmentNowVisible', 'It is now visible in the Programs table'),
|
|
133
|
+
title: t('enrollmentSaved', 'Program enrollment saved'),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
mutate(`/ws/rest/v1/programenrollment?patient=${patientUuid}&v=${customRepresentation}`);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
(err) => {
|
|
140
|
+
createErrorHandler();
|
|
141
|
+
|
|
142
|
+
showNotification({
|
|
143
|
+
title: t('programEnrollmentSaveError', 'Error saving program enrollment'),
|
|
144
|
+
kind: 'error',
|
|
145
|
+
critical: true,
|
|
146
|
+
description: err?.message,
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
);
|
|
100
150
|
return () => {
|
|
101
151
|
sub.unsubscribe();
|
|
102
152
|
};
|
|
103
153
|
},
|
|
104
|
-
[
|
|
154
|
+
[
|
|
155
|
+
closeWorkspace,
|
|
156
|
+
completionDate,
|
|
157
|
+
enrollmentDate,
|
|
158
|
+
mutate,
|
|
159
|
+
patientUuid,
|
|
160
|
+
selectedProgram,
|
|
161
|
+
t,
|
|
162
|
+
userLocation,
|
|
163
|
+
currentEnrollment,
|
|
164
|
+
],
|
|
105
165
|
);
|
|
106
166
|
|
|
107
167
|
return (
|
|
108
168
|
<Form className={styles.form} onSubmit={handleSubmit}>
|
|
109
|
-
<
|
|
110
|
-
|
|
169
|
+
<Stack className={styles.formContainer} gap={7}>
|
|
170
|
+
{!eligiblePrograms.length ? (
|
|
171
|
+
<InlineNotification
|
|
172
|
+
style={{ minWidth: '100%', margin: '0rem', padding: '0rem' }}
|
|
173
|
+
kind={'error'}
|
|
174
|
+
lowContrast
|
|
175
|
+
subtitle={t('configurePrograms', 'Please configure programs to continue.')}
|
|
176
|
+
title={t('noProgramsConfigured', 'No programs configured')}
|
|
177
|
+
/>
|
|
178
|
+
) : null}
|
|
179
|
+
<FormGroup style={{ maxWidth: '50%' }} legendText={t('programName', 'Program name')}>
|
|
111
180
|
<div className={styles.selectContainer}>
|
|
181
|
+
{isTablet ? (
|
|
182
|
+
<Layer>
|
|
183
|
+
<Select
|
|
184
|
+
id="program"
|
|
185
|
+
invalidText={t('required', 'Required')}
|
|
186
|
+
labelText=""
|
|
187
|
+
onChange={(event) => setSelectedProgram(event.target.value)}
|
|
188
|
+
>
|
|
189
|
+
{!selectedProgram ? <SelectItem text={t('chooseProgram', 'Choose a program')} value="" /> : null}
|
|
190
|
+
{eligiblePrograms?.length > 0 &&
|
|
191
|
+
eligiblePrograms.map((program) => (
|
|
192
|
+
<SelectItem key={program.uuid} text={program.display} value={program.uuid}>
|
|
193
|
+
{program.display}
|
|
194
|
+
</SelectItem>
|
|
195
|
+
))}
|
|
196
|
+
</Select>
|
|
197
|
+
</Layer>
|
|
198
|
+
) : (
|
|
199
|
+
<Select
|
|
200
|
+
id="program"
|
|
201
|
+
invalidText={t('required', 'Required')}
|
|
202
|
+
labelText=""
|
|
203
|
+
onChange={(event) => setSelectedProgram(event.target.value)}
|
|
204
|
+
>
|
|
205
|
+
{!selectedProgram ? <SelectItem text={t('chooseProgram', 'Choose a program')} value="" /> : null}
|
|
206
|
+
{eligiblePrograms?.length > 0 &&
|
|
207
|
+
eligiblePrograms.map((program) => (
|
|
208
|
+
<SelectItem key={program.uuid} text={program.display} value={program.uuid}>
|
|
209
|
+
{program.display}
|
|
210
|
+
</SelectItem>
|
|
211
|
+
))}
|
|
212
|
+
</Select>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
</FormGroup>
|
|
216
|
+
<FormGroup style={{ maxWidth: '50%' }} legendText={t('dateEnrolled', 'Date enrolled')}>
|
|
217
|
+
{isTablet ? (
|
|
218
|
+
<Layer>
|
|
219
|
+
<DatePicker
|
|
220
|
+
id="enrollmentDate"
|
|
221
|
+
datePickerType="single"
|
|
222
|
+
dateFormat="d/m/Y"
|
|
223
|
+
maxDate={new Date().toISOString()}
|
|
224
|
+
placeholder="dd/mm/yyyy"
|
|
225
|
+
onChange={([date]) => setEnrollmentDate(date)}
|
|
226
|
+
value={enrollmentDate}
|
|
227
|
+
>
|
|
228
|
+
<DatePickerInput id="enrollmentDateInput" labelText="" />
|
|
229
|
+
</DatePicker>
|
|
230
|
+
</Layer>
|
|
231
|
+
) : (
|
|
232
|
+
<DatePicker
|
|
233
|
+
id="enrollmentDate"
|
|
234
|
+
datePickerType="single"
|
|
235
|
+
dateFormat="d/m/Y"
|
|
236
|
+
maxDate={new Date().toISOString()}
|
|
237
|
+
placeholder="dd/mm/yyyy"
|
|
238
|
+
onChange={([date]) => setEnrollmentDate(date)}
|
|
239
|
+
value={enrollmentDate}
|
|
240
|
+
>
|
|
241
|
+
<DatePickerInput id="enrollmentDateInput" labelText="" />
|
|
242
|
+
</DatePicker>
|
|
243
|
+
)}
|
|
244
|
+
</FormGroup>
|
|
245
|
+
<FormGroup style={{ width: '50%' }} legendText={t('dateCompleted', 'Date completed')}>
|
|
246
|
+
{isTablet ? (
|
|
247
|
+
<Layer>
|
|
248
|
+
<DatePicker
|
|
249
|
+
id="completionDate"
|
|
250
|
+
datePickerType="single"
|
|
251
|
+
dateFormat="d/m/Y"
|
|
252
|
+
minDate={new Date(enrollmentDate).toISOString()}
|
|
253
|
+
maxDate={new Date().toISOString()}
|
|
254
|
+
placeholder="dd/mm/yyyy"
|
|
255
|
+
onChange={([date]) => setCompletionDate(date)}
|
|
256
|
+
value={completionDate}
|
|
257
|
+
>
|
|
258
|
+
<DatePickerInput id="completionDateInput" labelText="" />
|
|
259
|
+
</DatePicker>
|
|
260
|
+
</Layer>
|
|
261
|
+
) : (
|
|
262
|
+
<DatePicker
|
|
263
|
+
id="completionDate"
|
|
264
|
+
datePickerType="single"
|
|
265
|
+
dateFormat="d/m/Y"
|
|
266
|
+
minDate={new Date(enrollmentDate).toISOString()}
|
|
267
|
+
maxDate={new Date().toISOString()}
|
|
268
|
+
placeholder="dd/mm/yyyy"
|
|
269
|
+
onChange={([date]) => setCompletionDate(date)}
|
|
270
|
+
value={completionDate}
|
|
271
|
+
>
|
|
272
|
+
<DatePickerInput id="completionDateInput" labelText="" />
|
|
273
|
+
</DatePicker>
|
|
274
|
+
)}
|
|
275
|
+
</FormGroup>
|
|
276
|
+
<FormGroup style={{ width: '50%' }} legendText={t('enrollmentLocation', 'Enrollment location')}>
|
|
277
|
+
{isTablet ? (
|
|
278
|
+
<Layer>
|
|
279
|
+
<Select
|
|
280
|
+
id="location"
|
|
281
|
+
invalidText="Required"
|
|
282
|
+
labelText=""
|
|
283
|
+
onChange={(event) => setUserLocation(event.target.value)}
|
|
284
|
+
value={userLocation}
|
|
285
|
+
>
|
|
286
|
+
{!userLocation ? <SelectItem text={t('chooseLocation', 'Choose a location')} value="" /> : null}
|
|
287
|
+
{availableLocations?.length > 0 &&
|
|
288
|
+
availableLocations.map((location) => (
|
|
289
|
+
<SelectItem key={location.uuid} text={location.display} value={location.uuid}>
|
|
290
|
+
{location.display}
|
|
291
|
+
</SelectItem>
|
|
292
|
+
))}
|
|
293
|
+
</Select>
|
|
294
|
+
</Layer>
|
|
295
|
+
) : (
|
|
112
296
|
<Select
|
|
113
|
-
id="
|
|
114
|
-
invalidText=
|
|
297
|
+
id="location"
|
|
298
|
+
invalidText="Required"
|
|
115
299
|
labelText=""
|
|
116
|
-
|
|
117
|
-
|
|
300
|
+
onChange={(event) => setUserLocation(event.target.value)}
|
|
301
|
+
value={userLocation}
|
|
118
302
|
>
|
|
119
|
-
{!
|
|
120
|
-
{
|
|
121
|
-
|
|
122
|
-
<SelectItem key={
|
|
123
|
-
{
|
|
303
|
+
{!userLocation ? <SelectItem text={t('chooseLocation', 'Choose a location')} value="" /> : null}
|
|
304
|
+
{availableLocations?.length > 0 &&
|
|
305
|
+
availableLocations.map((location) => (
|
|
306
|
+
<SelectItem key={location.uuid} text={location.display} value={location.uuid}>
|
|
307
|
+
{location.display}
|
|
124
308
|
</SelectItem>
|
|
125
309
|
))}
|
|
126
310
|
</Select>
|
|
127
|
-
|
|
128
|
-
</FormGroup>
|
|
129
|
-
<FormGroup legendText={t('dateEnrolled', 'Date enrolled')}>
|
|
130
|
-
<DatePicker
|
|
131
|
-
id="enrollmentDate"
|
|
132
|
-
datePickerType="single"
|
|
133
|
-
dateFormat="d/m/Y"
|
|
134
|
-
light={isTablet}
|
|
135
|
-
maxDate={new Date().toISOString()}
|
|
136
|
-
placeholder="dd/mm/yyyy"
|
|
137
|
-
onChange={([date]) => setEnrollmentDate(date)}
|
|
138
|
-
value={enrollmentDate}
|
|
139
|
-
>
|
|
140
|
-
<DatePickerInput id="enrollmentDateInput" labelText="" />
|
|
141
|
-
</DatePicker>
|
|
142
|
-
</FormGroup>
|
|
143
|
-
<FormGroup legendText={t('dateCompleted', 'Date completed')}>
|
|
144
|
-
<DatePicker
|
|
145
|
-
id="completionDate"
|
|
146
|
-
datePickerType="single"
|
|
147
|
-
dateFormat="d/m/Y"
|
|
148
|
-
light={isTablet}
|
|
149
|
-
minDate={new Date(enrollmentDate).toISOString()}
|
|
150
|
-
maxDate={new Date().toISOString()}
|
|
151
|
-
placeholder="dd/mm/yyyy"
|
|
152
|
-
onChange={([date]) => setCompletionDate(date)}
|
|
153
|
-
value={completionDate}
|
|
154
|
-
>
|
|
155
|
-
<DatePickerInput id="completionDateInput" labelText="" />
|
|
156
|
-
</DatePicker>
|
|
157
|
-
</FormGroup>
|
|
158
|
-
<FormGroup style={{ width: '50%' }} legendText={t('enrollmentLocation', 'Enrollment location')}>
|
|
159
|
-
<Select
|
|
160
|
-
id="location"
|
|
161
|
-
invalidText="Required"
|
|
162
|
-
labelText=""
|
|
163
|
-
light={isTablet}
|
|
164
|
-
onChange={(event) => setUserLocation(event.target.value)}
|
|
165
|
-
value={userLocation}
|
|
166
|
-
>
|
|
167
|
-
{!userLocation ? <SelectItem text={t('chooseLocation', 'Choose a location')} value="" /> : null}
|
|
168
|
-
{availableLocations?.length > 0 &&
|
|
169
|
-
availableLocations.map((location) => (
|
|
170
|
-
<SelectItem key={location.uuid} text={location.display} value={location.uuid}>
|
|
171
|
-
{location.display}
|
|
172
|
-
</SelectItem>
|
|
173
|
-
))}
|
|
174
|
-
</Select>
|
|
311
|
+
)}
|
|
175
312
|
</FormGroup>
|
|
176
|
-
</
|
|
313
|
+
</Stack>
|
|
177
314
|
<ButtonSet className={isTablet ? styles.tablet : styles.desktop}>
|
|
178
315
|
<Button className={styles.button} kind="secondary" onClick={() => closeWorkspace()}>
|
|
179
316
|
{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;
|