update
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"ms-dotnettools.csharp"
|
||||||
|
]
|
||||||
|
}
|
||||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"name": "Debug React App",
|
"name": "Debug React App",
|
||||||
"type": "firefox",
|
"type": "chrome",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"url": "http://localhost:3000",
|
"url": "http://localhost:3000",
|
||||||
"webRoot": "${workspaceFolder}/src",
|
"webRoot": "${workspaceFolder}/src",
|
||||||
|
|||||||
30
db.json
30
db.json
@@ -5,7 +5,7 @@
|
|||||||
"clientName": "Иванов Иван Иванович",
|
"clientName": "Иванов Иван Иванович",
|
||||||
"orderCost": 15000,
|
"orderCost": 15000,
|
||||||
"orderDate": "2025-01-15",
|
"orderDate": "2025-01-15",
|
||||||
"status": "in_progress",
|
"status": "completed",
|
||||||
"address": "ул. Ленина, 10",
|
"address": "ул. Ленина, 10",
|
||||||
"description": "Доставка строительных материалов"
|
"description": "Доставка строительных материалов"
|
||||||
},
|
},
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"clientName": "Сергеев Алексей Петрович",
|
"clientName": "Сергеев Алексей Петрович",
|
||||||
"orderCost": 12000,
|
"orderCost": 12000,
|
||||||
"orderDate": "2025-01-18",
|
"orderDate": "2025-01-18",
|
||||||
"status": "pending",
|
"status": "in_progress",
|
||||||
"address": "пр. Космонавтов, 78",
|
"address": "пр. Космонавтов, 78",
|
||||||
"description": "Переезд квартиры"
|
"description": "Переезд квартиры"
|
||||||
},
|
},
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
"clientName": "Александрова Ольга Викторовна",
|
"clientName": "Александрова Ольга Викторовна",
|
||||||
"orderCost": 9500,
|
"orderCost": 9500,
|
||||||
"orderDate": "2025-01-23",
|
"orderDate": "2025-01-23",
|
||||||
"status": "pending",
|
"status": "cancelled",
|
||||||
"address": "ул. Центральная, 89",
|
"address": "ул. Центральная, 89",
|
||||||
"description": "Перевозка личных вещей"
|
"description": "Перевозка личных вещей"
|
||||||
},
|
},
|
||||||
@@ -184,14 +184,6 @@
|
|||||||
"endTime": "12:00",
|
"endTime": "12:00",
|
||||||
"date": "2025-11-21"
|
"date": "2025-11-21"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "e320",
|
|
||||||
"vehicleId": "1",
|
|
||||||
"orderId": "1",
|
|
||||||
"startTime": "08:00",
|
|
||||||
"endTime": "10:00",
|
|
||||||
"date": "2025-11-21"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "08a1",
|
"id": "08a1",
|
||||||
"vehicleId": "2",
|
"vehicleId": "2",
|
||||||
@@ -207,6 +199,22 @@
|
|||||||
"startTime": "08:00",
|
"startTime": "08:00",
|
||||||
"endTime": "09:00",
|
"endTime": "09:00",
|
||||||
"date": "2025-11-21"
|
"date": "2025-11-21"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6f66",
|
||||||
|
"vehicleId": "1",
|
||||||
|
"orderId": "1",
|
||||||
|
"startTime": "08:00",
|
||||||
|
"endTime": "11:00",
|
||||||
|
"date": "2025-11-21"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f275",
|
||||||
|
"vehicleId": "2",
|
||||||
|
"orderId": "4",
|
||||||
|
"startTime": "08:00",
|
||||||
|
"endTime": "10:00",
|
||||||
|
"date": "2025-11-23"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
40
src/App.tsx
40
src/App.tsx
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
|
||||||
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
|
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
|
||||||
import OrdersPage from './pages/OrdersPage';
|
import OrdersPage from './pages/OrdersPage';
|
||||||
import VehiclesPage from './pages/VehiclesPage';
|
import VehiclesPage from './pages/VehiclesPage';
|
||||||
@@ -6,43 +6,29 @@ import VehicleDetailPage from './pages/VehicleDetailPage';
|
|||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<div className="App">
|
<div className="App">
|
||||||
{/* Навигация */}
|
|
||||||
<nav className="navbar navbar-expand-lg navbar-dark bg-primary">
|
<nav className="navbar navbar-dark bg-primary">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
|
|
||||||
<Link className="navbar-brand" to="/">
|
<Link className="navbar-brand" to="/">
|
||||||
Логистическая компания
|
Логистическая компания
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<div className="d-flex">
|
||||||
className="navbar-toggler"
|
<Link className="nav-link text-white me-3" to="/orders">
|
||||||
type="button"
|
Заказы
|
||||||
data-bs-toggle="collapse"
|
</Link>
|
||||||
data-bs-target="#navbarNav"
|
<Link className="nav-link text-white" to="/vehicles">
|
||||||
>
|
Машины
|
||||||
<span className="navbar-toggler-icon"></span>
|
</Link>
|
||||||
</button>
|
|
||||||
<div className="collapse navbar-collapse" id="navbarNav">
|
|
||||||
<ul className="navbar-nav">
|
|
||||||
<li className="nav-item">
|
|
||||||
<Link className="nav-link" to="/orders">
|
|
||||||
Заказы
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li className="nav-item">
|
|
||||||
<Link className="nav-link" to="/vehicles">
|
|
||||||
Машины
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Основной контент */}
|
<main className="container mt-4">
|
||||||
<main>
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<OrdersPage />} />
|
<Route path="/" element={<OrdersPage />} />
|
||||||
<Route path="/orders" element={<OrdersPage />} />
|
<Route path="/orders" element={<OrdersPage />} />
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ interface WaybillWidgetProps {
|
|||||||
date: string;
|
date: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let durationOrder;
|
|
||||||
|
|
||||||
const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
||||||
const [entries, setEntries] = useState<WaybillEntry[]>([]);
|
const [entries, setEntries] = useState<WaybillEntry[]>([]);
|
||||||
const [orders, setOrders] = useState<Order[]>([]);
|
const [orders, setOrders] = useState<Order[]>([]);
|
||||||
@@ -21,7 +19,7 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
const [selectedTimeSlot, setSelectedTimeSlot] = useState<string | null>(null);
|
const [selectedTimeSlot, setSelectedTimeSlot] = useState<string | null>(null);
|
||||||
const [duration, setDuration] = useState<number>(2);
|
const [duration, setDuration] = useState<number>(2);
|
||||||
|
|
||||||
// Часовые интервалы с 8:00 до 20:00
|
|
||||||
const timeSlots = Array.from({ length: 13 }, (_, i) => {
|
const timeSlots = Array.from({ length: 13 }, (_, i) => {
|
||||||
const hour = i + 8;
|
const hour = i + 8;
|
||||||
return `${hour.toString().padStart(2, '0')}:00`;
|
return `${hour.toString().padStart(2, '0')}:00`;
|
||||||
@@ -34,14 +32,14 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// Загружаем записи путевого листа
|
|
||||||
const entriesResponse = await waybillApi.getEntries();
|
const entriesResponse = await waybillApi.getEntries();
|
||||||
const vehicleEntries = entriesResponse.data.filter(
|
const vehicleEntries = entriesResponse.data.filter(
|
||||||
entry => entry.vehicleId === vehicleId && entry.date === date
|
entry => entry.vehicleId === vehicleId && entry.date === date
|
||||||
);
|
);
|
||||||
setEntries(vehicleEntries);
|
setEntries(vehicleEntries);
|
||||||
|
|
||||||
// Загружаем доступные заказы
|
|
||||||
const ordersResponse = await ordersApi.getOrders();
|
const ordersResponse = await ordersApi.getOrders();
|
||||||
const assignedOrderIds = new Set(vehicleEntries.map(entry => entry.orderId));
|
const assignedOrderIds = new Set(vehicleEntries.map(entry => entry.orderId));
|
||||||
const available = ordersResponse.data.filter(
|
const available = ordersResponse.data.filter(
|
||||||
@@ -71,13 +69,12 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrderInfo = (orderId: number, allOrders: Order[]): Order | undefined => {
|
const getOrderInfo = (orderId: number, allOrders: Order[]): Order | undefined => {
|
||||||
// Ищем заказ среди всех заказов
|
|
||||||
const foundOrder = allOrders.find(order => order.id === orderId);
|
const foundOrder = allOrders.find(order => order.id === orderId);
|
||||||
if (foundOrder) {
|
if (foundOrder) {
|
||||||
return foundOrder;
|
return foundOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если заказ не найден, но есть в путевом листе - создаем базовый объект
|
|
||||||
const waybillEntry = entries.find(entry => entry.orderId === orderId);
|
const waybillEntry = entries.find(entry => entry.orderId === orderId);
|
||||||
if (waybillEntry) {
|
if (waybillEntry) {
|
||||||
const basicOrder: Order = {
|
const basicOrder: Order = {
|
||||||
@@ -92,7 +89,6 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
return basicOrder;
|
return basicOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если заказ не найден нигде
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,7 +96,7 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
if (selectedOrder) {
|
if (selectedOrder) {
|
||||||
try {
|
try {
|
||||||
const startHour = parseInt(timeSlot.split(':')[0]);
|
const startHour = parseInt(timeSlot.split(':')[0]);
|
||||||
const endHour = startHour + duration; // Используем выбранную длительность
|
const endHour = startHour + duration;
|
||||||
|
|
||||||
const newEntry: Omit<WaybillEntry, 'id'> = {
|
const newEntry: Omit<WaybillEntry, 'id'> = {
|
||||||
vehicleId,
|
vehicleId,
|
||||||
@@ -128,7 +124,7 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
if (selectedTimeSlot) {
|
if (selectedTimeSlot) {
|
||||||
try {
|
try {
|
||||||
const startHour = parseInt(selectedTimeSlot.split(':')[0]);
|
const startHour = parseInt(selectedTimeSlot.split(':')[0]);
|
||||||
const endHour = startHour + duration; // Используем выбранную длительность
|
const endHour = startHour + duration;
|
||||||
|
|
||||||
const newEntry: Omit<WaybillEntry, 'id'> = {
|
const newEntry: Omit<WaybillEntry, 'id'> = {
|
||||||
vehicleId,
|
vehicleId,
|
||||||
@@ -172,7 +168,6 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
<h5 className="mb-0">Путевой лист на {new Date(date).toLocaleDateString()}</h5>
|
<h5 className="mb-0">Путевой лист на {new Date(date).toLocaleDateString()}</h5>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
{/* Состояние выбора */}
|
|
||||||
<div className="alert alert-info mb-3">
|
<div className="alert alert-info mb-3">
|
||||||
{selectedOrder && !selectedTimeSlot && (
|
{selectedOrder && !selectedTimeSlot && (
|
||||||
<div>
|
<div>
|
||||||
@@ -220,7 +215,6 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Таблица временных интервалов */}
|
|
||||||
<div className="table-responsive mb-4">
|
<div className="table-responsive mb-4">
|
||||||
<table className="table table-bordered">
|
<table className="table table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -275,7 +269,6 @@ const WaybillWidget: React.FC<WaybillWidgetProps> = ({ vehicleId, date }) => {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Список доступных заказов */}
|
|
||||||
<div className="available-orders">
|
<div className="available-orders">
|
||||||
<h6>Доступные заказы:</h6>
|
<h6>Доступные заказы:</h6>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ const OrdersPage: React.FC = () => {
|
|||||||
status: ''
|
status: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
// Загрузка данных
|
|
||||||
const loadOrders = async () => {
|
const loadOrders = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -35,23 +34,18 @@ const OrdersPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Функция изменения статуса заказа
|
|
||||||
const updateOrderStatus = async (orderId: number, newStatus: Order['status']) => {
|
const updateOrderStatus = async (orderId: number, newStatus: Order['status']) => {
|
||||||
try {
|
try {
|
||||||
// Находим заказ для обновления
|
|
||||||
const orderToUpdate = orders.find(order => order.id === orderId);
|
const orderToUpdate = orders.find(order => order.id === orderId);
|
||||||
if (!orderToUpdate) return;
|
if (!orderToUpdate) return;
|
||||||
|
|
||||||
// Создаем обновленный заказ
|
|
||||||
const updatedOrder = {
|
const updatedOrder = {
|
||||||
...orderToUpdate,
|
...orderToUpdate,
|
||||||
status: newStatus
|
status: newStatus
|
||||||
};
|
};
|
||||||
|
|
||||||
// Отправляем запрос на сервер
|
|
||||||
await ordersApi.updateOrder(orderId, updatedOrder);
|
await ordersApi.updateOrder(orderId, updatedOrder);
|
||||||
|
|
||||||
// Обновляем локальное состояние
|
|
||||||
setOrders(prevOrders =>
|
setOrders(prevOrders =>
|
||||||
prevOrders.map(order =>
|
prevOrders.map(order =>
|
||||||
order.id === orderId ? updatedOrder : order
|
order.id === orderId ? updatedOrder : order
|
||||||
@@ -69,7 +63,6 @@ const OrdersPage: React.FC = () => {
|
|||||||
loadOrders();
|
loadOrders();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Применение фильтров
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let result = orders;
|
let result = orders;
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,9 @@ const VehicleDetailPage: React.FC = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// Загружаем данные машины
|
|
||||||
const vehicleResponse = await vehiclesApi.getVehicle(parseInt(id));
|
const vehicleResponse = await vehiclesApi.getVehicle(parseInt(id));
|
||||||
setVehicle(vehicleResponse.data);
|
setVehicle(vehicleResponse.data);
|
||||||
|
|
||||||
// Загружаем историю выполненных заказов
|
|
||||||
const ordersResponse = await ordersApi.getOrders();
|
const ordersResponse = await ordersApi.getOrders();
|
||||||
const completed = ordersResponse.data.filter(
|
const completed = ordersResponse.data.filter(
|
||||||
order => order.status === 'completed'
|
order => order.status === 'completed'
|
||||||
|
|||||||
@@ -1,27 +1,24 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Order, Vehicle, WaybillEntry } from '../types';
|
import { Order, Vehicle, WaybillEntry } from '../types';
|
||||||
|
|
||||||
const API_BASE = 'http://localhost:3001';
|
const API_BASE = 'http://localhost:3001/';
|
||||||
|
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: API_BASE,
|
baseURL: API_BASE,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Сервис для работы с заказами
|
|
||||||
// Сервис для работы с заказами
|
|
||||||
export const ordersApi = {
|
export const ordersApi = {
|
||||||
getOrders: () => api.get<Order[]>('/orders'),
|
getOrders: () => api.get<Order[]>('/orders'),
|
||||||
getOrder: (id: number) => api.get<Order>(`/orders/${id}`),
|
getOrder: (id: number) => api.get<Order>(`/orders/${id}`),
|
||||||
updateOrder: (id: number, order: Order) => api.put<Order>(`/orders/${id}`, order),
|
updateOrder: (id: number, order: Order) => api.put<Order>(`/orders/${id}`, order),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Сервис для работы с машинами
|
|
||||||
export const vehiclesApi = {
|
export const vehiclesApi = {
|
||||||
getVehicles: () => api.get<Vehicle[]>('/vehicles'),
|
getVehicles: () => api.get<Vehicle[]>('/vehicles'),
|
||||||
getVehicle: (id: number) => api.get<Vehicle>(`/vehicles/${id}`),
|
getVehicle: (id: number) => api.get<Vehicle>(`/vehicles/${id}`),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Сервис для работы с путевыми листами
|
|
||||||
export const waybillApi = {
|
export const waybillApi = {
|
||||||
getEntries: () => api.get<WaybillEntry[]>('/waybillEntries'),
|
getEntries: () => api.get<WaybillEntry[]>('/waybillEntries'),
|
||||||
createEntry: (entry: Omit<WaybillEntry, 'id'>) =>
|
createEntry: (entry: Omit<WaybillEntry, 'id'>) =>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Основные типы данных
|
|
||||||
export interface Order {
|
export interface Order {
|
||||||
id: number;
|
id: number;
|
||||||
clientName: string;
|
clientName: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user