@kenyaemr/esm-patient-clinical-view-app 5.4.2-pre.2714 → 5.4.2-pre.2722

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 (66) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/dist/805.js +1 -0
  3. package/dist/805.js.map +1 -0
  4. package/dist/kenyaemr-esm-patient-clinical-view-app.js +2 -2
  5. package/dist/kenyaemr-esm-patient-clinical-view-app.js.buildmanifest.json +27 -27
  6. package/dist/main.js +27 -27
  7. package/dist/main.js.map +1 -1
  8. package/dist/routes.json +1 -1
  9. package/package.json +1 -1
  10. package/src/config-schema.ts +97 -0
  11. package/src/contact-list/contact-tracing-history.component.tsx +18 -15
  12. package/src/maternal-and-child-health/partography/components/pulse-bp-graph.component.tsx +1 -0
  13. package/src/maternal-and-child-health/partography/components/temperature-graph.component.tsx +218 -0
  14. package/src/maternal-and-child-health/partography/components/uterine-contractions-graph.component.tsx +209 -0
  15. package/src/maternal-and-child-health/partography/forms/cervical-contractions-form.component.tsx +211 -0
  16. package/src/maternal-and-child-health/partography/forms/cervix-form.component.tsx +354 -0
  17. package/src/maternal-and-child-health/partography/forms/drugs-iv-fluids-form.component.tsx +321 -0
  18. package/src/maternal-and-child-health/partography/forms/fetal-heart-rate-form.component.tsx +275 -0
  19. package/src/maternal-and-child-health/partography/forms/index.ts +9 -0
  20. package/src/maternal-and-child-health/partography/forms/membrane-amniotic-fluid-form.component.tsx +330 -0
  21. package/src/maternal-and-child-health/partography/forms/oxytocin-form.component.tsx +207 -0
  22. package/src/maternal-and-child-health/partography/forms/pulse-bp-form.component.tsx +174 -0
  23. package/src/maternal-and-child-health/partography/forms/temperature-form.component.tsx +210 -0
  24. package/src/maternal-and-child-health/partography/forms/time-picker-dropdown.component.tsx +218 -0
  25. package/src/maternal-and-child-health/partography/forms/time-picker-dropdown.scss +107 -0
  26. package/src/maternal-and-child-health/partography/forms/time-picker-with-clock.component.tsx +174 -0
  27. package/src/maternal-and-child-health/partography/forms/time-picker-with-clock.scss +178 -0
  28. package/src/maternal-and-child-health/partography/forms/urine-test-form.component.tsx +255 -0
  29. package/src/maternal-and-child-health/partography/forms/useCervixData.ts +16 -0
  30. package/src/maternal-and-child-health/partography/graphs/cervical-contractions-graph.component.tsx +266 -0
  31. package/src/maternal-and-child-health/partography/graphs/cervix-graph.component.tsx +429 -0
  32. package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph-wrapper.component.tsx +163 -0
  33. package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph.component.tsx +82 -0
  34. package/src/maternal-and-child-health/partography/graphs/fetal-heart-rate-graph.component.tsx +359 -0
  35. package/src/maternal-and-child-health/partography/graphs/index.ts +10 -0
  36. package/src/maternal-and-child-health/partography/graphs/membrane-amniotic-fluid-graph.component.tsx +266 -0
  37. package/src/maternal-and-child-health/partography/graphs/oxytocin-graph-wrapper.component.tsx +190 -0
  38. package/src/maternal-and-child-health/partography/graphs/oxytocin-graph.component.tsx +126 -0
  39. package/src/maternal-and-child-health/partography/graphs/partograph-graph.component.tsx +266 -0
  40. package/src/maternal-and-child-health/partography/graphs/pulse-bp-graph-wrapper.component.tsx +298 -0
  41. package/src/maternal-and-child-health/partography/graphs/pulse-bp-graph.component.tsx +267 -0
  42. package/src/maternal-and-child-health/partography/graphs/temperature-graph.component.tsx +242 -0
  43. package/src/maternal-and-child-health/partography/graphs/urine-test-graph.component.tsx +246 -0
  44. package/src/maternal-and-child-health/partography/partograph.component.tsx +2141 -118
  45. package/src/maternal-and-child-health/partography/partography-dashboard.meta.ts +8 -0
  46. package/src/maternal-and-child-health/partography/partography-data-form.scss +163 -0
  47. package/src/maternal-and-child-health/partography/partography.resource.ts +233 -326
  48. package/src/maternal-and-child-health/partography/partography.scss +1341 -3
  49. package/src/maternal-and-child-health/partography/resources/blood-pressure.resource.ts +96 -0
  50. package/src/maternal-and-child-health/partography/resources/cervical-dilation.resource.ts +109 -0
  51. package/src/maternal-and-child-health/partography/resources/cervix.resource.ts +362 -0
  52. package/src/maternal-and-child-health/partography/resources/descent-of-head.resource.ts +101 -0
  53. package/src/maternal-and-child-health/partography/resources/drugs-fluids.resource.ts +88 -0
  54. package/src/maternal-and-child-health/partography/resources/fetal-heart-rate.resource.ts +122 -0
  55. package/src/maternal-and-child-health/partography/resources/maternal-pulse.resource.ts +77 -0
  56. package/src/maternal-and-child-health/partography/resources/membrane-amniotic-fluid.resource.ts +108 -0
  57. package/src/maternal-and-child-health/partography/resources/oxytocin.resource.ts +159 -0
  58. package/src/maternal-and-child-health/partography/resources/progress-events.resource.ts +6 -0
  59. package/src/maternal-and-child-health/partography/resources/pulse-bp-combined.resource.ts +53 -0
  60. package/src/maternal-and-child-health/partography/resources/temperature.resource.ts +84 -0
  61. package/src/maternal-and-child-health/partography/resources/uterine-contractions.resource.ts +173 -0
  62. package/src/maternal-and-child-health/partography/table/temperature-table.component.tsx +99 -0
  63. package/src/maternal-and-child-health/partography/table/uterine-contractions-table.component.tsx +86 -0
  64. package/src/maternal-and-child-health/partography/types/index.ts +319 -101
  65. package/dist/397.js +0 -1
  66. package/dist/397.js.map +0 -1
@@ -0,0 +1,207 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { useForm, Controller } from 'react-hook-form';
4
+ import { Button, Modal, Grid, Column, NumberInput, Dropdown } from '@carbon/react';
5
+ import TimePickerDropdown from './time-picker-dropdown.component';
6
+ import styles from '../partography-data-form.scss';
7
+
8
+ type OxytocinFormData = {
9
+ time: string;
10
+ oxytocinUsed: string;
11
+ dropsPerMinute: string;
12
+ };
13
+
14
+ type OxytocinFormProps = {
15
+ isOpen: boolean;
16
+ onClose: () => void;
17
+ onSubmit: (data: { oxytocinUsed: 'yes' | 'no'; dropsPerMinute: number; timeSlot: string }) => void;
18
+ onDataSaved?: () => void;
19
+ existingTimeEntries?: Array<{ hour: number; time: string }>;
20
+ patient?: {
21
+ uuid: string;
22
+ name: string;
23
+ gender: string;
24
+ age: string;
25
+ };
26
+ };
27
+
28
+ const OxytocinForm: React.FC<OxytocinFormProps> = ({
29
+ isOpen,
30
+ onClose,
31
+ onSubmit,
32
+ onDataSaved,
33
+ existingTimeEntries = [],
34
+ patient,
35
+ }) => {
36
+ const { t } = useTranslation();
37
+
38
+ const { control, handleSubmit, reset, setError, clearErrors, watch } = useForm<OxytocinFormData>({
39
+ defaultValues: {
40
+ time: '',
41
+ oxytocinUsed: '',
42
+ dropsPerMinute: '',
43
+ },
44
+ });
45
+
46
+ const watchOxytocinUsed = watch('oxytocinUsed');
47
+
48
+ const oxytocinOptions = [
49
+ { id: 'yes', text: t('yes', 'Yes') },
50
+ { id: 'no', text: t('no', 'No') },
51
+ ];
52
+
53
+ const onSubmitForm = async (data: OxytocinFormData) => {
54
+ if (!data.time || data.time === '') {
55
+ setError('time', {
56
+ type: 'manual',
57
+ message: 'Time selection is required',
58
+ });
59
+ return;
60
+ }
61
+ if (!data.oxytocinUsed || data.oxytocinUsed === '') {
62
+ setError('oxytocinUsed', {
63
+ type: 'manual',
64
+ message: 'Oxytocin usage selection is required',
65
+ });
66
+ return;
67
+ }
68
+ if (data.oxytocinUsed === 'yes' && (!data.dropsPerMinute || data.dropsPerMinute === '')) {
69
+ setError('dropsPerMinute', {
70
+ type: 'manual',
71
+ message: 'Drops per minute is required when oxytocin is used',
72
+ });
73
+ return;
74
+ }
75
+ const dropsPerMinute = data.oxytocinUsed === 'yes' ? parseInt(data.dropsPerMinute) : 0;
76
+
77
+ if (data.oxytocinUsed === 'yes' && isNaN(dropsPerMinute)) {
78
+ setError('dropsPerMinute', {
79
+ type: 'manual',
80
+ message: 'Invalid drops per minute value',
81
+ });
82
+ return;
83
+ }
84
+ if (data.oxytocinUsed === 'yes' && (dropsPerMinute < 0 || dropsPerMinute > 60)) {
85
+ setError('dropsPerMinute', {
86
+ type: 'manual',
87
+ message: 'Drops per minute must be between 0 and 60',
88
+ });
89
+ return;
90
+ }
91
+ clearErrors();
92
+ const timeSlot = data.time;
93
+ onSubmit({
94
+ oxytocinUsed: data.oxytocinUsed as 'yes' | 'no',
95
+ dropsPerMinute: dropsPerMinute,
96
+ timeSlot: timeSlot,
97
+ });
98
+
99
+ reset();
100
+ onClose();
101
+ };
102
+
103
+ const handleClose = () => {
104
+ reset();
105
+ onClose();
106
+ };
107
+
108
+ const patientLabel = patient ? `${patient.name}, ${patient.gender}, ${patient.age}` : 'Patient Information';
109
+
110
+ return (
111
+ <Modal
112
+ open={isOpen}
113
+ onRequestClose={handleClose}
114
+ modalHeading="Oxytocin"
115
+ modalLabel={patientLabel}
116
+ primaryButtonText={t('save', 'Save')}
117
+ secondaryButtonText={t('cancel', 'Cancel')}
118
+ onRequestSubmit={handleSubmit(onSubmitForm)}
119
+ onSecondarySubmit={handleClose}
120
+ size="md">
121
+ <div className={styles.modalContent}>
122
+ <Grid>
123
+ <Column sm={4} md={8} lg={16}>
124
+ <Controller
125
+ name="time"
126
+ control={control}
127
+ rules={{
128
+ required: 'Time selection is required',
129
+ }}
130
+ render={({ field, fieldState }) => (
131
+ <TimePickerDropdown
132
+ id="time-input"
133
+ labelText={t('chooseTime', 'Choose a time')}
134
+ value={field.value}
135
+ onChange={(value) => field.onChange(value)}
136
+ invalid={!!fieldState.error}
137
+ invalidText={fieldState.error?.message}
138
+ existingTimeEntries={existingTimeEntries}
139
+ />
140
+ )}
141
+ />
142
+ </Column>
143
+ <Column sm={4} md={8} lg={16}>
144
+ <Controller
145
+ name="oxytocinUsed"
146
+ control={control}
147
+ rules={{
148
+ required: 'Oxytocin usage selection is required',
149
+ }}
150
+ render={({ field, fieldState }) => (
151
+ <Dropdown
152
+ id="oxytocin-usage-dropdown"
153
+ titleText={t('oxytocin', 'Oxytocin')}
154
+ label="Select if oxytocin is used"
155
+ items={oxytocinOptions}
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 || '')}
159
+ invalid={!!fieldState.error}
160
+ invalidText={fieldState.error?.message}
161
+ />
162
+ )}
163
+ />
164
+ </Column>
165
+ {watchOxytocinUsed === 'yes' && (
166
+ <Column sm={4} md={8} lg={16}>
167
+ <Controller
168
+ name="dropsPerMinute"
169
+ control={control}
170
+ rules={{
171
+ required: watchOxytocinUsed === 'yes' ? 'Drops per minute is required when oxytocin is used' : false,
172
+ validate: {
173
+ isNumber: (value) => !isNaN(parseInt(value)) || 'Must be a valid number',
174
+ minValue: (value) => {
175
+ const numValue = parseInt(value);
176
+ return numValue >= 0 || 'Cannot be less than 0';
177
+ },
178
+ maxValue: (value) => {
179
+ const numValue = parseInt(value);
180
+ return numValue <= 60 || 'Cannot exceed 60 drops per minute';
181
+ },
182
+ },
183
+ }}
184
+ render={({ field, fieldState }) => (
185
+ <NumberInput
186
+ id="drops-per-minute-input"
187
+ 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
+ min={0}
192
+ max={60}
193
+ step={1}
194
+ invalid={!!fieldState.error}
195
+ invalidText={fieldState.error?.message}
196
+ />
197
+ )}
198
+ />
199
+ </Column>
200
+ )}
201
+ </Grid>
202
+ </div>
203
+ </Modal>
204
+ );
205
+ };
206
+
207
+ export default OxytocinForm;
@@ -0,0 +1,174 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { useForm, Controller } from 'react-hook-form';
4
+ import { Modal, Grid, Column, NumberInput } from '@carbon/react';
5
+ import styles from '../partography-data-form.scss';
6
+
7
+ type PulseBPFormData = {
8
+ pulse: string;
9
+ systolicBP: string;
10
+ diastolicBP: string;
11
+ };
12
+
13
+ type PulseBPFormProps = {
14
+ isOpen: boolean;
15
+ onClose: () => void;
16
+ onSubmit: (data: { pulse: number; systolicBP: number; diastolicBP: number }) => void;
17
+ onDataSaved?: () => void;
18
+ patient?: {
19
+ uuid: string;
20
+ name: string;
21
+ gender: string;
22
+ age: string;
23
+ };
24
+ };
25
+
26
+ const PulseBPForm: React.FC<PulseBPFormProps> = ({ isOpen, onClose, onSubmit, onDataSaved, patient }) => {
27
+ const { t } = useTranslation();
28
+
29
+ const {
30
+ control,
31
+ handleSubmit,
32
+ reset,
33
+ setError,
34
+ clearErrors,
35
+ formState: { errors },
36
+ } = useForm<PulseBPFormData>({
37
+ defaultValues: {
38
+ pulse: '',
39
+ systolicBP: '',
40
+ diastolicBP: '',
41
+ },
42
+ });
43
+
44
+ const onSubmitForm = async (data: PulseBPFormData) => {
45
+ const pulseValue = parseInt(data.pulse);
46
+ const systolicValue = parseInt(data.systolicBP);
47
+ const diastolicValue = parseInt(data.diastolicBP);
48
+
49
+ clearErrors();
50
+
51
+ if (!data.pulse || isNaN(pulseValue) || pulseValue < 30 || pulseValue > 200) {
52
+ setError('pulse', {
53
+ type: 'manual',
54
+ message: 'Please enter a valid pulse rate (30-200 bpm)',
55
+ });
56
+ return;
57
+ }
58
+
59
+ if (!data.systolicBP || isNaN(systolicValue) || systolicValue < 60 || systolicValue > 250) {
60
+ setError('systolicBP', {
61
+ type: 'manual',
62
+ message: 'Please enter a valid systolic BP (60-250 mmHg)',
63
+ });
64
+ return;
65
+ }
66
+
67
+ if (!data.diastolicBP || isNaN(diastolicValue) || diastolicValue < 40 || diastolicValue > 150) {
68
+ setError('diastolicBP', {
69
+ type: 'manual',
70
+ message: 'Please enter a valid diastolic BP (40-150 mmHg)',
71
+ });
72
+ return;
73
+ }
74
+
75
+ if (systolicValue <= diastolicValue) {
76
+ setError('systolicBP', {
77
+ type: 'manual',
78
+ message: 'Systolic BP must be higher than diastolic BP',
79
+ });
80
+ return;
81
+ }
82
+
83
+ onSubmit({
84
+ pulse: pulseValue,
85
+ systolicBP: systolicValue,
86
+ diastolicBP: diastolicValue,
87
+ });
88
+
89
+ reset();
90
+ onClose();
91
+ };
92
+
93
+ const handleClose = () => {
94
+ reset();
95
+ clearErrors();
96
+ onClose();
97
+ };
98
+
99
+ return (
100
+ <Modal
101
+ open={isOpen}
102
+ onRequestClose={handleClose}
103
+ modalHeading={t('addPulseBpData', 'Pulse & BP')}
104
+ modalLabel={patient ? `${patient.name}, ${patient.gender}, ${patient.age}` : 'Patient Information'}
105
+ primaryButtonText={t('save', 'Save')}
106
+ secondaryButtonText={t('cancel', 'Cancel')}
107
+ onRequestSubmit={handleSubmit(onSubmitForm)}
108
+ className={styles.modal}>
109
+ <form>
110
+ <Grid>
111
+ <Column lg={16} md={8} sm={4}>
112
+ <Controller
113
+ name="pulse"
114
+ control={control}
115
+ render={({ field }) => (
116
+ <NumberInput
117
+ id="pulse"
118
+ label={t('pulse', 'Pulse Rate')}
119
+ helperText="Normal: 60-100 bpm"
120
+ min={30}
121
+ max={200}
122
+ value={field.value}
123
+ onChange={(e, { value }) => field.onChange(value)}
124
+ invalid={!!errors.pulse}
125
+ invalidText={errors.pulse?.message}
126
+ />
127
+ )}
128
+ />
129
+ </Column>
130
+ <Column lg={8} md={4} sm={4}>
131
+ <Controller
132
+ name="systolicBP"
133
+ control={control}
134
+ render={({ field }) => (
135
+ <NumberInput
136
+ id="systolicBP"
137
+ label={t('systolicBP', 'BP Systolic')}
138
+ helperText="Normal: 90-149 mmHg"
139
+ min={60}
140
+ max={250}
141
+ value={field.value}
142
+ onChange={(e, { value }) => field.onChange(value)}
143
+ invalid={!!errors.systolicBP}
144
+ invalidText={errors.systolicBP?.message}
145
+ />
146
+ )}
147
+ />
148
+ </Column>
149
+ <Column lg={8} md={4} sm={4}>
150
+ <Controller
151
+ name="diastolicBP"
152
+ control={control}
153
+ render={({ field }) => (
154
+ <NumberInput
155
+ id="diastolicBP"
156
+ label={t('diastolicBP', 'BP Diastolic')}
157
+ helperText="Normal: 60-80 mmHg"
158
+ min={40}
159
+ max={150}
160
+ value={field.value}
161
+ onChange={(e, { value }) => field.onChange(value)}
162
+ invalid={!!errors.diastolicBP}
163
+ invalidText={errors.diastolicBP?.message}
164
+ />
165
+ )}
166
+ />
167
+ </Column>
168
+ </Grid>
169
+ </form>
170
+ </Modal>
171
+ );
172
+ };
173
+
174
+ export default PulseBPForm;
@@ -0,0 +1,210 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { useForm, Controller } from 'react-hook-form';
4
+ import { Modal, Grid, Column, NumberInput } from '@carbon/react';
5
+ import TimePickerDropdown from './time-picker-dropdown.component';
6
+ import styles from '../partography-data-form.scss';
7
+
8
+ type TemperatureFormData = {
9
+ time: string;
10
+ temperature: string;
11
+ };
12
+
13
+ type TemperatureFormProps = {
14
+ isOpen: boolean;
15
+ onClose: () => void;
16
+ onSubmit: (data: { timeSlot: string; exactTime: string; temperature: number }) => void;
17
+ onDataSaved?: () => void;
18
+ existingTimeEntries?: Array<{ hour: number; time: string }>;
19
+ patient?: {
20
+ uuid: string;
21
+ name: string;
22
+ gender: string;
23
+ age: string;
24
+ };
25
+ initialTime?: string;
26
+ };
27
+
28
+ const TemperatureForm: React.FC<TemperatureFormProps> = ({
29
+ isOpen,
30
+ onClose,
31
+ onSubmit,
32
+ onDataSaved,
33
+ existingTimeEntries = [],
34
+ patient,
35
+ }) => {
36
+ const { t } = useTranslation();
37
+
38
+ const {
39
+ control,
40
+ handleSubmit,
41
+ reset,
42
+ setError,
43
+ clearErrors,
44
+ watch,
45
+ formState: { errors },
46
+ } = useForm<TemperatureFormData>({
47
+ defaultValues: {
48
+ temperature: '',
49
+ },
50
+ });
51
+
52
+ const watchedTime = watch('time');
53
+
54
+ const onSubmitForm = async (data: TemperatureFormData) => {
55
+ const temperatureValue = parseFloat(data.temperature);
56
+
57
+ clearErrors();
58
+
59
+ if (!data.time) {
60
+ setError('time', {
61
+ type: 'manual',
62
+ message: t('timeRequired', 'Please select a time'),
63
+ });
64
+ return;
65
+ }
66
+
67
+ if (!data.temperature || isNaN(temperatureValue) || temperatureValue < 30 || temperatureValue > 45) {
68
+ setError('temperature', {
69
+ type: 'manual',
70
+ message: t('temperatureValidation', 'Please enter a valid temperature (30-45°C)'),
71
+ });
72
+ return;
73
+ }
74
+
75
+ const isDuplicateTime = existingTimeEntries.some((entry) => entry.time === data.time);
76
+
77
+ if (isDuplicateTime) {
78
+ setError('time', {
79
+ type: 'manual',
80
+ message: t('duplicateTimeEntry', 'This time already exists'),
81
+ });
82
+ return;
83
+ }
84
+
85
+ onSubmit({
86
+ timeSlot: data.time,
87
+ exactTime: data.time,
88
+ temperature: temperatureValue,
89
+ });
90
+
91
+ if (onDataSaved) {
92
+ onDataSaved();
93
+ }
94
+
95
+ reset();
96
+ onClose();
97
+ };
98
+
99
+ const handleClose = () => {
100
+ reset();
101
+ clearErrors();
102
+ onClose();
103
+ };
104
+
105
+ const getTemperatureAdvice = (temp: string): { status: string; color: string; advice: string } => {
106
+ const temperature = parseFloat(temp);
107
+ if (isNaN(temperature)) {
108
+ return { status: '', color: '', advice: '' };
109
+ }
110
+
111
+ if (temperature < 36.1) {
112
+ return {
113
+ status: 'Low',
114
+ color: '#0043ce',
115
+ advice: 'Temperature below normal range. Monitor closely.',
116
+ };
117
+ } else if (temperature >= 36.1 && temperature <= 37.2) {
118
+ return {
119
+ status: 'Normal',
120
+ color: '#198038',
121
+ advice: 'Temperature within normal range.',
122
+ };
123
+ } else if (temperature > 37.2) {
124
+ return {
125
+ status: 'High (Fever)',
126
+ color: '#da1e28',
127
+ advice: 'Elevated temperature. Consider fever management.',
128
+ };
129
+ }
130
+ return { status: '', color: '', advice: '' };
131
+ };
132
+
133
+ const temperatureAdvice = getTemperatureAdvice(watch('temperature') || '');
134
+
135
+ return (
136
+ <Modal
137
+ open={isOpen}
138
+ onRequestClose={handleClose}
139
+ modalHeading={t('addTemperatureData', 'Temperature')}
140
+ modalLabel={patient ? `${patient.name}, ${patient.gender}, ${patient.age}` : 'Patient Information'}
141
+ primaryButtonText={t('save', 'Save')}
142
+ secondaryButtonText={t('cancel', 'Cancel')}
143
+ onRequestSubmit={handleSubmit(onSubmitForm)}
144
+ className={styles.modal}>
145
+ <form>
146
+ <Grid>
147
+ <Column lg={8} md={4} sm={4}>
148
+ <Controller
149
+ name="time"
150
+ control={control}
151
+ render={({ field }) => (
152
+ <TimePickerDropdown
153
+ id="time"
154
+ labelText={t('time', 'Time')}
155
+ value={field.value}
156
+ onChange={field.onChange}
157
+ invalid={!!errors.time}
158
+ invalidText={errors.time?.message}
159
+ existingTimeEntries={existingTimeEntries}
160
+ />
161
+ )}
162
+ />
163
+ </Column>
164
+ <Column lg={16} md={8} sm={4}>
165
+ <Controller
166
+ name="temperature"
167
+ control={control}
168
+ render={({ field }) => (
169
+ <NumberInput
170
+ id="temperature"
171
+ label={t('temperature', 'Temperature')}
172
+ helperText="Normal: 36.1-37.2°C"
173
+ min={30}
174
+ max={45}
175
+ step={0.1}
176
+ value={field.value}
177
+ onChange={(e, { value }) => field.onChange(value)}
178
+ invalid={!!errors.temperature}
179
+ invalidText={errors.temperature?.message}
180
+ />
181
+ )}
182
+ />
183
+ </Column>
184
+
185
+ {temperatureAdvice.status && (
186
+ <Column lg={16} md={8} sm={4}>
187
+ <div className={styles.temperatureStatus}>
188
+ <div
189
+ className={
190
+ styles.statusIndicator +
191
+ ' ' +
192
+ (temperatureAdvice.status === 'Low'
193
+ ? styles['statusIndicator--low']
194
+ : temperatureAdvice.status === 'Normal'
195
+ ? styles['statusIndicator--normal']
196
+ : styles['statusIndicator--high'])
197
+ }>
198
+ <strong>{temperatureAdvice.status}</strong>
199
+ <span className={styles.statusAdvice}>{temperatureAdvice.advice}</span>
200
+ </div>
201
+ </div>
202
+ </Column>
203
+ )}
204
+ </Grid>
205
+ </form>
206
+ </Modal>
207
+ );
208
+ };
209
+
210
+ export default TemperatureForm;