// Dialogs screen — 3-column layout const { useState: useStateD, useEffect: useEffectD, useRef: useRefD, useMemo: useMemoD } = React; function ConvCard({ conv, active, onClick }) { const statusDot = { new: "bg-[#4F8EF7]", in_progress: "bg-[#eab308]", closed: "bg-zinc-500", }[conv.status]; return ( ); } function MessageBubble({ msg, onImageClick }) { if (msg.kind === "system") { return (
{msg.text} · {msg.time}
); } if (msg.kind === "user") { return (
{msg.fileType === "photo" || msg.kindOfContent === "image" ? ( ) : (
{msg.text}
)}
{msg.time}
); } if (msg.kind === "ai") { return (
ИИ
{msg.text}
{msg.time}
); } if (msg.kind === "operator") { return (
{msg.text}
{msg.operator} · {msg.time}
); } return null; } function DialogsScreen({ conversations, setConversations, activeId, setActiveId, showToast, onReply, onToggleAI, onClose, onHandoff, onBillingAction, servers, }) { const [searchQ, setSearchQ] = useStateD(""); const [filter, setFilter] = useStateD("all"); const [draft, setDraft] = useStateD(""); const [aiEnabled, setAiEnabled] = useStateD(true); const [lightboxOpen, setLightboxOpen] = useStateD(false); const [confirmClose, setConfirmClose] = useStateD(false); const scrollRef = useRefD(null); const active = conversations.find((c) => c.id === activeId) || conversations[0]; // Sync AI toggle state when active dialog changes useEffectD(() => { if (active) setAiEnabled(active.aiEnabled ?? true); }, [active?.id, active?.aiEnabled]); useEffectD(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [active?.id, active?.messages?.length]); const filtered = useMemoD(() => { let list = conversations; if (filter === "open") list = list.filter((c) => c.status === "new"); if (filter === "wip") list = list.filter((c) => c.status === "in_progress"); if (filter === "closed") list = list.filter((c) => c.status === "closed"); if (searchQ.trim()) { const q = searchQ.toLowerCase(); list = list.filter( (c) => c.name.toLowerCase().includes(q) || c.username.toLowerCase().includes(q) || c.preview.toLowerCase().includes(q) ); } return list; }, [conversations, filter, searchQ]); const counts = useMemoD(() => ({ all: conversations.length, open: conversations.filter((c) => c.status === "new").length, wip: conversations.filter((c) => c.status === "in_progress").length, closed: conversations.filter((c) => c.status === "closed").length, }), [conversations]); function sendMessage() { const text = draft.trim(); if (!text || !active) return; const time = new Date().toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" }); const newMsg = { id: Date.now(), kind: "operator", text, time, operator: "Мария" }; // Optimistic update setConversations((convs) => convs.map((c) => c.id === active.id ? { ...c, messages: [...(c.messages || []), newMsg], preview: text, time, status: c.status === "new" ? "in_progress" : c.status, unread: 0 } : c ) ); setDraft(""); // API call if (onReply) onReply(active.id, text, "Мария"); } function handoffToOperator() { if (!active) return; const time = new Date().toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" }); setConversations((convs) => convs.map((c) => c.id === active.id ? { ...c, messages: [...(c.messages || []), { id: Date.now(), kind: "system", text: "Диалог передан оператору Мария", time }], status: "in_progress", operatorCalled: true } : c ) ); showToast("Диалог взят в работу"); if (onHandoff) onHandoff(active.id, "Мария"); } function closeDialog() { if (!active) return; const time = new Date().toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" }); setConversations((convs) => convs.map((c) => c.id === active.id ? { ...c, messages: [...(c.messages || []), { id: Date.now(), kind: "system", text: "Диалог закрыт оператором", time }], status: "closed", operatorCalled: false } : c ) ); setConfirmClose(false); showToast("Диалог закрыт"); if (onClose) onClose(active.id); } async function toggleAI() { if (!active) return; if (onToggleAI) { const result = await onToggleAI(active.id); if (result && result.ai_enabled !== undefined) { setAiEnabled(result.ai_enabled); } } else { setAiEnabled((v) => !v); } } const filterTabs = [ { id: "all", label: "Все", count: counts.all }, { id: "open", label: "Открытые", count: counts.open }, { id: "wip", label: "В работе", count: counts.wip }, { id: "closed", label: "Закрытые", count: counts.closed }, ]; return ( <>
{/* Left: conversation list */} {/* Center: chat */}
{active && ( <> {/* Top bar */}
{active.name}
{active.username} · ID {active.tgId}
{/* Messages */}
{(active.messages || []).length === 0 && (
Загрузка сообщений...
)} {(active.messages || []).map((m) => ( setLightboxOpen(true)} /> ))}
{/* Composer */}