130 lines
4.5 KiB
TypeScript
130 lines
4.5 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { useParams, Link } from 'react-router-dom';
|
||
import { Vehicle, Order, WaybillEntry } from '../types';
|
||
import { vehiclesApi, ordersApi, waybillApi } from '../services/api';
|
||
import WaybillWidget from '../components/WaybillWidget';
|
||
import Loading from '../components/Loading';
|
||
import ErrorAlert from '../components/ErrorAlert';
|
||
|
||
const VehicleDetailPage: React.FC = () => {
|
||
const { id } = useParams<{ id: string }>();
|
||
const [vehicle, setVehicle] = useState<Vehicle | null>(null);
|
||
const [completedOrders, setCompletedOrders] = useState<Order[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState<string | null>(null);
|
||
const [selectedDate, setSelectedDate] = useState(
|
||
new Date().toISOString().split('T')[0]
|
||
);
|
||
|
||
const loadVehicleData = async () => {
|
||
if (!id) return;
|
||
|
||
try {
|
||
setLoading(true);
|
||
setError(null);
|
||
|
||
const vehicleResponse = await vehiclesApi.getVehicle(parseInt(id));
|
||
setVehicle(vehicleResponse.data);
|
||
|
||
const ordersResponse = await ordersApi.getOrders();
|
||
const completed = ordersResponse.data.filter(
|
||
order => order.status === 'completed'
|
||
);
|
||
setCompletedOrders(completed);
|
||
|
||
} catch (err) {
|
||
setError('Не удалось загрузить данные машины');
|
||
console.error('Error loading vehicle data:', err);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
loadVehicleData();
|
||
}, [id]);
|
||
|
||
if (loading) return <Loading />;
|
||
if (error) return <ErrorAlert message={error} onRetry={loadVehicleData} />;
|
||
if (!vehicle) return <div>Машина не найдена</div>;
|
||
|
||
return (
|
||
<div className="container mt-4">
|
||
<nav aria-label="breadcrumb">
|
||
<ol className="breadcrumb">
|
||
<li className="breadcrumb-item">
|
||
<Link to="/vehicles">Машины</Link>
|
||
</li>
|
||
<li className="breadcrumb-item active">{vehicle.licensePlate}</li>
|
||
</ol>
|
||
</nav>
|
||
|
||
<div className="row">
|
||
<div className="col-md-4">
|
||
<div className="card mb-4">
|
||
<div className="card-header">
|
||
<h5 className="mb-0">Информация о машине</h5>
|
||
</div>
|
||
<div className="card-body">
|
||
<p><strong>Тип машины:</strong> {vehicle.vehicleType}</p>
|
||
<p><strong>Водитель:</strong> {vehicle.driverName}</p>
|
||
<p><strong>Гос. номер:</strong> {vehicle.licensePlate}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="card">
|
||
<div className="card-header">
|
||
<h5 className="mb-0">История выполненных заказов</h5>
|
||
</div>
|
||
<div className="card-body">
|
||
{completedOrders.length === 0 ? (
|
||
<p className="text-muted">Нет выполненных заказов</p>
|
||
) : (
|
||
<div className="list-group">
|
||
{completedOrders.map(order => (
|
||
<div key={order.id} className="list-group-item">
|
||
<h6 className="mb-1">{order.clientName}</h6>
|
||
<p className="mb-1 small">{order.address}</p>
|
||
<small className="text-muted">
|
||
{new Date(order.orderDate).toLocaleDateString()} - {' '}
|
||
{order.orderCost.toLocaleString()} руб.
|
||
</small>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="col-md-8">
|
||
<div className="card">
|
||
<div className="card-header d-flex justify-content-between align-items-center">
|
||
<h5 className="mb-0">Путевой лист</h5>
|
||
<div>
|
||
<label htmlFor="waybillDate" className="form-label me-2">
|
||
Дата:
|
||
</label>
|
||
<input
|
||
type="date"
|
||
id="waybillDate"
|
||
className="form-control"
|
||
value={selectedDate}
|
||
onChange={(e) => setSelectedDate(e.target.value)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div className="card-body">
|
||
<WaybillWidget
|
||
vehicleId={vehicle.id}
|
||
date={selectedDate}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default VehicleDetailPage; |