mirror of
https://github.com/wasrusgen/wasrusgen1-crm.git
synced 2026-06-03 18:24:47 +00:00
162 lines
13 KiB
JavaScript
162 lines
13 KiB
JavaScript
/**
|
||
* @wasrusgen1 CRM — Единый источник данных
|
||
* Подключается во все кабинеты: <script src="data.js"></script>
|
||
* Содержит: вертикаль управления, менеджеры, расписание, заявки, статистика
|
||
* Версия: 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||'Май'] || {}) : {};
|
||
}
|