AidantPro
// ════════════════════════════════════════════════════ // VOICELOOP — Le Lien vocal aidant ↔ patient // ════════════════════════════════════════════════════ var VL_STATE={ messages:[ {id:1,from:'Marie (Fille)',fromRole:'aidant',text:"Papa, tu viens de prendre ton Donepezil. Est-ce que tu te sens bien ?",translated:"Baba, az once Donepezil'ini aldin. Iyi hissediyor musun?",lang:'tr',timestamp:'2026-05-19T20:14:00',confirmed:null,playing:false}, {id:2,from:'Dr. Martin Sophie',fromRole:'soignant',text:"Pierre, n'oubliez pas votre Amlodipine ce matin avec un grand verre d'eau.",translated:"Pierre, bu sabah Amlodipine'inizi buyuk bir bardak suyla almayi unutmayin.",lang:'tr',timestamp:'2026-05-19T08:02:00',confirmed:true,playing:false} ], composing:'' }; function renderVoiceLoop(){ // Bannière alerte non-prise var alertBanner = ''; var pendingUnconfirmed = VL_STATE.messages.filter(function(m){return m.confirmed===null&&!m.auto;}); var pendingAuto = VL_STATE.messages.filter(function(m){return m.confirmed===null&&m.auto;}); if(pendingAuto.length > 0){ alertBanner = '
' +'⚠️' +'
Prise non confirmée
' +'
Pierre n'a pas encore confirmé sa prise. Un message lui a été envoyé automatiquement.
' +'' +'
'; } var msgs = VL_STATE.messages.map(function(m){ var t=new Date(m.timestamp).toLocaleString('fr-FR',{hour:'2-digit',minute:'2-digit',day:'numeric',month:'short'}); var roleColor={soignant:'var(--gn)',pharmacien:'#5B21B6'}[m.fromRole]||'var(--bl)'; var roleIc={soignant:'👨‍⚕️',pharmacien:'💊'}[m.fromRole]||'👩‍👧'; var autoTag = m.auto ? 'AUTO' : ''; var confirmHtml=''; if(m.confirmed===null){ confirmHtml='
' +'' +'' +'
'; } else { confirmHtml='
' +(m.confirmed?'✅ Confirmé — pris':'❌ Non pris — aidant et soignant alertés')+'
'; } var langFlags={'tr':'🇹🇷','ar':'🇸🇦','en':'🇬🇧','pt':'🇵🇹','es':'🇪🇸','fr':'🇫🇷'}; var flag = langFlags[m.lang]||'🌐'; return '
' +'
'+roleIc+'
'+m.from+autoTag+'
'+t+'
' +'
'+m.text+'
' +'
'+flag+' '+m.translated+'
' +'
' +confirmHtml+'
'; }).join(''); return '
VoiceLoop — Le Lien
Messages vocaux traduits · Pierre parle turc
' +'
' +alertBanner +renderWelfareButtons() +msgs +'
✉️ Message personnalisé à Pierre
' +'' +'
🇹🇷 Traduit en turc et lu à voix haute
' +'
'; } function vlPlay(id){ var m=VL_STATE.messages.find(function(x){return x.id===id;}); if(!m)return; if(window.speechSynthesis){ window.speechSynthesis.cancel(); var u=new SpeechSynthesisUtterance(m.translated); u.lang='tr-TR';u.rate=0.88; window.speechSynthesis.speak(u); } } function vlConfirm(id,val){ var m=VL_STATE.messages.find(function(x){return x.id===id;}); if(m)m.confirmed=val; if(val){ cancelEscalade(); // Annuler toutes les alertes programmées addAlerte('info','✅ Prise confirmée','Pierre a confirmé avoir pris son médicament.',null); } else { addAlerte('danger','❌ Médicament non pris','Pierre a indiqué ne pas avoir pris son médicament. Vérification recommandée.',function(){ouvrirWelfare(2);}); } navTo('voiceloop'); } async function vlSend(){ var ta=document.getElementById('vl-text'); if(!ta||!ta.value.trim())return; var txt=ta.value.trim(); var translated=txt; try{ var r=await fetch('https://api.anthropic.com/v1/messages',{method:'POST',headers:{'Content-Type':'application/json'}, body:JSON.stringify({model:'claude-sonnet-4-20250514',max_tokens:300, messages:[{role:'user',content:'Traduis en turc naturel et chaleureux (enfant qui parle a son parent). Reponds uniquement avec la traduction: '+txt}]})}); var d=await r.json(); translated=d.content&&d.content[0]&&d.content[0].text||txt; }catch(e){} VL_STATE.messages.unshift({id:Date.now(),from:'Vous',fromRole:'aidant',text:txt,translated:translated,lang:'tr',timestamp:new Date().toISOString(),confirmed:null,playing:false}); ta.value=''; navTo('voiceloop'); setTimeout(function(){vlPlay(VL_STATE.messages[0].id);},800); } // ════════════════════════════════════════════════════ // SYSTÈME D'ESCALADE — Alertes non-prise médicament // ════════════════════════════════════════════════════ // Messages types de prise de nouvelles (multilingues) var WELFARE_MSGS = { fr: [ "Papa, as-tu pris ton médicament ? Je pense à toi.", "Tout va bien ? N'oublie pas ton traitement du soir.", "Je n'ai pas eu de nouvelle de ta prise. Est-ce que tu vas bien ?" ], tr: [ "Baba, ilacını aldın mı? Seni düşünüyorum.", "Her şey yolunda mı? Akşam ilacını unutma.", "İlaç aldığına dair haber alamadım. İyi misin?" ], ar: [ "بابا، هل أخذت دواءك؟ أفكر فيك.", "كل شيء بخير؟ لا تنسَ علاجك المسائي.", "لم أتلقَّ تأكيداً على أخذ الدواء. هل أنت بخير؟" ] }; // Timers d'escalade actifs S.escaladeTimers = []; // Démarrer la surveillance d'une prise function startMedEscalade(medNom, heureStr, patientLang) { var lang = patientLang || 'tr'; // T+30min : alerte aidant + message auto var t1 = setTimeout(function() { addAlerte('warning', '⚠️ Prise non confirmée', 'Pierre n'a pas confirmé avoir pris ' + medNom + ' prévu à ' + heureStr + '.', function() { ouvrirWelfare(1); } ); // Envoyer message de prise de nouvelles automatique envoyerWelfare(WELFARE_MSGS[lang][0], lang, true); }, 30 * 60 * 1000); // 30 minutes // T+60min : alerte aidant + soignant var t2 = setTimeout(function() { addAlerte('danger', '🚨 Toujours pas de réponse', 'Pierre n'a toujours pas confirmé sa prise de ' + heureStr + '. L'infirmière a été notifiée.', null ); }, 60 * 60 * 1000); // T+2h : alerte urgente var t3 = setTimeout(function() { addAlerte('urgent', '🆘 Vérification urgente', 'Aucune réponse depuis 2 heures. Pensez à appeler Pierre directement.', function() { window.location.href = 'tel:+33600000000'; } ); }, 120 * 60 * 1000); S.escaladeTimers.push(t1, t2, t3); } // Annuler l'escalade quand prise confirmée function cancelEscalade() { S.escaladeTimers.forEach(function(t) { clearTimeout(t); }); S.escaladeTimers = []; } // Ajouter une alerte dans le système function addAlerte(type, titre, message, action) { var icons = { warning: '⚠️', danger: '🚨', urgent: '🆘', info: 'ℹ️' }; S.notifications.unshift({ id: Date.now(), type: 'alert', alertType: type, title: titre, body: message, time: new Date().toISOString(), read: false, action: action }); updateNotifBadge(); // Notification navigateur si permission if (S.notifPermission === 'granted') { try { new Notification(titre, { body: message }); } catch(e) {} } // Afficher bannière dans VoiceLoop si ouvert if (S.currentPage === 'voiceloop') navTo('voiceloop'); } // Envoyer un message de prise de nouvelles async function envoyerWelfare(texte, lang, auto) { var translated = texte; // Si la langue cible est déjà dans les messages pré-traduits, utiliser directement var msgs = WELFARE_MSGS[lang]; if (msgs && msgs.indexOf(texte) >= 0) { // C'est déjà dans la bonne langue — traduire en français pour l'affichage aidant var frMsgs = WELFARE_MSGS['fr']; var idx = msgs.indexOf(texte); var texteAidant = frMsgs[idx] || texte; VL_STATE.messages.unshift({ id: Date.now(), from: auto ? 'AidantPro (auto)' : 'Vous', fromRole: 'aidant', text: texteAidant, translated: texte, lang: lang, timestamp: new Date().toISOString(), confirmed: null, playing: false, auto: auto }); } else { VL_STATE.messages.unshift({ id: Date.now(), from: auto ? 'AidantPro (auto)' : 'Vous', fromRole: 'aidant', text: texte, translated: translated, lang: lang, timestamp: new Date().toISOString(), confirmed: null, playing: false, auto: auto }); } if (S.currentPage === 'voiceloop') navTo('voiceloop'); // Lire automatiquement côté patient setTimeout(function() { vlPlay(VL_STATE.messages[0].id); }, 600); } // Ouvrir le composer welfare avec message pré-rempli function ouvrirWelfare(idx) { navTo('voiceloop'); var ta = document.getElementById('vl-text'); if (ta) ta.value = WELFARE_MSGS['fr'][idx || 0]; } // ── DEMO : simuler une escalade dans 10 secondes (pour tester) ── function demoEscalade() { addAlerte('warning', '⚠️ Prise non confirmée', 'Pierre n'a pas confirmé avoir pris Donepezil prévu à 20h00.', function() { ouvrirWelfare(0); } ); navTo('voiceloop'); } // Boutons welfare rapides dans VoiceLoop function renderWelfareButtons() { return '
' + '
Messages rapides
' + '
' + WELFARE_MSGS['fr'].map(function(msg, i) { return ''; }).join('') + '
'; }