canina/frontend/application/components/Hero.tsx
parsa aghaei 8d1b786a24
All checks were successful
Deploy Canina / deploy (push) Successful in 1m50s
chore(brand): rename all Persian occurrences of کانینا to کنینا across the project
2026-08-16 16:40:08 +03:30

224 lines
13 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable @next/next/no-img-element */
"use client";
import { motion, animate } from "motion/react";
import { ChevronLeft, Globe, ShieldCheck, Calendar } from "lucide-react";
import { useEffect, useState } from "react";
import { toPersian } from "../lib/utils";
import { useSettingsStore } from "../lib/store/settingsStore";
function StatCounter({ target }: { target: number }) {
const [displayValue, setDisplayValue] = useState(0);
useEffect(() => {
const controls = animate(0, target, {
duration: 3,
ease: [0.16, 1, 0.3, 1], // Custom cubic-bezier for a more polished feel
onUpdate: (latest) => setDisplayValue(Math.round(latest))
});
return controls.stop;
}, [target]);
return (
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 2.5, ease: "easeOut" }}
className="text-4xl lg:text-6xl font-black text-canina-blue font-vazir origin-right flex items-center"
>
<motion.span
animate={{ scale: [1, 1.1, 1] }}
transition={{ duration: 3, ease: "easeInOut" }}
>
{toPersian(displayValue)}+
</motion.span>
</motion.div>
);
}
import { useRouter } from 'next/navigation';
import { Banner } from "../lib/types";
export default function Hero({ banners = [], onShopNavigate }: { banners?: Banner[]; onShopNavigate?: () => void }) {
const router = useRouter();
const getText = useSettingsStore((state) => state?.getText || ((_k: string, fb: string) => fb));
const isInitialized = useSettingsStore((state) => state?.isInitialized ?? false);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
const defaultTitle = banners.length > 0 && banners[0].title ? banners[0].title : "تخصص آلمانی در خدمت\nسلامت پت‌های خانگی";
const defaultSubtitle = banners.length > 0 && banners[0].subtitle ? banners[0].subtitle : 'از سال ۱۹۸۴، شرکت Canina pharma GmbH در برگیش گلادباخ آلمان، با بهره‌گیری از مواد اولیه طبیعی و فرآیندهای پیشرفته، استاندارد طلایی مکمل‌های دامپزشکی را تعریف می‌کند. کنینا نماینده رسمی این برند در ایران است.';
const title = (mounted && isInitialized ? getText('hero_title', '') : '') || defaultTitle;
const subtitle = (mounted && isInitialized ? getText('hero_desc', '') : '') || defaultSubtitle;
const titleParts = title.split('\n');
return (
<section className="relative overflow-hidden bg-white py-20 lg:py-32 border-b border-medical-gray-100">
{/* Background patterns */}
<div className="absolute top-0 left-0 w-full h-full opacity-[0.03] pointer-events-none select-none overflow-hidden">
<div className="absolute top-0 right-0 w-[800px] h-[800px] bg-canina-blue rounded-full blur-[120px] -mr-96 -mt-96" />
<div className="absolute bottom-0 left-0 w-[600px] h-[600px] bg-medical-gray-500 rounded-full blur-[100px] -ml-40 -mb-40" />
</div>
<div className="max-w-7xl mx-auto px-4 relative flex flex-col lg:flex-row items-center gap-12 mb-16 lg:mb-20">
{/* Content */}
<div className="lg:w-1/2 text-center lg:text-right">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<div className="inline-flex items-center gap-2 px-3 py-1 bg-canina-blue/5 border border-canina-blue/10 rounded-full mb-6">
<span className="w-2 h-2 rounded-full bg-canina-blue animate-pulse" />
<span className="text-canina-blue text-xs font-bold uppercase tracking-widest leading-none">
{mounted && isInitialized ? getText('hero_badge', "از ۱۹۸۴ در خدمت سلامت حیوانات") : "از ۱۹۸۴ در خدمت سلامت حیوانات"}
</span>
</div>
<h1 className="text-4xl lg:text-7xl font-black text-medical-gray-900 leading-[1.2] mb-6 font-lalezar tracking-wide">
{titleParts[0]} <br />
{titleParts[1] && <span className="text-canina-blue">{titleParts[1]}</span>}
</h1>
<p
className="text-lg lg:text-xl text-medical-gray-600 mb-10 max-w-2xl mx-auto lg:mx-0 leading-relaxed font-vazir animate-fade-in"
dangerouslySetInnerHTML={{
__html: subtitle
}}
/>
<div className="flex flex-wrap justify-center lg:justify-start gap-4 sm:gap-6">
<button
onClick={() => {
const element = document.getElementById('canino-advisor');
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
}}
className="bg-canina-blue text-white px-10 py-5 rounded-full font-bold text-lg hover:bg-canina-blue/90 hover:shadow-2xl focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all flex items-center gap-2 group font-vazir min-h-[58px] shadow-lg shadow-canina-blue/20 cursor-pointer"
>
{mounted && isInitialized ? getText('hero_btn_advisor', "دستیار سلامت پت") : "دستیار سلامت پت"}
<ChevronLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
</button>
<button
onClick={() => {
if (onShopNavigate) {
onShopNavigate();
} else {
router.push('/shop');
}
}}
className="bg-white border-2 border-medical-gray-900 text-medical-gray-900 px-10 py-5 rounded-full font-bold text-lg hover:bg-medical-gray-50 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all font-vazir min-h-[58px] shadow-sm cursor-pointer"
>
{mounted && isInitialized ? getText('hero_btn_products', "مشاهده محصولات") : "مشاهده محصولات"}
</button>
</div>
</motion.div>
</div>
{/* Visual Element */}
<div className="lg:w-1/2 relative">
<motion.div
initial={{ opacity: 0, scale: 0.9, rotate: -5 }}
animate={{ opacity: 1, scale: 1, rotate: 0 }}
transition={{ duration: 1, ease: "easeOut" }}
className="relative z-10"
>
<div className="aspect-square bg-gradient-to-br from-slate-200 to-slate-300 rounded-[2.5rem] overflow-hidden border-4 border-white shadow-2xl relative">
<img
src={(mounted && isInitialized ? getText('hero_image_url', '') : '') || (banners.length > 0 && banners[0].imageUrl ? banners[0].imageUrl : "/assets/images/hero-section-image.png")}
alt={banners.length > 0 ? banners[0].title : "Canina Pharma Germany"}
className="w-full h-full object-cover"
loading="eager"
referrerPolicy="no-referrer"
/>
<div className="absolute bottom-0 left-0 right-0 p-8 bg-gradient-to-t from-black/80 to-transparent text-white text-right">
<div className="text-sm font-medium mb-1 opacity-80 uppercase tracking-widest text-[10px]">
{mounted && isInitialized ? getText('hero_image_badge', "سرآمد علمی در پزشکی پت‌ها") : "سرآمد علمی در پزشکی پت‌ها"}
</div>
<div className="text-xl font-bold italic tracking-tighter">
{mounted && isInitialized ? getText('hero_image_title', "مکمل‌های تایید شده دامپزشکی با گواهی IFS") : "مکمل‌های تایید شده دامپزشکی با گواهی IFS"}
</div>
</div>
{/* Quality Badge inside the image container to prevent text collision */}
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 20, repeat: Infinity, ease: "linear" }}
className="absolute top-6 right-6 lg:top-8 lg:right-8 z-20 w-24 h-24 lg:w-28 lg:h-28 bg-white shadow-2xl rounded-full p-2 border-2 border-dashed border-canina-blue flex items-center justify-center text-center"
>
<div className="flex flex-col items-center">
<span className="text-xl lg:text-2xl font-black text-canina-blue leading-none tracking-tighter">DE</span>
<span className="text-[7px] lg:text-[8px] font-bold text-medical-gray-500 uppercase tracking-widest mt-0.5 leading-tight">
{mounted && isInitialized ? getText('hero_quality_standard', "استاندارد کیفی آلمان (IFS & HACCP)") : "استاندارد کیفی آلمان (IFS & HACCP)"}
</span>
</div>
</motion.div>
{/* 3D Action Badge clarifying the 3D mark */}
<div className="absolute bottom-28 right-6 bg-canina-gold text-medical-gray-900 px-4 py-2 rounded-2xl text-xs font-black shadow-2xl z-20 flex items-center gap-1.5 font-vazir border border-white/20 select-none animate-bounce">
<span className="text-sm"></span>
<span>فرمول ۳کاره (تاثیر سهبعدی)</span>
</div>
</div>
</motion.div>
</div>
</div>
{/* Stats - 3 side-by-side cards with centered text on all screens */}
<div className="max-w-7xl mx-auto px-2 sm:px-4 relative">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.8, duration: 0.6 }}
className="grid grid-cols-3 gap-2 sm:gap-6 py-6 sm:py-10 border-t border-medical-gray-150"
>
{/* Stat 1: Founded Year */}
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-2xl sm:rounded-3xl p-2.5 sm:p-6 flex flex-col sm:flex-row items-center justify-center text-center sm:text-right gap-2 sm:gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-9 h-9 sm:w-14 sm:h-14 bg-white rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300 flex-shrink-0">
<Calendar className="w-4 h-4 sm:w-7 sm:h-7" />
</div>
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
<div className="text-base sm:text-3xl font-black text-canina-blue font-vazir leading-none">۱۹۸۴</div>
<div className="text-[9px] sm:text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-1 sm:mt-2 font-vazir">
{mounted && isInitialized ? getText('hero_stat_founded', "سال تاسیس برند") : "سال تاسیس برند"}
</div>
</div>
</div>
{/* Stat 2: Active Agencies */}
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-2xl sm:rounded-3xl p-2.5 sm:p-6 flex flex-col sm:flex-row items-center justify-center text-center sm:text-right gap-2 sm:gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-9 h-9 sm:w-14 sm:h-14 bg-white rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300 flex-shrink-0">
<Globe className="w-4 h-4 sm:w-7 sm:h-7" />
</div>
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
<div className="flex items-baseline justify-center sm:justify-start">
<StatCounter target={40} />
</div>
<div className="text-[9px] sm:text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-1 sm:mt-2 font-vazir">
{mounted && isInitialized ? getText('hero_stat_agencies', "نمایندگی فعال") : "نمایندگی فعال"}
</div>
</div>
</div>
{/* Stat 3: German Formula */}
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-2xl sm:rounded-3xl p-2.5 sm:p-6 flex flex-col sm:flex-row items-center justify-center text-center sm:text-right gap-2 sm:gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-9 h-9 sm:w-14 sm:h-14 bg-white rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300 flex-shrink-0">
<ShieldCheck className="w-4 h-4 sm:w-7 sm:h-7" />
</div>
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
<div className="text-base sm:text-3xl font-black text-canina-blue font-vazir leading-none">۱۰۰٪</div>
<div className="text-[9px] sm:text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-1 sm:mt-2 font-vazir">
{mounted && isInitialized ? getText('hero_stat_german_formula', "فرمول آلمانی") : "فرمول آلمانی"}
</div>
</div>
</div>
</motion.div>
</div>
</section>
);
}