mirror of
https://github.com/wasrusgen/wasrusgen1-crm.git
synced 2026-06-03 15:04:47 +00:00
feat(crm): вкладка «Отклонения» — эталон Елены против выбора клиента
Оператор при внедрении видит расхождения: решение клиента (внедряем) + рекомендация Елены (флажок) + причина клиента. Бейдж со счётчиком.
This commit is contained in:
parent
cf5e4f050a
commit
9558c8b423
@ -414,14 +414,15 @@ async function kDrop(e,pipe){
|
|||||||
await loadProjects();renderPipeline();
|
await loadProjects();renderPipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAINTABS=[{id:"deal",name:"Сделка",icon:"📇"},{id:"pricing",name:"Ценообразование",icon:"💰"},{id:"payments",name:"Платежи",icon:"💳"},{id:"tasks",name:"Задачи",icon:"📌"},{id:"analysis",name:"Анализ",icon:"📊"}];
|
const MAINTABS=[{id:"deal",name:"Сделка",icon:"📇"},{id:"pricing",name:"Ценообразование",icon:"💰"},{id:"payments",name:"Платежи",icon:"💳"},{id:"tasks",name:"Задачи",icon:"📌"},{id:"analysis",name:"Анализ",icon:"📊"},{id:"deviations",name:"Отклонения",icon:"⚠️"}];
|
||||||
function renderClient(){
|
function renderClient(){
|
||||||
const crm=state.crm||{pipeline:"lead",deal_amount:0,paid_amount:0,contact:"",source:"",note:""};
|
const crm=state.crm||{pipeline:"lead",deal_amount:0,paid_amount:0,contact:"",source:"",note:""};
|
||||||
const billing=crm.billing_type||"paid";
|
const billing=crm.billing_type||"paid";
|
||||||
const deal=crm.deal_amount||0, pays=crm.payments||[], paid=pays.reduce((s,p)=>s+(p.amount||0),0), left=deal-paid;
|
const deal=crm.deal_amount||0, pays=crm.payments||[], paid=pays.reduce((s,p)=>s+(p.amount||0),0), left=deal-paid;
|
||||||
const today=new Date().toISOString().slice(0,10);
|
const today=new Date().toISOString().slice(0,10);
|
||||||
const overdue=(state.tasks||[]).filter(t=>!t.done&&t.due&&t.due<today).length;
|
const overdue=(state.tasks||[]).filter(t=>!t.done&&t.due&&t.due<today).length;
|
||||||
const badge=id=>{if(id==="payments"&&deal>0&&left>0)return `<span class="badge">${money(left)}</span>`;if(id==="tasks"&&overdue)return `<span class="badge">${overdue}</span>`;return ''};
|
const devN=(state.deviations||[]).length;
|
||||||
|
const badge=id=>{if(id==="payments"&&deal>0&&left>0)return `<span class="badge">${money(left)}</span>`;if(id==="tasks"&&overdue)return `<span class="badge">${overdue}</span>`;if(id==="deviations"&&devN)return `<span class="badge">${devN}</span>`;return ''};
|
||||||
document.getElementById("view").innerHTML=`
|
document.getElementById("view").innerHTML=`
|
||||||
<div class="cc-top"><div class="cc-av">${esc((state.client_name||'?')[0])}</div><div style="flex:1"><div class="cc-name">${esc(state.client_name||'Без имени')}</div><div class="cc-meta">${esc(state.niche||'')} · ${state.messages.length} сообщений</div></div>
|
<div class="cc-top"><div class="cc-av">${esc((state.client_name||'?')[0])}</div><div style="flex:1"><div class="cc-name">${esc(state.client_name||'Без имени')}</div><div class="cc-meta">${esc(state.niche||'')} · ${state.messages.length} сообщений</div></div>
|
||||||
<div style="display:flex;gap:4px;background:#F1F5F9;border-radius:10px;padding:3px">
|
<div style="display:flex;gap:4px;background:#F1F5F9;border-radius:10px;padding:3px">
|
||||||
@ -433,6 +434,35 @@ function renderClient(){
|
|||||||
renderMainPanel();
|
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){mainTab=t;editPayIdx=-1;renderMainPanel();document.querySelectorAll('.mtab').forEach((b,i)=>b.classList.toggle('active',MAINTABS[i].id===mainTab));}
|
||||||
|
const DEV_STAGE={canvas:"📊 Стратегия",idef0:"🔧 Функции",spec:"📋 ТЗ",documents:"📁 Документы",methods:"🎯 Методологии",interview:"💬 Интервью"};
|
||||||
|
function renderDeviations(){
|
||||||
|
const dev=state.deviations||[];
|
||||||
|
if(!dev.length)return `<div class="run-card" style="background:#fff;border:1px solid #E5E7EB;border-radius:12px;padding:22px;text-align:center">
|
||||||
|
<div style="font-size:26px;margin-bottom:6px">✅</div>
|
||||||
|
<div style="font-size:14px;font-weight:700">Отклонений нет</div>
|
||||||
|
<div style="font-size:12px;color:#6B7280;margin-top:4px">Клиент пока не настаивал на изменениях. Когда в кабинете на этапах 3–5 он скажет Елене «мне так удобно» — расхождение появится здесь: эталон против выбора клиента.</div></div>`;
|
||||||
|
const fmtD=d=>{if(!d)return'';const a=String(d).slice(0,10).split('-');return a.length===3?a[2]+'.'+a[1]+'.'+a[0].slice(2):'';};
|
||||||
|
return `<div style="font-size:12px;color:#6B7280;margin-bottom:12px;line-height:1.5">При внедрении реализуем <b style="color:#1A1A2E">решение клиента</b> (он за него отвечает), но рекомендация Елены остаётся флажком — это и подстраховка, и карта что дотянуть позже.</div>`+
|
||||||
|
dev.map(d=>`<div style="background:#fff;border:1px solid #E5E7EB;border-left:3px solid #F59E0B;border-radius:12px;padding:14px 16px;margin-bottom:10px">
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:9px;flex-wrap:wrap">
|
||||||
|
<span style="font-size:10px;font-weight:700;color:#6B7280;background:#F3F4F6;padding:2px 8px;border-radius:5px">${DEV_STAGE[d.stage]||esc(d.stage||'этап')}</span>
|
||||||
|
<span style="font-size:13px;font-weight:800">${esc(d.node||'')}</span>
|
||||||
|
${d.at?`<span style="margin-left:auto;font-size:10px;color:#9CA3AF">${fmtD(d.at)}</span>`:''}
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:8px;align-items:flex-start;margin-bottom:7px">
|
||||||
|
<span style="font-size:13px;flex:0 0 18px">✅</span>
|
||||||
|
<div><div style="font-size:10px;font-weight:700;color:#047857;text-transform:uppercase;letter-spacing:.3px">Решение клиента · внедряем</div><div style="font-size:13px;font-weight:600;color:#1A1A2E">${esc(d.client_choice||'')}</div></div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:8px;align-items:flex-start;margin-bottom:7px">
|
||||||
|
<span style="font-size:13px;flex:0 0 18px">⚠️</span>
|
||||||
|
<div><div style="font-size:10px;font-weight:700;color:#92400E;text-transform:uppercase;letter-spacing:.3px">Елена рекомендовала</div><div style="font-size:13px;color:#374151">${esc(d.elena_rec||'')}</div></div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:8px;align-items:flex-start;background:#F9FAFB;border-radius:8px;padding:8px 10px;margin-top:8px">
|
||||||
|
<span style="font-size:13px;flex:0 0 18px">💬</span>
|
||||||
|
<div><div style="font-size:10px;font-weight:700;color:#6B7280;text-transform:uppercase;letter-spacing:.3px">Причина клиента</div><div style="font-size:12px;color:#4B5563;font-style:italic">${esc(d.reason||'')}</div></div>
|
||||||
|
</div>
|
||||||
|
</div>`).join('');
|
||||||
|
}
|
||||||
function renderMainPanel(){
|
function renderMainPanel(){
|
||||||
const p=document.getElementById("mainPanel");if(!p)return;
|
const p=document.getElementById("mainPanel");if(!p)return;
|
||||||
const crm=state.crm||{pipeline:"lead",deal_amount:0,source:""};
|
const crm=state.crm||{pipeline:"lead",deal_amount:0,source:""};
|
||||||
@ -485,6 +515,7 @@ function renderMainPanel(){
|
|||||||
else if(mainTab==="payments"){p.innerHTML=`<div id="planBox"></div><div id="paymentsBox"></div>`;renderPaymentPlan();renderPayments();}
|
else if(mainTab==="payments"){p.innerHTML=`<div id="planBox"></div><div id="paymentsBox"></div>`;renderPaymentPlan();renderPayments();}
|
||||||
else if(mainTab==="tasks"){p.innerHTML=`<div id="tasksBox"></div>`;renderTasks();}
|
else if(mainTab==="tasks"){p.innerHTML=`<div id="tasksBox"></div>`;renderTasks();}
|
||||||
else if(mainTab==="analysis"){p.innerHTML=`<div class="tabs">${TABS.map(t=>`<div class="tab ${t.id===activeTab?'active':''} ${approved(t.id)?'done':''}" onclick="setTab('${t.id}')">${t.icon} ${t.name}</div>`).join("")}</div><div id="tabContent"></div>`;renderTab();}
|
else if(mainTab==="analysis"){p.innerHTML=`<div class="tabs">${TABS.map(t=>`<div class="tab ${t.id===activeTab?'active':''} ${approved(t.id)?'done':''}" onclick="setTab('${t.id}')">${t.icon} ${t.name}</div>`).join("")}</div><div id="tabContent"></div>`;renderTab();}
|
||||||
|
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));}
|
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){
|
async function setBilling(t){
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user