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

[User] Show me the implementation of store-components/react/components/BuyButton/assemblyUtils.test.ts from the store-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  transformAssemblyOptions,
  ItemOption,
  InputValuesOption,
} from './assemblyUtils'
// eslint-disable-next-line jest/no-mocks-import
import { customBell, comboPizza, starColor } from './__mocks__/assemblyOptions'

test('should transform assemblyOptions', () => {
  const parentPrice = 450
  const parentQuantity = 1

  const resultBell = transformAssemblyOptions(
    customBell.items,
    {},
    parentPrice,
    parentQuantity
  )

  expect(resultBell.options).toHaveLength(5)
  const addonOption = resultBell.options[0] as ItemOption

  expect(addonOption.assemblyId).toBe('add-on_Add-on')
  expect(addonOption.id).toBe('2000588')
  expect(addonOption.quantity).toBe(1)
  expect(addonOption.seller).toBe('1')

  const resultPizza = transformAssemblyOptions(
    comboPizza.items,
    {},
    parentPrice,
    parentQuantity
  )

  expect(resultPizza.options).toHaveLength(2)
  const pizzaOption = resultPizza.options[0] as ItemOption

  expect(pizzaOption.assemblyId).toBe('pizza_composition_Pizza flavor')
  expect(pizzaOption.id).toBe('5101')
  expect(pizzaOption.quantity).toBe(1)
  expect(pizzaOption.seller).toBe('1')
  expect(pizzaOption.options).toHaveLength(3)

  const drinksOptions = resultPizza.options[1] as ItemOption

  expect(drinksOptions.options).toBeUndefined()
})

test('input values', () => {
  const parentPrice = 450
  const parentQuantity = 1

  const resultStar = transformAssemblyOptions(
    starColor.items,
    starColor.inputValues,
    parentPrice,
    parentQuantity
  )

  expect(resultStar.options).toHaveLength(1)
  const customization = resultStar.options[0] as InputValuesOption

  expect(customization.assemblyId).toBe('Customization')
  expect(customization.inputValues).toMatchObject({
    Font: 'Sans serif',
    'Front text': 'Frente',
    'Back text': 'Verso',
    'Glossy print': true,
  })
  expect(resultStar).toMatchInlineSnapshot(`
    Object {
      "assemblyOptions": Object {
        "added": Array [],
        "parentPrice": 450,
        "removed": Array [],
      },
      "options": Array [
        Object {
          "assemblyId": "Customization",
          "inputValues": Object {
            "Back text": "Verso",
            "Font": "Sans serif",
            "Front text": "Frente",
            "Glossy print": true,
          },
        },
      ],
    }
  `)
})

test('recursive input values', () => {
  const parentPrice = 450
  const parentQuantity = 1

  const resultBell = transformAssemblyOptions(
    customBell.items,
    customBell.inputValues,
    parentPrice,
    parentQuantity
  )

  expect(resultBell.options).toHaveLength(5)

  const engraving = resultBell.options[4] as ItemOption

  expect(engraving.options).toHaveLength(1)

  const recursiveInputValue = engraving.options![0] as InputValuesOption

  expect(recursiveInputValue.assemblyId).toBe('1-3-lines')
  expect(recursiveInputValue.inputValues).toBe(
    customBell.inputValues['1-3-lines']
  )
  expect(resultBell).toMatchInlineSnapshot(`
    Object {
      "assemblyOptions": Object {
        "added": Array [
          Object {
            "choiceType": "TOGGLE",
            "extraQuantity": 1,
            "item": Object {
              "id": "2000588",
              "name": "Bells add-ons Logo small",
              "quantity": 1,
              "sellingPrice": 75,
              "sellingPriceWithAssemblies": 75,
            },
            "normalizedQuantity": 1,
          },
          Object {
            "choiceType": "TOGGLE",
            "extraQuantity": 1,
            "item": Object {
              "id": "2000589",
              "name": "Bells add-ons Logo big",
              "quantity": 1,
              "sellingPrice": 90,
              "sellingPriceWithAssemblies": 90,
            },
            "normalizedQuantity": 1,
          },
          Object {
            "choiceType": "SINGLE",
            "extraQuantity": 1,
            "item": Object {
              "id": "2000592",
              "name": "Bells add-ons Script",
              "quantity": 1,
              "sellingPrice": 15,
              "sellingPriceWithAssemblies": 15,
            },
            "normalizedQuantity": 1,
          },
          Object {
            "choiceType": "SINGLE",
            "extraQuantity": 1,
            "item": Object {
              "assemblyOptions": Object {
                "added": Array [],
                "parentPrice": 26,
                "removed": Array [],
              },
              "id": "2000586",
              "name": "Bells add-ons 1-3 lines",
              "quantity": 1,
              "sellingPrice": 26,
              "sellingPriceWithAssemblies": 26,
            },
            "normalizedQuantity": 1,
          },
        ],
        "parentPrice": 450,
        "removed": Array [],
      },
      "options": Array [
        Object {
          "assemblyId": "add-on_Add-on",
          "id": "2000588",
          "quantity": 1,
          "seller": "1",
        },
        Object {
          "assemblyId": "add-on_Add-on",
          "id": "2000589",
          "quantity": 1,
          "seller": "1",
        },
        Object {
          "assemblyId": "text_style_Text Style",
          "id": "2000591",
          "quantity": 0,
          "seller": "1",
        },
        Object {
          "assemblyId": "text_style_Text Style",
          "id": "2000592",
          "quantity": 1,
          "seller": "1",
        },
        Object {
          "assemblyId": "engraving_Engraving",
          "id": "2000586",
          "options": Array [
            Object {
              "assemblyId": "1-3-lines",
              "inputValues": Object {
                "Line 1": "First line",
                "Line 2": "Second line",
                "Line 3": "Third line",
              },
            },
          ],
          "quantity": 1,
          "seller": "1",
        },
      ],
    }
  `)
})

test('empty input values should result in empty options', () => {
  const parentPrice = 450
  const parentQuantity = 1

  const resultStar = transformAssemblyOptions(
    starColor.items,
    {
      Customization: {},
    },
    parentPrice,
    parentQuantity
  )

  expect(resultStar.options).toHaveLength(0)
})

```


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

[User] Show me the implementation of store-components/react/components/BuyButton/assemblyUtils.ts from the store-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
type GroupId = string
type GroupTypes = 'SINGLE' | 'TOGGLE' | 'MULTIPLE'

export const sumAssembliesPrice = (
  assemblyOptions: Record<GroupId, AssemblyOptionItem[]>
) => {
  const cleanAssemblies = assemblyOptions || {}
  const assembliesGroupItems = Object.values(cleanAssemblies)

  return assembliesGroupItems.reduce<number>(
    (sum: number, groupItems: AssemblyOptionItem[]) => {
      const groupPrice = groupItems.reduce<number>((groupSum, item) => {
        const childrenPrice: number = item.children
          ? sumAssembliesPrice(item.children)
          : 0

        const itemCost = item.price * item.quantity

        return groupSum + itemCost + childrenPrice * item.quantity
      }, 0)

      return groupPrice + sum
    },
    0
  )
}

interface AssemblyOptionItem {
  price: number
  choiceType: GroupTypes
  children: Record<GroupId, AssemblyOptionItem[]> | null
  name: string
  id: string
  initialQuantity: number
  quantity: number
  seller: string
}

type InputValue = Record<string, string | boolean>

export interface AssemblyOptions {
  items: Record<GroupId, AssemblyOptionItem[]>
  inputValues: Record<GroupId, InputValue>
  areGroupsValid: Record<string, boolean>
}

type Option = ItemOption | InputValuesOption

export interface ItemOption {
  assemblyId: string
  id: string
  quantity: number
  seller: string
  options?: Option[]
}

export interface InputValuesOption {
  assemblyId: string
  inputValues: InputValue
}

interface AddedItem {
  id: string
  name: string
  quantity: number
  sellingPrice: number
  sellingPriceWithAssemblies: number
  assemblyOptions?: ParsedAssemblyOptionsMeta
}

interface CartAddedOption {
  normalizedQuantity: number
  extraQuantity: number
  choiceType: GroupTypes
  item: AddedItem
}

interface CartRemovedOption {
  name: string
  initialQuantity: number
  removedQuantity: number
}

interface ParsedAssemblyOptionsMeta {
  added: CartAddedOption[]
  removed: CartRemovedOption[]
  parentPrice: number
}

interface ParsedAssemblyOptions {
  options: Option[]
  assemblyOptions: ParsedAssemblyOptionsMeta
}

export const transformAssemblyOptions = (
  // eslint-disable-next-line @typescript-eslint/default-param-last
  assemblyOptionsItems: Record<GroupId, AssemblyOptionItem[]> = {},
  // eslint-disable-next-line @typescript-eslint/default-param-last
  inputValues: Record<GroupId, InputValue> = {},
  parentPrice: number,
  parentQuantity: number
  // eslint-disable-next-line max-params
): ParsedAssemblyOptions => {
  // contains options sent as arguments to graphql mutation
  const options: Option[] = []

  // array with added assemblies data to show in minicart optimistic preview
  const added: CartAddedOption[] = []

  // array with removed assemblies data to show in minicart optimistic preview
  const removed: CartRemovedOption[] = []

  // Start the list of all the input values that we should handle
  let assemblyInputValuesKeys: GroupId[] = Object.keys(inputValues)

  const assemblyItemsKeys: GroupId[] = Object.keys(assemblyOptionsItems)

  for (const groupId of assemblyItemsKeys) {
    const items = assemblyOptionsItems[groupId]

    for (const item of items) {
      let childrenAddedData = null

      if (item.children) {
        const childInputValues: Record<GroupId, InputValue> = {}

        // Get every input value of the item and add it as a child
        for (const key in item.children) {
          childInputValues[key] = inputValues[key]
        }

        // Get all the input values this item is handling
        const handledInputValues = Object.keys(childInputValues)

        // and remove the handled input values from the list
        assemblyInputValuesKeys = assemblyInputValuesKeys.filter(
          inputValueKey => {
            return handledInputValues.includes(inputValueKey)
          }
        )

        childrenAddedData = transformAssemblyOptions(
          item.children,
          childInputValues,
          item.price,
          item.quantity * parentQuantity
        )
      }

      const {
        options: childrenOptions,
        assemblyOptions: childrenAssemblyOptions,
      } = childrenAddedData ?? {
        options: undefined,
        assemblyOptions: undefined,
      }

      const { quantity, initialQuantity } = item

      if (quantity >= initialQuantity && quantity > 0) {
        added.push({
          normalizedQuantity: quantity,
          extraQuantity: quantity - initialQuantity,
          choiceType: item.choiceType,
          item: {
            name: item.name,
            sellingPrice: item.price,
            quantity,
            sellingPriceWithAssemblies:
              item.price + sumAssembliesPrice(item.children ?? {}),
            id: item.id,
            ...(childrenAssemblyOptions
              ? { assemblyOptions: childrenAssemblyOptions }
              : {}),
          },
        })
      }

      if (quantity < initialQuantity && item.choiceType === 'TOGGLE') {
        removed.push({
          name: item.name,
          initialQuantity,
          removedQuantity: initialQuantity - quantity,
        })
      }

      const addedChildrenCount = childrenAddedData
        ? childrenAddedData.options.length
        : 0

      if (quantity !== initialQuantity || addedChildrenCount > 0) {
        options.push({
          assemblyId: groupId,
          id: item.id,
          quantity: quantity * parentQuantity,
          seller: item.seller,
          ...(childrenOptions && childrenOptions.length > 0
            ? { options: childrenOptions }
            : {}),
        })
      }
    }
  }

  for (const groupId of assemblyInputValuesKeys) {
    const inputValuesObject = inputValues[groupId] || {}

    if (Object.keys(inputValuesObject).length > 0) {
      options.push({
        assemblyId: groupId,
        inputValues: inputValues[groupId],
      })
    }
  }

  return {
    options,
    assemblyOptions: {
      added,
      removed,
      parentPrice,
    },
  }
}

```


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

[User] Show me the implementation of store-components/react/components/BuyButton/Wrapper.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import useProduct from 'vtex.product-context/useProduct'
import { path, isEmpty, compose } from 'ramda'
import { FormattedMessage, injectIntl } from 'react-intl'
import { withToast } from 'vtex.styleguide'
import { useOrderForm } from 'vtex.store-resources/OrderFormContext'
import { graphql } from 'react-apollo'
import { useCssHandles } from 'vtex.css-handles'

import ProductPrice from '../ProductPrice'
import { BuyButton } from './index'
import { transformAssemblyOptions, sumAssembliesPrice } from './assemblyUtils'
import addToCartMutation from './mutations/addToCart.gql'
import setOpenMinicartMutation from './mutations/setOpenMinicart.gql'
import installedApp from './queries/installedApp.gql'

const MESSAGE_CSS_HANDLES = [
  'buyButtonText',
  'buttonDataContainer',
  'buttonItemsPrice',
]

const CHECKOUT_URL = {
  V0: '/checkout/#/cart',
  V1: '/cart',
}

const BuyButtonMessage = ({ showItemsPrice, skuItems }) => {
  const { handles } = useCssHandles(MESSAGE_CSS_HANDLES)

  if (!showItemsPrice) {
    return (
      <FormattedMessage id="store/buy-button.add-to-cart">
        {message => <span className={handles.buyButtonText}>{message}</span>}
      </FormattedMessage>
    )
  }

  const totalPrice = skuItems.reduce((acc, item) => {
    const itemPrice =
      item.sellingPriceWithAssemblies != null
        ? item.sellingPriceWithAssemblies
        : item.price

    const itemCost = itemPrice * item.quantity

    return acc + itemCost
  }, 0)

  return (
    <div
      className={`${handles.buttonDataContainer} flex w-100 justify-between items-center`}
    >
      <FormattedMessage id="store/buy-button.add-to-cart">
        {message => <span className={handles.buyButtonText}>{message}</span>}
      </FormattedMessage>
      <ProductPrice
        showLabels={false}
        showListPrice={false}
        sellingPrice={totalPrice}
        className={handles.buttonItemsPrice}
      />
    </div>
  )
}

const BuyButtonWrapper = ({
  intl,
  addToCart,
  showToast,
  onAddStart,
  onAddFinish,
  children,
  isOneClickBuy,
  shouldOpenMinicart,
  setMinicartOpen,
  showItemsPrice,
  available: propAvailable,
  skuItems: propSkuItems,
  large: propLarge,
  disabled: propDisabled,
  shouldAddToCart,
  customToastURL,
  showTooltipOnSkuNotSelected,
  checkoutVersion,
  selectedSeller,
}) => {
  const orderFormContext = useOrderForm()
  const valuesFromContext = useProduct()

  const isEmptyContext = !valuesFromContext || isEmpty(valuesFromContext)

  const product = valuesFromContext && valuesFromContext.product
  const selectedItem = valuesFromContext && valuesFromContext.selectedItem
  const assemblyOptions = valuesFromContext && valuesFromContext.assemblyOptions

  selectedSeller =
    selectedSeller || path(['selectedItem', 'sellers', 0], valuesFromContext)
  const selectedQuantity =
    valuesFromContext && valuesFromContext.selectedQuantity != null
      ? valuesFromContext.selectedQuantity
      : 1

  const skuItems =
    isEmptyContext || propSkuItems != null
      ? propSkuItems
      : // eslint-disable-next-line @typescript-eslint/no-use-before-define
        EnhancedBuyButton.mapCatalogItemToCart({
          product,
          selectedItem,
          selectedQuantity,
          selectedSeller,
          assemblyOptions,
        })

  const large = isEmptyContext || propLarge != null ? propLarge : true

  const available =
    isEmptyContext || propAvailable != null
      ? propAvailable
      : selectedSeller &&
        selectedSeller.commertialOffer &&
        selectedSeller.commertialOffer.AvailableQuantity > 0

  const groupsValidArray =
    (assemblyOptions &&
      assemblyOptions.areGroupsValid &&
      Object.values(assemblyOptions.areGroupsValid)) ||
    []

  const areAssemblyGroupsValid = groupsValidArray.every(Boolean)
  const disabled =
    isEmptyContext || propDisabled != null
      ? propDisabled
      : !areAssemblyGroupsValid

  const version =
    checkoutVersion &&
    checkoutVersion.installedAppPublic &&
    checkoutVersion.installedAppPublic.version

  const checkoutUrl =
    // eslint-disable-next-line radix
    version && parseInt(version.split('.')[0]) > 0
      ? CHECKOUT_URL.V1
      : CHECKOUT_URL.V0

  return (
    <BuyButton
      intl={intl}
      addToCart={addToCart}
      onAddStart={onAddStart}
      onAddFinish={onAddFinish}
      showToast={showToast}
      orderFormContext={orderFormContext}
      skuItems={skuItems}
      large={large}
      available={available}
      isOneClickBuy={isOneClickBuy}
      shouldOpenMinicart={shouldOpenMinicart}
      setMinicartOpen={setMinicartOpen}
      disabled={disabled}
      customToastURL={customToastURL}
      shouldAddToCart={shouldAddToCart}
      showTooltipOnSkuNotSelected={showTooltipOnSkuNotSelected}
      checkoutUrl={checkoutUrl}
    >
      {children && children.length > 0 ? (
        children
      ) : (
        <BuyButtonMessage showItemsPrice={showItemsPrice} skuItems={skuItems} />
      )}
    </BuyButton>
  )
}

const withAddToCart = graphql(addToCartMutation, {
  name: 'addToCart',
  props: ({ addToCart }) => ({
    addToCart: items => addToCart({ variables: { items } }),
  }),
})

const withOpenMinicart = graphql(setOpenMinicartMutation, {
  name: 'setMinicartOpen',
  props: ({ setMinicartOpen }) => ({
    setMinicartOpen: isOpen => setMinicartOpen({ variables: { isOpen } }),
  }),
})

const withCheckoutVersion = graphql(installedApp, {
  name: 'checkoutVersion',
  options: {
    ssr: false,
    variables: {
      slug: 'vtex.checkout',
    },
  },
})

/**
 * @deprecated This component is deprecated. Please use [vtex.add-to-cart-button](https://github.com/vtex-apps/add-to-cart-button) instead.
 */
const EnhancedBuyButton = compose(
  withAddToCart,
  withOpenMinicart,
  withCheckoutVersion,
  withToast,
  injectIntl
)(BuyButtonWrapper)

// This function is public available to be used only by vtex.product-summary.
// We do not garantee this API will not change and might happen breaking change anytime.
EnhancedBuyButton.mapCatalogItemToCart = function mapCatalogItemToCart({
  product,
  selectedItem,
  selectedQuantity,
  selectedSeller,
  assemblyOptions,
}) {
  return (
    product &&
    selectedItem &&
    selectedSeller &&
    selectedSeller.commertialOffer && [
      {
        index: 0,
        quantity: selectedQuantity,
        detailUrl: `/${product.linkText}/p`,
        name: product.productName,
        brand: product.brand,
        category:
          product.categories && product.categories.length > 0
            ? product.categories[0]
            : '',
        productRefId: product.productReference,
        seller: selectedSeller.sellerId,
        price: selectedSeller.commertialOffer.Price,
        listPrice: selectedSeller.commertialOffer.ListPrice,
        variant: selectedItem.name,
        skuId: selectedItem.itemId,
        imageUrl: path(['images', '0', 'imageUrl'], selectedItem),
        ...transformAssemblyOptions(
          path(['items'], assemblyOptions),
          path(['inputValues'], assemblyOptions),
          selectedSeller.commertialOffer.Price,
          selectedQuantity
        ),
        sellingPriceWithAssemblies:
          selectedSeller.commertialOffer.Price +
          sumAssembliesPrice(path(['items'], assemblyOptions) || {}),
      },
    ]
  )
}

export default EnhancedBuyButton

```


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

[User] Show me the implementation of store-components/react/components/BuyButton/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
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 { usePWA } from 'vtex.store-resources/PWAContext'
import { useCssHandles } from 'vtex.css-handles'
import useProduct from 'vtex.product-context/useProduct'
import { useProductDispatch } from 'vtex.product-context/ProductDispatchContext'
import { Button, ToastContext, Tooltip } from 'vtex.styleguide'

import useMarketingSessionParams from './hooks/useMarketingSessionParams'

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 ?? item.id,
    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()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [orderFormLoading])
}

/**
 * BuyButton Component.
 * Adds a list of sku items to the cart.
 * @deprecated This component is deprecated. Please use [vtex.add-to-cart-button](https://github.com/vtex-apps/add-to-cart-button) instead.
 */
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 { rootPath = '' } = useRuntime()
  const { promptOnCustomEvent } = settings
  const translateMessage = useCallback(id => intl.formatMessage({ 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: rootPath + customToastURL,
        }
      : undefined

    showToast({ message, action })
  }

  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 ?? item.id,
            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 || id === skuItem.id) &&
                seller === skuItem.seller
            )
        )
        await orderFormContext.refetch().catch(() => null)
      }

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

      const foundItem =
        addedItem.length &&
        orderFormItems &&
        orderFormItems.filter(
          item =>
            item.id === addedItem[0].skuId &&
            item.seller === addedItem[0].seller &&
            !item.canHaveAttachment
        ).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) {
        // eslint-disable-next-line no-restricted-globals
        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,
  /** Internationalization */
  intl: PropTypes.object.isRequired,
  /** 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-components/react/components/DiscountBadge/styles.css from the store-components 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-components/react/components/DiscountBadge/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { FormattedNumber } from 'react-intl'
import PropTypes from 'prop-types'
import { IOMessage } from 'vtex.native-types'

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.
 *
 * @deprecated This component is deprecated. Please use [vtex.product-price](https://github.com/vtex-apps/product-price) instead.
 */
function 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-components/react/components/Greeting/styles.css from the store-components 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-components/react/components/Greeting/index.js from the store-components 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 orderFormQuery from './queries/orderForm.gql'
import Loader from './Loader'
import styles from './styles.css'

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

// eslint-disable-next-line react/display-name
const withWrapper = Component => props =>
  (
    <Wrapper>
      <Component {...props} />
    </Wrapper>
  )

/**
 * @deprecated This component is deprecated.
 */
function 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-components/react/components/Greeting/Loader.js from the store-components 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-components/react/components/module/images.ts from the store-components 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-components/react/components/Animation/index.js from the store-components 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
 *
 * @deprecated This component is deprecated.
 */
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-components/react/components/Animation/animation.js from the store-components 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-components/react/components/Slider/slider.css from the store-components 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-components/react/components/Slider/global.css from the store-components 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-components/react/components/Slider/index.js from the store-components 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.
 *
 * @deprecated This component is deprecated. Please use [vtex.slider-layout](https://github.com/vtex-apps/slider-layout) instead.
 */
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) {
      // eslint-disable-next-line react/jsx-handler-names
      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-components/react/components/BackToTopButton/index.tsx from the store-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, useEffect } from 'react'
import { Button } from 'vtex.styleguide'
import { FormattedMessage } from 'react-intl'
import { Icon } from 'vtex.store-icons'
import classNames from 'classnames'

import styles from './styles.css'

interface Props {
  displayThreshold?: number
  display?: Display
}

type Display = 'button' | 'caret-icon'

const handleBackTop = () => {
  window.scroll({ top: 0, left: 0, behavior: 'smooth' })
}

const BackToTopButton: StorefrontFC<Props> = ({
  displayThreshold = 600,
  display = 'button',
}) => {
  const [isShowed, setIsShowed] = useState(false)
  const [scrollY, setScrollY] = useState(0)
  const backToTopButtonClasses = classNames(
    `${styles.backToTopButtonContainer} z-999 fixed bottom-2 right-2`,
    {
      [`${styles.backToTopButtonHidden}`]: !isShowed,
      [`${styles.backToTopButtonActive}`]: isShowed,
    }
  )

  function scrollValue() {
    setScrollY(window.pageYOffset)
  }

  useEffect(() => {
    window.addEventListener('scroll', scrollValue)

    return () => {
      window.removeEventListener('scroll', scrollValue)
    }
  }, [])

  useEffect(() => {
    if (scrollY > displayThreshold) {
      setIsShowed(true)
    } else {
      setIsShowed(false)
    }
  }, [scrollY, displayThreshold])

  if (!isShowed) {
    return null
  }

  return display === 'button' ? (
    <div className={backToTopButtonClasses}>
      <Button onClick={handleBackTop} size="regular">
        <FormattedMessage id="store/back-to-top.label" />
      </Button>
    </div>
  ) : (
    <div className={backToTopButtonClasses}>
      <Button onClick={handleBackTop} size="regular">
        <Icon id="nav-caret--up" size="16" />
      </Button>
    </div>
  )
}

export default BackToTopButton

```


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

[User] Show me the implementation of store-components/react/components/BackToTopButton/styles.css from the store-components VTEX app.

[Assistant]
Here's the implementation:

```css
.backToTopButtonContainer {
  border-bottom: 3.125rem;
  transition: all 0.3s ease-in-out;
}

.backToTopButtonHidden {
  overflow: hidden;
  height: 0;
  opacity: 0;
}

.backToTopButtonActive {
  height: auto;
  opacity: 1;
}

```


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

[User] Show me the implementation of store-components/react/components/BackToTopButton/BackToTopButton.test.tsx from the store-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { render } from '@vtex/test-tools/react'

import BackToTopButton from './index'

describe('<BackToTopButton /> component', () => {
  it('should be rendered', () => {
    const { asFragment } = render(<BackToTopButton />)

    expect(asFragment()).toBeTruthy()
  })
})

```


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

[User] Show me the implementation of store-components/react/components/ProductBrand/ProductBrandCssHandles.ts from the store-components VTEX app.

[Assistant]
Here's the implementation:

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

import { PRODUCT_BRAND_CSS_HANDLES } from '../../ProductBrand'

const { CssHandlesProvider, useContextCssHandles } = createCssHandlesContext(
  PRODUCT_BRAND_CSS_HANDLES
)

export {
  CssHandlesProvider as ProductBrandCssHandlesProvider,
  useContextCssHandles as useProductBrandCssHandles,
}

```


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

[User] Show me the implementation of store-components/react/components/ProductBrand/ProductBrandName.tsx from the store-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

import { useProductBrandCssHandles } from './ProductBrandCssHandles'

interface Props {
  brandName: string
  withLink: boolean
  slug?: string
}

export const CSS_HANDLES = ['productBrandNameLink', 'productBrandName'] as const

function ProductBrandName({ brandName, withLink, slug }: Props) {
  const { handles } = useProductBrandCssHandles()

  if (withLink && slug) {
    const nameLink = `/${slug}`

    return (
      <a
        href={nameLink}
        className={`${handles.productBrandNameLink}`}
        data-testid="name-redirect"
      >
        <span className={`${handles.productBrandName}`}>{brandName}</span>
      </a>
    )
  }

  return <span className={`${handles.productBrandName}`}>{brandName}</span>
}

export default ProductBrandName

```


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

[User] Show me the implementation of store-components/react/components/InfoCard/index.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import classNames from 'classnames'
import PropTypes, { bool, string, oneOf } from 'prop-types'
import React, { memo } from 'react'
import { values } from 'ramda'
import { injectIntl } from 'react-intl'
import {
  useRuntime,
  useExperimentalLazyImagesContext,
} from 'vtex.render-runtime'
import { formatIOMessage } from 'vtex.native-types'
import { useCssHandles } from 'vtex.css-handles'
import RichText from 'vtex.rich-text/index'

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

const ALLOWED_TAGS = ['p', 'span', 'a', 'div', 'br']
const ALLOWED_ATTRS = {
  a: ['class', 'href', 'title', 'target'],
  span: ['class'],
  p: ['class'],
  div: ['class'],
}

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 CSS_HANDLES = [
  'infoCardImageLinkWrapper',
  'infoCardContainer',
  'infoCardTextContainer',
  'infoCardHeadline',
  'infoCardSubhead',
  'infoCardBodyText',
  'infoCardImageContainer',
  'infoCardImage',
]

const InfoCard = ({
  isFullModeStyle,
  headline,
  subhead,
  bodyText,
  callToActionMode,
  callToActionText,
  callToActionUrl,
  callToActionLinkTarget,
  textPosition,
  textAlignment,
  imageUrl,
  mobileImageUrl,
  imageActionUrl,
  intl,
  htmlId,
  textMode,
  linkTarget,
  fetchpriority,
  preload,
  imageAltText,
}) => {
  const {
    hints: { mobile },
  } = useRuntime()

  const { lazyLoad } = useExperimentalLazyImagesContext()

  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
    ? {
        /* If lazyloaded, the background image comes from the `data-bg` attribute
         * below. Otherwise, sets it here as background-image */
        ...(!lazyLoad && { backgroundImage: `url(${finalImageUrl})` }),
        backgroundSize: 'cover',
      }
    : {}

  const containerAttributes =
    isFullModeStyle && lazyLoad ? { 'data-bg': finalImageUrl } : {}

  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,
      lazyload: lazyLoad,
    }
  )

  const linkWrapperClasses = `${handles.infoCardImageLinkWrapper} no-underline`

  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 bodyTextClasses = `${handles.infoCardBodyText} t-body mt6 c-on-base ${alignToken} mw-100`

  return (
    <LinkWrapper
      imageActionUrl={formatIOMessage({ id: imageActionUrl, intl })}
      extraCondition={!isFullModeStyle}
      linkProps={{ className: linkWrapperClasses, target: linkTarget }}
    >
      <div
        className={containerClasses}
        style={containerStyle}
        data-testid="container"
        id={htmlId}
        {...containerAttributes}
      >
        <div className={textContainerClasses}>
          {headline &&
            (textMode === 'html' ? (
              <div className={headlineClasses}>
                <SanitizedHTML
                  content={formatIOMessage({ id: headline, intl })}
                  allowedTags={ALLOWED_TAGS}
                  allowedAttributes={ALLOWED_ATTRS}
                />
              </div>
            ) : (
              <RichText className={headlineClasses} text={headline} />
            ))}
          {subhead &&
            (textMode === 'html' ? (
              <div className={subheadClasses}>
                <SanitizedHTML
                  content={formatIOMessage({ id: subhead, intl })}
                  allowedTags={ALLOWED_TAGS}
                  allowedAttributes={ALLOWED_ATTRS}
                />
              </div>
            ) : (
              <RichText className={subheadClasses} text={subhead} />
            ))}
          {bodyText &&
            (textMode === 'html' ? (
              <div className={bodyTextClasses}>
                <SanitizedHTML
                  content={formatIOMessage({ id: bodyText, intl })}
                  allowedTags={ALLOWED_TAGS}
                  allowedAttributes={ALLOWED_ATTRS}
                />
              </div>
            ) : (
              <RichText className={bodyTextClasses} text={bodyText} />
            ))}
          <CallToAction
            mode={callToActionMode}
            text={formatIOMessage({ id: callToActionText, intl })}
            url={formatIOMessage({ id: callToActionUrl, intl })}
            linkTarget={callToActionLinkTarget}
          />
        </div>
        {!isFullModeStyle && (
          <div className={`${handles.infoCardImageContainer} w-50-ns`}>
            <LinkWrapper
              imageActionUrl={formatIOMessage({ id: imageActionUrl, intl })}
              linkProps={{ target: linkTarget }}
            >
              <img
                className={handles.infoCardImage}
                src={finalImageUrl}
                style={{ objectFit: 'cover' }}
                alt={
                  imageAltText ||
                  formatIOMessage({ id: callToActionText, intl })
                }
                data-testid="half-image"
                fetchPriority={fetchpriority}
                {...(preload
                  ? {
                      'data-vtex-preload': 'true',
                    }
                  : {})}
              />
            </LinkWrapper>
          </div>
        )}
      </div>
    </LinkWrapper>
  )
}

const MemoizedInfoCard = memo(injectIntl(InfoCard))

MemoizedInfoCard.propTypes = {
  blockClass: string,
  isFullModeStyle: bool,
  textPosition: oneOf(getEnumValues(textPositionTypes)),
  headline: string,
  subhead: string,
  bodyText: string,
  callToActionMode: oneOf(getEnumValues(callToActionModeTypes)),
  callToActionText: string,
  callToActionUrl: string,
  imageUrl: string,
  mobileImageUrl: string,
  textAlignment: oneOf(getEnumValues(textAlignmentTypes)),
  imageActionUrl: string,
  imageAltText: string,
  intl: PropTypes.object,
  htmlId: string,
  textMode: oneOf(getEnumValues(textModeTypes)),
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a
  linkTarget: oneOf(['_self', '_blank', '_parent', '_top']),
  callToActionLinkTarget: oneOf(['_self', '_blank', '_parent', '_top']),
}

MemoizedInfoCard.defaultProps = {
  isFullModeStyle: false,
  textPosition: textPositionTypes.TEXT_POSITION_LEFT.value,
  headline: '',
  subhead: '',
  bodyText: '',
  callToActionMode: callToActionModeTypes.CALL_ACTION_BUTTON.value,
  callToActionText: '',
  callToActionUrl: '',
  imageUrl: '',
  imageAltText: '',
  mobileImageUrl: '',
  textAlignment: textAlignmentTypes.TEXT_ALIGNMENT_LEFT.value,
  textMode: textModeTypes.TEXT_MODE_HTML.value,
  linkTarget: '_self',
  callToActionLinkTarget: '_self',
  fetchpriority: 'auto',
  preload: false,
}

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,
    },
    imageAltText: {
      title: 'admin/editor.info-card.imageAltText.title',
      description: 'admin/editor.info-card.imageAltText.description',
      type: 'string',
      default: '',
    },
    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,
    },
    textMode: {
      title: 'admin/editor.info-card.textMode.title',
      description: 'admin/editor.info-card.textMode.description',
      type: 'string',
      default: textModeTypes.TEXT_MODE_HTML.value,
      enum: getEnumValues(textModeTypes),
      enumNames: getEnumNames(textModeTypes),
      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-components/react/components/InfoCard/CallToAction.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { Button } from 'vtex.styleguide'
import { Link } from 'vtex.render-runtime'
import { useCssHandles } from 'vtex.css-handles'

import { callActionValues } from './SchemaTypes'

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

// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {}

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

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

  // eslint-disable-next-line no-shadow
  const ActionWrapper = ({ text, mode }) => {
    if (mode === callActionValues.BUTTON) {
      return <Button onClick={noop}>{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`}
      target={linkTarget}
      to={url}
      alt={text}
    >
      <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-components/react/components/InfoCard/SchemaTypes.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
export const textModeValues = {
  HTML: 'html',
  RICHTEXT: 'rich-text',
}

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 textModeTypes = {
  TEXT_MODE_HTML: {
    name: 'admin/editor.info-card.textMode.html',
    value: textModeValues.HTML,
  },
  TEXT_MODE_RICHTEXT: {
    name: 'admin/editor.info-card.textMode.rich-text',
    value: textModeValues.RICHTEXT,
  },
}

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-components/react/components/InfoCard/LinkWrapper.js from the store-components 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-components/react/components/ProductSpecifications/Wrapper.js from the store-components VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import useProduct from 'vtex.product-context/useProduct'
import { isEmpty, propOr } 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(
    specificationGroup =>
      specificationGroup.originalName === 'allSpecifications' ||
      specificationGroup.name === 'allSpecifications'
  )

  const allSpecifications = groupWithAll ? groupWithAll.specifications : []

  return allSpecifications
}

/**
 * @deprecated This component is deprecated. Please use [vtex.product-specifications](https://github.com/vtex-apps/product-specifications) instead.
 */
function 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.schema = {
  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-components/react/components/ProductSpecifications/index.js from the store-components 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 { useCssHandles } from 'vtex.css-handles'
import { Tabs, Tab } from 'vtex.styleguide'
import { useDevice } from 'vtex.device-detector'

import { SanitizedHTML } from '../SanitizedHTML'
import GradientCollapse from '../GradientCollapse/index'

const CSS_HANDLES = [
  'specificationsTableContainer',
  'specificationsTabsContainer',
  'specificationsTitle',
  'specificationsTable',
  'specificationsTableRow',
  'specificationsTableHead',
  'specificationsTableBody',
  '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 &&
      visibleSpecifications.length > 0 &&
      hiddenSpecifications &&
      hiddenSpecifications.length > 0
    ) {
      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 && visibleSpecifications.length > 0) {
      return mappedSpecifications.filter(specification =>
        visibleSpecifications.find(
          filter =>
            specification.property.toLowerCase() === filter.toLowerCase()
        )
      )
    }

    if (hiddenSpecifications && hiddenSpecifications.length > 0) {
      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`}>
          <SanitizedHTML content={txt} />
        </h2>
      )}
    </FormattedMessage>
  )

  const specificationsTable = (
    <table
      className={`${handles.specificationsTable} w-100 bg-base border-collapse`}
    >
      <thead className={handles.specificationsTableHead}>
        <tr className={handles.specificationsTableRow}>
          <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 className={handles.specificationsTableBody}>
        {specificationItems.map((specification, i) => (
          <tr className={handles.specificationsTableRow} key={i}>
            <td
              data-specification={specification.property}
              className={`${handles.specificationItemProperty} w-50 b--muted-4 bb pa5`}
            >
              <SanitizedHTML content={specification.property} />
            </td>
            <td
              data-specification={specification.specifications}
              className={`${handles.specificationItemSpecifications} w-50 b--muted-4 bb pa5`}
            >
              <SanitizedHTML content={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={<SanitizedHTML content={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)}
                >
                  <SanitizedHTML content={specification.specifications} />
                </GradientCollapse>
              ) : (
                <SanitizedHTML content={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-components/react/components/ProductImages/styles.css from the store-components VTEX app.

[Assistant]
Here's the implementation:

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

.productVideo {
}

.productImage {
}

.highQualityProductImageImgElement {
  max-height: unset;
  object-fit: contain;
}

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

[Assistant]
Here's the implementation:

```javascript
import React, { useMemo } from 'react'
import useProduct from 'vtex.product-context/useProduct'
import { useResponsiveValues } from 'vtex.responsive-values'
import { path, 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,
    contentOrder,
    placeholder,
  } = useResponsiveValues(
    pick(
      [
        'aspectRatio',
        'maxHeight',
        'showNavigationArrows',
        'showPaginationDots',
        'contentOrder',
        'placeholder',
      ],
      props
    )
  )

  const { selectedItem, skuSelector, product } = valuesFromContext

  const images = useMemo(() => {
    if (props.images != null) {
      return props.images
    }

    let imagePaths

    // if there's a image sku defined
    if (
      product &&
      product.items &&
      skuSelector &&
      skuSelector.selectedImageVariationSKU
    ) {
      const skuItem = product.items.find(
        sku => sku.itemId === skuSelector.selectedImageVariationSKU
      )

      if (skuItem) {
        imagePaths = skuItem.images
      }
    }

    if (!imagePaths && selectedItem) {
      imagePaths = selectedItem.images
    }

    return (imagePaths || []).map(generateImageConfig)
  }, [props.images, product, skuSelector, selectedItem])

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

  return (
    <ProductImages
      images={images}
      videos={videos}
      hiddenImages={props.hiddenImages}
      placeholder={placeholder}
      // thumbnailPosition is a legacy prop from product-details
      position={props.position || props.thumbnailPosition}
      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}
      thumbnailVisibility={props.thumbnailVisibility}
      contentOrder={contentOrder}
      ModalZoomElement={props.ModalZoom}
      contentType={props.contentType}
      showImageLabel={props.showImageLabel}
      // Deprecated
      zoomProps={props.zoomProps}
      displayMode={props.displayMode}
    />
  )
}

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

[Assistant]
Here's the implementation:

```javascript
import React, { useMemo } from 'react'
import PropTypes from 'prop-types'
import { useCssHandles } from 'vtex.css-handles'

import Carousel from './components/Carousel'
import ProductImage from './components/ProductImage'
import {
  THUMBS_ORIENTATION,
  THUMBS_POSITION_HORIZONTAL,
  THUMBS_VISIBILITY,
  DEFAULT_EXCLUDE_IMAGE_WITH,
  DISPLAY_MODE,
} from './utils/enums'

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

const ProductImages = ({
  position,
  displayThumbnailsArrows,
  hiddenImages,
  placeholder,
  images: allImages,
  videos: allVideos,
  thumbnailsOrientation,
  aspectRatio,
  maxHeight,
  thumbnailAspectRatio,
  thumbnailMaxHeight,
  showNavigationArrows,
  showPaginationDots,
  showImageLabel = false,
  thumbnailVisibility,
  contentOrder = 'images-first',
  zoomMode,
  zoomFactor,
  ModalZoomElement,
  contentType = 'all',
  // Deprecated
  zoomProps,
  displayMode,
}) => {
  if (hiddenImages && !Array.isArray(hiddenImages)) {
    hiddenImages = [hiddenImages]
  }

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

  const { handles, withModifiers } = useCssHandles(CSS_HANDLES)
  const productImagesContainerClass = withModifiers(
    'productImagesContainer',
    displayMode
  )

  const images = useMemo(() => {
    const shouldIncludeImages = contentType !== 'videos'

    return shouldIncludeImages
      ? allImages
          .filter(
            image =>
              !image.imageLabel ||
              !excludeImageRegexes.some(regex => regex.test(image.imageLabel))
          )
          .map(image => ({
            type: 'image',
            url: image.imageUrls ? image.imageUrls[0] : image.imageUrl,
            alt: image.imageLabel,
            thumbUrl: image.thumbnailUrl || image.imageUrl,
            ...(showImageLabel && { imageLabel: image.imageLabel }),
          }))
      : []
  }, [allImages, contentType, excludeImageRegexes, showImageLabel])

  const videos = useMemo(() => {
    const shouldIncludeVideos = contentType !== 'images'

    return shouldIncludeVideos
      ? allVideos.map(video => ({
          type: 'video',
          src: video.videoUrl,
          thumbWidth: 300,
        }))
      : []
  }, [allVideos, contentType])

  const showVideosFirst = contentOrder === 'videos-first'

  const slides = useMemo(() => {
    return showVideosFirst ? [...videos, ...images] : [...images, ...videos]
  }, [showVideosFirst, videos, images])

  const { zoomType: legacyZoomType } = zoomProps || {}
  const isZoomDisabled = legacyZoomType === 'no-zoom' || zoomMode === 'disabled'

  const containerClass = `${productImagesContainerClass} ${handles.content} w-100`

  if (displayMode === DISPLAY_MODE.LIST) {
    return (
      <div className={containerClass}>
        {images.map(({ url, alt, imageLabel }, index) => (
          <ProductImage
            key={index}
            src={url}
            alt={alt}
            imageLabel={imageLabel}
            maxHeight={maxHeight}
            zoomFactor={zoomFactor}
            aspectRatio={aspectRatio}
            ModalZoomElement={ModalZoomElement}
            zoomMode={isZoomDisabled ? 'disabled' : zoomMode}
          />
        ))}
      </div>
    )
  }

  if (displayMode === DISPLAY_MODE.FIRST_IMAGE && images?.length) {
    const { url, alt, imageLabel } = images?.[0] ?? {}

    return (
      <div className={containerClass}>
        <ProductImage
          src={url}
          alt={alt}
          imageLabel={imageLabel}
          maxHeight={maxHeight}
          zoomFactor={zoomFactor}
          aspectRatio={aspectRatio}
          ModalZoomElement={ModalZoomElement}
          zoomMode={isZoomDisabled ? 'disabled' : zoomMode}
        />
      </div>
    )
  }

  return (
    <div className={containerClass}>
      <Carousel
        slides={slides}
        placeholder={placeholder}
        position={position}
        zoomMode={zoomMode}
        maxHeight={maxHeight}
        zoomFactor={zoomFactor}
        aspectRatio={aspectRatio}
        ModalZoomElement={ModalZoomElement}
        thumbnailMaxHeight={thumbnailMaxHeight}
        showPaginationDots={showPaginationDots}
        thumbnailAspectRatio={thumbnailAspectRatio}
        showNavigationArrows={showNavigationArrows}
        thumbnailsOrientation={thumbnailsOrientation}
        displayThumbnailsArrows={displayThumbnailsArrows}
        thumbnailVisibility={thumbnailVisibility}
        // Deprecated
        zoomProps={zoomProps}
      />
    </div>
  )
}

ProductImages.propTypes = {
  /** The position of the thumbs */
  position: PropTypes.oneOf([
    THUMBS_POSITION_HORIZONTAL.LEFT,
    THUMBS_POSITION_HORIZONTAL.RIGHT,
  ]),
  ModalZoomElement: PropTypes.any,
  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),
  ]),
  placeholder: 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,
    })
  ),
  videos: PropTypes.arrayOf(
    PropTypes.shape({
      videoUrl: PropTypes.string,
    })
  ),
  zoomProps: PropTypes.shape({
    zoomType: PropTypes.string,
  }),
  displayThumbnailsArrows: PropTypes.bool,
  aspectRatio: PropTypes.string,
  maxHeight: PropTypes.number,
  thumbnailAspectRatio: PropTypes.string,
  thumbnailMaxHeight: PropTypes.number,
  showNavigationArrows: PropTypes.bool,
  showPaginationDots: PropTypes.bool,
  showImageLabel: PropTypes.bool,
  thumbnailVisibility: PropTypes.oneOf([
    THUMBS_VISIBILITY.VISIBLE,
    THUMBS_VISIBILITY.HIDDEN,
  ]),
  contentOrder: PropTypes.oneOf(['images-first', 'videos-first']),
  zoomMode: PropTypes.oneOf([
    'disabled',
    'open-modal',
    'in-place-click',
    'in-place-hover',
  ]),
  zoomFactor: PropTypes.number,
  contentType: PropTypes.oneOf(['all', 'images', 'videos']),
  displayMode: PropTypes.oneOf([
    DISPLAY_MODE.CAROUSEL,
    DISPLAY_MODE.LIST,
    DISPLAY_MODE.FIRST_IMAGE,
  ]),
}

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

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-components/react/components/Logo/styles.css from the store-components 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-components/react/components/Logo/LogoPlaceholder.tsx from the store-components VTEX app.

[Assistant]
Here's the implementation:

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

interface Props {
  width?: string | number
  height?: string | number
  title: string
}

function LogoPlaceholder({ width, height, title }: Props) {
  return (
    <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>
  )
}

export default memo(LogoPlaceholder)

```


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

[User] Show me the implementation of store-components/react/components/GradientCollapse/styles.css from the store-components 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;
}
```