// Coordonnées réelles du PatientDetail, ajoutées sans modifier la V5 figée.
const PatientContactDetailsBaseResume = TabResume;
const PatientSectionEditBaseHeader = PatientHeader;
const PatientSectionEditBaseClinicalSummary = clinicalSummaryBlock;
const PatientSectionEditBaseActionSheet = ActionSheet;
const PatientSectionEditController = (() => {
let patientId = null;
let openActions = null;
let pending = null;
return Object.freeze({
bind(nextPatientId, nextOpenActions) {
patientId = nextPatientId;
openActions = nextOpenActions;
},
request(nextPatientId, scope) {
if (nextPatientId !== patientId || typeof openActions !== 'function') return false;
pending = { patientId: nextPatientId, scope };
openActions();
return true;
},
peek(nextPatientId) {
return pending?.patientId === nextPatientId ? pending.scope : null;
},
consume(nextPatientId, fallback) {
const scope = pending?.patientId === nextPatientId ? pending.scope : fallback;
if (pending?.patientId === nextPatientId) pending = null;
return scope;
},
});
})();
window.VascoPatientSectionEdit = PatientSectionEditController;
function exposePatientSectionEditError(error) {
const message = error?.message || String(error || 'Erreur inconnue');
console.error('[vasco-patient-section-edit-global]', {
message,
stack: error?.stack || null,
});
window.setTimeout(() => {
if (document.getElementById('vasco-patient-section-debug')) return;
const panel = document.createElement('pre');
panel.id = 'vasco-patient-section-debug';
panel.setAttribute('role', 'alert');
panel.textContent = `Erreur dossier patient\n${message}`;
Object.assign(panel.style, {
position: 'fixed', inset: '24px', zIndex: '99999', padding: '16px',
background: '#fff', color: '#991b1b', border: '1px solid #991b1b',
whiteSpace: 'pre-wrap', fontSize: '12px', overflow: 'auto',
});
document.body.appendChild(panel);
}, 0);
}
window.addEventListener('error', (event) => exposePatientSectionEditError(event.error || event.message));
window.addEventListener('unhandledrejection', (event) => exposePatientSectionEditError(event.reason));
function PatientSectionEditIconButton({ label, onClick }) {
if (!onClick) return null;
const { IconButton } = window.VascoUI;
return (
);
}
// Où le patient est hospitalisé, d'un bloc
//
// L'en-tête du dossier ne portait que le lit, sous une pastille « LIT … » dont
// le mot est écrit en dur dans la référence gelée. Un lit ne se retrouve pas
// sans l'étage et la chambre : c'est dans cet ordre qu'on les cherche en
// arrivant dans le service, du plus grand au plus petit.
//
// La pastille gelée est reprise telle quelle, même bordure, même graisse, même
// famille mono : seul son texte change. La refabriquer aurait recopié un style
// que le design system porte déjà. Sans aucune des trois parties, la pastille
// sort de la ligne : elle affichait « LIT – » à tout patient ambulatoire.
function patientStayChipRow(row, patient) {
if (!React.isValidElement(row)) return row;
const children = React.Children.toArray(row.props.children);
const index = children.findIndex((child) => React.isValidElement(child)
&& child.type === 'span'
&& String(React.Children.toArray(child.props.children)[0] ?? '').startsWith('LIT'));
if (index < 0) return row;
const label = window.VascoPatientContactDetails.stayLabel(patient);
const next = label
? children.map((child, position) => (position === index
? React.cloneElement(child, { key: 'patient-stay' }, label) : child))
: children.filter((child, position) => position !== index);
return React.cloneElement(row, row.props, next);
}
// La même place, dite au même mot dans la liste dépliée.
//
// La fiche dépliée de la V5 gelée porte une ligne « Lit » remplie du seul
// `patient.bed`. Un lit ne se retrouve pas sans son étage ni sa chambre : la
// ligne porte désormais le séjour entier et s'appelle donc « Séjour ». Le
// libellé vient de la même fonction que la pastille de l'en-tête ; rien n'est
// recomposé ici. Sans aucune des trois parties, la ligne sort de la carte :
// elle disait « Lit – », c'est-à-dire une place vide, à tout dossier
// ambulatoire, le tiret du client n'étant pas celui que la référence filtre.
function patientStayFocusLabel(row) {
if (!React.isValidElement(row)) return null;
const [caption] = React.Children.toArray(row.props.children);
if (!React.isValidElement(caption) || caption.props.className !== 'mono') return null;
const [text] = React.Children.toArray(caption.props.children);
return typeof text === 'string' ? text : null;
}
function patientStayFocusRows(node, patient) {
if (!React.isValidElement(node) || typeof node.type !== 'string') return node;
const children = React.Children.toArray(node.props.children);
if (!children.length) return node;
const index = children.findIndex((child) => patientStayFocusLabel(child) === 'Lit');
if (index < 0) {
return React.cloneElement(node, node.props,
children.map((child) => patientStayFocusRows(child, patient)));
}
const label = window.VascoPatientContactDetails.stayLabel(patient);
if (!label) {
const kept = children.filter((child, position) => position !== index);
// La carte gelée n'existe que pour ses lignes. Quand la place était la
// dernière, la retirer laissait un cadre blanc vide sous le nom : on retire
// alors le cadre avec elle.
return kept.length ? React.cloneElement(node, node.props, kept) : null;
}
return React.cloneElement(node, node.props, children.map((child, position) => {
if (position !== index) return child;
const [caption, value] = React.Children.toArray(child.props.children);
return React.cloneElement(child, { key: 'patient-stay-row' }, [
React.cloneElement(caption, { key: 'caption' }, 'Séjour'),
React.cloneElement(value, { key: 'value' }, label),
]);
}));
}
const PatientStayBaseFocusDetails = FocusDetails;
function PatientStayFocusDetails(props) {
const rendered = PatientStayBaseFocusDetails(props);
return patientStayFocusRows(rendered, props.patient);
}
FocusDetails = PatientStayFocusDetails;
function PatientSectionalHeader(props) {
PatientSectionEditController.bind(props.p.id, props.onActions);
const rendered = PatientSectionEditBaseHeader(props);
const blocks = React.Children.toArray(rendered.props.children);
const canArchive = props.p.capabilities?.archive === true;
const topBar = blocks[0];
if (!canArchive && React.isValidElement(topBar)) {
const controls = React.Children.toArray(topBar.props.children)
.filter((control) => control?.props?.['aria-label'] !== 'Actions');
blocks[0] = React.cloneElement(topBar, topBar.props, controls);
}
const identity = blocks[1];
if (!React.isValidElement(identity)) return rendered;
const identityChildren = React.Children.toArray(identity.props.children);
identityChildren[0] = patientStayChipRow(identityChildren[0], props.p);
blocks[1] = React.cloneElement(identity, {
...identity.props,
style: { ...identity.props.style, position: 'relative', paddingRight: 64 },
}, [
...identityChildren,
PatientSectionEditController.request(props.p.id, 'identity')} />
,
]);
return React.cloneElement(rendered, rendered.props, blocks);
}
function PatientSectionalClinicalSummary(patient) {
const rendered = PatientSectionEditBaseClinicalSummary(patient);
const content = React.Children.toArray(rendered.props.children);
content[0] = (