smart-ai-traffic-intel / frontend /src /hooks /useImageUpload.js
MR.HABITH
Fix main.py indentation error
bd29ae3
import { useState, useCallback, useRef } from 'react';
import { validateImageFiles } from '../utils/validators';
import { detectVehicles } from '../services/api';
/**
* Custom hook for image upload and vehicle detection.
* Returns:
* vehicleData β€” detection results array
* roadPreviews β€” array of [null | objectURL string] per road slot (length 4)
* loading, error, successMessage
* handleImageUpload(filesArray4) β€” takes array of 4 nullable File objects (indexed Road A-D)
* clearData
*/
export const useImageUpload = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [vehicleData, setVehicleData] = useState([]);
const [roadPreviews, setRoadPreviews] = useState([null, null, null, null]);
const [successMessage, setSuccessMessage] = useState(null);
const prevPreviewsRef = useRef([null, null, null, null]);
/**
* @param {Array<File|null>} filesArray - 4-element array (Road A, B, C, D), nulls for empty slots
*/
const handleImageUpload = useCallback(async (filesArray) => {
setError(null);
setSuccessMessage(null);
const nonNullFiles = filesArray.filter(Boolean);
if (nonNullFiles.length === 0) {
setError('Please upload at least one road image.');
return false;
}
// Basic validation on non-null files
const validation = validateImageFiles(nonNullFiles);
if (!validation.valid) {
setError(validation.errors.join('\n'));
return false;
}
setLoading(true);
try {
const formData = new FormData();
filesArray.forEach((file, index) => {
if (file) {
formData.append('roads', `Road ${String.fromCharCode(65 + index)}`);
formData.append('files', file);
}
});
const response = await detectVehicles(formData);
// Extract detections from response. The HF API returns { signalPlan: [...], totalCount: ... }
const detectionsArray = response.signalPlan || response.detections || response.results || response.data || [];
if (!detectionsArray || !Array.isArray(detectionsArray)) {
throw new Error('Invalid response structure from server');
}
// Map whatever the backend returned into the standard shape
const normalizedDetections = detectionsArray.map(r => ({
...r,
count: r.count !== undefined ? r.count : r.vehicleCount || 0,
// Insert a dummy array of 'car' so the UI doesn't say "No vehicles detected"
vehicles: r.vehicles || r.vehicleTypes || new Array(r.count || 0).fill('car'),
confidence: r.confidence || r.avgConfidence || 0
}));
setVehicleData(normalizedDetections);
// Build previews β€” match response.detections back to original slot positions
// Backend returns detections in order of images that were sent
// We need to map them back: build a map of road index β†’ detection
const detectionsBySlot = new Array(4).fill(null);
let detectionIdx = 0;
filesArray.forEach((file, slotIdx) => {
if (file && detectionIdx < normalizedDetections.length) {
detectionsBySlot[slotIdx] = normalizedDetections[detectionIdx];
detectionIdx++;
}
});
const totalVehicles = response.totalCount || response.totalVehicles || response.total_vehicles || normalizedDetections.reduce((sum, d) => sum + d.count, 0);
setSuccessMessage(
`βœ“ Detected ${totalVehicles} vehicles across ${normalizedDetections.length} road${normalizedDetections.length !== 1 ? 's' : ''}`
);
return true;
} catch (err) {
const msg = err.message || 'Detection failed. Ensure the backend is running.';
setError(msg);
console.error('[Upload Error]', err);
return false;
} finally {
setLoading(false);
}
}, []);
const clearData = useCallback(() => {
setVehicleData([]);
setRoadPreviews([null, null, null, null]);
setError(null);
setSuccessMessage(null);
prevPreviewsRef.current = [null, null, null, null];
}, []);
return {
vehicleData,
roadPreviews,
setRoadPreviews,
loading,
error,
successMessage,
handleImageUpload,
clearData,
};
};
export default useImageUpload;