// Patient dataset — realistic vascular patients with full medical context. const PATIENTS_FULL = [ { id: 'p-2210', last: 'MARTIN', first: 'Jean', civ: 'M.', sex: 'M', age: 72, ddn: '03/11/1953', phone: '06 14 22 87 03', email: 'j.martin@gmail.com', address: '14 rue Lacordaire, 75015 Paris', secu: '1 53 11 75 105 124 33', mt: 'Dr. Vidal, Hervé', ipp: '2210·44871', status: 'hosp', // vivant, decede, rea, hosp, palliatif, ambu bed: 'CV2 · 412', summary: 'AOMI stade IIb G — pontage fémoro-poplité sus-géniculé J0', flags: ['AVK', 'HTA', 'Tabagisme actif'], crsCount: 14, nextRdv: { when: 'AUJ · 14:30', what: 'Visite post-op J0' }, pinned: ['Allergie Pénicilline (urticaire 2018)', 'Refuse les transfusions'], blood: 'A+', allergies: ['Pénicilline', 'Iode (modéré)'], treatments: ['Warfarine 5mg', 'Atorvastatine 40mg', 'Aspirine 75mg'], atcdFam: ['Père : IDM 65 ans', 'Mère : AVC 78 ans'], atcdPerso: ['HTA depuis 2008', 'Tabagisme 40 PA, sevré 2020', 'Pontage coronarien 2015'], documents: [ { d: '24/05/26', label: 'CRO — Pontage fémoro-poplité G', type: 'CRO', tag: 'live' }, { d: '22/05/26', label: 'CRC — Consultation pré-op', type: 'CRC' }, { d: '12/04/26', label: 'Echo-doppler artériel MI', type: 'EXAM' }, { d: '03/03/26', label: 'Angio-TDM aorto-iliaque + MI', type: 'IMG' }, { d: '14/02/26', label: 'CRC — Suivi annuel', type: 'CRC' }, { d: '08/11/25', label: 'CRH — Sortie pontage coronarien', type: 'CRH' }, ], rdvs: [ { d: '24/05/26', t: '14:30', label: 'Visite post-op J0', state: 'today' }, { d: '26/05/26', t: '09:00', label: 'Doppler de contrôle J2', state: 'planned' }, { d: '24/06/26', t: '11:15', label: 'Consultation 1 mois', state: 'planned' }, ], notes: [ { pinned: true, d: '22/05/26', text: 'Allergie Pénicilline (urticaire 2018) — confirmée par le patient.' }, { pinned: true, d: '12/04/26', text: 'Refuse les transfusions sauf urgence vitale.' }, { pinned: false, d: '03/03/26', text: 'Marche avec canne depuis 6 mois, claudication à 60m.' }, { pinned: false, d: '14/02/26', text: 'Bon observant pour AVK. INR cible 2-3, dernier 2.4.' }, ], stats: { crs: 14, rdvs: 28, lastVisit: '24/05/26', firstVisit: '11/05/2018' }, }, { id: 'p-1987', last: 'DURAND', first: 'Hélène', civ: 'Mme', sex: 'F', age: 68, ddn: '17/02/1958', phone: '06 88 41 09 22', email: 'h.durand58@orange.fr', address: '7 av. de la République, 92100 Boulogne', secu: '2 58 02 92 012 048 17', mt: 'Dr. Lemoine, Sophie', ipp: '1987·22014', status: 'hosp', bed: 'CV1 · 308', summary: 'Sténose carotide D 78% symptomatique — endartériectomie J1', flags: ['AAP', 'Diabète T2'], crsCount: 8, nextRdv: { when: 'AUJ · 16:00', what: 'Doppler de contrôle' }, blood: 'O+', allergies: [], treatments: ['Clopidogrel 75mg', 'Metformine 1000mg ×2', 'Atorvastatine 20mg'], atcdFam: ['Frère : AVC 71 ans'], atcdPerso: ['Diabète T2 depuis 2014', 'AIT carrefour 02/05/26'], }, { id: 'p-3041', last: 'BERNARD', first: 'Paul', civ: 'M.', sex: 'M', age: 79, ddn: '22/06/1946', phone: '06 02 55 17 80', email: '', address: '22 bd Voltaire, 75011 Paris', secu: '1 46 06 75 511 022 89', mt: 'Dr. Ferrand, Luc', ipp: '3041·90112', status: 'ambu', bed: '—', summary: 'AAA infra-rénal 54mm — discussion staff EVAR vs chirurgie', flags: ['AOD', 'BPCO'], crsCount: 5, nextRdv: { when: '28/05 · 11:00', what: 'Staff vasculaire' }, blood: 'B+', allergies: ['Iode'], treatments: ['Apixaban 5mg ×2', 'Tiotropium inhalation'], }, { id: 'p-4128', last: 'LEFÈVRE', first: 'Denise', civ: 'Mme', sex: 'F', age: 81, ddn: '04/09/1944', phone: '06 19 33 02 11', email: 'denise.lefevre@laposte.net', address: '5 rue des Lilas, 94300 Vincennes', secu: '2 44 09 94 037 011 22', mt: 'Dr. Vidal, Hervé', ipp: '4128·11890', status: 'ambu', bed: '—', summary: 'Pontage fémoro-jambier postérieur J+5 — sortie', flags: ['HBPM', 'Ostéoporose'], crsCount: 11, nextRdv: { when: '24/06 · 10:30', what: 'Consultation 1 mois' }, }, { id: 'p-5023', last: 'PETIT', first: 'Aline', civ: 'Mme', sex: 'F', age: 56, ddn: '12/03/1969', phone: '06 71 88 09 14', email: 'aline.petit@gmail.com', address: '11 rue Jean Moulin, 93100 Montreuil', secu: '2 69 03 93 044 091 04', mt: 'Dr. Roussel, Antoine', ipp: '5023·77231', status: 'ambu', bed: '—', summary: 'FAV radio-céphalique D — suivi J+30', flags: ['Insuffisance rénale stade 5'], crsCount: 22, nextRdv: { when: 'AUJ · 13:00', what: 'Suivi FAV' }, }, { id: 'p-5511', last: 'GARNIER', first: 'Robert', civ: 'M.', sex: 'M', age: 64, ddn: '08/07/1961', phone: '06 28 14 77 03', email: 'robert.garnier@free.fr', address: '3 place du Marché, 78000 Versailles', secu: '1 61 07 78 022 123 71', mt: 'Dr. Becker, Anne', ipp: '5511·30221', status: 'ambu', bed: '—', summary: 'AOMI stade IIa — bilan', flags: ['Tabagisme', 'Diabète T2'], crsCount: 3, nextRdv: { when: 'AUJ · 13:30', what: 'Consultation' }, }, { id: 'p-6172', last: 'ROBERT', first: 'Lucas', civ: 'M.', sex: 'M', age: 47, ddn: '19/11/1978', phone: '06 55 09 28 71', email: 'lucas.robert78@gmail.com', address: '88 rue de Charenton, 75012 Paris', secu: '1 78 11 75 122 089 14', mt: 'Dr. Tran, Vinh', ipp: '6172·44017', status: 'rea', bed: 'REA · 12', summary: 'Ischémie aiguë MI G — embolectomie en urgence', flags: ['ACFA', 'AVK arrêté'], crsCount: 1, nextRdv: { when: 'AUJ · 14:00', what: 'Bloc 5 — embolectomie' }, }, { id: 'p-6890', last: 'FAURE', first: 'Sylvie', civ: 'Mme', sex: 'F', age: 71, ddn: '02/05/1955', phone: '06 14 78 22 91', email: 'sylvie.faure@yahoo.fr', address: '17 rue du 11 Novembre, 95100 Argenteuil', secu: '2 55 05 95 122 014 88', mt: 'Dr. Lemoine, Sophie', ipp: '6890·22014', status: 'ambu', bed: '—', summary: 'Sténose iliaque commune G — pré-op stent', flags: ['HTA'], crsCount: 4, nextRdv: { when: 'AUJ · 16:00', what: 'Pré-op stent iliaque' }, }, { id: 'p-7701', last: 'LEMAIRE', first: 'Christian', civ: 'M.', sex: 'M', age: 58, ddn: '24/01/1967', phone: '06 09 22 81 47', email: 'c.lemaire67@orange.fr', address: '42 av. Foch, 92500 Rueil-Malmaison', secu: '1 67 01 92 064 042 77', mt: 'Dr. Roussel, Antoine', ipp: '7701·90142', status: 'ambu', bed: '—', summary: 'Création FAV radio-céphalique D — pré-dialyse', flags: ['IRC stade 5'], crsCount: 9, nextRdv: { when: 'AUJ · 17:00', what: 'Bloc 4 — FAV' }, }, { id: 'p-8044', last: 'MOREAU', first: 'Jacqueline', civ: 'Mme', sex: 'F', age: 88, ddn: '11/12/1937', phone: '06 71 03 14 22', email: '', address: 'EHPAD Les Tilleuls, 91300 Massy', secu: '2 37 12 91 011 047 53', mt: 'Dr. Bonnet, Patricia', ipp: '8044·14710', status: 'palliatif', bed: '—', summary: 'Ischémie chronique critique MI D — soins palliatifs', flags: ['DNR', 'Démence Alzheimer'], crsCount: 6, nextRdv: null, }, { id: 'p-8902', last: 'GIRARD', first: 'Michel', civ: 'M.', sex: 'M', age: 75, ddn: '30/04/1950', phone: '06 12 89 71 03', email: '', address: '6 rue des Acacias, 78180 Montigny-le-Bretonneux', secu: '1 50 04 78 092 044 11', mt: 'Dr. Vidal, Hervé', ipp: '8902·71030', status: 'decede', bed: '—', summary: 'AAA rompu — décédé en peropératoire 14/03/26', flags: [], crsCount: 7, nextRdv: null, }, { id: 'p-9211', last: 'NGUYEN', first: 'Linh', civ: 'Mme', sex: 'F', age: 52, ddn: '18/10/1973', phone: '06 88 14 02 27', email: 'linh.nguyen@gmail.com', address: '24 rue de Belleville, 75020 Paris', secu: '2 73 10 75 120 088 04', mt: 'Dr. Ferrand, Luc', ipp: '9211·02274', status: 'ambu', bed: '—', summary: 'Varices MI bilatérales — bilan veineux', flags: [], crsCount: 2, nextRdv: { when: '03/06 · 09:30', what: 'Consultation' }, }, { id: 'p-1001', last: 'ROUSSEAU', first: 'Marc', civ: 'M.', sex: 'M', age: 63, ddn: '15/08/1962', phone: '06 11 22 33 44', email: '', address: '8 rue Pasteur, 75005 Paris', secu: '1 62 08 75 005 011 22', mt: 'Dr. Vidal, Hervé', ipp: '1001·22110', status: 'ambu', bed: '—', summary: 'Claudication intermittente MI D', flags: ['HTA'], crsCount: 4, nextRdv: { when: '05/06 · 10:00', what: 'Consultation' } }, { id: 'p-1002', last: 'LAMBERT', first: 'Catherine', civ: 'Mme', sex: 'F', age: 74, ddn: '22/01/1952', phone: '06 22 33 44 55', email: '', address: '3 av. Foch, 92000 Nanterre', secu: '2 52 01 92 011 044 11', mt: 'Dr. Lemoine, Sophie', ipp: '1002·33210', status: 'hosp', bed: 'CV1 · 205', summary: 'Sténose carotide G 82%', flags: ['AVK', 'Diabète T2'], crsCount: 9, nextRdv: { when: 'AUJ · 11:00', what: 'Doppler' } }, { id: 'p-1003', last: 'MERCIER', first: 'François', civ: 'M.', sex: 'M', age: 69, ddn: '03/04/1957', phone: '06 33 44 55 66', email: '', address: '12 bd Haussmann, 75008 Paris', secu: '1 57 04 75 008 033 22', mt: 'Dr. Ferrand, Luc', ipp: '1003·11420', status: 'ambu', bed: '—', summary: 'Suivi pontage aorto-bifémoral', flags: ['AOD'], crsCount: 12, nextRdv: { when: '10/06 · 14:00', what: 'Suivi' } }, { id: 'p-1004', last: 'BONNET', first: 'Marie', civ: 'Mme', sex: 'F', age: 58, ddn: '17/11/1967', phone: '06 44 55 66 77', email: '', address: '9 rue Victor Hugo, 93200 Saint-Denis', secu: '2 67 11 93 200 055 33', mt: 'Dr. Roussel, Antoine', ipp: '1004·55320', status: 'ambu', bed: '—', summary: 'Varices récidivantes MI bilatéral', flags: [], crsCount: 6, nextRdv: { when: '12/06 · 09:00', what: 'Echo-doppler' } }, { id: 'p-1005', last: 'FOURNIER', first: 'Jacques', civ: 'M.', sex: 'M', age: 82, ddn: '28/06/1943', phone: '06 55 66 77 88', email: '', address: '15 rue Danton, 94200 Ivry', secu: '1 43 06 94 200 044 77', mt: 'Dr. Vidal, Hervé', ipp: '1005·77410', status: 'ambu', bed: '—', summary: 'AAA 48mm — surveillance', flags: ['BPCO', 'HTA'], crsCount: 8, nextRdv: { when: '15/06 · 11:30', what: 'Angio-TDM' } }, { id: 'p-1006', last: 'MOREL', first: 'Isabelle', civ: 'Mme', sex: 'F', age: 66, ddn: '09/03/1960', phone: '06 66 77 88 99', email: '', address: '7 place de la Mairie, 78100 Saint-Germain', secu: '2 60 03 78 100 077 44', mt: 'Dr. Lemoine, Sophie', ipp: '1006·88520', status: 'ambu', bed: '—', summary: 'TVP MI G — anticoagulation', flags: ['AOD'], crsCount: 3, nextRdv: { when: '08/06 · 15:00', what: 'Contrôle INR' } }, { id: 'p-1007', last: 'SIMON', first: 'André', civ: 'M.', sex: 'M', age: 77, ddn: '14/09/1948', phone: '06 77 88 99 00', email: '', address: '20 rue Molière, 75001 Paris', secu: '1 48 09 75 001 088 55', mt: 'Dr. Ferrand, Luc', ipp: '1007·99630', status: 'ambu', bed: '—', summary: 'Ischémie effort MI D — bilan', flags: ['Tabagisme'], crsCount: 5, nextRdv: { when: '20/06 · 10:00', what: 'Épreuve marche' } }, { id: 'p-1008', last: 'HENRY', first: 'Françoise', civ: 'Mme', sex: 'F', age: 71, ddn: '05/12/1954', phone: '06 88 99 00 11', email: '', address: '4 rue des Roses, 92400 Courbevoie', secu: '2 54 12 92 400 099 66', mt: 'Dr. Roussel, Antoine', ipp: '1008·00740', status: 'hosp', bed: 'CV2 · 318', summary: 'Endartériectomie carotide D J0', flags: ['AAP'], crsCount: 7, nextRdv: { when: 'AUJ · 16:30', what: 'Post-op' } }, { id: 'p-1009', last: 'ROUX', first: 'Pierre', civ: 'M.', sex: 'M', age: 60, ddn: '21/07/1965', phone: '06 99 00 11 22', email: '', address: '11 rue Gambetta, 93100 Montreuil', secu: '1 65 07 93 100 000 77', mt: 'Dr. Vidal, Hervé', ipp: '1009·11850', status: 'ambu', bed: '—', summary: 'FAV huméro-céphalique — suivi', flags: ['IRC stade 4'], crsCount: 15, nextRdv: { when: '06/06 · 13:00', what: 'Suivi FAV' } }, { id: 'p-1010', last: 'DAVID', first: 'Monique', civ: 'Mme', sex: 'F', age: 85, ddn: '30/01/1941', phone: '06 00 11 22 33', email: '', address: '2 rue de la Paix, 75002 Paris', secu: '2 41 01 75 002 011 88', mt: 'Dr. Bonnet, Patricia', ipp: '1010·22960', status: 'ambu', bed: '—', summary: 'Artérite oblitérante stade III', flags: ['HTA', 'Diabète T2'], crsCount: 10, nextRdv: null }, { id: 'p-1011', last: 'CHEVALIER', first: 'René', civ: 'M.', sex: 'M', age: 73, ddn: '12/05/1953', phone: '06 11 22 33 00', email: '', address: '18 rue Leclerc, 91000 Évry', secu: '1 53 05 91 000 022 99', mt: 'Dr. Ferrand, Luc', ipp: '1011·33070', status: 'ambu', bed: '—', summary: 'Anévrysme poplité D 32mm', flags: [], crsCount: 4, nextRdv: { when: '22/06 · 09:30', what: 'Consultation' } }, { id: 'p-1012', last: 'MARTINEZ', first: 'Carmen', civ: 'Mme', sex: 'F', age: 55, ddn: '08/08/1970', phone: '06 22 33 00 11', email: '', address: '6 rue Voltaire, 75011 Paris', secu: '2 70 08 75 011 033 00', mt: 'Dr. Lemoine, Sophie', ipp: '1012·44180', status: 'ambu', bed: '—', summary: 'Insuffisance veineuse chronique', flags: [], crsCount: 3, nextRdv: { when: '25/06 · 14:30', what: 'Echo-doppler' } }, { id: 'p-1013', last: 'THOMAS', first: 'Georges', civ: 'M.', sex: 'M', age: 80, ddn: '25/02/1946', phone: '06 33 00 11 22', email: '', address: '14 av. Jean Jaurès, 94800 Villejuif', secu: '1 46 02 94 800 000 11', mt: 'Dr. Vidal, Hervé', ipp: '1013·55290', status: 'rea', bed: 'REA · 08', summary: 'Rupture AAA — post-op EVAR', flags: ['AVK arrêté'], crsCount: 2, nextRdv: null }, { id: 'p-1014', last: 'RICHARD', first: 'Nicole', civ: 'Mme', sex: 'F', age: 67, ddn: '19/06/1958', phone: '06 00 11 22 00', email: '', address: '9 rue Raspail, 92300 Levallois', secu: '2 58 06 92 300 011 22', mt: 'Dr. Roussel, Antoine', ipp: '1014·66300', status: 'ambu', bed: '—', summary: 'Sténose iliaque primitive D', flags: ['HTA'], crsCount: 5, nextRdv: { when: '18/06 · 10:00', what: 'Pré-op stent' } }, { id: 'p-1015', last: 'DUBOIS', first: 'Henri', civ: 'M.', sex: 'M', age: 70, ddn: '01/10/1955', phone: '06 11 00 22 33', email: '', address: '5 bd Saint-Michel, 75005 Paris', secu: '1 55 10 75 005 000 33', mt: 'Dr. Ferrand, Luc', ipp: '1015·77410', status: 'ambu', bed: '—', summary: 'AOMI stade IIb bilatéral', flags: ['Tabagisme', 'HTA'], crsCount: 11, nextRdv: { when: '28/06 · 11:00', what: 'Bilan' } }, { id: 'p-1016', last: 'LEROUX', first: 'Jeanne', civ: 'Mme', sex: 'F', age: 78, ddn: '07/04/1948', phone: '06 22 00 33 44', email: '', address: '1 rue du Château, 78000 Versailles', secu: '2 48 04 78 000 022 44', mt: 'Dr. Bonnet, Patricia', ipp: '1016·88520', status: 'ambu', bed: '—', summary: 'Ulcère veineux jambe G', flags: ['HBPM'], crsCount: 8, nextRdv: { when: '04/06 · 09:00', what: 'Pansement' } }, { id: 'p-1017', last: 'GARCIA', first: 'Luis', civ: 'M.', sex: 'M', age: 62, ddn: '13/12/1963', phone: '06 33 00 44 55', email: '', address: '22 rue de la Liberté, 93300 Aubervilliers', secu: '1 63 12 93 300 033 55', mt: 'Dr. Vidal, Hervé', ipp: '1017·99630', status: 'ambu', bed: '—', summary: 'Pontage fémoro-poplité G — suivi 6 mois', flags: [], crsCount: 7, nextRdv: { when: '30/06 · 14:00', what: 'Suivi' } }, { id: 'p-1018', last: 'PERRIN', first: 'Suzanne', civ: 'Mme', sex: 'F', age: 83, ddn: '26/03/1943', phone: '06 44 00 55 66', email: '', address: '10 rue Marbeuf, 75008 Paris', secu: '2 43 03 75 008 044 66', mt: 'Dr. Lemoine, Sophie', ipp: '1018·00740', status: 'palliatif', bed: '—', summary: 'Ischémie critique MI D — soins palliatifs', flags: ['DNR'], crsCount: 5, nextRdv: null }, ]; // Status meta: label, color treatment const STATUS_META = { vivant: { label: 'Vivant', dot: '#0a0a0a' }, ambu: { label: 'Ambulatoire', dot: '#0a0a0a' }, hosp: { label: 'Hospitalisé', dot: '#0a0a0a', invert: true }, rea: { label: 'Réa', dot: '#0a0a0a', accent: true }, palliatif: { label: 'Soins palliat.', dot: '#737373' }, decede: { label: 'Décédé', dot: '#737373', strike: true }, }; // ───────────────────────────────────────────────────────────── // Historiques documentaires — un seul patient (MARTIN) en avait un écrit à la // main ; l'onglet Documents des 30 autres était vide, ce qui se voyait en démo. // // Plutôt qu'un pavé de 30 listes, on les dérive du dossier : la pathologie // choisit la famille de documents, et l'étape du parcours décide de ce qui a // pu être produit. Un patient au stade « bilan » n'a pas de compte rendu // opératoire ; un patient à J+30 en a un, daté d'il y a 30 jours, avec du // suivi par-dessus. Le nombre de pièces colle à son `crsCount`, affiché sur la // page Patients — les deux ne peuvent donc pas diverger. // ───────────────────────────────────────────────────────────── // Trois temps par famille : `pre` (mise au point), `op` (le geste et la // sortie), `post` (le suivi). Le premier motif qui matche gagne. const PD_DOC_FAMILIES = [ { test: /carotid|TSA|AIT/i, pre: [ { t: 'CRC', l: 'CRC — Consultation pré-opératoire' }, { t: 'EXAM', l: 'Écho-doppler des troncs supra-aortiques' }, { t: 'IMG', l: 'Angio-TDM des TSA' }, { t: 'EXAM', l: 'Bilan biologique pré-opératoire' }, { t: 'IMG', l: 'IRM cérébrale de diffusion' }, { t: 'CRC', l: 'CRC — Consultation neurovasculaire' }, ], op: [ { t: 'CRO', l: 'CRO — Endartériectomie carotidienne' }, { t: 'CRH', l: "CRH — Sortie d'hospitalisation" }, ], post: [ { t: 'EXAM', l: 'Écho-doppler de contrôle post-opératoire' }, { t: 'CRC', l: 'CRC — Suivi neurovasculaire' }, ] }, { test: /AAA|aorte|anévrysme aortique|EVAR/i, pre: [ { t: 'IMG', l: 'Angio-TDM aortique avec mesures' }, { t: 'CRC', l: 'CRC — Discussion staff EVAR / chirurgie ouverte' }, { t: 'EXAM', l: 'Écho-doppler aortique de surveillance' }, { t: 'CRC', l: "CRC — Consultation d'annonce" }, { t: 'EXAM', l: 'Épreuve fonctionnelle respiratoire' }, { t: 'EXAM', l: 'Bilan cardiologique pré-opératoire' }, ], op: [ { t: 'CRO', l: 'CRO — Endoprothèse aortique (EVAR)' }, { t: 'CRH', l: 'CRH — Sortie de chirurgie aortique' }, ], post: [ { t: 'IMG', l: 'Angio-TDM de contrôle post-EVAR' }, { t: 'CRC', l: "CRC — Suivi d'endoprothèse" }, ] }, { test: /FAV|dialyse|abord vasculaire/i, pre: [ { t: 'CRC', l: 'CRC — Consultation pré-dialyse' }, { t: 'EXAM', l: 'Cartographie veineuse du membre supérieur' }, { t: 'EXAM', l: 'Bilan néphrologique conjoint' }, { t: 'CRC', l: "CRC — Information sur les modes d'épuration" }, { t: 'EXAM', l: 'Écho-doppler artériel du membre supérieur' }, { t: 'CRC', l: 'CRC — Consultation d’anesthésie' }, ], op: [ { t: 'CRO', l: 'CRO — Création de FAV radio-céphalique' }, { t: 'CRH', l: 'CRH — Sortie après création de FAV' }, ], post: [ { t: 'EXAM', l: 'Écho-doppler de maturation de FAV' }, { t: 'CRC', l: 'CRC — Suivi du débit de FAV' }, ] }, { test: /varice|veineu|TVP|phlébit/i, pre: [ { t: 'EXAM', l: 'Écho-doppler veineux des membres inférieurs' }, { t: 'CRC', l: 'CRC — Consultation phlébologique' }, { t: 'EXAM', l: 'Cartographie veineuse pré-opératoire' }, { t: 'CRC', l: 'CRC — Consultation de suivi veineux' }, { t: 'IMG', l: 'Phlébographie des membres inférieurs' }, { t: 'EXAM', l: 'Bilan de thrombophilie' }, ], op: [ { t: 'CRO', l: 'CRO — Stripping de la grande saphène' }, { t: 'CRH', l: 'CRH — Sortie de chirurgie veineuse' }, ], post: [ { t: 'CRC', l: 'CRC — Suivi post-opératoire veineux' }, { t: 'EXAM', l: 'Écho-doppler veineux de contrôle' }, ] }, { test: /iliaque|stent/i, pre: [ { t: 'IMG', l: 'Angio-TDM aorto-iliaque' }, { t: 'CRC', l: 'CRC — Consultation pré-interventionnelle' }, { t: 'EXAM', l: 'Index de pression systolique (IPS)' }, { t: 'IMG', l: 'Artériographie diagnostique' }, { t: 'EXAM', l: 'Écho-doppler artériel des membres inférieurs' }, { t: 'CRC', l: 'CRC — Consultation de claudication' }, ], op: [ { t: 'CRO', l: 'CRO — Angioplastie iliaque avec stent' }, { t: 'CRH', l: "CRH — Sortie d'angioplastie" }, ], post: [ { t: 'EXAM', l: 'Écho-doppler de contrôle du stent' }, { t: 'CRC', l: 'CRC — Suivi endovasculaire' }, ] }, { test: /ischémie aiguë|embolectom|Fogarty/i, pre: [ { t: 'IMG', l: 'Angio-TDM des membres inférieurs en urgence' }, { t: 'EXAM', l: 'Bilan biologique en urgence' }, { t: 'CRC', l: 'CRC — Consultation de cardiologie (ACFA)' }, { t: 'EXAM', l: 'ECG et bilan rythmologique' }, { t: 'EXAM', l: 'Écho-doppler artériel diagnostique' }, { t: 'CRC', l: "CRC — Suivi d'anticoagulation" }, ], op: [ { t: 'CRO', l: 'CRO — Embolectomie au Fogarty' }, { t: 'CRH', l: 'CRH — Passage en réanimation' }, ], post: [ { t: 'EXAM', l: 'Bilan de reperfusion (CPK, lactates)' }, { t: 'EXAM', l: 'Écho-doppler artériel post-embolectomie' }, ] }, { test: /palliatif|amputation/i, pre: [ { t: 'CRC', l: 'CRC — Réunion de concertation pluridisciplinaire' }, { t: 'EXAM', l: 'Écho-doppler artériel — évaluation de sauvetage' }, { t: 'IMG', l: 'Artériographie des membres inférieurs' }, { t: 'EXAM', l: 'Bilan nutritionnel et biologique' }, { t: 'CRC', l: 'CRC — Entretien avec la famille' }, { t: 'CRC', l: 'CRC — Consultation antalgique' }, ], op: [ { t: 'CRH', l: 'CRH — Prise en charge palliative' }, ], post: [ { t: 'CRC', l: 'CRC — Suivi des soins de plaie' }, { t: 'CRC', l: 'CRC — Réévaluation antalgique' }, ] }, // AOMI, pontages et le reste de la revascularisation des membres. { test: /.*/, pre: [ { t: 'EXAM', l: 'Écho-doppler artériel des membres inférieurs' }, { t: 'CRC', l: 'CRC — Consultation pré-opératoire' }, { t: 'IMG', l: 'Angio-TDM aorto-iliaque et membres inférieurs' }, { t: 'EXAM', l: 'Épreuve de marche sur tapis' }, { t: 'EXAM', l: 'Index de pression systolique (IPS)' }, { t: 'CRC', l: 'CRC — Suivi de claudication' }, ], op: [ { t: 'CRO', l: 'CRO — Pontage fémoro-poplité' }, { t: 'CRH', l: "CRH — Sortie d'hospitalisation" }, ], post: [ { t: 'EXAM', l: 'Écho-doppler de perméabilité du pontage' }, { t: 'CRC', l: 'CRC — Consultation post-opératoire' }, ] }, ]; function pdDocFamily(p) { const hay = `${p.summary || ''} ${(p.flags || []).join(' ')}`; return PD_DOC_FAMILIES.find((f) => f.test.test(hay)) || PD_DOC_FAMILIES[PD_DOC_FAMILIES.length - 1]; } // Où en est le patient ? `opDaysAgo` vaut null s'il n'a jamais été opéré. function pdStage(p) { const s = p.summary || ''; const jplus = /J\+(\d+)/.exec(s); if (jplus) return { opDaysAgo: Number(jplus[1]) }; if (/\bJ0\b/.test(s)) return { opDaysAgo: 0 }; if (/\bJ1\b/.test(s)) return { opDaysAgo: 1 }; if (/décédé/i.test(s)) return { opDaysAgo: 0 }; // Mise au point en cours : rien n'a encore été fait au bloc. if (/bilan|pré-op|pre-op|discussion|surveillance|pré-dialyse|palliatif/i.test(s)) { return { opDaysAgo: null }; } // Formulé comme un suivi sans jour précisé : l'intervention date d'un moment. if (/suivi|sortie|post-op|contrôle/i.test(s)) return { opDaysAgo: 45 }; return { opDaysAgo: null }; } function pdFmtDate(d) { const p2 = (n) => String(n).padStart(2, '0'); return `${p2(d.getDate())}/${p2(d.getMonth() + 1)}/${String(d.getFullYear()).slice(2)}`; } function pdBuildDocuments(p, seed) { const fam = pdDocFamily(p); const { opDaysAgo } = pdStage(p); // Au-delà d'une dizaine de lignes la liste n'apprend plus rien. const n = Math.max(1, Math.min(p.crsCount || 3, 10)); // Un dossier clos s'arrête au décès ; les autres courent jusqu'à // aujourd'hui dans la fiction de l'app (24/05/26). const dead = /décédé/i.test(p.summary || ''); const anchor = dead ? new Date(2026, 2, 14) : new Date(2026, 4, 24); const dayMs = 86400000; // Séquence du plus récent au plus ancien, selon l'étape du parcours. // Le compte rendu de sortie vient APRÈS le geste dans le temps, donc avant // lui dans une liste antichronologique — et il n'existe pas si le patient // est encore au lit, ou s'il est mort au bloc. const stillIn = p.status === 'hosp' || p.status === 'rea'; const seq = []; if (opDaysAgo === null) { seq.push(...fam.pre); } else { const followUps = opDaysAgo >= 25 ? 2 : opDaysAgo >= 5 ? 1 : 0; seq.push(...fam.post.slice(0, followUps)); const cro = fam.op[0]; const sortie = fam.op[1]; if (sortie && !stillIn && !dead) seq.push(sortie); seq.push(cro); seq.push(...fam.pre); } const out = []; let cursor = anchor.getTime(); for (let i = 0; i < n; i++) { const src = seq[i % seq.length]; // Ancrage sur la PREMIÈRE occurrence seulement : au-delà de `seq.length` // la liste reboucle, et re-caler le curseur sur la date du bloc le // ferait repartir en avant — les dates cessaient d'être décroissantes. const firstPass = i < seq.length; const isOp = firstPass && opDaysAgo !== null && src === fam.op[0]; const isSortie = firstPass && opDaysAgo !== null && fam.op[1] && src === fam.op[1]; if (isOp) { // Le compte rendu opératoire est daté par le J+N du dossier, pas par // la marche arrière générique : c'est ce que le médecin va vérifier. cursor = anchor.getTime() - opDaysAgo * dayMs; } else if (isSortie) { // Sortie 2 à 5 jours après le geste, jamais dans le futur. const stay = 2 + (seed % 4); cursor = Math.min(anchor.getTime(), anchor.getTime() - (opDaysAgo - stay) * dayMs); } else if (i > 0) { const gap = i < 3 ? 2 + ((seed + i * 7) % 9) : 24 + ((seed + i * 13) % 70); cursor -= gap * dayMs; } out.push({ d: pdFmtDate(new Date(cursor)), label: i < seq.length ? src.l : `${src.l} (${Math.floor(i / seq.length) + 1})`, type: src.t, // « En cours » ne vaut que pour un patient encore au lit, sur la pièce // la plus récente, et seulement si elle vient du bloc. ...(i === 0 && src.t === 'CRO' && (p.status === 'hosp' || p.status === 'rea') ? { tag: 'live' } : {}), }); } return out; } // Les patients écrits à la main gardent leur historique : on ne remplit que les vides. PATIENTS_FULL.forEach((p, i) => { if (!p.documents || !p.documents.length) p.documents = pdBuildDocuments(p, i * 5 + 3); }); Object.assign(window, { PATIENTS_FULL, STATUS_META });