feat(api): restructure api with axios and chainage related changes

This commit is contained in:
2026-03-18 18:42:40 +05:30
parent 7f8854ed78
commit 598098d7e8
31 changed files with 933 additions and 931 deletions

View File

@@ -14,7 +14,7 @@ const MapModal = dynamic(() => import("@/components/map-modal"), { ssr: false })
import { DetectionData, DetectionType } from "@/types"
import { cn } from "@/lib/utils"
const API_URL = process.env.NEXT_PUBLIC_API_URL
import { projectService } from "@/services/api"
type VideoPlayerSectionProps = {
data: DetectionData
@@ -37,7 +37,7 @@ type DetectionLog = {
videoTime: string // Video timestamp (MM:SS)
}
type LocationSummaryData = {
type ChainageSummaryData = {
project: {
id: string
name: string
@@ -48,9 +48,9 @@ type LocationSummaryData = {
[packageName: string]: {
package_id: string
region: string | null
locations: {
[locationName: string]: {
location_id: string
chainages: {
[chainageName: string]: {
chainage_id: string
chainage: string | null
detection_count: number
detections: Array<{
@@ -87,7 +87,7 @@ function DetailedSummarySection({
show: boolean
detectionType: DetectionType
}) {
const [summaryData, setSummaryData] = useState<LocationSummaryData | null>(null)
const [summaryData, setSummaryData] = useState<ChainageSummaryData | null>(null)
const [loading, setLoading] = useState(false)
const [showMap, setShowMap] = useState(false)
@@ -97,15 +97,8 @@ function DetailedSummarySection({
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}`)
}
const data = await projectService.getProjectSummaryByVideo(projectId, videoId);
setSummaryData(data)
} catch (err) {
console.error("Failed to fetch summary:", err)
} finally {
@@ -124,14 +117,14 @@ function DetailedSummarySection({
// Flatten all detections for the scrollable list
const allDetections: Array<{
detection: any
locationName: string
chainageName: 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 })
Object.entries(packageData?.chainages || {}).forEach(([chainageName, chainageData]) => {
chainageData?.detections?.forEach(detection => {
allDetections.push({ detection, chainageName, packageName })
})
})
})
@@ -147,7 +140,7 @@ function DetailedSummarySection({
</div>
<div>
<CardTitle className="text-base font-bold">
Locations
Chainages
</CardTitle>
<CardDescription className="text-xs">
{isCombined ? "Potholes & Signboards" : isPothole ? "Potholes" : "Signboards"} detected
@@ -186,16 +179,16 @@ function DetailedSummarySection({
)}
</div>
{/* Packages and Locations */}
{/* Packages and Chainages */}
{Object.entries(summaryData.packages).map(([packageName, packageData]) => (
<div key={packageData.package_id} className="space-y-2">
<div className="text-xs font-bold text-muted-foreground truncate">{packageName}</div>
<div className="pl-2 space-y-2 border-l-2 border-muted">
{Object.entries(packageData.locations).map(([locationName, locationData]) => (
<div key={locationData.location_id} className="text-xs p-3 rounded-md bg-muted/50 flex items-center justify-between gap-4">
<div className="font-semibold truncate">{locationName}</div>
{Object.entries(packageData.chainages).map(([chainageName, chainageData]) => (
<div key={chainageData.chainage_id} className="text-xs p-3 rounded-md bg-muted/50 flex items-center justify-between gap-4">
<div className="font-semibold truncate">{chainageName}</div>
<div className="text-[10px] bg-background px-2 py-0.5 rounded border font-bold">
{locationData.detection_count}
{chainageData.detection_count}
</div>
</div>
))}
@@ -226,7 +219,7 @@ function DetailedSummarySection({
<CardContent className="flex-1 p-0 overflow-hidden">
<ScrollArea className="h-[300px] p-4">
<div className="space-y-2">
{allDetections.map(({ detection, locationName }, idx) => (
{allDetections.map(({ detection, chainageName }, idx) => (
<div
key={`${detection.id}-${idx}`}
className="text-xs p-3 bg-muted/30 border rounded-md hover:bg-muted/50 transition-colors"
@@ -240,7 +233,7 @@ function DetailedSummarySection({
</Badge>
</div>
<div className="grid grid-cols-2 gap-2 text-[10px] text-muted-foreground">
<div className="truncate">Location: <span className="text-foreground font-medium">{locationName}</span></div>
<div className="truncate">Chainage: <span className="text-foreground font-medium">{chainageName}</span></div>
<div>Confidence: <span className="text-foreground font-medium">{(detection.confidence * 100).toFixed(1)}%</span></div>
<div className="col-span-2 font-mono bg-muted/50 p-1 rounded border">
GPS: {detection.latitude}, {detection.longitude}
@@ -512,7 +505,7 @@ export default function VideoPlayerSection({ data, videoId, videoFile, detection
useEffect(() => {
const video = videoRef.current
if (!video) return
const videoUrl = videoFile ? URL.createObjectURL(videoFile) : (videoId ? `${API_URL}/video/${videoId}` : '')
const videoUrl = videoFile ? URL.createObjectURL(videoFile) : (videoId ? `${process.env.NEXT_PUBLIC_API_URL}/video/${videoId}` : '')
if (videoUrl) {
video.src = videoUrl
video.onloadeddata = resizeCanvas