WEB_logistics/src/components/DeliveryOrdersList.jsx
2024-10-08 14:18:09 +05:00

201 lines
6.3 KiB
JavaScript

import {
getDeliveryOrders,
updateDeliveryOrderRoute,
getDeliveryOrderDetails,
getDeliveryAccessories,
} from "../api.jsx";
import { getCoordinates } from "../geocoder.jsx";
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import polyline from "@mapbox/polyline";
import "./DeliveryOrdersList.css";
const DELIVERY_CITY = {
name: "Челябинск",
latitude: 55.159902,
longitude: 61.402554,
};
const DeliveryOrdersList = ({
totalOrderId,
onSubOrderClick,
setDeliveryOrdersCount,
setTotalEstimatedTime,
}) => {
const [deliveryOrders, setDeliveryOrders] = useState([]);
const [loadingDeliveryOrders, setLoadingDeliveryOrders] = useState(true);
const [calculatingRoutes, setCalculatingRoutes] = useState([]);
const [hoveredOrderId, setHoveredOrderId] = useState(null);
const navigate = useNavigate();
useEffect(() => {
fetchDeliveryOrders();
}, [totalOrderId]);
const formatPrice = (price) => {
if (!price) {
return "не указана";
}
return `${price.toFixed(2)}`;
};
const fetchDeliveryOrders = async () => {
try {
const orders = await getDeliveryOrders(totalOrderId);
setDeliveryOrdersCount(orders.length);
setDeliveryOrders(orders);
const totalTime = orders.reduce((sum, order) => {
return sum + (order.estimated_route_time_in_minutes || 0);
}, 0);
setTotalEstimatedTime(totalTime);
} catch (error) {
if (error.response && error.response.status === 401) {
navigate("/login");
}
console.error("Ошибка при загрузке подзаказов:", error);
} finally {
setLoadingDeliveryOrders(false);
}
};
const formatTimeInHours = (minutes) => {
if (!minutes || minutes === 0) {
return "ещё не рассчитывалось";
}
return (minutes / 60).toFixed(2) + " час.";
};
const calculateRoutes = async () => {
const ordersToCalculate = deliveryOrders.filter(
(order) => !order.estimated_route_time_in_minutes
);
setCalculatingRoutes(ordersToCalculate.map((order) => order.id));
await Promise.all(
ordersToCalculate.map(async (order) => {
try {
const deliveryOrderDetails = await getDeliveryOrderDetails(order.id);
const accessories = await getDeliveryAccessories(
deliveryOrderDetails.id
);
const coords = await Promise.all(
accessories.map(async (accessory) => {
if (accessory.latitude && accessory.longitude) {
return {
city: accessory.city_name,
latitude: accessory.latitude,
longitude: accessory.longitude,
accessory_name:
accessory.accessory_name +
Math.round(
(accessory.accessory_volume * accessory.count) / 100
) +
"шт.",
};
} else {
const coords = await getCoordinates(accessory.city_name);
return {
city: accessory.city_name,
accessory_name:
accessory.accessory_name +
": " +
Math.round(
(accessory.accessory_volume * accessory.count) / 100
) +
"шт.",
...coords,
};
}
})
);
const fullCoordinates = [...coords, DELIVERY_CITY];
if (fullCoordinates.length > 1) {
const waypoints = fullCoordinates
.map(({ longitude, latitude }) => `${longitude},${latitude}`)
.join(";");
const routeUrl = `https://router.project-osrm.org/route/v1/driving/${waypoints}?overview=full`;
const response = await fetch(routeUrl);
const data = await response.json();
if (data.routes && data.routes.length > 0) {
const geometry = data.routes[0].geometry;
const decodedRoute = polyline.decode(geometry);
const duration = data.routes[0].duration;
await updateDeliveryOrderRoute(
order.id,
duration / 60,
decodedRoute
);
}
}
} catch (error) {
console.error("Ошибка при расчете маршрута для подзаказа:", error);
}
})
);
await fetchDeliveryOrders();
setCalculatingRoutes([]);
};
const ordersToCalculate = deliveryOrders.filter(
(order) => !order.estimated_route_time_in_minutes
);
return (
<div>
{loadingDeliveryOrders ? (
<div className="spinner-border" role="status">
<span className="visually-hidden"></span>
</div>
) : (
<>
{ordersToCalculate.length > 0 && (
<button onClick={calculateRoutes} className="btn btn-primary mb-3">
Рассчитать все
</button>
)}
{deliveryOrders.map((order, index) => (
<div
key={order.id}
className="card mb-1"
onClick={() => onSubOrderClick(order.id)}
onMouseEnter={() => setHoveredOrderId(order.id)}
onMouseLeave={() => setHoveredOrderId(null)}
>
<div className="card-body">
<p>Этап {index + 1}</p>
<p>
Прогнозируемое время этапа:{" "}
{formatTimeInHours(order.estimated_route_time_in_minutes)}
</p>
<p>Стоимость: {formatPrice(order.price)}</p>
{calculatingRoutes.includes(order.id) && (
<div className="spinner-border" role="status">
<span className="visually-hidden"></span>
</div>
)}
{hoveredOrderId === order.id && (
<p className="hover-info">
Нажмите, чтобы посмотреть подробную информацию
</p>
)}
</div>
</div>
))}
</>
)}
</div>
);
};
export default DeliveryOrdersList;