// Patients en page de bureau : la liste et le dossier sur la même page.
//
// Cette couche ne remplace ni la liste ni le dossier du téléphone, et elle n'en
// recopie aucune logique. Le volet de gauche monte l'écran Patients tel qu'il
// est : c'est lui qui tient la recherche serveur, sa cadence de frappe, le tri,
// les filtres, le compte du serveur et le changement de vue. Le volet de droite
// monte `PatientDetail` tel qu'il est, avec ses onglets et ses sections.
//
// Une seule chose change au bureau, et c'est de l'affichage : la pile de cartes
// Wallet, faite pour le pouce, devient une liste de lignes qui tient dans un
// volet de 360 px et montre laquelle est ouverte. La donnée qu'elle reçoit est
// déjà cherchée, triée et filtrée par l'écran ; cette couche n'en refait rien.
//
// Sous le seuil de bureau, rien de tout cela n'est monté : la coque rend la
// liste puis le dossier en plein écran, comme aujourd'hui.
// Dernière version enveloppée de chaque symbole, capturée après toutes les
// couches produit (vues filtrées, suivi, documents, notes, statistiques).
const VASCO_PATIENTS_DESKTOP_LIST = PatientFolders;
const VASCO_PATIENTS_DESKTOP_DETAIL = PatientDetail;
const VASCO_PATIENTS_DESKTOP_CARDS = WalletCards;
// Les gestes que la coque téléphone donne au répertoire : ouvrir un dossier,
// créer un patient, ouvrir une note, ouvrir un rendez-vous. Sur la route
// `patient`, la coque ne les donne plus – elle rend un dossier, pas une liste –
// alors que la page de bureau montre les deux ensemble. On garde donc ceux
// qu'elle vient de donner. Ce sont des fermetures sur les `setState` et les refs
// de la coque, stables par construction : aucune valeur de rendu n'y est
// capturée. Rien n'est recréé ici, rien n'est deviné.
const VASCO_PATIENTS_DESKTOP_SHELL = {
accent: null, onOpenPatient: null, onNewPatient: null,
onNewNote: null, onNewAppointment: null, onTabChange: null, onBack: null,
};
// Dossier actuellement ouvert, pour que la ligne correspondante le montre. C'est
// de l'affichage : la coque reste seule à savoir quel dossier est chargé.
const VASCO_PATIENTS_DESKTOP_SELECTION = { id: null };
function vascoPatientsDesktopDisplayName(patient) {
if (!patient) return '';
const last = String(patient.last || '');
const first = String(patient.first || '');
return `${last.toUpperCase()} ${first}`.trim();
}
function vascoPatientsDesktopSummary(patient) {
if (!patient) return null;
const age = Number.isFinite(patient.age) ? `${patient.age} ans` : null;
return [age, patient.summary || null].filter(Boolean).join(' · ') || null;
}
// Vignette d'identité : le même dessin que la liste du téléphone, réduit à ce
// qu'une ligne de 48 px peut porter.
function VascoPatientsDesktopAvatar({ patient, selected }) {
const tokens = window.VascoUI.tokens;
const initials = `${String(patient.first || '?')[0]}${String(patient.last || '?')[0]}`;
return {initials.toUpperCase()};
}
// Où le patient est hospitalisé, au bout de la ligne de soutien de sa rangée. Le
// libellé vient de la seule fonction qui sache le composer, celle de la fiche et
// de la liste du téléphone : le bureau n'en recopie pas la règle. Sans place, pas
// de pastille.
//
// Il partage la ligne du résumé et non celle du nom : dans un volet de 360 px,
// une place à trois parties posée au bout de la rangée entière abrégeait le nom
// du patient. Le résumé, lui, s'abrège déjà et se relit dans le dossier.
function VascoPatientsDesktopStay({ patient }) {
const tokens = window.VascoUI.tokens;
const label = window.VascoPatientContactDetails.stayLabel(patient);
if (!label) return null;
return {label};
}
// La présentation en lignes de ce que l'écran a déjà cherché, trié et filtré.
// Elle ne connaît ni requête, ni cadence de frappe, ni critère : elle reçoit la
// liste et les gestes, exactement comme la pile de cartes qu'elle remplace.
function VascoPatientsDesktopRoster({ patients, onOpenPatient, onNewPatient, selectedId }) {
const tokens = window.VascoUI.tokens;
const { Button, InteractiveRow } = window.VascoUI;
return
;
}
// La pile de cartes Wallet est un geste de pouce : une carte par écran, feuilletée
// au doigt. Dans un volet de 360 px elle n'a plus de sens, et la liste de lignes
// prend sa place. Elle reçoit exactement les mêmes propriétés, donc exactement la
// même donnée : l'écran garde la recherche, le tri et les filtres.
WalletCards = function VascoPatientCardsForLayout(props) {
const desktop = window.VascoUI.useLayout() === 'desktop';
// Appel inconditionnel de la couche de filtre : elle reste la seule a lire le
// critere et a reduire la liste. On lit la liste qu'elle a reduite au lieu
// d'appliquer le filtre une seconde fois.
const filtered = VASCO_PATIENTS_DESKTOP_CARDS(props);
if (!desktop) return filtered;
const patients = React.isValidElement(filtered) && Array.isArray(filtered.props.patients)
? filtered.props.patients : props.patients;
return ;
};
window.WalletCards = WalletCards;
// Le retour du dossier vit dans l'en-tête du dossier, pas dans le dossier
// lui-même : c'est donc cet en-tête qu'on corrige, une fois, pour toutes les
// compositions de bureau. Sous le seuil il rend exactement l'original.
const VascoLegacyPatientHeader = PatientHeader;
PatientHeader = function VascoPatientsDesktopHeader(props) {
const desktop = window.VascoUI.useLayout() === 'desktop';
const tree = VascoLegacyPatientHeader(props);
if (!desktop) return tree;
return window.VascoUI.hideAction(tree, props.onBack);
};
window.PatientHeader = PatientHeader;
// Le dossier tel qu'il est, moins le retour vers une liste déjà visible. La
// commande est repérée par son gestionnaire, pas par sa place : la référence
// figée n'est pas touchée et son geste de suppression continue de désélectionner.
function VascoPatientsDesktopDossier({ onDeselect, ...rest }) {
const tree = VASCO_PATIENTS_DESKTOP_DETAIL({ ...rest, onBack: onDeselect });
return window.VascoUI.hideAction(tree, onDeselect);
}
// Volet de droite au repos : une phrase, rien d'autre. Pas d'illustration, pas
// de bouton qui ferait semblant d'ouvrir quelque chose.
function VascoPatientsDesktopEmptyDetail() {
const tokens = window.VascoUI.tokens;
return
Choisissez un patient dans la liste pour ouvrir son dossier.
;
}
function VascoPatientsDesktopPage({ accent, patientId = null, patientTab = 'resume',
onOpenPatient, onNewPatient, onNewAppointment, onNewNote, onDeselect, onOpenChat,
onTabChange, onBack, runtime = null }) {
const tokens = window.VascoUI.tokens;
const { SplitView } = window.VascoUI;
// La sélection est posée avant que le volet de gauche ne se rende : ses lignes
// la lisent au moment de leur propre rendu, qui vient après celui-ci.
VASCO_PATIENTS_DESKTOP_SELECTION.id = patientId;
return
}
detail={patientId
?
: } />
;
}
window.VascoPatientsDesktop = VascoPatientsDesktopPage;
// Au bureau, le répertoire et le dossier sont la même page : le dossier ouvert
// est un patient choisi dans une liste visible, pas un autre écran. Les deux
// symboles de la coque téléphone désignent donc le même composant, et React ne
// remonte rien quand la route passe de l'un à l'autre : le volet de gauche garde
// sa recherche, son tri et son défilement. Sous le seuil, chacun rend l'écran
// que la coque attend de lui.
const VascoPatientsRouteForLayout = function VascoPatientsRouteForLayout(props) {
const desktop = window.VascoUI.useLayout() === 'desktop';
// La coque rend un dossier quand elle nomme un patient, sinon le répertoire.
const dossier = Object.prototype.hasOwnProperty.call(props, 'patientId');
const shell = VASCO_PATIENTS_DESKTOP_SHELL;
React.useEffect(() => {
if (!desktop || dossier) return;
shell.accent = props.accent;
shell.onOpenPatient = props.onOpenPatient || null;
shell.onNewPatient = props.onNewPatient || null;
shell.onNewNote = props.onNewNote || null;
shell.onNewAppointment = props.onNewAppointment || null;
shell.onTabChange = props.onTabChange || null;
shell.onBack = props.onBack || null;
});
if (!desktop) {
return dossier
?
: ;
}
if (!dossier) {
return {}} onOpenChat={props.onOpenChat} />;
}
return ;
};
PatientFolders = VascoPatientsRouteForLayout;
PatientDetail = VascoPatientsRouteForLayout;
window.PatientFolders = PatientFolders;
window.PatientDetail = PatientDetail;