aboutsummaryrefslogtreecommitdiff
path: root/web/src/hooks/useTicketEvents.ts
diff options
context:
space:
mode:
authorzwlucas <lucas.fariamo08@gmail.com>2026-07-25 21:23:29 +0000
committerzwlucas <lucas.fariamo08@gmail.com>2026-07-25 21:23:29 +0000
commitef55d9bb24430610b60c1b77821307cd72c08c55 (patch)
treee6935a810b34370b0a4c821e55f6397aac50c832 /web/src/hooks/useTicketEvents.ts
downloadhelpdesk-master.tar.gz
helpdesk-master.zip
feat: upload of the projectHEADmaster
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'web/src/hooks/useTicketEvents.ts')
-rw-r--r--web/src/hooks/useTicketEvents.ts91
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]);
+}