/
gitverse
/
studio-baker-template
Обзор
Документация
Войти
/
gitverse
/
studio-baker-template
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
master
src/components/sections/Menu.tsx
177 строк
8 KB
studio-agent
GigaStudio шаблон Пекарня
12 май 2026, 15:25
12 май 2026, 15:25
4e87baf
Код
Авторство
О чём код?
"use client"; import { useState } from "react"; // 6. Удаляем Framer Motion для оптимизации производительности // import { motion } from "framer-motion"; import Image from "next/image"; import { UtensilsCrossed, ArrowUpDown, TrendingUp, Clock } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; const PRODUCTS = [ { id: 2, name: "Классический Багет", category: "Breads", price: 150, desc: "Традиционный французский рецепт", img: "/Images/Menu/baget.jpg" }, { id: 3, name: "Чиабатта", category: "Breads", price: 180, desc: "Воздушная структура, хрустящая корка", img: "/Images/Menu/chibatta.jpg" }, { id: 4, name: "Сливочный Круассан", category: "Pastries", price: 120, desc: "Слоистый, маслянистый, золотистый", img: "/Images/Menu/croissant.jpg" }, { id: 5, name: "Булочка с черникой", category: "Pastries", price: 145, desc: "Начинка из свежих ягод", img: "/Images/Menu/bulochkachernika.jpg" }, { id: 6, name: "Шоколадный Кекс", category: "Pastries", price: 110, desc: "С кусочками горького шоколада", img: "/Images/Menu/chocolatecake.jpg" }, { id: 7, name: "Торт Тройной Шоколад", category: "Cakes", price: 3500, desc: "Темный, молочный и белый шоколад", img: "/Images/Menu/chokTort.jpg" }, { id: 8, name: "Красный Бархат", category: "Cakes", price: 3100, desc: "Классический крем-чиз", img: "/Images/Menu/red.jpg" }, { id: 9, name: "Нью-Йорк Чизкейк", category: "Cakes", price: 2900, desc: "Нежный и сливочный", img: "/Images/Menu/newyork.jpg" }, ]; const CATEGORIES = [ { id: "All", label: "Все" }, { id: "Breads", label: "Хлеб" }, { id: "Pastries", label: "Выпечка" }, { id: "Cakes", label: "Торты" }, { id: "Desserts", label: "Десерты" }, { id: "Drinks", label: "Напитки" } ]; const SORT_OPTIONS = [ { id: "popular", label: "Популярное", icon: TrendingUp }, { id: "price-low", label: "Сначала дешевле", icon: ArrowUpDown }, { id: "price-high", label: "Сначала дороже", icon: ArrowUpDown }, { id: "newest", label: "Новинки", icon: Clock }, ]; export default function Menu() { const [activeTab, setActiveTab] = useState("All"); const [filter, setFilter] = useState("popular"); // Apply Category Filter let result = activeTab === "All" ? [...PRODUCTS] : PRODUCTS.filter(p => p.category === activeTab); // Apply Sort Filter if (filter === "price-low") { result.sort((a, b) => a.price - b.price); } else if (filter === "price-high") { result.sort((a, b) => b.price - a.price); } else if (filter === "newest") { result.sort((a, b) => b.id - a.id); } else if (filter === "popular") { result.sort((a, b) => b.id - a.id); } const filteredProducts = result; const handleViewAll = () => { setActiveTab("All"); }; return ( <section id="menu" className="py-20 bg-[#FDFBF7]"> <div className="container"> <div className="text-center mb-12"> <span className="text-[#d97706] font-bold tracking-widest uppercase mb-2 block">02. Наше Меню</span> <h2 className="font-playfair text-4xl md:text-5xl font-bold text-[#78350F] mb-4"> Свежая выпечка каждый день </h2> <div className="h-1 w-20 bg-[#d97706] mx-auto rounded-full" /> </div> {/* Tabs */} <div className="flex flex-wrap justify-center gap-4 mb-4"> {CATEGORIES.map((cat) => ( <button key={cat.id} onClick={() => setActiveTab(cat.id)} className={cn( "px-6 py-2 rounded-full transition-all duration-200 font-medium text-sm md:text-base border border-transparent", activeTab === cat.id ? "bg-[#78350F] text-white shadow-md" : "bg-white text-gray-600 hover:text-[#78350F] hover:border-[#78350F]/20 hover:bg-[#78350F]/5" )} > {cat.label} </button> ))} </div> {/* Sort Filters */} <div className="flex justify-center md:justify-end mb-6 border-b border-gray-200 pb-4"> <div className="flex flex-wrap gap-2"> {SORT_OPTIONS.map((opt) => ( <button key={opt.id} onClick={() => setFilter(opt.id)} className={cn( "flex items-center gap-2 px-4 py-1.5 rounded-full text-sm transition-all duration-200", filter === opt.id ? "bg-[#d97706]/10 text-[#d97706] font-semibold border border-[#d97706]/20" : "text-gray-500 hover:text-[#78350F] hover:bg-[#78350F]/5" )} > <opt.icon className="w-3.5 h-3.5" /> {opt.label} </button> ))} </div> </div> {/* Content Area */} <div className="w-full relative"> {filteredProducts.length > 0 ? ( // 6. Заменяем motion.div на обычный div с CSS анимацией <div key={`grid-${activeTab}-${filter}`} className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 animate-fade-in" > {filteredProducts.map((product) => ( <div key={product.id} className="group bg-white rounded-xl overflow-hidden shadow-sm hover:shadow-2xl transition-all duration-300 border border-gray-100 flex flex-col h-full" > <div className="relative overflow-hidden h-64 w-full flex-shrink-0 bg-gray-100"> <Image src={product.img} alt={product.name} width={500} height={350} className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105" /> <div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors duration-300 pointer-events-none" /> </div> <div className="p-6 flex flex-col flex-grow"> <div className="flex justify-between items-start mb-2 gap-3"> <h3 className="font-playfair text-xl font-bold text-[#78350F] break-words flex-1 leading-tight">{product.name}</h3> <span className="text-[#d97706] font-bold text-lg whitespace-nowrap flex-shrink-0">{product.price} ₽</span> </div> <p className="text-gray-500 text-sm leading-relaxed">{product.desc}</p> </div> </div> ))} </div> ) : ( /* Empty State */ <div key={`empty-${activeTab}`} className="flex flex-col items-center justify-center w-full py-4 text-center border-2 border-dashed border-[#78350F]/10 rounded-2xl bg-[#d97706]/5 hover:bg-[#d97706]/10 transition-colors duration-500 animate-fade-in" > <div className="flex flex-col items-center"> <div className="animate-bounce"> <UtensilsCrossed className="w-10 h-10 text-[#d97706] mb-4" /> </div> <h3 className="font-cormorant text-2xl text-[#78350F] mb-1 font-semibold"> Скоро в меню </h3> <p className="text-gray-600 text-sm mb-4 max-w-sm mx-auto"> Мы печем новые вкусные рецепты. Следите за обновлениями! </p> <Button onClick={handleViewAll} variant="outline" className="border-[#d97706] text-[#d97706] hover:bg-[#d97706] hover:text-white px-8 h-10 rounded-full transition-all text-sm font-medium shadow-sm" > Посмотреть доступное </Button> </div> </div> )} </div> </div> </section> ); }