diff --git a/docs/data.js b/docs/data.js new file mode 100644 index 0000000..7cd5a0e --- /dev/null +++ b/docs/data.js @@ -0,0 +1,161 @@ +/** + * @wasrusgen1 CRM — Единый источник данных + * Подключается во все кабинеты: + * Содержит: вертикаль управления, менеджеры, расписание, заявки, статистика + * Версия: 2026-05-28 + */ + +// ═══════════════════════════════════════════════════════════════════ +// ВЕРТИКАЛЬ: КД → АДМИНИСТРАТОР → МЕНЕДЖЕРЫ +// Единый источник истины для всех кабинетов +// ═══════════════════════════════════════════════════════════════════ +var _HIERARCHY = [ + { + salonId:'lenina', salon:'Салон Ленина', color:'#3B82F6', + admin:{name:'Анна М.', short:'АМ', userId:'admin_lenina'}, + orders:27, ordersPlan:30, revenue:1537000, revenuePlan:1700000, + overdue:1, overdueRisk:98500, purchases:2, newLeads:14, status:'warn', + managers:[ + {id:'ak', name:'Анна К.', short:'АК', color:'#7C3AED', salon:'lenina', visits:42, deals:14, revenue:847000, conversion:33, avg:60500, rating:4.8, active:true}, + {id:'ms', name:'Мария С.', short:'МС', color:'#0891B2', salon:'lenina', visits:35, deals:9, revenue:610000, conversion:26, avg:67800, rating:4.5, active:true}, + ] + }, + { + salonId:'pobedy', salon:'Салон Победы', color:'#8B5CF6', + admin:{name:'Ирина С.', short:'ИС', userId:'admin_pobedy'}, + orders:20, ordersPlan:22, revenue:1310000, revenuePlan:1500000, + overdue:1, overdueRisk:113000, purchases:1, newLeads:12, status:'warn', + managers:[ + {id:'pv', name:'Пётр В.', short:'ПВ', color:'#059669', salon:'pobedy', visits:28, deals:7, revenue:490000, conversion:25, avg:70000, rating:4.3, active:true}, + {id:'iv', name:'Иван В.', short:'ИВ', color:'#D97706', salon:'pobedy', visits:31, deals:8, revenue:530000, conversion:26, avg:66250, rating:4.1, active:false}, + ] + }, +]; + +// ═══════════════════════════════════════════════════════════════════ +// ПЛОСКИЙ СПИСОК ВСЕХ МЕНЕДЖЕРОВ (производный от _HIERARCHY) +// ═══════════════════════════════════════════════════════════════════ +var _CHESS_MGRS = (function(){ + var list = []; + _HIERARCHY.forEach(function(h){ + h.managers.forEach(function(m){ list.push(m); }); + }); + return list; +})(); + +// ═══════════════════════════════════════════════════════════════════ +// ШАХМАТКА: временные слоты и расписание +// ═══════════════════════════════════════════════════════════════════ +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_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', salon:'lenina', 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', salon:'lenina', 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', salon:'pobedy', 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', salon:'pobedy', 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._MGR_REQUESTS = window._MGR_REQUESTS || [ + {id:'r1', mgr:'Анна К.', mgrId:'ak', salon:'lenina', color:'#7C3AED', type:'supply', prio:'high', title:'Закончились образцы ткани', body:'Нет образцов искусственной замши — теряем клиентов. Нужно срочно дозаказать у Мебельтекстиля.', created:'сегодня 09:14', status:'new'}, + {id:'r2', mgr:'Мария С.', mgrId:'ms', salon:'lenina', color:'#0891B2', type:'escalate', prio:'high', title:'Конфликт с клиентом Козлов Р.', body:'Клиент требует возврат 25 000 ₽ из-за задержки поставки. Говорит, что подаст жалобу. Прошу вмешаться.', created:'сегодня 10:30', status:'new'}, + {id:'r3', mgr:'Пётр В.', mgrId:'pv', salon:'pobedy', color:'#059669', type:'visit', prio:'normal', title:'Нужна машина для выезда к клиенту', body:'Клиент Сидорова на Васильевском — хочет видеть образцы на дому. Нужен транспорт на 15:00.', created:'сегодня 11:05', status:'new'}, + {id:'r4', mgr:'Анна К.', mgrId:'ak', salon:'lenina', color:'#7C3AED', type:'supply', prio:'normal', title:'Каталоги кухонь Hettich закончились', body:'Остался 1 экземпляр. Клиенты берут домой, не возвращают. Надо заказать 10 шт.', created:'вчера 17:22', status:'done'}, + {id:'r5', mgr:'Мария С.', mgrId:'ms', salon:'lenina', color:'#0891B2', type:'schedule', prio:'normal', title:'Запрос на замену смены — 31 мая', body:'Прошу разрешить обменяться сменой с Петром: я работаю 31 мая вместо него, он — 1 июня вместо меня.', created:'вчера 14:10', status:'new'}, +]; + +// ═══════════════════════════════════════════════════════════════════ +// ЗАПРОСЫ НА СМЕНЫ +// ═══════════════════════════════════════════════════════════════════ +window._SHIFT_REQS = window._SHIFT_REQS || [ + {id:'sr1', mgr:'Мария С.', mgrId:'ms', salon:'lenina', color:'#0891B2', type:'swap', dates:'31 мая ↔ 1 июня', with:'Пётр В.', status:'pending'}, + {id:'sr2', mgr:'Пётр В.', mgrId:'pv', salon:'pobedy', color:'#059669', type:'off', dates:'2 июня (отгул)', with:null, status:'pending'}, +]; + +// ═══════════════════════════════════════════════════════════════════ +// ХЕЛПЕРЫ — утилиты доступные всем кабинетам +// ═══════════════════════════════════════════════════════════════════ + +/** Найти запись _HIERARCHY по salonId */ +function _getSalon(salonId){ + return _HIERARCHY.find(function(h){ return h.salonId===salonId; }) || null; +} + +/** Найти менеджера по id из плоского списка */ +function _getMgr(mgrId){ + return _CHESS_MGRS.find(function(m){ return m.id===mgrId; }) || null; +} + +/** Получить KPI менеджера за период (из _MONTHLY_STATS) */ +function _getMgrStats(mgrId, period){ + var stat = _MONTHLY_STATS.find(function(s){ return s.id===mgrId; }); + return stat ? (stat.months[period||'Май'] || {}) : {}; +} diff --git a/docs/mockup_admin.html b/docs/mockup_admin.html index b984919..826102f 100644 --- a/docs/mockup_admin.html +++ b/docs/mockup_admin.html @@ -65,110 +65,20 @@ body[data-theme="dark"]{--accent:#4338CA;--accent2:#6366F1;--bg:#111827;--card:#
+