import { PLACEHOLDER, type Post, type Product } from "./data"; import { getBlogs, getProducts, getStoreSpecials, resolveProductSlug, type ApiBlog, type ApiProduct, type ApiSpecialItem, type ApiVariant, } from "./api"; import { formatDate } from "./format"; function productImage(url?: string | null) { return url || PLACEHOLDER.product; } function pickVariantPrice(variants: ApiVariant[]) { if (!variants.length) { return { price: 0, oldPrice: undefined as number | undefined, stock: 0 }; } const inStock = variants.filter((variant) => (variant.stockQuantity ?? 0) > 0); const pool = inStock.length ? inStock : variants; const best = pool.reduce((current, next) => { const currentPrice = current.discountedPrice ?? current.price; const nextPrice = next.discountedPrice ?? next.price; return nextPrice < currentPrice ? next : current; }); const price = best.discountedPrice ?? best.price ?? 0; const oldPrice = best.discountedPrice ? best.price : undefined; const stock = variants.reduce((sum, variant) => sum + (variant.stockQuantity ?? 0), 0); return { price, oldPrice, stock }; } export function mapProduct(item: ApiProduct): Product { return { id: item.id, name: item.nameFa || item.title, slug: item.slug, price: item.store?.minPrice ?? 0, stock: item.store?.inStock ? item.store.variantCount || 1 : 0, image: productImage(item.thumbnailUrl || item.image || item.thumbnail), }; } export function mapSpecialItem(item: ApiSpecialItem, slug: string): Product { const { price, oldPrice, stock } = pickVariantPrice(item.variants ?? []); return { id: item.productId, name: item.productNameFa || item.productTitle, slug, price, oldPrice, stock, image: productImage(item.productImage), }; } export function mapBlog(item: ApiBlog): Post { return { slug: item.slug, title: item.title, date: formatDate(item.publishedAt || item.createdAt || ""), image: item.titleImageUrl || PLACEHOLDER.blog, abstract: item.abstract?.trim() || undefined, }; } export async function getSpecialProducts(key: string): Promise { try { const specials = await getStoreSpecials(); const special = specials.find((item) => item.isActive && item.key.toLowerCase() === key.toLowerCase()); if (!special) return []; const items = special.items .filter((item) => item.isActive) .sort((a, b) => a.sortOrder - b.sortOrder); return Promise.all( items.map(async (item) => { const slug = await resolveProductSlug(item.productId, item.productTitle || item.productNameFa || ""); return mapSpecialItem(item, slug); }), ); } catch { return []; } } export async function getLatestPosts(pageSize = 12): Promise { try { const data = await getBlogs({ page: 1, pageSize }); return data.items.map(mapBlog); } catch { return []; } } export async function listMappedProducts(query: { page?: number; pageSize?: number; name?: string; categoryId?: string; }) { try { const data = await getProducts(query); return { ...data, items: data.items.map(mapProduct), }; } catch { return { items: [], total: 0, page: query.page ?? 1, pageSize: query.pageSize ?? 12, }; } } export async function listMappedProductsForCategory( categoryId: string, childIds: string[], page: number, pageSize: number, ) { const ids = [categoryId, ...childIds]; if (ids.length === 1) { return listMappedProducts({ page, pageSize, categoryId }); } const results = await Promise.all(ids.map((id) => listMappedProducts({ page: 1, pageSize: 50, categoryId: id }))); const seen = new Set(); const items = results.flatMap((result) => result.items).filter((item) => { if (seen.has(item.id)) return false; seen.add(item.id); return true; }); const start = (page - 1) * pageSize; return { items: items.slice(start, start + pageSize), total: items.length, page, pageSize, }; }