mirror of
https://github.com/wasrusgen/wasrusgen1-crm.git
synced 2026-06-03 15:44:45 +00:00
feat: Клиенты — список с фильтрами (Все/Лиды/Активные/Закрытые) и поиском, карточка открывается из списка
This commit is contained in:
parent
6f0e54c0bd
commit
297fcf90e6
@ -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';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user