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 <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-27 14:45:19 +03:30
co-authored by Cursor
parent c78abfa1de
commit 69e6261949
2 changed files with 84 additions and 3 deletions
+39 -3
View File
@@ -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<string>();
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);