Product pages emit crawler-readable meta, public URLs use id plus Farsi slugs, and the homepage catalog matches the dashboard slot keys. Co-authored-by: Cursor <cursoragent@cursor.com>
59 lines
2.2 KiB
TypeScript
59 lines
2.2 KiB
TypeScript
import type { Metadata } from "next";
|
|
import type { ApiProduct, ApiStoreItem, ApiVariant } from "./api";
|
|
|
|
function sellingPrice(variant: ApiVariant): number {
|
|
const value = variant.discountedPrice ?? variant.price;
|
|
return typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : 0;
|
|
}
|
|
|
|
function inStock(variant: ApiVariant): boolean {
|
|
return variant.stockQuantity === null || (typeof variant.stockQuantity === "number" && variant.stockQuantity > 0);
|
|
}
|
|
|
|
export function pickTorobVariant(variants: ApiVariant[]): ApiVariant | undefined {
|
|
if (!variants.length) return undefined;
|
|
const available = variants.filter(inStock);
|
|
const pool = available.length ? available : variants;
|
|
return pool.reduce((cheapest, variant) =>
|
|
sellingPrice(variant) < sellingPrice(cheapest) ? variant : cheapest,
|
|
);
|
|
}
|
|
|
|
function toHttpsUrl(url?: string | null): string | undefined {
|
|
if (!url) return undefined;
|
|
const trimmed = url.trim();
|
|
if (trimmed.startsWith("https://")) return trimmed;
|
|
if (trimmed.startsWith("http://")) return `https://${trimmed.slice("http://".length)}`;
|
|
if (trimmed.startsWith("//")) return `https:${trimmed}`;
|
|
return undefined;
|
|
}
|
|
|
|
export function torobMetadata(product: ApiProduct, storeItem: ApiStoreItem | null): Pick<Metadata, "other" | "openGraph"> {
|
|
const variant = storeItem ? pickTorobVariant(storeItem.variants ?? []) : undefined;
|
|
const price = storeItem && variant ? sellingPrice(variant) : 0;
|
|
const available = Boolean(storeItem && variant && inStock(variant));
|
|
const name = variant?.productNameFa || variant?.productTitle || product.nameFa || product.title;
|
|
const image =
|
|
toHttpsUrl(variant?.productImage) ||
|
|
toHttpsUrl(product.thumbnailUrl) ||
|
|
toHttpsUrl(product.image) ||
|
|
toHttpsUrl(product.thumbnail) ||
|
|
toHttpsUrl(product.images?.[0]?.url);
|
|
|
|
const other: Record<string, string> = {
|
|
product_id: product.id,
|
|
product_name: name,
|
|
product_price: String(price),
|
|
availability: available ? "instock" : "outofstock",
|
|
};
|
|
|
|
if (variant && variant.discountedPrice != null) {
|
|
other.product_old_price = String(typeof variant.price === "number" ? Math.trunc(variant.price) : 0);
|
|
}
|
|
|
|
return {
|
|
other,
|
|
...(image ? { openGraph: { images: [{ url: image }] } } : {}),
|
|
};
|
|
}
|