Files
asphp/app/meshkee.js
T
2026-09-06 00:14:09 +03:30

278 lines
10 KiB
JavaScript

import { articles, products } from "./data.js";
export const MESHKEE_DOMAIN = "asphp.ir";
export const MESHKEE_API = "https://api.meshkee.com/api/v1";
export const CUSTOMER_URL = "https://customer.asphp.ir.com";
export const DEFAULT_FAVICON = "/assets/favicon.png";
async function fetchJson(path, options = {}) {
const response = await fetch(`${MESHKEE_API}${path}`, {
cache: "no-store",
...options,
headers: {
Accept: "application/json",
...(options.body ? { "Content-Type": "application/json" } : {}),
...(options.headers || {})
}
});
if (!response.ok) {
throw new Error(`Meshkee API ${response.status}: ${path}`);
}
return response.json();
}
export async function getTenant() {
try {
return await fetchJson(`/tenants/${MESHKEE_DOMAIN}`);
} catch {
return { id: "33", domain: MESHKEE_DOMAIN, nameFa: "تجهیزات سرور پارس" };
}
}
export async function getBranding() {
try {
const branding = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/website/favicon`);
return {
faviconUrl: branding?.faviconUrl || DEFAULT_FAVICON,
logoUrl: branding?.logoUrl || "/assets/logo.png",
logoDarkUrl: branding?.logoDarkUrl || branding?.logoUrl || "/assets/logo.png",
hasDedicatedFavicon: Boolean(branding?.hasDedicatedFavicon)
};
} catch {
return {
faviconUrl: DEFAULT_FAVICON,
logoUrl: "/assets/logo.png",
logoDarkUrl: "/assets/logo.png",
hasDedicatedFavicon: false
};
}
}
export async function getStoreItems({ page = 1, pageSize = 12, query = "" } = {}) {
try {
const search = new URLSearchParams({ page: String(page), pageSize: String(pageSize) });
if (query) search.set("name", query);
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/store-items?${search}`);
const items = (data.items || []).map(normalizeStoreItem);
if (items.length) return { ...data, items };
} catch {}
const items = fallbackStoreItems();
return { items, total: items.length, page, pageSize, fallback: true };
}
export async function getStoreItem(id) {
try {
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/store-items/${encodeURIComponent(id)}`);
if (data.variant) return normalizeStoreItem(data.variant);
} catch {}
return fallbackStoreItems().find((item) => item.id === id || item.slug === id) || fallbackStoreItems()[0];
}
export async function getBlogs({ page = 1, pageSize = 12 } = {}) {
try {
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/blogs?page=${page}&pageSize=${pageSize}`);
const items = (data.items || []).map(normalizeBlog);
if (items.length) return { ...data, items };
} catch {}
const items = articles.map((article, index) => ({
id: `local-blog-${index + 1}`,
slug: `local-blog-${index + 1}`,
title: article.title,
date: article.date,
image: article.image,
abstract: article.abstract,
html: `<p>${article.abstract}</p>`
}));
return { items, total: items.length, page, pageSize, fallback: true };
}
export async function getStaticImageSlots({ pageKey = "home" } = {}) {
const fetchSlots = async (filterPageKey) => {
const search = filterPageKey ? `?pageKey=${encodeURIComponent(filterPageKey)}` : "";
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/website/static-images${search}`);
return Array.isArray(data.items) ? data.items.map(normalizeStaticImageSlot) : [];
};
try {
const pageSlots = await fetchSlots(pageKey);
if (pageSlots.length || !pageKey) return pageSlots;
return await fetchSlots("");
} catch {
return [];
}
}
export async function getStaticImageSlotMap(pageKey = "home") {
const slots = await getStaticImageSlots({ pageKey });
return Object.fromEntries(slots.map((slot) => [slot.key, slot]));
}
export function staticSlotImages(slotMap, key, fallbackImages = []) {
const slot = slotMap?.[key];
const images = slot?.images?.length ? slot.images : fallbackImages;
const pickedImages = slot?.kind === "single" ? images.slice(0, 1) : images;
return pickedImages.map((image, index) => {
const normalized = typeof image === "string" ? { url: image } : image;
return {
url: normalized.url || normalized.image || normalized.imageUrl || normalized.src,
href: normalized.linkUrl || normalized.href || normalized.link,
titleFa: normalized.titleFa || normalized.nameFa || normalized.title || normalized.alt,
titleEn: normalized.titleEn || normalized.nameEn || "",
subtext: normalized.subtext || normalized.description || "",
alt: normalized.alt || normalized.titleFa || normalized.title || slot?.label || "",
width: normalized.width,
height: normalized.height,
aspectRatio: cssAspectRatio(slot?.aspectRatio || normalized.aspectRatio),
sortOrder: normalized.sortOrder ?? index
};
}).filter((image) => image.url);
}
export async function getBlog(slug) {
try {
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/blogs/${encodeURIComponent(slug)}`);
if (data.blog) return normalizeBlog(data.blog);
} catch {}
const blogs = await getBlogs();
return blogs.items.find((blog) => blog.slug === slug || blog.id === slug) || blogs.items[0];
}
export async function submitContact(payload) {
return fetchJson(`/tenants/${MESHKEE_DOMAIN}/contact-submissions`, {
method: "POST",
body: JSON.stringify(payload)
});
}
export async function getCurrentUser(token) {
return fetchJson("/auth/me", {
headers: { Authorization: `Bearer ${token}` }
});
}
export async function addCartItem({ businessId, token, storeItemVariantId, quantity }) {
return fetchJson(`/businesses/${businessId}/cart/items`, {
method: "POST",
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ storeItemVariantId, quantity })
});
}
export function normalizeStoreItem(item) {
const product = item.product || {};
const images = [
...(Array.isArray(item.images) ? item.images : []),
...(Array.isArray(item.gallery) ? item.gallery : [])
]
.map((image) => image.url || image.imageUrl || image)
.filter(Boolean);
const image =
item.imageUrl ||
item.titleImageUrl ||
item.mainImageUrl ||
product.imageUrl ||
product.main_image ||
images[0] ||
"/assets/product-server-g9.jpg";
return {
id: String(item.id || item.variantId || item.storeItemVariantId || item.slug || product.id),
slug: String(item.slug || item.id || item.variantId || product.slug || product.id),
productId: String(item.productId || product.id || ""),
title: item.titleFa || item.nameFa || item.title || product.titleFa || product.nameFa || product.title || "محصول فروشگاه",
category: item.categoryName || product.categoryName || "تجهیزات سرور",
image,
images: images.length ? images : [image],
price: item.finalPrice || item.salePrice || item.price || item.amount || null,
currency: item.currency || item.priceCurrency || "تومان",
description: item.description || item.descriptionHtml || product.description || product.mainTextHtml || "",
technicalValues: item.technicalValues || item.technicalData || [],
quantity: item.quantity ?? item.stock ?? null,
isFestival: Boolean(item.isFestival),
raw: item
};
}
export function normalizeStaticImageSlot(slot) {
return {
key: slot.key,
pageKey: slot.pageKey,
kind: slot.kind || "single",
label: slot.label || slot.name || slot.key,
aspectRatio: cssAspectRatio(slot.aspectRatio),
recommendedWidth: slot.recommendedWidth,
itemCount: slot.itemCount,
images: Array.isArray(slot.images)
? slot.images
.map((image) => ({
id: image.id,
url: image.url || image.image || image.imageUrl || image.src,
titleFa: image.titleFa || image.nameFa || image.title,
titleEn: image.titleEn || image.nameEn || "",
subtext: image.subtext || image.description || "",
linkUrl: image.linkUrl || image.href || image.link,
width: image.width,
height: image.height,
aspectRatio: cssAspectRatio(image.aspectRatio),
sortOrder: image.sortOrder
}))
.filter((image) => image.url)
.sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0))
: []
};
}
export function cssAspectRatio(value) {
if (!value) return undefined;
const text = String(value).trim();
const comma = text.match(/^(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)$/);
if (comma) return `${comma[1]} / ${comma[2]}`;
const colon = text.match(/^(\d+(?:\.\d+)?)\s*:\s*(\d+(?:\.\d+)?)$/);
if (colon) return `${colon[1]} / ${colon[2]}`;
const slash = text.match(/^(\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)$/);
if (slash) return `${slash[1]} / ${slash[2]}`;
return undefined;
}
export function fallbackStoreItems() {
return products.map((product, index) => ({
id: `local-${index + 1}`,
slug: `local-${index + 1}`,
productId: `local-product-${index + 1}`,
title: product.title,
category: product.category,
image: product.image,
images: [product.image, "/assets/banner-2.jpg", "/assets/banner-3.jpg"],
price: null,
currency: "تومان",
description: "برای قیمت، موجودی و کانفیگ اختصاصی این محصول با کارشناسان فروش تماس بگیرید.",
technicalValues: [
{ label: "دسته بندی", value: product.category },
{ label: "وضعیت قیمت", value: "تماس با فروشگاه" }
],
quantity: null,
isFestival: index === 0,
raw: product
}));
}
export function normalizeBlog(blog) {
return {
id: String(blog.id || blog.blogId || blog.slug),
slug: String(blog.slug || blog.id || blog.blogId),
title: blog.titleFa || blog.title || blog.nameFa || "مقاله",
date: blog.publishedAt || blog.createdAt || "",
image: blog.titleImageUrl || blog.imageUrl || blog.main_image || blog.thumbnail || "/assets/banner-1.jpg",
abstract: blog.abstract || blog.summary || blog.description || "",
html: blog.mainTextHtml || blog.bodyHtml || blog.text || blog.content || "",
raw: blog
};
}