'use client';
import { useEffect, useMemo, useState } from 'react';
import { AlertTriangle, ChevronLeft, ChevronRight, Trash2 } from 'lucide-react';
import DetectionLocationMap from '@/components/map/detectionLocationMap';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import AnnotatedDetectionImage from '@/components/video/annotatedDetectionImage';
import { getDefectVisual } from '@/constants/defectVisualConfig';
import { cn } from '@/lib/utils';
import {
useDiscardDetectionMutation,
useTicketDetectionsQuery,
} from '../../hooks/useTicketQueries';
import { DetectionDiscardDialog } from './DetectionDiscardDialog';
interface TicketDetectionPreviewProps {
ticketId: string;
videoId?: string | null;
assignmentId: number;
}
function DetectionMetadataBar({
items,
}: {
items: { label: string; value: string | number }[];
}) {
return (
{items.map((item, index) => (
{item.label}
{item.value}
))}
);
}
function TicketDetectionPreviewSkeleton() {
return (
<>
{Array.from({ length: 3 }).map((_, index) => (
))}
>
);
}
export function TicketDetectionPreview({
ticketId,
videoId,
assignmentId,
}: TicketDetectionPreviewProps) {
const [currentIndex, setCurrentIndex] = useState(0);
const [isDiscardDialogOpen, setIsDiscardDialogOpen] = useState(false);
useEffect(() => {
setCurrentIndex(0);
}, [assignmentId, videoId]);
const queryParams = useMemo(
() => ({
skip: currentIndex,
limit: 1,
assignment_id: assignmentId,
sort: 'timestamp_asc',
}),
[assignmentId, currentIndex],
);
const detectionsQuery = useTicketDetectionsQuery(
videoId ?? undefined,
queryParams,
);
const detectionResult = detectionsQuery.data;
const activeDetection = detectionResult?.items[0];
const confirmedDetectionCount = detectionResult?.total;
const detectionCount = detectionResult?.total ?? 0;
const activeVisual = getDefectVisual(
activeDetection?.detection.class_name ?? '',
);
const ActiveIssueIcon = activeVisual.icon;
const activeDisplayName =
activeDetection?.detection.display_name ?? 'Detection';
const discardMutation = useDiscardDetectionMutation(
ticketId,
videoId ?? undefined,
);
useEffect(() => {
if (confirmedDetectionCount === undefined) return;
if (confirmedDetectionCount === 0 && currentIndex !== 0) {
setCurrentIndex(0);
return;
}
if (
confirmedDetectionCount > 0 &&
currentIndex > confirmedDetectionCount - 1
) {
setCurrentIndex(confirmedDetectionCount - 1);
}
}, [confirmedDetectionCount, currentIndex]);
const isNavigationDisabled =
detectionCount === 0 || detectionsQuery.isLoading;
return (
{activeDisplayName}
{detectionCount} Detections
Review all detections in ascending timestamp order.
{detectionCount === 0
? '0 of 0'
: `${currentIndex + 1} of ${detectionCount}`}
{detectionsQuery.isLoading && !detectionsQuery.data ? (
) : detectionsQuery.isError ? (
Failed to load detection preview.
) : !activeDetection || !detectionResult ? (
No detections are available for this issue.
) : (
<>
{
await discardMutation.mutateAsync({
detectionId: activeDetection.detection.id,
payload,
});
setIsDiscardDialogOpen(false);
}}
/>
>
)}
);
}