@kenyaemr/esm-shr-app 5.4.2-pre.2364 → 5.4.2-pre.2368
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 +12 -12
- package/dist/{820.js → 143.js} +1 -1
- package/dist/143.js.map +1 -0
- package/dist/197.js +1 -1
- package/dist/294.js +1 -1
- package/dist/300.js +1 -1
- package/dist/309.js +1 -0
- package/dist/309.js.map +1 -0
- package/dist/397.js +1 -0
- package/dist/397.js.map +1 -0
- package/dist/{695.js → 675.js} +1 -1
- package/dist/{695.js.map → 675.js.map} +1 -1
- package/dist/kenyaemr-esm-shr-app.js +1 -1
- package/dist/kenyaemr-esm-shr-app.js.buildmanifest.json +84 -84
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/config-schema.ts +6 -0
- package/src/hooks/usePatient.ts +1 -1
- package/src/schema/index.ts +30 -0
- package/src/types/index.ts +112 -0
- package/src/utils/function.tsx +185 -0
- package/src/workspace/referral-workspace.resource.ts +67 -31
- package/src/workspace/referral.workspace.scss +152 -24
- package/src/workspace/referrals.workspace.component.tsx +400 -134
- package/translations/am.json +23 -7
- package/translations/en.json +23 -7
- package/translations/sw.json +23 -7
- package/dist/221.js +0 -1
- package/dist/221.js.map +0 -1
- package/dist/677.js +0 -1
- package/dist/677.js.map +0 -1
- package/dist/820.js.map +0 -1
|
@@ -1,202 +1,468 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
Column,
|
|
5
|
+
Form,
|
|
6
|
+
Stack,
|
|
7
|
+
Search,
|
|
8
|
+
ButtonSet,
|
|
9
|
+
ComboBox,
|
|
10
|
+
Button,
|
|
11
|
+
Tile,
|
|
12
|
+
TextArea,
|
|
13
|
+
InlineLoading,
|
|
14
|
+
Tag,
|
|
15
|
+
} from '@carbon/react';
|
|
16
|
+
import { ExtensionSlot, showSnackbar, useSession, useConfig, formatDate, formatDatetime } from '@openmrs/esm-framework';
|
|
17
|
+
import { z } from 'zod';
|
|
5
18
|
import PatientInfo from './referral-patient-info.component';
|
|
6
19
|
import styles from './referral.workspace.scss';
|
|
7
|
-
import {
|
|
20
|
+
import { Hospital, Close } from '@carbon/react/icons';
|
|
21
|
+
import {
|
|
22
|
+
useFacilities,
|
|
23
|
+
useReasons,
|
|
24
|
+
useSendReferralToArtDirectory,
|
|
25
|
+
useSystemSetting,
|
|
26
|
+
} from './referral-workspace.resource';
|
|
27
|
+
import usePatient from '../hooks/usePatient';
|
|
28
|
+
import { type Concept, type Facility, type ReferralPayload } from '../types';
|
|
29
|
+
import { FacilityReferralFormData, facilityReferralSchema, ValidationErrors } from '../schema';
|
|
30
|
+
import {
|
|
31
|
+
getPatientName,
|
|
32
|
+
getPatientGender,
|
|
33
|
+
getPatientIdentifiers,
|
|
34
|
+
formatBirthDate,
|
|
35
|
+
getPatientAddress,
|
|
36
|
+
getPatientDeathInfo,
|
|
37
|
+
getPhoneNumber,
|
|
38
|
+
} from '../utils/function';
|
|
8
39
|
|
|
9
|
-
|
|
40
|
+
interface FacilityReferralProps {
|
|
10
41
|
closeWorkspace: () => void;
|
|
11
|
-
|
|
42
|
+
patientUuid?: string;
|
|
43
|
+
}
|
|
12
44
|
|
|
13
|
-
const FacilityReferralForm: React.FC<
|
|
45
|
+
const FacilityReferralForm: React.FC<FacilityReferralProps> = ({ closeWorkspace, patientUuid: initialPatientUuid }) => {
|
|
14
46
|
const { t } = useTranslation();
|
|
15
|
-
const {
|
|
16
|
-
const
|
|
17
|
-
|
|
47
|
+
const { mflCodeValue } = useSystemSetting('facility.mflcode');
|
|
48
|
+
const { mutate: sendReferral } = useSendReferralToArtDirectory();
|
|
49
|
+
|
|
50
|
+
const sendingFacilityMflCode = mflCodeValue;
|
|
51
|
+
|
|
52
|
+
const [patientUuid, setPatientUuid] = useState(initialPatientUuid || '');
|
|
53
|
+
const [patientSelected, setPatientSelected] = useState(!!initialPatientUuid);
|
|
18
54
|
const [reasonSearchTerm, setReasonSearchTerm] = useState('');
|
|
19
55
|
const [facilitySearchTerm, setFacilitySearchTerm] = useState('');
|
|
20
56
|
const [selectedReasons, setSelectedReasons] = useState<Concept[]>([]);
|
|
21
57
|
const [selectedFacility, setSelectedFacility] = useState<Facility | null>(null);
|
|
22
58
|
const [referralType, setReferralType] = useState('');
|
|
23
|
-
const
|
|
24
|
-
const
|
|
59
|
+
const [clinicalNotes, setClinicalNotes] = useState('');
|
|
60
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
61
|
+
const [validationErrors, setValidationErrors] = useState<ValidationErrors>({});
|
|
62
|
+
|
|
63
|
+
const { data: reasons } = useReasons(reasonSearchTerm);
|
|
64
|
+
const { data: facilities } = useFacilities(facilitySearchTerm);
|
|
65
|
+
const { patient, isLoading: isLoadingPatient, error: patientError } = usePatient(patientUuid);
|
|
66
|
+
|
|
67
|
+
const getPatientIdentifier = (type: string) => {
|
|
68
|
+
if (!patient?.identifiers?.length) {
|
|
69
|
+
return '';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const identifier = patient.identifiers?.find((id) =>
|
|
73
|
+
id.identifierType?.name?.toLowerCase().includes(type.toLowerCase()),
|
|
74
|
+
);
|
|
25
75
|
|
|
26
|
-
|
|
76
|
+
return identifier?.identifier || '';
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const validateForm = (): boolean => {
|
|
27
80
|
try {
|
|
81
|
+
const formData: FacilityReferralFormData = {
|
|
82
|
+
referralType,
|
|
83
|
+
patientUuid,
|
|
84
|
+
selectedFacility,
|
|
85
|
+
selectedReasons,
|
|
86
|
+
clinicalNotes,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
facilityReferralSchema.parse(formData);
|
|
90
|
+
setValidationErrors({});
|
|
91
|
+
return true;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error instanceof z.ZodError) {
|
|
94
|
+
const errors: ValidationErrors = {};
|
|
95
|
+
error.errors.forEach((err) => {
|
|
96
|
+
const path = err.path[0] as keyof FacilityReferralFormData;
|
|
97
|
+
errors[path] = err.message;
|
|
98
|
+
});
|
|
99
|
+
setValidationErrors(errors);
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const buildReferralPayload = (formData: FacilityReferralFormData): ReferralPayload => {
|
|
106
|
+
const receivingFacilityMflCode = selectedFacility.attributes?.[0]?.value || '';
|
|
107
|
+
const deathInfo = getPatientDeathInfo(patient);
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
MESSAGE_HEADER: {
|
|
111
|
+
SENDING_APPLICATION: 'KENYAEMR',
|
|
112
|
+
SENDING_FACILITY: sendingFacilityMflCode || '18080',
|
|
113
|
+
RECEIVING_APPLICATION: 'IL',
|
|
114
|
+
RECEIVING_FACILITY: receivingFacilityMflCode || '11161',
|
|
115
|
+
MESSAGE_DATETIME: formatDatetime(new Date(), { mode: 'standard' }),
|
|
116
|
+
SECURITY: null,
|
|
117
|
+
MESSAGE_TYPE: 'SIU^S20',
|
|
118
|
+
PROCESSING_ID: 'P',
|
|
119
|
+
},
|
|
120
|
+
PATIENT_IDENTIFICATION: {
|
|
121
|
+
EXTERNAL_PATIENT_ID: {
|
|
122
|
+
ID: null,
|
|
123
|
+
IDENTIFIER_TYPE: 'GODS_NUMBER',
|
|
124
|
+
ASSIGNING_AUTHORITY: 'MPI',
|
|
125
|
+
},
|
|
126
|
+
INTERNAL_PATIENT_ID: getPatientIdentifiers(patient),
|
|
127
|
+
PATIENT_NAME: getPatientName(patient),
|
|
128
|
+
MOTHER_NAME: {
|
|
129
|
+
FIRST_NAME: null,
|
|
130
|
+
MIDDLE_NAME: null,
|
|
131
|
+
LAST_NAME: null,
|
|
132
|
+
},
|
|
133
|
+
DATE_OF_BIRTH: formatBirthDate(patient),
|
|
134
|
+
SEX: getPatientGender(patient),
|
|
135
|
+
PATIENT_ADDRESS: {
|
|
136
|
+
PHYSICAL_ADDRESS: getPatientAddress(patient),
|
|
137
|
+
POSTAL_ADDRESS: null,
|
|
138
|
+
},
|
|
139
|
+
PHONE_NUMBER: getPhoneNumber(patient),
|
|
140
|
+
MARITAL_STATUS: null,
|
|
141
|
+
DEATH_DATE: deathInfo.DEATH_DATE,
|
|
142
|
+
DEATH_INDICATOR: deathInfo.DEATH_INDICATOR,
|
|
143
|
+
DATE_OF_BIRTH_PRECISION: 'EXACT',
|
|
144
|
+
},
|
|
145
|
+
DISCONTINUATION_MESSAGE: {
|
|
146
|
+
DISCONTINUATION_REASON: 'TRANSFER OUT',
|
|
147
|
+
EFFECTIVE_DISCONTINUATION_DATE: formatDate(new Date(), { mode: 'standard' }),
|
|
148
|
+
TARGET_PROGRAM: 'HIV',
|
|
149
|
+
SERVICE_REQUEST: {
|
|
150
|
+
TRANSFER_STATUS: 'ACTIVE',
|
|
151
|
+
TRANSFER_INTENT: 'ORDER',
|
|
152
|
+
TRANSFER_PRIORITY: 'ASAP',
|
|
153
|
+
TRANSFER_OUT_DATE: formatDate(new Date(), { mode: 'standard' }),
|
|
154
|
+
TO_ACCEPTANCE_DATE: null,
|
|
155
|
+
SENDING_FACILITY_MFLCODE: sendingFacilityMflCode || '18080',
|
|
156
|
+
RECEIVING_FACILITY_MFLCODE: receivingFacilityMflCode || '11161',
|
|
157
|
+
SUPPORTING_INFO: formData.clinicalNotes || null,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const onSubmit = async (event: React.FormEvent) => {
|
|
164
|
+
event.preventDefault();
|
|
165
|
+
|
|
166
|
+
if (!validateForm()) {
|
|
167
|
+
showSnackbar({
|
|
168
|
+
kind: 'error',
|
|
169
|
+
title: t('validationError', 'Validation Error'),
|
|
170
|
+
subtitle: t('fixValidationErrors', 'Please fix the validation errors and try again'),
|
|
171
|
+
timeoutInMs: 4000,
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (isLoadingPatient) {
|
|
177
|
+
showSnackbar({
|
|
178
|
+
kind: 'warning',
|
|
179
|
+
title: t('patientLoading', 'Patient Loading'),
|
|
180
|
+
subtitle: t('waitForPatientData', 'Please wait for patient data to load'),
|
|
181
|
+
timeoutInMs: 4000,
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!selectedFacility) {
|
|
187
|
+
showSnackbar({
|
|
188
|
+
kind: 'error',
|
|
189
|
+
title: t('facilityMissing', 'Facility Missing'),
|
|
190
|
+
subtitle: t('selectDestinationFacility', 'Please select a destination facility.'),
|
|
191
|
+
timeoutInMs: 4000,
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const hasMinimumPatientData = patientSelected && !!patientUuid;
|
|
197
|
+
|
|
198
|
+
if (!hasMinimumPatientData) {
|
|
199
|
+
showSnackbar({
|
|
200
|
+
kind: 'error',
|
|
201
|
+
title: t('patientDataMissing', 'Patient Data Missing'),
|
|
202
|
+
subtitle: t(
|
|
203
|
+
'patientDataNotLoaded',
|
|
204
|
+
'Patient data could not be loaded. Please try selecting the patient again.',
|
|
205
|
+
),
|
|
206
|
+
timeoutInMs: 4000,
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
setIsSubmitting(true);
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const formData: FacilityReferralFormData = {
|
|
215
|
+
referralType,
|
|
216
|
+
patientUuid,
|
|
217
|
+
selectedFacility,
|
|
218
|
+
selectedReasons,
|
|
219
|
+
clinicalNotes,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const payload = buildReferralPayload(formData);
|
|
223
|
+
await sendReferral(payload);
|
|
224
|
+
|
|
28
225
|
showSnackbar({
|
|
29
226
|
kind: 'success',
|
|
30
|
-
title: t('referSuccess', '
|
|
31
|
-
subtitle: t('
|
|
32
|
-
timeoutInMs:
|
|
33
|
-
isLowContrast: true,
|
|
227
|
+
title: t('referSuccess', 'Referral Successful'),
|
|
228
|
+
subtitle: t('facilityReferSuccess', `Patient successfully referred to ${selectedFacility?.name}`),
|
|
229
|
+
timeoutInMs: 5000,
|
|
34
230
|
});
|
|
231
|
+
|
|
35
232
|
closeWorkspace();
|
|
36
|
-
} catch (
|
|
37
|
-
console.error(err);
|
|
233
|
+
} catch (error) {
|
|
38
234
|
showSnackbar({
|
|
39
235
|
kind: 'error',
|
|
40
|
-
title: t('referError', '
|
|
41
|
-
subtitle: t('
|
|
42
|
-
timeoutInMs:
|
|
43
|
-
isLowContrast: true,
|
|
236
|
+
title: t('referError', 'Referral Failed'),
|
|
237
|
+
subtitle: t('referErrorDetails', error.message || 'An unexpected error occurred'),
|
|
238
|
+
timeoutInMs: 6000,
|
|
44
239
|
});
|
|
240
|
+
} finally {
|
|
241
|
+
setIsSubmitting(false);
|
|
45
242
|
}
|
|
46
243
|
};
|
|
47
244
|
|
|
48
|
-
const selectPatient = (
|
|
49
|
-
setPatientUuid(
|
|
245
|
+
const selectPatient = (selectedPatientUuid: string) => {
|
|
246
|
+
setPatientUuid(selectedPatientUuid);
|
|
50
247
|
setPatientSelected(true);
|
|
51
|
-
};
|
|
52
248
|
|
|
53
|
-
|
|
54
|
-
|
|
249
|
+
if (validationErrors.patientUuid) {
|
|
250
|
+
setValidationErrors((prev) => ({ ...prev, patientUuid: undefined }));
|
|
251
|
+
}
|
|
55
252
|
};
|
|
56
253
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
254
|
+
const handleReasonSelect = (reason: Concept) => {
|
|
255
|
+
if (!selectedReasons.some((r) => r.uuid === reason.uuid)) {
|
|
256
|
+
const newReasons = [...selectedReasons, reason];
|
|
257
|
+
setSelectedReasons(newReasons);
|
|
258
|
+
setReasonSearchTerm('');
|
|
60
259
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
260
|
+
if (validationErrors.selectedReasons) {
|
|
261
|
+
setValidationErrors((prev) => ({ ...prev, selectedReasons: undefined }));
|
|
262
|
+
}
|
|
64
263
|
}
|
|
65
264
|
};
|
|
66
265
|
|
|
67
|
-
const handleFacilitySelect = (facility) => {
|
|
266
|
+
const handleFacilitySelect = (facility: Facility) => {
|
|
68
267
|
setSelectedFacility(facility);
|
|
69
268
|
setFacilitySearchTerm('');
|
|
269
|
+
|
|
270
|
+
if (validationErrors.selectedFacility) {
|
|
271
|
+
setValidationErrors((prev) => ({ ...prev, selectedFacility: undefined }));
|
|
272
|
+
}
|
|
70
273
|
};
|
|
71
274
|
|
|
72
|
-
const handleRemoveReason = (uuid) => {
|
|
275
|
+
const handleRemoveReason = (uuid: string) => {
|
|
73
276
|
setSelectedReasons(selectedReasons.filter((reason) => reason.uuid !== uuid));
|
|
74
277
|
};
|
|
75
278
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
279
|
+
const isFormValid = useMemo(() => {
|
|
280
|
+
const checks = {
|
|
281
|
+
hasReferralType: !!referralType,
|
|
282
|
+
hasPatientUuid: !!patientUuid,
|
|
283
|
+
hasFacility: !!selectedFacility,
|
|
284
|
+
hasReasons: selectedReasons.length > 0,
|
|
285
|
+
hasNotes: clinicalNotes.length >= 10,
|
|
286
|
+
patientSelectedCorrectly: patientSelected && !!patientUuid,
|
|
287
|
+
notLoadingPatient: !isLoadingPatient,
|
|
288
|
+
noPatientError: !patientError,
|
|
289
|
+
};
|
|
79
290
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
291
|
+
return Object.values(checks).every(Boolean);
|
|
292
|
+
}, [
|
|
293
|
+
referralType,
|
|
294
|
+
patientUuid,
|
|
295
|
+
selectedFacility,
|
|
296
|
+
selectedReasons,
|
|
297
|
+
clinicalNotes,
|
|
298
|
+
patientSelected,
|
|
299
|
+
isLoadingPatient,
|
|
300
|
+
patientError,
|
|
301
|
+
]);
|
|
83
302
|
|
|
84
303
|
return (
|
|
85
304
|
<Form className={styles.form} onSubmit={onSubmit}>
|
|
86
|
-
<span className={styles.formTitle}>{t('formTitle', 'Fill in the form details')}</span>
|
|
87
305
|
<Stack gap={4} className={styles.grid}>
|
|
88
|
-
<
|
|
306
|
+
<h3 className={styles.formTitle}>{t('facilityReferral', 'Facility Referral')}</h3>
|
|
307
|
+
|
|
89
308
|
<Column>
|
|
90
309
|
<ComboBox
|
|
91
|
-
id="
|
|
92
|
-
|
|
310
|
+
id="referral-type"
|
|
311
|
+
titleText={t('referralType', 'Referral Type')}
|
|
93
312
|
items={['Facility to Facility']}
|
|
94
|
-
onChange={
|
|
313
|
+
onChange={({ selectedItem }) => {
|
|
314
|
+
setReferralType(selectedItem);
|
|
315
|
+
}}
|
|
316
|
+
placeholder={t('selectReferralType', 'Select referral type')}
|
|
317
|
+
selectedItem={referralType}
|
|
318
|
+
invalid={!!validationErrors.referralType}
|
|
319
|
+
invalidText={validationErrors.referralType}
|
|
95
320
|
/>
|
|
96
321
|
</Column>
|
|
97
322
|
|
|
98
|
-
{referralType
|
|
323
|
+
{referralType && (
|
|
99
324
|
<>
|
|
100
|
-
<span className={styles.sectionHeader}>Search for facility to refer to</span>
|
|
101
325
|
<Column>
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
326
|
+
<h4 className={styles.sectionHeader}>{t('destinationFacility', 'Destination Facility')}</h4>
|
|
327
|
+
{!selectedFacility ? (
|
|
328
|
+
<>
|
|
329
|
+
<Search
|
|
330
|
+
size="lg"
|
|
331
|
+
placeholder={t('searchFacility', 'Search for facility')}
|
|
332
|
+
labelText={t('search', 'Search')}
|
|
333
|
+
value={facilitySearchTerm}
|
|
334
|
+
onChange={(e) => {
|
|
335
|
+
const value = e.target.value;
|
|
336
|
+
setFacilitySearchTerm(value);
|
|
337
|
+
}}
|
|
338
|
+
/>
|
|
339
|
+
{facilitySearchTerm && facilities?.length > 0 && (
|
|
340
|
+
<div className={styles.searchResults}>
|
|
341
|
+
{facilities.map((facility) => (
|
|
342
|
+
<Tile
|
|
343
|
+
key={facility.uuid}
|
|
344
|
+
className={styles.resultTile}
|
|
345
|
+
onClick={() => handleFacilitySelect(facility)}>
|
|
346
|
+
<div className={styles.tileContent}>
|
|
347
|
+
<Hospital size={20} className={styles.illustrationPictogram} />
|
|
348
|
+
<div className={styles.facilityInfo}>
|
|
349
|
+
<strong>{facility.name}</strong>
|
|
350
|
+
<div>MFL: {facility.attributes?.[0]?.value || 'N/A'}</div>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
</Tile>
|
|
354
|
+
))}
|
|
355
|
+
</div>
|
|
356
|
+
)}
|
|
357
|
+
</>
|
|
358
|
+
) : (
|
|
359
|
+
<Tile className={styles.selectedItem}>
|
|
360
|
+
<div className={styles.tileContent}>
|
|
361
|
+
<Hospital size={20} className={styles.illustrationPictogram} />
|
|
362
|
+
<div className={styles.facilityInfo}>
|
|
363
|
+
<strong>{selectedFacility.name}</strong>
|
|
364
|
+
<div>MFL: {selectedFacility.attributes?.[0]?.value || 'N/A'}</div>
|
|
365
|
+
</div>
|
|
366
|
+
<Button
|
|
367
|
+
hasIconOnly
|
|
368
|
+
renderIcon={Close}
|
|
369
|
+
iconDescription={t('remove', 'Remove')}
|
|
370
|
+
kind="ghost"
|
|
371
|
+
onClick={() => {
|
|
372
|
+
setSelectedFacility(null);
|
|
373
|
+
}}
|
|
374
|
+
/>
|
|
375
|
+
</div>
|
|
376
|
+
</Tile>
|
|
377
|
+
)}
|
|
378
|
+
{validationErrors.selectedFacility && (
|
|
379
|
+
<div className={styles.errorMessage}>{validationErrors.selectedFacility}</div>
|
|
380
|
+
)}
|
|
381
|
+
</Column>
|
|
382
|
+
|
|
383
|
+
<Column>
|
|
384
|
+
<h4 className={styles.sectionHeader}>{t('patient', 'Patient')}</h4>
|
|
385
|
+
{patientSelected && patientUuid ? (
|
|
386
|
+
<PatientInfo patientUuid={patientUuid} />
|
|
387
|
+
) : (
|
|
388
|
+
<ExtensionSlot
|
|
389
|
+
name="patient-search-bar-slot"
|
|
390
|
+
state={{
|
|
391
|
+
selectPatientAction: selectPatient,
|
|
392
|
+
buttonProps: { kind: 'primary' },
|
|
393
|
+
}}
|
|
111
394
|
/>
|
|
112
395
|
)}
|
|
113
|
-
{
|
|
114
|
-
<div className={styles.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
396
|
+
{validationErrors.patientUuid && (
|
|
397
|
+
<div className={styles.errorMessage}>{validationErrors.patientUuid}</div>
|
|
398
|
+
)}
|
|
399
|
+
</Column>
|
|
400
|
+
|
|
401
|
+
<Column>
|
|
402
|
+
<h4 className={styles.sectionHeader}>{t('referralReasons', 'Referral Reasons')}</h4>
|
|
403
|
+
<Search
|
|
404
|
+
size="lg"
|
|
405
|
+
placeholder={t('searchReasons', 'Search for referral reasons')}
|
|
406
|
+
labelText={t('search', 'Search')}
|
|
407
|
+
value={reasonSearchTerm}
|
|
408
|
+
onChange={(e) => {
|
|
409
|
+
const value = e.target.value;
|
|
410
|
+
setReasonSearchTerm(value);
|
|
411
|
+
}}
|
|
412
|
+
/>
|
|
413
|
+
{reasonSearchTerm && reasons?.length > 0 && (
|
|
414
|
+
<div className={styles.searchResults}>
|
|
415
|
+
{reasons.map((reason) => (
|
|
416
|
+
<Tile key={reason.uuid} className={styles.resultTile} onClick={() => handleReasonSelect(reason)}>
|
|
417
|
+
{reason.name.name}
|
|
123
418
|
</Tile>
|
|
124
419
|
))}
|
|
125
420
|
</div>
|
|
126
421
|
)}
|
|
127
|
-
{
|
|
128
|
-
<
|
|
129
|
-
{
|
|
130
|
-
|
|
422
|
+
{selectedReasons.length > 0 && (
|
|
423
|
+
<div className={styles.selectedItems}>
|
|
424
|
+
{selectedReasons.map((reason) => (
|
|
425
|
+
<Tag key={reason.uuid} size="lg" type="gray" filter onClose={() => handleRemoveReason(reason.uuid)}>
|
|
426
|
+
{reason.name.name}
|
|
427
|
+
</Tag>
|
|
428
|
+
))}
|
|
429
|
+
</div>
|
|
131
430
|
)}
|
|
431
|
+
{validationErrors.selectedReasons && (
|
|
432
|
+
<div className={styles.errorMessage}>{validationErrors.selectedReasons}</div>
|
|
433
|
+
)}
|
|
434
|
+
</Column>
|
|
435
|
+
<Column>
|
|
436
|
+
<h4 className={styles.sectionHeader}>{t('clinicalNotes', 'Clinical Notes')}</h4>
|
|
437
|
+
<TextArea
|
|
438
|
+
labelText={t('clinicalNotes', 'Clinical Notes')}
|
|
439
|
+
rows={4}
|
|
440
|
+
value={clinicalNotes}
|
|
441
|
+
onChange={(e) => {
|
|
442
|
+
const value = e.target.value;
|
|
443
|
+
setClinicalNotes(value);
|
|
444
|
+
|
|
445
|
+
if (validationErrors.clinicalNotes) {
|
|
446
|
+
setValidationErrors((prev) => ({ ...prev, clinicalNotes: undefined }));
|
|
447
|
+
}
|
|
448
|
+
}}
|
|
449
|
+
invalid={!!validationErrors.clinicalNotes}
|
|
450
|
+
invalidText={validationErrors.clinicalNotes}
|
|
451
|
+
/>
|
|
132
452
|
</Column>
|
|
133
453
|
</>
|
|
134
454
|
)}
|
|
135
|
-
<span className={styles.sectionHeader}>Search for patient</span>
|
|
136
|
-
{patientSelected && <PatientInfo patientUuid={patientUuid} />}
|
|
137
|
-
{!patientSelected && (
|
|
138
|
-
<Column>
|
|
139
|
-
<ExtensionSlot
|
|
140
|
-
name="patient-search-bar-slot"
|
|
141
|
-
state={{
|
|
142
|
-
selectPatientAction: selectPatient,
|
|
143
|
-
buttonProps: {
|
|
144
|
-
kind: 'primary',
|
|
145
|
-
},
|
|
146
|
-
}}
|
|
147
|
-
/>
|
|
148
|
-
</Column>
|
|
149
|
-
)}
|
|
150
|
-
<span className={styles.sectionHeader}>Search for referral reasons</span>
|
|
151
|
-
<Column>
|
|
152
|
-
<Search
|
|
153
|
-
size="lg"
|
|
154
|
-
placeholder="Search for reasons for referral"
|
|
155
|
-
labelText="Search"
|
|
156
|
-
closeButtonLabelText="Clear reason"
|
|
157
|
-
id="reason-for-referral"
|
|
158
|
-
value={reasonSearchTerm}
|
|
159
|
-
onChange={handleReasonSearchChange}
|
|
160
|
-
/>
|
|
161
|
-
{reasonSearchTerm && reasons && (
|
|
162
|
-
<div className={styles.reasonList}>
|
|
163
|
-
{reasons.map((reason) => (
|
|
164
|
-
<Tile
|
|
165
|
-
key={reason.uuid}
|
|
166
|
-
className={styles.reasonTile}
|
|
167
|
-
onClick={() => handleReasonSelect(reason)}
|
|
168
|
-
role="button"
|
|
169
|
-
tabIndex={0}>
|
|
170
|
-
{reason.name.name}
|
|
171
|
-
</Tile>
|
|
172
|
-
))}
|
|
173
|
-
</div>
|
|
174
|
-
)}
|
|
175
|
-
</Column>
|
|
176
|
-
{selectedReasons.length > 0 && (
|
|
177
|
-
<Column className={styles.textbox}>
|
|
178
|
-
<span className={styles.sectionHeader}>Selected Reasons</span>
|
|
179
|
-
<div className={styles.selectedReasons}>
|
|
180
|
-
{selectedReasons.map((reason) => (
|
|
181
|
-
<Tile key={reason.uuid} className={styles.reasonTile} onClick={() => handleRemoveReason(reason.uuid)}>
|
|
182
|
-
{reason.name.name}
|
|
183
|
-
</Tile>
|
|
184
|
-
))}
|
|
185
|
-
</div>
|
|
186
|
-
</Column>
|
|
187
|
-
)}
|
|
188
|
-
<span className={styles.sectionHeader}>Clinical Notes</span>
|
|
189
|
-
<Column>
|
|
190
|
-
<TextArea labelText={t('reason', 'Reason')} rows={4} id="text-area-1" className={styles.reasonTextbox} />
|
|
191
|
-
</Column>
|
|
192
455
|
</Stack>
|
|
193
|
-
|
|
194
456
|
<ButtonSet className={styles.buttonSet}>
|
|
195
|
-
<Button
|
|
196
|
-
{t('
|
|
457
|
+
<Button kind="secondary" onClick={closeWorkspace} disabled={isSubmitting}>
|
|
458
|
+
{t('cancel', 'Cancel')}
|
|
197
459
|
</Button>
|
|
198
|
-
<Button
|
|
199
|
-
{
|
|
460
|
+
<Button kind="primary" type="submit" disabled={isSubmitting || !isFormValid || isLoadingPatient}>
|
|
461
|
+
{isSubmitting ? (
|
|
462
|
+
<InlineLoading description={t('submitting', 'Submitting...')} />
|
|
463
|
+
) : (
|
|
464
|
+
t('submitReferral', 'Submit Referral')
|
|
465
|
+
)}
|
|
200
466
|
</Button>
|
|
201
467
|
</ButtonSet>
|
|
202
468
|
</Form>
|