import React, { useState, useEffect } from 'react'; import { TextareaControl } from "../ui/textarea-control"; import { CustomSelectControl } from "../ui/custom-select-control"; import type { CustomSelectOption } from "../ui/custom-select-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, FhirReference, FhirReferenceCollection, FhirValueSetSearch } from '../../components'; import { useFhirConditions, useFhirGoalBroadcast, useFhirGoals, useFhirMedicationStatements, useFhirNutritionOrders, useFhirObservations, useFhirPractitioners, useFhirRelatedPersons, useFhirRiskAssessments } from '../../hooks'; import { FhirResource } from 'fhir/r4'; import { PlusCircle, Trash2 } from 'lucide-react'; // - WordPress's ToggleControl, swapped for the ui/* adapter. Same props, same JSX; // only `__nextHasNoMarginBottom` is dropped, which means nothing outside WordPress. import { ToggleControl } from '../ui/toggle-control'; interface FhirGoalProps { elevated?: boolean; disabled?: boolean; enableInlineActions?: boolean; } export const FhirGoal: 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 [goalStatus, setGoalStatus] = useState([]); const [goalAchievement, setGoalAchievement] = useState([]); const [goalCategory, setGoalCategory] = useState([]); const [goalPriority, setGoalPriority] = useState([]); const [goalStartEvent, setGoalStartEvent] = useState([]); const isPatient = [USER_ROLES.PATIENT, USER_ROLES.RELATED_PERSON].includes(userRole as string); const spacerMargin = '24px'; useFhirGoals(); useFhirGoalBroadcast(); const { conditionsList } = useFhirConditions(); const { observationsList } = useFhirObservations(); const { medicationStatementsList } = useFhirMedicationStatements(); const { nutritionOrdersList } = useFhirNutritionOrders(); const { riskAssessmentsList } = useFhirRiskAssessments(); const { practitionersList } = useFhirPractitioners(); const { relatedPersonsList } = useFhirRelatedPersons(); const targetFieldArray = useFieldArray({ control, name: 'target' }); const addressesFieldArray = useFieldArray({ control, name: 'addresses' }); const expressedByFieldArray = useFieldArray({ control, name: 'expressedBy' }); useEffect(() => { const loadValueSets = async () => { setLoading(true); try { const [ goalStatusValueSet, goalAchievementValueSet, goalCategoryValueSet, goalPriorityValueSet, goalStartEventValueSet ] = await Promise.all([ fetchValueSet(valueSetUrls.goalStatus), fetchValueSet(valueSetUrls.goalAchievement), fetchValueSet(valueSetUrls.goalCategory), fetchValueSet(valueSetUrls.goalPriority), fetchValueSet(valueSetUrls.goalStartEvent) ]); setGoalStatus(goalStatusValueSet); setGoalAchievement(goalAchievementValueSet); setGoalCategory(goalCategoryValueSet); setGoalPriority(goalPriorityValueSet); setGoalStartEvent(goalStartEventValueSet); } catch (err) { console.error('Error loading value sets:', err); } finally { setLoading(false); } }; loadValueSets(); }, [fetchValueSet, valueSetUrls]); const addTarget = () => { targetFieldArray.append({}); }; const removeTarget = (index: number) => { targetFieldArray.remove(index); }; return ( {elevated && } Details {/* description */} {/* expressedBy */} { switch (referenceType) { case 'Practitioner': return practitionersList.map(p => p.practitioner); case 'RelatedPerson': return relatedPersonsList; default: return []; } }} required={true} disabled={disabled} readonly={isPatient} multiple={false} enableInlineActions={enableInlineActions}/> {/* category */} {/* priority */} {/* lifecycleStatus */} {/* achievementStatus */} {/* startDate */} ( )}/> {/* start */} {/**/} {/**/} {/*{watch('start') === 'date' && (*/} {/* <>*/} {/* /!* startDate *!/*/} {/* (*/} {/* */} {/* )}/>*/} {/* */} {/* */} {/*)}*/} {/*{watch('start') === 'codeableConcept' && (*/} {/* <>*/} {/* /!* startCodeableConcept *!/*/} {/* */} {/* */} {/* */} {/*)}*/} {/* statusDate */} ( )}/> {/* statusReason */} ( )}/> {/* "Who's responsible for creating Goal?" was rendered a SECOND time here, bound to the same expressedBy field array as the one further up this card - so the question was asked twice, both copies showed the same rows, and this one had lost the readonly={isPatient} guard its twin carries. The first render is the one kept. */} {(tab) => { // targets if (tab.name === 'targets') { return (
{targetFieldArray.fields.length === 0 && ( <> Targets {!isPatient && ( )} No targets added yet. )} {targetFieldArray.fields.map((field, index) => (

Target {index + 1}

{!isPatient && ( )}
{/* target.measure */} {/* target.measureType */} ( { field.onChange(newValue.selectedItem.key); }}/> )}/> {watch(`target.${index}.measureType`) === 'string' && ( <> {/* target.detailString */} ( )}/> )} {watch(`target.${index}.measureType`) === 'boolean' && ( <> {/* target.detailBoolean */} ( )}/> )} {watch(`target.${index}.measureType`) === 'integer' && ( <> {/* target.detailInteger */} ( )}/> )} {/*{watch(`target.${index}.measureType`) === 'codeableConcept' && (*/} {/* <>*/} {/* /!* target.detailCodeableConcept *!/*/} {/* */} {/* */} {/* */} {/*)}*/} {/* target.dueType */} ( { field.onChange(newValue.selectedItem.key); }}/> )}/> {watch(`target.${index}.dueType`) === 'date' && ( <> {/* target.dueDate */} ( )}/> )} {watch(`target.${index}.dueType`) === 'duration' && ( <> ( )}/> ( { field.onChange(newValue.selectedItem.key); }}/> )}/> )}
))} {!isPatient && targetFieldArray.fields.length > 0 && ( )}
); } // addresses if (tab.name === 'addresses') { return (
{/* reasonReference */} { switch (referenceType) { case 'Condition': return conditionsList; case 'Observation': return observationsList; case 'MedicationStatement': return medicationStatementsList; case 'NutritionOrder': return nutritionOrdersList; case 'RiskAssessment': return riskAssessmentsList; default: return []; } }} required={false} disabled={disabled} readonly={isPatient} enableInlineActions={enableInlineActions}/>
); } // outcomes if (tab.name === 'outcomes') { return (
{/* outcomeCode */} {/* outcomeReference */}
); } // notes if (tab.name === 'notes') { return (
{/* note */}
); } }}
); }; export default FhirGoal;