Files
dashboards/services/meshkee-catalog.ts
T

207 lines
5.5 KiB
TypeScript

import { apiRequest, getAccessToken } from "@/lib/meshkee-api";
export type CatalogEntityType = "product" | "blog" | "portfolio";
export type MeshkeeEntityCategory = {
id: string;
name: string;
nameFa?: string | null;
parentId: string | null;
description?: string | null;
seoMetaTitle?: string | null;
seoMetaDescription?: string | null;
sortOrder?: number;
};
export type UpsertCategoryInput = {
parentId?: string | null;
name: string;
nameFa?: string;
description?: string;
seoMetaTitle?: string | null;
seoMetaDescription?: string | null;
};
export type CreateProductInput = {
title: string;
nameFa?: string;
summary?: string;
descriptionHtml?: string;
categoryId?: string;
featuredMediaId?: string;
galleryMediaIds?: string[];
tags?: string[];
seoMetaTitle?: string | null;
seoMetaDescription?: string | null;
status?: "draft" | "published" | "archived";
};
export type CreateBlogInput = {
title: string;
type: "news" | "article" | "blog";
abstract?: string;
mainTextHtml?: string;
categoryId?: string;
featuredMediaId?: string;
tags?: string[];
seoMetaTitle?: string | null;
seoMetaDescription?: string | null;
status?: "draft" | "published" | "archived";
};
export type CreatePortfolioInput = {
titleFa: string;
titleEn?: string;
abstract?: string;
projectUrl?: string;
mainTextHtml?: string;
categoryId?: string;
featuredMediaId?: string;
galleryMediaIds?: string[];
tags?: string[];
seoMetaTitle?: string | null;
seoMetaDescription?: string | null;
status?: "draft" | "published" | "archived";
};
export async function listMeshkeeEntityCategories(
businessId: string,
entityType: CatalogEntityType,
) {
const params = new URLSearchParams({ entityType });
const data = await apiRequest<{ items: MeshkeeEntityCategory[] }>(
`/businesses/${businessId}/categories?${params}`,
{ auth: true },
);
return (data.items ?? []).map((item) => ({
...item,
id: String(item.id),
parentId: item.parentId == null ? null : String(item.parentId),
description: item.description ?? "",
seoMetaTitle: item.seoMetaTitle ?? "",
seoMetaDescription: item.seoMetaDescription ?? "",
}));
}
export function categoryOptionLabel(category: MeshkeeEntityCategory) {
if (category.nameFa && category.nameFa !== category.name) {
return `${category.name} / ${category.nameFa}`;
}
return category.name || category.nameFa || "Category";
}
export async function createMeshkeeEntityCategory(
businessId: string,
entityType: CatalogEntityType,
body: UpsertCategoryInput,
) {
return apiRequest(`/businesses/${businessId}/categories`, {
method: "POST",
auth: true,
body: {
entityType,
parentId: body.parentId || undefined,
name: body.name.trim(),
nameFa: body.nameFa?.trim() || undefined,
description: body.description?.trim() || undefined,
seoMetaTitle: body.seoMetaTitle?.trim() || null,
seoMetaDescription: body.seoMetaDescription?.trim() || null,
},
});
}
export async function updateMeshkeeEntityCategory(
businessId: string,
categoryId: string,
body: UpsertCategoryInput,
) {
return apiRequest(`/businesses/${businessId}/categories/${categoryId}`, {
method: "PATCH",
auth: true,
body: {
parentId: body.parentId === undefined ? undefined : body.parentId || null,
name: body.name.trim(),
nameFa: body.nameFa?.trim() || null,
description: body.description?.trim() || null,
seoMetaTitle: body.seoMetaTitle?.trim() || null,
seoMetaDescription: body.seoMetaDescription?.trim() || null,
},
});
}
export async function deleteMeshkeeEntityCategory(
businessId: string,
categoryId: string,
) {
return apiRequest<{ deletedIds?: string[] }>(
`/businesses/${businessId}/categories/${categoryId}`,
{
method: "DELETE",
auth: true,
},
);
}
export async function uploadMeshkeeMediaFile(businessId: string, file: File) {
const token = getAccessToken();
const form = new FormData();
form.append("files", file);
const base =
process.env.NEXT_PUBLIC_MESHKEE_API_URL?.replace(/\/$/, "") ||
"http://localhost:3010/api/v1";
const response = await fetch(`${base}/businesses/${businessId}/media`, {
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);
}
const item = Array.isArray(payload?.items) ? payload.items[0] : payload;
return {
id: String(item.id),
publicUrl: String(item.publicUrl ?? item.url ?? ""),
};
}
export async function createMeshkeeProduct(
businessId: string,
body: CreateProductInput,
) {
return apiRequest(`/businesses/${businessId}/products`, {
method: "POST",
auth: true,
body: { status: "published", ...body },
});
}
export async function createMeshkeeBlog(
businessId: string,
body: CreateBlogInput,
) {
return apiRequest(`/businesses/${businessId}/blogs`, {
method: "POST",
auth: true,
body: { status: "published", ...body },
});
}
export async function createMeshkeePortfolio(
businessId: string,
body: CreatePortfolioInput,
) {
return apiRequest(`/businesses/${businessId}/portfolios`, {
method: "POST",
auth: true,
body: { status: "published", ...body },
});
}