feat(ui): refine desktop sticky buy box, redesign blog newsletter CTA, enhance wiki product cards padding, add symptom search & active filters bar

This commit is contained in:
parsa aghaei 2026-07-22 15:44:50 +03:30
parent c7b944f5c4
commit f367499a5c
4 changed files with 98 additions and 34 deletions

View File

@ -239,6 +239,7 @@ export default function ArchivePage({
const [page, setPage] = useState(1);
const [meta, setMeta] = useState({ total: 0, lastPage: 1 });
const [symptomSearch, setSymptomSearch] = useState("");
const [categories, setCategories] = useState<{ id: string; label: string; icon: React.ReactNode }[]>([]);
const [symptoms, setSymptoms] = useState<string[]>([]);
@ -462,7 +463,7 @@ export default function ArchivePage({
</div>
</div>
{/* Symptoms Pill Tags */}
{/* Symptoms Pill Tags with Search Field */}
<div>
<div className="flex items-center justify-between mb-3">
<span className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">جستجو بر اساس علائم</span>
@ -475,21 +476,32 @@ export default function ArchivePage({
</button>
)}
</div>
<div className="flex flex-wrap gap-1.5 max-h-52 overflow-y-auto scrollbar-thin scrollbar-thumb-medical-gray-200 scrollbar-track-transparent pr-0.5">
<div className="relative mb-3">
<input
type="text"
placeholder="جستجوی علائم..."
value={symptomSearch}
onChange={(e) => setSymptomSearch(e.target.value)}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 text-xs outline-none focus:border-canina-blue font-vazir"
/>
</div>
<div className="flex flex-wrap gap-1.5 max-h-48 overflow-y-auto scrollbar-thin scrollbar-thumb-medical-gray-200 scrollbar-track-transparent pr-0.5">
{symptoms.length === 0 ? (
<span className="text-[10px] text-medical-gray-400">در حال بارگذاری...</span>
) : symptoms.map(s => (
<button
key={s}
onClick={() => toggleSymptom(s)}
className={`px-2.5 py-1 rounded-full text-[10px] font-bold transition-all border whitespace-nowrap ${activeSymptoms.includes(s)
? 'bg-canina-blue border-canina-blue text-white shadow-sm shadow-canina-blue/30'
: 'bg-medical-gray-50 border-medical-gray-200 text-medical-gray-500 hover:border-canina-blue/40 hover:text-canina-blue'
}`}
>
{s}
</button>
))}
) : symptoms
.filter(s => s.toLowerCase().includes(symptomSearch.toLowerCase()))
.map(s => (
<button
key={s}
onClick={() => toggleSymptom(s)}
className={`px-2.5 py-1 rounded-full text-[10px] font-bold transition-all border whitespace-nowrap ${activeSymptoms.includes(s)
? 'bg-canina-blue border-canina-blue text-white shadow-sm shadow-canina-blue/30'
: 'bg-medical-gray-50 border-medical-gray-200 text-medical-gray-500 hover:border-canina-blue/40 hover:text-canina-blue'
}`}
>
{s}
</button>
))}
</div>
</div>
</div>
@ -506,7 +518,7 @@ export default function ArchivePage({
{/* Main Content */}
<main className="flex-1 relative">
<div className="flex flex-col md:flex-row items-center justify-between mb-8 gap-4">
<div className="flex flex-col md:flex-row items-center justify-between mb-6 gap-4">
<h2 className="text-3xl font-black text-medical-gray-900">کاتولوگ دارویی <span className="text-canina-blue">Canina</span></h2>
<div className="relative w-full md:w-80">
<Search className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400" />
@ -520,6 +532,43 @@ export default function ArchivePage({
</div>
</div>
{/* Active Filters Bar with X Dismiss Cross Buttons */}
{(selectedCategory !== "all" || selectedPet !== "all" || activeSymptoms.length > 0 || searchQuery) && (
<div className="flex flex-wrap items-center gap-2 mb-6 bg-white p-4 rounded-2xl border border-medical-gray-200 shadow-sm font-vazir" dir="rtl">
<span className="text-xs font-black text-medical-gray-400 ml-2">فیلترهای فعال:</span>
{selectedCategory !== "all" && (
<span className="inline-flex items-center gap-1.5 px-3 py-1 bg-canina-blue/10 text-canina-blue rounded-xl text-xs font-black border border-canina-blue/20">
{CATEGORY_LABELS[selectedCategory] || selectedCategory}
<button onClick={() => setSelectedCategory("all")} className="hover:text-red-500 text-sm leading-none"></button>
</span>
)}
{selectedPet !== "all" && (
<span className="inline-flex items-center gap-1.5 px-3 py-1 bg-canina-blue/10 text-canina-blue rounded-xl text-xs font-black border border-canina-blue/20">
پت: {selectedPet}
<button onClick={() => setSelectedPet("all")} className="hover:text-red-500 text-sm leading-none"></button>
</span>
)}
{activeSymptoms.map(sym => (
<span key={sym} className="inline-flex items-center gap-1.5 px-3 py-1 bg-canina-blue/10 text-canina-blue rounded-xl text-xs font-black border border-canina-blue/20">
علائم: {sym}
<button onClick={() => toggleSymptom(sym)} className="hover:text-red-500 text-sm leading-none"></button>
</span>
))}
{searchQuery && (
<span className="inline-flex items-center gap-1.5 px-3 py-1 bg-canina-blue/10 text-canina-blue rounded-xl text-xs font-black border border-canina-blue/20">
جستجو: {searchQuery}
<button onClick={() => setSearchQuery("")} className="hover:text-red-500 text-sm leading-none"></button>
</span>
)}
<button
onClick={() => { setSelectedCategory("all"); setSelectedPet("all"); setActiveSymptoms([]); setSearchQuery(""); }}
className="text-xs font-bold text-red-500 hover:underline mr-auto"
>
حذف همه فیلترها
</button>
</div>
)}
<AnimatePresence mode="wait">
{isUpdating ? (
<div key="skeleton" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">

View File

@ -258,22 +258,29 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
))}
{/* Newsletter / CTA Box */}
<div className="bg-canina-blue rounded-[2.5rem] sm:rounded-[3rem] p-6 sm:p-10 text-white relative overflow-hidden flex flex-col justify-center text-center sm:text-right font-vazir">
<div className="absolute top-0 right-0 p-8 opacity-20 pointer-events-none">
<Sparkles className="w-24 h-24" />
<div className="bg-canina-blue rounded-[2.5rem] sm:rounded-[3.5rem] p-8 sm:p-12 text-white relative overflow-hidden flex flex-col md:flex-row items-center justify-between gap-8 font-vazir shadow-2xl">
<div className="absolute top-0 right-0 p-8 opacity-10 pointer-events-none">
<div className="w-48 h-48 bg-white/20 rounded-full blur-3xl" />
</div>
<h3 className="text-2xl sm:text-3xl font-black mb-4 italic font-lalezar">مشترک خبرنامه علمی شوید</h3>
<p className="text-white/80 mb-6 font-medium leading-relaxed text-xs sm:text-sm font-shabnam">
ماهانه یک ایمیل حاوی مهمترین نکات سلامت پتها و کدهای تخفیف اختصاصی دریافت کنید.
</p>
<div className="flex flex-col sm:flex-row gap-2 max-w-md mx-auto sm:mx-0">
<div className="flex items-center gap-6 relative z-10 text-right">
<div className="w-16 h-16 bg-white/10 backdrop-blur-md rounded-3xl flex items-center justify-center text-white font-black text-3xl italic shadow-inner flex-shrink-0">
C
</div>
<div>
<h3 className="text-2xl sm:text-3xl font-black mb-2 italic font-vazir">مشترک خبرنامه علمی شوید</h3>
<p className="text-white/80 font-medium text-xs sm:text-sm font-vazir max-w-lg">
ماهانه یک ایمیل حاوی مهمترین نکات سلامت پتها و کدهای تخفیف اختصاصی دریافت کنید.
</p>
</div>
</div>
<div className="flex flex-col sm:flex-row gap-3 w-full md:w-auto relative z-10">
<input
type="email"
placeholder="آدرس ایمیل شما"
className="flex-1 bg-white/10 border border-white/20 rounded-xl px-4 py-3 text-xs outline-none focus:bg-white/20 placeholder:text-white/50 text-white"
className="bg-white/10 border border-white/20 rounded-2xl px-5 py-3.5 text-xs outline-none focus:bg-white/20 placeholder:text-white/50 text-white w-full sm:w-64 font-vazir"
/>
<button className="bg-white text-canina-blue px-6 py-3 rounded-xl font-black text-xs hover:bg-slate-100 transition-all whitespace-nowrap shadow-md">
عضویت
<button className="bg-white text-canina-blue px-8 py-3.5 rounded-2xl font-black text-xs hover:bg-canina-gold hover:text-canina-dark transition-all whitespace-nowrap shadow-lg">
عضویت در خبرنامه
</button>
</div>
</div>

View File

@ -231,21 +231,29 @@ export default function IngredientWiki() {
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
className="bg-medical-gray-100 rounded-[4rem] p-12 relative overflow-hidden"
className="bg-medical-gray-100/80 rounded-[3rem] p-8 sm:p-10 border border-medical-gray-200/60 relative overflow-hidden"
>
<div className="absolute top-0 right-0 p-8">
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-[0.3em]">موجود در محصولات</div>
<div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60">
<div className="flex items-center gap-2">
<span className="w-2.5 h-2.5 rounded-full bg-canina-blue animate-pulse" />
<h4 className="text-xs sm:text-sm font-black text-medical-gray-900 font-vazir">موجود در محصولات کاتالوگ</h4>
</div>
<span className="text-[10px] font-black text-canina-blue bg-canina-blue/10 px-3 py-1 rounded-full font-vazir">
{hasIngredient.length} محصول
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 relative z-10">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-5 relative z-10">
{hasIngredient.length > 0 ? hasIngredient.map(p => (
<Link
key={p.id}
href={`/shop/${p.slug || p.id}`}
className="bg-white rounded-[2.5rem] p-6 shadow-xl hover:scale-105 transition-transform cursor-pointer group block"
className="bg-white rounded-[2rem] p-5 shadow-lg border border-medical-gray-200/70 hover:border-canina-blue/40 hover:shadow-xl hover:scale-[1.03] transition-all cursor-pointer group flex flex-col justify-between"
>
<img src={p.image} alt={p.name} className="w-24 h-24 object-contain mx-auto mb-4" referrerPolicy="no-referrer" />
<h4 className="text-sm font-black text-center text-medical-gray-900 group-hover:text-canina-blue transition-colors px-2">{p.name}</h4>
<div className="aspect-square bg-medical-gray-50 rounded-2xl p-4 mb-4 flex items-center justify-center">
<img src={p.image} alt={p.name} className="max-h-24 w-auto object-contain group-hover:scale-110 transition-transform duration-500" referrerPolicy="no-referrer" />
</div>
<h4 className="text-xs font-black text-center text-medical-gray-900 group-hover:text-canina-blue transition-colors px-1 leading-snug font-vazir">{p.name}</h4>
</Link>
)) : (
<div className="col-span-full py-8 text-center">

View File

@ -585,7 +585,7 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
</div>
{/* Sticky Sidebar (Clean Buy Box) - Hidden on mobile as mobile floating buy bar is active */}
<div className="hidden lg:block lg:col-span-4 lg:sticky lg:top-28 space-y-8">
<div className="hidden lg:block lg:col-span-4 lg:sticky lg:top-36 space-y-8">
<div className="bg-white rounded-[2.5rem] border-2 border-medical-gray-100 p-8 shadow-2xl relative overflow-hidden">
<div className="absolute top-0 right-0 w-24 h-24 bg-canina-blue/5 rounded-full -mr-12 -mt-12" />