feat: Клиенты — список с фильтрами (Все/Лиды/Активные/Закрытые) и поиском, карточка открывается из списка

This commit is contained in:
wasrusgen 2026-05-28 23:29:27 +03:00
parent 6f0e54c0bd
commit 297fcf90e6

View File

@ -1844,7 +1844,8 @@ function renderScreen(id) {
if (id==='manager_schedule') return screenSchedule();
if (id==='manager_calc') return screenCalc();
if (id==='manager_salary') return screenSalary();
if (id==='manager_client') return screenClient();
if (id==='manager_client') return screenClientsList();
if (id==='manager_client_card') return screenClient();
if (id==='manager_own_tech') return screenOwnTech();
if (id==='manager_tech_client') return screenTechClient();
if (id==='manager_kb') return screenKB();
@ -3633,7 +3634,118 @@ function _renderSendPreview(msg, sel){
+'</div></div>';
}
// ── CLIENT ────────────────────────────────────────────────────────────────────
// ── CLIENTS LIST ─────────────────────────────────────────────────────────────
window._clientFilter = window._clientFilter || 'all';
window._clientSearch = window._clientSearch || '';
function screenClientsList() {
var all = window._managerOrders || [];
var filter = window._clientFilter;
var search = (window._clientSearch || '').toLowerCase();
// Фильтрация
var filtered = all.filter(function(o) {
var matchFilter =
filter === 'all' ? true :
filter === 'leads' ? !!o.isLead :
filter === 'active' ? (!o.isLead && o.stage < 7) :
filter === 'done' ? (!o.isLead && o.stage >= 7) : true;
var matchSearch = !search || o.client.toLowerCase().indexOf(search) >= 0
|| (o.contract||'').toLowerCase().indexOf(search) >= 0
|| (o.label||'').toLowerCase().indexOf(search) >= 0;
return matchFilter && matchSearch;
});
var counts = {
all: all.length,
leads: all.filter(function(o){ return !!o.isLead; }).length,
active: all.filter(function(o){ return !o.isLead && o.stage < 7; }).length,
done: all.filter(function(o){ return !o.isLead && o.stage >= 7; }).length,
};
var stageLabel = ['','Замер','Проект','Техника','Технолог','Производство','Сборка','Закрыт'];
var stageColor = ['','#3B82F6','#8B5CF6','#F59E0B','#EF4444','#10B981','#0891B2','#6B7280'];
var leadStageLabel = {new:'Новый',meeting:'Замер',kp:'КП',thinking:'Думает',done:'Договор'};
var typeIcon = {kitchen:'🍳', wardrobe:'🚪', other:'📦'};
// Фильтр-таб
var tabs = [
{key:'all', label:'Все', cnt:counts.all},
{key:'leads', label:'Лиды', cnt:counts.leads},
{key:'active', label:'Активные', cnt:counts.active},
{key:'done', label:'Закрытые', cnt:counts.done},
];
var tabHtml = '<div style="display:flex;gap:6px;padding:10px 16px;overflow-x:auto;scrollbar-width:none">'
+ tabs.map(function(t) {
var active = filter === t.key;
return '<div onclick="window._clientFilter=\''+t.key+'\';_nav(\'manager_client\')" '
+'style="padding:6px 13px;border-radius:20px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;flex-shrink:0;'
+(active ? 'background:var(--accent);color:#fff;' : 'background:var(--card);color:var(--muted);border:1.5px solid rgba(0,0,0,.08);')
+'">'+t.label+(t.cnt > 0 ? ' <span style="opacity:.7">'+t.cnt+'</span>' : '')+'</div>';
}).join('')
+ '</div>';
// Поиск
var searchHtml = '<div style="padding:0 16px 10px">'
+'<div style="background:var(--card);border-radius:12px;border:1.5px solid rgba(0,0,0,.07);display:flex;align-items:center;padding:0 12px;gap:8px">'
+'<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="flex-shrink:0;color:var(--muted)"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>'
+'<input id="clientSearch" type="text" placeholder="Поиск по имени, договору…" value="'+window._clientSearch+'" '
+'oninput="window._clientSearch=this.value;_nav(\'manager_client\')" '
+'style="flex:1;border:none;outline:none;font-size:13px;color:var(--ink);background:transparent;padding:10px 0">'
+(window._clientSearch ? '<button onclick="window._clientSearch=\'\';_nav(\'manager_client\')" style="border:none;background:none;color:var(--muted);cursor:pointer;font-size:16px;padding:0">×</button>' : '')
+'</div></div>';
// Список карточек
var listHtml = filtered.length === 0
? '<div style="padding:40px 16px;text-align:center;color:var(--muted);font-size:14px">Клиентов не найдено</div>'
: filtered.map(function(o, i) {
var idx = all.indexOf(o);
var initials = o.client.split(' ').slice(0,2).map(function(w){ return w[0]; }).join('');
var avatarColor = o.isLead ? '#F59E0B' : stageColor[o.stage] || '#3B82F6';
var stageText = o.isLead
? (leadStageLabel[o.leadStage] || 'Лид')
: (stageLabel[o.stage] || '');
var stageCol = o.isLead ? '#F59E0B'
: (o.stage >= 7 ? '#6B7280' : stageColor[o.stage] || '#3B82F6');
var icon = typeIcon[o.type] || '📦';
var hasBlocker = !!o.blocker;
return '<div onclick="window._activeOrder='+idx+';_nav(\'manager_client_card\')" '
+'style="display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid rgba(0,0,0,.05);cursor:pointer;active:background:var(--bg)">'
// Аватар
+'<div style="width:40px;height:40px;border-radius:50%;background:'+avatarColor+';display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;flex-shrink:0">'+initials+'</div>'
// Инфо
+'<div style="flex:1;min-width:0">'
+'<div style="display:flex;align-items:center;gap:6px;margin-bottom:2px">'
+'<div style="font-size:14px;font-weight:700;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+o.client+'</div>'
+(hasBlocker ? '<div style="width:6px;height:6px;border-radius:50%;background:var(--danger);flex-shrink:0"></div>' : '')
+'</div>'
+'<div style="font-size:11px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'
+icon+' '+(o.label || o.type)+(o.contract ? ' · '+o.contract : '')
+'</div>'
+'</div>'
// Статус + сумма
+'<div style="text-align:right;flex-shrink:0">'
+'<div style="font-size:11px;font-weight:700;color:'+stageCol+';margin-bottom:3px">'+stageText+'</div>'
+(o.amount ? '<div style="font-size:12px;font-weight:800;color:var(--ink)">'+Math.round(o.amount/1000)+' тыс ₽</div>' : '')
+'</div>'
+'</div>';
}).join('');
return '<div class="page">'
+'<div style="padding:14px 16px 10px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
+'<div style="font-size:18px;font-weight:800;color:var(--ink)">Клиенты</div>'
+'<div style="font-size:12px;color:var(--muted);margin-top:1px">'+all.length+' всего · '+counts.active+' активных</div>'
+'</div>'
+ tabHtml
+ searchHtml
+'<div style="background:var(--card);margin:0 16px;border-radius:14px;box-shadow:0 2px 8px rgba(0,0,0,.06);overflow:hidden">'
+ listHtml
+'</div>'
+'</div>';
}
// ── CLIENT CARD ───────────────────────────────────────────────────────────────
function screenClient() {
var o=window._managerOrders[window._activeOrder]||window._managerOrders[0];
var init=o.client.split(' ').slice(0,2).map(function(w){return w[0];}).join('');
@ -3710,7 +3822,7 @@ function screenClient() {
{label:'Телефон', val: o.phone},
{label:'Источник', val: 'Зал'},
{label:'Первый контакт', val: '03.05.2025'},
{label:'Ответственный', val: 'Анна Соколова'},
{label:'Ответственный', val: _MGR_IDENTITY.name},
];
var infoHtml = '<div style="background:var(--card);border-radius:16px;margin:0 16px 12px;overflow:hidden">'
+infoRows.map(function(r,i){
@ -3746,7 +3858,7 @@ function screenClient() {
+'</div>';
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_order\')">'+bk+'</button><h2>Клиент</h2></div>'
+'<div class="page-header"><button class="back-btn" onclick="_nav(\'manager_client\')">'+bk+'</button><h2>'+o.client.split(' ')[0]+'</h2></div>'
// Hero
+'<div style="display:flex;flex-direction:column;align-items:center;padding:20px 16px 16px;gap:6px">'
+'<div style="width:72px;height:72px;border-radius:50%;background:linear-gradient(135deg,var(--accent),#1E6BB8);display:flex;align-items:center;justify-content:center;color:#fff;font-size:24px;font-weight:700;box-shadow:0 4px 16px rgba(0,62,126,.3)">'+init+'</div>'
@ -3962,7 +4074,7 @@ function _renderAIContract(r){
function navBar() {
var s=window._currentScreen||'manager_home';
var isHome = s==='manager_home'||s==='manager_order'||s==='manager_tech'||s==='manager_wizard'||s==='manager_result';
var isClients = s==='manager_client'||s==='manager_lead';
var isClients = s==='manager_client'||s==='manager_client_card'||s==='manager_lead';
var isSched = s==='manager_schedule';
var isCalc = s==='manager_salary'||s==='manager_calc';
var isKB = s==='manager_kb';