diff --git a/package-lock.json b/package-lock.json index afb1954..ad1da57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@mui/material": "^7.2.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-icons": "^5.5.0", "react-imask": "^7.6.1", "react-router-dom": "^7.6.3" }, @@ -3547,6 +3548,15 @@ "react": "^19.1.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-imask": { "version": "7.6.1", "resolved": "https://registry.npmjs.org/react-imask/-/react-imask-7.6.1.tgz", diff --git a/package.json b/package.json index f74a6ad..8df1524 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@mui/material": "^7.2.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-icons": "^5.5.0", "react-imask": "^7.6.1", "react-router-dom": "^7.6.3" }, diff --git a/src/assets/icon/autobro.png b/src/assets/icon/autobro.png index fbe2db4..60cd662 100644 Binary files a/src/assets/icon/autobro.png and b/src/assets/icon/autobro.png differ diff --git a/src/assets/icon/autobro_old.png b/src/assets/icon/autobro_old.png new file mode 100644 index 0000000..fbe2db4 Binary files /dev/null and b/src/assets/icon/autobro_old.png differ diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 38aa113..e6aa035 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -72,7 +72,7 @@ const Header = () => { display: "flex", justifyContent: "center", alignItems: "center", - height: isMobile ? "15vw" : "10vw", + height: isMobile ? "15vw" : "5vw", gap: "2vw", fontFamily: "Unbounded", bgcolor: "#2D2D2D", @@ -95,7 +95,8 @@ const Header = () => { onClick={() => handleScrollToAnchor("#main")} sx={{ width: isMobile ? "17vw" : "9vw", - height: isMobile ? "17vw" : "9vw", + height: isMobile ? "6vw" : "2.8vw", + mb: "0.5vw", cursor: "pointer", "&:hover": { scale: 1.1 }, transition: "all 0.4s ease", @@ -152,7 +153,7 @@ const Header = () => { {/* Пустой блок для компенсации фиксированного хедера */} - + {/* Мобильное меню */} diff --git a/src/pages/CalculatorPage.tsx b/src/pages/CalculatorPage.tsx index 39439df..ad3910e 100644 --- a/src/pages/CalculatorPage.tsx +++ b/src/pages/CalculatorPage.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { Box, Typography, @@ -10,17 +10,25 @@ import { FormControlLabel, Radio, InputAdornment, + Collapse, + Paper, } from "@mui/material"; import { useResponsive } from "../theme/useResponsive"; import russia from "../assets/emoji/russia.png"; import belarus from "../assets/emoji/belarus.png"; +import { + fetchCurrencyRates, + getCurrencyRate, + mapCurrencyCodeToApiCode, + type CurrencyRatesResponse, +} from "../utils/currencyService"; function CalculatorPage() { const { isMobile } = useResponsive(); // Состояния для полей формы const [country, setCountry] = useState("russia"); // russia или belarus - const [age, setAge] = useState("Новые, не менее 3-х лет"); + const [age, setAge] = useState("3"); // Изменено на числовое значение для упрощения расчетов const [price, setPrice] = useState(""); const [currency, setCurrency] = useState("USD Доллар США"); const [engineType, setEngineType] = useState("Бензиновый"); @@ -29,34 +37,286 @@ function CalculatorPage() { const [enginePower, setEnginePower] = useState(""); const [enginePowerUnit, setEnginePowerUnit] = useState("л.с."); const [electricMotorPower, setElectricMotorPower] = useState(""); - const [electricMotorPowerUnit, setElectricMotorPowerUnit] = useState("л.с."); + const [electricMotorPowerUnit, setElectricMotorPowerUnit] = useState("кВт"); const [powerRatio, setPowerRatio] = useState("ДВС > ЭД"); const [calculationType, setCalculationType] = useState("Физ. лица"); const [enhancedPermeability, setEnhancedPermeability] = useState(false); + // Состояние для курсов валют + const [currencyRates, setCurrencyRates] = + useState(null); + const [isLoading, setIsLoading] = useState(false); + + // Состояния для результатов расчета + const [calculationResults, setCalculationResults] = useState<{ + priceInRub: string; + customDuty: string; + exciseTax: string; + vat: string; + utilityFee: string; + totalFees: string; + totalWithCar: string; + } | null>(null); + + // Получение курсов валют при монтировании компонента + useEffect(() => { + const loadCurrencyRates = async () => { + setIsLoading(true); + try { + const rates = await fetchCurrencyRates(); + setCurrencyRates(rates); + } catch (error) { + console.error("Не удалось загрузить курсы валют:", error); + } finally { + setIsLoading(false); + } + }; + + loadCurrencyRates(); + }, []); + const handleCalculate = () => { - // Здесь будет логика расчета + const calculatorResults = document.getElementById("calculator-results"); + if (calculatorResults) { + calculatorResults.scrollIntoView({ behavior: "smooth" }); + } + // Получаем курс выбранной валюты + const currencyCode = mapCurrencyCodeToApiCode(currency); + const exchangeRate = + currencyCode === "RUB" ? 1 : getCurrencyRate(currencyRates, currencyCode); + + // Конвертация цены в рубли для расчета + let priceInRub = parseFloat(price) || 0; + + // Конвертация из валюты пользователя в рубли + if (currencyCode !== "RUB") { + priceInRub *= exchangeRate; + } + + // Минимальная стоимость для расчета (устанавливается таможней) + const eurRate = getCurrencyRate(currencyRates, "EUR"); + const minCustomValue = 8500 * eurRate; // минимальная стоимость в рублях (8500 евро) + const calculationValue = Math.max(priceInRub, minCustomValue); + + let customDuty = 0; + let exciseTax = 0; + let vat = 0; + let utilityFee = 0; + let totalFees = 0; + + // Расчет для физических лиц + if (calculationType === "Физ. лица") { + // Коэффициент для возраста + let ageCoef = 1; + switch (age) { + case "3": // менее 3 лет + ageCoef = 0.54; // 54% от стоимости + break; + case "3-5": // от 3 до 5 лет + ageCoef = 0.48; // 48% от стоимости + break; + case "5+": // более 5 лет + ageCoef = 0.48; // 48% от стоимости (но не менее 2.5 евро/см3) + break; + default: + ageCoef = 0.54; + } + + // Расчет пошлины для ДВС + if (engineType === "Бензиновый" || engineType === "Дизельный") { + const engineVolumeNum = parseFloat(engineVolume) || 0; + + // Для авто менее 3 лет + if (age === "3") { + customDuty = calculationValue * 0.54; // 54% от стоимости + } + // Для авто 3-5 лет + else if (age === "3-5") { + customDuty = calculationValue * 0.48; // 48% от стоимости + } + // Для авто старше 5 лет + else { + const dutyPerCm3 = 2.5; // евро за см3 + const dutyInEur = engineVolumeNum * dutyPerCm3; + customDuty = dutyInEur * eurRate; // переводим в рубли по курсу евро + // Берем максимум между процентом и фиксированной ставкой + customDuty = Math.max(customDuty, calculationValue * 0.48); + } + + // Акцизы в зависимости от мощности + const enginePowerNum = parseFloat(enginePower) || 0; + let powerInHP = enginePowerNum; + + if (enginePowerUnit === "кВт") { + powerInHP = enginePowerNum * 1.35962; + } + + // Акцизные ставки на 2024 год (в рублях за 1 л.с.) + if (powerInHP <= 90) { + exciseTax = powerInHP * 0; + } else if (powerInHP <= 150) { + exciseTax = powerInHP * 53; + } else if (powerInHP <= 200) { + exciseTax = powerInHP * 511; + } else if (powerInHP <= 300) { + exciseTax = powerInHP * 836; + } else if (powerInHP <= 400) { + exciseTax = powerInHP * 1425; + } else { + exciseTax = powerInHP * 1475; + } + } + + // Для электромобилей + else if (engineType === "Электрический") { + customDuty = 0; // Пошлина 0% для электромобилей + exciseTax = 0; // Акциз 0 для электромобилей + } + + // Для гибридов + else if (engineType === "Гибридный") { + // Для гибридов применяются те же ставки, что и для ДВС + const engineVolumeNum = parseFloat(engineVolume) || 0; + + if (age === "3") { + customDuty = calculationValue * 0.54; + } else if (age === "3-5") { + customDuty = calculationValue * 0.48; + } else { + const dutyPerCm3 = 2.5; // евро за см3 + const dutyInEur = engineVolumeNum * dutyPerCm3; + customDuty = dutyInEur * eurRate; + customDuty = Math.max(customDuty, calculationValue * 0.48); + } + + // Акциз для гибридов рассчитывается так же, как для ДВС + const enginePowerNum = parseFloat(enginePower) || 0; + let powerInHP = enginePowerNum; + + if (enginePowerUnit === "кВт") { + powerInHP = enginePowerNum * 1.35962; + } + + if (powerInHP <= 90) { + exciseTax = powerInHP * 0; + } else if (powerInHP <= 150) { + exciseTax = powerInHP * 53; + } else if (powerInHP <= 200) { + exciseTax = powerInHP * 511; + } else if (powerInHP <= 300) { + exciseTax = powerInHP * 836; + } else if (powerInHP <= 400) { + exciseTax = powerInHP * 1425; + } else { + exciseTax = powerInHP * 1475; + } + } + + // НДС 20% от (стоимость + пошлина + акциз) + vat = (calculationValue + customDuty + exciseTax) * 0.2; + + // Утилизационный сбор + if (engineType === "Электрический") { + utilityFee = 3400; // Для электромобилей + } else { + utilityFee = enhancedPermeability ? 27000 : 3400; // Для внедорожников выше + } + } + // Расчет для юридических лиц + else { + // Здесь должна быть другая логика расчета для юр.лиц + // В данном примере используем упрощенный расчет, аналогичный физ.лицам + customDuty = calculationValue * 0.12; // Примерная ставка для юр.лиц + + // Акцизы в зависимости от мощности + const enginePowerNum = parseFloat(enginePower) || 0; + let powerInHP = enginePowerNum; + + if (enginePowerUnit === "кВт") { + powerInHP = enginePowerNum * 1.35962; + } + + // Акцизные ставки для юр.лиц могут отличаться + if (powerInHP <= 90) { + exciseTax = powerInHP * 0; + } else if (powerInHP <= 150) { + exciseTax = powerInHP * 55; + } else { + exciseTax = powerInHP * 531; + } + + // НДС 20% + vat = (calculationValue + customDuty + exciseTax) * 0.2; + + // Утилизационный сбор для юр.лиц + utilityFee = engineType === "Электрический" ? 3500 : 5000; + } + + // Итоговая сумма таможенных платежей + totalFees = customDuty + exciseTax + vat + utilityFee; + + // Сохраняем результаты расчетов + const results = { + priceInRub: priceInRub.toFixed(2), + customDuty: customDuty.toFixed(2), + exciseTax: exciseTax.toFixed(2), + vat: vat.toFixed(2), + utilityFee: utilityFee.toFixed(2), + totalFees: totalFees.toFixed(2), + totalWithCar: (priceInRub + totalFees).toFixed(2), + }; + + setCalculationResults(results); + + // Выводим результаты также в консоль для отладки console.log({ - country, - age, - price, - currency, - engineType, - hybridType, - engineVolume, - enginePower, - enginePowerUnit, - electricMotorPower, - electricMotorPowerUnit, - powerRatio, - calculationType, - enhancedPermeability, + original: { + price, + currency, + currencyRate: exchangeRate, + country, + age, + engineType, + hybridType, + engineVolume, + enginePower, + enginePowerUnit, + electricMotorPower, + electricMotorPowerUnit, + powerRatio, + calculationType, + enhancedPermeability, + }, + calculation: results, }); }; + // Функция для получения символа валюты + const getCurrencySymbol = () => { + switch (currency) { + case "USD Доллар США": + return "$"; + case "EUR Евро": + return "€"; + case "CNY Юань": + return "¥"; + case "RUB Рубль": + return "₽"; + default: + return "₽"; + } + }; + + // Функция для определения видимости полей в зависимости от типа двигателя + const isEngineVolumeVisible = engineType !== "Электрический"; + const isEngineICEPowerVisible = engineType !== "Электрический"; + const isElectricMotorPowerVisible = + engineType === "Электрический" || engineType === "Гибридный"; + const isHybridTypeVisible = engineType === "Гибридный"; + const isPowerRatioVisible = engineType === "Гибридный"; + return ( + {isMobile && ( + + + РАСЧИТАЙТЕ{" "} + СТОИМОСТЬ ДОСТАВКИ! + + + + С помощью калькулятора + таможенных платежей за + автомобиль + + + + + Обсуждаем ваш запрос и бюджет, подбираем варианты. Заключаем + договор и вносим аванс (100 000 P, возвращается, если не + подберем авто). + + + Выкупаем авто + + + Оформляем документы и доставляем авто в РФ. На всех этапах + машина застрахована. + + + Проходим таможню, делаем документы для регистрации. + + + Передаём авто вам - с актом и финальной оплатой. + + + + )} {/* Левая часть - форма */} {/* Первая линия */} - + Рассчитать для - - + setCountry("russia")} sx={{ cursor: "pointer", position: "relative" }} > @@ -106,17 +473,17 @@ function CalculatorPage() { checked={country === "russia"} sx={{ position: "absolute", opacity: 0 }} /> - Россия - setCountry("belarus")} sx={{ cursor: "pointer", position: "relative" }} > @@ -124,12 +491,12 @@ function CalculatorPage() { checked={country === "belarus"} sx={{ position: "absolute", opacity: 0 }} /> - Беларусь @@ -137,58 +504,73 @@ function CalculatorPage() { + + {/* Вторая линия */} - + Возраст - {/* setAge(e.target.value)} - variant="outlined" - placeholder="Новые, не менее 3-х лет" + displayEmpty sx={{ - "& .MuiOutlinedInput-root": { - borderRadius: "25px", - bgcolor: "white", - height: "40px", - } - }} - /> */} - + }, + }} + > + Менее 3х лет + 3-5 лет + 5+ лет + {/* Третья линия */} - + setPrice(e.target.value)} variant="outlined" InputProps={{ - endAdornment: , + endAdornment: ( + + {getCurrencySymbol()} + + ), }} sx={{ "& .MuiOutlinedInput-root": { - borderRadius: "2vw", + borderRadius: isMobile ? "5vw" : "2vw", bgcolor: "white", - height: "3.5vw", - width: "83%", + height: isMobile ? "4.5vw" : "2vw", + width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", - fontSize: "1.5vw", + fontSize: isMobile ? "2.5vw" : "1.2vw", color: "#C27664", - "& fieldset": { - border: "none" + "& fieldset": { + border: "none", }, - } + }, }} /> @@ -220,65 +618,67 @@ function CalculatorPage() { onChange={(e) => setCurrency(e.target.value as string)} displayEmpty sx={{ - borderRadius: "2vw", + borderRadius: isMobile ? "5vw" : "2vw", bgcolor: "white", - height: "3.5vw", - width: "83%", + height: isMobile ? "4.5vw" : "2vw", + width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", - fontSize: "1.5vw", + fontSize: isMobile ? "2.5vw" : "1.2vw", color: "#C27664", "& .MuiOutlinedInput-notchedOutline": { - border: "none" - } + border: "none", + }, + "& .MuiSvgIcon-root": { + fontSize: isMobile ? "5vw" : "2vw", + color: "#C27664", + }, }} > - USD Доллар США - EUR Евро - CNY Юань + + USD Доллар США{" "} + {isLoading + ? "..." + : `(курс: ${ + currencyRates?.Valute?.USD?.Value?.toFixed(2) || "93.00" + })`} + + + EUR Евро{" "} + {isLoading + ? "..." + : `(курс: ${ + currencyRates?.Valute?.EUR?.Value?.toFixed(2) || + "101.00" + })`} + + + CNY Юань{" "} + {isLoading + ? "..." + : `(курс: ${ + currencyRates?.Valute?.CNY?.Value?.toFixed(2) || "12.80" + })`} + + RUB Рубль - {/* - - */} - {/* Четвертая линия - Валюта */} - {/* - - - - */} - {/* Пятая линия */} - + setEngineType(e.target.value as string)} displayEmpty sx={{ - borderRadius: "2vw", + borderRadius: isMobile ? "5vw" : "2vw", bgcolor: "white", - height: "3.5vw", - width: "75%", + height: isMobile ? "4.5vw" : "2vw", + width: "100%", border: "0.1vw solid #C27664", fontFamily: "Unbounded", - fontSize: "1.5vw", + fontSize: isMobile ? "2.5vw" : "1.2vw", color: "#C27664", "& .MuiOutlinedInput-notchedOutline": { - border: "none" - } + border: "none", + }, + "& .MuiSvgIcon-root": { + fontSize: isMobile ? "5vw" : "2vw", + color: "#C27664", + }, }} > Бензиновый @@ -312,298 +716,370 @@ function CalculatorPage() { - {/* Шестая линия */} - - + - Тип гибрида - - - - - + Тип гибрида + + + + + + - {/* Седьмая линия */} - - setEngineVolume(e.target.value)} - variant="outlined" + {/* Седьмая линия - Объем двигателя */} + + - - см³ - - - - {/* Восьмая линия */} - - setEnginePower(e.target.value)} - variant="outlined" - sx={{ - "& .MuiOutlinedInput-root": { - borderRadius: "2vw", - bgcolor: "white", - height: "3.5vw", - width: "98%", - border: "0.1vw solid #C27664", - fontFamily: "Unbounded", - fontSize: "1.4vw", - color: "#C27664", - "& fieldset": { - border: "none" + setEngineVolume(e.target.value)} + variant="outlined" + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: isMobile ? "5vw" : "2vw", + bgcolor: "white", + height: isMobile ? "4.5vw" : "2vw", + width: "100%", + border: "0.1vw solid #C27664", + fontFamily: "Unbounded", + fontSize: isMobile ? "2.5vw" : "1.2vw", + color: "#C27664", + "& fieldset": { + border: "none", + }, }, - } - }} - /> - - setEnginePowerUnit("л.с.")} - sx={{ - color: "#C27664", - "&.Mui-checked": { color: "#C27664" }, - "& .MuiSvgIcon-root": { - fontSize: "1.5vw" - } - }} - /> - } - label="л.с." - sx={{ - "& .MuiFormControlLabel-label": { - fontSize: "2vw", - fontFamily: "Unbounded", - color: "#C27664", - } }} /> - setEnginePowerUnit("кВт")} - sx={{ - color: "#C27664", - "&.Mui-checked": { color: "#C27664" }, - "& .MuiSvgIcon-root": { - fontSize: "1.5vw" - } - }} - /> - } - label="кВт" - sx={{ - "& .MuiFormControlLabel-label": { - fontSize: "2vw", - fontFamily: "Unbounded", - color: "#C27664", - } - }} - /> - - - - {/* Девятая линия */} - - setElectricMotorPower(e.target.value)} - variant="outlined" - sx={{ - "& .MuiOutlinedInput-root": { - borderRadius: "2vw", - bgcolor: "white", - height: "3.5vw", - width: "112%", - border: "0.1vw solid #C27664", + - - setElectricMotorPowerUnit("л.с.")} - sx={{ - color: "#C27664", - "&.Mui-checked": { color: "#C27664" }, - "& .MuiSvgIcon-root": { - fontSize: "1.5vw" - } - }} - /> - } - label="л.с." - sx={{ - "& .MuiFormControlLabel-label": { - fontSize: "2vw", - fontFamily: "Unbounded", - color: "#C27664", - } }} - /> - setElectricMotorPowerUnit("кВт")} - sx={{ - color: "#C27664", - "&.Mui-checked": { color: "#C27664" }, - "& .MuiSvgIcon-root": { - fontSize: "1.5vw" - } - }} - /> - } - label="кВт" - sx={{ - "& .MuiFormControlLabel-label": { - fontSize: "2vw", - fontFamily: "Unbounded", - color: "#C27664", - } - }} - /> + > + см³ + - + - {/* Десятая линия */} - - + - ДВС vs ЭД - - - ЭД"} - onChange={() => setPowerRatio("ДВС > ЭД")} - sx={{ - color: "#C27664", - "&.Mui-checked": { color: "#C27664" }, - }} - /> - } - label="ДВС > ЭД" - sx={{ - "& .MuiFormControlLabel-label": { - fontSize: "1.7vw", + setEnginePower(e.target.value)} + variant="outlined" + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: isMobile ? "5vw" : "2vw", + bgcolor: "white", + height: isMobile ? "4.5vw" : "2vw", + width: "100%", + border: "0.1vw solid #C27664", fontFamily: "Unbounded", + fontSize: isMobile ? "2.5vw" : "1.2vw", color: "#C27664", - } + "& fieldset": { + border: "none", + }, + }, }} /> - setPowerRatio("ДВС < ЭД")} - sx={{ - color: "#C27664", - "&.Mui-checked": { color: "#C27664" }, - }} - /> - } - label="ДВС < ЭД" - sx={{ - "& .MuiFormControlLabel-label": { - fontSize: "1.7vw", - fontFamily: "Unbounded", - color: "#C27664", - } + + > + setEnginePowerUnit("л.с.")} + sx={{ + color: "#C27664", + "&.Mui-checked": { color: "#C27664" }, + "& .MuiSvgIcon-root": { + fontSize: isMobile ? "5vw" : "1.8vw", + }, + }} + /> + } + label="л.с." + sx={{ + "& .MuiFormControlLabel-label": { + fontSize: isMobile ? "2.5vw" : "2vw", + fontFamily: "Unbounded", + color: "#C27664", + }, + }} + /> + setEnginePowerUnit("кВт")} + sx={{ + color: "#C27664", + "&.Mui-checked": { color: "#C27664" }, + "& .MuiSvgIcon-root": { + fontSize: isMobile ? "5vw" : "1.8vw", + }, + }} + /> + } + label="кВт" + sx={{ + "& .MuiFormControlLabel-label": { + fontSize: isMobile ? "3vw" : "2vw", + fontFamily: "Unbounded", + color: "#C27664", + }, + }} + /> + - + + + {/* Девятая линия - Мощность электродвигателя */} + + + setElectricMotorPower(e.target.value)} + variant="outlined" + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: isMobile ? "5vw" : "2vw", + bgcolor: "white", + height: isMobile ? "4.5vw" : "2vw", + width: "100%", + border: "0.1vw solid #C27664", + fontFamily: "Unbounded", + fontSize: isMobile ? "3vw" : "1.2vw", + color: "#C27664", + "& fieldset": { + border: "none", + }, + }, + }} + /> + + кВт + + + = + + + {electricMotorPower + ? `${(parseFloat(electricMotorPower) * 1.35962).toFixed( + 1 + )} л.с.` + : "0 л.с."} + + + + + {/* Десятая линия - ДВС vs ЭД */} + + + + {isMobile ? "Мощность ДВС vs ЭД" : "ДВС vs ЭД"} + + + ЭД"} + onChange={() => setPowerRatio("ДВС > ЭД")} + sx={{ + color: "#C27664", + "&.Mui-checked": { color: "#C27664" }, + "& .MuiSvgIcon-root": { + fontSize: isMobile ? "5vw" : "1.8vw", + }, + }} + /> + } + label="ДВС > ЭД" + sx={{ + "& .MuiFormControlLabel-label": { + fontSize: isMobile ? "3vw" : "1.3vw", + fontFamily: "Unbounded", + color: "#C27664", + }, + }} + /> + setPowerRatio("ДВС < ЭД")} + sx={{ + color: "#C27664", + "&.Mui-checked": { color: "#C27664" }, + "& .MuiSvgIcon-root": { + fontSize: isMobile ? "5vw" : "1.8vw", + }, + }} + /> + } + label="ДВС < ЭД" + sx={{ + "& .MuiFormControlLabel-label": { + fontSize: isMobile ? "3vw" : "1.3vw", + fontFamily: "Unbounded", + color: "#C27664", + }, + }} + /> + + + {/* Одиннадцатая линия */} - + Физ. лица @@ -636,25 +1116,38 @@ function CalculatorPage() { {/* Двенадцатая линия */} - + setEnhancedPermeability(!enhancedPermeability)} + onChange={() => + setEnhancedPermeability(!enhancedPermeability) + } sx={{ color: "#C27664", "&.Mui-checked": { color: "#C27664" }, + "& .MuiSvgIcon-root": { + fontSize: isMobile ? "5vw" : "2vw", + }, }} /> } label="Повышенной проходимости" - sx={{ + sx={{ "& .MuiFormControlLabel-label": { - fontSize: "2vw", + fontSize: isMobile ? "3vw" : "1.5vw", color: "#C27664", fontFamily: "Unbounded", - } + }, }} /> @@ -665,13 +1158,13 @@ function CalculatorPage() { variant="contained" onClick={handleCalculate} sx={{ - maxWidth: "30vw", + maxWidth: isMobile ? "50vw" : "30vw", bgcolor: "#C27664", color: "white", - fontSize: "2vw", - padding: "1vw", + fontSize: isMobile ? "3.5vw" : "2vw", + padding: isMobile ? "2vw" : "1vw", textTransform: "none", - borderRadius: "3vw", + borderRadius: isMobile ? "5vw" : "3vw", fontFamily: "Unbounded", position: "relative", left: "50%", @@ -682,86 +1175,331 @@ function CalculatorPage() { Рассчитать + {/* Блок с результатами расчета */} + {calculationResults && ( + + + Результаты расчета: + + + + + + + Стоимость авто: + + + {Number(calculationResults.priceInRub).toLocaleString( + "ru-RU" + )}{" "} + ₽ + + + + + + Таможенная пошлина: + + + {Number(calculationResults.customDuty).toLocaleString( + "ru-RU" + )}{" "} + ₽ + + + + + + Акциз: + + + {Number(calculationResults.exciseTax).toLocaleString( + "ru-RU" + )}{" "} + ₽ + + + + + + НДС: + + + {Number(calculationResults.vat).toLocaleString("ru-RU")} ₽ + + + + + + Утилизационный сбор: + + + {Number(calculationResults.utilityFee).toLocaleString( + "ru-RU" + )}{" "} + ₽ + + + + + + + + + + + Всего таможенных платежей: + + + + + {Number(calculationResults.totalFees).toLocaleString("ru-RU")}{" "} + ₽ + + + + + + Итоговая стоимость с авто: + + + + + {Number(calculationResults.totalWithCar).toLocaleString( + "ru-RU" + )}{" "} + ₽ + + + + + )} {/* Правая часть - описание */} - - - РАСЧИТАЙТЕ СТОИМОСТЬ ДОСТАВКИ! - + + РАСЧИТАЙТЕ{" "} + СТОИМОСТЬ ДОСТАВКИ! + - - С помощью калькулятора таможенных платежей за автомобиль - + + С помощью калькулятора + таможенных платежей за + автомобиль + - - - Обсуждаем ваш запрос и бюджет, подбираем варианты. Заключаем договор и вносим аванс (100 000 P, возвращается, если не подберем авто). - - - Выкупаем авто - - - Оформляем документы и доставляем авто в РФ. На всех этапах машина застрахована. - - - Проходим таможню, делаем документы для регистрации. - - - Передаём авто вам - с актом и финальной оплатой. - + + Обсуждаем ваш запрос и бюджет, подбираем варианты. Заключаем + договор и вносим аванс (100 000 P, возвращается, если не + подберем авто). + + + Выкупаем авто + + + Оформляем документы и доставляем авто в РФ. На всех этапах + машина застрахована. + + + Проходим таможню, делаем документы для регистрации. + + + Передаём авто вам - с актом и финальной оплатой. + + - + )} ); diff --git a/src/pages/DeliveryPage.tsx b/src/pages/DeliveryPage.tsx index 4bde7b2..82603a9 100644 --- a/src/pages/DeliveryPage.tsx +++ b/src/pages/DeliveryPage.tsx @@ -47,27 +47,45 @@ function DeliveryPage() { fontSize: isMobile ? "6vw" : "4vw", fontWeight: "bold", marginBottom: "1vw", - maxWidth: isMobile ? "auto" :"50vw", + maxWidth: isMobile ? "auto" : "50vw", }} > Мы доставляем автомобили из{" "} США, Китая и Кореи - + usa china korea @@ -82,7 +100,7 @@ function DeliveryPage() { top: isMobile ? "0vw" : "-29vw", left: isMobile ? "50%" : "56vw", transform: isMobile ? "translateX(-50%)" : "none", - borderRadius: "3vw", + borderRadius: isMobile ? "5vw" : "3vw", border: "0.3vw solid #C27664", }} > diff --git a/src/utils/currencyService.ts b/src/utils/currencyService.ts new file mode 100644 index 0000000..e83ce81 --- /dev/null +++ b/src/utils/currencyService.ts @@ -0,0 +1,63 @@ +// Интерфейсы для данных от CBR XML Daily API +export interface CurrencyRate { + ID: string; + NumCode: string; + CharCode: string; + Nominal: number; + Name: string; + Value: number; + Previous: number; +} + +export interface CurrencyRatesResponse { + Date: string; + PreviousDate: string; + PreviousURL: string; + Timestamp: string; + Valute: { + [key: string]: CurrencyRate; + }; +} + +// Получение курсов валют с API cbr-xml-daily.ru +export const fetchCurrencyRates = async (): Promise => { + try { + const response = await fetch('https://www.cbr-xml-daily.ru/daily_json.js'); + + if (!response.ok) { + throw new Error(`Ошибка HTTP: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error('Ошибка при получении курсов валют:', error); + throw error; + } +}; + +// Функция для получения курса конкретной валюты (USD, EUR, CNY) +export const getCurrencyRate = (rates: CurrencyRatesResponse | null, code: string): number => { + if (!rates || !rates.Valute || !rates.Valute[code]) { + // Резервные значения на случай недоступности API + const fallbackRates: Record = { + USD: 93, + EUR: 101, + CNY: 12.8 + }; + return fallbackRates[code] || 1; + } + + return rates.Valute[code].Value; +}; + +// Преобразование кода валюты из пользовательского формата в формат API +export const mapCurrencyCodeToApiCode = (userCurrency: string): string => { + const currencyMap: Record = { + "USD Доллар США": "USD", + "EUR Евро": "EUR", + "CNY Юань": "CNY", + "RUB Рубль": "RUB" + }; + + return currencyMap[userCurrency] || userCurrency; +}; \ No newline at end of file diff --git a/src/utils/scrollUtils.ts b/src/utils/scrollUtils.ts index 3c03451..4d705b0 100644 --- a/src/utils/scrollUtils.ts +++ b/src/utils/scrollUtils.ts @@ -12,7 +12,7 @@ export const scrollToAnchor = ( const element = document.querySelector(anchor); if (element) { // Получаем высоту хедера (в соответствии с размерами в Header.tsx) - const headerHeight = isMobile ? window.innerWidth * 0.15 : window.innerWidth * 0.10; // 15vw или 10vw + const headerHeight = isMobile ? window.innerWidth * 0.15 : window.innerWidth * 0.05; // 15vw или 10vw // Получаем позицию элемента относительно верха страницы const elementPosition = element.getBoundingClientRect().top + window.scrollY;