feat: core pages and components migrated to Next.js (Home, Shop, Product)

This commit is contained in:
پارسا آقایی 2026-06-12 01:08:04 +03:30
parent 72d2a5600a
commit 3de0408e6f
11 changed files with 246 additions and 138 deletions

View File

@ -12,12 +12,13 @@ import LoginModal from "../components/LoginModal";
import B2BPortal from "../components/B2BPortal";
import { useUserStore } from "../lib/store/userStore";
import { useSettingsStore } from "../lib/store/settingsStore";
import { useUIStore } from "../lib/store/uiStore";
export default function ClientLayout({ children }: { children: React.ReactNode }) {
const [isCartOpen, setIsCartOpen] = useState(false);
const [showB2B, setShowB2B] = useState(false);
const [showLoginModal, setShowLoginModal] = useState(false);
const [advisorData, setAdvisorData] = useState<any>(null);
const {
isCartOpen, isLoginModalOpen, isB2BPortalOpen, advisorData,
setCartOpen, setLoginModalOpen, setB2BPortalOpen, clearAdvisorData
} = useUIStore();
const pathname = usePathname();
const router = useRouter();
@ -72,30 +73,30 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
onNavigate={handleNavigate}
onShopNavigate={navigateToShop}
currentView={currentView}
onCartOpen={() => setIsCartOpen(true)}
onCartOpen={() => setCartOpen(true)}
onSearch={handleSearch}
onB2BOpen={() => setShowB2B(true)}
onB2BOpen={() => setB2BPortalOpen(true)}
/>
<main className="min-h-screen">
{children}
</main>
<Footer onNavigate={handleNavigate} onShopNavigate={navigateToShop} onB2BOpen={() => setShowB2B(true)} />
<Footer onNavigate={handleNavigate} onShopNavigate={navigateToShop} onB2BOpen={() => setB2BPortalOpen(true)} />
{showB2B && <B2BPortal onClose={() => setShowB2B(false)} />}
{isB2BPortalOpen && <B2BPortal onClose={() => setB2BPortalOpen(false)} />}
<CartDrawer
isOpen={isCartOpen}
onClose={() => setIsCartOpen(false)}
onCheckout={() => { setIsCartOpen(false); router.push('/checkout'); }}
onClose={() => setCartOpen(false)}
onCheckout={() => { setCartOpen(false); router.push('/checkout'); }}
onShopNavigate={navigateToShop}
/>
<LoginModal
isOpen={showLoginModal}
isOpen={isLoginModalOpen}
onClose={() => {
setShowLoginModal(false);
setAdvisorData(null);
setLoginModalOpen(false);
clearAdvisorData();
}}
onLogin={() => {
setShowLoginModal(false);
setLoginModalOpen(false);
}}
petName={advisorData?.name}
isAdvisorContext={!!advisorData}

View File

@ -1,65 +1,116 @@
import Image from "next/image";
import Hero from "../components/Hero";
import SmartAdvisor from "../components/SmartAdvisor";
import FeaturedProducts from "../components/FeaturedProducts";
import VetGallery from "../components/VetGallery";
import Link from 'next/link';
export default function Home() {
return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
</div>
</main>
</div>
<>
<Hero />
{/* SmartAdvisor uses uiStore/userStore internally and handles routing automatically */}
<SmartAdvisor />
<FeaturedProducts />
<VetGallery onNavigate={() => {}} />
{/* Navigation to Sections */}
<section className="py-20 bg-medical-gray-50 border-y border-medical-gray-100">
<div className="max-w-7xl mx-auto px-4 grid md:grid-cols-2 gap-8">
<Link
href="/shop"
className="bg-white p-12 rounded-[3.5rem] border border-medical-gray-200 shadow-sm hover:shadow-2xl hover:-translate-y-2 transition-all cursor-pointer group block"
>
<h3 className="text-3xl font-black text-medical-gray-900 mb-4 group-hover:text-canina-blue transition-colors italic">فروشگاه تخصصی</h3>
<p className="text-medical-gray-500 mb-8 leading-relaxed">دسترسی به تمامی محصولات بر اساس علائم بالینی و نیازهای پت شما.</p>
<div className="flex items-center gap-2 text-canina-blue font-black tracking-widest uppercase text-xs">
کاتالوگ کامل محصولات
<span className="text-lg"></span>
</div>
</Link>
<Link
href="/wiki"
className="bg-medical-gray-900 p-12 rounded-[3.5rem] text-white hover:shadow-2xl hover:-translate-y-2 transition-all cursor-pointer group block"
>
<h3 className="text-3xl font-black mb-4 group-hover:text-canina-blue transition-colors italic">دانشنامه علمی</h3>
<p className="text-white/60 mb-8 leading-relaxed">آشنایی با ترکیبات نایاب و استانداردهای دارویی محصولات آلمانی کانینا.</p>
<div className="flex items-center gap-2 text-canina-blue font-black tracking-widest uppercase text-xs">
مطالعه مقالات تخصصی
<span className="text-lg"></span>
</div>
</Link>
</div>
</section>
{/* About Section Teaser */}
<section className="py-24 bg-white overflow-hidden">
<div className="max-w-7xl mx-auto px-4 grid lg:grid-cols-2 gap-16 items-center">
<div className="relative">
<div className="absolute -top-10 -right-10 w-40 h-40 bg-canina-blue/5 rounded-full blur-3xl" />
<img
src="/assets/images/regenerated_image_1779109861747.png"
alt="Laboratory"
className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%]"
/>
<div className="absolute -bottom-6 -left-6 bg-canina-blue text-white p-8 rounded-3xl shadow-xl z-20">
<div className="text-4xl font-black mb-1 italic">کیفیت</div>
<div className="text-xs uppercase tracking-widest font-bold opacity-80">استانداردهای فوقدارویی</div>
</div>
</div>
<div>
<div className="text-canina-blue text-xs font-bold uppercase tracking-[0.2em] mb-4">میراث ما از آلمان</div>
<h2 className="text-3xl lg:text-5xl font-black text-medical-gray-900 leading-[1.2] mb-8">
چرا برند آلمانی Canina مرجع دامپزشکان است؟
</h2>
<div className="space-y-6">
{[
{ title: 'مواد اولیه نایاب', desc: 'استفاده از پودر صدف لب‌سبز اصل نیوزیلند و مواد ارگانیک با گرید دارویی.' },
{ title: 'فاقد مواد نگهدارنده', desc: 'تمامی محصولات ۱۰۰٪ طبیعی و فاقد رنگ‌های مصنوعی و طعم‌دهنده‌های شیمیایی هستند.' },
{ title: 'تاییدیه اروپا', desc: 'مطابق با سخت‌گیرانه‌ترین استانداردهای ایمنی مواد غذایی و دارویی در اتحادیه اروپا.' }
].map((item, idx) => (
<div key={idx} className="flex gap-4">
<div className="w-12 h-12 rounded-2xl bg-medical-gray-50 flex items-center justify-center flex-shrink-0 text-canina-blue font-bold">
{idx + 1}
</div>
<div>
<h4 className="text-lg font-bold text-medical-gray-900 mb-1">{item.title}</h4>
<p className="text-sm text-medical-gray-500 leading-relaxed font-medium">{item.desc}</p>
</div>
</div>
))}
</div>
</div>
</div>
</section>
{/* Call to Action */}
<section className="bg-canina-blue py-20 relative overflow-hidden">
<div className="absolute inset-0 opacity-10">
<div className="grid grid-cols-10 h-full w-full">
{Array.from({length: 100}).map((_, i) => (
<div key={i} className="border border-white/20" />
))}
</div>
</div>
<div className="max-w-4xl mx-auto px-4 text-center relative z-10">
<h2 className="text-3xl lg:text-5xl font-black text-white mb-8 leading-tight">
میخواهید بدانید کدام محصول برای پت شما مناسبتر است؟
</h2>
<p className="text-white/80 text-lg mb-10 leading-relaxed">
تیم متخصص دامپزشکی کانینا ایران آماده پاسخگویی به سوالات شماست.
</p>
<div className="flex flex-wrap justify-center gap-4">
<button className="bg-white text-canina-blue px-12 py-5 rounded-full font-black text-xl hover:scale-105 transition-transform shadow-2xl shadow-black/20">
دریافت رژیم مکمل رایگان
</button>
<Link
href="/shop"
className="bg-canina-blue/20 backdrop-blur-md border-2 border-white text-white px-12 py-5 rounded-full font-black text-xl hover:bg-white hover:text-canina-blue transition-all"
>
ورود به محصولات تخصصی
</Link>
</div>
</div>
</section>
</>
);
}

View File

@ -0,0 +1,6 @@
import ProductPage from "../../../components/ProductPage";
export default async function ShopProductPage({ params }: { params: Promise<{ id: string }> }) {
const resolvedParams = await params;
return <ProductPage productId={resolvedParams.id} />;
}

View File

@ -0,0 +1,9 @@
import ArchivePage from "../../components/ArchivePage";
export default async function Shop({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
const resolvedParams = await searchParams;
const category = typeof resolvedParams.category === 'string' ? resolvedParams.category : "all";
const search = typeof resolvedParams.search === 'string' ? resolvedParams.search : "";
return <ArchivePage initialCategory={category} initialSearch={search} />;
}

View File

@ -162,19 +162,16 @@ const ArchiveProductCard: React.FC<{ product: Product, onClick: (p: Product) =>
);
}
import { useRouter } from 'next/navigation';
export default function ArchivePage({
onProductClick,
onBack,
onShopNavigate,
initialCategory = "all",
initialSearch = ""
}: {
onProductClick: (p: Product) => void,
onBack?: () => void,
onShopNavigate?: (c?: string, s?: string) => void,
initialCategory?: string,
initialSearch?: string
}) {
const router = useRouter();
const [selectedCategory, setSelectedCategory] = useState(initialCategory);
const [selectedPet, setSelectedPet] = useState<PetType | "all">("all");
const [activeSymptoms, setActiveSymptoms] = useState<string[]>([]);
@ -262,7 +259,7 @@ export default function ArchivePage({
{/* Mobile Navigation & Breadcrumbs */}
<div className="lg:hidden mb-10 space-y-4">
<button
onClick={onBack}
onClick={() => router.back()}
className="flex items-center gap-2 text-medical-gray-500 font-bold hover:text-canina-blue transition-colors"
>
<ChevronRight className="w-5 h-5 font-vazir" />
@ -270,7 +267,7 @@ export default function ArchivePage({
</button>
<div className="flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest overflow-x-auto whitespace-nowrap pb-2">
<span className="cursor-pointer hover:text-canina-blue" onClick={onBack}>کانینا</span>
<span className="cursor-pointer hover:text-canina-blue" onClick={() => router.push('/')}>کانینا</span>
<ChevronLeft className="w-2.5 h-2.5" />
<span className="text-canina-blue">فروشگاه تخصصی</span>
</div>
@ -278,7 +275,7 @@ export default function ArchivePage({
{/* Desktop Breadcrumbs */}
<div className="hidden lg:flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-10">
<span className="cursor-pointer hover:text-canina-blue" onClick={onBack}>خانه</span>
<span className="cursor-pointer hover:text-canina-blue" onClick={() => router.push('/')}>خانه</span>
<ChevronLeft className="w-2.5 h-2.5" />
<span className="text-canina-blue">کاتالوگ کامل محصولات</span>
</div>
@ -386,7 +383,7 @@ export default function ArchivePage({
<div key="grid" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
<AnimatePresence mode="popLayout">
{filteredProducts.map((p: Product) => (
<ArchiveProductCard key={p.id} product={p} onClick={onProductClick} />
<ArchiveProductCard key={p.id} product={p} onClick={(p) => router.push(`/shop/${p.id}`)} />
))}
</AnimatePresence>
</div>
@ -399,14 +396,11 @@ export default function ArchivePage({
<p className="text-medical-gray-400 max-w-sm mb-8 font-bold">با فیلترهای فعلی محصولی مطابق با نیاز شما پیدا نکردیم. لطفاً فیلترها را گستردهتر کنید یا دکمه ریست را بزنید.</p>
<button
onClick={() => {
if (onShopNavigate) {
onShopNavigate("all");
} else {
setSelectedCategory("all");
setSelectedPet("all");
setActiveSymptoms([]);
setSearchQuery("");
}
router.push('/shop');
setSelectedCategory("all");
setSelectedPet("all");
setActiveSymptoms([]);
setSearchQuery("");
}}
className="bg-canina-blue text-white px-8 py-3 rounded-xl font-black text-sm hover:scale-105 transition-transform shadow-lg shadow-canina-blue/20"
>

View File

@ -92,7 +92,10 @@ function ProductCard({ product, onClick }: { product: Product; key?: string | nu
);
}
export default function FeaturedProducts({ onProductClick, onShopNavigate }: { onProductClick: (p: Product) => void, onShopNavigate: () => void }) {
import { useRouter } from 'next/navigation';
export default function FeaturedProducts() {
const router = useRouter();
const [products, setProducts] = useState<Product[]>([]);
const [isLoading, setIsLoading] = useState(true);
@ -124,7 +127,7 @@ export default function FeaturedProducts({ onProductClick, onShopNavigate }: { o
محصولات دارویی کانینا با استفاده از دانش پیشرفته بیوتکنولوژی و مواد اولیه ارگانیک نایاب فرآوری شدهاند.
</p>
<button
onClick={onShopNavigate}
onClick={() => router.push('/shop')}
className="inline-flex items-center gap-2 text-canina-blue font-bold text-sm hover:translate-x-1 transition-transform rtl:hover:-translate-x-1"
>
مشاهده تمامی محصولات
@ -138,7 +141,7 @@ export default function FeaturedProducts({ onProductClick, onShopNavigate }: { o
Array.from({ length: 3 }).map((_, i) => <ProductCardSkeleton key={i} />)
) : (
products.map((p) => (
<ProductCard key={p.id} onClick={onProductClick} product={p} />
<ProductCard key={p.id} onClick={(p) => router.push(`/shop/${p.id}`)} product={p} />
))
)}
</div>

View File

@ -60,7 +60,10 @@ function Typewriter({ text }: { text: string }) {
);
}
export default function Hero({ onProfileClick, onShopNavigate }: { onProfileClick?: () => void, onShopNavigate?: () => void }) {
import { useRouter } from 'next/navigation';
export default function Hero() {
const router = useRouter();
const getText = useSettingsStore(state => state.getText);
const title = getText('hero_title', "تخصص آلمانی در خدمت\nسلامت پت‌های خانگی");
const titleParts = title.split('\n');
@ -115,7 +118,7 @@ export default function Hero({ onProfileClick, onShopNavigate }: { onProfileClic
<ChevronLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
</button>
<button
onClick={onShopNavigate}
onClick={() => router.push('/shop')}
className="bg-white border-2 border-medical-gray-200 text-medical-gray-700 px-10 py-5 rounded-full font-bold text-lg hover:border-canina-blue hover:text-canina-blue transition-all font-vazir"
>
{getText('hero_btn_products', "مشاهده محصولات")}

View File

@ -67,13 +67,12 @@ const useCalculatorStore = create<CalculatorState>((set) => ({
import Tooltip from "./Tooltip";
import SafeImage from "./SafeImage";
export default function ProductPage({ product, onBack, onProductClick, onWikiNavigate, onShopNavigate }: {
product: Product;
onBack: () => void;
onProductClick: (p: Product) => void;
onWikiNavigate?: (id: string) => void;
onShopNavigate?: (category?: string) => void;
}) {
import { useRouter } from 'next/navigation';
export default function ProductPage({ productId }: { productId: string }) {
const router = useRouter();
const [product, setProduct] = useState<Product | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [activeTab, setActiveTab] = useState<"specs" | "feeding" | "notes">("specs");
const [showRefillModal, setShowRefillModal] = useState(false);
const [itemQuantity, setItemQuantity] = useState(1);
@ -81,9 +80,14 @@ export default function ProductPage({ product, onBack, onProductClick, onWikiNav
useEffect(() => {
productService.getProducts()
.then(setAllProducts)
.catch(err => console.error("Error fetching products in ProductPage:", err));
}, []);
.then((products) => {
setAllProducts(products);
const found = products.find(p => p.id === productId);
setProduct(found || null);
})
.catch(err => console.error("Error fetching products in ProductPage:", err))
.finally(() => setIsLoading(false));
}, [productId]);
const { pets, getActivePet } = usePetStore();
const activePet = getActivePet();
const { petType, weight, setPetType, setWeight } = useCalculatorStore();
@ -142,9 +146,12 @@ export default function ProductPage({ product, onBack, onProductClick, onWikiNav
// Suggest quantity inside useEffect to avoid render-phase state update
useEffect(() => {
const suggestedQty = (calculation.duration < 30 && calculation.duration > 0) ? 2 : 1;
const suggestedQty = (calculation?.duration < 30 && calculation?.duration > 0) ? 2 : 1;
setItemQuantity(suggestedQty);
}, [calculation.duration]);
}, [calculation?.duration]);
if (isLoading) return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
if (!product || !fullProduct || !calculation) return <div className="min-h-screen flex items-center justify-center">محصول یافت نشد</div>;
return (
<div className="min-h-screen bg-medical-gray-50 pt-2 pb-20 px-4 md:px-0" dir="rtl">
@ -152,7 +159,7 @@ export default function ProductPage({ product, onBack, onProductClick, onWikiNav
{/* Mobile Header with Back Button */}
<div className="flex items-center justify-between mb-6 md:hidden">
<button
onClick={onBack}
onClick={() => router.back()}
className="p-3 bg-white border border-medical-gray-200 rounded-2xl text-medical-gray-700 shadow-sm flex items-center gap-2 font-vazir text-sm font-bold"
>
<ChevronRight className="w-5 h-5" />
@ -166,14 +173,14 @@ export default function ProductPage({ product, onBack, onProductClick, onWikiNav
{/* Breadcrumbs */}
<nav className="hidden md:flex items-center gap-2 text-xs font-bold text-medical-gray-400 mb-8 overflow-x-auto whitespace-nowrap py-2">
<button
onClick={() => onBack()}
onClick={() => router.push('/')}
className="hover:text-canina-blue transition-colors font-vazir cursor-pointer"
>
خانه
</button>
<ChevronLeft className="w-3 h-3 flex-shrink-0" />
<button
onClick={() => onShopNavigate?.(product.category)}
onClick={() => router.push(`/shop?category=${product.category}`)}
className="hover:text-canina-blue cursor-pointer font-vazir"
>
{product.category}
@ -281,7 +288,7 @@ export default function ProductPage({ product, onBack, onProductClick, onWikiNav
<div key={idx} className="bg-medical-gray-50 border border-medical-gray-100 px-5 py-3 rounded-2xl flex items-center gap-3 group hover:border-canina-blue/30 transition-all">
<span className="text-sm font-bold text-medical-gray-700 font-vazir">
{termKey ? (
<Tooltip termKey={termKey} onWikiNavigate={onWikiNavigate}>
<Tooltip termKey={termKey} onWikiNavigate={(id) => router.push(`/wiki?term=${id}`)}>
{ing}
</Tooltip>
) : ing}
@ -530,7 +537,7 @@ export default function ProductPage({ product, onBack, onProductClick, onWikiNav
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="bg-white rounded-[3rem] p-8 border border-medical-gray-200 flex flex-col md:flex-row gap-8 items-center cursor-pointer hover:shadow-2xl transition-all"
onClick={() => onProductClick(relProduct)}
onClick={() => router.push(`/shop/${relProduct.id}`)}
>
<div className="w-40 h-40 bg-medical-gray-50 rounded-[2rem] p-4 flex items-center justify-center">
<SafeImage src={relProduct.image} alt={relProduct.name} className="w-full h-full object-contain drop-shadow-xl" />

View File

@ -16,16 +16,13 @@ import {
} from "lucide-react";
import { toPersian, cn } from "../lib/utils";
import { useRouter } from "next/navigation";
import { useUserStore } from "../lib/store/userStore";
import { useUIStore } from "../lib/store/uiStore";
import { usePetStore } from "../lib/store/usePetStore";
interface SmartAdvisorProps {
onComplete: (data: {
name: string,
type: "سگ" | "گربه",
breed: string,
age: number,
weight: number,
activityLevel: "کم" | "متوسط" | "زیاد",
medicalConditions: string[]
}) => void;
onComplete?: (data: any) => void;
}
const MEDICAL_OPTIONS = [
@ -37,7 +34,11 @@ const MEDICAL_OPTIONS = [
{ id: "appetite", label: "بی‌اشتهایی", condition: "بی‌اشتهایی" },
];
export default function SmartAdvisor({ onComplete }: SmartAdvisorProps) {
export default function SmartAdvisor({ onComplete }: SmartAdvisorProps = {}) {
const router = useRouter();
const { isLoggedIn } = useUserStore();
const setLoginModalOpen = useUIStore(state => state.setLoginModalOpen);
const { addPet, setActivePet } = usePetStore();
const texts = useSettingsStore(state => state.texts);
const medicalOptions = React.useMemo(() => {
const jsonStr = texts['medical_options'];
@ -70,15 +71,21 @@ export default function SmartAdvisor({ onComplete }: SmartAdvisorProps) {
const handleBack = () => setStep(s => s - 1);
const handleSubmit = () => {
onComplete({
name,
type,
breed,
age,
weight,
activityLevel,
medicalConditions
});
const data = {
name, type, breed, age, weight, activityLevel, medicalConditions
};
if (onComplete) {
onComplete(data);
} else {
if (!isLoggedIn) {
setLoginModalOpen(true, data);
} else {
const newPet = { ...data, id: Date.now().toString() };
addPet(newPet);
setActivePet(newPet.id);
router.push('/profile');
}
}
};
return (

View File

@ -33,7 +33,8 @@ const VIDEOS = [
}
];
export default function VetGallery({ onNavigate }: { onNavigate?: (v: any) => void }) {
export default function VetGallery() {
const router = useRouter();
const [selectedVideo, setSelectedVideo] = useState<typeof VIDEOS[0] | null>(null);
return (
@ -53,8 +54,8 @@ export default function VetGallery({ onNavigate }: { onNavigate?: (v: any) => vo
</p>
</div>
<button
onClick={() => onNavigate?.('videos')}
className="flex items-center gap-2 text-canina-blue font-black border-b-2 border-canina-blue pb-1 hover:gap-4 transition-all whitespace-nowrap"
onClick={() => router.push('/videos')}
className="inline-flex items-center gap-2 text-canina-blue font-bold text-sm hover:translate-x-1 transition-transform rtl:hover:-translate-x-1"
>
مشاهده همه ویدئوها
<Play className="w-4 h-4" />
@ -80,9 +81,9 @@ export default function VetGallery({ onNavigate }: { onNavigate?: (v: any) => vo
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-black/30 group-hover:bg-black/20 transition-colors flex items-center justify-center">
<div className="w-16 h-16 bg-white/90 rounded-full flex items-center justify-center text-canina-blue shadow-2xl group-hover:scale-110 transition-transform">
<button onClick={() => router.push('/videos')} className="w-16 h-16 rounded-full bg-white/10 backdrop-blur-md flex items-center justify-center group-hover:scale-110 transition-transform border border-white/20 relative z-10">
<PlayCircle className="w-10 h-10" />
</div>
</button>
</div>
<div className="absolute bottom-4 right-4 bg-black/60 backdrop-blur-md text-white px-3 py-1 rounded-lg text-[10px] font-black">
{video.duration}

View File

@ -0,0 +1,26 @@
import { create } from 'zustand';
interface UIState {
isCartOpen: boolean;
isLoginModalOpen: boolean;
isB2BPortalOpen: boolean;
advisorData: any;
setCartOpen: (open: boolean) => void;
setLoginModalOpen: (open: boolean, advisorData?: any) => void;
setB2BPortalOpen: (open: boolean) => void;
clearAdvisorData: () => void;
}
export const useUIStore = create<UIState>((set) => ({
isCartOpen: false,
isLoginModalOpen: false,
isB2BPortalOpen: false,
advisorData: null,
setCartOpen: (open) => set({ isCartOpen: open }),
setLoginModalOpen: (open, data) => set((state) => ({
isLoginModalOpen: open,
advisorData: data !== undefined ? data : state.advisorData
})),
setB2BPortalOpen: (open) => set({ isB2BPortalOpen: open }),
clearAdvisorData: () => set({ advisorData: null }),
}));