first commit

This commit is contained in:
Dhaverd 2026-09-03 04:57:35 +02:00
commit 2d5f88772a
14 changed files with 840 additions and 0 deletions

12
index.html Executable file
View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>АвтоДетали Про — Запчасти и жидкости для автомобилей</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

28
package.json Executable file
View File

@ -0,0 +1,28 @@
{
"name": "react-vite-tailwind",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"clsx": "2.1.1",
"react": "19.2.3",
"react-dom": "19.2.3",
"tailwind-merge": "3.4.0"
},
"devDependencies": {
"@tailwindcss/vite": "4.1.17",
"@types/node": "^22.0.0",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-react": "5.1.1",
"tailwindcss": "4.1.17",
"typescript": "5.9.3",
"vite": "7.2.4",
"vite-plugin-singlefile": "2.3.0"
}
}

BIN
public/images/cat-brakes.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
public/images/cat-engine.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

BIN
public/images/cat-filters.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
public/images/cat-oil.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
public/images/cat-suspension.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
public/images/hero-bg.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

733
src/App.tsx Executable file
View File

@ -0,0 +1,733 @@
import { useState } from "react";
const categories = [
{
id: 1,
name: "Двигатель",
image: "/images/cat-engine.jpg",
count: 1842,
icon: "🔧",
},
{
id: 2,
name: "Масла и жидкости",
image: "/images/cat-oil.jpg",
count: 634,
icon: "🛢️",
},
{
id: 3,
name: "Тормозная система",
image: "/images/cat-brakes.jpg",
count: 921,
icon: "⚙️",
},
{
id: 4,
name: "Фильтры",
image: "/images/cat-filters.jpg",
count: 488,
icon: "🔩",
},
{
id: 5,
name: "Подвеска и рулевое",
image: "/images/cat-suspension.jpg",
count: 1103,
icon: "🚗",
},
{
id: 6,
name: "Электрика",
image: "/images/cat-engine.jpg",
count: 765,
icon: "⚡",
},
];
const products = [
{
id: 1,
name: "Масло моторное Castrol EDGE 5W-40",
category: "Масла и жидкости",
price: 2890,
oldPrice: 3400,
rating: 4.8,
reviews: 214,
badge: "Хит",
badgeColor: "bg-red-500",
image: "/images/cat-oil.jpg",
brand: "Castrol",
},
{
id: 2,
name: "Тормозные колодки Brembo P 85 020",
category: "Тормозная система",
price: 1650,
oldPrice: null,
rating: 4.9,
reviews: 87,
badge: "Новинка",
badgeColor: "bg-green-500",
image: "/images/cat-brakes.jpg",
brand: "Brembo",
},
{
id: 3,
name: "Воздушный фильтр Mann C 25 114",
category: "Фильтры",
price: 490,
oldPrice: 620,
rating: 4.6,
reviews: 132,
badge: "Скидка",
badgeColor: "bg-orange-500",
image: "/images/cat-filters.jpg",
brand: "Mann",
},
{
id: 4,
name: "Амортизатор передний KYB 335804",
category: "Подвеска и рулевое",
price: 3200,
oldPrice: null,
rating: 4.7,
reviews: 56,
badge: null,
badgeColor: "",
image: "/images/cat-suspension.jpg",
brand: "KYB",
},
{
id: 5,
name: "Антифриз Sintec Lux -40°C (зелёный)",
category: "Масла и жидкости",
price: 380,
oldPrice: 450,
rating: 4.5,
reviews: 301,
badge: "Хит",
badgeColor: "bg-red-500",
image: "/images/cat-oil.jpg",
brand: "Sintec",
},
{
id: 6,
name: "Ремень ГРМ Gates K015591XS",
category: "Двигатель",
price: 2100,
oldPrice: null,
rating: 4.9,
reviews: 44,
badge: null,
badgeColor: "",
image: "/images/cat-engine.jpg",
brand: "Gates",
},
{
id: 7,
name: "Свечи NGK Iridium IX BKR6EIX (4 шт.)",
category: "Двигатель",
price: 1280,
oldPrice: 1560,
rating: 4.8,
reviews: 178,
badge: "Скидка",
badgeColor: "bg-orange-500",
image: "/images/cat-engine.jpg",
brand: "NGK",
},
{
id: 8,
name: "Масло трансмиссионное Mobil ATF 3309",
category: "Масла и жидкости",
price: 1740,
oldPrice: null,
rating: 4.7,
reviews: 63,
badge: "Новинка",
badgeColor: "bg-green-500",
image: "/images/cat-oil.jpg",
brand: "Mobil",
},
];
const brands = [
"Castrol", "Bosch", "NGK", "Brembo", "KYB", "Mann", "Gates", "Mobil",
"Valeo", "Denso", "Monroe", "Febi",
];
const reviews = [
{
name: "Алексей К.",
date: "12 мая 2025",
rating: 5,
text: "Заказывал масло и фильтры — пришли быстро, всё оригинальное. Упаковка целая, цена лучшая в городе. Буду заказывать снова!",
car: "Toyota Camry 2019",
},
{
name: "Мария Д.",
date: "3 июня 2025",
rating: 5,
text: "Очень удобный сайт, нашла нужные тормозные колодки за минуту. Доставка на следующий день, качество отличное.",
car: "Kia Rio 2021",
},
{
name: "Дмитрий В.",
date: "28 апреля 2025",
rating: 4,
text: "Хороший ассортимент, есть редкие позиции. Немного долго ждал ответа от поддержки, но в итоге всё решили.",
car: "BMW 3 Series 2018",
},
];
function StarRating({ rating, size = "sm" }: { rating: number; size?: "sm" | "md" }) {
const sizeClass = size === "sm" ? "text-xs" : "text-sm";
return (
<div className={`flex items-center gap-0.5 ${sizeClass}`}>
{[1, 2, 3, 4, 5].map((s) => (
<span key={s} className={s <= Math.round(rating) ? "text-yellow-400" : "text-gray-300"}>
</span>
))}
</div>
);
}
export default function App() {
const [cartCount, setCartCount] = useState(0);
const [wishlist, setWishlist] = useState<number[]>([]);
const [searchValue, setSearchValue] = useState("");
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [addedToCart, setAddedToCart] = useState<number | null>(null);
const [selectedBrand] = useState<string | null>(null);
const handleAddToCart = (id: number) => {
setCartCount((c) => c + 1);
setAddedToCart(id);
setTimeout(() => setAddedToCart(null), 1500);
};
const toggleWishlist = (id: number) => {
setWishlist((prev) =>
prev.includes(id) ? prev.filter((w) => w !== id) : [...prev, id]
);
};
const filteredProducts = products.filter((p) => {
const matchSearch =
searchValue === "" ||
p.name.toLowerCase().includes(searchValue.toLowerCase()) ||
p.brand.toLowerCase().includes(searchValue.toLowerCase()) ||
p.category.toLowerCase().includes(searchValue.toLowerCase());
const matchBrand = !selectedBrand || p.brand === selectedBrand;
return matchSearch && matchBrand;
});
return (
<div className="min-h-screen bg-gray-50 font-sans">
{/* Top bar */}
<div className="bg-gray-900 text-gray-300 text-xs py-2">
<div className="max-w-7xl mx-auto px-4 flex flex-col sm:flex-row items-center justify-between gap-1">
<div className="flex items-center gap-4">
<span>📞 +7 (800) 555-35-35</span>
<span className="hidden sm:inline">|</span>
<span className="hidden sm:inline"> info@avtodetal-pro.ru</span>
</div>
<div className="flex items-center gap-4">
<span>🕐 ПнПт: 9:0020:00, СбВс: 10:0018:00</span>
<span className="hidden sm:inline">|</span>
<span className="hidden sm:inline text-green-400 font-medium"> Бесплатная доставка от 3000</span>
</div>
</div>
</div>
{/* Header */}
<header className="bg-white shadow-md sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 py-3 flex items-center gap-4">
{/* Logo */}
<div className="flex items-center gap-2 shrink-0">
<div className="w-10 h-10 bg-red-600 rounded-lg flex items-center justify-center">
<span className="text-white text-xl font-black">А</span>
</div>
<div className="leading-tight">
<div className="text-lg font-black text-gray-900 tracking-tight">АвтоДетали</div>
<div className="text-xs text-red-600 font-semibold tracking-wider uppercase">Про</div>
</div>
</div>
{/* Search */}
<div className="flex-1 hidden sm:flex items-center">
<div className="relative w-full max-w-xl">
<input
type="text"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Поиск по запчастям, маслам, артикулу..."
className="w-full border-2 border-gray-200 focus:border-red-500 rounded-l-lg px-4 py-2.5 text-sm outline-none transition-colors"
/>
<button className="bg-red-600 hover:bg-red-700 text-white px-5 py-2.5 rounded-r-lg text-sm font-medium transition-colors">
🔍 Найти
</button>
</div>
</div>
{/* Actions */}
<div className="flex items-center gap-2 ml-auto">
<button className="hidden md:flex flex-col items-center text-gray-600 hover:text-red-600 transition-colors p-2">
<span className="text-xl">👤</span>
<span className="text-xs mt-0.5">Войти</span>
</button>
<button className="hidden md:flex flex-col items-center text-gray-600 hover:text-red-600 transition-colors p-2 relative">
<span className="text-xl"></span>
<span className="text-xs mt-0.5">Избранное</span>
{wishlist.length > 0 && (
<span className="absolute top-1 right-1 bg-red-500 text-white text-[10px] rounded-full w-4 h-4 flex items-center justify-center font-bold">
{wishlist.length}
</span>
)}
</button>
<button className="flex flex-col items-center text-gray-600 hover:text-red-600 transition-colors p-2 relative">
<span className="text-xl">🛒</span>
<span className="text-xs mt-0.5 hidden md:block">Корзина</span>
{cartCount > 0 && (
<span className="absolute top-1 right-1 bg-red-500 text-white text-[10px] rounded-full w-4 h-4 flex items-center justify-center font-bold">
{cartCount}
</span>
)}
</button>
<button
className="md:hidden p-2 text-gray-600"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
<div className="w-6 flex flex-col gap-1">
<span className="block h-0.5 bg-gray-700 rounded"></span>
<span className="block h-0.5 bg-gray-700 rounded"></span>
<span className="block h-0.5 bg-gray-700 rounded"></span>
</div>
</button>
</div>
</div>
{/* Nav */}
<nav className="hidden md:block bg-red-600">
<div className="max-w-7xl mx-auto px-4">
<ul className="flex items-center gap-0 text-sm font-medium text-white">
{["Каталог", "Масла и жидкости", "Фильтры", "Тормоза", "Подвеска", "Электрика", "Аксессуары", "Акции"].map(
(item) => (
<li key={item}>
<a
href="#"
className="block px-4 py-3 hover:bg-red-700 transition-colors whitespace-nowrap"
>
{item}
</a>
</li>
)
)}
</ul>
</div>
</nav>
{/* Mobile menu */}
{mobileMenuOpen && (
<div className="md:hidden bg-white border-t px-4 py-3 flex flex-col gap-2">
<input
type="text"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Поиск..."
className="border border-gray-200 rounded-lg px-3 py-2 text-sm outline-none w-full"
/>
{["Каталог", "Масла и жидкости", "Фильтры", "Тормоза", "Подвеска", "Электрика", "Акции"].map(
(item) => (
<a key={item} href="#" className="text-sm text-gray-700 py-1 border-b border-gray-100">
{item}
</a>
)
)}
</div>
)}
</header>
{/* Hero */}
<section
className="relative bg-gray-900 text-white overflow-hidden"
style={{ minHeight: 420 }}
>
<img
src="/images/hero-bg.jpg"
alt="Автозапчасти"
className="absolute inset-0 w-full h-full object-cover opacity-40"
/>
<div className="relative z-10 max-w-7xl mx-auto px-4 py-16 md:py-24">
<div className="max-w-2xl">
<div className="inline-block bg-red-600 text-white text-xs font-bold px-3 py-1 rounded-full uppercase tracking-wider mb-4">
🔥 Летняя распродажа скидки до 30%
</div>
<h1 className="text-3xl md:text-5xl font-black leading-tight mb-4">
Запчасти и расходники<br />
<span className="text-red-400">для любого автомобиля</span>
</h1>
<p className="text-gray-300 text-base md:text-lg mb-8">
Более 50 000 наименований в наличии. Оригинальные и аналоговые запчасти,
масла, фильтры, тормоза и многое другое. Доставка по всей России.
</p>
<div className="flex flex-wrap gap-3">
<button className="bg-red-600 hover:bg-red-700 text-white font-bold px-6 py-3 rounded-lg transition-colors text-sm md:text-base">
Перейти в каталог
</button>
<button className="bg-white/10 hover:bg-white/20 border border-white/30 text-white font-semibold px-6 py-3 rounded-lg transition-colors text-sm md:text-base">
Подбор по марке авто
</button>
</div>
<div className="flex flex-wrap gap-6 mt-8 text-sm text-gray-300">
<div className="flex items-center gap-2">
<span className="text-green-400"></span> Гарантия подлинности
</div>
<div className="flex items-center gap-2">
<span className="text-green-400"></span> Возврат 30 дней
</div>
<div className="flex items-center gap-2">
<span className="text-green-400"></span> Доставка 13 дня
</div>
</div>
</div>
</div>
</section>
{/* VIN Search */}
<section className="bg-gray-800 py-5">
<div className="max-w-7xl mx-auto px-4">
<div className="flex flex-col md:flex-row items-center gap-3">
<div className="text-white font-semibold text-sm whitespace-nowrap">
🚘 Подбор по VIN:
</div>
<div className="flex flex-1 max-w-2xl w-full">
<input
type="text"
placeholder="Введите VIN номер вашего автомобиля..."
className="flex-1 bg-white/10 border border-white/20 text-white placeholder-gray-400 px-4 py-2.5 rounded-l-lg text-sm outline-none focus:border-red-400"
/>
<button className="bg-red-600 hover:bg-red-700 text-white px-5 py-2.5 rounded-r-lg text-sm font-semibold transition-colors">
Найти запчасти
</button>
</div>
<div className="text-gray-400 text-xs hidden md:block">
Введите 17-значный VIN для точного подбора
</div>
</div>
</div>
</section>
{/* Categories */}
<section className="max-w-7xl mx-auto px-4 py-12">
<h2 className="text-2xl font-black text-gray-900 mb-6">
Категории товаров
</h2>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-4">
{categories.map((cat) => (
<a
key={cat.id}
href="#"
className="group relative bg-white rounded-xl overflow-hidden shadow hover:shadow-lg transition-all border border-gray-100 hover:border-red-200"
>
<div className="h-28 overflow-hidden">
<img
src={cat.image}
alt={cat.name}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
<div className="absolute inset-0 bg-gradient-to-t from-gray-900/70 to-transparent" />
</div>
<div className="absolute bottom-0 left-0 right-0 p-2.5">
<div className="text-white font-bold text-xs leading-tight">{cat.name}</div>
<div className="text-gray-300 text-[10px] mt-0.5">{cat.count.toLocaleString()} товаров</div>
</div>
</a>
))}
</div>
</section>
{/* Popular Products */}
<section className="max-w-7xl mx-auto px-4 pb-12">
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-black text-gray-900">Популярные товары</h2>
<a href="#" className="text-red-600 hover:text-red-700 text-sm font-semibold">
Все товары
</a>
</div>
{filteredProducts.length === 0 ? (
<div className="text-center py-16 text-gray-500">
<div className="text-5xl mb-3">🔍</div>
<div className="font-semibold">Товары не найдены</div>
<div className="text-sm mt-1">Попробуйте изменить запрос</div>
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-5">
{filteredProducts.map((product) => (
<div
key={product.id}
className="bg-white rounded-xl shadow hover:shadow-lg transition-all border border-gray-100 hover:border-red-200 overflow-hidden flex flex-col"
>
<div className="relative h-44 overflow-hidden bg-gray-100">
<img
src={product.image}
alt={product.name}
className="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
/>
{product.badge && (
<span
className={`absolute top-2 left-2 ${product.badgeColor} text-white text-[10px] font-bold px-2 py-0.5 rounded-full uppercase`}
>
{product.badge}
</span>
)}
<button
onClick={() => toggleWishlist(product.id)}
className="absolute top-2 right-2 bg-white/80 hover:bg-white rounded-full w-7 h-7 flex items-center justify-center shadow transition-colors"
title="В избранное"
>
<span className={wishlist.includes(product.id) ? "text-red-500" : "text-gray-400"}>
</span>
</button>
</div>
<div className="p-3 flex flex-col flex-1">
<div className="text-[10px] text-red-600 font-semibold uppercase mb-1">{product.brand}</div>
<div className="text-sm font-semibold text-gray-800 leading-snug mb-2 flex-1">
{product.name}
</div>
<div className="flex items-center gap-2 mb-3">
<StarRating rating={product.rating} />
<span className="text-xs text-gray-400">({product.reviews})</span>
</div>
<div className="flex items-center gap-2 mb-3">
<span className="text-lg font-black text-gray-900">
{product.price.toLocaleString()}
</span>
{product.oldPrice && (
<span className="text-sm text-gray-400 line-through">
{product.oldPrice.toLocaleString()}
</span>
)}
{product.oldPrice && (
<span className="text-xs text-red-600 font-bold bg-red-50 px-1.5 py-0.5 rounded">
{Math.round((1 - product.price / product.oldPrice) * 100)}%
</span>
)}
</div>
<button
onClick={() => handleAddToCart(product.id)}
className={`w-full py-2 rounded-lg text-sm font-bold transition-all ${
addedToCart === product.id
? "bg-green-500 text-white"
: "bg-red-600 hover:bg-red-700 text-white"
}`}
>
{addedToCart === product.id ? "✔ Добавлено!" : "В корзину"}
</button>
</div>
</div>
))}
</div>
)}
</section>
{/* Brands */}
<section className="bg-white border-y border-gray-200 py-10">
<div className="max-w-7xl mx-auto px-4">
<h2 className="text-2xl font-black text-gray-900 mb-6 text-center">Бренды</h2>
<div className="flex flex-wrap items-center justify-center gap-3">
{brands.map((brand) => (
<button
key={brand}
className="border-2 border-gray-200 hover:border-red-500 text-gray-600 hover:text-red-600 font-bold px-5 py-2.5 rounded-lg transition-all text-sm"
>
{brand}
</button>
))}
</div>
</div>
</section>
{/* Advantages */}
<section className="max-w-7xl mx-auto px-4 py-14">
<h2 className="text-2xl font-black text-gray-900 mb-8 text-center">Почему выбирают нас</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{[
{
icon: "🚚",
title: "Быстрая доставка",
desc: "Доставляем по всей России за 15 рабочих дней. Бесплатно при заказе от 3000 ₽.",
},
{
icon: "✅",
title: "Только оригинал",
desc: "Гарантируем подлинность товара. Работаем только с официальными дистрибьюторами.",
},
{
icon: "🔄",
title: "Возврат 30 дней",
desc: "Если товар не подошёл — вернём деньги без лишних вопросов в течение 30 дней.",
},
{
icon: "🎧",
title: "Поддержка 24/7",
desc: "Наши специалисты помогут подобрать запчасти и ответят на любые вопросы.",
},
].map((adv) => (
<div
key={adv.title}
className="bg-white rounded-xl p-6 shadow border border-gray-100 text-center hover:border-red-200 hover:shadow-md transition-all"
>
<div className="text-4xl mb-3">{adv.icon}</div>
<div className="font-bold text-gray-900 mb-2">{adv.title}</div>
<div className="text-gray-500 text-sm leading-relaxed">{adv.desc}</div>
</div>
))}
</div>
</section>
{/* Reviews */}
<section className="bg-gray-100 py-14">
<div className="max-w-7xl mx-auto px-4">
<div className="flex items-center justify-between mb-8">
<h2 className="text-2xl font-black text-gray-900">Отзывы покупателей</h2>
<div className="flex items-center gap-2">
<StarRating rating={4.8} size="md" />
<span className="font-bold text-gray-800">4.8</span>
<span className="text-gray-500 text-sm">/ 5 (1 248 отзывов)</span>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-5">
{reviews.map((rev, idx) => (
<div key={idx} className="bg-white rounded-xl p-5 shadow border border-gray-100">
<div className="flex items-center gap-3 mb-3">
<div className="w-10 h-10 rounded-full bg-red-600 text-white flex items-center justify-center font-bold text-lg">
{rev.name[0]}
</div>
<div>
<div className="font-semibold text-gray-900 text-sm">{rev.name}</div>
<div className="text-gray-400 text-xs">{rev.date}</div>
</div>
<div className="ml-auto">
<StarRating rating={rev.rating} size="sm" />
</div>
</div>
<p className="text-gray-700 text-sm leading-relaxed mb-3">{rev.text}</p>
<div className="text-xs text-gray-400 bg-gray-50 rounded-lg px-3 py-1.5">
🚗 {rev.car}
</div>
</div>
))}
</div>
</div>
</section>
{/* Newsletter */}
<section className="bg-red-600 py-12">
<div className="max-w-2xl mx-auto px-4 text-center">
<h2 className="text-2xl font-black text-white mb-2">Не пропустите акции!</h2>
<p className="text-red-100 text-sm mb-6">
Подпишитесь на рассылку и получите скидку 10% на первый заказ
</p>
<div className="flex flex-col sm:flex-row gap-2 max-w-md mx-auto">
<input
type="email"
placeholder="Ваш email..."
className="flex-1 px-4 py-3 rounded-lg text-sm outline-none text-gray-800"
/>
<button className="bg-gray-900 hover:bg-gray-800 text-white font-bold px-6 py-3 rounded-lg text-sm transition-colors whitespace-nowrap">
Подписаться
</button>
</div>
</div>
</section>
{/* Footer */}
<footer className="bg-gray-900 text-gray-400 py-12">
<div className="max-w-7xl mx-auto px-4">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8 mb-8">
<div>
<div className="flex items-center gap-2 mb-4">
<div className="w-8 h-8 bg-red-600 rounded-lg flex items-center justify-center">
<span className="text-white font-black">А</span>
</div>
<div>
<div className="text-white font-black text-sm">АвтоДетали Про</div>
</div>
</div>
<p className="text-xs leading-relaxed text-gray-500">
Интернет-магазин автозапчастей. Более 50 000 товаров в наличии для всех марок автомобилей.
</p>
<div className="flex gap-3 mt-4">
{["VK", "TG", "OK"].map((s) => (
<a
key={s}
href="#"
className="w-8 h-8 bg-gray-700 hover:bg-red-600 rounded-lg flex items-center justify-center text-xs font-bold text-white transition-colors"
>
{s}
</a>
))}
</div>
</div>
<div>
<div className="text-white font-bold text-sm mb-4">Каталог</div>
<ul className="space-y-2 text-xs">
{["Двигатель", "Тормозная система", "Подвеска", "Масла и жидкости", "Фильтры", "Электрика", "Аксессуары"].map(
(item) => (
<li key={item}>
<a href="#" className="hover:text-white hover:text-red-400 transition-colors">
{item}
</a>
</li>
)
)}
</ul>
</div>
<div>
<div className="text-white font-bold text-sm mb-4">Покупателям</div>
<ul className="space-y-2 text-xs">
{["Как оформить заказ", "Доставка и оплата", "Возврат товара", "Гарантия", "Отзывы", "Бонусная программа"].map(
(item) => (
<li key={item}>
<a href="#" className="hover:text-red-400 transition-colors">
{item}
</a>
</li>
)
)}
</ul>
</div>
<div>
<div className="text-white font-bold text-sm mb-4">Контакты</div>
<ul className="space-y-2 text-xs">
<li>📞 +7 (800) 555-35-35 (бесплатно)</li>
<li> info@avtodetal-pro.ru</li>
<li>📍 г. Москва, ул. Автомобильная, 12</li>
<li>🕐 ПнПт: 9:0020:00</li>
<li>🕐 СбВс: 10:0018:00</li>
</ul>
<div className="mt-4 text-xs">
<div className="text-white font-semibold mb-1">Принимаем оплату:</div>
<div className="flex gap-2 flex-wrap">
{["Visa", "MC", "МИР", "SBP"].map((pay) => (
<span key={pay} className="bg-gray-700 text-gray-300 text-[10px] px-2 py-1 rounded font-mono">
{pay}
</span>
))}
</div>
</div>
</div>
</div>
<div className="border-t border-gray-800 pt-6 text-xs text-center text-gray-600">
© 2025 АвтоДетали Про. Все права защищены. ИНН: 7712345678 | ОГРН: 1157746123456
</div>
</div>
</footer>
</div>
);
}

1
src/index.css Executable file
View File

@ -0,0 +1 @@
@import "tailwindcss";

10
src/main.tsx Executable file
View File

@ -0,0 +1,10 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);

6
src/utils/cn.ts Executable file
View File

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

31
tsconfig.json Executable file
View File

@ -0,0 +1,31 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"types": ["node"],
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Path mapping */
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src", "vite.config.ts"]
}

19
vite.config.ts Executable file
View File

@ -0,0 +1,19 @@
import path from "path";
import { fileURLToPath } from "url";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import { viteSingleFile } from "vite-plugin-singlefile";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss(), viteSingleFile()],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
});