import { useState, useEffect, useRef, useCallback } from "react"; // ─── Data ─────────────────────────────────────────────────────────────────── const MANAGERS = ["Олег", "Дима", "Патрик"]; const SOURCES = ["Telegram", "WhatsApp", "Сайт", "Instagram", "Авито"]; const QUAL_STATUSES = ["Не обработан", "Квалифицирован", "Не квалифицирован"]; const FINAL_STATUSES = ["Думает", "Отмена", "Недозвон", "Назначена встреча", "Продажа", "Перенос"]; const BUY_TERMS = ["до 1 мес", "1–3 мес", "3–6 мес", "6+ мес", "не определен"]; const INITIAL_LEADS = [ { id: 1, source: "Telegram", channel: "@mebel_kazan", created: "2026-05-10", phone: "+7 916 111-22-33", rating: 5, name: "Алина Соколова", qual: "Квалифицирован", buyTerm: "до 1 мес", manager: "Олег", finalStatus: "Назначена встреча", comment: "Интересует диван угловой, бюджет 80k", saleAmount: 0, history: [{ date: "2026-05-10 09:00", text: "Лид создан из Telegram", user: "Система" }, { date: "2026-05-10 10:30", text: "Назначен менеджер: Олег", user: "Admin" }] }, { id: 2, source: "WhatsApp", channel: "Прямой", created: "2026-05-09", phone: "+7 903 222-33-44", rating: 4, name: "Виктор Морозов", qual: "Квалифицирован", buyTerm: "1–3 мес", manager: "Дима", finalStatus: "Думает", comment: "Кухня на заказ, размеры есть", saleAmount: 0, history: [{ date: "2026-05-09 14:00", text: "Лид создан из WhatsApp", user: "Система" }] }, { id: 3, source: "Сайт", channel: "mebel-pro.ru", created: "2026-05-11", phone: "+7 926 333-44-55", rating: 3, name: "Марина Белова", qual: "Не обработан", buyTerm: "3–6 мес", manager: "Патрик", finalStatus: "Недозвон", comment: "", saleAmount: 0, history: [{ date: "2026-05-11 11:00", text: "Лид создан с сайта", user: "Система" }] }, { id: 4, source: "Telegram", channel: "@furni_msk", created: "2026-05-08", phone: "+7 917 444-55-66", rating: 5, name: "Иван Козлов", qual: "Квалифицирован", buyTerm: "до 1 мес", manager: "Олег", finalStatus: "Продажа", comment: "Продажа закрыта! Спальный гарнитур", saleAmount: 145000, history: [{ date: "2026-05-08 09:00", text: "Лид создан", user: "Система" }, { date: "2026-05-08 16:00", text: "Статус: Продажа", user: "Олег" }] }, { id: 5, source: "Instagram", channel: "@mebel_ig", created: "2026-05-07", phone: "+7 905 555-66-77", rating: 4, name: "Светлана Попова", qual: "Квалифицирован", buyTerm: "1–3 мес", manager: "Дима", finalStatus: "Продажа", comment: "Закрыта. Гостиная мебель", saleAmount: 87000, history: [{ date: "2026-05-07 10:00", text: "Лид создан", user: "Система" }] }, { id: 6, source: "Авито", channel: "avito.ru", created: "2026-05-06", phone: "+7 909 666-77-88", rating: 2, name: "Олег Сидоров", qual: "Не квалифицирован", buyTerm: "6+ мес", manager: null, finalStatus: "Отмена", comment: "Просто смотрит цены", saleAmount: 0, history: [{ date: "2026-05-06 12:00", text: "Лид создан", user: "Система" }] }, { id: 7, source: "Telegram", channel: "@mebel_kazan", created: "2026-05-12", phone: "+7 912 777-88-99", rating: 5, name: "Анна Новикова", qual: "Квалифицирован", buyTerm: "до 1 мес", manager: "Патрик", finalStatus: "Назначена встреча", comment: "VIP клиент. Полный интерьер", saleAmount: 0, history: [{ date: "2026-05-12 08:00", text: "Лид создан", user: "Система" }] }, { id: 8, source: "Сайт", channel: "mebel-pro.ru", created: "2026-05-05", phone: "+7 911 888-99-00", rating: 3, name: "Дмитрий Орлов", qual: "Не обработан", buyTerm: "не определен", manager: null, finalStatus: "Думает", comment: "", saleAmount: 0, history: [{ date: "2026-05-05 15:00", text: "Лид создан", user: "Система" }] }, ]; const INITIAL_EVENTS = [ { id: 1, leadId: 1, type: "Встреча в салоне", title: "Встреча с Алиной Соколовой", manager: "Олег", date: "2026-05-15", time: "14:00", note: "Показать угловые диваны", done: false }, { id: 2, leadId: 7, type: "Замер", title: "Замер у Анны Новиковой", manager: "Патрик", date: "2026-05-14", time: "11:00", note: "Адрес: ул. Ленина 45", done: false }, ]; // ─── Helpers ───────────────────────────────────────────────────────────────── const genId = () => Date.now() + Math.floor(Math.random() * 1000); const today = () => new Date().toISOString().split("T")[0]; const fmtMoney = (n) => n ? Number(n).toLocaleString("ru-RU") + " ₽" : "—"; const initials = (n) => n ? n.split(" ").slice(0, 2).map(w => w[0]).join("").toUpperCase() : "?"; const MANAGER_COLORS = { "Олег": "#4f87c5", "Дима": "#6db877", "Патрик": "#c47dd4" }; const STATUS_COLORS = { "Продажа": "#22c55e", "Назначена встреча": "#3b82f6", "Думает": "#f59e0b", "Недозвон": "#94a3b8", "Отмена": "#ef4444", "Перенос": "#a78bfa" }; const RATING_COLOR = (r) => r >= 5 ? "#22c55e" : r >= 4 ? "#84cc16" : r >= 3 ? "#f59e0b" : r >= 2 ? "#f97316" : "#ef4444"; // ─── AI Agent ───────────────────────────────────────────────────────────────── async function askAI(messages) { const res = await fetch("https://api.anthropic.com/v1/messages", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "claude-sonnet-4-20250514", max_tokens: 1000, system: `Ты AI-помощник в CRM системе мебельной компании. Тебе доступны данные о лидах, менеджерах и статистике. Менеджеры: Олег, Дима, Патрик. Отвечай кратко и по делу. Давай конкретные советы по работе с лидами, анализируй данные, помогай с принятием решений. Когда тебя спрашивают о данных — анализируй их из контекста. Отвечай на русском языке.`, messages, }), }); const d = await res.json(); return d.content?.map(b => b.text || "").join("") || "Ошибка ответа"; } // ─── Components ─────────────────────────────────────────────────────────────── function Stars({ value, onChange }) { return (
{[1, 2, 3, 4, 5].map(s => ( onChange && onChange(s)} style={{ cursor: onChange ? "pointer" : "default", fontSize: 16, color: s <= value ? "#f59e0b" : "#334155", lineHeight: 1 }}>★ ))}
); } function Badge({ text, color, bg }) { return {text}; } function Avatar({ name, color, size = 32 }) { return (
{initials(name)}
); } // ─── Stat Card ──────────────────────────────────────────────────────────────── function StatCard({ label, value, sub, accent }) { return (
{label}
{value}
{sub &&
{sub}
}
); } // ─── Lead Row ───────────────────────────────────────────────────────────────── function LeadRow({ lead, onOpen, onAssign }) { const sc = STATUS_COLORS[lead.finalStatus] || "#475569"; return ( onOpen(lead)} style={{ cursor: "pointer", borderBottom: "1px solid #1e293b", transition: "background 0.1s" }} onMouseEnter={e => e.currentTarget.style.background = "#0f172a"} onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
{lead.name}
{lead.phone}
{lead.manager ?
{lead.manager}
: } {lead.created} ); } // ─── Lead Modal ─────────────────────────────────────────────────────────────── function LeadModal({ lead, onClose, onSave, currentUser }) { const [form, setForm] = useState({ ...lead }); const upd = (k, v) => setForm(p => ({ ...p, [k]: v })); const save = () => { const hist = [...(form.history || [])]; if (form.finalStatus !== lead.finalStatus) hist.push({ date: new Date().toLocaleString("ru-RU"), text: `Статус изменён: ${lead.finalStatus} → ${form.finalStatus}`, user: currentUser }); if (form.manager !== lead.manager && form.manager) hist.push({ date: new Date().toLocaleString("ru-RU"), text: `Назначен менеджер: ${form.manager}`, user: currentUser }); if (form.comment !== lead.comment) hist.push({ date: new Date().toLocaleString("ru-RU"), text: `Обновлён комментарий`, user: currentUser }); onSave({ ...form, history: hist }); }; return (
e.stopPropagation()} style={{ background: "#0a1628", border: "1px solid #1e293b", borderRadius: 14, width: "100%", maxWidth: 700, maxHeight: "90vh", overflow: "hidden", display: "flex", flexDirection: "column" }}> {/* Header */}
{form.name}
{form.phone} · {form.source}
upd("rating", v)} />
{/* Body */}
{/* Left */}
upd("name", v)} /> upd("phone", v)} /> upd("source", v)} options={SOURCES} /> upd("qual", v)} options={QUAL_STATUSES} /> upd("buyTerm", v)} options={BUY_TERMS} /> upd("manager", v)} options={["", ...MANAGERS]} /> upd("finalStatus", v)} options={FINAL_STATUSES} /> {form.finalStatus === "Продажа" && upd("saleAmount", Number(v))} type="number" />}
{/* Right */}
КОММЕНТАРИЙ