import React, { useState, useEffect } from 'react'; import { TextareaControl } from "../ui/textarea-control"; import { Card, CardHeader, CardBody, CardDivider } from '../ui/wp-card'; import { Elevation } from '../ui/elevation'; import { InputControl } from '../ui/input-control'; import { Divider } from '../ui/divider'; import { DismissibleAlert } from '../ui/dismissible-alert'; import { Heading } from '../ui/heading'; import { Flex, FlexItem } from '../ui/flex'; import { Spacer } from '../ui/spacer'; import { TabPanel } from '../ui/tab-panel'; import { Disabled } from '../ui/disabled'; import { useFormContext, Controller, useFieldArray } from 'react-hook-form'; import { IValueSet, useAuthServiceContext, useValueSetServiceContext } from '../../services'; import { FhirAnnotation, FhirCode, FhirCodeableConcept, FhirPeriod, FhirReference, FhirReferenceCollection, FhirValueSetSearch, TypeSelector } from '../../components'; import { useFhirAllergyIntolerances, useFhirClinicalImpressionBroadcast, useFhirClinicalImpressions, useFhirConditions, useFhirEncounters, useFhirFamilyMemberHistories, useFhirObservations, useFhirPractitioners, useFhirQuestionnaireResponses, useFhirRiskAssessments } from '../../hooks'; import { FhirResource } from 'fhir/r4'; import { PlusCircle, Trash2 } from 'lucide-react'; interface FhirClinicalImpressionProps { elevated?: boolean; disabled?: boolean; enableInlineActions?: boolean; } const FindingItemReferences: React.FC<{ index: number; disabled: boolean; readonly: boolean; enableInlineActions: boolean; }> = ({ index, disabled, readonly = false, enableInlineActions = true }) => { const { control } = useFormContext(); const itemReferenceFieldArray = useFieldArray({ control, name: `finding.${index}.itemReference` }); const { conditionsList } = useFhirConditions(); const { observationsList } = useFhirObservations(); return ( { switch (referenceType) { case 'Condition': return conditionsList; case 'Observation': return observationsList; default: return []; } }} required={false} disabled={disabled} readonly={readonly} multiple={false} enableInlineActions={enableInlineActions}/> ); }; const InvestigationItemReferences: React.FC<{ index: number; disabled: boolean; readonly: boolean; enableInlineActions: boolean; }> = ({ index, disabled, readonly = false, enableInlineActions = true }) => { const { control } = useFormContext(); const itemReferenceFieldArray = useFieldArray({ control, name: `investigation.${index}.item` }); const { observationsList } = useFhirObservations(); const { questionnaireResponsesList } = useFhirQuestionnaireResponses(); const { familyMemberHistoriesList } = useFhirFamilyMemberHistories(); const { riskAssessmentsList } = useFhirRiskAssessments(); return ( { switch (referenceType) { case 'Observation': return observationsList; case 'QuestionnaireResponse': return questionnaireResponsesList; case 'FamilyMemberHistory': return familyMemberHistoriesList; case 'RiskAssessment': return riskAssessmentsList; default: return []; } }} required={false} disabled={disabled} readonly={readonly} multiple={false} enableInlineActions={enableInlineActions}/> ); }; export const FhirClinicalImpression: React.FC = ({ elevated = true, disabled = false, enableInlineActions = true }) => { const { control, watch } = useFormContext(); const { userRole, USER_ROLES } = useAuthServiceContext(); const { fetchValueSet, valueSetUrls } = useValueSetServiceContext(); const [loading, setLoading] = useState(false); // value sets const [clinicalimpressionStatus, setClinicalimpressionStatus] = useState([]); const [investigationSets, setInvestigationSets] = useState([]); const [clinicalimpressionPrognosis, setClinicalimpressionPrognosis] = useState([]); const isPatient = [USER_ROLES.PATIENT, USER_ROLES.RELATED_PERSON].includes(userRole as string); const spacerMargin = '24px'; const problemFieldArray = useFieldArray({ control, name: 'problem' }); const findingFieldArray = useFieldArray({ control, name: 'finding' }); const investigationFieldArray = useFieldArray({ control, name: 'investigation' }); useFhirClinicalImpressionBroadcast(); const { clinicalImpressionsList } = useFhirClinicalImpressions(); const { encountersList } = useFhirEncounters(); const { conditionsList } = useFhirConditions(); const { allergyIntolerancesList } = useFhirAllergyIntolerances(); const { riskAssessmentsList } = useFhirRiskAssessments(); const { practitionersList } = useFhirPractitioners(); useEffect(() => { const loadValueSets = async () => { setLoading(true); try { const [ clinicalimpressionStatusValueSet, investigationSetsValueSet, clinicalimpressionPrognosisValueSet ] = await Promise.all([ fetchValueSet(valueSetUrls.clinicalimpressionStatus), fetchValueSet(valueSetUrls.investigationSets), fetchValueSet(valueSetUrls.clinicalimpressionPrognosis) ]); setClinicalimpressionStatus(clinicalimpressionStatusValueSet); setInvestigationSets(investigationSetsValueSet); setClinicalimpressionPrognosis(clinicalimpressionPrognosisValueSet); } catch (err) { console.error('Error loading value sets:', err); } finally { setLoading(false); } }; loadValueSets(); }, [fetchValueSet, valueSetUrls]); const addFinding = () => { findingFieldArray.append({ itemCodeableConcept: null, itemReference: [], basis: '' }); }; const removeFinding = (index: number) => { findingFieldArray.remove(index); }; const addInvestigation = () => { investigationFieldArray.append({ code: null, item: [] }); }; const removeInvestigation = (index: number) => { investigationFieldArray.remove(index); }; return ( {elevated && } Details {/* status */} {/* assessor Optional because the service stamps the signed-in practitioner when this is left empty. Asking somebody to find themselves in a picker to save their own note is friction, not safety - but the field stays, because a scribe writing on somebody else's behalf has to be able to name the clinician who actually assessed. */} p.practitioner)} enableInlineActions={enableInlineActions} required={false} disabled={disabled}/> {/* statusReason */} {/**/} {/**/} {/* code */} {/**/} {/**/} {/* description */} ( )}/> {/* date */} ( )}/> {/* effectiveType */} {watch('effectiveType') === 'dateTime' && ( <> {/* effective */} ( )}/> )} {watch('effectiveType') === 'period' && ( <> {/* effectivePeriod */} )} {/* summary */} ( )}/> {/* encounter */} {/* previous */} {(tab) => { // problems if (tab.name === 'problems') { return (
{/* problem */} { switch (referenceType) { case 'Condition': return conditionsList; case 'AllergyIntolerance': return allergyIntolerancesList; default: return []; } }} required={false} disabled={disabled} readonly={isPatient} multiple={true} enableInlineActions={enableInlineActions}/>
); } // findings if (tab.name === 'findings') { return (
{findingFieldArray.fields.length === 0 && ( <> Findings {!isPatient && ( )} No findings added yet. )} {findingFieldArray.fields.map((field, index) => (

Finding {index + 1}

{!isPatient && ( )}
{/* itemCodeableConcept */} {/* itemReference */} {/* basis */} ( )}/>
))} {!isPatient && findingFieldArray.fields.length > 0 && ( )}
); } // investigations if (tab.name === 'investigations') { return (
{investigationFieldArray.fields.length === 0 && ( <> Investigations {!isPatient && ( )} No investigations added yet. )} {investigationFieldArray.fields.map((field, index) => (

Investigation {index + 1}

{!isPatient && ( )}
{/* code */} {/* item */}
))} {!isPatient && investigationFieldArray.fields.length > 0 && ( )}
); } // prognosis if (tab.name === 'prognosis') { return (
{/* prognosisCodeableConcept */} {/* priorPrescription */}
); } // Notes tab if (tab.name === 'notes') { return ( // A bare defaults to TRUE, so this tab was inert for every user // including the clinician writing the note. Its four sibling tabs all key off // isPatient, and it passes readonly={isPatient} to its own child - which only // means anything if the wrapper is not unconditionally off.
{/* note */}
); } }}
); }; export default FhirClinicalImpression;