refactor : restructure types

This commit is contained in:
2026-03-18 17:09:58 +05:30
parent f35389ab69
commit 7f8854ed78
31 changed files with 629 additions and 259 deletions

View File

@@ -20,7 +20,8 @@ import { DetectionDonutChart } from "@/components/dashboard/detection-donut-char
import { LocationBarChart } from "@/components/dashboard/location-bar-chart";
import { DashboardMap } from "@/components/dashboard/dashboard-map";
import { PageHeader } from "@/components/page-header";
import { fetchProjects, type Project } from "@/lib/api";
import { fetchProjects } from "@/lib/api";
import { Project } from "@/types";
import {
Popover,
PopoverContent,

View File

@@ -18,13 +18,15 @@ import {
fetchAllPackages,
createLocation,
updateLocation,
deleteLocation,
type Project,
type Package as PackageType,
type Location,
type LocationCreate,
type LocationUpdate
deleteLocation
} from "@/lib/api"
import {
Project,
Package as PackageType,
Location,
LocationCreate,
LocationUpdate
} from "@/types"
import { ColumnDef } from "@tanstack/react-table"
import { toast } from "sonner"
import { Badge } from "@/components/ui/badge"

View File

@@ -2,7 +2,8 @@
import { useRouter } from "next/navigation"
import dynamic from "next/dynamic"
import { type SessionContext, saveSession } from "@/lib/api"
import { saveSession } from "@/lib/api"
import { SessionContext } from "@/types"
import { PowerCircle, TrendingUp } from "lucide-react"
import { PageHeader } from "@/components/page-header"
import { PoweredBy } from "@/components/powered-by"

View File

@@ -15,12 +15,14 @@ import {
fetchAllPackages,
createPackage,
updatePackage,
deletePackage,
type Project,
type Package as PackageType,
type PackageCreate,
type PackageUpdate
deletePackage
} from "@/lib/api"
import {
Project,
Package as PackageType,
PackageCreate,
PackageUpdate
} from "@/types"
import { ColumnDef } from "@tanstack/react-table"
import { toast } from "sonner"
import { Badge } from "@/components/ui/badge"

View File

@@ -9,7 +9,8 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f
import { Loader2, CheckCircle2, Layers, MapPin, Building2, Route, X } from "lucide-react"
import { DataTable } from "@/components/data-table"
import { PageHeader } from "@/components/page-header"
import { createProject, fetchProjects, updateProject, deleteProject, type ProjectCreate, type Project, type ProjectUpdate } from "@/lib/api"
import { createProject, fetchProjects, updateProject, deleteProject } from "@/lib/api"
import { ProjectCreate, Project, ProjectUpdate } from "@/types"
import { ColumnDef } from "@tanstack/react-table"
import { toast } from "sonner"
import { Badge } from "@/components/ui/badge"

View File

@@ -8,12 +8,11 @@ import VideoPlayerSection from "@/components/video-player-section"
import { PageHeader } from "@/components/page-header"
import { PoweredBy } from "@/components/powered-by"
import {
type SessionContext,
loadSession,
clearSession
} from "@/lib/api"
import { SessionContext, DetectionData, DetectionType } from "@/types"
import { getVideoFile, clearVideoFile } from "@/lib/video-storage"
import { DetectionData, DetectionType } from "@/lib/types"
import { ROUTES } from "@/utils/routes"
import { Card } from "@/components/ui/card"

View File

@@ -8,14 +8,13 @@ import VideoPlayerSection from "@/components/video-player-section"
import { PageHeader } from "@/components/page-header"
import { PoweredBy } from "@/components/powered-by"
import {
type SessionContext,
loadSession,
loadVideoData,
isSessionValid,
clearSession
} from "@/lib/api"
import { SessionContext, DetectionData, DetectionType } from "@/types"
import { clearVideoFile } from "@/lib/video-storage"
import { DetectionData, DetectionType } from "@/lib/types"
import { ROUTES } from "@/utils/routes"
import { Card } from "@/components/ui/card"

View File

@@ -7,9 +7,9 @@ import { Loader2, TrendingUp } from "lucide-react"
import { PageHeader } from "@/components/page-header"
import { PoweredBy } from "@/components/powered-by"
import {
type SessionContext,
loadSession,
} from "@/lib/api"
import { SessionContext } from "@/types"
import { ROUTES } from "@/utils/routes"
import { Button } from "@/components/ui/button"

View File

@@ -11,12 +11,12 @@ import { Loader2, TrendingUp } from "lucide-react"
import { PageHeader } from "@/components/page-header"
import { PoweredBy } from "@/components/powered-by"
import {
type SessionContext,
loadSession,
isSessionValid,
saveVideoData,
clearSession
} from "@/lib/api"
import { SessionContext } from "@/types"
import { ROUTES } from "@/utils/routes"
import { storeVideoFile } from "@/lib/video-storage"
import { cn } from "@/lib/utils"

View File

@@ -7,7 +7,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { type Project } from "@/lib/api"
import { Project } from "@/types"
interface CompactProjectSelectorProps {
projects: Project[]

View File

@@ -4,7 +4,7 @@ import { useEffect } from "react"
import { MapContainer, TileLayer, CircleMarker, Popup, useMap } from "react-leaflet"
import { LatLngBounds, LatLng } from "leaflet"
import "leaflet/dist/leaflet.css"
import { type Detection } from "@/lib/api"
import { Detection } from "@/types"
interface DashboardMapContentProps {
detections: Detection[]

View File

@@ -4,7 +4,7 @@ import { useEffect, useState } from "react"
import dynamic from "next/dynamic"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Loader2, MapPin } from "lucide-react"
import { type Detection } from "@/lib/api"
import { Detection } from "@/types"
// Dynamically import the map to avoid SSR issues with Leaflet
const DashboardMapContent = dynamic(

View File

@@ -8,7 +8,7 @@ import {
SelectValue,
} from "@/components/ui/select"
import { FolderOpen, Package, MapPin } from "lucide-react"
import { type Project } from "@/lib/api"
import { Project } from "@/types"
interface FilterSelectorProps {
projects: Project[]

View File

@@ -10,11 +10,13 @@ import {
fetchProjects,
fetchPackagesByProject,
fetchLocationsByPackage,
type Project,
type Package as PackageType,
type Location,
type SessionContext
} from "@/lib/api"
import {
Project,
Package as PackageType,
Location,
SessionContext
} from "@/types"
import { cn } from "@/lib/utils"
type ProjectSelectionSectionProps = {

View File

@@ -8,7 +8,7 @@ import { Label } from "@/components/ui/label"
import { Progress } from "@/components/ui/progress"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Upload, Loader2, AlertCircle } from "lucide-react"
import type { DetectionData, DetectionType } from "@/lib/types"
import type { DetectionData, DetectionType } from "@/types"
const API_URL = process.env.NEXT_PUBLIC_API_URL
const WS_URL = API_URL?.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://")

View File

@@ -11,7 +11,7 @@ import { Target, AlertTriangle, Film, Activity, Gauge, Monitor, SignpostBig, Map
// Dynamically import MapModal with SSR disabled (Leaflet requires window object)
const MapModal = dynamic(() => import("@/components/map-modal"), { ssr: false })
import { DetectionData, DetectionType } from "@/lib/types"
import { DetectionData, DetectionType } from "@/types"
import { cn } from "@/lib/utils"
const API_URL = process.env.NEXT_PUBLIC_API_URL

View File

@@ -0,0 +1,3 @@
export const ENV_CONSTANT = {
BASE_API_URL: process.env.NEXT_PUBLIC_API_URL,
};

View File

@@ -6,52 +6,16 @@
const API_URL = process.env.NEXT_PUBLIC_API_URL
// Type definitions
export interface Project {
id: string
name: string
state: string | null
corridor_name: string | null
start_lat: number | null
start_lng: number | null
end_lat: number | null
end_lng: number | null
created_at: string
updated_at: string
}
import {
Project, ProjectCreate, ProjectUpdate,
Package, PackageCreate, PackageUpdate,
Location, LocationCreate, LocationUpdate,
PaginatedResponse, PaginationParams,
Video, VideoResultData,
Detection,
SessionContext, emptySessionContext
} from "@/types"
export interface Package {
id: string
project_id: string
name: string
region: string | null
created_at: string
updated_at: string
}
export interface Location {
id: string
package_id: string
segment_name: string
chainage_start_km: number | null
chainage_end_km: number | null
start_lat: number
start_lng: number
end_lat: number
end_lng: number
created_at: string
updated_at: string
}
export interface PaginatedResponse<T> {
items: T[]
totalItems: number
}
export interface PaginationParams {
skip?: number;
limit?: number;
}
// Helper function for GET API requests
async function apiRequest<T>(endpoint: string): Promise<T> {
@@ -88,60 +52,6 @@ async function apiPostRequest<T>(endpoint: string, body: unknown): Promise<T> {
return response.json()
}
// Create request body types
export interface ProjectCreate {
name: string
state?: string | null
corridor_name?: string | null
start_lat?: number | null
start_lng?: number | null
end_lat?: number | null
end_lng?: number | null
}
export interface PackageCreate {
project_id: string
name: string
region?: string | null
}
export interface LocationCreate {
package_id: string
segment_name: string
chainage_start_km?: number | null
chainage_end_km?: number | null
start_lat: number
start_lng: number
end_lat: number
end_lng: number
}
// Update request body types
export interface ProjectUpdate {
name?: string
state?: string | null
corridor_name?: string | null
start_lat?: number | null
start_lng?: number | null
end_lat?: number | null
end_lng?: number | null
}
export interface PackageUpdate {
name?: string
region?: string | null
}
export interface LocationUpdate {
segment_name?: string
chainage_start_km?: number | null
chainage_end_km?: number | null
start_lat?: number
start_lng?: number
end_lat?: number
end_lng?: number
}
// Helper function for PUT API requests
async function apiPutRequest<T>(endpoint: string, body: unknown): Promise<T> {
const response = await fetch(`${API_URL}${endpoint}`, {
@@ -293,23 +203,7 @@ export async function fetchAllLocations(params?: PaginationParams): Promise<Pagi
return apiRequest<PaginatedResponse<Location>>(`/locations/?skip=${skip}&limit=${limit}`)
}
// Video type for dashboard
export interface Video {
id: string
filename: string
detection_type: "pothole-detection" | "sign-board-detection" | "pot-sign-detection"
status: "pending" | "processing" | "completed" | "failed"
unique_defected_sign_board?: number
unique_pothole?: number
unique_road_crack?: number
unique_damaged_road_marking?: number
unique_good_sign_board?: number
total_road_damage?: number
total_detections?: number
created_at: string
updated_at: string
}
// Video functions
/**
* Fetch all videos (for dashboard)
*/
@@ -351,18 +245,7 @@ export async function fetchVideos(params?: PaginationParams): Promise<Video[]> {
}))
}
// Detection type for map display
export interface Detection {
id: number
video_id: string
type: string
class: string
confidence: number
latitude: number | null
longitude: number | null
frame_number: number
timestamp_ms: number
}
// Detection specific functions
/**
* Fetch all detections from completed videos (for dashboard map)
@@ -410,25 +293,8 @@ export async function fetchAllDetections(): Promise<Detection[]> {
}
/**
* Session context type for storing user selections
* Session storage utility functions
*/
export interface SessionContext {
projectId: string | null
projectName: string | null
packageId: string | null
packageName: string | null
locationId: string | null
locationName: string | null
}
export const emptySessionContext: SessionContext = {
projectId: null,
projectName: null,
packageId: null,
packageName: null,
locationId: null,
locationName: null
}
// Session Storage Keys
const SESSION_KEY = "visionroad_session"
@@ -468,14 +334,6 @@ export function clearSession(): void {
}
}
/**
* Video data for results page
*/
export interface VideoResultData {
videoId: string
detectionType: string
}
/**
* Save video result data
*/
@@ -485,6 +343,7 @@ export function saveVideoData(data: VideoResultData): void {
}
}
/**
* Load video result data
*/

View File

@@ -1,75 +0,0 @@
export type DetectionType = "pothole-detection" | "sign-board-detection" | "pot-sign-detection"
export interface DetectionListItem {
detection_id?: number
pothole_id?: number
signboard_id?: number
type: string
first_detected_frame: number
first_detected_time: number
confidence: number
bbox?: { x1: number; y1: number; x2: number; y2: number }
lat?: number
lng?: number
}
export type DetectionData = {
video_id: string
detection_type?: string
output_video_path?: string
video_info: {
fps: number
width: number
height: number
total_frames: number
}
summary: {
unique_defected_sign_board?: number
unique_pothole?: number
unique_road_crack?: number
unique_damaged_road_marking?: number
unique_good_sign_board?: number
total_road_damage?: number
total_detections: number
total_frames: number
detection_rate: number
}
pothole_list?: Array<DetectionListItem>
defected_sign_board_list?: Array<DetectionListItem>
road_crack_list?: Array<DetectionListItem>
damaged_road_marking_list?: Array<DetectionListItem>
good_sign_board_list?: Array<DetectionListItem>
signboard_list?: Array<DetectionListItem> // Keeping for backward compatibility
frames: Array<{
frame_id: number
// Legacy format: separate arrays
potholes?: Array<{
pothole_id: number
bbox: { x1: number; y1: number; x2: number; y2: number }
confidence: number
}>
signboards?: Array<{
signboard_id: number
type: string
bbox: { x1: number; y1: number; x2: number; y2: number }
confidence: number
}>
// Flat format (pot-sign-detection): unified detections array
detections?: Array<{
frame_id: number
detection_id: number
type: string
confidence: number
bbox: { x1: number; y1: number; x2: number; y2: number }
center?: { x: number; y: number }
area?: number
count?: {
defected_sign_board: number
pothole: number
road_crack: number
damaged_road_marking: number
good_sign_board: number
}
}>
}>
}

View File

@@ -0,0 +1,44 @@
import { ENV_CONSTANT } from "@/constants/secrect.constant";
import axios from "axios";
const BASE_URL = ENV_CONSTANT.BASE_API_URL;
const axiosClient = axios.create({
baseURL: BASE_URL,
headers: {
// "Content-Type": "application/json",
"ngrok-skip-browser-warning": "true"
},
});
// Add request interceptor to inject access token
axiosClient.interceptors.request.use(
(config) => {
if (typeof window !== "undefined") {
const token = localStorage.getItem("token");
if (token && config.headers) {
config.headers["Authorization"] = `Bearer ${token}`;
}
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Basic response interceptor
axiosClient.interceptors.response.use(
(response) => response,
(error) => {
// Standard error handling can be added here later
return Promise.reject(error);
}
);
export const axiosAuth = axios.create({
baseURL: BASE_URL,
headers: { "Content-Type": "application/json" },
});
export default axiosClient;

65
src/types/analysis.ts Normal file
View File

@@ -0,0 +1,65 @@
import { DetectionListItem } from "./detection"
/**
* Analysis result types
*/
export type DetectionData = {
video_id: string
detection_type?: string
output_video_path?: string
video_info: {
fps: number
width: number
height: number
total_frames: number
}
summary: {
unique_defected_sign_board?: number
unique_pothole?: number
unique_road_crack?: number
unique_damaged_road_marking?: number
unique_good_sign_board?: number
total_road_damage?: number
total_detections: number
total_frames: number
detection_rate: number
}
pothole_list?: Array<DetectionListItem>
defected_sign_board_list?: Array<DetectionListItem>
road_crack_list?: Array<DetectionListItem>
damaged_road_marking_list?: Array<DetectionListItem>
good_sign_board_list?: Array<DetectionListItem>
signboard_list?: Array<DetectionListItem> // Keeping for backward compatibility
frames: Array<{
frame_id: number
// Legacy format: separate arrays
potholes?: Array<{
pothole_id: number
bbox: { x1: number; y1: number; x2: number; y2: number }
confidence: number
}>
signboards?: Array<{
signboard_id: number
type: string
bbox: { x1: number; y1: number; x2: number; y2: number }
confidence: number
}>
// Flat format (pot-sign-detection): unified detections array
detections?: Array<{
frame_id: number
detection_id: number
type: string
confidence: number
bbox: { x1: number; y1: number; x2: number; y2: number }
center?: { x: number; y: number }
area?: number
count?: {
defected_sign_board: number
pothole: number
road_crack: number
damaged_road_marking: number
good_sign_board: number
}
}>
}>
}

12
src/types/common.ts Normal file
View File

@@ -0,0 +1,12 @@
/**
* Common API pagination types
*/
export interface PaginatedResponse<T> {
items: T[]
totalItems: number
}
export interface PaginationParams {
skip?: number;
limit?: number;
}

29
src/types/detection.ts Normal file
View File

@@ -0,0 +1,29 @@
/**
* Detection types for map and display
*/
export interface Detection {
id: number
video_id: string
type: string
class: string
confidence: number
latitude: number | null
longitude: number | null
frame_number: number
timestamp_ms: number
}
export type DetectionType = "pothole-detection" | "sign-board-detection" | "pot-sign-detection"
export interface DetectionListItem {
detection_id?: number
pothole_id?: number
signboard_id?: number
type: string
first_detected_frame: number
first_detected_time: number
confidence: number
bbox?: { x1: number; y1: number; x2: number; y2: number }
lat?: number
lng?: number
}

8
src/types/index.ts Normal file
View File

@@ -0,0 +1,8 @@
export * from "./common"
export * from "./project"
export * from "./package"
export * from "./location"
export * from "./video"
export * from "./detection"
export * from "./analysis"
export * from "./session"

37
src/types/location.ts Normal file
View File

@@ -0,0 +1,37 @@
/**
* Location related types
*/
export interface Location {
id: string
package_id: string
segment_name: string
chainage_start_km: number | null
chainage_end_km: number | null
start_lat: number
start_lng: number
end_lat: number
end_lng: number
created_at: string
updated_at: string
}
export interface LocationCreate {
package_id: string
segment_name: string
chainage_start_km?: number | null
chainage_end_km?: number | null
start_lat: number
start_lng: number
end_lat: number
end_lng: number
}
export interface LocationUpdate {
segment_name?: string
chainage_start_km?: number | null
chainage_end_km?: number | null
start_lat?: number
start_lng?: number
end_lat?: number
end_lng?: number
}

22
src/types/package.ts Normal file
View File

@@ -0,0 +1,22 @@
/**
* Package related types
*/
export interface Package {
id: string
project_id: string
name: string
region: string | null
created_at: string
updated_at: string
}
export interface PackageCreate {
project_id: string
name: string
region?: string | null
}
export interface PackageUpdate {
name?: string
region?: string | null
}

35
src/types/project.ts Normal file
View File

@@ -0,0 +1,35 @@
/**
* Project related types
*/
export interface Project {
id: string
name: string
state: string | null
corridor_name: string | null
start_lat: number | null
start_lng: number | null
end_lat: number | null
end_lng: number | null
created_at: string
updated_at: string
}
export interface ProjectCreate {
name: string
state?: string | null
corridor_name?: string | null
start_lat?: number | null
start_lng?: number | null
end_lat?: number | null
end_lng?: number | null
}
export interface ProjectUpdate {
name?: string
state?: string | null
corridor_name?: string | null
start_lat?: number | null
start_lng?: number | null
end_lat?: number | null
end_lng?: number | null
}

20
src/types/session.ts Normal file
View File

@@ -0,0 +1,20 @@
/**
* Session related types for storing user selections
*/
export interface SessionContext {
projectId: string | null
projectName: string | null
packageId: string | null
packageName: string | null
locationId: string | null
locationName: string | null
}
export const emptySessionContext: SessionContext = {
projectId: null,
projectName: null,
packageId: null,
packageName: null,
locationId: null,
locationName: null
}

23
src/types/video.ts Normal file
View File

@@ -0,0 +1,23 @@
/**
* Video related types
*/
export interface Video {
id: string
filename: string
detection_type: "pothole-detection" | "sign-board-detection" | "pot-sign-detection"
status: "pending" | "processing" | "completed" | "failed"
unique_defected_sign_board?: number
unique_pothole?: number
unique_road_crack?: number
unique_damaged_road_marking?: number
unique_good_sign_board?: number
total_road_damage?: number
total_detections?: number
created_at: string
updated_at: string
}
export interface VideoResultData {
videoId: string
detectionType: string
}