import React, { useState, useEffect } from "react"; import { Box, Typography, Button, TextField, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Grid, CircularProgress, Alert, Card, CardContent, CardMedia, CardActions, Chip, Divider, InputAdornment, Tooltip, Fade, Tabs, Tab, MenuItem, Select, InputLabel, FormControl, } from "@mui/material"; import EditIcon from "@mui/icons-material/Edit"; import DeleteIcon from "@mui/icons-material/Delete"; import AddIcon from "@mui/icons-material/Add"; import SearchIcon from "@mui/icons-material/Search"; import ViewListIcon from "@mui/icons-material/ViewList"; import ViewModuleIcon from "@mui/icons-material/ViewModule"; import DirectionsCarIcon from "@mui/icons-material/DirectionsCar"; import CalendarTodayIcon from "@mui/icons-material/CalendarToday"; import SpeedIcon from "@mui/icons-material/Speed"; import AttachMoneyIcon from "@mui/icons-material/AttachMoney"; import { useAuth } from "../../context/AuthContext"; import { fetchCars, createCar, updateCar, deleteCar, getImageUrl, } from "../../utils/api"; import type { Car } from "../../utils/api"; // Определяем типы здесь, так как они не экспортируются из api.ts interface CarsResponse { cars: Car[]; total: number; } const Vehicle = () => { const { token } = useAuth(); const [cars, setCars] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [totalCars, setTotalCars] = useState(0); const [viewMode, setViewMode] = useState<"list" | "grid">("grid"); const [searchQuery, setSearchQuery] = useState(""); // Состояния для модальных окон const [openCreateDialog, setOpenCreateDialog] = useState(false); const [openEditDialog, setOpenEditDialog] = useState(false); const [openDeleteDialog, setOpenDeleteDialog] = useState(false); // Состояние для выбранного автомобиля const [selectedCar, setSelectedCar] = useState(null); // Состояния для формы const [formData, setFormData] = useState({ name: "", year: "", mileage: "", price: "", base_price: "", country_of_origin: "Россия", drive_type: "AWD", engine_type: "бензиновый", engine_capacity: "", engine_power: "", electric_motor_power: "", hybrid_type: "не гибрид", power_ratio: "не применимо", }); const [imageFile, setImageFile] = useState(null); const [imagePreview, setImagePreview] = useState(""); const translateWheelDrive = (wheelDrive: string) => { switch (wheelDrive) { case "4WD": return "Четырехколесный"; case "AWD": return "Полный"; case "RWD": return "Задний"; case "FWD": return "Передний"; case "передний": return "Передний"; case "задний": return "Задний"; case "полный": return "Полный"; } }; // Загрузка списка автомобилей const loadCars = async () => { setLoading(true); try { const data: CarsResponse = await fetchCars(); setCars(data.cars); setTotalCars(data.total); setError(""); } catch (err) { setError("Не удалось загрузить список автомобилей"); console.error(err); } finally { setLoading(false); } }; useEffect(() => { loadCars(); }, []); // Фильтрация автомобилей по поисковому запросу const filteredCars = cars.filter( (car) => car.name.toLowerCase().includes(searchQuery.toLowerCase()) || car.year.toString().includes(searchQuery) || car.price.toString().includes(searchQuery) ); // Обработчики для формы const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; const updatedData = { ...formData, [name]: value, }; // Если изменилась базовая цена или параметры, влияющие на расчет if ( name === "base_price" || name === "year" || name === "engine_power" || name === "engine_capacity" || name === "engine_type" || name === "hybrid_type" || name === "electric_motor_power" ) { // Расчет таможенных платежей const basePrice = parseFloat(updatedData.base_price) || 0; let customDuty = 0; let exciseTax = 0; let vat = 0; let utilityFee = 0; // Получаем числовые значения const year = parseInt(updatedData.year) || new Date().getFullYear(); const engineCapacity = parseFloat(updatedData.engine_capacity) || 0; const enginePower = parseFloat(updatedData.engine_power) || 0; const currentYear = new Date().getFullYear(); const age = currentYear - year; // Расчет пошлины в зависимости от возраста if (updatedData.engine_type === "электрический") { customDuty = 0; // Пошлина 0% для электромобилей } else { // Определяем коэффициент для возраста let ageCoef = 0.54; // По умолчанию для авто < 3 лет if (age > 5) { ageCoef = 0.48; // Для авто старше 5 лет используем фиксированную ставку const dutyPerCm3 = 2.5 * 100; // примерный курс евро * 2.5 евро за см3 const dutyFixed = engineCapacity * dutyPerCm3; customDuty = Math.max(dutyFixed, basePrice * ageCoef); } else if (age >= 3) { ageCoef = 0.48; customDuty = basePrice * ageCoef; } else { customDuty = basePrice * ageCoef; } } // Расчет акциза в зависимости от мощности if (updatedData.engine_type !== "электрический") { if (enginePower <= 90) { exciseTax = 0; } else if (enginePower <= 150) { exciseTax = enginePower * 53; } else if (enginePower <= 200) { exciseTax = enginePower * 511; } else if (enginePower <= 300) { exciseTax = enginePower * 836; } else if (enginePower <= 400) { exciseTax = enginePower * 1425; } else { exciseTax = enginePower * 1475; } } // НДС 20% vat = (basePrice + customDuty + exciseTax) * 0.2; // Утилизационный сбор utilityFee = updatedData.engine_type === "электрический" ? 3400 : 5000; // Итоговая цена const totalFees = customDuty + exciseTax + vat + utilityFee; const calculatedPrice = Math.round(basePrice + totalFees); // Обновляем цену в форме updatedData.price = calculatedPrice.toString(); } setFormData(updatedData); }; // Обработчик для select полей const handleSelectChange = ( e: React.ChangeEvent<{ name?: string; value: unknown }> ) => { const name = e.target.name as string; const value = e.target.value as string; // Используем тот же обработчик, что и для текстовых полей const event = { target: { name, value }, } as React.ChangeEvent; handleInputChange(event); }; const handleImageChange = (e: React.ChangeEvent) => { if (e.target.files && e.target.files[0]) { const file = e.target.files[0]; setImageFile(file); setImagePreview(URL.createObjectURL(file)); } }; // Открытие диалога создания const handleOpenCreateDialog = () => { setFormData({ name: "", year: "", mileage: "", price: "", base_price: "", country_of_origin: "Россия", drive_type: "AWD", engine_type: "бензиновый", engine_capacity: "", engine_power: "", electric_motor_power: "", hybrid_type: "не гибрид", power_ratio: "не применимо", }); setImageFile(null); setImagePreview(""); setOpenCreateDialog(true); }; // Открытие диалога редактирования const handleOpenEditDialog = (car: Car) => { setSelectedCar(car); setFormData({ name: car.name, year: car.year.toString(), mileage: car.mileage.toString(), price: car.price.toString(), base_price: car.base_price?.toString() || "", country_of_origin: car.country_of_origin || "Россия", drive_type: car.drive_type || "AWD", engine_type: car.engine_type || "бензиновый", engine_capacity: car.engine_capacity?.toString() || "", engine_power: car.engine_power?.toString() || "", electric_motor_power: car.electric_motor_power?.toString() || "", hybrid_type: car.hybrid_type || "не гибрид", power_ratio: car.power_ratio || "не применимо", }); setImagePreview(getImageUrl(car.image)); setImageFile(null); setOpenEditDialog(true); }; // Открытие диалога удаления const handleOpenDeleteDialog = (car: Car) => { setSelectedCar(car); setOpenDeleteDialog(true); }; // Закрытие диалогов const handleCloseDialogs = () => { setOpenCreateDialog(false); setOpenEditDialog(false); setOpenDeleteDialog(false); setSelectedCar(null); }; // Создание автомобиля const handleCreateCar = async () => { try { const carData = { name: formData.name, year: parseInt(formData.year), mileage: parseInt(formData.mileage), price: parseInt(formData.price), base_price: parseInt(formData.base_price), country_of_origin: formData.country_of_origin, drive_type: formData.drive_type, engine_type: formData.engine_type, engine_capacity: formData.engine_capacity ? parseInt(formData.engine_capacity) : undefined, engine_power: formData.engine_power ? parseInt(formData.engine_power) : undefined, electric_motor_power: formData.electric_motor_power ? parseInt(formData.electric_motor_power) : undefined, hybrid_type: formData.hybrid_type || "не гибрид", power_ratio: formData.power_ratio || "не применимо", image: "", }; await createCar(carData, imageFile || undefined); handleCloseDialogs(); loadCars(); } catch (err) { setError("Не удалось создать автомобиль"); console.error(err); } }; // Обновление автомобиля const handleUpdateCar = async () => { if (!selectedCar) return; try { const carData = { name: formData.name, year: parseInt(formData.year), mileage: parseInt(formData.mileage), price: parseInt(formData.price), base_price: parseInt(formData.base_price), country_of_origin: formData.country_of_origin, drive_type: formData.drive_type, engine_type: formData.engine_type, engine_capacity: formData.engine_capacity ? parseInt(formData.engine_capacity) : undefined, engine_power: formData.engine_power ? parseInt(formData.engine_power) : undefined, electric_motor_power: formData.electric_motor_power ? parseInt(formData.electric_motor_power) : undefined, hybrid_type: formData.hybrid_type || "не гибрид", power_ratio: formData.power_ratio || "не применимо", }; await updateCar(selectedCar.id, carData, imageFile || undefined); handleCloseDialogs(); loadCars(); } catch (err) { setError("Не удалось обновить автомобиль"); console.error(err); } }; // Удаление автомобиля const handleDeleteCar = async () => { if (!selectedCar) return; try { await deleteCar(selectedCar.id); handleCloseDialogs(); loadCars(); } catch (err) { setError("Не удалось удалить автомобиль"); console.error(err); } }; // Валидация формы const isFormValid = () => { return ( formData.name.trim() !== "" && !isNaN(parseInt(formData.year)) && !isNaN(parseInt(formData.mileage)) && !isNaN(parseInt(formData.price)) && !isNaN(parseInt(formData.base_price)) && formData.country_of_origin.trim() !== "" && formData.drive_type.trim() !== "" && formData.engine_type.trim() !== "" ); }; // Форматирование цены const formatPrice = (price: number) => { return new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB", maximumFractionDigits: 0, }).format(price); }; // Рендер таблицы const renderTable = () => ( ID Изображение Название Год Пробег Цена Действия {filteredCars.map((car) => ( {car.id} {car.name} } label={car.year} size="small" sx={{ bgcolor: "#e8f5e9", color: "#2e7d32" }} /> } label={`${car.mileage} км`} size="small" sx={{ bgcolor: "#e3f2fd", color: "#1565c0" }} /> } label={formatPrice(car.price)} size="small" sx={{ bgcolor: "#fce4ec", color: "#c2185b" }} /> handleOpenEditDialog(car)} color="primary" sx={{ color: "black", "&:hover": { color: "#C27664", bgcolor: "transparent", }, transition: "all 0.2s ease", }} > handleOpenDeleteDialog(car)} color="error" sx={{ "&:hover": { color: "#C27664", bgcolor: "transparent", }, transition: "all 0.2s ease", }} > ))}
); // Рендер сетки карточек const renderGrid = () => ( {filteredCars.map((car) => ( {car.name} Год выпуска: {car.year} Пробег: {car.mileage} км Цена: {formatPrice(car.price)} ))} ); return ( {/* Заголовок и кнопки управления */} Управление автомобилями {/* Панель поиска и переключения вида */} setSearchQuery(e.target.value)} size="small" sx={{ minWidth: 300, "& .MuiOutlinedInput-root": { borderRadius: "1vw", }, }} InputProps={{ startAdornment: ( ), }} /> Всего: {totalCars} автомобилей setViewMode(newValue)} sx={{ "& .MuiTab-root": { minWidth: "auto" }, "& .Mui-selected": { color: "#C27664 !important" }, "& .MuiTabs-indicator": { backgroundColor: "#C27664" }, }} > } value="list" sx={{ minWidth: "auto" }} /> } value="grid" sx={{ minWidth: "auto" }} /> {/* Сообщения об ошибках */} {error && ( {error} )} {/* Индикатор загрузки */} {loading ? ( ) : filteredCars.length === 0 ? ( Автомобили не найдены Попробуйте изменить параметры поиска или добавьте новый автомобиль ) : // Отображение списка автомобилей в зависимости от выбранного режима viewMode === "list" ? ( renderTable() ) : ( renderGrid() )} {/* Диалог создания автомобиля */} Добавить автомобиль {/* ), }} /> */} setFormData({ ...formData, name: e.target.value }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( ), }} /> {/* ), }} /> */} setFormData({ ...formData, year: e.target.value }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( ), }} /> {/* ), }} /> */} {/* ), }} /> */} ), }} /> ), }} /> setFormData({ ...formData, mileage: e.target.value }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( ), }} /> Страна происхождения Тип привода Тип двигателя {/* Поле объёма двигателя - показываем для всех, кроме электрических */} {formData.engine_type !== "электрический" && ( setFormData({ ...formData, engine_capacity: e.target.value, }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( см³ ), }} /> )} {/* Поле мощности двигателя - показываем для всех, кроме электрических */} {formData.engine_type !== "электрический" && ( setFormData({ ...formData, engine_power: e.target.value }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( л.с. ), }} /> )} {/* Поле мощности электромотора - показываем только для электрических и гибридных */} {(formData.engine_type === "электрический" || formData.engine_type === "гибридный") && ( setFormData({ ...formData, electric_motor_power: e.target.value, }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( кВт ), }} /> )} {/* Тип гибрида - показываем только для гибридов */} {formData.engine_type === "гибридный" && ( Тип гибрида )} {/* Соотношение мощности - показываем только для гибридов */} {formData.engine_type === "гибридный" && ( Соотношение мощности )} {imagePreview ? ( ) : ( Предпросмотр изображения )} {/* Диалог редактирования автомобиля */} Редактировать автомобиль setFormData({ ...formData, name: e.target.value }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( ), }} /> setFormData({ ...formData, year: e.target.value }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( ), }} /> ), }} /> ), }} /> setFormData({ ...formData, mileage: e.target.value }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( ), }} /> Страна происхождения Тип привода Тип двигателя {/* Поле объёма двигателя - показываем для всех, кроме электрических */} {formData.engine_type !== "электрический" && ( setFormData({ ...formData, engine_capacity: e.target.value, }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( л ), }} /> )} {/* Поле мощности двигателя - показываем для всех, кроме электрических */} {formData.engine_type !== "электрический" && ( setFormData({ ...formData, engine_power: e.target.value }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( л.с. ), }} /> )} {/* Поле мощности электромотора - показываем только для электрических и гибридных */} {(formData.engine_type === "электрический" || formData.engine_type === "гибридный") && ( setFormData({ ...formData, electric_motor_power: e.target.value, }) } variant="outlined" sx={{ "& .MuiOutlinedInput-root": { borderRadius: "1vw", bgcolor: "white", height: "4vw", width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", fontSize: "1.2vw", color: "#C27664", "& fieldset": { border: "none", }, }, }} InputProps={{ startAdornment: ( кВт ), }} /> )} {/* Тип гибрида - показываем только для гибридов */} {formData.engine_type === "гибридный" && ( Тип гибрида )} {/* Соотношение мощности - показываем только для гибридов */} {formData.engine_type === "гибридный" && ( Соотношение мощности )} {imagePreview ? ( ) : ( Предпросмотр изображения )} {/* Диалог удаления автомобиля */} Удалить автомобиль {selectedCar && ( )} {selectedCar?.name} {selectedCar && ( {selectedCar.year} г., {selectedCar.mileage} км,{" "} {formatPrice(selectedCar.price)} )} Вы уверены, что хотите удалить этот автомобиль? Это действие нельзя отменить. ); }; export default Vehicle;