From 69e6261949149679a9aea23a4640987394b22794 Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Sun, 27 Sep 2026 14:45:19 +0330 Subject: [PATCH] Fix product gallery to include all Meshkee images and show catalog downloads. Parse gallery entries as URL objects from the API and surface attached catalog files on the product detail page when present. Co-authored-by: Cursor --- src/app/products/[id]/[slug]/page.tsx | 45 +++++++++++++++++++++++++++ src/lib/meshkee.ts | 42 +++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 3 deletions(-) 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);