ui: вкладка Сделка — плотный обзор клиента (прогресс+финансы+контакт+заметка)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
wasrusgen 2026-06-01 07:48:35 +03:00
parent 6828ecfeb3
commit 984ead3f2f

View File

@ -305,6 +305,11 @@ function renderMainPanel(){
const p=document.getElementById("mainPanel");if(!p)return;
const crm=state.crm||{pipeline:"lead",deal_amount:0,source:""};
if(mainTab==="deal"){
const deal=crm.deal_amount||0;
const pays=crm.payments||[]; const paid=pays.reduce((s,x)=>s+(x.amount||0),0); const left=Math.max(0,deal-paid);
const doneArr=CLIENT_STAGES.map(s=>stageIsDone(s.key));
const firstUndone=doneArr.findIndex(d=>!d);
const doneCnt=doneArr.filter(Boolean).length;
p.innerHTML=`
<div class="cc-grid">
<div class="cc-field"><div class="cc-fl">Статус воронки</div><select class="cc-sel" id="cpPipe" onchange="saveCrm()">${PIPE.map(([k,n])=>`<option value="${k}" ${crm.pipeline===k?'selected':''}>${n}</option>`).join("")}</select></div>
@ -312,7 +317,36 @@ function renderMainPanel(){
<div class="cc-field"><div class="cc-fl">Источник</div><input class="cc-fi" id="cpSrc" value="${esc(crm.source||'')}" placeholder="откуда пришёл" onchange="saveCrm()"></div>
<div class="cc-field" id="payStatusBox"></div>
</div>
<div class="cc-actions"><button class="btn btn-p" onclick="inviteLink()">🔗 Ссылка клиенту</button><button class="btn btn-p" style="background:#0088cc" onclick="inviteTelegram()">В Telegram</button><a class="btn btn-g" href="cabinet.html?t=${current}" target="_blank">👁 Открыть кабинет</a><button class="btn btn-g" style="margin-left:auto;border-color:#FECACA;color:#DC2626" onclick="deleteClient()">🗑 Удалить</button></div>`;
<div class="cc-actions"><button class="btn btn-p" onclick="inviteLink()">🔗 Ссылка клиенту</button><button class="btn btn-p" style="background:#0088cc" onclick="inviteTelegram()">В Telegram</button><a class="btn btn-g" href="cabinet.html?t=${current}" target="_blank">👁 Открыть кабинет</a><button class="btn btn-g" style="margin-left:auto;border-color:#FECACA;color:#DC2626" onclick="deleteClient()">🗑 Удалить</button></div>
<div style="display:grid;grid-template-columns:1.35fr 1fr;gap:14px;align-items:start">
<div class="blk" style="margin:0">
<div style="display:flex;align-items:center;margin-bottom:6px"><b style="font-size:13px">📍 Прогресс проекта</b><span style="margin-left:auto;font-size:11px;color:var(--muted)">${doneCnt}/5 этапов</span></div>
${CLIENT_STAGES.map((s,i)=>{
const done=doneArr[i], cur=!done&&i===firstUndone;
const brd=done?'#047857':cur?'#10B981':'#E5E7EB';
const tag=done?'<span style="font-size:10px;font-weight:700;color:#047857;background:#ECFDF5;padding:1px 7px;border-radius:5px">✓ готово</span>':cur?'<span style="font-size:10px;font-weight:700;color:#92400E;background:#FEF3C7;padding:1px 7px;border-radius:5px">● в работе</span>':'<span style="font-size:10px;font-weight:700;color:#9CA3AF;background:#F3F4F6;padding:1px 7px;border-radius:5px">ожидает</span>';
return `<div style="display:flex;align-items:center;gap:10px;padding:7px 0;border-top:1px solid var(--bg)">
<span style="width:24px;height:24px;border-radius:6px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:12px;background:${done||cur?'#ECFDF5':'#F9FAFB'};border:1.5px solid ${brd}">${s.icon}</span>
<div style="flex:1;min-width:0"><div style="font-size:12.5px;font-weight:700;color:${done||cur?'var(--text)':'#9CA3AF'}">${s.name}</div><div style="font-size:11px;color:var(--muted)">${esc(s.desc)}</div></div>
${tag}
</div>`;
}).join('')}
</div>
<div style="display:flex;flex-direction:column;gap:14px">
<div class="blk" style="margin:0">
<div style="font-size:13px;font-weight:700;margin-bottom:8px">💰 Финансы</div>
<div style="display:flex;justify-content:space-between;padding:5px 0;font-size:13px"><span style="color:var(--muted)">Смета</span><b>${deal>0?money(deal):'—'}</b></div>
<div style="display:flex;justify-content:space-between;padding:5px 0;font-size:13px;border-top:1px solid var(--bg)"><span style="color:var(--muted)">Получено</span><b style="color:#047857">${money(paid)}</b></div>
<div style="display:flex;justify-content:space-between;padding:5px 0;font-size:13px;border-top:1px solid var(--bg)"><span style="color:var(--muted)">Остаток</span><b style="color:${left>0?'#DC2626':'#047857'}">${money(left)}</b></div>
</div>
<div class="blk" style="margin:0">
<div class="cc-fl">Контакт</div>
<input class="cc-fi" id="cpContact" value="${esc(crm.contact||'')}" placeholder="телефон / email" onchange="saveCrm()" style="margin-bottom:12px">
<div class="cc-fl">Заметка</div>
<textarea id="cpNote" onchange="saveCrm()" placeholder="заметки по клиенту..." style="width:100%;border:none;outline:none;background:transparent;font-family:Inter;font-size:13px;resize:vertical;min-height:46px;color:var(--text)">${esc(crm.note||'')}</textarea>
</div>
</div>
</div>`;
renderPayments();
}
else if(mainTab==="pricing"){p.innerHTML=`<div id="pricingBox"></div>`;renderPricing();}
@ -598,7 +632,10 @@ function addTask(){const t=document.getElementById("newTask").value.trim();if(!t
function toggleTask(i){state.tasks[i].done=!state.tasks[i].done;saveTasks();renderClient();}
function delTask(i){state.tasks.splice(i,1);saveTasks();renderClient();}
async function saveCrm(){
const crm={pipeline:document.getElementById("cpPipe").value,deal_amount:+document.getElementById("cpDeal").value||0,source:document.getElementById("cpSrc").value};
const g=id=>document.getElementById(id);
const crm={pipeline:g("cpPipe").value,deal_amount:+g("cpDeal").value||0,source:g("cpSrc").value};
if(g("cpContact"))crm.contact=g("cpContact").value;
if(g("cpNote"))crm.note=g("cpNote").value;
state.crm={...state.crm,...crm};
await fetch(`${API}/api/project/crm`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:current,...crm})});
await loadProjects();