@kenyaemr/esm-patient-clinical-view-app 5.4.2-pre.2716 → 5.4.2-pre.2724

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 (72) 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/805.js +1 -0
  5. package/dist/805.js.map +1 -0
  6. package/dist/916.js +1 -1
  7. package/dist/kenyaemr-esm-patient-clinical-view-app.js +2 -2
  8. package/dist/kenyaemr-esm-patient-clinical-view-app.js.buildmanifest.json +36 -36
  9. package/dist/main.js +27 -27
  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 +97 -0
  14. package/src/contact-list/contact-tracing-history.component.tsx +18 -15
  15. package/src/maternal-and-child-health/partography/components/pulse-bp-graph.component.tsx +1 -0
  16. package/src/maternal-and-child-health/partography/components/temperature-graph.component.tsx +218 -0
  17. package/src/maternal-and-child-health/partography/components/uterine-contractions-graph.component.tsx +209 -0
  18. package/src/maternal-and-child-health/partography/forms/cervical-contractions-form.component.tsx +211 -0
  19. package/src/maternal-and-child-health/partography/forms/cervix-form.component.tsx +354 -0
  20. package/src/maternal-and-child-health/partography/forms/drugs-iv-fluids-form.component.tsx +321 -0
  21. package/src/maternal-and-child-health/partography/forms/fetal-heart-rate-form.component.tsx +275 -0
  22. package/src/maternal-and-child-health/partography/forms/index.ts +9 -0
  23. package/src/maternal-and-child-health/partography/forms/membrane-amniotic-fluid-form.component.tsx +330 -0
  24. package/src/maternal-and-child-health/partography/forms/oxytocin-form.component.tsx +207 -0
  25. package/src/maternal-and-child-health/partography/forms/pulse-bp-form.component.tsx +174 -0
  26. package/src/maternal-and-child-health/partography/forms/temperature-form.component.tsx +210 -0
  27. package/src/maternal-and-child-health/partography/forms/time-picker-dropdown.component.tsx +218 -0
  28. package/src/maternal-and-child-health/partography/forms/time-picker-dropdown.scss +107 -0
  29. package/src/maternal-and-child-health/partography/forms/time-picker-with-clock.component.tsx +174 -0
  30. package/src/maternal-and-child-health/partography/forms/time-picker-with-clock.scss +178 -0
  31. package/src/maternal-and-child-health/partography/forms/urine-test-form.component.tsx +255 -0
  32. package/src/maternal-and-child-health/partography/forms/useCervixData.ts +16 -0
  33. package/src/maternal-and-child-health/partography/graphs/cervical-contractions-graph.component.tsx +266 -0
  34. package/src/maternal-and-child-health/partography/graphs/cervix-graph.component.tsx +429 -0
  35. package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph-wrapper.component.tsx +163 -0
  36. package/src/maternal-and-child-health/partography/graphs/drugs-iv-fluids-graph.component.tsx +82 -0
  37. package/src/maternal-and-child-health/partography/graphs/fetal-heart-rate-graph.component.tsx +359 -0
  38. package/src/maternal-and-child-health/partography/graphs/index.ts +10 -0
  39. package/src/maternal-and-child-health/partography/graphs/membrane-amniotic-fluid-graph.component.tsx +266 -0
  40. package/src/maternal-and-child-health/partography/graphs/oxytocin-graph-wrapper.component.tsx +190 -0
  41. package/src/maternal-and-child-health/partography/graphs/oxytocin-graph.component.tsx +126 -0
  42. package/src/maternal-and-child-health/partography/graphs/partograph-graph.component.tsx +266 -0
  43. package/src/maternal-and-child-health/partography/graphs/pulse-bp-graph-wrapper.component.tsx +298 -0
  44. package/src/maternal-and-child-health/partography/graphs/pulse-bp-graph.component.tsx +267 -0
  45. package/src/maternal-and-child-health/partography/graphs/temperature-graph.component.tsx +242 -0
  46. package/src/maternal-and-child-health/partography/graphs/urine-test-graph.component.tsx +246 -0
  47. package/src/maternal-and-child-health/partography/partograph.component.tsx +2141 -118
  48. package/src/maternal-and-child-health/partography/partography-dashboard.meta.ts +8 -0
  49. package/src/maternal-and-child-health/partography/partography-data-form.scss +163 -0
  50. package/src/maternal-and-child-health/partography/partography.resource.ts +233 -326
  51. package/src/maternal-and-child-health/partography/partography.scss +1341 -3
  52. package/src/maternal-and-child-health/partography/resources/blood-pressure.resource.ts +96 -0
  53. package/src/maternal-and-child-health/partography/resources/cervical-dilation.resource.ts +109 -0
  54. package/src/maternal-and-child-health/partography/resources/cervix.resource.ts +362 -0
  55. package/src/maternal-and-child-health/partography/resources/descent-of-head.resource.ts +101 -0
  56. package/src/maternal-and-child-health/partography/resources/drugs-fluids.resource.ts +88 -0
  57. package/src/maternal-and-child-health/partography/resources/fetal-heart-rate.resource.ts +122 -0
  58. package/src/maternal-and-child-health/partography/resources/maternal-pulse.resource.ts +77 -0
  59. package/src/maternal-and-child-health/partography/resources/membrane-amniotic-fluid.resource.ts +108 -0
  60. package/src/maternal-and-child-health/partography/resources/oxytocin.resource.ts +159 -0
  61. package/src/maternal-and-child-health/partography/resources/progress-events.resource.ts +6 -0
  62. package/src/maternal-and-child-health/partography/resources/pulse-bp-combined.resource.ts +53 -0
  63. package/src/maternal-and-child-health/partography/resources/temperature.resource.ts +84 -0
  64. package/src/maternal-and-child-health/partography/resources/uterine-contractions.resource.ts +173 -0
  65. package/src/maternal-and-child-health/partography/table/temperature-table.component.tsx +99 -0
  66. package/src/maternal-and-child-health/partography/table/uterine-contractions-table.component.tsx +86 -0
  67. package/src/maternal-and-child-health/partography/types/index.ts +319 -101
  68. package/translations/am.json +121 -1
  69. package/translations/en.json +121 -1
  70. package/translations/sw.json +121 -1
  71. package/dist/397.js +0 -1
  72. package/dist/397.js.map +0 -1
@@ -0,0 +1,209 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Button, SkeletonText } from '@carbon/react';
4
+ import { Add, ChartColumn, Table as TableIcon } from '@carbon/react/icons';
5
+ import styles from '../partography.scss';
6
+ import UterineContractionsTable from '../table/uterine-contractions-table.component';
7
+
8
+ interface UterineContractionsTableRow {
9
+ id: string;
10
+ date: string;
11
+ timeSlot: string;
12
+ contractionCount: string;
13
+ contractionLevel: string;
14
+ protein?: string;
15
+ acetone?: string;
16
+ volume?: string;
17
+ }
18
+
19
+ interface UterineContractionsGraphProps {
20
+ tableData: UterineContractionsTableRow[];
21
+ viewMode: 'graph' | 'table';
22
+ currentPage: number;
23
+ pageSize: number;
24
+ totalItems: number;
25
+ controlSize: 'sm' | 'md';
26
+ onPageChange: (page: number) => void;
27
+ onPageSizeChange: (size: number) => void;
28
+ onAddData?: () => void;
29
+ onViewModeChange?: (mode: 'graph' | 'table') => void;
30
+ isAddButtonDisabled?: boolean;
31
+ isLoading?: boolean;
32
+ }
33
+
34
+ const UterineContractionsGraph: React.FC<UterineContractionsGraphProps> = ({
35
+ tableData,
36
+ viewMode,
37
+ currentPage,
38
+ pageSize,
39
+ totalItems,
40
+ controlSize,
41
+ onPageChange,
42
+ onPageSizeChange,
43
+ onAddData,
44
+ onViewModeChange,
45
+ isAddButtonDisabled = false,
46
+ isLoading = false,
47
+ }) => {
48
+ const { t } = useTranslation();
49
+
50
+ // Only show rows with both date and timeSlot
51
+ const filteredTableData = tableData.filter((item) => item.date && item.timeSlot);
52
+ const timeColumns = filteredTableData.map((item) => item.timeSlot || '--');
53
+ const yAxisLabels = ['5', '4', '3', '2', '1'];
54
+
55
+ // Carbon skeleton for loading state
56
+ const renderSkeleton = () => (
57
+ <div className={styles.membraneGrid}>
58
+ <div className={styles.gridContainer}>
59
+ <div className={styles.gridHeader}>
60
+ <div className={styles.gridCell}>
61
+ <SkeletonText width="60px" />
62
+ </div>
63
+ {[...Array(6)].map((_, idx) => (
64
+ <div key={idx} className={styles.gridCell}>
65
+ <SkeletonText width="60px" />
66
+ </div>
67
+ ))}
68
+ </div>
69
+ <div className={styles.gridRow}>
70
+ <div className={styles.gridRowLabel}>
71
+ <SkeletonText width="80px" />
72
+ </div>
73
+ {[...Array(6)].map((_, idx) => (
74
+ <div key={idx} className={styles.gridCell}>
75
+ <SkeletonText width="40px" />
76
+ </div>
77
+ ))}
78
+ </div>
79
+ </div>
80
+ </div>
81
+ );
82
+
83
+ // Empty state for no data
84
+ const renderEmptyGraph = () => (
85
+ <div className={styles.membraneGrid}>
86
+ <div className={styles.gridContainer}>
87
+ <div className={styles.gridHeader}>
88
+ <div className={styles.gridCell}>{t('time', 'Time')}</div>
89
+ {[...Array(6)].map((_, idx) => (
90
+ <div key={idx} className={styles.gridCell}>
91
+ --
92
+ </div>
93
+ ))}
94
+ </div>
95
+ <div className={styles.gridRow}>
96
+ <div className={styles.gridRowLabel}>{t('contractions', 'Contractions')}</div>
97
+ {[...Array(6)].map((_, idx) => (
98
+ <div key={idx} className={styles.gridCell}>
99
+ --
100
+ </div>
101
+ ))}
102
+ </div>
103
+ <div style={{ textAlign: 'center', marginTop: '2rem', width: '100%' }}>
104
+ <p>{t('noContractionData', 'No contraction data available')}</p>
105
+ <Button kind="primary" size={controlSize} renderIcon={Add} onClick={onAddData}>
106
+ {t('addFirstDataPoint', 'Add first data point')}
107
+ </Button>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ );
112
+
113
+ return (
114
+ <div className={styles.fetalHeartRateSection}>
115
+ <div className={styles.fetalHeartRateContainer}>
116
+ <div className={styles.fetalHeartRateHeader}>
117
+ <div className={styles.fetalHeartRateHeaderLeft}>
118
+ <h3 className={styles.fetalHeartRateTitle}>Uterine Contractions</h3>
119
+ <div className={styles.fetalHeartRateControls}>
120
+ <span className={styles.legendText}>
121
+ Contractions per 10 min | Bar Heights: 0=None, 2=Mild, 3=Moderate, 5=Strong
122
+ </span>
123
+ </div>
124
+ </div>
125
+ <div className={styles.fetalHeartRateHeaderRight}>
126
+ <div className={styles.fetalHeartRateActions}>
127
+ <div className={styles.viewSwitcher}>
128
+ <Button
129
+ kind={viewMode === 'graph' ? 'primary' : 'secondary'}
130
+ size={controlSize}
131
+ hasIconOnly
132
+ iconDescription={t('graphView', 'Graph View')}
133
+ onClick={() => onViewModeChange?.('graph')}
134
+ className={styles.viewButton}>
135
+ <ChartColumn />
136
+ </Button>
137
+ <Button
138
+ kind={viewMode === 'table' ? 'primary' : 'secondary'}
139
+ size={controlSize}
140
+ hasIconOnly
141
+ iconDescription={t('tableView', 'Table View')}
142
+ onClick={() => onViewModeChange?.('table')}
143
+ className={styles.viewButton}>
144
+ <TableIcon />
145
+ </Button>
146
+ </div>
147
+ <Button
148
+ kind="primary"
149
+ size={controlSize}
150
+ renderIcon={Add}
151
+ iconDescription="Add uterine contractions data"
152
+ disabled={isAddButtonDisabled}
153
+ onClick={onAddData}
154
+ className={styles.addButton}>
155
+ Add
156
+ </Button>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ {viewMode === 'graph' ? (
161
+ isLoading ? (
162
+ renderSkeleton()
163
+ ) : filteredTableData.length === 0 ? (
164
+ renderEmptyGraph()
165
+ ) : (
166
+ <div className={styles.membraneGrid}>
167
+ <div className={styles.gridContainer}>
168
+ {/* Header row with time columns */}
169
+ <div className={styles.gridHeader}>
170
+ <div className={styles.gridCell}>{t('time', 'Time')}</div>
171
+ {timeColumns.map((timeColumn, idx) => (
172
+ <div key={idx} className={styles.gridCell}>
173
+ {timeColumn}
174
+ </div>
175
+ ))}
176
+ </div>
177
+ {/* Contractions row */}
178
+ <div className={styles.gridRow}>
179
+ <div className={styles.gridRowLabel}>{t('contractions', 'Contractions')}</div>
180
+ {filteredTableData.map((item, idx) => (
181
+ <div key={`contraction-${idx}`} className={styles.gridCell}>
182
+ {item.contractionCount !== undefined && item.contractionCount !== null
183
+ ? item.contractionCount
184
+ : '--'}
185
+ </div>
186
+ ))}
187
+ </div>
188
+ </div>
189
+ </div>
190
+ )
191
+ ) : (
192
+ <div className={styles.tableContainer}>
193
+ <UterineContractionsTable
194
+ tableData={filteredTableData}
195
+ currentPage={currentPage}
196
+ pageSize={pageSize}
197
+ totalItems={filteredTableData.length}
198
+ controlSize={controlSize}
199
+ onPageChange={onPageChange}
200
+ onPageSizeChange={onPageSizeChange}
201
+ />
202
+ </div>
203
+ )}
204
+ </div>
205
+ </div>
206
+ );
207
+ };
208
+
209
+ export default UterineContractionsGraph;
@@ -0,0 +1,211 @@
1
+ import React, { useMemo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { useForm, Controller } from 'react-hook-form';
4
+ import { Button, Modal, Grid, Column, Dropdown } from '@carbon/react';
5
+ import styles from '../partography.scss';
6
+ import { CONTRACTION_STRONG_UUID } from '../types';
7
+ import { MOULDING_NONE_CONCEPT, MOULDING_SLIGHT_CONCEPT, MOULDING_MODERATE_CONCEPT } from '../../../config-schema';
8
+
9
+ type CervicalContractionsFormData = {
10
+ contractionLevel: string;
11
+ contractionCount: string;
12
+ };
13
+
14
+ type CervicalContractionsFormProps = {
15
+ isOpen: boolean;
16
+ onClose: () => void;
17
+ onSubmit: (data: { contractionLevel: string; contractionCount: string; timeSlot: string }) => void;
18
+ onDataSaved?: () => void;
19
+ patient?: {
20
+ uuid: string;
21
+ name: string;
22
+ gender: string;
23
+ age: string;
24
+ };
25
+ };
26
+
27
+ const CervicalContractionsForm: React.FC<CervicalContractionsFormProps> = ({
28
+ isOpen,
29
+ onClose,
30
+ onSubmit,
31
+ onDataSaved,
32
+ patient,
33
+ }) => {
34
+ const { t } = useTranslation();
35
+
36
+ const {
37
+ control,
38
+ handleSubmit,
39
+ reset,
40
+ setError,
41
+ clearErrors,
42
+ formState: { errors },
43
+ } = useForm<CervicalContractionsFormData>({
44
+ defaultValues: {
45
+ contractionLevel: '',
46
+ contractionCount: '',
47
+ },
48
+ });
49
+
50
+ const contractionLevelOptions = useMemo(
51
+ () => [
52
+ {
53
+ value: 'none',
54
+ label: t('noContractions', 'No Contractions'),
55
+ concept: MOULDING_NONE_CONCEPT,
56
+ visual: '0',
57
+ visualClass: 'none',
58
+ title: t('none', 'None'),
59
+ },
60
+ {
61
+ value: 'mild',
62
+ label: t('mildContractions', 'Mild Contractions'),
63
+ concept: MOULDING_SLIGHT_CONCEPT,
64
+ visual: '1',
65
+ visualClass: 'mild',
66
+ title: t('mild', 'Mild'),
67
+ },
68
+ {
69
+ value: 'moderate',
70
+ label: t('moderateContractions', 'Moderate Contractions'),
71
+ concept: MOULDING_MODERATE_CONCEPT,
72
+ visual: '2',
73
+ visualClass: 'moderate',
74
+ title: t('moderate', 'Moderate'),
75
+ },
76
+ {
77
+ value: 'strong',
78
+ label: t('strongContractions', 'Strong Contractions'),
79
+ concept: CONTRACTION_STRONG_UUID,
80
+ visual: '3',
81
+ visualClass: 'strong',
82
+ title: 'Strong',
83
+ },
84
+ ],
85
+ [t],
86
+ );
87
+
88
+ const contractionCountItems = useMemo(
89
+ () => [
90
+ { id: '1', text: t('oneContraction', '1 contraction') },
91
+ { id: '2', text: t('twoContractions', '2 contractions') },
92
+ { id: '3', text: t('threeContractions', '3 contractions') },
93
+ { id: '4', text: t('fourContractions', '4 contractions') },
94
+ { id: '5', text: t('fiveContractions', '5 contractions') },
95
+ ],
96
+ [t],
97
+ );
98
+
99
+ const handleFormSubmit = (data: CervicalContractionsFormData) => {
100
+ const currentTime = new Date().toLocaleTimeString('en-GB', {
101
+ hour: '2-digit',
102
+ minute: '2-digit',
103
+ });
104
+
105
+ onSubmit({
106
+ contractionLevel: data.contractionLevel,
107
+ contractionCount: data.contractionCount,
108
+ timeSlot: currentTime,
109
+ });
110
+
111
+ reset();
112
+ };
113
+
114
+ const handleClose = () => {
115
+ reset();
116
+ clearErrors();
117
+ onClose();
118
+ };
119
+
120
+ return (
121
+ <Modal
122
+ open={isOpen}
123
+ onRequestClose={handleClose}
124
+ primaryButtonText={t('save', 'Save')}
125
+ secondaryButtonText={t('cancel', 'Cancel')}
126
+ onRequestSubmit={handleSubmit(handleFormSubmit)}
127
+ onSecondarySubmit={handleClose}
128
+ className={styles.cervixModal}
129
+ size="lg">
130
+ <div className={styles.contractionsFormContainer}>
131
+ <div className={styles.formMainSection}>
132
+ <div className={styles.formSectionLeft}>
133
+ <h3 className={styles.sectionTitle}>{t('cervicalContractionsData', 'Cervical Contractions')}</h3>
134
+ <h4 className={styles.sectionTitle}>{t('contractionLevel', 'Select Contraction Level')}</h4>
135
+ <p className={styles.sectionDescription}>
136
+ {t('contractionLevelDescription', 'Choose the intensity of uterine contractions observed')}
137
+ </p>
138
+
139
+ <Controller
140
+ name="contractionLevel"
141
+ control={control}
142
+ rules={{ required: t('contractionLevelRequired', 'Please select contraction level') }}
143
+ render={({ field, fieldState }) => (
144
+ <>
145
+ <div className={styles.contractionLevelSelector}>
146
+ {contractionLevelOptions.map((option) => (
147
+ <div
148
+ key={option.value}
149
+ className={`${styles.contractionLevelOption} ${
150
+ field.value === option.value ? styles.contractionLevelSelected : ''
151
+ }`}
152
+ onClick={() => field.onChange(option.value)}
153
+ onKeyDown={(e) => {
154
+ if (e.key === 'Enter' || e.key === ' ') {
155
+ e.preventDefault();
156
+ field.onChange(option.value);
157
+ }
158
+ }}
159
+ role="button"
160
+ tabIndex={0}
161
+ aria-pressed={field.value === option.value}
162
+ title={option.label}>
163
+ <div className={styles.contractionLevelTitle}>{option.title}</div>
164
+ <div className={`${styles.contractionLevelButton} ${styles[option.visualClass]}`}>
165
+ {option.visual}
166
+ </div>
167
+ </div>
168
+ ))}
169
+ </div>
170
+ {fieldState.error && <div className={styles.errorMessage}>{fieldState.error.message}</div>}
171
+ </>
172
+ )}
173
+ />
174
+ </div>
175
+
176
+ <div className={styles.formSectionRight}>
177
+ <Controller
178
+ name="contractionCount"
179
+ control={control}
180
+ rules={{ required: t('contractionCountRequired', 'Please select number of contractions') }}
181
+ render={({ field, fieldState }) => (
182
+ <>
183
+ <Dropdown
184
+ id="contraction-count-dropdown"
185
+ titleText={t('chooseCount', 'Number of Contractions')}
186
+ label={t('chooseCount', 'Choose count')}
187
+ items={contractionCountItems}
188
+ itemToString={(item) => (item ? item.text : '')}
189
+ selectedItem={
190
+ field.value
191
+ ? {
192
+ id: field.value,
193
+ text: `${field.value} contraction${field.value === '1' ? '' : 's'}`,
194
+ }
195
+ : null
196
+ }
197
+ onChange={({ selectedItem }) => field.onChange(selectedItem?.id || '')}
198
+ className={styles.contractionCountDropdown}
199
+ />
200
+ {fieldState.error && <div className={styles.errorMessage}>{fieldState.error.message}</div>}
201
+ </>
202
+ )}
203
+ />
204
+ </div>
205
+ </div>
206
+ </div>
207
+ </Modal>
208
+ );
209
+ };
210
+
211
+ export default CervicalContractionsForm;