diff --git a/docs/cabinet.html b/docs/cabinet.html index 88b20da..9ac4e60 100644 --- a/docs/cabinet.html +++ b/docs/cabinet.html @@ -406,7 +406,7 @@ function go(n){ document.getElementById('si'+n).classList.add('active'); if(PCTS[n]!=null){document.getElementById('pbPct').textContent=PCTS[n]+'%';document.getElementById('pbFill').style.width=PCTS[n]+'%';} if(n===1)requestAnimationFrame(scrollChatBottom); // sv1 уже видим — высота посчитана, прыгаем вниз без «пролёта» - if(n===6)renderOpChat(); + if(n===6){renderOpChat();startOpPoll();}else{stopOpPoll();} if(n===3)renderDocs(); if(n===4)renderAnalysis(); if(n===5)renderSpecPane(); @@ -467,6 +467,10 @@ function renderOpChat(){const c=document.getElementById("opChat");if(!c)return;c async function opSend(){const inp=document.getElementById("opInp");const t=inp.value.trim();if(!t)return;inp.value="";inp.style.height="auto";opMsg("user",t);state.operator_chat=state.operator_chat||[];state.operator_chat.push({role:"user",content:t}); try{const r=await fetch(`${API}/api/operator-chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token,message:t})});const d=await r.json();if(d.messages)state.operator_chat=d.messages;}catch(e){opMsg("elena","Не доставлено: "+e.message);} } +let opPollTimer=null; +async function opPollOnce(){try{const r=await fetch(`${API}/api/operator-chat?token=${encodeURIComponent(token)}`);const d=await r.json();if(d.messages&&d.messages.length!==(state.operator_chat||[]).length){state.operator_chat=d.messages;renderOpChat();}}catch(e){}} +function startOpPoll(){stopOpPoll();opPollOnce();opPollTimer=setInterval(opPollOnce,15000);} +function stopOpPoll(){if(opPollTimer){clearInterval(opPollTimer);opPollTimer=null;}} /* ── Спросить Елену (этапы 3-5) ── */ const STAGE_LBL={3:"о документах",4:"о стратегии и модели",5:"о ТЗ и плане"}; function toggleAsk(){document.getElementById("askDock").classList.toggle("open")} diff --git a/docs/crm.html b/docs/crm.html index 5428446..9e05138 100644 --- a/docs/crm.html +++ b/docs/crm.html @@ -320,9 +320,9 @@ async function submitNewClient(){ await loadProjects();openClient(d.token);toast('Клиент «'+name+'» создан','ok'); }catch(e){toast('Ошибка: '+e.message,'err');btn.disabled=false;btn.textContent='Создать →';} } -function setView(v){view=v;current=null;if(window.innerWidth<=680)closeSb();document.getElementById("nav-dash").classList.toggle("active",v==="dashboard");document.getElementById("nav-pipe").classList.toggle("active",v==="pipeline");renderClientList();render();} +function setView(v){stopCrmChatPoll();view=v;current=null;if(window.innerWidth<=680)closeSb();document.getElementById("nav-dash").classList.toggle("active",v==="dashboard");document.getElementById("nav-pipe").classList.toggle("active",v==="pipeline");renderClientList();render();} async function openClient(token){ - current=token;view="client";mainTab="deal";editPayIdx=-1;if(window.innerWidth<=680)closeSb();document.querySelectorAll(".nav-item").forEach(n=>n.classList.remove("active")); + stopCrmChatPoll();current=token;view="client";mainTab="deal";editPayIdx=-1;if(window.innerWidth<=680)closeSb();document.querySelectorAll(".nav-item").forEach(n=>n.classList.remove("active")); const r=await fetch(`${API}/api/project/${token}`);state=await r.json();renderClientList();render();syncPaymentReminders(); } function render(){ @@ -527,7 +527,7 @@ function renderClient(){
`; renderMainPanel(); } -function setMainTab(t){mainTab=t;editPayIdx=-1;renderMainPanel();document.querySelectorAll('.mtab').forEach((b,i)=>b.classList.toggle('active',MAINTABS[i].id===mainTab));} +function setMainTab(t){if(t!=='chat')stopCrmChatPoll();mainTab=t;editPayIdx=-1;renderMainPanel();document.querySelectorAll('.mtab').forEach((b,i)=>b.classList.toggle('active',MAINTABS[i].id===mainTab));} function renderDocsTab(){ const docs=state.documents||[]; let h=`
@@ -579,6 +579,17 @@ async function opChatSend(){ localStorage.setItem('opseen_'+current,(state.operator_chat||[]).filter(m=>m.role==='user').length); }catch(e){toast('Ошибка: '+e.message,'err');} } +let crmChatPoll=null; +async function crmChatPollOnce(){ + if(!current)return; + try{const r=await fetch(`${API}/api/operator-chat?token=${encodeURIComponent(current)}`);const d=await r.json(); + if(d.messages&&d.messages.length!==(state.operator_chat||[]).length){state.operator_chat=d.messages; + if(mainTab==='chat'){renderOpChatTab();localStorage.setItem('opseen_'+current,d.messages.filter(m=>m.role==='user').length);} + } + }catch(e){} +} +function startCrmChatPoll(){stopCrmChatPoll();crmChatPoll=setInterval(crmChatPollOnce,15000);} +function stopCrmChatPoll(){if(crmChatPoll){clearInterval(crmChatPoll);crmChatPoll=null;}} const DEV_STAGE={canvas:"📊 Стратегия",idef0:"🔧 Функции",spec:"📋 ТЗ",documents:"📁 Документы",methods:"🎯 Методологии",interview:"💬 Интервью"}; function renderDeviations(){ const dev=state.deviations||[]; @@ -661,7 +672,7 @@ function renderMainPanel(){ else if(mainTab==="tasks"){p.innerHTML=`
`;renderTasks();} else if(mainTab==="analysis"){p.innerHTML=`
${TABS.map(t=>`
${t.icon} ${t.name}
`).join("")}
`;renderTab();} else if(mainTab==="docs"){p.innerHTML=renderDocsTab();} - else if(mainTab==="chat"){p.innerHTML=`
`;renderOpChatTab();localStorage.setItem('opseen_'+current,(state.operator_chat||[]).filter(m=>m.role==='user').length);document.querySelectorAll('.mtab').forEach((b,i)=>{if(MAINTABS[i].id==='chat'){const bd=b.querySelector('.badge');if(bd)bd.remove();}});} + else if(mainTab==="chat"){p.innerHTML=`
`;renderOpChatTab();localStorage.setItem('opseen_'+current,(state.operator_chat||[]).filter(m=>m.role==='user').length);document.querySelectorAll('.mtab').forEach((b,i)=>{if(MAINTABS[i].id==='chat'){const bd=b.querySelector('.badge');if(bd)bd.remove();}});startCrmChatPoll();} else if(mainTab==="deviations"){p.innerHTML=renderDeviations();} } function inviteTelegram(){const url=`https://t.me/wasrusgen1_consulting_bot?start=${current}`;navigator.clipboard.writeText(url).then(()=>alert("Ссылка для Telegram скопирована:\n\n"+url+"\n\nКлиент откроет кабинет прямо в Telegram.")).catch(()=>prompt("Ссылка для Telegram:",url));}