import { API } from "./site"; export class ApiError extends Error { status: number; constructor(status: number, message: string) { super(message); this.status = status; } } type Query = Record; async function apiGet(path: string, query?: Query): Promise { const url = new URL(`${API.base}${path}`); if (query) { for (const [key, value] of Object.entries(query)) { if (value !== undefined && value !== "") { url.searchParams.set(key, String(value)); } } } const response = await fetch(url, { next: { revalidate: 120 } }); if (!response.ok) { throw new ApiError(response.status, `API ${response.status} ${path}`); } return response.json() as Promise; } export type Paginated = { items: T[]; total: number; page: number; pageSize: number; }; export type ApiProduct = { id: string; title: string; nameFa: string | null; slug: string; summary?: string | null; descriptionHtml?: string | null; categoryId?: string | null; categoryName?: string | null; categoryNameFa?: string | null; thumbnailUrl?: string | null; image?: string | null; thumbnail?: string | null; images?: { mediaId: string; url: string }[]; commentCount?: number; store?: { variantCount: number; minPrice: number | null; maxPrice: number | null; inStock: boolean; } | null; }; export type ApiComment = { id: string; authorName?: string | null; text?: string | null; createdAt?: string | null; }; export type ApiBlog = { id: string; title: string; slug: string; abstract?: string | null; mainTextHtml?: string | null; categoryName?: string | null; titleImageUrl?: string | null; publishedAt?: string | null; createdAt?: string | null; author?: { firstName?: string | null; lastName?: string | null; } | null; }; export type ApiCategory = { id: string; parentId: string | null; name: string; nameFa: string | null; slug: string; isActive?: boolean; }; export type ApiVariant = { id: string; label?: string | null; price: number; discountedPrice?: number | null; stockQuantity?: number | null; sortOrder?: number; selections?: { variationId: string; variationName: string; optionId: string; value: string; }[]; }; export type ApiSpecialItem = { id: string; productId: string; productTitle: string; productNameFa: string | null; productImage: string | null; isActive: boolean; sortOrder: number; variants: ApiVariant[]; }; export type ApiStoreSpecial = { id: string; key: string; title: string; isActive: boolean; sortOrder: number; items: ApiSpecialItem[]; }; export type ApiStoreItem = { id: string; productId: string; variants: ApiVariant[]; }; const tenant = () => `/tenants/${API.domain}`; export async function getProducts(query: { page?: number; pageSize?: number; name?: string; categoryId?: string; } = {}) { return apiGet>(`${tenant()}/products`, query); } export async function getProduct(slug: string) { try { const data = await apiGet<{ product: ApiProduct }>(`${tenant()}/products/${slug}`); return data.product; } catch (error) { if (error instanceof ApiError && error.status === 404) return null; throw error; } } export async function getBlogs(query: { page?: number; pageSize?: number; title?: string } = {}) { return apiGet>(`${tenant()}/blogs`, query); } export async function getBlog(slug: string) { try { const data = await apiGet<{ blog: ApiBlog }>(`${tenant()}/blogs/${slug}`); return data.blog; } catch (error) { if (error instanceof ApiError && error.status === 404) return null; throw error; } } export async function getStoreSpecials() { const data = await apiGet<{ items: ApiStoreSpecial[] }>(`${tenant()}/store-specials`); return data.items ?? []; } export async function getStoreItemByProduct(productId: string) { try { const data = await apiGet<{ storeItem: ApiStoreItem }>(`${tenant()}/store-items/by-product/${productId}`); return data.storeItem; } catch (error) { if (error instanceof ApiError && error.status === 404) return null; throw error; } } export async function getCategories() { try { const data = await apiGet<{ items: ApiCategory[] }>(`${tenant()}/categories`, { entityType: "product" }); return data.items ?? []; } catch { return []; } } export async function getCategoryBySlug(slug: string) { const categories = await getCategories(); return categories.find((category) => category.slug === slug) ?? null; } export type ApiTenant = { id: string | number; name: string; nameFa?: string | null; slug?: string; domain?: string; }; export async function getTenant() { try { return await apiGet(`/tenants/${API.domain}`); } catch { return null; } } export async function getComments(entityType: "product" | "blog", entityId: string) { try { const data = await apiGet<{ items: ApiComment[] }>(`${tenant()}/comments`, { entityType, entityId }); return data.items ?? []; } catch { return []; } } export async function resolveProductSlug(productId: string, name: string) { if (!name) return ""; const { items } = await getProducts({ name, pageSize: 20 }); return items.find((item) => item.id === productId)?.slug ?? ""; }