Compare commits

..

23 Commits

Author SHA1 Message Date
parsa aghaei
27ef5979aa fix(ui): remove overflow-x-auto from breadcrumbs to eliminate unwanted scrollbar pill
All checks were successful
Deploy Canina / deploy (push) Successful in 5m16s
2026-07-22 16:36:35 +03:30
parsa aghaei
51b661542a style: standardize breadcrumbs and integrated back button across all pages (Archive, Product, Blog, Blog detail, Wiki) 2026-07-22 16:10:14 +03:30
parsa aghaei
3683a768a8 fix(blog): integrate back button into top breadcrumb bar for a cleaner minimalist hero layout 2026-07-22 16:04:12 +03:30
parsa aghaei
85b542e8b4 fix(blog): add breadcrumb navigation, align header text and back button neatly 2026-07-22 16:02:57 +03:30
parsa aghaei
13a3b1f405 fix(blog): reorder form elements to ensure email input is always first before submit button 2026-07-22 16:00:24 +03:30
parsa aghaei
54ef14f006 fix(blog): stack mobile newsletter input and submit button inside container cleanly 2026-07-22 15:59:03 +03:30
parsa aghaei
1a1d828358 feat(blog): complete ultra-premium redesign of science newsletter card 2026-07-22 15:55:51 +03:30
parsa aghaei
cd52ac2b7d fix(blog): make newsletter input pill container expand to full width of description 2026-07-22 15:52:58 +03:30
parsa aghaei
6cd5e747de fix(blog): stack newsletter icon/title, description, and horizontal pill input vertically with clean space 2026-07-22 15:51:45 +03:30
parsa aghaei
5b07ddbf8f refactor(blog): rebuild science newsletter section with RTL text flow, Brand C logo badge, and integrated horizontal pill input 2026-07-22 15:48:18 +03:30
parsa aghaei
078f9ba1f2 fix(blog): fix newsletter card layout overflow and text clipping on medium screens 2026-07-22 15:46:30 +03:30
parsa aghaei
f367499a5c feat(ui): refine desktop sticky buy box, redesign blog newsletter CTA, enhance wiki product cards padding, add symptom search & active filters bar 2026-07-22 15:44:50 +03:30
parsa aghaei
c7b944f5c4 fix(css): remove global overflow-x-hidden from html/body to restore native position sticky 2026-07-22 15:33:14 +03:30
parsa aghaei
00a217ee02 fix(ui): unify ingredients section border container and fix sticky header overflow parent 2026-07-22 15:30:54 +03:30
parsa aghaei
e1da54f2f5 fix(product): set explicit grid-cols-1 and w-full for perfect mobile layout alignment 2026-07-22 15:27:35 +03:30
parsa aghaei
5005f0e68f fix(product): ensure 100% symmetrical grid width and centered element margins on mobile 2026-07-22 15:25:18 +03:30
parsa aghaei
12265c88e8 fix(product): ensure perfectly symmetrical left/right mobile container margins 2026-07-22 15:23:14 +03:30
parsa aghaei
0fa2b8c1d9 fix(ui): fix header sticky wrapper, increase footer bottom padding for buy bar, and balance mobile grid spacing 2026-07-22 15:21:30 +03:30
parsa aghaei
72a4073113 fix(wiki): wrap IngredientWiki in Suspense boundary for static prerendering 2026-07-22 15:16:51 +03:30
parsa aghaei
1c93aec1c9 fix(ui): refine mobile product details alignment, search overlay, remove full page loading, toast close button and dosage calculator layout 2026-07-22 15:16:00 +03:30
parsa aghaei
19c86c9955 fix(archive): add missing import for toPersian utility 2026-07-22 14:12:14 +03:30
parsa aghaei
913317809f fix(mobile-ui): comprehensive mobile responsiveness, header, store filters, wiki scroll and checkout overhaul 2026-07-22 14:11:45 +03:30
parsa aghaei
1340a2be87 refactor(backend): consolidate all prisma seeders into single master seed.ts entrypoint 2026-07-21 14:53:09 +03:30
13 changed files with 554 additions and 324 deletions

View File

@ -88,6 +88,6 @@
"testEnvironment": "node" "testEnvironment": "node"
}, },
"prisma": { "prisma": {
"seed": "ts-node prisma/seed.ts && ts-node prisma/seed-products.ts && ts-node prisma/seed-home.ts" "seed": "ts-node prisma/seed.ts"
} }
} }

View File

@ -255,7 +255,10 @@ async function main() {
// 2. Seed Scientific Terms // 2. Seed Scientific Terms
console.log('Seeding Scientific Terms...'); console.log('Seeding Scientific Terms...');
const termsFilePath = path.join(process.cwd() + "/prisma", '..', '..', 'frontend', 'application', 'lib', 'data', 'scientificTerms.ts'); let termsFilePath = path.join(process.cwd() + "/prisma", '..', '..', 'frontend', 'application', 'lib', 'data', 'scientificTerms.ts');
if (!fs.existsSync(termsFilePath)) {
termsFilePath = path.join(process.cwd(), 'prisma', 'scientificTerms.ts');
}
if (fs.existsSync(termsFilePath)) { if (fs.existsSync(termsFilePath)) {
const termsContent = fs.readFileSync(termsFilePath, 'utf-8'); const termsContent = fs.readFileSync(termsFilePath, 'utf-8');
const termsJsCode = ts.transpile(termsContent, { const termsJsCode = ts.transpile(termsContent, {
@ -290,7 +293,25 @@ async function main() {
} }
} }
console.log('Seeding completed successfully!'); // 3. Seed Products & Categories
console.log('Seeding Products & Categories...');
try {
const { main: seedProducts } = await import('./seed-products');
await seedProducts();
} catch (err) {
console.error('Failed to seed products:', err);
}
// 4. Seed Home Components & Testimonials
console.log('Seeding Home Components...');
try {
const { main: seedHome } = await import('./seed-home');
await seedHome();
} catch (err) {
console.error('Failed to seed home components:', err);
}
console.log('All Seeding completed successfully!');
} }

View File

@ -35,17 +35,7 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
} }
}, [fetchProfile, fetchSettings]); }, [fetchProfile, fetchSettings]);
if (!isInitialized) { // Remove full-page blocking screen to allow immediate render
return (
<div className="min-h-screen bg-medical-gray-50 flex flex-col items-center justify-center font-sans" dir="rtl">
<div className="flex flex-col items-center gap-4 text-center">
<div className="w-12 h-12 border-4 border-canina-blue border-t-transparent rounded-full animate-spin" />
<h2 className="text-xl font-black text-medical-gray-900 font-lalezar tracking-wide">در حال بارگذاری اطلاعات سایت...</h2>
<p className="text-sm text-medical-gray-500 font-shabnam">لطفاً شکیبا باشید</p>
</div>
</div>
);
}
// Derived currentView from pathname for Header // Derived currentView from pathname for Header
let currentView = "home"; let currentView = "home";
@ -114,7 +104,7 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
petName={advisorData?.name} petName={advisorData?.name}
isAdvisorContext={!!advisorData} isAdvisorContext={!!advisorData}
/> />
<Toaster position="top-center" expand={true} richColors /> <Toaster position="top-center" expand={true} richColors closeButton />
</> </>
); );
} }

View File

@ -104,7 +104,7 @@
html, body, #root { html, body, #root {
font-family: "Vazirmatn" !important; font-family: "Vazirmatn" !important;
@apply bg-medical-gray-50 text-medical-gray-900 antialiased overflow-x-hidden max-w-[100vw]; @apply bg-medical-gray-50 text-medical-gray-900 antialiased max-w-[100vw];
font-feature-settings: "ss01", "ss02", "cv01", "cv11"; font-feature-settings: "ss01", "ss02", "cv01", "cv11";
} }

View File

@ -1,3 +1,4 @@
import { Suspense } from "react";
import IngredientWiki from "../../components/IngredientWiki"; import IngredientWiki from "../../components/IngredientWiki";
import type { Metadata } from 'next'; import type { Metadata } from 'next';
@ -18,5 +19,9 @@ export const metadata: Metadata = {
}; };
export default function WikiPage() { export default function WikiPage() {
return <IngredientWiki />; return (
<Suspense fallback={<div className="min-h-screen flex items-center justify-center font-vazir text-sm">در حال بارگذاری دانشنامه...</div>}>
<IngredientWiki />
</Suspense>
);
} }

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import React, { useState, useMemo, useEffect } from "react"; import React, { useState, useMemo, useEffect } from "react";
import { toPersian } from "../lib/utils";
import { Product, PetType } from "../lib/data/products"; import { Product, PetType } from "../lib/data/products";
import { usePetStore } from "../lib/store/usePetStore"; import { usePetStore } from "../lib/store/usePetStore";
import { productService } from "../lib/services/productService"; import { productService } from "../lib/services/productService";
@ -160,15 +161,53 @@ const ArchiveProductCard: React.FC<{ product: Product }> = ({ product }) => {
))} ))}
</div> </div>
<div className="mt-auto flex items-center justify-between pt-4 border-t border-medical-gray-50"> <div className="mt-auto flex items-center justify-between pt-4 border-t border-medical-gray-50 gap-2">
<span className="font-black text-medical-gray-900 font-vazir">{product.price}</span> <span className="font-black text-medical-gray-900 font-vazir text-sm sm:text-base">{product.price}</span>
<button {(() => {
onClick={handleAddToCart} const cartItem = useCartStore.getState().items.find(i => i.product.id === product.id);
disabled={isAdding} if (cartItem && cartItem.quantity > 0) {
className="w-10 h-10 rounded-xl bg-medical-gray-50 text-canina-blue flex items-center justify-center group-hover:bg-canina-blue group-hover:text-white transition-all disabled:opacity-50" return (
> <div
{isAdding ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />} onClick={(e) => { e.preventDefault(); e.stopPropagation(); }}
</button> className="flex items-center gap-1 bg-canina-blue/10 border border-canina-blue/20 rounded-xl p-1"
>
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
useCartStore.getState().updateQuantity(product.id, cartItem.quantity + 1);
}}
className="w-7 h-7 rounded-lg bg-canina-blue text-white flex items-center justify-center font-bold text-xs"
>
+
</button>
<span className="w-5 text-center font-black text-canina-blue text-xs font-vazir">
{toPersian(cartItem.quantity)}
</span>
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
useCartStore.getState().updateQuantity(product.id, cartItem.quantity - 1);
}}
className="w-7 h-7 rounded-lg bg-white text-medical-gray-700 flex items-center justify-center font-bold text-xs shadow-sm"
>
-
</button>
</div>
);
}
return (
<button
onClick={handleAddToCart}
disabled={isAdding}
className="px-3 py-2 rounded-xl bg-canina-blue text-white text-xs font-black flex items-center gap-1.5 hover:bg-canina-dark transition-all disabled:opacity-50 shadow-md shadow-canina-blue/20"
>
{isAdding ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Plus className="w-3.5 h-3.5" />}
<span>خرید</span>
</button>
);
})()}
</div> </div>
</div> </div>
</Link> </Link>
@ -195,10 +234,12 @@ export default function ArchivePage({
const [activeSymptoms, setActiveSymptoms] = useState<string[]>(initialSymptoms ? initialSymptoms.split(',') : []); const [activeSymptoms, setActiveSymptoms] = useState<string[]>(initialSymptoms ? initialSymptoms.split(',') : []);
const [searchQuery, setSearchQuery] = useState(initialSearch); const [searchQuery, setSearchQuery] = useState(initialSearch);
const [isUpdating, setIsUpdating] = useState(false); const [isUpdating, setIsUpdating] = useState(false);
const [isMobileFilterOpen, setIsMobileFilterOpen] = useState(false);
const [filteredProducts, setFilteredProducts] = useState<Product[]>([]); const [filteredProducts, setFilteredProducts] = useState<Product[]>([]);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [meta, setMeta] = useState({ total: 0, lastPage: 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 [categories, setCategories] = useState<{ id: string; label: string; icon: React.ReactNode }[]>([]);
const [symptoms, setSymptoms] = useState<string[]>([]); const [symptoms, setSymptoms] = useState<string[]>([]);
@ -324,44 +365,54 @@ export default function ArchivePage({
<div className="min-h-screen bg-medical-gray-50 pb-20 px-4 pt-10 font-vazir" dir="rtl"> <div className="min-h-screen bg-medical-gray-50 pb-20 px-4 pt-10 font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
{/* Mobile Navigation & Breadcrumbs */} {/* Standard Breadcrumb Navigation & Integrated Back Button */}
<div className="lg:hidden mb-10 space-y-4"> <div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir">
<button <div className="flex items-center gap-2 text-xs font-bold text-medical-gray-400 whitespace-nowrap">
onClick={() => router.back()} <span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => router.push('/')}>خانه</span>
className="flex items-center gap-2 text-medical-gray-500 font-bold hover:text-canina-blue transition-colors" <ChevronLeft className="w-3 h-3 text-medical-gray-300" />
> <span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => { setSelectedCategory("all"); }}>کاتالوگ</span>
<ChevronRight className="w-5 h-5 font-vazir" /> {selectedCategory !== "all" && (
<span>بازگشت</span> <>
</button> <ChevronLeft className="w-3 h-3 text-medical-gray-300" />
<span className="text-canina-blue font-black">{CATEGORY_LABELS[selectedCategory] || selectedCategory}</span>
</>
)}
</div>
<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"> <div className="flex items-center gap-2">
<span className="cursor-pointer hover:text-canina-blue" onClick={() => router.push('/')}>کانینا</span> <button
<ChevronLeft className="w-2.5 h-2.5" /> onClick={() => setIsMobileFilterOpen(!isMobileFilterOpen)}
<span className="text-canina-blue">فروشگاه تخصصی</span> className="lg:hidden flex items-center gap-1.5 bg-canina-blue text-white px-3 py-1.5 rounded-lg text-xs font-bold shadow-xs"
>
<Filter className="w-3.5 h-3.5" />
<span>فیلترها {activeSymptoms.length > 0 || selectedPet !== "all" || selectedCategory !== "all" ? "•" : ""}</span>
</button>
<button
onClick={() => router.push('/')}
className="px-3.5 py-1.5 bg-white border border-medical-gray-200 rounded-lg text-medical-gray-600 shadow-xs flex items-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all whitespace-nowrap"
>
<ChevronRight className="w-3.5 h-3.5" />
<span>بازگشت</span>
</button>
</div> </div>
</div> </div>
{/* Desktop Breadcrumbs */} <div className="flex flex-col lg:flex-row gap-8">
<div className="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={() => router.push('/')}>خانه</span>
<ChevronLeft className="w-2.5 h-2.5" />
<span className="cursor-pointer hover:text-canina-blue" onClick={() => { setSelectedCategory("all"); }}>کاتالوگ</span>
{selectedCategory !== "all" && (
<>
<ChevronLeft className="w-2.5 h-2.5" />
<span className="text-canina-blue">{CATEGORY_LABELS[selectedCategory] || selectedCategory}</span>
</>
)}
</div>
<div className="flex flex-col lg:flex-row gap-10"> {/* Sidebar Filters - Collapsible on Mobile */}
<aside className={`lg:w-72 flex-shrink-0 space-y-6 ${isMobileFilterOpen ? 'block' : 'hidden lg:block'}`}>
{/* Sidebar Filters */}
<aside className="lg:w-72 flex-shrink-0 space-y-8">
<div className="bg-white rounded-[2rem] p-6 border border-medical-gray-200 shadow-sm"> <div className="bg-white rounded-[2rem] p-6 border border-medical-gray-200 shadow-sm">
<div className="flex items-center gap-2 mb-6"> <div className="flex items-center justify-between mb-6">
<Filter className="w-5 h-5 text-canina-blue" /> <div className="flex items-center gap-2">
<h3 className="font-black text-medical-gray-900">فیلترهای تخصصی</h3> <Filter className="w-5 h-5 text-canina-blue" />
<h3 className="font-black text-medical-gray-900">فیلترهای تخصصی</h3>
</div>
<button
onClick={() => setIsMobileFilterOpen(false)}
className="lg:hidden text-xs text-medical-gray-400 font-bold"
>
بستن ✕
</button>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
@ -393,7 +444,7 @@ export default function ArchivePage({
{categories.map(cat => ( {categories.map(cat => (
<button <button
key={cat.id} key={cat.id}
onClick={() => setSelectedCategory(cat.id)} onClick={() => { setSelectedCategory(cat.id); setIsMobileFilterOpen(false); }}
className={`flex items-center gap-3 px-4 py-3 rounded-2xl text-xs font-bold transition-all border ${selectedCategory === cat.id ? 'bg-canina-blue border-canina-blue text-white' : 'bg-white border-medical-gray-100 text-medical-gray-600 hover:border-canina-blue/30'}`} className={`flex items-center gap-3 px-4 py-3 rounded-2xl text-xs font-bold transition-all border ${selectedCategory === cat.id ? 'bg-canina-blue border-canina-blue text-white' : 'bg-white border-medical-gray-100 text-medical-gray-600 hover:border-canina-blue/30'}`}
> >
{cat.icon} {cat.icon}
@ -403,7 +454,7 @@ export default function ArchivePage({
</div> </div>
</div> </div>
{/* Symptoms Pill Tags */} {/* Symptoms Pill Tags with Search Field */}
<div> <div>
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<span className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">جستجو بر اساس علائم</span> <span className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">جستجو بر اساس علائم</span>
@ -416,21 +467,32 @@ export default function ArchivePage({
</button> </button>
)} )}
</div> </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 ? ( {symptoms.length === 0 ? (
<span className="text-[10px] text-medical-gray-400">در حال بارگذاری...</span> <span className="text-[10px] text-medical-gray-400">در حال بارگذاری...</span>
) : symptoms.map(s => ( ) : symptoms
<button .filter(s => s.toLowerCase().includes(symptomSearch.toLowerCase()))
key={s} .map(s => (
onClick={() => toggleSymptom(s)} <button
className={`px-2.5 py-1 rounded-full text-[10px] font-bold transition-all border whitespace-nowrap ${activeSymptoms.includes(s) key={s}
? 'bg-canina-blue border-canina-blue text-white shadow-sm shadow-canina-blue/30' onClick={() => toggleSymptom(s)}
: 'bg-medical-gray-50 border-medical-gray-200 text-medical-gray-500 hover:border-canina-blue/40 hover:text-canina-blue' 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> >
))} {s}
</button>
))}
</div> </div>
</div> </div>
</div> </div>
@ -447,7 +509,7 @@ export default function ArchivePage({
{/* Main Content */} {/* Main Content */}
<main className="flex-1 relative"> <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> <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"> <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" /> <Search className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400" />
@ -461,6 +523,43 @@ export default function ArchivePage({
</div> </div>
</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"> <AnimatePresence mode="wait">
{isUpdating ? ( {isUpdating ? (
<div key="skeleton" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6"> <div key="skeleton" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">

View File

@ -1,7 +1,8 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { ChevronRight, Calendar, User, ArrowLeft, Sparkles, BookOpen } from "lucide-react"; import { ChevronRight, Calendar, User, ArrowLeft, Sparkles, BookOpen, Mail, Check } from "lucide-react";
import { toast } from "sonner";
import { Product } from "../lib/data/products"; import { Product } from "../lib/data/products";
import { productService } from "../lib/services/productService"; import { productService } from "../lib/services/productService";
@ -31,13 +32,23 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
return ( return (
<div className="min-h-screen bg-medical-gray-50 pt-8 pb-20 px-4 font-vazir" dir="rtl"> <div className="min-h-screen bg-medical-gray-50 pt-8 pb-20 px-4 font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
{/* Breadcrumbs */} {/* Standard Breadcrumb Navigation & Integrated Back Button */}
<div className="flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-10"> <div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir" dir="rtl">
<span className="cursor-pointer hover:text-canina-blue" onClick={() => router.push('/')}>خانه</span> <div className="flex items-center gap-2 text-xs font-bold text-medical-gray-400 whitespace-nowrap">
<ChevronRight className="w-2.5 h-2.5" /> <span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => router.push('/')}>خانه</span>
<span className="cursor-pointer hover:text-canina-blue" onClick={() => setSelectedPost(null)}>مجله سلامت</span> <ChevronRight className="w-3 h-3 text-medical-gray-300" />
<ChevronRight className="w-2.5 h-2.5" /> <span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => setSelectedPost(null)}>مجله سلامت</span>
<span className="text-canina-blue">{selectedPost.title}</span> <ChevronRight className="w-3 h-3 text-medical-gray-300" />
<span className="text-canina-blue font-black">{selectedPost.title}</span>
</div>
<button
onClick={() => setSelectedPost(null)}
className="px-3.5 py-1.5 bg-white border border-medical-gray-200 rounded-lg text-medical-gray-600 shadow-xs flex items-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all whitespace-nowrap"
>
<ChevronRight className="w-3.5 h-3.5" />
<span>بازگشت</span>
</button>
</div> </div>
<div className="grid lg:grid-cols-12 gap-12"> <div className="grid lg:grid-cols-12 gap-12">
@ -144,29 +155,37 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
return ( return (
<div className="min-h-screen bg-medical-gray-50 pt-8 pb-20 px-4 font-vazir" dir="rtl"> <div className="min-h-screen bg-medical-gray-50 pt-8 pb-20 px-4 font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
{/* Header */} {/* Breadcrumb Navigation & Integrated Back Button */}
<div className="flex flex-col md:flex-row items-center justify-between mb-16 gap-6"> <div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir">
<div className="text-right"> <div className="flex items-center gap-2 text-xs font-bold text-medical-gray-400">
<div className="inline-flex items-center gap-2 px-3 py-1 bg-canina-blue/10 text-canina-blue rounded-full text-[10px] font-black uppercase tracking-widest mb-4"> <span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => router.push('/')}>خانه</span>
<BookOpen className="w-3 h-3" /> <ChevronRight className="w-3 h-3 text-medical-gray-300" />
Canina Science Blog <span className="text-canina-blue font-black">مجله سلامت</span>
</div>
<h1 className="text-4xl lg:text-6xl font-black text-medical-gray-900 italic leading-tight">
مجله تخصصی <span className="text-canina-blue">سلامت پت‌ها</span>
</h1>
<p className="text-lg text-medical-gray-500 font-medium mt-4 max-w-xl">
آخرین یافته‌های علمی محققین کانینا آلمان در مورد تغذیه و سلامت پت‌ها.
</p>
</div> </div>
<button <button
onClick={() => router.push('/')} onClick={() => router.push('/')}
className="px-8 py-3 bg-white border border-medical-gray-200 rounded-2xl text-medical-gray-700 shadow-sm flex items-center gap-2 font-black hover:bg-medical-gray-50 transition-all" className="px-3.5 py-1.5 bg-white border border-medical-gray-200 rounded-lg text-medical-gray-600 shadow-xs flex items-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all whitespace-nowrap"
> >
<ChevronRight className="w-5 h-5" /> <ChevronRight className="w-3.5 h-3.5" />
بازگشت به خانه <span>بازگشت</span>
</button> </button>
</div> </div>
{/* Header */}
<div className="mb-10 text-right">
<div className="inline-flex items-center gap-2 px-3 py-1 bg-canina-blue/10 text-canina-blue rounded-full text-xs font-black uppercase tracking-widest mb-3">
<BookOpen className="w-3.5 h-3.5" />
<span>Canina Science Blog</span>
</div>
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-black text-medical-gray-900 leading-tight">
مجله تخصصی <span className="text-canina-blue">سلامت پت‌ها</span>
</h1>
<p className="text-sm sm:text-base text-medical-gray-500 font-medium mt-3 max-w-xl leading-relaxed">
آخرین یافته‌های علمی محققین کانینا آلمان در مورد تغذیه و سلامت پت‌ها.
</p>
</div>
{/* Featured Post */} {/* Featured Post */}
{blogs.length > 0 && ( {blogs.length > 0 && (
<div className="mb-20"> <div className="mb-20">
@ -257,26 +276,79 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
</motion.div> </motion.div>
))} ))}
{/* Newsletter / CTA Box */} {/* Ultra-Premium Redesigned Newsletter / CTA Box */}
<div className="bg-canina-blue rounded-[3rem] p-10 text-white relative overflow-hidden flex flex-col justify-center"> <motion.div
<div className="absolute top-0 right-0 p-8 opacity-20"> initial={{ opacity: 0, y: 30 }}
<Sparkles className="w-24 h-24" /> whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7 }}
className="bg-gradient-to-br from-canina-blue via-canina-blue/95 to-medical-gray-900 rounded-[3rem] p-8 sm:p-14 text-white relative overflow-hidden font-vazir shadow-2xl border border-white/10"
dir="rtl"
>
{/* Decorative Glassmorphic Background Elements */}
<div className="absolute top-0 right-0 w-96 h-96 bg-canina-teal/15 rounded-full blur-[100px] pointer-events-none" />
<div className="absolute bottom-0 left-0 w-80 h-80 bg-canina-gold/10 rounded-full blur-[90px] pointer-events-none" />
<div className="absolute -top-12 -left-12 opacity-5 pointer-events-none select-none">
<span className="text-[180px] font-black italic tracking-tighter font-sans text-white">CANINA</span>
</div> </div>
<h3 className="text-3xl font-black mb-4 italic">مشترک خبرنامه علمی شوید</h3>
<p className="text-white/80 mb-8 font-medium leading-relaxed"> <div className="relative z-10 max-w-4xl mx-auto flex flex-col items-center text-center space-y-8">
ماهانه یک ایمیل حاوی مهم‌ترین نکات سلامت پت‌ها و کدهای تخفیف اختصاصی دریافت کنید.
</p> {/* Top Premium Badge */}
<div className="flex gap-2"> <div className="inline-flex items-center gap-2.5 px-4 py-2 bg-white/10 backdrop-blur-md rounded-full border border-white/20 shadow-inner">
<input <div className="w-6 h-6 rounded-lg bg-canina-gold text-canina-dark flex items-center justify-center font-black text-xs italic shadow-md">
type="email" C
placeholder="آدرس ایمیل شما" </div>
className="flex-1 bg-white/10 border border-white/20 rounded-xl px-4 py-3 outline-none focus:bg-white/20 placeholder:text-white/40" <span className="text-xs font-black text-white/90 tracking-wider font-vazir">
/> خبرنامه اختصاصی پژوهشگاه کانینا آلمان
<button className="bg-white text-canina-blue px-6 py-3 rounded-xl font-black"> </span>
عضویت <Sparkles className="w-3.5 h-3.5 text-canina-gold animate-pulse" />
</button> </div>
{/* Headline & Description */}
<div className="space-y-4 max-w-2xl">
<h3 className="text-3xl sm:text-4xl lg:text-5xl font-black font-vazir text-white leading-tight">
مشترک <span className="text-transparent bg-clip-text bg-gradient-to-r from-canina-gold via-white to-canina-teal">خبرنامه علمی</span> شوید
</h3>
<p className="text-sm sm:text-base text-white/80 font-medium font-vazir leading-relaxed">
ماهانه یک ایمیل حاوی تازه‌ترین یافتـه‌های پزشکی، کاتالوگ‌های درمانی اختصاصی کانینا و کدهای تخفیف ویژه برای شما ارسال می‌شود.
</p>
</div>
{/* Main Interactive Floating Input Bar */}
<div className="w-full max-w-xl">
<form onSubmit={(e) => { e.preventDefault(); toast.success("عضویت شما در خبرنامه علمی با موفقیت ثبت شد!"); }} className="w-full bg-white/15 backdrop-blur-xl border border-white/25 p-2 rounded-2xl sm:rounded-full flex flex-col sm:flex-row items-stretch sm:items-center gap-2 sm:gap-3 shadow-2xl hover:border-canina-gold/40 transition-all group" dir="ltr">
<input
type="email"
required
placeholder="آدرس ایمیل شما (name@domain.com)..."
className="flex-1 bg-transparent border-none text-xs sm:text-sm outline-none text-white placeholder:text-white/45 px-3 sm:px-4 py-2.5 sm:py-0 font-sans text-left"
/>
<button
type="submit"
className="bg-canina-gold hover:bg-white text-canina-dark px-6 sm:px-8 py-3.5 rounded-xl sm:rounded-full font-black text-xs sm:text-sm transition-all duration-300 whitespace-nowrap shadow-xl hover:scale-105 flex items-center justify-center gap-2 font-vazir cursor-pointer flex-shrink-0"
dir="rtl"
>
<span>عضویت آنی</span>
<Mail className="w-4 h-4" />
</button>
</form>
</div>
{/* Bottom Feature Micro Badges */}
<div className="pt-2 flex flex-wrap items-center justify-center gap-6 text-xs text-white/70 font-bold font-vazir border-t border-white/10 w-full max-w-xl">
<div className="flex items-center gap-2">
<Check className="w-4 h-4 text-canina-gold" />
<span>بدون اسپم (حداکثر ۱ ایمیل در ماه)</span>
</div>
<div className="flex items-center gap-2">
<Check className="w-4 h-4 text-canina-gold" />
<span>امکان لغو عضویت با یک کلیک</span>
</div>
</div>
</div> </div>
</div> </motion.div>
</div> </div>
{/* Synergy Products Cross-sell */} {/* Synergy Products Cross-sell */}

View File

@ -147,121 +147,112 @@ export default function CheckoutPage() {
<span className="text-canina-blue">درگاه پرداخت امن و نهایی‌سازی</span> <span className="text-canina-blue">درگاه پرداخت امن و نهایی‌سازی</span>
</div> </div>
<div className="grid lg:grid-cols-12 gap-12"> <div className="grid lg:grid-cols-12 gap-8 lg:gap-12">
{/* Main Form */} {/* Main Form */}
<div className="lg:col-span-8 space-y-8"> <div className="lg:col-span-8 space-y-6 sm:space-y-8">
{/* Step 1: Shipping */} {/* Step 1: Shipping */}
<section className="bg-white rounded-[3rem] border border-medical-gray-200 p-10 overflow-hidden relative"> <section className="bg-white rounded-[2rem] sm:rounded-[3rem] border border-medical-gray-200 p-5 sm:p-10 overflow-hidden relative">
<div className="absolute top-0 right-0 w-3 h-full bg-canina-blue opacity-20" /> <div className="absolute top-0 right-0 w-2.5 sm:w-3 h-full bg-canina-blue opacity-20" />
<div className="flex items-center gap-4 mb-10"> <div className="flex items-center gap-3 sm:gap-4 mb-6 sm:mb-10">
<div className="w-12 h-12 bg-medical-gray-900 text-white rounded-2xl flex items-center justify-center font-black">۱</div> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-medical-gray-900 text-white rounded-2xl flex items-center justify-center font-black text-sm sm:text-base">۱</div>
<h2 className="text-2xl font-black text-medical-gray-900 italic">اطلاعات ارسال</h2> <h2 className="text-xl sm:text-2xl font-black text-medical-gray-900 italic">اطلاعات ارسال</h2>
</div> </div>
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-4 sm:gap-6">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">نام و نام خانوادگی</label> <label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">نام و نام خانوادگی</label>
<input type="text" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20" placeholder="مثلاً: پارسا آقایی" /> <input type="text" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20" placeholder="مثلاً: پارسا آقایی" />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">شماره تماس</label> <label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">شماره تماس</label>
<input type="text" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20" placeholder="۰۹۱۲XXXXXXX" /> <input type="text" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20" placeholder="۰۹۱۲XXXXXXX" />
</div> </div>
<div className="md:col-span-2 space-y-2"> <div className="md:col-span-2 space-y-2">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">آدرس دقیق پستی</label> <label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">آدرس دقیق پستی</label>
<textarea className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 px-6 text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20 h-32" placeholder="استان، شهر، خیابان..." /> <textarea className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20 h-28 sm:h-32" placeholder="استان، شهر، خیابان..." />
</div> </div>
</div> </div>
</section> </section>
{/* Step 2: Payment */} {/* Step 2: Payment */}
<section className="bg-white rounded-[3rem] border border-medical-gray-200 p-10 overflow-hidden relative"> <section className="bg-white rounded-[2rem] sm:rounded-[3rem] border border-medical-gray-200 p-5 sm:p-10 overflow-hidden relative">
<div className="absolute top-0 right-0 w-3 h-full bg-medical-gray-900 opacity-20" /> <div className="absolute top-0 right-0 w-2.5 sm:w-3 h-full bg-medical-gray-900 opacity-20" />
<div className="flex items-center gap-4 mb-10"> <div className="flex items-center gap-3 sm:gap-4 mb-6 sm:mb-10">
<div className="w-12 h-12 bg-medical-gray-900 text-white rounded-2xl flex items-center justify-center font-black">۲</div> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-medical-gray-900 text-white rounded-2xl flex items-center justify-center font-black text-sm sm:text-base">۲</div>
<h2 className="text-2xl font-black text-medical-gray-900 italic">شیوه پرداخت</h2> <h2 className="text-xl sm:text-2xl font-black text-medical-gray-900 italic">شیوه پرداخت</h2>
</div> </div>
<div className="grid md:grid-cols-3 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-3 sm:gap-4">
{[ {[
{ id: 'online', label: 'پرداخت آنلاین', desc: 'کارت‌های بانکی', icon: <CreditCard className="w-6 h-6" /> }, { id: 'online', label: 'پرداخت آنلاین', desc: 'کارت‌های بانکی', icon: <CreditCard className="w-5 h-5 sm:w-6 sm:h-6" /> },
{ id: 'wallet', label: 'اعتبار حساب', desc: 'شارژ شده اختصاصی', icon: <Wallet className="w-6 h-6" /> }, { id: 'wallet', label: 'اعتبار حساب', desc: 'شارژ شده اختصاصی', icon: <Wallet className="w-5 h-5 sm:w-6 sm:h-6" /> },
{ id: 'cod', label: 'پرداخت در محل', desc: 'فقط تهران', icon: <Truck className="w-6 h-6" /> } { id: 'cod', label: 'پرداخت در محل', desc: 'فقط تهران', icon: <Truck className="w-5 h-5 sm:w-6 sm:h-6" /> }
].map((pay) => ( ].map((pay) => (
<div <div
key={pay.id} key={pay.id}
onClick={async () => { onClick={async () => {
setPaymentMethod(pay.id); setPaymentMethod(pay.id);
try {
// In a real scenario, this would call api.post('/payment/init', { method: pay.id, amount: getTotal() })
// and redirect to the returned gatewayUrl.
} catch (err) {
console.error("Payment init failed");
}
}} }}
className={cn("p-6 rounded-3xl border-2 cursor-pointer transition-all group", paymentMethod === pay.id ? "border-canina-blue bg-canina-blue/5" : "border-medical-gray-100 hover:border-canina-blue")} className={cn("p-4 sm:p-6 rounded-2xl sm:rounded-3xl border-2 cursor-pointer transition-all group flex sm:flex-col items-center sm:items-start gap-4 sm:gap-0", paymentMethod === pay.id ? "border-canina-blue bg-canina-blue/5" : "border-medical-gray-100 hover:border-canina-blue")}
> >
<div className="w-12 h-12 bg-medical-gray-50 rounded-2xl flex items-center justify-center text-medical-gray-400 group-hover:text-canina-blue group-hover:bg-canina-blue/10 mb-4 transition-all"> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-medical-gray-50 rounded-xl sm:rounded-2xl flex items-center justify-center text-medical-gray-400 group-hover:text-canina-blue group-hover:bg-canina-blue/10 sm:mb-4 transition-all flex-shrink-0">
{pay.icon} {pay.icon}
</div> </div>
<h4 className="font-black text-sm text-medical-gray-900 mb-1">{pay.label}</h4> <div>
<p className="text-[10px] text-medical-gray-400 font-bold">{pay.desc}</p> <h4 className="font-black text-xs sm:text-sm text-medical-gray-900 mb-0.5 sm:mb-1">{pay.label}</h4>
<p className="text-[10px] text-medical-gray-400 font-bold">{pay.desc}</p>
</div>
</div> </div>
))} ))}
</div> </div>
</section> </section>
{/* Step 3: Charity Section */} {/* Step 3: Charity Section */}
<section className="bg-white rounded-[3rem] border border-medical-gray-200 p-10 overflow-hidden relative"> <section className="bg-white rounded-[2rem] sm:rounded-[3rem] border border-medical-gray-200 p-5 sm:p-10 overflow-hidden relative">
<div className="absolute top-0 right-0 w-3 h-full bg-pink-500 opacity-20" /> <div className="absolute top-0 right-0 w-2.5 sm:w-3 h-full bg-pink-500 opacity-20" />
<div className="flex items-center justify-between mb-8"> <div className="flex items-center justify-between mb-6 sm:mb-8">
<div className="flex items-center gap-4"> <div className="flex items-center gap-3 sm:gap-4">
<div className="w-12 h-12 bg-pink-500 text-white rounded-2xl flex items-center justify-center"> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-pink-500 text-white rounded-2xl flex items-center justify-center flex-shrink-0">
<Heart className="w-6 h-6 fill-white" /> <Heart className="w-5 h-5 sm:w-6 sm:h-6 fill-white" />
</div> </div>
<div> <div>
<h2 className="text-2xl font-black text-medical-gray-900 italic">ردپای مهربانی</h2> <h2 className="text-xl sm:text-2xl font-black text-medical-gray-900 italic">ردپای مهربانی</h2>
<p className="text-xs font-bold text-pink-500 mt-1">سهم شما در حمایت از حیوانات بی‌پناه</p> <p className="text-[10px] sm:text-xs font-bold text-pink-500 mt-0.5">سهم شما در حمایت از حیوانات بی‌پناه</p>
</div> </div>
</div> </div>
</div> </div>
<div className="bg-pink-50 rounded-[2.5rem] p-8 border border-pink-100"> <div className="bg-pink-50 rounded-[2rem] sm:rounded-[2.5rem] p-5 sm:p-8 border border-pink-100">
<div className="flex flex-wrap items-center justify-between gap-6"> <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 sm:gap-6">
<div className="flex-1 min-w-[280px]"> <div className="flex-1">
<h4 className="text-lg font-black text-medical-gray-900 italic mb-2">رند کردن مبلغ و کمک به پناهگاه</h4> <h4 className="text-base sm:text-lg font-black text-medical-gray-900 italic mb-1 sm:mb-2">رند کردن مبلغ و کمک به پناهگاه</h4>
<p className="text-sm text-medical-gray-600 leading-relaxed font-medium"> با انتخاب این گزینه، مابقی مبلغ تا ده هزار تومان بعدی یا ۱٪ از خرید شما (هر کدام بیشتر باشد) صرف تامین غذا و دارو برای حیوانات بی-سرپرست تحت حمایت کانینا می‌شود.</p> <p className="text-xs sm:text-sm text-medical-gray-600 leading-relaxed font-medium"> مابقی مبلغ تا ۱۰,۰۰۰ تومان بعدی صرف تامین غذا و دارو برای حیوانات بی-سرپرست می‌شود.</p>
<div className="mt-4 flex items-center gap-3 text-pink-600">
<ShieldCheck className="w-4 h-4" />
<span className="text-[10px] font-black uppercase tracking-widest">گزارش شفاف هزینه‌کرد در داشبورد شما</span>
</div>
</div> </div>
<button <button
onClick={handleCharityToggle} onClick={handleCharityToggle}
className={cn( className={cn(
"w-20 h-20 rounded-full flex items-center justify-center transition-all shadow-xl hover:scale-110 active:scale-95", "w-14 h-14 sm:w-20 sm:h-20 rounded-full flex items-center justify-center transition-all shadow-lg self-end sm:self-center",
useRoundUp ? "bg-pink-500 text-white shadow-pink-200" : "bg-white text-medical-gray-300 border-2 border-medical-gray-100" useRoundUp ? "bg-pink-500 text-white shadow-pink-200" : "bg-white text-medical-gray-300 border-2 border-medical-gray-100"
)} )}
> >
<PlusCircle className={cn("w-10 h-10", useRoundUp ? "rotate-45" : "rotate-0")} /> <PlusCircle className={cn("w-7 h-7 sm:w-10 sm:h-10", useRoundUp ? "rotate-45" : "rotate-0")} />
</button> </button>
</div> </div>
{useRoundUp && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="mt-6 pt-6 border-t border-pink-100 flex items-center justify-between"
>
<span className="text-sm font-black text-pink-600 italic">مبلغ اهدایی شما:</span>
<span className="text-lg font-black font-vazir text-pink-500">{toPersian(charityDonation.toLocaleString())} تومان</span>
</motion.div>
)}
</div> </div>
{useRoundUp && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="mt-6 pt-6 border-t border-pink-100 flex items-center justify-between"
>
<span className="text-sm font-black text-pink-600 italic">مبلغ اهدایی شما:</span>
<span className="text-lg font-black font-vazir text-pink-500">{toPersian(charityDonation.toLocaleString())} تومان</span>
</motion.div>
)}
<p className="mt-6 text-[10px] text-center text-medical-gray-400 font-bold">«با خرید این محصول، شما هم در درمان یک حیوان پناهگاهی سهیم شدید.»</p> <p className="mt-6 text-[10px] text-center text-medical-gray-400 font-bold">«با خرید این محصول، شما هم در درمان یک حیوان پناهگاهی سهیم شدید.»</p>
</section> </section>
</div> </div>
{/* Sidebar Summary */} {/* Sidebar Summary */}

View File

@ -29,7 +29,7 @@ export default function Footer({ onNavigate, onShopNavigate, onB2BOpen }: {
}; };
return ( return (
<footer className="bg-medical-gray-900 text-white pt-24 pb-12 font-shabnam relative" dir="rtl"> <footer className="bg-medical-gray-900 text-white pt-24 pb-36 sm:pb-12 font-shabnam relative" dir="rtl">
{/* Floating Back-to-Top Button */} {/* Floating Back-to-Top Button */}
{showScroll && ( {showScroll && (
<button <button

View File

@ -124,45 +124,44 @@ export default function Header({
<> <>
<AuthModal isOpen={isAuthModalOpen} onClose={() => setIsAuthModalOpen(false)} /> <AuthModal isOpen={isAuthModalOpen} onClose={() => setIsAuthModalOpen(false)} />
<div className="bg-canina-gold text-canina-dark text-xs font-black py-2.5 text-center font-vazir tracking-wider flex items-center justify-center gap-2"> <div className="sticky top-0 z-50 w-full shadow-sm">
<span>🚚</span> <div className="bg-canina-gold text-canina-dark text-xs font-black py-2 text-center font-vazir tracking-wider flex items-center justify-center gap-2">
<span>{getText('shipping_notice', "ارسال رایگان برای سفارش‌های بالای ۱۵۰ هزار تومان")}</span> <span>🚚</span>
</div> <span>{getText('shipping_notice', "ارسال رایگان برای سفارش‌های بالای ۱۵۰ هزار تومان")}</span>
</div>
<header className="sticky top-0 z-50 bg-white/95 backdrop-blur-md border-b border-medical-gray-100 shadow-sm"> <header className="bg-white/95 backdrop-blur-md border-b border-medical-gray-100">
<div className="max-w-7xl mx-auto py-2 min-[470px]:py-0 min-[470px]:h-24 flex flex-col min-[470px]:flex-row min-[470px]:items-center justify-between gap-2 min-[470px]:gap-4 px-3 sm:px-6 lg:px-8" dir="rtl"> <div className="max-w-7xl mx-auto h-16 sm:h-20 md:h-24 flex items-center justify-between gap-1 sm:gap-4 px-2 sm:px-6 lg:px-8" dir="rtl">
{/* Logo & Mobile Toggle Area */} {/* Logo & Mobile Toggle Area */}
<div className="flex items-center justify-between min-[470px]:justify-start gap-2 w-full min-[470px]:w-auto"> <div className="flex items-center gap-1.5 sm:gap-2 min-w-0">
<div className="flex items-center gap-2"> {/* Mobile Menu Toggle */}
{/* Mobile Menu Toggle (visible below 800px) */} <button
<button onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} className="min-[800px]:hidden p-1.5 sm:p-2 rounded-xl text-medical-gray-600 hover:bg-medical-gray-50 transition-all flex-shrink-0"
className="min-[800px]:hidden p-2 rounded-xl text-medical-gray-600 hover:bg-medical-gray-50 transition-all flex-shrink-0" aria-label="منو"
aria-label="منو" >
> {isMobileMenuOpen ? <X className="w-5 h-5 sm:w-6 sm:h-6" /> : <Menu className="w-5 h-5 sm:w-6 sm:h-6" />}
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />} </button>
</button>
{/* Logo Section */} {/* Logo Section */}
<Link <Link
href="/" href="/"
className="flex items-center gap-2 sm:gap-3 cursor-pointer group" className="flex items-center gap-1.5 sm:gap-3 cursor-pointer group min-w-0"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
> >
<div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue rounded-2xl flex items-center justify-center text-white font-bold text-lg sm:text-2xl group-hover:bg-medical-gray-900 transition-all shadow-xl shadow-canina-blue/20 italic flex-shrink-0">C</div> <div className="w-8 h-8 sm:w-12 sm:h-12 bg-canina-blue rounded-xl sm:rounded-2xl flex items-center justify-center text-white font-bold text-base sm:text-2xl group-hover:bg-medical-gray-900 transition-all shadow-md sm:shadow-xl shadow-canina-blue/20 italic flex-shrink-0">C</div>
<div className="flex flex-col justify-center gap-0.5 sm:gap-1"> <div className="flex flex-col justify-center min-w-0">
<span className="text-canina-blue font-black text-base sm:text-2xl tracking-tighter italic font-sans flex items-center gap-1.5 sm:gap-2 leading-none whitespace-nowrap"> <span className="text-canina-blue font-black text-xs sm:text-2xl tracking-tighter italic font-sans flex items-center gap-1 leading-none whitespace-nowrap">
Canina Canina
<span className="text-xs sm:text-sm not-italic font-medium border-r-2 border-medical-gray-200 pr-1.5 sm:pr-2 font-vazir"> <span className="text-[10px] sm:text-sm not-italic font-medium border-r border-medical-gray-200 pr-1 font-vazir">
{getText('brand_name_fa', "ایران")} {getText('brand_name_fa', "ایران")}
</span>
</span> </span>
<span className="text-[8px] sm:text-[9px] text-medical-gray-400 font-bold uppercase tracking-wider font-vazir leading-none whitespace-nowrap"> </span>
{getText('brand_subtitle', "نماینده رسمی Canina Pharma GmbH آلمان")} <span className="text-[7px] sm:text-[9px] text-medical-gray-400 font-bold uppercase tracking-wider font-vazir leading-tight whitespace-nowrap mt-0.5">
</span> نماینده رسمی <span className="block sm:inline text-[6px] sm:text-[9px]">CANINA PHARMA GMBH</span>
</div> </span>
</Link> </div>
</div> </Link>
</div> </div>
{/* Navigation Items: Centered on XL screens (1224px+) */} {/* Navigation Items: Centered on XL screens (1224px+) */}
@ -249,15 +248,17 @@ export default function Header({
<div className="flex items-center gap-2 sm:gap-4 flex-shrink-0 justify-end h-auto md:h-16 pl-1 sm:pl-2"> <div className="flex items-center gap-2 sm:gap-4 flex-shrink-0 justify-end h-auto md:h-16 pl-1 sm:pl-2">
{/* Elastic Search */} {/* Elastic Search */}
<div className="relative flex items-center justify-end z-20"> {/* Elastic Search */}
<div className="relative flex items-center justify-end z-30">
<AnimatePresence> <AnimatePresence>
{isSearchFocused && ( {isSearchFocused && (
<motion.div <motion.div
initial={{ width: 0, opacity: 0 }} initial={{ width: 0, opacity: 0 }}
animate={{ width: 260, opacity: 1 }} animate={{ width: 220, opacity: 1 }}
exit={{ width: 0, opacity: 0 }} exit={{ width: 0, opacity: 0 }}
transition={{ type: "spring", damping: 25, stiffness: 220 }} transition={{ type: "spring", damping: 25, stiffness: 220 }}
className="absolute right-0 top-1/2 -translate-y-1/2 bg-white border border-canina-blue/30 border-l rounded-2xl shadow-xl shadow-canina-blue/5 overflow-hidden h-10 sm:h-12 flex items-center pr-10 pl-3 z-25" className="absolute left-full sm:right-0 top-1/2 -translate-y-1/2 bg-white border border-canina-blue/30 rounded-2xl shadow-2xl overflow-hidden h-10 sm:h-12 flex items-center pr-10 pl-3 z-30"
style={{ transformOrigin: "right center" }}
> >
<form onSubmit={handleSearch} className="w-full flex items-center"> <form onSubmit={handleSearch} className="w-full flex items-center">
<input <input
@ -510,6 +511,7 @@ export default function Header({
))} ))}
</div> </div>
</header> </header>
</div>
{/* Mobile Drawer Menu */} {/* Mobile Drawer Menu */}
<AnimatePresence> <AnimatePresence>

View File

@ -65,8 +65,10 @@ export default function IngredientWiki() {
const el = document.getElementById(`wiki-${termParam}`); const el = document.getElementById(`wiki-${termParam}`);
if (el) { if (el) {
setTimeout(() => { setTimeout(() => {
el.scrollIntoView({ behavior: 'smooth', block: 'center' }); const yOffset = -120;
}, 100); const y = el.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({ top: y, behavior: 'smooth' });
}, 200);
} }
} }
}, [termParam, ingredientsWiki]); }, [termParam, ingredientsWiki]);
@ -75,28 +77,21 @@ export default function IngredientWiki() {
<div className="bg-white py-12 px-4 overflow-hidden font-vazir" dir="rtl"> <div className="bg-white py-12 px-4 overflow-hidden font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
{/* Mobile Navigation & Breadcrumbs */} {/* Standard Breadcrumb Navigation & Integrated Back Button */}
<div className="lg:hidden mb-12 space-y-4"> <div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir" dir="rtl">
<div className="flex items-center gap-2 text-xs font-bold text-medical-gray-400 whitespace-nowrap">
<span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => router.push('/')}>خانه</span>
<ChevronLeft className="w-3 h-3 text-medical-gray-300" />
<span className="text-canina-blue font-black">دانشنامه مواد نایاب و ارگانیک</span>
</div>
<button <button
onClick={() => router.back()} onClick={() => router.push('/')}
className="flex items-center gap-2 text-medical-gray-500 font-bold hover:text-canina-blue transition-colors" className="px-3.5 py-1.5 bg-white border border-medical-gray-200 rounded-lg text-medical-gray-600 shadow-xs flex items-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all whitespace-nowrap"
> >
<ChevronRight className="w-5 h-5" /> <ChevronRight className="w-3.5 h-3.5" />
<span>بازگشت</span> <span>بازگشت</span>
</button> </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={() => router.push('/')}>کانینا</span>
<ChevronLeft className="w-2.5 h-2.5" />
<span className="text-canina-blue">دانشنامه علمی</span>
</div>
</div>
{/* Desktop Breadcrumbs */}
<div className="hidden lg:flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-12">
<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> </div>
<div className="flex flex-col items-center text-center mb-20"> <div className="flex flex-col items-center text-center mb-20">
@ -229,21 +224,29 @@ export default function IngredientWiki() {
initial={{ opacity: 0, scale: 0.9 }} initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }} whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }} 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="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60">
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-[0.3em]">موجود در محصولات</div> <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>
<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 => ( {hasIngredient.length > 0 ? hasIngredient.map(p => (
<Link <Link
key={p.id} key={p.id}
href={`/shop/${p.slug || 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" /> <div className="aspect-square bg-medical-gray-50 rounded-2xl p-4 mb-4 flex items-center justify-center">
<h4 className="text-sm font-black text-center text-medical-gray-900 group-hover:text-canina-blue transition-colors px-2">{p.name}</h4> <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> </Link>
)) : ( )) : (
<div className="col-span-full py-8 text-center"> <div className="col-span-full py-8 text-center">

View File

@ -161,66 +161,66 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
setItemQuantity(suggestedQty); setItemQuantity(suggestedQty);
}, [calculation?.duration]); }, [calculation?.duration]);
if (isLoading) return <div className="min-h-screen flex items-center justify-center">Loading...</div>; if (isLoading) return (
<div className="min-h-screen bg-medical-gray-50 p-6 max-w-7xl mx-auto font-vazir" dir="rtl">
<div className="animate-pulse space-y-6">
<div className="h-8 bg-medical-gray-200 rounded-2xl w-48" />
<div className="grid md:grid-cols-12 gap-8">
<div className="md:col-span-5 h-80 bg-white border border-medical-gray-200 rounded-[2.5rem]" />
<div className="md:col-span-7 space-y-4">
<div className="h-10 bg-medical-gray-200 rounded-2xl w-3/4" />
<div className="h-6 bg-medical-gray-100 rounded-xl w-1/2" />
<div className="h-24 bg-medical-gray-100 rounded-2xl w-full" />
</div>
</div>
</div>
</div>
);
if (!product || !fullProduct || !calculation) return <div className="min-h-screen flex items-center justify-center">محصول یافت نشد</div>; if (!product || !fullProduct || !calculation) return <div className="min-h-screen flex items-center justify-center">محصول یافت نشد</div>;
return ( return (
<div className="min-h-screen bg-medical-gray-50 pt-2 pb-20 px-4 md:px-0" dir="rtl"> <div className="min-h-screen bg-medical-gray-50 pt-2 pb-36 sm:pb-20 px-4 sm:px-6 lg:px-8 font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto w-full">
{/* Mobile Header with Back Button */} {/* Standard Breadcrumb Navigation & Integrated Back Button */}
<div className="flex items-center justify-between mb-6 md:hidden"> <div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir" dir="rtl">
<div className="flex items-center gap-2 text-xs font-bold text-medical-gray-400 whitespace-nowrap">
<span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => router.push('/')}>خانه</span>
<ChevronLeft className="w-3 h-3 text-medical-gray-300" />
<span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => router.push('/shop')}>فروشگاه</span>
<ChevronLeft className="w-3 h-3 text-medical-gray-300" />
<span className="text-canina-blue font-black">{product.nameFa || product.name}</span>
</div>
<button <button
onClick={() => router.back()} 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" className="px-3.5 py-1.5 bg-white border border-medical-gray-200 rounded-lg text-medical-gray-600 shadow-xs flex items-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all whitespace-nowrap"
> >
<ChevronRight className="w-5 h-5" /> <ChevronRight className="w-3.5 h-3.5" />
بازگشت <span>بازگشت</span>
</button> </button>
<div className="text-[10px] font-black text-canina-blue bg-canina-blue/5 px-4 py-2 rounded-xl font-vazir whitespace-nowrap">
کانینا ایران
</div>
</div> </div>
{/* Breadcrumbs */}
<nav className="hidden md:flex items-center gap-2 text-sm font-semibold text-medical-gray-600 mb-10 overflow-x-auto whitespace-nowrap py-3 border-b border-medical-gray-100">
<button
onClick={() => router.push('/')}
className="hover:text-canina-blue transition-colors font-vazir cursor-pointer"
>
خانه
</button>
<ChevronLeft className="w-3.5 h-3.5 flex-shrink-0 text-medical-gray-300" />
<button
onClick={() => router.push(`/shop?category=${product.categorySlug}`)}
className="hover:text-canina-blue cursor-pointer font-vazir"
>
{product.category}
</button>
<ChevronLeft className="w-3.5 h-3.5 flex-shrink-0 text-medical-gray-300" />
<span className="text-canina-blue font-vazir whitespace-nowrap font-bold">{product.name}</span>
</nav>
{/* Global Single Grid Layout - Aligns Sticky Buy Box at the Top */} {/* Global Single Grid Layout - Aligns Sticky Buy Box at the Top */}
<div className="grid lg:grid-cols-12 gap-12 items-start"> <div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-start w-full">
{/* Right/Main Content Column (lg:col-span-8) */} {/* Right/Main Content Column (lg:col-span-8) */}
<div className="lg:col-span-8 space-y-12"> <div className="w-full lg:col-span-8 space-y-12">
{/* Above the Fold Grid */} {/* Above the Fold Grid */}
<div className="grid md:grid-cols-12 gap-12 items-start"> <div className="grid grid-cols-1 md:grid-cols-12 gap-8 md:gap-12 items-center md:items-start text-center md:text-right font-vazir w-full" dir="rtl">
{/* Right Column: Product Image Frame (md:col-span-5) */} {/* Right Column: Product Image Frame (md:col-span-5) */}
<div className="md:col-span-5 w-full"> <div className="md:col-span-5 w-full mx-auto max-w-sm md:max-w-none">
<motion.div <motion.div
initial={{ opacity: 0, scale: 0.95 }} initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
className="bg-white rounded-[2.5rem] border border-medical-gray-200 shadow-xl relative group overflow-hidden h-[380px] flex items-center justify-center p-8" className="bg-white rounded-[2.5rem] border border-medical-gray-200 shadow-xl relative group overflow-hidden h-[300px] sm:h-[380px] flex items-center justify-center p-6 sm:p-8"
> >
<div className="absolute top-6 left-6 flex flex-col gap-2 z-10"> <div className="absolute top-4 left-4 sm:top-6 sm:left-6 flex flex-col gap-1.5 sm:gap-2 z-10 items-start">
<div className="px-3 py-1 bg-green-500 text-white rounded-full text-[10px] font-black uppercase tracking-widest flex items-center gap-1 font-vazir shadow-sm"> <div className="px-2.5 py-1 bg-green-500 text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest flex items-center gap-1 font-vazir shadow-sm">
<CheckCircle2 className="w-3 h-3" /> <CheckCircle2 className="w-3 h-3" />
موجود در انبار موجود در انبار
</div> </div>
<div className="px-3 py-1 bg-canina-blue text-white rounded-full text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm"> <div className="px-2.5 py-1 bg-canina-blue text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm">
گرید دارویی اختصاصی گرید دارویی اختصاصی
</div> </div>
</div> </div>
@ -228,15 +228,15 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
src={product.image} src={product.image}
alt={product.name} alt={product.name}
className="w-full h-full" className="w-full h-full"
imgClassName="max-h-[300px] w-auto object-contain drop-shadow-2xl group-hover:scale-105 transition-transform duration-700 mx-auto my-auto" imgClassName="max-h-[240px] sm:max-h-[300px] w-auto object-contain drop-shadow-2xl group-hover:scale-105 transition-transform duration-700 mx-auto my-auto"
/> />
</motion.div> </motion.div>
</div> </div>
{/* Left Column: Product Title & Description (md:col-span-7) */} {/* Left Column: Product Title & Description (md:col-span-7) */}
<div className="md:col-span-7 space-y-6"> <div className="md:col-span-7 space-y-4 sm:space-y-6 flex flex-col items-center md:items-start">
<div> <div className="w-full flex flex-col items-center md:items-start">
<div className="flex flex-wrap items-center gap-3 mb-4"> <div className="flex flex-wrap justify-center md:justify-start items-center gap-2 sm:gap-3 mb-3 sm:mb-4">
<div className="px-3 py-1 bg-canina-blue text-white rounded-full text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm"> <div className="px-3 py-1 bg-canina-blue text-white rounded-full text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm">
استاندارد صنعتی آلمان استاندارد صنعتی آلمان
</div> </div>
@ -246,17 +246,17 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
</div> </div>
)} )}
</div> </div>
<div className="flex flex-col gap-2 mb-4"> <div className="flex flex-col gap-1.5 mb-4 text-center md:text-right w-full">
<h1 dir="rtl" className="text-2xl md:text-3xl lg:text-4xl font-black text-medical-gray-900 leading-tight font-vazir text-right"> <h1 dir="rtl" className="text-xl sm:text-2xl md:text-3xl lg:text-4xl font-black text-medical-gray-900 leading-tight font-vazir">
{product.nameFa || product.name} {product.nameFa || product.name}
</h1> </h1>
{product.nameEn && ( {product.nameEn && (
<h2 dir="ltr" className="text-base md:text-lg text-slate-400 font-bold italic font-sans text-left"> <h2 dir="ltr" className="text-xs sm:text-base text-slate-400 font-bold italic font-sans text-center md:text-left">
{product.nameEn} {product.nameEn}
</h2> </h2>
)} )}
</div> </div>
<p className="text-xl text-canina-blue font-bold italic opacity-70 leading-relaxed max-w-2xl font-vazir mb-6 text-right" dir="rtl"> <p className="text-base sm:text-xl text-canina-blue font-bold italic opacity-80 leading-relaxed max-w-2xl font-vazir mb-4 text-center md:text-right" dir="rtl">
{isMounted && pets.length > 0 ? ( {isMounted && pets.length > 0 ? (
product.optimisticTemplate?.replace("[PetName]", activePet?.name || "").replace("[OnSet]", product.onSetOfAction || "به زودی") product.optimisticTemplate?.replace("[PetName]", activePet?.name || "").replace("[OnSet]", product.onSetOfAction || "به زودی")
) : ( ) : (
@ -264,9 +264,11 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
)} )}
</p> </p>
{fullProduct.shortDescription && ( {fullProduct.shortDescription && (
<p className="text-lg text-medical-gray-600 font-bold leading-relaxed max-w-3xl font-vazir border-r-4 border-canina-blue pr-6 py-2 bg-canina-blue/5 rounded-l-2xl"> <div className="w-full text-right bg-canina-blue/5 border-r-4 border-canina-blue p-4 sm:p-6 rounded-2xl">
{fullProduct.shortDescription} <p className="text-sm sm:text-lg text-medical-gray-600 font-bold leading-relaxed font-vazir">
</p> {fullProduct.shortDescription}
</p>
</div>
)} )}
</div> </div>
</div> </div>
@ -379,8 +381,8 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
<Clock className="w-6 h-6" /> <Clock className="w-6 h-6" />
</div> </div>
<div> <div>
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">ماشین حساب هوشمند مصرف</h3> <h3 className="text-lg sm:text-2xl font-black text-medical-gray-900 italic font-vazir whitespace-nowrap">ماشین حساب هوشمند مصرف</h3>
<p className="text-xs text-medical-gray-400 font-bold font-vazir mt-1">تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس مشخصات پت شما</p> <p className="text-[11px] sm:text-xs text-medical-gray-400 font-bold font-vazir mt-0.5 sm:mt-1">تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس مشخصات پت شما</p>
</div> </div>
</div> </div>
@ -433,18 +435,18 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
)} )}
{/* Ingredients Section */} {/* Ingredients Section */}
<section className="space-y-8"> <section className="bg-white rounded-[2.5rem] border border-medical-gray-200 p-8 md:p-10 shadow-md space-y-8">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4 border-b border-medical-gray-100 pb-6">
<div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg"> <div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg">
<FlaskConical className="w-6 h-6" /> <FlaskConical className="w-6 h-6" />
</div> </div>
<h3 className="text-3xl font-black text-medical-gray-900 italic font-vazir">ترکیبات و آنالیز علمی</h3> <h3 className="text-2xl md:text-3xl font-black text-medical-gray-900 italic font-vazir">ترکیبات و آنالیز علمی</h3>
</div> </div>
<div className="space-y-10"> <div className="space-y-8">
{/* Ingredients Cards */} {/* Ingredients Cards */}
<div className="bg-white rounded-[2.5rem] p-8 md:p-10 border border-medical-gray-200 shadow-md"> <div>
<h4 className="text-xs font-black text-medical-gray-400 uppercase tracking-widest mb-6 font-vazir">مواد تشکیل‌دهنده برتر</h4> <h4 className="text-xs font-black text-medical-gray-400 uppercase tracking-widest mb-4 font-vazir">مواد تشکیل‌دهنده برتر</h4>
<div className="flex flex-wrap gap-3"> <div className="flex flex-wrap gap-3">
{product.main_ingredients.map((ing, idx) => { {product.main_ingredients.map((ing, idx) => {
const termKey = termKeys.find(key => { const termKey = termKeys.find(key => {
@ -582,8 +584,8 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
)} )}
</div> </div>
{/* Sticky Sidebar (Clean Buy Box) - Sticky starts at the top, level with the title */} {/* Sticky Sidebar (Clean Buy Box) - Hidden on mobile as mobile floating buy bar is active */}
<div className="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="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" /> <div className="absolute top-0 right-0 w-24 h-24 bg-canina-blue/5 rounded-full -mr-12 -mt-12" />
@ -769,6 +771,45 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
</div> </div>
)} )}
</AnimatePresence> </AnimatePresence>
{/* Mobile Floating Sticky Bottom Buy Bar */}
<div className="lg:hidden fixed bottom-0 left-0 right-0 z-40 bg-white border-t border-medical-gray-200 p-3 shadow-2xl flex items-center justify-between gap-3 font-vazir" dir="rtl">
<div>
<span className="text-[10px] text-medical-gray-400 font-bold block">قیمت نهایی</span>
<span className="text-lg font-black text-medical-gray-900 font-vazir">{toPersian(product.price)}</span>
</div>
<div className="flex items-center gap-2 flex-1 max-w-[240px]">
<div className="flex items-center gap-1 bg-medical-gray-50 border border-medical-gray-200 rounded-xl p-1 h-11">
<button
onClick={() => setItemQuantity(Math.max(1, itemQuantity - 1))}
className="w-7 h-7 flex items-center justify-center rounded-lg bg-white text-medical-gray-900 border border-medical-gray-200 font-bold text-xs"
>
-
</button>
<span className="w-5 text-center font-black text-medical-gray-900 text-xs font-vazir">
{toPersian(itemQuantity)}
</span>
<button
onClick={() => setItemQuantity(itemQuantity + 1)}
className="w-7 h-7 flex items-center justify-center rounded-lg bg-white text-medical-gray-900 border border-medical-gray-200 font-bold text-xs"
>
+
</button>
</div>
<button
onClick={() => {
addItem(product, itemQuantity, { quantity: calculation.dailyDose, unit: calculation.unit });
toast.success(`${toPersian(itemQuantity)} عدد به سبد خرید اضافه شد`);
}}
className="flex-1 bg-canina-blue text-white py-2.5 px-3 rounded-xl font-black text-xs hover:bg-canina-dark transition-all flex items-center justify-center gap-1.5 shadow-md shadow-canina-blue/20 whitespace-nowrap"
>
<ShoppingBag className="w-4 h-4" />
افزودن به سبد
</button>
</div>
</div>
</div> </div>
); );
} }

View File

@ -128,51 +128,57 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
className="grid md:grid-cols-3 gap-6 mb-12" className="grid grid-cols-1 md:grid-cols-3 gap-4 sm:gap-6 mb-8 sm:mb-12"
> >
<div className="bg-white p-6 rounded-[2.5rem] border border-medical-gray-200/80 shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between"> <div className="bg-white p-4 sm:p-6 rounded-2xl sm:rounded-[2.5rem] border border-medical-gray-200/80 shadow-md sm:shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
<div> <div>
<div className="w-12 h-12 bg-canina-blue/10 rounded-2xl flex items-center justify-center text-canina-blue mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm"> <div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
<Pill className="w-6 h-6" /> <div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue/10 rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue sm:mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm flex-shrink-0">
<Pill className="w-5 h-5 sm:w-6 sm:h-6" />
</div>
<h3 className="text-base sm:text-lg font-black text-medical-gray-900 sm:mb-2 font-vazir">چرا ثبت شناسنامه؟</h3>
</div> </div>
<h3 className="text-lg font-black text-medical-gray-900 mb-2 font-vazir">چرا ثبت شناسنامه؟</h3>
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir"> <p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
مکمل‌های دارویی کانینا غلیظ و تخصصی هستند؛ الگوریتم هوشمند ما بر اساس سن، وزن و جثه پت شما، دوز دقیق مصرفی روزانه را محاسبه می‌کند تا حداکثر اثرگذاری ایجاد شود. مکمل‌های دارویی کانینا غلیظ و تخصصی هستند؛ الگوریتم هوشمند ما بر اساس سن، وزن و جثه پت شما، دوز دقیق مصرفی روزانه را محاسبه می‌کند.
</p> </p>
</div> </div>
<div className="mt-4 pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir"> <div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
<Check className="w-3.5 h-3.5" /> <Check className="w-3.5 h-3.5" />
تضمین ایمنی و عدم اوردوز تضمین ایمنی و عدم اوردوز
</div> </div>
</div> </div>
<div className="bg-white p-6 rounded-[2.5rem] border border-medical-gray-200/80 shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between"> <div className="bg-white p-4 sm:p-6 rounded-2xl sm:rounded-[2.5rem] border border-medical-gray-200/80 shadow-md sm:shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
<div> <div>
<div className="w-12 h-12 bg-canina-blue/10 rounded-2xl flex items-center justify-center text-canina-blue mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm"> <div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
<Activity className="w-6 h-6" /> <div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue/10 rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue sm:mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm flex-shrink-0">
<Activity className="w-5 h-5 sm:w-6 sm:h-6" />
</div>
<h3 className="text-base sm:text-lg font-black text-medical-gray-900 sm:mb-2 font-vazir">تطبیق علائم بالینی</h3>
</div> </div>
<h3 className="text-lg font-black text-medical-gray-900 mb-2 font-vazir">تطبیق علائم بالینی</h3>
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir"> <p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
با انتخاب علائمی مثل لنگیدن، ریزش مو، یا اسهال، موتور هوشمند ما دقیق‌ترین راهکار مکمل آلمانی مرتبط را به همراه دلایل علمی به شما پیشنهاد می‌دهد. با انتخاب علائمی مثل لنگیدن یا ریزش مو، دقیق‌ترین راهکار مکمل آلمانی به همراه دلایل علمی به شما پیشنهاد می‌شود.
</p> </p>
</div> </div>
<div className="mt-4 pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir"> <div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
<Check className="w-3.5 h-3.5" /> <Check className="w-3.5 h-3.5" />
پیشنهاد هدفمند و تخصصی پیشنهاد هدفمند و تخصصی
</div> </div>
</div> </div>
<div className="bg-white p-6 rounded-[2.5rem] border border-medical-gray-200/80 shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between"> <div className="bg-white p-4 sm:p-6 rounded-2xl sm:rounded-[2.5rem] border border-medical-gray-200/80 shadow-md sm:shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
<div> <div>
<div className="w-12 h-12 bg-canina-blue/10 rounded-2xl flex items-center justify-center text-canina-blue mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm"> <div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
<ShieldCheck className="w-6 h-6" /> <div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue/10 rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue sm:mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm flex-shrink-0">
<ShieldCheck className="w-5 h-5 sm:w-6 sm:h-6" />
</div>
<h3 className="text-base sm:text-lg font-black text-medical-gray-900 sm:mb-2 font-vazir">پایش موجودی و یادآور</h3>
</div> </div>
<h3 className="text-lg font-black text-medical-gray-900 mb-2 font-vazir">پایش موجودی و یادآور</h3>
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir"> <p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
پس از ایجاد شناسنامه، زمان‌های مصرف مکمل‌ها تنظیم شده و سیستم قبل از اتمام دوره درمانی، پیام هشدار شارژ مجدد به شما ارسال خواهد کرد. زمان‌های مصرف مکمل‌ها تنظیم شده و سیستم قبل از اتمام دوره درمانی، هشدار شارژ مجدد ارسال خواهد کرد.
</p> </p>
</div> </div>
<div className="mt-4 pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir"> <div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
<Check className="w-3.5 h-3.5" /> <Check className="w-3.5 h-3.5" />
تکمیل منظم دوره درمان تکمیل منظم دوره درمان
</div> </div>