mirror of
https://github.com/wasrusgen/wasrusgen1-crm.git
synced 2026-06-03 17:04:46 +00:00
feat: шахматка менеджеров, помесячные итоги, блок менеджеры-сейчас на главной
This commit is contained in:
parent
118804cb03
commit
5030931dc1
@ -74,7 +74,89 @@ window._MGR_REQUESTS = window._MGR_REQUESTS || [
|
|||||||
{id:'r4', mgr:'Анна К.', color:'#7C3AED', type:'supply', prio:'normal', title:'Каталоги кухонь Hettich закончились', body:'Остался 1 экземпляр. Клиенты берут домой, не возвращают. Надо заказать 10 шт.', created:'вчера 17:22', status:'done'},
|
{id:'r4', mgr:'Анна К.', color:'#7C3AED', type:'supply', prio:'normal', title:'Каталоги кухонь Hettich закончились', body:'Остался 1 экземпляр. Клиенты берут домой, не возвращают. Надо заказать 10 шт.', created:'вчера 17:22', status:'done'},
|
||||||
{id:'r5', mgr:'Мария С.', color:'#0891B2', type:'schedule', prio:'normal', title:'Запрос на замену смены — 31 мая', body:'Прошу разрешить обменяться сменой с Петром: я работаю 31 мая вместо него, он — 1 июня вместо меня.', created:'вчера 14:10', status:'new'},
|
{id:'r5', mgr:'Мария С.', color:'#0891B2', type:'schedule', prio:'normal', title:'Запрос на замену смены — 31 мая', body:'Прошу разрешить обменяться сменой с Петром: я работаю 31 мая вместо него, он — 1 июня вместо меня.', created:'вчера 14:10', status:'new'},
|
||||||
];
|
];
|
||||||
window._staffSubTab = window._staffSubTab || 'schedule';
|
window._staffSubTab = window._staffSubTab || 'chess';
|
||||||
|
|
||||||
|
// ── ДАННЫЕ: ШАХМАТКА (расписание менеджеров × слоты) ─────────────────────────
|
||||||
|
var _CHESS_HOURS = ['10:00','11:00','12:00','13:00','14:00','15:00','16:00','17:00','18:00','19:00','20:00'];
|
||||||
|
var _CHESS_MGRS = [
|
||||||
|
{id:'ak', name:'Анна К.', short:'АК', color:'#7C3AED'},
|
||||||
|
{id:'ms', name:'Мария С.', short:'МС', color:'#0891B2'},
|
||||||
|
{id:'pv', name:'Пётр В.', short:'ПВ', color:'#059669'},
|
||||||
|
{id:'iv', name:'Иван В.', short:'ИВ', color:'#D97706'},
|
||||||
|
];
|
||||||
|
// ячейка: {client, type, status} status: free|busy|done|noshow
|
||||||
|
// type: consult|measure|follow|tech
|
||||||
|
var _CHESS_DATA = {
|
||||||
|
'ak':{'10:00':{client:'Орлова М.', type:'consult', status:'done'},
|
||||||
|
'11:00':{client:'Соколов А.', type:'follow', status:'done'},
|
||||||
|
'12:00':{client:'', type:'', status:'free'},
|
||||||
|
'13:00':{client:'', type:'', status:'free'},
|
||||||
|
'14:00':{client:'Ким Л.', type:'consult', status:'busy'},
|
||||||
|
'15:00':{client:'Ким Л.', type:'consult', status:'busy'},
|
||||||
|
'16:00':{client:'Захаров П.', type:'measure', status:'busy'},
|
||||||
|
'17:00':{client:'Новикова С.', type:'consult', status:'free'},
|
||||||
|
'18:00':{client:'', type:'', status:'free'},
|
||||||
|
'19:00':{client:'Громов И.', type:'follow', status:'free'},
|
||||||
|
'20:00':{client:'', type:'', status:'free'}},
|
||||||
|
'ms':{'10:00':{client:'Козлов Р.', type:'follow', status:'done'},
|
||||||
|
'11:00':{client:'', type:'', status:'free'},
|
||||||
|
'12:00':{client:'Лебедев С.', type:'consult', status:'done'},
|
||||||
|
'13:00':{client:'', type:'', status:'free'},
|
||||||
|
'14:00':{client:'Петрова А.', type:'tech', status:'busy'},
|
||||||
|
'15:00':{client:'Петрова А.', type:'tech', status:'busy'},
|
||||||
|
'16:00':{client:'', type:'', status:'free'},
|
||||||
|
'17:00':{client:'Морозов В.', type:'consult', status:'busy'},
|
||||||
|
'18:00':{client:'', type:'', status:'free'},
|
||||||
|
'19:00':{client:'', type:'', status:'free'},
|
||||||
|
'20:00':{client:'', type:'', status:'free'}},
|
||||||
|
'pv':{'10:00':{client:'', type:'', status:'free'},
|
||||||
|
'11:00':{client:'Сидорова Н.', type:'consult', status:'done'},
|
||||||
|
'12:00':{client:'Фёдоров К.', type:'follow', status:'noshow'},
|
||||||
|
'13:00':{client:'', type:'', status:'free'},
|
||||||
|
'14:00':{client:'', type:'', status:'free'},
|
||||||
|
'15:00':{client:'Баринова Т.', type:'consult', status:'busy'},
|
||||||
|
'16:00':{client:'Баринова Т.', type:'consult', status:'busy'},
|
||||||
|
'17:00':{client:'', type:'', status:'free'},
|
||||||
|
'18:00':{client:'Яковлев М.', type:'measure', status:'busy'},
|
||||||
|
'19:00':{client:'Яковлев М.', type:'measure', status:'busy'},
|
||||||
|
'20:00':{client:'', type:'', status:'free'}},
|
||||||
|
'iv':{'10:00':{client:'Тихонова Р.', type:'consult', status:'done'},
|
||||||
|
'11:00':{client:'Тихонова Р.', type:'consult', status:'done'},
|
||||||
|
'12:00':{client:'', type:'', status:'free'},
|
||||||
|
'13:00':{client:'Волков Е.', type:'follow', status:'done'},
|
||||||
|
'14:00':{client:'', type:'', status:'free'},
|
||||||
|
'15:00':{client:'Кузьмин О.', type:'consult', status:'busy'},
|
||||||
|
'16:00':{client:'', type:'', status:'free'},
|
||||||
|
'17:00':{client:'Рыбаков Д.', type:'tech', status:'free'},
|
||||||
|
'18:00':{client:'Рыбаков Д.', type:'tech', status:'free'},
|
||||||
|
'19:00':{client:'', type:'', status:'free'},
|
||||||
|
'20:00':{client:'', type:'', status:'free'}},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── ДАННЫЕ: ИТОГИ ПО МЕНЕДЖЕРАМ (по месяцам) ──────────────────────────────
|
||||||
|
var _MONTHLY_STATS = [
|
||||||
|
{id:'ak', name:'Анна К.', color:'#7C3AED', months:{
|
||||||
|
'Май':{visits:42, deals:14, revenue:847000, conversion:33, avg:60500, rating:4.8},
|
||||||
|
'Апр':{visits:38, deals:11, revenue:712000, conversion:29, avg:64700, rating:4.7},
|
||||||
|
'Мар':{visits:45, deals:16, revenue:920000, conversion:36, avg:57500, rating:4.9},
|
||||||
|
}},
|
||||||
|
{id:'ms', name:'Мария С.', color:'#0891B2', months:{
|
||||||
|
'Май':{visits:35, deals:9, revenue:610000, conversion:26, avg:67800, rating:4.5},
|
||||||
|
'Апр':{visits:41, deals:13, revenue:780000, conversion:32, avg:60000, rating:4.6},
|
||||||
|
'Мар':{visits:39, deals:12, revenue:695000, conversion:31, avg:57900, rating:4.4},
|
||||||
|
}},
|
||||||
|
{id:'pv', name:'Пётр В.', color:'#059669', months:{
|
||||||
|
'Май':{visits:28, deals:7, revenue:490000, conversion:25, avg:70000, rating:4.3},
|
||||||
|
'Апр':{visits:32, deals:9, revenue:540000, conversion:28, avg:60000, rating:4.2},
|
||||||
|
'Мар':{visits:30, deals:8, revenue:520000, conversion:27, avg:65000, rating:4.4},
|
||||||
|
}},
|
||||||
|
{id:'iv', name:'Иван В.', color:'#D97706', months:{
|
||||||
|
'Май':{visits:31, deals:8, revenue:530000, conversion:26, avg:66250, rating:4.1},
|
||||||
|
'Апр':{visits:29, deals:7, revenue:480000, conversion:24, avg:68600, rating:4.0},
|
||||||
|
'Мар':{visits:33, deals:10, revenue:610000, conversion:30, avg:61000, rating:4.3},
|
||||||
|
}},
|
||||||
|
];
|
||||||
|
window._monthlyPeriod = window._monthlyPeriod || 'Май';
|
||||||
|
|
||||||
// ── СОГЛАСОВАНИЕ СМЕН: запросы ─────────────────────────────────────────────
|
// ── СОГЛАСОВАНИЕ СМЕН: запросы ─────────────────────────────────────────────
|
||||||
window._SHIFT_REQS = window._SHIFT_REQS || [
|
window._SHIFT_REQS = window._SHIFT_REQS || [
|
||||||
@ -424,6 +506,29 @@ function _screenHome(){
|
|||||||
+'</div>';
|
+'</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Менеджеры сейчас ──
|
||||||
|
var mgrNowHtml = '<div style="padding:0 16px;margin-bottom:4px">'
|
||||||
|
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">👥 Менеджеры сейчас</div>'
|
||||||
|
+'<div style="background:var(--card);border-radius:14px;box-shadow:0 2px 8px rgba(0,0,0,.06);overflow:hidden">'
|
||||||
|
+_CHESS_MGRS.map(function(mgr){
|
||||||
|
var rd=_CHESS_DATA[mgr.id]||{};
|
||||||
|
var curSlot=rd['15:00']||{};
|
||||||
|
var nextSlot=rd['16:00']||{};
|
||||||
|
var statusText=curSlot.client?'С клиентом: '+curSlot.client:'Свободен';
|
||||||
|
var statusColor=curSlot.client?'var(--success)':'var(--muted)';
|
||||||
|
var nextText=nextSlot.client?'→ '+nextSlot.client:'→ Свободен';
|
||||||
|
return '<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid rgba(0,0,0,.05);cursor:pointer" onclick="window._staffSubTab=\'chess\';_nav(\'staff\')">'
|
||||||
|
+'<div style="width:34px;height:34px;border-radius:50%;background:'+mgr.color+';display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;color:#fff;flex-shrink:0">'+mgr.short+'</div>'
|
||||||
|
+'<div style="flex:1;min-width:0">'
|
||||||
|
+'<div style="font-size:13px;font-weight:700;color:var(--ink)">'+mgr.name+'</div>'
|
||||||
|
+'<div style="font-size:11px;color:'+statusColor+';font-weight:600">'+statusText+'</div>'
|
||||||
|
+'</div>'
|
||||||
|
+'<div style="font-size:10px;color:var(--muted);text-align:right;flex-shrink:0">'+nextText+'</div>'
|
||||||
|
+'</div>';
|
||||||
|
}).join('')
|
||||||
|
+'<div style="padding:8px 12px;background:rgba(0,62,126,.04);cursor:pointer;text-align:center;font-size:12px;font-weight:700;color:var(--accent)" onclick="window._staffSubTab=\'chess\';_nav(\'staff\')">🔲 Открыть шахматку →</div>'
|
||||||
|
+'</div></div>';
|
||||||
|
|
||||||
// ── Быстрые действия ──
|
// ── Быстрые действия ──
|
||||||
var quickHtml = '<div style="padding:0 16px;display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:4px">'
|
var quickHtml = '<div style="padding:0 16px;display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:4px">'
|
||||||
+'<button onclick="_nav(\'sup_new\')" style="padding:11px;font-size:13px;font-weight:700;border-radius:14px;border:1.5px solid rgba(0,62,126,.2);background:rgba(0,62,126,.06);color:var(--accent);cursor:pointer">🛒 Создать заявку</button>'
|
+'<button onclick="_nav(\'sup_new\')" style="padding:11px;font-size:13px;font-weight:700;border-radius:14px;border:1.5px solid rgba(0,62,126,.2);background:rgba(0,62,126,.06);color:var(--accent);cursor:pointer">🛒 Создать заявку</button>'
|
||||||
@ -432,13 +537,14 @@ function _screenHome(){
|
|||||||
|
|
||||||
return '<div class="page anim">'
|
return '<div class="page anim">'
|
||||||
+'<div style="padding:16px 16px 12px;background:var(--card);border-bottom:1px solid var(--line)">'
|
+'<div style="padding:16px 16px 12px;background:var(--card);border-bottom:1px solid var(--line)">'
|
||||||
+'<div style="font-size:12px;color:var(--muted);margin-bottom:2px">Чт, 22 мая 2026</div>'
|
+'<div style="font-size:12px;color:var(--muted);margin-bottom:2px">Чт, 29 мая 2026</div>'
|
||||||
+'<div style="font-size:22px;font-weight:800;color:var(--ink)">Добрый день, Анна 👋</div>'
|
+'<div style="font-size:22px;font-weight:800;color:var(--ink)">Добрый день, Анна 👋</div>'
|
||||||
+'<div style="font-size:13px;color:var(--muted);margin-top:2px">Салон Ленина · Администратор</div>'
|
+'<div style="font-size:13px;color:var(--muted);margin-top:2px">Салон Ленина · Администратор</div>'
|
||||||
+'</div>'
|
+'</div>'
|
||||||
+kpiHtml
|
+kpiHtml
|
||||||
+barHtml
|
+barHtml
|
||||||
+alertsHtml
|
+alertsHtml
|
||||||
|
+mgrNowHtml
|
||||||
+quickHtml
|
+quickHtml
|
||||||
+'</div>';
|
+'</div>';
|
||||||
}
|
}
|
||||||
@ -1083,14 +1189,24 @@ function _screenStaff(){
|
|||||||
var sub = window._staffSubTab || 'schedule';
|
var sub = window._staffSubTab || 'schedule';
|
||||||
var mgrNew = (window._MGR_REQUESTS||[]).filter(function(r){return r.status==='new';}).length;
|
var mgrNew = (window._MGR_REQUESTS||[]).filter(function(r){return r.status==='new';}).length;
|
||||||
var shiftNew = (window._SHIFT_REQS||[]).filter(function(r){return r.status==='pending';}).length;
|
var shiftNew = (window._SHIFT_REQS||[]).filter(function(r){return r.status==='pending';}).length;
|
||||||
var chipBar = '<div style="display:flex;gap:6px;padding:10px 16px 0;background:var(--card);border-bottom:1px solid var(--line)">'
|
var _tabs=[
|
||||||
+'<div onclick="window._staffSubTab=\'schedule\';_render()" style="padding:7px 16px;border-radius:20px 20px 0 0;font-size:13px;font-weight:700;cursor:pointer;border-bottom:2px solid '+(sub==='schedule'?'var(--accent)':'transparent')+';color:'+(sub==='schedule'?'var(--accent)':'var(--muted)')+'">📅 График</div>'
|
{key:'chess', label:'🔲 Шахматка'},
|
||||||
+'<div onclick="window._staffSubTab=\'requests\';_render()" style="padding:7px 16px;border-radius:20px 20px 0 0;font-size:13px;font-weight:700;cursor:pointer;border-bottom:2px solid '+(sub==='requests'?'var(--accent)':'transparent')+';color:'+(sub==='requests'?'var(--accent)':'var(--muted)')+';position:relative">'
|
{key:'schedule', label:'📅 График'},
|
||||||
+'📋 Заявки'
|
{key:'requests', label:'📋 Заявки', badge: mgrNew+shiftNew},
|
||||||
+(mgrNew+shiftNew>0?'<span style="position:absolute;top:3px;right:2px;min-width:16px;height:16px;border-radius:8px;background:var(--danger);color:#fff;font-size:9px;font-weight:800;display:inline-flex;align-items:center;justify-content:center;padding:0 3px">'+(mgrNew+shiftNew)+'</span>':'')
|
{key:'monthly', label:'📊 Итоги'},
|
||||||
+'</div>'
|
];
|
||||||
|
var chipBar = '<div style="display:flex;gap:0;background:var(--card);border-bottom:1px solid var(--line);overflow-x:auto;scrollbar-width:none;padding:0 8px">'
|
||||||
|
+_tabs.map(function(t){
|
||||||
|
var act=sub===t.key;
|
||||||
|
return '<div onclick="window._staffSubTab=\''+t.key+'\';_render()" style="padding:10px 12px;font-size:12px;font-weight:700;cursor:pointer;border-bottom:2px solid '+(act?'var(--accent)':'transparent')+';color:'+(act?'var(--accent)':'var(--muted)')+';white-space:nowrap;position:relative;flex-shrink:0">'
|
||||||
|
+t.label
|
||||||
|
+(t.badge>0?'<span style="position:absolute;top:6px;right:2px;min-width:15px;height:15px;border-radius:8px;background:var(--danger);color:#fff;font-size:9px;font-weight:800;display:inline-flex;align-items:center;justify-content:center;padding:0 2px">'+t.badge+'</span>':'')
|
||||||
|
+'</div>';
|
||||||
|
}).join('')
|
||||||
+'</div>';
|
+'</div>';
|
||||||
if(sub==='requests') return '<div class="page anim">'+chipBar+_screenRequests()+'</div>';
|
if(sub==='requests') return '<div class="page anim">'+chipBar+_screenRequests()+'</div>';
|
||||||
|
if(sub==='chess') return '<div class="page anim">'+chipBar+_screenChess()+'</div>';
|
||||||
|
if(sub==='monthly') return '<div class="page anim">'+chipBar+_screenMonthly()+'</div>';
|
||||||
|
|
||||||
var edit = window._staffEditMode;
|
var edit = window._staffEditMode;
|
||||||
|
|
||||||
@ -1496,6 +1612,142 @@ function _cancelCheck(){
|
|||||||
_nav('inventory');
|
_nav('inventory');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── ШАХМАТКА ──────────────────────────────────────────────────────────────────
|
||||||
|
function _screenChess(){
|
||||||
|
var typeColor={consult:'#3B82F6',measure:'#8B5CF6',follow:'#059669',tech:'#F59E0B'};
|
||||||
|
var typeLabel={consult:'Консульт.',measure:'Замер',follow:'Повторный',tech:'Тех.вопрос'};
|
||||||
|
var statusBg={free:'transparent',busy:'',done:'',noshow:'rgba(239,68,68,.12)'};
|
||||||
|
var now = 15; // текущий час для демо
|
||||||
|
|
||||||
|
// Текущий час-индикатор
|
||||||
|
var nowIdx = _CHESS_HOURS.indexOf('15:00');
|
||||||
|
|
||||||
|
var CELL_W = 68; // px ширина ячейки
|
||||||
|
var NAME_W = 40; // px ширина имени менеджера
|
||||||
|
|
||||||
|
// Шапка часов
|
||||||
|
var headerRow = '<div style="display:flex;align-items:center;position:sticky;top:0;z-index:5;background:var(--card);border-bottom:1px solid var(--line)">'
|
||||||
|
+'<div style="width:'+NAME_W+'px;flex-shrink:0;padding:6px 4px;font-size:9px;font-weight:700;color:var(--muted)">Менеджер</div>'
|
||||||
|
+_CHESS_HOURS.map(function(h,i){
|
||||||
|
var isCur=i===nowIdx;
|
||||||
|
return '<div style="width:'+CELL_W+'px;flex-shrink:0;text-align:center;padding:6px 2px;font-size:10px;font-weight:'+(isCur?'800':'600')+';color:'+(isCur?'var(--accent)':'var(--muted)')+';border-left:1px solid rgba(0,0,0,.06);background:'+(isCur?'rgba(0,62,126,.05)':'')+'">'+h+'</div>';
|
||||||
|
}).join('')
|
||||||
|
+'</div>';
|
||||||
|
|
||||||
|
// Строки менеджеров
|
||||||
|
var rows = _CHESS_MGRS.map(function(mgr){
|
||||||
|
var rowData = _CHESS_DATA[mgr.id]||{};
|
||||||
|
return '<div style="display:flex;align-items:stretch;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||||
|
+'<div style="width:'+NAME_W+'px;flex-shrink:0;display:flex;align-items:center;justify-content:center;padding:4px 2px">'
|
||||||
|
+'<div style="width:30px;height:30px;border-radius:50%;background:'+mgr.color+';display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:800;color:#fff">'+mgr.short+'</div>'
|
||||||
|
+'</div>'
|
||||||
|
+_CHESS_HOURS.map(function(h,i){
|
||||||
|
var cell=rowData[h]||{status:'free'};
|
||||||
|
var isCur=i===nowIdx;
|
||||||
|
var bg=cell.status==='done'?'rgba(16,185,129,.1)':cell.status==='busy'?'rgba(59,130,246,.1)':cell.status==='noshow'?'rgba(239,68,68,.1)':'';
|
||||||
|
var brd=cell.status==='done'?'1px solid rgba(16,185,129,.25)':cell.status==='busy'?'1px solid rgba(59,130,246,.25)':cell.status==='noshow'?'1px solid rgba(239,68,68,.2)':'1px solid rgba(0,0,0,.05)';
|
||||||
|
var txt=cell.status==='done'?'var(--success)':cell.status==='busy'?'#1D4ED8':cell.status==='noshow'?'var(--danger)':'var(--muted)';
|
||||||
|
var icon=cell.status==='done'?'✓':cell.status==='noshow'?'✗':cell.status==='busy'?'●':'';
|
||||||
|
return '<div style="width:'+CELL_W+'px;flex-shrink:0;min-height:54px;border-left:'+brd+';background:'+(isCur?'rgba(0,62,126,.04)':bg||'transparent')+';padding:4px;display:flex;flex-direction:column;justify-content:center;cursor:'+(cell.client?'pointer':'default')+'" onclick="'+( cell.client?'alert(\''+mgr.name+' · '+h+' · '+cell.client+' · '+(typeLabel[cell.type]||'')+'\')':'')+'">'
|
||||||
|
+(cell.client
|
||||||
|
?'<div style="font-size:9px;font-weight:700;color:'+txt+';line-height:1.2;margin-bottom:2px">'+icon+' '+cell.client+'</div>'
|
||||||
|
+'<div style="font-size:8px;font-weight:600;background:'+(typeColor[cell.type]||'#94A3B8')+'20;color:'+(typeColor[cell.type]||'#94A3B8')+';padding:1px 4px;border-radius:4px;display:inline-block">'+typeLabel[cell.type]+'</div>'
|
||||||
|
:'<div style="font-size:11px;color:rgba(0,0,0,.1);text-align:center">—</div>')
|
||||||
|
+'</div>';
|
||||||
|
}).join('')
|
||||||
|
+'</div>';
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
// Легенда
|
||||||
|
var legend='<div style="display:flex;gap:10px;padding:8px 12px;flex-wrap:wrap">'
|
||||||
|
+'<span style="font-size:10px;color:var(--success);font-weight:700">✓ Завершено</span>'
|
||||||
|
+'<span style="font-size:10px;color:#1D4ED8;font-weight:700">● Занято</span>'
|
||||||
|
+'<span style="font-size:10px;color:var(--danger);font-weight:700">✗ Не пришёл</span>'
|
||||||
|
+'<span style="font-size:10px;color:var(--muted);font-weight:700">— Свободно</span>'
|
||||||
|
+'</div>';
|
||||||
|
|
||||||
|
// Итог строки: занятость
|
||||||
|
var summary='<div style="padding:8px 12px;border-top:1px solid var(--line)">'
|
||||||
|
+'<div style="font-size:11px;font-weight:700;color:var(--muted);margin-bottom:6px">ЗАНЯТОСТЬ СЕГОДНЯ</div>'
|
||||||
|
+'<div style="display:flex;flex-direction:column;gap:5px">'
|
||||||
|
+_CHESS_MGRS.map(function(mgr){
|
||||||
|
var rd=_CHESS_DATA[mgr.id]||{};
|
||||||
|
var total=_CHESS_HOURS.length;
|
||||||
|
var busy=_CHESS_HOURS.filter(function(h){return (rd[h]||{}).client;}).length;
|
||||||
|
var pct=Math.round(busy/total*100);
|
||||||
|
var col=pct>=70?'var(--success)':pct>=40?'var(--warn)':'var(--muted)';
|
||||||
|
return '<div style="display:flex;align-items:center;gap:8px">'
|
||||||
|
+'<div style="width:22px;height:22px;border-radius:50%;background:'+mgr.color+';display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:800;color:#fff;flex-shrink:0">'+mgr.short+'</div>'
|
||||||
|
+'<div style="flex:1"><div style="height:6px;background:var(--line);border-radius:3px;overflow:hidden"><div style="height:100%;background:'+col+';border-radius:3px;width:'+pct+'%"></div></div></div>'
|
||||||
|
+'<div style="font-size:11px;font-weight:700;color:'+col+';width:28px;text-align:right">'+pct+'%</div>'
|
||||||
|
+'<div style="font-size:10px;color:var(--muted);width:32px">'+busy+'/'+total+'сл</div>'
|
||||||
|
+'</div>';
|
||||||
|
}).join('')
|
||||||
|
+'</div></div>';
|
||||||
|
|
||||||
|
return '<div style="overflow-x:auto;scrollbar-width:thin;-webkit-overflow-scrolling:touch">'
|
||||||
|
+'<div style="min-width:'+(_CHESS_HOURS.length*CELL_W+NAME_W)+'px">'
|
||||||
|
+headerRow+rows
|
||||||
|
+'</div></div>'
|
||||||
|
+legend+summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ПОМЕСЯЧНЫЕ ИТОГИ ───────────────────────────────────────────────────────────
|
||||||
|
function _screenMonthly(){
|
||||||
|
var months=['Май','Апр','Мар'];
|
||||||
|
var per=window._monthlyPeriod||'Май';
|
||||||
|
|
||||||
|
var periodTabs='<div style="display:flex;gap:6px;padding:10px 12px;overflow-x:auto;scrollbar-width:none">'
|
||||||
|
+months.map(function(m){
|
||||||
|
var act=m===per;
|
||||||
|
return '<div onclick="window._monthlyPeriod=\''+m+'\';_render()" style="padding:6px 16px;border-radius:20px;font-size:12px;font-weight:700;cursor:pointer;border:1.5px solid '+(act?'var(--accent)':'var(--line)')+';background:'+(act?'var(--accent)':'var(--card)')+';color:'+(act?'#fff':'var(--muted)')+';flex-shrink:0">'+m+'</div>';
|
||||||
|
}).join('')
|
||||||
|
+'</div>';
|
||||||
|
|
||||||
|
// Топ-менеджер
|
||||||
|
var sorted=_MONTHLY_STATS.slice().sort(function(a,b){return (b.months[per]||{revenue:0}).revenue-(a.months[per]||{revenue:0}).revenue;});
|
||||||
|
var top=sorted[0];
|
||||||
|
|
||||||
|
var topCard='<div style="margin:0 12px 12px;background:linear-gradient(135deg,#002450,#003E7E,#1560BD);border-radius:16px;padding:14px">'
|
||||||
|
+'<div style="font-size:10px;font-weight:700;color:rgba(255,255,255,.6);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">🏆 Лидер месяца · '+per+'</div>'
|
||||||
|
+'<div style="display:flex;align-items:center;gap:10px">'
|
||||||
|
+'<div style="width:40px;height:40px;border-radius:50%;background:'+top.color+';display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:800;color:#fff">'+top.name.split(' ').map(function(w){return w[0];}).join('')+'</div>'
|
||||||
|
+'<div><div style="font-size:16px;font-weight:800;color:#fff">'+top.name+'</div>'
|
||||||
|
+'<div style="font-size:12px;color:rgba(255,255,255,.7)">'+_fmtMoney((top.months[per]||{revenue:0}).revenue)+' · '+((top.months[per]||{conversion:0}).conversion)+'% конверсия</div>'
|
||||||
|
+'</div></div></div>';
|
||||||
|
|
||||||
|
var cards=sorted.map(function(mgr,idx){
|
||||||
|
var s=mgr.months[per]||{};
|
||||||
|
var prev=mgr.months[months[1]]||{};
|
||||||
|
var revDelta=s.revenue&&prev.revenue?Math.round((s.revenue-prev.revenue)/prev.revenue*100):0;
|
||||||
|
var convDelta=s.conversion&&prev.conversion?s.conversion-prev.conversion:0;
|
||||||
|
return '<div style="background:var(--card);border-radius:14px;padding:14px;margin:0 12px 10px;box-shadow:0 2px 8px rgba(0,0,0,.06)">'
|
||||||
|
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">'
|
||||||
|
+'<div style="width:36px;height:36px;border-radius:50%;background:'+mgr.color+';display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff">'+mgr.name.split(' ').map(function(w){return w[0];}).join('')+'</div>'
|
||||||
|
+'<div style="flex:1"><div style="font-size:14px;font-weight:700;color:var(--ink)">'+mgr.name+'</div>'
|
||||||
|
+'<div style="font-size:11px;color:var(--muted)">Рейтинг: ★ '+s.rating+'</div></div>'
|
||||||
|
+'<div style="font-size:11px;font-weight:800;color:'+(idx===0?'#D97706':'var(--muted)')+'">#'+(idx+1)+'</div>'
|
||||||
|
+'</div>'
|
||||||
|
// Метрики 2×2
|
||||||
|
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">'
|
||||||
|
+[
|
||||||
|
{label:'Выручка', val:_fmtMoney(s.revenue||0), delta:revDelta+'%', up:revDelta>=0},
|
||||||
|
{label:'Конверсия', val:(s.conversion||0)+'%', delta:(convDelta>=0?'+':'')+convDelta+'%', up:convDelta>=0},
|
||||||
|
{label:'Визиты', val:(s.visits||0)+' кл.', delta:'', up:true},
|
||||||
|
{label:'Сделки', val:(s.deals||0)+' шт.', delta:'ср. '+_fmtMoney(s.avg||0), up:true},
|
||||||
|
].map(function(m){
|
||||||
|
return '<div style="background:var(--bg);border-radius:10px;padding:10px">'
|
||||||
|
+'<div style="font-size:10px;color:var(--muted);font-weight:600;margin-bottom:3px">'+m.label+'</div>'
|
||||||
|
+'<div style="font-size:15px;font-weight:800;color:var(--ink)">'+m.val+'</div>'
|
||||||
|
+(m.delta?'<div style="font-size:10px;font-weight:700;color:'+(m.up?'var(--success)':'var(--danger)')+'">'+m.delta+'</div>':'')
|
||||||
|
+'</div>';
|
||||||
|
}).join('')
|
||||||
|
+'</div></div>';
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
return periodTabs+topCard+cards;
|
||||||
|
}
|
||||||
|
|
||||||
// ── ЗАЯВКИ МЕНЕДЖЕРОВ ─────────────────────────────────────────────────────────
|
// ── ЗАЯВКИ МЕНЕДЖЕРОВ ─────────────────────────────────────────────────────────
|
||||||
function _screenRequests(){
|
function _screenRequests(){
|
||||||
var filter = window._reqFilter || 'all';
|
var filter = window._reqFilter || 'all';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user