diff --git a/docs/crm.html b/docs/crm.html
index 3ca2978..41f3bac 100644
--- a/docs/crm.html
+++ b/docs/crm.html
@@ -165,6 +165,9 @@ 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 setClientSearch(v){ui.clientSearch=v;renderDashboard();const el=document.getElementById('clientSearch');if(el){el.focus();const L=el.value.length;try{el.setSelectionRange(L,L);}catch(e){}}}
+function setClientSort(k){if(ui.clientSort===k)ui.clientSortDir=(ui.clientSortDir||1)*-1;else{ui.clientSort=k;ui.clientSortDir=1;}saveUi();renderDashboard();}
+function projStageCnt(p){return [p.msg_count>0,!!p.has_selection,!!p.has_canvas,!!p.has_idef0,!!p.has_spec].filter(Boolean).length;}
function secHead(k,title,right){
const col=!!ui.collapsed[k];
return `
@@ -230,12 +233,28 @@ function renderDashboard(){
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 q=(ui.clientSearch||'').toLowerCase().trim();
+ if(q)cl=cl.filter(p=>(((p.client_name||'')+' '+(p.niche||'')).toLowerCase().indexOf(q)>=0));
+ const sk=ui.clientSort||'name', dir=ui.clientSortDir||1;
+ const pipeOrder={lead:0,qualified:1,proposal:2,active:3,done:4};
+ cl=cl.slice().sort((a,b)=>{let av,bv;
+ if(sk==='amount'){av=(a.crm&&a.crm.deal_amount)||0;bv=(b.crm&&b.crm.deal_amount)||0;}
+ else if(sk==='stage'){av=projStageCnt(a);bv=projStageCnt(b);}
+ else if(sk==='status'){av=pipeOrder[pipeOf(a)]||0;bv=pipeOrder[pipeOf(b)]||0;}
+ else {av=(a.client_name||'').toLowerCase();bv=(b.client_name||'').toLowerCase();}
+ if(av
bv)return dir;return 0;});
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 arrow=k=>ui.clientSort===k?(dir<0?' ↓':' ↑'):'';
+ const bar=`
+
+ Сортировка:
+ ${[['name','Имя'],['amount','Сумма'],['stage','Этап'],['status','Статус']].map(([k,n])=>``).join('')}
+
`;
+ if(!cl.length)return head+bar+'';
+ return head+bar+`${cl.map(p=>renderClientRow(p)).join("")}
`;
})()}`;
}
const STAGE_DEFS=[{key:"interview",name:"Интервью"},{key:"methods",name:"Методологии"},{key:"canvas",name:"Стратегия"},{key:"idef0",name:"Функции"},{key:"spec",name:"ТЗ"}];