diff options
| author | zwlucas <lucas.fariamo08@gmail.com> | 2026-07-25 21:23:29 +0000 |
|---|---|---|
| committer | zwlucas <lucas.fariamo08@gmail.com> | 2026-07-25 21:23:29 +0000 |
| commit | ef55d9bb24430610b60c1b77821307cd72c08c55 (patch) | |
| tree | e6935a810b34370b0a4c821e55f6397aac50c832 /web/src/hooks/useTicketEvents.ts | |
| download | helpdesk-master.tar.gz helpdesk-master.zip | |
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'web/src/hooks/useTicketEvents.ts')
| -rw-r--r-- | web/src/hooks/useTicketEvents.ts | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/web/src/hooks/useTicketEvents.ts b/web/src/hooks/useTicketEvents.ts new file mode 100644 index 0000000..7994ace --- /dev/null +++ b/web/src/hooks/useTicketEvents.ts @@ -0,0 +1,91 @@ +/** + * useTicketEvents.ts + * + * Hook para escutar os Server-Sent Events (SSE) disparados pelo backend. + * Conecta via EventSource e despacha atualizações e invalidações de cache. + */ + +import { useEffect } from 'react'; +import { useQueryClient } from '@tanstack/react-query'; +import { useAuth } from '@/context/AuthContext'; +import { useToast } from '@/context/ToastContext'; + +export function useTicketEvents() { + const { token, isAuthenticated, user } = useAuth(); + const queryClient = useQueryClient(); + const { addToast } = useToast(); + + useEffect(() => { + // Pede permissão para notificações nativas, se suportado + if ('Notification' in window && Notification.permission === 'default') { + Notification.requestPermission(); + } + }, []); + + useEffect(() => { + if (!isAuthenticated || !token) return; + + // Conecta ao endpoint SSE passando o token na query string + const url = `${import.meta.env['VITE_API_URL'] ?? '/api/v1'}/events?token=${token}`; + const eventSource = new EventSource(url); + + const notify = (title: string, body?: string) => { + // Toast notification + addToast(title, 'info'); + // Native notification + if ('Notification' in window && Notification.permission === 'granted') { + new Notification(title, { body, icon: '/favicon.ico' }); + } + }; + + // Quando um novo chamado é criado + eventSource.addEventListener('ticket.created', (e) => { + const data = JSON.parse(e.data); + if (user?.role === 'admin' || user?.role === 'agent') { + if (data.creatorId !== user.id) { + notify(`Novo chamado: #${data.id}`, data.title); + } + } + + // Invalida a lista para forçar o refetch e atualizar o grid + queryClient.invalidateQueries({ queryKey: ['tickets'] }); + queryClient.invalidateQueries({ queryKey: ['stats'] }); + }); + + // Quando o status ou responsável do chamado é atualizado + eventSource.addEventListener('ticket.updated', (e) => { + const data = JSON.parse(e.data); + if (user && (user.id === data.creatorId || user.id === data.assigneeId) && user.id !== data.authorId) { + notify(`Chamado #${data.id} atualizado`, `O status ou responsável foi alterado.`); + } + + // Atualiza as listas, os stats e o detalhe do ticket + queryClient.invalidateQueries({ queryKey: ['tickets'] }); + queryClient.invalidateQueries({ queryKey: ['stats'] }); + queryClient.invalidateQueries({ queryKey: ['ticket', data.id] }); + }); + + // Quando um comentário é adicionado + eventSource.addEventListener('comment.added', (e) => { + const data = JSON.parse(e.data); + if (user && (user.id === data.creatorId || user.id === data.assigneeId) && user.id !== data.authorId) { + if (!data.isInternal || (data.isInternal && user.role !== 'requester')) { + notify(`Nova mensagem no chamado #${data.ticketId}`, 'Você tem uma nova interação no chamado.'); + } + } + + // Atualiza os comentários do ticket afetado + queryClient.invalidateQueries({ queryKey: ['tickets', data.ticketId, 'comments'] }); + }); + + // Tratamento de erro na conexão + eventSource.onerror = (err) => { + console.error('Erro na conexão SSE:', err); + // O navegador tentará reconectar automaticamente + }; + + return () => { + eventSource.close(); + }; + }, [token, isAuthenticated, queryClient, addToast]); +} |