import React from 'react'; import { ContactPoint } from 'fhir/r4'; import { formatPhoneNumber } from './phoneNumber'; import { Text } from '../components/ui/text'; interface FHIRContactPointDisplayProps { contact: ContactPoint | ContactPoint[] | undefined | null; system?: 'phone' | 'email'; separator?: string; emptyText?: string; className?: string; } const formatSingleContactPoint = (contact: ContactPoint, system?: 'phone' | 'email'): string => { if (!contact || !contact.value) { return ''; } const contactSystem = system || contact.system; const value: string = contactSystem === 'email' ? contact.value : formatPhoneNumber(contact.value); if (contact.use) { return `${value} (${contact.use})`; } return value; }; const getContactPointDisplayName = (contact: ContactPoint): string => { if (contact.system === 'phone') { return 'Phone'; } if (contact.system === 'email') { return 'Email'; } if (contact.system === 'fax') { return 'Fax'; } if (contact.system === 'pager') { return 'Pager'; } if (contact.system === 'url') { return 'Website'; } if (contact.system === 'sms') { return 'SMS'; } if (contact.system === 'other') { return 'Other'; } return 'Contact'; }; export const formatFhirContactPoint = ( value: ContactPoint | ContactPoint[] | undefined | null, system?: 'phone' | 'email', separator: string = ', ', emptyText: string = '-' ): string => { if (value == null) { return emptyText; } const contactPoints = Array.isArray(value) ? value : [value]; /** * A VALUE-LESS ENTRY IS NOT A CONTACT POINT, and until nothing produced one. * * Found in a browser 2026-08-23: recording *"Declined to say"* against a patient's email made the * field render **completely empty** - not the label, not even the dash it replaced. * * THE MECHANISM, and it is worth stating because the two halves look unrelated: records an * absence as FHIR does, a `telecom` entry that names its system and carries **no value**. This filter * matched that entry, so `filteredContacts.length` was 1 and `emptyText` never fired - and then * `formatSingleContactPoint` returned `''` for it and `.filter(Boolean)` dropped it. **One entry in, * nothing out, and no empty text either.** * * Worse than the dash it was meant to improve on: a blank field says nothing at all, where the dash * at least said "we have nothing". * * The `.filter(contact => contact?.value)` is correct beyond this case - an entry with no value has * nothing to display by definition, whatever put it there. */ const filteredContacts = (system ? contactPoints.filter(contact => contact?.system === system) : contactPoints ).filter(contact => contact?.value); if (filteredContacts.length === 0) { return emptyText; } return filteredContacts .map(contact => formatSingleContactPoint(contact, system)) .filter(contact => contact) .join(separator); }; export const useFhirContactPoint = ( value: ContactPoint | ContactPoint[] | undefined | null, system?: 'phone' | 'email', separator: string = ', ', emptyText: string = '-' ): string => { return formatFhirContactPoint(value, system, separator, emptyText); }; export const getFirstContactPointBySystem = ( value: ContactPoint | ContactPoint[] | undefined | null, system: 'phone' | 'email' ): ContactPoint | undefined => { if (value == null) { return undefined; } const contactPoints = Array.isArray(value) ? value : [value]; return contactPoints.find(contact => contact?.system === system); }; export const getAllContactPointsBySystem = ( value: ContactPoint | ContactPoint[] | undefined | null, system: 'phone' | 'email' ): ContactPoint[] => { if (value == null) { return []; } const contactPoints = Array.isArray(value) ? value : [value]; return contactPoints.filter(contact => contact?.system === system); }; export const FHIRContactPointDisplay: React.FC = ({ contact, system, separator = ', ', emptyText = '-', className }) => { if (!contact || (Array.isArray(contact) && contact.length === 0)) { return {emptyText}; } const contactArray = Array.isArray(contact) ? contact : [contact]; const filteredContacts = system ? contactArray.filter(c => c?.system === system) : contactArray; if (filteredContacts.length === 0) { return {emptyText}; } if (filteredContacts.length > 1 && !system) { return (
{filteredContacts.map((singleContact, index) => { const displayName = getContactPointDisplayName(singleContact); const formattedValue = formatSingleContactPoint(singleContact); if (!formattedValue) return null; return (
{displayName}: {formattedValue}
); })}
); } const formattedContact = formatFhirContactPoint(contact, system, separator, emptyText); return ( {formattedContact} ); };