[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-on-localhost/src/monobloco/components/new/StoreComponents/components/BuyButton/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable react-hooks/exhaustive-deps */
import PropTypes from 'prop-types'
import React, { useContext, useCallback, useState, useEffect } from 'react'
import { FormattedMessage } from 'react-intl'
import { path } from 'ramda'
import ContentLoader from 'react-content-loader'
import { useRuntime } from '../../../../../../vtex.render-runtime'
import PWAContext from '../../../StoreResources/PWAContext'
import useCssHandles from '../../../CssHandles/useCssHandles'

import useProduct from '../../../ProductContext/useProduct'
import ProductDispatchContext from '../../../ProductContext/ProductDispatchContext'

import Button from '../../../Styleguide/Button'
import ToastContext from '../../../Styleguide/ToastContext'
import Tooltip from '../../../Styleguide/Tooltip'
import useMarketingSessionParams from './hooks/useMarketingSessionParams'

const { useProductDispatch } = ProductDispatchContext
const { usePWA } = PWAContext
const CONSTANTS = {
  SUCCESS_MESSAGE_ID: 'store/buybutton.buy-success',
  SELECT_SKU_ERROR_ID: 'store/buybutton.select-sku-variations',
  OFFLINE_BUY_MESSAGE_ID: 'store/buybutton.buy-offline-success',
  DUPLICATE_CART_ITEM_ID: 'store/buybutton.buy-success-duplicate',
  ERROR_MESSAGE_ID: 'store/buybutton.add-failure',
  SEE_CART_ID: 'store/buybutton.see-cart',
  TOAST_TIMEOUT: 3000,
}

const CSS_HANDLES = ['buyButtonContainer', 'buyButtonText']

const isTooltipNeeded = ({
  showTooltipOnSkuNotSelected,
  skuSelector,
}) => {
  if (showTooltipOnSkuNotSelected && !skuSelector.areAllVariationsSelected) {
    return {
      showTooltip: true,
      labelId: CONSTANTS.SELECT_SKU_ERROR_ID,
    }
  }
  return {
    showTooltip: false,
  }
}

const skuItemToMinicartItem = item => {
  return {
    // Important for the mutation
    id: item.skuId,
    seller: item.seller,
    options: item.options,
    quantity: item.quantity,

    // Fields for optmistic cart
    sellingPrice: item.price,
    skuName: item.variant,
    detailUrl: item.detailUrl,
    imageUrl: item.imageUrl,
    name: item.name,
    listPrice: item.listPrice,
    assemblyOptions: item.assemblyOptions,
    sellingPriceWithAssemblies: item.sellingPriceWithAssemblies,

    // Fields for Analytics
    brand: item.brand,
    category: item.category,
    productRefId: item.productRefId,
  }
}

const useCallCartFinishIfPending = (orderFormContext, isAddingToCart, addToCartAndFinish) => {
  const orderFormLoading = orderFormContext && orderFormContext.loading
  useEffect(() => {
    if (!orderFormLoading && isAddingToCart) {
      addToCartAndFinish()
    }
  }, [orderFormLoading])
}

/**
 * BuyButton Component.
 * Adds a list of sku items to the cart.
 */
export const BuyButton = ({
  intl,
  large,
  addToCart,
  skuItems,
  onAddStart,
  onAddFinish,
  setMinicartOpen,
  available = true,
  orderFormContext,
  isOneClickBuy = false,
  children,
  disabled: disabledProp,
  shouldAddToCart = true,
  shouldOpenMinicart = false,
  showTooltipOnSkuNotSelected = true,
  checkoutUrl,
  customToastURL = checkoutUrl,
}) => {
  const handles = useCssHandles(CSS_HANDLES)
  const [isAddingToCart, setAddingToCart] = useState(false)
  const { showToast } = useContext(ToastContext)
  const {
    skuSelector = {
      areAllVariationsSelected: true,
    },
  } = useProduct()
  const dispatch = useProductDispatch()
  const { settings = {}, showInstallPrompt } = usePWA() || {}
  const { promptOnCustomEvent } = settings
  const translateMessage = useCallback(id => intl.formatMessage({ id: id }), [
    intl,
  ])
  const orderFormItems = path(['orderForm', 'items'], orderFormContext)
  const { utmParams, utmiParams } = useMarketingSessionParams()

  const resolveToastMessage = (success, isNewItem) => {
    if (!success) return translateMessage(CONSTANTS.ERROR_MESSAGE_ID)
    if (!isNewItem) return translateMessage(CONSTANTS.DUPLICATE_CART_ITEM_ID)

    const isOffline = window && window.navigator && !window.navigator.onLine
    const checkForOffline = !isOffline
      ? translateMessage(CONSTANTS.SUCCESS_MESSAGE_ID)
      : translateMessage(CONSTANTS.OFFLINE_BUY_MESSAGE_ID)

    return checkForOffline
  }

  const toastMessage = ({ success, isNewItem }) => {
    const message = resolveToastMessage(success, isNewItem)

    const action = success
      ? {
        label: translateMessage(CONSTANTS.SEE_CART_ID),
        href: customToastURL,
      }
      : undefined

    showToast({ message, action })
  }

  const { rootPath = '' } = useRuntime()
  const fullCheckoutUrl = rootPath + checkoutUrl

  const beforeAddToCart = event => {
    event.stopPropagation()
    event.preventDefault()

    setAddingToCart(true)
    onAddStart && onAddStart()
  }

  const addToCartAndFinish = async () => {
    let showToastMessage
    try {
      const minicartItems = skuItems.map(skuItemToMinicartItem)
      const localStateMutationResult = !isOneClickBuy
        ? await addToCart(minicartItems)
        : null
      const linkStateItems =
        localStateMutationResult && localStateMutationResult.data.addToCart
      const callOrderFormDirectly = !linkStateItems

      let success = null
      if (callOrderFormDirectly) {
        const variables = {
          orderFormId: orderFormContext.orderForm.orderFormId,
          items: skuItems.map(item => ({
            id: item.skuId,
            seller: item.seller,
            options: item.options,
            quantity: item.quantity,
          })),
          ...(utmParams ? {utmParams} : {}),
          ...(utmiParams ? {utmiParams} : {}),
        }
        const mutationRes = await orderFormContext.addItem({ variables })
        const { items } = mutationRes.data.addItem

        success = skuItems.filter(
          skuItem =>
            !!items.find(
              ({ id, seller }) =>
                id === skuItem.skuId && seller === skuItem.seller
            )
        )
        await orderFormContext.refetch().catch(() => null)
      }

      const addedItem =
        (linkStateItems &&
          skuItems.filter(
            skuItem =>
              !!linkStateItems.find(
                ({ id, seller }) =>
                  id === skuItem.skuId && seller === skuItem.seller
              )
          )) ||
        success

      const foundItem =
        addedItem.length &&
        orderFormItems &&
        orderFormItems.filter(
          item =>
            item.id === addedItem[0].skuId &&
            item.seller === addedItem[0].seller
        ).length > 0

      success = addedItem

      showToastMessage = () =>
        toastMessage({
          success: success && success.length >= 1,
          isNewItem: !foundItem,
        })

      /* PWA */
      if (promptOnCustomEvent === 'addToCart' && showInstallPrompt) {
        showInstallPrompt()
      }

      shouldOpenMinicart && !isOneClickBuy && setMinicartOpen(true)
    } catch (err) {
      console.error(err)
      showToastMessage = () => toastMessage(false)
    }

    setTimeout(() => {
      setAddingToCart(false)
      showToastMessage()
      if (isOneClickBuy) {
        window.location.assign(fullCheckoutUrl)
      }
      onAddFinish && onAddFinish()
    }, 500)
  }

  const handleAddToCart = async event => {
    beforeAddToCart(event)
    await addToCartAndFinish()
  }

  useCallCartFinishIfPending(orderFormContext, isAddingToCart, addToCartAndFinish)

  const handleClick = e => {
    if (dispatch) {
      dispatch({ type: 'SET_BUY_BUTTON_CLICKED', args: { clicked: true } })
    }

    if (skuSelector.areAllVariationsSelected && shouldAddToCart) {
      if (orderFormContext && orderFormContext.loading) {
        // Just call the before add to cart function and the useEffect hook will call the finish part when apropriate
        beforeAddToCart(e)
      } else {
        handleAddToCart(e)
      }
    }
  }

  if (!skuItems) {
    return <ContentLoader />
  }

  const disabled = disabledProp || !available
  const unavailableLabel = (
    <FormattedMessage id="store/buyButton-label-unavailable">
      {message => <span className={handles.buyButtonText}>{message}</span>}
    </FormattedMessage>
  )

  const ButtonWithLabel = <Button
    block={large}
    disabled={disabled}
    onClick={handleClick}
    isLoading={isAddingToCart}
  >
    {available ? children : unavailableLabel}
  </Button>

  const { showTooltip, labelId } = isTooltipNeeded({
    showTooltipOnSkuNotSelected,
    skuSelector,
  })

  const tooltipLabel = showTooltip &&
    <span className={handles.errorMessage}>
      <FormattedMessage id={labelId} />
    </span>

  return !showTooltip ? (
    ButtonWithLabel
  ) : (
      <Tooltip trigger="click" label={tooltipLabel}>
        {ButtonWithLabel}
      </Tooltip>
    )
}

BuyButton.propTypes = {
  /** SKU Items to be added to the cart */
  skuItems: PropTypes.arrayOf(
    PropTypes.shape({
      /** Specification of which product will be added to the cart */
      skuId: PropTypes.string.isRequired,
      /** Quantity of the product sku to be added to the cart */
      quantity: PropTypes.number.isRequired,
      /** Which seller is being referenced by the button */
      seller: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
      /* Sku name */
      name: PropTypes.string.isRequired,
      /* Sku price */
      price: PropTypes.number.isRequired,
      /* Sku variant */
      variant: PropTypes.string,
      /* Sku brand */
      brand: PropTypes.string.isRequired,
      /* Sku options. In delivery, for examples, are the pizza options */
      options: PropTypes.arrayOf(
        PropTypes.shape({
          /* Option id */
          id: PropTypes.string.isRequired,
          /* Option quantity */
          quantity: PropTypes.number.isRequired,
          /* Option assembly id */
          assemblyId: PropTypes.string.isRequired,
          /* Option seller */
          seller: PropTypes.string,
        })
      ),
    })
  ),
  /** Component children that will be displayed inside of the button **/
  children: PropTypes.node.isRequired,
  /** Should redirect to checkout after adding to cart */
  isOneClickBuy: PropTypes.bool,
  /** Should open the Minicart after click */
  shouldOpenMinicart: PropTypes.bool,
  /** If it should add to cart when clicked */
  shouldAddToCart: PropTypes.bool,
  /** Set style to large */
  large: PropTypes.bool,
  /** If the product is available or not*/
  available: PropTypes.bool,
  /** If it should a tooltip when you click the button but there's no SKU selected */
  showTooltipOnSkuNotSelected: PropTypes.bool,
  /** Function used to show toasts (messages) to user */
  showToast: PropTypes.func,
  /** Function to be called on the start of add to cart click event */
  onAddStart: PropTypes.func,
  /** Function to be called on the end of add to cart event */
  onAddFinish: PropTypes.func,
  /** Add to cart mutation */
  addToCart: PropTypes.func.isRequired,
  /** Open Minicart mutation */
  setMinicartOpen: PropTypes.func.isRequired,
  /** The orderFormContext object */
  orderFormContext: PropTypes.object,
  /** If the button is disabled or not */
  disabled: PropTypes.bool,
  /** A custom URL for the `VIEW CART` button inside the toast */
  customToastURL: PropTypes.string,
  /** The URL to the cart **/
  checkoutUrl: PropTypes.string,
}

export default BuyButton

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/DiscountBadge/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
/**
*   DiscountBadge CSS Module.
*/

.discountContainer {
}
.discountInsideContainer {
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/DiscountBadge/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable react/style-prop-object */
import React from 'react'
import { FormattedNumber } from 'react-intl'
import PropTypes from 'prop-types'

import IOMessage from '../../../NativeTypes/IOMessage'

import styles from './styles.css'

const calculateDiscountTax = (listPrice, sellingPrice) => {
  return (listPrice - sellingPrice) / listPrice
}

/**
 * The discount badge component. It receives the product's list and selling prices
 * and calculates the discount percent to show it in the product's sumary.
 */
const DiscountBadge = ({ listPrice, sellingPrice, label = '', children }) => {
  const percent = calculateDiscountTax(listPrice, sellingPrice)
  const shouldShowPercentage = percent && percent >= 0.01

  return (
    <div className={`${styles.discountContainer} relative dib`}>
      {shouldShowPercentage ? (
        <div
          className={`${styles.discountInsideContainer} t-mini white absolute right-0 pv2 ph3 bg-emphasis z-1`}
        >
          <IOMessage id={label}>
            {labelValue => (
              <>
                {!labelValue && '-'}
                <FormattedNumber value={percent} style="percent" />{' '}
                {labelValue && ' '}
                {labelValue && <span>{labelValue}</span>}
              </>
            )}
          </IOMessage>
        </div>
      ) : null}
      {children}
    </div>
  )
}

DiscountBadge.propTypes = {
  /** The product's default price */
  listPrice: PropTypes.number.isRequired,
  /** The product's price with discount */
  sellingPrice: PropTypes.number.isRequired,
  /** Label to track the discount percent */
  label: PropTypes.string,
  /** Image element */
  children: PropTypes.node.isRequired,
}

export default DiscountBadge

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Greeting/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
/**
*   Greeting CSS Module
*/
.greetingContainer {
}
.message {
}
.firstName {
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Greeting/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import React, { Fragment } from 'react'
import { compose, branch, renderComponent } from 'recompose'
import { FormattedMessage } from 'react-intl'
import { path } from 'ramda'
import { graphql } from 'react-apollo'
import { loader } from 'graphql.macro'

import Loader from './Loader'

import styles from './styles.css'

const orderFormQuery = loader('./queries/orderForm.gql')

const Wrapper = ({ children }) => (
  <div
    className={`${
      styles.greetingContainer
    } mh4 pv4 t-heading-4 c-on-base nowrap`}
  >
    {children}
  </div>
)

const withWrapper = Component => props => (
  <Wrapper>
    <Component {...props} />
  </Wrapper>
)

const Greeting = ({ orderForm }) => {
  const firstName = path(['clientProfileData', 'firstName'], orderForm) || null
  return (
    firstName && (
      <Wrapper>
        <Fragment>
          <span className={styles.message}>
            <FormattedMessage id="store/greeting" />,
          </span>
          <span className={`${styles.firstName} pl2 b`}>{firstName}</span>
        </Fragment>
      </Wrapper>
    )
  )
}

Greeting.propTypes = {
  orderForm: PropTypes.shape({
    clientProfileData: PropTypes.shape({
      firstName: PropTypes.string,
    }),
  }),
}

const withLinkStateOrderForm = graphql(orderFormQuery, {
  props: ({ data: { minicart } }) => ({
    orderForm: minicart && minicart.orderForm && JSON.parse(minicart.orderForm),
  }),
})

export default compose(
  withLinkStateOrderForm,
  branch(({ loading }) => loading, renderComponent(withWrapper(Loader)))
)(Greeting)

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Greeting/Loader.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

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

const GreetingLoading = props => (
  <ContentLoader
    height={32}
    width={300}
    speed={2}
    primaryColor="#f3f3f3"
    secondaryColor="#ecebeb"
    className="v-mid"
    data-testid="greeting-loader"
    {...props}
  >
    <rect rx="1" ry="1" width="300" height="32" />
  </ContentLoader>
)

export default GreetingLoading

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Notification/notificationBar.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { memo } from 'react'
import { string } from 'prop-types'
import NotificationContent from './notificationContent'
import hoistNonReactStatics from 'hoist-non-react-statics'
import formatIOMessage from '../../../NativeTypes/formatIOMessage'
import { injectIntl } from 'react-intl'

import styles from './styles.css'

const NotificationBar = ({ content, intl }) => {
  return (
    content && (
      <div
        className={`${
          styles.notificationBarContainer
        } bg-base--inverted c-on-base--inverted w-100`}
      >
        <div
          className={`${
            styles.notificationBarInner
          } min-h-large flex items-center justify-center`}
        >
          <NotificationContent content={formatIOMessage({ id: content, intl })} />
        </div>
      </div>
    )
  )
}

NotificationBar.propTypes = {
  content: string,
}

NotificationBar.defaultProps = {
  content: '',
}

NotificationBar.schema = {
  title: 'admin/editor.notification-bar.title'
}

export default hoistNonReactStatics(injectIntl(NotificationBar), memo(NotificationBar))

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Notification/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
.notificationBarContainer {}
.notificationBarInner {}
.notificationContent {}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Notification/notificationInline.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { memo } from 'react'
import { string } from 'prop-types'
import NotificationContent from './notificationContent'

const NotificationInline = ({ content }) => {
  return content && <NotificationContent content={content} />
}

NotificationInline.propTypes = {
  content: string,
}

NotificationInline.defaultProps = {
  content: '',
}

NotificationInline.schema = {
  title: 'admin/editor.notification-bar.title',
  description: 'admin/editor.notification-bar.description',
  type: 'object',
  properties: {
    content: {
      title: 'admin/editor.notification-bar.content.title',
      description: 'admin/editor.notification-bar.content.description',
      type: 'string',
      default: '',
    },
  },
}

export default memo(NotificationInline)

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Notification/notificationContent.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { memo } from 'react'
import { string } from 'prop-types'
import insane from 'insane'

import styles from './styles.css'

const NotificationContent = ({ content }) => {
  const safeContent = { __html: insane(content) }
  return (
    <div
      className={styles.notificationContent}
      dangerouslySetInnerHTML={safeContent}
    />
  )
}

NotificationContent.propTypes = {
  content: string,
}

NotificationContent.defaultProps = {
  content: '',
}

export default memo(NotificationContent)

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductDescription/Wrapper.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useContext } from 'react'
import ProductContext from '../../../ProductContext/ProductContext'
import { path } from 'ramda'

import ProductDescription from './index'

const ProductDescriptionWrapper = props => {
  const valuesFromContext = useContext(ProductContext)
  const description =
    props.description != null
      ? props.description
      : path(['product', 'description'], valuesFromContext)

  return (
    <ProductDescription
      title={props.title}
      description={description}
      collapseContent={props.collapseContent}
    />
  )
}

ProductDescriptionWrapper.schema = {
  title: 'admin/editor.product-description.title',
}

ProductDescriptionWrapper.defaultProps = {
  collapseContent: true,
}

export default ProductDescriptionWrapper

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductDescription/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage, injectIntl } from 'react-intl'
import HtmlParser from 'react-html-parser'
import useCssHandles from '../../../CssHandles/useCssHandles'
import formatIOMessage from '../../../NativeTypes/formatIOMessage'

import GradientCollapse from '../GradientCollapse/index'

const CSS_HANDLES = [
  'productDescriptionContainer',
  'productDescriptionTitle',
  'productDescriptionText',
]

/**
 * Product Description Component.
 * Render the description of a product
 */
const ProductDescription = ({ description, collapseContent, title, intl }) => {
  if (!description) {
    return null
  }

  const descriptionParsed = useMemo(() => HtmlParser(description), [
    description,
  ])

  const handles = useCssHandles(CSS_HANDLES)

  return (
    <div className={handles.productDescriptionContainer}>
      <FormattedMessage id="store/product-description.title">
        {txt => (
          <h2
            className={`${handles.productDescriptionTitle} t-heading-5 mb5 mt0`}
          >
            {title ? formatIOMessage({ id: title, intl }) : txt}
          </h2>
        )}
      </FormattedMessage>

      <div className={`${handles.productDescriptionText} c-muted-1`}>
        {collapseContent ? (
          <GradientCollapse collapseHeight={220}>
            {descriptionParsed}
          </GradientCollapse>
        ) : (
          descriptionParsed
        )}
      </div>
    </div>
  )
}

ProductDescription.propTypes = {
  title: PropTypes.string,
  /** Product description string */
  description: PropTypes.string,
  collapseContent: PropTypes.bool,
}

export default injectIntl(memo(ProductDescription))

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/module/images.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { head } from 'ramda'

export const imageUrlForSize = (imageUrl: string, size: number) => {
  if (!imageUrl) {
    return ''
  }
  const urlSplitted = imageUrl.split('/')
  const idsStringIdx = urlSplitted.findIndex(content => content === 'ids')
  if (idsStringIdx < 0 || idsStringIdx === urlSplitted.length - 1)
    return imageUrl
  const sizeStringIdx = idsStringIdx + 1
  const sizeString = urlSplitted[sizeStringIdx]
  const imageId = head(sizeString.split('-'))
  const newSizeString = `${imageId}-${size * 2}-auto`
  return [
    ...urlSplitted.slice(0, sizeStringIdx),
    newSizeString,
    ...urlSplitted.slice(sizeStringIdx + 1),
  ].join('/')
}

export const VARIATION_IMG_SIZE = 40
export const IMAGE_DEFAULT_SIZE = 900
export const THUMB_SIZE = 150

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Animation/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { ANIMATIONS } from './animation'

const ANIMATION_TRANSFER = 110
const ANIMATION_TRANSFER_ENTER = 0
const ANIMATION_TIME = 0.4

/**
 * Animation component
 */
export default class Animation extends Component {
  static propTypes = {
    /* Object to be animated */
    children: PropTypes.node.isRequired,
    /* Active the animation */
    isActive: PropTypes.bool,
    /* Classname to the animation */
    className: PropTypes.string,
    /* Type of animation */
    type: PropTypes.oneOf([
      'drawerLeft',
      'drawerRight',
      'drawerTop',
      'drawerBottom',
    ]),
    /* The animation's duration in seconds */
    duration: PropTypes.number,
    /* The active animation deslocation in percentage */
    transfer: PropTypes.number,
    /* The not active animation deslocation in percentage */
    transferEnter: PropTypes.number,
  }

  static defaultProps = {
    className: '',
    type: 'drawerLeft',
    duration: ANIMATION_TIME,
    transfer: ANIMATION_TRANSFER,
    transferEnter: ANIMATION_TRANSFER_ENTER,
  }

  get animation() {
    const { isActive, type, duration, transfer, transferEnter } = this.props
    let animation = ANIMATIONS[type]
    if (isActive) {
      animation = animation['from'](duration, transferEnter)
    } else {
      animation = animation['leave'](duration, transfer)
    }
    return animation
  }

  render() {
    const { className, children } = this.props

    return (
      <div className={className} style={this.animation}>
        {children}
      </div>
    )
  }
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Animation/animation.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
const DRAWER_HORIZONTAL = 'drawerHorizontal'
const DRAWER_VERTICAL = 'drawerVertical'

export const DRAWER_ANIMATION = {
  [DRAWER_HORIZONTAL]: (duration, transfer) => ({
    transform: `translateX(${transfer}%)`,
    transition: `transform ${duration}s ease-in-out`,
  }),
  [DRAWER_VERTICAL]: (duration, transfer) => ({
    transform: `translateY(${transfer}%)`,
    transition: `transform ${duration}s ease-in-out`,
  }),
}

export const ANIMATIONS = {
  drawerLeft: {
    from: (duration, transfer) =>
      DRAWER_ANIMATION[DRAWER_HORIZONTAL](duration, -transfer),
    leave: (duration, transfer) =>
      DRAWER_ANIMATION[DRAWER_HORIZONTAL](duration, transfer),
  },
  drawerRight: {
    from: (duration, transfer) =>
      DRAWER_ANIMATION[DRAWER_HORIZONTAL](duration, transfer),
    leave: (duration, transfer) =>
      DRAWER_ANIMATION[DRAWER_HORIZONTAL](duration, -transfer),
  },
  drawerTop: {
    from: (duration, transfer) =>
      DRAWER_ANIMATION[DRAWER_VERTICAL](duration, -transfer),
    leave: (duration, transfer) =>
      DRAWER_ANIMATION[DRAWER_VERTICAL](duration, transfer),
  },
  drawerBottom: {
    from: (duration, transfer) =>
      DRAWER_ANIMATION[DRAWER_VERTICAL](duration, transfer),
    leave: (duration, transfer) =>
      DRAWER_ANIMATION[DRAWER_VERTICAL](duration, -transfer),
  },
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Slider/slider.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
.arrowRight:before {
    display: inline-block;
    width: 48px;
    height: 48px;
    background: url(./images/stre-right.svg);
    content: '';
}
.arrowLeft:before {
    display: inline-block;
    width: 48px;
    height: 48px;
    background: url(./images/stre-left.svg);
    content: '';
}

.dots {}
```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Slider/global.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
@import url('~slick-carousel/slick/slick.css');
@import url('~slick-carousel/slick/slick-theme.css');
@import url('./slider.css');

/* Fixes uneven arrows distance */
.slick-prev, .slick-next {
    width: inherit !important;
    height: inherit !important;
}

/* Clearfix for left arrow */
.slick-prev {
    left: 0 !important;
}

/* Clearfix for right arrow */
.slick-next {
    right: 0 !important;
}

.slick-dots {
    position: inherit !important;
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Slider/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Slider from 'react-slick'
import ReactResizeDetector from 'react-resize-detector'
import { NoSSR } from '../../../../../../vtex.render-runtime'

import Dots from './components/Dots'
import Arrow from './components/Arrow'

import getItemsPerPage from './utils/ItemsPerPage'

import './global.css'
import slider from './slider.css'

const VTEXClasses = {
  ARROW_RIGHT_CLASS: `${slider.arrowRight}`,
  ARROW_LEFT_CLASS: `${slider.arrowLeft}`,
  DOTS_CLASS: `${slider.dots}`,
}

/**
 * Slick Slider Component.
 */
export default class SlickSlider extends Component {
  static propTypes = {
    /** Array of items to be rendered inside the slider. */
    children: PropTypes.array.isRequired,
    /** Slider settings. */
    sliderSettings: PropTypes.object,
    /** Makes the items per page to adapt by the slider width. */
    adaptToScreen: PropTypes.bool,
    /** Default item width, it's necessary when the adaptToScreen is true. */
    defaultItemWidth: PropTypes.number,
    /** If the scroll of items is by page or not. */
    scrollByPage: PropTypes.bool,
    /** SSR fallback. */
    ssrFallback: PropTypes.element,
    /** Left arrow custom classes */
    leftArrowClasses: PropTypes.string,
    /** Right arrow custom classes */
    rightArrowClasses: PropTypes.string,
    /** Dots custom classes */
    dotsClasses: PropTypes.string,
  }

  getSettings(slideWidth) {
    const {
      sliderSettings,
      adaptToScreen,
      scrollByPage,
      defaultItemWidth,
      children,
      leftArrowClasses,
      rightArrowClasses,
      dotsClasses,
    } = this.props
    const itemsPerPage = getItemsPerPage(
      this._slick,
      slideWidth,
      defaultItemWidth,
      sliderSettings.slidesToShow
    )
    const settings = { ...sliderSettings }
    const numItems = children.length

    settings.nextArrow = settings.nextArrow || (
      <Arrow
        customClasses={rightArrowClasses}
        cssClass={VTEXClasses.ARROW_RIGHT_CLASS}
      />
    )
    settings.prevArrow = settings.prevArrow || (
      <Arrow
        customClasses={leftArrowClasses}
        cssClass={VTEXClasses.ARROW_LEFT_CLASS}
      />
    )
    settings.appendDots = dots => (
      <Dots
        dots={dots}
        customClass={dotsClasses}
        cssClass={VTEXClasses.DOTS_CLASS}
      />
    )

    if (adaptToScreen) {
      settings.slidesToShow = itemsPerPage
    }

    if (scrollByPage) {
      settings.slidesToScroll = settings.slidesToShow
    }
    if (settings.infinite === undefined) {
      settings.infinite = settings.slidesToScroll < numItems
    }
    return settings
  }

  render() {
    const component = (
      <ReactResizeDetector handleWidth>
        {width => (
          <Slider
            {...this.getSettings(width)}
            ref={c => {
              this._slick = c
            }}
          >
            {this.props.children}
          </Slider>
        )}
      </ReactResizeDetector>
    )
    if (this.props.ssrFallback) {
      return <NoSSR onSSR={this.props.ssrFallback}>{component}</NoSSR>
    }
    return component
  }
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductBrand/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import React, { useContext } from 'react'
import { Query } from 'react-apollo'
import ProductContext from '../../../ProductContext/ProductContext'
import useCssHandles from '../../../CssHandles/useCssHandles'

import { changeImageUrlSize } from './urlHelpers'
import { loader } from 'graphql.macro'

const brandLogoQuery = loader('./productBrand.gql')

const DISPLAY_MODE = {
  LOGO: 'logo',
  TEXT: 'text',
}

const CSS_HANDLES = [
  'productBrandContainer',
  'productBrandName',
  'productBrandLogo',
  'productBrandLogoWrapper',
  'productBrandLogoLink',
  'productBrandLogoSpacer',
  'productBrandNameSpacer',
]

const shouldExcludeBrand = (brandName, brandId, excludeList) => {
  if (Array.isArray(excludeList)) {
    return excludeList.includes(brandName) || excludeList.includes(brandId)
  }
  return false
}

const useBrandInfoProps = (brandName, brandId) => {
  const productContext = useContext(ProductContext)
  const product = productContext && productContext.product
  if ((brandName && brandId) || !product) {
    return { brandName, brandId }
  }
  return { brandName: product.brand, brandId: product.brandId }
}

const ProductBrand = ({
  displayMode = DISPLAY_MODE.LOGO,
  fallbackToText = true,
  loadingPlaceholder = DISPLAY_MODE.LOGO,
  /** TODO: decide whether the prop width should be supported
   * It makes sense at surface, but setting both width and height
   * messes with the alignment of the logo, due to how our image
   * server handles resizing. */
  height = 100,
  excludeBrands,
  logoWithLink,
  brandName: brandNameProp,
  brandId: brandIdProp,
}) => {
  const { brandName, brandId } = useBrandInfoProps(brandNameProp, brandIdProp)
  const handles = useCssHandles(CSS_HANDLES)

  if (!brandName || !brandId) {
    return null
  }

  /** Certain brands (e.g. placeholder brands) can be filtered out via theme config */
  if (shouldExcludeBrand(brandName, brandId, excludeBrands)) {
    return null
  }

  const brandNameElement = (
    <span className={`${handles.productBrandName}`}>{brandName}</span>
  )

  const BrandContainer = ({ children }) => (
    <div className={`${handles.productBrandContainer}`}>{children}</div>
  )

  if (displayMode === DISPLAY_MODE.TEXT) {
    return <BrandContainer>{brandNameElement}</BrandContainer>
  }

  if (!height) {
    console.warn(
      'ProductBrand: It is strongly recommended that the height of the logo should be set, to avoid shifting positions of elements while the logo is loading.'
    )
  }

  return (
    <Query query={brandLogoQuery} ssr={false} variables={{ id: brandId }}>
      {query => {
        const { data } = query
        if (data && data.brand) {
          const { imageUrl } = data.brand
          if (imageUrl) {
            const dpi = (window && window.devicePixelRatio) || 1
            const logoLink = '/' + brandName + '/b'
            const logoImage = (
              <img
                className={`${handles.productBrandLogo}`}
                src={changeImageUrlSize(
                  `/arquivos/ids${imageUrl}`,
                  undefined,
                  height ? height * dpi : undefined
                )}
                alt={brandName}
                style={{
                  height: height || 'auto',
                }}
              />
            )

            return (
              <BrandContainer>
                <div
                  style={{
                    height,
                  }}
                  className={`${handles.productBrandLogoWrapper}`}
                >
                  {/** TODO: Use a smarter Image component that handles VTEX image resizing etc. */}
                  {logoWithLink ? (
                    <a
                      href={logoLink}
                      className={`${handles.productBrandLogoLink}`}
                      data-testid="logo-redirect"
                    >
                      {' '}
                      {logoImage}{' '}
                    </a>
                  ) : (
                    logoImage
                  )}
                </div>
              </BrandContainer>
            )
          }

          /** If imageUrl is exactly null, it means no brand
           * logo was uploaded. So it falls back to the brand name,
           * if said fallback is enabled. */
          if (imageUrl === null && fallbackToText) {
            return <BrandContainer>{brandNameElement}</BrandContainer>
          }
        }

        /** Renders an empty spacer in the size of the preferred
         * type (i.e. if most brands have no logo, it should render
         * a placeholder in the size of the brand name text; if
         * most brands do have a logo, it should render a spacer
         * in the size of the predefined image size)
         */
        if (loadingPlaceholder === DISPLAY_MODE.LOGO) {
          return (
            <BrandContainer>
              <div
                style={{
                  height,
                }}
                className={`${handles.productBrandLogoSpacer}`}
              />
            </BrandContainer>
          )
        }
        if (loadingPlaceholder === DISPLAY_MODE.TEXT && fallbackToText) {
          return (
            <BrandContainer>
              <span className={`${handles.productBrandNameSpacer} o-0`}>
                {brandNameElement}
              </span>
            </BrandContainer>
          )
        }

        return null
      }}
    </Query>
  )
}

ProductBrand.propTypes = {
  /** Brand name */
  brandName: PropTypes.string,
  /** Brand id */
  brandId: PropTypes.number,
  /** Whether it should be displayed as a logo or as a text */
  displayMode: PropTypes.oneOf(Object.values(DISPLAY_MODE)),
  /** Whether the loading placeholder should have the size of the logo or the text */
  loadingPlaceholder: PropTypes.oneOf(Object.values(DISPLAY_MODE)),
  /** Whether it should display the name of the brand if there is no logo */
  fallbackToText: PropTypes.bool,
  /** List of brands that should be hidden, if any */
  excludeBrands: PropTypes.arrayOf(
    PropTypes.oneOfType([PropTypes.number, PropTypes.string])
  ),
  /** Height of the logo */
  height: PropTypes.number,
  /** CSS Handler */
  blockClass: PropTypes.string,
  logoWithLink: PropTypes.bool,
}

export default ProductBrand

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductBrand/productBrand.gql from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```graphql
query Brand($id: Int) {
  brand(id: $id) {
    imageUrl
  }
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductBrand/urlHelpers.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
export const DEFAULT_WIDTH = 'auto'
export const DEFAULT_HEIGHT = 'auto'
export const MAX_WIDTH = 3000
export const MAX_HEIGHT = 4000

/**
 * Having the url below as base for the LEGACY file manager,
 * https://storecomponents.vteximg.com.br/arquivos/ids/155472/Frame-3.jpg?v=636793763985400000
 * the following regex will match https://storecomponents.vteximg.com.br/arquivos/ids/155472
 *
 * Also matches urls with defined sizes like:
 * https://storecomponents.vteximg.com.br/arquivos/ids/155473-160-auto
 * @type {RegExp}
 *
 * On the new vtex.file-manager isn't necessary replace the URL, just add the param on the querystring, like:
 * "?width=WIDTH&height=HEIGHT&aspect=true"
 *
 */
const baseUrlRegex = new RegExp(/.+ids\/(\d+)/)

const httpRegex = new RegExp(/http:\/\//)

export function toHttps(url) {
  return url.replace(httpRegex, 'https://')
}

export function cleanImageUrl(imageUrl) {
  const result = baseUrlRegex.exec(imageUrl)
  if (result.length > 0) return result[0]
}

function replaceLegacyFileManagerUrl(imageUrl, width, height) {
  const legacyUrlPattern = '/arquivos/ids/'
  const isLegacyUrl = imageUrl.includes(legacyUrlPattern)
  if (!isLegacyUrl) return imageUrl
  return `${cleanImageUrl(imageUrl)}-${width}-${height}`
}

export function changeImageUrlSize(
  imageUrl,
  width = DEFAULT_WIDTH,
  height = DEFAULT_HEIGHT
) {
  if (!imageUrl) return
  typeof width === 'number' && (width = Math.min(width, MAX_WIDTH))
  typeof height === 'number' && (height = Math.min(height, MAX_HEIGHT))

  const normalizedImageUrl = replaceLegacyFileManagerUrl(
    imageUrl,
    width,
    height
  )
  const queryStringSeparator = normalizedImageUrl.includes('?') ? '&' : '?'

  return `${normalizedImageUrl}${queryStringSeparator}width=${width}&height=${height}&aspect=true`
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/InfoCard/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import classNames from 'classnames'
import insane from 'insane'
import { bool, string, oneOf } from 'prop-types'
import { values } from 'ramda'
import React, { memo, useMemo } from 'react'
import { injectIntl } from 'react-intl'
import { useRuntime } from '../../../../../../vtex.render-runtime'
import formatIOMessage from '../../../NativeTypes/formatIOMessage'
import useCssHandles from '../../../CssHandles/useCssHandles'

import CallToAction from './CallToAction'
import LinkWrapper from './LinkWrapper'
import {
  textPositionTypes,
  textAlignmentTypes,
  callToActionModeTypes,
  textPostionValues,
  textAlignmentValues,
} from './SchemaTypes'

const justifyTokens = {
  [textPostionValues.LEFT]: 'justify-start',
  [textPostionValues.CENTER]: 'justify-center',
  [textPostionValues.RIGHT]: 'justify-end',
}

const alignTokens = {
  [textAlignmentValues.LEFT]: 'tl',
  [textAlignmentValues.CENTER]: 'tc',
  [textAlignmentValues.RIGHT]: 'tr',
}

const itemsTokens = {
  [textAlignmentValues.LEFT]: 'items-start',
  [textAlignmentValues.CENTER]: 'items-center',
  [textAlignmentValues.RIGHT]: 'items-end',
}

const flexOrderTokens = {
  [textPostionValues.LEFT]: 'flex-row',
  [textPostionValues.RIGHT]: 'flex-row-reverse',
}

const defaultValues = {
  textPosition: textPositionTypes.TEXT_POSITION_LEFT.value,
  textAlignment: textAlignmentTypes.TEXT_ALIGNMENT_LEFT.value,
}

const getEnumValues = enumObject => values(enumObject).map(({ value }) => value)
const getEnumNames = enumObject => values(enumObject).map(({ name }) => name)

const safelyGetToken = (tokenMap, valueWanted, propName) =>
  tokenMap[valueWanted] || defaultValues[propName]

const getImageUrl = (isMobile, imageUrl, mobileImageUrl) =>
  !!mobileImageUrl && isMobile ? mobileImageUrl : imageUrl

const sanitizerConfig = {
  allowedTags: ['p', 'span', 'a', 'div', 'br'],
  allowedAttributes: {
    a: ['class', 'href', 'title', 'target'],
    span: ['class'],
    p: ['class'],
    div: ['class'],
  },
}

const sanitizeHtml = input => (input ? insane(input, sanitizerConfig) : null)

const CSS_HANDLES = [
  'infoCardContainer',
  'infoCardTextContainer',
  'infoCardHeadline',
  'infoCardSubhead',
  'infoCardImageContainer',
  'infoCardImage',
]

const InfoCard = ({
  isFullModeStyle,
  headline,
  subhead,
  callToActionMode,
  callToActionText,
  callToActionUrl,
  textPosition,
  textAlignment,
  imageUrl,
  mobileImageUrl,
  imageActionUrl,
  intl,
  htmlId,
}) => {
  const {
    hints: { mobile },
  } = useRuntime()
  const handles = useCssHandles(CSS_HANDLES)
  const paddingClass =
    textPosition === textPostionValues.LEFT ? 'pr4-ns' : 'pl4-ns'

  // We ignore textAlignment tokens when full image mode
  const alignToken = isFullModeStyle
    ? safelyGetToken(alignTokens, textPosition, 'textPosition')
    : safelyGetToken(alignTokens, textAlignment, 'textAlignment')
  const itemsToken = isFullModeStyle
    ? safelyGetToken(itemsTokens, textPosition, 'textPosition')
    : safelyGetToken(itemsTokens, textAlignment, 'textAlignment')
  const justifyToken = safelyGetToken(
    justifyTokens,
    textPosition,
    'textPosition'
  )
  const flexOrderToken = safelyGetToken(
    flexOrderTokens,
    textPosition,
    'textPosition'
  )

  const finalImageUrl = getImageUrl(
    mobile,
    formatIOMessage({ id: imageUrl, intl }),
    formatIOMessage({ id: mobileImageUrl, intl })
  )

  const containerStyle = isFullModeStyle
    ? { backgroundImage: `url(${finalImageUrl})`, backgroundSize: 'cover' }
    : {}

  const containerClasses = classNames(
    `${handles.infoCardContainer} items-center`,
    {
      [`flex-ns ${flexOrderToken} bg-base ph2-ns pb2 justify-between`]: !isFullModeStyle,
      [`bg-center bb b--muted-4 flex ${justifyToken}`]: isFullModeStyle,
    }
  )

  const textContainerClasses = classNames(
    `${handles.infoCardTextContainer} flex flex-column mw-100`,
    {
      [`w-50-ns ph3-s ${itemsToken} ${paddingClass}`]: !isFullModeStyle,
      [`mh8-ns mh4-s w-40-ns ${itemsToken}`]: isFullModeStyle,
    }
  )

  const headlineClasses = `${handles.infoCardHeadline} t-heading-2 mt6 ${alignToken} c-on-base mw-100`

  const subheadClasses = `${handles.infoCardSubhead} t-body mt6 c-on-base ${alignToken} mw-100`

  const sanitizedHeadline = useMemo(
    () => sanitizeHtml(formatIOMessage({ id: headline, intl })),
    [headline, intl]
  )

  const sanitizedSubhead = useMemo(
    () => sanitizeHtml(formatIOMessage({ id: subhead, intl })),
    [intl, subhead]
  )

  return (
    <LinkWrapper
      imageActionUrl={formatIOMessage({ id: imageActionUrl, intl })}
      extraCondition={!isFullModeStyle}
      linkProps={{ className: 'no-underline' }}
    >
      <div
        className={containerClasses}
        style={containerStyle}
        data-testid="container"
        id={htmlId}
      >
        <div className={textContainerClasses}>
          {headline && (
            <div
              className={headlineClasses}
              dangerouslySetInnerHTML={{ __html: sanitizedHeadline }}
            />
          )}
          {subhead && (
            <div
              className={subheadClasses}
              dangerouslySetInnerHTML={{ __html: sanitizedSubhead }}
            />
          )}
          <CallToAction
            mode={callToActionMode}
            text={formatIOMessage({ id: callToActionText, intl })}
            url={formatIOMessage({ id: callToActionUrl, intl })}
          />
        </div>
        {!isFullModeStyle && (
          <div className={`${handles.infoCardImageContainer} w-50-ns`}>
            <LinkWrapper
              imageActionUrl={formatIOMessage({ id: imageActionUrl, intl })}
            >
              <img
                className={handles.infoCardImage}
                src={finalImageUrl}
                style={{ objectFit: 'cover' }}
                alt=""
                data-testid="half-image"
              />
            </LinkWrapper>
          </div>
        )}
      </div>
    </LinkWrapper>
  )
}

const MemoizedInfoCard = memo(injectIntl(InfoCard))

MemoizedInfoCard.propTypes = {
  blockClass: string,
  isFullModeStyle: bool,
  textPosition: oneOf(getEnumValues(textPositionTypes)),
  headline: string,
  subhead: string,
  callToActionMode: oneOf(getEnumValues(callToActionModeTypes)),
  callToActionText: string,
  callToActionUrl: string,
  imageUrl: string,
  mobileImageUrl: string,
  textAlignment: oneOf(getEnumValues(textAlignmentTypes)),
  imageActionUrl: string,
  htmlId: string,
}

MemoizedInfoCard.defaultProps = {
  isFullModeStyle: false,
  textPosition: textPositionTypes.TEXT_POSITION_LEFT.value,
  headline: '',
  subhead: '',
  callToActionMode: callToActionModeTypes.CALL_ACTION_BUTTON.value,
  callToActionText: '',
  callToActionUrl: '',
  imageUrl: '',
  mobileImageUrl: '',
  textAlignment: textAlignmentTypes.TEXT_ALIGNMENT_LEFT.value,
}

MemoizedInfoCard.schema = {
  title: 'admin/editor.info-card.title',
  description: 'admin/editor.info-card.description',
  type: 'object',
  properties: {
    isFullModeStyle: {
      title: 'admin/editor.info-card.isFullModeStyle.title',
      description: 'admin/editor.info-card.isFullModeStyle.description',
      type: 'boolean',
      default: false,
      isLayout: true,
    },
    textPosition: {
      title: 'admin/editor.info-card.textPosition.title',
      description: 'admin/editor.info-card.textPosition.description',
      type: 'string',
      enum: getEnumValues(textPositionTypes),
      enumNames: getEnumNames(textPositionTypes),
      default: textPositionTypes.TEXT_POSITION_LEFT.value,
      isLayout: true,
    },
    callToActionMode: {
      title: 'admin/editor.info-card.callToActionMode.title',
      description: 'admin/editor.info-card.callToActionMode.description',
      type: 'string',
      enum: getEnumValues(callToActionModeTypes),
      enumNames: getEnumNames(callToActionModeTypes),
      default: callToActionModeTypes.CALL_ACTION_BUTTON.value,
      isLayout: true,
    },
    textAlignment: {
      title: 'admin/editor.info-card.textAlignment.title',
      description: 'admin/editor.info-card.textAlignment.description',
      type: 'string',
      default: textAlignmentTypes.TEXT_ALIGNMENT_LEFT.value,
      enum: getEnumValues(textAlignmentTypes),
      enumNames: getEnumNames(textAlignmentTypes),
      isLayout: true,
    },
    blockClass: {
      title: 'admin/editor.blockClass.title',
      description: 'admin/editor.blockClass.description',
      type: 'string',
      isLayout: true,
    },
  },
}

export default MemoizedInfoCard

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/InfoCard/CallToAction.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import Button from '../../../Styleguide/Button'
import { Link } from '../../../../../../vtex.render-runtime'
import useCssHandles from '../../../CssHandles/useCssHandles'

import { callActionValues } from './SchemaTypes'

const CSS_HANDLES = ['infoCardCallActionContainer', 'infoCardCallActionText']

const CallToAction = ({ mode, text, url }) => {
  const handles = useCssHandles(CSS_HANDLES)

  if (mode === callActionValues.NONE) {
    return null
  }

  const ActionWrapper = ({ mode, text }) => {
    if (mode === callActionValues.BUTTON) {
      return <Button onClick={() => {}}>{text}</Button>
    }

    // Mode is link
    return (
      <p
        className={`${handles.infoCardCallActionText} link t-body b underline c-action-primary`}
      >
        {text}
      </p>
    )
  }

  return (
    <Link className={`${handles.infoCardCallActionContainer} mt6 mb6`} to={url}>
      <ActionWrapper text={text} mode={mode} />
    </Link>
  )
}

export default CallToAction

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/InfoCard/SchemaTypes.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
export const textPostionValues = {
  LEFT: 'left',
  CENTER: 'center',
  RIGHT: 'right',
}

export const textAlignmentValues = {
  LEFT: 'left',
  CENTER: 'center',
  RIGHT: 'right',
}

export const callActionValues = {
  NONE: 'none',
  BUTTON: 'button',
  LINK: 'link',
}

export const textPositionTypes = {
  TEXT_POSITION_LEFT: {
    name: 'admin/editor.info-card.textPosition.left',
    value: textPostionValues.LEFT,
  },
  TEXT_POSITION_CENTER: {
    name: 'admin/editor.info-card.textPosition.center',
    value: textPostionValues.CENTER,
  },
  TEXT_POSITION_RIGHT: {
    name: 'admin/editor.info-card.textPosition.right',
    value: textPostionValues.RIGHT,
  },
}

export const textAlignmentTypes = {
  TEXT_ALIGNMENT_LEFT: {
    name: 'admin/editor.info-card.textAlignment.left',
    value: textAlignmentValues.LEFT,
  },
  TEXT_ALIGNMENT_CENTER: {
    name: 'admin/editor.info-card.textAlignment.center',
    value: textAlignmentValues.CENTER,
  },
  TEXT_ALIGNMENT_RIGHT: {
    name: 'admin/editor.info-card.textAlignment.right',
    value: textAlignmentValues.RIGHT,
  },
}

export const callToActionModeTypes = {
  CALL_ACTION_NONE: {
    name: 'admin/editor.info-card.callAction.none',
    value: callActionValues.NONE,
  },
  CALL_ACTION_BUTTON: {
    name: 'admin/editor.info-card.callAction.button',
    value: callActionValues.BUTTON,
  },
  CALL_ACTION_LINK: {
    name: 'admin/editor.info-card.callAction.link',
    value: callActionValues.LINK,
  },
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/InfoCard/LinkWrapper.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Fragment } from 'react'
import { bool, string, object, node } from 'prop-types'
import { Link } from '../../../../../../vtex.render-runtime'

const LinkWrapper = ({
  imageActionUrl,
  children,
  extraCondition,
  linkProps = {},
}) => {
  if (!imageActionUrl || imageActionUrl.length === 0 || extraCondition) {
    return <Fragment>{children}</Fragment>
  }

  return (
    <Link {...linkProps} to={imageActionUrl}>
      {children}
    </Link>
  )
}

LinkWrapper.propTypes = {
  imageActionUrl: string,
  children: node,
  extraCondition: bool,
  linkProps: object,
}

export default LinkWrapper

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductSpecifications/Wrapper.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import useProduct from '../../../ProductContext/useProduct'
import { isEmpty, propOr, propEq } from 'ramda'

import ProductSpecifications from './index'

const getSpecifications = productContext => {
  if (!productContext || isEmpty(productContext)) {
    return []
  }
  const { product } = productContext
  const specificationGroups = propOr([], 'specificationGroups', product)
  const groupWithAll = specificationGroups.find(
    propEq('name', 'allSpecifications')
  )
  const allSpecifications = groupWithAll ? groupWithAll.specifications : []
  return allSpecifications
}

const ProductSpecificationsWrapper = ({
  hiddenSpecifications,
  visibleSpecifications,
  specifications: propsSpecifications,
  tabsMode, // This is a legacy prop passed by product-details
  showSpecificationsTab = false,
  collapsible = 'always',
}) => {
  const productContext = useProduct()

  const specifications =
    propsSpecifications || getSpecifications(productContext)

  return (
    <ProductSpecifications
      hiddenSpecifications={hiddenSpecifications}
      visibleSpecifications={visibleSpecifications}
      tabsMode={tabsMode != null ? tabsMode : showSpecificationsTab}
      specifications={specifications}
      collapsible={collapsible}
    />
  )
}

ProductSpecificationsWrapper.getSchema = () => {
  return {
      title: 'admin/editor.product-specifications.title',
      description: '',
      type: 'object',
      properties: {
        hiddenSpecifications: {
          items: {
            default: '',
            type: 'string',
            title: 'admin/editor.product-specifications.items.title'
          },
          description: 'admin/editor.product-specifications.hidden-specifications.description',
          title: 'admin/editor.product-specifications.hidden-specifications.title',
          type: 'array'
        },
        visibleSpecifications: {
          items: {
            default: '',
            type: 'string',
            title: 'admin/editor.product-specifications.items.title'
          },
          description: 'admin/editor.product-specifications.visible-specifications.description',
          title: 'admin/editor.product-specifications.visible-specifications.title',
          type: 'array'
        }
      }
    }
}

export default ProductSpecificationsWrapper

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductSpecifications/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Fragment, useState } from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'
import HtmlParser from 'react-html-parser'
import useCssHandles from '../../../CssHandles/useCssHandles'
import Tabs from '../../../Styleguide/Tabs'
import Tab from '../../../Styleguide/Tab'
import useDevice from '../../../DeviceDetector/useDevice'

import GradientCollapse from '../GradientCollapse/index'

const CSS_HANDLES = [
  'specificationsTableContainer',
  'specificationsTabsContainer',
  'specificationsTitle',
  'specificationsTable',
  'specificationsTab',
  'specificationsTablePropertyHeading',
  'specificationsTableSpecificationHeading',
  'specificationItemProperty',
  'specificationItemSpecifications',
]

/**
 * Product Specification Component.
 * Render the technical specifications of a product. Can be displayed in two views: Table view or Tabs view.
 */
const ProductSpecifications = ({
  tabsMode,
  specifications,
  collapsible = 'always',
  hiddenSpecifications,
  visibleSpecifications,
  shouldCollapseInTabChange,
}) => {
  const [currentTab, setCurrentTab] = useState(0)
  const [collapsed, setCollapsed] = useState(true)
  const handles = useCssHandles(CSS_HANDLES)
  const { isMobile } = useDevice()

  const shouldBeCollapsible = Boolean(
    collapsible === 'always' ||
      (collapsible === 'mobileOnly' && isMobile) ||
      (collapsible === 'desktopOnly' && !isMobile)
  )

  const handleTabChange = tabIndex => {
    setCurrentTab(tabIndex)
    if (shouldCollapseInTabChange) {
      setCollapsed(true)
    }
  }

  const getSpecificationItems = () => {
    const mappedSpecifications = specifications.map(specification => {
      return {
        property: specification.name,
        specifications: specification.values.join(", "),
      }
    })

    if (visibleSpecifications && hiddenSpecifications) {
      console.warn(
        'A product-specification block is using both visibleSpecifications and hiddenSpecifications props at the same time. Please choose only one of them.'
      )

      return mappedSpecifications
    }

    if (visibleSpecifications) {
      return mappedSpecifications.filter(specification =>
        visibleSpecifications.find(
          filter =>
            specification.property.toLowerCase() === filter.toLowerCase()
        )
      )
    }
    if (hiddenSpecifications) {
      return mappedSpecifications.filter(
        specification =>
          !hiddenSpecifications.find(
            filter =>
              specification.property.toLowerCase() === filter.toLowerCase()
          )
      )
    }

    return mappedSpecifications
  }

  const specificationItems = getSpecificationItems()

  const specificationTitle = (
    <FormattedMessage id="store/technicalspecifications.title">
      {txt => (
        <h2 className={`${handles.specificationsTitle} t-heading-5 mb5 mt0`}>
          {HtmlParser(txt)}
        </h2>
      )}
    </FormattedMessage>
  )

  const specificationsTable = (
    <table
      className={`${handles.specificationsTable} w-100 bg-base border-collapse`}
    >
      <thead>
        <tr>
          <th
            className={`${handles.specificationsTablePropertyHeading} w-50 b--muted-4 bb bt c-muted-2 t-body tl pa5`}
          >
            <FormattedMessage id="store/product-description.property" />
          </th>
          <th
            className={`${handles.specificationsTableSpecificationHeading} w-50 b--muted-4 bb bt c-muted-2 t-body tl pa5`}
          >
            <FormattedMessage id="store/product-description.specification" />
          </th>
        </tr>
      </thead>
      <tbody>
        {specificationItems.map((specification, i) => (
          <tr key={i}>
            <td
              className={`${handles.specificationItemProperty} w-50 b--muted-4 bb pa5`}
            >
              {HtmlParser(specification.property)}
            </td>
            <td
              className={`${handles.specificationItemSpecifications} w-50 b--muted-4 bb pa5`}
            >
              {HtmlParser(specification.specifications)}
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  )

  const tableView = (
    <Fragment>
      {specifications.length > 0 && (
        <div
          className={`${handles.specificationsTableContainer} mt9 mt0-l pl8-l`}
        >
          {specificationTitle}
          {shouldBeCollapsible ? (
            <GradientCollapse
              collapseHeight={220}
              collapsed={collapsed}
              onCollapsedChange={(_, newValue) => setCollapsed(newValue)}
            >
              {specificationsTable}
            </GradientCollapse>
          ) : (
            specificationsTable
          )}
        </div>
      )}
    </Fragment>
  )

  const tabsView = (
    <div className={`${handles.specificationsTabsContainer} pt8`}>
      {specificationTitle}
      <Tabs fullWidth>
        {specificationItems.map((specification, i) => (
          <Tab
            key={i}
            label={HtmlParser(specification.property)}
            active={currentTab === i}
            onClick={() => handleTabChange(i)}
          >
            <div className={`${handles.specificationsTab} pb8 c-muted-1 pv6`}>
              {shouldBeCollapsible ? (
                <GradientCollapse
                  collapseHeight={220}
                  collapsed={collapsed}
                  onCollapsedChange={(_, newValue) => setCollapsed(newValue)}
                >
                  {HtmlParser(specification.specifications)}
                </GradientCollapse>
              ) : (
                HtmlParser(specification.specifications)
              )}
            </div>
          </Tab>
        ))}
      </Tabs>
    </div>
  )

  return tabsMode ? tabsView : tableView
}

ProductSpecifications.defaultProps = {
  specifications: [],
  tabsMode: false,
  shouldCollapseInTabChange: false,
}

ProductSpecifications.propTypes = {
  /** Specifications that will be displayed on the table */
  specifications: PropTypes.arrayOf(
    PropTypes.shape({
      /** Specification name */
      name: PropTypes.string.isRequired,
      /** Specifications value */
      values: PropTypes.arrayOf(PropTypes.string).isRequired,
    })
  ),
  /** If should collapse when a tab is changed */
  shouldCollapseInTabChange: PropTypes.bool,
  /** Tabs mode view */
  tabsMode: PropTypes.bool,
  /** Specifications which will be shown exclusively (optional) */
  visibleSpecifications: PropTypes.array,
  /** Specifications which will be hidden (optional) */
  hiddenSpecifications: PropTypes.array,
  collapsible: PropTypes.oneOf([
    'always',
    'never',
    'desktopOnly',
    'mobileOnly',
  ]),
}

export default ProductSpecifications

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Container/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

import styles from './Container.css'

const render = ({ className, children, ...props }, ref) => {
  const classes = classNames(
    styles.container,
    'ph3 ph5-m ph2-xl mw9 center',
    className
  )

  return (
    <section {...props} className={classes} ref={ref}>
      {children}
    </section>
  )
}

render.displayName = 'Container'

const Container = React.forwardRef(render)

Container.propTypes = {
  children: PropTypes.node,
  className: PropTypes.string,
}

export default Container

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductImages/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
/**
*   Product Image CSS Module
*/

.productVideo { }

.productImage { }

.carouselGaleryCursor {
  cursor: url('./components/Carousel/cursor.svg') 8 8, default;
}

.carouselGaleryThumbs :global(.swiper-slide) {
  height: auto;
}

.carouselIconCaretRight,
.carouselIconCaretLeft {
  stroke: white;
  stroke-width: 0.5px;
  -webkit-filter: drop-shadow(0px 1px 1px #cacbcc);
  filter: drop-shadow(0px 1px 1px #cacbcc);
}

.carouselCursorDefault:hover {
  cursor: default;
}

.carouselImagePlaceholder {
}

/** Overrides figure css declaration from vtex-tachyons, which
 * for some reason adds margin to all figure elements */
.figure {
  margin: 0;
}

.gradientBaseBottom {
  /** TODO: use the actual bg-base token instead of a hardcoded color */
  background: linear-gradient(transparent, white);
}

.gradientBaseTop {
  /** TODO: use the actual bg-base token instead of a hardcoded color */
  background: linear-gradient(white, transparent);
}

.gradientBaseRight {
  /** TODO: use the actual bg-base token instead of a hardcoded color */
  background: linear-gradient(90deg, transparent, white);
}

.gradientBaseLeft {
  /** TODO: use the actual bg-base token instead of a hardcoded color */
  background: linear-gradient(90deg, white, transparent);
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductImages/Wrapper.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useMemo } from 'react'
import useProduct from '../../../ProductContext/useProduct'
import useResponsiveValues from '../../../ResponsiveValues/useResponsiveValues'
import { path, map, pick } from 'ramda'

import ProductImages from './index'
import generateImageConfig from './utils/generateImageConfig'
import { THUMBS_ORIENTATION, THUMBS_POSITION_HORIZONTAL } from './utils/enums'

const ProductImagesWrapper = props => {
  const valuesFromContext = useProduct() || {}
  const { aspectRatio, maxHeight, showNavigationArrows, showPaginationDots } = useResponsiveValues(
    pick(['aspectRatio', 'maxHeight', 'showNavigationArrows', 'showPaginationDots'], props)
  )
  const { selectedItem } = valuesFromContext
  const images = useMemo(
    () =>
      props.images != null
        ? props.images
        : map(generateImageConfig, path(['images'], selectedItem) || []),
    [props.images, selectedItem]
  )

  const videos = useMemo(
    () =>
      props.videos != null
        ? props.videos
        : path(['videos'], selectedItem) || [],
    [props.videos, selectedItem]
  )

  return (
    <ProductImages
      images={images}
      videos={videos}
      zoomProps={props.zoomProps}
      hiddenImages={props.hiddenImages}
      position={props.position || props.thumbnailPosition} //thumbnailPosition is a legacy prop from product-details
      displayThumbnailsArrows={props.displayThumbnailsArrows}
      thumbnailsOrientation={props.thumbnailsOrientation}
      zoomMode={props.zoomMode}
      zoomFactor={props.zoomFactor}
      aspectRatio={aspectRatio}
      maxHeight={maxHeight}
      thumbnailAspectRatio={props.thumbnailAspectRatio}
      thumbnailMaxHeight={props.thumbnailMaxHeight}
      showNavigationArrows={showNavigationArrows}
      showPaginationDots={showPaginationDots}
      // Deprecated
      zoomProps={props.zoomProps}
    />
  )
}

ProductImagesWrapper.getSchema = ({ zoomProps: { zoomType } = {} }) => ({
  title: 'admin/editor.product-images.title',
  description: 'admin/editor.product-images.description',
  type: 'object',
  properties: {
    zoomProps: {
      title: 'admin/editor.product-images.zoomOptions.title',
      type: 'object',
      properties: {
        zoomType: {
          title: 'admin/editor.product-images.zoomType.title',
          type: 'string',
          enum: ['gallery', 'in-page', 'no-zoom'],
          enumNames: [
            'admin/editor.product-images.gallery',
            'admin/editor.product-images.in-page',
            'admin/editor.product-images.no-zoom',
          ],
          widget: {
            'ui:options': {
              inline: false,
            },
            'ui:widget': 'radio',
          },
          default: 'no-zoom',
        },
        ...(zoomType === 'gallery' && {
          bgOpacity: {
            title: 'admin/editor.product-images.bgopacity.title',
            type: 'number',
            minimum: 0.0,
            maximum: 1.0,
            multipleOf: 0.01,
            default: 0.8,
          },
        }),
      },
    },
    thumbnailsOrientation: {
      title: 'admin/editor.product-images.thumbnailsOrientation.title',
      type: 'string',
      enum: [THUMBS_ORIENTATION.VERTICAL, THUMBS_ORIENTATION.HORIZONTAL],
      enumNames: [
        'admin/editor.product-images.vertical',
        'admin/editor.product-images.horizontal',
      ],
      widget: {
        'ui:options': {
          inline: false,
        },
        'ui:widget': 'radio',
      },
      default: THUMBS_ORIENTATION.VERTICAL,
      isLayout: true,
    },
    position: {
      title: 'admin/editor.product-images.position.title',
      description: 'admin/editor.product-images.position.description',
      type: 'string',
      enum: [THUMBS_POSITION_HORIZONTAL.LEFT, THUMBS_POSITION_HORIZONTAL.RIGHT],
      enumNames: [
        'admin/editor.product-images.left',
        'admin/editor.product-images.right',
      ],
      widget: {
        'ui:options': {
          inline: false,
        },
        'ui:widget': 'radio',
      },
      default: THUMBS_POSITION_HORIZONTAL.LEFT,
      isLayout: true,
    },
    displayThumbnailsArrows: {
      title: 'admin/editor.product-images.displayThumbnailsArrows.title',
      type: 'boolean',
      default: false,
      isLayout: true,
    },
  },
})

export default ProductImagesWrapper

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductImages/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import React, { useMemo } from 'react'

import Carousel from './components/Carousel'
import {
  THUMBS_ORIENTATION,
  THUMBS_POSITION_HORIZONTAL,
  DEFAULT_EXCLUDE_IMAGE_WITH,
} from './utils/enums'
import useCssHandles from '../../../CssHandles/useCssHandles'

const CSS_HANDLES = ['content', 'productImagesContainer']

const ProductImages = ({
  position,
  displayThumbnailsArrows,
  hiddenImages,
  images: allImages,
  videos,
  thumbnailsOrientation,
  aspectRatio,
  maxHeight,
  thumbnailAspectRatio,
  thumbnailMaxHeight,
  showNavigationArrows,
  showPaginationDots,
  zoomMode,
  zoomFactor,
  // Deprecated
  zoomProps,
}) => {
  if (hiddenImages && !Array.isArray(hiddenImages)) {
    hiddenImages = [hiddenImages]
  }

  const excludeImageRegexes = hiddenImages && hiddenImages.map(text => new RegExp(text, 'i'))
  const handles = useCssHandles(CSS_HANDLES)

  const images = allImages.filter(image =>
    !image.imageLabel || !excludeImageRegexes.some(regex => regex.test(image.imageLabel))
  )

  const slides = useMemo(() => {
    if (!images.length && !videos.length) return []

    return [
      ...images.map(image => ({
        type: 'image',
        url: image.imageUrls ? image.imageUrls[0] : image.imageUrl,
        alt: image.imageText,
        thumbUrl: image.thumbnailUrl || image.imageUrl,
      })),
      ...videos.map(video => ({
        type: 'video',
        src: video.videoUrl,
        thumbWidth: 300,
      })),
    ]
  }, [images, videos])

  return (
    <div className={`${handles.productImagesContainer} ${handles.content} w-100`}>
      <Carousel
        slides={slides}
        position={position}
        displayThumbnailsArrows={displayThumbnailsArrows}
        thumbnailsOrientation={thumbnailsOrientation}
        aspectRatio={aspectRatio}
        maxHeight={maxHeight}
        thumbnailAspectRatio={thumbnailAspectRatio}
        thumbnailMaxHeight={thumbnailMaxHeight}
        showNavigationArrows={showNavigationArrows}
        showPaginationDots={showPaginationDots}
        zoomMode={zoomMode}
        zoomFactor={zoomFactor}
        // Deprecated
        zoomProps={zoomProps}
      />
    </div>
  )
}

ProductImages.propTypes = {
  /** The position of the thumbs */
  position: PropTypes.oneOf([
    THUMBS_POSITION_HORIZONTAL.LEFT,
    THUMBS_POSITION_HORIZONTAL.RIGHT,
  ]),
  thumbnailsOrientation: PropTypes.oneOf([
    THUMBS_ORIENTATION.VERTICAL,
    THUMBS_ORIENTATION.HORIZONTAL,
  ]),
  /** This is a necessary prop if you're using SKUSelector to display color images
   * (like a image with only green to represent an SKU of something green) and you
   * want to not display this image in the ProductImages component, to do this you
   * just have to upload the image in the catalog with the value of this prop inside the imageText property */
  hiddenImages: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.arrayOf(PropTypes.string),
  ]),
  /** Array of images to be passed for the Thumbnail Slider component as a props */
  images: PropTypes.arrayOf(
    PropTypes.shape({
      /** URL of the image */
      imageUrls: PropTypes.arrayOf(PropTypes.string.isRequired),
      /** Size thresholds used to choose each image */
      thresholds: PropTypes.arrayOf(PropTypes.number),
      /** URL of the image thumbnail */
      thumbnailUrl: PropTypes.string,
      /** Text that describes the image */
      imageText: PropTypes.string.isRequired,
    })
  ),
  videos: PropTypes.arrayOf(
    PropTypes.shape({
      videoUrl: PropTypes.string,
    })
  ),
  zoomProps: PropTypes.shape({
    zoomType: PropTypes.string,
  }),
  displayThumbnailsArrows: PropTypes.bool,
}

ProductImages.defaultProps = {
  images: [],
  position: THUMBS_POSITION_HORIZONTAL.LEFT,
  zoomProps: { zoomType: 'in-page' },
  thumbnailsOrientation: THUMBS_ORIENTATION.VERTICAL,
  displayThumbnailsArrows: false,
  hiddenImages: DEFAULT_EXCLUDE_IMAGE_WITH,
}

export default ProductImages

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Logo/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
.sizeDesktop {
  max-width: 150px;
  max-height: 75px;
  min-width: 120px;
}

.sizeMobile {
  max-width: 90px;
  max-height: 40px;
  min-width: 72px;
}

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/Logo/Placeholder.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'

export default ({ width, height, title }) => (
  <svg
    width={width}
    height={height}
    viewBox="0 0 142 80"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <title>{title}</title>
    <rect width="142" height="80" fill="#F2F4F5" stroke="#E3E4E6" />
    <path
      d="M84.8156 47.5402L77.8157 33.9407C77.5479 33.4222 77.0282 33.0703 76.4349 33.0108C75.8434 32.9445 75.2572 33.1876 74.8844 33.6381L68.8854 40.9206L64.9707 38.386C64.5717 38.1293 64.08 38.0392 63.6162 38.1412C63.149 38.2432 62.7447 38.5271 62.4997 38.9249L57.2497 47.4246C56.9242 47.9499 56.9172 48.6026 57.227 49.1381C57.5367 49.6719 58.1195 50 58.7495 50H83.2494C83.8567 50 84.4184 49.694 84.7386 49.1925C85.0589 48.691 85.0851 48.0655 84.8156 47.5402Z"
      fill="#CACBCC"
    />
    <path
      d="M67.5 33C69.433 33 71 31.433 71 29.5C71 27.567 69.433 26 67.5 26C65.567 26 64 27.567 64 29.5C64 31.433 65.567 33 67.5 33Z"
      fill="#CACBCC"
    />
    <defs>
      <clipPath id="clip0">
        <rect width="127" height="65" fill="white" />
      </clipPath>
    </defs>
  </svg>
)

```


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

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/StoreComponents/components/Logo/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { Link, useRuntime } from '../../../../../../vtex.render-runtime'
import useCssHandles from '../../../CssHandles/useCssHandles'
import PropTypes from 'prop-types'
import classNames from 'classnames'
// import * as Amp from 'react-amphtml'

import Placeholder from './Placeholder'

import styles from './styles.css'

const CSS_HANDLES = ['logoLink', 'logoImage', 'logoContainer']

/**
 * Logo of the store
 */
const Logo = ({
  url,
  href,
  width,
  height,
  title,
  mobileWidth,
  mobileHeight,
}) => {
  const {
    // amp,
    account,
    hints: { mobile },
  } = useRuntime()
  const handles = useCssHandles(CSS_HANDLES)
  const logoClassNames = classNames('store-logo', handles.logoContainer, {
    [styles.sizeDesktop]: !mobile,
    [styles.sizeMobile]: mobile,
  })

  const imgWidth = mobile && mobileWidth ? mobileWidth : width
  const imgHeight = mobile && mobileHeight ? mobileHeight : height

  const imageUrl = url && url.replace(/{{account}}/g, account)

  let image = null

  // if (amp && url) {
    // image = (
    //   <Amp.AmpImg
    //     specName="default"
    //     width={imgWidth}
    //     height={imgHeight}
    //     alt={title}
    //     src={imageUrl}
    //     className={handles.logoImage}
    //   />
    // )
  // } else if (url) {
    image = (
      <img
        src={imageUrl}
        width={imgWidth}
        height={imgHeight}
        alt={title}
        className={handles.logoImage}
      />
    )
  // }

  const logo = (
    <span className={`${logoClassNames} pv4 ph6`}>
      {url ? (
        image
      ) : (
        <Placeholder width={width} height={height} title={title} />
      )}
    </span>
  )

  return href ? (
    <Link to={href} className={handles.logoLink}>
      {logo}
    </Link>
  ) : (
    logo
  )
}

Logo.propTypes = {
  /** URL of the logo */
  url: PropTypes.string,
  /** Title to be displayed as alt text */
  title: PropTypes.string.isRequired,
  /** Logo's width */
  width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  /** Logo's height */
  height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
}

Logo.schema = {
  title: 'admin/editor.logo.title',
  description: 'admin/editor.logo.description',
  type: 'object',
  properties: {
    href: {
      title: 'admin/editor.logo.href.title',
      description: 'admin/editor.logo.href.description',
      type: 'string',
    },
    url: {
      title: 'admin/editor.logo.url.title',
      description: 'admin/editor.logo.url.description',
      type: 'string',
      widget: {
        'ui:widget': 'image-uploader',
      },
    },
    width: {
      title: 'admin/editor.logo.width.title',
      description: 'admin/editor.logo.width.description',
      type: 'number',
      isLayout: true,
    },
    height: {
      title: 'admin/editor.logo.height.title',
      description: 'admin/editor.logo.height.description',
      type: 'number',
      isLayout: true,
    },
    title: {
      title: 'admin/editor.logo.title.title',
      description: 'admin/editor.logo.title.description',
      type: 'string',
    },
    mobileWidth: {
      title: 'admin/editor.logo.mobileWidth.title',
      description: 'admin/editor.logo.mobileWidth.description',
      type: 'number',
      isLayout: true,
    },
    mobileHeight: {
      title: 'admin/editor.logo.mobileHeight.title',
      description: 'admin/editor.logo.mobileHeight.description',
      type: 'number',
      isLayout: true,
    },
  },
}

export default Logo

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/GradientCollapse/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
/**
*   GradientCollapse CSS Modules
*/

.fadeBottom {
    background-image: linear-gradient(transparent, white);
}

.pointerEventsNone {
    pointer-events: none;
}

.pointerEventsAuto {
    pointer-events: auto;
}
```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/GradientCollapse/index.js from the store-on-localhost 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 styles from './styles.css'

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'
  )

function GradientCollapse(props) {
  const {
    children,
    collapseHeight,
    onCollapsedChange,
    collapsed: collapsedProp,
  } = props
  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="relative"
        >
          <div ref={wrapper} className="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="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-on-localhost/src/monobloco/components/new/StoreComponents/components/CollectionBadges/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
/**
*   CollectionBadges CSS Module
*/

.collectionContainer {}

.item {}
```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/CollectionBadges/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

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

import { CollectionBadgeItem } from './components/CollectionBadgeItem'

import styles from './styles.css'

/**
 * Collection Badges component.
 * Encapsulates and displays a responsive list of Collection Badges.
 */
const CollectionBadges = ({ collectionBadgesText, children }) => (
  <div className={`${styles.collectionContainer} relative dib w-100`}>
    {children}
    <div className="inline-flex justify-end absolute w-100 bottom-0 left-0">
      {collectionBadgesText.map((collectionBadgeText, index) => (
        <CollectionBadgeItem key={collectionBadgeText + index}>
          {collectionBadgeText}
        </CollectionBadgeItem>
      ))}
    </div>
  </div>
)

CollectionBadges.propTypes = {
  /** Array of collection badges text */
  collectionBadgesText: PropTypes.array.isRequired,
  /** Children component that should be render inside the collection badge item */
  children: PropTypes.node.isRequired,
}

CollectionBadges.defaultProps = {
  collectionBadgesText: [],
}

export default CollectionBadges

```


[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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductPrice/styles.css from the store-on-localhost 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-on-localhost/src/monobloco/components/new/StoreComponents/components/ProductPrice/Wrapper.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useContext } from 'react'
import ProductContext from '../../../ProductContext/ProductContext'
import { path, isEmpty, has } from 'ramda'

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 = commertialOffer =>
  Number.isNaN(+path(['AvailableQuantity'], commertialOffer)) ||
  path(['AvailableQuantity'], commertialOffer) > 0

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

  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,
      }
    }

    const { selectedItem } = valuesFromContext
    const commertialOffer = path(['sellers', 0, 'commertialOffer'], selectedItem)

    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'], commertialOffer),
      sellingPrice: sellingPrice || path(['Price'], commertialOffer),
      installments: installments || path(['Installments'], commertialOffer),
      labelSellingPrice,
      showLabels,
      showInstallments,
      showListPrice,
      showSavings,
      showProductPrice: isAvailable(commertialOffer),
    }
  }

  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

```