mirror of
https://github.com/wasrusgen/wasrusgen1-crm.git
synced 2026-06-03 16:24:47 +00:00
feat: tasks & reminders — per-client tasks with due dates + upcoming on dashboard
This commit is contained in:
parent
f5d1c466c5
commit
6477ef3ed4
@ -180,10 +180,20 @@ function renderDashboard(){
|
||||
<div class="kpi"><div class="kpi-v">${money(revenue)}</div><div class="kpi-l">Выручка (оплачено)</div></div>
|
||||
<div class="kpi"><div class="kpi-v">${conv}%</div><div class="kpi-l">Конверсия в сделку</div><div class="kpi-sub">${done} завершено</div></div>
|
||||
</div>
|
||||
${renderUpcomingTasks()}
|
||||
<div class="sec-h">Все клиенты · ${total}</div>
|
||||
${projects.map(p=>{const pc=pipeMap[(p.crm&&p.crm.pipeline)||"lead"];return `<div class="blk" style="display:flex;align-items:center;gap:14px;cursor:pointer" onclick="openClient('${p.token}')"><div class="cl-av" style="width:36px;height:36px;border-radius:9px;font-size:14px">${esc((p.client_name||'?')[0])}</div><div style="flex:1"><div style="font-weight:700">${esc(p.client_name)}</div><div style="font-size:12px;color:var(--muted)">${esc(p.niche)} · ${p.msg_count} сообщений</div></div><span style="font-size:11px;font-weight:700;color:${pc[2]};background:${pc[2]}1a;padding:4px 10px;border-radius:20px">${pc[1]}</span><span style="font-weight:700;color:var(--primary)">${money((p.crm&&p.crm.deal_amount)||0)}</span></div>`}).join("")||'<div class="empty">Создайте первого клиента</div>'}`;
|
||||
}
|
||||
|
||||
function renderUpcomingTasks(){
|
||||
const today=new Date().toISOString().slice(0,10);
|
||||
const all=[];
|
||||
projects.forEach(p=>(p.tasks||[]).forEach(t=>{if(!t.done)all.push({...t,client:p.client_name,token:p.token})}));
|
||||
all.sort((a,b)=>(a.due||"9999")<(b.due||"9999")?-1:1);
|
||||
const top=all.slice(0,6);
|
||||
if(!top.length)return"";
|
||||
return `<div class="sec-h">📌 Ближайшие задачи · ${all.length}</div>${top.map(t=>`<div class="blk" style="display:flex;align-items:center;gap:12px;cursor:pointer;padding:11px 14px" onclick="openClient('${t.token}')"><span style="font-size:13px;flex:1">${esc(t.text)}</span><span style="font-size:12px;color:var(--muted)">${esc(t.client)}</span>${t.due?`<span style="font-size:11px;font-weight:700;padding:3px 9px;border-radius:6px;${t.due<today?'background:#FEF2F2;color:#DC2626':t.due===today?'background:#FEF3C7;color:#92400E':'background:#F1F5F9;color:#6B7280'}">${t.due===today?'сегодня':t.due<today?'просрочено':fmtDate(t.due)}</span>`:''}</div>`).join("")}`;
|
||||
}
|
||||
function renderPipeline(){
|
||||
document.getElementById("view").innerHTML=`<div class="sec-h">Воронка продаж</div><div class="kanban">${PIPE.map(([k,name,col])=>{
|
||||
const items=projects.filter(p=>((p.crm&&p.crm.pipeline)||"lead")===k);
|
||||
@ -202,10 +212,27 @@ function renderClient(){
|
||||
<div class="cc-field"><div class="cc-fl">Источник</div><input class="cc-fi" id="cpSrc" value="${esc(crm.source)}" placeholder="откуда пришёл" onchange="saveCrm()"></div>
|
||||
</div>
|
||||
<div class="cc-actions"><button class="btn btn-p" onclick="inviteLink()">🔗 Ссылка клиенту</button><a class="btn btn-g" href="cabinet.html?t=${current}" target="_blank">👁 Открыть кабинет</a></div>
|
||||
<div id="tasksBox"></div>
|
||||
<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>`;
|
||||
renderTasks();
|
||||
renderTab();
|
||||
}
|
||||
function renderTasks(){
|
||||
const box=document.getElementById("tasksBox");if(!box)return;
|
||||
const tasks=state.tasks||[];
|
||||
const today=new Date().toISOString().slice(0,10);
|
||||
box.innerHTML=`<div style="background:var(--white);border:1.5px solid var(--border);border-radius:12px;padding:14px 16px;margin-bottom:18px">
|
||||
<div style="font-size:13px;font-weight:700;margin-bottom:10px;display:flex;align-items:center;gap:8px">📌 Задачи по клиенту</div>
|
||||
${tasks.map((t,i)=>`<div style="display:flex;align-items:center;gap:10px;padding:7px 0;border-top:1px solid var(--bg)"><input type="checkbox" ${t.done?'checked':''} onchange="toggleTask(${i})" style="width:16px;height:16px;cursor:pointer"><span style="flex:1;font-size:13px;${t.done?'text-decoration:line-through;color:#9ca3af':''}">${esc(t.text)}</span>${t.due?`<span style="font-size:11px;font-weight:600;padding:2px 8px;border-radius:6px;${!t.done&&t.due<today?'background:#FEF2F2;color:#DC2626':t.due===today?'background:#FEF3C7;color:#92400E':'background:#F1F5F9;color:#6B7280'}">${t.due===today?'сегодня':fmtDate(t.due)}</span>`:''}<button onclick="delTask(${i})" style="border:none;background:none;cursor:pointer;color:#cbd5e1;font-size:14px">✕</button></div>`).join("")||'<div style="font-size:12px;color:#cbd5e1;padding:4px">Задач нет</div>'}
|
||||
<div style="display:flex;gap:8px;margin-top:10px"><input id="newTask" placeholder="Новая задача..." style="flex:1;border:1.5px solid var(--border);border-radius:8px;padding:8px 11px;font-size:13px;font-family:Inter;outline:none" onkeydown="if(event.key==='Enter')addTask()"><input id="newTaskDue" type="date" style="border:1.5px solid var(--border);border-radius:8px;padding:8px;font-size:13px;font-family:Inter"><button class="cp-btn cp-a" onclick="addTask()">+ Добавить</button></div>
|
||||
</div>`;
|
||||
}
|
||||
function fmtDate(d){const[y,m,dd]=d.split("-");return `${dd}.${m}`}
|
||||
async function saveTasks(){await fetch(`${API}/api/project/tasks`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:current,tasks:state.tasks})});await loadProjects();}
|
||||
function addTask(){const t=document.getElementById("newTask").value.trim();if(!t)return;const due=document.getElementById("newTaskDue").value;state.tasks=state.tasks||[];state.tasks.push({text:t,due,done:false});renderTasks();saveTasks();}
|
||||
function toggleTask(i){state.tasks[i].done=!state.tasks[i].done;renderTasks();saveTasks();}
|
||||
function delTask(i){state.tasks.splice(i,1);renderTasks();saveTasks();}
|
||||
async function saveCrm(){
|
||||
const crm={pipeline:document.getElementById("cpPipe").value,deal_amount:+document.getElementById("cpDeal").value||0,paid_amount:+document.getElementById("cpPaid").value||0,source:document.getElementById("cpSrc").value};
|
||||
state.crm={...state.crm,...crm};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user