Files
dashboards/services/meshkee-businesses.ts

313 lines
7.7 KiB
TypeScript

import { apiRequest, getAccessToken } from "@/lib/meshkee-api";
export interface MeshkeeBusinessSearchItem {
id: string;
name: string;
nameFa: string | null;
slug: string;
label: string;
ownerName?: string | null;
ownerCellNumber?: string | null;
}
export interface MeshkeeBusinessCategory {
id: string;
name: string;
slug: string;
parentId: string | null;
}
export interface MeshkeeBusinessOwner {
id: string;
cellNumber: string;
firstName: string | null;
lastName: string | null;
firstNameEn: string | null;
lastNameEn: string | null;
email: string | null;
}
export interface MeshkeeBusinessDetail {
id: string;
name: string;
nameFa: string | null;
slug: string;
categories: MeshkeeBusinessCategory[];
categoryIds: string[];
owner: MeshkeeBusinessOwner | null;
logoUrl: string | null;
logoMediaId: string | null;
bannerUrl: string | null;
bannerMediaId: string | null;
createdAt?: string;
}
function mapSearchItem(
item: MeshkeeBusinessSearchItem,
): MeshkeeBusinessSearchItem {
const name = item.name ?? "";
const nameFa = item.nameFa ?? null;
return {
...item,
id: String(item.id),
name,
nameFa,
slug: item.slug ?? "",
label: item.label || name || item.slug || String(item.id),
ownerName: item.ownerName ?? null,
ownerCellNumber: item.ownerCellNumber ?? null,
};
}
export async function searchMeshkeeBusinesses(
q: string,
signal?: AbortSignal,
) {
const params = new URLSearchParams({
q,
limit: "20",
});
const data = await apiRequest<{ items: MeshkeeBusinessSearchItem[] }>(
`/businesses/search?${params}`,
{ auth: true, signal },
);
return {
items: (data.items ?? []).map(mapSearchItem),
};
}
/** Newest Meshkee businesses (super-admin list, created_at desc). */
export async function listLatestMeshkeeBusinesses(
limit = 5,
signal?: AbortSignal,
) {
const pageSize = Math.min(Math.max(limit, 5), 50);
const params = new URLSearchParams({
page: "1",
pageSize: String(pageSize),
sort: "created",
});
const data = await apiRequest<{ items: MeshkeeBusinessSearchItem[] }>(
`/businesses?${params}`,
{ auth: true, signal },
);
return {
items: (data.items ?? []).slice(0, limit).map(mapSearchItem),
};
}
export async function getMeshkeeBusiness(
businessId: string,
signal?: AbortSignal,
) {
const data = await apiRequest<MeshkeeBusinessDetail>(
`/businesses/${businessId}`,
{ auth: true, signal },
);
return {
...data,
id: String(data.id),
createdAt: data.createdAt,
categories: (data.categories ?? []).map((category) => ({
...category,
id: String(category.id),
parentId:
category.parentId == null ? null : String(category.parentId),
})),
categoryIds: (data.categoryIds ?? []).map(String),
owner: data.owner
? {
...data.owner,
id: String(data.owner.id),
}
: null,
};
}
export async function createMeshkeeBusiness(body: {
name: string;
nameFa: string;
slug?: string;
categoryIds: number[];
ownerFirstName: string;
ownerLastName: string;
ownerFirstNameEn: string;
ownerLastNameEn: string;
ownerCellNumber: string;
ownerPassword: string;
}) {
const data = await apiRequest<MeshkeeBusinessDetail>(`/businesses`, {
method: "POST",
auth: true,
body,
});
return { ...data, id: String(data.id) };
}
export async function uploadBusinessMedia(
businessId: string,
file: File,
): Promise<{ id: string; publicUrl: string }> {
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]
: Array.isArray(payload)
? payload[0]
: payload;
return {
id: String(item.id),
publicUrl: String(item.publicUrl ?? item.url ?? ""),
};
}
export async function updateBusinessBanner(
businessId: string,
bannerMediaId: string | null,
) {
return updateBusinessProfile(businessId, {
bannerMediaId: bannerMediaId == null ? null : Number(bannerMediaId),
});
}
export type BusinessPhoneType = "landline" | "cell";
export interface BusinessPhoneNumber {
type: BusinessPhoneType;
number: string;
/** Optional; supported after Meshkee deploy with label field */
label?: string;
}
export interface BusinessAddress {
id?: string;
/** Display name / label for the address */
name: string;
province: string;
city: string;
address: string;
postalCode: string;
landline?: string | null;
}
export interface BusinessProfile {
businessId: string;
profile: {
nameEn: string;
nameFa: string;
title: string;
about: string;
vision: string;
emails: string[];
phoneNumbers: BusinessPhoneNumber[];
socialMedia: Record<string, string>;
logoMediaId: string | null;
logoUrl: string | null;
bannerMediaId: string | null;
bannerUrl: string | null;
categoryIds: string[];
};
addresses: BusinessAddress[];
}
export type UpdateBusinessProfileInput = {
nameEn?: string;
nameFa?: string;
title?: string;
about?: string;
vision?: string;
phoneNumbers?: BusinessPhoneNumber[];
logoMediaId?: number | null;
bannerMediaId?: number | null;
categoryIds?: number[];
addresses?: BusinessAddress[];
};
export async function getBusinessProfile(businessId: string) {
const data = await apiRequest<BusinessProfile>(
`/businesses/${businessId}/profile`,
{ auth: true },
);
return {
businessId: String(data.businessId),
profile: {
...data.profile,
logoMediaId:
data.profile.logoMediaId == null
? null
: String(data.profile.logoMediaId),
bannerMediaId:
data.profile.bannerMediaId == null
? null
: String(data.profile.bannerMediaId),
categoryIds: (data.profile.categoryIds ?? []).map(String),
phoneNumbers: (data.profile.phoneNumbers ?? []).map((phone) => ({
type: phone.type === "landline" ? "landline" : "cell",
number: phone.number ?? "",
...(phone.label?.trim() ? { label: phone.label.trim() } : {}),
})),
emails: data.profile.emails ?? [],
about: data.profile.about ?? "",
vision: data.profile.vision ?? "",
nameEn: data.profile.nameEn ?? "",
nameFa: data.profile.nameFa ?? "",
title: data.profile.title ?? "",
logoUrl: data.profile.logoUrl ?? null,
bannerUrl: data.profile.bannerUrl ?? null,
socialMedia: data.profile.socialMedia ?? {},
},
addresses: (data.addresses ?? []).map((address) => ({
...address,
id: address.id == null ? undefined : String(address.id),
name: String(
(address as BusinessAddress & { label?: string | null }).name ??
(address as { label?: string | null }).label ??
"",
),
landline: address.landline ?? "",
postalCode: address.postalCode ?? "",
})),
};
}
export async function updateBusinessProfile(
businessId: string,
body: UpdateBusinessProfileInput,
) {
return apiRequest<BusinessProfile>(`/businesses/${businessId}/profile`, {
method: "PATCH",
auth: true,
body,
});
}