From 53fe4e11fe79a5569ecc4c9b55ab58f3f793f68c Mon Sep 17 00:00:00 2001 From: "santasri.pachhal" Date: Thu, 2 Jul 2026 13:57:18 +0530 Subject: [PATCH] feat: change interface of analysis video log --- .../components/VideoResultsSkeleton.tsx | 29 +++-- src/components/video/detectionLogs.tsx | 119 +++++++++++++----- src/services/api/video.service.ts | 4 +- src/types/analysis.ts | 10 +- src/types/detection.ts | 1 + src/utils/index.ts | 3 +- src/utils/media.ts | 13 ++ 7 files changed, 126 insertions(+), 53 deletions(-) create mode 100644 src/utils/media.ts diff --git a/src/app/(modules)/results/[videoId]/components/VideoResultsSkeleton.tsx b/src/app/(modules)/results/[videoId]/components/VideoResultsSkeleton.tsx index 47e2de8..8403a9d 100644 --- a/src/app/(modules)/results/[videoId]/components/VideoResultsSkeleton.tsx +++ b/src/app/(modules)/results/[videoId]/components/VideoResultsSkeleton.tsx @@ -14,20 +14,23 @@ function StatSkeleton() { function DetectionLogSkeleton() { return ( -
-
-
- - +
+
+ +
+
+
+ + +
+
+ + +
+
+ +
-
- - -
-
-
- -
); diff --git a/src/components/video/detectionLogs.tsx b/src/components/video/detectionLogs.tsx index b2e6c5e..5a89a65 100644 --- a/src/components/video/detectionLogs.tsx +++ b/src/components/video/detectionLogs.tsx @@ -1,10 +1,13 @@ 'use client'; import { useEffect, useRef } from 'react'; -import { Activity, Film, MapPin } from 'lucide-react'; +import { Activity, Film, ImageIcon, MapPin } from 'lucide-react'; + import { ScrollArea } from '@/components/ui/scroll-area'; -import { DetectionResultLog } from '@/types'; +import { Skeleton } from '@/components/ui/skeleton'; +import { useProtectedMediaObjectUrl } from '@/hooks/useProtectedMedia'; import { cn } from '@/lib/utils'; +import type { DetectionResultLog } from '@/types'; interface DetectionLogsProps { logs: DetectionResultLog[]; @@ -19,6 +22,47 @@ const formatVideoTime = (seconds: number) => { return `${minutes}:${secs.toString().padStart(2, '0')}`; }; +function DetectionLogThumbnail({ + url, + label, +}: { + url?: string | null; + label: string; +}) { + const { objectUrl, isLoading, isError } = useProtectedMediaObjectUrl(url); + + if (!url) { + return ( +
+ +
+ ); + } + + if (isLoading) { + return ; + } + + if (isError || !objectUrl) { + return ( +
+ N/A +
+ ); + } + + return ( +
+ {label} +
+ ); +} + const DetectionLogs = ({ logs, activeLogId, onSeek }: DetectionLogsProps) => { const activeLogRef = useRef(null); @@ -49,6 +93,7 @@ const DetectionLogs = ({ logs, activeLogId, onSeek }: DetectionLogsProps) => { const timestampSeconds = log.frame.timestamp_seconds; const { latitude, longitude } = log.location; const isActive = activeLogId === log.id; + const label = log.detection.display_name; return ( ); diff --git a/src/services/api/video.service.ts b/src/services/api/video.service.ts index e172bbd..183d513 100644 --- a/src/services/api/video.service.ts +++ b/src/services/api/video.service.ts @@ -45,7 +45,9 @@ export const videoService = { * Get analysis results for a video */ getVideoResults: async (videoId: string): Promise => { - const response = await axiosClient.get(API_ROUTES.VIDEOS.RESULTS(videoId)); + const response = await axiosClient.get( + API_ROUTES.VIDEOS.RESULTS(videoId), + ); return response.data; }, diff --git a/src/types/analysis.ts b/src/types/analysis.ts index ddaceb5..040cc3f 100644 --- a/src/types/analysis.ts +++ b/src/types/analysis.ts @@ -2,12 +2,12 @@ import { DetectionClassCount, DetectionResultLog } from './detection'; export type CompletedVideoResult = { video_id: string; - status: 'completed' | string; - processed_video_url: string | null; - raw_video_url: string | null; + status: 'completed'; + raw_video_url: string; + processed_video_url: string; summary: { - fps?: number; - duration_seconds?: number; + fps: number; + duration_seconds: number; total_detections: number; }; defect_counts: DetectionClassCount[]; diff --git a/src/types/detection.ts b/src/types/detection.ts index 4e8b07c..5be2d8b 100644 --- a/src/types/detection.ts +++ b/src/types/detection.ts @@ -12,6 +12,7 @@ export type DetectionResultLog = { class_name: string; display_name: string; confidence: number; + context_image_url: string; }; frame: { number: number; diff --git a/src/utils/index.ts b/src/utils/index.ts index f229781..5522ab7 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,4 @@ export * from './routes'; export * from './date'; -export * from './person'; \ No newline at end of file +export * from './person'; +export * from './media'; \ No newline at end of file diff --git a/src/utils/media.ts b/src/utils/media.ts new file mode 100644 index 0000000..3831ec7 --- /dev/null +++ b/src/utils/media.ts @@ -0,0 +1,13 @@ +import { ENV_CONSTANT } from '@/constants/secrect.constant'; + +export function resolveMediaUrl(url?: string | null) { + if (!url?.trim()) return null; + + const value = url.trim(); + if (/^https?:\/\//i.test(value)) return value; + + const baseUrl = ENV_CONSTANT.BASE_API_URL?.replace(/\/$/, ''); + const path = value.startsWith('/') ? value : `/${value}`; + + return baseUrl ? `${baseUrl}${path}` : path; +}