import type { Product } from "@/lib/data"; import { meshkeeGet, tenantPath } from "@/lib/meshkee"; export type StoreVariant = { id: string; storeItemId?: string; productId?: string; productTitle?: string | null; productNameFa?: string | null; productImage?: string | null; productTotalStock?: number | null; label?: string | null; price?: number | null; discountedPrice?: number | null; stockQuantity?: number | null; isFestival?: boolean; selections?: { variationName?: string; value?: string }[]; }; export type StoreSpecial = { id: string; key: string; title: string; items?: StoreSpecialProduct[]; }; export type StoreSpecialProduct = { id: string; productId?: string; productTitle?: string | null; productNameFa?: string | null; productImage?: string | null; variants?: StoreVariant[]; }; export type CatalogProduct = { id: string; title?: string | null; nameFa?: string | null; slug?: string; summary?: string | null; descriptionHtml?: string | null; brand?: { name?: string; nameFa?: string } | string | null; thumbnailUrl?: string | null; image?: string | null; thumbnail?: string | null; images?: Array; categoryNameFa?: string | null; categoryName?: string | null; commentCount?: number; }; export type Paged = { items: T[]; total: number; page: number; pageSize: number; }; const PLACEHOLDER = "/placeholders/product.svg"; function asNumber(value: unknown) { if (typeof value === "number" && Number.isFinite(value)) return value; if (typeof value === "string" && value.trim()) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; } return null; } function discountPercent(price?: number | null, discounted?: number | null) { if (!price || !discounted || discounted >= price) return undefined; return Math.round((1 - discounted / price) * 100); } function inStock(quantity?: number | null) { return quantity == null || quantity > 0; } export function variantTitle(variant: StoreVariant) { return variant.productNameFa?.trim() || variant.productTitle?.trim() || variant.label?.trim() || "محصول"; } export function variantToProduct(variant: StoreVariant): Product { const price = asNumber(variant.discountedPrice) ?? asNumber(variant.price); const oldPrice = asNumber(variant.discountedPrice) != null ? asNumber(variant.price) ?? undefined : undefined; return { id: String(variant.id), href: `/products/${encodeURIComponent(String(variant.id))}`, title: variantTitle(variant), brand: variant.selections?.map((item) => item.value).filter(Boolean).join(" / ") || variant.label || "", price: price ?? undefined, oldPrice, discount: discountPercent(asNumber(variant.price), asNumber(variant.discountedPrice)), image: variant.productImage || PLACEHOLDER, inStock: inStock(variant.stockQuantity ?? variant.productTotalStock), }; } export function specialItemToProduct(item: StoreSpecialProduct): Product | null { const variant = item.variants?.[0]; if (!variant) return null; return variantToProduct({ ...variant, productId: item.productId ?? variant.productId, productTitle: item.productTitle ?? variant.productTitle, productNameFa: item.productNameFa ?? variant.productNameFa, productImage: item.productImage ?? variant.productImage, }); } export async function fetchStoreItems(params: { page?: number; pageSize?: number; categoryId?: string; name?: string; } = {}) { const query = new URLSearchParams(); query.set("page", String(params.page ?? 1)); query.set("pageSize", String(params.pageSize ?? 20)); if (params.categoryId) query.set("categoryId", params.categoryId); if (params.name) query.set("name", params.name); return meshkeeGet>(tenantPath(`/store-items?${query.toString()}`), { items: [], total: 0, page: 1, pageSize: params.pageSize ?? 20, }); } export async function fetchStoreVariant(variantId: string) { const data = await meshkeeGet<{ variant?: StoreVariant }>( tenantPath(`/store-items/${encodeURIComponent(variantId)}`), {}, ); return data.variant ?? null; } export async function fetchStoreItemByProduct(productId: string) { return meshkeeGet<{ storeItem?: { id?: string; productId?: string; variants?: StoreVariant[] } }>( tenantPath(`/store-items/by-product/${encodeURIComponent(productId)}`), {}, ); } export async function fetchStoreSpecials() { const data = await meshkeeGet<{ items?: StoreSpecial[] }>(tenantPath("/store-specials"), { items: [] }); return data.items ?? []; } export async function fetchSpecialProducts(key: string) { const specials = await fetchStoreSpecials(); const special = specials.find((item) => item.key === key); const products = (special?.items ?? []).map(specialItemToProduct).filter((item): item is Product => Boolean(item)); return { special, products }; } export function galleryUrls(product?: CatalogProduct | null, fallback?: string | null) { const urls: string[] = []; for (const image of product?.images ?? []) { const url = typeof image === "string" ? image : image?.url; if (url) urls.push(url); } for (const url of [product?.image, product?.thumbnailUrl, product?.thumbnail, fallback]) { if (url && !urls.includes(url)) urls.push(url); } return urls.length ? urls : [PLACEHOLDER]; } export async function fetchCatalogProductByTitle(title?: string | null, productId?: string | null) { if (!title?.trim()) return null; const data = await meshkeeGet>( tenantPath(`/products?name=${encodeURIComponent(title.trim())}&pageSize=8`), { items: [], total: 0, page: 1, pageSize: 8 }, ); const match = data.items.find((item) => String(item.id) === String(productId)) ?? data.items[0] ?? null; if (!match?.slug) return match; const detail = await meshkeeGet<{ product?: CatalogProduct }>( tenantPath(`/products/${encodeURIComponent(match.slug)}`), {}, ); return detail.product ?? match; } export function brandName(product?: CatalogProduct | null) { const brand = product?.brand; if (!brand) return ""; if (typeof brand === "string") return brand; return brand.nameFa || brand.name || ""; }