import { Card, CardBody, CardHeader } from '../ui/wp-card'; import { TextControl } from "../ui/text-control"; import { Divider } from '../ui/divider'; import { DismissibleAlert } from '../ui/dismissible-alert'; import { Heading } from '../ui/heading'; import { Flex, FlexItem, FlexBlock } from '../ui/flex'; import { Spacer } from '../ui/spacer'; import { PlusCircle, Trash2 } from 'lucide-react'; import { Controller, useFormContext, useWatch } from 'react-hook-form'; import { FhirCode, FhirPeriod } from '../index.ts'; import { IValueSet, useValueSetServiceContext } from '../../services'; import { useEffect, useState } from 'react'; // / B51 - the ordering is pure and lives in its own module; see the comment there for why. import { orderForHumans, CONTACT_TYPE_ORDER, CONTACT_USE_ORDER } from '../../utils/contactPointOrder.ts'; interface FhirContactPointProps { telecoms: any; label?: string; required?: boolean; disabled?: boolean; readonly?: boolean; /** * What the absence COSTS, in the caller's own terms, shown beside the empty state. * * OPTIONAL because this component is shared by fourteen callers - locations, organizations, care * teams, healthcare services - and the consequence is different for each. "This person will not * receive any notices" is true of a practitioner and meaningless on a room. A single global sentence * would have to be vague enough to be true everywhere, which is how an empty state stops being read. */ emptyConsequence?: string; } const FhirContactPoint = ({ telecoms, label = 'Contact', required = false, disabled = false, readonly = false, emptyConsequence, }: FhirContactPointProps) => { const [contactPointSystem, setContactPointSystem] = useState([]); const [contactPointUse, setContactPointUse] = useState([]); const { control, getValues } = useFormContext(); const { fetchValueSet, valueSetUrls } = useValueSetServiceContext(); const spacerMargin = '24px'; const allTelecomFields = useWatch({ control, name: 'telecom', defaultValue: telecoms.fields }); useEffect(() => { const loadValueSets = async () => { try { const [contactPointSystemData, contactPointUseData] = await Promise.all([ fetchValueSet(valueSetUrls.contactPointSystem), fetchValueSet(valueSetUrls.contactPointUse) ]); setContactPointSystem(orderForHumans(contactPointSystemData, CONTACT_TYPE_ORDER)); setContactPointUse(orderForHumans(contactPointUseData, CONTACT_USE_ORDER)); } catch (error) { console.error('Failed to load value sets:', error); } }; loadValueSets(); }, []); const addContact = () => { if (canAddContact()) { telecoms.append({ system: '', value: '', use: '' }); } }; const removeContact = (index: number) => { telecoms.remove(index); }; const canAddContact = (): boolean => { if (telecoms.fields.length === 0) { return true; } const lastContactIndex = telecoms.fields.length - 1; const lastSystem = getValues(`telecom.${lastContactIndex}.system`) || ''; const lastValue = getValues(`telecom.${lastContactIndex}.value`) || ''; return lastSystem.trim() !== '' && lastValue.trim() !== ''; }; return ( <> {telecoms.fields.length === 0 && ( <> {label} {!readonly && ( )} {/* An empty state that only reports emptiness leaves the reader to guess whether it matters. Where the caller knows the cost, say it here - this is the screen the person is already on, and it is the last point at which the gap is cheap to fix. */} No contact added yet. {emptyConsequence ? ` ${emptyConsequence}` : ''} )} {telecoms.fields.map((telecom: any, telecomIndex: number) => ( <> {telecoms.fields.length > 0 && (

Contact {telecomIndex + 1}

{!readonly && ( )}
)} {/* Contact Type */} ( <> {fieldState.error && ( Contact value is required )} )}/> {/* Contact Use */} {/* Period */}
{!readonly && telecomIndex === telecoms.fields.length - 1 && ( {/* The WP `label` here read "Add address" while the visible text says "Add Contact" and the handler is addContact — a copy-paste error that surfaced as a wrong tooltip. Corrected to match. */} )} ))} ); }; export default FhirContactPoint;