// Демо-данные: ~30 заявок как в брифе (30-50 в день, пик до 100) const DEMO_DATA = [ { id: 1, name: "Алиев Арман Берикович", phone: "+7 777 123 45 67", address: "г. Алматы, ул. Абая 10, кв. 5", tariff: "Домашний интернет 100 Мбит/с", manager: "Серик Ж.", status: "в работе", comment: "", date: "2026-08-27", isDuplicate: false }, { id: 2, name: "Байкова Гульнара Талгатовна", phone: "+7 701 234 56 78", address: "г. Алматы, пр. Достык 45, офис 12", tariff: "ТВ + Интернет", manager: "Айгуль К.", status: "новая", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 3, name: "Васильченко Игорь Петрович", phone: "+7 778 345 67 89", address: "г. Алматы, ул. Толе би 88, кв. 42", tariff: "Домашний интернет 500 Мбит/с", manager: "", status: "новая", comment: "Клиент просит перезвонить после 18:00", date: "2026-08-28", isDuplicate: false }, { id: 4, name: "Гребенюк Сергей Владимирович", phone: "+7 702 456 78 90", address: "г. Алматы, мкр. Аксай-2, д. 15", tariff: "Тройной пакет", manager: "Серик Ж.", status: "на проверке", comment: "", date: "2026-08-26", isDuplicate: false }, { id: 5, name: "Дюсенова Айнур Маратовна", phone: "+7 777 567 89 01", address: "г. Алматы, ул. Сатпаева 22, кв. 18", tariff: "Домашний интернет 50 Мбит/с", manager: "Бауыржан Т.", status: "в работе", comment: "", date: "2026-08-27", isDuplicate: false }, { id: 6, name: "Ержанов Максат", phone: "+7 701 678 90 12", address: "г. Алматы, ул. Рыскулова 100, кв. 55", tariff: "Бизнес тариф", manager: "", status: "спорная", comment: "Нужно уточнить юридический адрес", date: "2026-08-25", isDuplicate: false }, { id: 7, name: "Жуманова Лейла Ахметовна", phone: "+7 778 789 01 23", address: "г. Алматы, пр. Абая 150, кв. 88", tariff: "Домашний интернет 100 Мбит/с", manager: "Айгуль К.", status: "завершена", comment: "", date: "2026-08-24", isDuplicate: false }, { id: 8, name: "Зинченко Александр", phone: "+7 702 890 12 34", address: "г. Алматы, ул. Макатаева 35, офис 5", tariff: "ТВ + Интернет", manager: "Серик Ж.", status: "в работе", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 9, name: "Ибраева Сауле", phone: "+7 777 901 23 45", address: "г. Алматы, мкр. Коктем-3, д. 20, кв. 10", tariff: "Домашний интернет 500 Мбит/с", manager: "", status: "новая", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 10, name: "Ким Дмитрий Юрьевич", phone: "+7 701 012 34 56", address: "г. Алматы, ул. Бейбитшилик 12, кв. 30", tariff: "Домашний интернет 100 Мбит/с", manager: "Бауыржан Т.", status: "на проверке", comment: "Клиент на больничном", date: "2026-08-26", isDuplicate: false }, { id: 11, name: "Лебедева Наталья", phone: "+7 778 123 45 67", address: "г. Алматы, пр. Райымбека 200, кв. 75", tariff: "Тройной пакет", manager: "", status: "новая", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 12, name: "Мамедов Руслан", phone: "+7 702 234 56 78", address: "г. Алматы, ул. Желтоксан 55, офис 8", tariff: "Бизнес тариф", manager: "Серик Ж.", status: "в работе", comment: "", date: "2026-08-27", isDuplicate: false }, { id: 13, name: "Нургалиева Айжан", phone: "+7 777 345 67 89", address: "г. Алматы, мкр. Таугуль-3, д. 8А, кв. 22", tariff: "Домашний интернет 50 Мбит/с", manager: "Айгуль К.", status: "завершена", comment: "", date: "2026-08-23", isDuplicate: false }, { id: 14, name: "Омаров Ерлан", phone: "+7 701 456 78 90", address: "г. Алматы, ул. Панфилова 90, кв. 15", tariff: "ТВ + Интернет", manager: "", status: "спорная", comment: "Неправильный адрес, уточняется", date: "2026-08-25", isDuplicate: false }, { id: 15, name: "Петрова Елена Сергеевна", phone: "+7 778 567 89 01", address: "г. Алматы, пр. Абая 50, кв. 100", tariff: "Домашний интернет 500 Мбит/с", manager: "Бауыржан Т.", status: "в работе", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 16, name: "Рахимов Берик", phone: "+7 702 678 90 12", address: "г. Алматы, ул. Кабанбай батыра 70, кв. 45", tariff: "Домашний интернет 100 Мбит/с", manager: "", status: "новая", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 17, name: "Смирнова Ольга", phone: "+7 777 789 01 23", address: "г. Алматы, мкр. Орбита-2, д. 55, кв. 8", tariff: "Тройной пакет", manager: "Серик Ж.", status: "на проверке", comment: "", date: "2026-08-26", isDuplicate: false }, { id: 18, name: "Турсунов Марат", phone: "+7 701 890 12 34", address: "г. Алматы, ул. Пушкина 25, офис 12", tariff: "Бизнес тариф", manager: "Айгуль К.", status: "завершена", comment: "", date: "2026-08-22", isDuplicate: false }, { id: 19, name: "Уалиева Жанар", phone: "+7 778 901 23 45", address: "г. Алматы, пр. Достык 100, кв. 60", tariff: "Домашний интернет 50 Мбит/с", manager: "", status: "новая", comment: "Заявка с сайта", date: "2026-08-28", isDuplicate: false }, { id: 20, name: "Федоров Виктор", phone: "+7 702 012 34 56", address: "г. Алматы, ул. Богенбай батыра 40, кв. 20", tariff: "ТВ + Интернет", manager: "Бауыржан Т.", status: "в работе", comment: "", date: "2026-08-27", isDuplicate: false }, { id: 21, name: "Хасанова Мадина", phone: "+7 777 123 45 67", address: "г. Алматы, мкр. Нуркент, д. 12, кв. 35", tariff: "Домашний интернет 100 Мбит/с", manager: "", status: "спорная", comment: "Дубль? Тот же телефон что у Алиева А.", date: "2026-08-27", isDuplicate: true }, { id: 22, name: "Иванов Петр", phone: "+7 701 333 22 11", address: "г. Алматы, ул. Тимирязева 80, кв. 50", tariff: "Домашний интернет 500 Мбит/с", manager: "Серик Ж.", status: "в работе", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 23, name: "Козлов Андрей", phone: "+7 778 444 33 22", address: "г. Алматы, пр. Абая 200, кв. 120", tariff: "Бизнес тариф", manager: "", status: "новая", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 24, name: "Алиев Арман Берикович", phone: "+7 777 123 45 67", address: "г. Алматы, ул. Абая 10, кв. 5", tariff: "Домашний интернет 100 Мбит/с", manager: "", status: "спорная", comment: "ВОЗМОЖНЫЙ ДУБЛЬ - заявка #1", date: "2026-08-28", isDuplicate: true }, { id: 25, name: "Шарипова Гульмира", phone: "+7 702 555 44 33", address: "г. Алматы, ул. Сейфуллина 150, кв. 70", tariff: "Тройной пакет", manager: "Айгуль К.", status: "на проверке", comment: "", date: "2026-08-26", isDuplicate: false }, { id: 26, name: "Яковлев Михаил", phone: "+7 777 666 55 44", address: "г. Алматы, мкр. Самал-2, д. 30, кв. 15", tariff: "Домашний интернет 50 Мбит/с", manager: "", status: "новая", comment: "Клиент из рекламы", date: "2026-08-28", isDuplicate: false }, { id: 27, name: "Есенбаева Айгуль", phone: "+7 701 777 66 55", address: "г. Алматы, ул. Масанчи 85, кв. 40", tariff: "ТВ + Интернет", manager: "Бауыржан Т.", status: "завершена", comment: "", date: "2026-08-21", isDuplicate: false }, { id: 28, name: "Морозов Игорь", phone: "+7 778 888 77 66", address: "г. Алматы, пр. Райымбека 300, офис 20", tariff: "Бизнес тариф", manager: "Серик Ж.", status: "в работе", comment: "", date: "2026-08-27", isDuplicate: false }, { id: 29, name: "Кенжебаева Сауле", phone: "+7 702 999 88 77", address: "г. Алматы, мкр. Айнабулак, д. 5, кв. 25", tariff: "Домашний интернет 100 Мбит/с", manager: "", status: "новая", comment: "", date: "2026-08-28", isDuplicate: false }, { id: 30, name: "Ткаченко Владимир", phone: "+7 777 000 99 88", address: "г. Алматы, ул. Курмангазы 60, кв. 90", tariff: "Домашний интернет 500 Мбит/с", manager: "Айгуль К.", status: "просрочка", comment: "Клиент не отвечает", date: "2026-08-20", isDuplicate: false } ]; let requests = []; let currentFilter = 'all'; let duplicateCheckId = null; // Инициализация function init() { const stored = localStorage.getItem('uchet_requests'); if (stored) { requests = JSON.parse(stored); } else { requests = [...DEMO_DATA]; saveToStorage(); } renderDashboard(); renderTable(); } function saveToStorage() { localStorage.setItem('uchet_requests', JSON.stringify(requests)); } // Дашборд function renderDashboard() { const today = new Date().toISOString().slice(0, 10); const counts = { 'новая': 0, 'в работе': 0, 'на проверке': 0, 'спорная': 0, 'завершена': 0, 'просрочка': 0, 'total': requests.length }; requests.forEach(r => { if (counts[r.status] !== undefined) counts[r.status]++; // Просрочка: старше 3 дней и не завершена if (r.status !== 'завершена' && r.status !== 'просрочка') { const daysOld = daysBetween(r.date, today); if (daysOld > 3) { counts['просрочка']++; } } }); document.getElementById('count-new').textContent = counts['новая']; document.getElementById('count-work').textContent = counts['в работе']; document.getElementById('count-duplicate').textContent = requests.filter(r => r.isDuplicate).length; document.getElementById('count-overdue').textContent = counts['просрочка']; document.getElementById('count-total').textContent = counts.total; } function daysBetween(date1, date2) { const d1 = new Date(date1); const d2 = new Date(date2); return Math.round((d2 - d1) / (1000 * 60 * 60 * 24)); } // Фильтры function setFilter(status) { currentFilter = status; document.querySelectorAll('.filter-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.status === status); }); renderTable(); } // Таблица function renderTable() { const tbody = document.getElementById('table-body'); const search = document.getElementById('search').value.toLowerCase(); let filtered = requests.filter(r => { const matchesSearch = r.name.toLowerCase().includes(search) || r.phone.toLowerCase().includes(search) || r.address.toLowerCase().includes(search) || (r.manager && r.manager.toLowerCase().includes(search)); const matchesStatus = currentFilter === 'all' || r.status === currentFilter; return matchesSearch && matchesStatus; }); // Сортировка: новые сверху filtered.sort((a, b) => new Date(b.date) - new Date(a.date)); if (filtered.length === 0) { tbody.innerHTML = `
Нет заявок по выбранным фильтрам
Найдено совпадение с заявкой #${dup.id}:
Пометить текущую заявку как дубль?
`; document.getElementById('duplicate-modal').classList.add('active'); } else { alert('Дубли не найдены'); } } function normalizeName(name) { return name.toLowerCase().replace(/[^а-яёa-z]/g, '').trim(); } function confirmDuplicate() { if (duplicateCheckId) { const r = requests.find(x => x.id === duplicateCheckId); if (r) { r.isDuplicate = true; r.status = 'спорная'; r.comment = (r.comment || '') + ' Помечена как дубль вручную.'; saveToStorage(); renderDashboard(); renderTable(); } closeDuplicateModal(); } } function keepBoth() { closeDuplicateModal(); } function closeDuplicateModal() { document.getElementById('duplicate-modal').classList.remove('active'); duplicateCheckId = null; } // Telegram сводка function showTelegramSummary() { const today = new Date().toISOString().slice(0, 10); const counts = { 'новая': requests.filter(r => r.status === 'новая').length, 'в работе': requests.filter(r => r.status === 'в работе').length, 'на проверке': requests.filter(r => r.status === 'на проверке').length, 'спорная': requests.filter(r => r.status === 'спорная').length, 'завершена': requests.filter(r => r.status === 'завершена').length, 'дубли': requests.filter(r => r.isDuplicate).length }; const managers = {}; requests.filter(r => r.manager && r.status !== 'завершена').forEach(r => { managers[r.manager] = (managers[r.manager] || 0) + 1; }); const text = `📊 *Сводка по заявкам на ${formatDate(today)}* 🔹 Новые: ${counts['новая']} 🔹 В работе: ${counts['в работе']} 🔹 На проверке: ${counts['на проверке']} 🔹 Спорные: ${counts['спорная']} 🔹 Дубли: ${counts['дубли']} 🔹 Завершено: ${counts['завершена']} 📈 *Всего в работе:* ${counts['новая'] + counts['в работе'] + counts['на проверке'] + counts['спорная']} 👥 *По менеджерам:* ${Object.entries(managers).map(([m, c]) => `${m}: ${c} заяв.`).join('\n') || '—'} ⚠️ *Просрочка:* ${requests.filter(r => r.status === 'просрочка').length} заявок старше 3 дней`; document.getElementById('telegram-content').textContent = text; document.getElementById('telegram-modal').classList.add('active'); } function closeTelegramModal() { document.getElementById('telegram-modal').classList.remove('active'); } function copyTelegram() { const text = document.getElementById('telegram-content').textContent; navigator.clipboard.writeText(text).then(() => { alert('Сводка скопирована в буфер обмена!'); }); } // Экспорт в Excel (CSV с BOM) function exportToExcel() { const headers = ['ID', 'Дата', 'ФИО', 'Телефон', 'Адрес', 'Тариф', 'Менеджер', 'Статус', 'Комментарий', 'Дубль']; const rows = requests.map(r => [ r.id, r.date, `"${r.name.replace(/"/g, '""')}"`, `"${r.phone.replace(/"/g, '""')}"`, `"${r.address.replace(/"/g, '""')}"`, `"${r.tariff.replace(/"/g, '""')}"`, `"${(r.manager || '').replace(/"/g, '""')}"`, r.status, `"${(r.comment || '').replace(/"/g, '""')}"`, r.isDuplicate ? 'Да' : 'Нет' ]); const csv = '\uFEFF' + [headers.join(';'), ...rows.map(r => r.join(';'))].join('\n'); const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `zayavki_${new Date().toISOString().slice(0, 10)}.csv`; a.click(); URL.revokeObjectURL(url); } // Старт init();