feat(ticket): add paginated class detection preview with image and map
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { getDefectVisual } from '@/constants/defectVisualConfig';
|
||||
import type { DetectionResultLog } from '@/types';
|
||||
import type { DetectionResultItem } from '@/types';
|
||||
|
||||
interface BoundingBoxOverlayProps {
|
||||
detections: DetectionResultLog[];
|
||||
detections: DetectionResultItem[];
|
||||
videoWidth: number;
|
||||
videoHeight: number;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { MapPin } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -14,6 +15,12 @@ type DetectionLocationMapProps = {
|
||||
latitude: number | null;
|
||||
longitude: number | null;
|
||||
label: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
aspectRatio?: string;
|
||||
showCoordinates?: boolean;
|
||||
variant?: 'card' | 'plain';
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type ClientDetectionLocationMapProps = {
|
||||
@@ -70,44 +77,66 @@ export default function DetectionLocationMap({
|
||||
latitude,
|
||||
longitude,
|
||||
label,
|
||||
title = 'Detection Location',
|
||||
description = 'Selected log GPS coordinates',
|
||||
aspectRatio = '16 / 9',
|
||||
showCoordinates = true,
|
||||
variant = 'card',
|
||||
className,
|
||||
}: DetectionLocationMapProps) {
|
||||
const mapContent =
|
||||
latitude == null || longitude == null ? (
|
||||
<div
|
||||
className="flex items-center justify-center rounded-lg border border-dashed bg-muted/30 px-4 py-8 text-sm text-muted-foreground"
|
||||
style={{ aspectRatio }}
|
||||
>
|
||||
Coordinates are unavailable for the selected log.
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{showCoordinates ? (
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{latitude.toFixed(6)}, {longitude.toFixed(6)}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className="overflow-hidden rounded-lg border bg-muted"
|
||||
style={{ aspectRatio }}
|
||||
>
|
||||
<ClientDetectionLocationMap
|
||||
latitude={latitude}
|
||||
longitude={longitude}
|
||||
label={label}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
if (variant === 'plain') {
|
||||
return (
|
||||
<div className={cn('space-y-2', className)}>
|
||||
{title ? (
|
||||
<p className="text-xs font-medium text-muted-foreground">{title}</p>
|
||||
) : null}
|
||||
{mapContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="overflow-hidden">
|
||||
<Card className={cn('overflow-hidden', className)}>
|
||||
<CardHeader className="border-b pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded bg-secondary p-2">
|
||||
<MapPin className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-base font-semibold">
|
||||
Detection Location
|
||||
</CardTitle>
|
||||
<CardDescription className="text-xs">
|
||||
Selected log GPS coordinates
|
||||
</CardDescription>
|
||||
<CardTitle className="text-base font-semibold">{title}</CardTitle>
|
||||
<CardDescription className="text-xs">{description}</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4 pt-6">
|
||||
{latitude == null || longitude == null ? (
|
||||
<div className="rounded-lg border border-dashed bg-muted/30 px-4 py-8 text-sm text-muted-foreground">
|
||||
Coordinates are unavailable for the selected log.
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{latitude.toFixed(6)}, {longitude.toFixed(6)}
|
||||
</div>
|
||||
<div className="h-72 overflow-hidden rounded-lg border bg-muted">
|
||||
<ClientDetectionLocationMap
|
||||
latitude={latitude}
|
||||
longitude={longitude}
|
||||
label={label}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
<CardContent className="space-y-4 pt-6">{mapContent}</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ import { AlertTriangle, ImageIcon, Loader2 } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { useProtectedMediaObjectUrl } from '@/hooks/useProtectedMedia';
|
||||
import type { DetectionResultLog } from '@/types';
|
||||
import type { DetectionResultItem } from '@/types';
|
||||
|
||||
import BoundingBoxOverlay from '@/components/annotation/boundingBoxOverlay';
|
||||
|
||||
interface AnnotatedDetectionImageProps {
|
||||
detection?: DetectionResultLog;
|
||||
detection?: DetectionResultItem;
|
||||
videoWidth: number;
|
||||
videoHeight: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user