import { useCallback, useEffect, useRef, useState } from "react" import { invoke } from "@tauri-apps/api/core" import { listen } from "@tauri-apps/api/event" import { Loader2, MessageCircle, ChevronUp } from "lucide-react" import { ChatSessionList } from "./ChatSessionList" import type { ChatSession, NewMessageEvent, SessionStartedEvent, SessionEndedEvent, UnreadUpdateEvent } from "./types" import { getMachineStoreConfig } from "./machineStore" /** * Hub Widget - Lista todas as sessoes de chat ativas * Ao clicar em uma sessao, abre/foca a janela de chat daquele ticket */ export function ChatHubWidget() { const [sessions, setSessions] = useState([]) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) const [isMinimized, setIsMinimized] = useState(true) // Inicia minimizado const configRef = useRef<{ apiBaseUrl: string; token: string } | null>(null) const ensureConfig = useCallback(async () => { const cfg = configRef.current ?? (await getMachineStoreConfig()) configRef.current = cfg return cfg }, []) // Buscar sessoes do backend const loadSessions = useCallback(async () => { try { const cfg = await ensureConfig() const response = await fetch(`${cfg.apiBaseUrl}/api/machines/chat/sessions`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ machineToken: cfg.token }), }) if (!response.ok) { throw new Error(`Falha ao buscar sessoes: ${response.status}`) } const data = await response.json() as { sessions: ChatSession[] } setSessions(data.sessions || []) setError(null) } catch (err) { console.error("Erro ao carregar sessoes:", err) setError(err instanceof Error ? err.message : "Erro desconhecido") } finally { setIsLoading(false) } }, [ensureConfig]) // Carregar sessoes na montagem useEffect(() => { loadSessions() }, [loadSessions]) // Escutar eventos de atualizacao useEffect(() => { const unlisteners: (() => void)[] = [] // Quando nova sessao inicia listen("raven://chat/session-started", () => { loadSessions() }).then((unlisten) => unlisteners.push(unlisten)).catch((err) => { console.error("Erro ao registrar listener session-started:", err) }) // Quando sessao encerra listen("raven://chat/session-ended", () => { loadSessions() }).then((unlisten) => unlisteners.push(unlisten)).catch((err) => { console.error("Erro ao registrar listener session-ended:", err) }) // Quando contador de nao lidos muda listen("raven://chat/unread-update", (event) => { setSessions(event.payload.sessions || []) }).then((unlisten) => unlisteners.push(unlisten)).catch((err) => { console.error("Erro ao registrar listener unread-update:", err) }) // Quando nova mensagem chega listen("raven://chat/new-message", (event) => { setSessions(event.payload.sessions || []) }).then((unlisten) => unlisteners.push(unlisten)).catch((err) => { console.error("Erro ao registrar listener new-message:", err) }) return () => { unlisteners.forEach((unlisten) => unlisten()) } }, [loadSessions]) // Sincronizar estado minimizado com tamanho da janela useEffect(() => { const mountTime = Date.now() const STABILIZATION_DELAY = 500 const handler = () => { if (Date.now() - mountTime < STABILIZATION_DELAY) { return } const h = window.innerHeight setIsMinimized(h < 100) } window.addEventListener("resize", handler) return () => window.removeEventListener("resize", handler) }, []) const handleSelectSession = async (ticketId: string, ticketRef: number) => { try { // Tauri 2 espera snake_case nos parametros await invoke("open_chat_window", { ticket_id: ticketId, ticket_ref: ticketRef }) } catch (err) { console.error("Erro ao abrir janela de chat:", err) } } const handleMinimize = async () => { setIsMinimized(true) try { await invoke("set_hub_minimized", { minimized: true }) } catch (err) { console.error("Erro ao minimizar hub:", err) } } const handleExpand = async () => { try { await invoke("set_hub_minimized", { minimized: false }) // Aguarda a janela redimensionar antes de atualizar o estado setTimeout(() => setIsMinimized(false), 100) } catch (err) { console.error("Erro ao expandir hub:", err) setIsMinimized(false) // Fallback } } const handleClose = () => { invoke("close_hub_window").catch((err) => { console.error("Erro ao fechar janela do hub:", err) }) } const totalUnread = sessions.reduce((sum, s) => sum + s.unreadCount, 0) // Loading if (isLoading) { return (
Carregando...
) } // Erro if (error) { return (
) } // Sem sessoes ativas - mostrar chip cinza if (sessions.length === 0) { return (
Sem chats
) } // Minimizado - mostrar chip com contador if (isMinimized) { return (
) } // Expandido - mostrar lista return (
) }