import { meshkeeApiBase, websiteDomain } from "@/lib/config"; import type { ApiBlog, ApiCategory, ApiPortfolio, ApiProduct, AuthUser, EntityComment, PagedResult, StoreItemVariants, TechnicalInfoForm, TechnicalInfoValue, } from "@/lib/types"; type Query = Record; function tenant(path: string) { return `/tenants/${encodeURIComponent(websiteDomain)}${path}`; } async function apiGet(path: string, params?: Query, revalidate = 60): Promise { const url = new URL(`${meshkeeApiBase}${path}`); if (params) { for (const [key, value] of Object.entries(params)) { if (value !== undefined && value !== null && value !== "") { url.searchParams.set(key, String(value)); } } } const response = await fetch(url.toString(), { next: { revalidate } }); if (!response.ok) throw new Error(`API ${response.status}: ${path}`); return (await response.json()) as T; } async function apiPost(path: string, body: unknown, token?: string): Promise { const response = await fetch(`${meshkeeApiBase}${path}`, { method: "POST", headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}), }, body: JSON.stringify(body), cache: "no-store", }); if (!response.ok) throw new Error(`API ${response.status}: ${path}`); return (await response.json()) as T; } async function apiGetAuthed(path: string, token: string): Promise { const response = await fetch(`${meshkeeApiBase}${path}`, { headers: { Authorization: `Bearer ${token}` }, cache: "no-store", }); if (!response.ok) throw new Error(`API ${response.status}: ${path}`); return (await response.json()) as T; } export async function safe(promise: Promise, fallback: T): Promise { try { return await promise; } catch { return fallback; } } export function getCategories(entityType: "product" | "blog" | "portfolio" = "product") { return apiGet<{ items: ApiCategory[] }>(tenant("/categories"), { entityType }).then( (data) => data.items ?? [], ); } export function getCategoryById(id: string) { return apiGet<{ category: ApiCategory }>(tenant(`/categories/by-id/${id}`)).then( (data) => data.category, ); } export function getProducts( params: { page?: number; pageSize?: number; name?: string; categoryId?: string; tag?: string; } = {}, ) { return apiGet>(tenant("/products"), params); } export function getProductById(id: string) { return apiGet<{ product: ApiProduct; relatedProducts: ApiProduct[] }>( tenant(`/products/by-id/${id}`), ); } export function getProductTechnicalInfo(id: string) { return apiGet<{ form: TechnicalInfoForm | null; values: TechnicalInfoValue[] }>( tenant(`/products/by-id/${id}/technical-info`), ); } export function getStoreItemByProduct(productId: string) { return apiGet<{ storeItem: StoreItemVariants | null }>( tenant(`/store-items/by-product/${productId}`), ); } export function getBlogs(params: { page?: number; pageSize?: number; title?: string } = {}) { return apiGet>(tenant("/blogs"), params); } export function getBlogBySlug(slug: string) { return apiGet<{ blog: ApiBlog }>(tenant(`/blogs/${encodeURIComponent(slug)}`)); } export function getPortfolios(params: { page?: number; pageSize?: number } = {}) { return apiGet>(tenant("/portfolios"), params); } export function getPortfolioBySlug(slug: string) { return apiGet<{ portfolio: ApiPortfolio }>(tenant(`/portfolios/${encodeURIComponent(slug)}`)); } export function getComments(entityType: "product" | "blog" | "portfolio", entityId: string) { return apiGet<{ items: EntityComment[] }>(tenant("/comments"), { entityType, entityId }); } export function postComment(input: { entityType: "product" | "blog" | "portfolio"; entityId: string; authorName: string; text: string; authorEmail?: string; }) { return apiPost<{ comment: EntityComment }>(tenant("/comments"), input); } export function getMe(token: string) { return apiGetAuthed<{ user: AuthUser }>("/auth/me", token); } /** Turn plain-text CMS bodies into safe HTML paragraphs when tags are absent. */ export function asRichHtml(input?: string | null): string | undefined { if (!input?.trim()) return undefined; if (/<[a-z][\s\S]*>/i.test(input)) return input; return input .split(/\r?\n+/) .map((line) => line.trim()) .filter(Boolean) .map((line) => `

${line.replace(/&/g, "&").replace(//g, ">")}

`) .join(""); } export function productImages(product: ApiProduct): string[] { const urls = [ product.thumbnailUrl, product.image, product.thumbnail, ...(product.images ?? []).map((image) => image.url), ].filter((url): url is string => Boolean(url)); return [...new Set(urls)]; } export function portfolioImages(portfolio: ApiPortfolio): string[] { const urls = [ portfolio.thumbnailUrl, portfolio.image, ...(portfolio.images ?? []).map((image) => image.url), ].filter((url): url is string => Boolean(url)); return [...new Set(urls)]; } export function jewelryCategories(categories: ApiCategory[]) { const jewelryParent = categories.find((item) => item.slug === "gold-jewels-and-rhinestones"); if (!jewelryParent) { return categories.filter((item) => item.parentId && item.isActive !== false); } return categories .filter((item) => item.parentId === jewelryParent.id && item.isActive !== false) .sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)); }