Files
dashboards/services/website-media.ts
T

134 lines
3.6 KiB
TypeScript

import { getAccessToken } from "@/lib/meshkee-api";
const API_BASE_URL =
process.env.NEXT_PUBLIC_MARKETPLACE_API_URL?.replace(/\/$/, "") ||
"http://localhost:3001/api/v1";
const WEBSITE_ORIGIN =
process.env.NEXT_PUBLIC_WEBSITE_URL?.replace(/\/$/, "") ||
"http://localhost:3000";
export type WebsiteMediaItem = {
id: string;
imageUrl: string;
linkUrl: string;
title?: string;
subtitle?: string;
cta?: string;
};
export type WebsiteHomeMedia = {
slider: WebsiteMediaItem[];
standingBanners: WebsiteMediaItem[];
categories: WebsiteMediaItem[];
menuImages: WebsiteMediaItem[];
aboutUs: WebsiteMediaItem;
banners: WebsiteMediaItem[];
};
function resolveMediaUrl(url: string) {
if (!url) return url;
if (url.startsWith("http://") || url.startsWith("https://")) return url;
// Uploaded files live on the IranBay marketplace API.
if (url.startsWith("/api/v1/")) {
const origin = API_BASE_URL.replace(/\/api\/v1$/, "");
return `${origin}${url}`;
}
// Bundled homepage assets live on the public website.
if (url.startsWith("/")) {
return `${WEBSITE_ORIGIN}${url}`;
}
return url;
}
async function parseResponse<T>(response: Response): Promise<T> {
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(", ")
: `Request failed (${response.status})`;
throw new Error(message);
}
return payload as T;
}
export async function getWebsiteHomeMedia(): Promise<WebsiteHomeMedia> {
const response = await fetch(`${API_BASE_URL}/website/home-media`, {
cache: "no-store",
});
const data = await parseResponse<WebsiteHomeMedia>(response);
return {
...data,
slider: data.slider.map((item) => ({
...item,
imageUrl: resolveMediaUrl(item.imageUrl),
})),
standingBanners: data.standingBanners.map((item) => ({
...item,
imageUrl: resolveMediaUrl(item.imageUrl),
})),
categories: data.categories.map((item) => ({
...item,
imageUrl: resolveMediaUrl(item.imageUrl),
})),
menuImages: (data.menuImages ?? []).map((item) => ({
...item,
imageUrl: resolveMediaUrl(item.imageUrl),
})),
aboutUs: {
...data.aboutUs,
imageUrl: resolveMediaUrl(data.aboutUs.imageUrl),
},
banners: data.banners.map((item) => ({
...item,
imageUrl: resolveMediaUrl(item.imageUrl),
})),
};
}
export async function saveWebsiteHomeMedia(
body: WebsiteHomeMedia,
): Promise<WebsiteHomeMedia> {
const response = await fetch(`${API_BASE_URL}/website/home-media`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
...(getAccessToken()
? { Authorization: `Bearer ${getAccessToken()}` }
: {}),
},
body: JSON.stringify(body),
});
return parseResponse<WebsiteHomeMedia>(response);
}
export async function uploadWebsiteMedia(file: File): Promise<string> {
const form = new FormData();
form.append("file", file);
const headers: HeadersInit = {};
const token = getAccessToken();
if (token) headers.Authorization = `Bearer ${token}`;
const response = await fetch(`${API_BASE_URL}/website/upload`, {
method: "POST",
headers,
body: form,
});
const data = await parseResponse<{ url: string }>(response);
return resolveMediaUrl(data.url);
}
export function newMediaItem(
partial?: Partial<WebsiteMediaItem>,
): WebsiteMediaItem {
return {
id: `item-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
imageUrl: "",
linkUrl: "/",
...partial,
};
}