From 2caeae7c91531e6deb3618a1c0403894e348aa02 Mon Sep 17 00:00:00 2001 From: "santasri.pachhal" Date: Fri, 31 Jul 2026 12:49:38 +0530 Subject: [PATCH] refactor(ticket): make details lot-based and remove class-detail flow --- .../components/TicketClassDetailSkeleton.tsx | 28 ----- .../components/TicketDefectClassTabs.tsx | 69 ----------- ...Preview.tsx => TicketDetectionPreview.tsx} | 11 +- .../components/TicketDetectionPreviewCard.tsx | 33 ++++++ .../components/TicketStatusActions.tsx | 14 +-- .../actions/AssignedContractorAction.tsx | 61 ---------- .../components/actions/ClosedTicketAction.tsx | 107 ------------------ .../actions/RequestExtensionAction.tsx | 8 +- .../actions/RequestExtensionForm.tsx | 8 +- .../actions/ReviewExtensionRequestAction.tsx | 16 ++- .../actions/TicketExtensionRequestPanel.tsx | 9 +- src/app/(modules)/ticket/[ticketId]/page.tsx | 26 ++--- .../ticket/hooks/useTicketDetailEvents.ts | 13 +-- .../ticket/hooks/useTicketQueries.ts | 45 ++------ src/app/(modules)/ticket/page.tsx | 7 +- .../(modules)/ticket/queries/ticketKeys.ts | 2 - src/constants/apiRoutes.ts | 2 - src/services/api/ticket.service.ts | 16 +-- src/types/ticket/detail.ts | 66 ----------- src/types/ticket/events.ts | 3 +- 20 files changed, 84 insertions(+), 460 deletions(-) delete mode 100644 src/app/(modules)/ticket/[ticketId]/components/TicketClassDetailSkeleton.tsx delete mode 100644 src/app/(modules)/ticket/[ticketId]/components/TicketDefectClassTabs.tsx rename src/app/(modules)/ticket/[ticketId]/components/{TicketClassDetectionPreview.tsx => TicketDetectionPreview.tsx} (97%) create mode 100644 src/app/(modules)/ticket/[ticketId]/components/TicketDetectionPreviewCard.tsx delete mode 100644 src/app/(modules)/ticket/[ticketId]/components/actions/AssignedContractorAction.tsx delete mode 100644 src/app/(modules)/ticket/[ticketId]/components/actions/ClosedTicketAction.tsx diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketClassDetailSkeleton.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketClassDetailSkeleton.tsx deleted file mode 100644 index 447d87b..0000000 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketClassDetailSkeleton.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Card, CardContent, CardHeader } from '@/components/ui/card'; -import { Skeleton } from '@/components/ui/skeleton'; - -export function TicketClassActionsSkeleton() { - return ( - - -
- - -
-
- -
-
- - -
-
- - -
- -
-
-
- ); -} diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketDefectClassTabs.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketDefectClassTabs.tsx deleted file mode 100644 index 14129a9..0000000 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketDefectClassTabs.tsx +++ /dev/null @@ -1,69 +0,0 @@ -'use client'; - -import { useEffect } from 'react'; -import { usePathname, useRouter, useSearchParams } from 'next/navigation'; - -import { Card, CardContent } from '@/components/ui/card'; -import type { TicketOverviewDetail } from '@/types'; - -import { useTicketDefectClassesQuery } from '../../hooks/useTicketQueries'; -import { TicketClassDetectionPreview } from './TicketClassDetectionPreview'; - -interface TicketDefectClassTabsProps { - ticketId: string; - ticket?: TicketOverviewDetail; - assignmentId: number; -} - -export function TicketDefectClassTabs({ - ticketId, - ticket, - assignmentId, -}: TicketDefectClassTabsProps) { - const router = useRouter(); - const pathname = usePathname(); - const searchParams = useSearchParams(); - const defectClassParam = searchParams.get('defect_class'); - const defectClassesQuery = useTicketDefectClassesQuery(ticketId); - - useEffect(() => { - const defectClasses = defectClassesQuery.data?.defect_classes ?? []; - - if (!defectClasses.length) return; - - const hasUrlClass = defectClasses.some( - (item) => item.class_name === defectClassParam, - ); - - if (hasUrlClass) return; - - const firstClass = defectClasses[0].class_name; - const nextParams = new URLSearchParams(searchParams.toString()); - nextParams.set('defect_class', firstClass); - router.replace(`${pathname}?${nextParams.toString()}`, { scroll: false }); - }, [ - defectClassParam, - defectClassesQuery.data?.defect_classes, - pathname, - router, - searchParams, - ]); - - const previewVideoId = ticket?.video_id ?? defectClassesQuery.data?.video_id; - - if (!previewVideoId) return null; - - return ( - - -
- -
-
-
- ); -} diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketClassDetectionPreview.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketDetectionPreview.tsx similarity index 97% rename from src/app/(modules)/ticket/[ticketId]/components/TicketClassDetectionPreview.tsx rename to src/app/(modules)/ticket/[ticketId]/components/TicketDetectionPreview.tsx index f9fba3f..66848dd 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketClassDetectionPreview.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketDetectionPreview.tsx @@ -17,7 +17,7 @@ import { } from '../../hooks/useTicketQueries'; import { DetectionDiscardDialog } from './DetectionDiscardDialog'; -interface TicketClassDetectionPreviewProps { +interface TicketDetectionPreviewProps { ticketId: string; videoId?: string | null; assignmentId: number; @@ -52,7 +52,7 @@ function DetectionMetadataBar({ ); } -function TicketClassDetectionPreviewSkeleton() { +function TicketDetectionPreviewSkeleton() { return ( <>
@@ -91,11 +91,11 @@ function TicketClassDetectionPreviewSkeleton() { ); } -export function TicketClassDetectionPreview({ +export function TicketDetectionPreview({ ticketId, videoId, assignmentId, -}: TicketClassDetectionPreviewProps) { +}: TicketDetectionPreviewProps) { const [currentIndex, setCurrentIndex] = useState(0); const [isDiscardDialogOpen, setIsDiscardDialogOpen] = useState(false); @@ -130,7 +130,6 @@ export function TicketClassDetectionPreview({ const discardMutation = useDiscardDetectionMutation( ticketId, videoId ?? undefined, - activeDetection?.detection.class_name, ); useEffect(() => { @@ -217,7 +216,7 @@ export function TicketClassDetectionPreview({
{detectionsQuery.isLoading && !detectionsQuery.data ? ( - + ) : detectionsQuery.isError ? (
diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketDetectionPreviewCard.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketDetectionPreviewCard.tsx new file mode 100644 index 0000000..d1c04c1 --- /dev/null +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketDetectionPreviewCard.tsx @@ -0,0 +1,33 @@ +'use client'; + +import { Card, CardContent } from '@/components/ui/card'; + +import { TicketDetectionPreview } from './TicketDetectionPreview'; + +interface TicketDetectionPreviewCardProps { + ticketId: string; + videoId?: string | null; + assignmentId: number; +} + +export function TicketDetectionPreviewCard({ + ticketId, + videoId, + assignmentId, +}: TicketDetectionPreviewCardProps) { + if (!videoId) return null; + + return ( + + +
+ +
+
+
+ ); +} diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx index b193b01..b908ad6 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx @@ -2,19 +2,19 @@ import { PERMISSIONS } from '@/constants/permissions'; import { PermissionGuard } from '@/guards'; -import type { TicketAssignmentSummary, TicketDetail } from '@/types'; +import type { TicketAssignmentSummary } from '@/types'; import { NoTicketAction } from './actions/NoTicketAction'; import { RequestExtensionAction } from './actions/RequestExtensionAction'; import { ReviewExtensionRequestAction } from './actions/ReviewExtensionRequestAction'; interface TicketStatusActionsProps { - ticket: TicketDetail; + ticketId: string; assignment?: TicketAssignmentSummary; } function getTicketAction( - ticket: TicketDetail, + ticketId: string, assignment?: TicketAssignmentSummary, ) { if (assignment && !assignment.is_complete) { @@ -27,7 +27,7 @@ function getTicketAction( fallback={} > @@ -35,7 +35,7 @@ function getTicketAction( } > @@ -46,10 +46,10 @@ function getTicketAction( } export function TicketStatusActions({ - ticket, + ticketId, assignment, }: TicketStatusActionsProps) { - const action = getTicketAction(ticket, assignment); + const action = getTicketAction(ticketId, assignment); return action ?
{action}
: null; } diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/AssignedContractorAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/AssignedContractorAction.tsx deleted file mode 100644 index 5e3a661..0000000 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/AssignedContractorAction.tsx +++ /dev/null @@ -1,61 +0,0 @@ -'use client'; - -import { CalendarClock, UserCheck } from 'lucide-react'; - -import { PersonInfo } from '@/components/person-avatar'; -import { Badge } from '@/components/ui/badge'; -import type { TicketDetail } from '@/types'; -import { formatDate } from '@/utils/date'; - -import { TicketActionCard } from './TicketActionCard'; - -function AssignmentField({ label, value }: { label: string; value: string }) { - return ( -
-

{label}

-

{value}

-
- ); -} - -export function AssignedContractorAction({ ticket }: { ticket: TicketDetail }) { - const worker = ticket.worker; - - return ( - - {worker ? ( -
-
- - - Assigned - -
- -
- - - -
-

Due Date

-

- - {formatDate(worker.due_at)} -

-
-
-
- ) : ( -

- Assignment details are not available. -

- )} -
- ); -} diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/ClosedTicketAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/ClosedTicketAction.tsx deleted file mode 100644 index 61ec5dd..0000000 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/ClosedTicketAction.tsx +++ /dev/null @@ -1,107 +0,0 @@ -'use client'; - -import type { ReactNode } from 'react'; -import { Lock, MessageSquare } from 'lucide-react'; - -import { PersonInfo } from '@/components/person-avatar'; -import { Button } from '@/components/ui/button'; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from '@/components/ui/card'; -import { cn } from '@/lib/utils'; -import type { TicketDetail } from '@/types'; -import { formatDateOnly } from '@/utils/date'; - -function MetaField({ - label, - value, - valueClassName, -}: { - label: string; - value: ReactNode; - valueClassName?: string; -}) { - return ( -
-

- {label} -

-
- {value} -
-
- ); -} - -function FinalCommentsPanel({ - comment, -}: { - comment: string | null | undefined; -}) { - if (!comment?.trim()) { - return ( -
- -

No comments added yet

-
- ); - } - - return ( -
-

- {comment.trim()} -

-
- ); -} - -export function ClosedTicketAction({ ticket }: { ticket: TicketDetail }) { - const reviewer = ticket.reviewer; - const reviewedAt = reviewer?.reviewed_at ?? ticket.timestamps.updated_at; - - return ( - - - Resolution Details - - Review record for {ticket.ticket_name} - - - - -
- } - /> - - -
- -
-

- Review Comment -

- -
- - -
-
- ); -} diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionAction.tsx index 48ba9a8..7486d59 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionAction.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionAction.tsx @@ -1,23 +1,21 @@ import { Clock3 } from 'lucide-react'; -import type { TicketDetail } from '@/types'; - import { TicketActionCard } from './TicketActionCard'; import { TicketExtensionRequestPanel } from './TicketExtensionRequestPanel'; export function RequestExtensionAction({ - ticket, + ticketId, assignmentId, dueAt, }: { - ticket: TicketDetail; + ticketId: string; assignmentId: number; dueAt: string | null; }) { return ( diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionForm.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionForm.tsx index 3324992..adaacae 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionForm.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionForm.tsx @@ -8,20 +8,19 @@ import { DatePickerSimple } from '@/components/form/DatePickerSimple'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; -import type { TicketDetail } from '@/types'; import { useRequestTicketExtensionMutation } from '../../../hooks/useTicketQueries'; const MAX_REASON_LENGTH = 300; interface RequestExtensionFormProps { - ticket: TicketDetail; + ticketId: string; assignmentId: number; currentDueAt: string | null; } export function RequestExtensionForm({ - ticket, + ticketId, assignmentId, currentDueAt, }: RequestExtensionFormProps) { @@ -42,9 +41,8 @@ export function RequestExtensionForm({ const [requestedDueDate, setRequestedDueDate] = useState(); const [reason, setReason] = useState(''); const requestExtensionMutation = useRequestTicketExtensionMutation( - ticket.id, + ticketId, assignmentId, - ticket.defect_class, ); const isPending = requestExtensionMutation.isPending; const isRequestedDateValid = Boolean( diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/ReviewExtensionRequestAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/ReviewExtensionRequestAction.tsx index e0ae635..03b9cc6 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/ReviewExtensionRequestAction.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/ReviewExtensionRequestAction.tsx @@ -24,7 +24,6 @@ import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import type { - TicketDetail, TicketExtensionRequest, TicketExtensionRequestsResponse, } from '@/types'; @@ -86,11 +85,11 @@ function Requester({ request }: { request: TicketExtensionRequest }) { } function ExtensionReviewForm({ - ticket, + ticketId, assignmentId, request, }: { - ticket: TicketDetail; + ticketId: string; assignmentId: number; request: TicketExtensionRequest; }) { @@ -102,9 +101,8 @@ function ExtensionReviewForm({ const [isRejectConfirmationOpen, setIsRejectConfirmationOpen] = useState(false); const reviewMutation = useReviewTicketExtensionMutation( - ticket.id, + ticketId, assignmentId, - ticket.defect_class, ); const extensionDays = getExtensionDays(request); @@ -274,14 +272,14 @@ function ExtensionReviewForm({ } export function ReviewExtensionRequestAction({ - ticket, + ticketId, assignmentId, }: { - ticket: TicketDetail; + ticketId: string; assignmentId: number; }) { const extensionRequestQuery = useTicketExtensionRequestQuery( - ticket.id, + ticketId, assignmentId, ); @@ -308,7 +306,7 @@ export function ReviewExtensionRequestAction({ return ( diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/TicketExtensionRequestPanel.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/TicketExtensionRequestPanel.tsx index 9e48ef2..c623a1f 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/TicketExtensionRequestPanel.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/TicketExtensionRequestPanel.tsx @@ -14,7 +14,6 @@ import { import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import type { - TicketDetail, TicketExtensionRequest, TicketExtensionRequestsResponse, } from '@/types'; @@ -186,16 +185,16 @@ function ExtensionRequestDetails({ } export function TicketExtensionRequestPanel({ - ticket, + ticketId, assignmentId, dueAt, }: { - ticket: TicketDetail; + ticketId: string; assignmentId: number; dueAt: string | null; }) { const extensionRequestQuery = useTicketExtensionRequestQuery( - ticket.id, + ticketId, assignmentId, ); @@ -234,7 +233,7 @@ export function TicketExtensionRequestPanel({ if (!latestRequest) { return ( diff --git a/src/app/(modules)/ticket/[ticketId]/page.tsx b/src/app/(modules)/ticket/[ticketId]/page.tsx index c78beed..48e2240 100644 --- a/src/app/(modules)/ticket/[ticketId]/page.tsx +++ b/src/app/(modules)/ticket/[ticketId]/page.tsx @@ -1,7 +1,7 @@ 'use client'; import { useState } from 'react'; -import { useParams, useRouter, useSearchParams } from 'next/navigation'; +import { useParams, useRouter } from 'next/navigation'; import { ArrowLeft } from 'lucide-react'; import { Button } from '@/components/ui/button'; @@ -11,24 +11,20 @@ import { TicketOverviewCardSkeleton, TicketOverviewHeaderSkeleton, } from './components/TicketOverviewSkeleton'; -import { TicketClassActionsSkeleton } from './components/TicketClassDetailSkeleton'; import { TicketAssignmentsCard } from './components/TicketAssignmentsCard'; import { TicketOverviewCard } from './components/TicketOverviewCard'; import { TicketStatusActions } from './components/TicketStatusActions'; -import { TicketDefectClassTabs } from './components/TicketDefectClassTabs'; +import { TicketDetectionPreviewCard } from './components/TicketDetectionPreviewCard'; import { TicketHistoryCard } from './components/TicketHistoryCard'; import { useTicketDetailEvents } from '../hooks/useTicketDetailEvents'; import { useTicketAssignmentsQuery, - useTicketClassDetailQuery, useTicketOverviewQuery, } from '../hooks/useTicketQueries'; export default function TicketDetailPage() { const router = useRouter(); - const searchParams = useSearchParams(); const { ticketId } = useParams() as { ticketId: string }; - const defectClass = searchParams.get('defect_class') ?? undefined; const [selectedAssignmentId, setSelectedAssignmentId] = useState(); const overviewQuery = useTicketOverviewQuery(ticketId); @@ -37,8 +33,6 @@ export default function TicketDetailPage() { ticketId, overview?.assignments, ); - const classDetailQuery = useTicketClassDetailQuery(ticketId, defectClass); - const classDetail = classDetailQuery.data; const assignments = (assignmentsQuery.data?.items ?? []).filter( (assignment) => assignment.item_count > 0, ); @@ -46,11 +40,8 @@ export default function TicketDetailPage() { assignments.find((assignment) => assignment.id === selectedAssignmentId) ?? assignments[0]; const activeAssignmentId = activeAssignment?.id; - const isClassDetailLoading = Boolean( - defectClass && classDetailQuery.isLoading, - ); - useTicketDetailEvents(ticketId, defectClass, Boolean(ticketId)); + useTicketDetailEvents(ticketId, Boolean(ticketId)); if (overviewQuery.isError) { return ( @@ -94,9 +85,9 @@ export default function TicketDetailPage() { /> ) : null} {activeAssignment ? ( - ) : null} @@ -104,12 +95,9 @@ export default function TicketDetailPage() {
- {activeAssignment && isClassDetailLoading ? ( - - ) : null} - {activeAssignment && !isClassDetailLoading && classDetail ? ( + {activeAssignment ? ( ) : null} diff --git a/src/app/(modules)/ticket/hooks/useTicketDetailEvents.ts b/src/app/(modules)/ticket/hooks/useTicketDetailEvents.ts index e7ba85c..94bd090 100644 --- a/src/app/(modules)/ticket/hooks/useTicketDetailEvents.ts +++ b/src/app/(modules)/ticket/hooks/useTicketDetailEvents.ts @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useCallback, useMemo } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { API_ROUTES } from '@/constants/apiRoutes'; @@ -18,15 +18,9 @@ function isHeartbeatEvent(event: { type?: string } | null | undefined) { export function useTicketDetailEvents( ticketId: string | undefined, - defectClass: string | undefined, enabled = true, ): void { const queryClient = useQueryClient(); - const defectClassRef = useRef(defectClass); - - useEffect(() => { - defectClassRef.current = defectClass; - }, [defectClass]); const refetchTicket = useCallback(() => { if (!ticketId) return; @@ -38,11 +32,6 @@ export function useTicketDetailEvents( queryClient.refetchQueries({ queryKey: ticketKeys.timeline(ticketId), }); - if (defectClassRef.current) { - queryClient.refetchQueries({ - queryKey: ticketKeys.classDetail(ticketId, defectClassRef.current), - }); - } queryClient.invalidateQueries({ queryKey: ticketKeys.defectClasses(ticketId), }); diff --git a/src/app/(modules)/ticket/hooks/useTicketQueries.ts b/src/app/(modules)/ticket/hooks/useTicketQueries.ts index 8fe2cdc..dbfc8f5 100644 --- a/src/app/(modules)/ticket/hooks/useTicketQueries.ts +++ b/src/app/(modules)/ticket/hooks/useTicketQueries.ts @@ -18,7 +18,6 @@ import type { RequestTicketExtensionPayload, ReviewDetectionRepairProofPayload, ReviewTicketExtensionPayload, - TicketDetail, TicketAssignmentSummary, TicketListParams, DetectionProofStatus, @@ -78,21 +77,6 @@ export function useTicketTimelineQuery(ticketId: string | undefined) { }); } -export function useTicketClassDetailQuery( - ticketId: string | undefined, - defectClass: string | undefined, -) { - return useQuery({ - queryKey: ticketKeys.classDetail(ticketId ?? '', defectClass ?? ''), - queryFn: () => - ticketService.getTicketClassDetail( - ticketId as string, - defectClass as string, - ), - enabled: Boolean(ticketId && defectClass), - }); -} - export function useTicketDefectClassesQuery(ticketId: string | undefined) { return useQuery({ queryKey: ticketKeys.defectClasses(ticketId ?? ''), @@ -224,7 +208,6 @@ export function useTicketReviewDetectionsQuery( export function useDiscardDetectionMutation( ticketId: string, videoId: string | undefined, - defectClass?: string, ) { const queryClient = useQueryClient(); @@ -248,13 +231,6 @@ export function useDiscardDetectionMutation( queryClient.invalidateQueries({ queryKey: ticketKeys.overview(ticketId), }), - ...(defectClass - ? [ - queryClient.invalidateQueries({ - queryKey: ticketKeys.classDetail(ticketId, defectClass), - }), - ] - : []), queryClient.invalidateQueries({ queryKey: ticketKeys.defectClasses(ticketId), }), @@ -372,7 +348,6 @@ export function useAssignTicketMutation(ticketId: string, videoId?: string) { export function useRequestTicketExtensionMutation( ticketId: string, assignmentId: number | undefined, - defectClass: string, ) { const queryClient = useQueryClient(); @@ -392,9 +367,6 @@ export function useRequestTicketExtensionMutation( queryClient.invalidateQueries({ queryKey: ticketKeys.extensionRequest(ticketId, assignmentId), }); - queryClient.invalidateQueries({ - queryKey: ticketKeys.classDetail(ticketId, defectClass), - }); queryClient.invalidateQueries({ queryKey: ticketKeys.assignments(ticketId), }); @@ -409,7 +381,6 @@ export function useRequestTicketExtensionMutation( export function useReviewTicketExtensionMutation( ticketId: string, assignmentId: number, - defectClass: string, ) { const queryClient = useQueryClient(); @@ -425,9 +396,6 @@ export function useReviewTicketExtensionMutation( queryClient.invalidateQueries({ queryKey: ticketKeys.extensionRequest(ticketId, assignmentId), }); - queryClient.invalidateQueries({ - queryKey: ticketKeys.classDetail(ticketId, defectClass), - }); queryClient.invalidateQueries({ queryKey: ticketKeys.assignments(ticketId), }); @@ -439,18 +407,21 @@ export function useReviewTicketExtensionMutation( onError: () => toast.error('Failed to review extension request'), }); } + export function useCloseTicketMutation(ticketId: string) { const queryClient = useQueryClient(); return useMutation({ mutationFn: (payload: CloseTicketPayload) => ticketService.closeTicket(ticketId, payload), - onSuccess: (ticket, payload) => { + onSuccess: () => { toast.success('Ticket closed'); - queryClient.setQueryData( - ticketKeys.classDetail(ticketId, payload.defect_class), - ticket, - ); + queryClient.invalidateQueries({ + queryKey: ticketKeys.overview(ticketId), + }); + queryClient.invalidateQueries({ + queryKey: ticketKeys.assignments(ticketId), + }); queryClient.invalidateQueries({ queryKey: ticketKeys.timeline(ticketId), }); diff --git a/src/app/(modules)/ticket/page.tsx b/src/app/(modules)/ticket/page.tsx index e670019..994dc29 100644 --- a/src/app/(modules)/ticket/page.tsx +++ b/src/app/(modules)/ticket/page.tsx @@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation'; import { PageHeader } from '@/components/page-header'; import { Ticket } from 'lucide-react'; import type { TicketListItem } from '@/types'; +import { ROUTES } from '@/utils/routes'; import { TicketFilters } from './components/TicketFilters'; import { TicketTable } from './components/TicketTable'; @@ -37,11 +38,7 @@ export default function TicketPage() { const handleView = useCallback( (ticket: TicketListItem) => { - const defectClassQuery = ticket.default_defect_class - ? `?defect_class=${encodeURIComponent(ticket.default_defect_class)}` - : ''; - - router.push(`/ticket/${ticket.id}${defectClassQuery}`); + router.push(ROUTES.TICKET_DETAIL(ticket.id)); }, [router], ); diff --git a/src/app/(modules)/ticket/queries/ticketKeys.ts b/src/app/(modules)/ticket/queries/ticketKeys.ts index 79de18c..3bb1de7 100644 --- a/src/app/(modules)/ticket/queries/ticketKeys.ts +++ b/src/app/(modules)/ticket/queries/ticketKeys.ts @@ -13,8 +13,6 @@ export const ticketKeys = { [...ticketKeys.details(), ticketId, 'overview'] as const, timeline: (ticketId: string) => [...ticketKeys.details(), ticketId, 'timeline'] as const, - classDetail: (ticketId: string, defectClass: string) => - [...ticketKeys.details(), ticketId, 'class', defectClass] as const, classDetectionLists: (videoId: string) => [...ticketKeys.details(), 'video', videoId, 'detections'] as const, classDetections: (videoId: string, params: VideoDetectionsParams) => diff --git a/src/constants/apiRoutes.ts b/src/constants/apiRoutes.ts index 641138a..8278e02 100644 --- a/src/constants/apiRoutes.ts +++ b/src/constants/apiRoutes.ts @@ -66,8 +66,6 @@ export const API_ROUTES = { BASE: '/biz/api/v1/tickets', DETAIL: (id: string) => `/biz/api/v1/tickets/${id}`, TIMELINE: (id: string) => `/biz/api/v1/tickets/${id}/timeline`, - CLASS_DETAIL: (id: string, defectClass: string) => - `/biz/api/v1/tickets/${id}/class-detail?defect_class=${defectClass}`, DEFECT_CLASSES: (id: string) => `/biz/api/v1/tickets/${id}/defect-classes`, ASSIGNMENTS: (id: string) => `/biz/api/v1/tickets/${id}/assignments`, REQUEST_EXTENSION: (ticketId: string, assignmentId: number) => diff --git a/src/services/api/ticket.service.ts b/src/services/api/ticket.service.ts index 5d20ed3..1ade1d6 100644 --- a/src/services/api/ticket.service.ts +++ b/src/services/api/ticket.service.ts @@ -8,7 +8,6 @@ import type { RequestTicketExtensionPayload, ReviewTicketExtensionPayload, SseTokenResponse, - TicketDetail, TicketAssignmentSummary, TicketAssignmentsResponse, TicketExtensionRequestsResponse, @@ -62,16 +61,6 @@ export const ticketService = { return response.data; }, - getTicketClassDetail: async ( - ticketId: string, - defectClass: string, - ): Promise => { - const response = await axiosClient.get( - API_ROUTES.TICKETS.CLASS_DETAIL(ticketId, defectClass), - ); - return response.data; - }, - getTicketDefectClasses: async ( ticketId: string, ): Promise => { @@ -166,11 +155,12 @@ export const ticketService = { ); return response.data; }, + closeTicket: async ( ticketId: string, payload: CloseTicketPayload, - ): Promise => { - const response = await axiosClient.post( + ): Promise => { + const response = await axiosClient.post( API_ROUTES.TICKETS.CLOSE(ticketId), payload, ); diff --git a/src/types/ticket/detail.ts b/src/types/ticket/detail.ts index a15219f..f1a7c4e 100644 --- a/src/types/ticket/detail.ts +++ b/src/types/ticket/detail.ts @@ -1,5 +1,3 @@ -import type { TicketDefectClassTicketStatus } from './defect-class'; -import type { TicketStatus } from './status'; import type { TicketAssignmentCriteria } from './actions'; export interface TicketActor { @@ -9,15 +7,6 @@ export interface TicketActor { avatar_url?: string | null; } -export interface TicketStatusMetadata { - reason: string | null; - label: string | null; - message: string | null; - detection_count?: number | null; - requires_action?: boolean; - review_comment?: string | null; -} - export interface TicketVideo { id: string; name: string | null; @@ -26,40 +15,6 @@ export interface TicketVideo { thumbnail: string | null; } -export interface TicketWorker extends TicketActor { - assignment_id: number; - assigned_at: string | null; - due_at: string | null; - assigned_by_user_id: number | null; - assigned_by_name: string | null; - role: { - id: number; - name: string; - } | null; -} - -export interface TicketRepair { - notes: string | null; - proof_video_url: string | null; - proof_image_urls: string[]; - submitted_at: string | null; -} - -export interface TicketReviewer extends TicketActor { - review_comment: string | null; - reviewed_at: string | null; -} - -export interface TicketAiResult { - detection_count: number; - available_defect_classes: string[]; -} - -export interface TicketTenant { - root_tenant_id: number; - organization_id: number; -} - export interface TicketTimestamps { created_at: string; updated_at: string; @@ -137,24 +92,3 @@ export interface TicketOverviewDetail { assignments?: TicketAssignmentSummary[]; timestamps?: TicketTimestamps | null; } - -export interface TicketDetail { - id: string; - ticket_name?: string; - video_id: string; - chainage_id: string | null; - chainage_name: string | null; - status: TicketStatus; - defect_class: string; - defect_display_name: string; - assignment_status: TicketDefectClassTicketStatus; - status_metadata: TicketStatusMetadata | null; - video: TicketVideo | null; - uploader: TicketActor | null; - worker: TicketWorker | null; - repair: TicketRepair | null; - reviewer: TicketReviewer | null; - ai_result: TicketAiResult | null; - tenant: TicketTenant | null; - timestamps: TicketTimestamps; -} diff --git a/src/types/ticket/events.ts b/src/types/ticket/events.ts index 3bb91a6..6697c09 100644 --- a/src/types/ticket/events.ts +++ b/src/types/ticket/events.ts @@ -1,4 +1,3 @@ -import type { TicketDetail } from './detail'; import type { TicketStatus } from './status'; export interface SseTokenResponse { @@ -22,7 +21,7 @@ export type TicketTableStatusEvent = { message?: string; }; -export type TicketDetailStatusEvent = Partial & { +export type TicketDetailStatusEvent = { type: 'ticket_status' | 'heartbeat'; kind?: 'snapshot' | 'transitioned'; id?: string;