import React, { useState, useEffect } from 'react'; import { Card, CardBody } from '../ui/wp-card'; import { Card as PanelCard } from '../ui/card'; import { InputControl } from '../ui/input-control'; import { Spacer } from '../ui/spacer'; import { TabPanel } from '../ui/tab-panel'; import { useFormContext, Controller, useFieldArray, useWatch } from 'react-hook-form'; import { IValueSet, useValueSetServiceContext } from '../../services'; import { FhirAddress, FhirCode, FhirCodeableConcept, FhirContactPoint, FhirHumanName, FhirIdentifier, PhotoAttachment } from '../../components'; import { useFhirPractitionerBroadcast } from '../../hooks'; import { Attachment, PractitionerRole } from 'fhir/r4'; import PractitionerQualification from '../../pages/DashboardPage/ProfilePage/EditProfilePage/EditPractitionerProfile/PractitionerQualification/PractitionerQualification.tsx'; import { holdsClinicalRole } from '../../utils/staffRoleCatalog.ts'; // - 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'; // TODO: restore fetchValueSet(valueSetUrls.languages) when server issue is resolved const LANGUAGE_OPTIONS: IValueSet[] = [ { code: "en", display: "English", system: "urn:ietf:bcp:47" }, { code: "es", display: "Spanish", system: "urn:ietf:bcp:47" }, { code: "fr", display: "French", system: "urn:ietf:bcp:47" }, { code: "de", display: "German", system: "urn:ietf:bcp:47" }, { code: "it", display: "Italian", system: "urn:ietf:bcp:47" }, { code: "pt", display: "Portuguese", system: "urn:ietf:bcp:47" }, { code: "zh", display: "Chinese", system: "urn:ietf:bcp:47" }, { code: "ja", display: "Japanese", system: "urn:ietf:bcp:47" }, { code: "ko", display: "Korean", system: "urn:ietf:bcp:47" }, { code: "ar", display: "Arabic", system: "urn:ietf:bcp:47" }, { code: "ru", display: "Russian", system: "urn:ietf:bcp:47" }, { code: "hi", display: "Hindi", system: "urn:ietf:bcp:47" }, { code: "vi", display: "Vietnamese", system: "urn:ietf:bcp:47" }, { code: "tl", display: "Tagalog", system: "urn:ietf:bcp:47" }, { code: "fr-CA", display: "French (Canada)", system: "urn:ietf:bcp:47" }, { code: "other", display: "Other", system: "urn:ietf:bcp:47" }, ]; interface FhirPractitionerProps { photo: Attachment | undefined; onPhotoChange: (photo: Attachment | undefined) => void; roles: PractitionerRole[]; disabled?: boolean; loginEmail?: string; } export const FhirPractitioner: React.FC = ({ photo, onPhotoChange, roles, disabled = false, loginEmail }) => { const { control } = useFormContext(); const { fetchValueSet, valueSetUrls } = useValueSetServiceContext(); const firstName = useWatch({ control, name: 'name.0' }) as { given?: string[]; family?: string } | undefined; const displayName = [firstName?.given?.[0], firstName?.family].filter(Boolean).join(' '); const [loading, setLoading] = useState(false); // value sets const [administrativeGender, setAdministrativeGender] = useState([]); const [languages] = useState(LANGUAGE_OPTIONS); const [qualifications, setQualifications] = useState([]); // THE SAME QUESTION THE NPI NAG ASKS, and it has to be, because the two are about one person. // // `staffFacts.isClinical` calls `holdsClinicalRole`, which asks whether the role holds the // Practitioner workspace ANYWHERE in its set. This gate used to ask `isPatientFacingPractitionerRole`, // which reads only the role's DEFAULT workspace - and that default falls back to whichever workspace // sorts first, so nobody chose it. Somebody holding Practitioner alongside another workspace was // therefore counted clinical by the readiness banner and denied the tab that answers it: nagged for // an NPI with no way to enter one. // // The two patient-facing call sites keep the default-workspace question deliberately - "which of // this person's roles does a patient see" is a different question from "does this person owe an NPI". const hasPractitionerRole = holdsClinicalRole(roles); const spacerMargin = '24px'; useFhirPractitionerBroadcast(); const nameFieldArray = useFieldArray({ control, name: 'name' }); const addressFieldArray = useFieldArray({ control, name: 'address' }); const telecomFieldArray = useFieldArray({ control, name: 'telecom' }); const identifierFieldArray = useFieldArray({ control, name: 'identifier' }); const npiFieldArray = useFieldArray({ control, name: 'npi' }); const qualificationFieldArray = useFieldArray({ control, name: 'qualification' }); useEffect(() => { const loadValueSets = async () => { setLoading(true); try { // const [administrativeGenderValueSet, languagesValueSet, qualificationsValueSet] = await Promise.all([ const [administrativeGenderValueSet, qualificationsValueSet] = await Promise.all([ fetchValueSet(valueSetUrls.administrativeGender), // fetchValueSet(valueSetUrls.languages), // temporarily disabled due to server issue fetchValueSet(valueSetUrls.qualifications) ]); setAdministrativeGender(administrativeGenderValueSet); // setLanguages(languagesValueSet); // temporarily disabled; using LANGUAGE_OPTIONS static fallback setQualifications(qualificationsValueSet); } catch (err) { console.error('Error loading value sets:', err); } finally { setLoading(false); } }; loadValueSets(); }, [fetchValueSet, valueSetUrls]); const renderContent = () => { let tabs = [ { name: 'name', title: 'Name' }, { name: 'info', title: 'Info' }, { name: 'address', title: 'Address' }, { name: 'contact', title: 'Contact Information' }, { name: 'identifiers', title: 'Identifiers' } ]; if (hasPractitionerRole) { tabs = [ ...tabs, { name: 'qualifications', title: 'Qualifications' }, { name: 'npi', title: 'NPI' } ]; } return ( {(tab) => { // name if (tab.name === 'name') { return (
); } // info if (tab.name === 'info') { return (
{/* login email — read-only, not part of the form payload */} {loginEmail !== undefined && ( <> undefined} help={ Used to sign in. Set when the account was created and cannot be changed here. }/> )} {/* birthDate */} ( )}/> {/* active */} ( )}/> {/* gender */} {/* communication */}
); } // address if (tab.name === 'address') { return (
); } // contact if (tab.name === 'contact') { return (
{/* Every notification the product sends resolves its recipient from THIS list, never from the sign-in account - so a person with nothing here signs in perfectly well and is silent for every notice. It took a CloudWatch read to discover that once; the screen says it now. */}
); } // qualifications if (tab.name === 'qualifications') { return (
); } // npi if (tab.name === 'npi') { return (
); } // identifiers if (tab.name === 'identifiers') { return (
); } }}
); }; return ( {renderContent()} ); }; export default FhirPractitioner;