@aglyn/plugins-commerce 1.0.0-beta.218 → 1.0.0-beta.220

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +6 -0
  2. package/package.json +14 -14
  3. package/src/index.d.ts +1 -0
  4. package/src/index.js +1 -19
  5. package/src/index.js.map +1 -1
  6. package/src/lib/components/cart.js +2 -1
  7. package/src/lib/components/cart.js.map +1 -1
  8. package/src/lib/components/console/catalog-organization-card.component.js +665 -459
  9. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  10. package/src/lib/components/console/host-orders-card.component.js +2 -1
  11. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  12. package/src/lib/components/console/member-posts-card.component.js +1 -1
  13. package/src/lib/components/console/member-posts-card.component.js.map +1 -1
  14. package/src/lib/components/console/order-detail-dialog.component.js +8 -7
  15. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  16. package/src/lib/components/console/payments-settings-card.component.js +2 -2
  17. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  18. package/src/lib/components/console/product-editor-dialog.component.js +1221 -889
  19. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  20. package/src/lib/components/console/product-zones.d.ts +184 -0
  21. package/src/lib/components/console/product-zones.js +54 -0
  22. package/src/lib/components/console/product-zones.js.map +1 -0
  23. package/src/lib/components/console/products-hub-card.component.js +3 -2
  24. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  25. package/src/lib/components/console/products-hub-zone.component.d.ts +1 -1
  26. package/src/lib/components/console/products-hub-zone.component.js +2 -1
  27. package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
  28. package/src/lib/components/console/site-member-purchases.component.d.ts +24 -0
  29. package/src/lib/components/console/site-member-purchases.component.js +339 -0
  30. package/src/lib/components/console/site-member-purchases.component.js.map +1 -0
  31. package/src/lib/components/console/store-settings-card.component.js +2 -2
  32. package/src/lib/components/console/store-settings-card.component.js.map +1 -1
  33. package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -1
  34. package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
  35. package/src/lib/components/member-signup.js +2 -2
  36. package/src/lib/components/member-signup.js.map +1 -1
  37. package/src/lib/components/newsletter-signup.js +2 -2
  38. package/src/lib/components/newsletter-signup.js.map +1 -1
  39. package/src/lib/components/product-detail.js +2 -1
  40. package/src/lib/components/product-detail.js.map +1 -1
  41. package/src/lib/components/product-grid.js +1 -1
  42. package/src/lib/components/product-grid.js.map +1 -1
  43. package/src/lib/constants/operator-alerts.js +5 -3
  44. package/src/lib/constants/operator-alerts.js.map +1 -1
  45. package/src/lib/constants/storefront-payments.d.ts +33 -0
  46. package/src/lib/constants/storefront-payments.js +35 -0
  47. package/src/lib/constants/storefront-payments.js.map +1 -0
  48. package/src/lib/declarations.server.d.ts +3 -1
  49. package/src/lib/declarations.server.js +19 -1
  50. package/src/lib/declarations.server.js.map +1 -1
  51. package/src/lib/model/commerce-io.js +1 -1
  52. package/src/lib/model/commerce-io.js.map +1 -1
  53. package/src/lib/model/commerce-record-lists.d.ts +31 -0
  54. package/src/lib/model/commerce-record-lists.js +45 -0
  55. package/src/lib/model/commerce-record-lists.js.map +1 -0
  56. package/src/lib/model/commerce-record-routes.d.ts +45 -0
  57. package/src/lib/model/commerce-record-routes.js +72 -0
  58. package/src/lib/model/commerce-record-routes.js.map +1 -0
  59. package/src/lib/model/commerce-search-sources.d.ts +21 -0
  60. package/src/lib/model/commerce-search-sources.js +42 -0
  61. package/src/lib/model/commerce-search-sources.js.map +1 -0
  62. package/src/lib/model/commerce-topics.d.ts +20 -0
  63. package/src/lib/model/commerce-topics.js +33 -0
  64. package/src/lib/model/commerce-topics.js.map +1 -0
  65. package/src/lib/model/index.d.ts +1 -0
  66. package/src/lib/model/index.js +1 -0
  67. package/src/lib/model/index.js.map +1 -1
  68. package/src/lib/model/product-record.d.ts +32 -0
  69. package/src/lib/model/product-record.js +82 -0
  70. package/src/lib/model/product-record.js.map +1 -0
  71. package/src/lib/model/site-member-purchases.d.ts +48 -0
  72. package/src/lib/model/site-member-purchases.js +50 -0
  73. package/src/lib/model/site-member-purchases.js.map +1 -0
  74. package/src/lib/model/storefront-tax-summary.d.ts +121 -0
  75. package/src/lib/model/storefront-tax-summary.js +148 -0
  76. package/src/lib/model/storefront-tax-summary.js.map +1 -0
  77. package/src/lib/plan-entitlements.d.ts +35 -0
  78. package/src/lib/plan-entitlements.js +54 -0
  79. package/src/lib/plan-entitlements.js.map +1 -0
  80. package/src/lib/plugin.js +21 -0
  81. package/src/lib/plugin.js.map +1 -1
  82. package/src/lib/server/billing-webhook.js +387 -248
  83. package/src/lib/server/billing-webhook.js.map +1 -1
  84. package/src/lib/server/cart-checkout.js +3 -3
  85. package/src/lib/server/cart-checkout.js.map +1 -1
  86. package/src/lib/server/checkout.js +2 -2
  87. package/src/lib/server/checkout.js.map +1 -1
  88. package/src/lib/server/connect.js +5 -5
  89. package/src/lib/server/connect.js.map +1 -1
  90. package/src/lib/server/draft-order.js +2 -2
  91. package/src/lib/server/draft-order.js.map +1 -1
  92. package/src/lib/server/gift-cards.js +7 -3
  93. package/src/lib/server/gift-cards.js.map +1 -1
  94. package/src/lib/server/low-stock.js +3 -1
  95. package/src/lib/server/low-stock.js.map +1 -1
  96. package/src/lib/server/member-post.js +2 -2
  97. package/src/lib/server/member-post.js.map +1 -1
  98. package/src/lib/server/membership-admin-password.js +4 -1
  99. package/src/lib/server/membership-admin-password.js.map +1 -1
  100. package/src/lib/server/membership-content.js +1 -1
  101. package/src/lib/server/membership-content.js.map +1 -1
  102. package/src/lib/server/membership-register.js +13 -2
  103. package/src/lib/server/membership-register.js.map +1 -1
  104. package/src/lib/server/newsletter.js +32 -19
  105. package/src/lib/server/newsletter.js.map +1 -1
  106. package/src/lib/server/pos-order.js +3 -3
  107. package/src/lib/server/pos-order.js.map +1 -1
  108. package/src/lib/server/process-abandoned.js +4 -2
  109. package/src/lib/server/process-abandoned.js.map +1 -1
  110. package/src/lib/server/process-restock.js +7 -5
  111. package/src/lib/server/process-restock.js.map +1 -1
  112. package/src/lib/server/product-record-index.d.ts +19 -0
  113. package/src/lib/server/product-record-index.js +83 -0
  114. package/src/lib/server/product-record-index.js.map +1 -0
  115. package/src/lib/server/reconcile-stock.js +1 -1
  116. package/src/lib/server/reconcile-stock.js.map +1 -1
  117. package/src/lib/server/reserve-stock.js +6 -1
  118. package/src/lib/server/reserve-stock.js.map +1 -1
  119. package/src/lib/server/reserve.js +2 -2
  120. package/src/lib/server/reserve.js.map +1 -1
  121. package/src/lib/server/revenue-source.d.ts +130 -0
  122. package/src/lib/server/revenue-source.js +286 -0
  123. package/src/lib/server/revenue-source.js.map +1 -0
  124. package/src/lib/server/supplier-outbox.js +16 -1
  125. package/src/lib/server/supplier-outbox.js.map +1 -1
  126. package/src/lib/server/supplier-update.js +19 -9
  127. package/src/lib/server/supplier-update.js.map +1 -1
  128. package/src/lib/server/tax-profile.d.ts +8 -5
  129. package/src/lib/server/tax-profile.js +22 -5
  130. package/src/lib/server/tax-profile.js.map +1 -1
  131. package/src/lib/server/tax-return-source.d.ts +125 -0
  132. package/src/lib/server/tax-return-source.js +524 -0
  133. package/src/lib/server/tax-return-source.js.map +1 -0
  134. package/src/lib/server/tax-summary.d.ts +92 -0
  135. package/src/lib/server/tax-summary.js +258 -0
  136. package/src/lib/server/tax-summary.js.map +1 -0
  137. package/src/lib/server.d.ts +1 -0
  138. package/src/lib/server.js +23 -0
  139. package/src/lib/server.js.map +1 -1
  140. package/src/lib/starter-templates.d.ts +19 -0
  141. package/src/lib/starter-templates.js +157 -0
  142. package/src/lib/starter-templates.js.map +1 -0
  143. package/src/lib/tenant-emails.d.ts +33 -0
  144. package/src/lib/tenant-emails.js +551 -0
  145. package/src/lib/tenant-emails.js.map +1 -0
  146. package/src/lib/testing/stand-in-crm-record-routes.d.ts +25 -0
  147. package/src/lib/testing/stand-in-crm-record-routes.js +46 -0
  148. package/src/lib/testing/stand-in-crm-record-routes.js.map +1 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/product-grid.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiMagnify, mdiViewGridOutline } from '@aglyn/shared-data-mdi'\nimport {\n ListQueryNotices,\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport type { ListFilterClause } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport CardActionArea from '@mui/material/CardActionArea'\nimport CardContent from '@mui/material/CardContent'\nimport CardMedia from '@mui/material/CardMedia'\nimport Chip from '@mui/material/Chip'\nimport InputAdornment from '@mui/material/InputAdornment'\nimport MenuItem from '@mui/material/MenuItem'\nimport Skeleton from '@mui/material/Skeleton'\nimport Slider from '@mui/material/Slider'\nimport SvgIcon from '@mui/material/SvgIcon'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useEffect, useRef, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport {\n STOREFRONT_CATALOG_DECLARATION,\n STOREFRONT_CATALOG_HEADERS,\n STOREFRONT_CATALOG_OPTIONS,\n} from '../constants/storefront-catalog-query'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'product-grid'\n\nexport interface ProductGridProps {\n /** Filter source; 'all' lists the whole active catalog. */\n source?: 'all' | 'collection' | 'category' | 'tag'\n /** Collection id when source = collection (rename-safe, AGL-343). */\n collectionId?: string\n /** Legacy collection slug; used only when no collectionId is set.\n * Blank follows the /collections/{slug} URL (AGL-298). */\n collectionSlug?: string\n /** Faceted controls above the grid: tags, availability, sort. */\n showFilters?: boolean\n /** Debounced visitor search box above the grid (server-side, AGL-561). */\n showSearch?: boolean\n /** Category filter chips from the host's categories (AGL-561). */\n showCategories?: boolean\n /** Sort select: newest, name, price low→high / high→low (AGL-561). */\n showSort?: boolean\n /** Physical / digital / services filter chips (AGL-561). */\n showTypeFilter?: boolean\n /** Price range slider bounded by the catalog's prices (AGL-564). */\n showPriceFilter?: boolean\n /** Products per page; shows a Load more button while more remain. */\n pageSize?: number\n /** Category id when source = category (rename-safe, AGL-343). */\n categoryId?: string\n /** Legacy category slug; used only when no categoryId is set. */\n categorySlug?: string\n /** Tag when source = tag. */\n tag?: string\n sort?: 'name' | 'price-asc' | 'price-desc' | 'newest'\n /** Grid columns on desktop (1-6); mobile always stacks to 2/1. */\n columns?: number\n maxItems?: number\n /** Empty-state copy when nothing matches. */\n emptyText?: string\n}\n\ninterface CatalogItem {\n id: string\n name: string\n slug: string\n priceUsd: number\n maxPriceUsd: number\n compareAtPriceUsd?: number\n imageUrl?: string\n soldOut: boolean\n tags?: string[]\n}\n\ninterface CatalogCategory {\n id: string\n name: string\n slug: string\n}\n\n/** Price facet from the catalog handler, in cents (AGL-564). */\ninterface CatalogPriceBounds {\n minCents: number\n maxCents: number\n}\n\n/**\n * A control the catalog query could not apply, and why (AGL-3321): the\n * plan's refusal as the handler returns it, named here by\n * `listQueryRefusals` in the words of this grid's controls.\n */\ninterface CatalogRefusal {\n clause: ListFilterClause | 'search'\n reason: string\n}\n\n/**\n * This grid's first page, resolved server-side by the commerce site-page\n * enricher (AGL-659). Same shape the catalog endpoint returns, because both\n * go through `queryPublicCatalog`.\n */\ninterface CatalogSeed {\n items?: CatalogItem[]\n categories?: CatalogCategory[]\n priceBounds?: CatalogPriceBounds\n nextCursor?: string\n refused?: CatalogRefusal[]\n notices?: string[]\n}\n\nconst SAMPLE_ITEMS: CatalogItem[] = [\n { id: 's1', name: 'Sample product', slug: '#', priceUsd: 29, maxPriceUsd: 29, soldOut: false },\n { id: 's2', name: 'Another product', slug: '#', priceUsd: 49, maxPriceUsd: 59, compareAtPriceUsd: 79, soldOut: false },\n { id: 's3', name: 'Third product', slug: '#', priceUsd: 12, maxPriceUsd: 12, soldOut: true },\n]\n\n/** Inert chips shown on the besigner canvas when categories are on. */\nconst SAMPLE_CATEGORIES: CatalogCategory[] = [\n { id: 'sc1', name: 'Apparel', slug: 'apparel' },\n { id: 'sc2', name: 'Accessories', slug: 'accessories' },\n]\n\n/** Inert slider bounds on the canvas, matching the sample cards. */\nconst SAMPLE_PRICE_BOUNDS: CatalogPriceBounds = {\n minCents: 1200,\n maxCents: 4900,\n}\n\nconst TYPE_OPTIONS = [\n { value: 'physical', label: 'Physical' },\n { value: 'digital', label: 'Digital' },\n { value: 'service', label: 'Services' },\n] as const\n\nconst SORT_OPTIONS = [\n { value: 'newest', label: 'Newest' },\n { value: 'name', label: 'Name' },\n { value: 'price-asc', label: 'Price: low to high' },\n { value: 'price-desc', label: 'Price: high to low' },\n] as const\n\nfunction collectionSlugFromLocation(): string {\n if (typeof window === 'undefined') return ''\n const match = window.location.pathname.match(/\\/collections\\/([^/?#]+)/)\n return match ? decodeURIComponent(match[1]) : ''\n}\n\nfunction priceLabel(item: CatalogItem): string {\n return item.priceUsd === item.maxPriceUsd\n ? `$${item.priceUsd}`\n : `From $${item.priceUsd}`\n}\n\n/**\n * Product grid / PLP block (AGL-291): active catalog items from the\n * public listing API, filterable by collection/category/tag, designable\n * in besigner with sample cards (no SiteContext = inert canvas, same\n * convention as the mui Product block). Cards navigate to\n * `/products/{slug}` (PDP routes, AGL-292). Catalog UX (AGL-561):\n * optional debounced search, category chips, type chips, a sort\n * select, and Load more paging — all resolved server-side by the\n * catalog handler so large catalogs stay cheap.\n *\n * Nothing is narrowed here (AGL-3321): every control is a parameter of the\n * catalog query, and the rows shown are the rows it returned. A control the\n * query could not combine with another comes back in `refused` and is said\n * above the grid, rather than applied to the cards that happen to be loaded.\n */\nconst ProductGrid = forwardRef<HTMLDivElement, ProductGridProps>(\n (props, ref) => {\n const {\n source,\n collectionId,\n collectionSlug: collectionSlugProp,\n categoryId,\n categorySlug,\n tag,\n sort: sortProp,\n columns,\n maxItems,\n emptyText,\n showFilters,\n showSearch,\n showCategories,\n showSort,\n showTypeFilter,\n showPriceFilter,\n pageSize,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const site = Aglyn.useSite()\n const { hostId } = site\n // Server-rendered first page (AGL-659). The site-page enricher ran this\n // grid's own query on the server and keyed the result by node id — two\n // grids on a page have different queries, so an unkeyed seed would put\n // the wrong products in one of them. Absent in the besigner and preview,\n // where there is no pageData at all and the effect below still fetches.\n const nodeId = Aglyn.useNodeId()\n const seed = (\n site.pageData as\n | { commerce?: { grids?: Record<string, CatalogSeed> } }\n | undefined\n )?.commerce?.grids?.[nodeId]\n\n const [items, setItems] = useState<CatalogItem[] | null>(\n seed?.items ?? null,\n )\n const [sort, setSort] = useState(sortProp)\n // The In stock chip, asked of the catalog (`inStock=1`), never matched\n // over the cards on screen (AGL-3321).\n const [inStockOnly, setInStockOnly] = useState(false)\n const [activeTag, setActiveTag] = useState('')\n // Catalog UX state (AGL-561): the search box debounces into\n // `query`; a chip pick overrides any pinned category ('*' = All);\n // paging appends via the handler's `nextCursor` (AGL-3321).\n const [searchInput, setSearchInput] = useState('')\n const [query, setQuery] = useState('')\n const [activeCategoryId, setActiveCategoryId] = useState('')\n const [activeType, setActiveType] = useState('')\n const [categories, setCategories] = useState<CatalogCategory[]>(\n seed?.categories ?? [],\n )\n // Price filter (AGL-564): the slider drags into `priceInput` (whole\n // dollars) and debounces into `priceFilter`, which drives the query;\n // null = untouched (no price params sent). Bounds come from the\n // handler's price facets.\n const [priceBounds, setPriceBounds] = useState<CatalogPriceBounds | null>(\n seed?.priceBounds ?? null,\n )\n const [priceInput, setPriceInput] = useState<number[] | null>(null)\n const [priceFilter, setPriceFilter] = useState<number[] | null>(null)\n const [nextCursor, setNextCursor] = useState<string | null>(\n seed?.nextCursor ?? null,\n )\n // What the query could not apply, said above the grid (AGL-3321).\n const [refused, setRefused] = useState<CatalogRefusal[]>(seed?.refused ?? [])\n const [notices, setNotices] = useState<string[]>(seed?.notices ?? [])\n const [loadingMore, setLoadingMore] = useState(false)\n const fetchSeq = useRef(0)\n const collectionSlug =\n source === 'collection'\n ? collectionSlugProp || collectionSlugFromLocation()\n : collectionSlugProp\n const pageLimit =\n pageSize && Number(pageSize) > 0 ? Math.floor(Number(pageSize)) : 0\n const pinnedCategoryId = source === 'category' ? categoryId ?? '' : ''\n\n // Debounce keystrokes so large catalogs aren't fetched per letter.\n useEffect(() => {\n const timer = setTimeout(() => setQuery(searchInput.trim()), 300)\n return () => clearTimeout(timer)\n }, [searchInput])\n\n // Debounce slider drags the same way (AGL-564): one fetch per\n // settled range, not one per pixel of thumb movement.\n useEffect(() => {\n const timer = setTimeout(() => setPriceFilter(priceInput), 300)\n return () => clearTimeout(timer)\n }, [priceInput])\n\n const loadPage = useCallback(\n async (after?: string): Promise<void> => {\n if (!hostId) return\n const seq = ++fetchSeq.current\n const params = new URLSearchParams({ hostId })\n // Ids first (rename-safe, AGL-343); slugs remain as the legacy path.\n if (source === 'collection') {\n if (collectionId) params.set('collectionId', collectionId)\n else if (collectionSlug) params.set('collection', collectionSlug)\n }\n const effectiveCategoryId =\n activeCategoryId === '*' ? '' : activeCategoryId || pinnedCategoryId\n if (effectiveCategoryId) {\n params.set('categoryId', effectiveCategoryId)\n } else if (!activeCategoryId && source === 'category' && categorySlug) {\n params.set('category', categorySlug)\n }\n // A tag chip overrides a pinned tag, as a category chip does.\n const effectiveTag = activeTag || (source === 'tag' ? tag ?? '' : '')\n if (effectiveTag) params.set('tag', effectiveTag)\n if (query) params.set('q', query)\n if (activeType) params.set('type', activeType)\n if (inStockOnly) params.set('inStock', '1')\n // Slider dollars → API cents (AGL-564).\n if (priceFilter) {\n params.set('minPriceCents', String(priceFilter[0] * 100))\n params.set('maxPriceCents', String(priceFilter[1] * 100))\n }\n if (sort) params.set('sort', sort)\n const limit = pageLimit || maxItems\n if (limit) params.set('limit', String(limit))\n if (after) params.set('after', after)\n if ((showCategories || showPriceFilter) && !after) {\n params.set('facets', '1')\n }\n try {\n const response = await fetch(\n `/api/commerce/catalog?${params.toString()}`,\n )\n const payload = await response.json()\n if (seq !== fetchSeq.current) return\n const pageItems: CatalogItem[] = payload?.items ?? []\n setItems((prev) =>\n after && prev ? [...prev, ...pageItems] : pageItems,\n )\n setNextCursor(\n typeof payload?.nextCursor === 'string' ? payload.nextCursor : null,\n )\n if (!after) {\n setRefused(Array.isArray(payload?.refused) ? payload.refused : [])\n setNotices(Array.isArray(payload?.notices) ? payload.notices : [])\n }\n if (Array.isArray(payload?.categories)) {\n setCategories(payload.categories)\n }\n const bounds = payload?.priceBounds\n if (\n typeof bounds?.minCents === 'number' &&\n typeof bounds?.maxCents === 'number'\n ) {\n setPriceBounds(bounds)\n }\n } catch {\n if (seq !== fetchSeq.current) return\n if (!after) setItems([])\n setNextCursor(null)\n }\n },\n [\n hostId,\n source,\n collectionId,\n collectionSlug,\n categorySlug,\n tag,\n sort,\n maxItems,\n query,\n activeCategoryId,\n activeTag,\n activeType,\n inStockOnly,\n priceFilter,\n pageLimit,\n pinnedCategoryId,\n showCategories,\n showPriceFilter,\n ],\n )\n\n // A seeded grid already holds exactly what `loadPage(0)` would return,\n // so the first pass skips the fetch (AGL-659) — every later run, after\n // a filter or sort change, goes to the API as before.\n const seededRef = useRef(Boolean(seed))\n useEffect(() => {\n if (!hostId) return\n if (seededRef.current) {\n seededRef.current = false\n return\n }\n void loadPage()\n // Invalidate any in-flight request on dep change or unmount.\n return () => {\n fetchSeq.current++\n }\n }, [hostId, loadPage])\n\n const desktopColumns = Math.min(6, Math.max(1, columns ?? 3))\n const gridSx = {\n display: 'grid',\n gap: 2,\n gridTemplateColumns: {\n xs: 'repeat(1, 1fr)',\n sm: 'repeat(2, 1fr)',\n md: `repeat(${desktopColumns}, 1fr)`,\n },\n }\n // The rows the query returned, as returned (AGL-3321).\n const visible = hostId ? items : SAMPLE_ITEMS\n // Tag chips: the tags on the cards showing, as CHOICES — picking one asks\n // the catalog for that tag, never narrows these cards. The picked one\n // stays offered whatever the page it brought back carries.\n const facetTags = showFilters\n ? [\n ...new Set([\n ...(activeTag ? [activeTag] : []),\n ...(items ?? []).flatMap((item) => item.tags ?? []),\n ]),\n ].slice(0, 12)\n : []\n\n if (hostId && items === null) {\n return (\n // MERGE, never replace (AGL-1450). The skeleton branch replaced the\n // node's sx outright, so an authored grid flashed unstyled for as\n // long as the first fetch took and then snapped into place.\n <Box ref={ref} {...rest} sx={[gridSx, ...nodeSx]}>\n {Array.from({ length: desktopColumns }, (_item, index) => (\n <Card key={index} variant=\"outlined\">\n <Skeleton variant=\"rectangular\" height={160} />\n <CardContent>\n <Skeleton width=\"70%\" />\n <Skeleton width=\"30%\" />\n </CardContent>\n </Card>\n ))}\n </Box>\n )\n }\n\n // Catalog controls (AGL-561): search on top, category/type chips\n // under it with the sort select right-aligned — the layout every\n // mainstream commerce PLP uses. Server-side; sample data inert.\n const chipCategories = categories.length\n ? categories\n : !hostId && showCategories\n ? SAMPLE_CATEGORIES\n : []\n const selectedCategoryId =\n activeCategoryId === '*' ? '' : activeCategoryId || pinnedCategoryId\n const hasCatalogControls = Boolean(\n showSearch || showCategories || showSort || showTypeFilter ||\n showPriceFilter,\n )\n // Price slider bounds (AGL-564), widened to whole dollars so both\n // ends stay reachable; sample bounds keep the canvas designable.\n const sliderBounds = hostId ? priceBounds : SAMPLE_PRICE_BOUNDS\n const sliderMin = sliderBounds ? Math.floor(sliderBounds.minCents / 100) : 0\n const sliderMax = sliderBounds ? Math.ceil(sliderBounds.maxCents / 100) : 0\n const showSlider = Boolean(showPriceFilter && sliderMin < sliderMax)\n const controls = hasCatalogControls ? (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5, mb: 2 }}>\n {showSearch ? (\n <TextField\n value={searchInput}\n onChange={(event) => setSearchInput(event.target.value)}\n placeholder=\"Search products…\"\n size=\"small\"\n fullWidth\n slotProps={{\n input: {\n startAdornment: (\n <InputAdornment position=\"start\">\n <SvgIcon fontSize=\"small\" color=\"disabled\">\n <path d={mdiMagnify.path} />\n </SvgIcon>\n </InputAdornment>\n ),\n },\n htmlInput: { 'aria-label': 'Search products', type: 'search' },\n }}\n />\n ) : null}\n {showCategories || showTypeFilter || showSort ? (\n <Box\n sx={{\n display: 'flex',\n gap: 1,\n flexWrap: 'wrap',\n alignItems: 'center',\n }}\n >\n {showCategories && chipCategories.length ? (\n <>\n <Chip\n label=\"All\"\n size=\"small\"\n variant={selectedCategoryId ? 'outlined' : 'filled'}\n color={selectedCategoryId ? 'default' : 'primary'}\n onClick={() => {\n // One array clause per query (AGL-3321): a chip asked\n // for while searching would be refused, so picking one\n // clears the search — and a tag chip — rather than\n // leaving a chip that does nothing.\n setSearchInput('')\n setQuery('')\n setActiveTag('')\n setActiveCategoryId('*')\n }}\n />\n {chipCategories.map((category) => (\n <Chip\n key={category.id}\n label={category.name}\n size=\"small\"\n variant={\n selectedCategoryId === category.id ? 'filled' : 'outlined'\n }\n color={\n selectedCategoryId === category.id\n ? 'secondary'\n : 'default'\n }\n onClick={() => {\n setSearchInput('')\n setQuery('')\n setActiveTag('')\n setActiveCategoryId((prev) =>\n (prev || pinnedCategoryId) === category.id\n ? '*'\n : category.id,\n )\n }}\n />\n ))}\n </>\n ) : null}\n {showTypeFilter\n ? TYPE_OPTIONS.map((option) => (\n <Chip\n key={option.value}\n label={option.label}\n size=\"small\"\n variant={activeType === option.value ? 'filled' : 'outlined'}\n color={\n activeType === option.value ? 'secondary' : 'default'\n }\n onClick={() =>\n setActiveType((prev) =>\n prev === option.value ? '' : option.value,\n )\n }\n />\n ))\n : null}\n {showSort ? (\n <>\n <Box sx={{ flex: 1 }} />\n <TextField\n select\n size=\"small\"\n value={\n SORT_OPTIONS.some((option) => option.value === sort)\n ? sort\n : 'name'\n }\n onChange={(event) =>\n setSort(event.target.value as ProductGridProps['sort'])\n }\n sx={{ minWidth: 180 }}\n slotProps={{\n htmlInput: { 'aria-label': 'Sort products' },\n }}\n >\n {SORT_OPTIONS.map((option) => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n </>\n ) : null}\n </Box>\n ) : null}\n {showSlider ? (\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n gap: 1.5,\n maxWidth: 360,\n px: 1,\n }}\n >\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ whiteSpace: 'nowrap' }}\n >\n {`$${sliderMin}`}\n </Typography>\n <Slider\n size=\"small\"\n value={priceInput ?? [sliderMin, sliderMax]}\n min={sliderMin}\n max={sliderMax}\n step={1}\n disableSwap\n disabled={!hostId}\n onChange={(_event, value) =>\n setPriceInput(Array.isArray(value) ? value : [value, value])\n }\n valueLabelDisplay=\"auto\"\n valueLabelFormat={(value) => `$${value}`}\n getAriaLabel={(index) =>\n index === 0 ? 'Minimum price' : 'Maximum price'\n }\n />\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ whiteSpace: 'nowrap' }}\n >\n {`$${sliderMax}`}\n </Typography>\n </Box>\n ) : null}\n </Box>\n ) : null\n\n if (visible && visible.length === 0 && !hasCatalogControls) {\n return (\n <Box ref={ref} {...rest} sx={[{ p: 3, textAlign: 'center' }, ...nodeSx]}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {emptyText || 'No products here yet — check back soon.'}\n </Typography>\n </Box>\n )\n }\n // With controls visible the empty state renders inline, so a\n // no-result search still leaves the box available to clear.\n const emptyState = (\n <Box sx={{ p: 3, textAlign: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {query ||\n activeType ||\n selectedCategoryId ||\n activeTag ||\n inStockOnly ||\n priceFilter\n ? 'No products match — try clearing a filter.'\n : emptyText || 'No products here yet — check back soon.'}\n </Typography>\n </Box>\n )\n const loadMore =\n hostId && pageLimit && nextCursor != null ? (\n <Box sx={{ display: 'flex', justifyContent: 'center', mt: 3 }}>\n <Button\n variant=\"outlined\"\n disabled={loadingMore}\n onClick={async () => {\n if (nextCursor == null || loadingMore) return\n setLoadingMore(true)\n await loadPage(nextCursor)\n setLoadingMore(false)\n }}\n >\n {loadingMore ? 'Loading…' : 'Load more'}\n </Button>\n </Box>\n ) : null\n\n const grid = (\n <Box sx={gridSx}>\n {(visible ?? []).map((item) => {\n // AGL-1726: resolved BEFORE the guard, so a stored value that is\n // truthy but resolves to nothing renders the placeholder rather\n // than an `<img>` with no `src`.\n const imageUrl = Aglyn.siteRelativeMediaSrc(item.imageUrl, { hostId })\n return (\n <Card key={item.id} variant=\"outlined\">\n <CardActionArea\n href={hostId ? `/products/${item.slug}` : undefined}\n disabled={!hostId}\n >\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={item.name}\n sx={{ height: 160, objectFit: 'cover' }}\n // Deferred (AGL-2486), and this is the worst offender of\n // the set: a grid renders one image PER PRODUCT, all of\n // them previously unhinted, so a storefront section below\n // the fold opened N eager default-priority fetches that\n // outranked everything above it.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : (\n <Box sx={{ height: 160, bgcolor: 'action.hover' }} />\n )}\n <CardContent>\n <Typography variant=\"subtitle2\" noWrap>\n {item.name}\n </Typography>\n <Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {priceLabel(item)}\n </Typography>\n {item.compareAtPriceUsd ? (\n <Typography\n variant=\"caption\"\n color=\"text.disabled\"\n sx={{ textDecoration: 'line-through' }}\n >\n {`$${item.compareAtPriceUsd}`}\n </Typography>\n ) : null}\n {item.soldOut ? (\n <Chip label=\"Sold out\" size=\"small\" variant=\"outlined\" />\n ) : null}\n </Box>\n </CardContent>\n </CardActionArea>\n </Card>\n )\n })}\n </Box>\n )\n\n const body = visible && visible.length === 0 ? emptyState : grid\n const unapplied =\n hostId && (refused.length || notices.length) ? (\n <Box sx={{ mb: 2 }}>\n <ListQueryNotices\n refused={listQueryRefusals(refused, {\n fields: STOREFRONT_CATALOG_DECLARATION.fields,\n headers: STOREFRONT_CATALOG_HEADERS,\n options: {\n ...STOREFRONT_CATALOG_OPTIONS,\n category: categories.map((category) => ({\n value: category.id,\n label: category.name,\n })),\n },\n })}\n notices={notices}\n />\n </Box>\n ) : null\n if (!showFilters) {\n return (\n <Box ref={ref} {...rest}>\n {controls}\n {unapplied}\n {body}\n {loadMore}\n </Box>\n )\n }\n return (\n <Box ref={ref} {...rest}>\n {controls}\n <Box\n sx={{\n display: 'flex',\n gap: 1,\n flexWrap: 'wrap',\n alignItems: 'center',\n mb: 2,\n }}\n >\n <Chip\n label=\"In stock\"\n size=\"small\"\n variant={inStockOnly ? 'filled' : 'outlined'}\n color={inStockOnly ? 'primary' : 'default'}\n onClick={() => setInStockOnly((prev) => !prev)}\n />\n {facetTags.map((facetTag) => (\n <Chip\n key={facetTag}\n label={facetTag}\n size=\"small\"\n variant={activeTag === facetTag ? 'filled' : 'outlined'}\n color={activeTag === facetTag ? 'primary' : 'default'}\n onClick={() => {\n // The same one-array-clause rule as the category chips.\n setSearchInput('')\n setQuery('')\n setActiveCategoryId((prev) => (prev && prev !== '*' ? '' : prev))\n setActiveTag((prev) => (prev === facetTag ? '' : facetTag))\n }}\n />\n ))}\n <Box sx={{ flex: 1 }} />\n <Chip\n label={\n sort === 'price-asc'\n ? 'Price ↑'\n : sort === 'price-desc'\n ? 'Price ↓'\n : 'A–Z'\n }\n size=\"small\"\n variant=\"outlined\"\n onClick={() =>\n setSort((prev) =>\n prev === 'price-asc'\n ? 'price-desc'\n : prev === 'price-desc'\n ? 'name'\n : 'price-asc',\n )\n }\n />\n </Box>\n {unapplied}\n {body}\n {loadMore}\n </Box>\n )\n },\n)\nProductGrid.displayName = 'AglynProductGrid'\n\nexport const schema: Aglyn.ComponentSchema<ProductGridProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Product grid',\n description:\n 'Your catalog as a grid, with optional search, category chips, sort and paging.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiViewGridOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'source',\n label: 'Show',\n description: 'Which products to list.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { label: 'All products', value: 'all' },\n { label: 'A collection', value: 'collection' },\n { label: 'A category', value: 'category' },\n { label: 'A tag', value: 'tag' },\n ],\n },\n {\n name: 'collectionId',\n label: 'Collection',\n description:\n 'Pick the collection — stored by id, so renaming it never breaks ' +\n 'this grid. Leave empty on collection template screens to follow ' +\n 'the /collections/{slug} URL.',\n component: Aglyn.FieldComponentType.COLLECTION_SELECT,\n },\n {\n name: 'collectionSlug',\n label: 'Collection slug (legacy)',\n description: 'Used only when no collection is picked above.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'categoryId',\n label: 'Category',\n description:\n 'Pick the category — stored by id, rename-safe.',\n component: Aglyn.FieldComponentType.CATEGORY_SELECT,\n },\n {\n name: 'categorySlug',\n label: 'Category slug (legacy)',\n description: 'Used only when no category is picked above.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'tag',\n label: 'Tag',\n description: 'Products tagged with this value.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'sort',\n label: 'Sort',\n description: 'Listing order.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { label: 'Name', value: 'name' },\n { label: 'Price (low → high)', value: 'price-asc' },\n { label: 'Price (high → low)', value: 'price-desc' },\n { label: 'Newest', value: 'newest' },\n ],\n },\n {\n name: 'columns',\n label: 'Columns',\n description: 'Desktop columns (1-6).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'maxItems',\n label: 'Max items',\n description: 'Cap the number of cards.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'emptyText',\n label: 'Empty text',\n description: 'Copy when nothing matches.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'showFilters',\n label: 'Show filters',\n description: 'Tag chips, in-stock toggle, and price sort above the grid.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showSearch',\n label: 'Search box',\n description:\n 'Visitor search box above the grid — matches words at the start of ' +\n 'product names across the whole catalog (server-side).',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showCategories',\n label: 'Category chips',\n description:\n 'Filter chips built from your product categories, with an All chip.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showSort',\n label: 'Sort select',\n description:\n 'Visitor sort: newest, name, price low→high, price high→low.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showTypeFilter',\n label: 'Type filter',\n description: 'Physical / digital / services chips.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showPriceFilter',\n label: 'Price filter',\n description:\n 'Price range slider between your lowest and highest product prices.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'pageSize',\n label: 'Page size',\n description:\n 'Products per page with a Load more button; blank loads once ' +\n '(capped by Max items).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Product grid',\n pluginId: BUNDLE_ID,\n description: 'Responsive grid of catalog products',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiViewGridOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { source: 'all', sort: 'name', columns: 3 },\n },\n },\n {\n // Full shop page (AGL-561): the grid with every catalog control on\n // — search, category chips, sort, and Load more paging.\n $id: generatePresetId(ID, 'shop'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Shop catalog',\n pluginId: BUNDLE_ID,\n description: 'Product grid with search, category chips, sort, and paging',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiViewGridOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n source: 'all',\n sort: 'newest',\n columns: 3,\n showSearch: true,\n showCategories: true,\n showSort: true,\n pageSize: 12,\n },\n },\n },\n]\n\nexport default ProductGrid\n"],"names":["Aglyn","mdiMagnify","mdiViewGridOutline","ListQueryNotices","listQueryRefusals","Box","Button","Card","CardActionArea","CardContent","CardMedia","Chip","InputAdornment","MenuItem","Skeleton","Slider","SvgIcon","TextField","Typography","forwardRef","useCallback","useEffect","useRef","useState","BUNDLE_ID","STOREFRONT_CATALOG_DECLARATION","STOREFRONT_CATALOG_HEADERS","STOREFRONT_CATALOG_OPTIONS","generatePresetId","ID","SAMPLE_ITEMS","id","name","slug","priceUsd","maxPriceUsd","soldOut","compareAtPriceUsd","SAMPLE_CATEGORIES","SAMPLE_PRICE_BOUNDS","minCents","maxCents","TYPE_OPTIONS","value","label","SORT_OPTIONS","collectionSlugFromLocation","window","match","location","pathname","decodeURIComponent","priceLabel","item","ProductGrid","props","ref","site","source","collectionId","collectionSlug","collectionSlugProp","categoryId","categorySlug","tag","sort","sortProp","columns","maxItems","emptyText","showFilters","showSearch","showCategories","showSort","showTypeFilter","showPriceFilter","pageSize","rest","nodeSx","Array","isArray","useSite","hostId","nodeId","useNodeId","seed","pageData","commerce","grids","items","setItems","setSort","inStockOnly","setInStockOnly","activeTag","setActiveTag","searchInput","setSearchInput","query","setQuery","activeCategoryId","setActiveCategoryId","activeType","setActiveType","categories","setCategories","priceBounds","setPriceBounds","priceInput","setPriceInput","priceFilter","setPriceFilter","nextCursor","setNextCursor","refused","setRefused","notices","setNotices","loadingMore","setLoadingMore","fetchSeq","pageLimit","Number","Math","floor","pinnedCategoryId","timer","setTimeout","trim","clearTimeout","loadPage","after","seq","current","params","URLSearchParams","set","effectiveCategoryId","effectiveTag","String","limit","response","fetch","toString","payload","json","pageItems","prev","bounds","seededRef","Boolean","desktopColumns","min","max","gridSx","display","gap","gridTemplateColumns","xs","sm","md","visible","facetTags","Set","flatMap","tags","slice","sx","from","length","_item","index","variant","height","width","chipCategories","selectedCategoryId","hasCatalogControls","sliderBounds","sliderMin","sliderMax","ceil","showSlider","controls","flexDirection","mb","onChange","event","target","placeholder","size","fullWidth","slotProps","input","startAdornment","position","fontSize","color","path","d","htmlInput","type","flexWrap","alignItems","onClick","map","category","option","flex","select","some","minWidth","maxWidth","px","whiteSpace","step","disableSwap","disabled","_event","valueLabelDisplay","valueLabelFormat","getAriaLabel","p","textAlign","emptyState","loadMore","justifyContent","mt","grid","imageUrl","siteRelativeMediaSrc","href","undefined","component","image","alt","objectFit","DEFERRED_IMAGE_ATTRIBUTES","bgcolor","noWrap","textDecoration","body","unapplied","fields","headers","options","facetTag","displayName","schema","$id","pluginId","description","ComponentCategory","COMMERCE","icon","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","FieldComponentType","SELECT","COLLECTION_SELECT","TEXT_FIELD","CATEGORY_SELECT","CHECKBOX","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/product-grid.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiMagnify, mdiViewGridOutline } from '@aglyn/shared-data-mdi'\nimport {\n ListQueryNotices,\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport type { ListFilterClause } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport CardActionArea from '@mui/material/CardActionArea'\nimport CardContent from '@mui/material/CardContent'\nimport CardMedia from '@mui/material/CardMedia'\nimport Chip from '@mui/material/Chip'\nimport InputAdornment from '@mui/material/InputAdornment'\nimport MenuItem from '@mui/material/MenuItem'\nimport Skeleton from '@mui/material/Skeleton'\nimport Slider from '@mui/material/Slider'\nimport SvgIcon from '@mui/material/SvgIcon'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useEffect, useRef, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport {\n STOREFRONT_CATALOG_DECLARATION,\n STOREFRONT_CATALOG_HEADERS,\n STOREFRONT_CATALOG_OPTIONS,\n} from '../constants/storefront-catalog-query'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'product-grid'\n\nexport interface ProductGridProps {\n /** Filter source; 'all' lists the whole active catalog. */\n source?: 'all' | 'collection' | 'category' | 'tag'\n /** Collection id when source = collection (rename-safe, AGL-343). */\n collectionId?: string\n /** Legacy collection slug; used only when no collectionId is set.\n * Blank follows the /collections/{slug} URL (AGL-298). */\n collectionSlug?: string\n /** Faceted controls above the grid: tags, availability, sort. */\n showFilters?: boolean\n /** Debounced visitor search box above the grid (server-side, AGL-561). */\n showSearch?: boolean\n /** Category filter chips from the host's categories (AGL-561). */\n showCategories?: boolean\n /** Sort select: newest, name, price low→high / high→low (AGL-561). */\n showSort?: boolean\n /** Physical / digital / services filter chips (AGL-561). */\n showTypeFilter?: boolean\n /** Price range slider bounded by the catalog's prices (AGL-564). */\n showPriceFilter?: boolean\n /** Products per page; shows a Load more button while more remain. */\n pageSize?: number\n /** Category id when source = category (rename-safe, AGL-343). */\n categoryId?: string\n /** Legacy category slug; used only when no categoryId is set. */\n categorySlug?: string\n /** Tag when source = tag. */\n tag?: string\n sort?: 'name' | 'price-asc' | 'price-desc' | 'newest'\n /** Grid columns on desktop (1-6); mobile always stacks to 2/1. */\n columns?: number\n maxItems?: number\n /** Empty-state copy when nothing matches. */\n emptyText?: string\n}\n\ninterface CatalogItem {\n id: string\n name: string\n slug: string\n priceUsd: number\n maxPriceUsd: number\n compareAtPriceUsd?: number\n imageUrl?: string\n soldOut: boolean\n tags?: string[]\n}\n\ninterface CatalogCategory {\n id: string\n name: string\n slug: string\n}\n\n/** Price facet from the catalog handler, in cents (AGL-564). */\ninterface CatalogPriceBounds {\n minCents: number\n maxCents: number\n}\n\n/**\n * A control the catalog query could not apply, and why (AGL-3321): the\n * plan's refusal as the handler returns it, named here by\n * `listQueryRefusals` in the words of this grid's controls.\n */\ninterface CatalogRefusal {\n clause: ListFilterClause | 'search'\n reason: string\n}\n\n/**\n * This grid's first page, resolved server-side by the commerce site-page\n * enricher (AGL-659). Same shape the catalog endpoint returns, because both\n * go through `queryPublicCatalog`.\n */\ninterface CatalogSeed {\n items?: CatalogItem[]\n categories?: CatalogCategory[]\n priceBounds?: CatalogPriceBounds\n nextCursor?: string\n refused?: CatalogRefusal[]\n notices?: string[]\n}\n\nconst SAMPLE_ITEMS: CatalogItem[] = [\n { id: 's1', name: 'Sample product', slug: '#', priceUsd: 29, maxPriceUsd: 29, soldOut: false },\n { id: 's2', name: 'Another product', slug: '#', priceUsd: 49, maxPriceUsd: 59, compareAtPriceUsd: 79, soldOut: false },\n { id: 's3', name: 'Third product', slug: '#', priceUsd: 12, maxPriceUsd: 12, soldOut: true },\n]\n\n/** Inert chips shown on the besigner canvas when categories are on. */\nconst SAMPLE_CATEGORIES: CatalogCategory[] = [\n { id: 'sc1', name: 'Apparel', slug: 'apparel' },\n { id: 'sc2', name: 'Accessories', slug: 'accessories' },\n]\n\n/** Inert slider bounds on the canvas, matching the sample cards. */\nconst SAMPLE_PRICE_BOUNDS: CatalogPriceBounds = {\n minCents: 1200,\n maxCents: 4900,\n}\n\nconst TYPE_OPTIONS = [\n { value: 'physical', label: 'Physical' },\n { value: 'digital', label: 'Digital' },\n { value: 'service', label: 'Services' },\n] as const\n\nconst SORT_OPTIONS = [\n { value: 'newest', label: 'Newest' },\n { value: 'name', label: 'Name' },\n { value: 'price-asc', label: 'Price: low to high' },\n { value: 'price-desc', label: 'Price: high to low' },\n] as const\n\nfunction collectionSlugFromLocation(): string {\n if (typeof window === 'undefined') return ''\n const match = window.location.pathname.match(/\\/collections\\/([^/?#]+)/)\n return match ? decodeURIComponent(match[1]) : ''\n}\n\nfunction priceLabel(item: CatalogItem): string {\n return item.priceUsd === item.maxPriceUsd\n ? `$${item.priceUsd}`\n : `From $${item.priceUsd}`\n}\n\n/**\n * Product grid / PLP block (AGL-291): active catalog items from the\n * public listing API, filterable by collection/category/tag, designable\n * in besigner with sample cards (no SiteContext = inert canvas, same\n * convention as the mui Product block). Cards navigate to\n * `/products/{slug}` (PDP routes, AGL-292). Catalog UX (AGL-561):\n * optional debounced search, category chips, type chips, a sort\n * select, and Load more paging — all resolved server-side by the\n * catalog handler so large catalogs stay cheap.\n *\n * Nothing is narrowed here (AGL-3321): every control is a parameter of the\n * catalog query, and the rows shown are the rows it returned. A control the\n * query could not combine with another comes back in `refused` and is said\n * above the grid, rather than applied to the cards that happen to be loaded.\n */\nconst ProductGrid = forwardRef<HTMLDivElement, ProductGridProps>(\n (props, ref) => {\n const {\n source,\n collectionId,\n collectionSlug: collectionSlugProp,\n categoryId,\n categorySlug,\n tag,\n sort: sortProp,\n columns,\n maxItems,\n emptyText,\n showFilters,\n showSearch,\n showCategories,\n showSort,\n showTypeFilter,\n showPriceFilter,\n pageSize,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const site = Aglyn.useSite()\n const { hostId } = site\n // Server-rendered first page (AGL-659). The site-page enricher ran this\n // grid's own query on the server and keyed the result by node id — two\n // grids on a page have different queries, so an unkeyed seed would put\n // the wrong products in one of them. Absent in the besigner and preview,\n // where there is no pageData at all and the effect below still fetches.\n const nodeId = Aglyn.useNodeId()\n const seed = (\n site.pageData as\n | { commerce?: { grids?: Record<string, CatalogSeed> } }\n | undefined\n )?.commerce?.grids?.[nodeId]\n\n const [items, setItems] = useState<CatalogItem[] | null>(\n seed?.items ?? null,\n )\n const [sort, setSort] = useState(sortProp)\n // The In stock chip, asked of the catalog (`inStock=1`), never matched\n // over the cards on screen (AGL-3321).\n const [inStockOnly, setInStockOnly] = useState(false)\n const [activeTag, setActiveTag] = useState('')\n // Catalog UX state (AGL-561): the search box debounces into\n // `query`; a chip pick overrides any pinned category ('*' = All);\n // paging appends via the handler's `nextCursor` (AGL-3321).\n const [searchInput, setSearchInput] = useState('')\n const [query, setQuery] = useState('')\n const [activeCategoryId, setActiveCategoryId] = useState('')\n const [activeType, setActiveType] = useState('')\n const [categories, setCategories] = useState<CatalogCategory[]>(\n seed?.categories ?? [],\n )\n // Price filter (AGL-564): the slider drags into `priceInput` (whole\n // dollars) and debounces into `priceFilter`, which drives the query;\n // null = untouched (no price params sent). Bounds come from the\n // handler's price facets.\n const [priceBounds, setPriceBounds] = useState<CatalogPriceBounds | null>(\n seed?.priceBounds ?? null,\n )\n const [priceInput, setPriceInput] = useState<number[] | null>(null)\n const [priceFilter, setPriceFilter] = useState<number[] | null>(null)\n const [nextCursor, setNextCursor] = useState<string | null>(\n seed?.nextCursor ?? null,\n )\n // What the query could not apply, said above the grid (AGL-3321).\n const [refused, setRefused] = useState<CatalogRefusal[]>(seed?.refused ?? [])\n const [notices, setNotices] = useState<string[]>(seed?.notices ?? [])\n const [loadingMore, setLoadingMore] = useState(false)\n const fetchSeq = useRef(0)\n const collectionSlug =\n source === 'collection'\n ? collectionSlugProp || collectionSlugFromLocation()\n : collectionSlugProp\n const pageLimit =\n pageSize && Number(pageSize) > 0 ? Math.floor(Number(pageSize)) : 0\n const pinnedCategoryId = source === 'category' ? categoryId ?? '' : ''\n\n // Debounce keystrokes so large catalogs aren't fetched per letter.\n useEffect(() => {\n const timer = setTimeout(() => setQuery(searchInput.trim()), 300)\n return () => clearTimeout(timer)\n }, [searchInput])\n\n // Debounce slider drags the same way (AGL-564): one fetch per\n // settled range, not one per pixel of thumb movement.\n useEffect(() => {\n const timer = setTimeout(() => setPriceFilter(priceInput), 300)\n return () => clearTimeout(timer)\n }, [priceInput])\n\n const loadPage = useCallback(\n async (after?: string): Promise<void> => {\n if (!hostId) return\n const seq = ++fetchSeq.current\n const params = new URLSearchParams({ hostId })\n // Ids first (rename-safe, AGL-343); slugs remain as the legacy path.\n if (source === 'collection') {\n if (collectionId) params.set('collectionId', collectionId)\n else if (collectionSlug) params.set('collection', collectionSlug)\n }\n const effectiveCategoryId =\n activeCategoryId === '*' ? '' : activeCategoryId || pinnedCategoryId\n if (effectiveCategoryId) {\n params.set('categoryId', effectiveCategoryId)\n } else if (!activeCategoryId && source === 'category' && categorySlug) {\n params.set('category', categorySlug)\n }\n // A tag chip overrides a pinned tag, as a category chip does.\n const effectiveTag = activeTag || (source === 'tag' ? tag ?? '' : '')\n if (effectiveTag) params.set('tag', effectiveTag)\n if (query) params.set('q', query)\n if (activeType) params.set('type', activeType)\n if (inStockOnly) params.set('inStock', '1')\n // Slider dollars → API cents (AGL-564).\n if (priceFilter) {\n params.set('minPriceCents', String(priceFilter[0] * 100))\n params.set('maxPriceCents', String(priceFilter[1] * 100))\n }\n if (sort) params.set('sort', sort)\n const limit = pageLimit || maxItems\n if (limit) params.set('limit', String(limit))\n if (after) params.set('after', after)\n if ((showCategories || showPriceFilter) && !after) {\n params.set('facets', '1')\n }\n try {\n const response = await fetch(\n `/api/commerce/catalog?${params.toString()}`,\n )\n const payload = await response.json()\n if (seq !== fetchSeq.current) return\n const pageItems: CatalogItem[] = payload?.items ?? []\n setItems((prev) =>\n after && prev ? [...prev, ...pageItems] : pageItems,\n )\n setNextCursor(\n typeof payload?.nextCursor === 'string' ? payload.nextCursor : null,\n )\n if (!after) {\n setRefused(Array.isArray(payload?.refused) ? payload.refused : [])\n setNotices(Array.isArray(payload?.notices) ? payload.notices : [])\n }\n if (Array.isArray(payload?.categories)) {\n setCategories(payload.categories)\n }\n const bounds = payload?.priceBounds\n if (\n typeof bounds?.minCents === 'number' &&\n typeof bounds?.maxCents === 'number'\n ) {\n setPriceBounds(bounds)\n }\n } catch {\n if (seq !== fetchSeq.current) return\n if (!after) setItems([])\n setNextCursor(null)\n }\n },\n [\n hostId,\n source,\n collectionId,\n collectionSlug,\n categorySlug,\n tag,\n sort,\n maxItems,\n query,\n activeCategoryId,\n activeTag,\n activeType,\n inStockOnly,\n priceFilter,\n pageLimit,\n pinnedCategoryId,\n showCategories,\n showPriceFilter,\n ],\n )\n\n // A seeded grid already holds exactly what `loadPage(0)` would return,\n // so the first pass skips the fetch (AGL-659) — every later run, after\n // a filter or sort change, goes to the API as before.\n const seededRef = useRef(Boolean(seed))\n useEffect(() => {\n if (!hostId) return\n if (seededRef.current) {\n seededRef.current = false\n return\n }\n void loadPage()\n // Invalidate any in-flight request on dep change or unmount.\n return () => {\n fetchSeq.current++\n }\n }, [hostId, loadPage])\n\n const desktopColumns = Math.min(6, Math.max(1, columns ?? 3))\n const gridSx = {\n display: 'grid',\n gap: 2,\n gridTemplateColumns: {\n xs: 'repeat(1, 1fr)',\n sm: 'repeat(2, 1fr)',\n md: `repeat(${desktopColumns}, 1fr)`,\n },\n }\n // The rows the query returned, as returned (AGL-3321).\n const visible = hostId ? items : SAMPLE_ITEMS\n // Tag chips: the tags on the cards showing, as CHOICES — picking one asks\n // the catalog for that tag, never narrows these cards. The picked one\n // stays offered whatever the page it brought back carries.\n const facetTags = showFilters\n ? [\n ...new Set([\n ...(activeTag ? [activeTag] : []),\n ...(items ?? []).flatMap((item) => item.tags ?? []),\n ]),\n ].slice(0, 12)\n : []\n\n if (hostId && items === null) {\n return (\n // MERGE, never replace (AGL-1450). The skeleton branch replaced the\n // node's sx outright, so an authored grid flashed unstyled for as\n // long as the first fetch took and then snapped into place.\n <Box ref={ref} {...rest} sx={[gridSx, ...nodeSx]}>\n {Array.from({ length: desktopColumns }, (_item, index) => (\n <Card key={index} variant=\"outlined\">\n <Skeleton variant=\"rectangular\" height={160} />\n <CardContent>\n <Skeleton width=\"70%\" />\n <Skeleton width=\"30%\" />\n </CardContent>\n </Card>\n ))}\n </Box>\n )\n }\n\n // Catalog controls (AGL-561): search on top, category/type chips\n // under it with the sort select right-aligned — the layout every\n // mainstream commerce PLP uses. Server-side; sample data inert.\n const chipCategories = categories.length\n ? categories\n : !hostId && showCategories\n ? SAMPLE_CATEGORIES\n : []\n const selectedCategoryId =\n activeCategoryId === '*' ? '' : activeCategoryId || pinnedCategoryId\n const hasCatalogControls = Boolean(\n showSearch || showCategories || showSort || showTypeFilter ||\n showPriceFilter,\n )\n // Price slider bounds (AGL-564), widened to whole dollars so both\n // ends stay reachable; sample bounds keep the canvas designable.\n const sliderBounds = hostId ? priceBounds : SAMPLE_PRICE_BOUNDS\n const sliderMin = sliderBounds ? Math.floor(sliderBounds.minCents / 100) : 0\n const sliderMax = sliderBounds ? Math.ceil(sliderBounds.maxCents / 100) : 0\n const showSlider = Boolean(showPriceFilter && sliderMin < sliderMax)\n const controls = hasCatalogControls ? (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5, mb: 2 }}>\n {showSearch ? (\n <TextField\n value={searchInput}\n onChange={(event) => setSearchInput(event.target.value)}\n placeholder=\"Search products…\"\n size=\"small\"\n fullWidth\n slotProps={{\n input: {\n startAdornment: (\n <InputAdornment position=\"start\">\n <SvgIcon fontSize=\"small\" color=\"disabled\">\n <path d={mdiMagnify.path} />\n </SvgIcon>\n </InputAdornment>\n ),\n },\n htmlInput: { 'aria-label': 'Search products', type: 'search' },\n }}\n />\n ) : null}\n {showCategories || showTypeFilter || showSort ? (\n <Box\n sx={{\n display: 'flex',\n gap: 1,\n flexWrap: 'wrap',\n alignItems: 'center',\n }}\n >\n {showCategories && chipCategories.length ? (\n <>\n <Chip\n label=\"All\"\n size=\"small\"\n variant={selectedCategoryId ? 'outlined' : 'filled'}\n color={selectedCategoryId ? 'default' : 'primary'}\n onClick={() => {\n // One array clause per query (AGL-3321): a chip asked\n // for while searching would be refused, so picking one\n // clears the search — and a tag chip — rather than\n // leaving a chip that does nothing.\n setSearchInput('')\n setQuery('')\n setActiveTag('')\n setActiveCategoryId('*')\n }}\n />\n {chipCategories.map((category) => (\n <Chip\n key={category.id}\n label={category.name}\n size=\"small\"\n variant={\n selectedCategoryId === category.id ? 'filled' : 'outlined'\n }\n color={\n selectedCategoryId === category.id\n ? 'secondary'\n : 'default'\n }\n onClick={() => {\n setSearchInput('')\n setQuery('')\n setActiveTag('')\n setActiveCategoryId((prev) =>\n (prev || pinnedCategoryId) === category.id\n ? '*'\n : category.id,\n )\n }}\n />\n ))}\n </>\n ) : null}\n {showTypeFilter\n ? TYPE_OPTIONS.map((option) => (\n <Chip\n key={option.value}\n label={option.label}\n size=\"small\"\n variant={activeType === option.value ? 'filled' : 'outlined'}\n color={\n activeType === option.value ? 'secondary' : 'default'\n }\n onClick={() =>\n setActiveType((prev) =>\n prev === option.value ? '' : option.value,\n )\n }\n />\n ))\n : null}\n {showSort ? (\n <>\n <Box sx={{ flex: 1 }} />\n <TextField\n select\n size=\"small\"\n value={\n SORT_OPTIONS.some((option) => option.value === sort)\n ? sort\n : 'name'\n }\n onChange={(event) =>\n setSort(event.target.value as ProductGridProps['sort'])\n }\n sx={{ minWidth: 180 }}\n slotProps={{\n htmlInput: { 'aria-label': 'Sort products' },\n }}\n >\n {SORT_OPTIONS.map((option) => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n </>\n ) : null}\n </Box>\n ) : null}\n {showSlider ? (\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n gap: 1.5,\n maxWidth: 360,\n px: 1,\n }}\n >\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ whiteSpace: 'nowrap' }}\n >\n {`$${sliderMin}`}\n </Typography>\n <Slider\n size=\"small\"\n value={priceInput ?? [sliderMin, sliderMax]}\n min={sliderMin}\n max={sliderMax}\n step={1}\n disableSwap\n disabled={!hostId}\n onChange={(_event, value) =>\n setPriceInput(Array.isArray(value) ? value : [value, value])\n }\n valueLabelDisplay=\"auto\"\n valueLabelFormat={(value) => `$${value}`}\n getAriaLabel={(index) =>\n index === 0 ? 'Minimum price' : 'Maximum price'\n }\n />\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ whiteSpace: 'nowrap' }}\n >\n {`$${sliderMax}`}\n </Typography>\n </Box>\n ) : null}\n </Box>\n ) : null\n\n if (visible && visible.length === 0 && !hasCatalogControls) {\n return (\n <Box ref={ref} {...rest} sx={[{ p: 3, textAlign: 'center' }, ...nodeSx]}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {emptyText || 'No products here yet — check back soon.'}\n </Typography>\n </Box>\n )\n }\n // With controls visible the empty state renders inline, so a\n // no-result search still leaves the box available to clear.\n const emptyState = (\n <Box sx={{ p: 3, textAlign: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {query ||\n activeType ||\n selectedCategoryId ||\n activeTag ||\n inStockOnly ||\n priceFilter\n ? 'No products match — try clearing a filter.'\n : emptyText || 'No products here yet — check back soon.'}\n </Typography>\n </Box>\n )\n const loadMore =\n hostId && pageLimit && nextCursor != null ? (\n <Box sx={{ display: 'flex', justifyContent: 'center', mt: 3 }}>\n <Button\n variant=\"outlined\"\n disabled={loadingMore}\n onClick={async () => {\n if (nextCursor == null || loadingMore) return\n setLoadingMore(true)\n await loadPage(nextCursor)\n setLoadingMore(false)\n }}\n >\n {loadingMore ? 'Loading…' : 'Load more'}\n </Button>\n </Box>\n ) : null\n\n const grid = (\n <Box sx={gridSx}>\n {(visible ?? []).map((item) => {\n // AGL-1726: resolved BEFORE the guard, so a stored value that is\n // truthy but resolves to nothing renders the placeholder rather\n // than an `<img>` with no `src`.\n const imageUrl = Aglyn.siteRelativeMediaSrc(item.imageUrl, { hostId })\n return (\n <Card key={item.id} variant=\"outlined\">\n <CardActionArea\n href={hostId ? `/products/${item.slug}` : undefined}\n disabled={!hostId}\n >\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={item.name}\n sx={{ height: 160, objectFit: 'cover' }}\n // Deferred (AGL-2486), and this is the worst offender of\n // the set: a grid renders one image PER PRODUCT, all of\n // them previously unhinted, so a storefront section below\n // the fold opened N eager default-priority fetches that\n // outranked everything above it.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : (\n <Box sx={{ height: 160, bgcolor: 'action.hover' }} />\n )}\n <CardContent>\n <Typography variant=\"subtitle2\" noWrap>\n {item.name}\n </Typography>\n <Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {priceLabel(item)}\n </Typography>\n {item.compareAtPriceUsd ? (\n <Typography\n variant=\"caption\"\n color=\"text.disabled\"\n sx={{ textDecoration: 'line-through' }}\n >\n {`$${item.compareAtPriceUsd}`}\n </Typography>\n ) : null}\n {item.soldOut ? (\n <Chip label=\"Sold out\" size=\"small\" variant=\"outlined\" />\n ) : null}\n </Box>\n </CardContent>\n </CardActionArea>\n </Card>\n )\n })}\n </Box>\n )\n\n const body = visible && visible.length === 0 ? emptyState : grid\n const unapplied =\n hostId && (refused.length || notices.length) ? (\n <Box sx={{ mb: 2 }}>\n <ListQueryNotices\n refused={listQueryRefusals(refused, {\n fields: STOREFRONT_CATALOG_DECLARATION.fields,\n headers: STOREFRONT_CATALOG_HEADERS,\n options: {\n ...STOREFRONT_CATALOG_OPTIONS,\n category: categories.map((category) => ({\n value: category.id,\n label: category.name,\n })),\n },\n })}\n notices={notices}\n />\n </Box>\n ) : null\n if (!showFilters) {\n return (\n <Box ref={ref} {...rest}>\n {controls}\n {unapplied}\n {body}\n {loadMore}\n </Box>\n )\n }\n return (\n <Box ref={ref} {...rest}>\n {controls}\n <Box\n sx={{\n display: 'flex',\n gap: 1,\n flexWrap: 'wrap',\n alignItems: 'center',\n mb: 2,\n }}\n >\n <Chip\n label=\"In stock\"\n size=\"small\"\n variant={inStockOnly ? 'filled' : 'outlined'}\n color={inStockOnly ? 'primary' : 'default'}\n onClick={() => setInStockOnly((prev) => !prev)}\n />\n {facetTags.map((facetTag) => (\n <Chip\n key={facetTag}\n label={facetTag}\n size=\"small\"\n variant={activeTag === facetTag ? 'filled' : 'outlined'}\n color={activeTag === facetTag ? 'primary' : 'default'}\n onClick={() => {\n // The same one-array-clause rule as the category chips.\n setSearchInput('')\n setQuery('')\n setActiveCategoryId((prev) => (prev && prev !== '*' ? '' : prev))\n setActiveTag((prev) => (prev === facetTag ? '' : facetTag))\n }}\n />\n ))}\n <Box sx={{ flex: 1 }} />\n <Chip\n label={\n sort === 'price-asc'\n ? 'Price ↑'\n : sort === 'price-desc'\n ? 'Price ↓'\n : 'A–Z'\n }\n size=\"small\"\n variant=\"outlined\"\n onClick={() =>\n setSort((prev) =>\n prev === 'price-asc'\n ? 'price-desc'\n : prev === 'price-desc'\n ? 'name'\n : 'price-asc',\n )\n }\n />\n </Box>\n {unapplied}\n {body}\n {loadMore}\n </Box>\n )\n },\n)\nProductGrid.displayName = 'AglynProductGrid'\n\nexport const schema: Aglyn.ComponentSchema<ProductGridProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Product grid',\n description:\n 'Your catalog as a grid, with optional search, category chips, sort and paging.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiViewGridOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'source',\n label: 'Show',\n description: 'Which products to list.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { label: 'All products', value: 'all' },\n { label: 'A collection', value: 'collection' },\n { label: 'A category', value: 'category' },\n { label: 'A tag', value: 'tag' },\n ],\n },\n {\n name: 'collectionId',\n label: 'Collection',\n description:\n 'Pick the collection — stored by id, so renaming it never breaks ' +\n 'this grid. Leave empty on collection template pages to follow ' +\n 'the /collections/{slug} URL.',\n component: Aglyn.FieldComponentType.COLLECTION_SELECT,\n },\n {\n name: 'collectionSlug',\n label: 'Collection slug (legacy)',\n description: 'Used only when no collection is picked above.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'categoryId',\n label: 'Category',\n description:\n 'Pick the category — stored by id, rename-safe.',\n component: Aglyn.FieldComponentType.CATEGORY_SELECT,\n },\n {\n name: 'categorySlug',\n label: 'Category slug (legacy)',\n description: 'Used only when no category is picked above.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'tag',\n label: 'Tag',\n description: 'Products tagged with this value.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'sort',\n label: 'Sort',\n description: 'Listing order.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { label: 'Name', value: 'name' },\n { label: 'Price (low → high)', value: 'price-asc' },\n { label: 'Price (high → low)', value: 'price-desc' },\n { label: 'Newest', value: 'newest' },\n ],\n },\n {\n name: 'columns',\n label: 'Columns',\n description: 'Desktop columns (1-6).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'maxItems',\n label: 'Max items',\n description: 'Cap the number of cards.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'emptyText',\n label: 'Empty text',\n description: 'Copy when nothing matches.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'showFilters',\n label: 'Show filters',\n description: 'Tag chips, in-stock toggle, and price sort above the grid.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showSearch',\n label: 'Search box',\n description:\n 'Visitor search box above the grid — matches words at the start of ' +\n 'product names across the whole catalog (server-side).',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showCategories',\n label: 'Category chips',\n description:\n 'Filter chips built from your product categories, with an All chip.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showSort',\n label: 'Sort select',\n description:\n 'Visitor sort: newest, name, price low→high, price high→low.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showTypeFilter',\n label: 'Type filter',\n description: 'Physical / digital / services chips.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'showPriceFilter',\n label: 'Price filter',\n description:\n 'Price range slider between your lowest and highest product prices.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n {\n name: 'pageSize',\n label: 'Page size',\n description:\n 'Products per page with a Load more button; blank loads once ' +\n '(capped by Max items).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Product grid',\n pluginId: BUNDLE_ID,\n description: 'Responsive grid of catalog products',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiViewGridOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { source: 'all', sort: 'name', columns: 3 },\n },\n },\n {\n // Full shop page (AGL-561): the grid with every catalog control on\n // — search, category chips, sort, and Load more paging.\n $id: generatePresetId(ID, 'shop'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Shop catalog',\n pluginId: BUNDLE_ID,\n description: 'Product grid with search, category chips, sort, and paging',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiViewGridOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n source: 'all',\n sort: 'newest',\n columns: 3,\n showSearch: true,\n showCategories: true,\n showSort: true,\n pageSize: 12,\n },\n },\n },\n]\n\nexport default ProductGrid\n"],"names":["Aglyn","mdiMagnify","mdiViewGridOutline","ListQueryNotices","listQueryRefusals","Box","Button","Card","CardActionArea","CardContent","CardMedia","Chip","InputAdornment","MenuItem","Skeleton","Slider","SvgIcon","TextField","Typography","forwardRef","useCallback","useEffect","useRef","useState","BUNDLE_ID","STOREFRONT_CATALOG_DECLARATION","STOREFRONT_CATALOG_HEADERS","STOREFRONT_CATALOG_OPTIONS","generatePresetId","ID","SAMPLE_ITEMS","id","name","slug","priceUsd","maxPriceUsd","soldOut","compareAtPriceUsd","SAMPLE_CATEGORIES","SAMPLE_PRICE_BOUNDS","minCents","maxCents","TYPE_OPTIONS","value","label","SORT_OPTIONS","collectionSlugFromLocation","window","match","location","pathname","decodeURIComponent","priceLabel","item","ProductGrid","props","ref","site","source","collectionId","collectionSlug","collectionSlugProp","categoryId","categorySlug","tag","sort","sortProp","columns","maxItems","emptyText","showFilters","showSearch","showCategories","showSort","showTypeFilter","showPriceFilter","pageSize","rest","nodeSx","Array","isArray","useSite","hostId","nodeId","useNodeId","seed","pageData","commerce","grids","items","setItems","setSort","inStockOnly","setInStockOnly","activeTag","setActiveTag","searchInput","setSearchInput","query","setQuery","activeCategoryId","setActiveCategoryId","activeType","setActiveType","categories","setCategories","priceBounds","setPriceBounds","priceInput","setPriceInput","priceFilter","setPriceFilter","nextCursor","setNextCursor","refused","setRefused","notices","setNotices","loadingMore","setLoadingMore","fetchSeq","pageLimit","Number","Math","floor","pinnedCategoryId","timer","setTimeout","trim","clearTimeout","loadPage","after","seq","current","params","URLSearchParams","set","effectiveCategoryId","effectiveTag","String","limit","response","fetch","toString","payload","json","pageItems","prev","bounds","seededRef","Boolean","desktopColumns","min","max","gridSx","display","gap","gridTemplateColumns","xs","sm","md","visible","facetTags","Set","flatMap","tags","slice","sx","from","length","_item","index","variant","height","width","chipCategories","selectedCategoryId","hasCatalogControls","sliderBounds","sliderMin","sliderMax","ceil","showSlider","controls","flexDirection","mb","onChange","event","target","placeholder","size","fullWidth","slotProps","input","startAdornment","position","fontSize","color","path","d","htmlInput","type","flexWrap","alignItems","onClick","map","category","option","flex","select","some","minWidth","maxWidth","px","whiteSpace","step","disableSwap","disabled","_event","valueLabelDisplay","valueLabelFormat","getAriaLabel","p","textAlign","emptyState","loadMore","justifyContent","mt","grid","imageUrl","siteRelativeMediaSrc","href","undefined","component","image","alt","objectFit","DEFERRED_IMAGE_ATTRIBUTES","bgcolor","noWrap","textDecoration","body","unapplied","fields","headers","options","facetTag","displayName","schema","$id","pluginId","description","ComponentCategory","COMMERCE","icon","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","FieldComponentType","SELECT","COLLECTION_SELECT","TEXT_FIELD","CATEGORY_SELECT","CHECKBOX","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
@@ -44,7 +44,9 @@ export const COMMERCE_TAX_NOT_REVERSED = {
44
44
  tier: 'must',
45
45
  category: 'payments',
46
46
  title: 'Sales tax not reversed on invoice {{invoiceId}}',
47
- body: '{{amount}} of sales tax on invoice {{invoiceId}} (site {{hostId}}) was not pulled back from the merchant: {{reason}}.',
47
+ // `{{site}}` is the site's name and id (AGL-3432), so staff need not look
48
+ // it up; the last sentence is the consequence the description states.
49
+ body: '{{amount}} of sales tax on invoice {{invoiceId}} for site {{site}} was not pulled back from the merchant: {{reason}}. The platform files and remits that tax, so it may owe tax it does not hold.',
48
50
  delivery: 'immediate',
49
51
  dedupeWindowMinutes: 7 * DAY,
50
52
  defaultEnabled: true
@@ -57,7 +59,7 @@ export const COMMERCE_SELLER_SHARE_NOT_REVERSED = {
57
59
  tier: 'must',
58
60
  category: 'payments',
59
61
  title: 'Seller share not reversed on dispute {{disputeId}}',
60
- body: 'The lost dispute {{disputeId}} on order {{orderId}} (site {{hostId}}) cost {{amount}}, and the merchant’s share was not reversed: {{reason}}.',
62
+ body: 'The lost dispute {{disputeId}} on order {{orderId}} from site {{site}} cost {{amount}}, and the merchant’s share was not reversed: {{reason}}. The platform is out that share until it is recovered by hand in Stripe.',
61
63
  delivery: 'immediate',
62
64
  dedupeWindowMinutes: 30 * DAY,
63
65
  defaultEnabled: true
@@ -70,7 +72,7 @@ export const COMMERCE_FEE_REPRICE_REFUSED = {
70
72
  tier: 'must',
71
73
  category: 'payments',
72
74
  title: 'Platform fee not corrected on {{subject}}',
73
- body: 'The platform fee on {{subject}} (site {{hostId}}) could not be corrected: {{reason}}.',
75
+ body: 'The platform fee on {{subject}} for site {{site}} could not be corrected: {{reason}}. Until it is, the merchant is charged a fee its plan does not set.',
74
76
  delivery: 'immediate',
75
77
  dedupeWindowMinutes: 7 * DAY,
76
78
  defaultEnabled: true
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/constants/operator-alerts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { OperatorAlertDefinition } from '@aglyn/aglyn/app-utils/operator-alerts'\nimport { BUNDLE_ID } from './bundle-common'\n\n/**\n * The storefront's operator alerts (AGL-3377): the money faults only this\n * plugin can see, declared in the registry's shape and registered from\n * `declarations.server`, so core lists none of them and an install without\n * commerce shows none of them on Staff → Operator alerts.\n *\n * Each is raised through `raiseOperatorAlert` with its definition, which\n * also registers it in a process whose declarations have not loaded yet.\n */\nconst DAY = 24 * 60\n\nexport const COMMERCE_CHARGEBACK_UNROUTABLE: OperatorAlertDefinition = {\n type: 'commerce.chargebackUnroutable',\n pluginId: BUNDLE_ID,\n label: 'Storefront chargeback could not be routed',\n description:\n 'A storefront chargeback matched no order, or more than one, so no order was flagged, no seller share reversed and no merchant told. A missing index means every storefront chargeback is being ignored.',\n tier: 'must',\n category: 'payments',\n title: 'A {{amount}} chargeback could not be routed to an order',\n body: '{{detail}}',\n delivery: 'immediate',\n dedupeWindowMinutes: DAY,\n defaultEnabled: true,\n}\n\nexport const COMMERCE_TAX_NOT_REVERSED: OperatorAlertDefinition = {\n type: 'commerce.taxNotReversed',\n pluginId: BUNDLE_ID,\n label: 'Subscription sales tax not reversed',\n description:\n 'Sales tax collected on a storefront subscription stayed with the merchant instead of coming back to the platform, which files and remits it. The platform may owe tax it does not hold.',\n tier: 'must',\n category: 'payments',\n title: 'Sales tax not reversed on invoice {{invoiceId}}',\n body:\n '{{amount}} of sales tax on invoice {{invoiceId}} (site {{hostId}}) was not pulled back from the merchant: {{reason}}.',\n delivery: 'immediate',\n dedupeWindowMinutes: 7 * DAY,\n defaultEnabled: true,\n}\n\nexport const COMMERCE_SELLER_SHARE_NOT_REVERSED: OperatorAlertDefinition = {\n type: 'commerce.sellerShareNotReversed',\n pluginId: BUNDLE_ID,\n label: 'Seller share not reversed on a lost dispute',\n description:\n 'A storefront dispute was lost and the merchant’s share could not be pulled back, so the platform is out the principal. Recover it by hand in Stripe.',\n tier: 'must',\n category: 'payments',\n title: 'Seller share not reversed on dispute {{disputeId}}',\n body:\n 'The lost dispute {{disputeId}} on order {{orderId}} (site {{hostId}}) cost {{amount}}, and the merchant’s share was not reversed: {{reason}}.',\n delivery: 'immediate',\n dedupeWindowMinutes: 30 * DAY,\n defaultEnabled: true,\n}\n\nexport const COMMERCE_FEE_REPRICE_REFUSED: OperatorAlertDefinition = {\n type: 'commerce.feeRepriceRefused',\n pluginId: BUNDLE_ID,\n label: 'Platform fee correction refused',\n description:\n 'Stripe refused to change a storefront subscription’s platform fee, or to refund the part of a fee charged on the wrong base. The merchant is being charged a fee the plan does not set.',\n tier: 'must',\n category: 'payments',\n title: 'Platform fee not corrected on {{subject}}',\n body: 'The platform fee on {{subject}} (site {{hostId}}) could not be corrected: {{reason}}.',\n delivery: 'immediate',\n dedupeWindowMinutes: 7 * DAY,\n defaultEnabled: true,\n}\n\nexport const COMMERCE_OPERATOR_ALERTS: readonly OperatorAlertDefinition[] = [\n COMMERCE_CHARGEBACK_UNROUTABLE,\n COMMERCE_TAX_NOT_REVERSED,\n COMMERCE_SELLER_SHARE_NOT_REVERSED,\n COMMERCE_FEE_REPRICE_REFUSED,\n]\n"],"names":["BUNDLE_ID","DAY","COMMERCE_CHARGEBACK_UNROUTABLE","type","pluginId","label","description","tier","category","title","body","delivery","dedupeWindowMinutes","defaultEnabled","COMMERCE_TAX_NOT_REVERSED","COMMERCE_SELLER_SHARE_NOT_REVERSED","COMMERCE_FEE_REPRICE_REFUSED","COMMERCE_OPERATOR_ALERTS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,SAAS,QAAQ,qBAAiB;AAE3C;;;;;;;;CAQC,GACD,MAAMC,MAAM,KAAK;AAEjB,OAAO,MAAMC,iCAA0D;IACrEC,MAAM;IACNC,UAAUJ;IACVK,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MAAM;IACNC,UAAU;IACVC,qBAAqBX;IACrBY,gBAAgB;AAClB,EAAC;AAED,OAAO,MAAMC,4BAAqD;IAChEX,MAAM;IACNC,UAAUJ;IACVK,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MACE;IACFC,UAAU;IACVC,qBAAqB,IAAIX;IACzBY,gBAAgB;AAClB,EAAC;AAED,OAAO,MAAME,qCAA8D;IACzEZ,MAAM;IACNC,UAAUJ;IACVK,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MACE;IACFC,UAAU;IACVC,qBAAqB,KAAKX;IAC1BY,gBAAgB;AAClB,EAAC;AAED,OAAO,MAAMG,+BAAwD;IACnEb,MAAM;IACNC,UAAUJ;IACVK,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MAAM;IACNC,UAAU;IACVC,qBAAqB,IAAIX;IACzBY,gBAAgB;AAClB,EAAC;AAED,OAAO,MAAMI,2BAA+D;IAC1Ef;IACAY;IACAC;IACAC;CACD,CAAA"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/constants/operator-alerts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { OperatorAlertDefinition } from '@aglyn/aglyn/app-utils/operator-alerts'\nimport { BUNDLE_ID } from './bundle-common'\n\n/**\n * The storefront's operator alerts (AGL-3377): the money faults only this\n * plugin can see, declared in the registry's shape and registered from\n * `declarations.server`, so core lists none of them and an install without\n * commerce shows none of them on Staff → Operator alerts.\n *\n * Each is raised through `raiseOperatorAlert` with its definition, which\n * also registers it in a process whose declarations have not loaded yet.\n */\nconst DAY = 24 * 60\n\nexport const COMMERCE_CHARGEBACK_UNROUTABLE: OperatorAlertDefinition = {\n type: 'commerce.chargebackUnroutable',\n pluginId: BUNDLE_ID,\n label: 'Storefront chargeback could not be routed',\n description:\n 'A storefront chargeback matched no order, or more than one, so no order was flagged, no seller share reversed and no merchant told. A missing index means every storefront chargeback is being ignored.',\n tier: 'must',\n category: 'payments',\n title: 'A {{amount}} chargeback could not be routed to an order',\n body: '{{detail}}',\n delivery: 'immediate',\n dedupeWindowMinutes: DAY,\n defaultEnabled: true,\n}\n\nexport const COMMERCE_TAX_NOT_REVERSED: OperatorAlertDefinition = {\n type: 'commerce.taxNotReversed',\n pluginId: BUNDLE_ID,\n label: 'Subscription sales tax not reversed',\n description:\n 'Sales tax collected on a storefront subscription stayed with the merchant instead of coming back to the platform, which files and remits it. The platform may owe tax it does not hold.',\n tier: 'must',\n category: 'payments',\n title: 'Sales tax not reversed on invoice {{invoiceId}}',\n // `{{site}}` is the site's name and id (AGL-3432), so staff need not look\n // it up; the last sentence is the consequence the description states.\n body:\n '{{amount}} of sales tax on invoice {{invoiceId}} for site {{site}} was not pulled back from the merchant: {{reason}}. The platform files and remits that tax, so it may owe tax it does not hold.',\n delivery: 'immediate',\n dedupeWindowMinutes: 7 * DAY,\n defaultEnabled: true,\n}\n\nexport const COMMERCE_SELLER_SHARE_NOT_REVERSED: OperatorAlertDefinition = {\n type: 'commerce.sellerShareNotReversed',\n pluginId: BUNDLE_ID,\n label: 'Seller share not reversed on a lost dispute',\n description:\n 'A storefront dispute was lost and the merchant’s share could not be pulled back, so the platform is out the principal. Recover it by hand in Stripe.',\n tier: 'must',\n category: 'payments',\n title: 'Seller share not reversed on dispute {{disputeId}}',\n body:\n 'The lost dispute {{disputeId}} on order {{orderId}} from site {{site}} cost {{amount}}, and the merchant’s share was not reversed: {{reason}}. The platform is out that share until it is recovered by hand in Stripe.',\n delivery: 'immediate',\n dedupeWindowMinutes: 30 * DAY,\n defaultEnabled: true,\n}\n\nexport const COMMERCE_FEE_REPRICE_REFUSED: OperatorAlertDefinition = {\n type: 'commerce.feeRepriceRefused',\n pluginId: BUNDLE_ID,\n label: 'Platform fee correction refused',\n description:\n 'Stripe refused to change a storefront subscription’s platform fee, or to refund the part of a fee charged on the wrong base. The merchant is being charged a fee the plan does not set.',\n tier: 'must',\n category: 'payments',\n title: 'Platform fee not corrected on {{subject}}',\n body:\n 'The platform fee on {{subject}} for site {{site}} could not be corrected: {{reason}}. Until it is, the merchant is charged a fee its plan does not set.',\n delivery: 'immediate',\n dedupeWindowMinutes: 7 * DAY,\n defaultEnabled: true,\n}\n\nexport const COMMERCE_OPERATOR_ALERTS: readonly OperatorAlertDefinition[] = [\n COMMERCE_CHARGEBACK_UNROUTABLE,\n COMMERCE_TAX_NOT_REVERSED,\n COMMERCE_SELLER_SHARE_NOT_REVERSED,\n COMMERCE_FEE_REPRICE_REFUSED,\n]\n"],"names":["BUNDLE_ID","DAY","COMMERCE_CHARGEBACK_UNROUTABLE","type","pluginId","label","description","tier","category","title","body","delivery","dedupeWindowMinutes","defaultEnabled","COMMERCE_TAX_NOT_REVERSED","COMMERCE_SELLER_SHARE_NOT_REVERSED","COMMERCE_FEE_REPRICE_REFUSED","COMMERCE_OPERATOR_ALERTS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,SAAS,QAAQ,qBAAiB;AAE3C;;;;;;;;CAQC,GACD,MAAMC,MAAM,KAAK;AAEjB,OAAO,MAAMC,iCAA0D;IACrEC,MAAM;IACNC,UAAUJ;IACVK,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MAAM;IACNC,UAAU;IACVC,qBAAqBX;IACrBY,gBAAgB;AAClB,EAAC;AAED,OAAO,MAAMC,4BAAqD;IAChEX,MAAM;IACNC,UAAUJ;IACVK,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACP,0EAA0E;IAC1E,sEAAsE;IACtEC,MACE;IACFC,UAAU;IACVC,qBAAqB,IAAIX;IACzBY,gBAAgB;AAClB,EAAC;AAED,OAAO,MAAME,qCAA8D;IACzEZ,MAAM;IACNC,UAAUJ;IACVK,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MACE;IACFC,UAAU;IACVC,qBAAqB,KAAKX;IAC1BY,gBAAgB;AAClB,EAAC;AAED,OAAO,MAAMG,+BAAwD;IACnEb,MAAM;IACNC,UAAUJ;IACVK,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MACE;IACFC,UAAU;IACVC,qBAAqB,IAAIX;IACzBY,gBAAgB;AAClB,EAAC;AAED,OAAO,MAAMI,2BAA+D;IAC1Ef;IACAY;IACAC;IACAC;CACD,CAAA"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * What a SHOPPER is told when the storefront's checkout answers that this
19
+ * deployment takes no payments (AGL-2019).
20
+ *
21
+ * The status is the platform's (`isPaymentsNotConfigured`, core's
22
+ * `payments-configured.ts`); the sentence is the storefront's, because only
23
+ * the storefront knows its visitor was trying to buy something. It is a fact
24
+ * about the store: no cause, no variable names, no mention of the platform —
25
+ * naming an environment variable to a stranger leaks the operator's
26
+ * deployment shape to the public internet, and there is nothing they could do
27
+ * with it anyway.
28
+ *
29
+ * Present tense and permanent-sounding on purpose: "right now" or
30
+ * "temporarily" would imply a transient outage and invite a retry that cannot
31
+ * succeed.
32
+ */
33
+ export declare function storefrontPaymentsNotConfiguredText(): string;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * What a SHOPPER is told when the storefront's checkout answers that this
18
+ * deployment takes no payments (AGL-2019).
19
+ *
20
+ * The status is the platform's (`isPaymentsNotConfigured`, core's
21
+ * `payments-configured.ts`); the sentence is the storefront's, because only
22
+ * the storefront knows its visitor was trying to buy something. It is a fact
23
+ * about the store: no cause, no variable names, no mention of the platform —
24
+ * naming an environment variable to a stranger leaks the operator's
25
+ * deployment shape to the public internet, and there is nothing they could do
26
+ * with it anyway.
27
+ *
28
+ * Present tense and permanent-sounding on purpose: "right now" or
29
+ * "temporarily" would imply a transient outage and invite a retry that cannot
30
+ * succeed.
31
+ */ export function storefrontPaymentsNotConfiguredText() {
32
+ return 'This store is not set up to take payments.';
33
+ }
34
+
35
+ //# sourceMappingURL=storefront-payments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/constants/storefront-payments.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * What a SHOPPER is told when the storefront's checkout answers that this\n * deployment takes no payments (AGL-2019).\n *\n * The status is the platform's (`isPaymentsNotConfigured`, core's\n * `payments-configured.ts`); the sentence is the storefront's, because only\n * the storefront knows its visitor was trying to buy something. It is a fact\n * about the store: no cause, no variable names, no mention of the platform —\n * naming an environment variable to a stranger leaks the operator's\n * deployment shape to the public internet, and there is nothing they could do\n * with it anyway.\n *\n * Present tense and permanent-sounding on purpose: \"right now\" or\n * \"temporarily\" would imply a transient outage and invite a retry that cannot\n * succeed.\n */\nexport function storefrontPaymentsNotConfiguredText() {\n return 'This store is not set up to take payments.'\n}\n"],"names":["storefrontPaymentsNotConfiguredText"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASA;IACd,OAAO;AACT"}
@@ -19,6 +19,8 @@
19
19
  * reads at boot, before any surface loads.
20
20
  *
21
21
  * Its operator alerts (AGL-3377), so Staff → Operator alerts lists them and
22
- * staff can switch them before the first one is ever raised.
22
+ * staff can switch them before the first one is ever raised; and the indexes
23
+ * of its products and categories, so another plugin reads them without
24
+ * reaching for this plugin's collections.
23
25
  */
24
26
  export declare function registerCommerceServerDeclarations(): void;
@@ -16,6 +16,7 @@
16
16
  */ // The seam from its own module, not the plugin-manager barrel: boot needs the
17
17
  // registry and nothing else, and the barrel reaches the client contexts.
18
18
  import { registerOperatorAlerts } from "@aglyn/aglyn/plugin-manager/operator-alerts";
19
+ import { registerPluginRecordIndex } from "@aglyn/aglyn/plugin-manager/plugin-record-index";
19
20
  import { BUNDLE_ID } from "./constants/bundle-common.js";
20
21
  import { COMMERCE_OPERATOR_ALERTS } from "./constants/operator-alerts.js";
21
22
  /**
@@ -23,11 +24,28 @@ import { COMMERCE_OPERATOR_ALERTS } from "./constants/operator-alerts.js";
23
24
  * reads at boot, before any surface loads.
24
25
  *
25
26
  * Its operator alerts (AGL-3377), so Staff → Operator alerts lists them and
26
- * staff can switch them before the first one is ever raised.
27
+ * staff can switch them before the first one is ever raised; and the indexes
28
+ * of its products and categories, so another plugin reads them without
29
+ * reaching for this plugin's collections.
27
30
  */ export function registerCommerceServerDeclarations() {
28
31
  registerOperatorAlerts(COMMERCE_OPERATOR_ALERTS, {
29
32
  pluginId: BUNDLE_ID
30
33
  });
34
+ // Products and categories, as another plugin reads them (AGL-3080). The
35
+ // readers and the Admin SDK arrive with the first read, not with the boot.
36
+ registerPluginRecordIndex('product', lazyIndex('productRecordIndex'), {
37
+ pluginId: BUNDLE_ID
38
+ });
39
+ registerPluginRecordIndex('productCategory', lazyIndex('productCategoryRecordIndex'), {
40
+ pluginId: BUNDLE_ID
41
+ });
42
+ }
43
+ function lazyIndex(name) {
44
+ const load = async ()=>(await import("./server/product-record-index.js"))[name];
45
+ return {
46
+ list: async (request)=>(await load()).list(request),
47
+ get: async (request)=>(await load()).get(request)
48
+ };
31
49
  }
32
50
 
33
51
  //# sourceMappingURL=declarations.server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/commerce/src/lib/declarations.server.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// The seam from its own module, not the plugin-manager barrel: boot needs the\n// registry and nothing else, and the barrel reaches the client contexts.\nimport { registerOperatorAlerts } from '@aglyn/aglyn/plugin-manager/operator-alerts'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { COMMERCE_OPERATOR_ALERTS } from './constants/operator-alerts'\n\n/**\n * The commerce plugin's server declarations: the light registrations core\n * reads at boot, before any surface loads.\n *\n * Its operator alerts (AGL-3377), so Staff → Operator alerts lists them and\n * staff can switch them before the first one is ever raised.\n */\nexport function registerCommerceServerDeclarations(): void {\n registerOperatorAlerts(COMMERCE_OPERATOR_ALERTS, { pluginId: BUNDLE_ID })\n}\n"],"names":["registerOperatorAlerts","BUNDLE_ID","COMMERCE_OPERATOR_ALERTS","registerCommerceServerDeclarations","pluginId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,8EAA8E;AAC9E,yEAAyE;AACzE,SAASA,sBAAsB,QAAQ,8CAA6C;AACpF,SAASC,SAAS,QAAQ,+BAA2B;AACrD,SAASC,wBAAwB,QAAQ,iCAA6B;AAEtE;;;;;;CAMC,GACD,OAAO,SAASC;IACdH,uBAAuBE,0BAA0B;QAAEE,UAAUH;IAAU;AACzE"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/commerce/src/lib/declarations.server.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// The seam from its own module, not the plugin-manager barrel: boot needs the\n// registry and nothing else, and the barrel reaches the client contexts.\nimport { registerOperatorAlerts } from '@aglyn/aglyn/plugin-manager/operator-alerts'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { COMMERCE_OPERATOR_ALERTS } from './constants/operator-alerts'\n\n/**\n * The commerce plugin's server declarations: the light registrations core\n * reads at boot, before any surface loads.\n *\n * Its operator alerts (AGL-3377), so Staff → Operator alerts lists them and\n * staff can switch them before the first one is ever raised; and the indexes\n * of its products and categories, so another plugin reads them without\n * reaching for this plugin's collections.\n */\nexport function registerCommerceServerDeclarations(): void {\n registerOperatorAlerts(COMMERCE_OPERATOR_ALERTS, { pluginId: BUNDLE_ID })\n // Products and categories, as another plugin reads them (AGL-3080). The\n // readers and the Admin SDK arrive with the first read, not with the boot.\n registerPluginRecordIndex('product', lazyIndex('productRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('productCategory', lazyIndex('productCategoryRecordIndex'), {\n pluginId: BUNDLE_ID,\n })\n}\n\ntype IndexName = 'productRecordIndex' | 'productCategoryRecordIndex'\n\nfunction lazyIndex(name: IndexName): PluginRecordIndex {\n const load = async () => (await import('./server/product-record-index'))[name]\n return {\n list: async (request) => (await load()).list(request),\n get: async (request) => (await load()).get(request),\n }\n}\n"],"names":["registerOperatorAlerts","registerPluginRecordIndex","BUNDLE_ID","COMMERCE_OPERATOR_ALERTS","registerCommerceServerDeclarations","pluginId","lazyIndex","name","load","list","request","get"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,8EAA8E;AAC9E,yEAAyE;AACzE,SAASA,sBAAsB,QAAQ,8CAA6C;AACpF,SACEC,yBAAyB,QAEpB,kDAAiD;AACxD,SAASC,SAAS,QAAQ,+BAA2B;AACrD,SAASC,wBAAwB,QAAQ,iCAA6B;AAEtE;;;;;;;;CAQC,GACD,OAAO,SAASC;IACdJ,uBAAuBG,0BAA0B;QAAEE,UAAUH;IAAU;IACvE,wEAAwE;IACxE,2EAA2E;IAC3ED,0BAA0B,WAAWK,UAAU,uBAAuB;QAAED,UAAUH;IAAU;IAC5FD,0BAA0B,mBAAmBK,UAAU,+BAA+B;QACpFD,UAAUH;IACZ;AACF;AAIA,SAASI,UAAUC,IAAe;IAChC,MAAMC,OAAO,UAAY,AAAC,CAAA,MAAM,MAAM,CAAC,mCAA+B,CAAE,CAACD,KAAK;IAC9E,OAAO;QACLE,MAAM,OAAOC,UAAY,AAAC,CAAA,MAAMF,MAAK,EAAGC,IAAI,CAACC;QAC7CC,KAAK,OAAOD,UAAY,AAAC,CAAA,MAAMF,MAAK,EAAGG,GAAG,CAACD;IAC7C;AACF"}
@@ -22,7 +22,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
22
22
  * with only `Image Src` add media. Export mirrors the same columns so
23
23
  * round-trips are lossless for the fields both sides share.
24
24
  */ import { commerceSlug, validateProduct } from "./commerce.js";
25
- import { parseCsv } from "@aglyn/aglyn/app-utils/dataset-csv";
25
+ import { parseCsv } from "@aglyn/aglyn/app-utils/csv";
26
26
  export const PRODUCT_CSV_HEADER = [
27
27
  'Handle',
28
28
  'Title',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-io.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Catalog CSV import/export (AGL-282) speaking Shopify's product CSV\n * dialect — the migration wedge for switchers: `Handle` groups variant\n * rows into one product, `Option1..3 Name/Value` build the option axes,\n * `Variant *` columns carry per-variant data, and repeated-handle rows\n * with only `Image Src` add media. Export mirrors the same columns so\n * round-trips are lossless for the fields both sides share.\n */\n\nimport {\n commerceSlug,\n validateProduct,\n type HostProduct,\n type ProductOption,\n type ProductStatus,\n type ProductVariant,\n} from './commerce'\nimport { parseCsv } from '@aglyn/aglyn/app-utils/dataset-csv'\n\nexport const PRODUCT_CSV_HEADER = [\n 'Handle',\n 'Title',\n 'Body (HTML)',\n 'Type',\n 'Tags',\n 'Published',\n 'Option1 Name',\n 'Option1 Value',\n 'Option2 Name',\n 'Option2 Value',\n 'Option3 Name',\n 'Option3 Value',\n 'Variant SKU',\n 'Variant Barcode',\n 'Variant Price',\n 'Variant Compare At Price',\n 'Variant Inventory Qty',\n 'Variant Grams',\n 'Image Src',\n] as const\n\nfunction csvCell(value: unknown): string {\n const text = value == null ? '' : String(value)\n return /[\",\\n]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text\n}\n\n/** One row per variant; extra image rows follow Shopify's convention. */\nexport function productsToCsv(\n products: Array<HostProduct & { $id?: string }>,\n): string {\n const lines = [PRODUCT_CSV_HEADER.join(',')]\n for (const product of products) {\n const options = product.options ?? []\n product.variants.forEach((variant, index) => {\n const optionCells: string[] = []\n for (let axis = 0; axis < 3; axis += 1) {\n const option = options[axis]\n optionCells.push(\n index === 0 ? (option?.name ?? '') : '',\n option ? (variant.options?.[option.name] ?? '') : '',\n )\n }\n lines.push(\n [\n csvCell(product.slug),\n csvCell(index === 0 ? product.name : ''),\n csvCell(index === 0 ? (product.description ?? '') : ''),\n csvCell(index === 0 ? product.type : ''),\n csvCell(index === 0 ? (product.tags ?? []).join(', ') : ''),\n csvCell(index === 0 ? String(product.status === 'active') : ''),\n ...optionCells.map(csvCell),\n csvCell(variant.sku ?? ''),\n csvCell(variant.barcode ?? ''),\n csvCell(variant.priceUsd),\n csvCell(variant.compareAtPriceUsd ?? ''),\n csvCell(variant.inventory ?? ''),\n csvCell(variant.weightGrams ?? ''),\n csvCell(index === 0 ? (product.mediaUrls?.[0] ?? '') : ''),\n ].join(','),\n )\n })\n for (const url of (product.mediaUrls ?? []).slice(1)) {\n lines.push(\n [\n csvCell(product.slug),\n ...Array(PRODUCT_CSV_HEADER.length - 2).fill(''),\n csvCell(url),\n ].join(','),\n )\n }\n }\n return lines.join('\\n')\n}\n\nexport interface ProductCsvImport {\n products: HostProduct[]\n /** Row-anchored problems; importable products still parse. */\n errors: string[]\n}\n\n/** Case-insensitive column lookup so hand-edited headers still map. */\nfunction headerIndex(header: string[]): Map<string, number> {\n const map = new Map<string, number>()\n header.forEach((cell, index) =>\n map.set(cell.trim().toLowerCase(), index),\n )\n return map\n}\n\nexport function parseProductsCsv(text: string): ProductCsvImport {\n const rows = parseCsv(text)\n if (rows.length < 2) {\n return { products: [], errors: ['No data rows found'] }\n }\n const columns = headerIndex(rows[0])\n const cell = (row: string[], name: string): string => {\n const index = columns.get(name.toLowerCase())\n return index == null ? '' : (row[index] ?? '').trim()\n }\n if (!columns.has('handle') || !columns.has('title')) {\n return {\n products: [],\n errors: ['Missing required columns: Handle, Title'],\n }\n }\n\n const byHandle = new Map<string, HostProduct>()\n const errors: string[] = []\n rows.slice(1).forEach((row, rowIndex) => {\n const line = rowIndex + 2\n const handle = commerceSlug(cell(row, 'handle'))\n if (!handle) {\n if (row.some((value) => value.trim())) {\n errors.push(`Row ${line}: missing Handle`)\n }\n return\n }\n let product = byHandle.get(handle)\n const title = cell(row, 'title')\n if (!product) {\n if (!title) {\n // Image-only continuation row for a product we never saw.\n errors.push(`Row ${line}: first row for \"${handle}\" needs a Title`)\n return\n }\n const published = cell(row, 'published').toLowerCase()\n const type = cell(row, 'type').toLowerCase()\n product = {\n name: title,\n slug: handle,\n description: cell(row, 'body (html)') || undefined,\n type: ['physical', 'digital', 'service'].includes(type)\n ? (type as HostProduct['type'])\n : 'physical',\n status: (published === 'false'\n ? 'draft'\n : 'active') as ProductStatus,\n tags: cell(row, 'tags')\n ? cell(row, 'tags')\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean)\n : undefined,\n options: [],\n variants: [],\n mediaUrls: [],\n }\n byHandle.set(handle, product)\n }\n\n const imageSrc = cell(row, 'image src')\n if (imageSrc && !(product.mediaUrls ?? []).includes(imageSrc)) {\n product.mediaUrls = [...(product.mediaUrls ?? []), imageSrc]\n }\n\n const priceRaw = cell(row, 'variant price')\n const hasVariant = priceRaw !== '' || cell(row, 'variant sku') !== ''\n if (!hasVariant) return\n\n const options: Record<string, string> = {}\n for (let axis = 1; axis <= 3; axis += 1) {\n const name = cell(row, `option${axis} name`)\n const value = cell(row, `option${axis} value`)\n if (name && product.options![axis - 1] == null) {\n product.options![axis - 1] = { name, values: [] }\n }\n const optionName = product.options![axis - 1]?.name\n if (optionName && value) {\n options[optionName] = value\n const axisOption = product.options![axis - 1] as ProductOption\n if (!axisOption.values.includes(value)) {\n axisOption.values.push(value)\n }\n }\n }\n\n const price = Number(priceRaw)\n if (!Number.isFinite(price) || price < 0) {\n errors.push(`Row ${line}: invalid Variant Price \"${priceRaw}\"`)\n return\n }\n const qtyRaw = cell(row, 'variant inventory qty')\n const compareRaw = cell(row, 'variant compare at price')\n const gramsRaw = cell(row, 'variant grams')\n const variant: ProductVariant = {\n id: `v${product.variants.length + 1}`,\n ...(Object.keys(options).length ? { options } : {}),\n priceUsd: price,\n ...(cell(row, 'variant sku') ? { sku: cell(row, 'variant sku') } : {}),\n ...(cell(row, 'variant barcode')\n ? { barcode: cell(row, 'variant barcode') }\n : {}),\n ...(compareRaw && Number.isFinite(Number(compareRaw))\n ? { compareAtPriceUsd: Number(compareRaw) }\n : {}),\n inventory: qtyRaw === '' ? null : Math.max(0, Math.round(Number(qtyRaw))),\n ...(gramsRaw && Number.isFinite(Number(gramsRaw))\n ? { weightGrams: Number(gramsRaw) }\n : {}),\n }\n product.variants.push(variant)\n })\n\n const products: HostProduct[] = []\n for (const [handle, product] of byHandle) {\n product.options = (product.options ?? []).filter(Boolean)\n if (product.options.length === 0) delete product.options\n if ((product.mediaUrls?.length ?? 0) === 0) delete product.mediaUrls\n if (product.variants.length === 0) {\n errors.push(`\"${handle}\": no variant rows (needs Variant Price)`)\n continue\n }\n const problem = validateProduct(product)\n if (problem) {\n errors.push(`\"${handle}\": ${problem}`)\n continue\n }\n products.push(product)\n }\n return { products, errors }\n}\n"],"names":["commerceSlug","validateProduct","parseCsv","PRODUCT_CSV_HEADER","csvCell","value","text","String","test","replace","productsToCsv","products","lines","join","product","options","variants","forEach","variant","index","optionCells","axis","option","push","name","slug","description","type","tags","status","map","sku","barcode","priceUsd","compareAtPriceUsd","inventory","weightGrams","mediaUrls","url","slice","Array","length","fill","headerIndex","header","Map","cell","set","trim","toLowerCase","parseProductsCsv","rows","errors","columns","row","get","has","byHandle","rowIndex","line","handle","some","title","published","undefined","includes","split","tag","filter","Boolean","imageSrc","priceRaw","hasVariant","values","optionName","axisOption","price","Number","isFinite","qtyRaw","compareRaw","gramsRaw","id","Object","keys","Math","max","round","problem"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-io.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Catalog CSV import/export (AGL-282) speaking Shopify's product CSV\n * dialect — the migration wedge for switchers: `Handle` groups variant\n * rows into one product, `Option1..3 Name/Value` build the option axes,\n * `Variant *` columns carry per-variant data, and repeated-handle rows\n * with only `Image Src` add media. Export mirrors the same columns so\n * round-trips are lossless for the fields both sides share.\n */\n\nimport {\n commerceSlug,\n validateProduct,\n type HostProduct,\n type ProductOption,\n type ProductStatus,\n type ProductVariant,\n} from './commerce'\nimport { parseCsv } from '@aglyn/aglyn/app-utils/csv'\n\nexport const PRODUCT_CSV_HEADER = [\n 'Handle',\n 'Title',\n 'Body (HTML)',\n 'Type',\n 'Tags',\n 'Published',\n 'Option1 Name',\n 'Option1 Value',\n 'Option2 Name',\n 'Option2 Value',\n 'Option3 Name',\n 'Option3 Value',\n 'Variant SKU',\n 'Variant Barcode',\n 'Variant Price',\n 'Variant Compare At Price',\n 'Variant Inventory Qty',\n 'Variant Grams',\n 'Image Src',\n] as const\n\nfunction csvCell(value: unknown): string {\n const text = value == null ? '' : String(value)\n return /[\",\\n]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text\n}\n\n/** One row per variant; extra image rows follow Shopify's convention. */\nexport function productsToCsv(\n products: Array<HostProduct & { $id?: string }>,\n): string {\n const lines = [PRODUCT_CSV_HEADER.join(',')]\n for (const product of products) {\n const options = product.options ?? []\n product.variants.forEach((variant, index) => {\n const optionCells: string[] = []\n for (let axis = 0; axis < 3; axis += 1) {\n const option = options[axis]\n optionCells.push(\n index === 0 ? (option?.name ?? '') : '',\n option ? (variant.options?.[option.name] ?? '') : '',\n )\n }\n lines.push(\n [\n csvCell(product.slug),\n csvCell(index === 0 ? product.name : ''),\n csvCell(index === 0 ? (product.description ?? '') : ''),\n csvCell(index === 0 ? product.type : ''),\n csvCell(index === 0 ? (product.tags ?? []).join(', ') : ''),\n csvCell(index === 0 ? String(product.status === 'active') : ''),\n ...optionCells.map(csvCell),\n csvCell(variant.sku ?? ''),\n csvCell(variant.barcode ?? ''),\n csvCell(variant.priceUsd),\n csvCell(variant.compareAtPriceUsd ?? ''),\n csvCell(variant.inventory ?? ''),\n csvCell(variant.weightGrams ?? ''),\n csvCell(index === 0 ? (product.mediaUrls?.[0] ?? '') : ''),\n ].join(','),\n )\n })\n for (const url of (product.mediaUrls ?? []).slice(1)) {\n lines.push(\n [\n csvCell(product.slug),\n ...Array(PRODUCT_CSV_HEADER.length - 2).fill(''),\n csvCell(url),\n ].join(','),\n )\n }\n }\n return lines.join('\\n')\n}\n\nexport interface ProductCsvImport {\n products: HostProduct[]\n /** Row-anchored problems; importable products still parse. */\n errors: string[]\n}\n\n/** Case-insensitive column lookup so hand-edited headers still map. */\nfunction headerIndex(header: string[]): Map<string, number> {\n const map = new Map<string, number>()\n header.forEach((cell, index) =>\n map.set(cell.trim().toLowerCase(), index),\n )\n return map\n}\n\nexport function parseProductsCsv(text: string): ProductCsvImport {\n const rows = parseCsv(text)\n if (rows.length < 2) {\n return { products: [], errors: ['No data rows found'] }\n }\n const columns = headerIndex(rows[0])\n const cell = (row: string[], name: string): string => {\n const index = columns.get(name.toLowerCase())\n return index == null ? '' : (row[index] ?? '').trim()\n }\n if (!columns.has('handle') || !columns.has('title')) {\n return {\n products: [],\n errors: ['Missing required columns: Handle, Title'],\n }\n }\n\n const byHandle = new Map<string, HostProduct>()\n const errors: string[] = []\n rows.slice(1).forEach((row, rowIndex) => {\n const line = rowIndex + 2\n const handle = commerceSlug(cell(row, 'handle'))\n if (!handle) {\n if (row.some((value) => value.trim())) {\n errors.push(`Row ${line}: missing Handle`)\n }\n return\n }\n let product = byHandle.get(handle)\n const title = cell(row, 'title')\n if (!product) {\n if (!title) {\n // Image-only continuation row for a product we never saw.\n errors.push(`Row ${line}: first row for \"${handle}\" needs a Title`)\n return\n }\n const published = cell(row, 'published').toLowerCase()\n const type = cell(row, 'type').toLowerCase()\n product = {\n name: title,\n slug: handle,\n description: cell(row, 'body (html)') || undefined,\n type: ['physical', 'digital', 'service'].includes(type)\n ? (type as HostProduct['type'])\n : 'physical',\n status: (published === 'false'\n ? 'draft'\n : 'active') as ProductStatus,\n tags: cell(row, 'tags')\n ? cell(row, 'tags')\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean)\n : undefined,\n options: [],\n variants: [],\n mediaUrls: [],\n }\n byHandle.set(handle, product)\n }\n\n const imageSrc = cell(row, 'image src')\n if (imageSrc && !(product.mediaUrls ?? []).includes(imageSrc)) {\n product.mediaUrls = [...(product.mediaUrls ?? []), imageSrc]\n }\n\n const priceRaw = cell(row, 'variant price')\n const hasVariant = priceRaw !== '' || cell(row, 'variant sku') !== ''\n if (!hasVariant) return\n\n const options: Record<string, string> = {}\n for (let axis = 1; axis <= 3; axis += 1) {\n const name = cell(row, `option${axis} name`)\n const value = cell(row, `option${axis} value`)\n if (name && product.options![axis - 1] == null) {\n product.options![axis - 1] = { name, values: [] }\n }\n const optionName = product.options![axis - 1]?.name\n if (optionName && value) {\n options[optionName] = value\n const axisOption = product.options![axis - 1] as ProductOption\n if (!axisOption.values.includes(value)) {\n axisOption.values.push(value)\n }\n }\n }\n\n const price = Number(priceRaw)\n if (!Number.isFinite(price) || price < 0) {\n errors.push(`Row ${line}: invalid Variant Price \"${priceRaw}\"`)\n return\n }\n const qtyRaw = cell(row, 'variant inventory qty')\n const compareRaw = cell(row, 'variant compare at price')\n const gramsRaw = cell(row, 'variant grams')\n const variant: ProductVariant = {\n id: `v${product.variants.length + 1}`,\n ...(Object.keys(options).length ? { options } : {}),\n priceUsd: price,\n ...(cell(row, 'variant sku') ? { sku: cell(row, 'variant sku') } : {}),\n ...(cell(row, 'variant barcode')\n ? { barcode: cell(row, 'variant barcode') }\n : {}),\n ...(compareRaw && Number.isFinite(Number(compareRaw))\n ? { compareAtPriceUsd: Number(compareRaw) }\n : {}),\n inventory: qtyRaw === '' ? null : Math.max(0, Math.round(Number(qtyRaw))),\n ...(gramsRaw && Number.isFinite(Number(gramsRaw))\n ? { weightGrams: Number(gramsRaw) }\n : {}),\n }\n product.variants.push(variant)\n })\n\n const products: HostProduct[] = []\n for (const [handle, product] of byHandle) {\n product.options = (product.options ?? []).filter(Boolean)\n if (product.options.length === 0) delete product.options\n if ((product.mediaUrls?.length ?? 0) === 0) delete product.mediaUrls\n if (product.variants.length === 0) {\n errors.push(`\"${handle}\": no variant rows (needs Variant Price)`)\n continue\n }\n const problem = validateProduct(product)\n if (problem) {\n errors.push(`\"${handle}\": ${problem}`)\n continue\n }\n products.push(product)\n }\n return { products, errors }\n}\n"],"names":["commerceSlug","validateProduct","parseCsv","PRODUCT_CSV_HEADER","csvCell","value","text","String","test","replace","productsToCsv","products","lines","join","product","options","variants","forEach","variant","index","optionCells","axis","option","push","name","slug","description","type","tags","status","map","sku","barcode","priceUsd","compareAtPriceUsd","inventory","weightGrams","mediaUrls","url","slice","Array","length","fill","headerIndex","header","Map","cell","set","trim","toLowerCase","parseProductsCsv","rows","errors","columns","row","get","has","byHandle","rowIndex","line","handle","some","title","published","undefined","includes","split","tag","filter","Boolean","imageSrc","priceRaw","hasVariant","values","optionName","axisOption","price","Number","isFinite","qtyRaw","compareRaw","gramsRaw","id","Object","keys","Math","max","round","problem"],"mappings":"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"}