← Files WixARCHIVED FILE

skills/wix-headless/references/astro/templates/stores/products/index.astro

3.05 KB · Oct 3, 2026 · 06:02 UTC

↓ Download file

---
import Layout from "../../layouts/Layout.astro";
import ProductCard from "../../components/ProductCard.astro";
import CategoryRail from "../../components/CategoryRail.astro";
import { productsV3 } from "@wix/stores";
import { listStoreCategories } from "../../utils/categories";

const PAGE_SIZE = 24;
const cursor = Astro.url.searchParams.get("cursor") ?? undefined;

let productList: any[] = [];
let nextCursor: string | null = null;
let prevCursor: string | null = null;
try {
  let builder = productsV3
    .queryProducts({ fields: ["CURRENCY"] })
    .limit(PAGE_SIZE);
  if (cursor) builder = builder.skipTo(cursor);
  const result = await builder.find();
  productList = result.items ?? [];
  nextCursor = result.cursors?.next ?? null;
  prevCursor = result.cursors?.prev ?? null;
} catch (err) {
  console.error("[products] listing query failed:", err);
}

// Drop the Wix Gift Card app's auto-mirror products. When the app is enabled,
// it provisions 5 DIGITAL Stores products (one per denomination) tagged with
// the "Gift Card" ribbon. Buying them yields a dud line item that does not
// trigger gift-card issuance — the customer-facing surface for gift cards
// is /gift-cards (gift-cards pack), not /products.
productList = productList.filter((p) => p.ribbon?.name !== "Gift Card");

const allCategories = await listStoreCategories();

// Payload for the client-side AddProductImpression event.
const impressionContents = productList.map((p, index) => ({
  id: p._id,
  name: p.name,
  price: Number(p.actualPriceRange?.minValue?.amount ?? 0),
  currency: p.actualPriceRange?.minValue?.currency ?? undefined,
  position: index + 1,
}));

function pageHref(c: string | null): string | null {
  if (!c) return null;
  const params = new URLSearchParams();
  params.set("cursor", c);
  return `/products?${params.toString()}`;
}
---

<Layout title="Products">
  <section class="page-header">
    <div class="container-page">
      <h1 class="page-header-title">Our Products</h1>
    </div>
  </section>

  <CategoryRail
    categories={allCategories}
    activeSlug={null}
    prevHref={pageHref(prevCursor)}
    nextHref={pageHref(nextCursor)}
  />

  <section class="section-pad">
    <div class="container-page">
      {productList.length === 0 ? (
        <p class="products-empty">
          No products yet &mdash; please check back shortly.
        </p>
      ) : (
        <div class="product-grid">
          {productList.map((product, idx) => (
            <ProductCard product={product} index={idx} />
          ))}
        </div>
      )}
    </div>
  </section>
</Layout>

<script
  type="application/json"
  id="analytics-impressions"
  is:inline
  set:html={JSON.stringify(impressionContents)}
/>
<script>
  import { trackEvent } from "../../utils/analytics";
  function fireImpressions() {
    const el = document.getElementById("analytics-impressions");
    if (!el?.textContent) return;
    trackEvent("AddProductImpression", {
      contents: JSON.parse(el.textContent),
      origin: "Product Listing",
    });
  }
  document.addEventListener("astro:page-load", fireImpressions);
</script>

SHA-256: a2c5fbb12b09738c027ad2c9d0c10a7de60d5bd0e46ea4bd2a47160c5b6298d4