Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8a29e59
BN-71 | Add. DiagnosisForm
rahu1ramesh May 20, 2025
c88ecea
BN-71 | Add. SelectedItem Component
rahu1ramesh May 21, 2025
1c5796e
BN-71 | Add. BoxWHeader Component
rahu1ramesh May 21, 2025
50d7d5a
BN-71 | Add. SelectedDiagnosis Component
rahu1ramesh May 22, 2025
2eda88b
BN-71 | Add utility functions to create FHIR Bundles (#22)
mohan-13 May 23, 2025
867e15a
BN-71 | Fix. Consultation Pad Position
rahu1ramesh May 24, 2025
edc068d
BN-71 | Refactor. SelectedDiagnosisItem To New File
rahu1ramesh May 24, 2025
bd2cec6
BN-71 | Add. DiagnosesForm Component
rahu1ramesh May 24, 2025
b50bc35
BN-71 | Add. useDebounce Hook
rahu1ramesh May 24, 2025
0b65a44
BN-71 | Add. useConceptSearch Hook
rahu1ramesh May 24, 2025
a05c2ab
BN-71 | Add. conceptService
rahu1ramesh May 24, 2025
246a3de
BN-71 | Add. Integrate DiagnosisForm into Consultation Pad
rahu1ramesh May 25, 2025
c9f4b6c
BN-71 | Fix. SelectedItem Children Width
rahu1ramesh May 25, 2025
4f5f093
BN-71 | Add. i18n Support For Certainity Concepts
rahu1ramesh May 25, 2025
9883cee
BN-71 | Add. i18n Support For Empty Result
rahu1ramesh May 25, 2025
53f805a
BN-71 | Fix. Remove Unintended Yarn Lock Change
rahu1ramesh May 25, 2025
cf0be3c
BN-71 | Fix. Broken Keyboard Navigation Support
rahu1ramesh May 25, 2025
398c4e2
Merge branch 'main' into BN-71
rahu1ramesh May 25, 2025
cac38b5
BN-71 | Fix. Combo box keyboard navigation for selection by upgrading…
mohan-13 May 26, 2025
1ae26f0
BN-71 | Add. FHIR Utility for creating Condition Resource for Encount…
mohan-13 May 26, 2025
594404e
BN-71 | Fix. Path Aliases
rahu1ramesh May 25, 2025
10f3e4b
BN-71 | Refactor. Move Search Logic Into Diagnoses
rahu1ramesh May 26, 2025
b884bbc
BN-71 | Fix. Typo in Practitioner reference
mohan-13 May 27, 2025
6438eab
BN-71 | Fix. Category field to FHIR Get API of Conditions control
mohan-13 May 25, 2025
6042931
BN-71 | Refactor. Remove Forbidden Require Style
rahu1ramesh May 27, 2025
7f7644a
BN-71 | Add. User Service
rahu1ramesh May 27, 2025
26ba6d0
Merge branch 'main' into BN-71
rahu1ramesh May 27, 2025
1e9e003
BN-71 | Fix. Issue with Date picker default value in v1.83.0
mohan-13 May 27, 2025
f4c612f
BN-71 | Fix. Empty Item ConceptName In Dropdown
rahu1ramesh May 27, 2025
2961f02
BN-71 | Add. Use Default Dropdown
rahu1ramesh May 27, 2025
eae7df1
BN-71 | Add. Diagnosis Bundle Service
rahu1ramesh May 27, 2025
441a3ce
BN-71 | Fix. Empty SelectedItem
rahu1ramesh May 27, 2025
226a8f6
BN-71 | Fix. Remove Incorrect Error Mock
rahu1ramesh May 27, 2025
92fab4a
BN-71 | Fix. Broken DiagnosesForm Tests
rahu1ramesh May 27, 2025
a73492d
BN-71 | Fix. Remove DiagnosesForm Border
rahu1ramesh May 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions public/locales/locale_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"ALLERGY_LIST_RECORDED_DATE": "Recorded Date",
"ALLERGY_LIST_STATUS": "Status",
"ALLERGY_TABLE_NOT_AVAILABLE": "Not available",
"CERTAINITY_CONFIRMED": "Confirmed",
"CERTAINITY_PROVISIONAL": "Provisional",
"CLINICAL_DAYS_TRANSLATION_KEY": "days",
"CLINICAL_MONTHS_TRANSLATION_KEY": "months",
"CLINICAL_YEARS_TRANSLATION_KEY": "years",
Expand Down Expand Up @@ -36,6 +38,13 @@
"DATE_ERROR_INVALID_FORMAT": "Invalid date format",
"DATE_ERROR_NULL_OR_UNDEFINED": "Date is null or undefined",
"DATE_ERROR_PARSE": "Date Parse Error",
"DIAGNOSES_ADDED_DIAGNOSES": "Added Diagnoses",
"DIAGNOSES_CERTAINTY_ARIA_LABEL": "Diagnoses Certainty",
"DIAGNOSES_DUPLICATE_ERROR": "This diagnosis has already been added. Please select a different diagnosis.",
"DIAGNOSES_FORM_TITLE": "Diagnoses",
"DIAGNOSES_SEARCH_ARIA_LABEL": "Search for diagnoses",
"DIAGNOSES_SEARCH_PLACEHOLDER": "Search to add new diagnosis",
"DIAGNOSES_SELECT_CERTAINTY": "Select Certainty",
"ENCOUNTER_DATE": "Encounter Date",
"ENCOUNTER_TYPE": "Encounter Type",
"ERROR_BAD_REQUEST_MESSAGE": "Invalid input parameters. Please check your request and try again.",
Expand Down Expand Up @@ -66,6 +75,7 @@
"LOCATION": "Location",
"NETWORK_ERROR": "A network error occurred",
"NO_ALLERGIES": "No Allergies recorded for this patient.",
"NO_MATCHING_CONCEPTS_FOUND": "No matching concepts found.",
"PARTICIPANT": "Participant(s)",
"PATIENT_HEADER_ACTION_AREA_IN_PROGRESS": "Consultation in progress",
"PATIENT_HEADER_LABEL": "Patient Header",
Expand All @@ -75,6 +85,8 @@
"SELECT_LOCATION": "Select location",
"SELECT_PRACTITIONER": "Select practitioner",
"SELECT_VISIT_TYPE": "Select visit type",
"SELECTED_ITEM_CLOSE": "Close",
"SELECTED_ITEM_CLOSE_ARIA_LABEL": "Close Selected Item",
"SEVERITY": "Severity",
"SIDE_NAVIGATION": "Side navigation",
"UNEXPECTED_ERROR": "An unexpected error occurred",
Expand Down
12 changes: 12 additions & 0 deletions public/locales/locale_es.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"ALLERGY_LIST_RECORDED_DATE": "Fecha de grabación",
"ALLERGY_LIST_STATUS": "Estado",
"ALLERGY_TABLE_NOT_AVAILABLE": "No disponible",
"CERTAINITY_CONFIRMED": "Confirmado",
"CERTAINITY_PROVISIONAL": "Provisorio",
"CLINICAL_DAYS_TRANSLATION_KEY": "días",
"CLINICAL_MONTHS_TRANSLATION_KEY": "meses",
"CLINICAL_YEARS_TRANSLATION_KEY": "años",
Expand Down Expand Up @@ -36,6 +38,13 @@
"DATE_ERROR_INVALID_FORMAT": "Formato de fecha no válido",
"DATE_ERROR_NULL_OR_UNDEFINED": "La fecha es nula o no está definida",
"DATE_ERROR_PARSE": "Error al analizar la fecha",
"DIAGNOSES_ADDED_DIAGNOSES": "Diagnósticos Agregados",
"DIAGNOSES_CERTAINTY_ARIA_LABEL": "Certeza del Diagnóstico",
"DIAGNOSES_DUPLICATE_ERROR": "Este diagnóstico ya ha sido agregado. Por favor seleccione un diagnóstico diferente.",
"DIAGNOSES_FORM_TITLE": "Diagnósticos",
"DIAGNOSES_SEARCH_ARIA_LABEL": "Buscar diagnósticos",
"DIAGNOSES_SEARCH_PLACEHOLDER": "Buscar para agregar un diagnóstico",
"DIAGNOSES_SELECT_CERTAINTY": "Seleccionar Certeza",
"ENCOUNTER_DATE": "Fecha de Encuentro",
"ENCOUNTER_TYPE": "Tipo de encuentro",
"ERROR_BAD_REQUEST_MESSAGE": "Parámetros de entrada no válidos. Por favor, verifique su solicitud e intente nuevamente.",
Expand Down Expand Up @@ -66,6 +75,7 @@
"LOCATION": "Ubicación",
"NETWORK_ERROR": "Se produjo un error de red",
"NO_ALLERGIES": "No hay alergias registradas para este paciente",
"NO_MATCHING_CONCEPTS_FOUND": "No se encontraron conceptos coincidentes",
"PARTICIPANT": "Partícipe(es)",
"PATIENT_HEADER_ACTION_AREA_IN_PROGRESS": "Consulta en progreso",
"PATIENT_HEADER_LABEL": "Cabecera del Paciente",
Expand All @@ -75,6 +85,8 @@
"SELECT_LOCATION": "Seleccionar ubicación",
"SELECT_PRACTITIONER": "Seleccionar profesional médico",
"SELECT_VISIT_TYPE": "Seleccionar tipo de visita",
"SELECTED_ITEM_CLOSE": "Cerrar",
"SELECTED_ITEM_CLOSE_ARIA_LABEL": "Cerrar elemento seleccionado",
"SEVERITY": "Gravedad",
"SIDE_NAVIGATION": "Navegación lateral",
"UNEXPECTED_ERROR": "Se produjo un error inesperado",
Expand Down
132 changes: 118 additions & 14 deletions src/components/clinical/consultationPad/ConsultationPad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import { useCurrentEncounter } from '@hooks/useCurrentEncounter';
import { useActivePractitioner } from '@hooks/useActivePractitioner';
import { useEncounterConcepts } from '@hooks/useEncounterConcepts';
import { useLocations } from '@hooks/useLocations';
import { useConceptSearch } from '@hooks/useConceptSearch';
import { Column, Grid, Loading } from '@carbon/react';
import * as styles from './styles/ConsultationPad.module.scss';
import BasicForm from '@components/clinical/basicForm/BasicForm';
import DiagnosesForm from '@components/clinical/diagnosesForm/DiagnosesForm';
import { SelectedDiagnosisItemProps } from '@components/clinical/diagnosesForm/SelectedDiagnosisItem';
import { Concept } from '@types/encounterConcepts';
import { ConceptSearch } from '@types/concepts';
import { ConsultationBundle } from '@types/consultationBundle';
import { postConsultationBundle } from '@services/consultationBundleService';
import useNotification from '@hooks/useNotification';
Expand All @@ -18,6 +22,8 @@ import {
createBundleEntry,
createConsultationBundle,
} from '@utils/fhir/consultationBundleCreator';
import { CERTAINITY_CONCEPTS } from '@constants/concepts';
import { Coding } from 'fhir/r4';

interface ConsultationPadProps {
patientUUID: string;
Expand All @@ -29,8 +35,91 @@ const ConsultationPad: React.FC<ConsultationPadProps> = ({
onClose,
}) => {
const [isSubmitting, setIsSubmitting] = React.useState(false);

// DiagnosesForm state management
const [searchDiagnosesTerm, setSearchDiagnosesTerm] = React.useState('');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The searchTerm, diagnosesErrors doesn't need to be a concern / part of consultation pad. This can be controlled within DiagnosisForm. Only the selectedDiagnosis can be part of ConsultationPad, which will be consumed during Consultation Bundle creation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have addressed this. Thanks for taking a look

const [selectedDiagnoses, setSelectedDiagnoses] = React.useState<
SelectedDiagnosisItemProps[]
>([]);
const [diagnosisErrors, setDiagnosisErrors] = React.useState<Error[]>([]);

const { t } = useTranslation();
const { addNotification } = useNotification();

// Use concept search hook for diagnoses
const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, this can be moved into DiagnosisForm. The parent (ConsultationPad) component is only interested in selectedDiagnosis, whatever happens before that can be the components action itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have addressed this. Thanks for taking a look

searchResults,
loading: isSearchLoading,
error: searchError,
} = useConceptSearch(searchDiagnosesTerm);

// Handle search errors
React.useEffect(() => {
if (searchError) {
setDiagnosisErrors([searchError]);
}
}, [searchError]);

// DiagnosesForm handler functions
const handleSearch = (searchTerm: string) => {
setSearchDiagnosesTerm(searchTerm);
// Clear previous errors when new search starts
setDiagnosisErrors([]);
};

const handleResultSelection = (
selectedItem: ConceptSearch | null | undefined,
) => {
if (!selectedItem) {
return;
}

// Check for duplicate diagnosis
const isDuplicate = selectedDiagnoses.some(
(diagnosis) => diagnosis.id === selectedItem.conceptUuid,
);

if (isDuplicate) {
setDiagnosisErrors([new Error(t('DIAGNOSES_DUPLICATE_ERROR'))]);
return;
}

// Create new diagnosis with certainty handler
const newDiagnosis: SelectedDiagnosisItemProps = {
id: selectedItem.conceptUuid,
title: selectedItem.conceptName,
certaintyConcepts: CERTAINITY_CONCEPTS,
selectedCertainty: null,
handleCertaintyChange: (data) => {
handleCertaintyChange(selectedItem.conceptUuid, data.selectedItem);
},
};

setSelectedDiagnoses([...selectedDiagnoses, newDiagnosis]);
setSearchDiagnosesTerm(''); // Clear search
setDiagnosisErrors([]); // Clear errors
};

const handleRemoveDiagnosis = (index: number) => {
setSelectedDiagnoses((prevDiagnoses) =>
prevDiagnoses.filter((_, i) => i !== index),
);
setDiagnosisErrors([]); // Clear any existing errors
};

const handleCertaintyChange = (
diagnosisId: string,
selectedCertainty: Coding | null | undefined,
) => {
setSelectedDiagnoses((prevDiagnoses) =>
prevDiagnoses.map((diagnosis) =>
diagnosis.id === diagnosisId
? { ...diagnosis, selectedCertainty: selectedCertainty || null }
: diagnosis,
),
);
};

const {
locations,
loading: loadingLocations,
Expand Down Expand Up @@ -79,11 +168,11 @@ const ConsultationPad: React.FC<ConsultationPadProps> = ({
const submitConsultation = () => {
const enconterResourceURL = `urn:uuid:${crypto.randomUUID()}`;
const encounterResource = createEncounterResource(
encounterTypeSelected?.uuid,
encounterTypeSelected?.name,
encounterTypeSelected!.uuid,
encounterTypeSelected!.name,
patientUUID,
[practitioner?.uuid],
currentEncounter?.id,
[practitioner!.uuid],
currentEncounter!.id,
locations[0].uuid,
new Date(),
);
Expand Down Expand Up @@ -192,16 +281,31 @@ const ConsultationPad: React.FC<ConsultationPadProps> = ({
secondaryButtonText={t('CONSULTATION_PAD_CANCEL_BUTTON')}
onSecondaryButtonClick={handleOnSecondaryButtonClick}
content={
<BasicForm
practitioner={practitioner}
encounterTypes={encounterConcepts.encounterTypes}
encounterTypeSelected={encounterTypeSelected}
visitTypes={encounterConcepts.visitTypes}
visitTypeSelected={visitTypeSelected}
location={locations[0]}
locationSelected={locations[0]}
defaultDate={formattedDate.formattedResult}
/>
<>
<BasicForm
practitioner={practitioner}
encounterTypes={encounterConcepts.encounterTypes}
encounterTypeSelected={encounterTypeSelected}
visitTypes={encounterConcepts.visitTypes}
visitTypeSelected={visitTypeSelected}
location={locations[0]}
locationSelected={locations[0]}
defaultDate={formattedDate.formattedResult}
/>
<DiagnosesForm
handleResultSelection={handleResultSelection}
handleSearch={handleSearch}
searchResults={searchResults}
isSearchEmpty={
searchResults.length === 0 &&
!isSearchLoading &&
searchDiagnosesTerm.length > 2
}
errors={diagnosisErrors}
selectedDiagnoses={selectedDiagnoses}
handleRemoveDiagnosis={handleRemoveDiagnosis}
/>
</>
}
/>
);
Expand Down
Loading