@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.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/dist/127.js +1 -1
  3. package/dist/40.js +1 -1
  4. package/dist/{189.js → 791.js} +1 -1
  5. package/dist/791.js.map +1 -0
  6. package/dist/916.js +1 -1
  7. package/dist/kenyaemr-esm-patient-clinical-view-app.js +3 -3
  8. package/dist/kenyaemr-esm-patient-clinical-view-app.js.buildmanifest.json +36 -36
  9. package/dist/main.js +3 -3
  10. package/dist/main.js.map +1 -1
  11. package/dist/routes.json +1 -1
  12. package/package.json +1 -1
  13. package/src/config-schema.ts +8 -2
  14. package/src/maternal-and-child-health/partography/forms/cervical-contractions-form.component.tsx +63 -28
  15. package/src/maternal-and-child-health/partography/forms/cervix-form.component.tsx +52 -108
  16. package/src/maternal-and-child-health/partography/forms/drugs-iv-fluids-form.component.tsx +110 -100
  17. package/src/maternal-and-child-health/partography/forms/fetal-heart-rate-form.component.tsx +7 -7
  18. package/src/maternal-and-child-health/partography/forms/membrane-amniotic-fluid-form.component.tsx +72 -75
  19. package/src/maternal-and-child-health/partography/forms/oxytocin-form.component.tsx +14 -19
  20. package/src/maternal-and-child-health/partography/forms/pulse-bp-form.component.tsx +34 -28
  21. package/src/maternal-and-child-health/partography/forms/temperature-form.component.tsx +10 -9
  22. package/src/maternal-and-child-health/partography/forms/time-picker-dropdown.component.tsx +10 -10
  23. package/src/maternal-and-child-health/partography/forms/urine-test-form.component.tsx +7 -6
  24. package/src/maternal-and-child-health/partography/graphs/cervical-contractions-graph.component.tsx +51 -61
  25. package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph-wrapper.component.tsx +0 -6
  26. package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph.component.tsx +3 -3
  27. package/src/maternal-and-child-health/partography/graphs/fetal-heart-rate-graph.component.tsx +53 -30
  28. package/src/maternal-and-child-health/partography/graphs/membrane-amniotic-fluid-graph.component.tsx +25 -3
  29. package/src/maternal-and-child-health/partography/graphs/oxytocin-graph-wrapper.component.tsx +102 -97
  30. package/src/maternal-and-child-health/partography/graphs/oxytocin-graph.component.tsx +21 -22
  31. package/src/maternal-and-child-health/partography/graphs/pulse-bp-graph.component.tsx +21 -24
  32. package/src/maternal-and-child-health/partography/graphs/temperature-graph.component.tsx +23 -26
  33. package/src/maternal-and-child-health/partography/graphs/urine-test-graph.component.tsx +1 -1
  34. package/src/maternal-and-child-health/partography/partograph.component.tsx +323 -171
  35. package/src/maternal-and-child-health/partography/partography-data-form.scss +23 -0
  36. package/src/maternal-and-child-health/partography/partography.resource.ts +70 -56
  37. package/src/maternal-and-child-health/partography/partography.scss +65 -6
  38. package/src/maternal-and-child-health/partography/resources/fetal-heart-rate.resource.ts +5 -10
  39. package/src/maternal-and-child-health/partography/resources/oxytocin.resource.ts +5 -3
  40. package/src/maternal-and-child-health/partography/types/index.ts +40 -37
  41. package/translations/am.json +1 -1
  42. package/translations/en.json +1 -1
  43. package/translations/sw.json +1 -1
  44. package/dist/189.js.map +0 -1
@@ -48,26 +48,26 @@ const PulseBPForm: React.FC<PulseBPFormProps> = ({ isOpen, onClose, onSubmit, on
48
48
 
49
49
  clearErrors();
50
50
 
51
- if (!data.pulse || isNaN(pulseValue) || pulseValue < 30 || pulseValue > 200) {
51
+ if (!data.pulse || isNaN(pulseValue) || pulseValue < 0 || pulseValue > 230) {
52
52
  setError('pulse', {
53
53
  type: 'manual',
54
- message: 'Please enter a valid pulse rate (30-200 bpm)',
54
+ message: 'Please enter a valid pulse rate (0-230 bpm)',
55
55
  });
56
56
  return;
57
57
  }
58
58
 
59
- if (!data.systolicBP || isNaN(systolicValue) || systolicValue < 60 || systolicValue > 250) {
59
+ if (!data.systolicBP || isNaN(systolicValue) || systolicValue < 0 || systolicValue > 250) {
60
60
  setError('systolicBP', {
61
61
  type: 'manual',
62
- message: 'Please enter a valid systolic BP (60-250 mmHg)',
62
+ message: 'Please enter a valid systolic BP (0-250 mmHg)',
63
63
  });
64
64
  return;
65
65
  }
66
66
 
67
- if (!data.diastolicBP || isNaN(diastolicValue) || diastolicValue < 40 || diastolicValue > 150) {
67
+ if (!data.diastolicBP || isNaN(diastolicValue) || diastolicValue < 0 || diastolicValue > 150) {
68
68
  setError('diastolicBP', {
69
69
  type: 'manual',
70
- message: 'Please enter a valid diastolic BP (40-150 mmHg)',
70
+ message: 'Please enter a valid diastolic BP (0-150 mmHg)',
71
71
  });
72
72
  return;
73
73
  }
@@ -112,17 +112,19 @@ const PulseBPForm: React.FC<PulseBPFormProps> = ({ isOpen, onClose, onSubmit, on
112
112
  <Controller
113
113
  name="pulse"
114
114
  control={control}
115
- render={({ field }) => (
115
+ render={({ field, fieldState }) => (
116
116
  <NumberInput
117
117
  id="pulse"
118
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}
119
+ helperText="Normal: 0-230 bpm"
120
+ min={0}
121
+ max={230}
122
+ step={1}
123
+ value={field.value || ''}
124
+ onChange={(e, { value }) => field.onChange(String(value))}
125
+ invalid={!!fieldState.error}
126
+ invalidText={fieldState.error?.message}
127
+ allowEmpty
126
128
  />
127
129
  )}
128
130
  />
@@ -131,17 +133,19 @@ const PulseBPForm: React.FC<PulseBPFormProps> = ({ isOpen, onClose, onSubmit, on
131
133
  <Controller
132
134
  name="systolicBP"
133
135
  control={control}
134
- render={({ field }) => (
136
+ render={({ field, fieldState }) => (
135
137
  <NumberInput
136
138
  id="systolicBP"
137
139
  label={t('systolicBP', 'BP Systolic')}
138
- helperText="Normal: 90-149 mmHg"
139
- min={60}
140
+ helperText="Normal: 0-250 mmHg"
141
+ min={0}
140
142
  max={250}
141
- value={field.value}
142
- onChange={(e, { value }) => field.onChange(value)}
143
- invalid={!!errors.systolicBP}
144
- invalidText={errors.systolicBP?.message}
143
+ step={1}
144
+ value={field.value || ''}
145
+ onChange={(e, { value }) => field.onChange(String(value))}
146
+ invalid={!!fieldState.error}
147
+ invalidText={fieldState.error?.message}
148
+ allowEmpty
145
149
  />
146
150
  )}
147
151
  />
@@ -150,17 +154,19 @@ const PulseBPForm: React.FC<PulseBPFormProps> = ({ isOpen, onClose, onSubmit, on
150
154
  <Controller
151
155
  name="diastolicBP"
152
156
  control={control}
153
- render={({ field }) => (
157
+ render={({ field, fieldState }) => (
154
158
  <NumberInput
155
159
  id="diastolicBP"
156
160
  label={t('diastolicBP', 'BP Diastolic')}
157
- helperText="Normal: 60-80 mmHg"
158
- min={40}
161
+ helperText="Normal: 0-150 mmHg"
162
+ min={0}
159
163
  max={150}
160
- value={field.value}
161
- onChange={(e, { value }) => field.onChange(value)}
162
- invalid={!!errors.diastolicBP}
163
- invalidText={errors.diastolicBP?.message}
164
+ step={1}
165
+ value={field.value || ''}
166
+ onChange={(e, { value }) => field.onChange(String(value))}
167
+ invalid={!!fieldState.error}
168
+ invalidText={fieldState.error?.message}
169
+ allowEmpty
164
170
  />
165
171
  )}
166
172
  />
@@ -64,10 +64,10 @@ const TemperatureForm: React.FC<TemperatureFormProps> = ({
64
64
  return;
65
65
  }
66
66
 
67
- if (!data.temperature || isNaN(temperatureValue) || temperatureValue < 30 || temperatureValue > 45) {
67
+ if (!data.temperature || isNaN(temperatureValue) || temperatureValue < 25 || temperatureValue > 43) {
68
68
  setError('temperature', {
69
69
  type: 'manual',
70
- message: t('temperatureValidation', 'Please enter a valid temperature (30-45°C)'),
70
+ message: t('temperatureValidation', 'Please enter a valid temperature (25-43°C)'),
71
71
  });
72
72
  return;
73
73
  }
@@ -165,18 +165,19 @@ const TemperatureForm: React.FC<TemperatureFormProps> = ({
165
165
  <Controller
166
166
  name="temperature"
167
167
  control={control}
168
- render={({ field }) => (
168
+ render={({ field, fieldState }) => (
169
169
  <NumberInput
170
170
  id="temperature"
171
171
  label={t('temperature', 'Temperature')}
172
172
  helperText="Normal: 36.1-37.2°C"
173
- min={30}
174
- max={45}
173
+ min={25}
174
+ max={43}
175
175
  step={0.1}
176
- value={field.value}
177
- onChange={(e, { value }) => field.onChange(value)}
178
- invalid={!!errors.temperature}
179
- invalidText={errors.temperature?.message}
176
+ value={field.value || ''}
177
+ onChange={(e, { value }) => field.onChange(String(value))}
178
+ invalid={!!fieldState.error}
179
+ invalidText={fieldState.error?.message}
180
+ allowEmpty
180
181
  />
181
182
  )}
182
183
  />
@@ -75,32 +75,32 @@ const TimePickerDropdown: React.FC<TimePickerDropdownProps> = ({
75
75
  reason: disableReason,
76
76
  };
77
77
  });
78
- }, [existingTimeEntries, latestEntry]);
78
+ }, [latestEntry]);
79
79
 
80
80
  const minuteOptions = useMemo(() => {
81
81
  if (!latestEntry || !hours) {
82
- return Array.from({ length: 12 }, (_, i) => {
83
- const minute = (i * 5).toString().padStart(2, '0');
82
+ return Array.from({ length: 60 }, (_, i) => {
83
+ const minute = i.toString().padStart(2, '0');
84
84
  return { value: minute, text: minute, disabled: false };
85
85
  });
86
86
  }
87
87
  const [latestHour, latestMinute] = latestEntry.time.split(':').map(Number);
88
88
  const currentHour = parseInt(hours);
89
- return Array.from({ length: 12 }, (_, i) => {
90
- const minute = (i * 5).toString().padStart(2, '0');
89
+ return Array.from({ length: 60 }, (_, i) => {
90
+ const minute = i.toString().padStart(2, '0');
91
91
  let isDisabled = false;
92
92
  let disableReason = '';
93
93
  if (currentHour < latestHour) {
94
94
  isDisabled = true;
95
95
  disableReason = 'before latest entry';
96
- } else if (currentHour === latestHour && i * 5 <= latestMinute) {
96
+ } else if (currentHour === latestHour && i <= latestMinute) {
97
97
  isDisabled = true;
98
98
  disableReason = `≤ ${latestMinute} min`;
99
99
  }
100
100
  const displayText = isDisabled ? `${minute} ${disableReason}` : minute;
101
101
  return { value: minute, text: displayText, disabled: isDisabled };
102
102
  });
103
- }, [hours, existingTimeEntries, latestEntry]);
103
+ }, [hours, latestEntry]);
104
104
 
105
105
  const handleHourChange = (selectedHour: string) => {
106
106
  if (!selectedHour || selectedHour === '') {
@@ -145,7 +145,7 @@ const TimePickerDropdown: React.FC<TimePickerDropdownProps> = ({
145
145
 
146
146
  return (
147
147
  <div className={styles.timePickerContainer}>
148
- <FormGroup legendText={labelText} invalid={invalid}>
148
+ <FormGroup legendText={labelText} invalid={invalid === true}>
149
149
  <div className={styles.timeInputsWrapper}>
150
150
  <div className={styles.timeInput}>
151
151
  <Select
@@ -153,7 +153,7 @@ const TimePickerDropdown: React.FC<TimePickerDropdownProps> = ({
153
153
  labelText="Hours (HH)"
154
154
  value={hours}
155
155
  onChange={(e) => handleHourChange((e.target as HTMLSelectElement).value)}
156
- invalid={invalid}>
156
+ invalid={invalid === true}>
157
157
  <SelectItem value="" text="HH" />
158
158
  {hourOptions.map((option) => (
159
159
  <SelectItem
@@ -175,7 +175,7 @@ const TimePickerDropdown: React.FC<TimePickerDropdownProps> = ({
175
175
  labelText="Minutes (MM)"
176
176
  value={minutes}
177
177
  onChange={(e) => handleMinuteChange((e.target as HTMLSelectElement).value)}
178
- invalid={invalid}>
178
+ invalid={invalid === true}>
179
179
  <SelectItem value="" text="MM" />
180
180
  {minuteOptions.map((option) => (
181
181
  <SelectItem
@@ -213,17 +213,18 @@ const UrineTestForm: React.FC<UrineTestFormProps> = ({
213
213
  <Controller
214
214
  name="volume"
215
215
  control={control}
216
- render={({ field }) => (
216
+ render={({ field, fieldState }) => (
217
217
  <NumberInput
218
218
  id="volume"
219
219
  label={t('volume', 'Volume Produced ml')}
220
- helperText={t('volumeHelper', 'Enter the Dilation')}
220
+ helperText={t('volumeHelper', 'Enter the Exact Volume Produced in ml')}
221
221
  min={0}
222
222
  step={1}
223
- value={field.value}
224
- onChange={(e, { value }) => field.onChange(value)}
225
- invalid={!!errors.volume}
226
- invalidText={errors.volume?.message}
223
+ value={field.value || ''}
224
+ onChange={(e, { value }) => field.onChange(String(value))}
225
+ invalid={!!fieldState.error}
226
+ invalidText={fieldState.error?.message}
227
+ allowEmpty
227
228
  />
228
229
  )}
229
230
  />
@@ -93,7 +93,7 @@ const CervicalContractionsGraph: React.FC<CervicalContractionsGraphProps> = ({
93
93
  <div className={styles.fetalHeartRateContainer}>
94
94
  <div className={styles.fetalHeartRateHeader}>
95
95
  <div className={styles.fetalHeartRateHeaderLeft}>
96
- <h3 className={styles.fetalHeartRateTitle}>Cervical Contractions</h3>
96
+ <h3 className={styles.fetalHeartRateTitle}>Contractions</h3>
97
97
  <div className={styles.fetalHeartRateControls}>
98
98
  <span className={styles.legendText}>
99
99
  Contractions per 10 min | Bar Heights: 0=None, 2=Mild, 3=Moderate, 5=Strong
@@ -126,7 +126,7 @@ const CervicalContractionsGraph: React.FC<CervicalContractionsGraphProps> = ({
126
126
  kind="primary"
127
127
  size={controlSize}
128
128
  renderIcon={Add}
129
- iconDescription="Add cervical contractions data"
129
+ iconDescription="Add contractions data"
130
130
  disabled={isAddButtonDisabled}
131
131
  onClick={() => onAddData?.()}
132
132
  className={styles.addButton}>
@@ -141,68 +141,58 @@ const CervicalContractionsGraph: React.FC<CervicalContractionsGraphProps> = ({
141
141
  <div
142
142
  className={styles.contractionsContainer}
143
143
  style={{ minWidth: Math.max(1100, 70 * Math.max(13, data.length) + 60) }}>
144
- <div className={styles.contractionsTimeHeaders} style={{ display: 'flex' }}>
145
- <div className={styles.contractionsYAxisLabel} style={{ minWidth: 60, fontWeight: 600 }}>
146
- {t('time', 'Time')}
147
- </div>
148
- {Array.from({ length: Math.max(13, data.length) }, (_, colIndex) => (
149
- <div
150
- key={`header-${colIndex}`}
151
- className={styles.contractionsTimeHeader}
152
- style={{ minWidth: 70, textAlign: 'center', fontWeight: 600 }}>
153
- {data[colIndex]?.timeSlot || ''}
154
- </div>
155
- ))}
156
- </div>
157
- {yAxisLabels.map((label, rowIndex) => (
158
- <div key={label} className={styles.contractionsGridRow} style={{ display: 'flex' }}>
159
- <div className={styles.contractionsYAxisLabel} style={{ minWidth: 60 }}>
160
- {label}
161
- </div>
162
- {Array.from({ length: Math.max(13, data.length) }, (_, colIndex) => {
163
- const dataForTimeSlot = data?.[colIndex];
164
- const yPosition = parseInt(label);
165
- const contractionCount = dataForTimeSlot ? parseInt(dataForTimeSlot.contractionCount) : 0;
166
- const contractionLevel = dataForTimeSlot?.contractionLevel || 'none';
144
+ {yAxisLabels.map((label, rowIndex) => {
145
+ if (!label) {
146
+ return null;
147
+ }
148
+ return (
149
+ <div key={label} className={styles.contractionsGridRow} style={{ display: 'flex' }}>
150
+ <div className={styles.contractionsYAxisLabel} style={{ minWidth: 60 }}>
151
+ {label}
152
+ </div>
153
+ {Array.from({ length: Math.max(20, data.length) }, (_, colIndex) => {
154
+ const dataForTimeSlot = data?.[colIndex];
155
+ const yPosition = parseInt(label);
156
+ const contractionCount = dataForTimeSlot ? parseInt(dataForTimeSlot.contractionCount) : 0;
157
+ const contractionLevel = dataForTimeSlot?.contractionLevel || 'none';
167
158
 
168
- const shouldFill = contractionCount > 0 && yPosition <= contractionCount;
159
+ const shouldFill = contractionCount > 0 && yPosition <= contractionCount;
169
160
 
170
- const getColorClass = (level: string) => {
171
- switch (level) {
172
- case 'mild':
173
- return 'contractionBarMild';
174
- case 'moderate':
175
- return 'contractionBarModerate';
176
- case 'strong':
177
- return 'contractionBarStrong';
178
- default:
179
- return 'contractionBarNone';
180
- }
181
- };
161
+ const getColorClass = (level: string) => {
162
+ switch (level) {
163
+ case 'mild':
164
+ return 'contractionBarMild';
165
+ case 'moderate':
166
+ return 'contractionBarModerate';
167
+ case 'strong':
168
+ return 'contractionBarStrong';
169
+ default:
170
+ return 'contractionBarNone';
171
+ }
172
+ };
182
173
 
183
- return (
184
- <div
185
- key={`${label}-${colIndex}`}
186
- className={`${styles.contractionsGridCell} ${
187
- shouldFill ? styles[getColorClass(contractionLevel)] : ''
188
- }`}
189
- data-y={label}
190
- data-x={colIndex}
191
- data-filled={shouldFill}
192
- data-count={contractionCount}
193
- data-level={contractionLevel}
194
- style={{ minWidth: 70 }}>
195
- {shouldFill && (
196
- <div className={`${styles.contractionBar} ${styles[getColorClass(contractionLevel)]}`} />
197
- )}
198
- </div>
199
- );
200
- })}
201
- </div>
202
- ))}
203
- <div className={styles.contractionsYAxisTitle}>
204
- <span>{t('contractionsPerTenMin', 'Contractions per 10 min')}</span>
205
- </div>
174
+ return (
175
+ <div
176
+ key={`${label}-${colIndex}`}
177
+ className={`${styles.contractionsGridCell} ${
178
+ shouldFill ? styles[getColorClass(contractionLevel)] : ''
179
+ }`}
180
+ data-y={label}
181
+ data-x={colIndex}
182
+ data-filled={shouldFill}
183
+ data-count={contractionCount}
184
+ data-level={contractionLevel}
185
+ style={{ minWidth: 70 }}>
186
+ {shouldFill && (
187
+ <div className={`${styles.contractionBar} ${styles[getColorClass(contractionLevel)]}`} />
188
+ )}
189
+ </div>
190
+ );
191
+ })}
192
+ </div>
193
+ );
194
+ })}
195
+ {/* Y-axis title removed to prevent overlap with numbers */}
206
196
  </div>
207
197
  </div>
208
198
  ) : (
@@ -120,7 +120,6 @@ const DrugsIVFluidsGraphWrapper: React.FC<DrugsIVFluidsGraphWrapperProps> = ({
120
120
  <th>{t('dosage', 'Dosage')}</th>
121
121
  <th>{t('route', 'Route')}</th>
122
122
  <th>{t('frequency', 'Frequency')}</th>
123
- <th>{t('source', 'Source')}</th>
124
123
  </tr>
125
124
  </thead>
126
125
  <tbody>
@@ -131,11 +130,6 @@ const DrugsIVFluidsGraphWrapper: React.FC<DrugsIVFluidsGraphWrapperProps> = ({
131
130
  <td>{item.dosage}</td>
132
131
  <td>{item.route || '-'}</td>
133
132
  <td>{item.frequency || '-'}</td>
134
- <td>
135
- <span className={item.source === 'order' ? styles.orderSource : styles.manualSource}>
136
- {item.source === 'order' ? t('drugOrder', 'Drug Order') : t('manual', 'Manual')}
137
- </span>
138
- </td>
139
133
  </tr>
140
134
  ))}
141
135
  </tbody>
@@ -14,14 +14,14 @@ interface DrugsIVFluidsGraphProps {
14
14
  const DrugsIVFluidsGraph: React.FC<DrugsIVFluidsGraphProps> = ({ data }) => {
15
15
  const { t } = useTranslation();
16
16
  const getColumns = () => {
17
- const emptyColumns = Array.from({ length: 13 }, (_, i) => `grid-${i + 1}`);
17
+ const emptyColumns = Array.from({ length: 20 }, (_, i) => `grid-${i + 1}`);
18
18
 
19
19
  if (data.length === 0) {
20
20
  return emptyColumns;
21
21
  }
22
- if (data.length <= 13) {
22
+ if (data.length <= 20) {
23
23
  const dataColumns = data.map((_, index) => `data-${index}`);
24
- const remainingEmpty = Array.from({ length: 13 - data.length }, (_, i) => `empty-${i + 1}`);
24
+ const remainingEmpty = Array.from({ length: 20 - data.length }, (_, i) => `empty-${i + 1}`);
25
25
  return [...dataColumns, ...remainingEmpty];
26
26
  }
27
27
  return data.map((_, index) => `data-${index}`);
@@ -16,7 +16,7 @@ import {
16
16
  import { Add, ChartColumn, Table as TableIcon } from '@carbon/react/icons';
17
17
  import { LineChart } from '@carbon/charts-react';
18
18
  import styles from '../partography.scss';
19
- import { getColorForGraph, generateRange, defaultFetalHeartRateChartData } from '../types';
19
+ import { getColorForGraph, generateRange } from '../types';
20
20
  import { usePaginationInfo } from '@openmrs/esm-patient-common-lib';
21
21
 
22
22
  enum ScaleTypes {
@@ -78,12 +78,12 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
78
78
  );
79
79
  const getFetalHeartRateStatus = (value: string): { type: string; text: string; color: string } => {
80
80
  const numValue = parseInt(value.replace(' bpm', ''));
81
- if (numValue < 110) {
82
- return { type: 'red', text: 'Low', color: getColorForGraph('red') };
83
- } else if (numValue >= 110 && numValue <= 160) {
84
- return { type: 'green', text: 'Normal', color: getColorForGraph('green') };
81
+ if (numValue < 100) {
82
+ return { type: 'orange', text: t('low', 'Low'), color: getColorForGraph('orange') };
83
+ } else if (numValue >= 100 && numValue <= 180) {
84
+ return { type: 'green', text: t('normal', 'Normal'), color: getColorForGraph('green') };
85
85
  } else {
86
- return { type: 'red', text: 'High', color: getColorForGraph('red') };
86
+ return { type: 'red', text: t('abnormal', 'Abnormal'), color: getColorForGraph('red') };
87
87
  }
88
88
  };
89
89
  const tableHeaders = [
@@ -94,9 +94,9 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
94
94
  { key: 'status', header: t('status', 'Status') },
95
95
  ];
96
96
  const getFetalHeartRateColor = (value: number): string => {
97
- if (value < 110) {
98
- return getColorForGraph('red');
99
- } else if (value >= 110 && value <= 160) {
97
+ if (value < 100) {
98
+ return getColorForGraph('orange');
99
+ } else if (value >= 100 && value <= 180) {
100
100
  return getColorForGraph('green');
101
101
  } else {
102
102
  return getColorForGraph('red');
@@ -107,13 +107,13 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
107
107
  if (data && data.length > 0) {
108
108
  return data.map((point) => ({
109
109
  ...point,
110
- group: 'Fetal Heart Rate',
110
+ group: t('fetalHeartRate', 'Fetal Heart Rate'),
111
111
  color: getFetalHeartRateColor(point.value),
112
112
  }));
113
113
  }
114
114
 
115
- return defaultFetalHeartRateChartData;
116
- }, [data]);
115
+ return [];
116
+ }, [data, t]);
117
117
 
118
118
  const chartData = enhancedChartData;
119
119
  const chartKey = React.useMemo(() => JSON.stringify(enhancedChartData), [enhancedChartData]);
@@ -146,7 +146,7 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
146
146
  },
147
147
  },
148
148
  left: {
149
- title: 'Fetal Heart Rate (bpm)',
149
+ title: t('fetalHeartRateBpm', 'Fetal Heart Rate (bpm)'),
150
150
  mapsTo: 'value',
151
151
  scaleType: ScaleTypes.LINEAR,
152
152
  domain: [80, 200],
@@ -198,7 +198,7 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
198
198
  data && data.length > 0
199
199
  ? (datapoint: any) => getFetalHeartRateColor(datapoint.value)
200
200
  : {
201
- 'Fetal Heart Rate': 'transparent',
201
+ [t('fetalHeartRate', 'Fetal Heart Rate')]: 'transparent', // Hide empty data lines
202
202
  },
203
203
  },
204
204
  legend: {
@@ -215,16 +215,16 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
215
215
  <div className={styles.fetalHeartRateContainer}>
216
216
  <div className={styles.fetalHeartRateHeader}>
217
217
  <div className={styles.fetalHeartRateHeaderLeft}>
218
- <h3 className={styles.fetalHeartRateTitle}>Fetal Heart Rate</h3>
218
+ <h3 className={styles.fetalHeartRateTitle}>{t('fetalHeartRate', 'Fetal Heart Rate')}</h3>
219
219
  <div className={styles.fetalHeartRateControls}>
220
- <Tag type="green" title="Normal Range">
221
- Normal (110-160)
220
+ <Tag type="green" title={t('normalRange', 'Normal Range')}>
221
+ {t('normalRange100180', 'Normal (100-180)')}
222
222
  </Tag>
223
- <Tag type="red" title="Abnormal Range">
224
- Abnormal (&gt;160)
223
+ <Tag type="red" title={t('abnormalRange', 'Abnormal Range')}>
224
+ {t('abnormalRangeGreater180', 'Abnormal (>180)')}
225
225
  </Tag>
226
- <Tag type="red" title="Low Range">
227
- Low (&lt;110)
226
+ <Tag type="gray" title={t('lowRange', 'Low Range')} className={styles.tagLowRange}>
227
+ {t('lowRangeLess100', 'Low (<100)')}
228
228
  </Tag>
229
229
  </div>
230
230
  </div>
@@ -254,11 +254,11 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
254
254
  kind="primary"
255
255
  size={controlSize}
256
256
  renderIcon={Add}
257
- iconDescription="Add fetal heart rate data"
257
+ iconDescription={t('addFetalHeartRateData', 'Add fetal heart rate data')}
258
258
  disabled={isAddButtonDisabled}
259
259
  onClick={onAddData}
260
260
  className={styles.addButton}>
261
- Add
261
+ {t('add', 'Add')}
262
262
  </Button>
263
263
  </div>
264
264
  </div>
@@ -266,7 +266,20 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
266
266
 
267
267
  {viewMode === 'graph' ? (
268
268
  <div className={styles.fetalHeartRateChart}>
269
- <LineChart data={chartData} options={chartOptions} key={chartKey} />
269
+ {chartData.length > 0 ? (
270
+ <LineChart data={chartData} options={chartOptions} key={chartKey} />
271
+ ) : (
272
+ <LineChart
273
+ data={[{ hour: 0, value: 140, group: 'No Data', time: '0:00' }]}
274
+ options={{
275
+ ...chartOptions,
276
+ legend: { enabled: false },
277
+ points: { enabled: false },
278
+ color: { scale: { 'No Data': 'transparent' } },
279
+ }}
280
+ key="empty-chart"
281
+ />
282
+ )}
270
283
  </div>
271
284
  ) : (
272
285
  <div className={styles.tableContainer}>
@@ -296,7 +309,11 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
296
309
  row.cells.find((c) => c.info.header === 'value')?.value || '0 bpm',
297
310
  );
298
311
  return (
299
- <Tag type={status.type as any} title={`Fetal Heart Rate: ${status.text}`}>
312
+ <Tag
313
+ type={status.type as any}
314
+ title={t('fetalHeartRateStatus', 'Fetal Heart Rate: {{status}}', {
315
+ status: status.text,
316
+ })}>
300
317
  {status.text}
301
318
  </Tag>
302
319
  );
@@ -304,23 +321,29 @@ const FetalHeartRateGraph: React.FC<FetalHeartRateGraphProps> = ({
304
321
  : cell.info.header === 'value'
305
322
  ? (() => {
306
323
  const numValue = parseInt(cell.value.replace(' bpm', ''));
307
- if (numValue < 110) {
324
+ if (numValue < 100) {
308
325
  return (
309
- <span className={`${styles.fetalHeartRateValue} ${styles.low}`}>
326
+ <span
327
+ className={`${styles.fetalHeartRateValue} ${styles.low}`}
328
+ style={{ color: getColorForGraph('orange') }}>
310
329
  <span className={styles.arrow}>↓</span>
311
330
  {cell.value}
312
331
  </span>
313
332
  );
314
- } else if (numValue > 160) {
333
+ } else if (numValue > 180) {
315
334
  return (
316
- <span className={`${styles.fetalHeartRateValue} ${styles.high}`}>
335
+ <span
336
+ className={`${styles.fetalHeartRateValue} ${styles.high}`}
337
+ style={{ color: getColorForGraph('red') }}>
317
338
  <span className={styles.arrow}>↑</span>
318
339
  {cell.value}
319
340
  </span>
320
341
  );
321
342
  } else {
322
343
  return (
323
- <span className={`${styles.fetalHeartRateValue} ${styles.normal}`}>
344
+ <span
345
+ className={`${styles.fetalHeartRateValue} ${styles.normal}`}
346
+ style={{ color: getColorForGraph('green') }}>
324
347
  {cell.value}
325
348
  </span>
326
349
  );