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

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

[Assistant]
Here's the implementation:

```tsx
import type { ReactChildren, ReactChild } from 'react'
import React from 'react'
import { ToastProvider } from 'vtex.styleguide'

import ComparisonContext from './ProductComparisonContext'
import ComparisonProductWrapper from './ComparisonProductWrapper'

interface Props {
  children: ReactChildren | ReactChild
  maxNumberOfItemsToCompare: number | undefined
}
const ProductComparisonWrapper = ({ children }: Props) => {
  const { ProductComparisonProvider } = ComparisonContext

  return (
    <ProductComparisonProvider>
      <ComparisonProductWrapper>
        <ToastProvider positioning="window">{children}</ToastProvider>
      </ComparisonProductWrapper>
    </ProductComparisonProvider>
  )
}

export default ProductComparisonWrapper

```


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

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

[Assistant]
Here's the implementation:

```tsx
import ProductSummaryRow from './components/comparisonPageRow/ProductSummaryRow'

export default ProductSummaryRow

```


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

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

[Assistant]
Here's the implementation:

```tsx
import ProductSummaryList from './components/productSummaryList/ProductSummaryList'

export default ProductSummaryList

```


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

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

[Assistant]
Here's the implementation:

```tsx
import RemoveButton from './components/productRemover/RemoveButton'

export default RemoveButton

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "scripts": {
    "test": "vtex-test-tools test",
    "lint": "tsc --noEmit && eslint --ext ts,tsx ."
  },
  "dependencies": {
    "@vtex/css-handles": "^1.0.0",
    "classnames": "^2.2.6",
    "ramda": "^0.26.1",
    "react": "^16.8.3",
    "react-dom": "^16.8.3",
    "react-intl": "^2.7.2",
    "react-apollo": "^3.1.3"
  },
  "devDependencies": {
    "@types/classnames": "^2.2.7",
    "@types/jest": "^24.0.18",
    "@types/node": "^12.7.5",
    "@types/prop-types": "^15.7.0",
    "@types/ramda": "^0.26.5",
    "@types/react": "^16.9.2",
    "@vtex/test-tools": "^1.1.0",
    "eslint": "^5.16.0",
    "eslint-config-vtex-react": "^4.1.0",
    "prettier": "^1.18.2",
    "tslint-eslint-rules": "^5.4.0",
    "typescript": "3.9.7",
    "vtex.apps-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.apps-graphql@2.8.0/public/@types/vtex.apps-graphql",
    "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles",
    "vtex.flex-layout": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.flex-layout@0.16.0/public/@types/vtex.flex-layout",
    "vtex.list-context": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.list-context@0.2.0/public/@types/vtex.list-context",
    "vtex.pixel-manager": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.pixel-manager@1.8.0/public/@types/vtex.pixel-manager",
    "vtex.product-list-context": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.product-list-context@0.3.1/public/@types/vtex.product-list-context",
    "vtex.product-summary": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.product-summary@2.71.2/public/@types/vtex.product-summary",
    "vtex.product-summary-context": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.product-summary-context@0.7.0/public/@types/vtex.product-summary-context",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.128.3/public/@types/vtex.render-runtime",
    "vtex.slider-layout": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.slider-layout@0.18.0/public/@types/vtex.slider-layout",
    "vtex.store-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.140.0/public/@types/vtex.store-graphql",
    "vtex.store-icons": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-icons@0.18.0/public/@types/vtex.store-icons",
    "vtex.store-resources": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-resources@0.79.0/public/@types/vtex.store-resources",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.138.2/public/@types/vtex.styleguide"
  },
  "version": "0.20.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 product-comparison/react/GroupedProductSpecifications.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import GroupedProductSpecifications from './components/comparisonFields/GroupedProductSpecifications'

export default GroupedProductSpecifications

```


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

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

[Assistant]
Here's the implementation:

```tsx
import ComparisonGridRowContent from './components/comparisonPageRow/ComparisonGridRowContent'

export default ComparisonGridRowContent

```


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

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

[Assistant]
Here's the implementation:

```tsx
import type { ReactChild, ReactChildren } from 'react'
import React from 'react'
import { isEmpty, pathOr } from 'ramda'
import { useCssHandles } from 'vtex.css-handles'
import {
  Button,
  Layout,
  PageBlock,
  PageHeader,
  Spinner,
  withToast,
} from 'vtex.styleguide'
import type { InjectedIntlProps } from 'react-intl'
import { defineMessages, injectIntl } from 'react-intl'

import ComparisonContext from './ProductComparisonContext'
import './global.css'

const CSS_HANDLES = ['pageContainer', 'sortBy', 'removeAllItemsButtonWrapper']

interface Props extends InjectedIntlProps {
  children: ReactChildren | ReactChild
  showToast?: (input: ToastInput) => void
}

const messages = defineMessages({
  removeAll: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.remove-all',
  },
  removeAllMessage: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.remove-all-message',
  },
  backToProducts: {
    defaultMessage: '',
    id: 'store/product-comparison.main-page.back-to-products',
  },
  sortBy: {
    defaultMessage: '',
    id: 'store/product-comparison.main-page.sort-by',
  },
  orderAdded: {
    defaultMessage: '',
    id: 'store/product-comparison.main-page.order-added',
  },
  title: {
    defaultMessage: '',
    id: 'store/product-comparison.main-page.title',
  },
})

const ComparisonPage = ({ children, intl, showToast }: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)
  const {
    useProductComparisonState,
    useProductComparisonDispatch,
  } = ComparisonContext

  const comparisonData = useProductComparisonState()
  const dispatchComparison = useProductComparisonDispatch()

  const comparisonProducts = pathOr(
    [] as ProductToCompare[],
    ['products'],
    comparisonData
  )

  const showMessage = (message: string) => {
    if (showToast) {
      showToast({
        message,
      })
    }
  }

  const removeAllItems = () => {
    dispatchComparison({
      type: 'REMOVE_ALL',
    })
    showMessage(intl.formatMessage(messages.removeAllMessage))
    window.history.back()
  }

  const onBackButtonClick = () => {
    window.history.back()
  }

  return isEmpty(comparisonProducts) ? (
    <div className="mw3 center">
      <Spinner />
    </div>
  ) : (
    <div className={`${cssHandles.pageContainer} mw9 center`}>
      <Layout
        fullWidth
        pageHeader={
          <PageHeader
            title={intl.formatMessage(messages.title, {
              productsLength: ` ${comparisonProducts.length}`,
            })}
            linkLabel={intl.formatMessage(messages.backToProducts)}
            onLinkClick={onBackButtonClick}
          >
            <div className="flex">
              <div className={`${cssHandles.sortBy} flex items-center`}>
                {/* <span className="mr2">
                  {intl.formatMessage(messages.sortBy)}
                </span>
                <Dropdown
                  variation="inline"
                  size="default"
                  options={[
                    { value: 'productName', label: 'Product Name' },
                    { value: 'price', label: 'Price' },
                  ]}
                  value={`productName`}
                /> */}
              </div>
              <div className={cssHandles.removeAllItemsButtonWrapper}>
                <Button
                  variation="tertiary"
                  size="regular"
                  onClick={removeAllItems}
                >
                  {intl.formatMessage(messages.removeAll)}
                </Button>
              </div>
            </div>
          </PageHeader>
        }
      >
        <PageBlock>{children}</PageBlock>
      </Layout>
    </div>
  )
}

export default withToast(injectIntl(ComparisonPage))

```


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

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

[Assistant]
Here's the implementation:

```tsx
import SkuSpecifications from './components/comparisonFields/SkuSpecifications'

export default SkuSpecifications

```


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

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

[Assistant]
Here's the implementation:

```tsx
import ComparisonDrawer from './components/drawer/ComparisonDrawer'

export default ComparisonDrawer

```


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

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

[Assistant]
Here's the implementation:

```tsx
import ProductSelector from './components/productSelector/ProductSelector'

export default ProductSelector

```


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

[User] Show me the implementation of product-comparison/react/graphQLTypes.ts from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.

//==============================================================
// START Enums and Input Objects
//==============================================================

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

export interface ProductUniqueIdentifier {
  field: ProductUniqueIdentifierField
  value: string
}

//==============================================================
// END Enums and Input Objects
//==============================================================

```


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

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

[Assistant]
Here's the implementation:

```json
{
  // START: Product comparison drawer
  "product-comparison-drawer": {
    "blocks": ["list-context.comparison-product-summary-slider#drawer"]
  },
  "list-context.comparison-product-summary-slider#drawer": {
    "blocks": ["product-summary.shelf.product-comparison#drawer"],
    "children": ["slider-layout#comparison-drawer"]
  },
  "slider-layout#comparison-drawer": {
    "props": {
      "blockClass": "comparison-drawer",
      "itemsPerPage": {
        "desktop": 4,
        "tablet": 3,
        "phone": 1
      },
      "showPaginationDots": "never",
      "infinite": true,
      "fullWidth": true
    }
  },
  "product-summary.shelf.product-comparison#drawer": {
    "children": [
      "product-summary-column#drawer-col1",
      "product-summary-column#drawer-col2",
      "product-comparison-block.close-button"
    ],
    "props": {
      "blockClass": "drawer-summary"
    }
  },
  "product-summary-column#drawer-col1": {
    "children": ["product-summary-image#drawer"],
    "props": {
      "blockClass": "drawer-summary-col1"
    }
  },
  "product-summary-image#drawer": {
    "props": {
      "showBadge": false,
      "width": 75,
      "heightProp": 75
    }
  },
  "product-summary-column#drawer-col2": {
    "children": ["product-summary-name", "product-summary-price#comparison"],
    "props": {
      "blockClass": "drawer-summary-col2"
    }
  },
  "product-summary-price#comparison": {
    "props": {
      "showListPrice": false,
      "showSellingPriceRange": false,
      "showLabels": false,
      "showInstallments": false,
      "showDiscountValue": false
    }
  },
  // START: Product comparison drawer

  // START: Product comparison page
  "comparison-page": {
    "children": ["slider-layout-group#comparison-page"]
  },

  "slider-layout-group#comparison-page": {
    "children": [
      "product-comparison-block.product-summary-row",
      "product-comparison-block.grouped-product-specifications"
    ]
  },
  "product-comparison-block.product-summary-row": {
    "blocks": ["list-context.comparison-product-summary-slider#comparison-page"]
  },
  "list-context.comparison-product-summary-slider#comparison-page": {
    "blocks": ["product-summary.shelf.product-comparison#comparison-page"],
    "children": ["slider-layout#comparison-page-product-summary"]
  },
  "product-summary.shelf.product-comparison#comparison-page": {
    "children": [
      "flex-layout.row",
      "product-summary-image#comparison-page",
      "product-summary-name",
      "product-summary-space",
      "product-summary-price#comparison",
      "product-summary-buy-button"
    ],
    "props": {
      "blockClass": "comparison-page-summary"
    }
  },
  "flex-layout.row": {
    "children": ["product-comparison-block.close-button"],
    "props": {
      "blockClass": "close",
      "horizontalAlign": "right"
    }
  },

  "product-summary-image#comparison-page": {
    "props": {
      "width": 200,
      "heightProp": 200
    }
  },
  "product-comparison-block.grouped-product-specifications": {
    "blocks": ["list-context.comparison-row#comparison-page-row"]
  },
  // "product-comparison-block.product-specifications": {
  //   "blocks": ["list-context.comparison-row#comparison-page-row"],
  //   "props": {
  //     "titleText": "Product Specifications"
  //   }
  // },
  // "product-comparison-block.sku-specifications": {
  //   "blocks": ["list-context.comparison-row#comparison-page-row"],
  //   "props": {
  //     "titleText": "Sku Specifications"
  //   }
  // },
  "list-context.comparison-row#comparison-page-row": {
    "children": ["slider-layout#comparison-no-arrows"]
  },
  "slider-layout#comparison-page-product-summary": {
    "props": {
      "blockClass": "comparison-page",
      "itemsPerPage": {
        "desktop": 4,
        "tablet": 3,
        "phone": 1
      },
      "showPaginationDots": "never",
      "infinite": true,
      "fullWidth": true
    }
  },
  "slider-layout#comparison-no-arrows": {
    "props": {
      "itemsPerPage": {
        "desktop": 4,
        "tablet": 3,
        "phone": 1
      },
      "showPaginationDots": "never",
      "infinite": true,
      "fullWidth": true,
      "blockClass": "comparison-page",
      "showNavigationArrows": "never"
    }
  }
  // END: Product comparison page
}

```


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

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

[Assistant]
Here's the implementation:

```json
{
  "comparison-context-wrapper": {
    "component": "ProductComparisonWrapper"
  },

  "product-comparison-block": {},

  "product-comparison-block.selector": {
    "component": "ProductSelector"
  },

  "product-comparison-drawer": {
    "allowed": ["list-context.comparison-product-summary-slider"],
    "component": "ComparisonDrawer"
  },

  "list-context.comparison-product-summary-slider": {
    "allowed": ["product-summary"],
    "composition": "children",
    "component": "ProductSummaryList"
  },

  "comparison-page": {
    "around": ["comparison-context-wrapper"],
    "component": "ComparisonPage",
    "composition": "children"
  },

  "product-comparison-block.product-summary-row": {
    "component": "ProductSummaryRow",
    "allowed": ["list-context.comparison-product-summary-slider"]
  },

  "product-comparison-block.product-specifications": {
    "allowed": ["list-context.comparison-row"],
    "component": "ProductSpecifications"
  },

  "product-comparison-block.grouped-product-specifications": {
    "allowed": ["list-context.comparison-row"],
    "component": "GroupedProductSpecifications"
  },

  "product-comparison-block.sku-specifications": {
    "allowed": ["list-context.comparison-row"],
    "component": "SkuSpecifications"
  },

  "product-comparison-block.close-button": {
    "component": "RemoveButton"
  },

  "list-context.comparison-row": {
    "composition": "children",
    "component": "ComparisonGridRowContent"
  },

  "product-summary.shelf.product-comparison": {
    "allowed": ["product-comparison-block"]
  }
}

```


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

[User] Show me the implementation of product-comparison/react/typings/vtex.list-context.d.ts from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.list-context*' {
  import type { Context, Provider } from 'react'

  export const useListContext: Context
  export const ListContextProvider: Provider
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.product-summary-context*' {
  import type { Context } from 'react'

  export const useProductSummary: Context
}

```


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

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

[Assistant]
Here's the implementation:

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

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

  interface StorefrontComponent<P = {}, S = {}> extends Component<P, S> {
    getSchema?(props: P): object
    schema: object
  }
}

```


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

[User] Show me the implementation of product-comparison/react/typings/vtex.product-list-context.d.ts from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.product-list-context*' {
  import type { Context, Provider } from 'react'

  export const ProductListContext: Context
  export const useProductImpression: Provider
}

```


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

[User] Show me the implementation of product-comparison/react/typings/vtex.render-runtime.d.ts from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
/* Typings for `render-runtime` */
declare module 'vtex.render-runtime' {
  import type { ComponentType, ReactElement, ReactType } from 'react'
  import { Component } from 'react'

  export interface NavigationOptions {
    page: string
    params?: any
  }

  export interface RenderContextProps {
    runtime: {
      navigate: (options: NavigationOptions) => void
    }
  }

  interface ExtensionPointProps {
    id: string
    [key: string]: any
  }

  export const ExtensionPoint: ComponentType<ExtensionPointProps>
  export const useTreePath: any

  interface ChildBlockProps {
    id: string
  }

  export const ChildBlock: ComponentType<ChildBlockProps>
  export const useChildBlock = function ({ id: string }): object {}

  export const Helmet: ReactElement
  export const Link: ReactType
  export const NoSSR: ReactElement
  export const RenderContextConsumer: ReactElement
  export const canUseDOM: boolean
  export const withRuntimeContext: <TOriginalProps extends {}>(
    Component: ComponentType<TOriginalProps & RenderContextProps>
  ) => ComponentType<TOriginalProps>
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.store-icons' {
  import type { ComponentType } from 'react'

  interface Props {
    size: number
    viewBox?: string
    orientation?: string
    type?: string
  }

  export const IconClose: ComponentType<Props>
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
interface ToastInput {
  message: string
  action?: {
    label: string
    onClick: () => void
  }
}

interface ProductToCompare {
  productId: string
  skuId: string
}
interface Sku {
  itemId: string
  [key: string]: any | unknown
}

interface Product {
  productId: string
  [key: string]: any | unknown
}

interface ComparisonItem {
  imageUrl: string
  product: Product
  selectedSku: Sku
}

interface ComparisonThumbnail {
  productId: string
  skuId: string
  productName: string
  skuName: string
  imageUrl: string
}

interface CommercialOffer {
  [key: string]: any | unknown
}

interface SkuImage {
  imageUrl: string
  imageTag: string
}

interface Styles {
  [key: string]: any | unknown
}

interface GroupedComparisonFields {
  [key: string]: ComparisonField[]
}

interface ComparisonField {
  fieldType: string
  groupName?: string
  name: string
  displayValue: string
  showOnSite: boolean
}

interface ProductSpecificationGroup {
  name: string
  originalName: string
  specifications: ProductSpecification[]
}

interface ProductSpecification {
  name: string
  values: string[]
  groupName?: string
}

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

interface SpecificationGroupValues {
  [key: string]: ProductSpecification[]
}

```


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

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

[Assistant]
Here's the implementation:

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

```


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

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

[Assistant]
Here's the implementation:

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

  const value: DocumentNode
  export default value
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.store-resources/Queries' {
  import type { DocumentNode } from 'graphql'

  export const orderForm: DocumentNode
}

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

  const document: DocumentNode
  export = document
}

```


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

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

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
declare module 'vtex.styleguide' {
  import type { ComponentType } from 'react'

  export const Input: ComponentType<InputProps>
  export const Checkbox: ComponentType<InputProps>
  export const Button: ComponentType<InputProps>
  export const Collapsible: ComponentType<InputProps>
  export const Layout: ComponentType<InputProps>
  export const PageHeader: ComponentType<InputProps>
  export const PageBlock: ComponentType<InputProps>
  export const ToastProvider: ComponentType<InputProps>
  export const Spinner: ComponentType<InputProps>
  export const ToastConsumer: ComponentType<InputProps>
  export const Dropdown: ComponentType<InputProps>
  export const withToast

  interface InputProps {
    [key: string]: any
  }
}

```


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

[User] Show me the implementation of product-comparison/react/queries/productsByIdentifier.graphql from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```graphql
query productsByIdentifier($ids: [ID!]) {
  productsByIdentifier(field: id, values: $ids)
    @context(provider: "vtex.search-graphql") {
    cacheId
    productId
    productName
    description
    categories
    categoryTree {
      name
      href
    }
    link
    linkText
    brand
    items {
      name
      itemId
      referenceId {
        Value
      }
      images {
        imageUrl
        imageTag
      }
      sellers {
        sellerId
        commertialOffer {
          Installments {
            Value
            InterestRate
            TotalValuePlusInterestRate
            NumberOfInstallments
            Name
          }
          AvailableQuantity
          Price
          ListPrice
        }
      }
    }
    productClusters {
      id
      name
    }
  }
}
```


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

[User] Show me the implementation of product-comparison/react/queries/productThumbnails.graphql from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```graphql
query Product(
  $slug: String
  $identifier: ProductUniqueIdentifier
  $skipCategoryTree: Boolean = false
) {
  product(slug: $slug, identifier: $identifier)
    @context(provider: "vtex.search-graphql") {
    cacheId
    productName
    productId
    categoryTree @skip(if: $skipCategoryTree) {
      id
      name
      href
    }
    items {
      itemId
      name
      nameComplete
      images {
        imageId
        imageLabel
        imageUrl
        imageText
      }
    }
  }
}
```


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

[User] Show me the implementation of product-comparison/react/queries/AppSettings.graphql from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```graphql
query AppSettings($version: String) {
  publicSettingsForApp(app: "vtex.product-comparison", version: $version)
    @context(provider: "vtex.apps-graphql") {
    message
  }
}

```


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

[User] Show me the implementation of product-comparison/react/__tests__/index.test.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

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

// describe('HelloWorld Component', () => {
//   const { getByText } = render(<Component />)

//   it('should render the example in TypeScript', () => {
//     expect(getByText(/This is an example/)).toBeDefined()
//   })
// })

```


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

[User] Show me the implementation of product-comparison/react/components/drawer/drawer.css from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```css
.drawer {
}

.compareProductsButton {
}

.drawerContainer {
  position: fixed;
  max-height: 500px;
  bottom: 0;
  left: 0;
  z-index: 2;
}

.drawerContainer :global(.vtex__icon-caret-up) {
  display: none;
}

.drawerContainer :global(.vtex__icon-caret-down) {
  display: none;
}

.drawerContainer :global(.vtex-product-summary-2-x-element--drawer-summary) {
  flex-direction: row;
}

.drawerContainer
  :global(.vtex-product-summary-2-x-column--drawer-summary-col2) {
  flex-direction: column;
  padding-left: 1rem;
}

.drawerContainer
  :global(.vtex-product-summary-2-x-container--drawer-summary)
  :global(.vtex-product-summary-2-x-nameContainer),
.drawerContainer
  :global(.vtex-product-summary-2-x-container--drawer-summary)
  :global(.vtex-product-summary-2-x-priceContainer) {
  padding-top: 5px;
  padding-bottom: 5px;
}

/* .drawerContainer
  :global(.vtex-product-summary-2-x-container--drawer-summary)
  :global(.vtex-product-summary-2-x-nameContainer) {
  text-overflow: ellipsis;
  overflow: hidden;
  width: 175px;
  white-space: nowrap;
} */

.drawerContainer :global(.vtex-product-summary-2-x-container--drawer-summary) {
  max-width: 275px;
}

.expandCollapseButton {
}

.comparisonButtons {
}

```


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

[User] Show me the implementation of product-comparison/react/components/drawer/ComparisonDrawer.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import React from 'react'
import { pathOr, isEmpty } from 'ramda'
import { Button, Collapsible, withToast } from 'vtex.styleguide'
import { ExtensionPoint, useRuntime } from 'vtex.render-runtime'
import { useCssHandles } from 'vtex.css-handles'
import type { InjectedIntlProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'

import ComparisonContext from '../../ProductComparisonContext'
import './drawer.css'

const CSS_HANDLES = [
  'drawerContainer',
  'expandCollapseButton',
  'comparisonButtons',
  'compareProductsButton',
  'drawer',
  'compareProductButtonWrapper',
  'removeAllWrapper',
  'hideOrShowText',
  'drawerTitleOuterContainer',
  'drawerTitleInnerContainer',
  'drawerOpened',
  'drawerClosed'
]

const messages = defineMessages({
  removeAll: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.remove-all',
  },
  products: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.products',
  },
  compare: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.compare',
  },
  removeAllMessage: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.remove-all-message',
  },
  show: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.show',
  },
  hide: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.hide',
  },
  minItemsMessage: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.min-items-message',
  },
  maxItemsMessage1: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.max-items-message-1',
  },
  maxItemsMessage2: {
    defaultMessage: '',
    id: 'store/product-comparison.drawer.max-items-message-2',
  },
})

interface Props extends InjectedIntlProps {
  showToast?: (input: ToastInput) => void
  comparisonPageUrl?: string
}

const ComparisonDrawer = ({ showToast, intl, comparisonPageUrl }: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)
  const { navigate } = useRuntime()
  // const [isCollapsed, setCollapsed] = useState(false)
  const {
    useProductComparisonState,
    useProductComparisonDispatch,
  } = ComparisonContext

  const comparisonData = useProductComparisonState()
  const dispatchComparison = useProductComparisonDispatch()

  const comparisonProducts = pathOr(
    [] as ProductToCompare[],
    ['products'],
    comparisonData
  )

  const isCollapsed = pathOr(false, ['isDrawerCollapsed'], comparisonData)

  const showMessage = (message: string) => {
    if (showToast) {
      showToast({
        message,
      })
    }
  }

  const removeAllItems = () => {
    dispatchComparison({
      type: 'REMOVE_ALL',
    })
    showMessage(intl.formatMessage(messages.removeAllMessage))
  }

  const onExpandCollapse = () => {
    dispatchComparison({
      args: {
        isDrawerCollapsed: !isCollapsed,
      },
      type: 'IS_DRAWER_COLLAPSED',
    })
  }

  const onClickCompare = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    if (!comparisonProducts || comparisonProducts.length < 2) {
      showMessage(intl.formatMessage(messages.minItemsMessage))
      e.preventDefault()
      e.stopPropagation()
    } else if (comparisonProducts.length > 10) {
      showMessage(
        `${intl.formatMessage(
          messages.maxItemsMessage1
        )} ${10} ${intl.formatMessage(messages.maxItemsMessage2)}`
      )
      e.preventDefault()
      e.stopPropagation()
    }
  }

  const navigateToComparisonPage = () => {
    const url =
      comparisonProducts.length < 2
        ? '#'
        : comparisonPageUrl || '/product-comparison'

    navigate({ to: url })
  }

  return isEmpty(comparisonProducts) ? (
    <div />
  ) : (
    <div
      className={`${cssHandles.drawerContainer} ${isCollapsed ? cssHandles.drawerClosed : cssHandles.drawerOpened} bg-white w-100 bt-ns b--light-gray flex justify-center`}
    >
      <div className="mw9 w-100 ">
        <Collapsible
          header={
            <div
              className={`${cssHandles.comparisonButtons} flex flex-row ma3`}
            >
              <div className={`flex items-center-ns mr2 ml2  ${cssHandles.drawerTitleOuterContainer}`}>
                <span className={`fw5 black  ${cssHandles.drawerTitleInnerContainer}`}>
                  <span>{intl.formatMessage(messages.compare)} </span>{' '}
                  <span>{comparisonProducts.length}</span>{' '}
                  <span>{intl.formatMessage(messages.products, { productsLength: comparisonProducts.length })}</span>
                </span>
              </div>
              <div className="flex-grow-1" />
              <div className="flex mr2 ml2">
                <button
                  onClick={onExpandCollapse}
                  className={`${cssHandles.expandCollapseButton} bg-transparent bn-ns t-small c-action-primary hover-c-action-primary pointer`}
                >
                  <span className={cssHandles.hideOrShowText}>
                    {!isCollapsed
                      ? intl.formatMessage(messages.hide)
                      : intl.formatMessage(messages.show)}
                  </span>
                </button>
              </div>
              <div className={`flex mr2 ml2 ${cssHandles.removeAllWrapper}`}>
                <Button
                  block
                  variation="danger-tertiary"
                  size="small"
                  onClick={removeAllItems}
                >
                  {intl.formatMessage(messages.removeAll)}
                </Button>
              </div>
              <div
                className={`flex mr2 ml2 ${cssHandles.compareProductButtonWrapper}`}
                onClick={onClickCompare}
              >
                <Button
                  block
                  size="small"
                  className={`${cssHandles.compareProductsButton} ma3`}
                  onClick={navigateToComparisonPage}
                >
                  {intl.formatMessage(messages.compare)}
                </Button>
              </div>
            </div>
          }
          isOpen={!isCollapsed}
        >
          <div
            className={`${cssHandles.drawer} flex flex-row justify-center pl3 pr3`}
          >
            <ExtensionPoint id="list-context.comparison-product-summary-slider" />
          </div>
        </Collapsible>
      </div>
    </div>
  )
}

ComparisonDrawer.schema = {
  title: 'admin/editor.comparison-drawer.title',
  description: 'admin/editor.comparison-drawer.description',
  type: 'object',
  properties: {
    comparisonPageUrl: {
      title: 'admin/editor.comparison-grid.drawer.title',
      type: 'string',
    },
  },
}

export default withToast(injectIntl(ComparisonDrawer))

```


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

[User] Show me the implementation of product-comparison/react/components/productSelector/ProductSelector.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable jsx-a11y/no-static-element-interactions */
import type { MouseEvent } from 'react'
import React, { useState, useEffect } from 'react'
import { pathOr, find, propEq, allPass, isEmpty } from 'ramda'
import { Checkbox, withToast } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'
import { useProductSummary } from 'vtex.product-summary-context/ProductSummaryContext'
import type { InjectedIntlProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'
import { useProduct } from 'vtex.product-context'

import ComparisonContext from '../../ProductComparisonContext'

const CSS_HANDLES = ['productSelectorContainer']

const messages = defineMessages({
  product: {
    defaultMessage: '',
    id: 'store/product-comparison.product-selector.product',
  },
  added: {
    defaultMessage: '',
    id: 'store/product-comparison.product-selector.product-added',
  },
  removed: {
    defaultMessage: '',
    id: 'store/product-comparison.product-selector.product-removed',
  },
  compare: {
    defaultMessage: '',
    id: 'store/product-comparison.product-selector.compare',
  },
  comparisonUpperLimit: {
    defaultMessage: '',
    id: 'store/product-comparison.product-selector.upper-limit-exceeded',
  },
})

interface Props extends InjectedIntlProps {
  showToast?: (input: ToastInput) => void
}
const getContextValue = (
  productContext: unknown,
  productSummaryContext: unknown
) => {
  let contextValue = productSummaryContext !== undefined? productSummaryContext : productContext
    let productId = pathOr('', ['product', 'productId'], contextValue)
    let productName = pathOr('', ['product', 'productName'], contextValue)
    let itemId = pathOr('', ['selectedItem', 'itemId'], contextValue)
    return { productName, productId, itemId }
}
const ProductSelector = ({ showToast, intl }: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)
  const [isChecked, setIsChecked] = useState(false)
  const valuesFromContext = useProductSummary()
  const valuesFromProductContext = useProduct()
  const {productId, productName, itemId} = getContextValue(
    valuesFromProductContext,
    valuesFromContext
  )
  const {
    useProductComparisonState,
    useProductComparisonDispatch,
  } = ComparisonContext

  const comparisonData = useProductComparisonState()
  const dispatchComparison = useProductComparisonDispatch()

  const isDrawerCollapsed = pathOr(false, ['isDrawerCollapsed'], comparisonData)
  const productsSelected = pathOr([], ['products'], comparisonData)
  const maxItemsToCompare = pathOr(
    0,
    ['maxNumberOfItemsToCompare'],
    comparisonData
  )

  useEffect(() => {
    const selectedProducts =
      productId && itemId
        ? find(
            allPass([propEq('productId', productId), propEq('skuId', itemId)])
          )(productsSelected)
        : []

    setIsChecked(selectedProducts && !isEmpty(selectedProducts))
  }, [productsSelected, itemId, productId])

  const showMessage = (message: string, show = true) => {
    if (showToast && show) {
      showToast({
        message,
      })
    }
  }

  const productSelectorChanged = (e: { target: { checked: boolean } }) => {
    if (e.target.checked && productsSelected.length === maxItemsToCompare) {
      setIsChecked(false)
      showMessage(`${intl.formatMessage(messages.comparisonUpperLimit)}`, true)
    } else if (e.target.checked) {
      dispatchComparison({
        args: {
          product: { productId, skuId: itemId },
        },
        type: 'ADD',
      })
      showMessage(
        `${intl.formatMessage(
          messages.product
        )} "${productName}" ${intl.formatMessage(messages.added)}`,
        isDrawerCollapsed
      )
    } else {
      dispatchComparison({
        args: {
          product: { productId, skuId: itemId },
        },
        type: 'REMOVE',
      })
      showMessage(
        `${intl.formatMessage(
          messages.product
        )} "${productName}" ${intl.formatMessage(messages.removed)}`,
        isDrawerCollapsed
      )
    }
  }

  const productSelectionOnClicked = (e: MouseEvent) => {
    e.preventDefault()
    e.stopPropagation()
  }

  return (
    // eslint-disable-next-line jsx-a11y/no-static-element-interactions
    // eslint-disable-next-line jsx-a11y/click-events-have-key-events
    <div
      onClick={productSelectionOnClicked}
      className={`${cssHandles.productSelectorContainer} mb3`}
    >
      <Checkbox
        checked={isChecked}
        id={`${productId}-${itemId}-product-comparison`}
        label={intl.formatMessage(messages.compare)}
        name={`${productId}-${itemId}-product-comparison`}
        onChange={productSelectorChanged}
        value={isChecked}
      />
    </div>
  )
}

export default withToast(injectIntl(ProductSelector))

```


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

[User] Show me the implementation of product-comparison/react/components/utils/fieldUtils.ts from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { split, trim, without } from 'ramda'

export const splitString = (text?: string) => {
  if (!(text && text.length > 0)) {
    return []
  }

  return split(',', text)
    .filter((str) => str !== null && str !== '')
    .map((str) => trim(str))
}

export const getProductSpecificationFields = (
  existing: string[],
  fieldsToHide?: string
) => {
  const removeList = splitString(fieldsToHide)

  return without(removeList, existing)
}

export const getSkuSpecificationFields = (
  existing: string[],
  fieldsToHide?: string
) => {
  const removeList = splitString(fieldsToHide)

  return without(removeList, existing)
}

export const getProductFields = (fieldsToHide?: string) => {
  const fieldNames = ['productName', 'brand', 'description', 'productReference']
  const removeList = splitString(fieldsToHide)
  const fieldsSelected = without(removeList, fieldNames)

  const fields = fieldsSelected.map((fieldName) => {
    let value = {}

    switch (fieldName) {
      case 'productName':
        value = {
          fieldType: 'ProductField',
          name: 'productName',
          displayValue: 'Product Name',
          showOnSite: true,
        }
        break

      case 'brand':
        value = {
          fieldType: 'ProductField',
          name: 'brand',
          displayValue: 'Brand',
          showOnSite: true,
        }
        break

      case 'description':
        value = {
          fieldType: 'ProductField',
          name: 'description',
          displayValue: 'Product Description',
          showOnSite: true,
        }
        break

      case 'productReference':
        value = {
          fieldType: 'ProductField',
          name: 'productReference',
          displayValue: 'Product Reference',
          showOnSite: true,
        }
        break

      default:
        value = {
          fieldType: '',
          name: '',
          displayValue: '',
          showOnSite: false,
        }
        break
    }

    return value as ComparisonField
  })

  return fields
}

export const getSkuFields = (fieldsToHide?: string) => {
  const fieldNames = ['name', 'ean']
  const removeList = splitString(fieldsToHide)
  const fieldsSelected = without(removeList, fieldNames)

  const fields = fieldsSelected.map((fieldName) => {
    let value = {}

    switch (fieldName) {
      case 'name':
        value = {
          fieldType: 'SkuField',
          name: 'name',
          displayValue: 'Sku Name',
          showOnSite: true,
        }
        break

      case 'ean':
        value = {
          fieldType: 'SkuField',
          name: 'ean',
          displayValue: 'Ean',
          showOnSite: true,
        }
        break

      default:
        value = {
          fieldType: '',
          name: '',
          displayValue: '',
          showOnSite: false,
        }
        break
    }

    return value as ComparisonField
  })

  return fields
}

```


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

[User] Show me the implementation of product-comparison/react/components/utils/resolveSlidesNumber.ts from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```typescript
// /**
//  * This function returns the number of slides to show depending on the size of the window.
//  * If you pass a number it just returns that number.
//  * If you pass an object it will return the number of slides of the closest breakpoint to the size of the window.
//  * If you pass for example
//  * perPage = {
//  *   400: 2,
//  *   1000: 3
//  * }
//  *
//  * If the size of the window is something between 400px and 999px if will return 2,
//  * if it is 1000px or bigger, it will return 3, and if it is smaller than 400px it
//  * will return the default value; that is, 1.
//  * @param {number|object} perPage
//  * @param {number|undefined} minPerPage
//  */
// const resolveSlidesNumber = (
//   minPerPage: number,
//   perPage: number | object,
//   isMobile: boolean
// ) => {
//   let result = minPerPage || 1
//   if (typeof perPage === 'number') {
//     result = perPage
//   } else if (typeof perPage === 'object') {
//     const innerWidth = window && window.innerWidth

//     /** If it's on SSR, use placeholder screen sizes to get an approximate
//      * guess of how many items are displayed per page */
//     const windowSize = innerWidth || (isMobile ? 320 : 1024)

//     // for (const viewport in perPage) {
//     //   if (windowSize >= viewport) {
//     //     result = perPage[viewport]
//     //   }
//     // }
//   }
//   return result < minPerPage ? minPerPage : result
// }

// export default resolveSlidesNumber

```


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

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

[Assistant]
Here's the implementation:

```typescript
import { pathOr, find, propEq } from 'ramda'

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

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

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

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

const cleanImageUrl = (imageUrl: string) => {
  const result = baseUrlRegex.exec(imageUrl)

  return result && result.length > 0 ? result[0] : ''
}

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

  if (!isLegacyUrl) return imageUrl

  return `${cleanImageUrl(imageUrl)}-${width}-${height}`
}

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

  const normalizedImageUrl = replaceLegacyFileManagerUrl(
    imageUrl,
    width,
    height
  )

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

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

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

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

export const mapCatalogProductToProductSummary = (
  product: Product,
  skuId: string,
  imageSize = 500
) => {
  // eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
  if (!product) return {} as Product
  const normalizedProduct = { ...product }
  const items = normalizedProduct.items || []
  const sku = find(propEq('itemId', skuId))(items)

  if (sku) {
    const [seller = defaultSeller] = pathOr([], ['sellers'], sku)
    const [referenceId = defaultReference] = pathOr([], ['referenceId'], sku)
    const catalogImages: SkuImage[] = pathOr([], ['images'], sku)
    const normalizedImages = catalogImages.map((image) => ({
      ...image,
      imageUrl: resizeImage(image.imageUrl, imageSize),
    }))

    const [image = defaultImage] = normalizedImages

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

  return normalizedProduct
}

```


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

[User] Show me the implementation of product-comparison/react/components/comparisonPageRow/ProductSummaryRow.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, useEffect } from 'react'
import { pathOr, isEmpty } from 'ramda'
import { ExtensionPoint } from 'vtex.render-runtime'
import { useCssHandles } from 'vtex.css-handles'
import { Checkbox } from 'vtex.styleguide'

import ComparisonProductContext from '../../ComparisonProductContext'

import './row.css'
import { usePixel } from 'vtex.pixel-manager'
import type { InjectedIntlProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'

import ComparisonContext from '../../ProductComparisonContext'
import type { Dispatch } from '../../ProductComparisonContext'

interface ProductSummaryRowProps extends InjectedIntlProps {
  isShowDifferenceDefault: boolean
}

const CSS_HANDLES = [
  'productSummaryRowContainer',
  'fieldNameCol',
  'showDifferencesContainer',
]

const setShowDifferenceFirstTime = (
  isShowDifferenceDefault: boolean,
  dispatchComparison: Dispatch
) => {
  dispatchComparison({
    args: {
      showDifferences: isShowDifferenceDefault,
    },
    type: 'SET_SHOW_DIFFERENCES',
  })
}

const messages = defineMessages({
  showDifferences: {
    defaultMessage: '',
    id: 'store/product-comparison.product-summary-row.show-differences',
  },
})

const ProductSummaryRow = ({
  isShowDifferenceDefault,
  intl,
}: ProductSummaryRowProps) => {
  const cssHandles = useCssHandles(CSS_HANDLES)

  const [isShowDifferenceByDefault, changesChecked] = useState(
    isShowDifferenceDefault
  )

  const [showDifferences, setShowDifferences] = useState(false)
  const {
    useProductComparisonState,
    useProductComparisonDispatch,
  } = ComparisonContext

  const { useComparisonProductState } = ComparisonProductContext

  const productData = useComparisonProductState()
  const comparisonData = useProductComparisonState()
  const dispatchComparison = useProductComparisonDispatch()

  const comparisonProducts = pathOr(
    [] as ProductToCompare[],
    ['products'],
    comparisonData
  )

  const products = pathOr([] as ProductToCompare[], ['products'], productData)

  useEffect(() => {
    setShowDifferenceFirstTime(isShowDifferenceByDefault, dispatchComparison)
  }, [])

  const { push } = usePixel()

  const pixelEvent = (products: object, length: number) => {
    if (!!length && length >= 2) {
      push({
        event: 'productComparison',
        products,
        compareProductN: length,
      })
    }
  }

  useEffect(() => {
    pixelEvent(comparisonData.products, comparisonData.products.length)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])
  useEffect(() => {
    const showDifferences =
      comparisonData.products &&
      comparisonData.products.length > 1 &&
      comparisonData.showDifferences

    setShowDifferences(showDifferences)
  }, [comparisonData])

  const onSelectorChanged = (e: { target: { checked: boolean } }) => {
    changesChecked(!isShowDifferenceByDefault)
    dispatchComparison({
      args: {
        showDifferences: e.target.checked,
      },
      type: 'SET_SHOW_DIFFERENCES',
    })
  }

  return isEmpty(comparisonProducts) ? (
    <div />
  ) : (
    <div
      className={`mw9 ${cssHandles.productSummaryRowContainer} flex flex-row mt6 pa3`}
    >
      <div className={`${cssHandles.fieldNameCol} w-20 flex items-end ma1 pa3`}>
        {comparisonProducts.length > 1 && products ? (
          <div className={`${cssHandles.showDifferencesContainer} mb3`}>
            <Checkbox
              checked={showDifferences}
              id="id-differences"
              label={intl.formatMessage(messages.showDifferences)}
              name="name-differences"
              onChange={onSelectorChanged}
              value={showDifferences}
            />
          </div>
        ) : (
          <div />
        )}
      </div>
      <ExtensionPoint id="list-context.comparison-product-summary-slider" />
    </div>
  )
}

ProductSummaryRow.schema = {
  title: 'editor.product-summary-row.title',
  description: 'editor.product-summary-row.description',
  type: 'object',
  properties: {
    isShowDifferenceDefault: {
      title: 'Show difference',
      description: '',
      default: false,
      type: 'boolean',
    },
  },
}
export default injectIntl(ProductSummaryRow)

```


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

[User] Show me the implementation of product-comparison/react/components/comparisonPageRow/ComparisonFieldValue.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { pathOr, find, propEq, findLast } from 'ramda'
import { useCssHandles } from 'vtex.css-handles'

import ComparisonProductContext from '../../ComparisonProductContext'
import './row.css'

const CSS_HANDLES = [
  'productFieldValue',
  'skuFieldValue',
  'productSpecificationValues',
  'productSpecificationValue',
  'skuSpecificationValues',
  'skuSpecificationValue',
]

interface Props {
  productToCompare: ProductToCompare
  field: ComparisonField
}
const ComparisonFieldValue = ({ field, productToCompare }: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)
  const { useComparisonProductState } = ComparisonProductContext
  const productData = useComparisonProductState()

  const products = pathOr([] as ProductToCompare[], ['products'], productData)
  const selectedProduct = find(propEq('productId', productToCompare.productId))(
    products
  )

  const selectedSku = find(propEq('itemId', productToCompare.skuId))(
    pathOr([], ['items'], selectedProduct)
  )

  if (field.fieldType === 'ProductField') {
    return (
      <span className={cssHandles.productFieldValue}>
        {pathOr('', [field.name], selectedProduct)}
      </span>
    )
  }

  if (field.fieldType === 'SkuField') {
    return (
      <span className={cssHandles.skuFieldValue}>
        {pathOr('', [field.name], selectedSku)}
      </span>
    )
  }

  if (field.fieldType === 'ProductSpecificationField') {
    const groups = pathOr([], ['specificationGroups'], selectedProduct)
    const allSpecifications = findLast(propEq('name', 'allSpecifications'))(
      groups
    )

    const specifications = pathOr([], ['specifications'], allSpecifications)

    const values = pathOr(
      [],
      ['values'],
      find(propEq('name', field.name))(specifications)
    )

    return (
      <div
        className={`${cssHandles.productSpecificationValues} flex flex-column`}
      >
        {values.map((value) => {
          return (
            <span
              className={`${cssHandles.productSpecificationValue}`}
              key={`${field.fieldType}-${field.name}-${value}`}
            >
              {value}
            </span>
          )
        })}
      </div>
    )
  }

  if (field.fieldType === 'SkuSpecificationField') {
    const skuSpecifications = pathOr([], ['variations'], selectedSku)
    const values = pathOr(
      [],
      ['values'],
      find(propEq('name', field.name))(skuSpecifications)
    )

    return (
      <div className={`${cssHandles.skuSpecificationValues} flex flex-column`}>
        {values.map((value) => {
          return (
            <span
              className={`${cssHandles.skuSpecificationValue}`}
              key={`${field.fieldType}-${field.name}-${value}`}
            >
              {value}
            </span>
          )
        })}
      </div>
    )
  }

  if (field.fieldType === 'GroupedSpecification') {
    const groups = pathOr([], ['specificationGroups'], selectedProduct)
    const groupedSpecification = findLast(propEq('name', field.groupName))(
      groups
    )

    const specifications = pathOr([], ['specifications'], groupedSpecification)

    const values = pathOr(
      [],
      ['values'],
      find(propEq('name', field.name))(specifications)
    )

    return (
      <div
        className={`${cssHandles.productSpecificationValues} flex flex-column`}
      >
        {values.map((value) => {
          return (
            <span
              className={`${cssHandles.productSpecificationValue}`}
              key={`${field.fieldType}-${field.name}-${value}`}
            >
              {value}
            </span>
          )
        })}
      </div>
    )
  }

  return <div />
}

export default ComparisonFieldValue

```


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

[User] Show me the implementation of product-comparison/react/components/comparisonPageRow/ComparisonFieldRow.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { ExtensionPoint } from 'vtex.render-runtime'
import { useCssHandles } from 'vtex.css-handles'
import './row.css'

const CSS_HANDLES = ['rowContainer', 'fieldNameCol']

interface Props {
  field: ComparisonField
}

const ComparisonFieldRow: StorefrontFunctionComponent<Props> = ({
  field,
}: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)

  return field && field.name && field.fieldType && field.showOnSite ? (
    <div
      className={`${cssHandles.rowContainer} flex flex-row`}
      key={`field-${field.name}`}
    >
      <div className={`${cssHandles.fieldNameCol} w-100 ma1 pa3`}>
        <span>{field.displayValue}</span>
      </div>
      <ExtensionPoint id="list-context.comparison-row" field={field} />
    </div>
  ) : null
}

export default ComparisonFieldRow

```


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

[User] Show me the implementation of product-comparison/react/components/comparisonPageRow/ComparisonGridRowContent.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ReactChildren, ReactChild } from 'react'
import React, { useMemo } from 'react'
import { pathOr } from 'ramda'
import { useListContext, ListContextProvider } from 'vtex.list-context'
import { ProductListContext } from 'vtex.product-list-context'
import { useCssHandles } from 'vtex.css-handles'

import ProductSummeryListEventCaller from '../productSummaryList/ProductSummeryListEventCaller'
import ComparisonContext from '../../ProductComparisonContext'
import ComparisonProductContext from '../../ComparisonProductContext'
import ComparisonFieldValue from './ComparisonFieldValue'
import './row.css'

const CSS_HANDLES = ['comparisonCol']

interface Props {
  field: ComparisonField
  children: ReactChildren | ReactChild
  products: Product[]
  comparisonProducts: ProductToCompare[]
}

const List = ({ children, comparisonProducts, field }: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)
  const { list } = useListContext()

  const newListContextValue = useMemo(() => {
    const componentList =
      comparisonProducts &&
      comparisonProducts.map((comparisonProduct) => {
        return (
          <div
            key={`${comparisonProduct.productId}-col`}
            className={`${cssHandles.comparisonCol} w-100 ma1 pa3`}
          >
            <ComparisonFieldValue
              field={field}
              productToCompare={comparisonProduct}
            />
          </div>
        )
      })

    return list.concat(componentList)
  }, [comparisonProducts, cssHandles, field, list])

  return (
    <ListContextProvider list={newListContextValue}>
      {children}
    </ListContextProvider>
  )
}

const ComparisonGridRowContent = ({ children, field }: Props) => {
  const { ProductListProvider } = ProductListContext
  const { useProductComparisonState } = ComparisonContext
  const { useComparisonProductState } = ComparisonProductContext

  const comparisonData = useProductComparisonState()
  const productData = useComparisonProductState()

  const comparisonProducts = pathOr(
    [] as ProductToCompare[],
    ['products'],
    comparisonData
  )

  const products = pathOr([] as ProductToCompare[], ['products'], productData)

  return (
    <ProductListProvider listName="row-content">
      <List
        products={products}
        comparisonProducts={comparisonProducts}
        field={field}
      >
        {children}
      </List>
      <ProductSummeryListEventCaller />
    </ProductListProvider>
  )
}

export default ComparisonGridRowContent

```


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

[User] Show me the implementation of product-comparison/react/components/comparisonPageRow/row.css from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```css
.productSummaryRowContainer
  :global(.vtex-product-summary-2-x-nameContainer) {
  display: flex;
  justify-content: center;
}

.rowContainer:nth-child(even) {
  background-color: #F2F4F5;
}

.rowContainer :global(.vtex-slider-layout-0-x-slide){
  margin-bottom: unset;
}

.productSummaryRowContainer :global(.vtex-slider-layout-0-x-slideChildrenContainer){
  width: 220px;
}

.productSummaryRowContainer :global(.vtex-product-summary-2-x-imageContainer){
  border-radius: 0 0 6px 6px;
  padding-bottom: 0.1rem;
}

.productSummaryRowContainer :global(.vtex-product-summary-2-x-nameContainer),
.productSummaryRowContainer :global(.vtex-product-summary-2-x-priceContainer){
  padding-top: 0.6rem;
  padding-bottom: 0.6rem;
}

.productSummaryRowContainer :global(.vtex-product-summary-2-x-buyButtonContainer){
  padding-left: 0.5rem;
  padding-right: 0.5rem;
}

.productSummaryRowContainer :global(.vtex-slider-layout-0-x-slide),
.rowContainer :global(.vtex-slider-layout-0-x-slide){
  width:220px !important;
}

.productSummaryRowContainer :global(.vtex-slider-layout-0-x-sliderTrackContainer),
.rowContainer :global(.vtex-slider-layout-0-x-sliderTrackContainer){
  max-width: 1255px;
}

.fieldNameCol {
  min-width: 198px;
  width: 15%;
  margin-right: 5px;
}

.comparisonProductCol {
}

.productComparisonGrid {
}

.productSpecifications {
}

.skuSpecifications {
}

.productSummaryRowContainer :global(.vtex-slider-layout-0-x-sliderLayoutContainer--comparison-page),
.rowContainer :global(.vtex-slider-layout-0-x-sliderLayoutContainer){
  width: 85%;
}
```


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

[User] Show me the implementation of product-comparison/react/components/comparisonFields/ProductSpecifications.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useMemo } from 'react'
import { pathOr, findLast, propEq, sort, uniq } from 'ramda'
import { useCssHandles } from 'vtex.css-handles'

import ComparisonFieldRow from '../comparisonPageRow/ComparisonFieldRow'
import ComparisonProductContext from '../../ComparisonProductContext'
import ComparisonContext from '../../ProductComparisonContext'
import { getProductSpecificationFields } from '../utils/fieldUtils'
import './fieldGroup.css'

const CSS_HANDLES = ['title']

interface Props {
  titleText: string
  productSpecificationsToHide?: string
}

const ProductSpecifications = ({
  productSpecificationsToHide,
  titleText,
}: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)
  const { useComparisonProductState } = ComparisonProductContext
  const { useProductComparisonState } = ComparisonContext

  const comparisonData = useProductComparisonState()
  const productData = useComparisonProductState()
  const products = pathOr([] as ProductToCompare[], ['products'], productData)

  const allProductSpecificationsList: ComparisonField[] = useMemo(() => {
    const allProductSpecificationsList: ProductSpecification[][] = products.map(
      (product) => {
        const groups = pathOr([], ['specificationGroups'], product)
        const allSpecifications = findLast(propEq('name', 'allSpecifications'))(
          groups
        )

        return pathOr([], ['specifications'], allSpecifications)
      }
    )

    let specificationsList: string[] = allProductSpecificationsList.reduce(
      (accumulator: string[], currentValue: ProductSpecification[]) => {
        const current = currentValue.map((specification) =>
          pathOr('', ['name'], specification)
        )

        return [...new Set([...accumulator, ...current])]
      },
      [] as string[]
    )

    specificationsList = getProductSpecificationFields(
      specificationsList,
      productSpecificationsToHide
    )

    const productSpecificationFields = specificationsList.map(
      (specificationName) => ({
        fieldType: 'ProductSpecificationField',
        name: specificationName,
        displayValue: specificationName,
        showOnSite: true,
      })
    )

    if (comparisonData.showDifferences) {
      return productSpecificationFields.map((field) => {
        const specifications = allProductSpecificationsList.map(
          (specifications) => {
            return findLast(propEq('name', field.name))(specifications)
          }
        )

        const specificationValues = specifications.map(
          (specification: ProductSpecification) => {
            const specs = pathOr([], ['values'], specification)

            return sort(
              (a: string, b: string) => a.localeCompare(b),
              specs
            ).join(',')
          }
        )

        const uniqueSpecifications = uniq(specificationValues)

        return {
          ...field,
          ...{ showOnSite: uniqueSpecifications.length !== 1 },
        }
      })
    }

    return productSpecificationFields
  }, [productSpecificationsToHide, products, comparisonData])

  return allProductSpecificationsList &&
    allProductSpecificationsList.length > 0 ? (
    <div className="mt3">
      <div className={`${cssHandles.title} pa5 b`}>
        <span>{titleText}</span>
      </div>
      <div>
        {allProductSpecificationsList.map((field: ComparisonField) => (
          <ComparisonFieldRow key={`field-${field.name}`} field={field} />
        ))}
      </div>
    </div>
  ) : (
    <div />
  )
}

ProductSpecifications.schema = {
  title: 'admin/editor.comparison-grid.title',
  description: 'admin/editor.comparison-grid.description',
  type: 'object',
  properties: {
    titleText: {
      title: 'admin/editor.comparison-grid.titleText.title',
      type: 'string',
    },
    productSpecificationsToHide: {
      title:
        'admin/editor.comparison-grid.product-specifications-to-be-removed.title',
      description:
        'admin/editor.comparison-grid.product-specifications-to-be-removed.description',
      type: 'string',
      widget: {
        'ui:widget': 'textarea',
      },
    },
  },
}

export default ProductSpecifications

```


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

[User] Show me the implementation of product-comparison/react/components/comparisonFields/GroupedProductSpecifications.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useMemo } from 'react'
import { contains, pathOr, keys, findLast, propEq, sort, uniq } from 'ramda' // , findLast, propEq, sort, uniq,
import { useCssHandles } from 'vtex.css-handles'

import ComparisonFieldRow from '../comparisonPageRow/ComparisonFieldRow'
import ComparisonProductContext from '../../ComparisonProductContext'
import ComparisonContext from '../../ProductComparisonContext'
import { splitString } from '../utils/fieldUtils'
import './fieldGroup.css'

const CSS_HANDLES = ['title']

interface Props {
  showGroupName: boolean
  productSpecificationsToHide?: string
  productSpecificationGroupsToHide?: string
}

const GroupedProductSpecifications = ({
  productSpecificationsToHide,
  productSpecificationGroupsToHide,
}: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)
  const { useComparisonProductState } = ComparisonProductContext
  const { useProductComparisonState } = ComparisonContext

  const comparisonData = useProductComparisonState()
  const productData = useComparisonProductState()
  const products = pathOr([] as ProductToCompare[], ['products'], productData)

  const specificationsToHide = splitString(productSpecificationsToHide)
  const specificationGroupsToHide = splitString(
    productSpecificationGroupsToHide
  )

  const allProductSpecificationsList: GroupedComparisonFields = useMemo(() => {
    const allProductSpecificationGroups: ProductSpecificationGroup[][] = products.map(
      (product) => {
        return pathOr([], ['specificationGroups'], product)
      }
    )

    const groupedSpecifications = allProductSpecificationGroups.reduce(
      (
        accumulator: SpecificationGroupValues,
        productSpecificationGroups: ProductSpecificationGroup[]
      ) => {
        productSpecificationGroups.forEach(
          (specificationGroup: ProductSpecificationGroup) => {
            const groupName = pathOr('', ['name'], specificationGroup)
            const specifications = pathOr(
              [],
              ['specifications'],
              specificationGroup
            )

            if (
              groupName !== '' &&
              !contains(groupName, specificationGroupsToHide) &&
              specifications.length > 0
            ) {
              const groupedSpecifications = pathOr(
                [] as ProductSpecification[],
                [groupName],
                accumulator
              )

              specifications.forEach((specification: ProductSpecification) => {
                const isExists = groupedSpecifications.find(
                  (s: ProductSpecification) => s.name == specification.name
                )

                if (
                  !isExists &&
                  !contains(specification.name, specificationsToHide)
                ) {
                  const gSpec: ProductSpecification = {
                    ...specification,
                    ...{ groupName },
                  }

                  groupedSpecifications.push(gSpec)
                }
              })

              accumulator = {
                ...accumulator,
                ...{ [groupName]: groupedSpecifications },
              }
            }
          }
        )

        return accumulator
      },
      {}
    )

    const selectedGroups = keys(groupedSpecifications)

    const groupedComparisonFields = selectedGroups.reduce(
      (accumulator: GroupedComparisonFields, currentValue: string | number) => {
        const specifications = pathOr([], [currentValue], groupedSpecifications)
        const fields: ComparisonField[] = specifications.map(
          (specification: ProductSpecification) => ({
            fieldType: 'GroupedSpecification',
            groupName: currentValue.toString(),
            name: specification.name,
            displayValue: specification.name,
            showOnSite: true,
          })
        )

        return {
          ...accumulator,
          ...{ [currentValue]: fields },
        }
      },
      {}
    )

    if (comparisonData.showDifferences) {
      const differentComparisonFields = selectedGroups.reduce(
        (
          accumulator: GroupedComparisonFields,
          currentValue: string | number
        ) => {
          const specificationFields = pathOr(
            [],
            [currentValue],
            groupedComparisonFields
          )

          const differentFields = [] as ComparisonField[]

          specificationFields.forEach((field: ComparisonField) => {
            const specificationFieldValues = products.map(
              (product: Product) => {
                const groupedSpecification = findLast(
                  propEq('name', field.groupName)
                )(pathOr([], ['specificationGroups'], product))

                const specifications = pathOr(
                  [],
                  ['specifications'],
                  groupedSpecification
                )

                const selectedSpecification = findLast(
                  propEq('name', field.name)
                )(specifications)

                const specs = pathOr([], ['values'], selectedSpecification)

                return sort(
                  (a: string, b: string) => a.localeCompare(b),
                  specs
                ).join(',')
              }
            )

            const uniqueSpecifications = uniq(specificationFieldValues)

            if (uniqueSpecifications.length !== 1) {
              differentFields.push(field)
            }
          })

          return {
            ...accumulator,
            ...{ [currentValue]: differentFields },
          }
        },
        {}
      )

      return differentComparisonFields
    }

    return groupedComparisonFields
  }, [
    comparisonData.showDifferences,
    products,
    specificationGroupsToHide,
    specificationsToHide,
  ])

  const selectedGroups = keys(allProductSpecificationsList)

  return selectedGroups && selectedGroups.length > 0 ? (
    selectedGroups.map((groupName: string | number) => {
      const specifications: ComparisonField[] = pathOr(
        [],
        [groupName],
        allProductSpecificationsList
      )

      return (
        specifications.length > 0 && (
          <div key={groupName} className="mt3">
            <div className={`${cssHandles.title} pa5 b`}>
              <span>{groupName}</span>
            </div>
            <div>
              {specifications.map((field: ComparisonField) => (
                <ComparisonFieldRow key={`field-${field.name}`} field={field} />
              ))}
            </div>
          </div>
        )
      )
    })
  ) : (
    <div />
  )
}

GroupedProductSpecifications.schema = {
  title: 'admin/editor.comparison-grid.title',
  description: 'admin/editor.comparison-grid.description',
  type: 'object',
  properties: {
    showGroupName: {
      title: 'admin/editor.comparison-grid.titleText.title',
      type: 'boolean',
    },
    productSpecificationsToHide: {
      title:
        'admin/editor.comparison-grid.product-specifications-to-be-removed.title',
      description:
        'admin/editor.comparison-grid.product-specifications-to-be-removed.description',
      type: 'string',
      widget: {
        'ui:widget': 'textarea',
      },
    },
    productSpecificationGroupsToHide: {
      title:
        'admin/editor.comparison-grid.product-specification-groups-to-be-removed.title',
      description:
        'admin/editor.comparison-grid.product-specification-groups-to-be-removed.description',
      type: 'string',
      widget: {
        'ui:widget': 'textarea',
      },
    },
  },
}

export default GroupedProductSpecifications

```


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

[User] Show me the implementation of product-comparison/react/components/comparisonFields/SkuSpecifications.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useMemo } from 'react'
import { pathOr, find, findLast, propEq, sort, uniq } from 'ramda'
import { useCssHandles } from 'vtex.css-handles'

import ComparisonFieldRow from '../comparisonPageRow/ComparisonFieldRow'
import ComparisonProductContext from '../../ComparisonProductContext'
import ComparisonContext from '../../ProductComparisonContext'
import { getSkuSpecificationFields } from '../utils/fieldUtils'
import './fieldGroup.css'

const CSS_HANDLES = ['title']

interface Props {
  titleText: string
  skuSpecificationsToHide?: string
}

const SkuSpecifications = ({ skuSpecificationsToHide, titleText }: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)
  const { useComparisonProductState } = ComparisonProductContext
  const { useProductComparisonState } = ComparisonContext

  const comparisonData = useProductComparisonState()
  const productData = useComparisonProductState()
  const products = pathOr([] as ProductToCompare[], ['products'], productData)

  const skuSpecificationFields: ComparisonField[] = useMemo(() => {
    const allSkuSpecificationsList: string[][] = products.map((product) => {
      const skuSpecificationsList = pathOr([], ['skuSpecifications'], product)
      const skuSpecificationNamesList: string[] = skuSpecificationsList.reduce(
        (accumulator: string[], currentValue) => {
          return [
            ...new Set([
              ...accumulator,
              pathOr('', ['field', 'name'], currentValue),
            ]),
          ]
        },
        [] as string[]
      )

      return skuSpecificationNamesList.filter((name) => name !== '')
    })

    let specificationsList: string[] = allSkuSpecificationsList.reduce(
      (accumulator: string[], currentValue: string[]) => {
        return [...new Set([...accumulator, ...currentValue])]
      },
      [] as string[]
    )

    specificationsList = getSkuSpecificationFields(
      specificationsList,
      skuSpecificationsToHide
    )

    const skuSpecificationFieldsList = specificationsList.map(
      (specificationName) => ({
        fieldType: 'SkuSpecificationField',
        name: specificationName,
        displayValue: specificationName,
        showOnSite: true,
      })
    )

    if (comparisonData.products && comparisonData.showDifferences) {
      const allVariations: Variation[][] = comparisonData.products.map(
        (comparisonItem) => {
          const selectedProduct = find(
            propEq('productId', comparisonItem.productId)
          )(products)

          const selectedSku = find(propEq('itemId', comparisonItem.skuId))(
            pathOr([], ['items'], selectedProduct)
          )

          return pathOr([], ['variations'], selectedSku)
        }
      )

      return skuSpecificationFieldsList.map((field) => {
        const specifications = allVariations.map((variations) => {
          return findLast(propEq('name', field.name))(variations)
        })

        const specificationValues = specifications.map(
          (specification: ProductSpecification) => {
            const specs = pathOr([], ['values'], specification)

            return sort(
              (a: string, b: string) => a.localeCompare(b),
              specs
            ).join(',')
          }
        )

        const uniqueSpecifications = uniq(specificationValues)

        return {
          ...field,
          ...{ showOnSite: uniqueSpecifications.length !== 1 },
        }
      })
    }

    return skuSpecificationFieldsList
  }, [comparisonData, products, skuSpecificationsToHide])

  return skuSpecificationFields && skuSpecificationFields.length > 0 ? (
    <div className="mt3">
      <div className={`${cssHandles.title} pa5 b`}>
        <span>{titleText}</span>
      </div>
      <div>
        {skuSpecificationFields.map((field: ComparisonField) => (
          <ComparisonFieldRow key={`field-${field.name}`} field={field} />
        ))}
      </div>
    </div>
  ) : (
    <div />
  )
}

SkuSpecifications.schema = {
  title: 'admin/editor.comparison-grid.title',
  description: 'admin/editor.comparison-grid.description',
  type: 'object',
  properties: {
    titleText: {
      title: 'admin/editor.comparison-grid.titleText.title',
      type: 'string',
    },
    skuSpecificationsToHide: {
      title:
        'admin/editor.comparison-grid.sku-specifications-to-be-removed.title',
      description:
        'admin/editor.comparison-grid.sku-specifications-to-be-removed.description',
      type: 'string',
      widget: {
        'ui:widget': 'textarea',
      },
    },
  },
}

export default SkuSpecifications

```


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

[User] Show me the implementation of product-comparison/react/components/productRemover/RemoveButton.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { MouseEvent } from 'react'
import React from 'react'
import { pathOr } from 'ramda'
import { useProductSummary } from 'vtex.product-summary-context/ProductSummaryContext'
import { useCssHandles } from 'vtex.css-handles'
import { withToast } from 'vtex.styleguide'
import { IconClose } from 'vtex.store-icons'
import type { InjectedIntlProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'

import ComparisonContext from '../../ProductComparisonContext'
import './remove.css'

const CSS_HANDLES = ['closeButton', 'closeButtonContainer']

const messages = defineMessages({
  product: {
    defaultMessage: '',
    id: 'store/product-comparison.product-selector.product',
  },
  removed: {
    defaultMessage: '',
    id: 'store/product-comparison.product-selector.product-removed',
  },
})

interface Props extends InjectedIntlProps {
  showToast?: (input: ToastInput) => void
}

const RemoveButton = ({ showToast, intl }: Props) => {
  const cssHandles = useCssHandles(CSS_HANDLES)

  const {
    useProductComparisonState,
    useProductComparisonDispatch,
  } = ComparisonContext

  const dispatchComparison = useProductComparisonDispatch()
  const valuesFromContext = useProductSummary()
  const productId = pathOr('', ['product', 'productId'], valuesFromContext)
  const productName = pathOr('', ['product', 'productName'], valuesFromContext)
  const itemId = pathOr('', ['selectedItem', 'itemId'], valuesFromContext)

  const comparisonData = useProductComparisonState()
  const isDrawerCollapsed = pathOr(false, ['isDrawerCollapsed'], comparisonData)

  const showMessage = (message: string, show = true) => {
    if (showToast && show) {
      showToast({
        message,
      })
    }
  }

  const removeProductFromCompare = (e: MouseEvent) => {
    e.preventDefault()
    e.stopPropagation()

    dispatchComparison({
      args: {
        product: {
          productId,
          skuId: itemId,
        },
      },
      type: 'REMOVE',
    })
    showMessage(
      `${intl.formatMessage(
        messages.product
      )} "${productName}" ${intl.formatMessage(messages.removed)}`,
      isDrawerCollapsed
    )
  }

  return (
    <div
      className={`${cssHandles.closeButtonContainer} flex justify-end items-start flex-grow-1`}
    >
      <button
        className={`${cssHandles.closeButton} bg-transparent button-reset t-small pointer b--none-ns outline-0`}
        onClick={removeProductFromCompare}
      >
        <IconClose orientation="right" size={12} type="line" />
      </button>
    </div>
  )
}

export default withToast(injectIntl(RemoveButton))

```


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

[User] Show me the implementation of product-comparison/react/components/productRemover/remove.css from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```css
.closeButton {
  font-weight: 300;
  padding: unset;
}

.closeButtonContainer {
  border-radius: 6px 6px 0 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 product-comparison/react/components/productSummaryList/ProductSummeryListEventCaller.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

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

const ProductSummeryListEventCaller = () => {
  useProductImpression()

  return null
}

export default ProductSummeryListEventCaller

```


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

[User] Show me the implementation of product-comparison/react/components/productSummaryList/ProductSummaryList.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
// eslint-disable-next-line prettier/prettier
import type { ReactChildren, ReactChild } from 'react'
import React, { useMemo } from 'react'
import { pathOr, propEq, find } from 'ramda'
import { ExtensionPoint, useTreePath } from 'vtex.render-runtime'
import { useListContext, ListContextProvider } from 'vtex.list-context'
import { ProductListContext } from 'vtex.product-list-context'

import { mapCatalogProductToProductSummary } from '../utils/normalize'
import ProductSummeryListEventCaller from './ProductSummeryListEventCaller'
import ComparisonContext from '../../ProductComparisonContext'
import ComparisonProductContext from '../../ComparisonProductContext'

interface Props {
  children: ReactChildren | ReactChild
  products: Product[]
  comparisonProducts: ProductToCompare[]
}

const List = ({ children, products, comparisonProducts }: Props) => {
  const { list } = useListContext()
  const { treePath } = useTreePath()

  const newListContextValue = useMemo(() => {
    const componentList =
      comparisonProducts &&
      products &&
      comparisonProducts
        .map((comparisonProduct) => {
          const selectedProduct = find(
            propEq('productId', comparisonProduct.productId)
          )(products)

          return mapCatalogProductToProductSummary(
            selectedProduct,
            comparisonProduct.skuId
          )
        })
        .filter((product) => pathOr('', ['productId'], product) !== '')
        .map((normalizedProduct) => {
          return (
            <ExtensionPoint
              id="product-summary"
              key={`${pathOr('', ['productId'], normalizedProduct)}`}
              treePath={treePath}
              product={normalizedProduct}
            />
          )
        })

    return list.concat(componentList)
  }, [comparisonProducts, list, products, treePath])

  return (
    <ListContextProvider list={newListContextValue}>
      {children}
    </ListContextProvider>
  )
}

const ProductSummaryList = ({ children }: Props) => {
  const { ProductListProvider } = ProductListContext
  const { useProductComparisonState } = ComparisonContext
  const { useComparisonProductState } = ComparisonProductContext

  const comparisonData = useProductComparisonState()
  const productData = useComparisonProductState()

  const comparisonProducts = pathOr(
    [] as ProductToCompare[],
    ['products'],
    comparisonData
  )

  const products = pathOr([] as ProductToCompare[], ['products'], productData)

  return (
    <ProductListProvider listName="product-list">
      <List products={products} comparisonProducts={comparisonProducts}>
        {children}
      </List>
      <ProductSummeryListEventCaller />
    </ProductListProvider>
  )
}

export default ProductSummaryList

```


[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 ffm-segment-prices-node-template/docs/README.md from the ffm-segment-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```markdown
# External Price App

A reference IO app to fetch segmented prices from main account.

## Using VTEX Pricing as price provider

If you wish to use the VTEX Pricing as the source of your prices the app already handle that based on the priceTables configured in the user profile. You will also need to ask our support team to enable and configure the pricing hub feature.

For more information visit: [Pricing Hub documentation](https://developers.vtex.com/vtex-rest-api/docs/pricing-hub) and [Configuring price tables for specific users](https://help.vtex.com/tutorial/setting-up-price-tables-for-specific-users--5S9oDOMHNmY4K0kAewAiWY)

## Implementation using external price provider

1. Fork this app.
2. In the `manifest.json` file:
    * Change the `vendor` field to the name of the account you are using.
    * Change the `name` field to one of your choosing.
    * Add your service host (e.g. `myhost.com`) in an `outbound-access` policy.
3. In the `node/env.ts` file, add your service endpoint as follows:

    ```
    const ENV = {
      SERVICE_ENDPOINT: 'http://myservice.com',
    }

    export default ENV
    ```

4. Change the ` node/clients/externalPrice.ts` file to parse data received by the external pricing app and return it in a way that Pricing Hub can understand. See more details on the specification of this format in the [Pricing Hub documentation](https://developers.vtex.com/vtex-rest-api/docs/pricing-hub).

> ⛔ Do not change the `"routes"` in `node/service.json` nor the files in `node/typings/`, since they were created to reflect Pricing Hub behavior.

```


[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 ffm-segment-prices-node-template/node/service.json from the ffm-segment-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "memory": 256,
  "ttl": 10,
  "timeout": 2,
  "minReplicas": 2,
  "maxReplicas": 4,
  "workers": 1,
  "routes": {
    "price": {
      "path": "/_v/external-prices/price",
      "public": false,
      "access": "authorized",
      "policies": [
        {
          "effect": "allow",
          "actions": ["post"],
          "principals": ["vrn:vtex.vtex-id:*:*:*:user/vtexappkey-appvtex"]
        }
      ]
    },
    "keep-alive": {
      "path": "/_v/external-prices/keep-alive",
      "public": true,
      "policies": [
        {
          "effect": "allow",
          "actions": ["get"]
        }
      ]
    }
  }
}

```


[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 ffm-segment-prices-node-template/node/package.json from the ffm-segment-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "dependencies": {
    "co-body": "^6.0.0",
    "ramda": "^0.25.0"
  },
  "devDependencies": {
    "@types/co-body": "^0.0.3",
    "@types/jest": "^24.0.18",
    "@types/node": "^12.0.0",
    "@types/node-fetch": "^2.5.8",
    "@types/ramda": "types/npm-ramda#dist",
    "@vtex/api": "6.45.12",
    "@vtex/test-tools": "^1.0.0",
    "@vtex/tsconfig": "^0.6.0",
    "typescript": "3.9.7"
  },
  "scripts": {
    "lint": "tsc --noEmit --pretty"
  },
  "version": "0.3.1"
}

```


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

[User] Show me the implementation of ffm-segment-prices-node-template/node/index.ts from the ffm-segment-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ClientsConfig } from '@vtex/api'
import { LRUCache, method, Service } from '@vtex/api'

import { Clients } from './clients'
import { validate } from './handlers/validate'
import { fetchPrice } from './handlers/fetchPrice'
import { formatResponse } from './handlers/formatResponse'

const TIMEOUT_MS = 800
const memoryCache = new LRUCache<string, never>({ max: 5000 })

const clients: ClientsConfig<Clients> = {
  implementation: Clients,
  options: {
    default: {
      retries: 2,
      timeout: TIMEOUT_MS,
    },
    status: {
      memoryCache,
    },
  },
}

async function keepAlive(ctx: Context) {
  ctx.set('Cache-Control', 'no-cache')
  ctx.status = 200
}

export default new Service({
  clients,
  routes: {
    price: method({
      POST: [validate, fetchPrice, formatResponse],
    }),
    'keep-alive': method({
      GET: keepAlive,
    }),
  },
})

```


[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 ffm-segment-prices-node-template/node/env.ts from the ffm-segment-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
// App Settings
const ENV = {
  SERVICE_ENDPOINT: 'YOUR_SERVICE_ENDPOINT',
}

export default ENV

```