[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/ManualPrice.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent } from 'react'
import React, { Fragment, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { FormattedPrice } from 'vtex.formatted-price'
import { Button, ButtonPlain, InputCurrency, Modal, Tag } from 'vtex.styleguide'
import { useRuntime } from 'vtex.render-runtime'

import { useItemContext } from './ItemContext'
import { getFormattedPrice } from './utils/price'

const ManualPrice: FunctionComponent = () => {
  const {
    item,
    itemIndex,
    shouldAllowManualPrice,
    onSetManualPrice,
  } = useItemContext()

  const [isModalOpen, setIsModalOpen] = useState(false)
  const [manualPrice, setManualPrice] = useState(item.manualPrice)
  const {
    culture: { currency, locale },
  } = useRuntime()

  const intl = useIntl()
  const priceChanged =
    item.sellingPrice === item.manualPrice && item.sellingPrice !== item.price

  const submitManualPrice = (evt: React.FormEvent<HTMLFormElement>) => {
    evt.preventDefault()

    if (manualPrice != null && !Number.isNaN(manualPrice)) {
      onSetManualPrice(manualPrice, itemIndex)
    }

    setIsModalOpen(false)
  }

  const handleManualPriceChange = (
    evt: React.ChangeEvent<HTMLInputElement>
  ) => {
    const newManualPrice = (evt.target.value as unknown) as number

    setManualPrice(Math.floor(newManualPrice * 100))
  }

  const handleOpenModal = () => {
    setIsModalOpen(true)
  }

  const handleCloseModal = () => {
    setManualPrice(item.sellingPrice)
    setIsModalOpen(false)
  }

  const revertToOriginalPrice = () => {
    if (item.price != null) {
      onSetManualPrice(item.price, itemIndex)
      setManualPrice(item.price)
    }

    setIsModalOpen(false)
  }

  return (
    <Fragment>
      {shouldAllowManualPrice && (
        <div className="flex flex-column items-center mt3">
          {priceChanged ? (
            <div>
              <div className="flex-grow-0 tc mb2 c-muted-1">
                <FormattedPrice value={getFormattedPrice(item.manualPrice)} />
              </div>
              <div className="flex-grow-0 mb3 tc">
                <Tag size="small" bgColor="#3F3F40" className="fw5">
                  <FormattedMessage id="store/product-list.priceChanged" />
                </Tag>
              </div>
              <ButtonPlain size="small" onClick={handleOpenModal}>
                <FormattedMessage id="store/product-list.priceOptions" />
              </ButtonPlain>
            </div>
          ) : (
            <ButtonPlain size="small" onClick={handleOpenModal}>
              <FormattedMessage id="store/product-list.changePrice" />
            </ButtonPlain>
          )}
        </div>
      )}

      <Modal isOpen={isModalOpen} onClose={handleCloseModal} className="w-25">
        <div className="flex flex-column">
          <span className="t-small mw9 mb1">
            <FormattedMessage id="store/product-list.originalPrice" />
          </span>
          <div className={`c-muted-1 mb3 ${priceChanged ? 'strike' : ''}`}>
            <FormattedPrice value={getFormattedPrice(item.price)} />
          </div>

          <label className="t-small mw9 mb2" htmlFor="manual-price-input">
            <FormattedMessage id="store/product-list.changeTo" />
          </label>
          <div className="flex flex-row flex-grow mb3">
            <div className="mr2">
              <InputCurrency
                id="manual-price-input"
                placeholder={intl.formatMessage({
                  id: 'store/product-list.manualPricePlaceholder',
                })}
                locale={locale}
                currencyCode={currency}
                value={getFormattedPrice(manualPrice)}
                onChange={handleManualPriceChange}
              />
            </div>

            <Button
              variation="secondary"
              size="regular"
              onClick={submitManualPrice}
            >
              <FormattedMessage id="store/product-list.applyManualPrice" />
            </Button>
          </div>

          {priceChanged && (
            <div>
              <ButtonPlain onClick={revertToOriginalPrice}>
                <FormattedMessage id="store/product-list.revertToOriginal" />
              </ButtonPlain>
            </div>
          )}
        </div>
      </Modal>
    </Fragment>
  )
}

export default ManualPrice

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/RemoveButton.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { Loading } from 'vtex.render-runtime'
import { applyModifiers, useCssHandles } from 'vtex.css-handles'
import { Button } from 'vtex.styleguide'
import { IconDelete } from 'vtex.store-icons'

import { useItemContext } from './ItemContext'
import { opaque } from './utils/opaque'

const CSS_HANDLES = ['removeButtonContainer', 'removeButton', 'item'] as const

type DisplayMode = 'icon-button' | 'text-button'
type Variation =
  | 'primary'
  | 'secondary'
  | 'tertiary'
  | 'inverted-tertiary'
  | 'danger'
  | 'danger-tertiary'

interface Props {
  displayMode?: DisplayMode
  variation?: Variation
}

function RemoveButton(props: Props) {
  const { displayMode = 'icon-button', variation = 'danger' } = props
  const { item, loading, onRemove } = useItemContext()
  const handles = useCssHandles(CSS_HANDLES)

  if (loading) {
    return <Loading />
  }

  if (displayMode === 'text-button') {
    return (
      <Button variation={variation} onClick={onRemove}>
        <FormattedMessage id="store/product-list.delete-button.default-label" />
      </Button>
    )
  }

  return (
    <div
      className={`${handles.removeButtonContainer} ${applyModifiers(
        handles.item,
        item.sellingPrice === 0 ? 'gift' : ''
      )} ${opaque(item.availability)}`}
    >
      <button
        id={`remove-button-${item.id}`}
        style={{ color: '#727273' }}
        className={`${handles.removeButton} pointer bg-transparent bn pa2`}
        title="remove"
        onClick={onRemove}
      >
        <IconDelete />
      </button>
    </div>
  )
}

export default RemoveButton

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/LazyRender.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent } from 'react'
import React, { useRef, useState } from 'react'
import { useOnView } from 'vtex.on-view'

interface Props {
  height?: number
  offset?: number
  initializeOnInteraction?: boolean
}

const LazyRender: FunctionComponent<Props> = ({
  children,
  height = 400,
  offset = 300,
  initializeOnInteraction = false,
}) => {
  const ref = useRef(null)
  const [hasBeenViewed, setHasBeenViewed] = useState(false)

  const handleView = () => {
    setHasBeenViewed(true)
  }

  useOnView({
    ref,
    onView: handleView,
    once: true,
    initializeOnInteraction,
  })

  if (hasBeenViewed) {
    return <>{children}</>
  }

  return (
    <div
      style={{
        position: 'relative',
        width: '100%',
        height,
      }}
    >
      <div
        ref={ref}
        style={{
          position: 'relative',
          width: '100%',
          top: -offset,
          height: '100%',
          paddingBottom: offset * 2,
          boxSizing: 'content-box',
        }}
      />
    </div>
  )
}

export default LazyRender

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/package.json from the product-list VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "scripts": {
    "test": "vtex-test-tools test",
    "lint": "tsc --noEmit && eslint --ext ts,tsx ."
  },
  "dependencies": {
    "@vtex/css-handles": "^1.0.0",
    "classnames": "^2.2.6",
    "react": "^16.8.3",
    "react-apollo": "^2.5.8",
    "react-dom": "^16.8.3",
    "react-intl": "3",
    "react-sizeme": "^2.6.7"
  },
  "devDependencies": {
    "@apollo/react-testing": "^3.1.3",
    "@types/classnames": "^2.2.7",
    "@types/graphql": "^14.2.0",
    "@types/jest": "^24.0.11",
    "@types/node": "^11.13.0",
    "@types/prop-types": "^15.7.0",
    "@types/react": "^16.8.10",
    "@vtex/styleguide": "^8.70.4",
    "@vtex/test-tools": "^3.0.0",
    "apollo-client": "^2.5.1",
    "typescript": "3.9.7",
    "vtex.checkout-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.checkout-graphql@0.55.1/public/@types/vtex.checkout-graphql",
    "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles",
    "vtex.device-detector": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.device-detector@0.2.6/public/@types/vtex.device-detector",
    "vtex.flex-layout": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.flex-layout@0.15.2/public/@types/vtex.flex-layout",
    "vtex.format-currency": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.format-currency@0.3.0/public/@types/vtex.format-currency",
    "vtex.formatted-price": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.formatted-price@0.4.0/public/@types/vtex.formatted-price",
    "vtex.on-view": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.on-view@1.0.0/public/@types/vtex.on-view",
    "vtex.order-manager": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.order-manager@0.9.0/public/@types/vtex.order-manager",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.126.7/public/@types/vtex.render-runtime",
    "vtex.store-icons": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-icons@0.18.0/public/@types/vtex.store-icons",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.134.1/public/@types/vtex.styleguide"
  },
  "version": "0.37.4"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/ProductVariations.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent } from 'react'
import React from 'react'
import type { SkuSpecification } from 'vtex.checkout-graphql'
import { useCssHandles } from 'vtex.css-handles'

import { useItemContext } from './ItemContext'
import { opaque } from './utils/opaque'

const CSS_HANDLES = [
  'productVariationsContainer',
  'productVariationsItem',
] as const

const ProductVariations: FunctionComponent = () => {
  const { item } = useItemContext()
  const handles = useCssHandles(CSS_HANDLES)

  return item.skuSpecifications && item.skuSpecifications.length > 0 ? (
    <div
      className={`c-muted-1 f6 lh-copy ${
        handles.productVariationsContainer
      } ${opaque(item.availability)}`}
    >
      {item.skuSpecifications.map((spec: SkuSpecification) => {
        return (
          <div
            className={handles.productVariationsItem}
            id={`specification-${item.id}-${spec.fieldName}`}
            key={spec.fieldName ?? undefined}
          >
            {`${spec.fieldName}: ${spec.fieldValues.join(', ')}`}
          </div>
        )
      })}
    </div>
  ) : null
}

export default ProductVariations

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/UnitPrice.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import classnames from 'classnames'
import { FormattedMessage, useIntl } from 'react-intl'
import { FormattedCurrency } from 'vtex.format-currency'
import { useCssHandles } from 'vtex.css-handles'

import { useItemContext } from './ItemContext'
import { opaque } from './utils/opaque'
import type { TextAlignProp } from './utils/textAlign'
import { parseTextAlign } from './utils/textAlign'
import styles from './styles.css'

const CSS_HANDLES = [
  'unitPriceContainer',
  'unitPricePerUnitCurrency',
  'unitPriceMeasurementUnit',
  'unitListPrice',
] as const

interface UnitPriceProps extends TextAlignProp {
  unitPriceType?: UnitPriceType
  unitPriceDisplay?: UnitPriceDisplayType
  displayUnitListPrice?: DisplayUnitListPriceType
}

type UnitPriceDisplayType = 'always' | 'default'

type DisplayUnitListPriceType = 'showWhenDifferent' | 'notShow'

type UnitPriceType = 'price' | 'sellingPrice'

const UnitPrice: React.FC<UnitPriceProps> = ({
  textAlign = 'left',
  unitPriceType = 'sellingPrice',
  unitPriceDisplay = 'default',
  displayUnitListPrice = 'notShow',
}) => {
  const { item, loading } = useItemContext()
  const handles = useCssHandles(CSS_HANDLES)
  const intl = useIntl()
  const isManualPriceDefined =
    item.sellingPrice === item.manualPrice && item.sellingPrice !== item.price

  if (loading) {
    return null
  }

  const unitPrice = item[unitPriceType] ?? 0

  if (
    !(
      (item.quantity > 1 || unitPriceDisplay === 'always') &&
      unitPrice > 0 &&
      !isManualPriceDefined
    )
  ) {
    return null
  }

  return (
    <div
      id={`unit-price-${item.id}`}
      className={classnames(
        handles.unitPriceContainer,
        opaque(item.availability),
        parseTextAlign(textAlign),
        // kept for backwards compatibility
        styles.quantity,
        styles.unitPrice,
        't-mini c-muted-1 lh-title'
      )}
    >
      {item.listPrice &&
        unitPrice !== item.listPrice &&
        displayUnitListPrice === 'showWhenDifferent' && (
          <div className={`strike ${handles.unitListPrice}`}>
            <FormattedCurrency value={item.listPrice / 100} />
          </div>
        )}
      <FormattedMessage
        id="store/product-list.pricePerUnit"
        values={{
          price: (
            <div className={`${handles.unitPricePerUnitCurrency} dib`}>
              <FormattedCurrency value={unitPrice / 100} />
            </div>
          ),
          perMeasurementUnit: (
            <div className={`${handles.unitPriceMeasurementUnit} dib`}>
              <FormattedMessage
                id="store/product-list.pricePerUnit.measurementUnit"
                values={{
                  unitMultiplier: intl.formatNumber(item.unitMultiplier ?? 1),
                  measurementUnit: item.measurementUnit,
                }}
              />
            </div>
          ),
        }}
      />
    </div>
  )
}

UnitPrice.schema = {
  properties: {
    textAlign: {
      type: 'string',
      default: 'left',
      isLayout: true,
    },
  },
}

export default UnitPrice

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/Price.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { FormattedCurrency } from 'vtex.format-currency'
import { FormattedPrice } from 'vtex.formatted-price'
import { Loading } from 'vtex.render-runtime'
import { useCssHandles, applyModifiers } from 'vtex.css-handles'

import { useItemContext } from './ItemContext'
import { opaque } from './utils/opaque'
import type { TextAlignProp } from './utils/textAlign'
import { parseTextAlign } from './utils/textAlign'
import styles from './styles.css'

const CSS_HANDLES = [
  'productPriceContainer',
  'productPriceCurrency',
  'productPrice',
] as const

type PriceProps = TextAlignProp & {
  showListPrice?: boolean
}

const Price: React.FC<PriceProps> = ({
  textAlign = 'left',
  showListPrice = true,
}) => {
  const { item, loading } = useItemContext()
  const handles = useCssHandles(CSS_HANDLES)

  if (loading) {
    return <Loading />
  }

  return (
    <div
      className={`${opaque(item.availability)} ${styles.price} ${
        handles.productPriceContainer
      } ${parseTextAlign(textAlign)}`}
    >
      {item.listPrice && item.listPrice !== item.sellingPrice && showListPrice ? (
        <div
          id={`list-price-${item.id}`}
          className={`${handles.productPriceCurrency}  ${applyModifiers(
            handles.productPriceCurrency,
            item.sellingPrice === 0 ? 'gift' : ''
          )} c-muted-1 strike t-mini mb2`}
        >
          <FormattedCurrency
            value={
              (item.listPrice * (item.unitMultiplier ?? 1) * item.quantity) /
              100
            }
          />
        </div>
      ) : null}
      <div
        id={`price-${item.id}`}
        className={`${handles.productPrice} ${applyModifiers(
          handles.productPrice,
          item.sellingPrice === 0 ? 'gift' : ''
        )} div fw6 fw5-m`}
      >
        <FormattedPrice
          value={
            item.sellingPrice != null
              ? (item.sellingPrice * item.quantity) / 100
              : item.sellingPrice
          }
        />
      </div>
    </div>
  )
}

Price.schema = {
  properties: {
    textAlign: {
      type: 'string',
      default: 'left',
      isLayout: true,
    },
  },
}

export default Price

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/AvailabilityMessage.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'

import { useItemContext } from './ItemContext'
import { AVAILABLE, CANNOT_BE_DELIVERED } from './constants/Availability'

interface Props {
  layout: 'cols' | 'rows'
}

const setContainerLayout = (prop: string) => {
  switch (prop) {
    case 'cols':
      return 'flex justify-between items-center'

    case 'rows':
      return ''

    default:
      return ''
  }
}

const CSS_HANDLES = [
  'availabilityMessageContainer',
  'availabilityMessageTextContainer',
  'availabilityMessageText',
] as const

const AvailabilityMessage: React.FC<Props> = ({ layout }) => {
  const {
    item: { availability },
    loading,
  } = useItemContext()

  const handles = useCssHandles(CSS_HANDLES)

  if (loading) {
    return null
  }

  return availability !== AVAILABLE ? (
    <div
      className={`bg-warning--faded br2 ${
        handles.availabilityMessageContainer
      } ${setContainerLayout(layout)}`}
    >
      <div
        className={`lh-title pa4 ${handles.availabilityMessageTextContainer}`}
      >
        {availability === CANNOT_BE_DELIVERED ? (
          <FormattedMessage id="store/product-list.message.cantBeDelivered">
            {(message) => (
              <span className={handles.availabilityMessageText}>{message}</span>
            )}
          </FormattedMessage>
        ) : (
          <FormattedMessage id="store/product-list.message.noLongerAvailable">
            {(message) => (
              <span className={handles.availabilityMessageText}>{message}</span>
            )}
          </FormattedMessage>
        )}
      </div>
    </div>
  ) : null
}

AvailabilityMessage.defaultProps = {
  layout: 'cols',
}

AvailabilityMessage.schema = {
  properties: {
    layout: {
      type: 'string',
      default: AvailabilityMessage.defaultProps.layout,
      isLayout: true,
    },
  },
}

export default AvailabilityMessage

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/ProductName.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent } from 'react'
import React from 'react'
import { Loading, useRuntime } from 'vtex.render-runtime'
import { useCssHandles } from 'vtex.css-handles'

import { useItemContext } from './ItemContext'
import { opaque } from './utils/opaque'

const CSS_HANDLES = ['productName'] as const

const ProductName: FunctionComponent = () => {
  const { item, loading } = useItemContext()
  const { rootPath = '' } = useRuntime()
  const handles = useCssHandles(CSS_HANDLES)

  if (loading) {
    return <Loading />
  }

  return (
    <a
      id={`name-${item.id}`}
      className={`c-on-base t-title lh-copy fw6 no-underline fw5-m ${
        handles.productName
      } ${opaque(item.availability)}`}
      href={item.detailUrl ? `${rootPath}${item.detailUrl}` : undefined}
    >
      {item.name}
    </a>
  )
}

export default ProductName

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/Image.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent } from 'react'
import React from 'react'
import { Loading, useRuntime } from 'vtex.render-runtime'
import type { Item } from 'vtex.checkout-graphql'
import { useCssHandles } from 'vtex.css-handles'

import { useItemContext } from './ItemContext'
import { NoImageIcon } from './components/NoImageIcon'
import { opaque } from './utils/opaque'

const getImageUrl = (imageUrls: Item['imageUrls']) => {
  if (!imageUrls) {
    return null
  }

  if (!window || !window.devicePixelRatio) {
    return imageUrls.at1x
  }

  if (window.devicePixelRatio <= 1.25) {
    return imageUrls.at1x
  }

  if (window.devicePixelRatio <= 2.25) {
    return imageUrls.at2x
  }

  return imageUrls.at3x
}

const CSS_HANDLES = [
  'productImageContainer',
  'productImageAnchor',
  'productImage',
] as const

interface ImageProps {
  width?: number
}

const Image: FunctionComponent<ImageProps> = ({ width = 96 }) => {
  const { item, loading } = useItemContext()
  const handles = useCssHandles(CSS_HANDLES)
  const { rootPath = '' } = useRuntime()

  if (loading) {
    return <Loading />
  }

  const imageUrl = getImageUrl(item.imageUrls)

  return (
    <div
      id={`image-${item.id}`}
      className={`${handles.productImageContainer} ${opaque(
        item.availability
      )}`}
      style={{ width }}
    >
      <a
        className={handles.productImageAnchor}
        href={item.detailUrl ? `${rootPath}${item.detailUrl}` : undefined}
      >
        {imageUrl ? (
          <img
            className={`${handles.productImage} br2`}
            loading="lazy"
            alt={item.name ?? undefined}
            src={imageUrl}
            width="100%"
          />
        ) : (
          <NoImageIcon />
        )}
      </a>
    </div>
  )
}

export default Image

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/ItemContext.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FC } from 'react'
import React, { createContext, useContext } from 'react'
import type { Item } from 'vtex.checkout-graphql'

interface Context {
  item: Item
  itemIndex: number
  loading: boolean
  shouldAllowManualPrice: boolean
  onQuantityChange: (value: number) => void
  onRemove: () => void
  onSetManualPrice: (price: number, itemIndex: number) => void
}

interface ItemContextProviderProps {
  value: Context
}

const ItemContext = createContext<Context | undefined>(undefined)

export const ItemContextProvider: FC<ItemContextProviderProps> = ({
  value,
  children,
}) => <ItemContext.Provider value={value}>{children}</ItemContext.Provider>

export const useItemContext = () => {
  const context = useContext(ItemContext)

  if (context === undefined) {
    throw new Error('useItemContext must be used within a ItemContextProvider')
  }

  return context
}

export default { useItemContext }

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/ProductReference.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent } from 'react'
import React, { useMemo } from 'react'
import { Loading } from 'vtex.render-runtime'
import { useCssHandles } from 'vtex.css-handles'

import { useItemContext } from './ItemContext'
import { opaque } from './utils/opaque'
import { IdentifierType } from './constants/Identifiers'

const CSS_HANDLES = [
  'productIdentifier',
  'productIdentifierValue',
  'productIdentifierLabelValue',
] as const

interface Props {
  identifierLabel?: string
  identifierOption?: string
}

const ProductReference: FunctionComponent<Props> = (props) => {
  const { identifierOption, identifierLabel } = props
  const { item, loading } = useItemContext()
  const handles = useCssHandles(CSS_HANDLES)

  const identifierValue = useMemo(() => {
    if (identifierOption === IdentifierType.PRODUCT_REFERENCE_ID)
      return item.productRefId
    if (identifierOption === IdentifierType.PRODUCT_SKU_REFERENCE_ID)
      return item.refId
    if (identifierOption === IdentifierType.PRODUCT_SKU_ITEM_ID) return item.id
    if (identifierOption === IdentifierType.PRODUCT_ID) return item.productId

    return item.productRefId
  }, [item.productRefId, identifierOption, item.id, item.productId, item.refId])

  if (loading) return <Loading />
  if (!identifierValue) return null

  return (
    <div
      className={`c-on-base t-title lh-copy fw6 fw5-m ${
        handles.productIdentifier
      } ${opaque(item.availability)}`}
    >
      {identifierLabel && (
        <span className={`${handles.productIdentifierLabelValue}`}>
          {identifierLabel}
        </span>
      )}
      <span className={`${handles.productIdentifierValue}`}>
        {identifierValue}
      </span>
    </div>
  )
}

export default ProductReference

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/store/blocks.json from the product-list VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "product-list": {
    "blocks": [
      "product-list-content-desktop",
      "product-list-content-mobile"
    ]
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/store/interfaces.json from the product-list VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "product-list": {
    "component": "ProductListWrapper",
    "allowed": ["product-list-content-desktop", "product-list-content-mobile"]
  },
  "product-list-content-desktop": {
    "component": "ProductList",
    "composition": "children",
    "allowed": "*"
  },
  "product-list-content-mobile": {
    "component": "ProductList",
    "composition": "children",
    "allowed": "*"
  },
  "product-list-image": {
    "component": "Image",
    "preview": {
      "type": "box",
      "height": 96,
      "width": 96,
      "options": {
        "padding": 0,
        "minWidth": 96
      }
    }
  },
  "message": {
    "component": "AvailabilityMessage"
  },
  "product-name": {
    "component": "ProductName",
    "preview": {
      "type": "box",
      "height": 30,
      "width": 140,
      "options": {
        "padding": 0
      }
    }
  },
  "product-reference": {
    "component": "ProductReference"
  },
  "product-brand": {
    "component": "ProductBrand",
    "preview": {
      "type": "box",
      "height": 15,
      "width": 110,
      "options": {
        "padding": 0
      }
    }
  },
  "product-variations": {
    "component": "ProductVariations"
  },
  "product-quantity-label": {
    "component": "ProductQuantityLabel"
  },
  "quantity-selector": {
    "component": "QuantitySelector",
    "preview": {
      "type": "box",
      "height": 35,
      "width": 80,
      "options": {
        "padding": 0
      }
    }
  },
  "unit-price": {
    "component": "UnitPrice"
  },
  "price": {
    "component": "Price",
    "preview": {
      "type": "box",
      "height": 35,
      "width": 90,
      "options": {
        "padding": 0
      }
    }
  },
  "remove-button": {
    "component": "RemoveButton",
    "preview": {
      "type": "box",
      "height": 25,
      "width": 25,
      "options": {
        "padding": 0
      }
    }
  },
  "manual-pricing": {
    "component": "ManualPrice"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/__fixtures__/items.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Item } from 'vtex.checkout-graphql'

export const items: Array<Item & { index: number }> = [
  {
    index: 0,
    additionalInfo: {
      brandName: 'Test Brand 0',
    },
    availability: 'available',
    id: '1',
    detailUrl: '/work-shirt/p',
    listPrice: 2800000,
    measurementUnit: 'un',
    name: 'قميص العمل الأعلى',
    price: 2400000,
    productId: '1',
    quantity: 3,
    sellingPrice: 2400000,
    skuName: 'Test SKU 0',
    skuSpecifications: [],
    uniqueId: 'SomeUniqueId0',
    attachmentOfferings: [],
    priceTags: [],
    bundleItems: [],
    offerings: [],
    attachments: [],
  },
  {
    index: 1,
    additionalInfo: {
      brandName: 'Test Brand 1',
    },
    availability: 'withoutStock',
    id: '30',
    detailUrl: '/long-sleeve-shirt/p',
    listPrice: 945000,
    measurementUnit: 'un',
    name: '上品なサングラス',
    price: 945000,
    productId: '2000005',
    quantity: 1,
    sellingPrice: 945000,
    skuName: 'Test SKU 1',
    skuSpecifications: [],
    uniqueId: 'SomeUniqueId1',
    attachmentOfferings: [],
    priceTags: [],
    bundleItems: [],
    offerings: [],
    attachments: [],
  },
  {
    index: 2,
    additionalInfo: {
      brandName: 'Test Brand 2',
    },
    availability: 'cannotBeDelivered',
    id: '2000535',
    detailUrl: '/classy--sunglasses/p',
    listPrice: 400000,
    measurementUnit: 'un',
    name: 'กางเกงขาสั้น St Tropez',
    price: 360000,
    productId: '13',
    quantity: 4,
    sellingPrice: 360000,
    skuName: 'Test SKU 2',
    skuSpecifications: [],
    uniqueId: 'SomeUniqueId2',
    attachmentOfferings: [],
    priceTags: [],
    bundleItems: [],
    offerings: [],
    attachments: [],
  },
]

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/constants/Availability.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const AVAILABLE = 'available'
export const CANNOT_BE_DELIVERED = 'cannotBeDelivered'
export const WITHOUT_STOCK = 'withoutStock'

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/constants/Identifiers.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const enum IdentifierType {
  PRODUCT_ID = 'ProductId',
  PRODUCT_SKU_ITEM_ID = 'ProductSkuItemId',
  PRODUCT_REFERENCE_ID = 'ProductReferenceId',
  PRODUCT_SKU_REFERENCE_ID = 'ProductSkuReferenceId',
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/constants/User.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const CALL_CENTER_OPERATOR = 'CALL_CENTER_OPERATOR'

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/typings/react.d.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
import 'react'

declare module 'react' {
  export interface ImgHTMLAttributes<T> extends HTMLAttributes<T> {
    loading?: 'auto' | 'eager' | 'lazy'
  }

  // eslint-disable-next-line @typescript-eslint/ban-types
  export interface FunctionComponent<P = {}> extends FunctionComponent<P> {
    schema?: Record<string, unknown>
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/typings/css.d.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const css: any
  export default css
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/typings/graphql.d.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.graphql' {
  import type { DocumentNode } from 'graphql'

  const value: DocumentNode
  export default value
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/typings/vtex.styleguide.d.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { ComponentType, Context } from 'react'

declare module 'vtex.styleguide' {
  interface InputProps {
    [key: string]: any
  }

  export const Button: ComponentType<any>
  export const Dropdown: ComponentType<any>
  export const IconDelete: ComponentType<any>
  export const Input: ComponentType<InputProps>
  export const InputCurrency: ComponentType<any>
  export const Link: ComponentType<any>
  export const Modal: ComponentType<any>
  export const Spinner: ComponentType<any>
  export const Tag: ComponentType<any>
  export const ToastContext: Context<{ showToast(message: string): void }>
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/utils/price.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const getFormattedPrice = (price: number | undefined | null) => {
  return price != null ? price / 100 : price
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/utils/textAlign.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface TextAlignProp {
  textAlign: 'left' | 'center' | 'right'
}

export const parseTextAlign = (prop: string) => {
  switch (prop) {
    case 'left':
      return 'tl'

    case 'center':
      return 'tc'

    case 'right':
      return 'tr'

    default:
      return ''
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/utils/opaque.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { WITHOUT_STOCK } from '../constants/Availability'

export const opaque = (availability?: string | null) =>
  availability === WITHOUT_STOCK ? 'o-70' : ''

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/useQuantitySelectorState.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { useContext, useCallback } from 'react'
import { ToastContext } from 'vtex.styleguide'
import { useIntl } from 'react-intl'

import { parseValue, parseDisplayValue } from './utils'

const useQuantitySelectorState = ({
  maxValue,
  measurementUnit,
  minValue: propMinValue = 0,
}: {
  maxValue?: number
  minValue?: number
  measurementUnit?: string
}) => {
  const { showToast } = useContext(ToastContext)
  const intl = useIntl()

  const onChange = useCallback(
    ({
      value,
      unitMultiplier,
      displayUnitMultiplier = unitMultiplier,
      minValue = propMinValue,
    }: {
      value: string
      unitMultiplier: number
      displayUnitMultiplier?: number
      minValue?: number
    }) => {
      const validatedValue = parseValue({
        value,
        maxValue,
        unitMultiplier,
        minValue,
      })

      const rawParsedValue = parseValue({
        value,
        maxValue,
        unitMultiplier,
        minValue: 0,
        round: false,
      })

      const validatedDisplayValue = intl.formatNumber(
        parseDisplayValue({
          value: validatedValue.toString(),
          maxValue,
          unitMultiplier: displayUnitMultiplier,
        }),
        { useGrouping: false }
      )

      if (validatedValue !== rawParsedValue) {
        showToast(
          intl.formatMessage(
            {
              id: 'store/product-list.quantityUnitMultiplierMismatch',
            },
            {
              unitMultiplier: intl.formatNumber(unitMultiplier),
              measurementUnit,
              roundedValue: intl.formatNumber(validatedValue * unitMultiplier),
            }
          )
        )
      }

      return { validatedValue, validatedDisplayValue }
    },
    [intl, maxValue, propMinValue, measurementUnit, showToast]
  )

  return [onChange] as const
}

export default useQuantitySelectorState

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/IncreaseIcon.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { VFC } from 'react'
import React from 'react'

const IncreaseIcon: VFC = () => {
  return (
    <svg
      width="16"
      height="16"
      viewBox="0 0 16 16"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d="M14.6666 6.88905H9.11103V1.3335H6.88881V6.88905H1.33325V9.11127H6.88881V14.6668H9.11103V9.11127H14.6666V6.88905Z"
        fill="currentColor"
      />
    </svg>
  )
}

export default IncreaseIcon

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/utils.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const parseValue = ({
  value,
  maxValue,
  minValue,
  unitMultiplier,
  round = true,
}: {
  value: string
  maxValue?: number
  minValue: number
  unitMultiplier: number
  round?: boolean
}) => {
  const parsedValue = parseFloat(value.replace(',', '.'))

  if (Number.isNaN(parsedValue)) {
    return 1
  }

  const roundedValue = Math.max(
    (round ? Math.round : (n: number) => n)(parsedValue / unitMultiplier),
    minValue
  )

  return Math.min(roundedValue, maxValue ?? roundedValue)
}

export const parseDisplayValue = ({
  value,
  maxValue,
  unitMultiplier,
}: {
  value: string
  maxValue?: number
  unitMultiplier: number
}) => {
  const parsedValue = parseFloat(value.replace(',', '.'))

  if (Number.isNaN(parsedValue) || parsedValue < 0) {
    return unitMultiplier
  }

  return Math.min(parsedValue, maxValue ?? parsedValue) * unitMultiplier
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/QuantitySelector.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ComponentProps, FC } from 'react'
import React, { useState, Fragment, useEffect } from 'react'
import type { IntlShape } from 'react-intl'
import { defineMessages, useIntl } from 'react-intl'
import { Dropdown, Input } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import { parseDisplayValue } from './utils'
import useQuantitySelectorState from './useQuantitySelectorState'

const range = (startValue: number, endNumber: number) => {
  const array = []

  for (let i = startValue; i < endNumber; i++) {
    array.push(i)
  }

  return array
}

const messages = defineMessages({
  remove: {
    defaultMessage: '',
    id: 'store/product-list.quantity-selector.remove',
  },
  label: { id: 'store/product-list.quantitySelector.label' },
  maxValueLabel: { id: 'store/product-list.quantitySelector.maxValueLabel' },
})

const MAX_DROPDOWN_VALUE = 10
const MAX_INPUT_LENGTH = 5

/* eslint-disable @typescript-eslint/no-shadow */
const enum SelectorType {
  Dropdown = 'Dropdown',
  Input = 'Input',
}
/* eslint-enable @typescript-eslint/no-shadow */

interface Props {
  id?: string
  value: number
  maxValue: number
  onChange: (value: number) => void
  disabled?: boolean
  unitMultiplier?: number
  measurementUnit?: string
}

const getDropdownOptions = ({
  maxValue,
  intl,
  unitMultiplier,
  measurementUnit,
}: {
  maxValue: number
  intl: IntlShape
  unitMultiplier: number
  measurementUnit?: string
}) => {
  const limit = Math.min(9, maxValue)
  const options = [
    { value: 0, label: `0 - ${intl.formatMessage(messages.remove)}` },
    ...range(1, limit + 1).map((idx) => ({
      value: idx,
      label: intl.formatMessage(messages.label, {
        quantity: intl.formatNumber(idx * unitMultiplier),
        measurementUnit,
      }),
    })),
  ]

  if (maxValue >= MAX_DROPDOWN_VALUE) {
    options.push({
      value: MAX_DROPDOWN_VALUE,
      label: intl.formatMessage(messages.maxValueLabel, {
        quantity: intl.formatNumber(MAX_DROPDOWN_VALUE * unitMultiplier),
        measurementUnit,
      }),
    })
  }

  return options
}

const CSS_HANDLES = [
  'quantityDropdownMobileContainer',
  'quantityDropdownContainer',
  'quantityInputMobileContainer',
  'quantityInputContainer',
] as const

const QuantitySelector: FC<Props> = ({
  id,
  value,
  maxValue,
  onChange,
  disabled = false,
  unitMultiplier = 1,
  measurementUnit: itemMeasurementUnit,
}) => {
  const measurementUnit =
    itemMeasurementUnit && itemMeasurementUnit !== 'un'
      ? itemMeasurementUnit
      : undefined

  const intl = useIntl()

  const [curSelector, setSelector] = useState(
    value < MAX_DROPDOWN_VALUE ? SelectorType.Dropdown : SelectorType.Input
  )

  const [inputFocused, setInputFocused] = useState(false)
  const [getUpdatedValue] = useQuantitySelectorState({
    maxValue,
    measurementUnit,
    minValue: 1,
  })

  const normalizedValue = Math.min(value, maxValue)

  const [curDisplayValue, setDisplayValue] = useState(
    intl.formatNumber(
      parseDisplayValue({
        value: normalizedValue.toString(),
        maxValue,
        unitMultiplier,
      }),
      { useGrouping: false }
    )
  )

  const handles = useCssHandles(CSS_HANDLES)

  const handleDropdownChange: ComponentProps<'select'>['onChange'] = (
    event
  ) => {
    const { validatedValue, validatedDisplayValue } = getUpdatedValue({
      value: event.target.value,
      unitMultiplier: 1,
      displayUnitMultiplier: unitMultiplier,
      minValue: 0,
    })

    if (validatedValue >= MAX_DROPDOWN_VALUE) {
      setSelector(SelectorType.Input)
    }

    setDisplayValue(validatedDisplayValue)
    onChange(validatedValue)
  }

  const handleInputChange: ComponentProps<'input'>['onChange'] = (event) => {
    setDisplayValue(event.target.value)
  }

  const handleInputBlur = () => {
    setInputFocused(false)

    if (curDisplayValue === '') {
      setDisplayValue(
        intl.formatNumber(
          parseDisplayValue({ value: '1', maxValue, unitMultiplier }),
          {
            useGrouping: false,
          }
        )
      )
    }

    const { validatedValue, validatedDisplayValue } = getUpdatedValue({
      value: curDisplayValue,
      unitMultiplier,
    })

    onChange(validatedValue)

    if (validatedValue < MAX_DROPDOWN_VALUE) {
      setSelector(SelectorType.Dropdown)
    }

    setDisplayValue(validatedDisplayValue)
  }

  const handleInputFocus = () => {
    setInputFocused(true)
  }

  useEffect(() => {
    if (inputFocused) {
      return
    }

    if (normalizedValue >= MAX_DROPDOWN_VALUE) {
      setSelector(SelectorType.Input)
    }

    setDisplayValue(
      intl.formatNumber(
        parseDisplayValue({
          value: normalizedValue.toString(),
          maxValue,
          unitMultiplier,
        }),
        { useGrouping: false }
      )
    )
  }, [
    inputFocused,
    maxValue,
    measurementUnit,
    unitMultiplier,
    normalizedValue,
    intl,
  ])

  if (curSelector === SelectorType.Dropdown) {
    const dropdownOptions = getDropdownOptions({
      maxValue,
      intl,
      unitMultiplier,
      measurementUnit,
    })

    return (
      <Fragment>
        <div className={`${handles.quantityDropdownMobileContainer} dn-m`}>
          <Dropdown
            id={`quantity-dropdown-mobile-${id}`}
            testId={`quantity-dropdown-mobile-${id}`}
            options={dropdownOptions}
            size="small"
            value={normalizedValue}
            onChange={handleDropdownChange}
            placeholder=" "
            disabled={disabled}
          />
        </div>
        <div className={`${handles.quantityDropdownContainer} dn db-m`}>
          <Dropdown
            id={`quantity-dropdown-${id}`}
            testId={`quantity-dropdown-${id}`}
            options={dropdownOptions}
            value={normalizedValue}
            onChange={handleDropdownChange}
            placeholder=" "
            disabled={disabled}
          />
        </div>
      </Fragment>
    )
  }

  return (
    <Fragment>
      <div className={`${handles.quantityInputMobileContainer} dn-m`}>
        <Input
          id={`quantity-input-mobile-${id}`}
          size="small"
          value={curDisplayValue}
          maxLength={MAX_INPUT_LENGTH}
          onChange={handleInputChange}
          onBlur={handleInputBlur}
          onFocus={handleInputFocus}
          placeholder=""
          disabled={disabled}
          suffix={measurementUnit}
        />
      </div>
      <div className={`${handles.quantityInputContainer} dn db-m`}>
        <Input
          id={`quantity-input-${id}`}
          value={curDisplayValue}
          maxLength={MAX_INPUT_LENGTH}
          onChange={handleInputChange}
          onBlur={handleInputBlur}
          onFocus={handleInputFocus}
          placeholder=""
          disabled={disabled}
          suffix={measurementUnit}
        />
      </div>
    </Fragment>
  )
}

export default QuantitySelector

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/NoImageIcon.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { memo } from 'react'

export const NoImageIcon = memo(function NoImageIcon() {
  return (
    <svg
      width="96"
      height="96"
      viewBox="0 0 96 96"
      fill="#F2F4F5"
      xmlns="http://www.w3.org/2000/svg"
    >
      <rect width="96" height="96" rx="4" fill="#F2F4F5" />
      <path
        d="M52.9312 47.3125C52.9622 47.5395 53.0002 47.7645 53.0002 48.0005C53.0002 50.7615 50.7612 53.0005 48.0002 53.0005C47.7642 53.0005 47.5392 52.9625 47.3122 52.9315L44.2422 56.0005H58.0002C58.5532 56.0005 59.0002 55.5525 59.0002 55.0005V42.0005C59.0002 41.7875 58.9182 41.6005 58.8052 41.4385L52.9312 47.3125Z"
        fill="#979899"
      />
      <path
        d="M38 56H40L44.464 51.536C43.56 50.631 43 49.381 43 48C43 45.239 45.239 43 48 43C49.381 43 50.631 43.56 51.536 44.464L55 41H53.535L51.832 38.445C51.646 38.167 51.334 38 51 38H45C44.666 38 44.354 38.167 44.168 38.445L42.465 41H38C37.447 41 37 41.448 37 42V55C37 55.552 37.447 56 38 56Z"
        fill="#979899"
      />
      <path
        d="M37.0002 60.0002C36.7442 60.0002 36.4882 59.9023 36.2932 59.7073C35.9022 59.3162 35.9022 58.6842 36.2932 58.2932L58.2932 36.2932C58.6842 35.9022 59.3162 35.9022 59.7073 36.2932C60.0983 36.6842 60.0983 37.3162 59.7073 37.7072L37.7072 59.7073C37.5122 59.9023 37.2563 60.0002 37.0002 60.0002Z"
        fill="#979899"
      />
    </svg>
  )
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/VisuallyHidden.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FC } from 'react'
import React from 'react'

import styles from './VisuallyHidden.css'

const VisuallyHidden: FC = ({ children }) => {
  return <div className={styles.visuallyHidden}>{children}</div>
}

export default VisuallyHidden

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/DecreaseIcon.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { VFC } from 'react'
import React from 'react'

const DecreaseIcon: VFC = () => {
  return (
    <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M1.333 9.334h13.334V7.11H1.333v2.223z" fill="currentColor" />
    </svg>
  )
}

export default DecreaseIcon

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/VisuallyHidden.css from the product-list VTEX app.

[Assistant]
Here's the implementation:

```css
.visuallyHidden {
  /**
   * Visually hidden styles, equivalent to
   * the `sr-only` class of TailwindCSS: https://tailwindcss.com/docs/screen-readers#class-reference
   */
  border: 0;
  clip: rect(0, 0, 0, 0);
  clip: rect(0 0 0 0);
  width: 2px;
  height: 2px;
  margin: -2px;
  overflow: hidden;
  padding: 0;
  position: absolute;
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/QuantityStepper.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { VFC } from 'react'
import React, { useState, useEffect } from 'react'
import { FormattedMessage, useIntl, defineMessages } from 'react-intl'
import classnames from 'classnames'
import { useCssHandles } from 'vtex.css-handles'

import VisuallyHidden from './VisuallyHidden'
import IncreaseIcon from './IncreaseIcon'
import DecreaseIcon from './DecreaseIcon'
import { parseDisplayValue } from './utils'
import useQuantitySelectorState from './useQuantitySelectorState'
import styles from './QuantityStepper.css'

const CSS_HANDLES = [
  'quantitySelectorWrapper',
  'quantitySelectorButton',
  'quantitySelectorDecrease',
  'quantitySelectorIncrease',
] as const

const messages = defineMessages({
  increaseQuantity: {
    id: 'store/product-list.quantityStepper.increaseQuantity',
  },
  decreaseQuantity: {
    id: 'store/product-list.quantityStepper.decreaseQuantity',
  },
})

interface Props {
  id?: string
  value?: number
  maxValue?: number
  onChange?: (value: number) => void
  disabled?: boolean
  unitMultiplier?: number
  measurementUnit?: string
}

const QuantityStepper: VFC<Props> = ({
  id,
  value = 1,
  maxValue,
  onChange,
  disabled,
  unitMultiplier = 1,
  measurementUnit = 'un',
}) => {
  const intl = useIntl()

  const normalizedValue = Math.min(value, maxValue ?? value)

  const [getUpdatedValue] = useQuantitySelectorState({
    maxValue,
    measurementUnit,
    minValue: 1,
  })

  const handles = useCssHandles(CSS_HANDLES)

  const [focused, setFocused] = useState(false)

  const [currentValue, setCurrentValue] = useState(
    intl.formatNumber(
      parseDisplayValue({
        value: normalizedValue.toString(),
        maxValue,
        unitMultiplier,
      }),
      { useGrouping: false }
    )
  )

  const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = (
    evt
  ) => {
    setCurrentValue(evt.target.value)
  }

  const handleInputBlur = () => {
    const { validatedValue, validatedDisplayValue } = getUpdatedValue({
      value: currentValue,
      unitMultiplier,
    })

    onChange?.(validatedValue)

    setCurrentValue(validatedDisplayValue)
    setFocused(false)
  }

  const handleDecrease: React.MouseEventHandler = (evt) => {
    evt.preventDefault()

    const parsedValue = parseDisplayValue({
      value: normalizedValue.toString(),
      maxValue,
      unitMultiplier,
    })

    const { validatedValue, validatedDisplayValue } = getUpdatedValue({
      value: (parsedValue - unitMultiplier).toString(),
      unitMultiplier,
      minValue: 0,
    })

    onChange?.(validatedValue)
    setCurrentValue(validatedDisplayValue)
  }

  const handleIncrease: React.MouseEventHandler = (evt) => {
    evt.preventDefault()

    const parsedValue = parseDisplayValue({
      value: normalizedValue.toString(),
      maxValue,
      unitMultiplier,
    })

    const { validatedValue, validatedDisplayValue } = getUpdatedValue({
      value: (parsedValue + unitMultiplier).toString(),
      unitMultiplier,
    })

    onChange?.(validatedValue)
    setCurrentValue(validatedDisplayValue)
  }

  useEffect(() => {
    if (focused) {
      return
    }

    setCurrentValue(
      intl.formatNumber(
        parseDisplayValue({
          value: normalizedValue.toString(),
          maxValue,
          unitMultiplier,
        }),
        { useGrouping: false }
      )
    )
  }, [focused, intl, normalizedValue, maxValue, unitMultiplier])

  const uniqueId = id ? `product-list-quantity-stepper-${id}` : undefined

  return (
    <div className={`flex ${handles.quantitySelectorWrapper}`}>
      <button
        className={classnames(
          handles.quantitySelectorButton,
          handles.quantitySelectorDecrease,
          'pa4 ba br2 br--left flex items-center justify-center',
          {
            'c-muted-1 b--muted-4 hover-b--muted-3 bg-muted-5 hover-bg-muted-4 pointer': !disabled,
            'bg-muted-5 c-muted-3 b--muted-4': disabled,
          }
        )}
        aria-label={intl.formatMessage(messages.decreaseQuantity)}
        disabled={disabled}
        onClick={handleDecrease}
      >
        <DecreaseIcon />
      </button>
      <VisuallyHidden>
        <label htmlFor={uniqueId}>
          <FormattedMessage id="store/product-list.quantityStepper.label" />
        </label>
      </VisuallyHidden>
      <div
        className={classnames(
          styles.inputContainer,
          'flex-auto flex-shrink-0 flex items-center pv1 bt bb ph5',
          {
            'c-muted-3 b--muted-4': disabled,
            'b--muted-4 hover-b--muted-3': !disabled,
          }
        )}
      >
        <input
          className={classnames('flex-auto h-100 bn bg-transparent', {
            tc: measurementUnit === 'un',
            tr: measurementUnit !== 'un',
          })}
          id={uniqueId}
          disabled={disabled}
          value={currentValue}
          onChange={handleInputChange}
          onFocus={() => setFocused(true)}
          onBlur={handleInputBlur}
        />
        {measurementUnit !== 'un' && (
          <span className="c-muted-1 ml3">{measurementUnit}</span>
        )}
      </div>
      <button
        className={classnames(
          handles.quantitySelectorButton,
          handles.quantitySelectorIncrease,
          'pa4 ba br2 br--right flex items-center justify-center',
          {
            'c-action-primary b--muted-4 hover-b--muted-3 bg-base hover-bg-muted-5 pointer': !disabled,
            'bg-muted-5 c-muted-3 b--muted-4': disabled,
          }
        )}
        aria-label={intl.formatMessage(messages.increaseQuantity)}
        disabled={disabled}
        onClick={handleIncrease}
      >
        <IncreaseIcon />
      </button>
    </div>
  )
}

export default QuantityStepper

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/__tests__/ProductList.test.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent } from 'react'
import React from 'react'
import { render, fireEvent, screen } from '@vtex/test-tools/react'
import { useRuntime } from 'vtex.render-runtime'

import { items } from '../__fixtures__/items'
import AvailabilityMessage from '../AvailabilityMessage'
import Image from '../Image'
import Price from '../Price'
import ProductBrand from '../ProductBrand'
import ProductList from '../ProductList'
import ProductName from '../ProductName'
import ProductVariations from '../ProductVariations'
import QuantitySelector from '../QuantitySelector'
import RemoveButton from '../RemoveButton'
import UnitPrice from '../UnitPrice'

jest.mock('../LazyRender', () => ({ children } = { children: undefined }) =>
  children
)

const ListItem: FunctionComponent = () => (
  <div>
    <AvailabilityMessage layout="cols" />
    <Image />
    <Price textAlign="left" />
    <ProductBrand />
    <ProductName />
    <ProductVariations />
    <QuantitySelector />
    <RemoveButton />
    <UnitPrice textAlign="left" />
  </div>
)

describe('Product List', () => {
  it('should display the list of products', () => {
    const { queryByText } = render(
      <ProductList
        items={items}
        loading={false}
        onQuantityChange={() => {}}
        onRemove={() => {}}
      >
        <ListItem />
      </ProductList>
    )

    expect(queryByText(items[0].name as string)).toBeTruthy()
    expect(queryByText(items[1].name as string)).toBeTruthy()
    expect(queryByText(items[2].name as string)).toBeTruthy()
  })

  it('should call onRemove when remove button is clicked', () => {
    const mockHandleRemove = jest.fn((_: string) => {})

    const { getByTitle } = render(
      <ProductList
        items={[items[2]]}
        loading={false}
        onQuantityChange={() => {}}
        onRemove={mockHandleRemove}
      >
        <ListItem />
      </ProductList>
    )

    const removeButton = getByTitle('remove')

    fireEvent.click(removeButton)

    expect(mockHandleRemove).toHaveBeenCalledTimes(1)
    expect(mockHandleRemove.mock.calls[0][0]).toBe(items[2].uniqueId)
  })

  it('should display unavailable items separately', () => {
    const { getByText } = render(
      <ProductList
        items={items}
        loading={false}
        onQuantityChange={() => {}}
        onRemove={() => {}}
      >
        <ListItem />
      </ProductList>
    )

    expect(getByText('2 unavailable products')).toBeTruthy()
    expect(getByText('1 available product')).toBeTruthy()
  })

  it('should display a message when item is out of stock', () => {
    const { queryByText } = render(
      <ProductList
        items={[items[1]]}
        loading={false}
        onQuantityChange={() => {}}
        onRemove={() => {}}
      >
        <ListItem />
      </ProductList>
    )

    expect(queryByText(/no longer available/)).toBeTruthy()
  })

  it('should display a message when item cannot be delivered', () => {
    const { queryByText } = render(
      <ProductList
        items={[items[2]]}
        loading={false}
        onQuantityChange={() => {}}
        onRemove={() => {}}
      >
        <ListItem />
      </ProductList>
    )

    expect(queryByText(/cannot be delivered/)).toBeTruthy()
  })

  it('should render the product detailUrl with correct rootPath', () => {
    const { unmount } = render(
      <ProductList
        items={[items[0]]}
        loading={false}
        onQuantityChange={() => {}}
        onRemove={() => {}}
      >
        <ListItem />
      </ProductList>
    )

    let [productImageLink, productNameLink] = screen.getAllByRole('link')

    expect(productImageLink).toHaveAttribute('href', '/work-shirt/p')
    expect(productImageLink).toHaveClass('productImageAnchor')
    expect(productNameLink).toHaveAttribute('href', '/work-shirt/p')
    expect(productNameLink).toHaveClass('productName')

    const mockedUseRuntime = useRuntime as jest.MockedFunction<
      () => ReturnType<typeof useRuntime>
    >

    // @ts-expect-error: we are not mocking everything on purpose
    mockedUseRuntime.mockReturnValue({
      rootPath: '/pt-br',
    })

    unmount()

    render(
      <ProductList
        items={[items[0]]}
        loading={false}
        onQuantityChange={() => {}}
        onRemove={() => {}}
      >
        <ListItem />
      </ProductList>
    )
    ;[productImageLink, productNameLink] = screen.getAllByRole('link')

    expect(productImageLink).toHaveAttribute('href', '/pt-br/work-shirt/p')
    expect(productImageLink).toHaveClass('productImageAnchor')
    expect(productNameLink).toHaveAttribute('href', '/pt-br/work-shirt/p')
    expect(productNameLink).toHaveClass('productName')
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/typings/@vtex/styleguide.d.ts from the product-list VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '@vtex/styleguide/lib/Button'
declare module '@vtex/styleguide/lib/Dropdown'
declare module '@vtex/styleguide/lib/Input'
declare module '@vtex/styleguide/lib/Link'
declare module '@vtex/styleguide/lib/Spinner'
declare module '@vtex/styleguide/lib/icon/Delete'
declare module '@vtex/styleguide/lib/ToastProvider' {
  export const ToastContext: Context<{ showToast(message: string): void }>
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/__tests__/QuantitySelector.test.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { render, screen, fireEvent } from '@vtex/test-tools/react'
import { ToastContext } from '@vtex/styleguide/lib/ToastProvider'

import QuantitySelector from '../QuantitySelector'

describe('<QuantitySelector />', () => {
  it('should change from dropdown to input after quantity exceeds max value', () => {
    const onChange = jest.fn()

    render(
      <QuantitySelector id="1" value={5} maxValue={50} onChange={onChange} />
    )

    const [dropdown] = screen.getAllByRole('combobox')

    expect(dropdown).toHaveValue('5')

    fireEvent.change(dropdown, { target: { value: '10' } })

    expect(onChange).toHaveBeenLastCalledWith(10)
    expect(dropdown).not.toBeInTheDocument()

    const [input] = screen.getAllByRole('textbox')

    expect(input).toHaveValue('10')
  })

  it('should change back to dropdown after changing the value to under max value', () => {
    const onChange = jest.fn()

    render(
      <QuantitySelector id="1" value={10} maxValue={50} onChange={onChange} />
    )

    const [input] = screen.getAllByRole('textbox')

    fireEvent.change(input, { target: { value: '5' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenLastCalledWith(5)
    expect(input).not.toBeInTheDocument()
  })

  it("shouldn't display default measurement unit value", () => {
    render(
      <QuantitySelector
        id="1"
        value={1}
        maxValue={50}
        onChange={jest.fn()}
        measurementUnit="un"
      />
    )

    const [dropdown] = screen.getAllByRole('combobox')

    expect(dropdown).toHaveDisplayValue('1')
  })

  it("shouldn't display raw quantity value when unit multiplier exists", () => {
    const onChange = jest.fn()

    render(
      <QuantitySelector
        id="1"
        value={1}
        maxValue={50}
        onChange={onChange}
        unitMultiplier={0.5}
        measurementUnit="kg"
      />
    )

    const [dropdown] = screen.getAllByRole('combobox')

    expect(dropdown).toHaveValue('1')
    expect(dropdown).toHaveDisplayValue('0.5 kg')

    let options = screen.getAllByRole('option')

    // Get the first half options, because we have two
    // dropdowns in the page
    options = options.slice(0, options.length / 2)

    expect(options).toHaveLength(12)

    expect(options[1]).toHaveTextContent('0 - Remove')
    expect(options[1]).toHaveValue('0')

    expect(options[11]).toHaveTextContent('5 kg +')
    expect(options[11]).toHaveValue('10')

    fireEvent.change(dropdown, { target: { value: '10' } })
    expect(onChange).toHaveBeenLastCalledWith(10)

    expect(dropdown).not.toBeInTheDocument()

    const [input] = screen.getAllByRole('textbox')

    expect(input).toHaveValue('5')

    fireEvent.change(input, { target: { value: '6' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenLastCalledWith(12)
  })

  it('can handle decimal values with both periods and commas with unit multiplier', () => {
    const onChange = jest.fn()
    const showToast = jest.fn()

    render(
      <ToastContext.Provider
        value={{ showToast, hideToast: jest.fn(), toastState: {} }}
      >
        <QuantitySelector
          id="1"
          value={10}
          maxValue={50}
          onChange={onChange}
          unitMultiplier={0.5}
          measurementUnit="kg"
        />
      </ToastContext.Provider>
    )

    const [input] = screen.getAllByRole('textbox')

    fireEvent.change(input, { target: { value: '5.5' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenLastCalledWith(11)

    fireEvent.change(input, { target: { value: '5.6' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenCalledTimes(2)
    expect(onChange).toHaveBeenLastCalledWith(11)

    expect(showToast).toHaveBeenCalledTimes(1)
    expect(showToast).toHaveBeenLastCalledWith(
      'This product is sold by fractions of 0.5kg. That is why the entered quantity has been rounded to 5.5kg.'
    )

    fireEvent.change(input, { target: { value: '6,5' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenLastCalledWith(13)
  })

  it('should not include grouping separators on large numbers', () => {
    const onChange = jest.fn()

    render(
      <QuantitySelector id="1" value={10} maxValue={1000} onChange={onChange} />
    )

    const [input] = screen.getAllByRole('textbox')

    fireEvent.change(input, { target: { value: '1000' } })
    fireEvent.blur(input)

    expect(input).toHaveValue('1000')
    expect(onChange).toHaveBeenLastCalledWith(1000)
  })

  it('should not remove the product when rounding down the value', () => {
    const onChange = jest.fn()

    render(
      <QuantitySelector id="1" value={10} maxValue={50} onChange={onChange} />
    )

    const [input] = screen.getAllByRole('textbox')

    fireEvent.change(input, { target: { value: '0.1' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenLastCalledWith(1)
  })

  it('should not show toast if typed number is the same', () => {
    const onChange = jest.fn()
    const showToast = jest.fn()

    render(
      <ToastContext.Provider
        value={{ showToast, hideToast: jest.fn(), toastState: {} }}
      >
        <QuantitySelector
          id="1"
          value={11}
          maxValue={50}
          onChange={onChange}
          unitMultiplier={0.5}
          measurementUnit="kg"
        />
      </ToastContext.Provider>
    )

    const [input] = screen.getAllByRole('textbox')

    fireEvent.change(input, { target: { value: '5,5' } })
    fireEvent.blur(input)

    expect(showToast).not.toHaveBeenCalled()
  })

  it('should show toast if number is rounded to below MAX_DROPDOWN_VALUE threshold', () => {
    const onChange = jest.fn()
    const showToast = jest.fn()

    render(
      <ToastContext.Provider
        value={{ showToast, hideToast: jest.fn(), toastState: {} }}
      >
        <QuantitySelector
          id="1"
          value={10}
          maxValue={50}
          onChange={onChange}
          unitMultiplier={0.5}
          measurementUnit="kg"
        />
      </ToastContext.Provider>
    )

    const [input] = screen.getAllByRole('textbox')

    fireEvent.change(input, { target: { value: '0.1' } })
    fireEvent.blur(input)

    expect(showToast).toHaveBeenCalled()
  })

  it('should remove item when changing quantity in dropdown to 0', () => {
    const onChange = jest.fn()
    const showToast = jest.fn()

    render(
      <ToastContext.Provider
        value={{ showToast, hideToast: jest.fn(), toastState: {} }}
      >
        <QuantitySelector id="1" value={1} maxValue={50} onChange={onChange} />
      </ToastContext.Provider>
    )

    const [input] = screen.getAllByRole('combobox')

    fireEvent.change(input, { target: { value: '0' } })

    expect(showToast).not.toHaveBeenCalled()
    expect(onChange).toHaveBeenCalledWith(0)
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/react/components/__tests__/QuantityStepper.test.tsx from the product-list VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { render, screen, fireEvent } from '@vtex/test-tools/react'
import { ToastContext } from '@vtex/styleguide/lib/ToastProvider'

import QuantityStepper from '../QuantityStepper'

describe('<QuantityStepper />', () => {
  it('should allow use to type the quantity', () => {
    const onChange = jest.fn()

    render(<QuantityStepper id="1" value={1} onChange={onChange} />)

    const input = screen.getByLabelText(/product quantity/i)

    expect(input).toHaveValue('1')

    fireEvent.change(input, { target: { value: '10' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenCalledWith(10)
    expect(input).toHaveValue('10')
  })

  it('should display toast when rounding values', () => {
    const onChange = jest.fn()
    const showToast = jest.fn()

    render(
      <ToastContext.Provider value={{ showToast }}>
        <QuantityStepper id="1" value={1} onChange={onChange} />
      </ToastContext.Provider>
    )

    const input = screen.getByLabelText(/product quantity/i)

    fireEvent.change(input, { target: { value: '1.9' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenCalledWith(2)
    expect(input).toHaveValue('2')
    expect(showToast).toHaveBeenCalledTimes(1)
    expect(showToast).toHaveBeenCalledWith(
      'This product is sold by fractions of 1un. That is why the entered quantity has been rounded to 2un.'
    )
  })

  it('should increase quantity by one when pressing the increase button', () => {
    const onChange = jest.fn()

    render(<QuantityStepper id="1" value={1} onChange={onChange} />)

    const increaseButton = screen.getByRole('button', {
      name: /Increase quantity/i,
    })

    fireEvent.click(increaseButton)

    expect(onChange).toHaveBeenCalledWith(2)

    const input = screen.getByLabelText(/product quantity/i)

    expect(input).toHaveValue('2')
  })

  it('should decrease quantity by one when pressing the decrease button', () => {
    const onChange = jest.fn()

    render(<QuantityStepper id="1" value={2} onChange={onChange} />)

    const decreaseButton = screen.getByRole('button', {
      name: /Decrease quantity/i,
    })

    fireEvent.click(decreaseButton)

    expect(onChange).toHaveBeenCalledWith(1)

    const input = screen.getByLabelText(/product quantity/i)

    expect(input).toHaveValue('1')
  })

  it('should update input value when value prop changes', () => {
    const { rerender } = render(<QuantityStepper id="1" value={1} />)

    const input = screen.getByLabelText(/product quantity/i)

    expect(input).toHaveValue('1')

    rerender(<QuantityStepper id="1" value={2} />)

    expect(input).toHaveValue('2')
  })

  it('should not update input value when input is focused', () => {
    const { rerender } = render(<QuantityStepper id="1" value={1} />)

    const input = screen.getByLabelText(/product quantity/i)

    expect(input).toHaveValue('1')

    fireEvent.focus(input)

    rerender(<QuantityStepper id="1" value={2} />)

    expect(input).toHaveValue('1')

    fireEvent.blur(input)

    expect(input).toHaveValue('2')
  })

  it('can handle unit multipliers changes in the input', () => {
    const onChange = jest.fn()
    const showToast = jest.fn()

    render(
      <ToastContext.Provider value={{ showToast }}>
        <QuantityStepper
          id="1"
          value={1}
          onChange={onChange}
          unitMultiplier={0.5}
        />
      </ToastContext.Provider>
    )

    const input = screen.getByLabelText(/product quantity/i)

    expect(input).toHaveValue('0.5')

    fireEvent.change(input, { target: { value: '1.4' } })
    fireEvent.blur(input)

    expect(input).toHaveValue('1.5')
    expect(onChange).toHaveBeenLastCalledWith(3)
    expect(showToast).toHaveBeenLastCalledWith(
      'This product is sold by fractions of 0.5un. That is why the entered quantity has been rounded to 1.5un.'
    )
  })

  it('can handle increments of unit multiplier in increase and decrease button', () => {
    const onChange = jest.fn()

    const { rerender } = render(
      <QuantityStepper
        id="1"
        value={1}
        onChange={onChange}
        unitMultiplier={0.5}
      />
    )

    const increaseButton = screen.getByRole('button', {
      name: /increase quantity/i,
    })

    const decreaseButton = screen.getByRole('button', {
      name: /decrease quantity/i,
    })

    const input = screen.getByLabelText(/product quantity/i)

    expect(input).toHaveValue('0.5')

    fireEvent.click(increaseButton)

    expect(input).toHaveValue('1')
    expect(onChange).toHaveBeenLastCalledWith(2)

    expect(onChange).toHaveBeenCalledTimes(1)

    rerender(
      <QuantityStepper
        id="1"
        value={2}
        onChange={onChange}
        unitMultiplier={0.5}
      />
    )

    fireEvent.click(decreaseButton)

    expect(input).toHaveValue('0.5')
    expect(onChange).toHaveBeenLastCalledWith(1)

    expect(onChange).toHaveBeenCalledTimes(2)
  })

  it('should display measurement unit alongside input', () => {
    render(<QuantityStepper id="1" value={1} measurementUnit="kg" />)

    expect(screen.getByText('kg')).toBeInTheDocument()
  })

  it('should remove item when decreasing quantity with button', () => {
    const onChange = jest.fn()

    render(<QuantityStepper id="1" value={1} onChange={onChange} />)

    const decreaseButton = screen.getByLabelText(/decrease quantity/i)

    fireEvent.click(decreaseButton)

    expect(onChange).toHaveBeenCalledWith(0)
  })

  it("shouldn't show toast when typed number isn't a number", () => {
    const showToast = jest.fn()
    const onChange = jest.fn()

    render(
      <ToastContext.Provider value={{ showToast }}>
        <QuantityStepper
          id="1"
          value={1}
          unitMultiplier={0.5}
          onChange={onChange}
        />
      </ToastContext.Provider>
    )

    const input = screen.getByLabelText(/product quantity/i)

    fireEvent.change(input, { target: { value: 'abc' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenCalledWith(1)
    expect(showToast).not.toHaveBeenCalled()
  })

  it("shouldn't remove product when quantity is rounded to minimum value", () => {
    const onChange = jest.fn()

    render(<QuantityStepper id="1" value={1} onChange={onChange} />)

    const input = screen.getByLabelText(/product quantity/i)

    fireEvent.change(input, { target: { value: '0.1' } })
    fireEvent.blur(input)

    expect(onChange).toHaveBeenCalledWith(1)
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/store/blocks/mobile.json from the product-list VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "product-list-content-mobile": {
    "children": ["flex-layout.row#list-row.mobile"]
  },
  "flex-layout.row#list-row.mobile": {
    "children": [
      "flex-layout.col#image.mobile",
      "flex-layout.col#main-container.mobile"
    ],
    "props": {
      "fullWidth": true,
      "paddingBottom": "6",
      "paddingTop": "5",
      "colSizing": "auto",
      "preserveLayoutOnMobile": "true"
    }
  },
  "flex-layout.col#image.mobile": {
    "children": ["product-list-image"],
    "props": {
      "marginRight": "5"
    }
  },
  "flex-layout.col#main-container.mobile": {
    "children": [
      "flex-layout.row#top.mobile",
      "flex-layout.row#quantity-selector.mobile",
      "flex-layout.row#unit-price.mobile",
      "flex-layout.row#price.mobile",
      "flex-layout.row#message.mobile"
    ],
    "props": {
      "width": "grow"
    }
  },
  "flex-layout.row#top.mobile": {
    "children": [
      "flex-layout.col#product-description",
      "flex-layout.col#remove-button.mobile"
    ],
    "props": {
      "colSizing": "auto",
      "preserveLayoutOnMobile": "true"
    }
  },
  "flex-layout.row#quantity-selector.mobile": {
    "children": ["quantity-selector"],
    "props": {
      "marginTop": "5",
      "preventHorizontalStretch": "true"
    }
  },
  "flex-layout.row#unit-price.mobile": {
    "children": ["unit-price"],
    "props": {
      "marginTop": "3"
    }
  },
  "flex-layout.row#price.mobile": {
    "children": ["price#mobile"],
    "props": {
      "marginTop": "5",
      "preventHorizontalStretch": "true"
    }
  },
  "price#mobile": {
    "props": {
      "textAlign": "left"
    }
  },
  "flex-layout.col#remove-button.mobile": {
    "children": ["remove-button"],
    "props": {
      "marginLeft": "3"
    }
  },
  "flex-layout.row#message.mobile": {
    "children": ["message#mobile"],
    "props": {
      "marginTop": "3"
    }
  },
  "message#mobile": {
    "props": {
      "layout": "rows"
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/store/blocks/common.jsonc from the product-list VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "flex-layout.col#product-description": {
    "children": [
      "flex-layout.row#product-brand",
      "flex-layout.row#product-name",
      "flex-layout.row#product-variations"
    ],
    "props": {
      "marginBottom": "5",
      "width": "grow",
      "preventVerticalStretch": "true"
    }
  },
  "flex-layout.row#product-brand": {
    "children": ["product-brand"],
    "props": {
      "marginBottom": "2"
    }
  },
  "flex-layout.row#product-name": {
    // Using app prefix here because it might conflict with
    // vtex.store-components:product-name
    "children": ["vtex.product-list:product-name"]
  },
  "flex-layout.row#product-variations": {
    "children": ["product-variations"],
    "props": {
      "marginTop": "2"
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of product-list/store/blocks/desktop.json from the product-list VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "product-list-content-desktop": {
    "children": ["flex-layout.row#list-row.desktop"]
  },
  "flex-layout.row#list-row.desktop": {
    "children": [
      "flex-layout.col#image.desktop",
      "flex-layout.col#main-container.desktop"
    ],
    "props": {
      "fullWidth": true,
      "paddingBottom": "7",
      "paddingTop": "6",
      "colSizing": "auto",
      "preserveLayoutOnMobile": "true"
    }
  },
  "flex-layout.col#image.desktop": {
    "children": ["product-list-image"],
    "props": {
      "marginRight": "6"
    }
  },
  "flex-layout.col#main-container.desktop": {
    "children": [
      "flex-layout.row#sub-container.desktop",
      "flex-layout.row#message.desktop"
    ],
    "props": {
      "width": "grow"
    }
  },
  "flex-layout.row#sub-container.desktop": {
    "children": [
      "flex-layout.col#product-description",
      "flex-layout.col#quantity.desktop",
      "flex-layout.row#price-remove"
    ],
    "props": {
      "preserveLayoutOnMobile": "true",
      "colSizing": "auto"
    }
  },
  "flex-layout.col#quantity.desktop": {
    "children": [
      "flex-layout.row#quantity-selector.desktop",
      "flex-layout.row#unit-price.desktop",
      "flex-layout.col#manual-pricing"
    ],
    "props": {
      "marginLeft": "8",
      "horizontalAlign": "center"
    }
  },
  "flex-layout.row#price-remove": {
    "children": [
      "flex-layout.col#price.desktop",
      "flex-layout.col#remove-button.desktop"
    ],
    "props": {
      "colSizing": "auto"
    }
  },
  "flex-layout.row#quantity-selector.desktop": {
    "children": ["quantity-selector"],
    "props": {
      "preventHorizontalStretch": "true"
    }
  },
  "flex-layout.row#unit-price.desktop": {
    "children": ["unit-price#desktop"],
    "props": {
      "marginTop": "3",
      "preventHorizontalStretch": "true"
    }
  },
  "unit-price#desktop": {
    "props": {
      "textAlign": "center"
    }
  },
  "flex-layout.col#price.desktop": {
    "children": ["price#desktop"],
    "props": {
      "blockClass": "priceWrapper",
      "marginLeft": "6",
      "preventHorizontalStretch": "true",
      "verticalAlign": "middle"
    }
  },
  "price#desktop": {
    "props": {
      "textAlign": "right"
    }
  },
  "flex-layout.col#remove-button.desktop": {
    "children": ["remove-button"],
    "props": {
      "marginLeft": "6",
      "verticalAlign": "middle"
    }
  },
  "flex-layout.row#message.desktop": {
    "children": ["message#desktop"],
    "props": {
      "marginTop": "4"
    }
  },
  "message#desktop": {
    "props": {
      "layout": "cols"
    }
  },
  "flex-layout.col#manual-pricing": {
    "children": ["manual-pricing"]
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of vtex-ads/messages/pt.json from the vtex-ads VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/vtex-vtex-ads.common.active.title": "Ativo",
  "admin/vtex-vtex-ads.common.placementNameAdmin.title": "Nome do placement",
  "admin/vtex-vtex-ads.common.quantityAdmin.title": "Quantidade de anúncios",
  "admin/vtex-vtex-ads.common.quantityAdmin.banner.description": "Insira um valor maior ou igual a 1. Por padrão, será solicitado 1 anúncio. Para desativar um placement, utilize a propriedade 'Ativo'.",
  "admin/vtex-vtex-ads.common.quantityAdmin.product.description": "Insira um valor maior ou igual a 1. Por padrão, serão solicitados 20 anúncios. Para desativar um placement, utilize a propriedade 'Ativo'.",
  "admin/vtex-vtex-ads.common.categoryNameAdmin.title": "Categoria (opcional)",
  "admin/vtex-vtex-ads.common.categoryNameAdmin.description": "[Avançado]  Use esse campo em páginas que não tem contexto e precise forçar alguma categoria para o banner ou em casos pontuais.",

  "admin/vtex-vtex-ads.shelf.title": "VTEX Ads - Shelf",
  "admin/vtex-vtex-ads.shelf.placementNameAdmin.description": "Por padrão, será usado 'placement_product_default'. Dê preferência ao nome cadastrado na plataforma de anúncios.",

  "admin/vtex-vtex-ads.sponsoredBrand.title": "VTEX Ads - Sponsored Brand",
  "admin/vtex-vtex-ads.sponsoredBrand.placementNameAdmin.description": "Caso não tenho um nome de placement cadastrado no tema ou no admin, será usado 'placement_sb_default' por padrão. Dê preferência ao nome cadastrado na plataforma de anúncios.",

  "admin/vtex-vtex-ads.banner.title": "VTEX Ads - Banner",
  "admin/vtex-vtex-ads.banner.placementNameAdmin.description": "Por padrão, será usado 'placement_banner_default'. Dê preferência ao nome cadastrado na plataforma de anúncios.",
  "admin/vtex-vtex-ads.banner.sizeAdmin.title": "Tamanho da mídia desktop",
  "admin/vtex-vtex-ads.banner.sizeAdmin.description": "Use o mesmo tamanho cadastrado na plataforma de anúncios. Por padrão, será usado o valor 'banner'.",
  "admin/vtex-vtex-ads.banner.sizeMobileAdmin.title": "Tamanho da mídia mobile (opcional)",
  "admin/vtex-vtex-ads.banner.sizeMobileAdmin.description": "Use o mesmo tamanho cadastrado na plataforma de anúncios. Se não for passado, o valor 'Tamanho da mídia desktop' será usado.",

  "admin/vtex-vtex-ads.search.title": "VTEX Ads - Busca",
  "admin/vtex-vtex-ads.search.placementNameAdmin.description": "Por padrão, será usado 'placement_search_default'. Dê preferência ao nome cadastrado na plataforma de anúncios.",
  "admin/vtex-vtex-ads.search.tagPosition.title": "Posição da tag",
  "admin/vtex-vtex-ads.search.tagPosition.description": "Indique onde a tag deverá ser exibida dentro do card do produto",
  "admin/vtex-vtex-ads.search.tagPosition.start": "Começo",
  "admin/vtex-vtex-ads.search.tagPosition.end": "Fim",
  "admin/vtex-vtex-ads.search.tagText.default": "Patrocinado",
  "admin/vtex-vtex-ads.search.tagText.title": "Texto da tag patrocinada",
  "admin/vtex-vtex-ads.search.tagText.description": "Por padrão, será usado 'patrocinado' com tradução automática. Para substituir, preencha este campo.",
  "admin/vtex-vtex-ads.search.tagClassname.title": "Classe da Tag",
  "admin/vtex-vtex-ads.search.tagClassname.description": "Por padrão, será usado '.newtail-sponsored-tag'.",
  "admin/vtex-vtex-ads.search.parentSearchSelector.title": "Elemento HTML para Inserção de Tags",
  "admin/vtex-vtex-ads.search.parentSearchSelector.description": "[Avançado] Este campo deve ser editado apenas por desenvolvedores. Por padrão, será usado '.vtex-search-result-3-x-searchResultContainer #gallery-layout-container'.",
  "admin/vtex-vtex-ads.search.onlyFirstSKU.title": "Usar só primeiro SKU. Se marcado, será usado apenas o primeiro SKU do produto para busca no Newtail. Se desmarcado, serão usados todos os SKUs do produto para busca no Newtail."
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of vtex-ads/messages/en.json from the vtex-ads VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/vtex-vtex-ads.common.active.title": "Active",
  "admin/vtex-vtex-ads.common.placementNameAdmin.title": "Placement Name",
  "admin/vtex-vtex-ads.common.quantityAdmin.title": "Quantity of Ads",
  "admin/vtex-vtex-ads.common.quantityAdmin.banner.description": "Enter a value greater than or equal to 1. By default, 1 ad will be requested. To disable a placement, use the 'Active' property.",
  "admin/vtex-vtex-ads.common.quantityAdmin.product.description": "Enter a value greater than or equal to 1. By default, 20 ads will be requested. To disable a placement, use the 'Active' property.",
  "admin/vtex-vtex-ads.common.categoryNameAdmin.title": "Category (optional)",
  "admin/vtex-vtex-ads.common.categoryNameAdmin.description": "[Advanced] Use this field on pages without context where a category needs to be forced for the banner or in specific cases.",

  "admin/vtex-vtex-ads.shelf.title": "VTEX Ads - Shelf",
  "admin/vtex-vtex-ads.shelf.placementNameAdmin.description": "By default, 'placement_product_default' will be used. Prefer the name registered in the ad platform.",

  "admin/vtex-vtex-ads.sponsoredBrand.title": "VTEX Ads - Sponsored Brand",
  "admin/vtex-vtex-ads.sponsoredBrand.placementNameAdmin.description": "If you don't have a placement name registered in the theme or admin, 'placement_sb_default' will be used by default. Prefer the name registered in the ad platform.",

  "admin/vtex-vtex-ads.banner.title": "VTEX Ads - Banner",
  "admin/vtex-vtex-ads.banner.placementNameAdmin.description": "By default, 'placement_banner_default' will be used. Prefer the name registered in the ad platform.",
  "admin/vtex-vtex-ads.banner.sizeAdmin.title": "Desktop Media Size",
  "admin/vtex-vtex-ads.banner.sizeAdmin.description": "Use the same size registered in the ad platform. By default, the value 'banner' will be used.",
  "admin/vtex-vtex-ads.banner.sizeMobileAdmin.title": "Mobile Media Size (optional)",
  "admin/vtex-vtex-ads.banner.sizeMobileAdmin.description": "Use the same size registered in the ad platform. If not provided, the 'Desktop Media Size' value will be used.",

  "admin/vtex-vtex-ads.search.title": "VTEX Ads - Search",
  "admin/vtex-vtex-ads.search.placementNameAdmin.description": "By default, 'placement_search_default' will be used. Prefer the name registered in the ad platform.",
  "admin/vtex-vtex-ads.search.tagPosition.title": "Tag Position",
  "admin/vtex-vtex-ads.search.tagPosition.description": "Indicate where the tag should be displayed within the product card",
  "admin/vtex-vtex-ads.search.tagPosition.start": "Start",
  "admin/vtex-vtex-ads.search.tagPosition.end": "End",
  "admin/vtex-vtex-ads.search.tagText.default": "Sponsored",
  "admin/vtex-vtex-ads.search.tagText.title": "Sponsored Tag Text",
  "admin/vtex-vtex-ads.search.tagText.description": "By default, 'sponsored' will be used with automatic translation. To replace it, fill in this field.",
  "admin/vtex-vtex-ads.search.tagClassname.title": "Tag Class",
  "admin/vtex-vtex-ads.search.tagClassname.description": "By default, '.newtail-sponsored-tag' will be used.",
  "admin/vtex-vtex-ads.search.parentSearchSelector.title": "HTML Element for Tag Insertion",
  "admin/vtex-vtex-ads.search.parentSearchSelector.description": "[Advanced] This field should only be edited by developers. By default, '.vtex-search-result-3-x-searchResultContainer #gallery-layout-container' will be used.",
  "admin/vtex-vtex-ads.search.onlyFirstSKU.title": "Use only the first SKU. If checked, only the first SKU of the product will be used for search in Newtail. If unchecked, all SKUs of the product will be used for search in Newtail."
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of vtex-ads/messages/es.json from the vtex-ads VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/vtex-vtex-ads.common.active.title": "Activo",
  "admin/vtex-vtex-ads.common.placementNameAdmin.title": "Nombre del placement",
  "admin/vtex-vtex-ads.common.quantityAdmin.title": "Cantidad de anuncios",
  "admin/vtex-vtex-ads.common.quantityAdmin.banner.description": "Ingrese un valor mayor o igual a 1. Por defecto, se solicitará 1 anuncio. Para desactivar un placement, utilice la propiedad 'Activo'.",
  "admin/vtex-vtex-ads.common.quantityAdmin.product.description": "Ingrese un valor mayor o igual a 1. Por defecto, se solicitarán 20 anuncios. Para desactivar un placement, utilice la propiedad 'Activo'.",
  "admin/vtex-vtex-ads.common.categoryNameAdmin.title": "Categoría (opcional)",
  "admin/vtex-vtex-ads.common.categoryNameAdmin.description": "[Avanzado] Use este campo en páginas que no tienen contexto y necesitan forzar alguna categoría para el banner o en casos puntuales.",

  "admin/vtex-vtex-ads.shelf.title": "VTEX Ads - Shelf",
  "admin/vtex-vtex-ads.shelf.placementNameAdmin.description": "Por defecto, se utilizará 'placement_product_default'. Prefiera el nombre registrado en la plataforma de anuncios.",

  "admin/vtex-vtex-ads.sponsoredBrand.title": "VTEX Ads - Sponsored Brand",
  "admin/vtex-vtex-ads.sponsoredBrand.placementNameAdmin.description": "Si no tiene un nombre de placement registrado en el tema o en el admin, se utilizará 'placement_sb_default' por defecto. Prefiera el nombre registrado en la plataforma de anuncios.",

  "admin/vtex-vtex-ads.banner.title": "VTEX Ads - Banner",
  "admin/vtex-vtex-ads.banner.placementNameAdmin.description": "Por defecto, se utilizará 'placement_banner_default'. Prefiera el nombre registrado en la plataforma de anuncios.",
  "admin/vtex-vtex-ads.banner.sizeAdmin.title": "Tamaño del medio - desktop",
  "admin/vtex-vtex-ads.banner.sizeAdmin.description": "Utilice el mismo tamaño registrado en la plataforma de anuncios. Por defecto, se utilizará el valor 'banner'.",
  "admin/vtex-vtex-ads.banner.sizeMobileAdmin.title": "Tamaño del medio - móvil (opcional)",
  "admin/vtex-vtex-ads.banner.sizeMobileAdmin.description": "Utilice el mismo tamaño registrado en la plataforma de anuncios. Si no se proporciona, se utilizará el valor 'Tamaño del medio - desktop'.",

  "admin/vtex-vtex-ads.search.title": "VTEX Ads - Búsqueda",
  "admin/vtex-vtex-ads.search.placementNameAdmin.description": "Por defecto, se utilizará 'placement_search_default'. Prefiera el nombre registrado en la plataforma de anuncios.",
  "admin/vtex-vtex-ads.search.tagPosition.title": "Posición de la etiqueta",
  "admin/vtex-vtex-ads.search.tagPosition.description": "Indique dónde se debe mostrar la etiqueta dentro de la tarjeta del producto",
  "admin/vtex-vtex-ads.search.tagPosition.start": "Inicio",
  "admin/vtex-vtex-ads.search.tagPosition.end": "Final",
  "admin/vtex-vtex-ads.search.tagText.default": "Patrocinado",
  "admin/vtex-vtex-ads.search.tagText.title": "Texto de la etiqueta patrocinada",
  "admin/vtex-vtex-ads.search.tagText.description": "Por defecto, se utilizará 'patrocinado' con traducción automática. Para reemplazar, complete este campo.",
  "admin/vtex-vtex-ads.search.tagClassname.title": "Clase de la etiqueta",
  "admin/vtex-vtex-ads.search.tagClassname.description": "Por defecto, se utilizará '.newtail-sponsored-tag'.",
  "admin/vtex-vtex-ads.search.parentSearchSelector.title": "Elemento HTML para Inserción de Etiquetas",
  "admin/vtex-vtex-ads.search.parentSearchSelector.description": "[Avanzado] Este campo debe ser editado solo por desarrolladores. Por defecto, se utilizará '.vtex-search-result-3-x-searchResultContainer #gallery-layout-container'.",
  "admin/vtex-vtex-ads.search.onlyFirstSKU.title": "Usar solo el primer SKU. Si está marcado, solo se utilizará el primer SKU del producto para la búsqueda en Newtail. Si no está marcado, se utilizarán todos los SKUs del producto para la búsqueda en Newtail."
}

```