diff --git a/src/app/products/[id]/[slug]/page.tsx b/src/app/products/[id]/[slug]/page.tsx index c711a96..b92552c 100644 --- a/src/app/products/[id]/[slug]/page.tsx +++ b/src/app/products/[id]/[slug]/page.tsx @@ -10,6 +10,7 @@ import HomeContactStrip from "@/components/HomeContactStrip"; import { getProductById, getProducts, + productCatalog, productImages, productPriceLabel, stripHtml, @@ -52,6 +53,7 @@ export default async function ProductDetailPage({ } const images = productImages(product); + const catalog = productCatalog(product); const shortDesc = product.summary || stripHtml(product.descriptionHtml, 160); const related = product.categoryId @@ -117,6 +119,49 @@ export default async function ProductDetailPage({ استعلام قیمت + + {catalog && ( + + + + + + + + + دانلود فایل پیوست + + {catalog.fileName} + + + + + + + )} diff --git a/src/lib/meshkee.ts b/src/lib/meshkee.ts index 8fc0b57..71ac35c 100644 --- a/src/lib/meshkee.ts +++ b/src/lib/meshkee.ts @@ -113,6 +113,9 @@ export type ProductStore = { inStock: boolean; }; +/** Gallery entry from Meshkee — list/detail may return a URL string or `{ url }`. */ +export type ProductImageRef = string | { url: string; mediaId?: string }; + export type Product = { id: string; businessId: string; @@ -131,7 +134,11 @@ export type Product = { thumbnailUrl: string | null; thumbnail: string | null; image: string | null; - images: string[]; + images: ProductImageRef[]; + /** Attached catalog / datasheet file (PDF etc.), when the owner uploaded one. */ + catalogUrl: string | null; + catalogFileName: string | null; + catalogMediaId: string | null; commentCount: number; variantCount: number; createdAt: string; @@ -200,11 +207,27 @@ export function productImage(product: Product): string | null { return product.thumbnail || product.image || product.thumbnailUrl || null; } -/** Unique gallery URLs: `images[]` first, then the primary thumbnail/image. */ +function imageRefUrl(ref: ProductImageRef | null | undefined): string | null { + if (!ref) return null; + if (typeof ref === "string") return ref || null; + return typeof ref.url === "string" && ref.url ? ref.url : null; +} + +/** + * Unique gallery URLs for the product detail lightbox. + * Primary thumbnail/image first, then every gallery entry (API may return + * `images` as `{ url, mediaId }[]` rather than plain strings). + */ export function productImages(product: Product): string[] { const seen = new Set(); const out: string[] = []; - for (const url of [...(product.images ?? []), product.thumbnail, product.image, product.thumbnailUrl]) { + for (const ref of [ + product.thumbnail, + product.image, + product.thumbnailUrl, + ...(product.images ?? []), + ]) { + const url = imageRefUrl(ref as ProductImageRef); if (!url || seen.has(url)) continue; seen.add(url); out.push(url); @@ -212,6 +235,19 @@ export function productImages(product: Product): string[] { return out; } +/** Attached catalog/datasheet when the business uploaded one in Meshkee. */ +export function productCatalog( + product: Product +): { url: string; fileName: string } | null { + const url = product.catalogUrl?.trim(); + if (!url) return null; + const fileName = + product.catalogFileName?.trim() || + decodeURIComponent(url.split("/").pop() || "catalog") || + "فایل پیوست"; + return { url, fileName }; +} + export function productPriceLabel(product: Product): string { if (product.store?.minPrice) { const formatted = new Intl.NumberFormat("fa-IR").format(product.store.minPrice);