Home
Wiki
Home
Wiki
  1. 2. Components
  • Back to home
  • 1. Themes
  • Vs Code
    • Getting Started
  • Kitchenware
    • Layout
      • New Layout
      • Legacy Layout
    • Components
      • Announcement
      • Banner Carousel
      • Banner With Products Carousel
      • Blog Category List
      • Blog List
      • Brand List
      • Brands Carousel
      • Breadcrumb
      • Call To Action
      • Cart
      • Categories List
      • Change Password
      • Checkout
      • Cookie Manager
      • Filter list
      • Footer
      • Forgot Password
      • Form
      • Hero Carousel
      • Icon Block
      • Invitation
      • Last Visited Products
      • Layout
      • Login
      • Map
      • Nav Bar
      • Offer
      • Product Attachments
      • Product Attributes
      • Product Documentation
      • Product Expected
      • Product Modal
      • Products Block
      • Products Carousel
      • Product Single
      • Profile
      • Quote
      • Register
      • Related Products
      • Search
      • Stores
      • Subscribe Newsletter
      • Text with Image
      • Top Bar
      • Video
    • Reusables
      • Getting Started
    • Assets
      • Getting Started
    • SDK
      • Products
        • _findProductsByCategory
        • _findProductsByIds
        • _findProductsByTitle
        • _findProductsByFilter
        • _findProductsByCriteria
        • _findProductsAndCalculate
        • _findProductsThenCalculate
        • _getProductAttributeSet
        • _setLastVisited
      • Categories
        • _findCategoryTreeById
        • _findCategoriesByIds
        • _findCategoryByAlias
        • _findCategoryTreeByAlias
        • _getCategoryContent
      • Collections
        • _getCollectionContent
        • _findCollectionsByIds
        • _findCollectionsByIdsThenCalculate
      • Brands
        • _getBrandContent
        • _findBrandsByIds
      • Cart
        • _addToCartMulti
        • _addToCart
        • _setCart
        • _clearCart
        • _setCartListener
        • _removeFromCart
        • _calculateCart
      • Checkout
        • _startCheckout
        • _updateCheckout
        • _completeCheckout
      • Shopping Lists
        • _getShoppingLists
        • _updateShoppingList
        • _createShoppingList
        • _deleteShoppingList
        • _getShoppingListByAlias
      • Navigation
        • _getFooterMenu
        • _getHeaderMenu
      • Users
        • _getUserById
      • Utils
        • _calculateCurrency
        • _getCurrencySymbol
        • _getCulture
        • _subscribeToNewsletter
        • _findUnitsByIds
  • Noir
    • 0. Introduction
    • 1. Structure
      • _Overview
      • LayoutA.liquid
      • ComponentsList.liquid
      • Metas.liquid
      • CssVariables.liquid
      • Json.liquid
      • GoogleTagManager.liquid
      • StagingButton.liquid
    • 2. Components
      • _Overview
      • Announcement
      • BannerCarousel
      • BlogCategoryList
      • BlogList
      • BrandList
      • Breadcrumb
      • Cart
      • CategoriesList
      • ChangePassword
      • Checkout
      • CookieManager
      • FilterList
      • Footer
      • ForgotPassword
      • Form
      • IconBlock
      • Invitation
      • LastVisitedProducts
      • Login
      • Map
      • NavBar
      • ProductAttachments
      • ProductAttributes
      • ProductComparison
      • ProductDocumentation
      • ProductMixList
      • ProductsBlock
      • ProductsCarousel
      • ProductSingle
      • Profile
      • Quote
      • Register
      • RelatedProducts
      • SingleBlog
      • Stores
      • TextWithImage
      • ThankYouPage
      • TopBar
      • Wishlist
    • 3. Reusables
      • _Overview
      • Addresses
      • BillingRetail
      • AddressForm
      • AnnouncementModal
      • BackToTop
      • Company
      • General
      • Login
      • LoginModal
      • MonthlyTransactions
      • Orders
      • Payment
      • ProductAttachments
      • ProductAttributes
      • ProductComparisonButton
      • ProductComparisonFloatingButton
      • ProductGridItem
      • ProductListItem
      • ProductModal
      • ProfileInfo
      • PromptModal
      • Register
      • Services
      • Shipping
      • ShoppingLists
      • ShoppingListsButton
      • ShoppingListsNavbar
      • Toast
      • Transactions
      • Users
      • VariantContent
      • WishlistButton
      • Quotes
    • 4. Assets
      • Fonts
      • Images
      • Templates
      • Javascript
        • _Overview
        • theme.js
      • Css - Scss
        • _Overview
        • ThemeClasses
    • 5. SDK
      • _Overview
      • LiquidGlobals
      • ServicesSDK
  1. 2. Components

ProductMixList

Purpose#

The ProductMixList component renders the “My product mix” page: a paginated table of previously purchased items.
It:
is gated by the feature flag GlobalData.Settings.EnableMyProductMix
shows an empty state when there are no items
renders a table of items with quantity controls and an add-to-cart action per row
supports sorting and pagination (with a “sliding window” pages UI)
validates quantity rules via an API call (debounced per row)
fetches pages from the backend and can show a retry UI if fetching fails
sends GA4 selectItem when a product is clicked

Inputs (model contract)#

Model shape (storefront example)#

{
  "items": [
    {
      "productId": "Product Id",
      "productVariantId": "Variant Id",
      "title": "Sample product title",
      "sku": "Sample text",
      "imageUrl": "https://example.com/media/sample.jpg",
      "alias": "sample-product-alias",
      "dimension1Name": "Sample text",
      "dimension1Value": "Sample text",
      "dimension2Name": "Sample text",
      "dimension2Value": "Sample text",
      "lastPurchasedPrice": 0,
      "lastPurchasedQuantity": 1,
      "lastPurchasedAtUtc": "2026-01-01T00:00:00.0000000+00:00",
      "lastPurchasedTotal": 0,
      "lastPurchasedTotalText": "Sample price text",
      "currentPrice": 0,
      "canOrder": false,
      "minOrderQuantity": 1,
      "maxOrderQuantity": 99,
      "orderQuantityStep": 1,
      "sellOutOfStock": true,
      "stock": 0,
      "quantityConstraints": {
        "additive": {
          "minimum": 1,
          "maximum": 999999,
          "step": 1,
          "isValid": true
        },
        "absolute": {
          "minimum": 1,
          "maximum": 999999,
          "step": 1,
          "isValid": true
        }
      }
    },
    "..."
  ],
  "currentPage": 1,
  "totalPages": 2,
  "totalCount": 23,
  "pageSize": 20,
  "name": "ProductMixList",
  "view": "Default",
  "section": "SectionA",
  "settings": {
    "id": "Component Id",
    "section": "SectionA",
    "type": "NoirProductMixList",
    "name": "ProductMixList",
    "configuredInContentApi": true,
    "view": "Default",
    "displayName": "",
    "cssClass": ""
  },
  "translations": {
    "myProductMix": "Sample text",
    "noProducts": "Sample text",
    "noProductsDescription": "Sample text",
    "...": "..."
  }
}

Required fields#

settings.id
items[]
Can be empty; the component renders an empty state.

Optional fields#

Pagination:
currentPage, totalPages, totalCount, pageSize
settings.cssClass
Per item:
imageUrl (falls back to global no-image)
dimension1Name, dimension1Value, dimension2Name, dimension2Value
quantityConstraints.*
minOrderQuantity, maxOrderQuantity, orderQuantityStep
translations.*

JavaScript#

Global object#

Components/ProductMixList/Default.js exposes:
Liquid binds it via Alpine:
<section
  x-data='productmixlistdefault.initComponent(items, currentPage, totalPages, totalCount, pageSize, errorMessages)'
>

initComponent(items, currentPage, totalPages, totalCount, pageSize, errorMessages)#

Purpose (why it exists)
Centralizes all list/table behavior (pagination, sorting, quantities, add-to-cart) into one Alpine component so the page can work without full reloads.
Keeps UI state (loading/error, currentSort, quantities) co-located with the event handlers used by the template.
What it does
Creates and returns the Alpine state object.
Stores list/pagination state and error message mapping.

init()#

Purpose (why it exists)
Initializes per-row UI-only fields (like _qty) so quantity inputs work even though the API payload doesn’t contain “current UI quantity”.
What it does
Initializes each item._qty to a computed start quantity (getStartQuantity(item)).

getItemByVariantId(variantId)#

Purpose (why it exists)
Provides a fast lookup helper so other handlers can target a row by variant id (the most stable identifier in the list UI).
What it does
Finds an item in items[] by productVariantId.

getQuantity(variantId)#

Purpose (why it exists)
Normalizes reads of the UI quantity so the template and handlers don’t need to care about null/empty values.
What it does
Returns numeric _qty for an item.

sendSelectEvent(product, listName, position)#

Purpose (why it exists)
Tracks product clicks from the Product Mix list for analytics (GA4 selectItem).
What it does
Sends GA4 selectItem when a product row is clicked.
Key behaviors / edge cases
Uses prepareListProducts([product]), so it expects the item shape to be compatible with list GA mapping.

formatDate(dateValue)#

Purpose (why it exists)
Keeps date formatting in one place so the list can render consistent, locale-aware “last purchased” dates.
What it does
Formats a UTC date string into a human-readable date using Intl.DateTimeFormat('el-GR').

fetchList(page)#

Purpose (why it exists)
Loads another page of “my product mix” items without a full page reload (pagination + sort changes reuse this).
Handles auth edge case (401) by redirecting to login.
What it does
Fetches a page of items via fetch() from /api/product-mix/my-list.
Reinitializes _qty and pagination fields from the response.
Key behaviors / edge cases
On failure it sets error = true and clears items.

handleSort(value)#

Purpose (why it exists)
Allows changing sorting while staying on the same page and reusing the same table UI.
What it does
Updates currentSort and refetches page 1.

goToPage(page)#

Purpose (why it exists)
Implements client-side pagination navigation (guarding invalid pages and preventing redundant fetches).
What it does
Fetches the requested page (if valid) and scrolls to top.

visiblePages (getter)#

Purpose (why it exists)
Produces a small “window” of page numbers so pagination UI stays compact even with many pages.
What it does
Returns up to 5 page numbers centered around currentPage.

increaseQuantity(item)#

Purpose (why it exists)
Provides +/- controls for quantity editing with correct step/min/max rules per item.
What it does
Increases item._qty by orderQuantityStep up to maxOrderQuantity.

decreaseQuantity(item)#

Purpose (why it exists)
Provides the “-” control for quantity editing while enforcing minimum constraints.
What it does
Decreases item._qty by orderQuantityStep down to minOrderQuantity.

updateQuantity(value, item)#

Purpose (why it exists)
Handles manual typing into the quantity input while enforcing min/max rules and keeping the row state consistent.
What it does
Parses the input value and clamps it to [minOrderQuantity, maxOrderQuantity].
Writes the normalized value back into item._qty.

addToCart(item)#

Purpose (why it exists)
Connects the list row to the cart system so users can re-order frequently purchased variants.
What it does
Adds the item to cart via servicesreusabledefault.addToCart(...).
Uses Alpine.store("toast") for error feedback.
Key behaviors / edge cases
On failure it shows a generic toast.
If response.data.lineFailures[] exists, it maps error types to translated messages via getLineFailureMessages(...).

getLineFailureMessages(lineFailure, productTitle)#

Purpose (why it exists)
Converts backend line failure error types into user-friendly, translated toast messages.
What it does
Maps line failure errorTypes[] values to translated error messages.
Returns a single combined, de-duplicated message prefixed by the product title.

Global Alpine stores (used by ProductMixList)#

Alpine.store("toast") (user feedback)#

Used for:
fetch errors
quantity validation errors
add-to-cart errors and line-failure details

Services / API calls#

GET /api/product-mix/my-list?page=<n>&pageSize=<n>&orderBy=<sort>
Fetches paginated/sorted product mix items.
servicesreusabledefault.addToCart(productId, productVariantId, quantity, currentPrice, originalPrice)
Adds a single product mix item to cart.

Dependencies#

Alpine.js
Browser fetch (for list paging)
Theme utilities:
prepareListProducts(...)
sendGAEvent(...)
Alpine stores:
toast

Notes#

This component uses servicesreusabledefault.addToCart(...) for add-to-cart.
The entire component is gated by GlobalData.Settings.EnableMyProductMix.
The Liquid template duplicates pagination logic that also exists in JS (generatePages()), so they must stay in sync.
The table uses 2 render paths:
server-rendered {% for item in items %}
client-rendered <template x-for="..."> after any fetch/update (updateFlag).

Extras#

Template behavior (Liquid + Alpine)#

Builds an errorMessages JSON string in Liquid and passes it to initComponent(...).
Renders an empty state when items is empty.
Renders pagination UI with aria labels for previous/next and individual pages.
Disables quantity controls when quantityConstraints.additive.isValid == false.
Uses a global no-image fallback from GlobalData.Settings.noImage (fallback to theme Assets/images/no-image.svg).
Modified at 2026-05-04 12:02:01
Previous
ProductDocumentation
Next
ProductsBlock
Built with