@openmrs/esm-appointments-app 4.1.1-pre.996 → 4.2.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 +20 -20
- package/dist/153.js +1 -1
- package/dist/153.js.map +1 -0
- package/dist/319.js +1 -1
- package/dist/34.js +1 -1
- package/dist/34.js.map +1 -0
- package/dist/351.js +1 -1
- package/dist/351.js.map +1 -0
- package/dist/352.js +1 -1
- package/dist/352.js.map +1 -0
- package/dist/430.js +1 -1
- package/dist/430.js.map +1 -0
- package/dist/437.js +1 -1
- package/dist/437.js.map +1 -0
- package/dist/45.js +1 -1
- package/dist/45.js.map +1 -0
- package/dist/456.js +1 -0
- package/dist/456.js.map +1 -0
- package/dist/458.js +2 -0
- package/dist/458.js.map +1 -0
- package/dist/497.js +1 -1
- package/dist/497.js.map +1 -0
- package/dist/542.js +1 -1
- package/dist/542.js.map +1 -0
- package/dist/568.js +1 -1
- package/dist/568.js.map +1 -0
- package/dist/574.js +1 -1
- package/dist/647.js +1 -1
- package/dist/647.js.map +1 -0
- package/dist/685.js +1 -1
- package/dist/685.js.map +1 -0
- package/dist/692.js +1 -1
- package/dist/692.js.map +1 -0
- package/dist/757.js +1 -1
- package/dist/784.js +1 -1
- package/dist/784.js.map +1 -0
- package/dist/807.js +1 -1
- package/dist/821.js +1 -1
- package/dist/821.js.map +1 -0
- package/dist/830.js +1 -1
- package/dist/830.js.map +1 -0
- package/dist/843.js +1 -1
- package/dist/843.js.map +1 -0
- package/dist/858.js +1 -1
- package/dist/858.js.map +1 -0
- package/dist/976.js +1 -0
- package/dist/976.js.map +1 -0
- package/dist/984.js +1 -0
- package/dist/984.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -0
- package/dist/openmrs-esm-appointments-app.js +1 -1
- package/dist/openmrs-esm-appointments-app.js.buildmanifest.json +205 -182
- package/dist/openmrs-esm-appointments-app.js.map +1 -0
- package/dist/openmrs-esm-appointments-app.old +1 -1
- package/package.json +2 -2
- package/src/appointment-forms/appointment-forms.resource.ts +5 -17
- package/src/appointment-forms/appointments-form.component.tsx +102 -234
- package/src/appointment-forms/appointments-form.scss +5 -5
- package/src/appointment-forms/appointments-forms.test.tsx +6 -16
- package/src/appointment-forms/useInitialFormValues.tsx +49 -0
- package/src/appointment-forms/workload-helper.ts +13 -19
- package/src/appointments/appointment-list.component.tsx +6 -6
- package/src/appointments/appointment-list.test.tsx +2 -2
- package/src/appointments-tabs/unscheduled-appointments.component.tsx +14 -9
- package/src/config-schema.ts +4 -13
- package/src/patient-search/patient-search.scss +1 -3
- package/translations/en.json +6 -7
- package/__mocks__/appointments.mock.ts +0 -411
- package/dist/268.js +0 -1
- package/dist/285.js +0 -2
- package/dist/339.js +0 -1
- package/dist/39.js +0 -1
- package/dist/842.js +0 -1
- /package/dist/{285.js.LICENSE.txt → 458.js.LICENSE.txt} +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React, { useState
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import dayjs from 'dayjs';
|
|
4
|
-
import isEmpty from 'lodash-es/isEmpty';
|
|
5
4
|
import {
|
|
6
5
|
Button,
|
|
7
6
|
ButtonSet,
|
|
@@ -13,7 +12,6 @@ import {
|
|
|
13
12
|
Select,
|
|
14
13
|
SelectItem,
|
|
15
14
|
Switch,
|
|
16
|
-
TextArea,
|
|
17
15
|
TimePicker,
|
|
18
16
|
TimePickerSelect,
|
|
19
17
|
Toggle,
|
|
@@ -23,38 +21,34 @@ import {
|
|
|
23
21
|
Tabs,
|
|
24
22
|
TabPanel,
|
|
25
23
|
TabPanels,
|
|
24
|
+
TextArea,
|
|
26
25
|
Layer,
|
|
27
|
-
TextInput,
|
|
28
26
|
} from '@carbon/react';
|
|
29
27
|
import {
|
|
30
28
|
useLocations,
|
|
31
|
-
useSession,
|
|
32
|
-
showToast,
|
|
33
|
-
showNotification,
|
|
34
29
|
ExtensionSlot,
|
|
35
30
|
usePatient,
|
|
36
31
|
useConfig,
|
|
37
|
-
|
|
32
|
+
showNotification,
|
|
33
|
+
showToast,
|
|
38
34
|
} from '@openmrs/esm-framework';
|
|
39
35
|
import { AppointmentPayload, MappedAppointment } from '../types';
|
|
40
|
-
import {
|
|
36
|
+
import { convertTime12to24, useAppointmentDate } from '../helpers';
|
|
41
37
|
import {
|
|
42
|
-
saveAppointment,
|
|
43
38
|
useServices,
|
|
44
39
|
useAppointmentSummary,
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
saveAppointment,
|
|
41
|
+
toAppointmentDateTime,
|
|
47
42
|
} from './appointment-forms.resource';
|
|
48
43
|
import { ConfigObject } from '../config-schema';
|
|
49
44
|
import { useProviders } from '../hooks/useProviders';
|
|
50
45
|
import { closeOverlay } from '../hooks/useOverlay';
|
|
51
|
-
import { mockFrequency } from '../../__mocks__/appointments.mock';
|
|
52
46
|
import WorkloadCard from './workload.component';
|
|
53
47
|
import first from 'lodash-es/first';
|
|
54
48
|
import styles from './appointments-form.scss';
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
57
|
-
import {
|
|
49
|
+
import { useCalendarDistribution } from './workload-helper';
|
|
50
|
+
import { PatientAppointment, useInitialAppointmentFormValue } from './useInitialFormValues';
|
|
51
|
+
import { mutate } from 'swr';
|
|
58
52
|
|
|
59
53
|
interface AppointmentFormProps {
|
|
60
54
|
appointment?: MappedAppointment;
|
|
@@ -62,135 +56,44 @@ interface AppointmentFormProps {
|
|
|
62
56
|
context: string;
|
|
63
57
|
}
|
|
64
58
|
const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientUuid, context }) => {
|
|
65
|
-
const initialState = {
|
|
66
|
-
patientUuid,
|
|
67
|
-
dateTime: undefined,
|
|
68
|
-
location: '',
|
|
69
|
-
serviceUuid: '',
|
|
70
|
-
comments: '',
|
|
71
|
-
appointmentKind: '',
|
|
72
|
-
status: '',
|
|
73
|
-
id: undefined,
|
|
74
|
-
gender: '',
|
|
75
|
-
serviceType: '',
|
|
76
|
-
provider: '',
|
|
77
|
-
appointmentNumber: undefined,
|
|
78
|
-
};
|
|
79
|
-
const appointmentState = !isEmpty(appointment) ? appointment : initialState;
|
|
80
59
|
const { t } = useTranslation();
|
|
81
|
-
const {
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const {
|
|
85
|
-
const { appointmentStatuses } = useConfig() as ConfigObject;
|
|
86
|
-
const { patient, isLoading } = usePatient(patientUuid ?? appointmentState.patientUuid);
|
|
60
|
+
const { appointmentTypes, appointmentStatuses, hiddenFormFields } = useConfig() as ConfigObject;
|
|
61
|
+
const initialAppointmentFormValues = useInitialAppointmentFormValue(appointment, patientUuid);
|
|
62
|
+
const [patientAppointment, setPatientAppointment] = useState<PatientAppointment>(initialAppointmentFormValues);
|
|
63
|
+
const { patient, isLoading } = usePatient(patientUuid ?? patientAppointment.patientUuid);
|
|
87
64
|
const locations = useLocations();
|
|
88
|
-
const session = useSession();
|
|
89
65
|
const { providers } = useProviders();
|
|
90
66
|
const { services } = useServices();
|
|
91
|
-
const [startDate, setStartDate] = useState(
|
|
92
|
-
dayjs(appointmentState.dateTime).format('hh:mm') || dayjs(new Date()).format('hh:mm'),
|
|
93
|
-
);
|
|
94
|
-
const [endDate, setEndDate] = useState(
|
|
95
|
-
dayjs(appointmentState.dateTime).format('hh:mm') || dayjs(new Date()).format('hh:mm'),
|
|
96
|
-
);
|
|
97
|
-
const [frequency, setFrequency] = useState('');
|
|
98
|
-
const [selectedLocation, setSelectedLocation] = useState(appointmentState.location);
|
|
99
|
-
const [selectedService, setSelectedService] = useState(appointmentState.serviceUuid);
|
|
100
|
-
const [selectedProvider, setSelectedProvider] = useState(appointmentState.provider);
|
|
101
|
-
const [reminder, setReminder] = useState('');
|
|
102
|
-
const [appointmentComment, setAppointmentComment] = useState(appointmentState.comments);
|
|
103
|
-
const [reason, setReason] = useState('');
|
|
104
|
-
const [timeFormat, setTimeFormat] = useState<amPm>(new Date().getHours() >= 12 ? 'PM' : 'AM');
|
|
105
|
-
const [visitDate, setVisitDate] = React.useState(
|
|
106
|
-
appointmentState.dateTime ? new Date(appointmentState.dateTime) : new Date(),
|
|
107
|
-
);
|
|
108
|
-
const [isFullDay, setIsFullDay] = useState<boolean>(false);
|
|
109
|
-
const [day, setDay] = useState(appointmentState.dateTime);
|
|
110
|
-
const [appointmentType, setAppointmentType] = useState(appointmentState.appointmentKind);
|
|
111
|
-
const [appointmentStatus, setAppointmentStatus] = useState(appointmentState.status ?? 'Scheduled');
|
|
112
67
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
113
|
-
const
|
|
114
|
-
const appointmentSummary = useAppointmentSummary(visitDate, selectedService);
|
|
115
|
-
const appointmentMonthly = useMonthlyAppointmentSummary(visitDate, selectedService);
|
|
68
|
+
const appointmentSummary = useAppointmentSummary(patientAppointment.visitDate, patientAppointment.serviceUuid);
|
|
116
69
|
const [selectedTab, setSelectedTab] = useState(0);
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const weeklyDistribution = useMemo(
|
|
123
|
-
() => getWeeklyCalendarDistribution(new Date(appointmentStartDate), appointmentSummary) ?? [],
|
|
124
|
-
[appointmentStartDate, appointmentSummary],
|
|
70
|
+
const calendarWorkload = useCalendarDistribution(
|
|
71
|
+
patientAppointment.serviceUuid,
|
|
72
|
+
selectedTab === 0 ? 'week' : 'month',
|
|
125
73
|
);
|
|
126
|
-
const
|
|
127
|
-
const appointmentService = services?.find(({ uuid }) => uuid === selectedService);
|
|
74
|
+
const appointmentStartDate = useAppointmentDate();
|
|
128
75
|
|
|
129
|
-
|
|
130
|
-
if (appointmentService) {
|
|
131
|
-
const [hours, minutes] = convertTime12to24(startDate, timeFormat);
|
|
132
|
-
const startDatetime = new Date(
|
|
133
|
-
dayjs(visitDate).year(),
|
|
134
|
-
dayjs(visitDate).month(),
|
|
135
|
-
dayjs(visitDate).date(),
|
|
136
|
-
hours,
|
|
137
|
-
minutes,
|
|
138
|
-
);
|
|
139
|
-
setEndDate(
|
|
140
|
-
dayjs(startDatetime)
|
|
141
|
-
.add(parseInt(appointmentService?.durationMins ?? '0'), 'minutes')
|
|
142
|
-
.format('hh:mm'),
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
}, [startDate, timeFormat, appointmentService, visitDate]);
|
|
76
|
+
const appointmentService = services?.find(({ uuid }) => uuid === patientAppointment.serviceUuid);
|
|
146
77
|
|
|
147
78
|
const handleSubmit = async () => {
|
|
148
|
-
const [hours, minutes] = convertTime12to24(
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
const startDatetime = new Date(
|
|
152
|
-
dayjs(visitDate).year(),
|
|
153
|
-
dayjs(visitDate).month(),
|
|
154
|
-
dayjs(visitDate).date(),
|
|
155
|
-
hours,
|
|
156
|
-
minutes,
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
const endDatetime = dayjs(
|
|
160
|
-
new Date(dayjs(visitDate).year(), dayjs(visitDate).month(), dayjs(visitDate).date(), hours, minutes),
|
|
161
|
-
);
|
|
79
|
+
const [hours, minutes] = convertTime12to24(patientAppointment.startDateTime, patientAppointment.timeFormat);
|
|
80
|
+
const startDatetime = toAppointmentDateTime(patientAppointment.visitDate, hours, minutes);
|
|
81
|
+
const endDatetime = toAppointmentDateTime(patientAppointment.visitDate, hours, minutes);
|
|
162
82
|
const appointmentPayload: AppointmentPayload = {
|
|
163
|
-
appointmentKind:
|
|
164
|
-
status:
|
|
165
|
-
serviceUuid:
|
|
83
|
+
appointmentKind: patientAppointment.appointmentKind,
|
|
84
|
+
status: patientAppointment.status,
|
|
85
|
+
serviceUuid: patientAppointment.serviceUuid,
|
|
166
86
|
startDateTime: dayjs(startDatetime).format(),
|
|
167
87
|
endDateTime: dayjs(endDatetime).format(),
|
|
168
|
-
providerUuid: providerUuid,
|
|
169
|
-
providers: [{ uuid: providerUuid }],
|
|
170
|
-
comments:
|
|
171
|
-
locationUuid:
|
|
172
|
-
patientUuid:
|
|
173
|
-
appointmentNumber:
|
|
174
|
-
uuid:
|
|
88
|
+
providerUuid: patientAppointment.providerUuid,
|
|
89
|
+
providers: [{ uuid: patientAppointment.providerUuid }],
|
|
90
|
+
comments: patientAppointment.comments,
|
|
91
|
+
locationUuid: patientAppointment.locationUuid,
|
|
92
|
+
patientUuid: patientAppointment.patientUuid,
|
|
93
|
+
appointmentNumber: patientAppointment.appointmentNumber,
|
|
94
|
+
uuid: patientAppointment.uuid,
|
|
175
95
|
};
|
|
176
96
|
|
|
177
|
-
const { data } = await checkAppointmentConflict(appointmentPayload);
|
|
178
|
-
const [bookingStatus] = Object.keys(data);
|
|
179
|
-
const isPatientDoubleBooking = 'PATIENT_DOUBLE_BOOKING' === bookingStatus;
|
|
180
|
-
|
|
181
|
-
if (isPatientDoubleBooking) {
|
|
182
|
-
showToast({
|
|
183
|
-
critical: true,
|
|
184
|
-
kind: 'warning',
|
|
185
|
-
description: t(
|
|
186
|
-
'doublePatientBooking',
|
|
187
|
-
'There exist an appointment on the specified service and appointment date',
|
|
188
|
-
),
|
|
189
|
-
title: t('doubleBooking', 'Appointment double booking'),
|
|
190
|
-
});
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
97
|
const abortController = new AbortController();
|
|
195
98
|
setIsSubmitting(true);
|
|
196
99
|
saveAppointment(appointmentPayload, abortController).then(
|
|
@@ -231,49 +134,12 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
231
134
|
extensionSlotName="patient-header-slot"
|
|
232
135
|
state={{
|
|
233
136
|
patient,
|
|
234
|
-
patientUuid:
|
|
137
|
+
patientUuid: patientAppointment.patientUuid,
|
|
235
138
|
}}
|
|
236
139
|
/>
|
|
237
140
|
</div>
|
|
238
141
|
)}
|
|
239
142
|
|
|
240
|
-
<div className={styles.childRow}>
|
|
241
|
-
<div className={styles.row}>
|
|
242
|
-
<Select
|
|
243
|
-
labelText={t('frequency', 'Frequency')}
|
|
244
|
-
id="frequency"
|
|
245
|
-
className={styles.select}
|
|
246
|
-
invalidText="Required"
|
|
247
|
-
value={frequency}
|
|
248
|
-
onChange={(event) => setFrequency(event.target.value)}
|
|
249
|
-
light>
|
|
250
|
-
{mockFrequency.data?.length > 0 &&
|
|
251
|
-
mockFrequency.data.map((frequency) => (
|
|
252
|
-
<SelectItem key={frequency.uuid} text={frequency.display} value={frequency.uuid}>
|
|
253
|
-
{frequency.display}
|
|
254
|
-
</SelectItem>
|
|
255
|
-
))}
|
|
256
|
-
</Select>
|
|
257
|
-
|
|
258
|
-
{isFullDay ? (
|
|
259
|
-
<Select
|
|
260
|
-
labelText={t('day', 'Day')}
|
|
261
|
-
id="day"
|
|
262
|
-
invalidText="Required"
|
|
263
|
-
value={day}
|
|
264
|
-
onChange={(event) => setDay(event.target.value)}
|
|
265
|
-
light>
|
|
266
|
-
{daysOfTheWeek?.length > 0 &&
|
|
267
|
-
daysOfTheWeek.map((day) => (
|
|
268
|
-
<SelectItem key={day} text={day} value={day}>
|
|
269
|
-
{day}
|
|
270
|
-
</SelectItem>
|
|
271
|
-
))}
|
|
272
|
-
</Select>
|
|
273
|
-
) : null}
|
|
274
|
-
</div>
|
|
275
|
-
</div>
|
|
276
|
-
|
|
277
143
|
<div className={styles.inputContainer} id="appointment-place">
|
|
278
144
|
<p>{t('selectAppointmentLocation', 'Select where the appointment will take place')}</p>
|
|
279
145
|
<ContentSwitcher className={styles.inputContainer} data-testid="appointment-place">
|
|
@@ -290,10 +156,9 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
290
156
|
labelText={t('selectLocation', 'Select a location')}
|
|
291
157
|
id="location"
|
|
292
158
|
invalidText="Required"
|
|
293
|
-
value={
|
|
159
|
+
value={patientAppointment.locationUuid}
|
|
294
160
|
className={styles.inputContainer}
|
|
295
|
-
onChange={(event) =>
|
|
296
|
-
light>
|
|
161
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, locationUuid: event.target.value })}>
|
|
297
162
|
{locations?.length > 0 &&
|
|
298
163
|
locations.map((location) => (
|
|
299
164
|
<SelectItem key={location.uuid} text={location.display} value={location.uuid}>
|
|
@@ -306,11 +171,10 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
306
171
|
id="service"
|
|
307
172
|
invalidText="Required"
|
|
308
173
|
labelText={t('selectService', 'Select a service')}
|
|
309
|
-
light
|
|
310
174
|
className={styles.inputContainer}
|
|
311
|
-
onChange={(event) =>
|
|
312
|
-
value={
|
|
313
|
-
{!
|
|
175
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, serviceUuid: event.target.value })}
|
|
176
|
+
value={patientAppointment.serviceUuid}>
|
|
177
|
+
{!patientAppointment.serviceUuid || patientAppointment.serviceUuid == '--' ? (
|
|
314
178
|
<SelectItem text={t('chooseService', 'Select service')} value="" />
|
|
315
179
|
) : null}
|
|
316
180
|
{services?.length > 0 &&
|
|
@@ -324,16 +188,22 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
324
188
|
<p>{t('appointmentDateAndTime', 'Appointments Date and Time')}</p>
|
|
325
189
|
|
|
326
190
|
<div className={styles.row}>
|
|
327
|
-
<Toggle
|
|
191
|
+
<Toggle
|
|
192
|
+
onToggle={(value) => setPatientAppointment({ ...patientAppointment, isFullDay: value })}
|
|
193
|
+
id="allDay"
|
|
194
|
+
labelA="Off"
|
|
195
|
+
labelB="On"
|
|
196
|
+
labelText="All Day"
|
|
197
|
+
defaultToggled={patientAppointment.isFullDay}
|
|
198
|
+
/>
|
|
328
199
|
<DatePicker
|
|
329
200
|
dateFormat="d/m/Y"
|
|
330
201
|
datePickerType="single"
|
|
331
202
|
id="visitDate"
|
|
332
|
-
|
|
333
|
-
minDate={visitDate}
|
|
203
|
+
minDate={patientAppointment.visitDate}
|
|
334
204
|
className={styles.datePickerInput}
|
|
335
|
-
onChange={([date]) =>
|
|
336
|
-
value={visitDate}>
|
|
205
|
+
onChange={([date]) => setPatientAppointment({ ...patientAppointment, visitDate: date })}
|
|
206
|
+
value={patientAppointment.visitDate}>
|
|
337
207
|
<DatePickerInput
|
|
338
208
|
id="visitStartDateInput"
|
|
339
209
|
labelText={t('date', 'Date')}
|
|
@@ -342,22 +212,21 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
342
212
|
/>
|
|
343
213
|
</DatePicker>
|
|
344
214
|
</div>
|
|
345
|
-
{!isFullDay ? (
|
|
215
|
+
{!patientAppointment.isFullDay ? (
|
|
346
216
|
<div className={styles.row}>
|
|
347
217
|
<TimePicker
|
|
348
218
|
disabled={!appointmentService}
|
|
349
|
-
light
|
|
350
219
|
className={styles.timePickerInput}
|
|
351
220
|
pattern="([\d]+:[\d]{2})"
|
|
352
|
-
onChange={(event) =>
|
|
353
|
-
value={
|
|
221
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, startDateTime: event.target.value })}
|
|
222
|
+
value={patientAppointment.endDateTime}
|
|
354
223
|
labelText={t('startTime', 'Start Time')}
|
|
355
224
|
id="start-time-picker">
|
|
356
225
|
<TimePickerSelect
|
|
357
226
|
disabled={!appointmentService}
|
|
358
227
|
id="start-time-picker"
|
|
359
|
-
onChange={(event) =>
|
|
360
|
-
value={timeFormat}
|
|
228
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, timeFormat: event.target.value })}
|
|
229
|
+
value={patientAppointment.timeFormat}
|
|
361
230
|
labelText={t('time', 'Time')}
|
|
362
231
|
aria-label={t('time', 'Time')}>
|
|
363
232
|
<SelectItem value="AM" text="AM" />
|
|
@@ -367,18 +236,17 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
367
236
|
|
|
368
237
|
<TimePicker
|
|
369
238
|
disabled={!appointmentService}
|
|
370
|
-
light
|
|
371
239
|
className={styles.timePickerInput}
|
|
372
240
|
pattern="([\d]+:[\d]{2})"
|
|
373
|
-
onChange={(event) =>
|
|
374
|
-
value={
|
|
241
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, endDateTime: event.target.value })}
|
|
242
|
+
value={patientAppointment.endDateTime}
|
|
375
243
|
labelText={t('endTime', 'End Time')}
|
|
376
244
|
id="end-time-picker">
|
|
377
245
|
<TimePickerSelect
|
|
378
246
|
disabled={!appointmentService}
|
|
379
247
|
id="end-time-picker"
|
|
380
|
-
onChange={(event) =>
|
|
381
|
-
value={timeFormat}
|
|
248
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, timeFormat: event.target.value })}
|
|
249
|
+
value={patientAppointment.timeFormat}
|
|
382
250
|
labelText={t('time', 'Time')}
|
|
383
251
|
aria-label={t('time', 'Time')}>
|
|
384
252
|
<SelectItem value="AM" text="AM" />
|
|
@@ -388,7 +256,7 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
388
256
|
</div>
|
|
389
257
|
) : null}
|
|
390
258
|
|
|
391
|
-
{
|
|
259
|
+
{patientAppointment.serviceUuid && (
|
|
392
260
|
<div className={styles.workLoadContainer}>
|
|
393
261
|
<>
|
|
394
262
|
<p className={styles.workLoadTitle}>
|
|
@@ -410,14 +278,17 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
410
278
|
<TabPanels>
|
|
411
279
|
<TabPanel style={{ padding: 0 }}>
|
|
412
280
|
<div className={styles.workLoadCard}>
|
|
413
|
-
{
|
|
281
|
+
{calendarWorkload?.map(({ date, count }, index) => {
|
|
414
282
|
return (
|
|
415
283
|
<WorkloadCard
|
|
416
|
-
onClick={() =>
|
|
284
|
+
onClick={() => setPatientAppointment({ ...patientAppointment, visitDate: new Date(date) })}
|
|
417
285
|
key={date}
|
|
418
286
|
date={dayjs(date).format('DD/MM')}
|
|
419
287
|
count={count}
|
|
420
|
-
isActive={
|
|
288
|
+
isActive={
|
|
289
|
+
dayjs(date).format('DD-MM-YYYY') ===
|
|
290
|
+
dayjs(patientAppointment.visitDate).format('DD-MM-YYYY')
|
|
291
|
+
}
|
|
421
292
|
/>
|
|
422
293
|
);
|
|
423
294
|
})}
|
|
@@ -425,14 +296,17 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
425
296
|
</TabPanel>
|
|
426
297
|
<TabPanel style={{ padding: 0 }}>
|
|
427
298
|
<div className={styles.monthlyWorkLoadCard}>
|
|
428
|
-
{
|
|
299
|
+
{calendarWorkload?.map(({ date, count }) => {
|
|
429
300
|
return (
|
|
430
301
|
<WorkloadCard
|
|
431
|
-
onClick={() =>
|
|
302
|
+
onClick={() => setPatientAppointment({ ...patientAppointment, visitDate: new Date(date) })}
|
|
432
303
|
key={date}
|
|
433
304
|
date={dayjs(date).format('DD/MM')}
|
|
434
305
|
count={count}
|
|
435
|
-
isActive={
|
|
306
|
+
isActive={
|
|
307
|
+
dayjs(date).format('DD-MM-YYYY') ===
|
|
308
|
+
dayjs(patientAppointment.visitDate).format('DD-MM-YYYY')
|
|
309
|
+
}
|
|
436
310
|
/>
|
|
437
311
|
);
|
|
438
312
|
})}
|
|
@@ -448,11 +322,10 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
448
322
|
id="appointmentType"
|
|
449
323
|
invalidText="Required"
|
|
450
324
|
labelText={t('selectAppointmentType', 'Select an appointment type')}
|
|
451
|
-
light
|
|
452
325
|
className={styles.inputContainer}
|
|
453
|
-
onChange={(event) =>
|
|
454
|
-
value={
|
|
455
|
-
{!
|
|
326
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, appointmentKind: event.target.value })}
|
|
327
|
+
value={patientAppointment.appointmentKind}>
|
|
328
|
+
{!patientAppointment.appointmentKind || patientAppointment.appointmentKind == '--' ? (
|
|
456
329
|
<SelectItem text={t('selectAppointmentType', 'Select an appointment type')} value="" />
|
|
457
330
|
) : null}
|
|
458
331
|
{appointmentTypes?.length > 0 &&
|
|
@@ -467,11 +340,10 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
467
340
|
id="appointmentStatus"
|
|
468
341
|
invalidText="Required"
|
|
469
342
|
labelText={t('selectAppointmentStatus', 'Select status')}
|
|
470
|
-
light
|
|
471
343
|
className={styles.inputContainer}
|
|
472
|
-
onChange={(event) =>
|
|
473
|
-
value={
|
|
474
|
-
{!
|
|
344
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, status: event.target.value })}
|
|
345
|
+
value={patientAppointment.status}>
|
|
346
|
+
{!patientAppointment.status || patientAppointment.status == '--' ? (
|
|
475
347
|
<SelectItem text={t('selectAppointmentStatus', 'Select status')} value="" />
|
|
476
348
|
) : null}
|
|
477
349
|
{appointmentStatuses?.length > 0 &&
|
|
@@ -487,14 +359,17 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
487
359
|
id="providers"
|
|
488
360
|
invalidText="Required"
|
|
489
361
|
labelText={t('selectProvider', 'Select a provider')}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
362
|
+
className={`${styles.inputContainer} ${hiddenFormFields.includes('providers') && styles.hide}`}
|
|
363
|
+
onChange={(event) => {
|
|
364
|
+
setPatientAppointment({ ...patientAppointment, providerUuid: event.target.value });
|
|
365
|
+
}}
|
|
366
|
+
value={patientAppointment.providerUuid}>
|
|
367
|
+
{!patientAppointment.providerUuid ? (
|
|
368
|
+
<SelectItem text={t('chooseProvider', 'Select Provider')} value="" />
|
|
369
|
+
) : null}
|
|
495
370
|
{providers?.length > 0 &&
|
|
496
371
|
providers.map((provider) => (
|
|
497
|
-
<SelectItem key={provider.uuid} text={provider.display} value={provider.
|
|
372
|
+
<SelectItem key={provider.uuid} text={provider.display} value={provider.uuid}>
|
|
498
373
|
{provider.display}
|
|
499
374
|
</SelectItem>
|
|
500
375
|
))}
|
|
@@ -507,33 +382,26 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
507
382
|
<RadioButtonGroup
|
|
508
383
|
defaultSelected="No"
|
|
509
384
|
orientation="vertical"
|
|
510
|
-
onChange={(event) => {
|
|
511
|
-
setReminder(event.toString());
|
|
512
|
-
}}
|
|
385
|
+
onChange={(event) => {}}
|
|
513
386
|
name="appointment-reminder-radio-group">
|
|
514
387
|
<RadioButton className={styles.radioButton} id="Yes" labelText="Yes" value="Yes" />
|
|
515
388
|
<RadioButton className={styles.radioButton} id="No" labelText="No" value="No" />
|
|
516
389
|
</RadioButtonGroup>
|
|
517
390
|
</div>
|
|
518
391
|
|
|
519
|
-
<
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
<SelectItem key={comments} text={comments} value={comments}>
|
|
533
|
-
{comments}
|
|
534
|
-
</SelectItem>
|
|
535
|
-
))}
|
|
536
|
-
</Select>
|
|
392
|
+
<Layer>
|
|
393
|
+
<TextArea
|
|
394
|
+
cols={50}
|
|
395
|
+
id="comment"
|
|
396
|
+
invalidText="A valid value is required"
|
|
397
|
+
labelText={t('comment', 'Comment')}
|
|
398
|
+
placeholder={t('typeInComments', 'Type in appointment comment')}
|
|
399
|
+
rows={4}
|
|
400
|
+
value={patientAppointment.comments}
|
|
401
|
+
onChange={(event) => setPatientAppointment({ ...patientAppointment, comments: event.target.value })}
|
|
402
|
+
/>
|
|
403
|
+
</Layer>
|
|
404
|
+
|
|
537
405
|
<ButtonSet>
|
|
538
406
|
<Button onClick={closeOverlay} className={styles.button} kind="secondary">
|
|
539
407
|
{t('discard', 'Discard')}
|
|
@@ -541,7 +409,7 @@ const AppointmentForm: React.FC<AppointmentFormProps> = ({ appointment, patientU
|
|
|
541
409
|
<Button
|
|
542
410
|
onClick={handleSubmit}
|
|
543
411
|
className={styles.button}
|
|
544
|
-
disabled={isSubmitting ||
|
|
412
|
+
disabled={isSubmitting || !patientAppointment.serviceUuid}
|
|
545
413
|
kind="primary"
|
|
546
414
|
type="submit">
|
|
547
415
|
{t('save', 'Save')}
|
|
@@ -22,12 +22,8 @@
|
|
|
22
22
|
margin-right: 60%;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
.childRow {
|
|
26
|
-
margin-left: spacing.$spacing-05;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
25
|
.inputContainer {
|
|
30
|
-
margin: spacing.$spacing-05;
|
|
26
|
+
margin: spacing.$spacing-05 0;
|
|
31
27
|
}
|
|
32
28
|
|
|
33
29
|
.button {
|
|
@@ -74,3 +70,7 @@
|
|
|
74
70
|
.input {
|
|
75
71
|
width: 13.5rem;
|
|
76
72
|
}
|
|
73
|
+
|
|
74
|
+
.hide {
|
|
75
|
+
display: none;
|
|
76
|
+
}
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { render, screen, within, fireEvent } from '@testing-library/react';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { useConfig, usePatient } from '@openmrs/esm-framework';
|
|
5
|
-
import { mockPatient, mockServices, mockProviders } from '
|
|
5
|
+
import { mockPatient, mockServices, mockProviders } from '../../../../__mocks__/appointments.mock';
|
|
6
6
|
import { mockLocations } from '../../../../__mocks__/locations.mock';
|
|
7
7
|
import { mockSession } from '../../../../__mocks__/session.mock';
|
|
8
8
|
import { MappedAppointment } from '../types';
|
|
@@ -39,6 +39,7 @@ let mockOpenmrsConfig = {
|
|
|
39
39
|
daysOfTheWeek: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
|
40
40
|
appointmentTypes: ['Scheduled', 'WalkIn', 'Virtual'],
|
|
41
41
|
appointmentStatuses: ['Requested', 'Scheduled', 'CheckedIn', 'Completed', 'Cancelled', 'Missed'],
|
|
42
|
+
hiddenFormFields: [],
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
describe('AppointmentForm', () => {
|
|
@@ -74,22 +75,15 @@ describe('AppointmentForm', () => {
|
|
|
74
75
|
allSelects.forEach((select) => inputAndSelectNamesAndTabValues.push(select?.id));
|
|
75
76
|
allTabs.forEach((tab) => inputAndSelectNamesAndTabValues.push(tab?.id));
|
|
76
77
|
|
|
77
|
-
expect(screen.queryByLabelText('Reason For Changes')).not.toBeInTheDocument();
|
|
78
78
|
expect(inputAndSelectNamesAndTabValues).toEqual([
|
|
79
79
|
'visitStartDateInput',
|
|
80
|
-
'start-time-picker',
|
|
81
|
-
'end-time-picker',
|
|
82
80
|
'Yes',
|
|
83
81
|
'No',
|
|
84
82
|
'',
|
|
85
|
-
'frequency',
|
|
86
83
|
'location',
|
|
87
84
|
'service',
|
|
88
|
-
'start-time-picker',
|
|
89
|
-
'end-time-picker',
|
|
90
85
|
'appointmentType',
|
|
91
86
|
'providers',
|
|
92
|
-
'reason',
|
|
93
87
|
'facility',
|
|
94
88
|
'community',
|
|
95
89
|
]);
|
|
@@ -97,20 +91,16 @@ describe('AppointmentForm', () => {
|
|
|
97
91
|
|
|
98
92
|
it('renders the form with all expected inputs in edit mode', () => {
|
|
99
93
|
renderAppointmentsForm('editing', mockPatient.uuid);
|
|
100
|
-
expect(screen.getByRole('option', { name: /Reason for change/ })).toBeInTheDocument();
|
|
101
94
|
});
|
|
102
95
|
|
|
103
96
|
it('renders the expected appointment types', () => {
|
|
104
97
|
renderAppointmentsForm('creating', mockPatient.uuid);
|
|
105
98
|
const appointmentTypeSelect = screen.getByLabelText('Select an appointment type');
|
|
106
99
|
|
|
107
|
-
expect((
|
|
108
|
-
|
|
109
|
-
);
|
|
110
|
-
expect(within(appointmentTypeSelect).getAllByRole('option')).
|
|
111
|
-
expect(within(appointmentTypeSelect).getAllByRole('option')[1]).toHaveValue('Scheduled');
|
|
112
|
-
expect(within(appointmentTypeSelect).getAllByRole('option')[2]).toHaveValue('WalkIn');
|
|
113
|
-
expect(within(appointmentTypeSelect).getAllByRole('option')[3]).toHaveValue('Virtual');
|
|
100
|
+
expect(within(appointmentTypeSelect).getAllByRole('option')).toHaveLength(3);
|
|
101
|
+
expect(within(appointmentTypeSelect).getAllByRole('option')[2]).toHaveValue('Virtual');
|
|
102
|
+
expect(within(appointmentTypeSelect).getAllByRole('option')[1]).toHaveValue('WalkIn');
|
|
103
|
+
expect(within(appointmentTypeSelect).getAllByRole('option')[0]).toHaveValue('Scheduled');
|
|
114
104
|
|
|
115
105
|
// TODO handle onselect an option
|
|
116
106
|
});
|