@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.
Files changed (54) hide show
  1. package/.turbo/turbo-build.log +33 -35
  2. package/dist/238.js +2 -0
  3. package/dist/{707.js.LICENSE.txt → 238.js.LICENSE.txt} +0 -0
  4. package/dist/402.js +1 -1
  5. package/dist/435.js +1 -0
  6. package/dist/461.js +1 -0
  7. package/dist/574.js +1 -1
  8. package/dist/641.js +1 -0
  9. package/dist/709.js +1 -0
  10. package/dist/733.js +1 -0
  11. package/dist/734.js +2 -0
  12. package/dist/734.js.LICENSE.txt +53 -0
  13. package/dist/784.js +1 -1
  14. package/dist/784.js.LICENSE.txt +2 -7
  15. package/dist/821.js +1 -0
  16. package/dist/858.js +1 -1
  17. package/dist/main.js +1 -0
  18. package/dist/openmrs-esm-patient-programs-app.js +1 -1
  19. package/dist/openmrs-esm-patient-programs-app.js.buildmanifest.json +134 -205
  20. package/dist/openmrs-esm-patient-programs-app.old +1 -1
  21. package/package.json +12 -14
  22. package/src/config-schema.ts +12 -0
  23. package/src/dashboard.meta.tsx +1 -1
  24. package/src/index.ts +8 -3
  25. package/src/programs/program-action-button/program-action-button.component.tsx +50 -0
  26. package/src/programs/programs-detailed-summary.component.tsx +68 -37
  27. package/src/programs/programs-detailed-summary.scss +1 -4
  28. package/src/programs/programs-detailed-summary.test.tsx +26 -7
  29. package/src/programs/programs-form.component.tsx +246 -109
  30. package/src/programs/programs-form.scss +1 -9
  31. package/src/programs/programs-form.test.tsx +113 -110
  32. package/src/programs/programs-overview.component.tsx +60 -46
  33. package/src/programs/programs-overview.scss +1 -3
  34. package/src/programs/programs-overview.test.tsx +6 -7
  35. package/src/programs/programs.resource.tsx +63 -8
  36. package/src/types/index.ts +16 -2
  37. package/translations/en.json +10 -2
  38. package/dist/13.js +0 -1
  39. package/dist/142.js +0 -1
  40. package/dist/145.js +0 -1
  41. package/dist/186.js +0 -2
  42. package/dist/186.js.LICENSE.txt +0 -23
  43. package/dist/252.js +0 -2
  44. package/dist/252.js.LICENSE.txt +0 -23
  45. package/dist/336.js +0 -2
  46. package/dist/336.js.LICENSE.txt +0 -20
  47. package/dist/373.js +0 -1
  48. package/dist/635.js +0 -2
  49. package/dist/635.js.LICENSE.txt +0 -16
  50. package/dist/692.js +0 -1
  51. package/dist/701.js +0 -1
  52. package/dist/707.js +0 -2
  53. package/dist/76.js +0 -1
  54. 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 { useSWRConfig } from 'swr';
7
- import { useTranslation } from 'react-i18next';
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
- const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patientUuid }) => {
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 eligiblePrograms = filter(
46
- availablePrograms,
47
- (program) => !includes(map(enrollments, 'program.uuid'), program.uuid),
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(null);
51
- const [enrollmentDate, setEnrollmentDate] = React.useState(new Date());
52
- const [selectedProgram, setSelectedProgram] = React.useState<string>('');
53
- const [userLocation, setUserLocation] = React.useState('');
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 = createProgramEnrollment(payload, abortController).subscribe(
75
- (response) => {
76
- if (response.status === 201) {
77
- closeWorkspace();
78
-
79
- showToast({
80
- critical: true,
81
- kind: 'success',
82
- description: t('enrollmentNowVisible', 'It is now visible on the Programs page'),
83
- title: t('enrollmentSaved', 'Program enrollment saved'),
84
- });
85
-
86
- mutate(`/ws/rest/v1/programenrollment?patient=${patientUuid}&v=${customRepresentation}`);
87
- }
88
- },
89
- (err) => {
90
- createErrorHandler();
91
-
92
- showNotification({
93
- title: t('programEnrollmentSaveError', 'Error saving program enrollment'),
94
- kind: 'error',
95
- critical: true,
96
- description: err?.message,
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
- [closeWorkspace, completionDate, enrollmentDate, mutate, patientUuid, selectedProgram, t, userLocation],
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
- <div className={styles.formContainer}>
110
- <FormGroup style={{ width: '50%' }} legendText={t('program', 'Program')}>
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="program"
114
- invalidText={t('required', 'Required')}
297
+ id="location"
298
+ invalidText="Required"
115
299
  labelText=""
116
- light={isTablet}
117
- onChange={(event) => setSelectedProgram(event.target.value)}
300
+ onChange={(event) => setUserLocation(event.target.value)}
301
+ value={userLocation}
118
302
  >
119
- {!selectedProgram ? <SelectItem text={t('chooseProgram', 'Choose a program')} value="" /> : null}
120
- {eligiblePrograms?.length > 0 &&
121
- eligiblePrograms.map((program) => (
122
- <SelectItem key={program.uuid} text={program.display} value={program.uuid}>
123
- {program.display}
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
- </div>
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
- </div>
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 "~@openmrs/esm-styleguide/src/vars";
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;