setup husky prettier eslint
This commit is contained in:
@@ -1,158 +1,172 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from "react"
|
||||
import dynamic from "next/dynamic"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Loader2, Milestone } from "lucide-react"
|
||||
import { Detection } from "@/types"
|
||||
import { useEffect, useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Loader2, Milestone } from 'lucide-react';
|
||||
import { Detection } from '@/types';
|
||||
|
||||
// Dynamically import the map to avoid SSR issues with Leaflet
|
||||
const DashboardMapContent = dynamic(
|
||||
() => import("./dashboard-map-content"),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="h-full w-full flex items-center justify-center">
|
||||
<Loader2 className="h-8 w-8 text-primary" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
const DashboardMapContent = dynamic(() => import('./dashboard-map-content'), {
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="h-full w-full flex items-center justify-center">
|
||||
<Loader2 className="h-8 w-8 text-primary" />
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
||||
interface DashboardMapProps {
|
||||
className?: string
|
||||
selectedProjectId?: string | null
|
||||
selectedPackageId?: string | null
|
||||
selectedChainageId?: string | null
|
||||
projectSummary?: any
|
||||
className?: string;
|
||||
selectedProjectId?: string | null;
|
||||
selectedPackageId?: string | null;
|
||||
selectedChainageId?: string | null;
|
||||
projectSummary?: any;
|
||||
}
|
||||
|
||||
export function DashboardMap({
|
||||
className,
|
||||
selectedProjectId,
|
||||
selectedPackageId,
|
||||
selectedChainageId,
|
||||
projectSummary
|
||||
className,
|
||||
selectedProjectId,
|
||||
selectedPackageId,
|
||||
selectedChainageId,
|
||||
projectSummary,
|
||||
}: DashboardMapProps) {
|
||||
const [detections, setDetections] = useState<Detection[]>([])
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [detections, setDetections] = useState<Detection[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectSummary) {
|
||||
setDetections([])
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
|
||||
const filteredDetections: Detection[] = []
|
||||
|
||||
const packagesToProcess = selectedPackageId && selectedPackageId !== "all"
|
||||
? { [selectedPackageId]: projectSummary.packages[selectedPackageId] }
|
||||
: projectSummary.packages || {}
|
||||
|
||||
for (const [pkgName, pkg] of Object.entries(packagesToProcess)) {
|
||||
const chainagesToProcess = selectedChainageId && selectedChainageId !== "all"
|
||||
? { [selectedChainageId]: (pkg as any).chainages[selectedChainageId] }
|
||||
: (pkg as any).chainages || {}
|
||||
|
||||
for (const [chnName, chn] of Object.entries(chainagesToProcess)) {
|
||||
if (!chn) continue
|
||||
const chainageDetections = (chn as any).detections || []
|
||||
filteredDetections.push(...chainageDetections)
|
||||
}
|
||||
}
|
||||
|
||||
setDetections(filteredDetections)
|
||||
} catch (err) {
|
||||
console.error("Failed to extract detections:", err)
|
||||
setError("Failed to load detection data")
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}, [projectSummary, selectedPackageId, selectedChainageId])
|
||||
|
||||
// Filter detections with valid GPS coordinates
|
||||
const validDetections = detections.filter(d => d.latitude && d.longitude)
|
||||
|
||||
// Count detections by category
|
||||
const counts = {
|
||||
defected_sign_board: validDetections.filter(d => d.type?.toLowerCase() === "defected_sign_board").length,
|
||||
pothole: validDetections.filter(d => d.type?.toLowerCase() === "pothole").length,
|
||||
road_crack: validDetections.filter(d => d.type?.toLowerCase() === "road_crack").length,
|
||||
damaged_road_marking: validDetections.filter(d => d.type?.toLowerCase() === "damaged_road_marking").length,
|
||||
good_sign_board: validDetections.filter(d => d.type?.toLowerCase() === "good_sign_board").length
|
||||
useEffect(() => {
|
||||
if (!projectSummary) {
|
||||
setDetections([]);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={`overflow-hidden ${className}`}>
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-md bg-secondary text-secondary-foreground flex items-center justify-center">
|
||||
<Milestone className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-base font-bold">
|
||||
Detection Map
|
||||
</CardTitle>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{isLoading ? "Loading..." : `${validDetections.length} detections with GPS coordinates`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
{/* Compact Color Legend */}
|
||||
<div className="flex flex-wrap items-center justify-end gap-x-4 gap-y-1 text-[10px] max-w-[60%]">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-red-500" />
|
||||
<span className="text-muted-foreground font-medium">Potholes ({counts.pothole})</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-blue-500" />
|
||||
<span className="text-muted-foreground font-medium">Defected Signs ({counts.defected_sign_board})</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-amber-500" />
|
||||
<span className="text-muted-foreground font-medium">Cracks ({counts.road_crack})</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-indigo-500" />
|
||||
<span className="text-muted-foreground font-medium">Markings ({counts.damaged_road_marking})</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-emerald-500" />
|
||||
<span className="text-muted-foreground font-medium">Good Signs ({counts.good_sign_board})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
<div className="h-[500px] p-2 w-full relative">
|
||||
{isLoading ? (
|
||||
<div className="h-full w-full flex items-center justify-center bg-muted/50">
|
||||
<Loader2 className="h-8 w-8 text-primary animate-spin" />
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="h-full w-full flex items-center justify-center bg-muted/50">
|
||||
<p className="text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
) : validDetections.length === 0 ? (
|
||||
<div className="h-full w-full flex items-center justify-center bg-muted/50">
|
||||
<div className="text-center">
|
||||
<p className="text-muted-foreground">No detections with GPS coordinates found</p>
|
||||
<p className="text-sm text-muted-foreground mt-1 opacity-70">Process some videos to see detections on the map</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<DashboardMapContent detections={validDetections} />
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
const filteredDetections: Detection[] = [];
|
||||
|
||||
const packagesToProcess =
|
||||
selectedPackageId && selectedPackageId !== 'all'
|
||||
? { [selectedPackageId]: projectSummary.packages[selectedPackageId] }
|
||||
: projectSummary.packages || {};
|
||||
|
||||
for (const [pkgName, pkg] of Object.entries(packagesToProcess)) {
|
||||
const chainagesToProcess =
|
||||
selectedChainageId && selectedChainageId !== 'all'
|
||||
? { [selectedChainageId]: (pkg as any).chainages[selectedChainageId] }
|
||||
: (pkg as any).chainages || {};
|
||||
|
||||
for (const [chnName, chn] of Object.entries(chainagesToProcess)) {
|
||||
if (!chn) continue;
|
||||
const chainageDetections = (chn as any).detections || [];
|
||||
filteredDetections.push(...chainageDetections);
|
||||
}
|
||||
}
|
||||
|
||||
setDetections(filteredDetections);
|
||||
} catch (err) {
|
||||
console.error('Failed to extract detections:', err);
|
||||
setError('Failed to load detection data');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [projectSummary, selectedPackageId, selectedChainageId]);
|
||||
|
||||
// Filter detections with valid GPS coordinates
|
||||
const validDetections = detections.filter((d) => d.latitude && d.longitude);
|
||||
|
||||
// Count detections by category
|
||||
const counts = {
|
||||
defected_sign_board: validDetections.filter(
|
||||
(d) => d.type?.toLowerCase() === 'defected_sign_board',
|
||||
).length,
|
||||
pothole: validDetections.filter((d) => d.type?.toLowerCase() === 'pothole').length,
|
||||
road_crack: validDetections.filter((d) => d.type?.toLowerCase() === 'road_crack').length,
|
||||
damaged_road_marking: validDetections.filter(
|
||||
(d) => d.type?.toLowerCase() === 'damaged_road_marking',
|
||||
).length,
|
||||
good_sign_board: validDetections.filter((d) => d.type?.toLowerCase() === 'good_sign_board')
|
||||
.length,
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className={`overflow-hidden ${className}`}>
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-md bg-secondary text-secondary-foreground flex items-center justify-center">
|
||||
<Milestone className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-base font-bold">Detection Map</CardTitle>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{isLoading
|
||||
? 'Loading...'
|
||||
: `${validDetections.length} detections with GPS coordinates`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Compact Color Legend */}
|
||||
<div className="flex flex-wrap items-center justify-end gap-x-4 gap-y-1 text-[10px] max-w-[60%]">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-red-500" />
|
||||
<span className="text-muted-foreground font-medium">Potholes ({counts.pothole})</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-blue-500" />
|
||||
<span className="text-muted-foreground font-medium">
|
||||
Defected Signs ({counts.defected_sign_board})
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-amber-500" />
|
||||
<span className="text-muted-foreground font-medium">
|
||||
Cracks ({counts.road_crack})
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-indigo-500" />
|
||||
<span className="text-muted-foreground font-medium">
|
||||
Markings ({counts.damaged_road_marking})
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-emerald-500" />
|
||||
<span className="text-muted-foreground font-medium">
|
||||
Good Signs ({counts.good_sign_board})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
<div className="h-[500px] p-2 w-full relative">
|
||||
{isLoading ? (
|
||||
<div className="h-full w-full flex items-center justify-center bg-muted/50">
|
||||
<Loader2 className="h-8 w-8 text-primary animate-spin" />
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="h-full w-full flex items-center justify-center bg-muted/50">
|
||||
<p className="text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
) : validDetections.length === 0 ? (
|
||||
<div className="h-full w-full flex items-center justify-center bg-muted/50">
|
||||
<div className="text-center">
|
||||
<p className="text-muted-foreground">No detections with GPS coordinates found</p>
|
||||
<p className="text-sm text-muted-foreground mt-1 opacity-70">
|
||||
Process some videos to see detections on the map
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<DashboardMapContent detections={validDetections} />
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user