import React, { useState, useEffect } from 'react'; import { TextareaControl } from "../ui/textarea-control"; import { Card, CardBody, CardHeader, 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, useFieldArray, Controller } from 'react-hook-form'; import { FhirResource } from 'fhir/r4'; import { PlusCircle, Trash2 } from 'lucide-react'; import { IValueSet, useAuthServiceContext, useValueSetServiceContext } from '../../services'; import { FhirCode, FhirCodeableConcept, FhirPeriod, FhirRange, FhirReference, FhirReferenceCollection, TypeSelector, FhirValueSetSearch, FhirAnnotation } from '../../components'; import { useFhirCarePlans, useFhirConditions, useFhirDevices, useFhirEncounters, useFhirLocations, useFhirMedicationAdministrations, useFhirObservations, useFhirPractitioners, useFhirProcedureBroadcast, useFhirProcedures, useFhirRelatedPersons } from '../../hooks'; const PerformerActorReferences: React.FC<{ index: number; disabled: boolean; readonly: boolean; enableInlineActions: boolean; }> = ({ index, disabled, readonly = false, enableInlineActions = true }) => { const { control } = useFormContext(); const itemReferenceFieldArray = useFieldArray({ control, name: `performer.${index}.actor` }); const { practitionersList } = useFhirPractitioners(); const { devicesList } = useFhirDevices(); const { relatedPersonsList } = useFhirRelatedPersons(); return ( { switch (referenceType) { case 'Practitioner': return practitionersList.map(p => p.practitioner); case 'RelatedPerson': return relatedPersonsList; case 'Device': return devicesList; default: return []; } }} required={true} disabled={disabled} readonly={readonly} multiple={false} enableInlineActions={enableInlineActions}/> ); }; interface FhirProcedureProps { elevated?: boolean; disabled?: boolean; enableInlineActions?: boolean; } export const FhirProcedure: React.FC = ({ elevated = true, disabled = false, enableInlineActions = true }) => { const { userRole, USER_ROLES } = useAuthServiceContext(); const { fetchValueSet, valueSetUrls } = useValueSetServiceContext(); const [loading, setLoading] = useState(false); // value sets const [procedureCategory, setProcedureCategory] = useState([]); const [procedureOutcome, setProcedureOutcome] = useState([]); const [procedureFollowup, setProcedureFollowup] = useState([]); const [eventStatus, setEventStatus] = useState([]); const isPatient = [USER_ROLES.PATIENT, USER_ROLES.RELATED_PERSON].includes(userRole as string); const spacerMargin = '24px'; useFhirProcedureBroadcast(); const { proceduresList } = useFhirProcedures(); const { locationsList } = useFhirLocations(); const { encountersList } = useFhirEncounters(); const { conditionsList } = useFhirConditions(); const { devicesList } = useFhirDevices(); const { observationsList } = useFhirObservations(); const { carePlansList } = useFhirCarePlans(); const { medicationAdministrationsList } = useFhirMedicationAdministrations(); const { watch, control } = useFormContext(); const performedType = watch('performedType'); const reasonReferenceFieldArray = useFieldArray({ control, name: 'reasonReference' }); const performerFieldArray = useFieldArray({ control, name: 'performer' }); const usedReferenceFieldArray = useFieldArray({ control, name: 'usedReference' }); const focalDeviceFieldArray = useFieldArray({ control, name: 'focalDevice' }); const basedOnFieldArray = useFieldArray({ control, name: 'basedOn' }); const partOfFieldArray = useFieldArray({ control, name: 'partOf' }); useEffect(() => { const loadValueSets = async () => { setLoading(true); try { const [ procedureCategoryValueSet, procedureOutcomeValueSet, procedureFollowupValueSet, eventStatusValueSet ] = await Promise.all([ fetchValueSet(valueSetUrls.procedureCategory), fetchValueSet(valueSetUrls.procedureOutcome), fetchValueSet(valueSetUrls.procedureFollowup), fetchValueSet(valueSetUrls.eventStatus), ]); setProcedureCategory(procedureCategoryValueSet); setProcedureOutcome(procedureOutcomeValueSet); setProcedureFollowup(procedureFollowupValueSet); setEventStatus(eventStatusValueSet); } catch (err) { console.error('Error loading value sets:', err); } finally { setLoading(false); } }; loadValueSets(); }, [fetchValueSet, valueSetUrls]); const addPerformer = () => { performerFieldArray.append({ function: null, actor: [] }); }; const removePerformer = (index: number) => { performerFieldArray.remove(index); }; const addFocalDevice = () => { focalDeviceFieldArray.append({ action: null, manipulated: [] }); }; const removeFocalDevice = (index: number) => { focalDeviceFieldArray.remove(index); }; return ( {elevated && } Details {/* code */} {/* category */} {/* status */} {/* performedType */} {performedType === 'dateTime' && ( <> {/* performedDateTime */} {/* The ANSWER is what is required, not the question. The selector above used to be the only required control here, so the form insisted you choose "Date" and then let you save with the date empty - a decision demanded of the user that bought nothing. */} ( )}/> )} {performedType === 'period' && ( <> {/* performedPeriod */} )} {performedType === 'string' && ( <> {/* performedString */} ( )}/> )} {/* The 'age' branch rendered nothing at all, and its option is commented out of the selector above, so it was unreachable and empty in both directions. The 'period' and 'range' branches still accept an empty value: FhirPeriod and FhirRange take no `required` prop, and adding one reaches every call site of two shared datatype components. The two ordinary branches refuse now, which is where procedures are actually recorded. */} {performedType === 'range' && ( <> {/* performedRange */} )} {/* bodySite */} {/* outcome */} {/* followUp */} {/* encounter */} {/* location */} {(tab) => { // reason if (tab.name === 'reason') { return (
{/* reasonCode */} {/* reasonReference */} { switch (referenceType) { case 'Condition': return conditionsList; case 'Observation': return observationsList; case 'Procedure': return proceduresList; default: return []; } }} multiple={true} required={false} disabled={disabled} readonly={isPatient} enableInlineActions={enableInlineActions}/>
); } // performers if (tab.name === 'performers') { return (
{performerFieldArray.fields.length === 0 && ( <> {!isPatient && ( )} No performers added yet. )} {performerFieldArray.fields.map((field, index) => (

Performer {index + 1}

{!isPatient && ( )}
{/* performerType */} {/* performer */}
))} {!isPatient && performerFieldArray.fields.length > 0 && ( )}
); } // complications if (tab.name === 'complications') { return (
{/* complication */} {/* complicationDetail */}
); } // used if (tab.name === 'used') { return (
{/* usedCode */} {/* usedReference */} { switch (referenceType) { case 'Device': return devicesList; default: return []; } }} multiple={true} required={false} disabled={disabled} readonly={isPatient} enableInlineActions={enableInlineActions}/>
); } // focalDevice if (tab.name === 'focalDevice') { return (
{focalDeviceFieldArray.fields.length === 0 && ( <> Manipulated, implanted, or removed device {!isPatient && ( )} No focal device added yet. )} {focalDeviceFieldArray.fields.map((field, index) => (

Focal Device {index + 1}

{!isPatient && ( )}
{/* focalDevice.action */} {/* focalDevice.manipulated */}
))} {!isPatient && focalDeviceFieldArray.fields.length > 0 && ( )}
); } // basedOn if (tab.name === 'basedOn') { return (
{/* basedOn */} { switch (referenceType) { case 'CarePlan': return carePlansList; default: return []; } }} required={false} disabled={disabled} readonly={isPatient} enableInlineActions={enableInlineActions}/>
); } // partOf if (tab.name === 'partOf') { return (
{/* partOf */} { switch (referenceType) { case 'Procedure': return proceduresList; case 'Observation': return observationsList; case 'MedicationAdministration': return medicationAdministrationsList; default: return []; } }} required={false} disabled={disabled} readonly={isPatient} enableInlineActions={enableInlineActions}/>
); } // notes if (tab.name === 'notes') { return (
{/* note */}
); } }}
); };