From 889bb4ef6423a7c091c54db54d02c091b3393f11 Mon Sep 17 00:00:00 2001 From: hadijahkyampeire Date: Sun, 22 Oct 2023 03:17:29 +0300 Subject: [PATCH 01/17] implement the vitals input states --- .../vitals-biometrics-form.component.tsx | 30 ++++++++ .../vitals-biometrics-input.component.tsx | 49 ++++++++++--- .../vitals-biometrics-input.scss | 73 ++++++++++++++++++- .../src/vitals/vitals.resource.tsx | 4 +- 4 files changed, 144 insertions(+), 12 deletions(-) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx index 33d1e0c11d..acee027e27 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx @@ -26,6 +26,8 @@ import { } from './vitals-biometrics-form.utils'; import { savePatientVitals, useVitals } from '../vitals.resource'; import VitalsBiometricInput from './vitals-biometrics-input.component'; +import { assessValue, getReferenceRangesForConcept } from '../vitals.resource'; + import styles from './vitals-biometrics-form.scss'; const vitalsBiometricsFormSchema = z @@ -232,6 +234,10 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, = ({ patientUuid, = ({ patientUuid, = ({ patientUuid, = ({ patientUuid, = ({ patientUuid, = ({ patientUuid, = ({ @@ -54,10 +55,24 @@ const VitalsBiometricInput: React.FC = ({ muacColorCode, useMuacColors, isWithinNormalRange = true, + interpretation, }) => { const { t } = useTranslation(); const isTablet = useLayoutType() === 'tablet'; const [invalid, setInvalid] = useState(false); + const [isFocused, setFocused] = useState(false); + + const handleFocus = () => { + setFocused(true); + }; + + const handleBlur = () => { + setFocused(false); + }; + + const flaggedCritical = + interpretation && ['critically_low', 'critically_high', 'high', 'low'].includes(interpretation); + const flaggedAbnormal = interpretation && interpretation !== 'normal'; function checkValidity(value, onChange) { setInvalid(!(Number(value) || value === '')); @@ -68,12 +83,29 @@ const VitalsBiometricInput: React.FC = ({ } return ( -
-

{title}

+
+
+

{title}

+ {flaggedAbnormal ? ( +
+ {interpretation === 'high' ? : null} + {interpretation === 'critically_high' ? : null} + {interpretation === 'low' ? : null} + {interpretation === 'critically_low' ? : null} +
+ ) : null} +
@@ -87,9 +119,7 @@ const VitalsBiometricInput: React.FC = ({ render={({ field: { onBlur, onChange, value, ref } }) => ( = ({ max={val.max} min={val.min} name={val.name} - onBlur={onBlur} + onBlur={handleBlur} + onFocus={handleFocus} onChange={(e) => checkValidity(e.target.value, onChange)} style={{ ...textFieldStyles }} ref={ref} diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.scss b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.scss index 4d0e7dbd3c..6d7b6550b7 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.scss +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.scss @@ -57,6 +57,14 @@ padding: spacing.$spacing-03; color: $text-02; background-color: $ui-01; + + &.focused { + border: 2px solid colors.$blue-60; + + input { + outline: none !important; + } + } } :global(.omrs-breakpoint-lt-desktop) .textInputContainer { @@ -125,4 +133,67 @@ .layer { width: 100%; -} \ No newline at end of file +} + +.labelAndIcons { + display: flex; + justify-content: space-between; + align-items: center; +} + +.critical-value { + background-color: colors.$red-10; + .textInput { + svg { + display: none; + } + + input { + background-color: colors.$red-10; + } + + input[data-invalid]:not(:focus) { + outline: none; + } + + } +} + +.critically-low, .critically-high, .low, .high { + &::after { + @include type.type-style('heading-compact-01'); + color: $text-02; + } +} + +.low::after { + content: " ↓"; +} + +.critically-low::after { + content: " ↓↓"; +} + +.high::after { + content: " ↑"; +} + +.critically-high::after { + content: " ↑↑"; +} + + +.inputInTabletView { + &.isCriticalInput { + .textInputContainer { + background-color: colors.$red-20; + + .textInput { + input { + background-color: colors.$red-20; + outline: none; + } + } + } + } +} diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals.resource.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals.resource.tsx index db4b27167c..8884f3c038 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals.resource.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals.resource.tsx @@ -244,7 +244,7 @@ export function editPatientVitals( } export function assessValue(value: number, range: ObsMetaInfo): ObservationInterpretation { - if (range?.hiCritical && value >= range.hiCritical) { + if ((range?.hiCritical && value >= range.hiCritical) || (range?.hiAbsolute && value >= range.hiAbsolute)) { return 'critically_high'; } @@ -252,7 +252,7 @@ export function assessValue(value: number, range: ObsMetaInfo): ObservationInter return 'high'; } - if (range?.lowCritical && value <= range.lowCritical) { + if ((range?.lowCritical && value <= range.lowCritical) || (range?.lowAbsolute && value <= range.lowAbsolute)) { return 'critically_low'; } From 588af903779b99e542117769d9aa5897af929e96 Mon Sep 17 00:00:00 2001 From: hadijahkyampeire Date: Mon, 23 Oct 2023 00:23:06 +0300 Subject: [PATCH 02/17] add mock functions to the tests --- .../vitals-biometrics-form/vitals-biometrics-form.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.test.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.test.tsx index 1bfc1a5881..f6af4751d2 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.test.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.test.tsx @@ -61,6 +61,8 @@ jest.mock('@openmrs/esm-patient-common-lib', () => { jest.mock('../vitals.resource', () => ({ savePatientVitals: jest.fn(), + assessValue: jest.fn(), + getReferenceRangesForConcept: jest.fn(), useVitals: jest.fn().mockImplementation(() => ({ mutate: jest.fn, })), From 3bebb9e0b91b4c6a8463c00986fa4ebb3e666138 Mon Sep 17 00:00:00 2001 From: hadijahkyampeire Date: Mon, 23 Oct 2023 15:37:36 +0300 Subject: [PATCH 03/17] add input placeholders as per zero height docs --- .../vitals-biometrics-form.utils.ts | 4 ++++ .../vitals-biometrics-input.component.tsx | 2 ++ .../src/vitals/vitals.resource.tsx | 20 +++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts index 1b66e0d6ad..18dd26ef41 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts @@ -19,6 +19,10 @@ export function isValueWithinReferenceRange( } const concept = conceptMetadata.find((c) => c.uuid === conceptUuid); + // Don't validate inputs if the value is empty or the concept is not found + if (value === undefined || value === '' || concept === undefined) { + return true; + } return isNumber(concept?.lowAbsolute) && isNumber(concept?.hiAbsolute) ? Number(value) >= Number(concept.lowAbsolute) && Number(value) <= Number(concept.hiAbsolute) : true; diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx index 69e0842324..d5cccf27fc 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx @@ -4,6 +4,7 @@ import { FormLabel, Layer, NumberInput, TextArea } from '@carbon/react'; import { useTranslation } from 'react-i18next'; import { useLayoutType } from '@openmrs/esm-framework'; import { VitalsBiometricsFormData } from './vitals-biometrics-form.component'; +import { generatePlaceholder } from '../vitals.resource'; import styles from './vitals-biometrics-input.scss'; type Id = @@ -119,6 +120,7 @@ const VitalsBiometricInput: React.FC = ({ render={({ field: { onBlur, onChange, value, ref } }) => ( Date: Mon, 23 Oct 2023 15:40:36 +0300 Subject: [PATCH 04/17] fix tests --- .../vitals-biometrics-form/vitals-biometrics-form.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.test.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.test.tsx index f6af4751d2..0c068a1ba7 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.test.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.test.tsx @@ -63,6 +63,7 @@ jest.mock('../vitals.resource', () => ({ savePatientVitals: jest.fn(), assessValue: jest.fn(), getReferenceRangesForConcept: jest.fn(), + generatePlaceholder: jest.fn(), useVitals: jest.fn().mockImplementation(() => ({ mutate: jest.fn, })), From a530ad27d374580832afa68361c77bff3d255bd3 Mon Sep 17 00:00:00 2001 From: hadijahkyampeire Date: Wed, 25 Oct 2023 00:10:06 +0300 Subject: [PATCH 05/17] PR feedback --- .../vitals-biometrics-form/vitals-biometrics-form.utils.ts | 7 ++----- .../vitals-biometrics-input.component.tsx | 5 +---- .../esm-patient-vitals-app/src/vitals/vitals.resource.tsx | 4 ++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts index 18dd26ef41..7c54a12f36 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts @@ -14,15 +14,12 @@ export function isValueWithinReferenceRange( conceptUuid: string, value: string | number, ) { - if (value === undefined || value === '') { - return true; - } - const concept = conceptMetadata.find((c) => c.uuid === conceptUuid); - // Don't validate inputs if the value is empty or the concept is not found + if (value === undefined || value === '' || concept === undefined) { return true; } + return isNumber(concept?.lowAbsolute) && isNumber(concept?.hiAbsolute) ? Number(value) >= Number(concept.lowAbsolute) && Number(value) <= Number(concept.hiAbsolute) : true; diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx index d5cccf27fc..387470a45d 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx @@ -94,10 +94,7 @@ const VitalsBiometricInput: React.FC = ({

{title}

{flaggedAbnormal ? (
- {interpretation === 'high' ? : null} - {interpretation === 'critically_high' ? : null} - {interpretation === 'low' ? : null} - {interpretation === 'critically_low' ? : null} +
) : null}
diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals.resource.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals.resource.tsx index 3308463458..73108bc6d4 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals.resource.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals.resource.tsx @@ -244,7 +244,7 @@ export function editPatientVitals( } export function assessValue(value: number, range: ObsMetaInfo): ObservationInterpretation { - if ((range?.hiCritical && value >= range.hiCritical) || (range?.hiAbsolute && value >= range.hiAbsolute)) { + if (range?.hiCritical && value >= range.hiCritical) { return 'critically_high'; } @@ -252,7 +252,7 @@ export function assessValue(value: number, range: ObsMetaInfo): ObservationInter return 'high'; } - if ((range?.lowCritical && value <= range.lowCritical) || (range?.lowAbsolute && value <= range.lowAbsolute)) { + if (range?.lowCritical && value <= range.lowCritical) { return 'critically_low'; } From d662ceb89e21c7e3fdd4d5d7bbdfe3fcc2c369f8 Mon Sep 17 00:00:00 2001 From: hadijahkyampeire Date: Wed, 25 Oct 2023 00:19:39 +0300 Subject: [PATCH 06/17] Remove duplicate variable --- .../vitals-biometrics-input.component.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx index 387470a45d..a8b4826648 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx @@ -73,7 +73,6 @@ const VitalsBiometricInput: React.FC = ({ const flaggedCritical = interpretation && ['critically_low', 'critically_high', 'high', 'low'].includes(interpretation); - const flaggedAbnormal = interpretation && interpretation !== 'normal'; function checkValidity(value, onChange) { setInvalid(!(Number(value) || value === '')); @@ -92,7 +91,7 @@ const VitalsBiometricInput: React.FC = ({ >

{title}

- {flaggedAbnormal ? ( + {flaggedCritical ? (
@@ -101,9 +100,7 @@ const VitalsBiometricInput: React.FC = ({
@@ -118,7 +115,7 @@ const VitalsBiometricInput: React.FC = ({ Date: Wed, 25 Oct 2023 02:18:11 +0300 Subject: [PATCH 07/17] fix the error state --- .../vitals-biometrics-form.component.tsx | 9 +++++++ .../vitals-biometrics-input.component.tsx | 19 +++++++++++--- .../vitals-biometrics-input.scss | 26 +++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx index acee027e27..01b8c04ea7 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx @@ -73,6 +73,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, const [muacColorCode, setMuacColorCode] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [showErrorNotification, setShowErrorNotification] = useState(false); + const [showErrorMessage, setShowErrorMessage] = useState(false); const { control, handleSubmit, getValues, watch, setValue } = useForm({ mode: 'all', @@ -128,6 +129,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, ); const savePatientVitalsAndBiometrics = (data: VitalsBiometricsFormData) => { + setShowErrorMessage(true); data?.computedBodyMassIndex && delete data.computedBodyMassIndex; const patientVitalAndBiometrics = data; @@ -149,6 +151,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, } if (isFieldValid) { setIsSubmitting(true); + setShowErrorMessage(false); const ac = new AbortController(); savePatientVitals( config.vitals.encounterTypeUuid, @@ -315,6 +318,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, config.concepts['respiratoryRateUuid'], respiratoryRate, )} + showErrorMessage={showErrorMessage} /> @@ -340,6 +344,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, config.concepts['oxygenSaturationUuid'], oxygenSaturation, )} + showErrorMessage={showErrorMessage} /> @@ -366,6 +371,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, config.concepts['temperatureUuid'], temperature, )} + showErrorMessage={showErrorMessage} /> @@ -416,6 +422,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, config.concepts['weightUuid'], weight, )} + showErrorMessage={showErrorMessage} /> @@ -441,6 +448,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, config.concepts['heightUuid'], height, )} + showErrorMessage={showErrorMessage} /> @@ -479,6 +487,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, config.concepts['midUpperArmCircumferenceUuid'], midUpperArmCircumference, )} + showErrorMessage={showErrorMessage} /> diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx index a8b4826648..c57039e4b3 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx @@ -1,6 +1,7 @@ import React, { Fragment, useState } from 'react'; import { Control, Controller } from 'react-hook-form'; import { FormLabel, Layer, NumberInput, TextArea } from '@carbon/react'; +import { Warning } from '@carbon/react/icons'; import { useTranslation } from 'react-i18next'; import { useLayoutType } from '@openmrs/esm-framework'; import { VitalsBiometricsFormData } from './vitals-biometrics-form.component'; @@ -42,6 +43,7 @@ interface VitalsBiometricInputProps { disabled?: boolean; isWithinNormalRange?: boolean; interpretation?: string; + showErrorMessage?: boolean; } const VitalsBiometricInput: React.FC = ({ @@ -57,6 +59,7 @@ const VitalsBiometricInput: React.FC = ({ useMuacColors, isWithinNormalRange = true, interpretation, + showErrorMessage, }) => { const { t } = useTranslation(); const isTablet = useLayoutType() === 'tablet'; @@ -82,6 +85,10 @@ const VitalsBiometricInput: React.FC = ({ } } + const isInvalidInput = !isWithinNormalRange || invalid; + const showInvalidInputError = showErrorMessage && isInvalidInput; + const errorMessageClass = showInvalidInputError ? styles.invalidInput : ''; + return (
= ({
+ ) : showInvalidInputError ? ( +
+ +
) : null}
@@ -168,11 +179,11 @@ const VitalsBiometricInput: React.FC = ({

{unitSymbol}

- {(!isWithinNormalRange || invalid) && ( + {showInvalidInputError ? ( - {t('numericInputError', 'Must be a number with in acceptable ranges')} + {t('numericInputError', `Only numbers ${textFields[0].min} - ${textFields[0].max} permitted`)} - )} + ) : null}
); }; diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.scss b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.scss index 6d7b6550b7..0a7b6bedb7 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.scss +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.scss @@ -64,6 +64,14 @@ input { outline: none !important; } + + input[data-invalid]:not(:focus) { + outline: none; + } + + svg { + display: none; + } } } @@ -94,6 +102,15 @@ } } +.textInput { + svg { + display: none; + } + + input[data-invalid]:not(:focus) { + outline: none !important; + } +} .textarea { border: none; @extend .bodyLong01; @@ -140,6 +157,15 @@ justify-content: space-between; align-items: center; } +.invalidInput { + border: 2px solid $danger; +} + +.invalidInputIcon { + svg { + color: $danger; + } +} .critical-value { background-color: colors.$red-10; From 6727447e612be62f4ad852505f7482e426065993 Mon Sep 17 00:00:00 2001 From: hadijahkyampeire Date: Wed, 25 Oct 2023 02:30:26 +0300 Subject: [PATCH 08/17] Fix error message and translations --- .../vitals-biometrics-input.component.tsx | 5 ++++- packages/esm-patient-vitals-app/translations/en.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx index c57039e4b3..11f962b81a 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx @@ -181,7 +181,10 @@ const VitalsBiometricInput: React.FC = ({
{showInvalidInputError ? ( - {t('numericInputError', `Only numbers ${textFields[0].min} - ${textFields[0].max} permitted`)} + {t('numericInputError', `Only numbers "{min}" - "{max}" permitted`, { + min: textFields[0].min, + max: textFields[0].max, + })} ) : null}
diff --git a/packages/esm-patient-vitals-app/translations/en.json b/packages/esm-patient-vitals-app/translations/en.json index 0a0f707d69..c10781d297 100644 --- a/packages/esm-patient-vitals-app/translations/en.json +++ b/packages/esm-patient-vitals-app/translations/en.json @@ -19,7 +19,7 @@ "muac": "MUAC", "noDataRecorded": "No data has been recorded for this patient", "notes": "Notes", - "numericInputError": "Must be a number within acceptable ranges", + "numericInputError": "Only numbers \"{min}\" - \"{max}\" permitted", "other": "Other", "overdue": "Overdue", "oxygenSaturation": "Oxygen Saturation", From df473690a5c39d500938f2a49a936324f94617f1 Mon Sep 17 00:00:00 2001 From: Dennis Kigen Date: Mon, 30 Oct 2023 23:52:52 +0300 Subject: [PATCH 09/17] Fix blood pressure interpretation --- .../vitals-biometrics-form.component.tsx | 30 +++++++++---------- .../vitals-biometrics-form.test.tsx | 3 +- .../vitals-biometrics-form.utils.ts | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx index 01b8c04ea7..cb1ab97d1a 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx @@ -3,7 +3,7 @@ import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Button, ButtonSet, Column, Form, Row, Stack, InlineNotification } from '@carbon/react'; +import { Button, ButtonSet, Column, Form, InlineNotification, Row, Stack } from '@carbon/react'; import { age, createErrorHandler, @@ -20,14 +20,18 @@ import { DefaultWorkspaceProps, useVitalsConceptMetadata } from '@openmrs/esm-pa import type { ConfigObject } from '../../config-schema'; import { calculateBodyMassIndex, - isValueWithinReferenceRange, extractNumbers, - getColorCode, + getMuacColorCode, + isValueWithinReferenceRange, } from './vitals-biometrics-form.utils'; -import { savePatientVitals, useVitals } from '../vitals.resource'; +import { + assessValue, + getReferenceRangesForConcept, + interpretBloodPressure, + savePatientVitals, + useVitals, +} from '../vitals.resource'; import VitalsBiometricInput from './vitals-biometrics-input.component'; -import { assessValue, getReferenceRangesForConcept } from '../vitals.resource'; - import styles from './vitals-biometrics-form.scss'; const vitalsBiometricsFormSchema = z @@ -69,7 +73,6 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, const { currentVisit } = useVisit(patientUuid); const { mutate } = useVitals(patientUuid); const { data: conceptUnits, conceptMetadata, conceptRanges } = useVitalsConceptMetadata(); - const [bodyMassIndex, setBodyMassIndex] = useState(); const [muacColorCode, setMuacColorCode] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [showErrorNotification, setShowErrorNotification] = useState(false); @@ -93,13 +96,8 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, const weight = watch('weight'); const height = watch('height'); - const isBodyMassIndexValueAbnormal = (bmi: number) => { - if (!bmi) return false; - return bmi < 18.5 || bmi > 24.9; - }; - useEffect(() => { - getColorCode(extractNumbers(age(patient.patient?.birthDate)), midUpperArmCircumference, setMuacColorCode); + getMuacColorCode(extractNumbers(age(patient.patient?.birthDate)), midUpperArmCircumference, setMuacColorCode); }, [watch, patient.patient?.birthDate, midUpperArmCircumference]); const concepts = useMemo( @@ -237,9 +235,11 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, { }); jest.mock('../vitals.resource', () => ({ - savePatientVitals: jest.fn(), assessValue: jest.fn(), getReferenceRangesForConcept: jest.fn(), generatePlaceholder: jest.fn(), + interpretBloodPressure: jest.fn(), + savePatientVitals: jest.fn(), useVitals: jest.fn().mockImplementation(() => ({ mutate: jest.fn, })), diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts index 7c54a12f36..da1d123124 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.utils.ts @@ -35,7 +35,7 @@ export function extractNumbers(str: string) { return parseInt(match[0], 10); } -export function getColorCode(age: number, muac: number, setColorCode: (color) => void) { +export function getMuacColorCode(age: number, muac: number, setColorCode: (color) => void) { switch (true) { // children 5 years and below with a muac equal to 14 case age <= 5 && muac <= 11.5 && muac > 0: From 5f477dbd61e735f14cd54766ebb759049d942f1e Mon Sep 17 00:00:00 2001 From: Dennis Kigen Date: Tue, 31 Oct 2023 10:39:22 +0300 Subject: [PATCH 10/17] Change validation error message and placeholder color --- .../vitals-biometrics-form.component.tsx | 20 ++- .../vitals-biometrics-form.scss | 4 + .../vitals-biometrics-input.component.tsx | 159 +++++++++--------- .../vitals-biometrics-input.scss | 16 +- .../translations/am.json | 2 +- .../translations/ar.json | 2 +- .../translations/en.json | 2 +- .../translations/es.json | 2 +- .../translations/fr.json | 2 +- .../translations/he.json | 2 +- 10 files changed, 117 insertions(+), 94 deletions(-) diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx index cb1ab97d1a..fdf76ada24 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.component.tsx @@ -54,7 +54,7 @@ const vitalsBiometricsFormSchema = z return Object.values(fields).some((value) => Boolean(value)); }, { - message: 'Atleast one fields is required', + message: 'Please fill at least one field', path: ['oneFieldRequired'], }, ); @@ -77,6 +77,7 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, const [isSubmitting, setIsSubmitting] = useState(false); const [showErrorNotification, setShowErrorNotification] = useState(false); const [showErrorMessage, setShowErrorMessage] = useState(false); + const [hasInvalidVitals, setHasInvalidVitals] = useState(false); const { control, handleSubmit, getValues, watch, setValue } = useForm({ mode: 'all', @@ -137,13 +138,8 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, isValueWithinReferenceRange(conceptMetadata, config.concepts[key + 'Uuid'], patientVitalAndBiometrics[key]) == false ) { + setHasInvalidVitals(true); isFieldValid = false; - showNotification({ - title: t('vitalsAndBiometricsSaveError', 'Error saving vitals and biometrics'), - kind: 'error', - critical: true, - description: t('checkForValidity', 'Some of the values entered are invalid'), - }); break; } } @@ -502,6 +498,16 @@ const VitalsAndBiometricForms: React.FC = ({ patientUuid, /> )} + {hasInvalidVitals && ( + + + + )}
diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.scss b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.scss index 49f947c0cb..57d08f36a6 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.scss +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-form.scss @@ -66,3 +66,7 @@ .noteInput { width: 100%; } + +.errorNotification { + margin: 0.5rem 0; +} diff --git a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx index 11f962b81a..7afd9854e8 100644 --- a/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx +++ b/packages/esm-patient-vitals-app/src/vitals/vitals-biometrics-form/vitals-biometrics-input.component.tsx @@ -90,104 +90,107 @@ const VitalsBiometricInput: React.FC = ({ const errorMessageClass = showInvalidInputError ? styles.invalidInput : ''; return ( -
-
-

{title}

- {flaggedCritical ? ( -
- -
- ) : showInvalidInputError ? ( -
- -
- ) : null} -
+ <>
-
- {textFields.map((val, i) => { - return val.type === 'number' ? ( - - +
+

{title}

+ {flaggedCritical ? ( +
+ +
+ ) : showInvalidInputError ? ( +
+ +
+ ) : null} +
+
+
+ {textFields.map((val, i) => { + return val.type === 'number' ? ( + + + ( + checkValidity(e.target.value, onChange)} + style={{ ...textFieldStyles }} + ref={ref} + title={val.name} + type={val.type} + value={value} + /> + )} + /> + + {val?.separator} + + ) : ( + ( - checkValidity(e.target.value, onChange)} +