[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 store-components/react/components/GradientCollapse/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useState, useRef, useLayoutEffect } from 'react'
import PropTypes from 'prop-types'
import { Transition } from 'react-transition-group'
import debounce from 'debounce'
import { FormattedMessage } from 'react-intl'
import classNames from 'classnames'
import { useCssHandles } from 'vtex.css-handles'

import styles from './styles.css'

const CSS_HANDLES = ['container', 'content', 'showMoreButton']

const transitionStyle = transitionTime => ({
  transition: `${transitionTime}ms ease-in-out`,
})

const fadeBottomClasses = state =>
  classNames(styles.fadeBottom, { 'o-0': state === 'entered' }, 'w-100 h-50')

const pointerEventsAutoClasses = state =>
  classNames(
    styles.pointerEventsAuto,
    {
      'bg-transparent': state === 'entered',
      'bg-base': state !== 'entered',
    },
    'tc w-100'
  )

/**
 * @deprecated This component is deprecated.
 */
function GradientCollapse(props) {
  const {
    children,
    collapseHeight,
    onCollapsedChange,
    collapsed: collapsedProp,
  } = props

  const { handles } = useCssHandles(CSS_HANDLES)
  const [collapsed, setCollapsed] = useState(collapsedProp)
  const [prevCollapsedProp, setPrevCollapsedProp] = useState(collapsedProp)
  const [maxHeight, setMaxHeight] = useState('auto')
  const [collapseVisible, setCollapseVisible] = useState(true)
  const wrapper = useRef()

  if (prevCollapsedProp !== collapsedProp) {
    setCollapsed(collapsedProp)
    setPrevCollapsedProp(collapsedProp)
  }

  const calcMaxHeight = () => {
    const wrapperEl = wrapper.current

    // check if the content is smaller than the passed
    // height to collapse
    if (wrapperEl.scrollHeight > collapseHeight) {
      setMaxHeight(wrapperEl.scrollHeight + 60)
      setCollapseVisible(true)
    } else {
      setCollapseVisible(false)
      setMaxHeight('auto')
    }
  }

  const handleCollapsedChange = (e, newValue) => {
    setCollapsed(newValue)
    setPrevCollapsedProp(collapsedProp)

    if (onCollapsedChange) {
      onCollapsedChange(e, newValue)
    }
  }

  const debouncedCalcMaxHeight = debounce(calcMaxHeight, 500)

  useLayoutEffect(() => {
    window.addEventListener('resize', debouncedCalcMaxHeight)
    calcMaxHeight()

    return () => {
      window.removeEventListener('resize', debouncedCalcMaxHeight)
    }
  })

  const height = collapseVisible && collapsed ? collapseHeight : maxHeight
  const transitionTime = 600
  const fadeOutTime = 400

  const pointerEventsNoneClasses = classNames(
    styles.pointerEventsNone,
    { flex: collapseVisible, dn: !collapseVisible },
    'absolute bottom-0 w-100 h-100 flex-column justify-end'
  )

  return (
    <Transition timeout={transitionTime} in={!collapsed}>
      {state => (
        <div
          style={{
            ...transitionStyle(transitionTime),
            height,
            overflow: 'hidden',
            display: 'block',
          }}
          onTransitionEnd={calcMaxHeight}
          className={`${handles.container} relative`}
        >
          <div ref={wrapper} className={`${handles.content} h-auto`}>
            {children}
          </div>
          <div className={pointerEventsNoneClasses}>
            <div
              style={transitionStyle(fadeOutTime)}
              className={fadeBottomClasses(state)}
            />
            <div className={pointerEventsAutoClasses(state)}>
              <button
                onClick={e => handleCollapsedChange(e, !collapsed)}
                className={`${handles.showMoreButton} c-action-primary t-action pointer ma5 bn outline-0`}
              >
                {state === 'entered' || (collapsed && state !== 'exited') ? (
                  <FormattedMessage id="store/product-description.collapse.showLess" />
                ) : (
                  <FormattedMessage id="store/product-description.collapse.showMore" />
                )}
              </button>
            </div>
          </div>
        </div>
      )}
    </Transition>
  )
}

GradientCollapse.propTypes = {
  /** Maximum height collapsed */
  collapseHeight: PropTypes.number.isRequired,
  collapsed: PropTypes.bool,
  children: PropTypes.node,
  onCollapsedChange: PropTypes.func,
}

GradientCollapse.defaultProps = {
  collapsed: true,
}

export default GradientCollapse

```


[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 store-components/react/components/GradientCollapse/index.d.ts from the store-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { FC } from 'react'

const GradientCollapse: FC<any>

export default GradientCollapse

```


[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 store-components/react/components/ProductPrice/styles.css from the store-components VTEX app.

[Assistant]
Here's the implementation:

```css
/**
*   ProductPrice CSS Modules
*/
.priceContainer {}
.listPrice {}
.listPriceLabel {}
.listPriceValue {}

.sellingPriceContainer {}
.sellingPrice {} /* deprecated */
.sellingPriceLabel {}
.sellingPriceValue {}
.sellingPriceValue--range {}

.savingPrice {}
.savingPriceValue {}

.installmentsPrice {}
.interestRatePrice {}

.priceLoaderContainer {}
.listPriceLoader {}
.sellingPriceLabelLoader {}
.sellingPriceLoader {}
.installmentsPriceLoader {}
.savingsPriceLoader {}
```


[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 store-components/react/components/ProductPrice/Wrapper.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useContext } from 'react'
import { ProductContext } from 'vtex.product-context'
import { path, isEmpty, has } from 'ramda'

import { useCommercialOffer } from '../../utils/useCommercialOffer'
import ProductPrice from './index'

const styles = {
  'vtex-price-list__container--loader': {
    x: 0,
    width: '7.219em',
    height: '0.56em',
  },
  'vtex-price-selling__label--loader': {
    x: 0,
    y: '2em',
    width: '2.85em',
    height: '1.08em',
  },
  'vtex-price-selling--loader': {
    x: '3.25em',
    y: '0.86em',
    width: '14.572em',
    height: '2.176em',
  },
  'vtex-price-installments--loader': {
    x: 0,
    y: '3.75em',
    width: '12em',
    height: '0.825em',
  },
  'vtex-price-savings--loader': {
    x: 0,
    y: '5em',
    width: '10em',
    height: '0.686em',
  },
}

const isAvailable = commercialOffer =>
  Number.isNaN(+path(['AvailableQuantity'], commercialOffer)) ||
  path(['AvailableQuantity'], commercialOffer) > 0

const ProductPriceWrapper = ({
  labelSellingPrice,
  showListPrice,
  showInstallments,
  showLabels,
  showSavings,
  ...props
}) => {
  const valuesFromContext = useContext(ProductContext)

  const commercialOffer = useCommercialOffer(valuesFromContext)

  const {
    className,
    listPriceContainerClass,
    listPriceLabelClass,
    listPriceClass,
    sellingPriceContainerClass,
    sellingPriceLabelClass,
    sellingPriceClass,
    savingsContainerClass,
    savingsClass,
    interestRateClass,
    installmentContainerClass,
    installmentClass,
    loaderClass,
    listPrice,
    sellingPrice,
    installments,
  } = props

  const productPriceProps = () => {
    if (
      !valuesFromContext ||
      isEmpty(valuesFromContext) ||
      (!has('query', props) && !has('params', props))
    ) {
      return {
        ...props,
        labelSellingPrice,
        showListPrice,
        showInstallments,
        showLabels,
        showSavings,
        showProductPrice: true,
      }
    }

    return {
      ...props,
      styles: props.styles || styles,
      className: className || '',
      listPriceContainerClass:
        listPriceContainerClass || 't-small-s t-small-ns c-muted-2 mb2',
      sellingPriceLabelClass:
        sellingPriceLabelClass || 't-heading-6-s t-heading-5-ns dib',
      listPriceLabelClass: listPriceLabelClass || 'dib strike',
      listPriceClass: listPriceClass || 'ph2 dib strike',
      sellingPriceContainerClass:
        sellingPriceContainerClass || 'pv1 b c-on-base',
      sellingPriceClass: sellingPriceClass || 't-heading-2-s dib ph2',
      installmentContainerClass:
        installmentContainerClass || 't-mini-s t-small-ns c-on-base',
      installmentClass: installmentClass || 't-body',
      interestRateClass: interestRateClass || 'dib ph2',
      savingsContainerClass: savingsContainerClass || 'c-success mt3',
      savingsClass: savingsClass || 'dib t-small',
      loaderClass: loaderClass || 'h4-s mw6-s pt2-s',
      listPrice: listPrice || path(['ListPrice'], commercialOffer),
      sellingPrice: sellingPrice || path(['Price'], commercialOffer),
      installments: installments || path(['Installments'], commercialOffer),
      labelSellingPrice,
      showLabels,
      showInstallments,
      showListPrice,
      showSavings,
      showProductPrice: isAvailable(commercialOffer),
    }
  }

  const { showProductPrice, ...priceProps } = productPriceProps()

  if (!showProductPrice) {
    return null
  }

  return <ProductPrice {...priceProps} />
}

ProductPriceWrapper.schema = {
  title: 'admin/editor.productPrice.title',
  description: 'admin/editor.productPrice.description',
  type: 'object',
  properties: {
    labelSellingPrice: {
      type: 'string',
      title: 'admin/editor.productPrice.labelSellingPrice',
      default: ProductPrice.defaultProps.labelSellingPrice,
      isLayout: false,
    },
    labelListPrice: {
      type: 'string',
      title: 'admin/editor.productPrice.labelListPrice',
      default: ProductPrice.defaultProps.labelListPrice,
      isLayout: false,
    },
    showSellingPriceRange: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showSellingPriceRange',
      default: ProductPrice.defaultProps.showSellingPriceRange,
      isLayout: true,
    },
    showListPriceRange: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showListPriceRange',
      default: ProductPrice.defaultProps.showListPriceRange,
      isLayout: true,
    },
    showListPrice: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showListPrice',
      default: ProductPrice.defaultProps.showListPrice,
      isLayout: true,
    },
    showLabels: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showLabels',
      default: ProductPrice.defaultProps.showLabels,
      isLayout: true,
    },
    showInstallments: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showInstallments',
      default: ProductPrice.defaultProps.showInstallments,
      isLayout: true,
    },
    showSavings: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showSavings',
      default: ProductPrice.defaultProps.showSavings,
      isLayout: true,
    },
  },
}

export default ProductPriceWrapper

```


[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 store-components/react/components/ProductPrice/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import classNames from 'classnames'
import { isNil, head, last, sort, equals } from 'ramda'
import { FormattedMessage, injectIntl } from 'react-intl'
import { IOMessage } from 'vtex.native-types'
import { useRuntime } from 'vtex.render-runtime'
import { formatCurrency } from 'vtex.format-currency'
import { useCssHandles } from 'vtex.css-handles'

import ProductPriceLoader from './Loader'
import PricePropTypes from './propTypes'
import Installments from './Installments'
import Price from './Price'
import productPrice from './styles.css'

const CSS_HANDLES = [
  'price_className',
  'price_loader',
  'price_listPriceContainer',
  'price_listPriceLabel',
  'price_listPrice',
  'price_listPriceRange',
  'price_sellingPriceRange',
  'price_sellingPriceContainer',
  'price_sellingPriceLabel',
  'price_sellingPrice',
  'price_savingsContainer',
  'price_savings',
  'price_savings_value',
  'price_installment',
  'price_interestRate',
  'price_installmentContainer',
]

const isValidPriceRange = priceRange => {
  const [lowPrice, highPrice] = priceRange

  return priceRange.length === 2 && lowPrice !== highPrice
}

const getPriceRange = prices => {
  const sortedPrices = sort((a, b) => a - b, prices)

  return [head(sortedPrices), last(sortedPrices)]
}

const canShowListPrice = props => {
  const {
    sellingPriceList,
    sellingPrice,
    listPrice,
    listPriceList,
    showListPrice,
    showListPriceRange,
    showSellingPriceRange,
  } = props

  if (!showListPrice) {
    return false
  }

  const sellingPriceRange =
    (sellingPriceList && getPriceRange(sellingPriceList)) || []

  const listPriceRange = (listPriceList && getPriceRange(listPriceList)) || []

  const showingSellingPriceRange =
    showSellingPriceRange && isValidPriceRange(sellingPriceRange)

  const showingListPriceRange =
    showListPriceRange && isValidPriceRange(listPriceRange)

  const sellingPriceToShow = showingSellingPriceRange
    ? sellingPriceRange
    : sellingPrice

  const listPriceToShow = showingListPriceRange ? listPriceRange : listPrice

  return !equals(listPriceToShow, sellingPriceToShow)
}

/**
 * The Price component. Shows the prices information of the Product Summary.
 */
const ProductPrice = props => {
  const {
    sellingPriceList,
    sellingPrice,
    listPrice,
    listPriceList,
    showListPrice,
    showSellingPriceRange,
    showListPriceRange,
    showInstallments,
    showLabels,
    showSavings,
    labelSellingPrice,
    labelListPrice,
    className,
    loaderClass,
    listPriceContainerClass,
    listPriceLabelClass,
    listPriceClass,
    listPriceRangeClass,
    sellingPriceRangeClass,
    sellingPriceContainerClass,
    sellingPriceLabelClass,
    sellingPriceClass,
    savingsContainerClass,
    savingsClass,
    installments,
    installmentClass,
    interestRateClass,
    installmentContainerClass,
    styles,
    intl,
  } = props

  const { culture } = useRuntime()

  const { handles } = useCssHandles(CSS_HANDLES)

  if ((showListPrice && isNil(listPrice)) || isNil(sellingPrice)) {
    return (
      <ProductPriceLoader
        loaderClass={`${loaderClass} ${handles.price_loader}`}
        {...styles}
      />
    )
  }

  const mayShowListPrice = canShowListPrice(props)

  const sellingPriceRange = sellingPriceList && getPriceRange(sellingPriceList)
  const listPriceRange = listPriceList && getPriceRange(listPriceList)

  return (
    <div
      className={classNames(
        productPrice.priceContainer,
        className,
        handles.price_className
      )}
    >
      {mayShowListPrice && (
        <div
          className={classNames(
            productPrice.listPrice,
            listPriceContainerClass,
            handles.price_listPriceContainer
          )}
        >
          {showLabels && (
            <div
              className={classNames(
                productPrice.listPriceLabel,
                listPriceLabelClass,
                handles.price_listPriceLabel,
                'dib ph2 t-small-ns t-mini'
              )}
            >
              <IOMessage id={labelListPrice} />
            </div>
          )}
          <Price
            showPriceRange={showListPriceRange}
            priceRange={listPriceRange}
            price={listPrice}
            rangeContainerClasses={classNames(
              productPrice.listPriceValue,
              listPriceRangeClass,
              handles.price_listPriceRange
            )}
            singleContainerClasses={classNames(
              productPrice.listPriceValue,
              listPriceClass,
              handles.price_listPrice
            )}
          />
        </div>
      )}
      <div
        className={classNames(
          productPrice.sellingPrice,
          productPrice.sellingPriceContainer,
          sellingPriceContainerClass,
          handles.price_sellingPriceContainer
        )}
      >
        {showLabels && mayShowListPrice && (
          <div
            className={classNames(
              productPrice.sellingPriceLabel,
              sellingPriceLabelClass,
              handles.price_sellingPriceLabel
            )}
          >
            <IOMessage id={labelSellingPrice} />
          </div>
        )}
        <Price
          showPriceRange={showSellingPriceRange}
          priceRange={sellingPriceRange}
          price={sellingPrice}
          rangeContainerClasses={classNames(
            productPrice.sellingPrice,
            productPrice.sellingPriceValue,
            productPrice['sellingPriceValue--range'],
            sellingPriceRangeClass,
            handles.price_sellingPriceRange
          )}
          singleContainerClasses={classNames(
            productPrice.sellingPrice,
            productPrice.sellingPriceValue,
            sellingPriceClass,
            handles.price_sellingPrice
          )}
        />
      </div>
      {showInstallments && (
        <Installments
          installments={installments}
          showLabels={showLabels}
          className={`${installmentContainerClass} ${handles.price_installmentContainer}`}
          interestRateClass={`${interestRateClass} ${handles.price_interestRate}`}
          installmentClass={`${installmentClass} ${handles.price_installment}`}
        />
      )}
      {mayShowListPrice && showSavings && listPrice - sellingPrice > 0 && (
        <div
          className={classNames(
            productPrice.savingPrice,
            savingsContainerClass,
            handles.price_savingsContainer
          )}
        >
          <div
            className={classNames(
              productPrice.savingPriceValue,
              savingsClass,
              handles.price_savings
            )}
          >
            <FormattedMessage
              id="store/pricing.savings"
              values={{
                savings: (
                  <span className={handles.price_savings_value}>
                    {formatCurrency({
                      intl,
                      culture,
                      value: listPrice - sellingPrice,
                    })}
                  </span>
                ),
              }}
            />
          </div>
        </div>
      )}
    </div>
  )
}

ProductPrice.propTypes = PricePropTypes

const PriceWithIntl = injectIntl(ProductPrice)

PriceWithIntl.defaultProps = {
  showSellingPriceRange: false,
  showListPriceRange: false,
  showListPrice: true,
  showLabels: true,
  showInstallments: false,
  showSavings: false,
  labelSellingPrice: null,
  labelListPrice: null,
}

PriceWithIntl.schema = {
  title: 'admin/editor.productPrice.title',
  description: 'admin/editor.productPrice.description',
  type: 'object',
  properties: {
    showSellingPriceRange: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showSellingPriceRange',
      default: PriceWithIntl.defaultProps.showSellingPriceRange,
      isLayout: true,
    },
    showListPriceRange: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showListPriceRange',
      default: PriceWithIntl.defaultProps.showListPriceRange,
      isLayout: true,
    },
    showListPrice: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showListPrice',
      default: PriceWithIntl.defaultProps.showListPrice,
      isLayout: true,
    },
    showLabels: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showLabels',
      default: PriceWithIntl.defaultProps.showLabels,
      isLayout: true,
    },
    showInstallments: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showInstallments',
      default: PriceWithIntl.defaultProps.showInstallments,
      isLayout: true,
    },
    showSavings: {
      type: 'boolean',
      title: 'admin/editor.productPrice.showSavings',
      default: PriceWithIntl.defaultProps.showSavings,
      isLayout: true,
    },
  },
}

export default PriceWithIntl

```


[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 store-components/react/components/ProductPrice/Installments.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Fragment } from 'react'
import { useRuntime } from 'vtex.render-runtime'
import { injectIntl, FormattedMessage } from 'react-intl'
import classNames from 'classnames'
import { isEmpty } from 'ramda'
import PropTypes from 'prop-types'
import { formatCurrency } from 'vtex.format-currency'

import PricePropTypes from './propTypes'
import productPrice from './styles.css'

/** Installments component */
const Installments = ({
  showLabels,
  installments = [],
  className,
  installmentClass,
  interestRateClass,
  intl,
}) => {
  const { culture } = useRuntime()

  if (
    !installments ||
    isEmpty(
      installments.filter(
        ({ NumberOfInstallments }) => NumberOfInstallments > 1
      )
    )
  ) {
    return null
  }

  const noInterestRateInstallments = installments.filter(
    installment =>
      !installment.InterestRate && installment.NumberOfInstallments > 1
  )

  /*
   * - The selected installment will be the one with the highest `NumberOfInstallments`;
   * - If there is no 'interest-free' installments, the normal installments will be analyzed.
   */
  const installment = (
    isEmpty(noInterestRateInstallments)
      ? installments
      : noInterestRateInstallments
  ).reduce((previous, current) =>
    previous.NumberOfInstallments > current.NumberOfInstallments
      ? previous
      : current
  )

  const formattedInstallmentPrice = formatCurrency({
    intl,
    culture,
    value: installment.Value,
  })

  const [installmentsElement, installmentPriceElement, timesElement] = [
    installment.NumberOfInstallments,
    formattedInstallmentPrice,
    <Fragment key="x">x</Fragment>,
  ].map((element, index) => (
    <span className={installmentClass} key={index}>
      {element}
    </span>
  ))

  return (
    <div className={classNames(productPrice.installmentsPrice, className)}>
      {showLabels ? (
        <FormattedMessage
          id="store/pricing.installment-display"
          values={{
            installments: installmentsElement,
            installmentPrice: installmentPriceElement,
            times: timesElement,
          }}
        />
      ) : (
        <Fragment>
          {installmentsElement}
          {timesElement} {installmentPriceElement}
        </Fragment>
      )}
      {!installment.InterestRate && (
        <div
          className={classNames(
            productPrice.interestRatePrice,
            interestRateClass
          )}
        >
          <FormattedMessage id="store/pricing.interest-free" />
        </div>
      )}
    </div>
  )
}

Installments.propTypes = {
  /** Classes to be applied to the root element */
  className: PropTypes.string,
  /** Classes to be applied to installment value element */
  installmentClass: PropTypes.string,
  /** Classes to be applied to interest rate element */
  interestRateClass: PropTypes.string,
  /** Product installments to be displayed */
  installments: PricePropTypes.installments,
  /** Pages editor config to display labels */
  showLabels: PropTypes.bool.isRequired,
  intl: PropTypes.object.isRequired,
}

export default injectIntl(Installments)

```


[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 store-components/react/components/ProductPrice/propTypes.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'

export default {
  /** Product list of selling prices */
  sellingPriceList: PropTypes.arrayOf(PropTypes.number),
  /** Product list of list prices */
  listPriceList: PropTypes.arrayOf(PropTypes.number),
  /** Product selling price */
  sellingPrice: PropTypes.number,
  /** Product list price */
  listPrice: PropTypes.number,
  /** Set visibility of selling prices range */
  showSellingPriceRange: PropTypes.bool.isRequired,
  /** Set visibility of list prices range */
  showListPriceRange: PropTypes.bool.isRequired,
  /** Set visibility of list price */
  showListPrice: PropTypes.bool.isRequired,
  /** Set visibility of labels */
  showLabels: PropTypes.bool.isRequired,
  /** Set visibility of installments */
  showInstallments: PropTypes.bool.isRequired,
  /** Set visibility of savings */
  showSavings: PropTypes.bool,
  /** Text to the selling price's label */
  labelSellingPrice: PropTypes.string,
  /** Text to the list price's label */
  labelListPrice: PropTypes.string,
  /** Available installments */
  installments: PropTypes.arrayOf(
    PropTypes.shape({
      /** Installment value */
      Value: PropTypes.number.isRequired,
      /** Interest rate (zero if interest-free) */
      InterestRate: PropTypes.number.isRequired,
      /** Calculated total value */
      TotalValuePlusInterestRate: PropTypes.number,
      /** Number of installments */
      NumberOfInstallments: PropTypes.number.isRequired,
      /** Installment offer name */
      Name: PropTypes.string,
    })
  ),
  /** Classes to be applied to root element */
  className: PropTypes.string,
  /** Classes to be applied to loader root element */
  loaderClass: PropTypes.string,
  /** Classes to be applied to container of list price */
  listPriceContainerClass: PropTypes.string,
  /** Classes to be applied to label of price */
  listPriceLabelClass: PropTypes.string,
  /** Classes to be applied to price value */
  listPriceClass: PropTypes.string,
  /** Classes to be applied to range selling prices value */
  sellingPriceRangeClass: PropTypes.string,
  /** Classes to be applied to range list prices value */
  listPriceRangeClass: PropTypes.string,
  /** Classes to be applied to selling price container */
  sellingPriceContainerClass: PropTypes.string,
  /** Classes to be applied to selling price label */
  sellingPriceLabelClass: PropTypes.string,
  /** Classes to be applied to selling price value */
  sellingPriceClass: PropTypes.string,
  /** Classes to be applied to savings container */
  savingsContainerClass: PropTypes.string,
  /** Classes to be applied to savings */
  savingsClass: PropTypes.string,
  /** Classes to be applied to installment element */
  installmentClass: PropTypes.string,
  /** Classes to be applied to installment container */
  installmentContainerClass: PropTypes.string,
  /** Classes to be applied to interest rate element */
  interestRateClass: PropTypes.string,
  /** Component and content loader styles */
  styles: PropTypes.object,
  /** intl property to format data */
  intl: PropTypes.object.isRequired,
}

```


[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 store-components/react/components/ProductPrice/Price.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { injectIntl } from 'react-intl'
import { useRuntime } from 'vtex.render-runtime'
import { FormattedCurrency, formatCurrency } from 'vtex.format-currency'

const isValidPriceRange = priceRange => {
  const [lowPrice, highPrice] = priceRange

  return priceRange.length === 2 && lowPrice !== highPrice
}

const formatPriceRange = (intl, culture, rawPriceRange) => {
  const priceRangeFormatted = (rawPriceRange || []).map(value =>
    formatCurrency({ intl, culture, value })
  )

  return priceRangeFormatted.join(' - ')
}

const Price = ({
  showPriceRange,
  priceRange,
  price,
  rangeContainerClasses,
  singleContainerClasses,
  intl,
}) => {
  const { culture } = useRuntime()

  const mustShowPriceRange =
    showPriceRange && priceRange && isValidPriceRange(priceRange)

  if (mustShowPriceRange) {
    return (
      <span className={rangeContainerClasses}>
        {formatPriceRange(intl, culture, priceRange)}
      </span>
    )
  }

  return (
    <span className={singleContainerClasses}>
      <FormattedCurrency value={price} />
    </span>
  )
}

export default injectIntl(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 store-components/react/components/ProductPrice/Loader.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import classNames from 'classnames'
import ContentLoader from 'react-content-loader'

import productPrice from './styles.css'

const ProductPriceLoader = (loaderProps = {}) => {
  const { loaderClass, ...props } = loaderProps

  return (
    <div
      className={classNames(
        productPrice.priceContainer,
        productPrice.priceLoaderContainer,
        loaderClass
      )}
    >
      <ContentLoader
        style={{
          width: '100%',
          height: '100%',
        }}
        width={300}
        height={70}
        preserveAspectRatio="xMinYMin meet"
        {...props}
      >
        <rect
          height="0.75em"
          width="50%"
          x="25%"
          {...loaderProps[productPrice.listPriceLoader]}
        />
        <rect {...loaderProps[productPrice.sellingPriceLabelLoader]} />
        <rect
          height="1em"
          width="70%"
          x="15%"
          y="1.25em"
          {...loaderProps[productPrice.sellingPriceLoader]}
        />
        <rect
          height="0.75em"
          width="80%"
          x="10%"
          y="2.75em"
          {...loaderProps[productPrice.installmentsPriceLoader]}
        />
        <rect {...loaderProps[productPrice.savingsPriceLoader]} />
      </ContentLoader>
    </div>
  )
}

export default ProductPriceLoader

```


[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 store-components/react/components/SKUSelector/index.tsx from the store-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, {
  useState,
  useEffect,
  useMemo,
  memo,
  useCallback,
  FC,
} from 'react'
import { filter, head, isEmpty, compose, keys, length } from 'ramda'
import { useRuntime } from 'vtex.render-runtime'
import {
  useResponsiveValue,
  ResponsiveValuesTypes,
} from 'vtex.responsive-values'
import { useProduct, useProductDispatch } from 'vtex.product-context'

import SKUSelector, {
  ShowValueForVariation,
  ShowVariationsLabels,
} from './components/SKUSelector'
import {
  parseSku,
  isColor,
  uniqueOptionToSelect,
  findItemWithSelectedVariations,
} from './utils'
import {
  Image,
  ImageMap,
  Variations,
  SelectedVariations,
  SelectorProductItem,
  InitialSelectionType,
  DisplayMode,
} from './types'
import useEffectSkipMount from './components/hooks/useEffectSkipMount'

const keyCount = compose(length, keys)
const filterSelected = filter(Boolean)

const areAllVariationsSelected = (
  selected: SelectedVariations,
  numberOfVariations: number
) => {
  const selectedCount = keyCount(filterSelected(selected))

  return selectedCount === numberOfVariations
}

const buildEmptySelectedVariation = (variations: Variations) => {
  const variationNames = Object.keys(variations)
  const result = {} as Record<string, null>

  for (const variationName of variationNames) {
    result[variationName] = null
  }

  return result
}

/** receives an item and the variations object, returns the selected variations for that item */
const selectedVariationFromItem = (
  item: SelectorProductItem,
  variations: Variations
) => {
  const variationNames = Object.keys(variations)
  const result = {} as Record<string, string>

  for (const variationName of variationNames) {
    result[variationName] = item.variationValues[variationName]
  }

  return result
}

function filterColorImages(
  items: SelectorProductItem[],
  imageRegexText: string
) {
  const imageRegex = new RegExp(imageRegexText, 'i')

  return items.map(item => {
    if (!item.images) {
      return item
    }

    const hasVariationImage = item.images.some(
      image => image.imageLabel && imageRegex.test(image.imageLabel)
    )

    return {
      ...item,
      // if it doesn't have a variation image, it wont remove images without a label
      images: hasVariationImage
        ? item.images.filter(image => {
            if (!image.imageLabel) {
              return false
            }

            return imageRegex.test(image.imageLabel)
          })
        : item.images,
    }
  })
}

const useImagesMap = (
  items: SelectorProductItem[],
  variations: Variations,
  thumbnailImage?: string
) => {
  return useMemo(() => {
    let filteredItems = items

    if (thumbnailImage) {
      filteredItems = filterColorImages(items, thumbnailImage)
    }

    const variationNames = Object.keys(variations)

    const result: ImageMap = {}

    for (const variationName of variationNames) {
      // Today, only "Color" variation should show image, need to find a more resilient way to tell this, waiting for backend
      if (!isColor(variations[variationName].originalName)) {
        continue
      }

      const imageMap = {} as Record<string, Image | undefined>
      const variationValues = variations[variationName].values

      for (const variationValue of variationValues) {
        const item = filteredItems.find(
          sku => sku.variationValues[variationName] === variationValue.name
        )

        imageMap[variationValue.name] = item && head(item.images)
      }

      result[variationName] = imageMap
    }

    return result
  }, [items, variations, thumbnailImage])
}

const useAllSelectedEvent = (
  selectedVariations: SelectedVariations | null,
  variationsCount: number
) => {
  const dispatch = useProductDispatch()

  useEffect(() => {
    if (dispatch && selectedVariations) {
      dispatch({
        type: 'SKU_SELECTOR_SET_VARIATIONS_SELECTED',
        args: {
          allSelected: areAllVariationsSelected(
            selectedVariations,
            variationsCount
          ),
        },
      })
    }
  }, [dispatch, selectedVariations, variationsCount])
}

interface Props {
  skuItems: ProductItem[]
  onSKUSelected?: (skuId: string) => void
  seeMoreLabel: string
  maxItems?: number
  variations: Variations
  skuSelected: ProductItem | null
  hideImpossibleCombinations?: boolean
  disableUnavailableSelectOptions?: boolean
  showValueForVariation?: ShowValueForVariation
  imageHeight?: number
  imageWidth?: number
  thumbnailImage?: string
  showVariationsLabels?: ShowVariationsLabels
  variationsSpacing?: number
  showVariationsErrorMessage?: boolean
  initialSelection?: InitialSelectionType
  displayMode?: ResponsiveValuesTypes.ResponsiveValue<DisplayMode>
  sliderDisplayThreshold?: number
  sliderArrowSize?: number
  sliderItemsPerPage?: ResponsiveValuesTypes.ResponsiveValue<number>
  sortVariationsByLabel?: boolean
}

const getNewSelectedVariations = (
  query: Record<string, string> | undefined,
  skuSelected: Props['skuSelected'],
  variations: Props['variations'],
  initialSelection?: Props['initialSelection']
  // eslint-disable-next-line max-params
) => {
  const emptyVariations = buildEmptySelectedVariation(variations)

  if (skuSelected == null) {
    return emptyVariations
  }

  const hasSkuInQuery = Boolean(query?.skuId)
  const parsedSku = parseSku(skuSelected)

  if (hasSkuInQuery || initialSelection === 'complete') {
    return selectedVariationFromItem(parsedSku, variations)
  }

  if (initialSelection === 'image') {
    const colorVariationName = parsedSku.variations.find(isColor)

    return {
      ...emptyVariations,
      ...(colorVariationName
        ? {
            [colorVariationName]: parsedSku.variationValues[colorVariationName],
          }
        : {}),
    }
  }

  return emptyVariations
}

/**
 * Display a list of SKU items of a product and its specifications.
 */
const SKUSelectorContainer: FC<Props> = ({
  skuItems = [],
  onSKUSelected,
  seeMoreLabel,
  maxItems = 10,
  variations,
  skuSelected,
  imageWidth,
  imageHeight,
  thumbnailImage,
  variationsSpacing,
  showVariationsLabels = 'variation',
  displayMode = 'default',
  hideImpossibleCombinations = true,
  disableUnavailableSelectOptions = false,
  showVariationsErrorMessage = true,
  showValueForVariation = 'none',
  initialSelection = 'complete',
  sliderDisplayThreshold = 3,
  sliderArrowSize = 12,
  sortVariationsByLabel = false,
  sliderItemsPerPage = {
    desktop: 3,
    tablet: 2,
    phone: 1,
  },
}) => {
  const productContext = useProduct()
  const selectedItem = productContext?.selectedItem

  const variationsCount = keyCount(variations)
  const { query } = useRuntime()
  const responsiveDisplayMode = useResponsiveValue(displayMode)

  const parsedItems = useMemo(() => skuItems.map(parseSku), [skuItems])
  const { setQuery } = useRuntime()
  const redirectToSku = (skuId: string) => {
    setQuery({ skuId }, { replace: true })
  }

  const [selectedVariations, setSelectedVariations] =
    useState<SelectedVariations>(() =>
      getNewSelectedVariations(query, skuSelected, variations, initialSelection)
    )

  useAllSelectedEvent(selectedVariations, variationsCount)

  useEffectSkipMount(() => {
    setSelectedVariations(
      getNewSelectedVariations(query, skuSelected, variations, initialSelection)
    )
  }, [variations, skuSelected])

  // This is used to selected an SKU when initialSelection is not 'empty'.
  // Runs only on the first render.
  useEffect(() => {
    if (skuSelected && onSKUSelected) {
      onSKUSelected(skuSelected.itemId)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const imagesMap = useImagesMap(parsedItems, variations, thumbnailImage)

  const dispatch = useProductDispatch()

  const onSelectItem = useCallback(
    ({
      name: variationName,
      value: variationValue,
      skuId,
      isMainAndImpossible,
      possibleItems,
    }) => {
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      const isRemoving = selectedVariations![variationName] === variationValue

      const newSelectedVariation = !isMainAndImpossible
        ? {
            ...selectedVariations,
            [variationName]: isRemoving ? null : variationValue,
          }
        : {
            ...buildEmptySelectedVariation(variations),
            [variationName]: variationValue,
          }

      // Set here for a better response to user
      setSelectedVariations(newSelectedVariation)

      if (selectedItem && selectedItem.itemId !== skuId && !isRemoving) {
        dispatch({
          type: 'SET_LOADING_ITEM',
          args: { loadingItem: true },
        })
      } else if (selectedItem?.itemId === skuId) {
        dispatch({
          type: 'SET_LOADING_ITEM',
          args: { loadingItem: false },
        })
      }

      const uniqueOptions = isRemoving
        ? {}
        : uniqueOptionToSelect(
            possibleItems,
            newSelectedVariation,
            isMainAndImpossible
          )

      const finalSelected = {
        ...newSelectedVariation,
        ...uniqueOptions,
      }

      if (!isEmpty(uniqueOptions)) {
        setSelectedVariations(finalSelected)
      }

      const allSelected = areAllVariationsSelected(
        finalSelected,
        variationsCount
      )

      let skuIdToRedirect = skuId

      if (!skuIdToRedirect || !isEmpty(uniqueOptions)) {
        const newItem = findItemWithSelectedVariations(
          possibleItems,
          finalSelected
        )

        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        skuIdToRedirect = newItem!.itemId
      }

      // if (un)selecting a color variation, dispatch to the product context
      // so we can show/hide the mainImageLabel if defined
      if (isColor(variationName)) {
        const variationSKU = isRemoving ? null : skuIdToRedirect

        dispatch({
          type: 'SELECT_IMAGE_VARIATION',
          args: {
            selectedImageVariationSKU: variationSKU,
          },
        })
      }

      // only redirect to a specific sku id if every variation was defined
      if (allSelected === false) {
        skuIdToRedirect = null

        dispatch({
          type: 'SET_LOADING_ITEM',
          args: { loadingItem: false },
        })
      }

      if (onSKUSelected) {
        onSKUSelected(skuIdToRedirect)

        return
      }

      // If its just removing, no need to redirect
      if (!isRemoving && (allSelected || isColor(variationName))) {
        redirectToSku(skuIdToRedirect)
      }
    },
    // Adding selectedVariations, variationsCount and onSKUSelected causes an infinite loop
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [selectedVariations, variations, onSKUSelected, skuSelected?.itemId]
  )

  return (
    <SKUSelector
      maxItems={maxItems}
      imagesMap={imagesMap}
      skuItems={parsedItems}
      variations={variations}
      imageWidth={imageWidth}
      displayMode={responsiveDisplayMode}
      imageHeight={imageHeight}
      seeMoreLabel={seeMoreLabel}
      onSelectItem={onSelectItem}
      variationsSpacing={variationsSpacing}
      selectedVariations={selectedVariations}
      showVariationsLabels={showVariationsLabels}
      showValueForVariation={showValueForVariation}
      hideImpossibleCombinations={hideImpossibleCombinations}
      disableUnavailableSelectOptions={disableUnavailableSelectOptions}
      showVariationsErrorMessage={showVariationsErrorMessage}
      sliderDisplayThreshold={sliderDisplayThreshold}
      sliderArrowSize={sliderArrowSize}
      sliderItemsPerPage={sliderItemsPerPage}
      sortVariationsByLabel={sortVariationsByLabel}
    />
  )
}

export default memo(SKUSelectorContainer)

```


[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 store-components/react/components/SKUSelector/styles.css from the store-components VTEX app.

[Assistant]
Here's the implementation:

```css
/**
*   SKU Selector CSS Module
*/
.skuSelectorContainer {
}
.skuSelectorSubcontainer {
}
.skuSelectorName {
}
.skuSelectorNameContainer {
}
.skuSelectorNameSeparator {
}
.skuSelectorSelectorImageValue {
}
.skuSelectorTextContainer {
}

.skuSelectorSelectContainer {}

.skuSelectorItem {
  font-size: 90%;
  height: 36px;
}

.skuSelectorItemImage {
  padding: 0;
  height: 40px;
  width: 40px;
}

.skuSelectorItem p {
  margin: 0;
  padding-top: 10px;
}

.skuSelectorBagde {
  font-size: 0.71429rem;
  background-color: #ff4136;
  border-radius: 50%;
  color: #fff;
  height: 18px;
  line-height: 18px;
  right: -5px;
  text-align: center;
  top: -6px;
  width: 18px;
  position: absolute;
  padding: 3px;
}

.diagonalCross {
  background-image: linear-gradient(
    to top right,
    transparent 44%,
    rgba(0, 0, 0, 0.3) 48%,
    currentColor 48%,
    currentColor 52%,
    transparent 52%
  );
}

.frameAround {
  bottom: -0.25rem;
  top: -0.25rem;
  left: -0.25rem;
  right: -0.25rem;
}

.seeMoreButton {
}

.skuSelectorOptionsList {
}

```


[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 store-components/react/components/SKUSelector/SKUSelectorCssHandles.ts from the store-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { createCssHandlesContext } from 'vtex.css-handles'

import { SKU_SELECTOR_CSS_HANDLES } from './Wrapper'

const { CssHandlesProvider, useContextCssHandles } = createCssHandlesContext(
  SKU_SELECTOR_CSS_HANDLES
)

export {
  CssHandlesProvider as SKUSelectorCssHandlesProvider,
  useContextCssHandles as useSKUSelectorCssHandles,
}

```


[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 store-components/react/components/SKUSelector/Wrapper.tsx from the store-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useMemo, useEffect } from 'react'
import { useProduct, useProductDispatch } from 'vtex.product-context'
import { pick } from 'ramda'
import {
  useResponsiveValues,
  ResponsiveValuesTypes,
} from 'vtex.responsive-values'
import { useCssHandles } from 'vtex.css-handles'
import type { CssHandlesTypes } from 'vtex.css-handles'

import SKUSelector from './index'
import { CSS_HANDLES as SelectorItemCssHandles } from './components/SelectorItem'
import { CSS_HANDLES as ErrorMessageCssHandles } from './components/ErrorMessage'
import { Variations, InitialSelectionType, DisplayMode } from './types'
import {
  ShowValueForVariation,
  ShowVariationsLabels,
  CSS_HANDLES as SKUSelectorCssHandles,
} from './components/SKUSelector'
import { SKUSelectorCssHandlesProvider } from './SKUSelectorCssHandles'

export const SKU_SELECTOR_CSS_HANDLES = [
  ...ErrorMessageCssHandles,
  ...SelectorItemCssHandles,
  ...SKUSelectorCssHandles,
] as const

const getVariationsFromItems = (
  skuItems: ProductItem[],
  visibleVariations?: string[]
) => {
  const variations: Variations = {}
  const variationsSet: Record<string, Set<string>> = {}

  for (const skuItem of skuItems) {
    for (const currentVariation of skuItem.variations) {
      const { name, values } = currentVariation

      if (
        !visibleVariations ||
        visibleVariations.includes(name.toLowerCase().trim())
      ) {
        const [value] = values
        const currentSet = variationsSet[name] || new Set()

        currentSet.add(value)
        variationsSet[name] = currentSet
      }
    }
  }

  const variationsNames = Object.keys(variationsSet)

  // Transform set back to array
  for (const variationName of variationsNames) {
    const set = variationsSet[variationName]

    variations[variationName] = {
      originalName: variationName,
      values: Array.from(set).map(value => ({
        name: value,
        originalName: value,
      })),
    }
  }

  return variations
}

const getVariationsFromSpecifications = (
  skuSpecifications: SkuSpecification[],
  visibleVariations?: string[]
) => {
  const variations: Variations = {}

  for (const specification of skuSpecifications) {
    if (
      !visibleVariations ||
      visibleVariations.includes(
        specification.field.originalName.toLowerCase().trim()
      )
    ) {
      variations[specification.field.name] = {
        originalName: specification.field.originalName,
        values: specification.values.map(value => ({
          name: value.name,
          originalName: value.originalName,
        })),
      }
    }
  }

  return variations
}

const useVariations = ({
  skuItems,
  skuSpecifications,
  shouldNotShow,
  visibleVariations,
}: {
  skuItems: ProductItem[]
  skuSpecifications: SkuSpecification[]
  shouldNotShow: boolean
  visibleVariations?: string[]
}) => {
  const isSkuSpecificationsEmpty = skuSpecifications.length === 0
  /* if the skuSpecifications array has values, then it should be used to find
   * the variations, which will come ordered the same way they are in the catalog */
  const variationsSource = isSkuSpecificationsEmpty
    ? skuItems
    : skuSpecifications

  const result = useMemo(() => {
    if (
      shouldNotShow ||
      (visibleVariations && visibleVariations.length === 0)
    ) {
      return {}
    }

    let formattedVisibleVariations = visibleVariations

    if (visibleVariations) {
      formattedVisibleVariations = visibleVariations.map(variation =>
        variation.toLowerCase().trim()
      )
    }

    return isSkuSpecificationsEmpty
      ? getVariationsFromItems(
          variationsSource as ProductItem[],
          formattedVisibleVariations
        )
      : getVariationsFromSpecifications(
          variationsSource as SkuSpecification[],
          formattedVisibleVariations
        )
  }, [
    variationsSource,
    shouldNotShow,
    visibleVariations,
    isSkuSpecificationsEmpty,
  ])

  return result
}

interface Props {
  skuItems: ProductItem[]
  skuSelected: ProductItem | null
  onSKUSelected?: (skuId: string) => void
  maxItems?: number
  visibility?: string
  seeMoreLabel: string
  hideImpossibleCombinations?: boolean
  disableUnavailableSelectOptions?: boolean
  showValueNameForImageVariation?: boolean
  showValueForVariation?: ShowValueForVariation
  imageHeight?: ResponsiveValuesTypes.ResponsiveValue<number>
  imageWidth?: ResponsiveValuesTypes.ResponsiveValue<number>
  thumbnailImage?: string
  visibleVariations?: string[]
  showVariationsLabels?: ShowVariationsLabels
  variationsSpacing?: number
  showVariationsErrorMessage?: boolean
  initialSelection?: InitialSelectionType
  displayMode?: ResponsiveValuesTypes.ResponsiveValue<DisplayMode>
  sliderDisplayThreshold?: number
  sliderArrowSize?: number
  sliderItemsPerPage?: ResponsiveValuesTypes.ResponsiveValue<number>
  sortVariationsByLabel?: boolean
  /** Used to override default CSS handles */
  classes?: CssHandlesTypes.CustomClasses<typeof SKU_SELECTOR_CSS_HANDLES>
}

function SKUSelectorWrapper(props: Props) {
  const { handles, withModifiers } = useCssHandles(SKU_SELECTOR_CSS_HANDLES, {
    classes: props.classes,
  })

  const valuesFromContext = useProduct()
  const dispatch = useProductDispatch()
  const { imageHeight, imageWidth } = useResponsiveValues(
    pick(['imageHeight', 'imageWidth'], props)
  )

  const shouldSelectInitialSKU = props.initialSelection !== 'empty'

  const skuItems =
    props.skuItems != null
      ? props.skuItems
      : valuesFromContext?.product?.items ?? []

  let skuSelected = props.skuSelected ?? null

  if (shouldSelectInitialSKU && skuSelected == null) {
    skuSelected = valuesFromContext.selectedItem
  }

  const visibility = props.visibility != null ? props.visibility : 'always'

  const shouldNotShow =
    (shouldSelectInitialSKU && skuSelected == null) ||
    skuItems.length === 0 ||
    skuSelected?.variations.length === 0 ||
    (visibility === 'more-than-one' && skuItems.length === 1)

  const skuSpecifications = valuesFromContext?.product?.skuSpecifications ?? []
  const variations = useVariations({
    skuItems,
    skuSpecifications,
    shouldNotShow,
    visibleVariations: props.visibleVariations,
  })

  useEffect(() => {
    if (dispatch) {
      dispatch({
        type: 'SKU_SELECTOR_SET_IS_VISIBLE',
        args: { isVisible: !shouldNotShow },
      })
    }
  }, [shouldNotShow, dispatch])

  if (shouldNotShow) {
    return null
  }

  let showValueForVariation: ShowValueForVariation = 'none'

  if (props.showValueForVariation) {
    showValueForVariation = props.showValueForVariation
  } else if (props.showValueNameForImageVariation) {
    showValueForVariation = 'image'
  }

  return (
    <SKUSelectorCssHandlesProvider
      handles={handles}
      withModifiers={withModifiers}
    >
      <SKUSelector
        skuItems={skuItems}
        variations={variations}
        imageWidth={imageWidth}
        skuSelected={skuSelected}
        maxItems={props.maxItems}
        imageHeight={imageHeight}
        displayMode={props.displayMode}
        seeMoreLabel={props.seeMoreLabel}
        onSKUSelected={props.onSKUSelected}
        thumbnailImage={props.thumbnailImage}
        initialSelection={props.initialSelection}
        variationsSpacing={props.variationsSpacing}
        showValueForVariation={showValueForVariation}
        showVariationsLabels={props.showVariationsLabels}
        hideImpossibleCombinations={props.hideImpossibleCombinations}
        disableUnavailableSelectOptions={props.disableUnavailableSelectOptions}
        showVariationsErrorMessage={props.showVariationsErrorMessage}
        sliderItemsPerPage={props.sliderItemsPerPage}
        sliderArrowSize={props.sliderArrowSize}
        sliderDisplayThreshold={props.sliderDisplayThreshold}
        sortVariationsByLabel={props.sortVariationsByLabel}
      />
    </SKUSelectorCssHandlesProvider>
  )
}

SKUSelectorWrapper.schema = {
  title: 'admin/editor.skuSelector.title',
  description: 'admin/editor.skuSelector.description',
}

export default SKUSelectorWrapper

```


[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 store-components/react/components/Newsletter/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component, Fragment } from 'react'
import { graphql } from 'react-apollo'
import { compose } from 'ramda'
import PropTypes from 'prop-types'
import { injectIntl } from 'react-intl'
import { Input, Button } from 'vtex.styleguide'
import { withCssHandles } from 'vtex.css-handles'
import { formatIOMessage } from 'vtex.native-types'
import { PixelContext } from 'vtex.pixel-manager'

import SUBSCRIBE_NEWSLETTER from './mutations/subscribeNewsletter.graphql'

const EMAIL_REGEX =
  /^[A-z0-9+_-]+(?:\.[A-z0-9+_-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/

const CSS_HANDLES = [
  'newsletter',
  'confirmation',
  'container',
  'confirmationTitle',
  'confirmationText',
  'form',
  'inputGroup',
  'buttonContainer',
  'label',
  'error',
]

class Newsletter extends Component {
  state = {
    email: '',
    loading: false,
    error: null,
    success: null,
    invalidEmail: false,
  }

  inputRef = React.createRef()

  componentDidMount() {
    this.mounted = true
    this.props.push({ event: 'newsletterInput' })
  }

  componentWillUnmount() {
    this.mounted = false
  }

  safeSetState(...params) {
    if (this.mounted) {
      this.setState(...params)
    }
  }

  handleChangeEmail = e => {
    this.setState({ email: e.target.value.trim() })
  }

  validateEmail = () => {
    return EMAIL_REGEX.test(this.state.email)
  }

  handleSubmit = e => {
    e.preventDefault()
    if (!this.validateEmail()) {
      this.setState({ invalidEmail: true })
      if (this.inputRef && this.inputRef.current) {
        this.inputRef.current.focus()
      }

      return
    }

    this.setState({
      invalidEmail: false,
      loading: true,
      error: null,
      success: null,
    })

    this.props
      .subscribeNewsletter({ variables: { email: this.state.email } })
      .then(() => {
        this.safeSetState({ success: true, loading: false })
      })
      .catch(() => {
        this.safeSetState({ error: true, loading: false })
      })
  }

  render() {
    const { hideLabel, intl, submit, label, placeholder, handles } = this.props

    const submitText = formatIOMessage({ id: submit, intl })
    const labelText = formatIOMessage({ id: label, intl })
    const placeholderText = formatIOMessage({ id: placeholder, intl })
    const confirmationTitle = formatIOMessage({
      id: 'store/newsletter.confirmationTitle',
      intl,
    })

    const confirmationText = formatIOMessage({
      id: 'store/newsletter.confirmationText',
      intl,
    })

    const invalidEmailText = formatIOMessage({
      id: 'store/newsletter.invalidEmail',
      intl,
    })

    const errorMsg = formatIOMessage({
      id: 'store/newsletter.error',
      intl,
    })

    return (
      <div
        className={`${handles.newsletter} ${
          this.state.success ? handles.confirmation : ''
        } w-100`}
      >
        <div className={`${handles.container} mw9 mr-auto ml-auto pv9`}>
          {this.state.success ? (
            <Fragment>
              <div
                className={`${handles.confirmationTitle} t-heading-3 pb4 tc`}
              >
                {confirmationTitle}
              </div>
              <div className={`${handles.confirmationText} t-body tc`}>
                {confirmationText}
              </div>
            </Fragment>
          ) : (
            <form className={`${handles.form} mw6 center tc ph5 ph0-ns`}>
              <label
                className={`${handles.label} t-heading-3 tc ${
                  hideLabel ? 'dn' : ''
                }`}
                htmlFor="newsletter-input"
              >
                {labelText}
              </label>
              <div className={`${handles.inputGroup} flex-ns pt5`}>
                <Input
                  ref={this.inputRef}
                  id="newsletter-input"
                  errorMessage={
                    this.state.invalidEmail ? invalidEmailText : null
                  }
                  placeholder={placeholderText}
                  name="newsletter"
                  value={this.state.email}
                  onChange={this.handleChangeEmail}
                />
                <div
                  className={`${handles.buttonContainer} pl4-ns flex-none pt3 pt0-ns`}
                >
                  <Button
                    variation="primary"
                    type="submit"
                    onClick={this.handleSubmit}
                    isLoading={this.state.loading}
                  >
                    {submitText}
                  </Button>
                </div>
              </div>
              {this.state.error && (
                <div className={`${handles.error} c-danger t-body pt5`}>
                  {errorMsg}
                </div>
              )}
            </form>
          )}
        </div>
      </div>
    )
  }
}

const NewsletterWrapper = compose(
  graphql(SUBSCRIBE_NEWSLETTER, { name: 'subscribeNewsletter' }),
  withCssHandles(CSS_HANDLES),
  injectIntl,
  PixelContext.withPixel
)(Newsletter)

Newsletter.defaultProps = {
  hideLabel: false,
  showTerms: false,
}

Newsletter.propTypes = {
  hideLabel: PropTypes.bool.isRequired,
  showTerms: PropTypes.bool.isRequired,
  label: PropTypes.string,
  placeholder: PropTypes.string,
  submit: PropTypes.string,
  subscribeNewsletter: PropTypes.func.isRequired,
  intl: PropTypes.object,
  handles: PropTypes.any,
  push: PropTypes.func,
}

NewsletterWrapper.getSchema = () => {
  return {
    title: 'admin/editor.newsletter.title',
    description: 'admin/editor.newsletter.description',
    type: 'object',
    properties: {
      hideLabel: {
        type: 'boolean',
        title: 'admin/editor.newsletter.hideLabel',
        default: false,
        isLayout: true,
      },
    },
  }
}

export default NewsletterWrapper

```


[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 store-components/react/components/ProductHighlights/styles.css from the store-components VTEX app.

[Assistant]
Here's the implementation:

```css
/**
*   ProductHighlights CSS Modules
*/

.highlightContent {}
.itemHighlight {}
.highlightTitle {}
.highlightValue {}

```


[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 store-components/react/components/ProductHighlights/Wrapper.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useContext } from 'react'
import { ProductContext } from 'vtex.product-context'
import { isEmpty, propOr } from 'ramda'

import ProductHighlights from './index'

const ProductHighlightsWrapper = props => {
  const { conditional } = props

  const valuesFromContext = useContext(ProductContext)

  const getHighlights = () => {
    const { product } = valuesFromContext

    const choose = propOr('', 'highlight', conditional)

    if (choose === 'admin/editor.product-details.highlights.chooseDefault') {
      const typeHighlight = propOr('', 'typeHighlight', conditional)
      const highlightName = typeHighlight.trim()
      const names = highlightName.split(',')
      const specificationGroups = propOr([], 'specificationGroups', product)

      return names.reduce((acc, item) => {
        const [highlightSpecificationGroup] = specificationGroups.filter(
          x => x.name.toLowerCase() === item.trim().toLowerCase()
        )

        const highlight = propOr(
          [],
          'specifications',
          highlightSpecificationGroup
        )

        return acc.concat(highlight)
      }, [])
    }

    if (
      choose ===
      'admin/editor.product-details.highlights.chooseDefaultSpecification'
    ) {
      const typeSpecifications = propOr('', 'typeSpecifications', conditional)
      const specificationNames = typeSpecifications.trim().split(',')
      const allSpecifications = propOr([], 'properties', product)

      return specificationNames.reduce((acc, item) => {
        const highlight = allSpecifications.filter(
          x => x.name.toLowerCase() === item.trim().toLowerCase()
        )

        return acc.concat(highlight)
      }, [])
    }

    if (
      choose === 'admin/editor.product-details.highlights.allSpecifications'
    ) {
      return propOr([], 'properties', product)
    }
  }

  const productHighlightsProps = () => {
    if (!valuesFromContext || isEmpty(valuesFromContext)) {
      return props
    }

    return {
      ...props,
      highlights: props.highlights || getHighlights(),
    }
  }

  return <ProductHighlights {...productHighlightsProps()} />
}

ProductHighlightsWrapper.schema = {
  // @TODO review title and description
  title: 'admin/editor.product-details.title',
  description: 'admin/editor.product-details.description',
  type: 'object',
  definitions: {
    highlightGroupDefault: {
      title: 'highlightGroupDefault',
      type: 'object',
      properties: {
        highlight: {
          title: 'admin/editor.product-details.highlights.default',
          type: 'string',
          enum: [
            'admin/editor.product-details.highlights.allSpecifications',
            'admin/editor.product-details.highlights.chooseDefault',
            'admin/editor.product-details.highlights.chooseDefaultSpecification',
          ],
          default: 'admin/editor.product-details.highlights.allSpecifications',
        },
      },
      required: ['highlight'],
      dependencies: {
        highlight: {
          oneOf: [
            {
              properties: {
                highlight: {
                  enum: [
                    'admin/editor.product-details.highlights.allSpecifications',
                  ],
                },
              },
            },
            {
              properties: {
                highlight: {
                  enum: [
                    'admin/editor.product-details.highlights.chooseDefault',
                  ],
                },
                typeHighlight: {
                  type: 'string',
                  title: 'admin/editor.product-details.highlights.title',
                },
              },
              required: [''],
            },
            {
              properties: {
                highlight: {
                  enum: [
                    'admin/editor.product-details.highlights.chooseDefaultSpecification',
                  ],
                },
                typeSpecifications: {
                  type: 'string',
                  title:
                    'admin/editor.product-details.highlights.typeSpecifications.title',
                },
              },
              required: [''],
            },
          ],
        },
      },
    },
  },
  properties: {
    showHighlight: {
      type: 'boolean',
      title: 'admin/editor.product-details.showHighlight.title',
      default: false,
      isLayout: false,
    },
    conditional: {
      title: 'Conditional',
      $ref: '#/definitions/highlightGroupDefault',
    },
  },
}

export default ProductHighlightsWrapper

```


[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 store-components/react/components/ProductHighlights/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import PropTypes from 'prop-types'
import { injectIntl } from 'react-intl'

import { SanitizedHTML } from '../SanitizedHTML'
import styles from './styles.css'

/**
 * Product highlights Component.
 * Render the highlights specifications of a product.
 */
const ProductHighlights = ({ ...props }) => {
  const { highlights } = props

  return (
    <div className={`${styles.highlightContent} pt3 pb5`}>
      {highlights.map((item, i) => (
        <div
          className={`${styles.itemHighlight} pv2`}
          data-name={item.name}
          data-value={item.values[0]}
          key={i}
        >
          <span
            className={`${styles.highlightTitle} t-body c-on-base fw7 pr3 `}
          >
            <SanitizedHTML content={item.name} />
            {': '}
          </span>
          <span
            className={`${styles.highlightValue} t-body c-muted-1 lh-copy `}
          >
            <SanitizedHTML content={item.values[0]} />
          </span>
        </div>
      ))}
    </div>
  )
}

ProductHighlights.defaultProps = {
  highlights: [],
}

ProductHighlights.propTypes = {
  /** Intl object to provides internationalization */
  intl: PropTypes.object.isRequired,
  /** Specifications that will be displayed on the table */
  highlights: PropTypes.arrayOf(
    PropTypes.shape({
      /** Highlight name */
      name: PropTypes.string.isRequired,
      /** Highlight value */
      values: PropTypes.arrayOf(PropTypes.string).isRequired,
    })
  ),
}

export default injectIntl(ProductHighlights)

```


[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 store-components/react/components/UserAddress/UserAddressCssHandles.ts from the store-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { createCssHandlesContext } from 'vtex.css-handles'

import { USER_ADDRESS_CSS_HANDLES } from '../../UserAddress'

const { CssHandlesProvider, useContextCssHandles } = createCssHandlesContext(
  USER_ADDRESS_CSS_HANDLES
)

export {
  CssHandlesProvider as UserAddressCssHandlesProvider,
  useContextCssHandles as useUserAddressCssHandles,
}

```


[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 store-components/react/components/UserAddress/AddressInfo.tsx from the store-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Fragment } from 'react'
import { ExtensionPoint, useChildBlock } from 'vtex.render-runtime'
import { useIntl, FormattedMessage } from 'react-intl'
import { IconLocationMarker } from 'vtex.store-icons'

import { useUserAddressCssHandles } from './UserAddressCssHandles'

type Props = {
  // todo: fix orderform typings
  orderForm: any
  inverted: boolean
  inline: boolean
  showStreet: boolean
  showCityAndState: boolean
  showPostalCode: boolean
  showIfMasked: boolean
  showPrefix: boolean
  showIfEmpty: boolean
}

export const CSS_HANDLES = [
  'addressInfoIconContainer',
  'addressInfoTextContainer',
  'addressInfoPrefixContainer',
  'addressInfoAddressContainer',
  'addressInfoDivider',
  'addressInfoModalContainer',
] as const

const AddressInfo = ({
  inverted,
  inline,
  orderForm,
  showStreet,
  showCityAndState,
  showPostalCode,
  showIfMasked,
  showPrefix,
  showIfEmpty,
}: Props) => {
  const { shippingData } = orderForm
  const hasModal = !!useChildBlock({ id: 'modal' })
  const { handles } = useUserAddressCssHandles()
  const intl = useIntl()

  if (
    !shippingData ||
    !shippingData.address ||
    (!showIfMasked && shippingData.address.postalCode.includes('*'))
  ) {
    if (!showIfEmpty) {
      return null
    }

    return (
      <div
        className={`flex ${inline ? 'items-end' : 'items-center flex-auto'}`}
      >
        <div
          className={`${
            handles.addressInfoIconContainer
          } mr3 flex items-center ${
            inverted ? 'c-on-base--inverted' : 'c-muted-2'
          }`}
        >
          <IconLocationMarker size={27} viewBox="0 0 21 27" />
        </div>
        {hasModal ? (
          <div
            className={`${handles.addressInfoModalContainer} flex items-center`}
          >
            <ExtensionPoint
              id="modal"
              centered
              buttonLabel={intl.formatMessage({
                id: 'store/user-address.add',
              })}
              buttonClass={
                inverted ? 'c-on-base--inverted' : 'c-action-primary'
              }
              showTopBar={false}
            />
          </div>
        ) : (
          <div className={`${handles.addressInfoAddressContainer} truncate`}>
            <FormattedMessage id="store/user-address.add" />
          </div>
        )}
      </div>
    )
  }

  const { street, number, complement, addressType, city, state, postalCode } =
    shippingData.address

  let displayStreet = number ? `${street}, ${number}` : street

  if (complement) displayStreet = `${displayStreet} - ${complement}`

  const displayCityAndState = !!city && !!state ? `${city}, ${state}` : ''

  const displayAddress = `${showStreet ? displayStreet || '' : ''}${
    showStreet && (showCityAndState || showPostalCode) ? ', ' : ''
  }${showCityAndState ? displayCityAndState : ''}${
    showCityAndState && showPostalCode && !postalCode.includes('*')
      ? `${displayCityAndState ? ', ' : ''}`
      : ''
  }${showPostalCode && !postalCode.includes('*') ? postalCode : ''}`

  const isPickup = addressType === 'pickup'
  const friendlyName = orderForm?.pickupPointCheckedIn?.friendlyName ?? ''

  return (
    <div className={`flex ${inline ? 'items-end' : 'items-center flex-auto'}`}>
      <div className="flex flex-auto items-center">
        <div
          className={`${
            handles.addressInfoIconContainer
          } mr3 flex items-center ${
            inverted ? 'c-on-base--inverted' : 'c-muted-2'
          }`}
        >
          <IconLocationMarker size={27} viewBox="0 0 21 27" />
        </div>

        <div
          className={`${handles.addressInfoTextContainer} flex flex-auto flex-column`}
        >
          {showPrefix && (
            <div
              className={`${handles.addressInfoPrefixContainer} t-small ${
                inverted ? 'c-on-base--inverted' : 'c-muted-2'
              }`}
            >
              {isPickup ? (
                <FormattedMessage
                  id="store/user-address.pickup"
                  values={{ name: friendlyName }}
                />
              ) : (
                <FormattedMessage id="store/user-address.order" />
              )}
            </div>
          )}

          <div className={`${handles.addressInfoAddressContainer} truncate`}>
            {displayAddress}
          </div>
        </div>
      </div>
      {hasModal && (
        <Fragment>
          <div
            className={`${handles.addressInfoDivider} bl bw1 mh4 ${
              inline ? 'nb2' : ''
            } ${inverted ? 'b--on-base--inverted' : 'b--muted-5'}`}
            style={{
              height: '1.5rem',
            }}
          />

          <div
            className={`${handles.addressInfoModalContainer} flex items-center`}
          >
            <ExtensionPoint
              id="modal"
              centered
              buttonLabel={intl.formatMessage({
                id: 'store/user-address.change',
              })}
              buttonClass={
                inverted ? 'c-on-base--inverted' : 'c-action-primary'
              }
              showTopBar={false}
            />
          </div>
        </Fragment>
      )}
    </div>
  )
}

export default AddressInfo

```


[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 store-components/react/components/CategoriesHighlights/constants.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
export const ITEMS_PER_ROW = 2
export const SQUARED = 'squared'
export const RECTANGULAR = 'rectangular'

```


[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 store-components/react/components/CategoriesHighlights/categoriesHighlights.css from the store-components VTEX app.

[Assistant]
Here's the implementation:

```css
.squaredCategoriesHighlights:only-child,
.rectangularCategoriesHighlights:only-child {
  display: none;
}

:global(.vtex-carousel) + .squared,
:global(.vtex-carousel) + .rectangular {
  top: -5.625rem;
}

.squaredCard,
.rectangularCard {
  background: rgba(61, 61, 61, 0.8);
  height: 18.75rem;
}

.squaredCard {
  width: 18.75rem;
}

.rectangularCard {
  width: 37.5rem;
}

.squaredCardImage,
.rectangularCardImage {
  object-fit: cover;
}

.squaredCardImage {
  width: 18.75rem;
}

.rectangularCardImage {
  width: 37.5rem;
}

@media only screen and (min-width: 20em) and (max-width: 50em) {
  :global(.vtex-carousel) + .squaredCategoriesHighlights {
    top: -3.125rem;
  }

  :global(.vtex-carousel) + .rectangularCategoriesHighlights {
    top: 0;
  }

  .squaredCard,
  .squaredCardImage {
    height: 9.375rem;
    width: 9.375rem;
  }

  .rectangularCard,
  .rectangularCardImage {
    height: 12.5rem;
    width: 100%;
  }
}

@media only screen and (min-width: 50em) and (max-width: 64em) {
  :global(.vtex-carousel) + .squaredCategoriesHighlights,
  :global(.vtex-carousel) + .rectangularCategoriesHighlights {
    top: -4.375rem;
  }

  .squaredCard,
  .rectangularCard {
    margin: 0;
    height: 12.5rem;
  }

  .squaredCard,
  .squaredCardImage {
    width: 12.5rem;
  }

  .rectangularCard,
  .rectangularCardImage {
    width: 25rem;
  }

  .squaredCardImage,
  .rectangularCardImage {
    height: 12.5rem;
  }
}
```


[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 store-components/react/components/CategoriesHighlights/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import { range, values } from 'ramda'
import React, { Component } from 'react'

import categoriesHighlights from './categoriesHighlights.css'
import CategoryCard from './components/CategoryCard'
import { ITEMS_PER_ROW, RECTANGULAR, SQUARED } from './constants.js'

/**
 * CategoriesHighlights is a component responsible to display the
 * Categories highlighted in a department.
 *
 * @deprecated This component is deprecated.
 */
class CategoriesHighlights extends Component {
  static propTypes = {
    /** Categories highlighted in the department */
    categoriesHighlighted: PropTypes.object,
    /** Flag which indicates if the categories highlighted should be displayed or not */
    showCategoriesHighlighted: PropTypes.bool,
    /** Number of categories highlighted to be displayed (it should be 2 or 4) */
    quantityOfItems: PropTypes.number.isRequired,
    /** Shape of the card box which wraps each category (it should be 'squared' or 'rectangular')  */
    cardShape: PropTypes.oneOf([SQUARED, RECTANGULAR]).isRequired,
  }

  static defaultProps = {
    categoriesHighlighted: {},
    showCategoriesHighlighted: false,
    quantityOfItems: ITEMS_PER_ROW,
    cardShape: SQUARED,
  }

  static uiSchema = {
    categoriesHighlighted: {
      items: {
        image: {
          'ui:widget': 'image-uploader',
        },
      },
    },
  }

  static getSchema = ({ quantityOfItems }) => {
    const categoriesHighlightedProps = {}

    range(0, quantityOfItems || ITEMS_PER_ROW).forEach(index => {
      categoriesHighlightedProps[`category${index}`] = {
        type: 'object',
        title: 'admin/editor.categoriesHighlighted.category',
        properties: {
          name: {
            type: 'string',
            default: '',
            title: 'admin/editor.categoriesHighlighted.item.categoryName',
          },
          image: {
            type: 'string',
            title: 'admin/editor.categoriesHighlighted.item.categoryImage',
            default: '',
            widget: {
              'ui:widget': 'image-uploader',
            },
          },
        },
      }
    })

    return {
      title: 'admin/editor.categoriesHighlighted.title',
      description: 'admin/editor.categoriesHighlighted.description',
      type: 'object',
      properties: {
        showCategoriesHighlighted: {
          type: 'boolean',
          title: 'admin/editor.categoriesHighlighted.showCategoriesHighlighted',
          default: false,
          isLayout: true,
        },
        quantityOfItems: {
          type: 'number',
          title: 'admin/editor.categoriesHighlighted.quantityOfItems',
          enum: [2, 4],
          default: 2,
          widget: {
            'ui:widget': 'radio',
            'ui:options': {
              inline: true,
            },
          },
          isLayout: true,
        },
        cardShape: {
          type: 'string',
          title: 'admin/editor.categoriesHighlighted.cardShape',
          enum: [SQUARED, RECTANGULAR],
          enumNames: [
            'admin/editor.categoriesHighlighted.cardShape.squared',
            'admin/editor.categoriesHighlighted.cardShape.rectangular',
          ],
          default: SQUARED,
          widget: {
            'ui:widget': 'radio',
            'ui:options': {
              inline: true,
            },
          },
          isLayout: true,
        },
        categoriesHighlighted: {
          type: 'object',
          title: 'admin/editor.categoriesHighlighted.categoriesHighlighted',
          properties: categoriesHighlightedProps,
          isLayout: false,
        },
      },
    }
  }

  render() {
    const {
      categoriesHighlighted,
      showCategoriesHighlighted,
      quantityOfItems,
      cardShape,
    } = this.props

    if (!showCategoriesHighlighted) return null

    const categories = values(categoriesHighlighted).map(category => category)

    range(categories.length, quantityOfItems).forEach(() => {
      categories.push({
        name: '',
        image: '',
      })
    })

    return (
      <div
        className={`${
          categoriesHighlights[`${cardShape}CategoriesHighlights`]
        } relative`}
      >
        <div className="flex flex-row flex-wrap items-center justify-center">
          {range(0, quantityOfItems / ITEMS_PER_ROW).map(indexRow => (
            <div
              key={`row${indexRow}`}
              className="flex flex-row flex-wrap items-center justify-center"
            >
              {range(0, ITEMS_PER_ROW).map(indexCol => (
                <CategoryCard
                  key={2 * indexRow + indexCol}
                  shape={cardShape}
                  {...categories[2 * indexRow + indexCol]}
                />
              ))}
            </div>
          ))}
        </div>
      </div>
    )
  }
}

export default CategoriesHighlights

```


[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 store-components/react/components/Share/styles.css from the store-components VTEX app.

[Assistant]
Here's the implementation:

```css
.shareContainer {}
.shareLoader {}
.shareLabel {}
.shareButtons {}
.shareSocialButton {}
.shareSocialIcon {}
```


[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 store-components/react/components/Share/Wrapper.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useContext } from 'react'
import { ProductContext } from 'vtex.product-context'
import { path, isEmpty } from 'ramda'
import { injectIntl } from 'react-intl'
import { useRuntime } from 'vtex.render-runtime'
import { usePixel } from 'vtex.pixel-manager'

import Share from './index'

const ShareWrapper = props => {
  const { intl } = props

  const valuesFromContext = useContext(ProductContext)
  const { getSettings, account } = useRuntime()
  const settings = getSettings('vtex.store')

  const { push } = usePixel()

  const shareProps = () => {
    if (!valuesFromContext || isEmpty(valuesFromContext)) {
      return props
    }

    const { selectedItem, product } = valuesFromContext

    const title = intl.formatMessage(
      { id: 'store/share.title' },
      {
        product: path(['productName'], product),
        sku: path(['name'], selectedItem),
        store: settings.storeName || account,
      }
    )

    return {
      ...props,
      shareLabelClass: props.shareLabelClass || 'c-muted-2 t-small mb3',
      className: props.className || 'db',
      imageUrl:
        props.imageUrl || path(['items', 0, 'images', 0, 'imageUrl'], product),
      loading:
        props.loading != null ? props.loading : !path(['name'], selectedItem),
      title: props.title || title,
      sendShareEvent: method => {
        push({
          event: 'share',
          itemId: product.productId,
          contentType: 'product',
          method,
        })
      },
    }
  }

  return <Share {...shareProps()} />
}

ShareWrapper.schema = Share.schema

export default injectIntl(ShareWrapper)

```


[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 store-components/react/components/Share/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { indexBy, prop } from 'ramda'
import React, { Component } from 'react'
import ContentLoader from 'react-content-loader'
import { FormattedMessage } from 'react-intl'

import SocialButton from './components/SocialButton'
import { SOCIAL_LIST } from './constants/social'
import styles from './styles.css'

class Share extends Component {
  static propTypes = {
    /** Social Networks configuration */
    social: PropTypes.object,
    /** Share buttons options */
    options: PropTypes.shape({
      /** Share buttons size in pixels */
      size: PropTypes.number,
    }),
    /** Share URL title */
    title: PropTypes.string,
    /** Indcates if the component should render the Content Loader */
    loading: PropTypes.bool,
    /** Component and content loader styles */
    styles: PropTypes.object,
    /** Classes to be applied to root element  */
    className: PropTypes.string,
    /** Classes to be applied to Share label */
    shareLabelClass: PropTypes.string,
    /** Classes to be applied to social button */
    socialButtonClass: PropTypes.string,
    /** Classes to be applied to container of the buttons */
    buttonsContainerClass: PropTypes.string,
    /** Classes to be applied to icon of the button */
    socialIconClass: PropTypes.string,
    /** Classes to be applied to the Content Loader container */
    loaderContainerClass: PropTypes.string,
    /** Classes to be applied to the Content Loader */
    contentLoaderClass: PropTypes.string,
    /** Image url for share in social medias */
    imageUrl: PropTypes.string,
    /** Function that sends share event to analytics */
    sendShareEvent: PropTypes.func,
  }

  static Loader = (loaderProps = {}) => {
    const {
      'vtex-share__button--loader': button,
      'vtex-share__button--loader-1': button1,
      'vtex-share__button--loader-2': button2,
      'vtex-share__button--loader-3': button3,
      containerClass,
      contentLoaderClass,
      ...rest
    } = loaderProps

    const loaderStyles = {
      r: '1em',
      height: '2em',
      cy: '1em',
      ...button,
    }

    return (
      <div
        className={classNames(
          styles.shareContainer,
          styles.shareLoader,
          containerClass
        )}
      >
        <ContentLoader
          className={contentLoaderClass}
          style={{
            width: '100%',
            height: '100%',
          }}
          height="100"
          width="100"
          {...rest}
        >
          <circle cx="1em" {...loaderStyles} {...button1} />
          <circle cx="3.5em" {...loaderStyles} {...button2} />
          <circle cx="6em" {...loaderStyles} {...button3} />
        </ContentLoader>
      </div>
    )
  }

  static defaultProps = {
    social: {
      Facebook: true,
      Twitter: true,
      WhatsApp: true,
      Pinterest: true,
    },
    options: {},
    className: 'flex flex-row flex-wrap w-100',
    shareLabelClass: 'pv2 pr3 t-small',
    buttonsContainerClass: 'flex flex-row',
  }

  static schema = {
    title: 'admin/editor.share.title',
    description: 'admin/editor.share.description',
    type: 'object',
    properties: {
      social: {
        title: 'admin/editor.share.social.title',
        type: 'object',
        properties: {
          ...indexBy(
            prop('title'),
            SOCIAL_LIST.map(socialNetwork => ({
              type: 'boolean',
              title: socialNetwork,
              default: Share.defaultProps.social[socialNetwork],
            }))
          ),
        },
      },
    },
  }

  render() {
    const {
      social,
      title,
      loading,
      options: { size },
      className,
      shareLabelClass,
      buttonsContainerClass,
      socialButtonClass,
      socialIconClass,
      loaderContainerClass,
      contentLoaderClass,
      imageUrl,
      sendShareEvent,
    } = this.props

    if (loading) {
      return (
        <Share.Loader
          containerClass={loaderContainerClass}
          contentLoaderClass={contentLoaderClass}
          {...this.props.styles}
        />
      )
    }

    return (
      <div className={classNames(styles.shareContainer, className)}>
        <div className={classNames(styles.shareLabel, shareLabelClass)}>
          <FormattedMessage id="store/store-components.share.label" />
        </div>
        <div className={classNames(styles.shareButtons, buttonsContainerClass)}>
          {Object.keys(social).map(
            (socialNetwork, index) =>
              social[socialNetwork] && (
                <SocialButton
                  key={index}
                  imageUrl={imageUrl}
                  url={window.location && window.location.href}
                  message={title}
                  iconClass={socialIconClass}
                  buttonClass={socialButtonClass}
                  socialEnum={socialNetwork}
                  size={size}
                  sendShareEvent={sendShareEvent}
                />
              )
          )}
        </div>
      </div>
    )
  }
}

export default Share

```


[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 store-components/react/components/ShippingSimulator/constants/PricingMode.ts from the store-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const GROUPED = 'grouped'
export const INDIVIDUAL_ITEMS = 'individualItems'

```


[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 store-components/react/components/ShippingSimulator/queries/getShippingEstimates.gql from the store-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
query getShippingEstimates(
  $items: [ShippingItem]
  $postalCode: String
  $country: String
) {
  shipping(items: $items, postalCode: $postalCode, country: $country) {
    logisticsInfo {
      itemIndex
      slas {
        id
        friendlyName
        price
        shippingEstimate
        shippingEstimateDate
      }
    }
  }
}

```


[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 store-components/react/components/ShippingSimulator/utils/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
let addressId = 1

const getRandomAddressId = () =>
  (addressId++ * new Date().getTime() * -1).toString().replace('-', '')

export const getNewAddress = (country, postalCode) => {
  const randomAddressId = getRandomAddressId()

  return {
    addressId: randomAddressId,
    addressType: 'residential',
    city: null,
    complement: null,
    country,
    geoCoordinates: [],
    neighborhood: null,
    number: null,
    postalCode: postalCode ?? null,
    receiverName: null,
    reference: null,
    state: null,
    street: null,
    addressQuery: null,
  }
}

```


[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 store-components/react/components/ShippingSimulator/components/ShippingTableRow.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import PropTypes from 'prop-types'
import { useIntl } from 'react-intl'
import TranslateEstimate from 'vtex.shipping-estimate-translator/TranslateEstimate'
import classNames from 'classnames'
import { FormattedCurrency } from 'vtex.format-currency'

import styles from '../shippingSimulator.css'

const ShippingTableRow = ({ name, shippingEstimate, price }) => {
  const { formatMessage } = useIntl()
  const etaClassName = classNames(
    `${styles.shippingTableCell} ${styles.shippingTableCellDeliveryEstimate} pv1 ph3 t-small c-muted-2`,
    {
      tc: typeof shippingEstimate === 'undefined',
    }
  )

  const valueClassName = classNames(
    `${styles.shippingTableCell} ${styles.shippingTableCellDeliveryPrice} pv1 ph3 t-small c-muted-2`,
    {
      tc: typeof price === 'undefined',
    }
  )

  let valueText

  if (typeof price === 'undefined') {
    valueText = '-'
  } else if (price === 0) {
    valueText = formatMessage({ id: 'store/shipping.free' })
  } else {
    valueText = <FormattedCurrency value={price / 100} />
  }

  return (
    <tr className={styles.shippingTableRow} key={name}>
      <td
        className={`${styles.shippingTableCell} ${styles.shippingTableCellDeliveryName} pv1 ph3 t-small`}
      >
        <label className={styles.shippingTableLabel}>
          <input
            className={`${styles.shippingTableRadioBtn} mr4`}
            name="shipping-option"
            type="radio"
            value={name}
          />
          {name}
        </label>
      </td>
      <td className={etaClassName}>
        <TranslateEstimate shippingEstimate={shippingEstimate} />
      </td>
      <td className={valueClassName}>{valueText}</td>
    </tr>
  )
}

ShippingTableRow.propTypes = {
  name: PropTypes.string,
  shippingEstimate: PropTypes.string,
  price: PropTypes.number,
}

export default ShippingTableRow

```


[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 store-components/react/components/ShippingSimulator/components/ShippingTable.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'

import { GROUPED } from '../constants/PricingMode'
import ShippingTableRow from './ShippingTableRow'
import styles from '../shippingSimulator.css'

const ShippingTable = ({ shipping, pricingMode }) => {
  if ((shipping?.logisticsInfo?.length ?? 0) === 0) {
    return null
  }

  const slaList = shipping.logisticsInfo.reduce(
    (slas, info) => [...slas, ...info.slas],
    []
  )

  const slaSumValuesList = []

  if (pricingMode === GROUPED) {
    slaList.reduce(function (res, value) {
      if (!res[value.id]) {
        res[value.id] = { id: value.id, ...value, price: 0 }
        slaSumValuesList.push(res[value.id])
      }

      res[value.id].price += value.price

      return res
    }, {})
  }

  if (slaList.length === 0) {
    return (
      <FormattedMessage id="store/shipping.empty-sla">
        {text => (
          <span className={`${styles.shippingNoMessage} dib t-small mt4`}>
            {text}
          </span>
        )}
      </FormattedMessage>
    )
  }

  return (
    <table
      className={`${styles.shippingTable} bt bb b--muted-4 c-muted-1 ph0 pv3 mt4 w-100`}
    >
      <thead className={`${styles.shippingTableHead} dn`}>
        <tr className={styles.shippingTableRow}>
          <th className={styles.shippingTableHeadDeliveryName}>
            <FormattedMessage id="store/shipping.deliveryName" />
          </th>
          <th className={styles.shippingTableHeadDeliveryEstimate}>
            <FormattedMessage id="store/shipping.deliveryEstimate" />
          </th>
          <th className={styles.shippingTableHeadDeliveryPrice}>
            <FormattedMessage id="store/shipping.deliveryPrice" />
          </th>
        </tr>
      </thead>
      <tbody className={styles.shippingTableBody}>
        {(pricingMode === GROUPED ? slaSumValuesList : slaList).map(
          shippingItem => (
            <ShippingTableRow
              key={shippingItem.id}
              name={shippingItem.friendlyName}
              shippingEstimate={shippingItem.shippingEstimate}
              price={shippingItem.price}
            />
          )
        )}
      </tbody>
    </table>
  )
}

ShippingTable.propTypes = {
  /** Shipping informations */
  shipping: PropTypes.shape({
    logisticsInfo: PropTypes.arrayOf(
      PropTypes.shape({
        itemIndex: PropTypes.string,
        slas: PropTypes.arrayOf(
          PropTypes.shape({
            id: PropTypes.string,
            friendlyName: PropTypes.string,
            price: PropTypes.number,
            shippingEstimate: PropTypes.string,
          })
        ),
      })
    ),
  }),
  pricingMode: PropTypes.enum,
}

export default ShippingTable

```


[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 store-components/react/components/BuyButton/mutations/setOpenMinicart.gql from the store-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation setMinicartOpen($isOpen: Boolean!) {
  setMinicartOpen(isOpen: $isOpen) @client
}

```


[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 store-components/react/components/BuyButton/mutations/addToCart.gql from the store-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation addToCart($items: [MinicartItem]) {
  addToCart(items: $items) @client
}

```


[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 store-components/react/components/BuyButton/queries/installedApp.gql from the store-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
query installedAppPublic($slug: String!) {
  installedAppPublic(slug: $slug) @context(provider: "vtex.apps-graphql") {
    version
  }
}

```