Merge branch 'develop'
This commit is contained in:
commit
ca4876bf01
@ -119,21 +119,21 @@
|
||||
background: #003da6;
|
||||
}
|
||||
|
||||
/* Micro horizontal scrollbar for mobile filter rows */
|
||||
/* Micro horizontal scrollbar for mobile slider and filter rows */
|
||||
.micro-hscroll {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(30, 64, 83, 0.25) transparent;
|
||||
scrollbar-color: rgba(30, 64, 83, 0.2) transparent;
|
||||
}
|
||||
.micro-hscroll::-webkit-scrollbar {
|
||||
height: 3.5px;
|
||||
height: 2.5px;
|
||||
}
|
||||
.micro-hscroll::-webkit-scrollbar-track {
|
||||
background: rgba(229, 236, 233, 0.4);
|
||||
background: rgba(229, 236, 233, 0.35);
|
||||
border-radius: 9999px;
|
||||
margin: 0 16px;
|
||||
margin: 0 24px;
|
||||
}
|
||||
.micro-hscroll::-webkit-scrollbar-thumb {
|
||||
background: rgba(30, 64, 83, 0.3);
|
||||
background: rgba(30, 64, 83, 0.25);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
.micro-hscroll::-webkit-scrollbar-thumb:hover {
|
||||
|
||||
@ -83,20 +83,20 @@ export default function BlogPreviewSection() {
|
||||
|
||||
{/* Blog Grid */}
|
||||
{isLoading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div className="flex md:grid md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-8 overflow-x-auto md:overflow-visible pb-4 md:pb-0 micro-hscroll -mx-4 px-4 md:mx-0 md:px-0 select-none touch-pan-x snap-x snap-mandatory md:snap-none">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="bg-medical-gray-50 rounded-3xl p-4 border border-medical-gray-100 animate-pulse space-y-4"
|
||||
className="w-[68vw] sm:w-[50vw] md:w-auto flex-shrink-0 snap-start bg-medical-gray-50 rounded-3xl p-3 sm:p-4 border border-medical-gray-100 animate-pulse space-y-3 sm:space-y-4"
|
||||
>
|
||||
<div className="h-48 bg-medical-gray-200 rounded-2xl" />
|
||||
<div className="h-6 bg-medical-gray-200 rounded-md w-3/4" />
|
||||
<div className="h-4 bg-medical-gray-200 rounded-md w-1/2" />
|
||||
<div className="h-36 sm:h-48 bg-medical-gray-200 rounded-2xl" />
|
||||
<div className="h-5 sm:h-6 bg-medical-gray-200 rounded-md w-3/4" />
|
||||
<div className="h-3.5 sm:h-4 bg-medical-gray-200 rounded-md w-1/2" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div className="flex md:grid md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-8 overflow-x-auto md:overflow-visible pb-4 md:pb-0 micro-hscroll -mx-4 px-4 md:mx-0 md:px-0 select-none touch-pan-x snap-x snap-mandatory md:snap-none">
|
||||
{blogs.map((blog, idx) => {
|
||||
const image = blog.imageUrl?.startsWith("http")
|
||||
? blog.imageUrl
|
||||
@ -113,63 +113,64 @@ export default function BlogPreviewSection() {
|
||||
const blogHref = `/blog/${encodeURIComponent(blog.slug || blog.id)}`;
|
||||
|
||||
return (
|
||||
<motion.article
|
||||
key={blog.id || idx}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: idx * 0.1 }}
|
||||
className="bg-medical-gray-50/60 rounded-[2rem] border border-medical-gray-100 overflow-hidden hover:shadow-lg hover:-translate-y-1 transition-all flex flex-col group"
|
||||
>
|
||||
<Link href={blogHref} className="block relative aspect-video w-full overflow-hidden bg-medical-gray-100">
|
||||
<SafeImage
|
||||
src={image}
|
||||
alt={blog.title}
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
className="w-full h-full"
|
||||
imgClassName="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
</Link>
|
||||
<div key={blog.id || idx} className="w-[68vw] sm:w-[50vw] md:w-auto flex-shrink-0 snap-start flex">
|
||||
<motion.article
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: idx * 0.1 }}
|
||||
className="bg-medical-gray-50/60 rounded-[1.5rem] sm:rounded-[2rem] border border-medical-gray-100 overflow-hidden hover:shadow-lg hover:-translate-y-1 transition-all flex flex-col group w-full"
|
||||
>
|
||||
<Link href={blogHref} className="block relative aspect-video w-full overflow-hidden bg-medical-gray-100">
|
||||
<SafeImage
|
||||
src={image}
|
||||
alt={blog.title}
|
||||
sizes="(max-width: 768px) 70vw, (max-width: 1200px) 50vw, 33vw"
|
||||
className="w-full h-full"
|
||||
imgClassName="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<div className="p-6 flex-1 flex flex-col justify-between space-y-4">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-4 text-xs text-medical-gray-400 font-medium">
|
||||
<span className="flex items-center gap-1">
|
||||
<Calendar className="w-3.5 h-3.5" />
|
||||
{toPersian(formattedDate)}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Eye className="w-3.5 h-3.5" />
|
||||
{toPersian(blog.viewCount || 0)} {getText("blog_preview_views", "بازدید")}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<MessageSquare className="w-3.5 h-3.5" />
|
||||
{toPersian(blog._count?.comments || 0)} {getText("blog_preview_comments", "نظر")}
|
||||
</span>
|
||||
<div className="p-3.5 sm:p-6 flex-1 flex flex-col justify-between space-y-3 sm:space-y-4">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<div className="flex items-center gap-2.5 sm:gap-4 text-[10px] sm:text-xs text-medical-gray-400 font-medium">
|
||||
<span className="flex items-center gap-1">
|
||||
<Calendar className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
||||
{toPersian(formattedDate)}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Eye className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
||||
{toPersian(blog.viewCount || 0)}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<MessageSquare className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
||||
{toPersian(blog._count?.comments || 0)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Link href={blogHref}>
|
||||
<h3 className="text-xs sm:text-lg font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors line-clamp-2 leading-snug">
|
||||
{blog.title}
|
||||
</h3>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Link href={blogHref}>
|
||||
<h3 className="text-lg font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors line-clamp-2 leading-snug">
|
||||
{blog.title}
|
||||
</h3>
|
||||
</Link>
|
||||
<div className="pt-2.5 sm:pt-4 border-t border-medical-gray-200/60 flex items-center justify-between">
|
||||
<span className="text-[10px] sm:text-xs font-bold text-medical-gray-500 truncate max-w-[100px] sm:max-w-none">
|
||||
{authorName}
|
||||
</span>
|
||||
<Link
|
||||
href={blogHref}
|
||||
aria-label={`مطالعه مقاله: ${blog.title}`}
|
||||
className="text-[10px] sm:text-xs font-black text-canina-blue group-hover:underline flex items-center gap-1 shrink-0"
|
||||
>
|
||||
{getText("blog_preview_read_article", "خواندن مقاله")}
|
||||
<ArrowLeft className="w-2.5 h-2.5 sm:w-3 sm:h-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-4 border-t border-medical-gray-200/60 flex items-center justify-between">
|
||||
<span className="text-xs font-bold text-medical-gray-500">
|
||||
{authorName}
|
||||
</span>
|
||||
<Link
|
||||
href={blogHref}
|
||||
aria-label={`مطالعه مقاله: ${blog.title}`}
|
||||
className="text-xs font-black text-canina-blue group-hover:underline flex items-center gap-1"
|
||||
>
|
||||
{getText("blog_preview_read_article", "خواندن مقاله")}
|
||||
<ArrowLeft className="w-3 h-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</motion.article>
|
||||
</motion.article>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@ -122,52 +122,53 @@ export default function VetGallery({ testimonials = [] }: { testimonials?: Testi
|
||||
|
||||
{/* Video Grid or Skeleton */}
|
||||
{isLoading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div className="flex md:grid md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6 overflow-x-auto md:overflow-visible pb-4 md:pb-0 micro-hscroll -mx-4 px-4 md:mx-0 md:px-0 select-none touch-pan-x snap-x snap-mandatory md:snap-none">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div key={i} className="rounded-[2rem] p-2 space-y-4 animate-pulse">
|
||||
<div className="aspect-video bg-medical-gray-100 rounded-[2rem]" />
|
||||
<div className="h-6 bg-medical-gray-100 rounded-lg w-3/4" />
|
||||
<div className="h-4 bg-medical-gray-100 rounded-lg w-1/2" />
|
||||
<div key={i} className="w-[68vw] sm:w-[50vw] md:w-auto flex-shrink-0 snap-start rounded-[2rem] p-2 space-y-3 animate-pulse">
|
||||
<div className="aspect-video bg-medical-gray-100 rounded-[1.5rem] sm:rounded-[2rem]" />
|
||||
<div className="h-5 bg-medical-gray-100 rounded-lg w-3/4" />
|
||||
<div className="h-3.5 bg-medical-gray-100 rounded-lg w-1/2" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div className="flex md:grid md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6 overflow-x-auto md:overflow-visible pb-4 md:pb-0 micro-hscroll -mx-4 px-4 md:mx-0 md:px-0 select-none touch-pan-x snap-x snap-mandatory md:snap-none">
|
||||
{apiVideos.map((video, index) => (
|
||||
<motion.button
|
||||
key={video.id}
|
||||
whileHover={{ y: -8 }}
|
||||
onClick={() => setSelectedVideo(video)}
|
||||
className="w-full text-right block group cursor-pointer focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none rounded-[2rem] p-2"
|
||||
>
|
||||
<div className="relative aspect-video rounded-[2rem] overflow-hidden mb-4 shadow-xl bg-medical-gray-900">
|
||||
<div className="absolute inset-0 bg-medical-gray-900/20 group-hover:bg-transparent transition-colors z-10" />
|
||||
<SafeImage
|
||||
src={video.thumbnail || ''}
|
||||
alt={video.title || 'ویدیو دامپزشک'}
|
||||
className="w-full h-full"
|
||||
imgClassName="object-cover grayscale-[30%] group-hover:grayscale-0 transition-all duration-700"
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center z-20">
|
||||
<div className="w-16 h-16 bg-white/90 backdrop-blur-sm rounded-full flex items-center justify-center text-canina-blue scale-90 group-hover:scale-100 group-hover:bg-white transition-all shadow-xl">
|
||||
<Play className="w-6 h-6 ml-1" />
|
||||
<div key={video.id} className="w-[68vw] sm:w-[50vw] md:w-auto flex-shrink-0 snap-start flex">
|
||||
<motion.button
|
||||
whileHover={{ y: -8 }}
|
||||
onClick={() => setSelectedVideo(video)}
|
||||
className="w-full text-right block group cursor-pointer focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none rounded-[1.75rem] sm:rounded-[2rem] p-1.5 sm:p-2"
|
||||
>
|
||||
<div className="relative aspect-video rounded-[1.5rem] sm:rounded-[2rem] overflow-hidden mb-3 sm:mb-4 shadow-md sm:shadow-xl bg-medical-gray-900">
|
||||
<div className="absolute inset-0 bg-medical-gray-900/20 group-hover:bg-transparent transition-colors z-10" />
|
||||
<SafeImage
|
||||
src={video.thumbnail || ''}
|
||||
alt={video.title || 'ویدیو دامپزشک'}
|
||||
className="w-full h-full"
|
||||
imgClassName="object-cover grayscale-[30%] group-hover:grayscale-0 transition-all duration-700"
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center z-20">
|
||||
<div className="w-12 h-12 sm:w-16 sm:h-16 bg-white/90 backdrop-blur-sm rounded-full flex items-center justify-center text-canina-blue scale-90 group-hover:scale-100 group-hover:bg-white transition-all shadow-xl">
|
||||
<Play className="w-4 h-4 sm:w-6 sm:h-6 ml-1" />
|
||||
</div>
|
||||
</div>
|
||||
{video.duration && (
|
||||
<div className="absolute bottom-2.5 right-2.5 sm:bottom-4 sm:right-4 bg-black/60 backdrop-blur-md text-white px-2 py-0.5 sm:py-1 rounded-lg text-[10px] sm:text-xs font-black z-20 font-vazir flex items-center gap-1 shadow-lg">
|
||||
<span>⏱️</span>
|
||||
<span>{video.duration}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{video.duration && (
|
||||
<div className="absolute bottom-4 right-4 bg-black/60 backdrop-blur-md text-white px-2 py-1 rounded-lg text-xs font-black z-20 font-vazir flex items-center gap-1 shadow-lg">
|
||||
<span>⏱️</span>
|
||||
<span>{video.duration}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-lg font-black text-medical-gray-900 mb-2 group-hover:text-canina-blue transition-colors leading-tight font-vazir">
|
||||
{video.title}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 text-medical-gray-500 font-bold text-sm font-vazir">
|
||||
<ShieldCheck className="w-4 h-4 text-canina-blue" />
|
||||
{video.doctor || 'کادر علمی کنینا'}
|
||||
</div>
|
||||
</motion.button>
|
||||
<h3 className="text-sm sm:text-lg font-black text-medical-gray-900 mb-1.5 sm:mb-2 group-hover:text-canina-blue transition-colors leading-snug line-clamp-2 font-vazir">
|
||||
{video.title}
|
||||
</h3>
|
||||
<div className="flex items-center gap-1.5 sm:gap-2 text-medical-gray-500 font-bold text-xs sm:text-sm font-vazir">
|
||||
<ShieldCheck className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-canina-blue" />
|
||||
<span className="truncate">{video.doctor || 'کادر علمی کنینا'}</span>
|
||||
</div>
|
||||
</motion.button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
2
graphify-out/cache/stat-index.json
vendored
2
graphify-out/cache/stat-index.json
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user