/** * useSessionsLogic — toda a lógica da página /sessions. Portado do motor. * Adaptações no satélite: imports apontam p/ os shims; o bootstrap chama * socketService.connect() sem depender de localStorage['token'] (o shim lê o * token do scoreodonto internamente). */ import { useState, useEffect, useCallback } from 'react' import { useInstanceStore, type Instance } from '../store/instanceStore' import { socketService } from '../services/socketService' interface QrModalState { instanceId: string instanceName: string qrBase64: string | null } interface Toast { msg: string ok: boolean } export function useSessionsLogic() { const storeInstances = useInstanceStore((s) => s.instances) const storeLoading = useInstanceStore((s) => s.loading) const loadInstances = useInstanceStore((s) => s.loadInstances) const createInstance = useInstanceStore((s) => s.createInstance) const connectInst = useInstanceStore((s) => s.connectInstance) const disconnectInst = useInstanceStore((s) => s.disconnectInstance) const deleteInst = useInstanceStore((s) => s.deleteInstance) const patchInstance = useInstanceStore((s) => s.patchInstance) const setQr = useInstanceStore((s) => s.setQr) const clearQr = useInstanceStore((s) => s.clearQr) const qrByInstance = useInstanceStore((s) => s.qrByInstance) const initListeners = useInstanceStore((s) => s.initSocketListeners) const [actionLoading, setActionLoading] = useState(null) const [addOpen, setAddOpen] = useState(false) const [newName, setNewName] = useState('') const [creating, setCreating] = useState(false) const [qrModal, setQrModal] = useState(null) const [deleteTarget, setDeleteTarget] = useState(null) const [deleting, setDeleting] = useState(false) const [toast, setToast] = useState(null) useEffect(() => { socketService.connect() // shim lê o token do scoreodonto internamente initListeners() loadInstances() }, []) useEffect(() => { const onStatus = ({ instanceId, status, qrBase64 }: { instanceId: string; status: Instance['status']; qrBase64?: string }) => { patchInstance(instanceId, { status }) if (qrBase64) { setQr(instanceId, qrBase64) setQrModal((prev) => (prev?.instanceId === instanceId ? { ...prev, qrBase64 } : prev)) } if (status === 'CONNECTED') { clearQr(instanceId) setQrModal((prev) => (prev?.instanceId === instanceId ? null : prev)) showToast('WhatsApp conectado!') } } const onQr = ({ instanceId, qrBase64 }: { instanceId: string; qrBase64: string }) => { setQr(instanceId, qrBase64) patchInstance(instanceId, { status: 'QR_PENDING' }) setQrModal((prev) => (prev?.instanceId === instanceId ? { ...prev, qrBase64 } : prev)) } socketService.on('instance:status', onStatus) socketService.on('qr', onQr) return () => { socketService.off('instance:status', onStatus) socketService.off('qr', onQr) } }, []) const showToast = (msg: string, ok = true) => { setToast({ msg, ok }) setTimeout(() => setToast(null), 3000) } const openAddModal = useCallback(() => setAddOpen(true), []) const closeAddModal = useCallback(() => { setAddOpen(false); setNewName('') }, []) const handleCreate = useCallback(async () => { const name = newName.trim() if (!name) return setCreating(true) try { const created = await createInstance(name) closeAddModal() if (created?.id) { socketService.joinInstance(created.id) await connectInst(created.id) setQrModal({ instanceId: created.id, instanceName: name, qrBase64: null }) } } catch (err: any) { showToast(err.response?.data?.error ?? 'Erro ao criar instância', false) } finally { setCreating(false) } }, [newName, createInstance, connectInst, closeAddModal]) const handleConnect = useCallback(async (inst: Instance) => { setActionLoading(`connect-${inst.id}`) try { socketService.joinInstance(inst.id) await connectInst(inst.id) setQrModal({ instanceId: inst.id, instanceName: inst.name, qrBase64: null }) } catch (err: any) { showToast(err.response?.data?.error ?? 'Erro ao conectar', false) } finally { setActionLoading(null) } }, [connectInst]) const handleDisconnect = useCallback(async (inst: Instance) => { setActionLoading(`disconnect-${inst.id}`) try { await disconnectInst(inst.id) showToast('Instância desconectada') } catch (err: any) { showToast(err.response?.data?.error ?? 'Erro ao desconectar', false) } finally { setActionLoading(null) } }, [disconnectInst]) const handleShowQr = useCallback(async (inst: Instance) => { socketService.joinInstance(inst.id) const cached = qrByInstance[inst.id] if (cached) { setQrModal({ instanceId: inst.id, instanceName: inst.name, qrBase64: cached }) return } setQrModal({ instanceId: inst.id, instanceName: inst.name, qrBase64: null }) }, [qrByInstance]) const handleDelete = useCallback(async () => { if (!deleteTarget) return setDeleting(true) try { await deleteInst(deleteTarget.id) setDeleteTarget(null) showToast('Instância removida') } catch (err: any) { showToast(err.response?.data?.error ?? 'Erro ao deletar', false) } finally { setDeleting(false) } }, [deleteTarget, deleteInst]) const handleNewQr = useCallback(async (instanceId: string) => { socketService.joinInstance(instanceId) await connectInst(instanceId) }, [connectInst]) return { instances: storeInstances, loading: storeLoading, actionLoading, toast, addOpen, newName, creating, setNewName, openAddModal, closeAddModal, handleCreate, qrModal, closeQrModal: () => setQrModal(null), handleNewQr, deleteTarget, deleting, setDeleteTarget, closeDeleteModal: () => setDeleteTarget(null), handleDelete, handleConnect, handleDisconnect, handleShowQr, refreshInstances: loadInstances, } }