@digihmis/esm-triage-app 1.0.0

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 (148) hide show
  1. package/README.md +4 -0
  2. package/dist/117.js +1 -0
  3. package/dist/117.js.map +1 -0
  4. package/dist/132.js +1 -0
  5. package/dist/132.js.map +1 -0
  6. package/dist/152.js +1 -0
  7. package/dist/152.js.map +1 -0
  8. package/dist/183.js +66 -0
  9. package/dist/183.js.map +1 -0
  10. package/dist/209.js +1 -0
  11. package/dist/209.js.map +1 -0
  12. package/dist/22.js +1 -0
  13. package/dist/22.js.map +1 -0
  14. package/dist/222.js +1 -0
  15. package/dist/222.js.map +1 -0
  16. package/dist/317.js +1 -0
  17. package/dist/317.js.map +1 -0
  18. package/dist/349.js +1 -0
  19. package/dist/349.js.map +1 -0
  20. package/dist/371.js +1 -0
  21. package/dist/371.js.map +1 -0
  22. package/dist/378.js +1 -0
  23. package/dist/378.js.map +1 -0
  24. package/dist/442.js +1 -0
  25. package/dist/442.js.map +1 -0
  26. package/dist/45.js +1 -0
  27. package/dist/45.js.map +1 -0
  28. package/dist/466.js +1 -0
  29. package/dist/466.js.map +1 -0
  30. package/dist/508.js +1 -0
  31. package/dist/508.js.map +1 -0
  32. package/dist/61.js +1 -0
  33. package/dist/61.js.map +1 -0
  34. package/dist/640.js +1 -0
  35. package/dist/640.js.map +1 -0
  36. package/dist/689.js +1 -0
  37. package/dist/689.js.map +1 -0
  38. package/dist/709.js +1 -0
  39. package/dist/709.js.map +1 -0
  40. package/dist/712.js +1 -0
  41. package/dist/712.js.map +1 -0
  42. package/dist/720.js +1 -0
  43. package/dist/720.js.map +1 -0
  44. package/dist/737.js +12 -0
  45. package/dist/737.js.map +1 -0
  46. package/dist/742.js +1 -0
  47. package/dist/742.js.map +1 -0
  48. package/dist/771.js +1 -0
  49. package/dist/771.js.map +1 -0
  50. package/dist/806.js +1 -0
  51. package/dist/806.js.map +1 -0
  52. package/dist/809.js +1 -0
  53. package/dist/809.js.map +1 -0
  54. package/dist/9.js +15 -0
  55. package/dist/9.js.map +1 -0
  56. package/dist/912.js +1 -0
  57. package/dist/912.js.map +1 -0
  58. package/dist/913.js +1 -0
  59. package/dist/913.js.map +1 -0
  60. package/dist/940.js +1 -0
  61. package/dist/940.js.map +1 -0
  62. package/dist/974.js +1 -0
  63. package/dist/974.js.map +1 -0
  64. package/dist/976.js +1 -0
  65. package/dist/976.js.map +1 -0
  66. package/dist/digihmis-esm-triage-app.js +6 -0
  67. package/dist/digihmis-esm-triage-app.js.buildmanifest.json +905 -0
  68. package/dist/digihmis-esm-triage-app.js.map +1 -0
  69. package/dist/main.js +90 -0
  70. package/dist/main.js.map +1 -0
  71. package/dist/routes.json +1 -0
  72. package/jest.config.js +26 -0
  73. package/package.json +51 -0
  74. package/rspack.config.js +1 -0
  75. package/src/config-schema.ts +142 -0
  76. package/src/dashboard.meta.ts +16 -0
  77. package/src/declarations.d.ts +6 -0
  78. package/src/icon-wrapper/icon-wrapper.extension.tsx +17 -0
  79. package/src/index.ts +41 -0
  80. package/src/left-panel/left-panel.component.tsx +100 -0
  81. package/src/left-panel/left-panel.scss +13 -0
  82. package/src/page-header/page-header.component.tsx +35 -0
  83. package/src/page-header/page-header.scss +76 -0
  84. package/src/past-vitals/action-menu/vitals-anthropometrics.component.tsx +44 -0
  85. package/src/past-vitals/action-menu/vitals-anthropometrics.scss +0 -0
  86. package/src/past-vitals/paginated-antropometrics/paginated-antropometrics.component.tsx +132 -0
  87. package/src/past-vitals/paginated-antropometrics/paginated-antropometrics.scss +9 -0
  88. package/src/past-vitals/paginated-vitals/paginated-vitals.component.tsx +157 -0
  89. package/src/past-vitals/paginated-vitals/paginated-vitals.scss +94 -0
  90. package/src/past-vitals/paginated-vitals/type/index.ts +1 -0
  91. package/src/past-vitals/patient-banner/patient-banner.component.tsx +60 -0
  92. package/src/past-vitals/patient-banner/patient-banner.scss +72 -0
  93. package/src/past-vitals/patient-vitals-history.component.tsx +85 -0
  94. package/src/past-vitals/patient-vitals-history.resource.ts +30 -0
  95. package/src/past-vitals/patient-vitals-history.scss +75 -0
  96. package/src/past-vitals/triage-anthropometrics/antropometrics-chart.component.tsx +170 -0
  97. package/src/past-vitals/triage-anthropometrics/antropometrics-chart.scss +70 -0
  98. package/src/past-vitals/triage-anthropometrics/triage-antropometrics.component.tsx +124 -0
  99. package/src/past-vitals/triage-anthropometrics/triage-antropometrics.scss +37 -0
  100. package/src/past-vitals/triage-anthropometrics/type/index.ts +17 -0
  101. package/src/past-vitals/triage-notes/triage-note.scss +94 -0
  102. package/src/past-vitals/triage-notes/triage-notes.component.tsx +75 -0
  103. package/src/past-vitals/triage-notes/triage-notes.resource.ts +72 -0
  104. package/src/past-vitals/triage-notes/type/index.ts +50 -0
  105. package/src/past-vitals/triage-vitals/triage-vitals-history.component.tsx +229 -0
  106. package/src/past-vitals/triage-vitals/triage-vitals-history.scss +48 -0
  107. package/src/past-vitals/triage-vitals/type/index.ts +32 -0
  108. package/src/past-vitals/triage-vitals/vital-chart.component.tsx +198 -0
  109. package/src/past-vitals/triage-vitals/vitals-chart.scss +70 -0
  110. package/src/past-vitals/utils/index.ts +5 -0
  111. package/src/past-vitals/vitals-and-biometrics-header/vitals-header-item.component.tsx +61 -0
  112. package/src/past-vitals/vitals-and-biometrics-header/vitals-header-item.scss +171 -0
  113. package/src/past-vitals/vitals-and-biometrics-header/vitals-header-item.test.tsx +31 -0
  114. package/src/past-vitals/vitals-and-biometrics-header/vitals-header.extension.tsx +225 -0
  115. package/src/past-vitals/vitals-and-biometrics-header/vitals-header.scss +233 -0
  116. package/src/queue-room-table/queue-room-table.component.tsx +380 -0
  117. package/src/queue-room-table/queue-room-table.resource.ts +73 -0
  118. package/src/queue-room-table/queue-room-table.scss +193 -0
  119. package/src/queue-room-table/type/index.ts +30 -0
  120. package/src/queue-room-table/useQueueActions.ts +24 -0
  121. package/src/record-vitals-workspace/common/index.ts +19 -0
  122. package/src/record-vitals-workspace/component/vitals-biometrics-input.component.tsx +433 -0
  123. package/src/record-vitals-workspace/component/vitals-biometrics-input.scss +358 -0
  124. package/src/record-vitals-workspace/helper/index.ts +200 -0
  125. package/src/record-vitals-workspace/record-vitals.resource.ts +570 -0
  126. package/src/record-vitals-workspace/record-vitals.scss +140 -0
  127. package/src/record-vitals-workspace/record-vitals.workspace.tsx +612 -0
  128. package/src/record-vitals-workspace/schema/index.ts +28 -0
  129. package/src/record-vitals-workspace/type/index.ts +170 -0
  130. package/src/record-vitals-workspace/utils/index.ts +100 -0
  131. package/src/routes.json +70 -0
  132. package/src/setup-tests.ts +1 -0
  133. package/src/triage-queue-dashboard/triage-dashboard.resource.ts +75 -0
  134. package/src/triage-queue-dashboard/triage-queue-dashboard.component.tsx +261 -0
  135. package/src/triage-queue-dashboard/triage-queue-dashboard.scss +166 -0
  136. package/src/triage-queue-dashboard/type/index.ts +31 -0
  137. package/translations/am.json +92 -0
  138. package/translations/ar.json +92 -0
  139. package/translations/en.json +92 -0
  140. package/translations/es.json +92 -0
  141. package/translations/fr.json +92 -0
  142. package/translations/he.json +92 -0
  143. package/translations/km.json +92 -0
  144. package/translations/pt.json +92 -0
  145. package/translations/sw.json +92 -0
  146. package/translations/vi.json +92 -0
  147. package/translations/zh.json +92 -0
  148. package/tsconfig.json +5 -0
@@ -0,0 +1,5 @@
1
+ export function getPatientUuidFromUrl(): string {
2
+ const match = /\/patient\/([a-zA-Z0-9\-]+)\/?/.exec(location.pathname);
3
+ const patientUuidFromUrl = match && match[1];
4
+ return patientUuidFromUrl || '';
5
+ }
@@ -0,0 +1,61 @@
1
+ import React, { useId } from 'react';
2
+ import classNames from 'classnames';
3
+ import { useTranslation } from 'react-i18next';
4
+
5
+ import styles from './vitals-header-item.scss';
6
+ import { ObservationInterpretation } from '../../record-vitals-workspace/type';
7
+
8
+ interface VitalsHeaderItemProps {
9
+ interpretation?: ObservationInterpretation;
10
+ unitName: string;
11
+ unitSymbol: React.ReactNode;
12
+ value: string | number;
13
+ }
14
+
15
+ const VitalsHeaderItem: React.FC<VitalsHeaderItemProps> = ({ interpretation, value, unitName, unitSymbol }) => {
16
+ const { t } = useTranslation();
17
+ const flaggedCritical = interpretation && interpretation.includes('critically');
18
+ const flaggedAbnormal = interpretation && interpretation !== 'normal';
19
+
20
+ const generatedId = useId();
21
+
22
+ const trimmedUnitName = unitName.trim().toLowerCase();
23
+ const unitNameWithoutSpaces = trimmedUnitName.replace(/\s+/g, '-');
24
+
25
+ const labelId = `omrs-patient-chart-label-${unitNameWithoutSpaces}-${generatedId}`;
26
+ const valueId = `omrs-patient-chart-value-${unitNameWithoutSpaces}-${generatedId}`;
27
+ const unitId = `omrs-patient-chart-unit-${unitNameWithoutSpaces}-${generatedId}`;
28
+
29
+ const displayValue = Boolean(value) ? value : t('notAvailable', 'Not available');
30
+
31
+ return (
32
+ <section className={styles.container}>
33
+ <div
34
+ className={classNames({
35
+ [styles['critical-value']]: flaggedCritical,
36
+ [styles['abnormal-value']]: flaggedAbnormal,
37
+ })}>
38
+ <div className={styles['label-container']}>
39
+ <span id={labelId} className={styles.label}>
40
+ {unitName}
41
+ </span>
42
+ {flaggedAbnormal ? (
43
+ <span className={styles[interpretation.replace('_', '-')]} title={t('abnormalValue', 'Abnormal value')} />
44
+ ) : null}
45
+ </div>
46
+ <div className={styles['value-container']}>
47
+ <div className={styles['pad-right']}>
48
+ <span id={valueId} aria-labelledby={`${labelId} ${unitId}`} className={styles.value}>
49
+ {displayValue}
50
+ </span>{' '}
51
+ <span id={unitId} className={styles.units}>
52
+ {value ? ` ${''} ${unitSymbol}` : ''}
53
+ </span>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </section>
58
+ );
59
+ };
60
+
61
+ export default VitalsHeaderItem;
@@ -0,0 +1,171 @@
1
+ // vitals-header-item.scss
2
+ @use '@carbon/colors';
3
+ @use '@carbon/type';
4
+ @use '@carbon/layout';
5
+ @use '@openmrs/esm-styleguide/src/vars' as *;
6
+
7
+ .container {
8
+ display: flex;
9
+ flex-direction: column;
10
+ width: 100%;
11
+ min-width: 0;
12
+ height: 100%;
13
+ }
14
+
15
+ .abnormal-value {
16
+ border: solid 1px colors.$orange-20-hover;
17
+ background-color: colors.$orange-10;
18
+ display: flex;
19
+ flex-direction: column;
20
+ width: 100%;
21
+ gap: layout.$spacing-01;
22
+ padding: layout.$spacing-02 layout.$spacing-03;
23
+ border-radius: 2px;
24
+ min-width: 0;
25
+ height: 100%;
26
+
27
+ :global(.omrs-breakpoint-lt-tablet) & {
28
+ padding: layout.$spacing-02;
29
+ }
30
+ }
31
+
32
+ .critical-value {
33
+ @extend .abnormal-value;
34
+ border: 1px solid colors.$red-60;
35
+ background-color: colors.$red-20;
36
+ }
37
+
38
+ .label-container {
39
+ display: flex;
40
+ flex-direction: row;
41
+ align-items: center;
42
+ gap: layout.$spacing-02;
43
+ min-width: 0;
44
+ flex-wrap: wrap;
45
+ }
46
+
47
+ .value-container {
48
+ display: flex;
49
+ flex-direction: row;
50
+ align-items: baseline;
51
+ gap: layout.$spacing-02; // Add gap between value and units
52
+ min-width: 0;
53
+ margin-top: layout.$spacing-01;
54
+ }
55
+
56
+ .label {
57
+ @include type.type-style('label-01');
58
+ color: $text-02;
59
+ white-space: nowrap;
60
+ overflow: hidden;
61
+ text-overflow: ellipsis;
62
+ line-height: 1.2;
63
+
64
+ :global(.omrs-breakpoint-lt-tablet) & {
65
+ font-size: 0.75rem;
66
+ }
67
+ }
68
+
69
+ .danger-icon {
70
+ color: $danger;
71
+ height: layout.$spacing-05;
72
+ width: layout.$spacing-05;
73
+ flex-shrink: 0;
74
+ }
75
+
76
+ .value {
77
+ @include type.type-style('body-compact-02');
78
+ font-weight: 600;
79
+ word-break: break-word;
80
+ line-height: 1.3;
81
+
82
+ :global(.omrs-breakpoint-lt-tablet) & {
83
+ font-size: 0.875rem;
84
+ }
85
+ }
86
+
87
+ .units {
88
+ @include type.type-style('body-compact-01');
89
+ color: $text-02;
90
+ white-space: nowrap;
91
+ flex-shrink: 0;
92
+ margin-left: layout.$spacing-01;
93
+
94
+ :global(.omrs-breakpoint-lt-tablet) & {
95
+ font-size: 0.75rem;
96
+ }
97
+ }
98
+
99
+ .pad-right {
100
+ display: flex;
101
+ align-items: baseline;
102
+ min-width: 0;
103
+ flex-wrap: nowrap;
104
+ }
105
+
106
+ .critically-low,
107
+ .critically-high,
108
+ .low,
109
+ .high {
110
+ position: relative;
111
+ display: inline-flex;
112
+ align-items: baseline;
113
+ flex-shrink: 0;
114
+
115
+ &::after {
116
+ @include type.type-style('heading-compact-01');
117
+ color: $text-02;
118
+ margin-left: 2px;
119
+ }
120
+ }
121
+
122
+ .arrow {
123
+ color: colors.$gray-100;
124
+ font-weight: 500;
125
+ }
126
+
127
+ .low::after {
128
+ @extend .arrow;
129
+ content: '↓';
130
+ }
131
+
132
+ .critically-low::after {
133
+ @extend .arrow;
134
+ content: '↓↓';
135
+ }
136
+
137
+ .high::after {
138
+ @extend .arrow;
139
+ content: '↑';
140
+ }
141
+
142
+ .critically-high::after {
143
+ @extend .arrow;
144
+ content: '↑↑';
145
+ }
146
+
147
+ // Ensure arrows are visible on all screen sizes
148
+ :global(.omrs-breakpoint-lt-tablet) {
149
+ .critically-low::after,
150
+ .critically-high::after,
151
+ .low::after,
152
+ .high::after {
153
+ font-size: 0.875rem;
154
+ }
155
+ }
156
+
157
+ // RTL support
158
+ html[dir='rtl'] {
159
+ .units {
160
+ margin-left: 0;
161
+ margin-right: layout.$spacing-01;
162
+ }
163
+
164
+ .critically-low::after,
165
+ .critically-high::after,
166
+ .low::after,
167
+ .high::after {
168
+ margin-left: 0;
169
+ margin-right: 2px;
170
+ }
171
+ }
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import VitalsHeaderItem from './vitals-header-item.component';
4
+
5
+ const testProps = { unitName: 'Temp', value: '36.5', unitSymbol: '°C' };
6
+
7
+ describe('VitalsHeaderItem', () => {
8
+ it('renders a vital sign in the vitals header', () => {
9
+ render(<VitalsHeaderItem {...testProps} />);
10
+
11
+ expect(screen.getByText('Temp')).toBeInTheDocument();
12
+ expect(screen.getByText('°C')).toBeInTheDocument();
13
+ expect(screen.getByText('36.5')).toBeInTheDocument();
14
+ });
15
+
16
+ it('handles empty unit symbol gracefully', () => {
17
+ const propsWithEmptyUnit = { ...testProps, unitSymbol: '' };
18
+ render(<VitalsHeaderItem {...propsWithEmptyUnit} />);
19
+
20
+ expect(screen.getByText('Temp')).toBeInTheDocument();
21
+ expect(screen.getByText('36.5')).toBeInTheDocument();
22
+ });
23
+
24
+ it('handles undefined unit symbol gracefully', () => {
25
+ const propsWithUndefinedUnit = { ...testProps, unitSymbol: undefined };
26
+ render(<VitalsHeaderItem {...propsWithUndefinedUnit} />);
27
+
28
+ expect(screen.getByText('Temp')).toBeInTheDocument();
29
+ expect(screen.getByText('36.5')).toBeInTheDocument();
30
+ });
31
+ });
@@ -0,0 +1,225 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import classNames from 'classnames';
3
+ import dayjs from 'dayjs';
4
+ import duration from 'dayjs/plugin/duration';
5
+ import isToday from 'dayjs/plugin/isToday';
6
+ dayjs.extend(isToday);
7
+ dayjs.extend(duration);
8
+ import { Trans, useTranslation } from 'react-i18next';
9
+ import { Button, InlineLoading, Tag } from '@carbon/react';
10
+ import { ArrowRight } from '@carbon/react/icons';
11
+ import { ConfigurableLink, formatDate, parseDate, useConfig, useVisit, useWorkspaces } from '@openmrs/esm-framework';
12
+
13
+ import VitalsHeaderItem from './vitals-header-item.component';
14
+ import styles from './vitals-header.scss';
15
+ import { ConfigObject } from '../../config-schema';
16
+ import {
17
+ assessValue,
18
+ getReferenceRangesForConcept,
19
+ interpretBloodPressure,
20
+ useConceptUnits,
21
+ useVitalsAndBiometrics,
22
+ useVitalsConceptMetadata,
23
+ } from '../../record-vitals-workspace/common';
24
+
25
+ interface VitalsHeaderProps {
26
+ patientUuid: string;
27
+
28
+ /**
29
+ * This is useful for extensions slots using the Vitals Header
30
+ */
31
+ hideLinks?: boolean;
32
+ }
33
+
34
+ const VitalsHeader: React.FC<VitalsHeaderProps> = ({ patientUuid, hideLinks = false }) => {
35
+ const { t } = useTranslation();
36
+ const config = useConfig<ConfigObject>();
37
+ const { conceptUnits } = useConceptUnits();
38
+ const { data: vitals, isLoading, isValidating } = useVitalsAndBiometrics(patientUuid, 'both');
39
+ const { conceptRanges } = useVitalsConceptMetadata(patientUuid);
40
+ const latestVitals = vitals?.[0];
41
+ const [showDetailsPanel, setShowDetailsPanel] = useState(false);
42
+ const toggleDetailsPanel = () => setShowDetailsPanel(!showDetailsPanel);
43
+ const { activeVisit } = useVisit(patientUuid);
44
+ const { workspaces } = useWorkspaces();
45
+
46
+ const isWorkspaceOpen = useCallback(() => Boolean(workspaces?.length), [workspaces]);
47
+
48
+ if (isLoading) {
49
+ return (
50
+ <InlineLoading role="progressbar" className={styles.loading} description={`${t('loading', 'Loading')} ...`} />
51
+ );
52
+ }
53
+
54
+ if (latestVitals && Object.keys(latestVitals)?.length && conceptRanges?.length) {
55
+ const hasActiveVisit = Boolean(activeVisit?.uuid);
56
+ const now = dayjs();
57
+ const vitalsTakenTimeAgo = dayjs.duration(now.diff(latestVitals?.date));
58
+ const vitalsOverdueThresholdHours = config.vitals.vitalsOverdueThresholdHours;
59
+ const areVitalsOverdue = hasActiveVisit && vitalsTakenTimeAgo.asHours() >= vitalsOverdueThresholdHours;
60
+ const vitalsOverdueDayCount = Math.round(vitalsTakenTimeAgo.asDays());
61
+ const hoursSinceVitalsTaken = Math.round(vitalsTakenTimeAgo.asHours());
62
+ let overdueVitalsTagContent: React.ReactNode = null;
63
+
64
+ if (vitalsOverdueDayCount < 1) {
65
+ overdueVitalsTagContent = (
66
+ <Trans i18nKey="hoursOldVitals" count={hoursSinceVitalsTaken}>
67
+ <span>
68
+ {/* @ts-ignore: See comment below */}
69
+ These vitals are <strong>{{ count: hoursSinceVitalsTaken }} hour old</strong>
70
+ </span>
71
+ </Trans>
72
+ );
73
+ } else if (vitalsOverdueDayCount >= 1 && vitalsOverdueDayCount < 7) {
74
+ overdueVitalsTagContent = (
75
+ <Trans i18nKey="daysOldVitals" count={vitalsOverdueDayCount}>
76
+ <span>
77
+ {/* @ts-ignore Workaround for i18next types issue (see https://github.com/i18next/react-i18next/issues/1543 and https://github.com/i18next/react-i18next/issues/465). Additionally, I can't find a way to get the proper plural suffix to be used in the translation file without amending the translation file by hand. */}
78
+ These vitals are <strong>{{ count: vitalsOverdueDayCount }} day old</strong>
79
+ </span>
80
+ </Trans>
81
+ );
82
+ } else if (vitalsOverdueDayCount >= 8 && vitalsOverdueDayCount <= 14) {
83
+ overdueVitalsTagContent = (
84
+ <Trans i18nKey="overOneWeekOldVitals">
85
+ <span>
86
+ These vitals are <strong>over one week old</strong>
87
+ </span>
88
+ </Trans>
89
+ );
90
+ } else {
91
+ overdueVitalsTagContent = (
92
+ <Trans i18nKey="outOfDateVitals">
93
+ <span>
94
+ These vitals are <strong>out of date</strong>
95
+ </span>
96
+ </Trans>
97
+ );
98
+ }
99
+
100
+ return (
101
+ <div className={styles.container}>
102
+ <div className={styles.vitalsHeader} role="button" tabIndex={0} onClick={toggleDetailsPanel}>
103
+ <div className={styles.headerItems}>
104
+ <span className={styles.heading}>{t('vitalsAndAnthropometrics', 'Vitals and anthropometrics')}</span>
105
+ <span className={styles.bodyText}>
106
+ {formatDate(parseDate(latestVitals?.date), { day: true, time: true })}
107
+ </span>
108
+ {areVitalsOverdue ? (
109
+ <Tag className={styles.tag} type="red">
110
+ <span className={styles.overdueIndicator}>{overdueVitalsTagContent}</span>
111
+ </Tag>
112
+ ) : null}
113
+ </div>
114
+ {isValidating ? (
115
+ <div className={styles.backgroundDataFetchingIndicator}>
116
+ <span>{isValidating ? <InlineLoading /> : null}</span>
117
+ </div>
118
+ ) : null}
119
+ </div>
120
+ <div
121
+ className={classNames(styles.rowContainer, {
122
+ [styles.workspaceOpen]: isWorkspaceOpen(),
123
+ })}>
124
+ <div className={styles.row}>
125
+ <VitalsHeaderItem
126
+ interpretation={interpretBloodPressure(
127
+ latestVitals?.systolic,
128
+ latestVitals?.diastolic,
129
+ config?.concepts,
130
+ conceptRanges,
131
+ latestVitals?.systolicRenderInterpretation,
132
+ latestVitals?.diastolicRenderInterpretation,
133
+ )}
134
+ unitName={t('bp', 'BP')}
135
+ unitSymbol={(latestVitals?.systolic && conceptUnits.get(config.concepts.systolicBloodPressureUuid)) ?? ''}
136
+ value={`${latestVitals?.systolic ?? '--'} / ${latestVitals?.diastolic ?? '--'}`}
137
+ />
138
+ <VitalsHeaderItem
139
+ interpretation={
140
+ latestVitals?.pulseRenderInterpretation ??
141
+ assessValue(latestVitals?.pulse, getReferenceRangesForConcept(config.concepts.pulseUuid, conceptRanges))
142
+ }
143
+ unitName={t('heartRate', 'Heart rate')}
144
+ unitSymbol={(latestVitals?.pulse && conceptUnits.get(config.concepts.pulseUuid)) ?? ''}
145
+ value={latestVitals?.pulse ?? '--'}
146
+ />
147
+ <VitalsHeaderItem
148
+ interpretation={
149
+ latestVitals?.respiratoryRateRenderInterpretation ??
150
+ assessValue(
151
+ latestVitals?.respiratoryRate,
152
+ getReferenceRangesForConcept(config.concepts.respiratoryRateUuid, conceptRanges),
153
+ )
154
+ }
155
+ unitName={t('respiratoryRate', 'R. rate')}
156
+ unitSymbol={
157
+ (latestVitals?.respiratoryRate && conceptUnits.get(config.concepts.respiratoryRateUuid)) ?? ''
158
+ }
159
+ value={latestVitals?.respiratoryRate ?? '--'}
160
+ />
161
+ <VitalsHeaderItem
162
+ interpretation={
163
+ latestVitals?.spo2RenderInterpretation ??
164
+ assessValue(
165
+ latestVitals?.spo2,
166
+ getReferenceRangesForConcept(config.concepts.oxygenSaturationUuid, conceptRanges),
167
+ )
168
+ }
169
+ unitName={t('spo2', 'SpO2')}
170
+ unitSymbol={(latestVitals?.spo2 && conceptUnits.get(config.concepts.oxygenSaturationUuid)) ?? ''}
171
+ value={latestVitals?.spo2 ?? '--'}
172
+ />
173
+ <VitalsHeaderItem
174
+ interpretation={
175
+ latestVitals?.temperatureRenderInterpretation ??
176
+ assessValue(
177
+ latestVitals?.temperature,
178
+ getReferenceRangesForConcept(config.concepts.temperatureUuid, conceptRanges),
179
+ )
180
+ }
181
+ unitName={t('temperatureAbbreviated', 'Temp')}
182
+ unitSymbol={(latestVitals?.temperature && conceptUnits.get(config.concepts.temperatureUuid)) ?? ''}
183
+ value={latestVitals?.temperature ?? '--'}
184
+ />
185
+ <VitalsHeaderItem
186
+ unitName={t('weight', 'Weight')}
187
+ unitSymbol={(latestVitals?.weight && conceptUnits.get(config.concepts.weightUuid)) ?? ''}
188
+ value={latestVitals?.weight ?? '--'}
189
+ />
190
+ <VitalsHeaderItem
191
+ unitName={t('height', 'Height')}
192
+ unitSymbol={(latestVitals?.height && conceptUnits.get(config.concepts.heightUuid)) ?? ''}
193
+ value={latestVitals?.height ?? '--'}
194
+ />
195
+ <VitalsHeaderItem
196
+ unitName={t('bmi', 'BMI')}
197
+ unitSymbol={(latestVitals?.bmi && config.biometrics['bmiUnit']) ?? ''}
198
+ value={latestVitals?.bmi ?? '--'}
199
+ />
200
+ {latestVitals?.muac && (
201
+ <VitalsHeaderItem
202
+ unitName={t('muac', 'MUAC')}
203
+ unitSymbol={
204
+ (latestVitals?.muac && conceptUnits.get(config.concepts.midUpperArmCircumferenceUuid)) ?? ''
205
+ }
206
+ value={latestVitals?.muac ?? '--'}
207
+ />
208
+ )}
209
+ </div>
210
+ </div>
211
+ </div>
212
+ );
213
+ }
214
+
215
+ return (
216
+ <div className={styles.emptyStateVitalsHeader}>
217
+ <div className={styles.container}>
218
+ <span className={styles.heading}>{t('vitalsAndAnthropometrics', 'Vitals and anthropometrics')}</span>
219
+ <span className={styles.bodyText}>{t('noDataRecorded', 'No data has been recorded for this patient')}</span>
220
+ </div>
221
+ </div>
222
+ );
223
+ };
224
+
225
+ export default VitalsHeader;