'use client'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { API_ROUTES } from '@/constants/apiRoutes'; import { useSseWithToken } from '@/hooks/sse/useSseWithToken'; import { ticketService } from '@/services/api'; import type { TicketDetailStatusEvent, TicketProgressEvent, TicketStreamErrorEvent, } from '@/types'; import { ticketKeys } from '../queries/ticketKeys'; export interface TicketProcessingProgress { progress: number; message: string | null; } export interface TicketDetailLiveState { progress: TicketProcessingProgress | null; errorMessage: string | null; } const TICKET_DETAIL_RECONNECT_EVENTS = ['error'] as const; function isHeartbeatEvent(event: { type?: string } | null | undefined) { return event?.type === 'heartbeat'; } export function useTicketDetailEvents( ticketId: string | undefined, enabled = true, ): TicketDetailLiveState { const queryClient = useQueryClient(); const [progress, setProgress] = useState( null, ); const [errorMessage, setErrorMessage] = useState(null); const refetchTicket = useCallback(() => { if (!ticketId) return; queryClient.refetchQueries({ queryKey: ticketKeys.detail(ticketId) }); queryClient.invalidateQueries({ queryKey: ticketKeys.lists() }); }, [queryClient, ticketId]); const getToken = useCallback( () => ticketService.createTicketEventsToken(ticketId as string), [ticketId], ); const getPath = useCallback( (token: string) => API_ROUTES.TICKETS.DETAIL_EVENTS(ticketId as string, token), [ticketId], ); const events = useMemo( () => ({ ticket_status: (event: TicketDetailStatusEvent) => { if (isHeartbeatEvent(event)) return; refetchTicket(); }, progress: (event: TicketProgressEvent) => { setErrorMessage(null); setProgress({ progress: event.progress, message: event.message, }); }, complete: (event: TicketProgressEvent) => { setErrorMessage(null); setProgress({ progress: 100, message: event.message, }); refetchTicket(); }, error: (event: TicketStreamErrorEvent) => { setErrorMessage( event.message ?? 'Live ticket updates disconnected. Reconnecting...', ); }, heartbeat: () => undefined, }), [refetchTicket], ); const shouldStop = useCallback( (eventName: keyof typeof events, event: unknown) => eventName === 'ticket_status' && (event as TicketDetailStatusEvent).status === 'closed', [], ); const onConnectionError = useCallback(() => { setErrorMessage('Live ticket updates disconnected. Reconnecting...'); }, []); useEffect(() => { setProgress(null); setErrorMessage(null); }, [ticketId]); useSseWithToken({ enabled: Boolean(enabled && ticketId), getToken, getPath, events, reconnectOnEvents: TICKET_DETAIL_RECONNECT_EVENTS, shouldStop, onConnectionError, }); return { progress, errorMessage }; }