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);