- Product detail: interactive image gallery (thumbnails, prev/next,
hover-zoom), tabbed spec/review/comments section matching the site's
standard pattern, variations/options display, and an availability
label instead of duplicating the contact-store message.
- Fix product.images normalization — the API returns {url} media objects,
not plain strings, so galleries were silently rendering empty.
- Prefer the Persian name (nameFa/productNameFa) everywhere a product
name is shown.
- Product cards: padded, rounded images instead of edge-to-edge with a
dashed divider.
- Blog: wire the homepage carousel to real posts (was static sample
data), drop the detail page's separate cover image, and make images
embedded in article/description HTML fill their container with
rounded corners.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
import { Placeholder } from "@/components/ui/Placeholder";
|
|
import { SectionTitle } from "@/components/ui/SectionTitle";
|
|
import { Carousel } from "@/components/ui/Carousel";
|
|
import { EmptyState } from "@/components/ui/EmptyState";
|
|
import type { BlogSummary } from "@/lib/meshkee";
|
|
|
|
function formatDate(iso?: string) {
|
|
if (!iso) return "";
|
|
return new Intl.DateTimeFormat("fa-IR", { dateStyle: "long" }).format(new Date(iso));
|
|
}
|
|
|
|
export function BlogCarousel({ posts }: { posts: BlogSummary[] }) {
|
|
return (
|
|
<section className="py-10 md:py-14">
|
|
<div className="mx-auto max-w-7xl px-4">
|
|
<SectionTitle
|
|
title="آخرین مطالب وبلاگ"
|
|
enTitle="Latest Blog"
|
|
action="وبلاگ"
|
|
/>
|
|
{posts.length === 0 ? (
|
|
<EmptyState message="هنوز مطلبی ثبت نشده است." />
|
|
) : (
|
|
<Carousel ariaLabel="کاروسل وبلاگ">
|
|
{posts.map((post) => (
|
|
<article
|
|
key={post.id}
|
|
className="w-[280px] shrink-0 overflow-hidden rounded-xl border border-border bg-surface transition-shadow hover:shadow-md md:w-[300px]"
|
|
>
|
|
<a href={`/blog/${post.slug}`}>
|
|
<div className="p-[10px]">
|
|
<Placeholder
|
|
src={post.image ?? "/images/placeholder.svg"}
|
|
alt={post.title}
|
|
width="100%"
|
|
fit="contain"
|
|
className="aspect-square w-full rounded-lg border-0"
|
|
/>
|
|
</div>
|
|
<div className="p-4 pt-0">
|
|
{post.publishedAt && (
|
|
<div className="mb-2 text-[11px] text-muted">{formatDate(post.publishedAt)}</div>
|
|
)}
|
|
<h3 className="mb-2 line-clamp-2 text-sm font-bold leading-relaxed">
|
|
{post.title}
|
|
</h3>
|
|
{post.abstract && (
|
|
<p className="line-clamp-2 text-xs leading-relaxed text-muted">
|
|
{post.abstract}
|
|
</p>
|
|
)}
|
|
<span className="mt-3 block text-xs font-medium text-red">
|
|
ادامه مطلب ←
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</article>
|
|
))}
|
|
</Carousel>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|