diff --git a/src/app/(modules)/results/[videoId]/page.tsx b/src/app/(modules)/results/[videoId]/page.tsx index 0dcbd2a..24fb584 100644 --- a/src/app/(modules)/results/[videoId]/page.tsx +++ b/src/app/(modules)/results/[videoId]/page.tsx @@ -12,6 +12,7 @@ import { SessionContext, DetectionData, DetectionType } from '@/types'; import { getVideoFile, clearVideoFile } from '@/lib/video-storage'; import { ROUTES } from '@/utils/routes'; import { Card } from '@/components/ui/card'; +import { getDetectionModeConfig } from '@/constants/detectionModeConfig'; const API_URL = process.env.NEXT_PUBLIC_API_URL; @@ -36,7 +37,12 @@ export default function VideoResultsPage() { setDetectionData(data as any); // Try to infer detection type from results if possible - if (data.summary?.unique_signboards !== undefined && data.summary?.unique_signboards > 0) { + if (data.detection_mode) { + setDetectionType(data.detection_mode); + } else if ( + data.summary?.unique_signboards !== undefined && + data.summary?.unique_signboards > 0 + ) { setDetectionType('sign-board-detection'); } else if ( data.summary?.unique_potholes !== undefined && @@ -75,9 +81,8 @@ export default function VideoResultsPage() { }; const getTitle = () => { - if (detectionType === 'pothole-detection') return 'Pothole Detection Results'; - if (detectionType === 'sign-board-detection') return 'Signboard Detection Results'; - return 'Pothole & Signboard Detection Results'; + const config = getDetectionModeConfig(detectionType); + return `${config.label} Results`; }; if (isLoading) { diff --git a/src/app/(modules)/upload/[videoId]/page.tsx b/src/app/(modules)/upload/[videoId]/page.tsx index f92acf8..2e7f650 100644 --- a/src/app/(modules)/upload/[videoId]/page.tsx +++ b/src/app/(modules)/upload/[videoId]/page.tsx @@ -35,10 +35,10 @@ export default function VideoProcessingPage() { if (data.type === 'progress' || data.progress !== undefined) { setProgress(data.progress || 0); let message = data.message || 'Processing...'; - if (data.unique_potholes !== undefined) { - message += ` | Unique: ${data.unique_potholes} | Total: ${data.total_detections || 0}`; - } else if (data.unique_signboards !== undefined) { - message += ` | Unique: ${data.unique_signboards} | Total: ${data.total_detections || 0}`; + const uniqueCount = + data.unique_potholes ?? data.unique_signboards ?? data.unique_culverts; + if (uniqueCount !== undefined) { + message += ` | Unique: ${uniqueCount} | Total: ${data.total_detections || 0}`; } setStatusMessage(message); } diff --git a/src/components/map-modal.tsx b/src/components/map-modal.tsx index 69405cd..07e8536 100644 --- a/src/components/map-modal.tsx +++ b/src/components/map-modal.tsx @@ -5,6 +5,8 @@ import { MapContainer, TileLayer, Polyline, CircleMarker, Popup, useMap } from ' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { LatLngBounds, LatLng } from 'leaflet'; import 'leaflet/dist/leaflet.css'; +import { getDetectionModeConfig, DETECTION_TYPES } from '@/constants/detectionModeConfig'; +import { DetectionType } from '@/types'; type Detection = { id: number; @@ -20,7 +22,7 @@ type MapModalProps = { open: boolean; onClose: () => void; detections: Detection[]; - detectionType: 'pothole-detection' | 'sign-board-detection' | 'pot-sign-detection'; + detectionType: DetectionType | string; }; // Component to auto-fit map bounds to show all markers @@ -77,16 +79,13 @@ export default function MapModal({ open, onClose, detections, detectionType }: M (bounds.getEast() + bounds.getWest()) / 2, ]; - const isCombined = detectionType === 'pot-sign-detection'; - const isPothole = detectionType === 'pothole-detection'; + const modeConfig = getDetectionModeConfig(detectionType); return ( - - {isCombined ? 'Combined' : isPothole ? 'Pothole' : 'Signboard'} Detection Map - + {modeConfig.label} Map

{validDetections.length} points of interest identified with GPS data

@@ -103,14 +102,10 @@ export default function MapModal({ open, onClose, detections, detectionType }: M {validDetections.map((detection, idx) => { const type = (detection.type || '').toLowerCase(); - const colors: Record = { - pothole: { fill: '#ef4444', stroke: '#b91c1c' }, - defected_sign_board: { fill: '#3b82f6', stroke: '#1d4ed8' }, - road_crack: { fill: '#f59e0b', stroke: '#b45309' }, - damaged_road_marking: { fill: '#6366f1', stroke: '#4338ca' }, - good_sign_board: { fill: '#10b981', stroke: '#047857' }, - }; - const color = colors[type] || { fill: '#64748b', stroke: '#475569' }; + const typeConfig = DETECTION_TYPES[type]; + const color = typeConfig + ? { fill: typeConfig.color, stroke: typeConfig.color } + : { fill: '#64748b', stroke: '#475569' }; return ( (null); const [lastDetectedLat, setLastDetectedLat] = useState(null); const [lastDetectedLng, setLastDetectedLng] = useState(null); - const [currentFrameCounts, setCurrentFrameCounts] = useState({ - defected_sign_board: 0, - pothole: 0, - road_crack: 0, - damaged_road_marking: 0, - good_sign_board: 0, - }); + const detectionMode = data.detection_mode || detectionType; + const modeConfig = getDetectionModeConfig(detectionMode); + const enabledTypes = getEnabledDetectionTypes(detectionMode); + + const initialCounts = useMemo(() => { + const counts: any = {}; + enabledTypes.forEach((type) => { + counts[type.frameCountKey] = 0; + }); + return counts as DetectionCounts; + }, [enabledTypes]); + + const [currentFrameCounts, setCurrentFrameCounts] = useState(initialCounts); // Logs Reducer const [logs, dispatchLogs] = useReducer(logsReducer, []); @@ -217,6 +224,7 @@ export default function VideoPlayerSection({ currentFrame={currentFrame} lastDetectedLat={lastDetectedLat} lastDetectedLng={lastDetectedLng} + detectionMode={detectionMode} /> diff --git a/src/components/video/detailed-summary-section.tsx b/src/components/video/detailed-summary-section.tsx index 79f825d..f2834d9 100644 --- a/src/components/video/detailed-summary-section.tsx +++ b/src/components/video/detailed-summary-section.tsx @@ -7,8 +7,9 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com import { ScrollArea } from '@/components/ui/scroll-area'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; -import { DetectionType, ChainageSummaryData } from '@/types'; +import { ChainageSummaryData } from '@/types'; import { projectService } from '@/services/api'; +import { getDetectionModeConfig } from '@/constants/detectionModeConfig'; // Dynamically import MapModal with SSR disabled (Leaflet requires window object) const MapModal = dynamic(() => import('@/components/map-modal'), { ssr: false }); @@ -17,7 +18,7 @@ interface DetailedSummarySectionProps { projectId: string; videoId: string; show: boolean; - detectionType: DetectionType; + detectionType: string; } const DetailedSummarySection = ({ @@ -50,8 +51,7 @@ const DetailedSummarySection = ({ if (!show || loading || !summaryData) return null; - const isCombined = detectionType === 'pot-sign-detection'; - const isPothole = detectionType === 'pothole-detection' || isCombined; + const modeConfig = getDetectionModeConfig(detectionType); // Flatten all detections for the scrollable list const allDetections: Array<{ @@ -79,10 +79,7 @@ const DetailedSummarySection = ({
Chainages - - {isCombined ? 'Potholes & Signboards' : isPothole ? 'Potholes' : 'Signboards'}{' '} - detected - + {modeConfig.label} detected