[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 shelf-components/styles/css/vtex.product-summary.css from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```css
.container--buyTogether {
  max-width: none !important;
}

.skuSelectorContainer--buyTogether {
  margin: 0.25rem;
}

.skuSelectorContainer--buyTogether .skuSelectorItemImage .frameAround, .skuSelectorContainer--buyTogether .skuSelectorItemImage .skuSelectorInternalBox {
  border-radius: 50%;
}

```


[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 shelf-components/styles/css/vtex.slider-layout.css from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```css
.slide--shelf {
  margin-bottom: 25px;
  padding-left: .5rem;
  padding-right: .5rem;
}

.layoutContainer--shelf {
  margin-top: 20px;
  margin-bottom: 20px;
  max-width: 96rem;
}

```


[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 shelf-components/react/typings/vtex.recommendation-context.d.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.recommendation-context/RecommendationContext' {
  export const useRecommendation
}

```


[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 shelf-components/react/typings/vtex.render-runtime.d.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.render-runtime' {
  export const ExtensionPoint
  export const useTreePath
  export const Loading
  export const useRuntime
}

```


[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 shelf-components/react/typings/global.d.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

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

declare global {
  interface StorefrontFunctionComponent<P = {}> extends FunctionComponent<P> {
    schema?: object
    getSchema?(props?: P): object
  }

  interface Product {
    brand: string
    brandId: number
    cacheId: string
    categories?: string[]
    clusterHighlights?: Array<{ id: string; name: string }>
    categoryTree?: Array<{ slug: string }>
    description?: string
    items: SKU[]
    link: string
    linkText: string
    priceRange: ProductPriceRange
    productClusters?: Array<{ id: string; name: string }>
    productId: string
    productName: string
    productReference: string
    properties?: Array<{ name: string; values: string[] }>
    sku?: SKU
    specificationGroups: SpecificationGroup[]
  }

  interface ProductPriceRange {
    sellingPrice: PriceRange
    listPrice: PriceRange
  }

  interface PriceRange {
    highPrice: number
    lowPrice: number
  }

  interface SpecificationGroupProperty {
    originalName: string
    name: string
    values: string[]
  }

  interface SpecificationGroup {
    name?: string
    originalName?: string
    specifications?: SpecificationGroupProperty[]
  }

  interface Item {
    quantity: number
    product: Product
    selectedItem: SKU
  }

  interface CartItem {
    id: string
    productId: string
    quantity: number
    uniqueId: string
    detailUrl: string
    name: string
    brand: string
    category: string
    productRefId: string
    seller: string
    variant: string
    skuName: string
    price: number
    listPrice: number
    sellingPrice: number
    sellingPriceWithAssemblies: number
    measurementUnit: string
    skuSpecifications: any[]
    imageUrl: string
    options: any[]
    assemblyOptions: {
      added: any[]
      removed: any[]
      parentPrice: number
    }
    referenceId: Array<{
      Key: string
      Value: string
    }> | null
  }

  interface SKU {
    complementName?: string
    ean?: string
    image?: Image
    images: Image[]
    itemId: string
    measurementUnit: string
    name: string
    nameComplete?: string
    referenceId: Array<{
      Key: string
      Value: string
    }> | null
    seller?: Seller
    sellers: Seller[]
    unitMultiplier?: number
    variations: Array<{ name: string; values: string[] }>
  }

  interface Image {
    cacheId?: string
    imageId?: string
    imageLabel: string | null
    imageTag?: string
    imageUrl: string
    imageText?: string | null
  }

  interface Seller {
    addToCartLink?: string
    commertialOffer: CommertialOffer
    sellerId: string
    sellerDefault?: boolean
    sellerName?: string
  }

  interface CommertialOffer {
    AvailableQuantity: number
    Installments: Installment[]
    ListPrice: number
    Price: number
    PriceWithoutDiscount: number
    RewardValue?: number
    Tax: number
    discountHighlights?: Array<{ name: string }>
    spotPrice?: number
    taxPercentage: number
    teasers?: Array<{ name: string }>
  }

  interface Installment {
    InterestRate: number
    Name: string
    NumberOfInstallments: number
    PaymentSystemName?: string
    TotalValuePlusInterestRate: number
    Value: number
  }

  interface SpecificationFilter {
    id: string
    value: string
  }

  interface SuggestedProductsList {
    baseProductId: string
    suggestedProductsIds?: string
    category?: string
    specificationFilters?: SpecificationFilter[]
    collection?: string
    orderBy?: string
    hideUnavailableItems?: boolean
    maxItems?: number
    skusFilter?: string
    installmentCriteria?: string
  }

  interface RecommendedList {
    base: Product[]
    recommended: Product[]
  }

  interface SuggestedList {
    products: Product[]
    hidden: boolean
    current: 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 shelf-components/react/typings/css.d.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  const styles: {
    [key: string]: string
  }

  export default styles
}

```


[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 shelf-components/react/typings/vtex.pixel-manager.d.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.pixel-manager/PixelContext' {
  export const usePixel
}

```


[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 shelf-components/react/typings/graphql.d.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

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

  const query: DocumentNode
  export default query
}

```


[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 shelf-components/react/typings/vtex.product-summary.d.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.product-summary/ProductSummaryCustom' {
  export const mapCatalogProductToProductSummary: (product: any) => any
}

declare module 'vtex.product-summary' {
  export const ProductSummaryList
  export const 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 shelf-components/react/typings/vtex.styleguide.d.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.styleguide' {
  export const ButtonWithIcon
  export const ButtonPlain
  export const IconClear
  export const IconPlusLines
}

```


[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 shelf-components/react/queries/productsByIdentifier.gql from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
query productsByIdentifier(
  $field: ProductUniqueIdentifierField!
  $values: [ID!]
) {
  productsByIdentifier(field: $field, values: $values)
    @context(provider: "vtex.search-graphql") {
    brand
    brandId
    cacheId
    categoryTree {
      slug
    }
    description
    items {
      images {
        imageLabel
        imageTag
        imageUrl
      }
      itemId
      measurementUnit
      name
      sellers {
        commertialOffer {
          AvailableQuantity
          Installments {
            InterestRate
            Name
            NumberOfInstallments
            TotalValuePlusInterestRate
            Value
          }
          ListPrice
          Price
          PriceWithoutDiscount
          Tax
          discountHighlights {
            name
          }
          taxPercentage
          teasers {
            name
          }
        }
        sellerId
      }
      unitMultiplier
      variations {
        name
        values
      }
    }
    link
    linkText
    priceRange {
      listPrice {
        highPrice
        lowPrice
      }
      sellingPrice {
        highPrice
        lowPrice
      }
    }
    productClusters {
      id
      name
    }
    productId
    productName
    productReference
    properties {
      name
      values
    }
    specificationGroups {
      name
      specifications {
        name
        values
      }
    }
  }
}

```


[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 shelf-components/react/utils/events.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
const enum EventType {
  ProductClick = 'productClick',
  AddToCart = 'addToCart',
  View = 'shelfView',
}

export const handleProductClick = (push: (data: any) => void, page: string) => {
  return (product: any) => {
    push({
      page,
      event: 'shelf',
      eventType: EventType.ProductClick,
      product,
    })
  }
}

export const handleView = (push: (data: any) => void, page: string) => {
  return (shelf: string) => {
    push({
      page,
      event: 'shelf',
      eventType: EventType.View,
      shelf,
    })
  }
}

```


[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 shelf-components/react/utils/index.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const mapSKUItemsToCartItems = (
  skuItems: Item[]
): Array<CartItem | null> =>
  skuItems.map(item => {
    const {
      selectedItem: {
        itemId,
        name,
        measurementUnit,
        images,
        referenceId,
        seller,
        sellers,
      },
      product: {
        productId,
        linkText,
        productName,
        brand,
        categories,
        productReference,
      },
    } = item
    const selectedSeller = seller ?? sellers[0]

    if (!selectedSeller) {
      return null
    }

    return {
      id: itemId,
      productId,
      quantity: 1,
      uniqueId: '',
      detailUrl: `/${linkText}/p`,
      name: productName,
      brand,
      category: categories && categories.length > 0 ? categories[0] : '',
      productRefId: productReference,
      seller: selectedSeller.sellerId,
      variant: name,
      skuName: name,
      price: selectedSeller.commertialOffer.PriceWithoutDiscount * 100,
      listPrice: selectedSeller.commertialOffer.ListPrice * 100,
      sellingPrice: selectedSeller.commertialOffer.Price * 100,
      sellingPriceWithAssemblies: selectedSeller.commertialOffer.Price * 100,
      measurementUnit,
      skuSpecifications: [],
      imageUrl: images[0]?.imageUrl,
      options: [],
      assemblyOptions: {
        added: [],
        removed: [],
        parentPrice: selectedSeller.commertialOffer.Price,
      },
      referenceId,
    }
  })

export const sortBaseProductsBySuggestedLists = (
  products: Product[],
  suggestedLists: SuggestedProductsList[]
) => {
  const ids =
    suggestedLists
      ?.map(list => list.baseProductId)
      .filter(id => id && id !== '') ?? []
  return products?.sort(
    (a: Product, b: Product) =>
      ids.indexOf(a.productId) - ids.indexOf(b.productId)
  )
}

export const sortProductsBySuggestedIds = (
  products: Product[],
  suggestedIds: string[]
) => {
  return products?.sort(
    (a: Product, b: Product) =>
      suggestedIds.indexOf(a.productId) - suggestedIds.indexOf(b.productId)
  )
}

export const sortItemsByLists = (
  items: Item[],
  suggestedLists: SuggestedList[]
) => {
  const ids = suggestedLists.map(list => list.products[list.current].productId)
  const copyItems: Item[] = Object.assign([], items)
  return copyItems.sort(
    (a: Item, b: Item) =>
      ids.indexOf(a.product.productId) - ids.indexOf(b.product.productId)
  )
}

```


[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 shelf-components/react/hooks/useEvents.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

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

import { handleView, handleProductClick } from '../utils/events'

export const useEvents = (
  recommendation: { shouldSendEvents: boolean },
  push: (data: any) => void,
  page: string
) => {
  const shouldSendEvents = useMemo(() => {
    return recommendation?.shouldSendEvents
  }, [recommendation])
  const onView = shouldSendEvents ? handleView(push, page) : () => {}
  const onProductClick = shouldSendEvents
    ? handleProductClick(push, page)
    : () => {}

  return {
    onView,
    onProductClick,
  }
}

```


[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 shelf-components/react/hooks/useOnView.ts from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
/** Code borrowed from vtex.render-runtime */
/* eslint-disable @typescript-eslint/no-use-before-define */
import { useEffect, useRef, MutableRefObject } from 'react'

interface IntersectionEvent {
  entry: IntersectionObserverEntry
  unobserve: () => void
}

interface HookOptions {
  ref: MutableRefObject<HTMLElement | null>
  onView?: (event: IntersectionEvent) => any
  threshold?: number
  once?: boolean
  bailOut?: boolean
  initializeOnInteraction?: boolean
}

/** Hook for detecting when an element is inside the viewport.
 * Differs from react-intersection-observer (https://www.npmjs.com/package/react-intersection-observer)
 * in that this hook doesn't use setState, using a callback approach instead, to improve
 * performance by preventing re-rendering.
 */
const useOnView = ({
  ref,
  onView,
  threshold = 0,
  once = false,
  bailOut = false,
  initializeOnInteraction = false,
}: HookOptions) => {
  const isObserving = useRef(false)
  const didIntersect = useRef(false)

  useEffect(() => {
    const initializeObserver = () => {
      const element = ref.current

      if (
        bailOut ||
        isObserving.current ||
        !element ||
        !onView ||
        (once && didIntersect.current)
      ) {
        return () => {}
      }

      isObserving.current = true

      const unobserve = () => {
        if (isObserving.current) {
          observer.unobserve(element)
          isObserving.current = false
        }
      }

      const observer = new IntersectionObserver(([entry]) => {
        if (!entry.isIntersecting) {
          return
        }

        if (entry.intersectionRatio < threshold) {
          return
        }

        if (once) {
          unobserve()
        }

        didIntersect.current = true
        onView({ entry, unobserve })
      })

      observer.observe(element)

      return unobserve
    }

    if (initializeOnInteraction) {
      const cleanUpEvents = () => {
        window?.document?.removeEventListener('scroll', handleInteraction)
        window?.document?.removeEventListener('mouseover', handleInteraction)
      }

      let unobserve = () => {
        cleanUpEvents()
      }

      const handleInteraction = () => {
        cleanUpEvents()
        unobserve = initializeObserver()
      }

      window?.document?.addEventListener('scroll', handleInteraction)
      window?.document?.addEventListener('mouseover', handleInteraction)

      return unobserve
    }

    return initializeObserver()
  }, [bailOut, initializeOnInteraction, onView, once, ref, threshold])
}

export { useOnView }

```


[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 shelf-components/react/icons/IconRefresh.tsx from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

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

interface Props {
  size?: number
  color?: string
}

const IconRefresh: FC<Props> = ({ size = 16, color = 'currentColor' }) => {
  return (
    <svg width={size} height={size} viewBox="0 0 1792 1792">
      <path
        d="M1639 1056q0 5-1 7-64 268-268 434.5t-478 166.5q-146 0-282.5-55t-243.5-157l-129 129q-19 19-45 19t-45-19-19-45v-448q0-26 19-45t45-19h448q26 0 45 19t19 45-19 45l-137 137q71 66 161 102t187 36q134 0 250-65t186-179q11-17 53-117 8-23 30-23h192q13 0 22.5 9.5t9.5 22.5zm25-800v448q0 26-19 45t-45 19h-448q-26 0-45-19t-19-45 19-45l138-138q-148-137-349-137-134 0-250 65t-186 179q-11 17-53 117-8 23-30 23h-199q-13 0-22.5-9.5t-9.5-22.5v-7q65-268 270-434.5t480-166.5q146 0 284 55.5t245 156.5l130-129q19-19 45-19t45 19 19 45z"
        fill={color}
      />
    </svg>
  )
}

export default IconRefresh

```


[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 shelf-components/react/icons/IconEqual.tsx from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

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

interface Props {
  size?: number
  color?: string
}

const IconEqual: FC<Props> = ({ size = 16, color = 'currentColor' }) => {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" version="1.1">
      <g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
        <g fill={color}>
          <path d="M0,3 L0,5 C6.76353751e-17,5.55228475 0.44771525,6 1,6 L15,6 C15.5522847,6 16,5.55228475 16,5 L16,3 C16,2.44771525 15.5522847,2 15,2 L1,2 C0.44771525,2 -6.76353751e-17,2.44771525 0,3 Z" />
          <path d="M0,11 L0,13 C6.76353751e-17,13.5522847 0.44771525,14 1,14 L15,14 C15.5522847,14 16,13.5522847 16,13 L16,11 C16,10.4477153 15.5522847,10 15,10 L1,10 C0.44771525,10 -6.76353751e-17,10.4477153 0,11 Z" />
        </g>
      </g>
    </svg>
  )
}

export default IconEqual

```


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

[Assistant]
Here's the implementation:

```tsx
import React, { useState, useEffect, useMemo, useRef } from 'react'
import { useLazyQuery } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'
import { QueryProducts as productsQuery } from 'vtex.store-resources'
import { usePixel } from 'vtex.pixel-manager/PixelContext'
import { useRecommendation } from 'vtex.recommendation-context/RecommendationContext'

import productsByIdentifier from '../../queries/productsByIdentifier.gql'
import RefreshProductSummary from './RefreshProductSummary'
import SuggestedProducts from './SuggestedProducts'
import styles from './styles.css'
import {
  sortBaseProductsBySuggestedLists,
  sortProductsBySuggestedIds,
} from '../../utils'
import { useOnView } from '../../hooks/useOnView'
import { useEvents } from '../../hooks/useEvents'

enum ProductUniqueIdentifierField {
  id = 'id',
  slug = 'slug',
  ean = 'ean',
  reference = 'reference',
  sku = 'sku',
}

interface RefreshShelfProps {
  baseProductTitle?: string
  suggestedProductsTitle?: string
  suggestedLists?: SuggestedProductsList[]
  recommendedLists?: RecommendedList[]
  sliderLayout: {
    showNavigationArrows: string
    showPaginationDots: string
  }
  isSecondary?: boolean
  secondaryBaseProductTitle?: string
  secondarySuggestedProductsTitle?: string
}

const parseFilters = ({ id, value }: SpecificationFilter) =>
  `specificationFilter_${id}:${value}`

const RefreshShelf: StorefrontFunctionComponent<RefreshShelfProps> = ({
  baseProductTitle,
  suggestedProductsTitle,
  suggestedLists,
  recommendedLists,
  isSecondary,
  secondaryBaseProductTitle,
  secondarySuggestedProductsTitle,
  ...props
}) => {
  const { push } = usePixel()
  const { page } = useRuntime()
  const [current, setCurrent] = useState(0)
  const ref = useRef<HTMLDivElement | null>(null)
  const recommendation = useRecommendation?.()
  const { onView, onProductClick } = useEvents(recommendation, push, page)

  useOnView({
    ref,
    onView: () => onView('refresh'),
    once: true,
    initializeOnInteraction: true,
  })

  const [
    queryProducts,
    { data: productsData, loading: loadingProducts, called, refetch },
  ] = useLazyQuery(productsQuery, {
    notifyOnNetworkStatusChange: true,
  })
  const currentList = suggestedLists?.[current]

  const [
    queryBaseProductsByID,
    {
      data: baseProductsData,
      loading: loadingBaseProducts,
      called: queryBaseProductsCalled,
      refetch: baseProductsRefetch,
    },
  ] = useLazyQuery(productsByIdentifier, { notifyOnNetworkStatusChange: true })

  const [
    querySuggestedProductsByID,
    {
      data: suggestedProductsData,
      loading: loadingProductsById,
      called: calledById,
      refetch: refetchQueryById,
    },
  ] = useLazyQuery(productsByIdentifier, { notifyOnNetworkStatusChange: true })

  const baseProducts = useMemo(() => {
    if (recommendedLists) {
      return recommendedLists.map((list: RecommendedList) => list.base[0])
    }
    if (!suggestedLists || suggestedLists.length === 0) {
      return []
    }
    if (baseProductsData?.productsByIdentifier) {
      const sortProducts = sortBaseProductsBySuggestedLists(
        baseProductsData?.productsByIdentifier,
        suggestedLists
      )
      return sortProducts
    }
    if (productsData?.products?.[0]) {
      return productsData?.products?.[0]
    }
  }, [baseProductsData, productsData, suggestedLists, recommendedLists])

  const suggestedProducts = useMemo(() => {
    if (recommendedLists) {
      return recommendedLists[current].recommended
    }
    if (!suggestedLists) {
      return []
    }
    if (
      currentList?.suggestedProductsIds &&
      suggestedProductsData?.productsByIdentifier
    ) {
      const sortProducts = sortProductsBySuggestedIds(
        suggestedProductsData?.productsByIdentifier,
        currentList.suggestedProductsIds.split(',')
      )
      return sortProducts
    }
    if (productsData?.products) {
      return productsData.products
    }
  }, [
    productsData,
    suggestedLists,
    recommendedLists,
    current,
    currentList,
    suggestedProductsData,
  ])

  useEffect(() => {
    if (!suggestedLists || recommendedLists) {
      return
    }

    const baseIds = suggestedLists
      .map(list => list.baseProductId)
      .filter(id => id && id !== '')

    if (baseIds.length === 0) {
      return
    }

    const executeQuery = (variables: Record<string, any>) =>
      queryBaseProductsCalled
        ? baseProductsRefetch(variables)
        : queryBaseProductsByID({ variables })

    executeQuery({
      field: ProductUniqueIdentifierField.id,
      values: baseIds,
    })
  }, [
    suggestedLists,
    recommendedLists,
    queryBaseProductsCalled,
    queryBaseProductsByID,
    baseProductsRefetch,
  ])

  useEffect(() => {
    const executeQuery = (variables: Record<string, any>) =>
      called ? refetch(variables) : queryProducts({ variables })

    if (!suggestedLists || suggestedLists.length === 0 || recommendedLists) {
      executeQuery({
        category: '',
        collection: '',
        specificationFilters: [],
        orderBy: 'OrderByTopSaleDESC',
        from: 0,
        to: 9,
        hideUnavailableItems: false,
        skusFilter: 'ALL_AVAILABLE',
        installmentCriteria: 'MAX_WITHOUT_INTEREST',
      })
      return
    }

    if (!currentList) {
      return
    }

    if (currentList.suggestedProductsIds) {
      const executeQueryById = (variables: Record<string, any>) =>
        calledById
          ? refetchQueryById(variables)
          : querySuggestedProductsByID({ variables })

      const suggestedIds = currentList.suggestedProductsIds.split(',')

      executeQueryById({
        field: ProductUniqueIdentifierField.id,
        values: suggestedIds,
      })
    } else {
      const {
        category = '',
        collection,
        hideUnavailableItems = false,
        orderBy = 'OrderByTopSaleDESC',
        specificationFilters = [],
        maxItems = 10,
        skusFilter,
        installmentCriteria,
      } = currentList

      executeQuery({
        category,
        ...(collection != null
          ? {
              collection,
            }
          : {}),
        specificationFilters: specificationFilters.map(parseFilters),
        orderBy,
        from: 0,
        to: maxItems - 1,
        hideUnavailableItems,
        skusFilter,
        installmentCriteria,
      })
    }
  }, [
    suggestedLists,
    recommendedLists,
    currentList,
    called,
    calledById,
    queryProducts,
    querySuggestedProductsByID,
    refetch,
    refetchQueryById,
  ])

  const handleChangeProduct = () => {
    const newIndex = current < baseProducts?.length - 1 ? current + 1 : 0
    setCurrent(newIndex)
  }

  const loading = useMemo(
    () => loadingBaseProducts || loadingProducts || loadingProductsById,
    [loadingBaseProducts, loadingProducts, loadingProductsById]
  )

  if (
    !loading &&
    (!baseProducts ||
      !suggestedProducts ||
      !baseProducts.length ||
      !suggestedProducts.length)
  ) {
    return null
  }

  return (
    <div
      className={`flex flex-wrap flex-nowrap-ns justify-around ${styles.refreshShelf}`}
      ref={ref}
    >
      <RefreshProductSummary
        title={isSecondary ? secondaryBaseProductTitle : baseProductTitle}
        loading={loading}
        selected={current}
        products={baseProducts}
        onChangeSelected={handleChangeProduct}
        onProductClick={onProductClick}
      />
      <SuggestedProducts
        title={
          isSecondary ? secondarySuggestedProductsTitle : suggestedProductsTitle
        }
        loading={loading}
        products={suggestedProducts}
        sliderProps={props.sliderLayout}
        key={baseProducts?.[current]?.productId}
        onProductClick={onProductClick}
      />
    </div>
  )
}

RefreshShelf.schema = {
  title: 'admin/editor.refresh-shelf.title',
}

export default RefreshShelf

```


[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 shelf-components/react/components/RefreshShelf/RefreshProductSummary.tsx from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, useEffect } from 'react'
import { ExtensionPoint } from 'vtex.render-runtime'
import ProductSummary from 'vtex.product-summary/ProductSummaryCustom'
import { ButtonPlain } from 'vtex.styleguide'
import { useIntl, defineMessages } from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'

import IconRefresh from '../../icons/IconRefresh'
import ProductSummaryLoader from './ProductSummaryLoader'
import styles from './styles.css'

interface RefreshProductSummaryProps {
  products: Product[]
  selected: number
  loading: boolean
  onChangeSelected?: () => void
  title?: string
  onProductClick: (product: Product) => void
}

const messages = defineMessages({
  title: {
    id: 'store/shelf.refresh.refresh-product-summary.title',
    defaultMessage: '',
  },
})

const CSS_HANDLES = ['refreshProductSummary', 'refreshProductTitleContainer']

const RefreshProductSummary: StorefrontFunctionComponent<RefreshProductSummaryProps> = ({
  products,
  selected,
  onChangeSelected,
  loading,
  title,
  onProductClick,
}) => {
  const intl = useIntl()
  const handles = useCssHandles(CSS_HANDLES)
  const [selectedProduct, setSelectedProduct] = useState(
    products?.[selected]
      ? ProductSummary.mapCatalogProductToProductSummary(products[selected])
      : {}
  )

  useEffect(() => {
    if (products?.[selected]) {
      const normalized = ProductSummary.mapCatalogProductToProductSummary(
        products[selected]
      )
      setSelectedProduct(normalized)
    }
  }, [selected, products])

  const handleRefresh = () => {
    onChangeSelected?.()
  }

  return (
    <div className={`tc w-70 w-40-ns w-20-l ${handles.refreshProductSummary}`}>
      <div
        className={`nowrap mv4 f4 v-mid ${handles.refreshProductTitleContainer}`}
      >
        <span className={`mr3 ${styles.refreshProductTitle}`}>
          {title && title !== '' ? title : intl.formatMessage(messages.title)}
        </span>
        {products?.length > 1 && (
          <ButtonPlain onClick={handleRefresh} className="ml2">
            <IconRefresh size={20} />
          </ButtonPlain>
        )}
      </div>
      {loading && <ProductSummaryLoader />}
      {!loading && selectedProduct && selectedProduct.productId && (
        <div key={selectedProduct.productId}>
          <ExtensionPoint
            id="product-summary"
            product={selectedProduct}
            actionOnClick={() => onProductClick(selectedProduct)}
          />
        </div>
      )}
    </div>
  )
}

export default RefreshProductSummary

```


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

[Assistant]
Here's the implementation:

```css
.refreshProductSummary {
  margin-left: 0.25rem;
  margin-right: 0.25rem;
}

.refreshShelf {
  margin-bottom: 25px;
}

.suggestedProductsTitleContainer {
  height: 40px;
  font-weight: 600;
  text-transform: uppercase;
}

.refreshProductTitleContainer {
  margin: 0.75rem;
  text-transform: uppercase;
  font-weight: 600;
  height: 40px;
}

.suggestedProductsContainer {
  max-width: -webkit-fill-available;
  text-align: center;
}

.suggestedProductsTitle {
  vertical-align: middle;
}

.refreshProductTitle {
  vertical-align: middle;
}

@media screen and (min-width: 64.125em) {
  .suggestedProductsContainer {
    width: 75%;
  }
}

@media screen and (min-width: 40em) and (max-width: 64.124em) {
  .suggestedProductsContainer {
    width: 60%;
  }
}

.slide {
  margin-bottom: 25px;
  margin-left: .5rem;
  margin-right: .5rem;
}

```


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

[User] Show me the implementation of shelf-components/react/components/RefreshShelf/ProductSummaryLoader.tsx from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

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

const ProductSummaryLoader = () => (
  <ContentLoader height="560" width="300" speed={2}>
    <rect x="0" y="0" rx="3" ry="3" width="100%" height="300" />
    <rect x="0" y="310" rx="3" ry="3" width="100%" height="30" />
    <rect x="0" y="370" rx="3" ry="3" width="50%" height="16" />
    <rect x="0" y="390" rx="3" ry="3" width="80%" height="23" />
    <rect x="0" y="420" rx="3" ry="3" width="80%" height="16" />
    <rect x="0" y="480" rx="3" ry="3" width="100%" height="40" />
  </ContentLoader>
)

export default ProductSummaryLoader

```


[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 shelf-components/react/components/RefreshShelf/SuggestedProducts.tsx from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useMemo } from 'react'
import { useIntl, defineMessages } from 'react-intl'
import { SliderLayout } from 'vtex.slider-layout'
import { ProductSummaryListWithoutQuery } from 'vtex.product-summary'
import ProductSummary from 'vtex.product-summary/ProductSummaryCustom'
import { useCssHandles } from 'vtex.css-handles'
import { useDevice } from 'vtex.device-detector'

import styles from './styles.css'
import ProductSummaryLoader from './ProductSummaryLoader'

interface Props {
  title?: string
  products: any[]
  loading: boolean
  sliderProps?: {
    showNavigationArrows: string
    showPaginationDots: string
  }
  onProductClick: (product: Product) => void
}

const messages = defineMessages({
  title: {
    id: 'store/shelf.refresh.suggested-products.title',
    defaultMessage: '',
  },
})

const CSS_HANDLES = ['suggestedProductsTitleContainer']

const itemsPerPage = {
  desktop: 3,
  phone: 1,
  tablet: 1,
}

const SliderLayoutLoader = () => (
  <div className="flex mh8">
    <div className="mr7">
      <ProductSummaryLoader />
    </div>
    <div className="mr7">
      <ProductSummaryLoader />
    </div>
    <div>
      <ProductSummaryLoader />
    </div>
  </div>
)

const SuggestedProducts: StorefrontFunctionComponent<Props> = ({
  title,
  products,
  loading,
  sliderProps,
  onProductClick,
}) => {
  const { isMobile } = useDevice()
  const intl = useIntl()
  const handles = useCssHandles(CSS_HANDLES)
  const normalizedProducts = useMemo(
    () =>
      products?.length > 0
        ? products.map(product =>
            ProductSummary.mapCatalogProductToProductSummary(product)
          )
        : [],
    [products]
  )

  return (
    <div className={styles.suggestedProductsContainer}>
      <div
        className={`mv4 f4 v-mid ${handles.suggestedProductsTitleContainer}`}
      >
        <span className={styles.suggestedProductsTitle}>
          {title && title !== '' ? title : intl.formatMessage(messages.title)}
        </span>
      </div>
      {(loading || !normalizedProducts) &&
        (isMobile ? <ProductSummaryLoader /> : <SliderLayoutLoader />)}
      {!loading && normalizedProducts?.length > 0 && (
        <ProductSummaryListWithoutQuery
          products={normalizedProducts}
          actionOnProductClick={onProductClick}
        >
          <SliderLayout
            showNavigationArrows={sliderProps?.showNavigationArrows ?? 'always'}
            showPaginationDots={sliderProps?.showPaginationDots ?? 'always'}
            fullWidth={false}
            itemsPerPage={itemsPerPage}
            infinite
          />
        </ProductSummaryListWithoutQuery>
      )}
    </div>
  )
}

export default SuggestedProducts

```


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

[Assistant]
Here's the implementation:

```tsx
import React, { useState, Fragment, useMemo, useRef } from 'react'
import { FormattedMessage, defineMessages } from 'react-intl'
import { IconPlusLines } from 'vtex.styleguide'
import { ProductListContext } from 'vtex.product-list-context'
import { useProduct } from 'vtex.product-context'
import { useTreePath, ExtensionPoint, useRuntime } from 'vtex.render-runtime'
import { FormattedCurrency } from 'vtex.format-currency'
import ProductSummary from 'vtex.product-summary/ProductSummaryCustom'
import { ProductGroupContext } from 'vtex.product-group-context'
import { useCssHandles } from 'vtex.css-handles'
import { usePixel } from 'vtex.pixel-manager/PixelContext'
import { useRecommendation } from 'vtex.recommendation-context/RecommendationContext'

import styles from './styles.css'
import IconEqual from '../../icons/IconEqual'
import ProductSummaryWithActions from './ProductSummaryWithActions'
import { mapSKUItemsToCartItems, sortItemsByLists } from '../../utils'
import { useOnView } from '../../hooks/useOnView'
import { useEvents } from '../../hooks/useEvents'

const { ProductGroupProvider, useProductGroup } = ProductGroupContext

interface Props {
  title?: string
  suggestedProducts?: Product[][]
  BuyButton: React.ComponentType<{ skuItems: CartItem[] }>
}

const { ProductListProvider } = ProductListContext

const messages = defineMessages({
  title: {
    id: 'store/shelf.buy-together.title',
    defaultMessage: '',
  },
  totalProducts: {
    id: 'store/shelf.buy-together.total-products.label',
    defaultMessage: '',
  },
})

const CSS_HANDLES = [
  'buyTogetherContainer',
  'buyTogetherTitleContainer',
  'buyTogetherTitle',
  'totalValue',
]

const notNull = (item: CartItem | null): item is CartItem => item !== null

const BuyTogether: StorefrontFunctionComponent<Props> = ({
  title,
  suggestedProducts,
  BuyButton,
}) => {
  const handles = useCssHandles(CSS_HANDLES)
  const { page } = useRuntime()
  const { push } = usePixel()
  const ref = useRef<HTMLDivElement | null>(null)
  const { product: baseProduct } = useProduct() as any
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  const { items } = useProductGroup()!
  const { treePath } = useTreePath()
  const recommendation = useRecommendation?.()
  const { onView, onProductClick } = useEvents(recommendation, push, page)

  useOnView({
    ref,
    onView: () => onView('buy-together'),
    once: true,
    initializeOnInteraction: true,
  })

  const [suggestedLists, setSuggestedLists] = useState<SuggestedList[]>(
    suggestedProducts?.map(list => {
      return { products: list, hidden: false, current: 0 }
    }) ?? []
  )
  const normalizedBaseProduct = useMemo(
    () => ProductSummary.mapCatalogProductToProductSummary(baseProduct),
    [baseProduct]
  )

  const filteredItems = useMemo(() => {
    const sortedItems = sortItemsByLists(items, suggestedLists)
    return sortedItems.filter((item, index) => {
      if (index === 0) {
        return true
      }
      if (item.quantity === 1) {
        return !suggestedLists[index - 1].hidden
      }
      return !(suggestedLists[index - 1].hidden && suggestedLists[index].hidden)
    })
  }, [items, suggestedLists])

  const treePathList =
    (typeof treePath === 'string' && treePath.split('/')) || []
  const trackingId = treePathList[treePathList.length - 1] || 'BuyTogetherShelf'

  const onChangeProduct = (index: number) => {
    const newSuggestedLists = suggestedLists.map((list, listIndex) => {
      if (listIndex !== index) {
        return list
      }
      const current =
        list.current + 1 < list.products.length ? list.current + 1 : 0
      return { ...list, current }
    })
    setSuggestedLists(newSuggestedLists)
  }

  const onDeleteOrAdd = (index: number) => {
    let newSuggestedLists = []
    if (suggestedLists[index].hidden) {
      newSuggestedLists = suggestedLists.map((list, listIndex) =>
        listIndex !== index ? list : { ...list, hidden: false }
      )
    } else {
      newSuggestedLists = suggestedLists.map((list, listIndex) =>
        listIndex !== index ? list : { ...list, hidden: true }
      )
    }
    setSuggestedLists(newSuggestedLists)
  }

  const cartItems = useMemo(() => {
    return mapSKUItemsToCartItems(filteredItems).filter(notNull)
  }, [filteredItems])

  const totalProducts = cartItems.length
  const totalPrice = useMemo(() => {
    return cartItems.reduce((total: number, currentItem: CartItem) => {
      return total + currentItem.sellingPrice / 100
    }, 0)
  }, [cartItems])

  if (!suggestedLists.length) {
    return null
  }

  return (
    <div className={`flex-none tc ${handles.buyTogetherContainer}`} ref={ref}>
      <div className={`mv4 v-mid ${handles.buyTogetherTitleContainer}`}>
        <span className={handles.buyTogetherTitle}>
          {title && title !== '' ? (
            title
          ) : (
            <FormattedMessage {...messages.title} />
          )}
        </span>
      </div>
      <div className="flex flex-column flex-row-l">
        <ProductListProvider listName={trackingId}>
          <div className="w-100 w-20-l">
            <div className={styles.productSummary} />
            <div>
              <ExtensionPoint
                id="product-summary"
                product={normalizedBaseProduct}
                actionOnClick={() => onProductClick(normalizedBaseProduct)}
              />
            </div>
          </div>
          {suggestedLists.map((suggestedList, index) => {
            const { products, current } = suggestedList
            return (
              <Fragment key={`${products[current]?.productId}-${index}`}>
                <div className="self-center ma5">
                  <IconPlusLines size={20} />
                </div>
                <ProductSummaryWithActions
                  onDeleteOrAdd={onDeleteOrAdd}
                  index={index}
                  hidden={suggestedList.hidden}
                  hideChangeAction={products.length <= 1}
                  onChangeProduct={onChangeProduct}
                  product={products[current]}
                  onProductClick={onProductClick}
                />
              </Fragment>
            )
          })}
          <div className="self-center ma5">
            <IconEqual />
          </div>
          <div className="w-100 mh2 mh6-l w-20-l self-center">
            <div className="mb5">
              <FormattedMessage
                {...messages.totalProducts}
                values={{ total: totalProducts }}
              />
            </div>
            <div className={`mv5 ${handles.totalValue}`}>
              <FormattedCurrency value={totalPrice} />
            </div>
            <BuyButton skuItems={cartItems} />
          </div>
        </ProductListProvider>
      </div>
    </div>
  )
}

const EnhancedBuyTogether: StorefrontFunctionComponent<Props> = props => {
  return (
    <ProductGroupProvider>
      <BuyTogether {...props} />
    </ProductGroupProvider>
  )
}

EnhancedBuyTogether.schema = {
  title: 'admin/editor.buy-together.title',
}

export default EnhancedBuyTogether

```


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

[Assistant]
Here's the implementation:

```css
.productSummary {
  padding-bottom: 3rem;
}

.totalValue {
  font-size: 1.25rem;
  font-weight: 600;
}

.buyTogetherTitle {
  text-transform: uppercase;
  font-weight: 600;
  font-size: 1.25rem;
}

.buyTogetherContainer {
  padding-bottom: 2.5rem;
}

.disabledProduct {
  opacity: 50%;
  pointer-events: none;
}

```


[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 shelf-components/react/components/BuyTogether/ProductSummaryWithActions.tsx from the shelf-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useMemo } from 'react'
import classnames from 'classnames'
import { FormattedMessage, defineMessages } from 'react-intl'
import { ExtensionPoint } from 'vtex.render-runtime'
import ProductSummary from 'vtex.product-summary/ProductSummaryCustom'
import { ButtonWithIcon, IconClear, IconPlusLines } from 'vtex.styleguide'

import IconRefresh from '../../icons/IconRefresh'
import styles from './styles.css'

interface Props {
  product: Product
  index: number
  hidden?: boolean
  hideChangeAction?: boolean
  onDeleteOrAdd: (index: number) => void
  onChangeProduct: (index: number) => void
  onProductClick: (product: Product) => void
}

const messages = defineMessages({
  changeLabel: {
    id: 'store/shelf.buy-together.change.label',
    defaultMessage: '',
  },
  removeLabel: {
    id: 'store/shelf.buy-together.remove.label',
    defaultMessage: '',
  },
  addLabel: {
    id: 'store/shelf.buy-together.add.label',
    defaultMessage: '',
  },
})

const ProductSummaryWithActions: StorefrontFunctionComponent<Props> = ({
  product,
  index,
  hidden,
  hideChangeAction,
  onDeleteOrAdd,
  onChangeProduct,
  onProductClick,
}) => {
  const normalizedProduct = useMemo(
    () => ProductSummary.mapCatalogProductToProductSummary(product),
    [product]
  )

  return (
    <div className="w-100 w-20-l">
      <div className="tc nowrap mb3">
        {!hideChangeAction && (
          <ButtonWithIcon
            icon={<IconRefresh />}
            variation="tertiary"
            onClick={() => onChangeProduct(index)}
          >
            <FormattedMessage {...messages.changeLabel} />
          </ButtonWithIcon>
        )}
        <ButtonWithIcon
          icon={hidden ? <IconPlusLines /> : <IconClear />}
          variation="tertiary"
          onClick={() => onDeleteOrAdd(index)}
        >
          {hidden ? (
            <FormattedMessage {...messages.addLabel} />
          ) : (
            <FormattedMessage {...messages.removeLabel} />
          )}
        </ButtonWithIcon>
      </div>
      <div className={classnames({ [styles.disabledProduct]: hidden })}>
        <ExtensionPoint
          id="product-summary"
          product={normalizedProduct}
          actionOnClick={() => onProductClick(normalizedProduct)}
        />
      </div>
    </div>
  )
}

export default ProductSummaryWithActions

```


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

[Assistant]
Here's the implementation:

```tsx
import React, { useRef } from 'react'
import { ExtensionPoint, useRuntime } from 'vtex.render-runtime'
import { useCssHandles } from 'vtex.css-handles'
import { usePixel } from 'vtex.pixel-manager/PixelContext'
import { useRecommendation } from 'vtex.recommendation-context/RecommendationContext'

import styles from './styles.css'
import { useOnView } from '../../hooks/useOnView'
import { useEvents } from '../../hooks/useEvents'

interface Props {
  title?: string
  products?: Product[]
  isSecondary?: boolean
  secondaryTitle?: string
}

const CSS_HANDLES = ['shelfTitleContainer', 'shelfTitle']

const Shelf: StorefrontFunctionComponent<Props> = ({
  title,
  products,
  isSecondary,
  secondaryTitle,
}) => {
  const handles = useCssHandles(CSS_HANDLES)
  const { page } = useRuntime()
  const { push } = usePixel()
  const ref = useRef<HTMLDivElement | null>(null)
  const recommendation = useRecommendation?.()
  const { onView, onProductClick } = useEvents(recommendation, push, page)

  useOnView({
    ref,
    onView: () => onView('default'),
    once: true,
    initializeOnInteraction: true,
  })

  const currentTitle = isSecondary ? secondaryTitle : title

  return (
    <div className="flex-none tc" ref={ref}>
      {currentTitle && (
        <div className={`mv4 v-mid ${handles.shelfTitleContainer}`}>
          <span className={`${styles.shelfTitle} ${handles.shelfTitle}`}>
            {currentTitle}
          </span>
        </div>
      )}
      {(!products || products.length === 0) && (
        <ExtensionPoint
          id="list-context.product-list"
          actionOnProductClick={onProductClick}
        />
      )}
      {products && products.length > 0 && (
        <ExtensionPoint
          id="list-context.product-list-static"
          products={products}
          actionOnProductClick={onProductClick}
        />
      )}
    </div>
  )
}

Shelf.schema = {
  title: 'admin/editor.shelf.title',
}

export default Shelf

```


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

[Assistant]
Here's the implementation:

```css
.shelfTitle {
  text-transform: uppercase;
  font-weight: 600;
  font-size: 1.25rem;
}

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.video.title": "Video",
  "admin/editor.video.src.title": "URL",
  "admin/editor.video.width.title": "Largura",
  "admin/editor.video.height.title": "Altura",
  "admin/editor.video.autoplay.title": "Reproduzir automaticamente",
  "admin/editor.video.loop.title": "Reproduzir em loop",
  "admin/editor.video.playsInline.title": "Reproduzir in-line (em dispositivos móveis)",
  "admin/editor.video.poster.title": "URL da imagem de thumbnail",
  "admin/editor.video.poster.description": "Imagem a ser exibida quando o video ainda não estiver disponível",
  "admin/editor.video.name.title": "Nome",
  "admin/editor.video.name.description": "Nome do video para SEO e acessibilidade",
  "admin/editor.video.description.title": "Descrição",
  "admin/editor.video.description.description": "Descrição do video para SEO e acessibilidade",
  "admin/editor.video.uploadDate.title": "Data de upload",
  "admin/editor.video.uploadDate.description": "Data de upload do video para SEO, no formato `aaaa-mm-dd hh:mm:ss`",
  "admin/editor.video.fallbackImageUrl.title": "Fallback Image URL",
  "admin/editor.video.fallbackImageUrl.description": "URL da imagem a ser exibida caso o video não esteja disponível"
}

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.video.title": "Video",
  "admin/editor.video.src.title": "URL",
  "admin/editor.video.width.title": "Width",
  "admin/editor.video.height.title": "Height",
  "admin/editor.video.autoplay.title": "Play automatically",
  "admin/editor.video.loop.title": "Play in loop",
  "admin/editor.video.playsInline.title": "Play inline (in mobile devices)",
  "admin/editor.video.poster.title": "Thumbnail image URL",
  "admin/editor.video.poster.description": "Image to be displayed when video is not available yet",
  "admin/editor.video.name.title": "Name",
  "admin/editor.video.name.description": "Video name for SEO and accessibility",
  "admin/editor.video.description.title": "Description",
  "admin/editor.video.description.description": "Video resume for SEO and accessibility",
  "admin/editor.video.uploadDate.title": "Upload date",
  "admin/editor.video.uploadDate.description": "Video upload date for SEO, in the format `yyyy-mm-dd hh:mm:ss`",
  "admin/editor.video.fallbackImageUrl.title": "Fallback Image URL",
  "admin/editor.video.fallbackImageUrl.description": "Video fallback image URL to be displayed if the video is not available"
}

```


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

[User] Show me the implementation of store-video/messages/context.json from the store-video VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.video.title": "Video",
  "admin/editor.video.src.title": "URL",
  "admin/editor.video.width.title": "Width",
  "admin/editor.video.height.title": "Height",
  "admin/editor.video.autoplay.title": "Play automatically",
  "admin/editor.video.loop.title": "Play in loop",
  "admin/editor.video.playsInline.title": "Play inline (in mobile devices)",
  "admin/editor.video.poster.title": "Thumbnail image URL",
  "admin/editor.video.poster.description": "Image to be displayed when video is not available yet",
  "admin/editor.video.name.title": "Name",
  "admin/editor.video.name.description": "Video name for SEO and accessibility",
  "admin/editor.video.description.title": "Description",
  "admin/editor.video.description.description": "Video resume for SEO and accessibility",
  "admin/editor.video.uploadDate.title": "Upload date",
  "admin/editor.video.uploadDate.description": "Video upload date for SEO, in the format `yyyy-mm-dd hh:mm:ss`",
  "admin/editor.video.fallbackImageUrl.title": "Fallback Image URL",
  "admin/editor.video.fallbackImageUrl.description": "Video fallback image URL to be displayed if the video is not available"
}

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.video.title": "Video",
  "admin/editor.video.src.title": "URL",
  "admin/editor.video.width.title": "Anchura",
  "admin/editor.video.height.title": "Altura",
  "admin/editor.video.autoplay.title": "Jugar automáticamente",
  "admin/editor.video.loop.title": "Reproducción en bucle",
  "admin/editor.video.playsInline.title": "Reproducción intercalada (en dispositivos móviles)",
  "admin/editor.video.poster.title": "URL de la imagen en Thumbnail",
  "admin/editor.video.poster.description": "Imagen que se mostrará cuando el video aún no esté disponible",
  "admin/editor.video.name.title": "Nombre",
  "admin/editor.video.name.description": "Nombre del video para SEO y accesibilidad",
  "admin/editor.video.description.title": "Descripción",
  "admin/editor.video.description.description": "Video descripción para SEO y accesibilidad",
  "admin/editor.video.uploadDate.title": "Fecha de carga",
  "admin/editor.video.uploadDate.description": "Video fecha para SEO, en el formato `aaaa-mm-dd hh:mm:ss`",
  "admin/editor.video.fallbackImageUrl.title": "Fallback Image URL",
  "admin/editor.video.fallbackImageUrl.description": "URL de la imagen de respaldo del video que se mostrará si el video no está disponible"
}

```


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

[User] Show me the implementation of store-video/docs/README.md from the store-video VTEX app.

[Assistant]
Here's the implementation:

```markdown
📢 Use this project, [contribute](https://github.com/vtex-apps/store-video) to it or open issues to help evolve it using [Store Discussion](https://github.com/vtex-apps/store-discussion).

# Video

<!-- DOCS-IGNORE:start -->
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)

<!-- ALL-CONTRIBUTORS-BADGE:END -->
<!-- DOCS-IGNORE:end -->

The Video app allows you to display video assets on your store pages.

![Media Placeholder](https://storecomponents.vtexassets.com/arquivos/ids/155640)

## Configuration

1. Add the `store-video` app to your theme's dependencies in the `manifest.json` file:

```diff
 "dependencies ": {
+  "vtex.store-video": "1.x"
 }
```

2. In any desired theme template, add the `video` block with the desirable props. For example:

```json
  "video#background": {
    "props": {
      "width": "100%",
      "height": "600px",
      "loop": false,
      "autoPlay": true,
      "muted": false,
      "src": "https://www.youtube.com/watch?v=wygFqZXMIco",
      "blockClass": "videoEl"
    }
  }
```

### `video` props

| Prop name        | Type                 | Description                                                                                | Default value     |
| ---------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- |
| `name`           | `string`             | Video name for SEO and accessibility.                                                                                | `undefined`       |
| `description`    | `string`             | Video description for SEO and accessibility.                                                                                | `undefined`       |
| `src`            | `string`             | Video URL. It can be a `youtube` URL, `vimeo` URL or a self-hosted video URL.                                                                                | `undefined`       |
| `type`           | `string`             | Video type.                                                                                | `undefined`       |
| `poster`         | `string`             | Cover image URL to be displayed before the video playback.                                                                                | `undefined`       |
| `controlsType`   | `enum`               | The type of controls. It can be `custom-vtex`( only works if the video URL represents a HTML5 player ), `native` or `none`.                                                                            | `undefined`       |
| `autoPlay`       | `boolean`            | Whether the video will start automatically after loaded(`true`) or not(`false`). Note that if the value is `true`, the muted property will automatically be setted to `true`.                          | `false`           |
| `muted`          | `boolean`            | Whether the video will start with the audio on(`false`) or not(`true`).                                                                                | `false`           |
| `loop`           | `boolean`            | Whether the video will run in a loop(`true`) or not(`false`).                                                                                | `false`           |
| `playsInline`           | `boolean`            | Whether the video will play inline(`true`) or not(`false`).                                                                                | `false`           |
| `width`          | `number` or `string` | The width of the video exhibition area. It could be as %(`string`) or pixels(`number`).                                                                                | `undefined`       |
| `height`         | `number` or `string` | The height of the video exhibition area. It could be as %(`string`) or pixels(`number`).                                                                                | `undefined`       |
| `PlayIcon`       | `string`             | Video play icon for `custom-vtex` controls.                                                                                | `icon-play`       |
| `PauseIcon`      | `string`             | Video pause icon for `custom-vtex` controls.                                                                                | `icon-pause`      |
| `VolumeOnIcon`   | `string`             | Video volume on icon for `custom-vtex` controls.                                                                                | `icon-volume-on`  |
| `VolumeOffIcon`  | `string`             | Video volume off icon for `custom-vtex` controls.                                                                                | `icon-volume-off` |
| `FullscreenIcon` | `string`             | Video fullscreen icon for `custom-vtex` controls.                                                                                | `icon-extend`     |
| `classes`        | `CustomCSSClasses`   | Used to override default CSS handles. To better understand how this prop works, we recommend reading about it here. Note that this is only useful if you're importing this block as a React component. | `undefined`       |

Use the **admin's Site Editor** to manage some props declared in the `video` block.

## Customization

In order to apply CSS customizations in this and other blocks, follow the instructions given in the recipe on [Using CSS Handles for store customization](https://vtex.io/docs/recipes/style/using-css-handles-for-store-customization).

| CSS Handles         |
| ------------------- |
| `controlsContainer` |
| `fallbackContainer` |
| `fallbackImage`     |
| `fullscreenButton`  |
| `playButton`        |
| `trackBar`          |
| `trackContainer`    |
| `trackTimer`        |
| `videoContainer`    |
| `videoElement`      |
| `volumeContainer`   |
| `volumeButton`      |
| `volumeSlider`      |

:warning: \_It's required that `controlsType` prop is set as `custom-vtex` in order to have the following CSS Handles properly working: `controlsContainer`, `fullscreenButton`, `playButton`, `trackContainer`, `trackTimer`, `trackBar`, `volumeContainer`, `volumeSlider`, and `volumeButton`.

<!-- DOCS-IGNORE:start -->

## Contributors ✨

Thanks goes to these wonderful people:

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome!

<!-- DOCS-IGNORE:end -->

```


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

[User] Show me the implementation of store-video/react/useVideoTrack.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { RefObject } from 'react'
import { useState, useEffect } from 'react'

interface State {
  currentTime?: number
  duration?: number
}

export const formatTime = (timeInSeconds: number) => {
  let seconds: string = Math.floor(timeInSeconds % 60).toString()

  if (seconds.length === 1) {
    seconds = `0${seconds}`
  }

  const minutes: number = Math.floor((timeInSeconds / 60) % 60)
  const hours: number = Math.floor(timeInSeconds / 3600)

  if (hours) {
    return `${hours}:${minutes}:${seconds}`
  }

  return `${minutes}:${seconds}`
}

export const useVideoTrack = (
  videoRef: RefObject<HTMLVideoElement>,
  trackBarRef: RefObject<HTMLInputElement>
) => {
  const [state, setState] = useState<State>({})
  const currentVideoRef = videoRef?.current
  const currentTrackBarRef = trackBarRef?.current

  const updateMetaData = () => {
    setState({ currentTime: 0, duration: currentVideoRef?.duration })
  }

  const updateCurrentTime = () => {
    if (currentTrackBarRef) {
      currentTrackBarRef.value = `${currentVideoRef?.currentTime}`
    }

    return setState({ ...state, currentTime: currentVideoRef?.currentTime })
  }

  const setCurrentTime = (timeInSeconds: number) => {
    if (!currentVideoRef) return

    currentVideoRef.currentTime = timeInSeconds
  }

  useEffect(() => {
    currentVideoRef?.addEventListener('loadedmetadata', updateMetaData)
    currentVideoRef?.addEventListener('timeupdate', updateCurrentTime)

    return () => {
      currentVideoRef?.removeEventListener('loadedmetadata', updateMetaData)
      currentVideoRef?.removeEventListener('timeupdate', updateCurrentTime)
    }
    // Disabling because it is necessary to define some event listeners only when video ref is available
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentVideoRef])

  return {
    duration: currentVideoRef?.duration,
    currentTime: state.currentTime,
    setCurrentTime,
    formatTime,
  }
}

export default useVideoTrack

```


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

[User] Show me the implementation of store-video/react/VideoTypes.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { VideoHTMLAttributes, ElementType } from 'react'
import type { CssHandlesTypes } from 'vtex.css-handles'

import type { CSS_HANDLES } from './players/HTML5Player'

export interface VideoPlayer extends VideoHTMLAttributes<HTMLVideoElement> {
  name?: string
  description?: string
  src: string
  fallbackImageUrl?: string
  type?: string
  controlsType?: string
  IconPlay?: ElementType<unknown>
  IconPause?: ElementType<unknown>
  IconFullscreen?: ElementType<unknown>
  IconVolumeOn?: ElementType<unknown>
  IconVolumeOff?: ElementType<unknown>
  uploadDate?: string
  classes?: CssHandlesTypes.CustomClasses<typeof CSS_HANDLES>
}

```


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

[User] Show me the implementation of store-video/react/useVideo.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { RefObject } from 'react'
import { useState, useEffect } from 'react'

interface State {
  isMuted: boolean | null
  isPlaying: boolean | null
  networkStatus: string | null
  volume: number | undefined
}

const NETWORK_STATUS: string[] = [
  'NETWORK_EMPTY',
  'NETWORK_IDLE',
  'NETWORK_LOADING',
  'NETWORK_NO_SOURCE',
]

const useVideo = (
  videoRef: RefObject<HTMLVideoElement>,
  containerRef: RefObject<HTMLDivElement>
) => {
  const currentVideoRef = videoRef?.current
  const currentContainerRef = containerRef?.current
  const [state, setState] = useState<State>({
    isMuted: null,
    isPlaying: null,
    volume: undefined,
    networkStatus: null,
  })

  const play = () => {
    currentVideoRef?.play()

    setState({ ...state, isPlaying: currentVideoRef?.paused === false })
  }

  const pause = () => {
    currentVideoRef?.pause()

    if (currentVideoRef?.autoplay) currentVideoRef.autoplay = false

    setState({ ...state, isPlaying: currentVideoRef?.paused === false })
  }

  const setVolume = (volume: number) => {
    if (!currentVideoRef) return

    if (volume < 0 || volume > 1) return

    currentVideoRef.volume = volume

    setState({ ...state, volume })
  }

  const toggleMute = () => {
    if (!currentVideoRef) return

    currentVideoRef.muted = !currentVideoRef.muted

    setState({ ...state, isMuted: currentVideoRef.muted })
  }

  const toggleFullscreenMode = () => {
    if (document.fullscreenElement) {
      return document.exitFullscreen()
    }

    return currentContainerRef?.requestFullscreen()
  }

  const changeState = (property: string, value: unknown) => {
    setState({ ...state, [property]: value })
  }

  const setNetworkStatus = (networkStatus: string): void => {
    setState({ ...state, networkStatus })
  }

  const getNetworkStatus = (): string => {
    return NETWORK_STATUS[currentVideoRef?.networkState ?? 0]
  }

  const handleEnd = () => {
    setState({ ...state, isPlaying: false })
  }

  useEffect(() => {
    currentVideoRef?.addEventListener('ended', handleEnd)

    setState({
      ...state,
      isMuted: currentVideoRef?.muted === true,
      isPlaying:
        Boolean(currentVideoRef?.autoplay) || currentVideoRef?.paused === false,
      networkStatus: getNetworkStatus(),
    })

    return () => {
      currentVideoRef?.removeEventListener('ended', handleEnd)
    }
    // Disabling because it is necessary to set some default values only when video ref is available
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentVideoRef])

  return {
    isPlaying: state.isPlaying,
    isMuted: state.isMuted,
    volume: state.volume,
    networkStatus: state.networkStatus,
    hasEnded: currentVideoRef?.ended,
    play,
    pause,
    setVolume,
    setNetworkStatus,
    toggleMute,
    toggleFullscreenMode,
    changeState,
  }
}

export default useVideo

```


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

[User] Show me the implementation of store-video/react/Video.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { jsonLdScriptProps } from 'react-schemaorg'

import type { VideoPlayer } from './VideoTypes'
import VimeoPlayer from './players/VimeoPlayer'
import HTML5Player, { CSS_HANDLES } from './players/HTML5Player'
import YoutubePlayer from './players/YoutubePlayer'

const VIMEO_PATTERN = /vimeo/
const YOUTUBE_PATTERN = /youtube|youtu.be/

function Video(props: VideoPlayer) {
  const { src, name, description, poster, uploadDate } = props
  const isVimeo = VIMEO_PATTERN.test(src)
  const isYoutube = YOUTUBE_PATTERN.test(src)

  const StructuredData = () => {
    return (
      <script
        {...jsonLdScriptProps({
          '@context': 'http://schema.org',
          '@type': 'VideoObject',
          name,
          description,
          contentUrl: src,
          uploadDate: uploadDate ? new Date(uploadDate) : undefined,
          thumbnailUrl: poster ?? src,
        })}
      />
    )
  }

  let Player = HTML5Player

  if (isVimeo) {
    Player = VimeoPlayer
  }

  if (isYoutube) {
    Player = YoutubePlayer
  }

  return (
    <>
      <StructuredData />
      <Player {...props} />
    </>
  )
}

Video.schema = {
  title: 'admin/editor.video.title',
}

Video.cssHandles = CSS_HANDLES

export default Video

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "scripts": {
    "test": "vtex-test-tools test"
  },
  "dependencies": {
    "apollo-client": "^2.6.8",
    "react": "^16.12.0",
    "react-apollo": "^3.1.3",
    "react-dom": "^16.12.0",
    "react-intl": "^3.12.0",
    "react-schemaorg": "^1.3.1"
  },
  "devDependencies": {
    "@apollo/react-testing": "^3.1.3",
    "@types/jest": "^25.1.4",
    "@types/node": "^13.9.8",
    "@types/react": "^16.9.31",
    "@vtex/test-tools": "^3.1.0",
    "@vtex/tsconfig": "^0.4.4",
    "apollo-cache-inmemory": "^1.6.5",
    "graphql": "^14.6.0",
    "typescript": "3.9.7",
    "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@1.0.0/public/@types/vtex.css-handles",
    "vtex.native-types": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.native-types@0.7.5/public/@types/vtex.native-types",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.126.4/public/@types/vtex.render-runtime",
    "vtex.store-icons": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-icons@0.18.0/public/@types/vtex.store-icons",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.134.1/public/@types/vtex.styleguide"
  },
  "version": "1.4.3"
}

```


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

[User] Show me the implementation of store-video/react/HandlesContext.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

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

import { CSS_HANDLES } from './players/HTML5Player'

const { CssHandlesProvider, useContextCssHandles } = createCssHandlesContext(
  CSS_HANDLES
)

export {
  CssHandlesProvider as VideoHandlesProvider,
  useContextCssHandles as useVideoHandles,
}

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "definitions": {
    "Video": {
      "type": "object",
      "properties": {
        "name": {
          "title": "admin/editor.video.name.title",
          "description": "admin/editor.video.name.description",
          "type": "string"
        },
        "description": {
          "title": "admin/editor.video.description.title",
          "description": "admin/editor.video.description.description",
          "type": "string"
        },
        "width": {
          "title": "admin/editor.video.width.title",
          "type": "string"
        },
        "height": {
          "title": "admin/editor.video.height.title",
          "type": "string"
        },
        "src": {
          "title": "admin/editor.video.src.title",
          "type": "string"
        },
        "autoPlay": {
          "title": "admin/editor.video.autoplay.title",
          "type": "boolean",
          "default": false
        },
        "loop": {
          "title": "admin/editor.video.loop.title",
          "type": "boolean",
          "default": false
        },
        "playsInline": {
          "title": "admin/editor.video.playsInline.title",
          "type": "boolean",
          "default": false
        },
        "poster": {
          "title": "admin/editor.video.poster.title",
          "description": "admin/editor.video.poster.description",
          "type": "string"
        },
        "uploadDate": {
          "title": "admin/editor.video.uploadDate.title",
          "description": "admin/editor.video.uploadDate.description",
          "type": "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 store-video/store/interfaces.json from the store-video VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "video": {
    "component": "Video",
    "content": {
      "$ref": "app:vtex.store-video#/definitions/Video"
    }
  }
}

```


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

[User] Show me the implementation of store-video/react/players/YoutubePlayer.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

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

import type { VideoPlayer } from '../VideoTypes'

const CSS_HANDLES = ['videoContainer', 'videoElement'] as const

// https://regex101.com/r/CWmgOb/1
const YOUTUBE_REGEX = /(?:youtube(?:-nocookie)?\.com\/(?:[^/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/

function YoutubePlayer({
  width,
  height,
  autoPlay,
  controlsType,
  loop,
  playsInline,
  muted,
  src,
  description,
  classes,
}: VideoPlayer) {
  const { handles } = useCssHandles(CSS_HANDLES, { classes })
  const matchedSrc = src.match(YOUTUBE_REGEX)
  const videoId = matchedSrc?.[1]
  const params = `autoplay=${autoPlay}&loop=${
    loop ? `1&playlist=${videoId}` : '0'
  }&enablejsapi=1&iv_load_policy=3&modestbranding=1&rel=0&controls=${
    controlsType === 'none' ? 0 : 1
  }&playsinline=${playsInline ? '1' : '0'}&mute=${
    autoPlay || muted ? '1' : '0'
  }`

  return (
    <div className={`relative ${handles.videoContainer}`}>
      <iframe
        data-testid="youtube-player"
        width={width}
        height={height}
        title={description}
        className={`${handles.videoElement}`}
        src={`https://www.youtube.com/embed/${videoId}?${params}`}
        frameBorder="0"
        allowFullScreen
        allow="autoplay"
      />
    </div>
  )
}

export default YoutubePlayer

```


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

[User] Show me the implementation of store-video/react/players/HTML5Player.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable jsx-a11y/media-has-caption */
import React, { useRef } from 'react'
import { useCssHandles } from 'vtex.css-handles'

import useVideo from '../useVideo'
import styles from '../styles/styles.css'
import type { VideoPlayer } from '../VideoTypes'
import PlayButton, {
  CSS_HANDLES as PLAY_BUTTON_CSS_HANDLES,
} from '../controls/PlayButton'
import FullscreenButton, {
  CSS_HANDLES as FULLSCREEN_BUTTON_CSS_HANDLES,
} from '../controls/FullscreenButton'
import TrackControl, {
  CSS_HANDLES as TRACK_CONTROL_CSS_HANDLES,
} from '../controls/TrackControl'
import VolumeControl, {
  CSS_HANDLES as VOLUME_CONTROL_CSS_HANDLES,
} from '../controls/VolumeControl'
import { useVideoHandles, VideoHandlesProvider } from '../HandlesContext'

export const CSS_HANDLES = [
  'videoContainer',
  'videoElement',
  'fallbackContainer',
  'fallbackImage',
  'controlsContainer',
  ...PLAY_BUTTON_CSS_HANDLES,
  ...TRACK_CONTROL_CSS_HANDLES,
  ...FULLSCREEN_BUTTON_CSS_HANDLES,
  ...VOLUME_CONTROL_CSS_HANDLES,
] as const

interface FallbackImageProps {
  imageUrl?: string
  description?: string
}

function FallbackImage({
  imageUrl = 'https://storecomponents.vtexassets.com/arquivos/ids/155639',
  description,
}: FallbackImageProps) {
  const { handles } = useVideoHandles()

  return (
    <div className={`${handles.fallbackContainer}`}>
      <img
        className={`w-100 h-100 ${handles.fallbackImage}`}
        src={imageUrl}
        alt={description}
      />
    </div>
  )
}

function HTML5Player({
  src,
  type,
  width,
  height,
  autoPlay,
  playsInline,
  loop,
  poster,
  controlsType,
  description,
  muted,
  IconPlay,
  IconPause,
  IconFullscreen,
  IconVolumeOn,
  IconVolumeOff,
  classes,
  ...props
}: VideoPlayer) {
  const { handles, withModifiers } = useCssHandles(CSS_HANDLES, { classes })
  const videoRef = useRef<HTMLVideoElement>(null)
  const containerRef = useRef<HTMLDivElement>(null)
  const {
    isPlaying,
    isMuted,
    networkStatus,
    volume,
    play,
    pause,
    setVolume,
    toggleMute,
    toggleFullscreenMode,
    changeState,
  } = useVideo(videoRef, containerRef)

  const hasCustomControls = controlsType === 'custom-vtex'
  const hasNativeControls = controlsType === 'native'

  if (hasCustomControls && networkStatus === 'NETWORK_NO_SOURCE') {
    return <FallbackImage imageUrl={poster} description={description} />
  }

  return (
    <VideoHandlesProvider withModifiers={withModifiers} handles={handles}>
      <div
        ref={containerRef}
        aria-label={description}
        style={{ width, height }}
        className={`relative ${handles.videoContainer} ba b--gray`}
      >
        <video
          {...props}
          ref={videoRef}
          data-testid="html5-player"
          className={`${handles.videoElement} ${styles.videoElement} w-100 h-100`}
          poster={poster}
          loop={loop}
          autoPlay={autoPlay}
          controls={hasNativeControls}
          muted={autoPlay ? true : muted}
          playsInline={autoPlay ? true : playsInline}
        >
          <source src={src} type={type && `video/${type}`} />

          {<FallbackImage imageUrl={poster} description={description} />}
        </video>

        {videoRef && hasCustomControls && (
          <div
            data-testid="controls-container"
            className={`${handles.controlsContainer} absolute left-2 right-2 lh-copy`}
          >
            <>
              <PlayButton
                isPlaying={isPlaying}
                IconPlay={IconPlay}
                IconPause={IconPause}
                play={play}
                pause={pause}
              />
              <TrackControl videoRef={videoRef} changeState={changeState} />
              <FullscreenButton
                IconFullscreen={IconFullscreen}
                toggleFullscreenMode={toggleFullscreenMode}
              />
              <VolumeControl
                IconVolumeOn={IconVolumeOn}
                IconVolumeOff={IconVolumeOff}
                isMuted={isMuted}
                volume={volume}
                setVolume={setVolume}
                toggleMute={toggleMute}
              />
            </>
          </div>
        )}
      </div>
    </VideoHandlesProvider>
  )
}

export default HTML5Player

```


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

[User] Show me the implementation of store-video/react/players/VimeoPlayer.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

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

import type { VideoPlayer } from '../VideoTypes'

const CSS_HANDLES = ['videoContainer', 'videoElement'] as const

// https://regex101.com/r/wdKKHO/1
const VIMEO_REGEX = /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/

function VimeoPlayer({
  width,
  height,
  autoPlay,
  loop,
  playsInline,
  src,
  description,
  classes,
}: VideoPlayer) {
  const { handles } = useCssHandles(CSS_HANDLES, { classes })
  const params = `autoplay=${autoPlay}&loop=${loop}&playsinline=${Boolean(
    playsInline
  )}&enablejsapi=1&iv_load_policy=3&modestbranding=1`

  const matchedSrc = src.match(VIMEO_REGEX)
  const videoId = matchedSrc?.[5]

  return (
    <div className={`relative ${handles.videoContainer}`}>
      <iframe
        data-testid="vimeo-player"
        width={width}
        height={height}
        title={description}
        className={`${handles.videoElement}`}
        src={`https://player.vimeo.com/video/${videoId}?${params}`}
        frameBorder="0"
        allowFullScreen
        allow="autoplay"
      />
    </div>
  )
}

export default VimeoPlayer

```


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

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

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  const css: CSSHandles
  export default css
}

```


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

[User] Show me the implementation of store-video/react/controls/TrackControl.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { RefObject, ChangeEvent } from 'react'
import React, { useRef } from 'react'

import { useVideoHandles } from '../HandlesContext'
import useVideoTrack, { formatTime } from '../useVideoTrack'

export const CSS_HANDLES = ['trackContainer', 'trackTimer', 'trackBar'] as const

export interface TrackControlProps {
  videoRef: RefObject<HTMLVideoElement>
  changeState: (property: string, value: unknown) => void
}

function TrackControl({ videoRef, changeState }: TrackControlProps) {
  const { handles } = useVideoHandles()
  const trackBarRef = useRef<HTMLInputElement>(null)
  const { duration, currentTime, setCurrentTime } = useVideoTrack(
    videoRef,
    trackBarRef
  )

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    if (videoRef?.current?.ended) {
      changeState('isPlaying', false)
    }

    setCurrentTime(parseFloat(event?.target?.value))
  }

  return (
    <div
      className={`${handles.trackContainer} w-100 absolute bottom-1 lh-title`}
    >
      {currentTime != null && duration != null && (
        <span className={`${handles.trackTimer} ml7 white v-mid`}>
          {`${formatTime(currentTime)} / ${formatTime(duration)}`}
        </span>
      )}

      <input
        ref={trackBarRef}
        onChange={handleChange}
        className={`${handles.trackBar} w-100 v-mid`}
        type="range"
        min={0}
        step={0.05}
        defaultValue="0"
        max={duration ?? 0}
      />
    </div>
  )
}

export default TrackControl

```


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

[User] Show me the implementation of store-video/react/controls/FullscreenButton.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ElementType } from 'react'
import React from 'react'
import { IconExpand as DefaultIconFullscreen } from 'vtex.store-icons'

import { useVideoHandles } from '../HandlesContext'
import styles from '../styles/styles.css'

export const CSS_HANDLES = ['fullscreenButton'] as const
export interface FullscreenButtonProps {
  toggleFullscreenMode: () => void
  IconFullscreen?: ElementType
}

function FullscreenButton({
  toggleFullscreenMode,
  IconFullscreen,
}: FullscreenButtonProps) {
  const { handles } = useVideoHandles()

  return (
    <button
      className={`${handles.fullscreenButton} ${styles.button} ml4 absolute bottom-2 right-0`}
      onClick={toggleFullscreenMode}
    >
      {IconFullscreen ? <IconFullscreen /> : <DefaultIconFullscreen />}
    </button>
  )
}

export default FullscreenButton

```


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

[User] Show me the implementation of store-video/react/controls/PlayButton.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ElementType } from 'react'
import React from 'react'
import {
  IconPlay as DefaultIconPlay,
  IconPause as DefaultIconPause,
} from 'vtex.store-icons'

import { useVideoHandles } from '../HandlesContext'
import styles from '../styles/styles.css'

export const CSS_HANDLES = ['playButton'] as const

export interface PlayButtonProps {
  isPlaying: boolean | null
  play: () => void
  pause: () => void
  IconPlay?: ElementType<unknown>
  IconPause?: ElementType<unknown>
}

function PlayButton({
  isPlaying,
  IconPlay,
  IconPause,
  play,
  pause,
}: PlayButtonProps) {
  const { handles } = useVideoHandles()

  const PauseIcon = IconPause ? (
    <IconPause />
  ) : (
    DefaultIconPause && <DefaultIconPause />
  )

  const PlayIcon = IconPlay ? (
    <IconPlay />
  ) : (
    DefaultIconPlay && <DefaultIconPlay />
  )

  return (
    <button
      className={`${handles.playButton} ${styles.button} absolute bottom-2 left-0 v-mid z-1`}
      onClick={() => (isPlaying ? pause() : play())}
    >
      {isPlaying ? PauseIcon : PlayIcon}
    </button>
  )
}

export default PlayButton

```


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

[User] Show me the implementation of store-video/react/controls/VolumeControl.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ElementType, MouseEventHandler } from 'react'
import React, { useState } from 'react'
import {
  IconVolumeOff as DefaultIconVolumeOff,
  IconVolumeOn as DefaultIconVolumeOn,
} from 'vtex.store-icons'

import { useVideoHandles } from '../HandlesContext'
import styles from '../styles/styles.css'

export const CSS_HANDLES = [
  'volumeContainer',
  'volumeSlider',
  'volumeButton',
] as const

export interface VolumeControlProps {
  setVolume: (volume: number) => void
  toggleMute: MouseEventHandler<HTMLButtonElement>
  isMuted: boolean | null
  volume: number | undefined
  IconVolumeOn?: ElementType<unknown>
  IconVolumeOff?: ElementType<unknown>
}

function VolumeControl({
  setVolume,
  toggleMute,
  isMuted,
  volume,
  IconVolumeOn,
  IconVolumeOff,
}: VolumeControlProps) {
  const { handles } = useVideoHandles()

  const [showVolumeSlider, setShowVolumeSlider] = useState(false)

  const VolumeOffIcon = IconVolumeOff ? (
    <IconVolumeOff />
  ) : (
    DefaultIconVolumeOff && <DefaultIconVolumeOff />
  )

  const VolumeOnIcon = IconVolumeOn ? (
    <IconVolumeOn />
  ) : (
    DefaultIconVolumeOn && <DefaultIconVolumeOn />
  )

  return (
    <div
      className={`${handles.volumeContainer} dib absolute bottom-2 right-2`}
      onMouseEnter={() => setShowVolumeSlider(true)}
      onMouseLeave={() => setShowVolumeSlider(false)}
    >
      {showVolumeSlider && (
        <input
          className={`${handles.volumeSlider} ${styles.volumeSlider} v-mid`}
          disabled={isMuted === true}
          type="range"
          defaultValue={volume && volume * 10}
          min={0}
          max={10}
          onChange={(event) => setVolume(parseInt(event.target.value, 10) / 10)}
        />
      )}

      <button
        onClick={toggleMute}
        className={`${handles.volumeButton} ${styles.button} v-mid ml4`}
      >
        {isMuted ? VolumeOffIcon : VolumeOnIcon}
      </button>
    </div>
  )
}

export default VolumeControl

```


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

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

[Assistant]
Here's the implementation:

```css
.videoContainer:fullscreen .videoElement {
  width: 100%;
  height: 100%;
}

.videoElement {
  object-fit: cover;
}

.volumeSlider {
  width: 5rem;
  margin-top: 0;
}

.button {
  border: none;
  background: none;
  color: white;
  cursor: pointer;
}

.button:fullscreen {
  position: fixed;
}

```


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

[User] Show me the implementation of store-video/react/__tests__/Video.test.tsx from the store-video VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import type { RefObject } from 'react'
import { renderHook, render } from '@vtex/test-tools/react'
import { act } from 'react-test-renderer'

import Video from '../Video'
import useVideoTrack from '../useVideoTrack'
import useVideo from '../useVideo'

describe('Video rendering', () => {
  it.todo('should show a loading when the network status is not ready')

  it('should render youtube player correctly', () => {
    const { queryByTestId } = render(
      <Video src="https://www.youtube.com/watch?v=hT0OMD11b0A" />
    )

    expect(queryByTestId('html5-player')).not.toBeInTheDocument()
    expect(queryByTestId('vimeo-player')).not.toBeInTheDocument()
    expect(queryByTestId('youtube-player')).toBeInTheDocument()
  })

  it('should render vimeo player correctly', () => {
    const { queryByTestId } = render(<Video src="https://vimeo.com/89404519" />)

    expect(queryByTestId('html5-player')).not.toBeInTheDocument()
    expect(queryByTestId('vimeo-player')).toBeInTheDocument()
    expect(queryByTestId('youtube-player')).not.toBeInTheDocument()
  })

  it('should have native controls if controlsType is `native`', () => {
    const { queryByTestId } = render(
      <Video controlsType="native" src="vtex.mp4" />
    )

    expect(queryByTestId('html5-player')).toHaveAttribute('controls')
    expect(queryByTestId('controls-container')).not.toBeInTheDocument()
  })

  it('should have custom controls if controlsType is `vtex`', () => {
    const { queryByTestId } = render(
      <Video controlsType="custom-vtex" src="vtex.mp4" />
    )

    expect(queryByTestId('html5-player')).not.toHaveAttribute('controls')
    expect(queryByTestId('controls-container')).toBeInTheDocument()
  })
})

describe('Video logic', () => {
  let video: HTMLVideoElement
  let container: HTMLDivElement
  let videoRef: RefObject<HTMLVideoElement>
  let containerRef: RefObject<HTMLDivElement>

  beforeEach(() => {
    video = document.createElement('video')
    container = document.createElement('div')
    container.appendChild(video)
    videoRef = { current: video }
    containerRef = { current: container }
  })

  it('should change the volume of the video', () => {
    const {
      result: {
        current: { toggleMute, setVolume },
      },
    } = renderHook(() => useVideo(videoRef, containerRef))

    act(() => {
      setVolume(0.5)
    })

    expect(videoRef?.current?.muted).toBe(false)
    expect(videoRef?.current?.volume).toBe(0.5)

    act(() => {
      setVolume(0.3)
      toggleMute()
    })

    expect(videoRef?.current?.muted).toBe(true)
    expect(videoRef?.current?.volume).toBe(0.3)

    act(() => {
      setVolume(30)
    })

    expect(videoRef?.current?.volume).toBe(0.3)
  })

  it('should pause the video', () => {
    jest.spyOn(window.HTMLMediaElement.prototype, 'pause').mockImplementation()

    Object.defineProperty(HTMLMediaElement.prototype, 'paused', {
      get() {
        return true
      },
    })

    const {
      result: {
        current,
        current: { pause },
      },
    } = renderHook(() => useVideo(videoRef, containerRef))

    act(() => {
      pause()
    })

    expect(videoRef?.current?.pause).toHaveBeenCalled()
    expect(current.isPlaying).toBeFalsy()
  })

  it('should play the video', () => {
    jest
      .spyOn(window.HTMLMediaElement.prototype, 'play')
      .mockImplementation(() => Promise.resolve())

    Object.defineProperty(HTMLMediaElement.prototype, 'paused', {
      get() {
        return false
      },
    })

    const {
      result: {
        current,
        current: { play, pause },
      },
    } = renderHook(() => useVideo(videoRef, containerRef))

    act(() => {
      play()
      pause()
      play()
    })

    expect(videoRef?.current?.play).toHaveBeenCalled()
    expect(current.isPlaying).toBeTruthy()
  })

  it('should get the track timer with the right format', () => {
    const trackBar: HTMLInputElement = document.createElement('input')
    const trackBarRef = { current: trackBar }

    const {
      result: {
        current: { formatTime = () => {} },
      },
    } = renderHook(() => useVideoTrack(videoRef, trackBarRef))

    expect(formatTime(7)).toBe('0:07')
    expect(formatTime(30)).toBe('0:30')
    expect(formatTime(120)).toBe('2:00')
    expect(formatTime(10300)).toBe('2:51:40')
  })

  it('should update the current time when video is playing', () => {
    const trackBar: HTMLInputElement = document.createElement('input')
    const trackBarRef = { current: trackBar }

    const {
      result: {
        current: { setCurrentTime },
      },
    } = renderHook(() => useVideoTrack(videoRef, trackBarRef))

    act(() => {
      setCurrentTime(2)
    })

    expect(videoRef?.current?.currentTime).toBe(2)

    act(() => {
      videoRef?.current?.dispatchEvent(new window.Event('timeupdate'))
    })

    expect(trackBarRef?.current.value).toBe('2')
  })
})

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "store/promotion-banner.button-dismiss.label": "Agora não",
  "store/promotion-banner.install.title": "Acompanhe seus pedidos",
  "store/promotion-banner.install.description": "Nosso app é rápido, pequeno e funciona offline",
  "store/promotion-banner.install.button-accept.label": "Instalar",
  "store/promotion-banner.push-notification.title": "Acompanhe seus pedidos",
  "store/promotion-banner.push-notification.description": "Receba notificações sobre os status do seus pedidos.",
  "store/promotion-banner.push-notification.button-accept.label": "Aceitar",
  "store/offline-warning.button": "Atualizar",
  "store/offline-warning.message": "Não é possível carregar está página. Verifique sua conexão de rede",
  "store/offline-warning.title": "Esta página não pode ser carregada"
}
```


[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 pwa-components/messages/en.json from the pwa-components VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store/promotion-banner.button-dismiss.label": "Not now",
  "store/promotion-banner.install.title": "Keep track of your orders",
  "store/promotion-banner.install.description": "Our app is fast, small, and works offline",
  "store/promotion-banner.install.button-accept.label": "Install",
  "store/promotion-banner.push-notification.title": "Keep track of your orders",
  "store/promotion-banner.push-notification.description": "Receive notifications about your orders status",
  "store/promotion-banner.push-notification.button-accept.label": "Accept",
  "store/offline-warning.button": "Refresh",
  "store/offline-warning.message": "Unable to load this page. Check your network connection",
  "store/offline-warning.title": "This page cannot be loaded"
}
```