import { apiRequest, getAccessToken } from "@/lib/meshkee-api"; export interface MeshkeeProduct { id: string; title: string; nameFa?: string; summary?: string; slug: string; status?: string; categoryId?: string | null; categoryName?: string; categoryParentName?: string; thumbnailUrl?: string | null; image?: string; thumbnail?: string; } export interface MeshkeeBlog { id: string; title: string; slug: string; abstract?: string | null; titleImageUrl?: string | null; createdAt?: string; } export interface MeshkeePortfolio { id: string; title?: string; titleFa?: string | null; titleEn?: string | null; name?: string; nameFa?: string | null; nameEn?: string | null; slug: string; abstract?: string | null; titleImageUrl?: string | null; coverUrl?: string | null; image?: string | null; } export interface MeshkeeMediaItem { id: string; mediaType: string; publicUrl: string; altText?: string | null; fileName?: string | null; width?: number | null; height?: number | null; } export interface MeshkeeBusinessCategory { id: string; name: string; nameFa?: string | null; parentId: string | null; slug?: string; isActive?: boolean; } export type ListResponse = { items: T[]; total: number; page: number; pageSize: number; }; type ListOptions = { page?: number; pageSize?: number; }; async function listResource( path: string, options: ListOptions = {}, ): Promise> { const page = options.page ?? 1; const pageSize = options.pageSize ?? 12; const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize), }); const data = await apiRequest>(`${path}?${params}`, { auth: true, }); return { items: (data.items ?? []).map((item) => ({ ...item, id: String((item as { id: string | number }).id), })), total: data.total ?? 0, page: data.page ?? page, pageSize: data.pageSize ?? pageSize, }; } export function listMeshkeeProducts( businessId: string, options: ListOptions = {}, ) { return listResource( `/businesses/${businessId}/products`, options, ); } export function listMeshkeeBlogs( businessId: string, options: ListOptions = {}, ) { return listResource(`/businesses/${businessId}/blogs`, options); } export function listMeshkeePortfolios( businessId: string, options: ListOptions = {}, ) { return listResource( `/businesses/${businessId}/portfolios`, options, ); } export async function listMeshkeeGallery( businessId: string, options: ListOptions = {}, ) { const page = options.page ?? 1; const pageSize = options.pageSize ?? 16; const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize), }); const data = await apiRequest>( `/businesses/${businessId}/gallery?${params}`, { auth: true }, ); return { items: (data.items ?? []).map((item) => ({ ...item, id: String(item.id), })), total: data.total ?? 0, page: data.page ?? page, pageSize: data.pageSize ?? pageSize, }; } export async function addMeshkeeGalleryItems( businessId: string, input: { files?: File[]; mediaIds?: string[] }, ) { const token = getAccessToken(); const base = process.env.NEXT_PUBLIC_MESHKEE_API_URL?.replace(/\/$/, "") || "http://localhost:3010/api/v1"; if (input.files?.length) { const form = new FormData(); for (const file of input.files) { form.append("files", file); } if (input.mediaIds?.length) { form.append("mediaIds", JSON.stringify(input.mediaIds)); } const response = await fetch(`${base}/businesses/${businessId}/gallery`, { method: "POST", headers: token ? { Authorization: `Bearer ${token}` } : undefined, body: form, }); const payload = await response.json().catch(() => null); if (!response.ok) { const message = typeof payload?.message === "string" ? payload.message : Array.isArray(payload?.message) ? payload.message.join(", ") : `Upload failed (${response.status})`; throw new Error(message); } return { items: ((payload?.items ?? []) as MeshkeeMediaItem[]).map((item) => ({ ...item, id: String(item.id), })), }; } const data = await apiRequest<{ items: MeshkeeMediaItem[] }>( `/businesses/${businessId}/gallery`, { method: "POST", auth: true, body: { mediaIds: input.mediaIds ?? [] }, }, ); return { items: (data.items ?? []).map((item) => ({ ...item, id: String(item.id), })), }; } export async function deleteMeshkeeGalleryItem( businessId: string, mediaId: string, ) { await apiRequest(`/businesses/${businessId}/gallery/${mediaId}`, { method: "DELETE", auth: true, }); } export async function listMeshkeeBusinessCategories(businessId: string) { const data = await apiRequest<{ items: MeshkeeBusinessCategory[] }>( `/businesses/${businessId}/categories`, { auth: true }, ); return (data.items ?? []).map((item) => ({ ...item, id: String(item.id), parentId: item.parentId == null ? null : String(item.parentId), })); } export async function deleteMeshkeeProduct( businessId: string, productId: string, ) { await apiRequest(`/businesses/${businessId}/products/${productId}`, { method: "DELETE", auth: true, }); } export async function deleteMeshkeeBlog(businessId: string, blogId: string) { await apiRequest(`/businesses/${businessId}/blogs/${blogId}`, { method: "DELETE", auth: true, }); } export async function deleteMeshkeeMedia(businessId: string, mediaId: string) { await apiRequest(`/businesses/${businessId}/media/${mediaId}`, { method: "DELETE", auth: true, }); } export async function deleteMeshkeePortfolio( businessId: string, portfolioId: string, ) { await apiRequest(`/businesses/${businessId}/portfolios/${portfolioId}`, { method: "DELETE", auth: true, }); } /** Collect category id plus all descendant ids. */ export function collectCategorySubtreeIds( categories: MeshkeeBusinessCategory[], rootId: string, ): Set { const ids = new Set([rootId]); let grew = true; while (grew) { grew = false; for (const category of categories) { if ( category.parentId && ids.has(category.parentId) && !ids.has(category.id) ) { ids.add(category.id); grew = true; } } } return ids; }