'use client';
import { useEffect, useMemo, useState } from 'react';
import {
AlertTriangle,
ChevronLeft,
ChevronRight,
ImageIcon,
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 type { DetectionRepairProof } from '@/types';
import {
useDiscardDetectionMutation,
useTicketClassDetectionsQuery,
} from '../../hooks/useTicketQueries';
import { DetectionDiscardDialog } from './DetectionDiscardDialog';
import { RepairProofGallery } from './repair/RepairProofGallery';
import { RepairSubmittedBanner } from './repair/RepairSubmittedBanner';
interface TicketClassDetectionPreviewProps {
ticketId: string;
videoId?: string | null;
defectClassName: string;
displayName: string;
totalCount?: number;
}
function DetectionMetadataBar({
items,
}: {
items: { label: string; value: string | number }[];
}) {
return (
{items.map((item, index) => (
{item.label}
{item.value}
))}
);
}
function DetectionRepairProofPanel({
repairProof,
}: {
repairProof: DetectionRepairProof;
}) {
if (!repairProof.submitted) {
return null;
}
return (
Repair Proof
{repairProof.submitted_at ? (
) : null}
Repair Notes
{repairProof.notes?.trim() || 'No repair notes provided.'}
);
}
function TicketClassDetectionPreviewSkeleton() {
return (
<>
{Array.from({ length: 3 }).map((_, index) => (
))}
>
);
}
export function TicketClassDetectionPreview({
ticketId,
videoId,
defectClassName,
displayName,
totalCount,
}: TicketClassDetectionPreviewProps) {
const [currentIndex, setCurrentIndex] = useState(0);
const [isDiscardDialogOpen, setIsDiscardDialogOpen] = useState(false);
const visual = getDefectVisual(defectClassName);
const IssueIcon = visual.icon;
useEffect(() => {
setCurrentIndex(0);
}, [defectClassName]);
const queryParams = useMemo(
() => ({
class_name: defectClassName,
skip: currentIndex,
limit: 1,
sort: 'timestamp_asc',
}),
[currentIndex, defectClassName],
);
const detectionsQuery = useTicketClassDetectionsQuery(
videoId ?? undefined,
queryParams,
);
const discardMutation = useDiscardDetectionMutation(
ticketId,
videoId ?? undefined,
defectClassName,
);
const detectionResult = detectionsQuery.data;
const activeDetection = detectionResult?.items[0];
const detectionCount = detectionResult?.total ?? totalCount ?? 0;
useEffect(() => {
if (detectionCount === 0 && currentIndex !== 0) {
setCurrentIndex(0);
return;
}
if (detectionCount > 0 && currentIndex > detectionCount - 1) {
setCurrentIndex(detectionCount - 1);
}
}, [currentIndex, detectionCount]);
const isNavigationDisabled =
detectionCount === 0 || detectionsQuery.isLoading;
return (
{displayName}
{detectionCount} Detections
Review 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.
) : (
<>
{activeDetection.repair_proof ? (
) : null}
{
await discardMutation.mutateAsync({
detectionId: activeDetection.detection.id,
payload,
});
setIsDiscardDialogOpen(false);
}}
/>
>
)}
);
}