Connect Meshkee APIs and add catalog, blog, and content pages.

Wire user-products and blogs to the Website API, add about/contact/categories routes, and polish flat UI filters and page banners.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-10 10:54:27 +03:30
co-authored by Cursor
parent e74e5ca45f
commit 8194b7292f
31 changed files with 2160 additions and 204 deletions
+128 -55
View File
@@ -1,64 +1,137 @@
import Image from "next/image";
import { products } from "@/data/home";
import Link from "next/link";
import {
formatCondition,
formatPrice,
productCategory,
productTitle,
} from "@/lib/api/user-products";
import type { UserProductListItem } from "@/lib/api/types";
export function Products() {
type ProductsProps = {
items: UserProductListItem[];
total?: number;
query?: string;
showHeader?: boolean;
showMoreLink?: boolean;
};
export function Products({
items,
total = 0,
query,
showHeader = true,
showMoreLink = true,
}: ProductsProps) {
return (
<section id="products" className="section">
<div className="container-page">
<div className="section-head">
<p className="section-kicker">Stock Machines</p>
<h2 className="section-title">محصولات موجود</h2>
<p className="section-desc">
منتخب ماشینآلات سنگین و صنعتی از فروشگاه ماشینیفای؛ برای جزئیات و
قیمت، استعلام بگیرید.
</p>
</div>
<section id="products" className={showHeader ? "section" : undefined}>
<div className={showHeader ? "container-page" : undefined}>
{showHeader ? (
<div className="section-head">
<p className="section-kicker">Stock Machines</p>
<h2 className="section-title">محصولات موجود</h2>
<p className="section-desc">
{query
? `نتایج جستجو برای «${query}»`
: "منتخب ماشین‌آلات سنگین و صنعتی از فروشگاه ماشینیفای؛ برای جزئیات و قیمت، استعلام بگیرید."}
</p>
</div>
) : query ? (
<p className="mb-6 text-sm text-muted">نتایج جستجو برای «{query}»</p>
) : null}
{items.length === 0 ? (
<div className="border border-line bg-surface px-6 py-14 text-center">
<p className="text-base font-bold text-brand-ink">
در حال حاضر محصول منتشرشدهای یافت نشد.
</p>
<p className="mt-2 text-sm text-muted">
بهمحض انتشار آگهیهای جدید، اینجا نمایش داده میشوند.
</p>
<Link href="/user-products" className="btn-primary mt-6 inline-flex">
مشاهده همه محصولات
</Link>
</div>
) : (
<>
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
{items.map((product) => {
const title = productTitle(product);
const category = productCategory(product);
const condition = formatCondition(product.condition);
const price = formatPrice(product.price, product.priceCurrency);
const href = `/user-products/${product.slug}`;
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
{products.map((product) => (
<article
key={product.id}
className="group flex h-full flex-col overflow-hidden border border-line bg-surface transition duration-300 hover:-translate-y-1 hover:border-brand/40"
>
<a href={product.href} className="relative block aspect-[4/3] overflow-hidden bg-[#e8eef4]">
<Image
src={product.image}
alt={product.title}
fill
className="object-cover transition duration-500 group-hover:scale-[1.04]"
sizes="(max-width: 768px) 100vw, 25vw"
/>
<span className="absolute left-3 top-3 bg-brand-ink/90 px-2.5 py-1 text-xs text-white">
{product.condition}
</span>
</a>
<div className="flex flex-1 flex-col p-4">
<p className="font-industrial text-[0.68rem] tracking-[0.14em] text-brand">
{product.titleEn}
</p>
<h3 className="mt-1 text-base font-extrabold leading-7 text-brand-ink">
<a href={product.href}>{product.title}</a>
</h3>
<p className="mt-2 text-sm text-muted">{product.category}</p>
<div className="mt-auto pt-4">
<a
href="#contact"
className="inline-flex w-full items-center justify-center border border-brand px-3 py-2.5 text-sm font-bold text-brand transition hover:bg-brand hover:text-white"
return (
<article
key={product.id}
className="group flex h-full flex-col overflow-hidden border border-line bg-surface transition duration-300 hover:-translate-y-1 hover:border-brand/40"
>
استعلام از فروشنده
</a>
</div>
</div>
</article>
))}
</div>
<Link
href={href}
className="relative block aspect-[4/3] overflow-hidden bg-[#e8eef4]"
>
{product.imageUrl ? (
<Image
src={product.imageUrl}
alt={title}
fill
unoptimized
className="object-cover transition duration-500 group-hover:scale-[1.04]"
sizes="(max-width: 768px) 100vw, 25vw"
/>
) : (
<div className="absolute inset-0 bg-[linear-gradient(135deg,#d7e0ea,#eef3f8)]" />
)}
{condition ? (
<span className="absolute left-3 top-3 bg-brand-ink/90 px-2.5 py-1 text-xs text-white">
{condition}
</span>
) : null}
{product.promoted ? (
<span className="absolute right-3 top-3 bg-brand px-2.5 py-1 text-xs text-white">
ویژه
</span>
) : null}
</Link>
<div className="mt-8 text-center">
<a href="#contact" className="btn-primary">
محصولات بیشتر
</a>
</div>
<div className="flex flex-1 flex-col p-4">
{product.titleEn ? (
<p className="font-industrial text-[0.68rem] tracking-[0.14em] text-brand">
{product.titleEn}
</p>
) : null}
<h3 className="mt-1 text-base font-extrabold leading-7 text-brand-ink">
<Link href={href}>{title}</Link>
</h3>
{category ? (
<p className="mt-2 text-sm text-muted">{category}</p>
) : null}
{price ? (
<p
dir="ltr"
className="font-industrial mt-2 w-full text-left text-sm font-bold tracking-wide text-brand-ink"
>
{price}
</p>
) : null}
<div className="mt-auto pt-4">
<Link href={href} className="btn-secondary w-full !min-h-11 text-sm">
جزئیات و استعلام
</Link> </div>
</div>
</article>
);
})}
</div>
{showMoreLink ? (
<div className="mt-8 text-center">
<Link href="/user-products" className="btn-primary">
{total > items.length ? "محصولات بیشتر" : "همه محصولات"}
</Link>
</div>
) : null} </>
)}
</div>
</section>
);