diff --git a/docs/cabinet.html b/docs/cabinet.html index 9ac4e60..f7abd21 100644 --- a/docs/cabinet.html +++ b/docs/cabinet.html @@ -350,7 +350,7 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);displ
-
💬
Личный консультант
Связаться с Русланом
Вопросы по проекту, срокам, оплате — напишите руководителю проекта напрямую
+
💬
Личный консультант
Связаться с Русланом
Вопросы по проекту, срокам, оплате — напишите напрямую
@@ -471,6 +471,29 @@ 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;}} +/* ── Предложения клиента ── */ +const SUG_ST={new:['🆕 Новое','#EFF6FF','#2563EB'],discussion:['💬 На обсуждении','#FEF3C7','#92400E'],accepted:['✅ Принято','#D1FAE5','#047857'],rejected:['❌ Отклонено','#FEE2E2','#B91C1C']}; +function openIdeas(){ + const sgs=state.suggestions||[]; + const list=sgs.length?sgs.map(s=>{const st=SUG_ST[s.status]||SUG_ST.new;return `
${esc(s.text)}
${st[0]}${s.decision?`— ${esc(s.decision)}`:''}
`}).join(''):'
Пока нет. Предложите первую идею — мы рассмотрим её.
'; + const m=document.createElement('div');m.id='ideaModal';m.style.cssText='position:fixed;inset:0;background:rgba(15,15,26,.5);z-index:260;display:flex;align-items:center;justify-content:center;padding:18px';m.onclick=()=>m.remove(); + m.innerHTML=`
+
💡 Мои предложения
+
Ваши идеи по проекту. Консультант рассмотрит каждую и ответит решением.
+
${list}
+ +
+
`; + document.body.appendChild(m);setTimeout(()=>{const e=document.getElementById('ideaInp');if(e)e.focus();},40); +} +async function submitIdea(){ + const t=(document.getElementById('ideaInp').value||'').trim();if(!t)return; + const b=document.getElementById('ideaOk');b.disabled=true;b.textContent='Отправляю…'; + try{const r=await fetch(`${API}/api/suggestion`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({token,text:t})});const d=await r.json(); + if(d.suggestions)state.suggestions=d.suggestions; + const mm=document.getElementById('ideaModal');if(mm)mm.remove();openIdeas(); + }catch(e){b.disabled=false;b.textContent='Предложить →';} +} /* ── Спросить Елену (этапы 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 9e05138..a14857c 100644 --- a/docs/crm.html +++ b/docs/crm.html @@ -507,7 +507,7 @@ async function kDrop(e,pipe){ await loadProjects();renderPipeline(); } -const MAINTABS=[{id:"deal",name:"Сделка",icon:"📇"},{id:"chat",name:"Чат с клиентом",icon:"💬"},{id:"pricing",name:"Ценообразование",icon:"💰"},{id:"payments",name:"Платежи",icon:"💳"},{id:"tasks",name:"Задачи",icon:"📌"},{id:"docs",name:"Документы",icon:"📎"},{id:"analysis",name:"Анализ",icon:"📊"},{id:"deviations",name:"Отклонения",icon:"⚠️"}]; +const MAINTABS=[{id:"deal",name:"Сделка",icon:"📇"},{id:"chat",name:"Чат с клиентом",icon:"💬"},{id:"pricing",name:"Ценообразование",icon:"💰"},{id:"payments",name:"Платежи",icon:"💳"},{id:"tasks",name:"Задачи",icon:"📌"},{id:"docs",name:"Документы",icon:"📎"},{id:"analysis",name:"Анализ",icon:"📊"},{id:"deviations",name:"Отклонения",icon:"⚠️"},{id:"suggestions",name:"Предложения",icon:"💡"}]; function renderClient(){ const crm=state.crm||{pipeline:"lead",deal_amount:0,paid_amount:0,contact:"",source:"",note:""}; const billing=crm.billing_type||"paid"; @@ -516,7 +516,8 @@ function renderClient(){ const overdue=(state.tasks||[]).filter(t=>!t.done&&t.due&&t.duem.role==='user').length-(+localStorage.getItem('opseen_'+current)||0); - const badge=id=>{if(id==="payments"&&deal>0&&left>0)return `${money(left)}`;if(id==="tasks"&&overdue)return `${overdue}`;if(id==="deviations"&&devN)return `${devN}`;if(id==="docs"&&docN)return `${docN}`;if(id==="chat"&&ocUnread>0)return `${ocUnread}`;return ''}; + const sugNew=(state.suggestions||[]).filter(s=>s.status==='new').length; + const badge=id=>{if(id==="payments"&&deal>0&&left>0)return `${money(left)}`;if(id==="tasks"&&overdue)return `${overdue}`;if(id==="deviations"&&devN)return `${devN}`;if(id==="docs"&&docN)return `${docN}`;if(id==="chat"&&ocUnread>0)return `${ocUnread}`;if(id==="suggestions"&&sugNew)return `${sugNew}`;return ''}; document.getElementById("view").innerHTML=`
${esc((state.client_name||'?')[0])}
${esc(state.client_name||'Без имени')}
${esc(state.niche||'')} · ${state.messages.length} сообщений
@@ -674,6 +675,29 @@ function renderMainPanel(){ 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();}});startCrmChatPoll();} else if(mainTab==="deviations"){p.innerHTML=renderDeviations();} + else if(mainTab==="suggestions"){p.innerHTML=renderSuggestions();} +} +const CRM_SUG_ST={new:['🆕','#EFF6FF','#2563EB'],discussion:['💬','#FEF3C7','#92400E'],accepted:['✅','#D1FAE5','#047857'],rejected:['❌','#FEE2E2','#B91C1C']}; +const SUG_STATUSES=[['new','🆕 Новое'],['discussion','💬 Обсуждаем'],['accepted','✅ Принять'],['rejected','❌ Отклонить']]; +function renderSuggestions(){ + const sgs=state.suggestions||[]; + if(!sgs.length)return `
💡
Предложений нет
Идеи клиента появятся здесь — из кабинета («Мои идеи») или когда Елена поймает предложение в диалоге. Меняешь статус и пишешь решение — клиент видит его в кабинете.
`; + return `
Идеи клиента — не игнорируем, а рассматриваем. Статус и решение видны клиенту: чувствует, что услышан.
`+sgs.map(s=>{ + const st=CRM_SUG_ST[s.status]||CRM_SUG_ST.new; + return `
+
${s.source==='client'?'👤 клиент':'✍ оператор'}${s.topic?' · '+esc(s.topic):''}${(s.at||'').slice(0,10).split('-').reverse().join('.')}
+
${esc(s.text)}
+
${SUG_STATUSES.map(([k,n])=>``).join('')}
+ +
`}).join(''); +} +function setSugStatus(id,status){sugUpdate(id,{status});} +function setSugDecision(id,decision){sugUpdate(id,{decision});} +async function sugUpdate(id,fields){ + try{const r=await fetch(`${API}/api/suggestion/update`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(Object.assign({token:current,id},fields))});const d=await r.json(); + if(d.error){toast('Ошибка: '+d.error,'err');return;} + state.suggestions=d.suggestions||state.suggestions;if(mainTab==='suggestions')renderClient();toast('Сохранено','ok'); + }catch(e){toast('Ошибка: '+e.message,'err');} } 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));} async function setBilling(t){