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

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

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { BuyButton } from 'vtex.store-components'
import { useRuntime } from 'vtex.render-runtime'
// eslint-disable-next-line no-restricted-imports
import { equals } from 'ramda'
import classnames from 'classnames'
import { IOMessage } from 'vtex.native-types'
import { ProductSummaryContext } from 'vtex.product-summary-context'
import { useCssHandles } from 'vtex.css-handles'

import displayButtonTypes, {
  getDisplayButtonNames,
  getDisplayButtonValues,
} from './utils/displayButtonTypes'
import productSummary from './productSummary.css'

const ALWAYS_ADD_TO_CART = 'alwaysAddToCart'
const DEFAULT_BUTTON_BEHAVIOR = 'default'

const CSS_HANDLES = ['buyButton', 'buyButtonContainer'] as const

interface Props {
  /** What the buy button should do when you click it, if you pass default it will add to cart only if there is only one SKU of that product */
  buyButtonBehavior?: 'default' | 'alwaysGoToProduct' | 'alwaysAddToCart'
  /** Should redirect to checkout after clicking on buy */
  isOneClickBuy?: boolean
  /** Custom buy button text */
  buyButtonText?: string
  /**
   * Defines the display mode of buy button
   * @default "displayButtonAlways"
   */
  displayBuyButton?:
    | 'displayButtonAlways'
    | 'displayButtonHover'
    | 'displayButtonNone'
  /** A custom URL for the `VIEW CART` button inside the toast created by BuyButton */
  customToastURL?: string
  isHovering?: boolean
}

/**
 * @deprecated This component is deprecated. Please migrate to vtex.add-to-cart-button and minicart.v2.
 */
function ProductSummaryBuyButton({
  displayBuyButton = 'displayButtonAlways',
  isOneClickBuy = false,
  buyButtonText,
  customToastURL,
  buyButtonBehavior = DEFAULT_BUTTON_BEHAVIOR,
  isHovering,
}: Props) {
  const {
    hints: { mobile },
  } = useRuntime()

  const { product, selectedItem, selectedQuantity } =
    ProductSummaryContext.useProductSummary() ?? {}

  const { handles } = useCssHandles(CSS_HANDLES)

  const hoverBuyButton =
    equals(displayBuyButton, displayButtonTypes.DISPLAY_ALWAYS.value) ||
    !equals(displayBuyButton, displayButtonTypes.DISPLAY_ON_HOVER.value) ||
    (isHovering && !mobile)

  const showBuyButton =
    !equals(displayBuyButton, displayButtonTypes.DISPLAY_NONE.value) &&
    !(
      equals(displayBuyButton, displayButtonTypes.DISPLAY_ON_HOVER.value) &&
      mobile
    )

  const buyButtonClasses = classnames(handles.buyButton, 'center mw-100', {
    [productSummary.isHidden]: !hoverBuyButton,
  })

  const containerClass = classnames(handles.buyButtonContainer, 'pv3 w-100 db')

  const selectedSeller = selectedItem?.seller
  const isAvailable =
    selectedSeller?.commertialOffer &&
    selectedSeller.commertialOffer.AvailableQuantity > 0

  const skuItems = BuyButton.mapCatalogItemToCart({
    product,
    selectedItem,
    selectedSeller,
    selectedQuantity,
  })

  const { items = [] } = product
  const shouldAddToCart =
    isAvailable &&
    (buyButtonBehavior === ALWAYS_ADD_TO_CART ||
      (buyButtonBehavior === DEFAULT_BUTTON_BEHAVIOR && items.length === 1))

  return (
    showBuyButton && (
      <div
        className={containerClass}
        data-testid="product-summary__buy-button-container"
      >
        <div className={buyButtonClasses}>
          <BuyButton
            skuItems={skuItems}
            available={isAvailable}
            isOneClickBuy={isOneClickBuy}
            customToastURL={customToastURL}
            shouldAddToCart={shouldAddToCart}
          >
            <IOMessage id={buyButtonText} />
          </BuyButton>
        </div>
      </div>
    )
  )
}

ProductSummaryBuyButton.schema = {
  title: 'admin/editor.productSummaryBuyButton.title',
  type: 'object',
  properties: {
    isOneClickBuy: {
      type: 'boolean',
      title: 'admin/editor.productSummary.isOneClickBuy.title',
      default: false,
      isLayout: true,
    },
    displayBuyButton: {
      title: 'admin/editor.productSummary.displayBuyButton.title',
      type: 'string',
      enum: getDisplayButtonValues(),
      enumNames: getDisplayButtonNames(),
      default: displayButtonTypes.DISPLAY_ALWAYS.value,
      isLayout: true,
    },
  },
}

export default ProductSummaryBuyButton

```


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

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

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import type { PropsWithChildren } from 'react'
import { useCssHandles } from 'vtex.css-handles'

const CSS_HANDLES = ['column'] as const

function Column({ children }: PropsWithChildren<{}>) {
  const { handles } = useCssHandles(CSS_HANDLES)

  return <div className={`${handles.column} flex items-center`}>{children}</div>
}

export default Column

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "definitions": {
    "ProductSummary": {
      "properties": {
        "badgeText": {
          "title": "admin/editor.productSummary.badgeText.title",
          "$ref": "app:vtex.native-types#/definitions/text"
        },
        "buyButtonText": {
          "title": "admin/editor.productSummary.buyButtonText.title",
          "$ref": "app:vtex.native-types#/definitions/text",
          "default": "store/button-label"
        },
        "labelSellingPrice": {
          "title": "admin/editor.productSummaryPrice.labelSellingPrice.title",
          "$ref": "app:vtex.native-types#/definitions/text",
          "default": "store/pricing.to"
        },
        "labelListPrice": {
          "title": "admin/editor.productSummaryPrice.labelListPrice.title",
          "$ref": "app:vtex.native-types#/definitions/text",
          "default": "store/pricing.from"
        },
        "sponsoredBadgeLabel": {
          "title": "admin/editor.productSummary.sponsoredBadge.title",
          "$ref": "app:vtex.native-types#/definitions/text",
          "default": "store/sponsored-badge.label"
        },
        "sponsoredBadgePosition": {
          "title": "admin/editor.productSummary.sponsoredBadge.position",
          "type": "string",
          "enum": ["titleTop", "containerTopLeft", "none"],
          "default": "containerTopLeft",
          "enumNames": [
            "admin/editor.productSummary.sponsoredBadge.position.titleTop",
            "admin/editor.productSummary.sponsoredBadge.position.containerTopLeft",
            "admin/editor.productSummary.sponsoredBadge.position.none"
          ]
        },
        "trackListName": {
          "title": "admin/editor.productSummary.trackListName.title",
          "type": "boolean",
          "default": true
        }
      }
    },
    "ProductSummaryList": {
      "title": "admin/editor.productSummary.buyButtonText.title",
      "$ref": "app:vtex.native-types#/definitions/text",
      "default": "store/button-label"
    }
  }
}

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "product-summary.shelf": {
    "children": [
      "product-summary-image",
      "product-summary-name",
      "product-summary-attachment-list",
      "product-summary-space",
      "product-summary-column#1"
    ]
  },
  "product-summary-column#1": {
    "children": ["product-summary-price", "product-summary-buy-button"]
  },
  "product-summary.unstable--flex": {
    "children": [
      "product-summary-image",
      "product-summary-name",
      "product-summary-attachment-list",
      "product-summary-space",
      "unstable--product-summary-column#1"
    ]
  },
  "unstable--product-summary-column#1": {
    "children": ["product-summary-price", "product-summary-buy-button"]
  }
}

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "product-summary": {
    "component": "ProductSummaryLegacy",
    "content": {
      "$ref": "app:vtex.product-summary#/definitions/ProductSummary"
    },
    "allowed": ["addon-summary-btn"]
  },
  "product-summary.shelf": {
    "component": "ProductSummaryCustom",
    "composition": "children"
  },
  "product-summary-column": {
    "component": "Column",
    "composition": "children"
  },
  "product-summary.unstable--flex": {
    "component": "ProductSummaryCustom",
    "composition": "children"
  },
  "unstable--product-summary-column": {
    "component": "Column",
    "composition": "children"
  },
  "product-summary-space": {
    "component": "Spacer"
  },
  "product-summary-attachment-list": {
    "component": "ProductSummaryAttachmentList"
  },
  "product-summary-buy-button": {
    "component": "ProductSummaryBuyButton",
    "content": {
      "properties": {
        "buyButtonText": {
          "$ref": "app:vtex.product-summary#/definitions/ProductSummary/properties/buyButtonText"
        }
      }
    }
  },
  "product-summary-description": {
    "component": "ProductSummaryDescription"
  },
  "product-summary-image": {
    "component": "ProductSummaryImage",
    "content": {
      "properties": {
        "badgeText": {
          "$ref": "app:vtex.product-summary#/definitions/ProductSummary/properties/badgeText"
        }
      }
    }
  },
  "product-summary-name": {
    "component": "ProductSummaryName"
  },
  "product-summary-sku-name": {
    "component": "ProductSummarySKUName"
  },
  "product-summary-brand": {
    "component": "ProductSummaryBrand"
  },
  "product-summary-price": {
    "component": "ProductSummaryPrice",
    "content": {
      "properties": {
        "labelSellingPrice": {
          "$ref": "app:vtex.product-summary#/definitions/ProductSummary/properties/labelSellingPrice"
        },
        "labelListPrice": {
          "$ref": "app:vtex.product-summary#/definitions/ProductSummary/properties/labelListPrice"
        }
      }
    }
  },
  "product-summary-sku-selector": {
    "component": "ProductSummarySKUSelector"
  },
  "product-summary-specification-badges": {
    "component": "ProductSummarySpecificationBadges"
  },
  "product-summary-add-to-list-button": {
    "component": "ProductSummaryAddToListButton",
    "allowed": ["addon-summary-btn"]
  },
  "addon-summary-btn": {
    "component": "*"
  },
  "list-context.product-list": {
    "allowed": ["product-summary"],
    "composition": "children",
    "component": "ProductSummaryList",
    "preview": {
      "type": "grid",
      "height": 550
    }
  },
  "list-context.product-list-static": {
    "allowed": ["product-summary"],
    "composition": "children",
    "component": "ProductSummaryListWithoutQuery"
  }
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
export {}

declare module 'vtex.store-components' {
  export const SKUSelector
  export const DiscountBadge
  export const ProductPrice
  export const 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 product-summary/react/typings/vtex.render-runtime.d.ts from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```typescript
export {}

declare module 'vtex.render-runtime' {
  export function useTreePath(): {
    treePath: string
  }
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
declare module '*.gql' {
  const graphql: any
  export default graphql
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
export {}

declare module 'vtex.store-resources' {
  import type { DocumentNode } from 'graphql'

  export const QueryItemsWithSimulation: DocumentNode
}

```


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

[User] Show me the implementation of product-summary/react/legacy/index.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable react/prop-types */
/* eslint-disable react/jsx-handler-names */
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { ProductName, ProductPrice } from 'vtex.store-components'
import { ProductListContext } from 'vtex.product-list-context'
import { useInView } from 'react-intersection-observer'

import ProductSummaryNormal from './components/ProductSummaryNormal'
import ProductSummarySmall from './components/ProductSummarySmall'
import ProductSummaryInline from './components/ProductSummaryInline'
import ProductSummaryInlinePrice from './components/ProductSummaryInlinePrice'
import displayButtonTypes, {
  getDisplayButtonNames,
  getDisplayButtonValues,
} from '../utils/displayButtonTypes'
import { productShape } from '../utils/propTypes'

const DISPLAY_MODE_MAP = {
  normal: ProductSummaryNormal,
  small: ProductSummarySmall,
  inline: ProductSummaryInline,
  inlinePrice: ProductSummaryInlinePrice,
}

/**
 * Product Summary component. Summarizes the product information.
 */
class ProductSummary extends Component {
  static propTypes = {
    /** Product that owns the informations */
    product: productShape,
    /** Shows the product list price */
    showListPrice: PropTypes.bool,
    /** Should redirect to checkout after clicking on buy */
    isOneClickBuy: PropTypes.bool,
    /** Set pricing labels' visibility */
    showLabels: PropTypes.bool,
    /** Set installments' visibility */
    showInstallments: PropTypes.bool,
    /** Set the borders product's visibility */
    showBorders: PropTypes.bool,
    /** Set the discount badge's visibility */
    showBadge: PropTypes.bool,
    /** Set the product description visibility */
    showDescription: PropTypes.bool,
    /** Set the quantity selector visibility */
    showQuantitySelector: PropTypes.bool,
    /** Text of selling Price's label */
    labelSellingPrice: PropTypes.string,
    /** Text of list Price's label */
    labelListPrice: PropTypes.string,
    /** Text shown on badge */
    badgeText: PropTypes.string,
    /** Custom buy button text */
    buyButtonText: PropTypes.string,
    /** Defines the display mode of buy button */
    displayBuyButton: PropTypes.oneOf(getDisplayButtonValues()),
    /** Hides the buy button completely . If active, the button will not be shown in any condition */
    hideBuyButton: PropTypes.bool,
    /** Defines if the collection badges are shown */
    showCollections: PropTypes.bool,
    /** Name schema props */
    nameSchema: PropTypes.object,
    /** Display mode of the summary used in the search result */
    displayMode: PropTypes.oneOf(['normal', 'small', 'inline', 'inlinePrice']),
    /** Function that is executed when a product is clicked */
    actionOnClick: PropTypes.func,
    /** Set the price align to left */
    priceAlignLeft: PropTypes.bool,
    muted: PropTypes.bool,
    index: PropTypes.number,
    position: PropTypes.number,
    listName: PropTypes.string,
  }

  static defaultProps = {
    showListPrice: true,
    showInstallments: true,
    showLabels: true,
    showBadge: true,
    showQuantitySelector: true,
    showCollections: false,
    showDescription: false,
    displayBuyButton: displayButtonTypes.DISPLAY_ALWAYS.value,
    isOneClickBuy: false,
    nameSchema: {
      showProductReference: false,
      showBrandName: false,
      showSku: false,
    },
    displayMode: 'normal',
    showBorders: false,
    muted: false,
  }

  state = {
    isHovering: false,
    isUpdatingItems: false,
  }

  handleMouseLeave = () => {
    this.setState({ isHovering: false })
  }

  handleMouseEnter = () => {
    this.setState({ isHovering: true })
  }

  handleItemsStateUpdate = (isLoading) =>
    this.setState({ isUpdatingItems: isLoading })

  componentDidMount = () => {
    this.sendImpressionEvent()
  }

  componentDidUpdate = (prevProps) => {
    if (
      prevProps.productListDispatch !== this.props.productListDispatch ||
      prevProps.inView !== this.props.inView ||
      prevProps.product !== this.props.product
    ) {
      this.sendImpressionEvent()
    }
  }

  sendImpressionEvent = () => {
    const { inView, productListDispatch } = this.props

    if (inView && productListDispatch) {
      productListDispatch({
        type: 'SEND_IMPRESSION',
        args: { product: this.props.product, position: this.props.position },
      })
    }
  }

  render() {
    const {
      product,
      actionOnClick,
      displayMode,
      displayBuyButton,
      isOneClickBuy,
      buyButtonText,
      showBorders,
      showDescription,
      showBadge,
      badgeText,
      showCollections,
      showListPrice,
      showLabels,
      showInstallments,
      labelSellingPrice,
      labelListPrice,
      nameSchema: showFieldsProps,
      showQuantitySelector,
      priceAlignLeft,
      muted,
      index,
      listName,
      inViewRef,
    } = this.props

    const imageProps = {
      product,
      showBadge,
      badgeText,
      showCollections,
      displayMode,
    }

    const nameProps = { product, showFieldsProps }

    const priceProps = {
      product,
      showListPrice,
      showLabels,
      showInstallments,
      labelSellingPrice,
      labelListPrice,
      isLoading: this.state.isUpdatingItems,
    }

    const buyButtonProps = {
      product,
      displayBuyButton,
      isOneClickBuy,
      buyButtonText,
      isHovering: this.state.isHovering,
    }

    const ProductSummaryComponent =
      DISPLAY_MODE_MAP[displayMode] || ProductSummaryNormal

    return (
      <ProductSummaryComponent
        product={product}
        showBorders={showBorders}
        showDescription={showDescription}
        handleMouseEnter={this.handleMouseEnter}
        handleMouseLeave={this.handleMouseLeave}
        handleItemsStateUpdate={this.handleItemsStateUpdate}
        actionOnClick={actionOnClick}
        imageProps={imageProps}
        nameProps={nameProps}
        priceProps={priceProps}
        buyButtonProps={buyButtonProps}
        showQuantitySelector={showQuantitySelector}
        priceAlignLeft={priceAlignLeft}
        muted={muted}
        index={index}
        listName={listName}
        inViewRef={inViewRef}
      />
    )
  }
}

const ProductSummaryWrapper = (props) => {
  const { useProductListDispatch } = ProductListContext
  const productListDispatch = useProductListDispatch()
  const [inViewRef, inView] = useInView({
    // Triggers the event when the element is 75% visible
    threshold: 0.75,
    triggerOnce: true,
  })

  return (
    <ProductSummary
      {...props}
      productListDispatch={productListDispatch}
      inView={inView}
      inViewRef={inViewRef}
    />
  )
}

const defaultSchema = {
  title: 'admin/editor.productSummary.title',
  description: 'admin/editor.productSummary.description',
  type: 'object',
  properties: {
    isOneClickBuy: {
      type: 'boolean',
      title: 'admin/editor.productSummary.isOneClickBuy.title',
      default: false,
      isLayout: true,
    },
    showBadge: {
      type: 'boolean',
      title: 'admin/editor.productSummary.showBadge.title',
      default: true,
      isLayout: true,
    },
    displayBuyButton: {
      title: 'admin/editor.productSummary.displayBuyButton.title',
      type: 'string',
      enum: getDisplayButtonValues(),
      enumNames: getDisplayButtonNames(),
      default: displayButtonTypes.DISPLAY_ALWAYS.value,
      isLayout: true,
    },
    showCollections: {
      type: 'boolean',
      title: 'admin/editor.productSummary.showCollections.title',
      default: false,
      isLayout: true,
    },

    ...ProductPrice.schema.properties,
  },
}

ProductSummaryWrapper.getSchema = () => {
  const nameSchema = ProductName.schema

  return {
    ...defaultSchema,
    properties: {
      ...defaultSchema.properties,
      nameSchema,
    },
  }
}

export default ProductSummaryWrapper

```


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

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

[Assistant]
Here's the implementation:

```typescript
export default function clone(aObject: object) {
  if (!aObject) {
    return aObject
  }

  let v
  const bObject = Array.isArray(aObject) ? [] : {}

  for (const k in aObject) {
    // @ts-expect-error this function was copy-pasted from a JS snippet
    v = aObject[k]
    // @ts-expect-error this function was copy-pasted from a JS snippet
    bObject[k] = typeof v === 'object' ? clone(v) : v
  }

  return bObject
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
import {
  Product,
  SponsoredBadgePosition,
} from 'vtex.product-summary-context/react/ProductSummaryTypes'

interface NewAdvertisement {
  eventParameters?: string
}

const shouldShowSponsoredBadge = (
  product: Product,
  position: SponsoredBadgePosition,
  eligiblePosition: SponsoredBadgePosition
) => {
  const isSponsored =
    !!product?.advertisement?.adId ||
    !!(product.advertisement as NewAdvertisement)?.eventParameters
  const showSponsoredBadge = isSponsored && position === eligiblePosition

  return showSponsoredBadge
}

export default shouldShowSponsoredBadge

```


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

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

[Assistant]
Here's the implementation:

```typescript
// eslint-disable-next-line no-restricted-imports
import { pathOr } from 'ramda'

export const CHOICE_TYPES = {
  SINGLE: 'SINGLE',
  MULTIPLE: 'MULTIPLE',
  TOGGLE: 'TOGGLE',
}

export const getProductPrice = (product: any) =>
  pathOr(0, ['sku', 'seller', 'commertialOffer', 'Price'], product)

```


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

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

[Assistant]
Here's the implementation:

```typescript
import { changeImageUrlSize } from './normalize'

/** Parses ratio values into a multiplier to set the image height.
 * For example, turns "3:4" into 1.333, so the image height will be
 * 1.333 times its width.
 */
const parseAspectRatio = (input: string | number) => {
  if (!input) {
    return null
  }

  if (typeof input === 'string') {
    if (input === 'auto') {
      return null
    }

    const separator = ':'
    const data = input.split(separator)

    if (data.length !== 2) {
      return null
    }

    const [width, height] = data
    const ratio = parseFloat(height) / parseFloat(width)

    if (typeof ratio !== 'number' || Number.isNaN(ratio)) {
      return null
    }

    return ratio
  }

  if (typeof input === 'number') {
    return input
  }

  return null
}

export const imageUrl = (
  src: string,
  size: number,
  maxSize: number,
  aspectRatio: string | number
) => {
  let width = size
  let height: string | number = 'auto'

  if (aspectRatio && aspectRatio !== 'auto') {
    height = size * (parseAspectRatio(aspectRatio) ?? 1)

    if (width > maxSize) {
      height /= width / maxSize
      width = maxSize
    }

    if (height > maxSize) {
      width /= height / maxSize
      height = maxSize
    }

    width = Math.round(width)
    height = Math.round(height)
  } else {
    width = Math.min(maxSize, width)
  }

  return changeImageUrlSize(src, width, height)
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
import type { ProductTypes } from 'vtex.product-context'

import { getDefaultSeller } from '../modules/seller'

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

export type PreferenceType =
  | 'FIRST_AVAILABLE'
  | 'LAST_AVAILABLE'
  | 'PRICE_ASC'
  | 'PRICE_DESC'

type PriceConditionRule = 'lowest' | 'highest'

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

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

function getParamFromUrl(url: string, name: string) {
  return (url?.split(`${name}=`)[1] ?? '')?.split('&')[0]
}

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

export function cleanImageUrl(imageUrl: string) {
  const cleanUrlResult = baseUrlRegex.exec(imageUrl)
  const vParam = getParamFromUrl(imageUrl, 'v')

  if (cleanUrlResult && cleanUrlResult.length > 0) {
    return {
      cleanUrl: cleanUrlResult[0],
      vParam,
    }
  }

  return { cleanUrl: imageUrl }
}

function replaceLegacyFileManagerUrl(
  imageUrl: string,
  width: string | number,
  height: string | number
) {
  const legacyUrlPattern = '/arquivos/ids/'
  const isLegacyUrl = imageUrl.includes(legacyUrlPattern)

  if (!isLegacyUrl) return imageUrl

  const { vParam, cleanUrl } = cleanImageUrl(imageUrl)

  return vParam
    ? `${cleanUrl}-${width}-${height}?v=${vParam}`
    : `${cleanUrl}-${width}-${height}`
}

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

  const normalizedImageUrl = replaceLegacyFileManagerUrl(
    imageUrl,
    width,
    height
  )

  const queryStringSeparator = normalizedImageUrl.includes('?') ? '&' : '?'

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

function getPriceFromSeller(seller: ProductTypes.Seller) {
  return seller.commertialOffer.Price
}

function isAvailable({
  commertialOffer: { AvailableQuantity },
}: ProductTypes.Seller) {
  return AvailableQuantity != null && AvailableQuantity > 0
}

/**
 * @description
 * Find the lowest/highest `Price` between sellers
 * @param item an SKU
 * @param condition the condition enum used for comparison functions
 * */
function getBestPrice(item: ProductTypes.Item, condition: PriceConditionRule) {
  // If there's only 1 seller, avoid filtering
  const { sellers } = item

  if (sellers.length === 1) {
    return sellers[0].commertialOffer.Price
  }

  const availableSellers = sellers.filter(isAvailable)
  const allPrices = availableSellers.map(getPriceFromSeller)

  let itemPrice

  if (condition === 'highest') {
    itemPrice = allPrices.reduce((max, price) => (price > max ? price : max))
  } else {
    itemPrice = allPrices.reduce((min, price) => (price < min ? price : min))
  }

  return itemPrice
}

/**
 * @description
 * Retrieves the SKU with the best price considering a provided condition
 * @param items an array of SKUs
 * @param condition the condition [highest|lowest]
 * */
function findSKUByPrice(
  items: ProductTypes.Item[],
  condition: PriceConditionRule
) {
  // If none or only 1 sku is available, avoid reducing
  const availableItems = items.filter(findAnyAvailable)

  if (availableItems.length === 0) return items[0]
  if (availableItems.length === 1) return availableItems[0]

  const bestPrices = availableItems.map((item) => getBestPrice(item, condition))
  let itemToReturn

  if (condition === 'highest') {
    itemToReturn = bestPrices.reduce(
      (maxIndex, currentPrice, currentIndex) =>
        currentPrice > bestPrices[maxIndex] ? currentIndex : maxIndex,
      0
    )
  } else {
    itemToReturn = bestPrices.reduce(
      (minIndex, currentPrice, currentIndex) =>
        currentPrice < bestPrices[minIndex] ? currentIndex : minIndex,
      0
    )
  }

  return availableItems[itemToReturn]
}

/**
 * @summary
 * Used to return the first available SKU
 * */
function getAvailableProduct({ sellers }: ProductTypes.Item) {
  return sellers.find(isAvailable)
}

/**
 * @summary
 * Used to probe if at least 1 seller has stock
 * */
function findAnyAvailable({ sellers }: ProductTypes.Item) {
  return sellers.some(isAvailable)
}

/**
 * @description
 * Attempts to retrieve the specific SKU using a custom Product (field)
 * in the Catalog. The specification has the value of the SKU ID
 * that will be looking for. If not found for any reason, we default
 * to finding the preferred SKU
 * @param items an array of SKUs
 * @param defaultSKUspec the ID used to find the SKU
 * @param preferenceFallback the logic for finding an SKU if the ID fails
 * @see {@link https://vtex.io/docs/recipes/all} recipe on how to implement this
 * */
function findDefaultSKU(
  items: ProductTypes.Item[],
  defaultSKUspec: string[],
  preferenceFallback: PreferenceType
) {
  const specificSKU = items.find(
    ({ itemId }) => String(itemId) === String(defaultSKUspec)
  )

  if (specificSKU) {
    return specificSKU
  }

  return findPreferredSKU(items, preferenceFallback)
}

/**
 * @description
 * If there's more than one `Item`, and if there's no defaultSKU, this will
 * retrieve a suitable one depending on the logic provided
 * @param items an array of SKUs
 * @param preferredSKU the logic for finding an SKU
 * */
function findPreferredSKU(
  items: ProductTypes.Item[],
  preferredSKU: PreferenceType
) {
  switch (preferredSKU) {
    default:
    case 'FIRST_AVAILABLE':
      return items.find(getAvailableProduct) ?? items[0]

    case 'LAST_AVAILABLE':
      return (
        [...items].reverse().find(getAvailableProduct) ??
        items[items.length - 1]
      )

    case 'PRICE_ASC':
      return findSKUByPrice(items, 'lowest')

    case 'PRICE_DESC':
      return findSKUByPrice(items, 'highest')
  }
}

const defaultImage = { imageUrl: '', imageLabel: '' }
const defaultReference = { Value: '' }
const defaultSeller = { commertialOffer: { Price: 0, ListPrice: 0 } }

const resizeImage = (url: string, imageSize: string | number) =>
  changeImageUrlSize(toHttps(url), imageSize)

/**
 * @description
 * This method is responsible for constructing the product type for the product-context.
 * The property `product.sku` is built by these specifications
 * */
export function mapCatalogProductToProductSummary(
  product: ProductTypes.Product,
  preferredSKU: PreferenceType = 'FIRST_AVAILABLE',
  imageSize: string | number = 500
) {
  if (!product) return null
  const normalizedProduct: any = { ...product }
  const items = normalizedProduct.items || []
  const specifications = normalizedProduct.properties || []
  const defaultSKUspec =
    specifications.find(
      (spec: { name: string }) => spec.name === 'DefaultSKUSelected'
    ) ?? null

  // The SKU will be responsible of setting the `selectedItem` in the product context.
  let sku

  if (items.length === 1) {
    sku = items[0]
  } else if (defaultSKUspec) {
    sku = findDefaultSKU(items, defaultSKUspec.values, preferredSKU)
  } else {
    sku = findPreferredSKU(items, preferredSKU)
  }

  if (sku) {
    const seller = getDefaultSeller(sku?.sellers) ?? defaultSeller

    const [referenceId = defaultReference] = sku?.referenceId ?? []
    const catalogImages = sku?.images ?? []
    const normalizedImages = catalogImages.map(
      (image: { imageUrl: string }) => ({
        ...image,
        imageUrl: resizeImage(image.imageUrl, imageSize),
      })
    )

    const [image = defaultImage] = normalizedImages

    normalizedProduct.sku = {
      ...sku,
      seller,
      referenceId,
      image,
      images: normalizedImages,
    }
  }

  return normalizedProduct
}

```


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

[User] Show me the implementation of product-summary/react/utils/propTypes.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

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

import { CHOICE_TYPES } from './attachmentHelper'

export const addedOptionShape = PropTypes.shape({
  item: PropTypes.shape({
    name: PropTypes.string.isRequired,
    sellingPrice: PropTypes.number.isRequired,
    quantity: PropTypes.number.isRequired,
  }),
  normalizedQuantity: PropTypes.number,
  extraQuantity: PropTypes.number,
  choiceType: PropTypes.oneOf([
    CHOICE_TYPES.SINGLE,
    CHOICE_TYPES.MULTIPLE,
    CHOICE_TYPES.TOGGLE,
  ]).isRequired,
})

export const removedOptionShape = PropTypes.shape({
  removedQuantity: PropTypes.number.isRequired,
  initialQuantity: PropTypes.number.isRequired,
  name: PropTypes.string.isRequired,
})

export const productShape = PropTypes.shape({
  /** Product's id */
  productId: PropTypes.string.isRequired,
  /** Product's link text */
  linkText: PropTypes.string.isRequired,
  /** Product's name */
  productName: PropTypes.string.isRequired,
  /** Product's brand */
  brand: PropTypes.string,
  /** Product's brand id */
  brandId: PropTypes.number,
  /** Product's SKU */
  sku: PropTypes.shape({
    /** SKU name */
    name: PropTypes.string.isRequired,
    /** SKU id */
    itemId: PropTypes.string.isRequired,
    /** SKU Image to be shown */
    image: PropTypes.shape({
      /** Image URL */
      imageUrl: PropTypes.string.isRequired,
      /** Image tag as string */
      imageTag: PropTypes.string,
    }).isRequired,
    /** SKU seller */
    seller: PropTypes.shape({
      /** Seller id */
      sellerId: PropTypes.string,
      /** Seller comertial offer */
      commertialOffer: PropTypes.shape({
        /** SKU installments */
        Installments: PropTypes.arrayOf(
          PropTypes.shape({
            /** Installment value */
            Value: PropTypes.number.isRequired,
            /** Interest rate (zero if interest-free) */
            InterestRate: PropTypes.number.isRequired,
            /** Calculated total value */
            TotalValuePlusInterestRate: PropTypes.number,
            /** Number of installments */
            NumberOfInstallments: PropTypes.number.isRequired,
            /** Installments offer name */
            Name: PropTypes.string,
          })
        ),
        /** Selling Price */
        Price: PropTypes.number.isRequired,
        /** List Price */
        ListPrice: PropTypes.number.isRequired,
      }).isRequired,
    }).isRequired,
  }).isRequired,
  /** Product's collections */
  productClusters: PropTypes.arrayOf(
    PropTypes.shape({
      name: PropTypes.string.isRequired,
    })
  ),
  /** Contains relevant assembly options information */
  assemblyOptions: PropTypes.shape({
    added: PropTypes.arrayOf(addedOptionShape),
    removed: PropTypes.arrayOf(removedOptionShape),
    parentPrice: PropTypes.number,
  }),
  /** Quantity of item in Minicart */
  quantity: PropTypes.number,
})

```


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

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

[Assistant]
Here's the implementation:

```typescript
export default function compose(...funcs: Function[]) {
  const functions = funcs.reverse()

  return (...args: any[]) => {
    const [firstFunction, ...restFunctions] = functions
    let result = firstFunction(...args)

    restFunctions.forEach((fnc) => {
      result = fnc.call(null, result)
    })

    return result
  }
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
// eslint-disable-next-line no-restricted-imports
import { values, map } from 'ramda'

const displayButtonTypes = {
  DISPLAY_ALWAYS: {
    name: 'admin/editor.productSummary.displayBuyButton.option.always',
    value: 'displayButtonAlways',
  },
  DISPLAY_ON_HOVER: {
    name: 'admin/editor.productSummary.displayBuyButton.option.hover',
    value: 'displayButtonHover',
  },
  DISPLAY_NONE: {
    name: 'admin/editor.productSummary.displayBuyButton.option.none',
    value: 'displayButtonNone',
  },
}

export function getDisplayButtonNames() {
  return map((opt) => opt.name, values(displayButtonTypes))
}

export function getDisplayButtonValues() {
  return map((opt) => opt.value, values(displayButtonTypes))
}

export default displayButtonTypes

```


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

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

[Assistant]
Here's the implementation:

```tsx
import { useProductImpression } from 'vtex.product-list-context'

function ProductListEventCaller() {
  useProductImpression()

  return null
}

export default ProductListEventCaller

```


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

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

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

function ImagePlaceholder({ cssHandle }: { cssHandle: string }) {
  return (
    <div className="relative">
      <div className={`${cssHandle} absolute w-100 h-100 contain bg-center`} />
      <svg
        width="100%"
        height="100%"
        viewBox="0 0 512 512"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
        data-testid="image-placeholder"
      >
        <rect width="512" height="512" fill="#F2F2F2" />
        <rect
          x="183.857"
          y="180.2"
          width="144.286"
          height="150.474"
          stroke="#CACBCC"
          strokeWidth="2"
        />
        <path d="M183.78 303.688H328.214" stroke="#CACBCC" strokeWidth="2" />
        <path
          d="M205.082 279.563L223.599 240.507L242.116 260.035L269.892 220.979L306.926 279.563H205.082Z"
          stroke="#CACBCC"
          strokeWidth="2"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
        <path
          d="M252.225 213.939C252.225 219.822 247.66 224.52 242.114 224.52C236.569 224.52 232.004 219.822 232.004 213.939C232.004 208.057 236.569 203.359 242.114 203.359C247.66 203.359 252.225 208.057 252.225 213.939Z"
          stroke="#CACBCC"
          strokeWidth="2"
        />
      </svg>
    </div>
  )
}

export default ImagePlaceholder

```


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

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

[Assistant]
Here's the implementation:

```tsx
import classNames from 'classnames'
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'
import { IOMessage } from 'vtex.native-types'

const CSS_HANDLES = ['sponsoredBadgeContainer', 'sponsoredBadgeText'] as const

type Props = {
  /**
   * The message ID or string for the sponsored label.
   */
  label?: string
}

export const SponsoredBadge = ({
  label = 'store/sponsoredBadge.title',
}: Props) => {
  const { handles } = useCssHandles(CSS_HANDLES)

  const containerClasses = classNames(
    handles.sponsoredBadgeContainer,
    'absolute z-1'
  )

  const textClasses = classNames(
    handles.sponsoredBadgeText,
    'c-muted-1 t-mini-s'
  )

  return (
    <div className={containerClasses}>
      <span className={textClasses}>
        <IOMessage id={label} />
      </span>
    </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 product-summary/react/components/ProductPriceSimulationWrapper.tsx from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import type { PropsWithChildren } from 'react'
import { ProductSummaryContext } from 'vtex.product-summary-context'
import type { ProductSummaryTypes } from 'vtex.product-summary-context'

import useSimulation from '../hooks/useSimulation'
import useSetProduct from '../hooks/useSetProduct'

interface Props {
  product: ProductSummaryTypes.Product
  inView: boolean
  priceBehavior: 'async' | 'asyncOnly1P' | 'default'
}

function ProductPriceSimulationWrapper({
  product,
  inView,
  children,
  priceBehavior,
}: PropsWithChildren<Props>) {
  const productSummaryDispatch = ProductSummaryContext.useProductSummaryDispatch()
  const setProduct = useSetProduct()

  useSimulation({
    product,
    inView,
    priceBehavior,
    onError: () => {
      productSummaryDispatch({
        type: 'SET_PRICE_LOADING',
        args: { isPriceLoading: false },
      })
    },
    onComplete: (simulatedProduct) => {
      setProduct(simulatedProduct)

      productSummaryDispatch({
        type: 'SET_PRICE_LOADING',
        args: { isPriceLoading: false },
      })
    },
  })

  return <>{children}</>
}

export default ProductPriceSimulationWrapper

```


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

[User] Show me the implementation of product-summary/react/__tests__/ProductSummary.test.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import ProductSummary from '../ProductSummaryCustom'

describe('<ProductSummary /> component', () => {
  it(`should parse a catalog product and get an available SKU with the highest price`, () => {
    const inheritedProps = {
      preferredSKU: 'PRICE_ASC',
    }

    const mockProduct = {
      productId: '123456789',
      linkText: 'linkText',
      productName: 'productName',
      items: [
        {
          itemId: '1',
          name: 'item 1',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 0,
                Price: 100,
              },
            },
          ],
          images: [
            {
              imageUrl: 'image1',
              imageLabel: null,
            },
            {
              imageUrl: 'image2',
              imageLabel: null,
            },
          ],
        },
        {
          itemId: '2',
          name: 'item 2',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 3,
                Price: 50,
              },
            },
          ],
          images: [
            {
              imageUrl: 'image1',
              imageLabel: null,
            },
            {
              imageUrl: 'image2',
              imageLabel: null,
            },
          ],
        },
        {
          itemId: '3',
          name: 'item 3',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 1,
                Price: 500,
              },
            },
          ],
          images: [
            {
              imageUrl: 'image1',
              imageLabel: null,
            },
            {
              imageUrl: 'image2',
              imageLabel: null,
            },
          ],
        },
      ],
      productClusters: [
        {
          name: 'name',
        },
      ],
      properties: [],
    }

    const result = ProductSummary.mapCatalogProductToProductSummary(
      mockProduct,
      inheritedProps.preferredSKU
    )

    expect(result).toBeDefined()
    expect(result.sku.seller.sellerId).toBe('1')
    expect(result.sku.image).toBeDefined()
    expect(result.sku.itemId).toBe('2')
  })

  it(`should parse a catalog product and get a specific SKU by it's properties`, () => {
    const inheritedProps = {
      preferredSKU: 'LAST_AVAILABLE',
    }

    const mockProduct = {
      productId: '123456789',
      linkText: 'linkText',
      productName: 'productName',
      items: [
        {
          itemId: '1',
          name: 'item 1',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 0,
                Price: 100,
              },
            },
          ],
          images: [
            {
              imageUrl: 'image1',
              imageLabel: null,
            },
            {
              imageUrl: 'image2',
              imageLabel: null,
            },
          ],
        },
        {
          itemId: '2',
          name: 'item 2',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 3,
                Price: 50,
              },
            },
          ],
          images: [
            {
              imageUrl: 'image1',
              imageLabel: null,
            },
            {
              imageUrl: 'image2',
              imageLabel: null,
            },
          ],
        },
        {
          itemId: '3',
          name: 'item 3',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 1,
                Price: 500,
              },
            },
          ],
          images: [
            {
              imageUrl: 'image1',
              imageLabel: null,
            },
            {
              imageUrl: 'image2',
              imageLabel: null,
            },
          ],
        },
      ],
      productClusters: [
        {
          name: 'name',
        },
      ],
      properties: [{ name: 'DefaultSKUSelected', values: ['2'] }],
    }

    const result = ProductSummary.mapCatalogProductToProductSummary(
      mockProduct,
      inheritedProps.preferredSKU
    )

    expect(result).toBeDefined()
    expect(result.sku.itemId).toBe('2')
  })

  it('ProductSummary should have a schema', () => {
    expect(ProductSummary.schema).toBeDefined()
  })
})

```


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

[User] Show me the implementation of product-summary/react/__tests__/ProductSummaryImage.test.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

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

import ProductImage from '../ProductSummaryImage'
import ProductSummary from '../ProductSummaryCustom'

describe('<ProductImage /> component', () => {
  const inheritedProps = {
    preferredSKU: 'FIRST_AVAILABLE',
  }

  it('should render one img element', () => {
    const rawProduct = {
      productId: '123456789',
      linkText: 'linkText',
      productName: 'productName',
      items: [
        {
          itemId: '1',
          name: 'item 1',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 0,
                Price: 10,
                ListPrice: 7,
              },
            },
          ],
          images: [
            {
              imageUrl: 'image1',
              imageLabel: null,
            },
          ],
        },
      ],
      productClusters: [
        {
          name: 'name',
        },
      ],
      quantity: 1,
    }

    const product = ProductSummary.mapCatalogProductToProductSummary(
      rawProduct,
      inheritedProps.preferredSKU
    )

    const { container } = render(
      <ProductSummary product={product}>
        <ProductImage />
      </ProductSummary>
    )

    const imgElements = container.getElementsByTagName('img')

    expect(imgElements).toHaveLength(1)
    expect(imgElements[0].getAttribute('src')).toBeTruthy()
    expect(imgElements[0].getAttribute('src')).toContain('image1')
  })

  it('should render svg placeholder and no img element', () => {
    const rawProduct = {
      productId: '123456789',
      linkText: 'linkText',
      productName: 'productName',
      items: [
        {
          itemId: '1',
          name: 'item 1',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 0,
                Price: 10,
                ListPrice: 7,
              },
            },
          ],
          images: [],
        },
      ],
      productClusters: [
        {
          name: 'name',
        },
      ],
      quantity: 1,
    }

    const product = ProductSummary.mapCatalogProductToProductSummary(
      rawProduct,
      inheritedProps.preferredSKU
    )

    const { container } = render(
      <ProductSummary product={product}>
        <ProductImage />
      </ProductSummary>
    )

    const imgElements = container.getElementsByTagName('img')

    expect(imgElements).toHaveLength(0)
    const svgElements = container.getElementsByTagName('svg')

    expect(svgElements).toHaveLength(1)
  })

  it('should render custom placeholder img', () => {
    const rawProduct = {
      productId: '123456789',
      linkText: 'linkText',
      productName: 'productName',
      items: [
        {
          itemId: '1',
          name: 'item 1',
          sellers: [
            {
              sellerId: '1',
              commertialOffer: {
                AvailableQuantity: 0,
                Price: 10,
                ListPrice: 7,
              },
            },
          ],
          images: [],
        },
      ],
      productClusters: [
        {
          name: 'name',
        },
      ],
      quantity: 1,
    }

    const placeholder = 'placeholder-image-url'

    const product = ProductSummary.mapCatalogProductToProductSummary(
      rawProduct,
      inheritedProps.preferredSKU
    )

    const { container } = render(
      <ProductSummary product={product}>
        <ProductImage placeholder={placeholder} />
      </ProductSummary>
    )

    const imgElements = container.getElementsByTagName('img')

    expect(imgElements).toHaveLength(1)
    expect(imgElements[0].src.includes(placeholder)).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 product-summary/react/__tests__/ProductSummaryLegacy.test.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import fs from 'fs'

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

import ProductSummary from '../legacy/index'

describe('<ProductSummary /> component', () => {
  // TODO: Remove this later when we have a better way to resolve contentSchemas.json at test-tools
  const rawData = fs.readFileSync('../store/contentSchemas.json')
  const contentSchema = JSON.parse(rawData)
  const buyButtonText =
    contentSchema.definitions.ProductSummary.properties.buyButtonText.default

  const props = {
    runtime: { hints: {} },
    buyButtonText,
    product: {
      productId: '123456789',
      linkText: 'linkText',
      productName: 'productName',
      sku: {
        name: 'name',
        itemId: 'itemId',
        image: {
          imageUrl: '',
        },
        seller: {
          sellerId: 'sellerId',
          commertialOffer: {
            Installments: [
              {
                Value: 1,
                InterestRate: 1,
                NumberOfInstallments: 1,
              },
            ],
            Price: 1,
            ListPrice: 1,
          },
        },
      },
      productClusters: [
        {
          name: 'name',
        },
      ],
      quantity: 1,
    },
  }

  function renderComponent(customProps) {
    return render(<ProductSummary {...props} {...customProps} />)
  }

  it('should match the snapshot for normal mode', () => {
    const { asFragment } = renderComponent({ displayMode: 'normal' })

    expect(asFragment()).toMatchSnapshot()
  })

  it('should match the snapshot for small mode', () => {
    const { asFragment } = renderComponent({ displayMode: 'small' })

    expect(asFragment()).toMatchSnapshot()
  })

  it('should match the snapshot for inline normal mode', () => {
    const { asFragment } = renderComponent({ displayMode: 'inline' })

    expect(asFragment()).toMatchSnapshot()
  })

  it('should match the snapshot for inline price mode', () => {
    const { asFragment } = renderComponent({ displayMode: 'inlinePrice' })

    expect(asFragment()).toMatchSnapshot()
  })

  it('should render buy button in inline, small and normal mode', () => {
    const { getByText, rerender, container } = renderComponent({
      displayMode: 'normal',
    })

    expect(getByText('Buy')).toBeTruthy()

    rerender(<ProductSummary {...props} displayMode="small" />)
    expect(getByText('Buy')).toBeTruthy()

    rerender(<ProductSummary {...props} displayMode="inline" />)
    expect(getByText('Buy')).toBeTruthy()

    rerender(<ProductSummary {...props} displayMode="inlinePrice" />)
    expect(container.querySelector('.buyButtonContainer')).toBeFalsy()
  })

  it('should render quantity stepper only in inline price mode', () => {
    const { container, rerender } = renderComponent({ displayMode: 'normal' })

    expect(container.querySelector('.quantityStepperContainer')).toBeFalsy()

    rerender(<ProductSummary {...props} displayMode="inlinePrice" />)
    expect(container.querySelector('.quantityStepperContainer')).toBeTruthy()

    rerender(<ProductSummary {...props} displayMode="small" />)
    expect(container.querySelector('.quantityStepperContainer')).toBeFalsy()

    rerender(<ProductSummary {...props} displayMode="inline" />)
    expect(container.querySelector('.quantityStepperContainer')).toBeFalsy()
  })

  it(`should not break Shelf getSchema`, () => {
    expect(ProductSummary.getSchema).toEqual(expect.any(Function))
  })

  describe('Site editor editable', () => {
    it('should export getSchema and return object with title', () => {
      const schema = ProductSummary.schema || ProductSummary.getSchema({})

      expect(schema).toEqual(
        expect.objectContaining({ title: expect.any(String) })
      )
    })
  })
})

```


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

[User] Show me the implementation of product-summary/react/hooks/useSimulation.ts from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { useMemo } from 'react'
import { useQuery } from 'react-apollo'
import type { ProductSummaryTypes } from 'vtex.product-summary-context'
import { QueryItemsWithSimulation } from 'vtex.store-resources'

import clone from '../utils/clone'

const mergeSellers = (
  sellerA: ProductSummaryTypes.Seller,
  sellerB: ProductSummaryTypes.Seller,
  sellerDefault?: string
) => {
  sellerA.commertialOffer = {
    ...sellerA.commertialOffer,
    ...sellerB.commertialOffer,
  }

  // Deal with withoutPriceFulfillment cases
  if (sellerA.commertialOffer.Price === 0) {
    sellerA.commertialOffer.AvailableQuantity = 0
  }

  if (!sellerDefault) {
    return sellerA
  }

  return {
    ...sellerA,
    sellerDefault: sellerA.sellerId === sellerDefault,
  }
}

const getDefaultSeller = (sellers: ProductSummaryTypes.Seller[]) => {
  const sellersWithStock = sellers.filter(
    (seller) => seller.commertialOffer.AvailableQuantity !== 0
  )

  return sellersWithStock
    ?.sort((a, b) => a.commertialOffer.Price - b.commertialOffer.Price)
    .map((seller) => seller.sellerId)[0]
}

type Params = {
  product: ProductSummaryTypes.Product
  inView: boolean
  onComplete: (product: ProductSummaryTypes.Product) => void
  onError: () => void
  priceBehavior: 'async' | 'asyncOnly1P' | 'default'
}

function useSimulation({
  product,
  inView,
  onComplete,
  onError,
  priceBehavior,
}: Params) {
  const items = product.items || []

  const simulationItemsInput = useMemo(() => {
    if (priceBehavior === 'async') {
      return items.map((item) => ({
        itemId: item.itemId,
        sellers: item.sellers.map((seller) => ({ sellerId: seller.sellerId })),
      }))
    }

    return items.map((item) => ({
      itemId: item.itemId,
      sellers: [{ sellerId: '1' }],
    }))
  }, [items, priceBehavior])

  useQuery(QueryItemsWithSimulation, {
    variables: {
      items: simulationItemsInput,
    },
    skip: priceBehavior === 'default' || !inView,
    ssr: false,
    onError,
    onCompleted: (response) => {
      if (!response) {
        return
      }

      const simulationItems = response.itemsWithSimulation

      const mergedProduct = clone(product) as ProductSummaryTypes.Product

      mergedProduct.items.forEach((item, itemIndex) => {
        const simulationItem = simulationItems[itemIndex]

        if (priceBehavior === 'async') {
          const sellerDefault = getDefaultSeller(simulationItem.sellers)

          item.sellers = item.sellers.map((seller, simulationIndex) => {
            const sellerSimulation = simulationItem.sellers[simulationIndex]

            return mergeSellers(seller, sellerSimulation, sellerDefault)
          })
        } else {
          const seller1PIndex = item.sellers.findIndex(
            (seller) => seller.sellerId === '1'
          )

          const sellers = Array.from(item.sellers)

          sellers[seller1PIndex] = simulationItem.sellers[0]
          const sellerDefault = getDefaultSeller(sellers)

          item.sellers = item.sellers.map((seller) => {
            if (seller.sellerId !== '1') {
              return !sellerDefault
                ? seller
                : {
                    ...seller,
                    sellerDefault: seller.sellerId === sellerDefault,
                  }
            }

            return mergeSellers(
              seller,
              simulationItem.sellers[0],
              sellerDefault
            )
          })
        }
      })

      mergedProduct.sku = mergedProduct.items.find(
        (item) => item.itemId === mergedProduct.sku.itemId
      ) as ProductSummaryTypes.SingleSKU

      if (mergedProduct.sku.sellers.length > 0) {
        mergedProduct.sku.seller = (mergedProduct.sku.sellers.find(
          (seller) => seller.sellerDefault
        ) ?? mergedProduct.sku.sellers[0]) as ProductSummaryTypes.Seller
      } else {
        mergedProduct.sku.seller = {
          // @ts-expect-error We are not providing the full type
          commertialOffer: { Price: 0, ListPrice: 0 },
        }
      }

      mergedProduct.sku.image = product.sku.image

      onComplete(mergedProduct)
    },
  })
}

export default useSimulation

```


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

[User] Show me the implementation of product-summary/react/hooks/useSetProduct.ts from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { useProduct, useProductDispatch } from 'vtex.product-context'
import type { ProductTypes } from 'vtex.product-context'
import { ProductSummaryContext } from 'vtex.product-summary-context'
import type { ProductSummaryTypes } from 'vtex.product-summary-context'

const { useProductSummaryDispatch } = ProductSummaryContext

function useSetProduct() {
  const { selectedItem: currentSelectedItem } = useProduct() ?? {}
  const productSummaryDispatch = useProductSummaryDispatch()
  const productDispatch = useProductDispatch()

  return (product: ProductSummaryTypes.Product) => {
    const newSelectedItem =
      currentSelectedItem &&
      product.items.find((item) => item.itemId === currentSelectedItem.itemId)

    productSummaryDispatch({
      type: 'SET_PRODUCT',
      args: { product },
    })

    productDispatch?.({
      type: 'SET_PRODUCT',
      args: { product: (product as unknown) as ProductTypes.Product },
    })

    productDispatch?.({
      type: 'SET_SELECTED_ITEM',
      args: { item: (newSelectedItem as unknown) as ProductTypes.Item },
    })
  }
}

export default useSetProduct

```


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

[User] Show me the implementation of product-summary/react/hooks/useSession.ts from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { useCallback } from 'react'
import { useRuntime } from 'vtex.render-runtime'

const useSession = () => {
  const { rootPath } = useRuntime()

  const getShippingOptionFromSession = useCallback(async () => {
    const headers = new Headers()

    headers.append('Content-Type', 'application/json')

    const requestOptions: RequestInit = {
      method: 'GET',
      headers,
      redirect: 'follow',
    }

    const session = await fetch(
      `${rootPath ?? ''}/api/sessions?items=public.shippingOption`,
      requestOptions
    )

    const data = await session.json()

    if (!data?.namespaces?.public?.shippingOption?.value) {
      return null
    }

    return JSON.parse(data.namespaces.public.shippingOption.value)?.map(
      (option: { key: string; value: string }) => option.value
    )
  }, [rootPath])

  return { getShippingOptionFromSession }
}

export default useSession

```


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

[User] Show me the implementation of product-summary/react/modules/seller.ts from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface SellerDefault {
  sellerDefault: boolean
}

export function getDefaultSeller<T extends SellerDefault>(
  sellers?: T[]
): T | undefined {
  if (!sellers || sellers.length === 0) {
    return
  }

  const defaultSeller = sellers.find((seller) => seller.sellerDefault)

  if (!defaultSeller) {
    return sellers[0]
  }

  return defaultSeller
}

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductSummaryNormal.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
// eslint-disable-next-line no-restricted-imports
import { path } from 'ramda'
import classNames from 'classnames'
import { ExtensionPoint, Link } from 'vtex.render-runtime'

import AttachmentList from './AttachmentList'
import ProductImage from './ProductImage'
import ProductSummaryBuyButton from './ProductSummaryBuyButton'
import ProductSummaryPrice from './ProductSummaryPrice'
import ProductSummaryName from './ProductSummaryName'
import ProductSummaryDescription from './ProductSummaryDescription'
import productSummary from '../../productSummary.css'

class ProductSummaryNormal extends Component {
  render() {
    const {
      product,
      showBorders,
      showDescription,
      handleMouseEnter,
      handleMouseLeave,
      actionOnClick,
      imageProps,
      nameProps,
      priceProps,
      buyButtonProps,
      inViewRef,
      listName,
    } = this.props

    const containerClasses = classNames(
      productSummary.container,
      productSummary.containerNormal,
      'overflow-hidden br3 h-100 w-100 flex flex-column justify-between center tc'
    )

    const summaryClasses = classNames(
      `${productSummary.element} pointer pt3 pb4 flex flex-column h-100`,
      {
        'bb b--muted-4 mh2 mt2': showBorders,
      }
    )

    const nameClasses = {
      containerClass: `flex items-start ${productSummary.nameContainer} justify-center pv6`,
      brandNameClass: 't-body',
    }

    const priceClasses = {
      containerClass: classNames(
        'flex flex-column justify-end items-center relative',
        {
          [`${productSummary.priceContainer} pv5`]: !showBorders,
        }
      ),
      sellingPriceClass: 'dib ph2 t-body t-heading-5-ns',
    }

    const buyButtonClasses = {
      containerClass: `${productSummary.buyButtonContainer} pv3 w-100 db`,
    }

    const descriptionClasses = `${productSummary.description} c-muted-2 t-small`

    return (
      <section
        className={containerClasses}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
        ref={inViewRef}
      >
        <div className={`${productSummary.addToListBtn} absolute z-1 mt3`}>
          <ExtensionPoint
            id="addon-summary-btn"
            product={{
              skuId: path(['sku', 'itemId'], product),
              productId: path(['productId'], product),
              quantity: 1,
            }}
          />
        </div>
        <Link
          className={`${productSummary.clearLink} h-100 flex flex-column`}
          page="store.product"
          params={{
            slug: product && product.linkText,
            id: product && product.productId,
            __listName: listName,
          }}
          onClick={actionOnClick}
        >
          <article className={summaryClasses}>
            <div className={`${productSummary.imageContainer} db w-100 center`}>
              <ProductImage {...imageProps} />
            </div>
            <div
              className={`${productSummary.information} h-100 flex flex-column justify-between`}
            >
              <ProductSummaryName {...nameProps} {...nameClasses} />
              <AttachmentList product={product} />
              {showDescription && (
                <ProductSummaryDescription
                  description={product.description}
                  descriptionClasses={descriptionClasses}
                />
              )}
              <div>
                <ProductSummaryPrice {...priceProps} {...priceClasses} />
              </div>
            </div>
          </article>
          <ProductSummaryBuyButton {...buyButtonProps} {...buyButtonClasses} />
        </Link>
      </section>
    )
  }
}

export default ProductSummaryNormal

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductSummarySmall.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
// eslint-disable-next-line no-restricted-imports
import { path } from 'ramda'
import classNames from 'classnames'
import { Link } from 'vtex.render-runtime'

import AttachmentList from './AttachmentList'
import ProductImage from './ProductImage'
import ProductSummaryBuyButton from './ProductSummaryBuyButton'
import ProductSummaryPrice from './ProductSummaryPrice'
import ProductSummaryName from './ProductSummaryName'
import styles from '../../productSummary.css'

class ProductSummarySmall extends Component {
  render() {
    const {
      product,
      showBorders,
      handleMouseEnter,
      handleMouseLeave,
      actionOnClick,
      imageProps,
      nameProps,
      priceProps,
      buyButtonProps,
      inViewRef,
      listName,
    } = this.props

    const containerClasses = classNames(
      styles.container,
      styles.containerSmall,
      'overflow-hidden br3 h-100 flex flex-column justify-between center tc'
    )

    const summaryClasses = classNames(
      styles.element,
      'pointer pt3 pb4 flex flex-column',
      {
        'bb b--muted-4 mh2 mt2': showBorders,
      }
    )

    const nameClasses = {
      containerClass: `flex items-start ${styles.nameContainer} justify-center pv5 t-mini pb2`,
      brandNameClass: 't-body t-mini',
    }

    const priceClasses = {
      containerClass: classNames('flex flex-column justify-end items-center', {
        [`${styles.priceContainer} pv5`]: !showBorders,
      }),
      sellingPriceClass: 'dib ph2 t-body t-heading-5-ns',
    }

    const buyButtonClasses = {
      containerClass: `${styles.buyButtonContainer} pv3 w-100 dn`,
    }

    return (
      <section
        className={containerClasses}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
        ref={inViewRef}
      >
        <Link
          className={`${styles.clearLink} flex flex-column`}
          page="store.product"
          params={{ slug: path(['linkText'], product), __listName: listName }}
          onClick={actionOnClick}
        >
          <article className={summaryClasses}>
            <div className={`${styles.imageContainer} db w-100 center`}>
              <ProductImage {...imageProps} />
            </div>
            <div className={styles.information}>
              <ProductSummaryName {...nameProps} {...nameClasses} />
              <AttachmentList product={product} />
              <div>
                <ProductSummaryPrice {...priceProps} {...priceClasses} />
              </div>
            </div>
          </article>
          <ProductSummaryBuyButton {...buyButtonProps} {...buyButtonClasses} />
        </Link>
      </section>
    )
  }
}

export default ProductSummarySmall

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductSummaryInlinePrice.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import classNames from 'classnames'
import { Link } from 'vtex.render-runtime'

import AttachmentList from './AttachmentList'
import ProductImage from './ProductImage'
import ProductQuantityStepper from './ProductQuantityStepper'
import ProductSummaryPrice from './ProductSummaryPrice'
import ProductSummaryName from './ProductSummaryName'
import styles from '../../productSummary.css'

const ProductSummaryInlinePrice = ({
  product,
  showBorders,
  handleMouseEnter,
  handleMouseLeave,
  handleItemsStateUpdate,
  actionOnClick,
  imageProps,
  nameProps,
  priceProps,
  showQuantitySelector,
  priceAlignLeft,
  muted,
  index,
  inViewRef,
  listName,
}) => {
  const containerClasses = classNames(
    styles.container,
    styles.containerInline,
    'br3 h-100 w-100'
  )

  const summaryClasses = classNames(
    `${styles.element} pointer ph2 pt3 pb4 flex flex-column h-100`,
    {
      'bb b--muted-4 mh2 mh3-ns mt2': showBorders,
    }
  )

  const nameClasses = {
    containerClass: classNames(
      'flex items-start justify-left tl w-90 t-mini pb2',
      { 'c-muted-1': muted }
    ),
    brandNameClass: classNames('t-body', {
      'c-muted-1': muted,
      'c-on-base': !muted,
    }),
    skuNameClass: classNames('t-small', {
      'c-muted-2': muted,
    }),
  }

  const priceClasses = {
    containerClass: classNames('flex flex-column nr1 h1', {
      'items-start': priceAlignLeft,
      'items-end': !priceAlignLeft,
      [`${styles.priceContainer} pv5`]: !showBorders,
    }),
    sellingPriceClass: classNames('dib ph2 t-body t-heading-5-ns', {
      'c-muted-1': muted,
    }),
  }

  return (
    <section
      className={containerClasses}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      ref={inViewRef}
    >
      <article className={summaryClasses}>
        <Link
          className={`${styles.clearLink} flex h-100`}
          page="store.product"
          params={{
            slug: product && product.linkText,
            id: product && product.productId,
            __listName: listName,
          }}
          onClick={actionOnClick}
        >
          <div className={`${styles.imageContainer} db h-100`}>
            <ProductImage {...imageProps} />
          </div>
          <div className={`${styles.information} w-70 pb2 ph3`}>
            <ProductSummaryName {...nameProps} {...nameClasses} />
            <AttachmentList product={product} />
            <div className="mv3 nr2">
              {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */}
              <div
                onClick={(e) => {
                  e.preventDefault()
                  // Stop propagation so it doesn't trigger the Link component above
                  e.stopPropagation()
                }}
                className={`flex justify-end nr4 mb2 ${styles.quantityStepperContainer}`}
              >
                {showQuantitySelector && (
                  <ProductQuantityStepper
                    product={product}
                    onUpdateItemsState={handleItemsStateUpdate}
                    index={index}
                  />
                )}
              </div>
              <ProductSummaryPrice {...priceProps} {...priceClasses} />
            </div>
          </div>
        </Link>
      </article>
    </section>
  )
}

export default ProductSummaryInlinePrice

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductSummaryName.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable react/prop-types */
import React from 'react'
import PropTypes from 'prop-types'
// eslint-disable-next-line no-restricted-imports
import { path } from 'ramda'
import { ProductName } from 'vtex.store-components'

import { productShape } from '../../utils/propTypes'

const ProductSummaryName = ({
  product,
  showFieldsProps,
  containerClass,
  brandNameClass,
  skuNameClass,
}) => {
  const productName = path(['productName'], product)
  const skuName = path(['sku', 'name'], product)
  const brandName = path(['brand'], product)

  return (
    <div className={containerClass}>
      <ProductName
        className="overflow-hidden c-on-base"
        brandNameClass={brandNameClass}
        skuNameClass={skuNameClass}
        loaderClass="pt5 overflow-hidden"
        name={productName}
        skuName={skuName}
        brandName={brandName}
        {...showFieldsProps}
      />
    </div>
  )
}

ProductSummaryName.propTypes = {
  /** Product that owns the informations */
  product: productShape,
  /** Name schema props */
  showFieldsProps: PropTypes.object,
  /** Styles used in the container div */
  containerClass: PropTypes.string,
  /** Styles used in the brand name */
  brandNameClass: PropTypes.string,
}

export default ProductSummaryName

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductSummaryPrice.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import PropTypes from 'prop-types'
// eslint-disable-next-line no-restricted-imports
import { path, prop } from 'ramda'
import { Spinner } from 'vtex.styleguide'
import { ProductPrice } from 'vtex.store-components'

import { productShape } from '../../utils/propTypes'

const ProductSummaryPrice = ({
  product,
  showListPrice,
  showLabels,
  showInstallments,
  labelSellingPrice,
  labelListPrice,
  isLoading,
  containerClass,
  sellingPriceClass,
}) => {
  const commertialOffer = path(['sku', 'seller', 'commertialOffer'], product)

  if (isLoading) {
    return (
      <div className="flex items-end justify-end w-100 h1 pr6">
        <Spinner size={20} />
      </div>
    )
  }

  const sellingPrice = prop('Price', commertialOffer)

  return (
    <div className={containerClass}>
      {sellingPrice !== 0 && (
        <ProductPrice
          className="flex flex-column justify-start"
          listPriceContainerClass="pv1 normal c-muted-2"
          listPriceLabelClass="dib strike t-small t-mini"
          listPriceClass="dib ph2 strike t-small-ns t-mini"
          sellingPriceContainerClass="pt1 pb3 c-on-base"
          sellingPriceLabelClass="dib"
          sellingPriceClass={sellingPriceClass}
          savingsContainerClass="t-small-ns c-muted-2"
          savingsClass="dib"
          interestRateClass="dib pl2"
          installmentContainerClass="t-small-ns c-muted-2"
          listPrice={prop('ListPrice', commertialOffer)}
          sellingPrice={prop('Price', commertialOffer)}
          installments={prop('Installments', commertialOffer)}
          showListPrice={showListPrice}
          showLabels={showLabels}
          showInstallments={showInstallments}
          labelSellingPrice={labelSellingPrice}
          labelListPrice={labelListPrice}
        />
      )}
    </div>
  )
}

ProductSummaryPrice.propTypes = {
  /** Product that owns the informations */
  product: productShape,
  /** Set the product list price's visibility */
  showListPrice: PropTypes.bool,
  /** Set pricing labels' visibility */
  showLabels: PropTypes.bool,
  /** Set installments' visibility */
  showInstallments: PropTypes.bool,
  /** Text of selling Price's label */
  labelSellingPrice: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
  /** Text of selling Price's label */
  labelListPrice: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
  /** Defines if the loading spinner is shown */
  isLoading: PropTypes.bool,
  /** Styles used in the container div */
  containerClass: PropTypes.string,
  /** Styles used in the selling price */
  sellingPriceClass: PropTypes.string,
}

export default ProductSummaryPrice

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductSummaryDescription.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

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

const MAX_SIZE_DESCRIPTION = 120

const ProductSummaryDescription = ({ descriptionClasses, description }) => {
  if (!description) return <Fragment />

  const descriptionTruncated =
    description.length > MAX_SIZE_DESCRIPTION
      ? `${description.substring(0, MAX_SIZE_DESCRIPTION)}...`
      : description

  return <span className={descriptionClasses}>{descriptionTruncated}</span>
}

ProductSummaryDescription.propTypes = {
  /** Styles used in the description */

  descriptionClasses: PropTypes.string.isRequired,
  /** Description of the product */

  description: PropTypes.string,
}

export default ProductSummaryDescription

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductSummaryBuyButton.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import BuyButton from 'vtex.store-components/BuyButton'
// eslint-disable-next-line no-restricted-imports
import { equals, path } from 'ramda'
import classNames from 'classnames'
import { IOMessage } from 'vtex.native-types'
import { useRuntime } from 'vtex.render-runtime'

import displayButtonTypes from '../../utils/displayButtonTypes'
import productSummary from '../../productSummary.css'

const ProductSummaryBuyButton = ({
  product,
  displayBuyButton,
  isOneClickBuy,
  buyButtonText,
  isHovering,
  containerClass,
}) => {
  const {
    hints: { mobile },
  } = useRuntime()

  const hoverBuyButton =
    equals(displayBuyButton, displayButtonTypes.DISPLAY_ALWAYS.value) ||
    !equals(displayBuyButton, displayButtonTypes.DISPLAY_ON_HOVER.value) ||
    (isHovering && !mobile)

  const showBuyButton =
    !equals(displayBuyButton, displayButtonTypes.DISPLAY_NONE.value) &&
    !(
      equals(displayBuyButton, displayButtonTypes.DISPLAY_ON_HOVER.value) &&
      mobile
    )

  const buyButtonClasses = classNames(
    `${productSummary.buyButton} center mw-100`,
    {
      [productSummary.isHidden]: !hoverBuyButton,
    }
  )

  // TODO: change ProductSummaryContext to have `selectedSku` field instead of `sku`
  const selectedItem = product.sku
  const selectedSeller = path(['seller'], selectedItem)
  const isAvailable =
    selectedSeller &&
    selectedSeller.commertialOffer &&
    selectedSeller.commertialOffer.AvailableQuantity > 0

  const skuItems = BuyButton.mapCatalogItemToCart({
    product,
    selectedItem,
    selectedSeller,
    selectedQuantity: 1,
  })

  return (
    showBuyButton && (
      <div className={containerClass}>
        <div className={buyButtonClasses}>
          <BuyButton
            available={isAvailable}
            skuItems={skuItems}
            isOneClickBuy={isOneClickBuy}
          >
            <IOMessage id={buyButtonText} />
          </BuyButton>
        </div>
      </div>
    )
  )
}

export default ProductSummaryBuyButton

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductQuantityStepper.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
// eslint-disable-next-line no-restricted-imports
import { path } from 'ramda'
import PropTypes from 'prop-types'
import { NumericStepper, withToast } from 'vtex.styleguide'
import { Pixel } from 'vtex.pixel-manager/PixelContext'
import { debounce } from 'debounce'
import { injectIntl, intlShape } from 'react-intl'
import { graphql } from 'react-apollo'
import { parse } from 'graphql'

import compose from '../../utils/compose'
import { productShape } from '../../utils/propTypes'

const UPDATE_ITEMS_MUTATION = parse(`
  mutation updateItems($items: [MinicartItem]) {
    updateItems(items: $items) @client
  }
`)

const UPDATE_LOCAL_ITEMS_MUTATION = parse(`
  mutation updateLocalItems($items: [MinicartItem]) {
    updateLocalItems(items: $items) @client
  }
`)

class ProductQuantityStepper extends Component {
  static propTypes = {
    product: productShape.isRequired,
    onUpdateItemsState: PropTypes.func.isRequired,
    showToast: PropTypes.func,
    intl: intlShape,
    minicartItems: PropTypes.array,
    updateItems: PropTypes.func.isRequired,
    updateLocalItems: PropTypes.func.isRequired,
    push: PropTypes.func.isRequired,
    index: PropTypes.number,
  }

  state = {
    quantity: this.props.product.quantity,
    canIncrease: true,
  }

  pushPixelCartEvents = ({ isAdditionOfProd, product }) => {
    const updatedProduct = {
      skuId: path(['sku', 'itemId'], product),
      variant: path(['sku', 'name'], product),
      price: path(['assemblyOptions', 'parentPrice'], product),
      name: path(['productName'], product),
      quantity: path(['quantity'], product),
      productRefId: product.productRefId,
      brand: product.brand,
      category: product.category,
    }

    if (isAdditionOfProd) {
      this.props.push({
        event: 'addToCart',
        items: [updatedProduct],
      })
    } else {
      // removal of product
      this.props.push({
        event: 'removeFromCart',
        items: [updatedProduct],
      })
    }
  }

  componentDidUpdate = (prevProps) => {
    const {
      product: { quantity: prevQuantity },
    } = prevProps

    const {
      product: { quantity },
      showToast,
      intl,
    } = this.props

    // eslint-disable-next-line vtex/prefer-early-return
    if (prevQuantity !== quantity) {
      this.pushPixelCartEvents({
        isAdditionOfProd: prevQuantity < quantity,
        product: this.props.product,
      })
      // eslint-disable-next-line react/no-access-state-in-setstate
      const canIncrease = quantity === this.state.quantity

      this.setState({ quantity, canIncrease })
      if (!canIncrease) {
        showToast({
          message: intl.formatMessage({
            id: 'store/productSummary.quantity-error',
          }),
        })
      }
    }
  }

  handleOnChange = (e) => {
    e.stopPropagation()
    e.preventDefault()
    this.props.onUpdateItemsState(true)
    this.setState({ quantity: e.value }, () =>
      this.debouncedUpdate(this.state.quantity)
    )
  }

  updateItemQuantity = async (quantity) => {
    const { product, updateItems, updateLocalItems, index } = this.props

    this.setState({ canIncrease: true })
    const {
      sku: { itemId: id, seller = {} },
      cartIndex,
    } = product

    try {
      if (cartIndex != null) {
        await updateItems([
          {
            id,
            quantity,
            seller: seller.sellerId,
            index: cartIndex,
          },
        ])
      } else {
        await updateLocalItems([
          {
            id,
            quantity,
            seller: seller.sellerId,
            index,
          },
        ])
      }
    } catch (err) {
      // gone wrong, rollback to old quantity value
      console.error(err)
    }

    this.props.onUpdateItemsState(false)
  }

  debouncedUpdate = debounce(this.updateItemQuantity, 1000)

  render() {
    return (
      <NumericStepper
        lean
        size="small"
        value={this.state.quantity}
        minValue={1}
        maxValue={this.state.canIncrease ? undefined : this.state.quantity}
        onChange={this.handleOnChange}
      />
    )
  }
}

const withUpdateItemsMutation = graphql(UPDATE_ITEMS_MUTATION, {
  props: ({ mutate }) => ({
    updateItems: (items) => mutate({ variables: { items } }),
  }),
})

const withUpdateLocalItemsMutation = graphql(UPDATE_LOCAL_ITEMS_MUTATION, {
  props: ({ mutate }) => ({
    updateLocalItems: (items) => mutate({ variables: { items } }),
  }),
})

export default compose(
  Pixel,
  injectIntl,
  withToast,
  withUpdateItemsMutation,
  withUpdateLocalItemsMutation
)(ProductQuantityStepper)

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductSummaryInline.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
import classNames from 'classnames'
import { Link } from 'vtex.render-runtime'

import AttachmentList from './AttachmentList'
import ProductImage from './ProductImage'
import ProductSummaryBuyButton from './ProductSummaryBuyButton'
import ProductSummaryPrice from './ProductSummaryPrice'
import ProductSummaryName from './ProductSummaryName'
import productSummary from '../../productSummary.css'

class ProductSummaryInline extends Component {
  render() {
    const {
      product,
      showBorders,
      handleMouseEnter,
      handleMouseLeave,
      actionOnClick,
      imageProps,
      nameProps,
      priceProps,
      buyButtonProps,
      inViewRef,
      listName,
    } = this.props

    const containerClasses = classNames(
      productSummary.container,
      productSummary.containerInlinePrice,
      'overflow-hidden br3 h-100 w-100'
    )

    const summaryClasses = classNames(
      `${productSummary.element} ${productSummary.clearLink} pointer pt3 pb4 flex flex-column`,
      {
        'bb b--muted-4 mh2 mh3-ns mt2': showBorders,
      }
    )

    const nameClasses = {
      containerClass: 'flex items-start justify-left tl w-90 t-mini pb2',
      brandNameClass: 't-body c-on-base',
    }

    const priceClasses = {
      containerClass: classNames('flex flex-column nr1', {
        [`${productSummary.priceContainer}`]: !showBorders,
      }),
      sellingPriceClass: 'dib ph2 t-body t-heading-5-ns',
    }

    const buyButtonClasses = {
      containerClass: `${productSummary.buyButtonContainer} pt3 w-100`,
    }

    return (
      <section
        className={containerClasses}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
        ref={inViewRef}
      >
        <Link
          className={summaryClasses}
          page="store.product"
          params={{
            slug: product && product.linkText,
            id: product && product.productId,
            __listName: listName,
          }}
          onClick={actionOnClick}
        >
          <article className="flex">
            <div className={`${productSummary.imageContainer} db w-70`}>
              <ProductImage {...imageProps} />
            </div>
            <div
              className={`${productSummary.information} w-80 pb2 pl3 flex flex-wrap flex-column justify-between`}
            >
              <div className="flex flex-column">
                <ProductSummaryName {...nameProps} {...nameClasses} />
                <AttachmentList product={product} />
                <div className="nr2">
                  <ProductSummaryPrice {...priceProps} {...priceClasses} />
                </div>
              </div>
              <div className="flex flex-column-reverse">
                <ProductSummaryBuyButton
                  {...buyButtonProps}
                  {...buyButtonClasses}
                />
              </div>
            </div>
          </article>
        </Link>
      </section>
    )
  }
}

export default ProductSummaryInline

```


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

[User] Show me the implementation of product-summary/react/legacy/components/AttachmentList.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
// eslint-disable-next-line no-restricted-imports
import { pathOr, reject } from 'ramda'

import { productShape } from '../../utils/propTypes'
import RemovedAttachmentsList from '../../components/ProductSummaryAttachmentList/RemovedAttachmentsList'
import AddedAttachmentsList from '../../components/ProductSummaryAttachmentList/AddedAttachmentsList'
import styles from '../../productSummary.css'

const itemShouldHide = ({ item, extraQuantity }) =>
  extraQuantity === 0 && item.sellingPriceWithAssemblies === 0

const AttachmentList = ({ product }) => {
  const addedOptions = pathOr([], ['assemblyOptions', 'added'], product)
  const removedOptions = pathOr([], ['assemblyOptions', 'removed'], product)

  const filteredOption = reject(itemShouldHide, addedOptions)

  if (filteredOption.length === 0 && removedOptions.length === 0) {
    return null
  }

  return (
    <div className={`${styles.attachmentListContainer} pv2`}>
      <AddedAttachmentsList addedOptions={filteredOption} />
      <RemovedAttachmentsList removedOptions={removedOptions} />
    </div>
  )
}

AttachmentList.propTypes = {
  product: productShape,
}

export default AttachmentList

```


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

[User] Show me the implementation of product-summary/react/legacy/components/ProductImage.js from the product-summary VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
// eslint-disable-next-line no-restricted-imports
import { compose, pathOr, path } from 'ramda'
import { CollectionBadges, DiscountBadge } from 'vtex.store-components'

import ImagePlaceholder from '../../components/ImagePlaceholder'
import { productShape } from '../../utils/propTypes'
import productSummary from '../../productSummary.css'

const maybeBadge = ({ listPrice, price, label }) => (shouldShow) => (
  component
) => {
  if (shouldShow) {
    return (
      <DiscountBadge listPrice={listPrice} sellingPrice={price} label={label}>
        {component}
      </DiscountBadge>
    )
  }

  return component
}

const maybeCollection = ({ productClusters }) => (shouldShow) => (
  component
) => {
  if (shouldShow && productClusters && productClusters.length > 0) {
    const collections = productClusters.map((cl) => cl.name)

    return (
      <CollectionBadges collectionBadgesText={collections}>
        {component}
      </CollectionBadges>
    )
  }

  return component
}

const ProductImage = ({
  product,
  showBadge,
  badgeText,
  showCollections,
  displayMode,
}) => {
  const [error, setError] = useState(false)

  if (!path(['sku', 'image', 'imageUrl'], product) || error) {
    return <ImagePlaceholder cssHandle={productSummary.imagePlaceholder} />
  }

  const {
    productClusters,
    productName: name,
    sku: {
      image: { imageUrl },
    },
  } = product

  const imageClassName = classNames({
    [productSummary.imageNormal]: displayMode !== 'inline',
    [productSummary.imageInline]: displayMode === 'inline',
  })

  const commertialOffer = pathOr(
    {},
    ['sku', 'seller', 'commertialOffer'],
    product
  )

  const withBadge = maybeBadge({
    listPrice: commertialOffer.ListPrice,
    price: commertialOffer.Price,
    label: badgeText,
  })

  const withCollection = maybeCollection({ productClusters })
  const img = (
    <img
      className={imageClassName}
      src={imageUrl}
      alt={name}
      onError={() => setError(true)}
    />
  )

  return compose(withBadge(showBadge), withCollection(showCollections))(img)
}

ProductImage.propTypes = {
  /** Product that owns the informations */
  product: productShape,
  /** Set the discount badge's visibility */
  showBadge: PropTypes.bool,
  /** Text shown on badge */
  badgeText: PropTypes.string,
  /** Defines if the collection badges are shown */
  showCollections: PropTypes.bool,
  /** Display mode of the summary */
  displayMode: PropTypes.string,
}

export default ProductImage

```