@kenyaemr/esm-patient-clinical-view-app 5.4.2-pre.2788 → 5.4.2-pre.2797
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 +4 -4
- package/dist/127.js +1 -1
- package/dist/40.js +1 -1
- package/dist/{189.js → 791.js} +1 -1
- package/dist/791.js.map +1 -0
- package/dist/916.js +1 -1
- package/dist/kenyaemr-esm-patient-clinical-view-app.js +3 -3
- package/dist/kenyaemr-esm-patient-clinical-view-app.js.buildmanifest.json +36 -36
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/config-schema.ts +8 -2
- package/src/maternal-and-child-health/partography/forms/cervical-contractions-form.component.tsx +63 -28
- package/src/maternal-and-child-health/partography/forms/cervix-form.component.tsx +52 -108
- package/src/maternal-and-child-health/partography/forms/drugs-iv-fluids-form.component.tsx +110 -100
- package/src/maternal-and-child-health/partography/forms/fetal-heart-rate-form.component.tsx +7 -7
- package/src/maternal-and-child-health/partography/forms/membrane-amniotic-fluid-form.component.tsx +72 -75
- package/src/maternal-and-child-health/partography/forms/oxytocin-form.component.tsx +14 -19
- package/src/maternal-and-child-health/partography/forms/pulse-bp-form.component.tsx +34 -28
- package/src/maternal-and-child-health/partography/forms/temperature-form.component.tsx +10 -9
- package/src/maternal-and-child-health/partography/forms/time-picker-dropdown.component.tsx +10 -10
- package/src/maternal-and-child-health/partography/forms/urine-test-form.component.tsx +7 -6
- package/src/maternal-and-child-health/partography/graphs/cervical-contractions-graph.component.tsx +51 -61
- package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph-wrapper.component.tsx +0 -6
- package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph.component.tsx +3 -3
- package/src/maternal-and-child-health/partography/graphs/fetal-heart-rate-graph.component.tsx +53 -30
- package/src/maternal-and-child-health/partography/graphs/membrane-amniotic-fluid-graph.component.tsx +25 -3
- package/src/maternal-and-child-health/partography/graphs/oxytocin-graph-wrapper.component.tsx +102 -97
- package/src/maternal-and-child-health/partography/graphs/oxytocin-graph.component.tsx +21 -22
- package/src/maternal-and-child-health/partography/graphs/pulse-bp-graph.component.tsx +21 -24
- package/src/maternal-and-child-health/partography/graphs/temperature-graph.component.tsx +23 -26
- package/src/maternal-and-child-health/partography/graphs/urine-test-graph.component.tsx +1 -1
- package/src/maternal-and-child-health/partography/partograph.component.tsx +323 -171
- package/src/maternal-and-child-health/partography/partography-data-form.scss +23 -0
- package/src/maternal-and-child-health/partography/partography.resource.ts +70 -56
- package/src/maternal-and-child-health/partography/partography.scss +65 -6
- package/src/maternal-and-child-health/partography/resources/fetal-heart-rate.resource.ts +5 -10
- package/src/maternal-and-child-health/partography/resources/oxytocin.resource.ts +5 -3
- package/src/maternal-and-child-health/partography/types/index.ts +40 -37
- package/translations/am.json +1 -1
- package/translations/en.json +1 -1
- package/translations/sw.json +1 -1
- package/dist/189.js.map +0 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import React, { useState, useCallback } from 'react';
|
|
1
|
+
import React, { useState, useCallback, useEffect } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { useForm, Controller } from 'react-hook-form';
|
|
4
|
-
import { Button, Modal, Grid, Column, Dropdown,
|
|
5
|
-
import {
|
|
6
|
-
import { launchWorkspace } from '@openmrs/esm-framework';
|
|
4
|
+
import { Button, Modal, Grid, Column, Dropdown, TextInput, ButtonSkeleton } from '@carbon/react';
|
|
5
|
+
import { launchWorkspace, useSession, openmrsFetch, showSnackbar } from '@openmrs/esm-framework';
|
|
7
6
|
import { saveDrugOrderData } from '../partography.resource';
|
|
8
7
|
import styles from '../partography-data-form.scss';
|
|
9
8
|
import { ROUTE_OPTIONS, FREQUENCY_OPTIONS } from '../types';
|
|
@@ -30,9 +29,27 @@ type DrugsIVFluidsFormProps = {
|
|
|
30
29
|
|
|
31
30
|
const DrugsIVFluidsForm: React.FC<DrugsIVFluidsFormProps> = ({ isOpen, onClose, onSubmit, onDataSaved, patient }) => {
|
|
32
31
|
const { t } = useTranslation();
|
|
32
|
+
const session = useSession();
|
|
33
33
|
const [isSaving, setIsSaving] = useState(false);
|
|
34
|
-
const [
|
|
35
|
-
|
|
34
|
+
const [providerUuid, setProviderUuid] = useState<string | undefined>(undefined);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const fetchProviderData = async () => {
|
|
38
|
+
if (session?.user?.person?.uuid && !providerUuid) {
|
|
39
|
+
try {
|
|
40
|
+
const providerResp = await openmrsFetch(`/ws/rest/v1/provider?person=${session.user.person.uuid}&v=default`);
|
|
41
|
+
const providerData = await providerResp.json();
|
|
42
|
+
if (providerData.results && providerData.results.length > 0) {
|
|
43
|
+
setProviderUuid(providerData.results[0].uuid);
|
|
44
|
+
}
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.warn('Failed to fetch provider data:', e);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
fetchProviderData();
|
|
52
|
+
}, [session?.user?.person?.uuid, providerUuid]);
|
|
36
53
|
|
|
37
54
|
const {
|
|
38
55
|
control,
|
|
@@ -71,8 +88,6 @@ const DrugsIVFluidsForm: React.FC<DrugsIVFluidsFormProps> = ({ isOpen, onClose,
|
|
|
71
88
|
|
|
72
89
|
const onSubmitForm = async (data: DrugsIVFluidsFormData) => {
|
|
73
90
|
clearErrors();
|
|
74
|
-
setSaveError(null);
|
|
75
|
-
setSaveSuccess(false);
|
|
76
91
|
|
|
77
92
|
if (!data.drugName || data.drugName === '') {
|
|
78
93
|
setError('drugName', {
|
|
@@ -106,67 +121,109 @@ const DrugsIVFluidsForm: React.FC<DrugsIVFluidsFormProps> = ({ isOpen, onClose,
|
|
|
106
121
|
return;
|
|
107
122
|
}
|
|
108
123
|
|
|
109
|
-
if (patient
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
124
|
+
if (!patient) {
|
|
125
|
+
showSnackbar({
|
|
126
|
+
title: t('validationError', 'Validation Error'),
|
|
127
|
+
subtitle: t('noPatientSelected', 'No patient selected'),
|
|
128
|
+
kind: 'error',
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!patient.uuid) {
|
|
133
|
+
showSnackbar({
|
|
134
|
+
title: t('validationError', 'Validation Error'),
|
|
135
|
+
subtitle: t('patientMissingUuid', 'Patient is missing a UUID'),
|
|
136
|
+
kind: 'error',
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
setIsSaving(true);
|
|
142
|
+
|
|
143
|
+
const abortController = new AbortController();
|
|
144
|
+
|
|
145
|
+
const timeoutId = setTimeout(() => {
|
|
146
|
+
abortController.abort();
|
|
147
|
+
}, 30000);
|
|
148
|
+
try {
|
|
149
|
+
const locationUuid = session?.sessionLocation?.uuid;
|
|
150
|
+
|
|
151
|
+
const result = await saveDrugOrderData(
|
|
152
|
+
patient.uuid,
|
|
153
|
+
{
|
|
113
154
|
drugName: data.drugName,
|
|
114
155
|
dosage: data.dosage,
|
|
115
156
|
route: data.route,
|
|
116
157
|
frequency: data.frequency,
|
|
117
|
-
}
|
|
158
|
+
},
|
|
159
|
+
locationUuid,
|
|
160
|
+
providerUuid,
|
|
161
|
+
abortController.signal,
|
|
162
|
+
);
|
|
118
163
|
|
|
119
|
-
|
|
120
|
-
setSaveSuccess(true);
|
|
164
|
+
clearTimeout(timeoutId);
|
|
121
165
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
166
|
+
if (result.success) {
|
|
167
|
+
// Show success notification
|
|
168
|
+
showSnackbar({
|
|
169
|
+
title: t('drugOrderSaved', 'Drug order saved'),
|
|
170
|
+
subtitle: t('drugOrderSavedSuccessfully', 'Drug order has been saved successfully'),
|
|
171
|
+
kind: 'success',
|
|
172
|
+
isLowContrast: true,
|
|
173
|
+
});
|
|
125
174
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
route: data.route,
|
|
130
|
-
frequency: data.frequency,
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
reset();
|
|
134
|
-
|
|
135
|
-
setTimeout(() => {
|
|
136
|
-
setSaveSuccess(false);
|
|
137
|
-
onClose();
|
|
138
|
-
}, 1500);
|
|
139
|
-
} else {
|
|
140
|
-
setSaveError(result.message || t('saveError', 'Failed to save data'));
|
|
175
|
+
// Call callbacks
|
|
176
|
+
if (onDataSaved) {
|
|
177
|
+
onDataSaved();
|
|
141
178
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
179
|
+
onSubmit({
|
|
180
|
+
drugName: data.drugName,
|
|
181
|
+
dosage: data.dosage,
|
|
182
|
+
route: data.route,
|
|
183
|
+
frequency: data.frequency,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Reset form and close modal
|
|
187
|
+
reset();
|
|
188
|
+
onClose();
|
|
189
|
+
} else {
|
|
190
|
+
showSnackbar({
|
|
191
|
+
title: t('errorSavingDrugOrder', 'Error saving drug order'),
|
|
192
|
+
subtitle: result.message || t('saveError', 'Failed to save data'),
|
|
193
|
+
kind: 'error',
|
|
194
|
+
});
|
|
152
195
|
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
196
|
+
} catch (error) {
|
|
197
|
+
clearTimeout(timeoutId);
|
|
198
|
+
|
|
199
|
+
// Handle request cancellation (timeout or manual abort)
|
|
200
|
+
if (error.name === 'AbortError') {
|
|
201
|
+
showSnackbar({
|
|
202
|
+
title: t('requestCancelled', 'Request cancelled'),
|
|
203
|
+
subtitle: t('saveTimeout', 'Request was cancelled due to timeout. Please try again.'),
|
|
204
|
+
kind: 'warning',
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Handle other errors
|
|
210
|
+
showSnackbar({
|
|
211
|
+
title: t('errorSavingDrugOrder', 'Error saving drug order'),
|
|
212
|
+
subtitle:
|
|
213
|
+
error?.message ||
|
|
214
|
+
error?.responseBody?.error?.message ||
|
|
215
|
+
error?.response?.data?.error?.message ||
|
|
216
|
+
t('saveError', 'Failed to save data'),
|
|
217
|
+
kind: 'error',
|
|
159
218
|
});
|
|
160
|
-
|
|
161
|
-
|
|
219
|
+
} finally {
|
|
220
|
+
setIsSaving(false);
|
|
162
221
|
}
|
|
163
222
|
};
|
|
164
223
|
|
|
165
224
|
const handleClose = () => {
|
|
166
225
|
reset();
|
|
167
226
|
clearErrors();
|
|
168
|
-
setSaveError(null);
|
|
169
|
-
setSaveSuccess(false);
|
|
170
227
|
onClose();
|
|
171
228
|
};
|
|
172
229
|
|
|
@@ -184,55 +241,8 @@ const DrugsIVFluidsForm: React.FC<DrugsIVFluidsFormProps> = ({ isOpen, onClose,
|
|
|
184
241
|
onRequestSubmit={handleSubmit(onSubmitForm)}
|
|
185
242
|
onSecondarySubmit={handleClose}
|
|
186
243
|
size="md">
|
|
187
|
-
{saveSuccess && (
|
|
188
|
-
<InlineNotification
|
|
189
|
-
kind="success"
|
|
190
|
-
title={t('saveSuccess', 'Data saved successfully')}
|
|
191
|
-
subtitle={t('drugOrderDataSaved', 'Drug order data has been saved to OpenMRS')}
|
|
192
|
-
hideCloseButton
|
|
193
|
-
/>
|
|
194
|
-
)}
|
|
195
|
-
|
|
196
|
-
{saveError && (
|
|
197
|
-
<InlineNotification
|
|
198
|
-
kind="error"
|
|
199
|
-
title={t('saveError', 'Error saving data')}
|
|
200
|
-
subtitle={saveError}
|
|
201
|
-
onCloseButtonClick={() => setSaveError(null)}
|
|
202
|
-
/>
|
|
203
|
-
)}
|
|
204
|
-
|
|
205
244
|
<div className={styles.modalContent}>
|
|
206
245
|
<Grid>
|
|
207
|
-
<Column sm={4} md={8} lg={16}>
|
|
208
|
-
<div className={styles.workspaceLauncherSection}>
|
|
209
|
-
<h4>{t('selectFromDrugList', 'Select from Drug List')}</h4>
|
|
210
|
-
<p className={styles.helperText}>
|
|
211
|
-
{t(
|
|
212
|
-
'drugOrderDescription',
|
|
213
|
-
'Use the drug order workspace to select from the complete list of available drugs with proper dosing and administration details.',
|
|
214
|
-
)}
|
|
215
|
-
</p>
|
|
216
|
-
<Button
|
|
217
|
-
kind="tertiary"
|
|
218
|
-
renderIcon={Add}
|
|
219
|
-
onClick={handleLaunchDrugOrderWorkspace}
|
|
220
|
-
disabled={!patient?.uuid}
|
|
221
|
-
className={styles.workspaceLauncherButton}>
|
|
222
|
-
{t('addDrugOrder', 'Add drug order')}
|
|
223
|
-
</Button>
|
|
224
|
-
</div>
|
|
225
|
-
</Column>
|
|
226
|
-
|
|
227
|
-
<Column sm={4} md={8} lg={16}>
|
|
228
|
-
<div className={styles.manualEntrySection}>
|
|
229
|
-
<h4>{t('manualEntry', 'Manual Entry')}</h4>
|
|
230
|
-
<p className={styles.helperText}>
|
|
231
|
-
{t('manualEntryDescription', 'Or enter drug information manually for quick documentation.')}
|
|
232
|
-
</p>
|
|
233
|
-
</div>
|
|
234
|
-
</Column>
|
|
235
|
-
|
|
236
246
|
<Column sm={4} md={8} lg={16}>
|
|
237
247
|
<Controller
|
|
238
248
|
name="drugName"
|
|
@@ -57,13 +57,13 @@ const FetalHeartRateForm: React.FC<FetalHeartRateFormProps> = ({
|
|
|
57
57
|
});
|
|
58
58
|
const generateHourOptions = () => {
|
|
59
59
|
const options = [];
|
|
60
|
-
|
|
61
|
-
options.push({ value: '0
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
60
|
+
// Add 00 hour option first
|
|
61
|
+
options.push({ value: '0', label: '0hr' });
|
|
62
|
+
// Start from 0.5hr, then 1hr, 1.5hr, ... up to 24hr (in 0.5 increments)
|
|
63
|
+
for (let i = 1; i <= 48; i++) {
|
|
64
|
+
const value = (i * 0.5).toString();
|
|
65
|
+
const label = i % 2 === 0 ? `${i / 2}hr` : `${i * 0.5}hr`;
|
|
66
|
+
options.push({ value, label });
|
|
67
67
|
}
|
|
68
68
|
return options;
|
|
69
69
|
};
|
package/src/maternal-and-child-health/partography/forms/membrane-amniotic-fluid-form.component.tsx
CHANGED
|
@@ -69,88 +69,48 @@ const MembraneAmnioticFluidForm: React.FC<MembraneAmnioticFluidFormProps> = ({
|
|
|
69
69
|
[],
|
|
70
70
|
);
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
// Calculate the latest used hour from existing entries (similar to fetal heart rate form)
|
|
73
|
+
const latestUsedHour = React.useMemo(() => {
|
|
73
74
|
if (!existingTimeEntries || existingTimeEntries.length === 0) {
|
|
74
75
|
return null;
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.slice()
|
|
79
|
-
.sort((a, b) => {
|
|
80
|
-
const getMinutes = (time: string) => {
|
|
81
|
-
const [hours, minutes] = time.split(':').map(Number);
|
|
77
|
+
// Convert timeSlot values to numeric hours and find the maximum
|
|
78
|
+
const hours = existingTimeEntries.map((entry) => parseFloat(entry.timeSlot || '0')).filter((hour) => !isNaN(hour)); // Filter out invalid values
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
return adjustedHours * 60 + minutes;
|
|
85
|
-
};
|
|
86
|
-
return getMinutes(a) - getMinutes(b);
|
|
87
|
-
});
|
|
88
|
-
return sortedTimeSlots[sortedTimeSlots.length - 1];
|
|
80
|
+
return hours.length > 0 ? Math.max(...hours) : null;
|
|
89
81
|
}, [existingTimeEntries]);
|
|
90
82
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const getMinutes = (time: string) => {
|
|
97
|
-
const [hours, minutes] = time.split(':').map(Number);
|
|
98
|
-
|
|
99
|
-
const adjustedHours = hours <= 5 ? hours + 24 : hours;
|
|
100
|
-
return adjustedHours * 60 + minutes;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
return getMinutes(timeSlot) <= getMinutes(latestUsedTimeSlot);
|
|
104
|
-
};
|
|
83
|
+
// Generate time slot options with disabled state (similar to fetal heart rate form)
|
|
84
|
+
const timeSlotOptionsWithDisabled = React.useMemo(() => {
|
|
85
|
+
return timeSlotOptions.map((option) => {
|
|
86
|
+
const hourValue = parseFloat(option.value);
|
|
87
|
+
const isDisabled = latestUsedHour !== null && hourValue <= latestUsedHour;
|
|
105
88
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
},
|
|
113
|
-
{ value: 'Clear liquor', label: t('clearLiquor', 'Clear liquor'), concept: AMNIOTIC_CLEAR_LIQUOR_CONCEPT },
|
|
114
|
-
{
|
|
115
|
-
value: 'Meconium Stained',
|
|
116
|
-
label: t('meconiumStained', 'Meconium Stained'),
|
|
117
|
-
concept: AMNIOTIC_MECONIUM_STAINED_CONCEPT,
|
|
118
|
-
},
|
|
119
|
-
{ value: 'Absent', label: t('absent', 'Absent'), concept: AMNIOTIC_ABSENT_CONCEPT },
|
|
120
|
-
{
|
|
121
|
-
value: 'Blood Stained',
|
|
122
|
-
label: t('bloodStained', 'Blood Stained'),
|
|
123
|
-
concept: AMNIOTIC_BLOOD_STAINED_CONCEPT,
|
|
124
|
-
},
|
|
125
|
-
],
|
|
126
|
-
[t],
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const mouldingOptions = useMemo(
|
|
130
|
-
() => [
|
|
131
|
-
{ value: '0', label: '0', concept: MOULDING_NONE_CONCEPT },
|
|
132
|
-
{ value: '+', label: '+', concept: MOULDING_SLIGHT_CONCEPT },
|
|
133
|
-
{ value: '++', label: '++', concept: MOULDING_MODERATE_CONCEPT },
|
|
134
|
-
{ value: '+++', label: '+++', concept: MOULDING_SEVERE_CONCEPT },
|
|
135
|
-
],
|
|
136
|
-
[],
|
|
137
|
-
);
|
|
89
|
+
return {
|
|
90
|
+
...option,
|
|
91
|
+
disabled: isDisabled,
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}, [timeSlotOptions, latestUsedHour]);
|
|
138
95
|
|
|
139
96
|
const handleFormSubmit = (data: MembraneAmnioticFluidFormData) => {
|
|
140
97
|
clearErrors();
|
|
141
98
|
|
|
142
99
|
let hasErrors = false;
|
|
143
100
|
|
|
101
|
+
// Validate time slot selection
|
|
144
102
|
if (!data.timeSlot || data.timeSlot.trim() === '') {
|
|
145
103
|
setError('timeSlot', { type: 'manual', message: t('timeSlotRequired', 'Please select a time slot') });
|
|
146
104
|
hasErrors = true;
|
|
147
105
|
}
|
|
148
106
|
|
|
107
|
+
// Validate exact time
|
|
149
108
|
if (!data.exactTime || data.exactTime.trim() === '') {
|
|
150
109
|
setError('exactTime', { type: 'manual', message: t('exactTimeRequired', 'Exact time is required') });
|
|
151
110
|
hasErrors = true;
|
|
152
111
|
}
|
|
153
112
|
|
|
113
|
+
// Validate amniotic fluid selection
|
|
154
114
|
if (!data.amnioticFluid || data.amnioticFluid.trim() === '') {
|
|
155
115
|
setError('amnioticFluid', {
|
|
156
116
|
type: 'manual',
|
|
@@ -159,6 +119,7 @@ const MembraneAmnioticFluidForm: React.FC<MembraneAmnioticFluidFormProps> = ({
|
|
|
159
119
|
hasErrors = true;
|
|
160
120
|
}
|
|
161
121
|
|
|
122
|
+
// Validate moulding selection
|
|
162
123
|
if (!data.moulding || data.moulding.trim() === '') {
|
|
163
124
|
setError('moulding', {
|
|
164
125
|
type: 'manual',
|
|
@@ -172,10 +133,16 @@ const MembraneAmnioticFluidForm: React.FC<MembraneAmnioticFluidFormProps> = ({
|
|
|
172
133
|
return;
|
|
173
134
|
}
|
|
174
135
|
|
|
175
|
-
|
|
136
|
+
// Progressive validation - prevent selecting hours before the latest entered hour
|
|
137
|
+
const selectedHour = parseFloat(data.timeSlot);
|
|
138
|
+
|
|
139
|
+
if (latestUsedHour !== null && selectedHour <= latestUsedHour) {
|
|
176
140
|
setError('timeSlot', {
|
|
177
141
|
type: 'manual',
|
|
178
|
-
message: t(
|
|
142
|
+
message: t(
|
|
143
|
+
'timeSlotDisabled',
|
|
144
|
+
`Cannot select ${data.timeSlot}hr. Please select a time after ${latestUsedHour}hr.`,
|
|
145
|
+
),
|
|
179
146
|
});
|
|
180
147
|
alert(t('timeSlotValidationError', 'Please select a valid time slot that comes after the previous entry.'));
|
|
181
148
|
return;
|
|
@@ -191,6 +158,39 @@ const MembraneAmnioticFluidForm: React.FC<MembraneAmnioticFluidFormProps> = ({
|
|
|
191
158
|
reset();
|
|
192
159
|
};
|
|
193
160
|
|
|
161
|
+
const amnioticFluidOptions = useMemo(
|
|
162
|
+
() => [
|
|
163
|
+
{
|
|
164
|
+
value: 'Membrane intact',
|
|
165
|
+
label: t('membraneIntact', 'Membrane intact'),
|
|
166
|
+
concept: AMNIOTIC_MEMBRANE_INTACT_CONCEPT,
|
|
167
|
+
},
|
|
168
|
+
{ value: 'Clear liquor', label: t('clearLiquor', 'Clear liquor'), concept: AMNIOTIC_CLEAR_LIQUOR_CONCEPT },
|
|
169
|
+
{
|
|
170
|
+
value: 'Meconium Stained',
|
|
171
|
+
label: t('meconiumStained', 'Meconium Stained'),
|
|
172
|
+
concept: AMNIOTIC_MECONIUM_STAINED_CONCEPT,
|
|
173
|
+
},
|
|
174
|
+
{ value: 'Absent', label: t('absent', 'Absent'), concept: AMNIOTIC_ABSENT_CONCEPT },
|
|
175
|
+
{
|
|
176
|
+
value: 'Blood Stained',
|
|
177
|
+
label: t('bloodStained', 'Blood Stained'),
|
|
178
|
+
concept: AMNIOTIC_BLOOD_STAINED_CONCEPT,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
[t],
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const mouldingOptions = useMemo(
|
|
185
|
+
() => [
|
|
186
|
+
{ value: '0', label: '0', concept: MOULDING_NONE_CONCEPT },
|
|
187
|
+
{ value: '+', label: '+', concept: MOULDING_SLIGHT_CONCEPT },
|
|
188
|
+
{ value: '++', label: '++', concept: MOULDING_MODERATE_CONCEPT },
|
|
189
|
+
{ value: '+++', label: '+++', concept: MOULDING_SEVERE_CONCEPT },
|
|
190
|
+
],
|
|
191
|
+
[],
|
|
192
|
+
);
|
|
193
|
+
|
|
194
194
|
const handleClose = () => {
|
|
195
195
|
reset();
|
|
196
196
|
clearErrors();
|
|
@@ -223,25 +223,22 @@ const MembraneAmnioticFluidForm: React.FC<MembraneAmnioticFluidFormProps> = ({
|
|
|
223
223
|
invalid={!!fieldState.error}
|
|
224
224
|
invalidText={fieldState.error?.message}
|
|
225
225
|
helperText={
|
|
226
|
-
|
|
227
|
-
? t('timeSlotInfo', `Select a time after ${
|
|
226
|
+
latestUsedHour !== null
|
|
227
|
+
? t('timeSlotInfo', `Select a time after ${latestUsedHour}hr`)
|
|
228
228
|
: t('timeSlotInfoInitial', 'Select a time slot')
|
|
229
229
|
}
|
|
230
230
|
value={field.value}
|
|
231
231
|
onChange={(e) => field.onChange(e.target.value)}>
|
|
232
232
|
<SelectItem value="" text={t('chooseAnOption', 'Choose an option')} />
|
|
233
|
-
{
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
/>
|
|
243
|
-
);
|
|
244
|
-
})}
|
|
233
|
+
{timeSlotOptionsWithDisabled.map((option) => (
|
|
234
|
+
<SelectItem
|
|
235
|
+
key={option.value}
|
|
236
|
+
value={option.value}
|
|
237
|
+
text={option.label}
|
|
238
|
+
disabled={option.disabled}
|
|
239
|
+
className={option.disabled ? styles.disabledOption : undefined}
|
|
240
|
+
/>
|
|
241
|
+
))}
|
|
245
242
|
</Select>
|
|
246
243
|
)}
|
|
247
244
|
/>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { useForm, Controller } from 'react-hook-form';
|
|
4
|
-
import { Button, Modal, Grid, Column, NumberInput,
|
|
4
|
+
import { Button, Modal, Grid, Column, NumberInput, Select, SelectItem } from '@carbon/react';
|
|
5
5
|
import TimePickerDropdown from './time-picker-dropdown.component';
|
|
6
6
|
import styles from '../partography-data-form.scss';
|
|
7
7
|
|
|
@@ -45,11 +45,6 @@ const OxytocinForm: React.FC<OxytocinFormProps> = ({
|
|
|
45
45
|
|
|
46
46
|
const watchOxytocinUsed = watch('oxytocinUsed');
|
|
47
47
|
|
|
48
|
-
const oxytocinOptions = [
|
|
49
|
-
{ id: 'yes', text: t('yes', 'Yes') },
|
|
50
|
-
{ id: 'no', text: t('no', 'No') },
|
|
51
|
-
];
|
|
52
|
-
|
|
53
48
|
const onSubmitForm = async (data: OxytocinFormData) => {
|
|
54
49
|
if (!data.time || data.time === '') {
|
|
55
50
|
setError('time', {
|
|
@@ -148,17 +143,17 @@ const OxytocinForm: React.FC<OxytocinFormProps> = ({
|
|
|
148
143
|
required: 'Oxytocin usage selection is required',
|
|
149
144
|
}}
|
|
150
145
|
render={({ field, fieldState }) => (
|
|
151
|
-
<
|
|
152
|
-
id="oxytocin-usage-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
itemToString={(item) => (item ? item.text : '')}
|
|
157
|
-
selectedItem={field.value ? oxytocinOptions.find((opt) => opt.id === field.value) : null}
|
|
158
|
-
onChange={({ selectedItem }) => field.onChange(selectedItem?.id || '')}
|
|
146
|
+
<Select
|
|
147
|
+
id="oxytocin-usage-select"
|
|
148
|
+
labelText={t('oxytocin', 'Oxytocin')}
|
|
149
|
+
value={field.value}
|
|
150
|
+
onChange={(e) => field.onChange(e.target.value)}
|
|
159
151
|
invalid={!!fieldState.error}
|
|
160
|
-
invalidText={fieldState.error?.message}
|
|
161
|
-
|
|
152
|
+
invalidText={fieldState.error?.message}>
|
|
153
|
+
<SelectItem value="" text={t('selectIfOxytocinIsUsed', 'Select if oxytocin is used')} />
|
|
154
|
+
<SelectItem value="yes" text={t('yes', 'Yes')} />
|
|
155
|
+
<SelectItem value="no" text={t('no', 'No')} />
|
|
156
|
+
</Select>
|
|
162
157
|
)}
|
|
163
158
|
/>
|
|
164
159
|
</Column>
|
|
@@ -185,14 +180,14 @@ const OxytocinForm: React.FC<OxytocinFormProps> = ({
|
|
|
185
180
|
<NumberInput
|
|
186
181
|
id="drops-per-minute-input"
|
|
187
182
|
label={t('dropsPerMinute', 'Drops per minute')}
|
|
188
|
-
placeholder="Enter drops per minute"
|
|
189
|
-
value={field.value || ''}
|
|
190
|
-
onChange={(e, { value }) => field.onChange(String(value))}
|
|
191
183
|
min={0}
|
|
192
184
|
max={60}
|
|
193
185
|
step={1}
|
|
186
|
+
value={field.value || ''}
|
|
187
|
+
onChange={(e, { value }) => field.onChange(String(value))}
|
|
194
188
|
invalid={!!fieldState.error}
|
|
195
189
|
invalidText={fieldState.error?.message}
|
|
190
|
+
allowEmpty
|
|
196
191
|
/>
|
|
197
192
|
)}
|
|
198
193
|
/>
|