entregas_app/hooks/use-real-route-data.tsx

161 lines
4.9 KiB
TypeScript
Raw Permalink Normal View History

"use client"
import { useState, useEffect } from "react"
import type { Delivery } from "../types"
// Token de acesso do Mapbox
const MAPBOX_ACCESS_TOKEN =
"pk.eyJ1IjoiYWxlaW5jb25uZXh0IiwiYSI6ImNtOGtvdzFueDBuMGUybHBvYjd4d3kyZDQifQ.MXDcXpxKAXtQkyAwv-_1tQ"
interface RouteResponse {
routes: Array<{
distance: number // em metros
duration: number // em segundos
geometry: {
coordinates: number[][]
}
}>
}
interface RealRouteData {
totalDistance: number // em km
totalDuration: number // em minutos
isLoading: boolean
error: string | null
routeCoordinates: Array<{ latitude: number; longitude: number }>
}
interface UseRealRouteDataProps {
deliveries: Delivery[]
distributionCenter: { latitude: number; longitude: number }
}
export const useRealRouteData = ({ deliveries, distributionCenter }: UseRealRouteDataProps): RealRouteData => {
const [routeData, setRouteData] = useState<RealRouteData>({
totalDistance: 0,
totalDuration: 0,
isLoading: false,
error: null,
routeCoordinates: [],
})
useEffect(() => {
if (deliveries.length === 0) return
calculateRealRoute()
}, [deliveries])
const calculateHaversineDistance = (lat1: number, lon1: number, lat2: number, lon2: number): number => {
const R = 6371 // Raio da Terra em km
const dLat = ((lat2 - lat1) * Math.PI) / 180
const dLon = ((lon2 - lon1) * Math.PI) / 180
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return R * c
}
const calculateSimpleRoute = (
startPoint: { latitude: number; longitude: number },
deliveryPoints: Array<{ latitude: number; longitude: number; id: string }>,
): RealRouteData => {
let totalDistance = 0
let currentPoint = startPoint
// Calcular distância entre pontos consecutivos
deliveryPoints.forEach((point) => {
const distance = calculateHaversineDistance(
currentPoint.latitude,
currentPoint.longitude,
point.latitude,
point.longitude,
)
totalDistance += distance
currentPoint = point
})
// Estimar tempo baseado em velocidade média urbana (25 km/h)
const estimatedDuration = (totalDistance / 25) * 60 // minutos
return {
totalDistance,
totalDuration: estimatedDuration,
isLoading: false,
error: null,
routeCoordinates: [startPoint, ...deliveryPoints],
}
}
const calculateRealRoute = async () => {
setRouteData((prev) => ({ ...prev, isLoading: true, error: null }))
try {
// Filtrar entregas com coordenadas válidas
const validDeliveries = deliveries
.filter((delivery) => delivery.coordinates || (delivery.lat && delivery.lng))
.sort((a, b) => a.deliverySeq - b.deliverySeq)
.map((delivery) => ({
id: delivery.id,
latitude: delivery.coordinates?.latitude || delivery.lat!,
longitude: delivery.coordinates?.longitude || delivery.lng!,
}))
if (validDeliveries.length === 0) {
throw new Error("Nenhuma entrega com coordenadas válidas")
}
// Tentar usar Mapbox Directions API
try {
const coordinates = [
[distributionCenter.longitude, distributionCenter.latitude],
...validDeliveries.map((point) => [point.longitude, point.latitude]),
]
const coordinatesString = coordinates.map((coord) => `${coord[0]},${coord[1]}`).join(";")
const url =
`https://api.mapbox.com/directions/v5/mapbox/driving/${coordinatesString}?` +
`geometries=geojson&` +
`overview=full&` +
`steps=true&` +
`access_token=${MAPBOX_ACCESS_TOKEN}`
const response = await fetch(url)
const data: RouteResponse = await response.json()
if (data.routes && data.routes.length > 0) {
const route = data.routes[0]
setRouteData({
totalDistance: route.distance / 1000, // converter para km
totalDuration: route.duration / 60, // converter para minutos
isLoading: false,
error: null,
routeCoordinates: route.geometry.coordinates.map((coord) => ({
latitude: coord[1],
longitude: coord[0],
})),
})
return
}
} catch (mapboxError) {
console.warn("Erro na API do Mapbox, usando cálculo simples:", mapboxError)
}
// Fallback para cálculo simples
const simpleRoute = calculateSimpleRoute(distributionCenter, validDeliveries)
setRouteData(simpleRoute)
} catch (error) {
console.error("Erro ao calcular rota:", error)
setRouteData((prev) => ({
...prev,
isLoading: false,
error: error instanceof Error ? error.message : "Erro desconhecido",
}))
}
}
return routeData
}