diff --git a/docs/crm.html b/docs/crm.html
index b281248..a25fd24 100644
--- a/docs/crm.html
+++ b/docs/crm.html
@@ -51,6 +51,11 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);displ
.tbl-row{display:flex;align-items:center;gap:12px;cursor:pointer;padding:9px 14px;border-top:1px solid var(--bg)}
.tbl-row:first-child{border-top:none}
.tbl-row:hover{background:#FAFBFC}
+.fchip{font-size:11px;font-weight:600;font-family:'Inter';padding:4px 11px;border-radius:7px;border:1px solid var(--border);background:var(--white);color:var(--muted);cursor:pointer;margin-left:6px}
+.fchip:hover{border-color:var(--primary);color:var(--primary)}
+.fchip.on{background:var(--primary);color:#fff;border-color:var(--primary)}
+.sec-h.collapsible{display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none}
+.sec-chev{display:inline-block;transition:transform .15s;font-size:11px;color:var(--muted)}
/* Pipeline kanban */
.kanban{display:flex;gap:12px;overflow-x:auto;padding-bottom:8px;align-items:flex-start}
.kcol{flex:1;min-width:200px;background:#eef0f3;border-radius:12px;padding:10px}
@@ -150,6 +155,25 @@ const pipeMap=Object.fromEntries(PIPE.map(p=>[p[0],p]));
function esc(s){return (s||"").replace(/&/g,"&").replace(//g,">")}
function fmt(s){return esc(s).replace(/\*\*(.+?)\*\*/g,"$1")}
function money(n){return (n||0).toLocaleString("ru-RU")+" ₽"}
+// ── UI-состояние: сворачивание секций + фильтры (запоминается) ──
+const ui=(()=>{try{return JSON.parse(localStorage.getItem('crm_ui'))||{}}catch(e){return{}}})();
+ui.collapsed=ui.collapsed||{revenue:true}; // динамика свёрнута по умолчанию
+if(ui.collapsed.revenue===undefined)ui.collapsed.revenue=true;
+ui.clientFilter=ui.clientFilter||'all';
+ui.taskFilter=ui.taskFilter||'all';
+function saveUi(){try{localStorage.setItem('crm_ui',JSON.stringify(ui))}catch(e){}}
+function toggleSec(k){ui.collapsed[k]=!ui.collapsed[k];saveUi();renderDashboard();}
+function setClientFilter(f){ui.clientFilter=f;saveUi();renderDashboard();}
+function setTaskFilter(f){ui.taskFilter=f;saveUi();renderDashboard();}
+function secHead(k,title,right){
+ const col=!!ui.collapsed[k];
+ return `
+ ▼
+ ${title}
+ ${right?`${right}`:''}
+
`;
+}
+function chips(cur,opts,fn){return opts.map(([k,n])=>``).join('');}
async function loadProjects(){
const r=await fetch(`${API}/api/projects`);const d=await r.json();projects=d.projects;renderClientList();
@@ -198,8 +222,21 @@ function renderDashboard(){
${renderRevenueChart()}
${renderUpcomingTasks()}
- Все клиенты · ${total}
- ${projects.length?`${projects.map(p=>renderClientRow(p)).join("")}
`:'Создайте первого клиента
'}`;
+ ${(()=>{
+ const cf=ui.clientFilter;
+ const pipeOf=p=>(p.crm&&p.crm.pipeline)||'lead';
+ let cl=projects;
+ if(cf==='lead')cl=projects.filter(p=>pipeOf(p)==='lead');
+ else if(cf==='active')cl=projects.filter(p=>pipeOf(p)==='active');
+ else if(cf==='done')cl=projects.filter(p=>pipeOf(p)==='done');
+ else if(cf==='free')cl=projects.filter(p=>((p.crm&&p.crm.billing_type))==='free');
+ const cChips=chips(cf,[['all','Все'],['lead','Лиды'],['active','В работе'],['done','Завершён'],['free','Бесплатные']],'setClientFilter');
+ const head=secHead('clients',`Все клиенты · ${cl.length}`,cChips);
+ if(ui.collapsed.clients)return head;
+ if(!projects.length)return head+'Создайте первого клиента
';
+ if(!cl.length)return head+'';
+ return head+`${cl.map(p=>renderClientRow(p)).join("")}
`;
+ })()}`;
}
const STAGE_DEFS=[{key:"interview",name:"Интервью"},{key:"methods",name:"Методологии"},{key:"canvas",name:"Стратегия"},{key:"idef0",name:"Функции"},{key:"spec",name:"ТЗ"}];
function clientStages(p){
@@ -263,17 +300,27 @@ function renderRevenueChart(){
const MN=["янв","фев","мар","апр","май","июн","июл","авг","сен","окт","ноя","дек"];
const lbl=k=>{const[y,m]=k.split("-");return MN[+m-1]+" "+y.slice(2)};
const totalRev=keys.reduce((s,k)=>s+months[k],0);
- return `📈 Выручка по месяцам · ${money(totalRev)}
- ${keys.map(k=>`
${(months[k]/1000).toFixed(0)}к
${lbl(k)}
`).join("")}
`;
+ const head=secHead('revenue',`📈 Выручка по месяцам · ${money(totalRev)}`);
+ if(ui.collapsed.revenue)return head;
+ return head+`${keys.map(k=>`
${(months[k]/1000).toFixed(0)}к
${lbl(k)}
`).join("")}
`;
}
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 `📌 Ближайшие задачи · ${all.length}
${top.map(t=>`
${esc(t.text)}${esc(t.client)}${t.due?`${t.due===today?'сегодня':t.due`:''}
`).join("")}
`;
+ if(!all.length)return"";
+ const f=ui.taskFilter;
+ let list=all;
+ if(f==='overdue')list=all.filter(t=>t.due&&t.duet.due===today);
+ const overdueN=all.filter(t=>t.due&&t.dueНет задач по фильтру
';
+ return head+`${top.map(t=>`
${esc(t.text)}${esc(t.client)}${t.due?`${t.due===today?'сегодня':t.due`:''}
`).join("")}
`;
}
function renderPipeline(){
document.getElementById("view").innerHTML=`Воронка продаж
${PIPE.map(([k,name,col])=>{