fix(ui): refine mobile product details alignment, search overlay, remove full page loading, toast close button and dosage calculator layout

This commit is contained in:
parsa aghaei 2026-07-22 15:16:00 +03:30
parent 19c86c9955
commit 1c93aec1c9
4 changed files with 66 additions and 54 deletions

View File

@ -35,17 +35,7 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
}
}, [fetchProfile, fetchSettings]);
if (!isInitialized) {
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>
);
}
// Remove full-page blocking screen to allow immediate render
// Derived currentView from pathname for Header
let currentView = "home";
@ -114,7 +104,7 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
petName={advisorData?.name}
isAdvisorContext={!!advisorData}
/>
<Toaster position="top-center" expand={true} richColors />
<Toaster position="top-center" expand={true} richColors closeButton />
</>
);
}

View File

@ -247,15 +247,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">
{/* 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>
{isSearchFocused && (
<motion.div
initial={{ width: 0, opacity: 0 }}
animate={{ width: 260, opacity: 1 }}
animate={{ width: 220, opacity: 1 }}
exit={{ width: 0, opacity: 0 }}
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">
<input

View File

@ -161,14 +161,28 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
setItemQuantity(suggestedQty);
}, [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>;
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-32 sm:pb-20 px-4 md:px-0" dir="rtl">
<div className="max-w-7xl mx-auto">
{/* Breadcrumbs for Mobile and Desktop */}
<nav className="flex items-center gap-2 text-xs md:text-sm font-semibold text-medical-gray-600 mb-6 md:mb-10 overflow-x-auto whitespace-nowrap py-3 border-b border-medical-gray-100 font-vazir" dir="rtl">
<nav className="flex items-center gap-2 text-xs md:text-sm font-semibold text-medical-gray-600 mb-6 md:mb-10 overflow-x-auto whitespace-nowrap py-3 border-b border-medical-gray-100 font-vazir scrollbar-thin scrollbar-thumb-medical-gray-200 scrollbar-track-transparent pr-1" dir="rtl">
<button
onClick={() => router.push('/')}
className="hover:text-canina-blue transition-colors font-vazir cursor-pointer"
@ -193,20 +207,20 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
<div className="lg:col-span-8 space-y-12">
{/* Above the Fold Grid */}
<div className="grid md:grid-cols-12 gap-12 items-start">
<div className="grid md:grid-cols-12 gap-8 md:gap-12 items-center md:items-start text-center md:text-right font-vazir" dir="rtl">
{/* 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
initial={{ opacity: 0, scale: 0.95 }}
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="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="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-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" />
موجود در انبار
</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>
@ -214,15 +228,15 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
src={product.image}
alt={product.name}
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>
</div>
{/* Left Column: Product Title & Description (md:col-span-7) */}
<div className="md:col-span-7 space-y-6">
<div>
<div className="flex flex-wrap items-center gap-3 mb-4">
<div className="md:col-span-7 space-y-4 sm:space-y-6 flex flex-col items-center md:items-start">
<div className="w-full flex flex-col items-center md:items-start">
<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>
@ -232,17 +246,17 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
</div>
)}
</div>
<div className="flex flex-col gap-2 mb-4">
<h1 dir="rtl" className="text-2xl md:text-3xl lg:text-4xl font-black text-medical-gray-900 leading-tight font-vazir text-right">
<div className="flex flex-col gap-1.5 mb-4 text-center md:text-right w-full">
<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}
</h1>
{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}
</h2>
)}
</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 ? (
product.optimisticTemplate?.replace("[PetName]", activePet?.name || "").replace("[OnSet]", product.onSetOfAction || "به زودی")
) : (
@ -250,7 +264,7 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
)}
</p>
{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">
<p className="text-sm sm:text-lg text-medical-gray-600 font-bold leading-relaxed max-w-3xl font-vazir border-r-4 border-canina-blue pr-4 sm:pr-6 py-2 bg-canina-blue/5 rounded-l-2xl text-right w-full">
{fullProduct.shortDescription}
</p>
)}
@ -365,8 +379,8 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
<Clock className="w-6 h-6" />
</div>
<div>
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">ماشین حساب هوشمند مصرف</h3>
<p className="text-xs text-medical-gray-400 font-bold font-vazir mt-1">تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس مشخصات پت شما</p>
<h3 className="text-lg sm:text-2xl font-black text-medical-gray-900 italic font-vazir whitespace-nowrap">ماشین حساب هوشمند مصرف</h3>
<p className="text-[11px] sm:text-xs text-medical-gray-400 font-bold font-vazir mt-0.5 sm:mt-1">تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس مشخصات پت شما</p>
</div>
</div>

View File

@ -128,51 +128,57 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
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 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">
<Pill className="w-6 h-6" />
<div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
<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>
<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>
</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" />
تضمین ایمنی و عدم اوردوز
</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 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">
<Activity className="w-6 h-6" />
<div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
<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>
<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>
</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" />
پیشنهاد هدفمند و تخصصی
</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 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">
<ShieldCheck className="w-6 h-6" />
<div className="flex items-center gap-3 sm:block mb-2 sm:mb-0">
<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>
<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>
</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" />
تکمیل منظم دوره درمان
</div>