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

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
export const GetText = (items: any) => {
  const joinLines = items
    .map((line: any) => {
      return line.content
    })
    .join('\n')

  return joinLines
}

export const ParseText = (textAreaValue: string) => {
  const rawText: any = String(textAreaValue || '')
  const arrText = String(rawText).split(/[\n\r]/)
  const items = arrText
    .filter((item: any) => {
      return String(item).trim() !== ''
    })
    .map((line: any, index: number) => {
      const lineSplitted: any = line.split(',')

      if (lineSplitted.length === 2) {
        if (
          !!lineSplitted[0] &&
          !!String(lineSplitted[1]).trim() &&
          // eslint-disable-next-line no-restricted-globals
          !isNaN(lineSplitted[1])
        ) {
          return {
            index,
            line: index,
            sku: String(lineSplitted[0]).trim(),
            quantity: parseFloat(String(lineSplitted[1]).trim()),
            content: line,
            error: null,
          }
        }
      }

      return {
        index,
        line: index,
        content: line,
        sku: null,
        quantity: null,
        error: 'store/quickorder.invalidPattern',
      }
    })

  return items
}

```


[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 quickorder/react/components/QuickOrderAutocomplete.tsx from the quickorder VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FunctionComponent } from 'react'
import React, { useState, useRef } from 'react'
import { AutocompleteInput } from 'vtex.styleguide'
import PropTypes from 'prop-types'
import type { WrappedComponentProps } from 'react-intl'
import { injectIntl } from 'react-intl'
import { useApolloClient } from 'react-apollo'
import { useCssHandles } from 'vtex.css-handles'

import { autocompleteMessages as messages } from '../utils/messages'
import autocomplete from '../queries/autocomplete.gql'

const getImageSrc = (img: string) => {
  const td = img.split('/')
  const ids = td[td.indexOf('ids') + 1]

  return img.replace(ids, `${ids}-50-50`)
}

const CustomOption = (props: any) => {
  const { roundedBottom, searchTerm, value, selected, onClick } = props
  const [highlightOption, setHighlightOption] = useState(false)
  const CSS_HANDLES = ['customOptionButton'] as const

  const handles = useCssHandles(CSS_HANDLES)
  const renderOptionHighlightedText = () => {
    const highlightableText = typeof value === 'string' ? value : value.label
    const index: number = highlightableText
      .toLowerCase()
      .indexOf(searchTerm.toLowerCase())

    if (index === -1) {
      return highlightableText
    }

    const prefix = highlightableText.substring(0, index)
    const match = highlightableText.substr(index, searchTerm.length)
    const suffix = highlightableText.substring(
      index + parseInt(match.length, 10)
    )

    return (
      <span className="truncate">
        <span className="fw7">{prefix}</span>
        {match}
        <span className="fw7">{suffix}</span>
      </span>
    )
  }

  const buttonClasses = `${
    handles.customOptionButton
  } bn w-100 tl pointer pa4 f6 ${roundedBottom ? 'br2 br--bottom' : ''} ${
    highlightOption || selected ? 'bg-muted-5' : 'bg-base'
  }`

  const thumb = value.thumb ? value.thumb : ''

  return (
    <button
      className={buttonClasses}
      onFocus={() => setHighlightOption(true)}
      onMouseEnter={() => setHighlightOption(true)}
      onMouseLeave={() => setHighlightOption(false)}
      onClick={onClick}
    >
      <div className="flex items-center">
        <span className="mr3 c-muted-2 flex pt1">
          {thumb && <img src={thumb} alt="" />}
        </span>
        <span className="pr2">{renderOptionHighlightedText()}</span>
        {typeof value !== 'string' && (
          <div className="t-mini c-muted-1">{value.caption}</div>
        )}
      </div>
    </button>
  )
}

interface QuickOrderAutocompleteInt {
  onSelect: any
}
const QuickOrderAutocomplete: FunctionComponent<
  WrappedComponentProps & QuickOrderAutocompleteInt
> = ({ onSelect, intl }: any) => {
  const client = useApolloClient()
  const [optionsResult, setOptions] = useState([])
  const [term, setTerm] = useState('')
  const [loading, setLoading] = useState(false)
  const [lastSearched, setLastSearched] = useState([])
  const timeoutRef: any = useRef(null)

  const handleSearch = async ({ value }: any) => {
    if (value.length > 1) {
      const { data } = await client.query({
        query: autocomplete,
        variables: { inputValue: value },
      })

      setOptions(
        !!data &&
          !!data.productSuggestions &&
          !!data.productSuggestions.products
          ? data.productSuggestions.products
          : []
      )
    }
  }

  const options = {
    onSelect: (...args: any) => {
      onSelect(args)
    },
    loading,
    value: !term.length
      ? []
      : optionsResult
          .filter((item: any) => {
            return !!item.items[0].images[0].imageUrl
          })
          .map((item: any) => {
            return {
              value: item.items[0].itemId,
              label: item.productName,
              slug: item.linkText,
              thumb: getImageSrc(item.items[0].images[0].imageUrl),
            }
          }),
    lastSearched: {
      value: lastSearched,
      label: 'Last searched products',
      onChange: (option: never) =>
        option && setLastSearched([...new Set([...lastSearched, option])]),
    },
    // --- This is what makes the custom option work!
    // eslint-disable-next-line react/display-name
    renderOption: (props: any) => <CustomOption {...props} />,
  }

  const input = {
    onChange: (nterm: any) => {
      if (nterm) {
        setLoading(true)
        if (timeoutRef.current) {
          clearTimeout(timeoutRef.current)
        }

        timeoutRef.current = setTimeout(() => {
          setLoading(false)
          setTerm(nterm)
          handleSearch({ value: nterm })
          timeoutRef.current = null
        }, 1000)
      } else {
        setTerm(nterm)
      }
    },
    onSearch: () => () => {},
    onClear: () => setTerm(''),
    placeholder: intl.formatMessage(messages.placeholder),
    value: term,
  }

  return <AutocompleteInput input={input} options={options} />
}

QuickOrderAutocomplete.propTypes = {
  onSelect: PropTypes.func,
}
export default injectIntl(QuickOrderAutocomplete)

```


[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 quickorder/react/components/ReviewBlock.tsx from the quickorder VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable vtex/prefer-early-return */
import type { FunctionComponent } from 'react'
import React, { useState, useEffect, useContext } from 'react'
import {
  Table,
  Input,
  ButtonWithIcon,
  IconDelete,
  Dropdown,
  ToastContext,
  Tag,
} from 'vtex.styleguide'
import type { WrappedComponentProps } from 'react-intl'
import { injectIntl } from 'react-intl'
import PropTypes from 'prop-types'
import { useApolloClient, useQuery } from 'react-apollo'

import { reviewMessages as messages } from '../utils/messages'
import { ParseText, GetText } from '../utils'
import getRefIdTranslation from '../queries/refids.gql'
import OrderFormQuery from '../queries/orderForm.gql'
import autocomplete from '../queries/autocomplete.gql'

const remove = <IconDelete />

let orderFormId = ''

const ReviewBlock: FunctionComponent<WrappedComponentProps & any> = ({
  onReviewItems,
  hiddenColumns,
  reviewedItems,
  refidLoading,
  onRefidLoading,
  intl,
  backList,
}: any) => {
  const client = useApolloClient()
  const { showToast } = useContext(ToastContext)

  const { data: orderFormData } = useQuery<{
    orderForm
  }>(OrderFormQuery, {
    ssr: false,
    skip: !!orderFormId,
  })

  const checkRestriction = async (sku: any) => {
    return client.query({
      query: autocomplete,
      variables: { inputValue: sku },
    })
  }

  const setRestriction = async (data: any) => {
    const promises = data.map((item: any) =>
      checkRestriction(item.refid).then((res: any) => {
        const foundSku = res?.data?.productSuggestions?.products[0]?.items.find(
          (suggestedItem) => suggestedItem.itemId === item.sku
        )

        return foundSku ? item : null
      })
    )

    return Promise.all(promises)
  }

  const [state, setReviewState] = useState<any>({
    reviewItems:
      reviewedItems.map((item: any, index: number) => {
        return {
          ...item,
          index,
        }
      }) || [],
  })

  const { reviewItems } = state

  if (orderFormData?.orderForm?.orderFormId) {
    orderFormId = orderFormData.orderForm.orderFormId
  }

  const errorMessage = {
    'store/quickorder.valid': messages.valid,
    'store/quickorder.available': messages.available,
    'store/quickorder.invalidPattern': messages.invalidPattern,
    'store/quickorder.skuNotFound': messages.skuNotFound,
    'store/quickorder.partiallyAvailable': messages.partiallyAvailable,
    'store/quickorder.withoutStock': messages.withoutStock,
    'store/quickorder.withoutPriceFulfillment':
      messages.withoutPriceFulfillment,
    'store/quickorder.limited': messages.limited,
    'store/quickorder.cannotBeDelivered': messages.cannotBeDelivered,
    'store/quickorder.inactive': messages.inactive,
    'store/quickorder.ORD002': messages.ORD002,
    'store/quickorder.ORD003': messages.ORD003,
    'store/quickorder.ORD004': messages.ORD004,
    'store/quickorder.ORD005': messages.ORD005,
    'store/quickorder.ORD006': messages.ORD006,
    'store/quickorder.ORD007': messages.ORD007,
    'store/quickorder.ORD008': messages.ORD008,
    'store/quickorder.ORD009': messages.ORD009,
    'store/quickorder.ORD011': messages.ORD011,
    'store/quickorder.ORD012': messages.ORD012,
    'store/quickorder.ORD013': messages.ORD013,
    'store/quickorder.ORD014': messages.ORD014,
    'store/quickorder.ORD015': messages.ORD015,
    'store/quickorder.ORD016': messages.ORD016,
    'store/quickorder.ORD017': messages.ORD017,
    'store/quickorder.ORD019': messages.ORD019,
    'store/quickorder.ORD020': messages.ORD020,
    'store/quickorder.ORD021': messages.ORD021,
    'store/quickorder.ORD022': messages.ORD022,
    'store/quickorder.ORD023': messages.ORD023,
    'store/quickorder.ORD024': messages.ORD024,
    'store/quickorder.ORD025': messages.ORD025,
    'store/quickorder.ORD026': messages.ORD026,
    'store/quickorder.ORD027': messages.ORD027,
    'store/quickorder.ORD028': messages.ORD028,
    'store/quickorder.ORD029': messages.ORD029,
    'store/quickorder.ORD030': messages.ORD030,
    'store/quickorder.ORD031': messages.ORD031,
  }

  const validateRefids = async (refidData: any, reviewed: any) => {
    let error = false

    // drops sellers from refidData
    refidData?.skuFromRefIds.items.forEach((item: any) => {
      if (!item?.sellers) return
      item.sellers = item.sellers.filter(
        (seller: any) =>
          seller?.availability === 'available' ||
          seller?.availability === 'partiallyAvailable' ||
          seller?.availability === 'withoutStock'
      )
    })

    if (refidData) {
      const refIdNotFound =
        !!refidData && !!refidData.skuFromRefIds.items
          ? refidData.skuFromRefIds.items.filter((item: any) => {
              return item.sku === null
            })
          : []

      const refIdFound =
        !!refidData && !!refidData.skuFromRefIds.items
          ? refidData.skuFromRefIds.items.filter((item: any) => {
              return item.sku !== null
            })
          : []

      const refNotAvailable =
        !!refidData && !!refidData.skuFromRefIds.items
          ? refidData.skuFromRefIds.items.filter((item: any) => {
              return !!item.sellers?.length
            })
          : []

      const mappedRefId = {}

      if (refidData?.skuFromRefIds?.items) {
        const restrictedData = await setRestriction(
          refidData.skuFromRefIds.items
        ).then((data) =>
          data.filter((item: any) => {
            return item != null
          })
        )

        restrictedData.forEach((item: any) => {
          mappedRefId[item.refid] = item
        })
      }

      const errorMsg = (item: any, sellerWithStock: string) => {
        const { sku } = item

        // Error precedence
        const notfound = refIdNotFound.find(
          (curr: any) => curr.refid === sku && curr.sku === null
        )

        const found = refIdFound.find(
          (curr: any) => curr.refid === sku && curr.sku !== null
        )

        if (found?.sku && found.sellers.length === 0) {
          return 'store/quickorder.inactive'
        }

        // Check for stock in the selected seller
        let selectedSellerHasPartialStock = false
        const foundHasStock =
          found?.sellers?.some((seller: any) => {
            if (seller?.id === sellerWithStock) {
              selectedSellerHasPartialStock =
                seller.availability === 'partiallyAvailable'
            }

            return (
              seller?.availability &&
              (seller.availability === 'available' ||
                seller.availability === 'partiallyAvailable')
            )
          }) ?? false

        // Error handling for availability and restriction
        const partialStockError = selectedSellerHasPartialStock
          ? 'store/quickorder.partiallyAvailable'
          : null

        const availabilityError = foundHasStock
          ? partialStockError
          : 'store/quickorder.withoutStock'

        const itemRestricted = !sellerWithStock
          ? 'store/quickorder.limited'
          : null

        // Final return
        return notfound
          ? 'store/quickorder.skuNotFound'
          : availabilityError ?? itemRestricted
      }

      if (refIdNotFound.length || refNotAvailable.length) {
        error = true
      }

      const items = reviewed.map((item: any) => {
        const sellerWithStock = item.seller
          ? item.seller
          : item.sku && mappedRefId[item.sku]?.sellers?.length
          ? mappedRefId[item.sku]?.sellers.find(
              (seller: any) =>
                seller.availability === 'available' ||
                seller.availability === 'partiallyAvailable'
            )?.id ?? ''
          : ''

        const sellerUnitMultiplier =
          item.sku && mappedRefId[item.sku]?.sellers?.length
            ? mappedRefId[item.sku]?.sellers.find(
                (seller: any) => seller.id === sellerWithStock
              )?.unitMultiplier ?? '1'
            : '1'

        const sellerAvailableQuantity =
          item.sku && mappedRefId[item.sku]?.sellers?.length
            ? mappedRefId[item.sku]?.sellers.find(
                (seller: any) => seller.id === sellerWithStock
              )?.availableQuantity
            : null

        return {
          ...item,
          sellers: item.sku ? mappedRefId[item.sku]?.sellers : [],
          seller: sellerWithStock,
          vtexSku: item.sku ? mappedRefId[item.sku]?.sku : '1',
          unitMultiplier: sellerUnitMultiplier,
          totalQuantity: sellerUnitMultiplier
            ? sellerUnitMultiplier * item.quantity
            : '',
          availableQuantity: sellerAvailableQuantity ?? item.quantity,
          error: errorMsg(item, sellerWithStock),
        }
      })

      const merge = (original: any) => {
        const item = items.find((curr: any) => {
          return original.sku === curr.sku
        })

        return item || original
      }

      const updated = reviewItems.map((item: any) => {
        return merge(item)
      })

      onReviewItems(updated)

      if (JSON.stringify(reviewItems) !== JSON.stringify(updated)) {
        setReviewState({
          ...state,
          reviewItems: updated,
          hasError: error,
        })
      }
    }
  }

  const getRefIds = async (
    _refids: any,
    reviewed: any,
    refIdSellerMap: any
  ) => {
    onRefidLoading(true)
    const refids = _refids.length ? Array.from(new Set(_refids)) : []

    const refIdQuantityMap = reviewed.reduce((prev, item) => {
      return {
        ...prev,
        [item.sku]: item.quantity,
      }
    }, {})

    const query = {
      query: getRefIdTranslation,
      variables: { refids, orderFormId, refIdSellerMap, refIdQuantityMap },
    }

    try {
      const { data } = await client.query(query)

      await validateRefids(data, reviewed)
      onRefidLoading(false)
    } catch (err) {
      showToast({
        message: intl.formatMessage(messages.cannotGetSkuInfo),
      })
      backList()
    }
  }

  const convertRefIds = (items: any) => {
    const refIdSellerMap = {}
    const refids = items
      .filter((item: any) => {
        return item.error === null
      })
      .map((item: any) => {
        refIdSellerMap[item.sku] = ['1']

        return item.sku
      })

    getRefIds(refids, items, refIdSellerMap)
  }

  const checkValidatedItems = () => {
    const items: [any] = reviewItems.filter((item: any) => {
      return item.sku !== null && item.error === null && !item.vtexSku
    })

    if (items.length) {
      convertRefIds(items)
    }
  }

  useEffect(() => {
    checkValidatedItems()
  }, [reviewItems])

  const removeLine = (i: number) => {
    const items: [any] = reviewItems
      .filter((item: any) => {
        return item.index !== i
      })
      .map((item: any, index: number) => {
        return {
          ...item,
          line: index,
          index,
        }
      })

    onReviewItems(items)
    setReviewState({
      ...state,
      reviewItems: items,
    })
  }

  const updateLineContent = (index: number, content: string) => {
    const items = reviewItems.map((item: any) => {
      return item.index === index
        ? {
            ...item,
            content,
          }
        : item
    })

    setReviewState({
      ...state,
      reviewItems: items,
    })
  }

  const updateLineSeller = (index: number, seller: string) => {
    const refIdSellerMap = {}
    const items = reviewItems.map((item: any) => {
      return item.index === index
        ? {
            ...item,
            seller,
          }
        : item
    })

    const refids = items.map((item: any) => {
      refIdSellerMap[item.sku] = [item.seller]

      return item.sku
    })

    getRefIds(refids, items, refIdSellerMap)
  }

  const onBlurField = (line: number) => {
    const joinLines = GetText(reviewItems)
    const reviewd: any = ParseText(joinLines)

    if (reviewd[line].error === null) {
      setReviewState({
        ...state,
        reviewItems: reviewd,
      })
    }
  }

  const tableSchema: {
    properties: any
  } = {
    properties: {},
  }

  const createSchema = (columnsToBeHidden: string[]) => {
    if (columnsToBeHidden.indexOf('line') === -1) {
      let count = 0

      tableSchema.properties.line = {
        type: 'object',
        title: intl.formatMessage({
          id: 'store/quickorder.review.label.lineNumber',
        }),
        width: 50,
        // eslint-disable-next-line react/display-name
        cellRenderer: () => {
          count++

          return <div>{refidLoading ? '-' : count}</div>
        },
      }
    }

    if (columnsToBeHidden.indexOf('content') === -1) {
      tableSchema.properties.content = {
        type: 'object',
        title: intl.formatMessage({
          id: 'store/quickorder.review.label.content',
        }),
        // eslint-disable-next-line react/display-name
        cellRenderer: ({ cellData, rowData }: any) => {
          if (rowData.error) {
            return (
              <div>
                <Input
                  value={cellData}
                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                    updateLineContent(rowData.index, e.target.value)
                  }}
                  onBlur={() => {
                    onBlurField(rowData.line)
                  }}
                />
              </div>
            )
          }

          return <span>{cellData}</span>
        },
      }
    }

    if (columnsToBeHidden.indexOf('sku') === -1) {
      tableSchema.properties.sku = {
        type: 'string',
        title: intl.formatMessage({ id: 'store/quickorder.review.label.sku' }),
        width: 112,
      }
    }

    if (columnsToBeHidden.indexOf('quantity') === -1) {
      tableSchema.properties.quantity = {
        type: 'string',
        title: intl.formatMessage({
          id: 'store/quickorder.review.label.quantity',
        }),
        width: 72,
      }
    }

    if (columnsToBeHidden.indexOf('unitMultiplier') === -1) {
      tableSchema.properties.unitMultiplier = {
        type: 'float',
        title: intl.formatMessage({
          id: 'store/quickorder.review.label.multiplier',
        }),
        width: 100,
      }
    }

    if (columnsToBeHidden.indexOf('totalQuantity') === -1) {
      tableSchema.properties.totalQuantity = {
        type: 'float',
        title: (
          <div style={{ whiteSpace: 'break-spaces' }}>
            {intl.formatMessage({
              id: 'store/quickorder.review.label.totalQuantity',
            })}
          </div>
        ),
        width: 82,
      }
    }

    if (columnsToBeHidden.indexOf('seller') === -1) {
      tableSchema.properties.seller = {
        type: 'string',
        title: intl.formatMessage({
          id: 'store/quickorder.review.label.seller',
        }),
        cellRenderer: ({ rowData }: any) => {
          if (rowData?.sellers?.length > 1) {
            return (
              <div>
                <Dropdown
                  options={rowData.sellers
                    .filter((seller) => seller?.availability !== 'withoutStock')
                    .map((item: any) => {
                      return {
                        label: item.name,
                        value: item.id,
                      }
                    })}
                  value={rowData.seller}
                  onChange={(_: any, v: any) => {
                    updateLineSeller(rowData.index, v)
                  }}
                />
              </div>
            )
          }

          const hasStock = rowData?.sellers?.find(
            (seller?: { availability: string; [key: string]: unknown }) =>
              seller?.availability !== 'withoutStock'
          )

          return rowData?.sellers?.length && hasStock
            ? rowData.sellers[0].name
            : ''
        },
      }
    }

    if (columnsToBeHidden.indexOf('error') === -1) {
      tableSchema.properties.error = {
        type: 'string',
        title: intl.formatMessage({
          id: 'store/quickorder.review.label.status',
        }),
        cellRenderer: ({ cellData, rowData }: any) => {
          if (rowData.error) {
            const errMsg = errorMessage[cellData || 'store/quickorder.valid']
            const text =
              errMsg === messages.partiallyAvailable
                ? intl.formatMessage(errMsg, {
                    quantity: rowData.availableQuantity,
                    totalQuantity:
                      rowData.availableQuantity * rowData.unitMultiplier,
                  })
                : intl.formatMessage(errMsg)

            return (
              <span>
                <Tag type="error" variation="low" size="small">
                  {text}
                </Tag>
              </span>
            )
          }

          return (
            !refidLoading &&
            intl.formatMessage({ id: 'store/quickorder.valid' })
          )
        },
      }
    }

    if (columnsToBeHidden.indexOf('delete') === -1) {
      tableSchema.properties.delete = {
        type: 'object',
        title: ' ',
        width: 58,
        // eslint-disable-next-line react/display-name
        cellRenderer: ({ rowData }: any) => {
          return (
            <div>
              <ButtonWithIcon
                icon={remove}
                variation="tertiary"
                onClick={() => {
                  removeLine(rowData.index)
                }}
              />
            </div>
          )
        },
      }
    }
  }

  createSchema(hiddenColumns)

  return (
    <div>
      <Table
        schema={tableSchema}
        items={reviewItems}
        emptyStateLabel={intl.formatMessage({
          id: 'store/quickorder.review.label.emptyState',
        })}
        fullWidth
      />
    </div>
  )
}

ReviewBlock.propTypes = {
  onReviewItems: PropTypes.func,
  reviewItems: PropTypes.array,
  hiddenColumns: PropTypes.array,
  onRefidLoading: PropTypes.func,
}

export default injectIntl(ReviewBlock)

```


[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 quickorder/react/assets/DropZoneIcon.tsx from the quickorder VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

export const DropzoneIcon = ({ className }) => {
  return (
    <svg
      className={className}
      width="40"
      height="46"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d="M24.793.793A1 1 0 0 0 24.086.5H1.5a1 1 0 0 0-1 1v43a1 1 0 0 0 1 1h37a1 1 0 0 0 1-1V15.914a1 1 0 0 0-.293-.707L24.793.793z"
        fill="#E3E4E6"
      />
      <path
        d="M9.5 33.5h21M9.5 24.5h21"
        stroke="#3F3F40"
        strokeWidth="1.2"
        strokeMiterlimit="10"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      <path
        d="M9.5 15.5h6"
        stroke="#F71963"
        strokeWidth="1.2"
        strokeMiterlimit="10"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      <path d="M24.5.5v15h15" fill="#CACBCC" />
    </svg>
  )
}

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "store/skip-link-text": "Pular para conteúdo principal"
}
```


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

[Assistant]
Here's the implementation:

```json
{
  "store/skip-link-text": "Skip to main content"
}
```


[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 skip-link/docs/README.md from the skip-link VTEX app.

[Assistant]
Here's the implementation:

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

# Skip Link

<!-- 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 -->

Implements Skip-Link blocks.

## Configuration

1. Adding this app as a theme dependency in the `manifest.json` file;
2. Add `skip-link` block to the upmost position possible. For example:
```
"header-layout.desktop": {
    "children": [
      "skip-link",
      "flex-layout.row#1-desktop",
      "flex-layout.row#2-desktop",
      "flex-layout.row#3-desktop",
      "sticky-layout#4-desktop"
    ]
  },
 ```
2. Add the anchor block `skip-link-anchor` in a given template where you want the link navigating to. For example:
```
"store.home": {
  "blocks": [
    "skip-link-anchor",
    "list-context.image-list#demo",
    /* You can make references to blocks defined in other files.
     * For example, `flex-layout.row#deals` is defined in the `deals.json` file. */
    "__fold__.experimentalLazyAssets",
    "flex-layout.row#deals",
    "rich-text#shelf-title",
    "flex-layout.row#shelf",
    "info-card#home",
    "rich-text#question",
    "rich-text#link",
    "newsletter"
  ]
},
```

### `skip-link-anchor` props

None so far.

## Modus Operandi

Do NOT place **more than one** `skip-link-anchor` block within the same template. Probably, there is no guarantee that the link will scroll correctly.

## 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 |
| ----------- |
| `skiplink` |

<!-- 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 skip-link/react/SkipLink.tsx from the skip-link VTEX app.

[Assistant]
Here's the implementation:

```tsx
import SkipLink from './components/SkipLink'
export default SkipLink
```


[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 skip-link/react/package.json from the skip-link VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "CHANGE_ME",
  "version": "0.0.0",
  "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"
  },
  "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.3.2",
    "@vtex/tsconfig": "^0.4.4",
    "apollo-cache-inmemory": "^1.6.5",
    "graphql": "^14.6.0",
    "typescript": "3.9.7",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.130.0/public/@types/vtex.render-runtime"
  }
}

```


[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 skip-link/react/SkipLinkAnchor.tsx from the skip-link VTEX app.

[Assistant]
Here's the implementation:

```tsx
import SkipLinkAnchor from './components/SkipLinkAnchor'
export default SkipLinkAnchor
```


[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 skip-link/store/interfaces.json from the skip-link VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "skip-link": {
    "component": "SkipLink"
  },
  "skip-link-anchor": {
    "component": "SkipLinkAnchor"
  }
}
```


[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 skip-link/react/typings/gql.d.ts from the skip-link VTEX app.

[Assistant]
Here's the implementation:

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

  const Schema: DocumentNode

  export default Schema
}

declare module '*.graphql' {
  import { DocumentNode } from 'graphql'

  const schema: DocumentNode

  export default Schema
}

```


[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 skip-link/react/typings/css.d.ts from the skip-link VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  type Styles = {
    [selector: string]: string
  }

  const styles: Styles

  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 skip-link/react/components/SkipLinkAnchor/index.tsx from the skip-link VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

function SkipLinkAnchor() {
  return <a id='skip-link-anchor'></a>
}

export default SkipLinkAnchor

```


[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 skip-link/react/components/SkipLink/index.tsx from the skip-link VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'
import styles from './skiplink.module.css'

const CSS_HANDLES = ['skiplink']

function onClick() {
  var a = window.document.getElementById('skip-link-anchor');
  a?.scrollIntoView(!0);
  a?.focus();
}

function onKeyUp(c: any) {
  13 === c.keyCode && onClick()
}

function SkipLinkAnchor() {
  const { handles } = useCssHandles(CSS_HANDLES)
  return (
    <FormattedMessage id="store/skip-link-text">
      {message => <a id='skip-link' style={{ 'position': 'absolute', 'zIndex': -1 }} tabIndex={1} className={`${styles.skiplink} ${handles.skiplink}`} onClick={onClick} onKeyUp={onKeyUp}>{message}</a>}
    </FormattedMessage>
  )
}

export default SkipLinkAnchor

```


[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 skip-link/react/components/SkipLink/skiplink.module.css from the skip-link VTEX app.

[Assistant]
Here's the implementation:

```css
.skiplink {
  position: absolute;
  top: 10px;
  z-index: -1;
  margin-left: 7px;
  padding: 7px 8px 7px 8px;
  background-color: #fff;
  border: 1px solid #555;
  border-radius: 3px;
}

.skiplink:focus {
  z-index: 10 !important;
}
```


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

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

[Assistant]
Here's the implementation:

```markdown
# VTEX GraphQL Resolver

This app is an implementation of the VTEX search protocol that wraps the VTEX catalog searches related API calls.
This protocol is defined by the schema graphQL located here: https://github.com/vtex-apps/search-graphql

```


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

[User] Show me the implementation of search-resolver/node/service.json from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "stack": "nodejs",
  "memory": 2048,
  "cpu": {
    "type": "shared",
    "value": 80,
    "scalingPercentage": 95
  },
  "ttl": 30,
  "timeout": 12,
  "minReplicas": 30,
  "maxReplicas": 250,
  "workers": 2,
  "routes": {
    "setWorkspaceSearchParams": {
      "path": "/_v/setWorkspaceSearchParams",
      "public": false,
      "cache": false,
      "smartcache": false
    },
    "getWorkspaceSearchParams": {
      "path": "/_v/getWorkspaceSearchParams",
      "public": false,
      "cache": false,
      "smartcache": false
    }
  }
}

```


[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 search-resolver/node/package.json from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.search-resolver",
  "scripts": {
    "lint": "tsc --noEmit --pretty",
    "test": "jest"
  },
  "dependencies": {
    "@gocommerce/utils": "^0.6.11",
    "camelcase": "^5.0.0",
    "co-body": "^6.2.0",
    "cookie": "^0.3.1",
    "graphql": "^14.7.0",
    "graphql-tools": "^4.0.6",
    "qs": "^6.6.0",
    "ramda": "^0.27.1",
    "slugify": "^1.6.4",
    "vtex.search-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.search-graphql@0.65.0/public"
  },
  "devDependencies": {
    "@types/camelcase": "^4.1.0",
    "@types/co-body": "^6.1.3",
    "@types/cookie": "^0.4.1",
    "@types/graphql": "^14.5.0",
    "@types/jest": "^29.5.14",
    "@types/node": "^20.0.0",
    "@types/qs": "^6.5.1",
    "@types/ramda": "^0.26.21",
    "@vtex/api": "^7.2.7",
    "@vtex/tsconfig": "^0.6.0",
    "axios": "^0.21.1",
    "jest": "^29.7.0",
    "ts-jest": "^29.2.5",
    "typemoq": "^2.1.0",
    "typescript": "5.5.3",
    "vtex.catalog-api-proxy": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-api-proxy@0.10.1/public/_types/react",
    "vtex.messages": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.messages@1.64.0/public/@types/vtex.messages",
    "vtex.rewriter": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.rewriter@1.53.4/public/@types/vtex.rewriter"
  },
  "version": "1.91.0"
}

```


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

[User] Show me the implementation of search-resolver/node/global.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  IOContext,
  ParamsContext,
  RecorderState,
  SegmentData,
  ServiceContext,
  MessagesLoaderV2,
} from '@vtex/api'

import type { Clients } from './clients'

declare global {
  type Context = ServiceContext<Clients, State, CustomContext>

  interface State extends RecorderState {
    messagesTenantLanguage?: MessagesLoaderV2
    messagesBindingLanguage?: MessagesLoaderV2
  }

  interface StaleRevalidateData<T> {
    ttl: Date
    data: T
  }

  interface CustomContext extends ParamsContext {
    cookie: string
    originalPath: string
    vtex: CustomIOContext
    translated?: boolean
  }

  interface CustomIOContext extends IOContext {
    segment?: SegmentData
  }

  interface Property {
    name: string
    values: [string]
  }

  interface TranslatableMessage {
    content: string
    from: string
    id: string
  }

  interface Reference {
    Key: string
    Value: string
  }

  interface AppSettings {
    slugifyLinks: boolean
  }

  interface DegradedSearchError {
    service: string
    error: string
    errorStack?: unknown
  }
}

```


[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 search-resolver/node/index.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Cached, RecorderState } from '@vtex/api'
import { LRUCache, Service, method } from '@vtex/api'
import schema from 'vtex.search-graphql/graphql'

import { Clients } from './clients'
import { schemaDirectives } from './directives'
import { resolvers } from './resolvers'
import {
  getWorkspaceSearchParams,
  setWorkspaceSearchParams,
} from './routes/workspaceSearchParams'

const TWO_SECONDS_MS = 2 * 1000
const THREE_SECONDS_MS = 3 * 1000
const SIX_SECONDS_MS = 6 * 1000
const NINE_SECONDS_MS = 9 * 1000

// Segments are small and immutable.
const segmentCache = new LRUCache<string, Cached>({ max: 1000 })
const searchCache = new LRUCache<string, Cached>({ max: 3000 })
const messagesCache = new LRUCache<string, Cached>({ max: 3000 })
const vbaseCache = new LRUCache<string, Cached>({ max: 3000 })
const appsCache = new LRUCache<string, Cached>({ max: 1500 })
const intschCache = new LRUCache<string, Cached>({ max: 3000 })

metrics.trackCache('segment', segmentCache)
metrics.trackCache('search', searchCache)
metrics.trackCache('messages', messagesCache)
metrics.trackCache('vbase', vbaseCache)
metrics.trackCache('apps', appsCache)
metrics.trackCache('intsch', intschCache)

export default new Service<Clients, RecorderState, CustomContext>({
  clients: {
    implementation: Clients,
    options: {
      default: {
        retries: 2,
        timeout: THREE_SECONDS_MS,
      },
      apps: {
        retries: 2,
        concurrency: 5,
        memoryCache: appsCache,
        timeout: TWO_SECONDS_MS,
      },
      messagesGraphQL: {
        concurrency: 10,
        memoryCache: messagesCache,
        timeout: TWO_SECONDS_MS,
      },
      segment: {
        concurrency: 10,
        memoryCache: segmentCache,
        timeout: THREE_SECONDS_MS,
      },
      search: {
        concurrency: 10,
        memoryCache: searchCache,
        timeout: SIX_SECONDS_MS,
      },
      intelligentSearchApi: {
        retries: 0,
        concurrency: 10,
        timeout: NINE_SECONDS_MS,
      },
      intsch: {
        retries: 0,
        concurrency: 10,
        timeout: NINE_SECONDS_MS,
      },
      rewriter: {
        timeout: SIX_SECONDS_MS,
      },
      vbase: {
        concurrency: 2,
        memoryCache: vbaseCache,
        timeout: TWO_SECONDS_MS,
      },
    },
  },
  graphql: {
    resolvers,
    schema,
    schemaDirectives,
  },
  routes: {
    setWorkspaceSearchParams: method({
      GET: setWorkspaceSearchParams,
    }),
    getWorkspaceSearchParams: method({
      GET: getWorkspaceSearchParams,
    }),
  },
})

```


[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 search-resolver/node/directives/withSegment.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

/**
 * Warning: we stopped using the segment client to decode the segment token for us because it added unnecessary overhead for now.
 * If the getSegment API evolves to do more than a decode we must stop decoding it here and start calling the API once again.
 */
export class WithSegment extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field
    field.resolve = async (root, args, ctx: Context, info) => {
      const {
        vtex: { segmentToken },
        clients: { segment },
      } = ctx
      ctx.vtex.segment = segmentToken
        ? JSON.parse(Buffer.from(segmentToken, 'base64').toString())
        : await segment.getSegment()
      return resolve(root, args, ctx, info)
    }
  }
}

```


[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 search-resolver/node/directives/toVtexAssets.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

import { flags } from '../featureFlags'

export class ToVtexAssets extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field
    field.resolve = async (root, args, ctx: Context, info) => {
      const result = resolve(root, args, ctx, info)
      return flags.VTEX_ASSETS_URL && result
        ? result.replace('vteximg.com.br', 'vtexassets.com')
        : result
    }
  }
}

```


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

[User] Show me the implementation of search-resolver/node/directives/index.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { WithSegment } from './withSegment'
import { ToVtexAssets } from './toVtexAssets'

export const schemaDirectives = {
  withSegment: WithSegment,
  toVtexAssets: ToVtexAssets,
}

```


[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 search-resolver/node/mocks/contextFactory.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import parse from 'co-body'

import type { IntelligentSearchClientArgs } from './intsch'
import { MockedIntschClient } from './intsch'
import type { SegmentData } from '../typings/Search'

export function createContext<Ctx = Context>({
  accountName,
  intelligentSearchApiSettings,
  intschSettings,
  cookies,
  body,
  req,
  query,
  production,
  appSettings,
  segment,
  tenantLocale,
  vtexLocale,
}: {
  production?: boolean
  appSettings?: Record<string, any>
  intschSettings?: IntelligentSearchClientArgs
  intelligentSearchApiSettings?: IntelligentSearchClientArgs
  req?: {
    body?: any
  }
  query?: Record<string, unknown>
  body?: any
  cookies?: Record<string, string>
  accountName?: string
  segment?: SegmentData
  tenantLocale?: string
  vtexLocale?: string
}) {
  if (req?.body instanceof Error) {
    jest.spyOn(parse, 'json').mockRejectedValue(req.body)
  } else {
    jest.spyOn(parse, 'json').mockReturnValue(req?.body ?? {})
  }

  return {
    req: {},
    body: body ?? {},
    cookies: {
      get: jest.fn().mockImplementation((key: string) => cookies?.[key]),
    },
    get: jest.fn().mockReturnValue('localhost'),
    response: {
      set: jest.fn(),
    },
    clients: {
      intsch: new MockedIntschClient(intschSettings),
      intelligentSearchApi: new MockedIntschClient(
        intelligentSearchApiSettings
      ),
      apps: {
        getAppSettings: jest.fn().mockReturnValue(appSettings ?? {}),
      },
      vbase: {
        getJSON: jest.fn().mockResolvedValue({}),
      }
    },
    vtex: {
      production: production ?? false,
      account: accountName ?? 'biggy',
      logger: {
        info: jest.fn(),
        warn: jest.fn(),
        error: jest.fn(),
      },
      segment,
      tenant: {
        locale: tenantLocale,
      },
      locale: vtexLocale,
    },
    query: query ?? {},
  } as unknown as Ctx
}

```


[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 search-resolver/node/mocks/intsch.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  IIntelligentSearchClient,
  AutocompleteSuggestionsResponse,
  TopSearchesResponse,
  SearchSuggestionsResponse,
  CorrectionResponse,
  FetchBannersResponse,
  FetchProductResponse,
  ProductSearchResponse,
  FacetsResponse,
} from '../clients/intsch/types'

export class MockedIntschClient implements IIntelligentSearchClient {
  constructor(args?: IntelligentSearchClientArgs) {
    if (args?.fetchAutocompleteSuggestions instanceof Error) {
      this.fetchAutocompleteSuggestions.mockRejectedValue(
        args.fetchAutocompleteSuggestions
      )
    } else {
      this.fetchAutocompleteSuggestions.mockResolvedValue(
        args?.fetchAutocompleteSuggestions ?? null
      )
    }

    if (args?.fetchTopSearches instanceof Error) {
      this.fetchTopSearches.mockRejectedValue(args.fetchTopSearches)
    } else {
      this.fetchTopSearches.mockResolvedValue(args?.fetchTopSearches ?? null)
    }

    if (args?.fetchSearchSuggestions instanceof Error) {
      this.fetchSearchSuggestions.mockRejectedValue(args.fetchSearchSuggestions)
    } else {
      this.fetchSearchSuggestions.mockResolvedValue(
        args?.fetchSearchSuggestions ?? null
      )
    }

    if (args?.fetchCorrection instanceof Error) {
      this.fetchCorrection.mockRejectedValue(args.fetchCorrection)
    } else {
      this.fetchCorrection.mockResolvedValue(args?.fetchCorrection ?? null)
    }

    if (args?.fetchBanners instanceof Error) {
      this.fetchBanners.mockRejectedValue(args.fetchBanners)
    } else {
      this.fetchBanners.mockResolvedValue(args?.fetchBanners ?? null)
    }

    if (args?.fetchProduct instanceof Error) {
      this.fetchProduct.mockRejectedValue(args.fetchProduct)
    } else {
      this.fetchProduct.mockResolvedValue(args?.fetchProduct ?? null)
    }

    if (args?.fetchAutocompleteSuggestionsV1 instanceof Error) {
      this.fetchAutocompleteSuggestionsV1.mockRejectedValue(
        args.fetchAutocompleteSuggestionsV1
      )
    } else {
      this.fetchAutocompleteSuggestionsV1.mockResolvedValue(
        args?.fetchAutocompleteSuggestionsV1 ?? null
      )
    }

    if (args?.fetchTopSearchesV1 instanceof Error) {
      this.fetchTopSearchesV1.mockRejectedValue(args.fetchTopSearchesV1)
    } else {
      this.fetchTopSearchesV1.mockResolvedValue(
        args?.fetchTopSearchesV1 ?? null
      )
    }

    if (args?.fetchSearchSuggestionsV1 instanceof Error) {
      this.fetchSearchSuggestionsV1.mockRejectedValue(
        args.fetchSearchSuggestionsV1
      )
    } else {
      this.fetchSearchSuggestionsV1.mockResolvedValue(
        args?.fetchSearchSuggestionsV1 ?? null
      )
    }

    if (args?.fetchCorrectionV1 instanceof Error) {
      this.fetchCorrectionV1.mockRejectedValue(args.fetchCorrectionV1)
    } else {
      this.fetchCorrectionV1.mockResolvedValue(args?.fetchCorrectionV1 ?? null)
    }

    if (args?.fetchBannersV1 instanceof Error) {
      this.fetchBannersV1.mockRejectedValue(args.fetchBannersV1)
    } else {
      this.fetchBannersV1.mockResolvedValue(args?.fetchBannersV1 ?? null)
    }

    if (args?.productSearch instanceof Error) {
      this.productSearch.mockRejectedValue(args.productSearch)
    } else {
      this.productSearch.mockResolvedValue(args?.productSearch ?? null)
    }

    if (args?.facets instanceof Error) {
      this.facets.mockRejectedValue(args.facets)
    } else {
      this.facets.mockResolvedValue(args?.facets ?? null)
    }
  }

  public fetchAutocompleteSuggestions = jest.fn()
  public fetchTopSearches = jest.fn()
  public fetchSearchSuggestions = jest.fn()
  public fetchCorrection = jest.fn()
  public fetchBanners = jest.fn()
  public fetchProduct = jest.fn()
  public fetchAutocompleteSuggestionsV1 = jest.fn()
  public fetchTopSearchesV1 = jest.fn()
  public fetchSearchSuggestionsV1 = jest.fn()
  public fetchCorrectionV1 = jest.fn()
  public fetchBannersV1 = jest.fn()
  public productSearch = jest.fn()
  public facets = jest.fn()
}

export type IntelligentSearchClientArgs = {
  fetchAutocompleteSuggestions?: AutocompleteSuggestionsResponse | Error
  fetchTopSearches?: TopSearchesResponse | Error
  fetchSearchSuggestions?: SearchSuggestionsResponse | Error
  fetchCorrection?: CorrectionResponse | Error
  fetchBanners?: FetchBannersResponse | Error
  fetchProduct?: FetchProductResponse | Error
  fetchAutocompleteSuggestionsV1?: AutocompleteSuggestionsResponse | Error
  fetchTopSearchesV1?: TopSearchesResponse | Error
  fetchSearchSuggestionsV1?: SearchSuggestionsResponse | Error
  fetchCorrectionV1?: CorrectionResponse | Error
  fetchBannersV1?: FetchBannersResponse | Error
  productSearch?: ProductSearchResponse | Error
  facets?: FacetsResponse | Error
}

```


[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 search-resolver/node/clients/search.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  AppClient,
  CacheType,
  InstanceOptions,
  IOContext,
  RequestConfig,
  SegmentData,
} from '@vtex/api'
import { stringify } from 'qs'

import {
  SearchCrossSellingTypes,
  searchEncodeURI,
} from '../resolvers/search/utils'

interface AutocompleteArgs {
  maxRows: number | string
  searchTerm: string
}

enum SimulationBehavior {
  SKIP = 'skip',
  DEFAULT = 'default',
}

const inflightKey = ({ baseURL, url, params, headers }: RequestConfig) => {
  return (
    (baseURL ?? '') +
    (url ?? '') +
    stringify(params, { arrayFormat: 'repeat', addQueryPrefix: true }) +
    `&segmentToken=${headers ? headers['x-vtex-segment'] : ''}`
  )
}

interface SearchPageTypeResponse {
  id: string
  pageType: string
  name: string
  url: string
  title: string | null
  metaTagDescription: string | null
}

/** Search API
 * Docs: https://documenter.getpostman.com/view/845/catalogsystem-102/Hs44
 */
export class Search extends AppClient {
  private searchEncodeURI: (x: string) => string
  private basePath: string

  private addSalesChannel = (
    url: string,
    salesChannel?: string | number | null
  ) => {
    if (!salesChannel) {
      return url
    }

    if (!url.includes('?')) {
      return url.concat(`?sc=${salesChannel}`)
    }

    return url.concat(`&sc=${salesChannel}`)
  }

  private addCompleteSpecifications = (url: string) => {
    if (!url.includes('?')) {
      return `${url}?compSpecs=true`
    }

    return `${url}&compSpecs=true`
  }

  private getVtexSegmentCookieAsHeader = (vtexSegment?: string) => {
    return vtexSegment ? { 'x-vtex-segment': vtexSegment } : {}
  }

  public constructor(ctx: IOContext, opts?: InstanceOptions) {
    super('vtex.catalog-api-proxy@0.x', ctx, opts)

    this.basePath = ctx.sessionToken
      ? '/proxy/authenticated/catalog'
      : '/proxy/catalog'
    this.searchEncodeURI = searchEncodeURI(ctx.account)
  }

  public pageType = (path: string, query: string = '') => {
    const pageTypePath = encodeURI(path.startsWith('/') ? path.substr(1) : path)

    const pageTypeQuery = !query || query.startsWith('?') ? query : `?${query}`

    return this.get<SearchPageTypeResponse>(
      `/pub/portal/pagetype/${pageTypePath}${pageTypeQuery}`,
      { metric: 'search-pagetype' }
    )
  }

  public product = (
    slug: string,
    vtexSegment?: string,
    salesChannel?: string | number | null
  ) =>
    this.get<SearchProduct[]>(
      this.addCompleteSpecifications(
        this.addSalesChannel(
          `/pub/products/search/${this.searchEncodeURI(
            slug && slug.toLowerCase()
          )}/p`,
          salesChannel
        )
      ),
      {
        metric: 'search-product',
        headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      }
    )

  public productByEan = (
    id: string,
    vtexSegment?: string,
    salesChannel?: string | number | null
  ) =>
    this.get<SearchProduct[]>(
      this.addCompleteSpecifications(
        this.addSalesChannel(
          `/pub/products/search?fq=alternateIds_Ean:${id}`,
          salesChannel
        )
      ),
      {
        metric: 'search-productByEan',
        headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      }
    )

  public productsByEan = (
    ids: string[],
    vtexSegment?: string,
    salesChannel?: string | number | null
  ) =>
    this.get<SearchProduct[]>(
      this.addCompleteSpecifications(
        this.addSalesChannel(
          `/pub/products/search?${ids
            ?.map(id => `fq=alternateIds_Ean:${id}`)
            .join('&')}`,
          salesChannel
        )
      ),
      {
        metric: 'search-productByEan',
        headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      }
    )

  public productById = (
    id: string,
    vtexSegment?: string,
    salesChannel?: string | number | null,
    cacheable: boolean = true
  ) => {
    const isVtex = this.context.platform === 'vtex'
    const url = isVtex
      ? this.addCompleteSpecifications(
          this.addSalesChannel(
            `/pub/products/search?fq=productId:${id}`,
            salesChannel
          )
        )
      : `/products/${id}`
    return this.get<SearchProduct[]>(url, {
      metric: 'search-productById',
      headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      ...(cacheable ? {} : { cacheable: CacheType.None }),
    })
  }

  public productsById = (
    ids: string[],
    vtexSegment?: string,
    salesChannel?: string | number | null
  ) =>
    this.get<SearchProduct[]>(
      this.addCompleteSpecifications(
        this.addSalesChannel(
          `/pub/products/search?${ids
            ?.map(id => `fq=productId:${id}`)
            .join('&')}`,
          salesChannel
        )
      ),
      {
        metric: 'search-productById',
        headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      }
    )

  public productByReference = (
    id: string,
    vtexSegment?: string,
    salesChannel?: string | number | null
  ) =>
    this.get<SearchProduct[]>(
      this.addCompleteSpecifications(
        this.addSalesChannel(
          `/pub/products/search?fq=alternateIds_RefId:${id}`,
          salesChannel
        )
      ),
      {
        metric: 'search-productByReference',
        headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      }
    )

  public productsByReference = (
    ids: string[],
    vtexSegment?: string,
    salesChannel?: string | number | null
  ) =>
    this.get<SearchProduct[]>(
      this.addCompleteSpecifications(
        this.addSalesChannel(
          `/pub/products/search?${ids
            ?.map(id => `fq=alternateIds_RefId:${id}`)
            .join('&')}`,
          salesChannel
        )
      ),
      {
        metric: 'search-productByReference',
        headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      }
    )

  public productBySku = (
    skuId: string,
    vtexSegment?: string,
    salesChannel?: string | number | null
  ) =>
    this.get<SearchProduct[]>(
      this.addCompleteSpecifications(
        this.addSalesChannel(
          `/pub/products/search?fq=skuId:${skuId}`,
          salesChannel
        )
      ),
      {
        metric: 'search-productBySku',
        headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      }
    )

  public productsBySku = (
    skuIds: string[],
    vtexSegment?: string,
    salesChannel?: string | number | null
  ) =>
    this.get<SearchProduct[]>(
      this.addCompleteSpecifications(
        this.addSalesChannel(
          `/pub/products/search?${skuIds
            ?.map(skuId => `fq=skuId:${skuId}`)
            .join('&')}`,
          salesChannel
        )
      ),
      {
        metric: 'search-productsBySku',
        headers: this.getVtexSegmentCookieAsHeader(vtexSegment),
      }
    )

  public products = (args: SearchArgs) => {
    return this.get<SearchProduct[]>(this.productSearchUrl(args), {
      metric: 'search-products',
    })
  }

  public brands = () =>
    this.get<Brand[]>('/pub/brand/list', { metric: 'search-brands' })

  public categories = (treeLevel: number) =>
    this.get<CategoryTreeResponse[]>(`/pub/category/tree/${treeLevel}/`, {
      metric: 'search-categories',
    })

  public getCategoryChildren = (id: number) =>
    this.get<Record<string, string>>(
      `/pub/category/categories/children?id=${id}`,
      {
        metric: 'search-category-children',
      }
    )

  public facets = (facets: string = '') => {
    const [path, options] = decodeURI(facets).split('?')
    return this.get<SearchFacets>(
      `/pub/facets/search/${encodeURI(
        this.searchEncodeURI(`${path.trim()}${options ? '?' + options : ''}`)
      )}`,
      { metric: 'search-facets' }
    )
  }

  public category = (id: string | number) =>
    this.get<CategoryByIdResponse>(`/pub/category/${id}`, {
      metric: 'search-category',
    })

  public crossSelling = (id: string, type: SearchCrossSellingTypes, groupByProduct = true, acceptLanguage?: string) =>
    this.get<SearchProduct[]>(
      `/pub/products/crossselling/${type}/${id}?groupByProduct=${groupByProduct}`,
      {
        metric: 'search-crossSelling',
        // This Accept Language header is used to request the cross selling products in the desired language from new dataplane endpoint
        headers: acceptLanguage ? { 'Accept-Language': acceptLanguage } : {}
      }
    )

  public filtersInCategoryFromId = (id: string | number) =>
    this.get<FilterListTreeCategoryById[]>(
      `/pub/specification/field/listTreeByCategoryId/${id}`,
      {
        metric: 'search-listTreeByCategoryId',
      }
    )

  public autocomplete = ({ maxRows, searchTerm }: AutocompleteArgs) =>
    this.get<{ itemsReturned: SearchAutocompleteUnit[] }>(
      `/buscaautocomplete?maxRows=${maxRows}&productNameContains=${encodeURIComponent(
        this.searchEncodeURI(searchTerm)
      )}`,
      { metric: 'search-autocomplete' }
    )

  private get = <T = any>(url: string, config: RequestConfig = {}) => {
    const segmentData: SegmentData | undefined = (this
      .context! as CustomIOContext).segment
    const { channel: salesChannel = '' } = segmentData || {}

    config.params = {
      ...config.params,
      ...(!!salesChannel && { sc: salesChannel }),
    }
    config.inflightKey = inflightKey

    return this.http.get<T>(`${this.basePath}${url}`, config)
  }

  private productSearchUrl = ({
    query = '',
    category = '',
    specificationFilters,
    priceRange = '',
    collection = '',
    salesChannel = '',
    orderBy = '',
    from = 0,
    to = 9,
    map = '',
    hideUnavailableItems = false,
    simulationBehavior = SimulationBehavior.DEFAULT,
    completeSpecifications = true,
  }: SearchArgs) => {
    const sanitizedQuery = encodeURIComponent(
      this.searchEncodeURI(decodeURIComponent(query || '').trim())
    )
    if (hideUnavailableItems) {
      const segmentData = (this.context as CustomIOContext).segment
      salesChannel = (segmentData && segmentData.channel.toString()) || ''
    }
    let url = `/pub/products/search/${sanitizedQuery}?`
    if (category && !query) {
      url += `&fq=C:/${category}/`
    }
    if (specificationFilters && specificationFilters.length > 0) {
      url += specificationFilters.map(filter => `&fq=${filter}`)
    }
    if (priceRange) {
      url += `&fq=P:[${priceRange}]`
    }
    if (collection) {
      url += `&fq=productClusterIds:${collection}`
    }
    if (salesChannel) {
      url += `&fq=isAvailablePerSalesChannel_${salesChannel}:1`
    }
    if (orderBy) {
      url += `&O=${orderBy}`
    }
    if (map) {
      url += `&map=${map}`
    }
    if (from != null && from > -1) {
      url += `&_from=${from}`
    }
    if (to != null && to > -1) {
      url += `&_to=${to}`
    }
    if (simulationBehavior === SimulationBehavior.SKIP) {
      url += `&simulation=false`
    }
    if (completeSpecifications) {
      url = this.addCompleteSpecifications(url)
    }
    return url
  }
}

```


[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 search-resolver/node/clients/checkout.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  InstanceOptions,
  IOContext,
  JanusClient,
  RequestConfig,
} from '@vtex/api'

import { statusToError } from '../utils'

export class Checkout extends JanusClient {
  public constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, {
      ...options,
      headers: {
        ...(options && options.headers),
        vtexIdclientAutCookie: ctx.authToken
      },
    })
  }

  private getChannelQueryString = (tradePolicy?: string) => {
    const { segment } = this.context as CustomIOContext
    const channel = segment && segment.channel
    const selectedTradePolicy = tradePolicy ? tradePolicy : (channel ? channel : "")
    return selectedTradePolicy ? `?sc=${selectedTradePolicy}` : ''
  }

  public simulation = (simulation: SimulationPayload, tradePolicy?: string) =>
    this.post<OrderForm>(
      this.routes.simulation(this.getChannelQueryString(tradePolicy)),
      simulation,
      {
        metric: 'checkout-simulation',
      }
    )

  protected post = <T>(url: string, data?: any, config: RequestConfig = {}) => {
    return this.http.post<T>(url, data, config).catch(statusToError) as Promise<
      T
    >
  }

  private get routes() {
    const base = '/api/checkout'
    return {
      simulation: (queryString: string) =>
        `${base}/pvt/orderForms/simulation${queryString}`,
    }
  }
}

```


[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 search-resolver/node/clients/rewriter.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AppGraphQLClient, IOContext, InstanceOptions, GraphQLResponse, Serializable } from "@vtex/api"

const getRouteQuery = `query GetRoute($id: String!, $type: String!) {
  internal {
    routes(locator: {id:$id, type:$type}) {
      route
      binding
    }
  }
}
`

interface Route {
  route: string
  binding: string
}

interface GetRoutesResponse {
  internal: {
    routes: Route[]
  }
}

class CustomGraphQLError extends Error {
  public graphQLErrors: any

  public constructor(message: string, graphQLErrors: any[]) {
    super(message)
    this.graphQLErrors = graphQLErrors
  }
}
export function throwOnGraphQLErrors<T extends Serializable>(message: string) {
  return function maybeGraphQLResponse(response: GraphQLResponse<T>) {
    if (response && response.errors && response.errors.length > 0) {
      throw new CustomGraphQLError(message, response.errors)
    }

    return response
  }
}

export class Rewriter extends AppGraphQLClient {
  public constructor(context: IOContext, options?: InstanceOptions) {
    super('vtex.rewriter@1.x', context, {
      ...options,
      headers: {
        ...options?.headers,
      }
    })
  }

  public getRoute = async (id: string, type: string, bindingId: string) => {
    const data = await this.graphql.query<GetRoutesResponse, { id: string, type: string }>({
      query: getRouteQuery,
      variables: { id, type },
    },
      {
        metric: 'search-get-route'
      }
    )
      .then(throwOnGraphQLErrors('Error getting route data from vtex.rewriter'))
      .then(data => {
        return data.data!.internal.routes
      })

    return data.find(route => route.binding === bindingId)?.route
  }
}

```


[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 search-resolver/node/clients/index.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClients } from '@vtex/api'

import { Search } from './search'
import { Checkout } from './checkout'
import { Rewriter } from './rewriter'
import { IntelligentSearchApi } from './intelligent-search-api'
import { Intsch } from './intsch'

export class Clients extends IOClients {
  public get search() {
    return this.getOrSet('search', Search)
  }

  public get checkout() {
    return this.getOrSet('checkout', Checkout)
  }

  public get rewriter() {
    return this.getOrSet('rewriter', Rewriter)
  }

  public get intelligentSearchApi() {
    return this.getOrSet('intelligentSearchApi', IntelligentSearchApi)
  }

  public get intsch() {
    return this.getOrSet('intsch', Intsch)
  }
}

```


[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 search-resolver/node/clients/intelligent-search-api.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { ExternalClient } from '@vtex/api'

import { parseState } from '../utils/searchState'
import type {
  FetchBannersArgs,
  IIntelligentSearchClient,
  FetchProductArgs,
  FetchProductResponse,
  AutocompleteSuggestionsResponse,
  CorrectionResponse,
  FetchBannersResponse,
  SearchSuggestionsResponse,
  TopSearchesResponse,
} from './intsch/types'
import type { Options, SearchResultArgs } from '../typings/Search'

export const isPathTraversal = (str: string) => str.indexOf('..') >= 0

interface CorrectionParams {
  query: string
}

interface SearchSuggestionsParams {
  query: string
}

interface AutocompleteSearchSuggestionsParams {
  query: string
}

export type FacetsArgs = {
  query?: string
  page?: number
  count?: number
  sort?: string
  operator?: string
  fuzzy?: string
  leap?: boolean
  tradePolicy?: number
  searchState?: string
  hideUnavailableItems?: boolean | null
  removeHiddenFacets?: boolean | null
  options?: Options
  initialAttributes?: string
  workspaceSearchParams?: object
  regionId?: string | null
}

export const decodeQuery = (query: string) => {
  try {
    return decodeURIComponent(query)
  } catch (e) {
    return query
  }
}

export class IntelligentSearchApi
  extends ExternalClient
  implements IIntelligentSearchClient
{
  private locale: string | undefined

  constructor(context: IOContext, options?: InstanceOptions) {
    super(
      `http://${context.workspace}--${context.account}.myvtex.com/_v/api/intelligent-search`,
      context,
      {
        ...options,
        headers: {
          ...options?.headers,
        },
      }
    )

    const { locale, tenant } = context

    this.locale = locale ?? tenant?.locale
  }

  public async fetchTopSearches() {
    return this.http.get('/top_searches', {
      params: {
        locale: this.locale,
      },
      metric: 'topSearches',
    })
  }

  public async fetchCorrection(params: CorrectionParams) {
    return this.http.get('/correction_search', {
      params: { ...params, locale: this.locale },
      metric: 'correction',
    })
  }

  public async fetchSearchSuggestions(params: SearchSuggestionsParams) {
    return this.http.get('/search_suggestions', {
      params: { ...params, locale: this.locale },
      metric: 'searchSuggestions',
    })
  }

  public async fetchAutocompleteSuggestions(
    params: AutocompleteSearchSuggestionsParams
  ) {
    return this.http.get('/autocomplete_suggestions', {
      params: { ...params, locale: this.locale },
      metric: 'autocompleteSearchSuggestions',
    })
  }

  public async fetchBanners(params: FetchBannersArgs) {
    if (isPathTraversal(params.path)) {
      throw new Error('Malformed URL')
    }

    return this.http.get(`/banners/${params.path}`, {
      params: { query: params.query, locale: this.locale },
      metric: 'banners',
    })
  }

  public async facets(
    params: FacetsArgs,
    path: string,
    shippingHeader?: string[]
  ) {
    if (isPathTraversal(path)) {
      throw new Error('Malformed URL')
    }

    const { query, leap, searchState } = params

    const authToken =
      this.context.storeUserAuthToken ?? this.context.adminUserAuthToken

    return this.http.get(`/facets/${path}`, {
      params: {
        ...params,
        query: query && decodeQuery(query),
        locale: this.locale,
        bgy_leap: leap ? true : undefined,
        ...parseState(searchState),
      },
      metric: 'facets',
      headers: {
        'x-vtex-shipping-options': shippingHeader ?? '',
        ...(authToken ? { VtexIdclientAutCookie: authToken } : {}), // This is required when the sales channel is private
      },
    })
  }

  public async productSearch(
    params: SearchResultArgs,
    path: string,
    shippingHeader?: string[]
  ) {
    const { query, leap, searchState } = params

    if (isPathTraversal(path)) {
      throw new Error('Malformed URL')
    }

    const authToken =
      this.context.storeUserAuthToken ?? this.context.adminUserAuthToken

    return this.http.get(`/product_search/${path}`, {
      params: {
        query: query && decodeQuery(query),
        locale: this.locale,
        bgy_leap: leap ? true : undefined,
        ...parseState(searchState),
        ...params,
      },
      metric: 'product-search',
      headers: {
        'x-vtex-shipping-options': shippingHeader ?? '',
        ...(authToken ? { VtexIdclientAutCookie: authToken } : {}), // This is required when the sales channel is private
      },
    })
  }

  public async sponsoredProducts(
    params: SearchResultArgs,
    path: string,
    shippingHeader?: string[]
  ) {
    const { query, leap, searchState } = params

    if (isPathTraversal(path)) {
      throw new Error('Malformed URL')
    }

    return this.http.get(`/sponsored_products/${path}`, {
      params: {
        query: query && decodeQuery(query),
        locale: this.locale,
        bgy_leap: leap ? true : undefined,
        ...parseState(searchState),
        ...params,
      },
      metric: 'product-search',
      headers: {
        'x-vtex-shipping-options': shippingHeader ?? '',
      },
    })
  }

  public async fetchAutocompleteSuggestionsV1(): Promise<AutocompleteSuggestionsResponse> {
    throw new Error('Method not implemented.')
  }

  public async fetchTopSearchesV1(): Promise<TopSearchesResponse> {
    throw new Error('Method not implemented.')
  }

  public async fetchSearchSuggestionsV1(): Promise<SearchSuggestionsResponse> {
    throw new Error('Method not implemented.')
  }

  public async fetchCorrectionV1(): Promise<CorrectionResponse> {
    throw new Error('Method not implemented.')
  }

  public async fetchBannersV1(): Promise<FetchBannersResponse> {
    throw new Error('Method not implemented.')
  }

  public async fetchProduct(
    _: FetchProductArgs
  ): Promise<FetchProductResponse> {
    throw new Error('Method not implemented.')
  }
}

```


[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 search-resolver/node/commons/compatibility-layer.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { removeDiacriticsFromURL } from '../utils/string'

export enum IndexingType {
  API = 'API',
  XML = 'XML',
}

/**
 * Convert from VTEX OrderBy into Biggy's sort.
 *
 * @export
 * @param {OrderBy} orderBy VTEX OrderBy.
 * @returns {string} Biggy's sort.
 */
export const convertOrderBy = (orderBy?: string | null): string => {
  switch (orderBy?.toLowerCase()) {
    case 'orderbypricedesc':
      return 'price:desc'
    case 'orderbypriceasc':
      return 'price:asc'
    case 'orderbytopsaledesc':
      return 'orders:desc'
    case 'orderbyreviewratedesc':
      return '' // TODO: Not Supported
    case 'orderbynamedesc':
      return 'name:desc'
    case 'orderbynameasc':
      return 'name:asc'
    case 'orderbyreleasedatedesc':
      return 'release:desc'
    case 'orderbybestdiscountdesc':
      return 'discount:desc'
    case 'orderbyscoredesc':
      return ''
    default:
      return orderBy || ''
  }
}

const encodeSafeURI = (uri: string) => encodeURI(decodeURI(uri))

export const buildAttributePath = (selectedFacets: SelectedFacet[]) => {
  return selectedFacets
    ? selectedFacets.reduce((attributePath, facet) => {
        if (facet.key === 'priceRange') {
          facet.key = 'price'
          facet.value = facet.value.replace(` TO `, ':')
        }

        return facet.key !== 'ft'
          ? `${attributePath}${encodeSafeURI(
              facet.key
            )}/${removeDiacriticsFromURL(encodeSafeURI(facet.value)).replace(
              / |%20/,
              '-'
            )}/`
          : attributePath
      }, '')
    : ''
}

```


[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 search-resolver/node/typings/unescape.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'unescape' {
  export default function (codedString: string): 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 search-resolver/node/typings/Search.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
export type SegmentData = {
  campaigns?: any
  channel: string
  priceTables?: any
  utm_campaign: string | null
  regionId?: string
  utm_source: string | null
  utmi_campaign: string | null
  currencyCode: string
  currencySymbol: string
  countryCode: string
  cultureInfo: string
  [key: string]: any
}

export type IndexingType = 'API' | 'XML'

export interface SearchResultArgs extends AdvertisementOptions {
  attributePath?: string
  query?: string
  page?: number
  count?: number
  sort?: string
  operator?: string
  fuzzy?: string
  leap?: boolean
  tradePolicy?: number
  segment?: SegmentData
  indexingType?: IndexingType
  searchState?: string
  sellers?: RegionSeller[]
  hideUnavailableItems?: boolean | null
  removeHiddenFacets?: boolean | null
  options?: Options
  initialAttributes?: string
  workspaceSearchParams?: object
  regionId?: string | null
  from?: number | null
  to?: number | null
  showSponsored?: boolean
}

export type RegionSeller = {
  id: string
  name: string
}

export interface SuggestionProductsArgs {
  fullText: string
  facetKey?: string
  facetValue?: string
  salesChannel?: number
  segment?: SegmentData
  indexingType?: IndexingType
  productOriginVtex: boolean
  simulationBehavior: 'skip' | 'default' | 'only1P' | null
  hideUnavailableItems?: boolean | null
  regionId?: string
  workspaceSearchParams?: object
  segmentedFacets?: SelectedFacet[]
  orderBy?: string
  count?: number
  shippingOptions?: string[]
  advertisementOptions: AdvertisementOptions
}

interface SelectedFacet {
  value: string
  key: string
}

export interface Options {
  allowRedirect?: boolean
}

export interface AdvertisementOptions {
  showSponsored?: boolean
  sponsoredCount?: number
  repeatSponsoredProducts?: boolean
  advertisementPlacement?: string
}

export interface FacetsInput {
  map: string
  selectedFacets: SelectedFacet[]
  fullText: string
  query: string
  searchState?: string
  removeHiddenFacets: boolean
  hideUnavailableItems: boolean
  initialAttributes?: string
  categoryTreeBehavior: 'default' | 'show' | 'hide'
}

export interface ProductsInput extends SearchArgs {
  advertisementOptions?: AdvertisementOptions
}

export interface ProductSearchInput {
  query: string
  from: number
  to: number
  selectedFacets: SelectedFacet[]
  fullText: string
  fuzzy: string
  operator: string
  orderBy: string
  productOriginVtex: boolean
  searchState?: string
  simulationBehavior: 'skip' | 'default' | null
  hideUnavailableItems: boolean
  map?: string
  options?: Options
  showSponsored?: boolean
  advertisementOptions?: AdvertisementOptions
}

```


[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 search-resolver/node/typings/Filter.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface FilterListTreeCategoryById {
  Name: string
  CategoryId: number
  FieldId: number
  isActive: boolean
  IsStockKeepingUnit: boolean
}

interface FacetValuesArgs {
  from: number
  to: number
}

//This Type represents all kind of facets, from department, to categories, to brand and specifications
interface GenericFacet extends SearchFacet {
  NameWithTranslation?: string
  Id?: string
  Children?: GenericFacet
}

interface Facet {
  name: string,
  values: GenericFacet[]
}
```


[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 search-resolver/node/typings/Checkout.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface OrderFormMarketingData {
  utmCampaign?: string
  utmMedium?: string
  utmSource?: string
  utmiCampaign?: string
  utmiPart?: string
  utmipage?: string
  marketingTags?: string
}

interface CheckoutAddress {
  addressType: string
  receiverName: string
  addressId: string
  postalCode: string
  city: string
  state: string
  country: string
  street: string
  number: string
  neighborhood: string
  complement: string
  reference: string | null
  geoCoordinates: [number, number]
}
interface OrderFormItem {
  id: string
  name: string
  detailUrl: string
  imageUrl: string
  productRefId: string
  skuName: string
  quantity: number
  uniqueId: string
  productId: string
  refId: string
  ean: string
  priceValidUntil: string
  price: number
  tax: number
  listPrice: number
  sellingPrice: number
  rewardValue: number
  isGift: boolean
  parentItemIndex: number | null
  parentAssemblyBinding: string | null
  productCategoryIds: string
  priceTags: string[]
  measurementUnit: string
  additionalInfo: {
    brandName: string
    brandId: string
    offeringInfo: any | null
    offeringType: any | null
    offeringTypeId: any | null
  }
  productCategories: Record<string, string>
  seller: string
  sellerChain: string[]
  availability: string
  unitMultiplier: number
}

interface PaymentData {
  installmentOptions: {
    paymentSystem: string
    bin: string | null
    paymentName: string | null
    paymentGroupName: string | null
    value: number
    installments: {
      count: number
      hasInterestRate: false
      interestRate: number
      value: number
      total: number
      sellerMerchantInstallments: {
        count: number
        hasInterestRate: false
        interestRate: number
        value: number
        total: number
      }[]
    }[]
  }[]
  paymentSystems: {
    id: string
    name: string
    groupName: string
    validator: {
      regex: string
      mask: string
      cardCodeRegex: string
      cardCodeMask: string
      weights: number[]
      useCvv: boolean
      useExpirationDate: boolean
      useCardHolderName: boolean
      useBillingAddress: boolean
    }
    stringId: string
    template: string
    requiresDocument: boolean
    isCustom: boolean
    description: string | null
    requiresAuthentication: boolean
    dueDate: string
    availablePayments: any | null
  }[]
  payments: any[]
  giftCards: any[]
  giftCardMessages: any[]
  availableAccounts: any[]
  availableTokens: any[]
}

interface LogisticsInfo {
    itemIndex: number
    selectedSla: string
    selectedDeliveryChannel: string
    addressId: string
    stockBalance: number
    slas: {
      id: string
      deliveryChannel: string
      name: string
      deliveryIds: {
        courierId: string
        warehouseId: string
        dockId: string
        courierName: string
        quantity: number
      }[]
      shippingEstimate: string
      shippingEstimateDate: string | null
      lockTTL: string | null
      availableDeliveryWindows: any[]
      deliveryWindow: string | null
      price: number
      listPrice: number
      tax: number
      pickupStoreInfo: {
        isPickupStore: boolean
        friendlyName: string | null

        address: CheckoutAddress | null
        additionalInfo: any | null
        dockId: string | null
      }
      pickupPointId: string | null
      pickupDistance: number
      polygonName: string | null
    }
    shipsTo: string[]
    itemId: string
    deliveryChannels: { id: string }[]

}

interface OrderForm {
  orderFormId: string
  salesChannel: string
  loggedIn: boolean
  isCheckedIn: boolean
  storeId: string | null
  checkedInPickupPointId: string | null
  allowManualPrice: boolean
  canEditData: boolean
  userProfileId: string | null
  userType: string | null
  ignoreProfileData: boolean
  value: number
  messages: any[]
  items: OrderFormItem[]
  selectableGifts: any[]
  totalizers: { id: string; name: string; value: number }[]
  logisticsInfo: LogisticsInfo[]
  shippingData: {
    address: CheckoutAddress
    selectedAddresses: CheckoutAddress[]
    availableAddresses: CheckoutAddress[]
    pickupPoints: {
      friendlyName: string
      address: CheckoutAddress
      additionalInfo: string
      id: string
      businessHours: {
        DayOfWeek: number
        OpeningTime: string
        ClosingTime: string
      }[]
    }[]
  }
  clientProfileData: any | null
  paymentData: PaymentData
  marketingData: OrderFormMarketingData | null
  sellers: {
    id: string
    name: string
    logo: string
  }[]
  clientPreferencesData: {
    locale: string
    optinNewsLetter: any | null
  }
  commercialConditionData: any | null
  storePreferencesData: {
    countryCode: string
    saveUserData: boolean
    timeZone: string
    currencyCode: string
    currencyLocale: number
    currencySymbol: string
    currencyFormatInfo: {
      currencyDecimalDigits: number
      currencyDecimalSeparator: string
      currencyGroupSeparator: string
      currencyGroupSize: number
      startsWithCurrencySymbol: boolean
    }
  }
  giftRegistryData: any | null
  openTextField: any | null
  invoiceData: any | null
  customData: any | null
  itemMetadata: {
    items: MetadataItem[]
  }
  hooksData: any | null
  ratesAndBenefitsData: RatesAndBenefitsData
  subscriptionData: any | null
  itemsOrdination: any | null
}

interface Teaser {
  featured: boolean
  id: string
  name: string
  conditions: {
    parameters: {
      name: string
      value: string
    }[]
    minimumQuantity: number
  }
  effects: {
    parameters: {
      name: string
      value: string
    }[]
  }
  teaserType: string
}
interface RatesAndBenefitsData {
  rateAndBenefitsIdentifiers: {
    id: string
    name: string
    featured: boolean
    description: string
  }[]
  teaser: Teaser[]
}

interface PayloadItem {
  id: string
  quantity: number
  seller: string
  parentItemIndex?: number | null
  parentAssemblyBinding?: string | null
}

interface SimulationPayload {
  country?: string
  items: PayloadItem[]
  postalCode?: string
  isCheckedIn?: boolean
  priceTables?: string[]
  marketingData?: Record<string, string>
  shippingData?: ShippingData
}

interface ShippingData {
  logisticsInfo?: { regionId?: string | null }[]
}

```


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

[User] Show me the implementation of search-resolver/node/typings/SearchMetadata.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface SearchMetadataArgs {
  query?: string | null
  map?: string | null
  selectedFacets?: SelectedFacet[]
}

interface SearchMetadata {
  titleTag?: string | null
  metaTagDescription?: string | null
  id?: string | null
}

```


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

[User] Show me the implementation of search-resolver/node/typings/ItemMetadata.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
/**
 * Assembly options related types
 */

interface CompositionItem {
  id: string
  minQuantity: number
  maxQuantity: number
  initialQuantity: number
  priceTable: string
  seller: string
}

interface Composition {
  minQuantity: number
  maxQuantity: number
  items: CompositionItem[]
}

interface AssemblyOption {
  id: string
  name: string
  composition: Composition | null
  inputValues: InputValues
}

interface InputValues {
  [key: string]: RawInputValue
}

interface RawInputValue {
  maximumNumberOfCharacters: number
  domain: string[]
}

interface SearchMetadataItem {
  Name: string
  NameComplete: string
  MainImage: string
  BrandName: string
  CategoryId: number
  ProductId: number
  id: string
  seller: string
  assemblyOptions: AssemblyOption[]
}

interface MetadataItem {
  id: string
  name: string
  imageUrl: string
  detailUrl: string
  seller: string
  assemblyOptions: AssemblyOption[]
  skuName: string
  productId: string
  refId: string
  ean: string | null
}
interface AddedItem {
  choiceType: string
  compositionItem: CompositionItem
  extraQuantity: number
  item: OrderFormItem
  normalizedQuantity: number
}

interface RemovedItem {
  initialQuantity: number
  name: string
  removedQuantity: 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 search-resolver/node/typings/Catalog.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface Range {
  from: number
  to: number
}

interface SelectedFacet {
  key: string
  value: string
}

interface QueryArgs {
  query: string
  map?: string
  selectedFacets?: SelectedFacet[]
}

interface SearchArgs extends QueryArgs {
  category: string | null
  specificationFilters: string[] | null
  priceRange?: string | null
  collection: string | null
  salesChannel: string | null
  orderBy: string | null
  from: number | null
  to: number | null
  hideUnavailableItems: boolean | null
  simulationBehavior: 'skip' | 'default' | null
  completeSpecifications: boolean
  shippingOptions?: string[]
}

interface Metadata {
  metaTagDescription?: string
  titleTag?: string
}

interface Brand {
  id: string
  name: string
  isActive: boolean
  title: string | null
  metaTagDescription: string | null
  imageUrl: string | null
}

interface CategoryTreeResponse {
  id: number
  name: string
  hasChildren: boolean
  url: string
  children: CategoryTreeResponse[]
  Title: string
  MetaTagDescription: string
}

interface CategoryByIdResponse {
  parentId: number | null
  GlobalCategoryId: number
  GlobalCategoryName: string
  position: number
  slug: string
  id: number
  name: string
  hasChildren: boolean
  url: string
  children: null
  Title: string
  MetaTagDescription: string
}

interface FacetsArgs extends QueryArgs {
  hideUnavailableItems?: boolean
  behavior?: FacetsBehavior
}

enum FacetsBehavior {
  STATIC = 'Static',
  DYNAMIC = 'Dynamic',
}

interface SpecificationGroup {
  name: string
  originalName: string
  specifications: { name: string; originalName: string; values: string[] }
}
interface SearchProduct {
  origin?: string
  productId: string
  productName: string
  brand: string
  brandId: number
  linkText: string
  productReference: string
  categoryId: string
  productTitle: string
  metaTagDescription: string
  clusterHighlights: Record<string, string>
  productClusters: Record<string, string>
  searchableClusters: Record<string, string>
  categories: string[]
  categoriesIds: string[]
  link: string
  description: string
  items: SearchItem[]
  itemMetadata: {
    items: SearchMetadataItem[]
  }
  titleTag: string
  Specifications?: string[]
  allSpecifications?: string[]
  allSpecificationsGroups?: string[]
  completeSpecifications?: CompleteSpecification[]
  skuSpecifications?: SkuSpecification[]
  specificationGroups?: SpecificationGroup[]
  properties?: { name: string; values: string[] }[]
}

interface SearchItem {
  itemId: string
  name: string
  nameComplete: string
  complementName: string
  ean: string
  referenceId: { Key: string; Value: string }[]
  measurementUnit: string
  unitMultiplier: number
  modalType: any | null
  images: SearchImage[]
  Videos: string[]
  videos: string[]
  variations: string[]
  sellers: Seller[]
  attachments: {
    id: number
    name: string
    required: boolean
    isRequired?: boolean // Response from intsch
    domainValues?: string
    fields?: { // Response from intsch
      fieldName: string
      maxCharacters: string
      domainValues: string
    }[]
  }[]
  isKit: boolean
  kitItems?: {
    itemId: string
    amount: number
  }[]
}

interface CompleteSpecification {
  Values: {
    Id: string
    Position: number
    Value: string
  }[]
  Name: string
  Position: number
  IsOnProductDetails: boolean
  FieldId: string
}

interface SearchItemExtended extends SearchItem {
  skuSpecifications?: SkuSpecification[]
}

interface SkuSpecification {
  field: SKUSpecificationField
  values: SKUSpecificationValue[]
}

interface SKUSpecificationField {
  name: string
  originalName?: string
  id?: string
}

interface SKUSpecificationValue {
  name: string
  id?: string
  fieldId?: string
  originalName?: string
}

interface SearchImage {
  imageId: string
  imageLabel: string | null
  imageTag: string
  imageUrl: string
  imageText: string
}

interface SearchInstallment {
  Value: number
  InterestRate: number
  TotalValuePlusInterestRate: number
  NumberOfInstallments: number
  PaymentSystemName: string
  PaymentSystemGroupName: string
  Name: string
}

interface CommertialOffer {
  DeliverySlaSamplesPerRegion: Record<
    string,
    { DeliverySlaPerTypes: any[]; Region: any | null }
  >
  Installments: SearchInstallment[]
  DiscountHighLight: any[]
  GiftSkuIds: string[]
  Teasers: object[]
  teasers?: object[]
  BuyTogether: any[]
  ItemMetadataAttachment: any[]
  Price: number
  ListPrice: number
  spotPrice?: number
  PriceWithoutDiscount: number
  RewardValue: number
  PriceValidUntil: string
  AvailableQuantity: number
  Tax: number
  DeliverySlaSamples: {
    DeliverySlaPerTypes: any[]
    Region: any | null
  }[]
  GetInfoErrorMessage: any | null
  CacheVersionUsedToCallCheckout: string
  // Supports the Intelligent Search API which
  // uses the same name from the simulation
  discountHighlights: any[]
}

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

interface SearchFacet {
  Quantity: number
  Name: string
  Link: string
  LinkEncoded: string
  Map: string
  Value: string
}

interface SearchFacetCategory {
  Id: number
  Quantity: number
  Name: string
  Link: string
  LinkEncoded: string
  Map: string
  Value: string
  Children: SearchFacetCategory[]
}

interface SummaryItem {
  DisplayedItems: number
  TotalItems: number
}

interface SearchFacets {
  Departments: SearchFacet[]
  Brands: SearchFacet[]
  SpecificationFilters: Record<string, SearchFacet[]>
  CategoriesTrees: SearchFacetCategory[]
  PriceRanges: {
    Slug: string
    Quantity: number
    Name: string
    Link: string
    LinkEncoded: string
    Map: null
    Value: string
  }[]
  Summary: {
    Departments: SummaryItem
    CategoriesTrees: SummaryItem
    Brands: SummaryItem
    PriceRanges: SummaryItem
    SpecificationFilters: Record<string, SummaryItem>
  }
}

interface SearchAutocompleteItem {
  productId: string
  itemId: string
  name: string
  nameComplete: string
  imageUrl: string
}

interface SearchAutocompleteUnit {
  items: SearchAutocompleteItem[]
  thumb: string
  thumbUrl: string | null
  name: string
  href: string
  criteria: string
}

interface FieldTreeResponseAPI {
  Name: string
  CategoryId: number
  FieldId: number
  IsActive: boolean
  IsStockKeepingUnit: boolean
}

interface FieldResponseAPI {
  Name: string
  CategoryId: number | null
  FieldId: number
  IsActive: boolean
  IsRequired: boolean
  FieldTypeId: number
  FieldTypeName: string
  FieldValueId: string | null
  Description: string | null
  IsStockKeepingUnit: boolean
  IsFilter: boolean
  IsOnProductDetails: boolean
  Position: number
  IsWizard: boolean
  IsTopMenuLinkActive: boolean
  IsSideMenuLinkActive: boolean
  DefaultValue: string | null
  FieldGroupId: number
  FieldGroupName: string
}

interface FieldValuesResponseAPI {
  FieldValueId: number
  Value: string
  Position: number
  IsActive: boolean
}

```


[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 search-resolver/node/utils/categoryTreeSearch.ts from the search-resolver VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { searchSlugify } from "./slug"

export const categoryTreeSearch = (categoryTree: CategoryTreeResponse[], query: string) => {
  const querySegments = query.startsWith('/')? query.split('/').slice(1): query.split('/')
  return bfsCategory(categoryTree, querySegments, 0)
}

const bfsCategory = (categoryTree: CategoryTreeResponse[], segments: string[], depth: number): CategoryTreeResponse[]  => {
  if(segments.length === 0 || segments.length === depth){
    return []
  }

  for (const categoryNode of categoryTree){
    if(searchSlugify(categoryNode.name) === segments[depth]){
      return [categoryNode, ...bfsCategory(categoryNode.children, segments, depth + 1)]
    }
  }

  segments.shift()
  return []
}
```