import React, { useState } from 'react'; import type { Encounter, Patient } from 'fhir/r4'; import { ExternalLink, Globe, Lock } from 'lucide-react'; import { ClinicalNoteEditor } from './ClinicalNoteEditor'; import { EncounterCarePlanPanel } from './EncounterCarePlanPanel'; import PatientNotes from '../../../components/PatientNotes/PatientNotes'; import { readTabMemory, writeTabMemory } from './encounterDocumentationTabMemory'; import { encounterHostReference, hostingRoleIds, mayWriteClinicalNote } from './encounterLifecycle'; import { useOptionalAuthServiceContext } from '../../../services'; import { useIsPhoneWidth } from '../../../hooks/useIsPhoneWidth'; import './EncounterDocumentation.css'; type Mode = 'public' | 'private' | 'care-plan'; interface Props { patient?: Patient; encounter?: Encounter; /** * E6 - pop the note into a floating window. * * Passed IN rather than read from the encounter context here, because this component also renders * inside the floating window and inside the meeting window, where popping out again is meaningless. * The control appears only where the caller supplies a way to do it. */ onPopOut?: () => void; } export const EncounterDocumentation: React.FC = ({ patient, encounter, onPopOut }) => { //: remember the tab per encounter. A remount ( pane pops out, docks back, minimises) // used to drop a practitioner who was mid-PRIVATE-note back onto the PUBLIC editor with no warning. const encounterId = encounter?.id; /* - THE CLINICAL NOTE BELONGS TO THE PRACTITIONER HOSTING THE VISIT. > Spencer, 2026-08-13: "Only the practitioner hosting the encounter should be using the actual clinical > notes piece, all other practitioners should just have private notes all separate from each other." A second clinician on the encounter - a colleague pulled in, a specialist on a collaboration visit - gets the Private tab only. That is not a lesser surface: private notes are per-author already, carry their own visibility model (`me-only` / `all-staff` / `roles`) and are enforced author-only server-side. Do not "helpfully" let them into the clinical note. One visit has one record note, and two people typing into it is what this ruling exists to prevent. A visit that names no host has no visit note here, and the server refuses one. */ // The OPTIONAL accessor, deliberately - the same rule learned on `ClinicalNoteEditor`. This // component IS the documentation surface; reading who the caller is only REFINES which tab it offers, so // it must never be able to blank the screen a practitioner is documenting a visit on. Absent unknown // `mayWriteClinicalNote` applies its own safe reading. const auth = useOptionalAuthServiceContext(); const currentUser = auth?.currentUser; const callerPractitionerId = currentUser?.fhir?.resourceType === 'Practitioner' ? currentUser.fhir.id : undefined; const ownRoleIds = hostingRoleIds(auth?.practitionerRoles ?? [], callerPractitionerId, encounter); const mayWriteClinical = mayWriteClinicalNote(encounter, callerPractitionerId, ownRoleIds); const hostless = !encounterHostReference(encounter); const [mode, setModeState] = useState( () => (mayWriteClinical ? readTabMemory(encounterId) ?? 'public' : 'private'), ); // Swap during render, not in an effect: an effect commits one frame late, and that frame would show the // previous encounter's tab under this one. Same reasoning as the draft swap in PatientNotes. const [tabOwner, setTabOwner] = useState(encounterId); if (tabOwner !== encounterId) { setTabOwner(encounterId); setModeState(mayWriteClinical ? readTabMemory(encounterId) ?? 'public' : 'private'); } const setMode = (next: Mode) => { setModeState(next); writeTabMemory(encounterId, next); }; // Never `public` for a non-host, whatever the remembered tab said. The memory predates this rule, so // a practitioner who was the host on a previous encounter would otherwise land straight in the clinical // editor on one where they are not. const isPublic = mode === 'public' && mayWriteClinical; // On a phone the tabs take the small button and the pop-out goes, as the phone frame draws the row. const isPhone = useIsPhoneWidth(); const tabClass = isPhone ? 'btn btn-blue-ghost-sm' : 'btn btn-blue-ghost'; // A PLAN IS CLINICAL AUTHORSHIP, so it is gated exactly as the clinical note is - same reasoning as the // line above. A practitioner who was the host on a previous encounter must not land on it here, because // the remembered tab predates the rule. const isCarePlan = mode === 'care-plan' && mayWriteClinical; return (
{/* toggle */}
{/* The clinical tab does not exist for a non-host — not disabled, ABSENT. A greyed control on a clinical surface invites a support call; the honest reading is that this is not their note. */} {mayWriteClinical && ( )} {/* The plan outlives the encounter, unlike either note. That is said by where it appears afterwards, not by making it awkward to write here. Gated with the clinical note because a plan is clinical authorship rather than a per-author note. */} {mayWriteClinical && ( )}
{/* 🚨 CARD 263 E6 — the window utility lives on the card it acts on, as an ICON. 📏 It was a full solid-blue button 147px above and 283px right of this card (card 261, B64), carrying the same fill as "Finish encounter" — the terminal action of the whole visit. ⛔ Icon-only is acceptable HERE and would not be for a clinical control: it is a window utility, its `aria-label` names it, and D6's "nothing important lives only behind an icon" is about things a patient must find, not about where a pane is docked. */} {onPopOut && !isPhone && ( )}
{/* LOUD mode banner — safety: the provider must never be unsure what they're writing to */}

{isPublic || isCarePlan ? <>Visible in patient's record : <>Private — org only, not shown to the patient}

{/* Says WHY there is one tab here, once, quietly. Without it the missing clinical tab reads as a permissions fault rather than as whose note it is. */} {!mayWriteClinical && (

{hostless ? 'This visit names no host, so its visit note cannot be written here. Your notes here are private to you.' : 'The visit note belongs to the practitioner hosting this encounter. Your notes here are private to you.'}

)}
{isCarePlan ? : isPublic ? : (patient?.id ? :

No patient.

)}
); };