/** Shared client-side catalog search (catalog.dedupe2.json). */ let catalogPromise = null; export function loadSearchCatalog() { if (!catalogPromise) { catalogPromise = fetch("/data/catalog.dedupe2.json") .then((r) => { if (!r.ok) throw new Error("catalog fetch failed"); return r.json(); }) .then((data) => data.products || []); } return catalogPromise; } export function displayName(p) { const variants = p.colorVariants; let title = p.name; if (variants && variants.length >= 2) { const shade = variants.find((v) => v.slug === p.slug)?.shade; if (shade) { const trimmed = p.name.trim(); if (trimmed.endsWith(shade)) title = trimmed.slice(0, -shade.length).trim(); } } const fa = p.categoryFa || null; if (fa && !title.includes(fa)) return `\u2067${fa} · \u2066${title}\u2069\u2069`; return title; } export function searchCatalogProducts(products, query, limit) { const needle = String(query || "").trim().toLowerCase(); if (!needle) return []; const hits = products.filter((p) => { const hay = `${p.name} ${p.category || ""} ${p.categoryFa || ""}`.toLowerCase(); return hay.includes(needle); }); return limit != null ? hits.slice(0, limit) : hits; } export function formatSearchPrice(price) { const amount = Number.isFinite(price) ? Math.round(price) : 0; return `${new Intl.NumberFormat("fa-IR", { maximumFractionDigits: 0 }).format(amount)} تومان`; } export function escapeSearchHtml(s) { return String(s) .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """); }