diff --git a/src/app/(modules)/results/[videoId]/page.tsx b/src/app/(modules)/results/[videoId]/page.tsx index c7a6ae6..fc891a8 100644 --- a/src/app/(modules)/results/[videoId]/page.tsx +++ b/src/app/(modules)/results/[videoId]/page.tsx @@ -1,27 +1,13 @@ 'use client'; -import { useMemo } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { ArrowLeft, TrendingUp } from 'lucide-react'; import VideoPlayerSection from '@/components/videoPlayerSection'; import { PageHeader } from '@/components/page-header'; import { Button } from '@/components/ui/button'; -import { CompletedVideoResult, DetectionType } from '@/types'; -import { getDetectionModeConfig } from '@/constants/detectionModeConfig'; import { useVideoResultsQuery } from '../hooks/useVideoResults'; import { VideoResultsSkeleton } from './components/VideoResultsSkeleton'; -const inferDetectionType = (data: CompletedVideoResult): DetectionType => { - if (data.detection_mode) return data.detection_mode as DetectionType; - - const signboards = data.summary?.unique_signboards || 0; - const potholes = data.summary?.unique_potholes || 0; - - if (signboards > 0 && potholes > 0) return 'pot-sign-detection'; - if (signboards > 0) return 'sign-board-detection'; - return 'pothole-detection'; -}; - export default function VideoResultsPage() { const router = useRouter(); const { videoId } = useParams() as { videoId: string }; @@ -33,30 +19,19 @@ export default function VideoResultsPage() { error: queryError, } = useVideoResultsQuery(videoId); - const detectionType = useMemo( - () => - detectionData ? inferDetectionType(detectionData) : 'pothole-detection', - [detectionData], - ); - const error = isError ? queryError instanceof Error ? queryError.message : 'Failed to load results' : null; - const getTitle = () => { - const config = getDetectionModeConfig(detectionType); - return `${config.label} Results`; - }; - if (isLoading) { return ; } if (error) { return ( -
+

{error}

); @@ -65,7 +40,7 @@ export default function VideoResultsPage() { return (
= 3 ? 'sm:grid-cols-3' : 'sm:grid-cols-2'; return (
@@ -67,33 +55,48 @@ export default function CurrentDetectionBar({ ) : null}
-
- {countItems.map((item) => { - const Icon = item.icon; +
+ {defectCounts.map((item) => { + const visual = getDefectVisual(item.class_name); + const Icon = visual.icon; return (

- {item.label} + {item.display_name}

- {item.value} + {item.count}

); })} + +
+
+ + + +

+ Total +

+
+

+ {totalDetections} +

+
); diff --git a/src/components/video/defectVisualConfig.ts b/src/components/video/defectVisualConfig.ts new file mode 100644 index 0000000..d3e4bf3 --- /dev/null +++ b/src/components/video/defectVisualConfig.ts @@ -0,0 +1,54 @@ +'use client'; + +import { + Activity, + AlertTriangle, + CircleAlert, + CircleDot, + Droplets, + LucideIcon, + SignpostBig, +} from 'lucide-react'; + +type DefectVisualConfig = { + icon: LucideIcon; + colorClassName: string; + iconClassName: string; +}; + +const DEFECT_VISUALS: Record = { + manhole_cover: { + icon: CircleDot, + colorClassName: 'text-zinc-500', + iconClassName: 'bg-zinc-500/10 text-zinc-500', + }, + pothole: { + icon: AlertTriangle, + colorClassName: 'text-orange-500', + iconClassName: 'bg-orange-500/10 text-orange-500', + }, + road_crack: { + icon: CircleAlert, + colorClassName: 'text-rose-500', + iconClassName: 'bg-rose-500/10 text-rose-500', + }, + sign_board: { + icon: SignpostBig, + colorClassName: 'text-blue-500', + iconClassName: 'bg-blue-500/10 text-blue-500', + }, + water_puddle: { + icon: Droplets, + colorClassName: 'text-cyan-500', + iconClassName: 'bg-cyan-500/10 text-cyan-500', + }, +}; + +const DEFAULT_VISUAL: DefectVisualConfig = { + icon: Activity, + colorClassName: 'text-green-500', + iconClassName: 'bg-green-500/10 text-green-500', +}; + +export const getDefectVisual = (className: string) => + DEFECT_VISUALS[className] || DEFAULT_VISUAL; diff --git a/src/components/video/detectionLogs.tsx b/src/components/video/detectionLogs.tsx index 92b711a..b2e6c5e 100644 --- a/src/components/video/detectionLogs.tsx +++ b/src/components/video/detectionLogs.tsx @@ -2,10 +2,8 @@ import { useEffect, useRef } from 'react'; import { Activity, Film, MapPin } from 'lucide-react'; -import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { DetectionResultLog } from '@/types'; -import { DETECTION_TYPES } from '@/constants/detectionModeConfig'; import { cn } from '@/lib/utils'; interface DetectionLogsProps { @@ -34,7 +32,7 @@ const DetectionLogs = ({ logs, activeLogId, onSeek }: DetectionLogsProps) => { return (
-

+

Detection Logs

@@ -43,20 +41,18 @@ const DetectionLogs = ({ logs, activeLogId, onSeek }: DetectionLogsProps) => {
{logs.length === 0 ? (
- +

No detections found

) : ( logs.map((log) => { - const typeId = (log.type || '').toLowerCase(); - const typeConfig = DETECTION_TYPES[typeId]; - const label = - log.label || typeConfig?.label || typeId.replace(/_/g, ' '); + const timestampSeconds = log.frame.timestamp_seconds; + const { latitude, longitude } = log.location; const isActive = activeLogId === log.id; return (