@aglyn/plugins-commerce 1.0.0-beta.234 → 1.0.0-beta.236
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.
- package/package.json +13 -13
- package/src/lib/components/cart.js +3 -2
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/console/commerce-analytics-card.component.js +3 -6
- package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -1
- package/src/lib/components/console/commerce-glance-card.component.js +9 -3
- package/src/lib/components/console/commerce-glance-card.component.js.map +1 -1
- package/src/lib/components/console/customer-notifications-card.component.js +3 -1
- package/src/lib/components/console/customer-notifications-card.component.js.map +1 -1
- package/src/lib/components/console/discounts-card.component.js +2 -3
- package/src/lib/components/console/discounts-card.component.js.map +1 -1
- package/src/lib/components/console/host-coupons-card.component.js +3 -4
- package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
- package/src/lib/components/console/local-delivery-settings-card.component.js +2 -2
- package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -1
- package/src/lib/components/console/locations-card.component.js +1 -1
- package/src/lib/components/console/locations-card.component.js.map +1 -1
- package/src/lib/components/console/member-posts-card.component.js +2 -2
- package/src/lib/components/console/member-posts-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +35 -30
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-fulfillment-panel.component.d.ts +11 -1
- package/src/lib/components/console/order-fulfillment-panel.component.js +75 -4
- package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -1
- package/src/lib/components/console/order-zones.d.ts +45 -1
- package/src/lib/components/console/order-zones.js +8 -0
- package/src/lib/components/console/order-zones.js.map +1 -1
- package/src/lib/components/console/pickup-delivery-queue-card.component.js +15 -1
- package/src/lib/components/console/pickup-delivery-queue-card.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.js +3 -2
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.d.ts +2 -0
- package/src/lib/components/console/product-editor-dialog.component.js +30 -12
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +13 -4
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/products-hub-zone.component.d.ts +2 -1
- package/src/lib/components/console/products-hub-zone.component.js +1 -1
- package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
- package/src/lib/components/console/reviews-moderation-card.component.js +1 -2
- package/src/lib/components/console/reviews-moderation-card.component.js.map +1 -1
- package/src/lib/components/console/shipping-settings-card.component.js +2 -2
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
- package/src/lib/components/console/shipstation-card.component.js +1 -2
- package/src/lib/components/console/shipstation-card.component.js.map +1 -1
- package/src/lib/components/console/store-settings-card.component.js +3 -1
- package/src/lib/components/console/store-settings-card.component.js.map +1 -1
- package/src/lib/components/console/tax-settings-card.component.js +4 -1
- package/src/lib/components/console/tax-settings-card.component.js.map +1 -1
- package/src/lib/components/order-status.js +24 -1
- package/src/lib/components/order-status.js.map +1 -1
- package/src/lib/components/product-detail.js +30 -16
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-grid.d.ts +7 -0
- package/src/lib/components/product-grid.js +50 -7
- package/src/lib/components/product-grid.js.map +1 -1
- package/src/lib/components/related-products.js +1 -1
- package/src/lib/components/related-products.js.map +1 -1
- package/src/lib/components/wishlist.js +1 -1
- package/src/lib/components/wishlist.js.map +1 -1
- package/src/lib/declarations.server.js +11 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/commerce-orders.d.ts +8 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce.d.ts +21 -0
- package/src/lib/model/commerce.js +39 -1
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +1 -0
- package/src/lib/model/index.js +1 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/order-annotations.d.ts +54 -0
- package/src/lib/model/order-annotations.js +76 -0
- package/src/lib/model/order-annotations.js.map +1 -0
- package/src/lib/model/order-events.d.ts +2 -0
- package/src/lib/model/order-events.js.map +1 -1
- package/src/lib/model/order-fulfillment.d.ts +29 -0
- package/src/lib/model/order-fulfillment.js +63 -0
- package/src/lib/model/order-fulfillment.js.map +1 -1
- package/src/lib/model/order-local-fulfillment.d.ts +36 -0
- package/src/lib/model/order-local-fulfillment.js +19 -1
- package/src/lib/model/order-local-fulfillment.js.map +1 -1
- package/src/lib/model/order-status-view.d.ts +11 -0
- package/src/lib/model/order-status-view.js +20 -3
- package/src/lib/model/order-status-view.js.map +1 -1
- package/src/lib/model/product-write.js +8 -4
- package/src/lib/model/product-write.js.map +1 -1
- package/src/lib/server/api-v1/openapi.js +1 -0
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/api-v1/order-view.d.ts +1 -0
- package/src/lib/server/api-v1/order-view.js +10 -0
- package/src/lib/server/api-v1/order-view.js.map +1 -1
- package/src/lib/server/cart-checkout.js +9 -1
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/catalog.d.ts +6 -0
- package/src/lib/server/catalog.js +3 -1
- package/src/lib/server/catalog.js.map +1 -1
- package/src/lib/server/checkout.js +11 -1
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/fulfill-order.d.ts +12 -0
- package/src/lib/server/fulfill-order.js +13 -0
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/local-delivery-records.d.ts +30 -0
- package/src/lib/server/local-delivery-records.js +284 -0
- package/src/lib/server/local-delivery-records.js.map +1 -0
- package/src/lib/server/order-annotations.d.ts +28 -0
- package/src/lib/server/order-annotations.js +143 -0
- package/src/lib/server/order-annotations.js.map +1 -0
- package/src/lib/server/order-events.js +2 -1
- package/src/lib/server/order-events.js.map +1 -1
- package/src/lib/server/order-notifications.js +39 -8
- package/src/lib/server/order-notifications.js.map +1 -1
- package/src/lib/server/pos-payment.js +9 -3
- package/src/lib/server/pos-payment.js.map +1 -1
- package/src/lib/server/product-catalog.js +6 -2
- package/src/lib/server/product-catalog.js.map +1 -1
- package/src/lib/server/product-drafts.d.ts +36 -9
- package/src/lib/server/product-drafts.js +24 -5
- package/src/lib/server/product-drafts.js.map +1 -1
- package/src/lib/server/product.d.ts +6 -1
- package/src/lib/server/product.js +1 -1
- package/src/lib/server/product.js.map +1 -1
- package/src/lib/server/products-write.d.ts +29 -0
- package/src/lib/server/products-write.js +7 -7
- package/src/lib/server/products-write.js.map +1 -1
- package/src/lib/server/shipment-records.js +2 -0
- package/src/lib/server/shipment-records.js.map +1 -1
- package/src/lib/server/site-page-resolver.d.ts +13 -0
- package/src/lib/server/site-page-resolver.js +138 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +5 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/utils/use-storefront-purchase-event.js +8 -2
- package/src/lib/utils/use-storefront-purchase-event.js.map +1 -1
|
@@ -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 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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"}
|
|
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 * How each product is drawn (AGL-3676): `outlined`, the default, a card\n * with a short photo strip; `photo`, the photo first and tall, with the\n * name and price under it and no card around it — the way a storefront\n * leads with what it sells.\n */\n cardStyle?: 'outlined' | 'photo'\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 /** Listed before it has a price (AGL-3676): no price to print yet. */\n priceComingSoon?: boolean\n}\n\n/** What a card says where a product listed before it has a price would show one (AGL-3676). */\nconst PRICE_COMING_SOON_LABEL = 'Price coming soon'\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 if (item.priceComingSoon) return PRICE_COMING_SOON_LABEL\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 cardStyle,\n ...rest\n } = props\n const photoCards = cardStyle === 'photo'\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 // Photo cards pair up on a phone, as a shop's catalog does; an\n // outlined card with its photo strip takes the row.\n xs: photoCards ? 'repeat(2, 1fr)' : '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\n key={item.id}\n variant={photoCards ? 'elevation' : 'outlined'}\n elevation={0}\n sx={photoCards ? { bgcolor: 'transparent', overflow: 'visible' } : undefined}\n >\n <CardActionArea\n href={hostId ? `/products/${item.slug}` : undefined}\n disabled={!hostId}\n sx={photoCards ? { borderRadius: 2 } : undefined}\n >\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={item.name}\n sx={\n photoCards\n ? { aspectRatio: '4 / 5', objectFit: 'cover', borderRadius: 2 }\n : { height: 160, objectFit: 'cover' }\n }\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\n sx={\n photoCards\n ? { aspectRatio: '4 / 5', bgcolor: 'action.hover', borderRadius: 2 }\n : { height: 160, bgcolor: 'action.hover' }\n }\n />\n )}\n <CardContent sx={photoCards ? { px: 0.5, pt: 1.5, pb: 1 } : undefined}>\n <Typography variant={photoCards ? 'subtitle1' : '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 name: 'cardStyle',\n label: 'Card style',\n description: 'Outlined cards, or tall photos with the name and price under them.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { label: 'Outlined', value: 'outlined' },\n { label: 'Photo', value: 'photo' },\n ],\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","PRICE_COMING_SOON_LABEL","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","priceComingSoon","ProductGrid","props","ref","site","source","collectionId","collectionSlug","collectionSlugProp","categoryId","categorySlug","tag","sort","sortProp","columns","maxItems","emptyText","showFilters","showSearch","showCategories","showSort","showTypeFilter","showPriceFilter","pageSize","cardStyle","rest","photoCards","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","elevation","bgcolor","overflow","undefined","href","borderRadius","component","image","alt","aspectRatio","objectFit","DEFERRED_IMAGE_ATTRIBUTES","pt","pb","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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"}
|
|
@@ -155,7 +155,7 @@ export const ID = 'related-products';
|
|
|
155
155
|
/*#__PURE__*/ _jsx(Typography, {
|
|
156
156
|
variant: "caption",
|
|
157
157
|
color: "text.secondary",
|
|
158
|
-
children: `$${item.priceUsd}`
|
|
158
|
+
children: item.priceComingSoon ? 'Price coming soon' : `$${item.priceUsd}`
|
|
159
159
|
})
|
|
160
160
|
]
|
|
161
161
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/related-products.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 { mdiShapePlus } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\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 Typography from '@mui/material/Typography'\nimport { forwardRef, useEffect, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'related-products'\n\nexport interface RelatedProductsProps {\n /** Anchor product id; blank follows the /products/{slug} page. */\n productId?: string\n heading?: string\n maxItems?: number\n}\n\ninterface CatalogItem {\n id: string\n name: string\n slug: string\n priceUsd: number\n imageUrl?: string\n}\n\n/**\n * Related products / upsell strip (AGL-325): the product's manual list,\n * else \"frequently bought together\" from recent orders, else tag and\n * category neighbors — resolved server-side, rendered as cards.\n */\nconst RelatedProducts = forwardRef<HTMLDivElement, RelatedProductsProps>(\n (props, ref) => {\n const { productId: productIdProp, heading, maxItems, ...rest } = 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 { hostId } = Aglyn.useSite()\n const [items, setItems] = useState<CatalogItem[] | null>(null)\n\n useEffect(() => {\n if (!hostId) return\n let active = true\n void (async () => {\n let anchor = productIdProp ?? ''\n if (!anchor) {\n const match = window.location.pathname.match(/\\/products\\/([^/?#]+)/)\n if (!match) return\n const response = await fetch(\n `/api/commerce/product?hostId=${encodeURIComponent(hostId)}` +\n `&slug=${encodeURIComponent(decodeURIComponent(match[1]))}`,\n ).catch(() => null)\n const payload = await response?.json().catch(() => ({}))\n anchor = String(payload?.product?.id ?? '')\n }\n if (!active || !anchor) return\n const relatedResponse = await fetch(\n `/api/commerce/related?hostId=${encodeURIComponent(hostId)}` +\n `&productId=${encodeURIComponent(anchor)}`,\n ).catch(() => null)\n const related = await relatedResponse?.json().catch(() => ({}))\n const ids: string[] = related?.productIds ?? []\n if (!active || ids.length === 0) return setItems([])\n const catalogResponse = await fetch(\n `/api/commerce/catalog?hostId=${encodeURIComponent(hostId)}` +\n `&ids=${encodeURIComponent(ids.join(','))}`,\n ).catch(() => null)\n const catalog = await catalogResponse?.json().catch(() => ({}))\n if (active) setItems(catalog?.items ?? [])\n })()\n return () => {\n active = false\n }\n }, [hostId, productIdProp])\n\n if (!hostId) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 3,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontSize: 13,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Related products render here'}\n </Box>\n )\n }\n if (!items || items.length === 0) return <Box ref={ref} {...rest} />\n\n return (\n <Box ref={ref} {...rest}>\n <Typography variant=\"h6\" gutterBottom>\n {heading || 'You may also like'}\n </Typography>\n <Box sx={{ display: 'flex', gap: 1.5, overflowX: 'auto', pb: 1 }}>\n {items\n .slice(0, maxItems && maxItems > 0 ? maxItems : 6)\n .map((item) => {\n // AGL-1726: resolved BEFORE the guard — see the note in\n // `product-detail.tsx`.\n const imageUrl = Aglyn.siteRelativeMediaSrc(item.imageUrl, {\n hostId,\n })\n return (\n <Card key={item.id} variant=\"outlined\" sx={{ minWidth: 160 }}>\n <CardActionArea href={`/products/${item.slug}`}>\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={item.name}\n sx={{ height: 110, objectFit: 'cover' }}\n // Deferred (AGL-2486). A related-products rail sits at\n // the BOTTOM of a product page by construction, and it\n // was fetching eagerly against the gallery hero above\n // it — the one image on that page that is the LCP.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : (\n <Box sx={{ height: 110, bgcolor: 'action.hover' }} />\n )}\n <CardContent sx={{ py: 1 }}>\n <Typography variant=\"body2\" noWrap>\n {item.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`$${item.priceUsd}`}\n </Typography>\n </CardContent>\n </CardActionArea>\n </Card>\n )\n })}\n </Box>\n </Box>\n )\n },\n)\nRelatedProducts.displayName = 'AglynRelatedProducts'\n\nexport const schema: Aglyn.ComponentSchema<RelatedProductsProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Related products',\n description:\n 'An upsell strip — your manual picks, or what people buy together.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiShapePlus.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n label: 'Product id',\n description:\n 'The product to find relatives OF. Blank reads it from the URL, so ' +\n 'a product template shows each product’s own related items; naming ' +\n 'one pins the rail to that product’s relatives everywhere.',\n component: Aglyn.FieldComponentType.PRODUCT_SELECT,\n },\n {\n name: 'heading',\n label: 'Heading',\n description: 'Defaults to \"You may also like\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'maxItems',\n label: 'Max items',\n description: 'Cap the strip (default 6).',\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: 'Related products',\n pluginId: BUNDLE_ID,\n description: 'Manual picks or frequently-bought-together',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiShapePlus.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n]\n\nexport default RelatedProducts\n"],"names":["Aglyn","mdiShapePlus","Box","Card","CardActionArea","CardContent","CardMedia","Typography","forwardRef","useEffect","useState","BUNDLE_ID","generatePresetId","ID","RelatedProducts","props","ref","productId","productIdProp","heading","maxItems","rest","nodeSx","Array","isArray","hostId","useSite","items","setItems","active","anchor","payload","match","window","location","pathname","response","fetch","encodeURIComponent","decodeURIComponent","catch","json","String","product","id","relatedResponse","related","ids","productIds","length","catalogResponse","join","catalog","sx","p","border","borderColor","borderRadius","color","fontSize","fontFamily","variant","gutterBottom","display","gap","overflowX","pb","slice","map","item","imageUrl","siteRelativeMediaSrc","minWidth","href","slug","component","image","alt","name","height","objectFit","DEFERRED_IMAGE_ATTRIBUTES","bgcolor","py","noWrap","priceUsd","displayName","schema","$id","pluginId","description","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","label","FieldComponentType","PRODUCT_SELECT","TEXT_FIELD","type","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/related-products.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 { mdiShapePlus } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\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 Typography from '@mui/material/Typography'\nimport { forwardRef, useEffect, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'related-products'\n\nexport interface RelatedProductsProps {\n /** Anchor product id; blank follows the /products/{slug} page. */\n productId?: string\n heading?: string\n maxItems?: number\n}\n\ninterface CatalogItem {\n id: string\n name: string\n slug: string\n priceUsd: number\n imageUrl?: string\n /** Listed before it has a price (AGL-3676). */\n priceComingSoon?: boolean\n}\n\n/**\n * Related products / upsell strip (AGL-325): the product's manual list,\n * else \"frequently bought together\" from recent orders, else tag and\n * category neighbors — resolved server-side, rendered as cards.\n */\nconst RelatedProducts = forwardRef<HTMLDivElement, RelatedProductsProps>(\n (props, ref) => {\n const { productId: productIdProp, heading, maxItems, ...rest } = 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 { hostId } = Aglyn.useSite()\n const [items, setItems] = useState<CatalogItem[] | null>(null)\n\n useEffect(() => {\n if (!hostId) return\n let active = true\n void (async () => {\n let anchor = productIdProp ?? ''\n if (!anchor) {\n const match = window.location.pathname.match(/\\/products\\/([^/?#]+)/)\n if (!match) return\n const response = await fetch(\n `/api/commerce/product?hostId=${encodeURIComponent(hostId)}` +\n `&slug=${encodeURIComponent(decodeURIComponent(match[1]))}`,\n ).catch(() => null)\n const payload = await response?.json().catch(() => ({}))\n anchor = String(payload?.product?.id ?? '')\n }\n if (!active || !anchor) return\n const relatedResponse = await fetch(\n `/api/commerce/related?hostId=${encodeURIComponent(hostId)}` +\n `&productId=${encodeURIComponent(anchor)}`,\n ).catch(() => null)\n const related = await relatedResponse?.json().catch(() => ({}))\n const ids: string[] = related?.productIds ?? []\n if (!active || ids.length === 0) return setItems([])\n const catalogResponse = await fetch(\n `/api/commerce/catalog?hostId=${encodeURIComponent(hostId)}` +\n `&ids=${encodeURIComponent(ids.join(','))}`,\n ).catch(() => null)\n const catalog = await catalogResponse?.json().catch(() => ({}))\n if (active) setItems(catalog?.items ?? [])\n })()\n return () => {\n active = false\n }\n }, [hostId, productIdProp])\n\n if (!hostId) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 3,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontSize: 13,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Related products render here'}\n </Box>\n )\n }\n if (!items || items.length === 0) return <Box ref={ref} {...rest} />\n\n return (\n <Box ref={ref} {...rest}>\n <Typography variant=\"h6\" gutterBottom>\n {heading || 'You may also like'}\n </Typography>\n <Box sx={{ display: 'flex', gap: 1.5, overflowX: 'auto', pb: 1 }}>\n {items\n .slice(0, maxItems && maxItems > 0 ? maxItems : 6)\n .map((item) => {\n // AGL-1726: resolved BEFORE the guard — see the note in\n // `product-detail.tsx`.\n const imageUrl = Aglyn.siteRelativeMediaSrc(item.imageUrl, {\n hostId,\n })\n return (\n <Card key={item.id} variant=\"outlined\" sx={{ minWidth: 160 }}>\n <CardActionArea href={`/products/${item.slug}`}>\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={item.name}\n sx={{ height: 110, objectFit: 'cover' }}\n // Deferred (AGL-2486). A related-products rail sits at\n // the BOTTOM of a product page by construction, and it\n // was fetching eagerly against the gallery hero above\n // it — the one image on that page that is the LCP.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : (\n <Box sx={{ height: 110, bgcolor: 'action.hover' }} />\n )}\n <CardContent sx={{ py: 1 }}>\n <Typography variant=\"body2\" noWrap>\n {item.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {item.priceComingSoon ? 'Price coming soon' : `$${item.priceUsd}`}\n </Typography>\n </CardContent>\n </CardActionArea>\n </Card>\n )\n })}\n </Box>\n </Box>\n )\n },\n)\nRelatedProducts.displayName = 'AglynRelatedProducts'\n\nexport const schema: Aglyn.ComponentSchema<RelatedProductsProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Related products',\n description:\n 'An upsell strip — your manual picks, or what people buy together.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiShapePlus.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n label: 'Product id',\n description:\n 'The product to find relatives OF. Blank reads it from the URL, so ' +\n 'a product template shows each product’s own related items; naming ' +\n 'one pins the rail to that product’s relatives everywhere.',\n component: Aglyn.FieldComponentType.PRODUCT_SELECT,\n },\n {\n name: 'heading',\n label: 'Heading',\n description: 'Defaults to \"You may also like\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'maxItems',\n label: 'Max items',\n description: 'Cap the strip (default 6).',\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: 'Related products',\n pluginId: BUNDLE_ID,\n description: 'Manual picks or frequently-bought-together',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiShapePlus.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n]\n\nexport default RelatedProducts\n"],"names":["Aglyn","mdiShapePlus","Box","Card","CardActionArea","CardContent","CardMedia","Typography","forwardRef","useEffect","useState","BUNDLE_ID","generatePresetId","ID","RelatedProducts","props","ref","productId","productIdProp","heading","maxItems","rest","nodeSx","Array","isArray","hostId","useSite","items","setItems","active","anchor","payload","match","window","location","pathname","response","fetch","encodeURIComponent","decodeURIComponent","catch","json","String","product","id","relatedResponse","related","ids","productIds","length","catalogResponse","join","catalog","sx","p","border","borderColor","borderRadius","color","fontSize","fontFamily","variant","gutterBottom","display","gap","overflowX","pb","slice","map","item","imageUrl","siteRelativeMediaSrc","minWidth","href","slug","component","image","alt","name","height","objectFit","DEFERRED_IMAGE_ATTRIBUTES","bgcolor","py","noWrap","priceComingSoon","priceUsd","displayName","schema","$id","pluginId","description","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","label","FieldComponentType","PRODUCT_SELECT","TEXT_FIELD","type","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
@@ -224,7 +224,7 @@ function writeLocalWishlist(hostId, productIds) {
|
|
|
224
224
|
/*#__PURE__*/ _jsx(Typography, {
|
|
225
225
|
variant: "caption",
|
|
226
226
|
color: "text.secondary",
|
|
227
|
-
children: item.soldOut ? 'Sold out' : `$${item.priceUsd}`
|
|
227
|
+
children: item.soldOut ? 'Sold out' : item.priceComingSoon ? 'Price coming soon' : `$${item.priceUsd}`
|
|
228
228
|
})
|
|
229
229
|
]
|
|
230
230
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/wishlist.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 { mdiHeartOutline } from '@aglyn/shared-data-mdi'\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 Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useEffect, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'wishlist'\n\nexport const WISHLIST_UPDATED_EVENT = 'aglyn:wishlist-updated'\n\nfunction storageKey(hostId: string): string {\n return `aglyn_wishlist_${hostId}`\n}\n\n/** Guest wishlist ids from localStorage. */\nexport function readLocalWishlist(hostId: string): string[] {\n try {\n const raw = window.localStorage.getItem(storageKey(hostId))\n return raw ? (JSON.parse(raw) as string[]).slice(0, 200) : []\n } catch {\n return []\n }\n}\n\nfunction writeLocalWishlist(hostId: string, productIds: string[]): void {\n try {\n window.localStorage.setItem(\n storageKey(hostId),\n JSON.stringify(productIds.slice(0, 200)),\n )\n } catch {\n // Private-mode storage failures degrade to session-only behavior.\n }\n}\n\n/**\n * Toggles a product on the wishlist (AGL-297): members persist on their\n * doc (guest list merges in on first signed-in call); guests persist in\n * localStorage. Returns whether the product is on the list afterward.\n *\n * Takes its own `fetch` (AGL-1139). This is a plain module function, not a\n * component, so it cannot read the site context that says whether it is\n * running in Preview — and both callers are components that can. Defaulting\n * to the global keeps every other caller unchanged; passing `useSiteFetch()`\n * is what stops a preview click from writing to a real member's wishlist.\n */\nexport async function toggleWishlist(\n hostId: string,\n productId: string,\n siteFetch: typeof fetch = fetch,\n): Promise<boolean> {\n const local = readLocalWishlist(hostId)\n const has = local.includes(productId)\n const next = has\n ? local.filter((id) => id !== productId)\n : [...local, productId]\n writeLocalWishlist(hostId, next)\n // Best-effort member sync — 401 just means guest.\n void siteFetch('/api/membership/wishlist', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n action: has ? 'remove' : 'add',\n productId,\n }),\n }).catch(() => undefined)\n window.dispatchEvent(new Event(WISHLIST_UPDATED_EVENT))\n return !has\n}\n\nexport interface WishlistProps {\n heading?: string\n emptyText?: string\n}\n\ninterface CatalogItem {\n id: string\n name: string\n slug: string\n priceUsd: number\n imageUrl?: string\n soldOut: boolean\n}\n\n/**\n * Wishlist block (AGL-297): renders the saved products (member doc when\n * signed in — merging any guest list — else localStorage) as cards\n * linking to their PDPs, with remove buttons.\n */\nconst Wishlist = forwardRef<HTMLDivElement, WishlistProps>((props, ref) => {\n const { heading, emptyText, ...rest } = 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 { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n const [items, setItems] = useState<CatalogItem[] | null>(null)\n\n const refresh = useCallback(async () => {\n if (!hostId) return\n let productIds = readLocalWishlist(hostId)\n try {\n // Merge the guest list into the member doc; response is canonical.\n const response = await siteFetch('/api/membership/wishlist', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, action: 'merge', productIds }),\n })\n if (response.ok) {\n const payload = await response.json()\n productIds = payload?.productIds ?? productIds\n writeLocalWishlist(hostId, productIds)\n }\n } catch {\n // Guest path.\n }\n if (productIds.length === 0) return setItems([])\n try {\n const response = await fetch(\n `/api/commerce/catalog?hostId=${encodeURIComponent(hostId)}` +\n `&ids=${encodeURIComponent(productIds.join(','))}&limit=100`,\n )\n const payload = await response.json()\n setItems(payload?.items ?? [])\n } catch {\n setItems([])\n }\n }, [hostId, siteFetch])\n\n useEffect(() => {\n void refresh()\n const handler = () => void refresh()\n window.addEventListener(WISHLIST_UPDATED_EVENT, handler)\n return () => window.removeEventListener(WISHLIST_UPDATED_EVENT, handler)\n }, [refresh])\n\n if (!hostId) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 3,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontSize: 13,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Wishlist — saved products render here'}\n </Box>\n )\n }\n\n return (\n <Box ref={ref} {...rest}>\n {heading ? (\n <Typography variant=\"h5\" gutterBottom>\n {heading}\n </Typography>\n ) : null}\n {items && items.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {emptyText || 'Nothing saved yet — tap the heart on any product.'}\n </Typography>\n ) : (\n <Box\n sx={{\n display: 'grid',\n gap: 2,\n gridTemplateColumns: {\n xs: 'repeat(2, 1fr)',\n md: 'repeat(4, 1fr)',\n },\n }}\n >\n {(items ?? []).map((item) => {\n // AGL-1726: resolved BEFORE the guard — see the note in\n // `product-detail.tsx`.\n const imageUrl = Aglyn.siteRelativeMediaSrc(item.imageUrl, {\n hostId,\n })\n return (\n <Card key={item.id} variant=\"outlined\">\n <CardActionArea href={`/products/${item.slug}`}>\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={item.name}\n sx={{ height: 120, objectFit: 'cover' }}\n // Deferred (AGL-2486), same reasoning as the product\n // grid: one image per saved item, none of them the lead.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : (\n <Box sx={{ height: 120, bgcolor: 'action.hover' }} />\n )}\n <CardContent sx={{ pb: 1 }}>\n <Typography variant=\"body2\" noWrap>\n {item.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {item.soldOut ? 'Sold out' : `$${item.priceUsd}`}\n </Typography>\n </CardContent>\n </CardActionArea>\n <Button\n size=\"small\"\n color=\"error\"\n fullWidth\n onClick={() => void toggleWishlist(hostId, item.id, siteFetch)}\n >\n {'Remove'}\n </Button>\n </Card>\n )\n })}\n </Box>\n )}\n </Box>\n )\n})\nWishlist.displayName = 'AglynWishlist'\n\nexport const schema: Aglyn.ComponentSchema<WishlistProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Wishlist',\n description: 'Products a visitor saved, linking back to their pages.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiHeartOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Shown above the saved products.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'emptyText',\n label: 'Empty text',\n description: 'Copy when nothing is saved.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Wishlist',\n pluginId: BUNDLE_ID,\n description: 'Saved products with links back to their pages',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiHeartOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { heading: 'Your wishlist' },\n },\n },\n]\n\nexport default Wishlist\n"],"names":["Aglyn","mdiHeartOutline","Box","Button","Card","CardActionArea","CardContent","CardMedia","Typography","forwardRef","useCallback","useEffect","useState","BUNDLE_ID","generatePresetId","ID","WISHLIST_UPDATED_EVENT","storageKey","hostId","readLocalWishlist","raw","window","localStorage","getItem","JSON","parse","slice","writeLocalWishlist","productIds","setItem","stringify","toggleWishlist","productId","siteFetch","fetch","local","has","includes","next","filter","id","method","headers","body","action","catch","undefined","dispatchEvent","Event","Wishlist","props","ref","heading","emptyText","rest","nodeSx","Array","isArray","useSite","useSiteFetch","items","setItems","refresh","response","ok","payload","json","length","encodeURIComponent","join","handler","addEventListener","removeEventListener","sx","p","border","borderColor","borderRadius","color","fontSize","fontFamily","variant","gutterBottom","display","gap","gridTemplateColumns","xs","md","map","item","imageUrl","siteRelativeMediaSrc","href","slug","component","image","alt","name","height","objectFit","DEFERRED_IMAGE_ATTRIBUTES","bgcolor","pb","noWrap","soldOut","priceUsd","size","fullWidth","onClick","displayName","schema","$id","pluginId","description","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","label","FieldComponentType","TEXT_FIELD","presets","type","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/wishlist.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 { mdiHeartOutline } from '@aglyn/shared-data-mdi'\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 Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useEffect, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'wishlist'\n\nexport const WISHLIST_UPDATED_EVENT = 'aglyn:wishlist-updated'\n\nfunction storageKey(hostId: string): string {\n return `aglyn_wishlist_${hostId}`\n}\n\n/** Guest wishlist ids from localStorage. */\nexport function readLocalWishlist(hostId: string): string[] {\n try {\n const raw = window.localStorage.getItem(storageKey(hostId))\n return raw ? (JSON.parse(raw) as string[]).slice(0, 200) : []\n } catch {\n return []\n }\n}\n\nfunction writeLocalWishlist(hostId: string, productIds: string[]): void {\n try {\n window.localStorage.setItem(\n storageKey(hostId),\n JSON.stringify(productIds.slice(0, 200)),\n )\n } catch {\n // Private-mode storage failures degrade to session-only behavior.\n }\n}\n\n/**\n * Toggles a product on the wishlist (AGL-297): members persist on their\n * doc (guest list merges in on first signed-in call); guests persist in\n * localStorage. Returns whether the product is on the list afterward.\n *\n * Takes its own `fetch` (AGL-1139). This is a plain module function, not a\n * component, so it cannot read the site context that says whether it is\n * running in Preview — and both callers are components that can. Defaulting\n * to the global keeps every other caller unchanged; passing `useSiteFetch()`\n * is what stops a preview click from writing to a real member's wishlist.\n */\nexport async function toggleWishlist(\n hostId: string,\n productId: string,\n siteFetch: typeof fetch = fetch,\n): Promise<boolean> {\n const local = readLocalWishlist(hostId)\n const has = local.includes(productId)\n const next = has\n ? local.filter((id) => id !== productId)\n : [...local, productId]\n writeLocalWishlist(hostId, next)\n // Best-effort member sync — 401 just means guest.\n void siteFetch('/api/membership/wishlist', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n action: has ? 'remove' : 'add',\n productId,\n }),\n }).catch(() => undefined)\n window.dispatchEvent(new Event(WISHLIST_UPDATED_EVENT))\n return !has\n}\n\nexport interface WishlistProps {\n heading?: string\n emptyText?: string\n}\n\ninterface CatalogItem {\n id: string\n name: string\n slug: string\n priceUsd: number\n imageUrl?: string\n soldOut: boolean\n /** Listed before it has a price (AGL-3676). */\n priceComingSoon?: boolean\n}\n\n/**\n * Wishlist block (AGL-297): renders the saved products (member doc when\n * signed in — merging any guest list — else localStorage) as cards\n * linking to their PDPs, with remove buttons.\n */\nconst Wishlist = forwardRef<HTMLDivElement, WishlistProps>((props, ref) => {\n const { heading, emptyText, ...rest } = 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 { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n const [items, setItems] = useState<CatalogItem[] | null>(null)\n\n const refresh = useCallback(async () => {\n if (!hostId) return\n let productIds = readLocalWishlist(hostId)\n try {\n // Merge the guest list into the member doc; response is canonical.\n const response = await siteFetch('/api/membership/wishlist', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, action: 'merge', productIds }),\n })\n if (response.ok) {\n const payload = await response.json()\n productIds = payload?.productIds ?? productIds\n writeLocalWishlist(hostId, productIds)\n }\n } catch {\n // Guest path.\n }\n if (productIds.length === 0) return setItems([])\n try {\n const response = await fetch(\n `/api/commerce/catalog?hostId=${encodeURIComponent(hostId)}` +\n `&ids=${encodeURIComponent(productIds.join(','))}&limit=100`,\n )\n const payload = await response.json()\n setItems(payload?.items ?? [])\n } catch {\n setItems([])\n }\n }, [hostId, siteFetch])\n\n useEffect(() => {\n void refresh()\n const handler = () => void refresh()\n window.addEventListener(WISHLIST_UPDATED_EVENT, handler)\n return () => window.removeEventListener(WISHLIST_UPDATED_EVENT, handler)\n }, [refresh])\n\n if (!hostId) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 3,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontSize: 13,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Wishlist — saved products render here'}\n </Box>\n )\n }\n\n return (\n <Box ref={ref} {...rest}>\n {heading ? (\n <Typography variant=\"h5\" gutterBottom>\n {heading}\n </Typography>\n ) : null}\n {items && items.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {emptyText || 'Nothing saved yet — tap the heart on any product.'}\n </Typography>\n ) : (\n <Box\n sx={{\n display: 'grid',\n gap: 2,\n gridTemplateColumns: {\n xs: 'repeat(2, 1fr)',\n md: 'repeat(4, 1fr)',\n },\n }}\n >\n {(items ?? []).map((item) => {\n // AGL-1726: resolved BEFORE the guard — see the note in\n // `product-detail.tsx`.\n const imageUrl = Aglyn.siteRelativeMediaSrc(item.imageUrl, {\n hostId,\n })\n return (\n <Card key={item.id} variant=\"outlined\">\n <CardActionArea href={`/products/${item.slug}`}>\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={item.name}\n sx={{ height: 120, objectFit: 'cover' }}\n // Deferred (AGL-2486), same reasoning as the product\n // grid: one image per saved item, none of them the lead.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : (\n <Box sx={{ height: 120, bgcolor: 'action.hover' }} />\n )}\n <CardContent sx={{ pb: 1 }}>\n <Typography variant=\"body2\" noWrap>\n {item.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {item.soldOut\n ? 'Sold out'\n : item.priceComingSoon\n ? 'Price coming soon'\n : `$${item.priceUsd}`}\n </Typography>\n </CardContent>\n </CardActionArea>\n <Button\n size=\"small\"\n color=\"error\"\n fullWidth\n onClick={() => void toggleWishlist(hostId, item.id, siteFetch)}\n >\n {'Remove'}\n </Button>\n </Card>\n )\n })}\n </Box>\n )}\n </Box>\n )\n})\nWishlist.displayName = 'AglynWishlist'\n\nexport const schema: Aglyn.ComponentSchema<WishlistProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Wishlist',\n description: 'Products a visitor saved, linking back to their pages.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiHeartOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Shown above the saved products.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'emptyText',\n label: 'Empty text',\n description: 'Copy when nothing is saved.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Wishlist',\n pluginId: BUNDLE_ID,\n description: 'Saved products with links back to their pages',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiHeartOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { heading: 'Your wishlist' },\n },\n },\n]\n\nexport default Wishlist\n"],"names":["Aglyn","mdiHeartOutline","Box","Button","Card","CardActionArea","CardContent","CardMedia","Typography","forwardRef","useCallback","useEffect","useState","BUNDLE_ID","generatePresetId","ID","WISHLIST_UPDATED_EVENT","storageKey","hostId","readLocalWishlist","raw","window","localStorage","getItem","JSON","parse","slice","writeLocalWishlist","productIds","setItem","stringify","toggleWishlist","productId","siteFetch","fetch","local","has","includes","next","filter","id","method","headers","body","action","catch","undefined","dispatchEvent","Event","Wishlist","props","ref","heading","emptyText","rest","nodeSx","Array","isArray","useSite","useSiteFetch","items","setItems","refresh","response","ok","payload","json","length","encodeURIComponent","join","handler","addEventListener","removeEventListener","sx","p","border","borderColor","borderRadius","color","fontSize","fontFamily","variant","gutterBottom","display","gap","gridTemplateColumns","xs","md","map","item","imageUrl","siteRelativeMediaSrc","href","slug","component","image","alt","name","height","objectFit","DEFERRED_IMAGE_ATTRIBUTES","bgcolor","pb","noWrap","soldOut","priceComingSoon","priceUsd","size","fullWidth","onClick","displayName","schema","$id","pluginId","description","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","label","FieldComponentType","TEXT_FIELD","presets","type","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
@@ -19,6 +19,7 @@ import { registerOperatorAlerts } from "@aglyn/aglyn/plugin-manager/operator-ale
|
|
|
19
19
|
import { registerPluginEventHandler } from "@aglyn/aglyn/plugin-manager/plugin-events";
|
|
20
20
|
import { registerPluginRecordIndex } from "@aglyn/aglyn/plugin-manager/plugin-record-index";
|
|
21
21
|
import { registerPluginShipmentRecords } from "@aglyn/aglyn/plugin-manager/plugin-shipment-records";
|
|
22
|
+
import { registerPluginLocalDeliveryRecords } from "@aglyn/aglyn/plugin-manager/plugin-local-deliveries";
|
|
22
23
|
import { registerPluginProductCatalog } from "@aglyn/aglyn/plugin-manager/plugin-product-catalog";
|
|
23
24
|
import { registerPluginStockLevels } from "@aglyn/aglyn/plugin-manager/plugin-stock-levels";
|
|
24
25
|
import { registerPluginChannelOrders } from "@aglyn/aglyn/plugin-manager/plugin-channel-orders";
|
|
@@ -55,6 +56,11 @@ import { registerCommerceEventTriggers } from "./server/order-event-triggers.js"
|
|
|
55
56
|
registerPluginShipmentRecords(lazyShipmentRecords, {
|
|
56
57
|
pluginId: BUNDLE_ID
|
|
57
58
|
});
|
|
59
|
+
// Local deliveries, as a courier plugin reads and writes them (AGL-3695):
|
|
60
|
+
// the drop's two ends, and the courier's run and the step it brings.
|
|
61
|
+
registerPluginLocalDeliveryRecords(lazyLocalDeliveries, {
|
|
62
|
+
pluginId: BUNDLE_ID
|
|
63
|
+
});
|
|
58
64
|
// A site's domains as Stripe payment method domains (AGL-3629), so Apple
|
|
59
65
|
// Pay and the other wallets show on the merchant's own domain. The Stripe
|
|
60
66
|
// calls arrive with the first event, not with the boot.
|
|
@@ -103,6 +109,11 @@ const lazyShipmentRecords = {
|
|
|
103
109
|
recordTracking: async (update)=>(await loadShipmentRecords()).recordTracking(update),
|
|
104
110
|
shipFromAddresses: async (hostId)=>(await loadShipmentRecords()).shipFromAddresses(hostId)
|
|
105
111
|
};
|
|
112
|
+
const loadLocalDeliveries = async ()=>(await import("./server/local-delivery-records.js")).commerceLocalDeliveryRecords;
|
|
113
|
+
const lazyLocalDeliveries = {
|
|
114
|
+
read: async (hostId, recordId)=>(await loadLocalDeliveries()).read(hostId, recordId),
|
|
115
|
+
recordCourier: async (write)=>(await loadLocalDeliveries()).recordCourier(write)
|
|
116
|
+
};
|
|
106
117
|
/** The catalog, with the Admin SDK and the model arriving on the first read. */ const lazyCatalog = {
|
|
107
118
|
store: async (hostId)=>(await import("./server/product-catalog.js")).productCatalog.store(hostId),
|
|
108
119
|
page: async (request)=>(await import("./server/product-catalog.js")).productCatalog.page(request)
|
|
@@ -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 { registerPluginEventHandler } from '@aglyn/aglyn/plugin-manager/plugin-events'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport {\n registerPluginShipmentRecords,\n type PluginShipmentRecords,\n} from '@aglyn/aglyn/plugin-manager/plugin-shipment-records'\nimport {\n registerPluginProductCatalog,\n type PluginProductCatalog,\n} from '@aglyn/aglyn/plugin-manager/plugin-product-catalog'\nimport {\n registerPluginStockLevels,\n type PluginStockLevels,\n} from '@aglyn/aglyn/plugin-manager/plugin-stock-levels'\nimport {\n registerPluginChannelOrders,\n type PluginChannelOrders,\n} from '@aglyn/aglyn/plugin-manager/plugin-channel-orders'\nimport {\n registerPluginProductWriter,\n type PluginProductWriter,\n} from '@aglyn/aglyn/plugin-manager/plugin-product-writer'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { COMMERCE_OPERATOR_ALERTS } from './constants/operator-alerts'\nimport { registerCommerceEventTriggers } from './server/order-event-triggers'\n\n/**\n * The commerce plugin's server declarations: the light registrations core\n * reads at boot, before any surface loads — among them its orders as a label\n * buyer reads and writes them, through core's `core.shipment-records`.\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 // Its order and return events (AGL-3611), and each as a workflow trigger.\n registerCommerceEventTriggers()\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 // Orders, as a label buyer reads and writes them (AGL-3612): read the\n // lines and the address, write a shipment, record tracking. The module and\n // the Admin SDK load with the first call.\n registerPluginShipmentRecords(lazyShipmentRecords, { pluginId: BUNDLE_ID })\n // A site's domains as Stripe payment method domains (AGL-3629), so Apple\n // Pay and the other wallets show on the merchant's own domain. The Stripe\n // calls arrive with the first event, not with the boot.\n registerPluginEventHandler(\n 'host.domain.attached',\n async (payload) =>\n (await import('./server/payment-method-domains')).onHostDomainAttached(payload),\n { pluginId: BUNDLE_ID },\n )\n registerPluginEventHandler(\n 'host.domain.released',\n async (payload) =>\n (await import('./server/payment-method-domains')).onHostDomainReleased(payload),\n { pluginId: BUNDLE_ID },\n )\n // The store's catalog, as a shopping-channel feed reads it (AGL-3637).\n registerPluginProductCatalog(lazyCatalog, { pluginId: BUNDLE_ID })\n // Counts another warehouse keeps, applied as a stock-take (AGL-3634): a\n // fulfillment network says how many it can ship and the products say so.\n registerPluginStockLevels(lazyStockLevels, { pluginId: BUNDLE_ID })\n // Products another plugin brings from a source of its own (AGL-3641),\n // written through the store's own rules.\n registerPluginProductWriter(lazyProductWriter, { pluginId: BUNDLE_ID })\n // Orders a marketplace sold (AGL-3638), recorded as this plugin's own:\n // numbered, their units off the same shelf in the same write, announced.\n registerPluginChannelOrders(lazyChannelOrders, { pluginId: BUNDLE_ID })\n}\n\n/** Outside orders, with the Admin SDK and the model arriving on the first call. */\nconst loadChannelOrders = async () => (await import('./server/channel-orders')).commerceChannelOrders\n\nconst lazyChannelOrders: PluginChannelOrders = {\n importOrder: async (order) => (await loadChannelOrders()).importOrder(order),\n cancelOrder: async (request) => (await loadChannelOrders()).cancelOrder(request),\n recordFees: async (request) => (await loadChannelOrders()).recordFees(request),\n}\n\n/** Stock counts, with the Admin SDK and the model arriving on the first call. */\nconst lazyStockLevels: PluginStockLevels = {\n setAvailable: async (request) => (await import('./server/stock-levels')).commerceStockLevels.setAvailable(request),\n}\n\n/** The writer, with the Admin SDK and the model arriving on the first write. */\nconst lazyProductWriter: PluginProductWriter = {\n upsertSourced: async (write) => (await import('./server/product-writer')).commerceProductWriter.upsertSourced(write),\n}\n\nconst loadShipmentRecords = async () =>\n (await import('./server/shipment-records')).commerceShipmentRecords\n\nconst lazyShipmentRecords: PluginShipmentRecords = {\n read: async (hostId, recordId) => (await loadShipmentRecords()).read(hostId, recordId),\n recordShipment: async (write) => (await loadShipmentRecords()).recordShipment(write),\n recordTracking: async (update) => (await loadShipmentRecords()).recordTracking(update),\n shipFromAddresses: async (hostId) => (await loadShipmentRecords()).shipFromAddresses(hostId),\n}\n\n/** The catalog, with the Admin SDK and the model arriving on the first read. */\nconst lazyCatalog: PluginProductCatalog = {\n store: async (hostId) => (await import('./server/product-catalog')).productCatalog.store(hostId),\n page: async (request) => (await import('./server/product-catalog')).productCatalog.page(request),\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","registerPluginEventHandler","registerPluginRecordIndex","registerPluginShipmentRecords","registerPluginProductCatalog","registerPluginStockLevels","registerPluginChannelOrders","registerPluginProductWriter","BUNDLE_ID","COMMERCE_OPERATOR_ALERTS","registerCommerceEventTriggers","registerCommerceServerDeclarations","pluginId","lazyIndex","lazyShipmentRecords","payload","onHostDomainAttached","onHostDomainReleased","lazyCatalog","lazyStockLevels","lazyProductWriter","lazyChannelOrders","loadChannelOrders","commerceChannelOrders","importOrder","order","cancelOrder","request","recordFees","setAvailable","commerceStockLevels","upsertSourced","write","commerceProductWriter","loadShipmentRecords","commerceShipmentRecords","read","hostId","recordId","recordShipment","recordTracking","update","shipFromAddresses","store","productCatalog","page","name","load","list","get"],"mappings":"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"}
|
|
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 { registerPluginEventHandler } from '@aglyn/aglyn/plugin-manager/plugin-events'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport {\n registerPluginShipmentRecords,\n type PluginShipmentRecords,\n} from '@aglyn/aglyn/plugin-manager/plugin-shipment-records'\nimport {\n registerPluginLocalDeliveryRecords,\n type PluginLocalDeliveryRecords,\n} from '@aglyn/aglyn/plugin-manager/plugin-local-deliveries'\nimport {\n registerPluginProductCatalog,\n type PluginProductCatalog,\n} from '@aglyn/aglyn/plugin-manager/plugin-product-catalog'\nimport {\n registerPluginStockLevels,\n type PluginStockLevels,\n} from '@aglyn/aglyn/plugin-manager/plugin-stock-levels'\nimport {\n registerPluginChannelOrders,\n type PluginChannelOrders,\n} from '@aglyn/aglyn/plugin-manager/plugin-channel-orders'\nimport {\n registerPluginProductWriter,\n type PluginProductWriter,\n} from '@aglyn/aglyn/plugin-manager/plugin-product-writer'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { COMMERCE_OPERATOR_ALERTS } from './constants/operator-alerts'\nimport { registerCommerceEventTriggers } from './server/order-event-triggers'\n\n/**\n * The commerce plugin's server declarations: the light registrations core\n * reads at boot, before any surface loads — among them its orders as a label\n * buyer reads and writes them, through core's `core.shipment-records`.\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 // Its order and return events (AGL-3611), and each as a workflow trigger.\n registerCommerceEventTriggers()\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 // Orders, as a label buyer reads and writes them (AGL-3612): read the\n // lines and the address, write a shipment, record tracking. The module and\n // the Admin SDK load with the first call.\n registerPluginShipmentRecords(lazyShipmentRecords, { pluginId: BUNDLE_ID })\n // Local deliveries, as a courier plugin reads and writes them (AGL-3695):\n // the drop's two ends, and the courier's run and the step it brings.\n registerPluginLocalDeliveryRecords(lazyLocalDeliveries, { pluginId: BUNDLE_ID })\n // A site's domains as Stripe payment method domains (AGL-3629), so Apple\n // Pay and the other wallets show on the merchant's own domain. The Stripe\n // calls arrive with the first event, not with the boot.\n registerPluginEventHandler(\n 'host.domain.attached',\n async (payload) =>\n (await import('./server/payment-method-domains')).onHostDomainAttached(payload),\n { pluginId: BUNDLE_ID },\n )\n registerPluginEventHandler(\n 'host.domain.released',\n async (payload) =>\n (await import('./server/payment-method-domains')).onHostDomainReleased(payload),\n { pluginId: BUNDLE_ID },\n )\n // The store's catalog, as a shopping-channel feed reads it (AGL-3637).\n registerPluginProductCatalog(lazyCatalog, { pluginId: BUNDLE_ID })\n // Counts another warehouse keeps, applied as a stock-take (AGL-3634): a\n // fulfillment network says how many it can ship and the products say so.\n registerPluginStockLevels(lazyStockLevels, { pluginId: BUNDLE_ID })\n // Products another plugin brings from a source of its own (AGL-3641),\n // written through the store's own rules.\n registerPluginProductWriter(lazyProductWriter, { pluginId: BUNDLE_ID })\n // Orders a marketplace sold (AGL-3638), recorded as this plugin's own:\n // numbered, their units off the same shelf in the same write, announced.\n registerPluginChannelOrders(lazyChannelOrders, { pluginId: BUNDLE_ID })\n}\n\n/** Outside orders, with the Admin SDK and the model arriving on the first call. */\nconst loadChannelOrders = async () => (await import('./server/channel-orders')).commerceChannelOrders\n\nconst lazyChannelOrders: PluginChannelOrders = {\n importOrder: async (order) => (await loadChannelOrders()).importOrder(order),\n cancelOrder: async (request) => (await loadChannelOrders()).cancelOrder(request),\n recordFees: async (request) => (await loadChannelOrders()).recordFees(request),\n}\n\n/** Stock counts, with the Admin SDK and the model arriving on the first call. */\nconst lazyStockLevels: PluginStockLevels = {\n setAvailable: async (request) => (await import('./server/stock-levels')).commerceStockLevels.setAvailable(request),\n}\n\n/** The writer, with the Admin SDK and the model arriving on the first write. */\nconst lazyProductWriter: PluginProductWriter = {\n upsertSourced: async (write) => (await import('./server/product-writer')).commerceProductWriter.upsertSourced(write),\n}\n\nconst loadShipmentRecords = async () =>\n (await import('./server/shipment-records')).commerceShipmentRecords\n\nconst lazyShipmentRecords: PluginShipmentRecords = {\n read: async (hostId, recordId) => (await loadShipmentRecords()).read(hostId, recordId),\n recordShipment: async (write) => (await loadShipmentRecords()).recordShipment(write),\n recordTracking: async (update) => (await loadShipmentRecords()).recordTracking(update),\n shipFromAddresses: async (hostId) => (await loadShipmentRecords()).shipFromAddresses(hostId),\n}\n\nconst loadLocalDeliveries = async () =>\n (await import('./server/local-delivery-records')).commerceLocalDeliveryRecords\n\nconst lazyLocalDeliveries: PluginLocalDeliveryRecords = {\n read: async (hostId, recordId) => (await loadLocalDeliveries()).read(hostId, recordId),\n recordCourier: async (write) => (await loadLocalDeliveries()).recordCourier(write),\n}\n\n/** The catalog, with the Admin SDK and the model arriving on the first read. */\nconst lazyCatalog: PluginProductCatalog = {\n store: async (hostId) => (await import('./server/product-catalog')).productCatalog.store(hostId),\n page: async (request) => (await import('./server/product-catalog')).productCatalog.page(request),\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","registerPluginEventHandler","registerPluginRecordIndex","registerPluginShipmentRecords","registerPluginLocalDeliveryRecords","registerPluginProductCatalog","registerPluginStockLevels","registerPluginChannelOrders","registerPluginProductWriter","BUNDLE_ID","COMMERCE_OPERATOR_ALERTS","registerCommerceEventTriggers","registerCommerceServerDeclarations","pluginId","lazyIndex","lazyShipmentRecords","lazyLocalDeliveries","payload","onHostDomainAttached","onHostDomainReleased","lazyCatalog","lazyStockLevels","lazyProductWriter","lazyChannelOrders","loadChannelOrders","commerceChannelOrders","importOrder","order","cancelOrder","request","recordFees","setAvailable","commerceStockLevels","upsertSourced","write","commerceProductWriter","loadShipmentRecords","commerceShipmentRecords","read","hostId","recordId","recordShipment","recordTracking","update","shipFromAddresses","loadLocalDeliveries","commerceLocalDeliveryRecords","recordCourier","store","productCatalog","page","name","load","list","get"],"mappings":"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"}
|