@kenyaemr/esm-patient-clinical-view-app 5.4.2-pre.2716 → 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,190 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import {
4
+ Button,
5
+ DataTable,
6
+ TableContainer,
7
+ Table,
8
+ TableHead,
9
+ TableRow,
10
+ TableHeader,
11
+ TableBody,
12
+ TableCell,
13
+ Pagination,
14
+ Tag,
15
+ } from '@carbon/react';
16
+ import { Add, ChartColumn, Table as TableIcon } from '@carbon/react/icons';
17
+ import styles from '../partography.scss';
18
+ import OxytocinGraphComponent from './oxytocin-graph.component';
19
+ import { usePaginationInfo } from '@openmrs/esm-patient-common-lib';
20
+
21
+ export interface OxytocinData {
22
+ timeSlot: string;
23
+ oxytocinUsed: 'yes' | 'no';
24
+ dropsPerMinute: number;
25
+ date?: string;
26
+ id?: string;
27
+ }
28
+
29
+ interface OxytocinGraphProps {
30
+ data: OxytocinData[];
31
+ tableData: any[];
32
+ viewMode: 'graph' | 'table';
33
+ currentPage: number;
34
+ pageSize: number;
35
+ totalItems: number;
36
+ controlSize: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
37
+ onAddData: () => void;
38
+ onViewModeChange: (mode: 'graph' | 'table') => void;
39
+ onPageChange: (page: number) => void;
40
+ onPageSizeChange: (size: number) => void;
41
+ isAddButtonDisabled: boolean;
42
+ }
43
+
44
+ const OxytocinGraph: React.FC<OxytocinGraphProps> = ({
45
+ data,
46
+ tableData,
47
+ viewMode,
48
+ currentPage,
49
+ pageSize,
50
+ totalItems,
51
+ controlSize,
52
+ onAddData,
53
+ onViewModeChange,
54
+ onPageChange,
55
+ onPageSizeChange,
56
+ isAddButtonDisabled,
57
+ }) => {
58
+ const { t } = useTranslation();
59
+
60
+ const startIndex = (currentPage - 1) * pageSize;
61
+ const endIndex = startIndex + pageSize;
62
+ const paginatedData = tableData.slice(startIndex, endIndex);
63
+ const { pageSizes: calculatedPageSizes, itemsDisplayed } = usePaginationInfo(
64
+ pageSize,
65
+ Math.ceil(totalItems / pageSize),
66
+ currentPage,
67
+ totalItems,
68
+ );
69
+
70
+ const tableHeaders = [
71
+ { key: 'date', header: t('date', 'Date') },
72
+ { key: 'timeSlot', header: t('time', 'Time') },
73
+ { key: 'oxytocinUsed', header: t('oxytocinUsed', 'Oxytocin Used') },
74
+ { key: 'dropsPerMinute', header: t('dropsPerMinute', 'Drops per Minute') },
75
+ ];
76
+
77
+ return (
78
+ <div className={styles.graphContainer}>
79
+ <div className={styles.graphHeader}>
80
+ <div className={styles.graphHeaderLeft}>
81
+ <h6>{t('oxytocinAdministration', 'Oxytocin Administration')}</h6>
82
+ <Tag type="outline">{t('oxytocinRange', '0-60 drops/min')}</Tag>
83
+ </div>
84
+ <div className={styles.graphHeaderRight}>
85
+ <div className={styles.viewSwitcher}>
86
+ <Button
87
+ kind={viewMode === 'graph' ? 'primary' : 'secondary'}
88
+ size={controlSize}
89
+ hasIconOnly
90
+ iconDescription={t('graphView', 'Graph View')}
91
+ onClick={() => onViewModeChange('graph')}
92
+ className={styles.viewButton}>
93
+ <ChartColumn />
94
+ </Button>
95
+ <Button
96
+ kind={viewMode === 'table' ? 'primary' : 'secondary'}
97
+ size={controlSize}
98
+ hasIconOnly
99
+ iconDescription={t('tableView', 'Table View')}
100
+ onClick={() => onViewModeChange('table')}
101
+ className={styles.viewButton}>
102
+ <TableIcon />
103
+ </Button>
104
+ </div>
105
+ <Button kind="primary" size={controlSize} renderIcon={Add} onClick={onAddData} disabled={isAddButtonDisabled}>
106
+ {t('add', 'Add')}
107
+ </Button>
108
+ </div>
109
+ </div>
110
+ <p className={styles.graphDescription}>
111
+ {t('oxytocinDescription', 'Track oxytocin administration and drops per minute over time')}
112
+ </p>
113
+
114
+ {viewMode === 'graph' ? (
115
+ <div className={styles.chartContainer}>
116
+ <OxytocinGraphComponent data={data} />
117
+ </div>
118
+ ) : (
119
+ <div className={styles.tableContainer}>
120
+ {paginatedData.length > 0 ? (
121
+ <>
122
+ <DataTable rows={paginatedData} headers={tableHeaders}>
123
+ {({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
124
+ <TableContainer title="" description="">
125
+ <Table {...getTableProps()} size="sm">
126
+ <TableHead>
127
+ <TableRow>
128
+ {headers.map((header) => (
129
+ <TableHeader {...getHeaderProps({ header })} key={header.key}>
130
+ {header.header}
131
+ </TableHeader>
132
+ ))}
133
+ </TableRow>
134
+ </TableHead>
135
+ <TableBody>
136
+ {rows.map((row) => (
137
+ <TableRow {...getRowProps({ row })} key={row.id}>
138
+ {row.cells.map((cell) => (
139
+ <TableCell key={cell.id}>{cell.value}</TableCell>
140
+ ))}
141
+ </TableRow>
142
+ ))}
143
+ </TableBody>
144
+ </Table>
145
+ </TableContainer>
146
+ )}
147
+ </DataTable>
148
+
149
+ {totalItems > 0 && (
150
+ <Pagination
151
+ page={currentPage}
152
+ totalItems={totalItems}
153
+ pageSize={pageSize}
154
+ pageSizes={calculatedPageSizes}
155
+ onChange={(event) => {
156
+ onPageChange(event.page);
157
+ if (event.pageSize !== pageSize) {
158
+ onPageSizeChange(event.pageSize);
159
+ }
160
+ }}
161
+ size={controlSize as 'sm' | 'md' | 'lg'}
162
+ />
163
+ )}
164
+ {totalItems > 0 && <div className={styles.paginationInfo}>{itemsDisplayed}</div>}
165
+ <div className={styles.tableStats}>
166
+ <span className={styles.recordCount}>
167
+ {t('showingResults', 'Showing {{start}}-{{end}} of {{total}} {{itemType}}', {
168
+ start: totalItems === 0 ? 0 : startIndex + 1,
169
+ end: Math.min(endIndex, totalItems),
170
+ total: totalItems,
171
+ itemType: totalItems === 1 ? t('record', 'record') : t('records', 'records'),
172
+ })}
173
+ </span>
174
+ </div>
175
+ </>
176
+ ) : (
177
+ <div className={styles.emptyState}>
178
+ <p>{t('noOxytocinData', 'No oxytocin data available')}</p>
179
+ <Button kind="primary" size={controlSize} renderIcon={Add} onClick={onAddData}>
180
+ {t('addFirstDataPoint', 'Add first data point')}
181
+ </Button>
182
+ </div>
183
+ )}
184
+ </div>
185
+ )}
186
+ </div>
187
+ );
188
+ };
189
+
190
+ export default OxytocinGraph;
@@ -0,0 +1,126 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import styles from '../partography.scss';
4
+
5
+ export interface OxytocinData {
6
+ timeSlot: string;
7
+ oxytocinUsed: 'yes' | 'no';
8
+ dropsPerMinute: number;
9
+ }
10
+
11
+ interface OxytocinGraphProps {
12
+ data: OxytocinData[];
13
+ }
14
+
15
+ const OxytocinGraph: React.FC<OxytocinGraphProps> = ({ data }) => {
16
+ const { t } = useTranslation();
17
+
18
+ const getTimeColumns = () => {
19
+ const emptyColumns = Array.from({ length: 13 }, (_, i) => `grid-${i + 1}`);
20
+ if (data.length === 0) {
21
+ return emptyColumns;
22
+ }
23
+ const dataTimeSlots = [...new Set(data.map((item) => item.timeSlot))].sort();
24
+ if (dataTimeSlots.length <= 13) {
25
+ const remainingEmpty = Array.from({ length: 13 - dataTimeSlots.length }, (_, i) => `empty-${i + 1}`);
26
+ return [...dataTimeSlots, ...remainingEmpty];
27
+ }
28
+ return dataTimeSlots;
29
+ };
30
+
31
+ const timeColumns = getTimeColumns();
32
+
33
+ const rows = [
34
+ { id: 'oxytocin', label: t('oxytocin', 'Oxytocin') },
35
+ { id: 'drops', label: t('dropsPerMinute', 'Drops per minute') },
36
+ ];
37
+
38
+ const getDataForTimeSlot = (timeSlot: string): OxytocinData | undefined => {
39
+ if (timeSlot.startsWith('grid-') || timeSlot.startsWith('empty-')) {
40
+ return undefined;
41
+ }
42
+ return data.find((item) => item.timeSlot === timeSlot);
43
+ };
44
+
45
+ const getCellContent = (rowId: string, timeSlot: string) => {
46
+ const dataPoint = getDataForTimeSlot(timeSlot);
47
+
48
+ if (rowId === 'oxytocin') {
49
+ return dataPoint?.oxytocinUsed === 'yes' ? '✅' : '';
50
+ }
51
+
52
+ if (rowId === 'drops') {
53
+ return dataPoint?.oxytocinUsed === 'yes' ? dataPoint.dropsPerMinute.toString() : '';
54
+ }
55
+
56
+ return '';
57
+ };
58
+
59
+ const getCellClass = (rowId: string, timeSlot: string): string => {
60
+ const dataPoint = getDataForTimeSlot(timeSlot);
61
+ if (!dataPoint || dataPoint.oxytocinUsed === 'no') {
62
+ return '';
63
+ }
64
+
65
+ if (rowId === 'drops') {
66
+ const drops = dataPoint.dropsPerMinute;
67
+ if (drops >= 40) {
68
+ return 'oxytocinBarHigh';
69
+ }
70
+ if (drops >= 20) {
71
+ return 'oxytocinBarMedium';
72
+ }
73
+ if (drops > 0) {
74
+ return 'oxytocinBarLow';
75
+ }
76
+ }
77
+
78
+ return '';
79
+ };
80
+
81
+ return (
82
+ <div className={styles.membraneGrid}>
83
+ <div className={styles.gridContainer}>
84
+ <div className={styles.gridHeader}>
85
+ <div className={styles.gridCell}>{t('time', 'Time')}</div>
86
+ {timeColumns.map((timeColumn) => (
87
+ <div key={timeColumn} className={styles.gridCell}>
88
+ {timeColumn.startsWith('grid-') || timeColumn.startsWith('empty-') ? '' : timeColumn}
89
+ </div>
90
+ ))}
91
+ </div>
92
+
93
+ {rows.map((row) => (
94
+ <div key={row.id} className={styles.gridRow}>
95
+ <div className={styles.gridRowLabel}>{row.label}</div>
96
+ {timeColumns.map((timeColumn) => {
97
+ const content = getCellContent(row.id, timeColumn);
98
+ const cellClass = getCellClass(row.id, timeColumn);
99
+ return (
100
+ <div
101
+ key={`${timeColumn}-${row.id}`}
102
+ className={`${styles.gridCell} ${cellClass}`}
103
+ data-time-slot={timeColumn}
104
+ data-row={row.id}
105
+ title={`${timeColumn}: ${content || 'No data'}`}>
106
+ {content && (
107
+ <span
108
+ style={{
109
+ fontWeight: 'bold',
110
+ fontSize: row.id === 'oxytocin' ? '16px' : '14px',
111
+ color: cellClass.includes('High') ? 'white' : 'inherit',
112
+ }}>
113
+ {content}
114
+ </span>
115
+ )}
116
+ </div>
117
+ );
118
+ })}
119
+ </div>
120
+ ))}
121
+ </div>
122
+ </div>
123
+ );
124
+ };
125
+
126
+ export default OxytocinGraph;
@@ -0,0 +1,266 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import {
4
+ Tag,
5
+ Button,
6
+ DataTable,
7
+ TableContainer,
8
+ Table,
9
+ TableHead,
10
+ TableRow,
11
+ TableHeader,
12
+ TableBody,
13
+ TableCell,
14
+ Pagination,
15
+ } from '@carbon/react';
16
+ import { Add, ChartColumn, Table as TableIcon } from '@carbon/react/icons';
17
+ import { LineChart } from '@carbon/charts-react';
18
+ import styles from '../partography.scss';
19
+ import { getColorForGraph } from '../types';
20
+ import { usePaginationInfo } from '@openmrs/esm-patient-common-lib';
21
+
22
+ enum ScaleTypes {
23
+ LABELS = 'labels',
24
+ LINEAR = 'linear',
25
+ }
26
+
27
+ interface ChartDataPoint {
28
+ hour: number;
29
+ time?: string;
30
+ group: string;
31
+ value: number;
32
+ }
33
+
34
+ interface GraphDefinition {
35
+ id: string;
36
+ title: string;
37
+ color: string;
38
+ yAxisLabel: string;
39
+ yMin: number;
40
+ yMax: number;
41
+ normalRange: string;
42
+ description: string;
43
+ }
44
+
45
+ interface PartographGraphProps {
46
+ graph: GraphDefinition;
47
+ data: ChartDataPoint[];
48
+ tableData: any[];
49
+ viewMode: 'graph' | 'table';
50
+ currentPage: number;
51
+ pageSize: number;
52
+ totalItems: number;
53
+ isLoading: boolean;
54
+ controlSize: 'sm' | 'md';
55
+ onAddData: () => void;
56
+ onViewModeChange: (mode: 'graph' | 'table') => void;
57
+ onPageChange: (page: number) => void;
58
+ onPageSizeChange: (size: number) => void;
59
+ getTableHeaders: () => Array<{ key: string; header: string }>;
60
+ getColorForGraph: (color: string) => string;
61
+ }
62
+
63
+ const PartographGraph: React.FC<PartographGraphProps> = ({
64
+ graph,
65
+ data,
66
+ tableData,
67
+ viewMode,
68
+ currentPage,
69
+ pageSize,
70
+ totalItems,
71
+ isLoading,
72
+ controlSize,
73
+ onAddData,
74
+ onViewModeChange,
75
+ onPageChange,
76
+ onPageSizeChange,
77
+ getTableHeaders,
78
+ getColorForGraph,
79
+ }) => {
80
+ const { t } = useTranslation();
81
+
82
+ const startIndex = (currentPage - 1) * pageSize;
83
+ const endIndex = startIndex + pageSize;
84
+ const paginatedData = tableData.slice(startIndex, endIndex);
85
+ const { pageSizes: calculatedPageSizes, itemsDisplayed } = usePaginationInfo(
86
+ pageSize,
87
+ Math.ceil(totalItems / pageSize),
88
+ currentPage,
89
+ totalItems,
90
+ );
91
+
92
+ const chartOptions = {
93
+ title: graph.title,
94
+ axes: {
95
+ bottom: {
96
+ title: undefined,
97
+ mapsTo: 'time',
98
+ scaleType: ScaleTypes.LABELS,
99
+ tick: {
100
+ formatter: () => '',
101
+ },
102
+ },
103
+ left: {
104
+ title: graph.yAxisLabel,
105
+ mapsTo: 'value',
106
+ scaleType: ScaleTypes.LINEAR,
107
+ domain: [graph.yMin, graph.yMax],
108
+ },
109
+ },
110
+ curve: 'curveLinear',
111
+ height: '500px',
112
+ color: {
113
+ scale: {
114
+ [data[0]?.group || graph.id]: getColorForGraph(graph.color),
115
+ Systolic: getColorForGraph('red'),
116
+ Diastolic: getColorForGraph('green'),
117
+ },
118
+ },
119
+ points: {
120
+ enabled: true,
121
+ radius: 5,
122
+ filled: true,
123
+ },
124
+ legend: {
125
+ position: 'bottom',
126
+ clickable: false,
127
+ },
128
+ theme: 'white',
129
+ toolbar: {
130
+ enabled: false,
131
+ },
132
+ };
133
+
134
+ const shouldRenderChart = data.length > 0;
135
+
136
+ return (
137
+ <div className={styles.graphContainer}>
138
+ <div className={styles.graphHeader}>
139
+ <div className={styles.graphHeaderLeft}>
140
+ <h6>{graph.title}</h6>
141
+ <Tag type="outline">{graph.normalRange}</Tag>
142
+ </div>
143
+ <div className={styles.graphHeaderRight}>
144
+ <div className={styles.viewSwitcher}>
145
+ <Button
146
+ kind={viewMode === 'graph' ? 'primary' : 'secondary'}
147
+ size={controlSize}
148
+ hasIconOnly
149
+ iconDescription={t('graphView', 'Graph View')}
150
+ onClick={() => onViewModeChange('graph')}
151
+ className={styles.viewButton}>
152
+ <ChartColumn />
153
+ </Button>
154
+ <Button
155
+ kind={viewMode === 'table' ? 'primary' : 'secondary'}
156
+ size={controlSize}
157
+ hasIconOnly
158
+ iconDescription={t('tableView', 'Table View')}
159
+ onClick={() => onViewModeChange('table')}
160
+ className={styles.viewButton}>
161
+ <TableIcon />
162
+ </Button>
163
+ </div>
164
+ <Button kind="primary" size={controlSize} renderIcon={Add} onClick={onAddData}>
165
+ {t('add', 'Add')}
166
+ </Button>
167
+ </div>
168
+ </div>
169
+ <p className={styles.graphDescription}>{graph.description}</p>
170
+
171
+ {viewMode === 'graph' ? (
172
+ <div className={styles.chartContainer}>
173
+ {shouldRenderChart ? (
174
+ <LineChart data={data} options={chartOptions} />
175
+ ) : (
176
+ <LineChart
177
+ data={[{ group: graph.title, time: t('noData', 'No Data'), value: graph.yMin }]}
178
+ options={{
179
+ ...chartOptions,
180
+ legend: { enabled: false },
181
+ points: { enabled: false },
182
+ color: { scale: { [graph.title]: getColorForGraph('gray') } },
183
+ }}
184
+ />
185
+ )}
186
+
187
+ {data.length > 0 && !isLoading && (
188
+ <div className={styles.chartStats}>
189
+ <div className={styles.statItem}>
190
+ <span className={styles.statLabel}>{t('latest', 'Latest')}:</span>
191
+ <span className={styles.statValue}>
192
+ {data[data.length - 1]?.value?.toFixed(1)} {graph.yAxisLabel}
193
+ </span>
194
+ </div>
195
+ <div className={styles.statItem}>
196
+ <span className={styles.statLabel}>{t('average', 'Average')}:</span>
197
+ <span className={styles.statValue}>
198
+ {(data.reduce((sum, item) => sum + item.value, 0) / data.length).toFixed(1)} {graph.yAxisLabel}
199
+ </span>
200
+ </div>
201
+ </div>
202
+ )}
203
+ </div>
204
+ ) : (
205
+ <div className={styles.tableContainer}>
206
+ {paginatedData.length > 0 ? (
207
+ <>
208
+ <DataTable rows={paginatedData} headers={getTableHeaders()}>
209
+ {({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
210
+ <TableContainer title="" description="">
211
+ <Table {...getTableProps()} size="sm">
212
+ <TableHead>
213
+ <TableRow>
214
+ {headers.map((header) => (
215
+ <TableHeader {...getHeaderProps({ header })} key={header.key}>
216
+ {header.header}
217
+ </TableHeader>
218
+ ))}
219
+ </TableRow>
220
+ </TableHead>
221
+ <TableBody>
222
+ {rows.map((row) => (
223
+ <TableRow {...getRowProps({ row })} key={row.id}>
224
+ {row.cells.map((cell) => (
225
+ <TableCell key={cell.id}>{cell.value}</TableCell>
226
+ ))}
227
+ </TableRow>
228
+ ))}
229
+ </TableBody>
230
+ </Table>
231
+ </TableContainer>
232
+ )}
233
+ </DataTable>
234
+
235
+ {totalItems > 0 && (
236
+ <Pagination
237
+ page={currentPage}
238
+ totalItems={totalItems}
239
+ pageSize={pageSize}
240
+ pageSizes={calculatedPageSizes}
241
+ onChange={(event) => {
242
+ onPageChange(event.page);
243
+ if (event.pageSize !== pageSize) {
244
+ onPageSizeChange(event.pageSize);
245
+ }
246
+ }}
247
+ size={controlSize}
248
+ />
249
+ )}
250
+ {totalItems > 0 && <div className={styles.paginationInfo}>{itemsDisplayed}</div>}
251
+ </>
252
+ ) : (
253
+ <div className={styles.emptyState}>
254
+ <p>{t('noDataAvailable', 'No data available for this graph')}</p>
255
+ <Button kind="primary" size={controlSize} renderIcon={Add} onClick={onAddData}>
256
+ {t('addFirstDataPoint', 'Add first data point')}
257
+ </Button>
258
+ </div>
259
+ )}
260
+ </div>
261
+ )}
262
+ </div>
263
+ );
264
+ };
265
+
266
+ export default PartographGraph;