Files
Kikomiko/kikomiko/dist/js/search-page.js
T

168 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
displayName,
escapeSearchHtml,
formatSearchPrice,
loadSearchCatalog,
searchCatalogProducts,
} from "/js/search-catalog.js?v=persianCopyRtl2";
import { initCardShadePickers } from "/js/buy.js?v=priceSort2";
import { refreshProductFilters } from "/js/filters.js?v=priceSort2";
import { loveYourEyesPillHtml } from "/js/love-your-eyes.js?v=lyeOff";
function shadeDots(p) {
const variants = p.colorVariants;
if (!variants || variants.length < 2) return "";
const shown = variants.slice(0, 5);
const extra = variants.length - shown.length;
return `<div class="shade-dots" role="listbox" aria-label="رنگ">${shown
.map((v) => {
const active = v.slug === p.slug ? " is-active" : "";
const bg = v.heroImage
? `background-image:url('${escapeSearchHtml(v.heroImage)}')`
: "";
const href = `/products/${escapeSearchHtml(v.slug)}/`;
return `<button
type="button"
class="shade-dot${active}"
style="${bg}"
title="${escapeSearchHtml(v.shade)}"
aria-label="${escapeSearchHtml(v.shade)}"
aria-selected="${active ? "true" : "false"}"
data-card-shade
data-slug="${escapeSearchHtml(v.slug)}"
data-image="${escapeSearchHtml(v.heroImage || "")}"
data-price="${v.price}"
data-compare="${v.compareAtPrice ?? ""}"
data-href="${href}"
></button>`;
})
.join("")}${
extra > 0
? `<span class="shade-dot-more">+${extra.toLocaleString("fa-IR")} عدد</span>`
: ""
}</div>`;
}
function cardIsInStock(p) {
const variants = p.colorVariants;
if (variants && variants.length > 1) {
return variants.some((v) => Number(v.stock) > 0);
}
return Number(p.stock) > 0;
}
function stockBadge(p) {
if (cardIsInStock(p)) return "";
return `<span class="stock-badge stock-badge--oos">نا موجود</span>`;
}
function filterAttrs(p) {
const updated = p.updatedAt ? new Date(p.updatedAt).getTime() : 0;
const discount =
p.compareAtPrice && p.compareAtPrice > p.price
? Math.round((1 - p.price / p.compareAtPrice) * 100)
: 0;
const prices = [Number(p.price) || 0];
for (const v of p.colorVariants || []) {
const n = Number(v.price);
if (Number.isFinite(n)) prices.push(n);
}
const min = Math.min(...prices);
const max = Math.max(...prices);
return [
`data-category="${escapeSearchHtml(p.category || "")}"`,
`data-category-fa="${escapeSearchHtml(p.categoryFa || "")}"`,
`data-price="${p.price}"`,
`data-price-min="${min}"`,
`data-price-max="${max}"`,
`data-name="${escapeSearchHtml(p.name)}"`,
`data-updated="${Number.isFinite(updated) ? updated : 0}"`,
`data-rating="${p.ratingAvg || 0}"`,
`data-reviews="${p.reviewCount || 0}"`,
`data-featured="${p.featured ? 1 : 0}"`,
`data-in-stock="${cardIsInStock(p) ? 1 : 0}"`,
`data-discount="${discount}"`,
].join(" ");
}
function productCardHtml(p) {
const title = displayName(p);
const href = `/products/${escapeSearchHtml(p.slug)}/`;
const discount =
p.compareAtPrice && p.compareAtPrice > p.price
? Math.round((1 - p.price / p.compareAtPrice) * 100)
: 0;
const badge =
discount >= 1 ? `<span class="offer-badge">−${discount}%</span>` : "";
const priceHtml =
discount >= 1
? `<p class="price price--offer" data-card-price>
<span class="price-now">${formatSearchPrice(p.price)}</span>
<span class="price-was">${formatSearchPrice(p.compareAtPrice)}</span>
</p>`
: `<p class="price" data-card-price>${formatSearchPrice(p.price)}</p>`;
return `<article class="product-card${discount >= 1 ? " product-card--offer" : ""}" data-product-card ${filterAttrs(p)}>
<a class="thumb" href="${href}" data-card-link>
${badge}
${stockBadge(p)}
${
p.heroImage
? `<img src="${escapeSearchHtml(p.heroImage)}" alt="${escapeSearchHtml(title)}" loading="lazy" data-card-image />`
: ""
}
</a>
<div class="meta">
${loveYourEyesPillHtml(p.slug)}
<p class="name"><a href="${href}" data-card-link>${escapeSearchHtml(title)}</a></p>
${shadeDots(p)}
<div class="card-action-row">
${priceHtml}
<button type="button" class="card-add-btn" data-add-to-cart data-product-id="${escapeSearchHtml(p.id)}" data-slug="${escapeSearchHtml(p.slug)}" data-name="${escapeSearchHtml(title)}" data-price="${p.price}" data-image="${escapeSearchHtml(p.heroImage || "")}" data-category="${escapeSearchHtml(p.category || "")}" data-category-fa="${escapeSearchHtml(p.categoryFa || "")}" aria-label="افزودن به سبد خرید">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>
</button>
</div>
</div>
</article>`;
}
const params = new URLSearchParams(location.search);
const q = (params.get("q") || "").trim();
const titleEl = document.querySelector("[data-search-title]");
const resultsEl = document.querySelector("[data-search-results]");
const emptyEl = document.querySelector("[data-search-empty]");
const countEl = document.querySelector("[data-listing-count]");
const resultPill = document.querySelector("[data-filters-result-count]");
if (titleEl && q) titleEl.textContent = `نتایج برای «${q}»`;
try {
const products = await loadSearchCatalog();
const hits = searchCatalogProducts(products, q);
if (!resultsEl) {
/* no-op */
} else if (!hits.length) {
if (emptyEl) emptyEl.hidden = false;
resultsEl.innerHTML = "";
if (countEl) countEl.textContent = "۰";
if (resultPill) resultPill.textContent = "۰";
} else {
if (emptyEl) emptyEl.hidden = true;
resultsEl.innerHTML = hits.map(productCardHtml).join("");
const n = hits.length.toLocaleString("fa-IR");
if (countEl) countEl.textContent = n;
if (resultPill) resultPill.textContent = n;
initCardShadePickers();
refreshProductFilters();
}
} catch {
if (emptyEl) {
emptyEl.hidden = false;
emptyEl.querySelector("p") &&
(emptyEl.querySelector("p").textContent = "بارگذاری نتایج ممکن نشد.");
}
}