Product listing
Frontastic component name
Product listing
Page type
Used on the Category dynamic page
Example images

Code samples
{
"tasticType": "frontastic/boost/product-listing/product-listing-page",
"name": "Product listing page",
"category": "Product listing",
"icon": "list",
"schema": [
{
"name": "Data source selection",
"fields": [
{
"label": "Data source",
"field": "stream",
"type": "stream",
"streamType": "product-list",
"default": null
},
{
"label": "Show facets",
"field": "showFacets",
"type": "boolean",
"default": true
},
{
"label": "Show next page",
"field": "showNextPage",
"type": "enum",
"default": "LoadMore",
"values": [
{ "value": "None", "name": "None" },
{ "value": "LoadMore", "name": "Load more button" },
{ "value": "InfinityScroll", "name": "Infinity scroll" }
]
},
{
"label": "Show products count label",
"field": "showProductsCount",
"type": "boolean",
"default": true
},
{
"label": "Show percentual reducement",
"field": "showPercent",
"type": "boolean",
"default": true
},
{
"label": "Show strike price",
"field": "showStrikePrice",
"type": "boolean",
"default": true
}
]
},
{
"name": "Sidebar",
"fields": [
{
"label": "Show sidebar",
"field": "showSidebar",
"type": "boolean",
"default": true
},
{
"label": "Sidebar header",
"field": "sidebarHeader",
"translatable": true,
"type": "string"
},
{
"label": "Navigation tree",
"field": "tree",
"type": "tree"
}
]
}
]
}
import React from 'react'
import { connect } from 'react-redux'
import { compose } from 'redux'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import app from '@frontastic/catwalk/src/js/app/app'
import tastify from '@frontastic/catwalk/src/js/helper/tastify'
import UrlHandler from '@frontastic/catwalk/src/js/app/urlHandler'
import facetConnector from '@frontastic/catwalk/src/js/app/connector/facet'
import categoryConnector from '@frontastic/catwalk/src/js/app/connector/category'
import urlHandlerConnector from '@frontastic/catwalk/src/js/app/connector/urlHandler'
import ProductListing from '../../../patterns/organisms/Product/ProductListing'
import CategoryNavigationTree from '../../../patterns/molecules/Product/CategoryNavigationTree'
function ProductListingPageTastic ({ data, node, route, tastic, wishlist, urlHandler }) {
if (!urlHandler) {
return null
}
if (wishlist.isComplete()) {
data.stream.items.map((product) => {
const wishlisted = wishlist.data.lineItems.find((item) => {
return item.variant.sku === product.variants[0].sku
})
product.wishlisted = !!wishlisted
product.wishlistItemId = wishlisted ? wishlisted.lineItemId : null
return product
})
}
const parameters = urlHandler.parameterReader(tastic.configuration.stream).getParameters()
var sortState = {}
if (parameters) {
sortState = {
attributeId: parameters.sortAttributeId,
order: parameters.sortOrder,
}
}
const onChangeStreamParameters = (parameters) => {
const newParams = {
...route.parameters,
...parameters,
}
app.getRouter().push(route.route, newParams)
}
const handleAddToWishlist = (product, variant) => {
app.getLoader('wishlist').add(product, variant, 1, null)
}
const handleLoadNextPage = () => {
const parameters = urlHandler.deriveParameters((urlState) => {
var stream = urlState.getStream(tastic.configuration.stream)
stream.setOffset(0)
if (data.stream.count + 24 > data.stream.total) {
stream.setLimit(data.stream.total)
} else {
stream.setLimit(data.stream.count + 24)
}
})
onChangeStreamParameters(parameters)
}
const hanleSortChange = (sort) => {
const parameters = urlHandler.deriveParameters((urlState) => {
var stream = urlState.getStream(tastic.configuration.stream)
stream.setOffset(0)
stream.setLimit(24)
stream.setSortOrder(sort.attributeId, sort.order)
})
onChangeStreamParameters(parameters)
}
const handleFacetsChanged = (facets) => {
const parameters = urlHandler.deriveParameters((urlState) => {
var stream = urlState.getStream(tastic.configuration.stream)
stream.setOffset(0)
stream.setLimit(24)
facets.forEach((facet) => {
if (facet.selected) {
if (facet.type === 'range') {
stream.setFilter(facet.handle, {
min: facet.value.min,
max: facet.value.max,
})
}
if (facet.type === 'term') {
var newTerms = facet.terms
.filter((facet) => {
return facet.selected === true
})
.map((facet) => {
return facet.value
})
if (newTerms) {
stream.setFilter(facet.handle, {
terms: newTerms,
})
}
}
} else {
stream.removeFilter(facet.handle)
}
})
})
onChangeStreamParameters(parameters)
}
return (
<div className='flex flex-row'>
{data.showSidebar && (
<div className='hidden md:block md:w-1/4 pt-4 pl-4'>
<CategoryNavigationTree title={data.sidebarHeader} navTree={data.tree} currentPage={node} />
</div>
)}
<div
className={classnames({
'flex flex-col w-full': true,
'md:w-3/4': data.showSidebar,
})}
>
<ProductListing
data={data}
sortState={sortState}
onLoadNextPage={handleLoadNextPage}
onSortChange={hanleSortChange}
onFacetsChanged={handleFacetsChanged}
onAddToWishlist={handleAddToWishlist}
isFullWidth={!data.showSidebar}
showFacets={data.showFacets}
showNextPage={data.showNextPage}
showProductsCount={data.showProductsCount}
showPercent={data.showPercent}
showStrikePrice={data.showStrikePrice}
/>
</div>
</div>
)
}
ProductListingPageTastic.propTypes = {
data: PropTypes.object.isRequired,
node: PropTypes.object.isRequired,
tastic: PropTypes.object.isRequired,
route: PropTypes.object.isRequired,
wishlist: PropTypes.object.isRequired,
urlHandler: PropTypes.instanceOf(UrlHandler),
}
export default tastify({
translate: true,
connect: { node: true, tastic: true, route: true, wishlist: true, urlHandler: true },
})(
compose(
connect(facetConnector),
connect(categoryConnector),
connect(urlHandlerConnector),
connect((globalState) => {
let streamParameters = globalState.app.route.parameters.s || {}
return {
route: globalState.app.route,
streamParameters: streamParameters,
}
})
)(ProductListingPageTastic)
)
Updated about 2 years ago