@openmrs/esm-patient-vitals-app 11.3.1-patch.9064 → 11.3.1-patch.9310
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.
- package/.turbo/turbo-build.log +21 -18
- package/dist/3174.js +2 -0
- package/dist/3174.js.map +1 -0
- package/dist/4341.js +1 -0
- package/dist/4341.js.map +1 -0
- package/dist/5652.js +1 -0
- package/dist/5652.js.map +1 -0
- package/dist/5670.js +1 -1
- package/dist/5670.js.map +1 -1
- package/dist/6336.js +1 -0
- package/dist/6336.js.map +1 -0
- package/dist/7299.js +1 -1
- package/dist/7437.js +1 -0
- package/dist/7437.js.map +1 -0
- package/dist/8953.js +1 -1
- package/dist/9228.js +1 -0
- package/dist/9228.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-vitals-app.js +1 -1
- package/dist/openmrs-esm-patient-vitals-app.js.buildmanifest.json +155 -130
- package/dist/openmrs-esm-patient-vitals-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +4 -3
- package/src/biometrics/biometrics-base.component.tsx +4 -2
- package/src/biometrics/biometrics-main.component.tsx +11 -2
- package/src/biometrics/biometrics-overview.component.tsx +11 -2
- package/src/biometrics/biometrics-overview.test.tsx +3 -0
- package/src/biometrics/paginated-biometrics.component.tsx +3 -1
- package/src/common/data.resource.ts +20 -18
- package/src/common/helpers.ts +38 -9
- package/src/common/types.ts +13 -1
- package/src/components/action-menu/vitals-biometrics-action-menu.component.tsx +5 -5
- package/src/index.ts +6 -2
- package/src/routes.json +10 -4
- package/src/utils.ts +2 -1
- package/src/vitals/paginated-vitals.component.tsx +3 -1
- package/src/vitals/vitals-overview.component.tsx +2 -1
- package/src/vitals-and-biometrics-header/{vitals-header.component.tsx → vitals-header.extension.tsx} +31 -21
- package/src/vitals-and-biometrics-header/vitals-header.test.tsx +107 -11
- package/src/vitals-biometrics-form/exported-vitals-biometrics-form.workspace.tsx +640 -0
- package/src/vitals-biometrics-form/vitals-biometrics-form.test.tsx +38 -17
- package/src/vitals-biometrics-form/vitals-biometrics-form.workspace.tsx +19 -604
- package/src/vitals-biometrics-form/vitals-biometrics-input.component.tsx +4 -1
- package/dist/5415.js +0 -1
- package/dist/5415.js.map +0 -1
- package/dist/5639.js +0 -1
- package/dist/5639.js.map +0 -1
- package/dist/5810.js +0 -1
- package/dist/5810.js.map +0 -1
- package/dist/6712.js +0 -2
- package/dist/6712.js.map +0 -1
- package/dist/8803.js +0 -1
- package/dist/8803.js.map +0 -1
- /package/dist/{6712.js.LICENSE.txt → 3174.js.LICENSE.txt} +0 -0
|
@@ -16,10 +16,11 @@ interface BiometricsBaseProps {
|
|
|
16
16
|
pageSize: number;
|
|
17
17
|
pageUrl: string;
|
|
18
18
|
patientUuid: string;
|
|
19
|
+
patient: fhir.Patient;
|
|
19
20
|
urlLabel: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
const BiometricsBase: React.FC<BiometricsBaseProps> = ({ patientUuid, pageSize, urlLabel, pageUrl }) => {
|
|
23
|
+
const BiometricsBase: React.FC<BiometricsBaseProps> = ({ patientUuid, patient, pageSize, urlLabel, pageUrl }) => {
|
|
23
24
|
const { t } = useTranslation();
|
|
24
25
|
const displayText = t('biometrics_lower', 'biometrics');
|
|
25
26
|
const headerTitle = t('biometrics', 'Biometrics');
|
|
@@ -30,7 +31,7 @@ const BiometricsBase: React.FC<BiometricsBaseProps> = ({ patientUuid, pageSize,
|
|
|
30
31
|
const { bmiUnit } = config.biometrics;
|
|
31
32
|
const { data: biometrics, isLoading, error, isValidating } = useVitalsAndBiometrics(patientUuid, 'biometrics');
|
|
32
33
|
const { conceptUnits } = useConceptUnits();
|
|
33
|
-
const launchBiometricsForm = useLaunchVitalsAndBiometricsForm();
|
|
34
|
+
const launchBiometricsForm = useLaunchVitalsAndBiometricsForm(patientUuid);
|
|
34
35
|
|
|
35
36
|
const tableHeaders: Array<BiometricsTableHeader> = [
|
|
36
37
|
{
|
|
@@ -130,6 +131,7 @@ const BiometricsBase: React.FC<BiometricsBaseProps> = ({ patientUuid, pageSize,
|
|
|
130
131
|
urlLabel={urlLabel}
|
|
131
132
|
pageUrl={pageUrl}
|
|
132
133
|
tableHeaders={tableHeaders}
|
|
134
|
+
patient={patient}
|
|
133
135
|
/>
|
|
134
136
|
)}
|
|
135
137
|
</div>
|
|
@@ -4,15 +4,24 @@ import BiometricsBase from './biometrics-base.component';
|
|
|
4
4
|
|
|
5
5
|
interface BiometricsProps {
|
|
6
6
|
patientUuid: string;
|
|
7
|
+
patient: fhir.Patient;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
const BiometricsMain: React.FC<BiometricsProps> = ({ patientUuid }) => {
|
|
10
|
+
const BiometricsMain: React.FC<BiometricsProps> = ({ patientUuid, patient }) => {
|
|
10
11
|
const pageSize = 10;
|
|
11
12
|
const { t } = useTranslation();
|
|
12
13
|
const pageUrl: string = `$\{openmrsSpaBase}/patient/${patientUuid}/chart`;
|
|
13
14
|
const urlLabel = t('goToSummary', 'Go to Summary');
|
|
14
15
|
|
|
15
|
-
return
|
|
16
|
+
return (
|
|
17
|
+
<BiometricsBase
|
|
18
|
+
patientUuid={patientUuid}
|
|
19
|
+
patient={patient}
|
|
20
|
+
pageSize={pageSize}
|
|
21
|
+
urlLabel={urlLabel}
|
|
22
|
+
pageUrl={pageUrl}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
16
25
|
};
|
|
17
26
|
|
|
18
27
|
export default BiometricsMain;
|
|
@@ -5,15 +5,24 @@ import BiometricsBase from './biometrics-base.component';
|
|
|
5
5
|
interface BiometricsProps {
|
|
6
6
|
patientUuid: string;
|
|
7
7
|
basePath: string;
|
|
8
|
+
patient: fhir.Patient;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
const BiometricsOverview: React.FC<BiometricsProps> = ({ patientUuid, basePath }) => {
|
|
11
|
+
const BiometricsOverview: React.FC<BiometricsProps> = ({ patientUuid, patient, basePath }) => {
|
|
11
12
|
const { t } = useTranslation();
|
|
12
13
|
const pageSize = 5;
|
|
13
14
|
const pageUrl = `\${openmrsSpaBase}/patient/${patientUuid}/chart/Vitals & Biometrics`;
|
|
14
15
|
const urlLabel = t('seeAll', 'See all');
|
|
15
16
|
|
|
16
|
-
return
|
|
17
|
+
return (
|
|
18
|
+
<BiometricsBase
|
|
19
|
+
patientUuid={patientUuid}
|
|
20
|
+
patient={patient}
|
|
21
|
+
pageSize={pageSize}
|
|
22
|
+
urlLabel={urlLabel}
|
|
23
|
+
pageUrl={pageUrl}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
17
26
|
};
|
|
18
27
|
|
|
19
28
|
export default BiometricsOverview;
|
|
@@ -12,6 +12,9 @@ import BiometricsOverview from './biometrics-overview.component';
|
|
|
12
12
|
const testProps = {
|
|
13
13
|
basePath: patientChartBasePath,
|
|
14
14
|
patientUuid: mockPatient.id,
|
|
15
|
+
patient: mockPatient,
|
|
16
|
+
visitContext: null,
|
|
17
|
+
mutateVisitContext: null,
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
const mockUseConfig = jest.mocked(useConfig<ConfigObject>);
|
|
@@ -23,6 +23,7 @@ interface PaginatedBiometricsProps {
|
|
|
23
23
|
pageUrl: string;
|
|
24
24
|
urlLabel: string;
|
|
25
25
|
tableHeaders: Array<BiometricsTableHeader>;
|
|
26
|
+
patient: fhir.Patient;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
const PaginatedBiometrics: React.FC<PaginatedBiometricsProps> = ({
|
|
@@ -31,6 +32,7 @@ const PaginatedBiometrics: React.FC<PaginatedBiometricsProps> = ({
|
|
|
31
32
|
pageUrl,
|
|
32
33
|
urlLabel,
|
|
33
34
|
tableHeaders,
|
|
35
|
+
patient,
|
|
34
36
|
}) => {
|
|
35
37
|
const isTablet = useLayoutType() === 'tablet';
|
|
36
38
|
|
|
@@ -110,7 +112,7 @@ const PaginatedBiometrics: React.FC<PaginatedBiometricsProps> = ({
|
|
|
110
112
|
<TableCell key={cell.id}>{cell.value?.content ?? cell.value}</TableCell>
|
|
111
113
|
))}
|
|
112
114
|
<TableCell className="cds--table-column-menu" id="actions">
|
|
113
|
-
<VitalsAndBiometricsActionMenu encounterUuid={row.id} />
|
|
115
|
+
<VitalsAndBiometricsActionMenu patient={patient} encounterUuid={row.id} />
|
|
114
116
|
</TableCell>
|
|
115
117
|
</TableRow>
|
|
116
118
|
))}
|
|
@@ -11,7 +11,12 @@ import {
|
|
|
11
11
|
import useSWRImmutable from 'swr/immutable';
|
|
12
12
|
import useSWRInfinite from 'swr/infinite';
|
|
13
13
|
import { type ConfigObject } from '../config-schema';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
assessValue,
|
|
16
|
+
calculateBodyMassIndex,
|
|
17
|
+
interpretBloodPressure,
|
|
18
|
+
mapFhirInterpretationToObservationInterpretation,
|
|
19
|
+
} from './helpers';
|
|
15
20
|
import type {
|
|
16
21
|
FHIRObservationResource,
|
|
17
22
|
FHIRSearchBundleResponse,
|
|
@@ -323,23 +328,13 @@ export function useVitalsAndBiometrics(patientUuid: string, mode: VitalsAndBiome
|
|
|
323
328
|
vitalSigns.diastolic,
|
|
324
329
|
concepts,
|
|
325
330
|
conceptRanges,
|
|
331
|
+
vitalSigns.systolicRenderInterpretation,
|
|
332
|
+
vitalSigns.diastolicRenderInterpretation,
|
|
326
333
|
);
|
|
327
|
-
result.pulseRenderInterpretation =
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
result.temperatureRenderInterpretation = assessValue(
|
|
332
|
-
vitalSigns.temperature,
|
|
333
|
-
getReferenceRangesForConcept(concepts.temperatureUuid, conceptRanges),
|
|
334
|
-
);
|
|
335
|
-
result.spo2RenderInterpretation = assessValue(
|
|
336
|
-
vitalSigns.spo2,
|
|
337
|
-
getReferenceRangesForConcept(concepts.oxygenSaturationUuid, conceptRanges),
|
|
338
|
-
);
|
|
339
|
-
result.respiratoryRateRenderInterpretation = assessValue(
|
|
340
|
-
vitalSigns.respiratoryRate,
|
|
341
|
-
getReferenceRangesForConcept(concepts.respiratoryRateUuid, conceptRanges),
|
|
342
|
-
);
|
|
334
|
+
result.pulseRenderInterpretation = vitalSigns.pulseRenderInterpretation;
|
|
335
|
+
result.temperatureRenderInterpretation = vitalSigns.temperatureRenderInterpretation;
|
|
336
|
+
result.spo2RenderInterpretation = vitalSigns.spo2RenderInterpretation;
|
|
337
|
+
result.respiratoryRateRenderInterpretation = vitalSigns.respiratoryRateRenderInterpretation;
|
|
343
338
|
}
|
|
344
339
|
|
|
345
340
|
return result;
|
|
@@ -472,7 +467,14 @@ function mapVitalsAndBiometrics(resource: FHIRObservationResource): MappedVitals
|
|
|
472
467
|
return {
|
|
473
468
|
code: resource?.code?.coding?.[0]?.code,
|
|
474
469
|
encounterId: extractEncounterUuid(resource.encounter),
|
|
475
|
-
interpretation
|
|
470
|
+
// Use Observation.interpretation from FHIR when available (preferred).
|
|
471
|
+
// Fallback to calculation for backward compatibility: existing observations may not have
|
|
472
|
+
// interpretation set if they were created before interpretation was added, or if reference
|
|
473
|
+
// ranges weren't available at creation time (OpenMRS core only sets interpretation when
|
|
474
|
+
// ObsReferenceRange is present).
|
|
475
|
+
interpretation: resource.interpretation?.[0]?.coding?.[0]?.display
|
|
476
|
+
? mapFhirInterpretationToObservationInterpretation(resource.interpretation?.[0]?.coding?.[0]?.display)
|
|
477
|
+
: assessValue(resource?.valueQuantity?.value, referenceRanges),
|
|
476
478
|
recordedDate: resource?.effectiveDateTime,
|
|
477
479
|
value: resource?.valueQuantity?.value,
|
|
478
480
|
};
|
package/src/common/helpers.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type OpenmrsResource } from '@openmrs/esm-framework';
|
|
2
2
|
import { type ConceptMetadata } from '../common';
|
|
3
|
-
import type { ObsReferenceRanges, ObservationInterpretation } from './types';
|
|
3
|
+
import type { FHIRInterpretation, ObsReferenceRanges, ObservationInterpretation } from './types';
|
|
4
4
|
import { type VitalsBiometricsFormData } from '../vitals-biometrics-form/schema';
|
|
5
5
|
import { type VitalsAndBiometricsFieldValuesMap } from './data.resource';
|
|
6
6
|
|
|
@@ -33,24 +33,53 @@ export function assessValue(value: number | undefined, range?: ObsReferenceRange
|
|
|
33
33
|
return 'normal';
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export function mapFhirInterpretationToObservationInterpretation(
|
|
37
|
+
interpretation: FHIRInterpretation,
|
|
38
|
+
): ObservationInterpretation {
|
|
39
|
+
const normalized = interpretation?.trim();
|
|
40
|
+
switch (normalized) {
|
|
41
|
+
case 'Critically Low':
|
|
42
|
+
return 'critically_low';
|
|
43
|
+
case 'Critically High':
|
|
44
|
+
return 'critically_high';
|
|
45
|
+
case 'High':
|
|
46
|
+
return 'high';
|
|
47
|
+
case 'Low':
|
|
48
|
+
return 'low';
|
|
49
|
+
case 'Normal':
|
|
50
|
+
return 'normal';
|
|
51
|
+
default:
|
|
52
|
+
return 'normal';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
36
56
|
export function interpretBloodPressure(
|
|
37
57
|
systolic: number | undefined,
|
|
38
58
|
diastolic: number | undefined,
|
|
39
59
|
concepts: { systolicBloodPressureUuid?: string; diastolicBloodPressureUuid?: string } | undefined,
|
|
40
60
|
conceptMetadata: Array<ConceptMetadata> | undefined,
|
|
61
|
+
systolicInterpretation?: ObservationInterpretation,
|
|
62
|
+
diastolicInterpretation?: ObservationInterpretation,
|
|
41
63
|
): ObservationInterpretation {
|
|
42
64
|
if (!conceptMetadata) {
|
|
43
65
|
return 'normal';
|
|
44
66
|
}
|
|
45
67
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
// Use interpretation from FHIR Observation when available (preferred).
|
|
69
|
+
// Fallback to calculation for backward compatibility: existing observations may not have
|
|
70
|
+
// interpretation set if they were created before interpretation was added, or if reference
|
|
71
|
+
// ranges weren't available at creation time.
|
|
72
|
+
const systolicAssessment =
|
|
73
|
+
systolicInterpretation ??
|
|
74
|
+
(concepts?.systolicBloodPressureUuid
|
|
75
|
+
? assessValue(systolic, getReferenceRangesForConcept(concepts.systolicBloodPressureUuid, conceptMetadata))
|
|
76
|
+
: 'normal');
|
|
77
|
+
|
|
78
|
+
const diastolicAssessment =
|
|
79
|
+
diastolicInterpretation ??
|
|
80
|
+
(concepts?.diastolicBloodPressureUuid
|
|
81
|
+
? assessValue(diastolic, getReferenceRangesForConcept(concepts.diastolicBloodPressureUuid, conceptMetadata))
|
|
82
|
+
: 'normal');
|
|
54
83
|
|
|
55
84
|
if (systolicAssessment === 'critically_high' || diastolicAssessment === 'critically_high') {
|
|
56
85
|
return 'critically_high';
|
package/src/common/types.ts
CHANGED
|
@@ -20,12 +20,14 @@ export type ObservationInterpretation = 'critically_low' | 'critically_high' | '
|
|
|
20
20
|
|
|
21
21
|
export type MappedVitals = {
|
|
22
22
|
code: string;
|
|
23
|
-
interpretation:
|
|
23
|
+
interpretation: ObservationInterpretation;
|
|
24
24
|
recordedDate: string | Date;
|
|
25
25
|
value: number;
|
|
26
26
|
encounterId: string;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
export type FHIRInterpretation = 'Critically Low' | 'Critically High' | 'High' | 'Low' | 'Normal';
|
|
30
|
+
|
|
29
31
|
export interface FHIRObservationResource {
|
|
30
32
|
resourceType: string;
|
|
31
33
|
id: string;
|
|
@@ -82,13 +84,23 @@ export interface FHIRObservationResource {
|
|
|
82
84
|
hasMember?: Array<{
|
|
83
85
|
reference: string;
|
|
84
86
|
}>;
|
|
87
|
+
interpretation?: Array<{
|
|
88
|
+
coding: Array<{
|
|
89
|
+
code: string;
|
|
90
|
+
display: FHIRInterpretation;
|
|
91
|
+
system: string;
|
|
92
|
+
}>;
|
|
93
|
+
text: string;
|
|
94
|
+
}>;
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
export interface PatientVitalsAndBiometrics {
|
|
88
98
|
id: string;
|
|
89
99
|
date: string;
|
|
90
100
|
systolic?: number;
|
|
101
|
+
systolicRenderInterpretation?: ObservationInterpretation;
|
|
91
102
|
diastolic?: number;
|
|
103
|
+
diastolicRenderInterpretation?: ObservationInterpretation;
|
|
92
104
|
bloodPressureRenderInterpretation?: ObservationInterpretation;
|
|
93
105
|
pulse?: number;
|
|
94
106
|
pulseRenderInterpretation?: ObservationInterpretation;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import React, { useCallback } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { Layer, OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
4
|
-
import {
|
|
5
|
-
import { launchWorkspace, showModal, useLayoutType } from '@openmrs/esm-framework';
|
|
4
|
+
import { launchWorkspace2, showModal, useLayoutType } from '@openmrs/esm-framework';
|
|
6
5
|
import { patientVitalsBiometricsFormWorkspace } from '../../constants';
|
|
7
6
|
import styles from './vitals-biometrics-action-menu.scss';
|
|
8
7
|
|
|
9
8
|
interface VitalsAndBiometricsActionMenuProps {
|
|
9
|
+
patient: fhir.Patient;
|
|
10
10
|
encounterUuid: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export const VitalsAndBiometricsActionMenu = ({ encounterUuid }: VitalsAndBiometricsActionMenuProps) => {
|
|
13
|
+
export const VitalsAndBiometricsActionMenu = ({ encounterUuid, patient }: VitalsAndBiometricsActionMenuProps) => {
|
|
14
14
|
const { t } = useTranslation();
|
|
15
|
-
const patientUuid =
|
|
15
|
+
const patientUuid = patient.id;
|
|
16
16
|
const isTablet = useLayoutType() === 'tablet';
|
|
17
17
|
|
|
18
18
|
const handleLaunchVitalsAndBiometricsForm = useCallback(() => {
|
|
19
|
-
|
|
19
|
+
launchWorkspace2(patientVitalsBiometricsFormWorkspace, {
|
|
20
20
|
workspaceTitle: t('editVitalsAndBiometrics', 'Edit Vitals and Biometrics'),
|
|
21
21
|
editEncounterUuid: encounterUuid,
|
|
22
22
|
formContext: 'editing',
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { configSchema } from './config-schema';
|
|
|
11
11
|
import biometricsDetailedSummaryComponent from './biometrics/biometrics-main.component';
|
|
12
12
|
import biometricsOverviewComponent from './biometrics/biometrics-overview.component';
|
|
13
13
|
import { dashboardMeta } from './dashboard.meta';
|
|
14
|
-
import vitalsHeaderComponent from './vitals-and-biometrics-header/vitals-header.
|
|
14
|
+
import vitalsHeaderComponent from './vitals-and-biometrics-header/vitals-header.extension';
|
|
15
15
|
import vitalsMainComponent from './vitals/vitals-main.component';
|
|
16
16
|
import vitalsSummaryComponent from './vitals/vitals-summary.component';
|
|
17
17
|
|
|
@@ -59,12 +59,16 @@ export const vitalsAndBiometricsDashboardLink =
|
|
|
59
59
|
|
|
60
60
|
export const weightTile = getAsyncLifecycle(() => import('./components/weight-tile/weight-tile.component'), options);
|
|
61
61
|
|
|
62
|
-
// t('recordVitalsAndBiometrics', 'Record Vitals and Biometrics')
|
|
63
62
|
export const vitalsBiometricsFormWorkspace = getAsyncLifecycle(
|
|
64
63
|
() => import('./vitals-biometrics-form/vitals-biometrics-form.workspace'),
|
|
65
64
|
options,
|
|
66
65
|
);
|
|
67
66
|
|
|
67
|
+
export const exportedVitalsBiometricsFormWorkspace = getAsyncLifecycle(
|
|
68
|
+
() => import('./vitals-biometrics-form/exported-vitals-biometrics-form.workspace'),
|
|
69
|
+
options,
|
|
70
|
+
);
|
|
71
|
+
|
|
68
72
|
export const vitalsAndBiometricsDeleteConfirmationModal = getAsyncLifecycle(
|
|
69
73
|
() => import('./components/delete-vitals-biometrics-modal/delete-vitals-biometrics.modal'),
|
|
70
74
|
options,
|
package/src/routes.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.openmrs.org/routes.schema.json",
|
|
3
3
|
"backendDependencies": {
|
|
4
4
|
"fhir2": ">=1.2",
|
|
5
|
-
"webservices.rest": "
|
|
5
|
+
"webservices.rest": ">=2.2.0"
|
|
6
6
|
},
|
|
7
7
|
"extensions": [
|
|
8
8
|
{
|
|
@@ -67,11 +67,17 @@
|
|
|
67
67
|
}
|
|
68
68
|
],
|
|
69
69
|
"pages": [],
|
|
70
|
-
"
|
|
70
|
+
"workspaces2": [
|
|
71
71
|
{
|
|
72
72
|
"name": "patient-vitals-biometrics-form-workspace",
|
|
73
|
-
"
|
|
74
|
-
"
|
|
73
|
+
"component": "vitalsBiometricsFormWorkspace",
|
|
74
|
+
"window": "patient-vitals-biometrics-form-workspace"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"workspaceWindows2": [
|
|
78
|
+
{
|
|
79
|
+
"name": "patient-vitals-biometrics-form-workspace",
|
|
80
|
+
"group": "patient-chart"
|
|
75
81
|
}
|
|
76
82
|
],
|
|
77
83
|
"modals": [
|
package/src/utils.ts
CHANGED
|
@@ -10,10 +10,11 @@ import { invalidateCachedVitalsAndBiometrics } from './common';
|
|
|
10
10
|
* @param currentVisit - The current visit.
|
|
11
11
|
* @param config - The configuration object.
|
|
12
12
|
*/
|
|
13
|
-
export function useLaunchVitalsAndBiometricsForm() {
|
|
13
|
+
export function useLaunchVitalsAndBiometricsForm(patientUuid: string) {
|
|
14
14
|
const config = useConfig<ConfigObject>();
|
|
15
15
|
const { useFormEngine, formName, formUuid } = config.vitals;
|
|
16
16
|
const launchVitalsAndBiometricsForm = useLaunchWorkspaceRequiringVisit(
|
|
17
|
+
patientUuid,
|
|
17
18
|
useFormEngine ? 'patient-form-entry-workspace' : patientVitalsBiometricsFormWorkspace,
|
|
18
19
|
);
|
|
19
20
|
|
|
@@ -24,6 +24,7 @@ interface PaginatedVitalsProps {
|
|
|
24
24
|
tableHeaders: Array<VitalsTableHeader>;
|
|
25
25
|
tableRows: Array<VitalsTableRow>;
|
|
26
26
|
urlLabel: string;
|
|
27
|
+
patient: fhir.Patient;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const PaginatedVitals: React.FC<PaginatedVitalsProps> = ({
|
|
@@ -33,6 +34,7 @@ const PaginatedVitals: React.FC<PaginatedVitalsProps> = ({
|
|
|
33
34
|
tableHeaders,
|
|
34
35
|
tableRows,
|
|
35
36
|
urlLabel,
|
|
37
|
+
patient,
|
|
36
38
|
}) => {
|
|
37
39
|
const { t } = useTranslation();
|
|
38
40
|
const isTablet = useLayoutType() === 'tablet';
|
|
@@ -132,7 +134,7 @@ const PaginatedVitals: React.FC<PaginatedVitalsProps> = ({
|
|
|
132
134
|
);
|
|
133
135
|
})}
|
|
134
136
|
<TableCell className="cds--table-column-menu" id="actions">
|
|
135
|
-
<VitalsAndBiometricsActionMenu encounterUuid={row.id} />
|
|
137
|
+
<VitalsAndBiometricsActionMenu patient={patient} encounterUuid={row.id} />
|
|
136
138
|
</TableCell>
|
|
137
139
|
</TableRow>
|
|
138
140
|
))}
|
|
@@ -40,7 +40,7 @@ const VitalsOverview: React.FC<VitalsOverviewProps> = ({ patientUuid, patient, p
|
|
|
40
40
|
const isTablet = useLayoutType() === 'tablet';
|
|
41
41
|
const [isPrinting, setIsPrinting] = useState(false);
|
|
42
42
|
const contentToPrintRef = useRef(null);
|
|
43
|
-
const launchVitalsBiometricsForm = useLaunchVitalsAndBiometricsForm();
|
|
43
|
+
const launchVitalsBiometricsForm = useLaunchVitalsAndBiometricsForm(patientUuid);
|
|
44
44
|
|
|
45
45
|
const { excludePatientIdentifierCodeTypes } = useConfig();
|
|
46
46
|
const { data: vitals, error, isLoading, isValidating } = useVitalsAndBiometrics(patientUuid);
|
|
@@ -243,6 +243,7 @@ const VitalsOverview: React.FC<VitalsOverviewProps> = ({ patientUuid, patient, p
|
|
|
243
243
|
tableHeaders={tableHeaders}
|
|
244
244
|
tableRows={tableRows}
|
|
245
245
|
urlLabel={urlLabel}
|
|
246
|
+
patient={patient}
|
|
246
247
|
/>
|
|
247
248
|
</div>
|
|
248
249
|
)}
|
package/src/vitals-and-biometrics-header/{vitals-header.component.tsx → vitals-header.extension.tsx}
RENAMED
|
@@ -8,8 +8,7 @@ dayjs.extend(duration);
|
|
|
8
8
|
import { Trans, useTranslation } from 'react-i18next';
|
|
9
9
|
import { Button, InlineLoading, Tag } from '@carbon/react';
|
|
10
10
|
import { ArrowRight } from '@carbon/react/icons';
|
|
11
|
-
import { ConfigurableLink, formatDate, parseDate, useConfig, useWorkspaces } from '@openmrs/esm-framework';
|
|
12
|
-
import { useVisitOrOfflineVisit } from '@openmrs/esm-patient-common-lib';
|
|
11
|
+
import { ConfigurableLink, formatDate, parseDate, useConfig, useVisit, useWorkspaces } from '@openmrs/esm-framework';
|
|
13
12
|
import {
|
|
14
13
|
assessValue,
|
|
15
14
|
getReferenceRangesForConcept,
|
|
@@ -41,11 +40,11 @@ const VitalsHeader: React.FC<VitalsHeaderProps> = ({ patientUuid, hideLinks = fa
|
|
|
41
40
|
const latestVitals = vitals?.[0];
|
|
42
41
|
const [showDetailsPanel, setShowDetailsPanel] = useState(false);
|
|
43
42
|
const toggleDetailsPanel = () => setShowDetailsPanel(!showDetailsPanel);
|
|
44
|
-
const {
|
|
43
|
+
const { activeVisit } = useVisit(patientUuid);
|
|
45
44
|
const { workspaces } = useWorkspaces();
|
|
46
45
|
|
|
47
46
|
const isWorkspaceOpen = useCallback(() => Boolean(workspaces?.length), [workspaces]);
|
|
48
|
-
const launchForm = useLaunchVitalsAndBiometricsForm();
|
|
47
|
+
const launchForm = useLaunchVitalsAndBiometricsForm(patientUuid);
|
|
49
48
|
|
|
50
49
|
const launchVitalsAndBiometricsForm = useCallback(
|
|
51
50
|
(event: React.MouseEvent<HTMLButtonElement>) => {
|
|
@@ -62,7 +61,7 @@ const VitalsHeader: React.FC<VitalsHeaderProps> = ({ patientUuid, hideLinks = fa
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
if (latestVitals && Object.keys(latestVitals)?.length && conceptRanges?.length) {
|
|
65
|
-
const hasActiveVisit = Boolean(
|
|
64
|
+
const hasActiveVisit = Boolean(activeVisit?.uuid);
|
|
66
65
|
const now = dayjs();
|
|
67
66
|
const vitalsTakenTimeAgo = dayjs.duration(now.diff(latestVitals?.date));
|
|
68
67
|
const vitalsOverdueThresholdHours = config.vitals.vitalsOverdueThresholdHours;
|
|
@@ -161,25 +160,30 @@ const VitalsHeader: React.FC<VitalsHeaderProps> = ({ patientUuid, hideLinks = fa
|
|
|
161
160
|
latestVitals?.diastolic,
|
|
162
161
|
config?.concepts,
|
|
163
162
|
conceptRanges,
|
|
163
|
+
latestVitals?.systolicRenderInterpretation,
|
|
164
|
+
latestVitals?.diastolicRenderInterpretation,
|
|
164
165
|
)}
|
|
165
166
|
unitName={t('bp', 'BP')}
|
|
166
167
|
unitSymbol={(latestVitals?.systolic && conceptUnits.get(config.concepts.systolicBloodPressureUuid)) ?? ''}
|
|
167
168
|
value={`${latestVitals?.systolic ?? '--'} / ${latestVitals?.diastolic ?? '--'}`}
|
|
168
169
|
/>
|
|
169
170
|
<VitalsHeaderItem
|
|
170
|
-
interpretation={
|
|
171
|
-
latestVitals?.
|
|
172
|
-
getReferenceRangesForConcept(config.concepts.pulseUuid, conceptRanges)
|
|
173
|
-
|
|
171
|
+
interpretation={
|
|
172
|
+
latestVitals?.pulseRenderInterpretation ??
|
|
173
|
+
assessValue(latestVitals?.pulse, getReferenceRangesForConcept(config.concepts.pulseUuid, conceptRanges))
|
|
174
|
+
}
|
|
174
175
|
unitName={t('heartRate', 'Heart rate')}
|
|
175
176
|
unitSymbol={(latestVitals?.pulse && conceptUnits.get(config.concepts.pulseUuid)) ?? ''}
|
|
176
177
|
value={latestVitals?.pulse ?? '--'}
|
|
177
178
|
/>
|
|
178
179
|
<VitalsHeaderItem
|
|
179
|
-
interpretation={
|
|
180
|
-
latestVitals?.
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
interpretation={
|
|
181
|
+
latestVitals?.respiratoryRateRenderInterpretation ??
|
|
182
|
+
assessValue(
|
|
183
|
+
latestVitals?.respiratoryRate,
|
|
184
|
+
getReferenceRangesForConcept(config.concepts.respiratoryRateUuid, conceptRanges),
|
|
185
|
+
)
|
|
186
|
+
}
|
|
183
187
|
unitName={t('respiratoryRate', 'R. rate')}
|
|
184
188
|
unitSymbol={
|
|
185
189
|
(latestVitals?.respiratoryRate && conceptUnits.get(config.concepts.respiratoryRateUuid)) ?? ''
|
|
@@ -187,19 +191,25 @@ const VitalsHeader: React.FC<VitalsHeaderProps> = ({ patientUuid, hideLinks = fa
|
|
|
187
191
|
value={latestVitals?.respiratoryRate ?? '--'}
|
|
188
192
|
/>
|
|
189
193
|
<VitalsHeaderItem
|
|
190
|
-
interpretation={
|
|
191
|
-
latestVitals?.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
interpretation={
|
|
195
|
+
latestVitals?.spo2RenderInterpretation ??
|
|
196
|
+
assessValue(
|
|
197
|
+
latestVitals?.spo2,
|
|
198
|
+
getReferenceRangesForConcept(config.concepts.oxygenSaturationUuid, conceptRanges),
|
|
199
|
+
)
|
|
200
|
+
}
|
|
194
201
|
unitName={t('spo2', 'SpO2')}
|
|
195
202
|
unitSymbol={(latestVitals?.spo2 && conceptUnits.get(config.concepts.oxygenSaturationUuid)) ?? ''}
|
|
196
203
|
value={latestVitals?.spo2 ?? '--'}
|
|
197
204
|
/>
|
|
198
205
|
<VitalsHeaderItem
|
|
199
|
-
interpretation={
|
|
200
|
-
latestVitals?.
|
|
201
|
-
|
|
202
|
-
|
|
206
|
+
interpretation={
|
|
207
|
+
latestVitals?.temperatureRenderInterpretation ??
|
|
208
|
+
assessValue(
|
|
209
|
+
latestVitals?.temperature,
|
|
210
|
+
getReferenceRangesForConcept(config.concepts.temperatureUuid, conceptRanges),
|
|
211
|
+
)
|
|
212
|
+
}
|
|
203
213
|
unitName={t('temperatureAbbreviated', 'Temp')}
|
|
204
214
|
unitSymbol={(latestVitals?.temperature && conceptUnits.get(config.concepts.temperatureUuid)) ?? ''}
|
|
205
215
|
value={latestVitals?.temperature ?? '--'}
|