From 7b1682ab7a8be1c87451df18ec59f7682110930a Mon Sep 17 00:00:00 2001 From: sumona-banerjeee Date: Thu, 5 Feb 2026 16:21:07 +0530 Subject: [PATCH] Added extra summary --- app/results/page.tsx | 1 + components/video-player-section.tsx | 214 +++++++++++++++++++++++++--- 2 files changed, 197 insertions(+), 18 deletions(-) diff --git a/app/results/page.tsx b/app/results/page.tsx index 0599d4d..97ef8f2 100644 --- a/app/results/page.tsx +++ b/app/results/page.tsx @@ -229,6 +229,7 @@ export default function ResultsPage() { videoId={videoId} videoFile={videoFile} detectionType={detectionType} + projectId={session?.projectId || undefined} /> )} diff --git a/components/video-player-section.tsx b/components/video-player-section.tsx index 8c385bf..94487c1 100644 --- a/components/video-player-section.tsx +++ b/components/video-player-section.tsx @@ -75,6 +75,7 @@ type VideoPlayerSectionProps = { videoId: string videoFile: File | null // null when loading from server (results page) detectionType: DetectionType + projectId?: string // Optional project ID for fetching detailed summary } type DetectionLog = { @@ -90,6 +91,184 @@ type DetectionLog = { videoTime: string // Video timestamp (MM:SS) } +type LocationSummaryData = { + project: { + id: string + name: string + corridor_name: string | null + state: string | null + } + packages: { + [packageName: string]: { + package_id: string + region: string | null + locations: { + [locationName: string]: { + location_id: string + chainage: string | null + detection_count: number + detections: Array<{ + id: number + video_id: string + type: string + class: string + confidence: number + latitude: number + longitude: number + frame_number: number + timestamp_ms: number + bounding_box: { + x1: number + y1: number + x2: number + y2: number + } + }> + } + } + } + } +} + +function DetailedSummarySection({ + projectId, + videoId, + show, + detectionType +}: { + projectId: string + videoId: string + show: boolean + detectionType: DetectionType +}) { + const [summaryData, setSummaryData] = useState(null) + const [loading, setLoading] = useState(false) + + useEffect(() => { + if (!show || !videoId || !projectId) return + + const fetchSummary = async () => { + setLoading(true) + try { + const response = await fetch(`${API_URL}/summary/projects/${projectId}?video_id=${videoId}`, { + headers: { "ngrok-skip-browser-warning": "true" } + }) + if (response.ok) { + const data = await response.json() + setSummaryData(data) + } else { + console.error(`Failed to fetch summary: ${response.status}`) + } + } catch (err) { + console.error("Failed to fetch summary:", err) + } finally { + setLoading(false) + } + } + + fetchSummary() + }, [show, videoId, projectId]) + + if (!show || loading || !summaryData) return null + + const isPothole = detectionType === "pothole-detection" + + // Flatten all detections for the scrollable list + const allDetections: Array<{ + detection: any + locationName: string + packageName: string + }> = [] + + Object.entries(summaryData.packages).forEach(([packageName, packageData]) => { + Object.entries(packageData.locations).forEach(([locationName, locationData]) => { + locationData.detections.forEach(detection => { + allDetections.push({ detection, locationName, packageName }) + }) + }) + }) + + return ( +
+ {/* Location-based Summary */} + + + Detection Locations + + {isPothole ? "Potholes" : "Signboards"} detected across project locations + + + +
+ {/* Project Info */} +
+
Project: {summaryData.project.name}
+ {summaryData.project.corridor_name && ( +
Corridor: {summaryData.project.corridor_name}
+ )} +
+ + {/* Packages and Locations */} + {Object.entries(summaryData.packages).map(([packageName, packageData]) => ( +
+
{packageName}
+
+ {Object.entries(packageData.locations).map(([locationName, locationData]) => ( +
+
{locationName}
+
+ {locationData.detection_count} {isPothole ? "pothole" : "signboard"}{locationData.detection_count !== 1 ? "s" : ""} detected +
+
+ ))} +
+
+ ))} +
+
+
+ + {/* All Detections List */} + + + All Detections + + Complete list of {isPothole ? "potholes" : "signboards"} with GPS coordinates + + + + +
+ {allDetections.map(({ detection, locationName, packageName }, idx) => ( +
+
+ + {isPothole ? "Pothole" : detection.class.replace(/_/g, " ")} #{detection.id} + + + Frame {detection.frame_number} + +
+
+
Location: {locationName}
+
Confidence: {(detection.confidence * 100).toFixed(1)}%
+
+ GPS: {detection.latitude}, {detection.longitude} +
+
+
+ ))} +
+
+
+
+
+ ) +} + function SummarySection({ data, show, detectionType }: { data: DetectionData; show: boolean; detectionType: DetectionType }) { if (!show) return null @@ -143,27 +322,27 @@ function SummarySection({ data, show, detectionType }: { data: DetectionData; sh return ( - - Detection Summary - + + Quick Stats + Overview of {isPothole ? "pothole" : "signboard"} detection results -
+
{stats.map((stat, index) => { const Icon = stat.icon return (
-
- +
+
-
{stat.value}
-
{stat.label}
+
{stat.value}
+
{stat.label}
) })} @@ -173,7 +352,7 @@ function SummarySection({ data, show, detectionType }: { data: DetectionData; sh ) } -export default function VideoPlayerSection({ data, videoId, videoFile, detectionType }: VideoPlayerSectionProps) { +export default function VideoPlayerSection({ data, videoId, videoFile, detectionType, projectId }: VideoPlayerSectionProps) { const videoRef = useRef(null) const canvasRef = useRef(null) const containerRef = useRef(null) @@ -635,12 +814,6 @@ export default function VideoPlayerSection({ data, videoId, videoFile, detection Current Frame: {currentFrame}
-
- Detections: - 0 ? (isPothole ? "destructive" : "default") : "secondary"}> - {detectionsCount} - -
FPS: {data.video_info.fps.toFixed(1)} @@ -727,8 +900,13 @@ export default function VideoPlayerSection({ data, videoId, videoFile, detection - {/* Summary Section - Shows after first complete playback and persists */} - + {/* Summary Sections - Show after first complete playback and persist */} + {showSummary && ( +
+ + +
+ )}
) } \ No newline at end of file