import { useEffect, useState } from 'react'; import { TextControl } from "../ui/text-control"; import { CustomSelectControl } from "../ui/custom-select-control"; import type { CustomSelectOption } from "../ui/custom-select-control"; import { Card, CardBody, CardHeader } from '../ui/wp-card'; 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, useWatch, useFormContext } from 'react-hook-form'; import { Identifier } from 'fhir/r4'; import { IValueSet, useValueSetServiceContext } from '../../services'; import { FhirCode, FhirCodeableConcept, FhirPeriod } from '../index.ts'; import { isValidNpi, NPI_FORMAT_MESSAGE } from '../../utils/npi.ts'; interface FHIRIdentifierProps { identifiers: any; label?: string; context?: string; showSystem?: boolean; required?: boolean; disabled?: boolean; } const FhirIdentifier = ({ identifiers, label = 'Identifier', context = 'identifier', showSystem = true, required = false, disabled = false }: FHIRIdentifierProps) => { const [identifierUse, setIdentifierUse] = useState([]); const [identifierType, setIdentifierType] = useState([]); const { control, formState: { errors }, getValues } = useFormContext(); const { fetchValueSet, valueSetUrls } = useValueSetServiceContext(); const spacerMargin = '24px'; /** * - NPI format, checked in the field. * * Applies on the `npi` context only, which covers BOTH the practitioner form and the organization * form: the 80840-prefixed Luhn check digit is defined for Type 1 (individual) and Type 2 * (organization) NPIs alike, so one rule is correct for both. * * This is convenience, not the gate - `Practitioner/put.ts` refuses a bad NPI server-side and would * do so even if this file were bypassed entirely. * An empty value passes: whether the field is REQUIRED is a separate rule, and duplicating it here * would make a blank optional NPI unsavable. */ const validateIdentifierValue = (value: unknown): true | string => { if (context !== 'npi') return true; const candidate = typeof value === 'string' ? value.trim() : ''; if (candidate === '') return true; return isValidNpi(candidate) ? true : `That NPI is not valid. ${NPI_FORMAT_MESSAGE}`; }; const allIdentifierFields = useWatch({ control, name: context, defaultValue: identifiers.fields }); useEffect(() => { const loadValueSets = async () => { try { const [identifierUseValueSet, identifierTypeValueSet] = await Promise.all([ fetchValueSet(valueSetUrls.identifierUse), fetchValueSet(valueSetUrls.identifierType) ]); setIdentifierUse(identifierUseValueSet); setIdentifierType(identifierTypeValueSet); } catch (error) { console.error('Failed to load value sets:', error); } }; loadValueSets(); }, []); const addIdentifier = () => { if (canAddIdentifier()) { identifiers.append({ value: '', use: '', type: null }); } }; const removeIdentifier = (index: number) => { identifiers.remove(index); }; const canAddIdentifier = (): boolean => { if (identifiers.fields.length === 0) { return true; } const lastIdentifierIndex = identifiers.fields.length - 1; const lastValue = getValues(`${context}.${lastIdentifierIndex}.value`) || ''; return lastValue.trim() !== ''; }; return ( <> {(!showSystem || identifiers.fields.length === 0) && ( <> {label} {identifiers.fields.length === 0 && ( )} {identifiers.fields.length === 0 && ( {required ? 'At least one identifier is required.' : 'No identifiers added yet.'} )} )} {identifiers.fields.map((identifier: Identifier, identifierIndex: number) => ( <> {identifiers.fields.length > 0 && (

Identifier {identifierIndex + 1}

)} {/* value */} ( <> {/* The message now follows the RULE that failed. It was hardcoded to "is required", so the NPI format error would have rendered as a required-field error — telling the person to fill in a field they had already filled in, which is worse than no message. */} {fieldState.error && ( {fieldState.error.type === 'validate' && fieldState.error.message ? fieldState.error.message : 'Identifier value is required'} )} )}/> {/* type */} {/* use */} {/* system */} {showSystem && ( <> { const options: CustomSelectOption[] = [ { name: 'URIs: when the identifier is a URI', key: 'urn:ietf:rfc:3986' }, { name: 'DICOM Unique Id ', key: 'urn:dicom:uid' }, { name: 'United States Social Security Number', key: 'http://hl7.org/fhir/sid/us-ssn' }, { name: 'United States Medicare Number', key: 'http://hl7.org/fhir/sid/us-medicare' }, { name: 'Medicare Beneficiary Identifier (United States)', key: 'http://hl7.org/fhir/sid/us-mbi' }, { name: 'United States National Provider Identifier', key: 'http://hl7.org/fhir/sid/us-npi' }, { name: 'Global Trade Item Number (GTIN)', key: 'https://www.gs1.org/gtin' }, { name: 'Alaska Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.2' }, { name: 'Alabama Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.1' }, { name: 'Arkansas Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.5' }, { name: 'Arizona Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.4' }, { name: 'California Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.6' }, { name: 'Colorado Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.8' }, { name: 'Connecticut Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.9' }, { name: 'DC Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.11' }, { name: 'Delaware Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.10' }, { name: 'Florida Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.12' }, { name: 'Georgia Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.13' }, { name: 'Hawaii Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.15' }, { name: 'Indiana Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.18' }, { name: 'Iowa Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.19' }, { name: 'Idaho Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.16' }, { name: 'Illinois Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.17' }, { name: 'Kansas Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.20' }, { name: 'Kentucky Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.21' }, { name: 'Louisiana Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.22' }, { name: 'Massachusetts Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.25' }, { name: 'Maryland Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.24' }, { name: 'Maine Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.23' }, { name: 'Michigan Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.26' }, { name: 'Minnesota Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.27' }, { name: 'Missouri Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.29' }, { name: 'Mississippi Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.28' }, { name: 'Montana Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.30' }, { name: 'New York Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.36' }, { name: 'North Carolina Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.37' }, { name: 'North Dakota Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.38' }, { name: 'Nebraska Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.31' }, { name: 'New Hampshire Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.33' }, { name: 'New Jersey Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.34' }, { name: 'New Mexico Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.35' }, { name: 'Nevada Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.32' }, { name: 'Ohio Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.39' }, { name: 'Oklahoma Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.40' }, { name: 'Oregon Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.41' }, { name: 'Pennsylvania Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.42' }, { name: 'Rhode Island Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.44' }, { name: 'South Carolina Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.45' }, { name: 'South Dakota Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.46' }, { name: 'Tennessee Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.47' }, { name: 'Texas Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.48' }, { name: 'Utah Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.49' }, { name: 'Virginia Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.51' }, { name: 'Vermont Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.50' }, { name: 'Washington Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.53' }, { name: 'Wisconsin Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.55' }, { name: 'West Virginia Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.54' }, { name: 'Wyoming Driver\'s License', key: 'urn:oid:2.16.840.1.113883.4.3.56' } ]; const selectedOption = options.find(option => option.key === field.value); const value = selectedOption ? { key: selectedOption.key, name: selectedOption.name } : undefined; return ( { field.onChange(newValue.selectedItem.key); }}/> ); }}/> )} {/* period */}
{identifierIndex === identifiers.fields.length - 1 && ( )} ))} ); }; export default FhirIdentifier;