@openmrs/esm-patient-programs-app 3.0.0-pre.65 → 3.0.0-pre.656

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 (76) hide show
  1. package/dist/133.openmrs-esm-patient-programs-app.js +3 -0
  2. package/dist/133.openmrs-esm-patient-programs-app.js.LICENSE.txt +14 -0
  3. package/dist/133.openmrs-esm-patient-programs-app.js.map +1 -0
  4. package/dist/401.openmrs-esm-patient-programs-app.js +3 -0
  5. package/dist/401.openmrs-esm-patient-programs-app.js.LICENSE.txt +14 -0
  6. package/dist/401.openmrs-esm-patient-programs-app.js.map +1 -0
  7. package/dist/574.openmrs-esm-patient-programs-app.js +1 -0
  8. package/dist/639.openmrs-esm-patient-programs-app.js +2 -0
  9. package/dist/639.openmrs-esm-patient-programs-app.js.map +1 -0
  10. package/dist/757.openmrs-esm-patient-programs-app.js +1 -0
  11. package/dist/783.openmrs-esm-patient-programs-app.js +3 -0
  12. package/dist/783.openmrs-esm-patient-programs-app.js.LICENSE.txt +23 -0
  13. package/dist/783.openmrs-esm-patient-programs-app.js.map +1 -0
  14. package/dist/839.openmrs-esm-patient-programs-app.js +2 -0
  15. package/dist/839.openmrs-esm-patient-programs-app.js.map +1 -0
  16. package/dist/972.openmrs-esm-patient-programs-app.js +2 -0
  17. package/dist/972.openmrs-esm-patient-programs-app.js.map +1 -0
  18. package/dist/openmrs-esm-patient-programs-app.js +2 -1
  19. package/dist/openmrs-esm-patient-programs-app.js.LICENSE.txt +5 -0
  20. package/dist/openmrs-esm-patient-programs-app.js.buildmanifest.json +220 -0
  21. package/dist/openmrs-esm-patient-programs-app.js.map +1 -1
  22. package/package.json +10 -11
  23. package/src/dashboard.meta.tsx +6 -0
  24. package/src/declarations.d.tsx +3 -3
  25. package/src/index.ts +31 -30
  26. package/src/programs/program-record.component.tsx +29 -48
  27. package/src/programs/program-record.test.outdated.tsx +30 -34
  28. package/src/programs/programs-detailed-summary.component.tsx +123 -114
  29. package/src/programs/programs-detailed-summary.scss +24 -0
  30. package/src/programs/programs-detailed-summary.test.tsx +71 -0
  31. package/src/programs/programs-form.component.tsx +257 -473
  32. package/src/programs/programs-form.scss +20 -0
  33. package/src/programs/programs-form.test.tsx +206 -0
  34. package/src/programs/programs-overview.component.tsx +44 -74
  35. package/src/programs/programs-overview.scss +4 -0
  36. package/src/programs/programs-overview.test.tsx +70 -0
  37. package/src/programs/programs.component.test.tsx +21 -0
  38. package/src/programs/programs.component.tsx +6 -10
  39. package/src/programs/programs.context.tsx +1 -1
  40. package/src/programs/programs.resource.tsx +31 -43
  41. package/translations/en.json +29 -1
  42. package/webpack.config.js +1 -80
  43. package/dist/0.openmrs-esm-patient-programs-app.js +0 -22
  44. package/dist/0.openmrs-esm-patient-programs-app.js.map +0 -1
  45. package/dist/1.openmrs-esm-patient-programs-app.js +0 -2
  46. package/dist/1.openmrs-esm-patient-programs-app.js.map +0 -1
  47. package/dist/3.openmrs-esm-patient-programs-app.js +0 -23
  48. package/dist/3.openmrs-esm-patient-programs-app.js.map +0 -1
  49. package/dist/4.openmrs-esm-patient-programs-app.js +0 -2
  50. package/dist/4.openmrs-esm-patient-programs-app.js.map +0 -1
  51. package/dist/5.openmrs-esm-patient-programs-app.js +0 -2
  52. package/dist/5.openmrs-esm-patient-programs-app.js.map +0 -1
  53. package/dist/6.openmrs-esm-patient-programs-app.js +0 -2
  54. package/dist/6.openmrs-esm-patient-programs-app.js.map +0 -1
  55. package/dist/7.openmrs-esm-patient-programs-app.js +0 -2
  56. package/dist/7.openmrs-esm-patient-programs-app.js.map +0 -1
  57. package/dist/8.openmrs-esm-patient-programs-app.js +0 -2
  58. package/dist/8.openmrs-esm-patient-programs-app.js.map +0 -1
  59. package/src/cards/summary-card.component.tsx +0 -82
  60. package/src/cards/summary-card.css +0 -65
  61. package/src/dashboard/dashboard-link.component.tsx +0 -7
  62. package/src/dashboard/dashboard.meta.tsx +0 -6
  63. package/src/openmrs-backend-dependencies.ts +0 -4
  64. package/src/programs/empty-state/empty-data-illustration.component.tsx +0 -53
  65. package/src/programs/empty-state/empty-state.component.tsx +0 -39
  66. package/src/programs/empty-state/empty-state.scss +0 -34
  67. package/src/programs/error-state/error-state.component.tsx +0 -26
  68. package/src/programs/error-state/error-state.scss +0 -36
  69. package/src/programs/openWorkspaceTab.tsx +0 -40
  70. package/src/programs/programs-detailed-summary.css +0 -63
  71. package/src/programs/programs-detailed-summary.test.outdated.tsx +0 -57
  72. package/src/programs/programs-form.css +0 -95
  73. package/src/programs/programs-form.test.outdated.tsx +0 -276
  74. package/src/programs/programs-overview.test.outdated.tsx +0 -73
  75. package/src/programs/programs.component.test.outdated.tsx +0 -18
  76. package/src/set-public-path.ts +0 -3
@@ -1,516 +1,300 @@
1
- import React, { useEffect, useRef, useState } from "react";
2
- import dayjs from "dayjs";
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-form.css";
7
- import SummaryCard from "../cards/summary-card.component";
8
- import { match, useHistory } from "react-router-dom";
9
- import { useTranslation, Trans } from "react-i18next";
10
- import { createErrorHandler } from "@openmrs/esm-framework";
1
+ import React, { SyntheticEvent } from 'react';
2
+ import dayjs from 'dayjs';
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-form.scss';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { createErrorHandler, detach, showNotification, showToast, useSessionUser } from '@openmrs/esm-framework';
9
+ import {
10
+ Button,
11
+ DatePicker,
12
+ DatePickerInput,
13
+ InlineLoading,
14
+ Select,
15
+ SelectItem,
16
+ Form,
17
+ FormGroup,
18
+ } from 'carbon-components-react';
11
19
  import {
12
20
  createProgramEnrollment,
13
- fetchPrograms,
14
21
  fetchEnrolledPrograms,
15
22
  fetchLocations,
16
- getPatientProgramByUuid,
17
- getSession,
18
- updateProgramEnrollment,
19
- } from "./programs.resource";
20
- import { DataCaptureComponentProps } from "../types";
23
+ fetchAvailablePrograms,
24
+ } from './programs.resource';
25
+ import { Program } from '../types';
21
26
 
22
- export type ProgramsFormProps = DataCaptureComponentProps & {
23
- match: match<ProgramMatchProps>;
24
- patientUuid: string;
25
- };
27
+ interface Idle {
28
+ type: ActionTypes.idle;
29
+ }
26
30
 
27
- // exported so we can use this for tests
28
- export interface ProgramMatchProps {
29
- program?: string;
30
- programUuid?: string;
31
- enrollmentDate?: string;
32
- completionDate?: string;
33
- locationUuid?: string;
31
+ interface SelectProgram {
32
+ availablePrograms?: Array<Program>;
33
+ eligiblePrograms?: Array<Program>;
34
+ program: Program | null;
35
+ type: ActionTypes.selectProgram;
34
36
  }
35
37
 
36
- export interface ProgramEnrollment {
37
- program: string;
38
- patient?: string;
39
- dateEnrolled?: string;
40
- dateCompleted?: string;
41
- location?: string;
38
+ interface Submit {
39
+ type: ActionTypes.submit;
42
40
  }
43
41
 
44
- const ProgramsForm: React.FC<ProgramsFormProps> = ({
45
- match,
46
- patientUuid,
47
- closeComponent = () => {},
48
- entryCancelled = () => {},
49
- entryStarted = () => {},
50
- }) => {
51
- const formRef = useRef<HTMLFormElement>(null);
52
- const enrollmentDateRef = useRef<HTMLInputElement>(null);
53
- const [viewEditForm, setViewEditForm] = useState(false);
54
- const [enableCreateButtons, setEnableCreateButtons] = useState(false);
55
- const [enableEditButtons, setEnableEditButtons] = useState(false);
56
- const [patientProgram, setPatientProgram] = useState(null);
57
- const [allPrograms, setAllPrograms] = useState(null);
58
- const [eligiblePrograms, setEligiblePrograms] = useState(null);
59
- const [enrolledPrograms, setEnrolledPrograms] = useState(null);
60
- const [location, setLocation] = useState("");
61
- const [program, setProgram] = useState("");
62
- const [enrollmentDate, setEnrollmentDate] = useState(
63
- dayjs(new Date()).format("YYYY-MM-DD")
64
- );
65
- const [completionDate, setCompletionDate] = useState(null);
66
- const [locations, setLocations] = useState(null);
67
- const [formChanged, setFormChanged] = useState<Boolean>(false);
68
- const history = useHistory();
69
- const { t } = useTranslation();
42
+ type Action = Idle | SelectProgram | Submit;
70
43
 
71
- useEffect(() => {
72
- const {
73
- program,
74
- programUuid,
75
- enrollmentDate,
76
- completionDate,
77
- locationUuid,
78
- } = match.params;
44
+ interface ViewState {
45
+ status: string;
46
+ program?: Program | null;
47
+ availablePrograms?: Array<Program>;
48
+ eligiblePrograms?: Array<Program>;
49
+ }
79
50
 
80
- if (program && enrollmentDate) {
81
- setViewEditForm(true);
82
- setLocation(locationUuid);
83
- setProgram(programUuid);
84
- setCompletionDate(completionDate);
85
- setEnrollmentDate(enrollmentDate);
86
- }
87
- }, [match.params]);
51
+ enum ActionTypes {
52
+ idle = 'idle',
53
+ selectProgram = 'selectProgram',
54
+ submit = 'submit',
55
+ }
88
56
 
89
- useEffect(() => {
90
- if (patientUuid && !viewEditForm) {
91
- const abortController = new AbortController();
92
- getSession(abortController).then(({ data }) => {
93
- const { sessionLocation } = data;
94
- if (sessionLocation && sessionLocation.uuid) {
95
- setLocation(sessionLocation.uuid);
96
- }
97
- });
98
- }
99
- }, [patientUuid, viewEditForm]);
57
+ function viewStateReducer(state: ViewState, action: Action): ViewState {
58
+ switch (action.type) {
59
+ case ActionTypes.idle:
60
+ return {
61
+ status: 'idle',
62
+ };
63
+ case ActionTypes.selectProgram:
64
+ return {
65
+ ...state,
66
+ status: 'selectProgram',
67
+ availablePrograms: action.availablePrograms,
68
+ eligiblePrograms: action.eligiblePrograms,
69
+ program: action.program,
70
+ };
71
+ case ActionTypes.submit:
72
+ return {
73
+ status: 'submit',
74
+ };
75
+ default:
76
+ return state;
77
+ }
78
+ }
79
+
80
+ const initialViewState: ViewState = {
81
+ status: 'idle',
82
+ };
83
+
84
+ interface ProgramsFormProps {
85
+ patientUuid: string;
86
+ isTablet: boolean;
87
+ }
88
+
89
+ const ProgramsForm: React.FC<ProgramsFormProps> = ({ patientUuid, isTablet }) => {
90
+ const { t } = useTranslation();
91
+ const session = useSessionUser();
92
+ const [availableLocations, setAvailableLocations] = React.useState(null);
93
+ const [completionDate, setCompletionDate] = React.useState(null);
94
+ const [enrollmentDate, setEnrollmentDate] = React.useState(new Date());
95
+ const [userLocation, setUserLocation] = React.useState('');
96
+ const [viewState, dispatch] = React.useReducer(viewStateReducer, initialViewState);
97
+
98
+ if (!userLocation && session?.sessionLocation?.uuid) {
99
+ setUserLocation(session?.sessionLocation?.uuid);
100
+ }
100
101
 
101
- useEffect(() => {
102
+ function handleProgramChange(event) {
103
+ dispatch({
104
+ type: ActionTypes.selectProgram,
105
+ program: event.target.value,
106
+ availablePrograms: viewState.availablePrograms,
107
+ eligiblePrograms: viewState.eligiblePrograms,
108
+ });
109
+ }
110
+
111
+ React.useEffect(() => {
102
112
  if (patientUuid) {
103
- const sub1 = fetchLocations().subscribe((locations) => {
104
- return setLocations(locations);
105
- }, createErrorHandler());
106
- const sub2 = fetchPrograms().subscribe(
107
- (programs) => setAllPrograms(programs),
108
- createErrorHandler()
113
+ const sub1 = fetchAvailablePrograms().subscribe(
114
+ (availablePrograms) =>
115
+ dispatch({
116
+ availablePrograms: availablePrograms,
117
+ program: null,
118
+ type: ActionTypes.selectProgram,
119
+ }),
120
+ () => createErrorHandler(),
109
121
  );
110
- const sub3 = fetchEnrolledPrograms(patientUuid).subscribe(
111
- (enrolledPrograms) =>
112
- setEnrolledPrograms(
113
- enrolledPrograms.filter(
114
- (enrolledProgram) => !enrolledProgram.dateCompleted
115
- )
116
- ),
117
- createErrorHandler()
122
+
123
+ const sub2 = fetchLocations().subscribe(
124
+ (locations) => setAvailableLocations(locations),
125
+ () => createErrorHandler(),
118
126
  );
119
127
 
120
128
  return () => {
121
129
  sub1.unsubscribe();
122
130
  sub2.unsubscribe();
123
- sub3.unsubscribe();
124
131
  };
125
132
  }
126
133
  }, [patientUuid]);
127
134
 
128
- useEffect(() => {
129
- if (viewEditForm && patientUuid && match.params) {
130
- const subscription = getPatientProgramByUuid(
131
- match.params["programUuid"]
132
- ).subscribe(
133
- (program) => setPatientProgram(program),
134
- createErrorHandler()
135
+ const availablePrograms = viewState.availablePrograms;
136
+ React.useEffect(() => {
137
+ if (availablePrograms) {
138
+ const sub = fetchEnrolledPrograms(patientUuid).subscribe(
139
+ (enrolledPrograms) => {
140
+ dispatch({
141
+ availablePrograms: availablePrograms,
142
+ eligiblePrograms: filter(
143
+ availablePrograms,
144
+ (program) => !includes(map(enrolledPrograms, 'program.uuid'), program.uuid),
145
+ ),
146
+ program: null,
147
+ type: ActionTypes.selectProgram,
148
+ });
149
+ },
150
+ () => createErrorHandler(),
135
151
  );
136
-
137
- return () => subscription.unsubscribe();
152
+ return () => sub.unsubscribe();
138
153
  }
139
- }, [viewEditForm, patientUuid, match.params]);
154
+ }, [patientUuid, availablePrograms]);
140
155
 
141
- useEffect(() => {
142
- if (allPrograms && enrolledPrograms) {
143
- setEligiblePrograms(
144
- filter(allPrograms, (program) => {
145
- return !includes(map(enrolledPrograms, "program.uuid"), program.uuid);
146
- })
147
- );
148
- }
149
- }, [allPrograms, enrolledPrograms]);
156
+ const closeWorkspace = React.useCallback(() => detach('patient-chart-workspace-slot', 'programs-form-workspace'), []);
150
157
 
151
- useEffect(() => {
152
- if (
153
- enrollmentDate &&
154
- program &&
155
- enrollmentDateRef?.current?.validity?.valid
156
- ) {
157
- setEnableCreateButtons(true);
158
- } else {
159
- setEnableCreateButtons(false);
160
- }
161
- }, [enrollmentDate, program]);
158
+ const handleSubmit = React.useCallback(
159
+ (event: SyntheticEvent<HTMLFormElement>) => {
160
+ event.preventDefault();
162
161
 
163
- useEffect(() => {
164
- if (viewEditForm && formChanged) {
165
- setEnableEditButtons(true);
166
- } else {
167
- setEnableEditButtons(false);
168
- }
169
- }, [viewEditForm, formChanged]);
162
+ if (viewState.status !== ActionTypes.selectProgram) return;
163
+
164
+ const program = viewState.program;
170
165
 
171
- const handleCreateSubmit = ($event) => {
172
- $event.preventDefault();
173
- const enrollmentPayload: ProgramEnrollment = {
174
- program: program,
175
- patient: patientUuid,
176
- dateEnrolled: enrollmentDate,
177
- dateCompleted: completionDate,
178
- location: location,
179
- };
180
- const abortController = new AbortController();
181
- createProgramEnrollment(enrollmentPayload, abortController).subscribe(
182
- (response) => {
183
- if (response.ok) {
184
- match.params["setEnrolledPrograms"]([
185
- ...match.params["enrolledPrograms"],
186
- response.data,
187
- ]);
188
- navigate();
189
- }
190
- }
191
- );
192
- return () => abortController.abort();
193
- };
166
+ dispatch({
167
+ type: ActionTypes.submit,
168
+ });
194
169
 
195
- const handleEditSubmit = ($event) => {
196
- $event.preventDefault();
197
- if (completionDate || enrollmentDate || location) {
198
- const updatePayload: ProgramEnrollment = {
170
+ const payload = {
171
+ patient: patientUuid,
199
172
  program: program,
200
- dateCompleted: completionDate,
201
- dateEnrolled: enrollmentDate,
202
- location: location,
173
+ dateEnrolled: enrollmentDate ? dayjs(enrollmentDate).format() : null,
174
+ dateCompleted: completionDate ? dayjs(completionDate).format() : null,
175
+ location: userLocation,
203
176
  };
177
+
204
178
  const abortController = new AbortController();
205
- updateProgramEnrollment(updatePayload, abortController).subscribe(
206
- (response) => response.status === 200 && navigate()
207
- );
208
- return () => abortController.abort();
209
- }
210
- };
179
+ const sub = createProgramEnrollment(payload, abortController).subscribe(
180
+ (response) => {
181
+ if (response.status === 201) {
182
+ closeWorkspace();
211
183
 
212
- const navigate = () => {
213
- history.push(`/patient/${patientUuid}/chart/programs/care-programs`);
214
- closeComponent();
215
- };
184
+ showToast({
185
+ kind: 'success',
186
+ description: t('programEnrollmentSaved', 'Program enrollment saved successfully'),
187
+ });
188
+ }
189
+ },
190
+ (err) => {
191
+ createErrorHandler();
216
192
 
217
- const closeForm = ($event) => {
218
- let userConfirmed: boolean = false;
219
- if (formChanged) {
220
- userConfirmed = confirm(
221
- "There is ongoing work, are you sure you want to close this tab?"
193
+ showNotification({
194
+ title: t('programEnrollmentSaveError', 'Error saving program enrollment'),
195
+ kind: 'error',
196
+ critical: true,
197
+ description: err?.message,
198
+ });
199
+ },
200
+ () => {
201
+ dispatch({
202
+ type: ActionTypes.idle,
203
+ });
204
+ },
222
205
  );
223
- }
224
-
225
- if (userConfirmed && formChanged) {
226
- entryCancelled();
227
- closeComponent();
228
- } else if (!formChanged) {
229
- entryCancelled();
230
- closeComponent();
231
- }
232
- };
206
+ return () => {
207
+ sub.unsubscribe();
208
+ };
209
+ },
210
+ [closeWorkspace, completionDate, enrollmentDate, patientUuid, t, userLocation, viewState.program, viewState.status],
211
+ );
233
212
 
234
- function createProgramForm() {
235
- return (
236
- <form
237
- onChange={() => {
238
- setFormChanged(true);
239
- return entryStarted();
240
- }}
241
- onSubmit={handleCreateSubmit}
242
- className={styles.programsForm}
243
- ref={formRef}
244
- >
245
- <SummaryCard
246
- name={t("addNewProgram", "Add a new program")}
247
- styles={{
248
- width: "100%",
249
- backgroundColor: "var(--omrs-color-bg-medium-contrast)",
250
- height: "auto",
251
- }}
252
- >
253
- <div className={styles.programsContainerWrapper}>
254
- <div style={{ flex: 1, margin: "0rem 0.5rem" }}>
255
- <div className={styles.programsInputContainer}>
256
- <label htmlFor="program">
257
- <Trans i18nKey="program">Program</Trans>
258
- </label>
259
- <select
260
- id="program"
261
- name="programs"
262
- value={program}
263
- onChange={(evt) => setProgram(evt.target.value)}
264
- required
265
- >
266
- <option>{t("chooseProgram", "Choose a program")}:</option>
267
- {eligiblePrograms &&
268
- eligiblePrograms.map((program) => (
269
- <option value={program.uuid} key={program.uuid}>
270
- {program.display}
271
- </option>
272
- ))}
273
- </select>
274
- </div>
275
- <div className={styles.programsInputContainer}>
276
- <label htmlFor="enrollmentDate">
277
- <Trans i18nKey="dateEnrolled">Date enrolled</Trans>
278
- </label>
279
- <div className="omrs-datepicker">
280
- <input
281
- ref={enrollmentDateRef}
282
- id="enrollmentDate"
283
- type="date"
284
- name="enrollmentDate"
285
- max={dayjs(new Date().toUTCString()).format("YYYY-MM-DD")}
286
- required
287
- onChange={(evt) => {
288
- setEnrollmentDate(evt.target.value);
289
- }}
290
- defaultValue={dayjs(new Date()).format("YYYY-MM-DD")}
291
- />
292
- <svg className="omrs-icon" role="img">
293
- <use xlinkHref="#omrs-icon-calendar"></use>
294
- </svg>
295
- </div>
296
- {enrollmentDateRef &&
297
- !enrollmentDateRef?.current?.validity?.valid && (
298
- <div className={styles.dateError}>
299
- <span>
300
- <svg className="omrs-icon" role="img">
301
- <use xlinkHref="#omrs-icon-important-notification"></use>
302
- </svg>
303
- <Trans i18nKey="futureDateErrorMsg">
304
- Please enter a date that is either on or before today.
305
- </Trans>
306
- </span>
307
- </div>
308
- )}
309
- </div>
310
- <div className={styles.programsInputContainer}>
311
- <label htmlFor="completionDate">
312
- <Trans i18nKey="dateCompleted">Date completed</Trans>
313
- </label>
314
- <div className="omrs-datepicker">
315
- <input
316
- id="completionDate"
317
- type="date"
318
- name="completionDate"
319
- onChange={(evt) => setCompletionDate(evt.target.value)}
320
- />
321
- <svg className="omrs-icon" role="img">
322
- <use xlinkHref="#omrs-icon-calendar"></use>
323
- </svg>
324
- </div>
325
- </div>
326
- <div className={styles.programsInputContainer}>
327
- <label htmlFor="location">
328
- <Trans i18nKey="enrollmentLocation">
329
- Enrollment location
330
- </Trans>
331
- </label>
332
- <select
333
- id="location"
334
- name="locations"
335
- value={location}
336
- onChange={(evt) => {
337
- setLocation(evt.target.value);
338
- }}
339
- >
340
- <option>{t("chooseLocation", "Choose a location")}:</option>
341
- {locations &&
342
- locations.map((location) => (
343
- <option value={location.uuid} key={location.uuid}>
344
- {location.display}
345
- </option>
346
- ))}
347
- </select>
348
- </div>
349
- </div>
350
- </div>
351
- </SummaryCard>
352
- <div
353
- className={
354
- enableCreateButtons
355
- ? `${styles.buttonStyles} ${styles.buttonStylesBorder}`
356
- : styles.buttonStyles
357
- }
358
- >
359
- <button
360
- type="button"
361
- className="omrs-btn omrs-outlined-neutral omrs-rounded"
362
- style={{ width: "50%" }}
363
- onClick={closeForm}
364
- >
365
- <Trans i18nKey="cancel">Cancel</Trans>
366
- </button>
367
- <button
368
- type="submit"
369
- style={{ width: "50%" }}
370
- className={
371
- enableCreateButtons
372
- ? "omrs-btn omrs-filled-action omrs-rounded"
373
- : "omrs-btn omrs-outlined omrs-rounded"
374
- }
375
- disabled={!enableCreateButtons}
376
- >
377
- <Trans i18nKey="enroll">Enroll</Trans>
378
- </button>
213
+ return (
214
+ <Form style={{ margin: '2rem' }} onSubmit={handleSubmit}>
215
+ <FormGroup style={{ width: '50%' }} legendText={t('program', 'Program')}>
216
+ <div className={styles.selectContainer}>
217
+ <Select
218
+ id="program"
219
+ invalidText={t('required', 'Required')}
220
+ labelText=""
221
+ light={isTablet}
222
+ onChange={handleProgramChange}>
223
+ {!viewState?.program ? <SelectItem text={t('chooseProgram', 'Choose a program')} value="" /> : null}
224
+ {viewState.eligiblePrograms?.length > 0 &&
225
+ viewState.eligiblePrograms.map((program) => (
226
+ <SelectItem key={program.uuid} text={program.display} value={program.uuid}>
227
+ {program.display}
228
+ </SelectItem>
229
+ ))}
230
+ </Select>
231
+ {(() => {
232
+ if (viewState.status !== ActionTypes.selectProgram) return null;
233
+ if (!viewState.eligiblePrograms) return <InlineLoading className={styles.loading} />;
234
+ })()}
379
235
  </div>
380
- </form>
381
- );
382
- }
383
-
384
- function editProgramForm() {
385
- return (
386
- <>
387
- {patientProgram && (
388
- <form
389
- onChange={() => {
390
- setFormChanged(true);
391
- return entryStarted();
392
- }}
393
- onSubmit={handleEditSubmit}
394
- className={styles.programsForm}
395
- ref={formRef}
396
- >
397
- <SummaryCard
398
- name={t("editProgram", "Edit program")}
399
- styles={{
400
- width: "100%",
401
- backgroundColor: "var(--omrs-color-bg-medium-contrast)",
402
- height: "auto",
403
- }}
404
- >
405
- <div className={styles.programsContainerWrapper}>
406
- <div style={{ flex: 1, margin: "0rem 0.5rem" }}>
407
- <div className={styles.programsInputContainer}>
408
- <label htmlFor="program">
409
- <Trans i18nKey="program">Program</Trans>
410
- </label>
411
- <span id="program" className="omrs-medium">
412
- {patientProgram.display}
413
- </span>
414
- </div>
415
- <div className={styles.programsInputContainer}>
416
- <label htmlFor="enrollmentDate">
417
- <Trans i18nKey="dateEnrolled">Date enrolled</Trans>
418
- </label>
419
- <div className="omrs-datepicker">
420
- <input
421
- id="enrollmentDate"
422
- type="date"
423
- name="enrollmentDate"
424
- required
425
- onChange={(evt) => setEnrollmentDate(evt.target.value)}
426
- defaultValue={dayjs(enrollmentDate).format(
427
- "YYYY-MM-DD"
428
- )}
429
- />
430
- <svg className="omrs-icon" role="img">
431
- <use xlinkHref="#omrs-icon-calendar"></use>
432
- </svg>
433
- </div>
434
- </div>
435
- <div className={styles.programsInputContainer}>
436
- <label htmlFor="completionDate">
437
- <Trans i18nKey="dateCompleted">Date completed</Trans>
438
- </label>
439
- <div className="omrs-datepicker">
440
- <input
441
- id="completionDate"
442
- type="date"
443
- name="completionDate"
444
- onChange={(evt) => setCompletionDate(evt.target.value)}
445
- defaultValue={
446
- completionDate
447
- ? dayjs(completionDate).format("YYYY-MM-DD")
448
- : ""
449
- }
450
- />
451
- <svg className="omrs-icon" role="img">
452
- <use xlinkHref="#omrs-icon-calendar"></use>
453
- </svg>
454
- </div>
455
- </div>
456
- <div className={styles.programsInputContainer}>
457
- <label htmlFor="location">
458
- <Trans i18nKey="enrollmentLocation">
459
- Enrollment location
460
- </Trans>
461
- </label>
462
- <select
463
- id="location"
464
- name="locations"
465
- value={location}
466
- onChange={(evt) => setLocation(evt.target.value)}
467
- >
468
- {locations &&
469
- locations.map((location) => (
470
- <option value={location.uuid} key={location.uuid}>
471
- {location.display}
472
- </option>
473
- ))}
474
- </select>
475
- </div>
476
- </div>
477
- </div>
478
- </SummaryCard>
479
- <div
480
- className={
481
- enableEditButtons
482
- ? styles.buttonStyles
483
- : `${styles.buttonStyles} ${styles.buttonStylesBorder}`
484
- }
485
- >
486
- <button
487
- type="submit"
488
- style={{ width: "50%" }}
489
- className={
490
- enableEditButtons
491
- ? "omrs-btn omrs-filled-action omrs-rounded"
492
- : "omrs-btn omrs-outlined omrs-rounded"
493
- }
494
- disabled={!enableEditButtons}
495
- >
496
- <Trans i18nKey="save">Save</Trans>
497
- </button>
498
- <button
499
- type="button"
500
- className="omrs-btn omrs-outlined-neutral omrs-rounded"
501
- style={{ width: "50%" }}
502
- onClick={closeForm}
503
- >
504
- <Trans i18nKey="cancel">Cancel</Trans>
505
- </button>
506
- </div>
507
- </form>
508
- )}
509
- </>
510
- );
511
- }
512
-
513
- return <div>{viewEditForm ? editProgramForm() : createProgramForm()}</div>;
236
+ {viewState.eligiblePrograms?.length === 0 &&
237
+ showNotification({
238
+ title: t('error', 'Error'),
239
+ kind: 'error',
240
+ critical: true,
241
+ description: t('alreadyEnrolledText', 'This patient is already enrolled in all of the available programs'),
242
+ })}
243
+ </FormGroup>
244
+ <FormGroup legendText={t('dateEnrolled', 'Date enrolled')}>
245
+ <DatePicker
246
+ id="enrollmentDate"
247
+ datePickerType="single"
248
+ dateFormat="d/m/Y"
249
+ light={isTablet}
250
+ maxDate={new Date().toISOString()}
251
+ placeholder="dd/mm/yyyy"
252
+ onChange={([date]) => setEnrollmentDate(date)}
253
+ value={enrollmentDate}>
254
+ <DatePickerInput id="enrollmentDateInput" labelText="" />
255
+ </DatePicker>
256
+ </FormGroup>
257
+ <FormGroup legendText={t('dateCompleted', 'Date completed')}>
258
+ <DatePicker
259
+ id="completionDate"
260
+ datePickerType="single"
261
+ dateFormat="d/m/Y"
262
+ light={isTablet}
263
+ minDate={new Date(enrollmentDate).toISOString()}
264
+ maxDate={new Date().toISOString()}
265
+ placeholder="dd/mm/yyyy"
266
+ onChange={([date]) => setCompletionDate(date)}
267
+ value={completionDate}>
268
+ <DatePickerInput id="completionDateInput" labelText="" />
269
+ </DatePicker>
270
+ </FormGroup>
271
+ <FormGroup style={{ width: '50%' }} legendText={t('enrollmentLocation', 'Enrollment location')}>
272
+ <Select
273
+ id="location"
274
+ invalidText="Required"
275
+ labelText=""
276
+ light={isTablet}
277
+ onChange={(event) => setUserLocation(event.target.value)}
278
+ value={userLocation}>
279
+ {!userLocation ? <SelectItem text={t('chooseLocation', 'Choose a location')} value="" /> : null}
280
+ {availableLocations?.length > 0 &&
281
+ availableLocations.map((location) => (
282
+ <SelectItem key={location.uuid} text={location.display} value={location.uuid}>
283
+ {location.display}
284
+ </SelectItem>
285
+ ))}
286
+ </Select>
287
+ </FormGroup>
288
+ <div style={{ marginTop: '1.625rem' }}>
289
+ <Button style={{ width: '50%' }} kind="secondary" type="button" onClick={closeWorkspace}>
290
+ {t('cancel', 'Cancel')}
291
+ </Button>
292
+ <Button style={{ width: '50%' }} kind="primary" type="submit" disabled={!viewState.program}>
293
+ {t('enroll', 'Enroll')}
294
+ </Button>
295
+ </div>
296
+ </Form>
297
+ );
514
298
  };
515
299
 
516
300
  export default ProgramsForm;