diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/ProcessingTicketAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/ProcessingTicketAction.tsx index 4df1014..e478bc9 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/ProcessingTicketAction.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/ProcessingTicketAction.tsx @@ -1,6 +1,6 @@ 'use client'; -import { AlertCircle, Loader2 } from 'lucide-react'; +import { AlertCircle, Loader } from 'lucide-react'; import { Progress } from '@/components/ui/progress'; import type { TicketProcessingProgress } from '../../../hooks/useTicketDetailEvents'; @@ -28,7 +28,11 @@ export function ProcessingTicketAction({ const hasProgress = Boolean(progress); return ( - +
diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/TicketActionCard.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/TicketActionCard.tsx index 370639a..2bca09a 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/TicketActionCard.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/TicketActionCard.tsx @@ -3,15 +3,18 @@ import type { LucideIcon } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { cn } from '@/lib/utils'; interface TicketActionCardProps { icon: LucideIcon; + iconClassName?: string; title: string; children: React.ReactNode; } export function TicketActionCard({ icon: Icon, + iconClassName, title, children, }: TicketActionCardProps) { @@ -19,7 +22,7 @@ export function TicketActionCard({ - + {title} diff --git a/src/app/(modules)/ticket/[ticketId]/page.tsx b/src/app/(modules)/ticket/[ticketId]/page.tsx index cd90ae8..e10ceb0 100644 --- a/src/app/(modules)/ticket/[ticketId]/page.tsx +++ b/src/app/(modules)/ticket/[ticketId]/page.tsx @@ -26,6 +26,7 @@ export default function TicketDetailPage() { const liveState = useTicketDetailEvents( ticketId, + defectClass, Boolean(ticketId && ticket && ticket.status !== 'closed'), ); diff --git a/src/app/(modules)/ticket/hooks/useTicketDetailEvents.ts b/src/app/(modules)/ticket/hooks/useTicketDetailEvents.ts index baf0510..5a7e105 100644 --- a/src/app/(modules)/ticket/hooks/useTicketDetailEvents.ts +++ b/src/app/(modules)/ticket/hooks/useTicketDetailEvents.ts @@ -1,7 +1,8 @@ 'use client'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useQueryClient } from '@tanstack/react-query'; +import { useRouter } from 'next/navigation'; import { API_ROUTES } from '@/constants/apiRoutes'; import { useSseWithToken } from '@/hooks/sse/useSseWithToken'; @@ -32,21 +33,45 @@ function isHeartbeatEvent(event: { type?: string } | null | undefined) { export function useTicketDetailEvents( ticketId: string | undefined, + defectClass: string | undefined, enabled = true, ): TicketDetailLiveState { + const router = useRouter(); const queryClient = useQueryClient(); + const defectClassRef = useRef(defectClass); const [progress, setProgress] = useState( null, ); const [errorMessage, setErrorMessage] = useState(null); + useEffect(() => { + defectClassRef.current = defectClass; + }, [defectClass]); + const refetchTicket = useCallback(() => { if (!ticketId) return; - queryClient.refetchQueries({ queryKey: ticketKeys.detail(ticketId) }); + queryClient.refetchQueries({ + queryKey: ticketKeys.detail(ticketId, defectClassRef.current), + }); + queryClient.invalidateQueries({ + queryKey: ticketKeys.defectClasses(ticketId), + }); queryClient.invalidateQueries({ queryKey: ticketKeys.lists() }); }, [queryClient, ticketId]); + const selectDefectClass = useCallback( + (nextDefectClass: string | null | undefined) => { + if (!ticketId || !nextDefectClass || defectClassRef.current) return; + + router.replace( + `/ticket/${ticketId}?defect_class=${encodeURIComponent(nextDefectClass)}`, + { scroll: false }, + ); + }, + [router, ticketId], + ); + const getToken = useCallback( () => ticketService.createTicketEventsToken(ticketId as string), [ticketId], @@ -77,6 +102,7 @@ export function useTicketDetailEvents( progress: 100, message: event.message, }); + selectDefectClass(event.defect_class); refetchTicket(); }, error: (event: TicketStreamErrorEvent) => { @@ -86,7 +112,7 @@ export function useTicketDetailEvents( }, heartbeat: () => undefined, }), - [refetchTicket], + [refetchTicket, selectDefectClass], ); const shouldStop = useCallback( @@ -103,7 +129,7 @@ export function useTicketDetailEvents( useEffect(() => { setProgress(null); setErrorMessage(null); - }, [ticketId]); + }, [defectClass, ticketId]); useSseWithToken({ enabled: Boolean(enabled && ticketId), diff --git a/src/types/ticket/events.ts b/src/types/ticket/events.ts index 187db31..766f33d 100644 --- a/src/types/ticket/events.ts +++ b/src/types/ticket/events.ts @@ -36,6 +36,7 @@ export interface TicketProgressEvent { status: string; progress: number; message: string; + defect_class?: string | null; } export interface TicketStreamErrorEvent {