[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 organizations/react/components/modals/UserEditModal.tsx from the organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, useEffect } from 'react'
import { Modal, Button, Dropdown, Checkbox, Spinner } from 'vtex.styleguide'
import { useMutation, useQuery } from 'react-apollo'
import { pathOr } from 'ramda'
import { injectIntl } from 'react-intl'
import CurrencyInput from '../../components/CurrencyInput'

import { updateCacheEditUser, updateCacheClient } from '../../utils/cacheUtils'
import UPDATE_DOCUMENT from '../../graphql/updateDocument.graphql'
import GET_DOCUMENT from '../../graphql/documents.graphql'

import { documentSerializer } from '../../utils/documentSerializer'

import {
  CLIENT_ACRONYM,
  CLIENT_FIELDS,
  ORG_ASSIGNMENT,
  ORG_ASSIGNMENT_SCHEMA,
} from '../../utils/const'
import { getErrorMessage } from '../../utils/graphqlErrorHandler'

interface Props {
  isOpen: boolean
  onClose: Function
  onSave: Function
  orgAssignment: OrganizationAssignment
  roles: Role[]
  showToast: (message: any) => void
  isCurrentUserAdmin: boolean
  intl: any
}

const UserEditModal = ({
  isOpen,
  onClose,
  onSave,
  orgAssignment,
  roles,
  showToast,
  isCurrentUserAdmin,
  intl,
}: Props) => {
  const [clientId, setClientId] = useState('')
  const [email, setEmail] = useState('')
  const [budgetAmount, setBudgetAmount] = useState('')
  const [roleId, setRoleId] = useState('')
  const [assignmentId, setAssignmentId] = useState('')
  const [organizationId, setOrganizationId] = useState('')
  const [isOrgAdmin, setIsOrgAdmin] = useState(false)

  const [updateUserDocument] = useMutation(UPDATE_DOCUMENT, {
    update: (cache: any, { data }: any) =>
      updateCacheEditUser(cache, data, roles, organizationId, roleId),
  })

  const [updateClientDocument] = useMutation(UPDATE_DOCUMENT)

  const { data: clientData, loading } = useQuery(GET_DOCUMENT, {
    skip: email === '',
    variables: {
      acronym: CLIENT_ACRONYM,
      fields: CLIENT_FIELDS,
      where: `email=${email}`,
    },
  })

  useEffect(() => {
    const abortController = new AbortController()

    setEmail(pathOr('', ['email'], orgAssignment))
    setBudgetAmount(pathOr('', ['budgetAmount'], orgAssignment))
    setAssignmentId(pathOr('', ['id'], orgAssignment))
    setRoleId(pathOr('', ['roleId_linked', 'id'], orgAssignment))
    setOrganizationId(pathOr('', ['businessOrganizationId'], orgAssignment))

    return () => {
      abortController.abort()
    }
  }, [orgAssignment])

  useEffect(() => {
    const abortController = new AbortController()

    const clients = documentSerializer(clientData ? clientData.myDocuments : [])
    setClientId(pathOr('', [0, 'id'], clients))
    setIsOrgAdmin((pathOr('', [0, 'isOrgAdmin'], clients) as string) === 'true')

    return () => {
      abortController.abort()
    }
  }, [clientData])

  // update client if isOrgAdmin changed
  const updateClient = () => {
    const clients = documentSerializer(clientData ? clientData.myDocuments : [])
    if (
      (pathOr('', [0, 'isOrgAdmin'], clients) as string) !==
      isOrgAdmin.toString()
    ) {
      return updateClientDocument({
        variables: {
          acronym: CLIENT_ACRONYM,
          document: {
            fields: [
              { key: 'id', value: clientId },
              { key: 'isOrgAdmin', value: isOrgAdmin.toString() },
            ],
          },
        },
        update: (cache: any, { data }: any) =>
          updateCacheClient(
            cache,
            data,
            email,
            budgetAmount,
            organizationId,
            isOrgAdmin.toString()
          ),
      })
    } else {
      return Promise.resolve() as any
    }
  }

  const onSaveEdit = () => {
    updateClient()
      .then(() => {
        return updateUserDocument({
          variables: {
            acronym: ORG_ASSIGNMENT,
            document: {
              fields: [
                { key: 'id', value: assignmentId },
                { key: 'budgetAmount', value: budgetAmount },
                { key: 'roleId', value: roleId },
              ],
            },
            schema: ORG_ASSIGNMENT_SCHEMA,
          },
        })
      })
      .then(() => {
        onSave()
      })
      .then(() => {
        window.location.reload()
      })
      .catch((e: Error) => {
        const message = getErrorMessage(e)
        showToast({
          message: `${intl.formatMessage({
            id: 'store/my-organization.toast.user.edit.error',
          })} "${message}"`,
          duration: 5000,
          horizontalPosition: 'right',
        })
      })
  }

  return (
    <Modal
      isOpen={isOpen}
      title={intl.formatMessage({
        id: 'store/my-organization.edit-user.title',
      })}
      responsiveFullScreen
      centered
      bottomBar={
        <div className="nowrap">
          <span className="mr4">
            <Button variation="tertiary" onClick={() => onClose()}>
              {intl.formatMessage({
                id: 'store/my-organization.button.cancel',
              })}
            </Button>
          </span>
          <span>
            <Button
              variation="secondary"
              disabled={roleId === ''}
              onClick={() => onSaveEdit()}>
              {intl.formatMessage({ id: 'store/my-organization.button.save' })}
            </Button>
          </span>
        </div>
      }
      onClose={() => onClose()}>
      {loading ? (
        <Spinner />
      ) : (
        <div>
          <div className="mb5 mt5">
            {intl.formatMessage({ id: 'store/my-organization.label.email' })} :{' '}
            {email}
          </div>
          <div className="mb5">
            <CurrencyInput
              type="text"
              label={intl.formatMessage({
                id: 'store/my-organization.budget-amount',
              })}
              onChange={(e: { target: { value: string } }) => {
                setBudgetAmount(e.target.value)
              }}
              defaultValue={budgetAmount}
              value={budgetAmount}
            />
          </div>
          <div className="mb5">
            <Dropdown
              label={'Role'}
              options={roles}
              onChange={(e: { target: { value: string } }) => {
                setRoleId(e.target.value)
              }}
              value={roleId}
              errorMessage={roleId === '' ? 'Role is required' : ''}
            />
          </div>
          {isCurrentUserAdmin && (
            <div className="mb5">
              <Checkbox
                checked={isOrgAdmin}
                name="disabled-checkbox-group"
                label="Organization Admin"
                onChange={(e: { target: { checked: boolean } }) => {
                  setIsOrgAdmin(e.target.checked)
                }}
                id="option-0"
              />
            </div>
          )}
        </div>
      )}
    </Modal>
  )
}

export default injectIntl(UserEditModal)

```


[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 organizations/react/components/modals/AddUser.tsx from the organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { SyntheticEvent, useReducer } from 'react'
import { isEmpty, path, contains, pathOr } from 'ramda'
import { injectIntl } from 'react-intl'
import { Modal, Button, Dropdown, Input, Checkbox } from 'vtex.styleguide'
import CurrencyInput from '../../components/CurrencyInput'
import { useMutation, useApolloClient } from 'react-apollo'

import CREATE_DOCUMENT from '../../graphql/createDocument.graphql'
import UPDATE_DOCUMENT from '../../graphql/updateDocument.graphql'
import GET_DOCUMENT from '../../graphql/documents.graphql'

import { documentSerializer } from '../../utils/documentSerializer'
import { updateCacheAddUser, updateCacheClient } from '../../utils/cacheUtils'
import { getErrorMessage } from '../../utils/graphqlErrorHandler'
import {
  CLIENT_ACRONYM,
  CLIENT_FIELDS,
  ORG_ASSIGNMENT,
  ORG_ASSIGNMENT_SCHEMA,
  ORG_ASSIGNMENT_STATUS_APPROVED,
} from '../../utils/const'

interface Props {
  isOpen: boolean
  onClose: Function
  onSuccess: Function
  existingUsers: string[]
  roles: Role[]
  organizationId: string
  showToast: (message: any) => void
  isCurrentUserAdmin: boolean
  intl: any
}

interface State {
  email: string
  roleId: string
  budgetAmount: string
  personaId: string
  isOrgAdmin: boolean
  formErrors: Errors
  touched: {
    email: boolean
    roleId: boolean
  }
  messages: {
    type: string
    value: string
  }[]
}

interface Errors {
  email: string[]
  roleId: string[]
}

type Action<K, V = void> = V extends void ? { type: K } : { type: K } & V

type Actions =
  | Action<'CHANGE_ROLE', { args: { roleId: string } }>
  | Action<'CHANGE_EMAIL', { args: { email: string } }>
  | Action<'CHANGE_BUDGET_AMOUNT', { args: { budgetAmount: string } }>
  | Action<'CHANGE_PERSONA_ID', { args: { personaId: string } }>
  | Action<'CHANGE_IS_ORG_ADMIN', { args: { isOrgAdmin: boolean } }>
  | Action<
      'RESPONSE',
      {
        args: {
          type: string
          message: string
        }
      }
    >
  | Action<'INPUT_TOUCHED', { args: { input: string } }>

const AddUser = ({
  isOpen,
  onClose,
  onSuccess,
  intl,
  roles,
  organizationId,
  existingUsers,
  showToast,
  isCurrentUserAdmin,
}: Props) => {
  const client = useApolloClient()
  const [createDocument, { loading: loadingAdd }] = useMutation(CREATE_DOCUMENT)
  const [updateDocument, { loading: loadingUpdate }] = useMutation(
    UPDATE_DOCUMENT
  )

  const reducer = (state: State, action: Actions): State => {
    let errors: string[] = []
    switch (action.type) {
      case 'CHANGE_ROLE':
        if (isEmpty(action.args.roleId)) {
          errors = [
            intl.formatMessage({
              id: 'store/my-organization.errors.role-id.empty',
            }),
          ]
        }
        return {
          ...state,
          roleId: action.args.roleId,
          formErrors: { roleId: errors, email: state.formErrors.email },
        }
      case 'CHANGE_EMAIL': {
        if (isEmpty(action.args.email)) {
          errors = [
            intl.formatMessage({
              id: 'store/my-organization.errors.email.empty',
            }),
          ]
        } else if (
          !/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(action.args.email)
        ) {
          errors = [
            intl.formatMessage({
              id: 'store/my-organization.errors.email.invalid',
            }),
          ]
        } else if (contains(action.args.email, existingUsers)) {
          errors = [
            intl.formatMessage({
              id: 'store/my-organization.errors.email.exists',
            }),
          ]
        }

        return {
          ...state,
          email: action.args.email,
          formErrors: { roleId: state.formErrors.roleId, email: errors },
        }
      }
      case 'CHANGE_BUDGET_AMOUNT': {
        return {
          ...state,
          budgetAmount: action.args.budgetAmount,
        }
      }
      case 'CHANGE_IS_ORG_ADMIN':
        return {
          ...state,
          ...{ isOrgAdmin: action.args.isOrgAdmin },
        }
      case 'RESPONSE': {
        return {
          ...state,
          messages: [
            {
              type: action.args.type,
              value: action.args.message,
            },
          ],
        }
      }
      case 'INPUT_TOUCHED': {
        if (action.args.input == 'email') {
          return {
            ...state,
            touched: {
              email: true,
              roleId: state.touched.roleId,
            },
          }
        } else if (action.args.input == 'roleId') {
          return {
            ...state,
            touched: {
              roleId: true,
              email: state.touched.email,
            },
          }
        }
        return state
      }
      case 'CHANGE_PERSONA_ID':
        return {
          ...state,
          personaId: action.args.personaId,
        }
      default:
        throw new Error(`Unhandled action type on my-users`)
    }
  }
  const initialState = {
    roleId: '',
    email: '',
    budgetAmount: '',
    isOrgAdmin: false,
    personaId: '',
    formErrors: {
      email: [],
      roleId: [],
    },
    messages: [],
    touched: {
      email: false,
      roleId: false,
    },
  }
  const [state, dispatch] = useReducer(reducer, initialState)

  const handleGraphqlError = () => {
    return (e: Error) => {
      const message = getErrorMessage(e)
      dispatch({
        type: 'RESPONSE',
        args: {
          type: 'ERROR',
          message: message,
        },
      })
      return Promise.reject(message)
    }
  }

  const getClientFields = (clientId?: string) => {
    const fields = [
      { key: 'email', value: state.email },
      { key: 'budgetAmount', value: state.budgetAmount },
      { key: 'organizationId', value: organizationId },
      { key: 'isOrgAdmin', value: state.isOrgAdmin.toString() },
      { key: 'approved', value: 'true' },
    ]
    if (clientId) {
      fields.push({ key: 'id', value: clientId })
    }
    return fields
  }

  const getAssignmentFields = () => {
    const fields = [
      {
        key: 'businessOrganizationId',
        value: organizationId,
      },
      {
        key: 'email',
        value: state.email,
      },
      {
        key: 'budgetAmount',
        value: state.budgetAmount,
      },
      {
        key: 'roleId',
        value: state.roleId,
      },
      {
        key: 'status',
        value: ORG_ASSIGNMENT_STATUS_APPROVED,
      },
    ]

    return fields
  }

  const handleSubmit = (e: SyntheticEvent) => {
    e.preventDefault()

    if (state.email && state.roleId) {
      client
        .query({
          query: GET_DOCUMENT,
          variables: {
            acronym: CLIENT_ACRONYM,
            fields: CLIENT_FIELDS,
            where: `email=${state.email}`,
          },
          fetchPolicy: 'no-cache',
        })
        .then(({ data }: any) => {
          const clients = documentSerializer(data ? data.myDocuments : [])

          const clientIdData = pathOr('', [0, 'id'], clients)
          const organizationIdData = pathOr('', [0, 'organizationId'], clients)

          if (clients.length == 0) {
            return createDocument({
              variables: {
                acronym: CLIENT_ACRONYM,
                document: {
                  fields: getClientFields(),
                },
              },
              update: (cache: any, { data }: any) =>
                updateCacheClient(
                  cache,
                  data,
                  state.email,
                  state.budgetAmount,
                  organizationId,
                  state.isOrgAdmin.toString()
                ),
            })
          } else if (
            organizationIdData == undefined ||
            organizationIdData === '' ||
            organizationIdData === 'null'
          ) {
            return updateDocument({
              variables: {
                acronym: CLIENT_ACRONYM,
                document: {
                  fields: getClientFields(clientIdData),
                },
              },
              update: (cache: any, { data }: any) =>
                updateCacheClient(
                  cache,
                  data,
                  state.email,
                  state.budgetAmount,
                  organizationId,
                  state.isOrgAdmin.toString()
                ),
            })
          } else {
            showToast({
              message: intl.formatMessage({
                id:
                  'store/my-organization.my-organization.user.already.assigned',
              }),
              duration: 5000,
              horizontalPosition: 'right',
            })
            return Promise.reject()
          }
        })
        .then(() => {
          return createDocument({
            variables: {
              acronym: ORG_ASSIGNMENT,
              document: {
                fields: getAssignmentFields(),
              },
              schema: ORG_ASSIGNMENT_SCHEMA,
            },
            update: (cache: any, { data }: any) => {
              updateCacheAddUser(
                cache,
                data,
                roles,
                organizationId,
                state.email,
                state.budgetAmount,
                state.roleId
              )
            },
          })
        })
        .catch(handleGraphqlError())
        .then(() => {
          dispatch({
            type: 'RESPONSE',
            args: {
              type: 'SUCCESS',
              message: intl.formatMessage({
                id: 'store/my-organization.success',
              }),
            },
          })
          dispatch({
            type: 'CHANGE_EMAIL',
            args: { email: '' },
          })
          dispatch({
            type: 'CHANGE_BUDGET_AMOUNT',
            args: { budgetAmount: '' },
          })
          dispatch({
            type: 'CHANGE_ROLE',
            args: { roleId: '' },
          })
          dispatch({
            type: 'CHANGE_PERSONA_ID',
            args: { personaId: '' },
          })
          dispatch({
            type: 'CHANGE_IS_ORG_ADMIN',
            args: { isOrgAdmin: false },
          })

          onSuccess()
        })
        .catch((message: string) => {
          if (message) {
            showToast({
              message: `${intl.formatMessage({
                id: 'store/my-organization.toast.user.create.error',
              })} "${message}"`,
              duration: 5000,
              horizontalPosition: 'right',
            })
          }
        })
    }
  }

  return (
    <Modal
      title={intl.formatMessage({ id: 'store/my-organization.add-user.title' })}
      isOpen={isOpen}
      onClose={() => onClose()}>
      <form onSubmit={(e: SyntheticEvent) => handleSubmit(e)}>
        <div className="mt3 flex">
          <Input
            type="text"
            label={intl.formatMessage({ id: 'store/my-organization.email' })}
            onChange={(e: { target: { value: string } }) => {
              dispatch({
                type: 'CHANGE_EMAIL',
                args: { email: e.target.value },
              })
              dispatch({ type: 'INPUT_TOUCHED', args: { input: 'email' } })
            }}
            value={state.email}
            errorMessage={path(['formErrors', 'email', 0], state)}
          />
        </div>
        <div className="mt3 flex">
          <CurrencyInput
            type="text"
            label={intl.formatMessage({
              id: 'store/my-organization.budget-amount',
            })}
            onChange={(e: { target: { value: string } }) => {
              dispatch({
                type: 'CHANGE_BUDGET_AMOUNT',
                args: { budgetAmount: e.target.value },
              })
              dispatch({
                type: 'INPUT_TOUCHED',
                args: { input: 'budgetAmount' },
              })
            }}
            value={state.budgetAmount}
            errorMessage={path(['formErrors', 'budgetAmount', 0], state)}
          />
        </div>
        <div className="mt5">
          <Dropdown
            label={intl.formatMessage({ id: 'store/my-organization.role-id' })}
            options={roles}
            onChange={(e: { target: { value: string } }) => {
              dispatch({
                type: 'CHANGE_ROLE',
                args: { roleId: e.target.value },
              })
              dispatch({ type: 'INPUT_TOUCHED', args: { input: 'roleId' } })
            }}
            value={state.roleId}
            errorMessage={path(['formErrors', 'roleId', 0], state)}
          />
        </div>
        {isCurrentUserAdmin && (
          <div className="mt5">
            <Checkbox
              checked={state.isOrgAdmin}
              name="disabled-checkbox-group"
              label="Organization Admin"
              onChange={(e: { target: { checked: boolean } }) => {
                dispatch({
                  type: 'CHANGE_IS_ORG_ADMIN',
                  args: { isOrgAdmin: e.target.checked },
                })
              }}
              id="option-0"
            />
          </div>
        )}

        <div className="mt5">
          <Button
            variation="primary"
            type="submit"
            isLoading={loadingAdd || loadingUpdate}
            disabled={
              !state.touched.email ||
              !state.touched.roleId ||
              (state.touched.email && !isEmpty(state.formErrors.email)) ||
              (state.touched.roleId && !isEmpty(state.formErrors.roleId))
            }>
            {intl.formatMessage({ id: 'store/my-organization.add-user' })}
          </Button>
        </div>
      </form>
    </Modal>
  )
}

export default injectIntl(AddUser)

```


[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/messages/hu.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "Visszaszámlálás",
  "editor.countdown.description": "Visszaszámlálás komponens",
  "admin/editor.comparison-grid.title": "Összehasonlító Rács",
  "admin/editor.comparison-grid.description": "Termék összehasonlító rács sorának információinak gyűjtése",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Termékspecifikációk elrejtése",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Azoknak a termékspecifikációknak a neve, amelyeket el kell rejteni az összehasonlító oldalon (vesszővel válassza el őket ,)",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "SKU specifikációk elrejtése",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Azoknak az SKU specifikációknak a neve, amelyeket el kell rejteni az összehasonlító oldalon (vesszővel válassza el őket ,)",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Termékmezők elrejtése",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Azok a termékmezők, amelyeket el kell rejteni az összehasonlító oldalon (vesszővel válassza el őket ,)",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "SKU mezők elrejtése",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Azok az SKU mezők, amelyeket el kell rejteni az összehasonlító oldalon (vesszővel válassza el őket ,)",
  "admin/editor.comparison-grid-row.title": "Összehasonlító Rács Sor",
  "admin/editor.comparison-grid-row.description": "Sor meta adatok a rács számára",
  "admin/editor.comparison-grid-row.field.title": "Mező",
  "admin/editor.comparison-grid-row.field.description": "Ez a mező, amelyet hozzá kell adni az összehasonlító részletes oldalhoz",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Mező típusa",
  "admin/editor.comparison-grid-row.field.fieldType.description": "A sor mezőjének típusa",
  "admin/editor.comparison-grid-row.field.name.title": "Mező neve",
  "admin/editor.comparison-grid-row.field.name.description": "Az összehasonlítási mező neve (termékmező neve, termék specifikáció neve vagy SKU specifikáció neve)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Megjelenítési név",
  "admin/editor.comparison-grid-row.field.display-value.description": "A mező megjelenített neve az összehasonlító oldalon",
  "admin/editor.comparison-grid-row.styles.title": "Cella stílusok",
  "admin/editor.comparison-grid-row.styles.width.title": "Szélesség",
  "admin/editor.comparison-grid-row.styles.width.description": "A cella szélessége százalékban",
  "admin/editor.comparison-grid.titleText.title": "Cím",
  "store/product-comparison.product-selector.product": "Termék:",
  "store/product-comparison.product-selector.product-added": "hasonlításhoz hozzáadva",
  "store/product-comparison.product-selector.product-removed": "hasonlításból eltávolítva",
  "store/product-comparison.product-selector.compare": "Hasonlítás",
  "store/product-comparison.drawer.products": "{productsLength, plural, one {termék} other {termékek}}",
  "store/product-comparison.drawer.compare": "Hasonlítás",
  "store/product-comparison.drawer.remove-all-message": "Az összes termék eltávolítva az összehasonlításból",
  "store/product-comparison.drawer.remove-all": "Minden eltávolítása",
  "store/product-comparison.drawer.hide": "elrejt",
  "store/product-comparison.drawer.show": "megjelenít",
  "store/product-comparison.drawer.min-items-message": "Legalább két terméket hozzá kell adnia az összehasonlításhoz",
  "store/product-comparison.drawer.max-items-message-1": "Egyszerre legfeljebb",
  "store/product-comparison.drawer.max-items-message-2": "terméket lehet összehasonlítani",
  "store/product-comparison.main-page.back-to-products": "Vissza a termékek listájához",
  "store/product-comparison.main-page.sort-by": "Rendezés szerint",
  "store/product-comparison.main-page.order-added": "Hozzáadási sorrend",
  "store/product-comparison.main-page.title": "{productsLength, number} termék összehasonlítása",
  "store/product-comparison.product-summary-row.show-differences": "Csak különbségek mutatása",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Termék Specifikáció Csoport elrejtése",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Azoknak a termék specifikáció csoportoknak a neve, amelyeket el kell rejteni az összehasonlító oldalon (vesszővel válassza el őket ,)",
  "store/product-comparison.product-selector.upper-limit-exceeded": "Ezt az elemet nem lehet hozzáadni az összehasonlításhoz, a kosár mérete túllépve"
}

```


[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/messages/de.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "Countdown",
  "editor.countdown.description": "Countdown-Komponente",
  "admin/editor.comparison-grid.title": "Vergleichstabelle",
  "admin/editor.comparison-grid.description": "Spalteninformationen der Vergleichstabelle",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Versteckte Spezifikationen",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Komma-separierte Liste der zu versteckenden Spezifikationsnamen",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "Versteckte SKU-Spezifikationen",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Kommagetrennte Liste der auszublendenden SKU-Spezifikationsnamen",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Versteckte Produktfelder",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Komma-separierte Liste der zu versteckenden Produktfelder",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "Versteckte SKU-Felder",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Komma-separierte Liste der zu versteckenden SKU-Felder",
  "admin/editor.comparison-grid-row.title": "Zeile der Vergleichstabelle",
  "admin/editor.comparison-grid-row.description": "Zeilen-Metainformationen der Vergleichstabelle",
  "admin/editor.comparison-grid-row.field.title": "Feld",
  "admin/editor.comparison-grid-row.field.description": "Dieses Feld wird für die Vergleichsdetailseite benötigt",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Feldtyp",
  "admin/editor.comparison-grid-row.field.fieldType.description": "Feldtyp der Zeile",
  "admin/editor.comparison-grid-row.field.name.title": "Feldname",
  "admin/editor.comparison-grid-row.field.name.description": "Name des Vergleichsfeldes (Produktfeldname, Spezifikationsname oder SKU-Spezifikationsname)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Angezeigter Name",
  "admin/editor.comparison-grid-row.field.display-value.description": "Der anzuzeigende Name des Feldes auf der Vergleichsseite",
  "admin/editor.comparison-grid-row.styles.title": "Zellen-Stil",
  "admin/editor.comparison-grid-row.styles.width.title": "Breite",
  "admin/editor.comparison-grid-row.styles.width.description": "Zellenbreite in Prozent",
  "admin/editor.comparison-grid.titleText.title": "Titel",
  "store/product-comparison.product-selector.product": "Produkt:",
  "store/product-comparison.product-selector.product-added": "Zum Vergleich hinzugefügt",
  "store/product-comparison.product-selector.product-removed": "Vom Vergleich entfernt",
  "store/product-comparison.product-selector.compare": "Vergleichen",
  "store/product-comparison.drawer.products": "{productsLength, plural, one {Produkt} other {Produkte}}",
  "store/product-comparison.drawer.compare": "Vergleichen",
  "store/product-comparison.drawer.remove-all-message": "Alle Produkte entfernt",
  "store/product-comparison.drawer.remove-all": "Alle entfernen",
  "store/product-comparison.drawer.hide": "Verstecken",
  "store/product-comparison.drawer.show": "Anzeigen",
  "store/product-comparison.drawer.min-items-message": "Mindestens 2 Produkte zum Vergleichen hinzufügen",
  "store/product-comparison.drawer.max-items-message-1": "Es können bis zu",
  "store/product-comparison.drawer.max-items-message-2": "Produkte auf einmal miteinander verglichen werden",
  "store/product-comparison.main-page.back-to-products": "Zurück zur Ergebnisseite",
  "store/product-comparison.main-page.sort-by": "Sortieren nach",
  "store/product-comparison.main-page.order-added": "Sortierung hinzugefügt",
  "store/product-comparison.main-page.title": "{productsLength, number} {productsLength, plural, one {Produkt} other {Produkte}} vergleichen",
  "store/product-comparison.product-summary-row.show-differences": "Nur Unterschiede anzeigen",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Versteckte Spezifikationsgruppen",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Komma-separierte Liste der zu versteckenden Spezifikationsgruppennamen",
  "store/product-comparison.product-selector.upper-limit-exceeded": "Produkt kann nicht verglichen werden. Maximale Anzahl erreicht"
}

```


[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/messages/ru.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "Обратный отсчет",
  "editor.countdown.description": "Компонент обратного отсчета",
  "admin/editor.comparison-grid.title": "Сравнительная таблица",
  "admin/editor.comparison-grid.description": "Информация со строк сравнительной таблицы продукта",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Скрыть спецификации продукта",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Названия спецификаций продукта, которые должны быть скрыты на сравнительной странице (отделяются запятой «,»)",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "Скрыть спецификации артикула",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Названия спецификаций артикула, которые должны быть скрыты на сравнительной странице (отделяются запятой «,»)",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Скрыть поля продукта",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Поля продукта, которые должны быть скрыты на сравнительной странице (отделяются запятой «,»)",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "Скрыть поля артикула",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Поля артикула, которые должны быть скрыты на сравнительной странице (отделяются запятой «,»)",
  "admin/editor.comparison-grid-row.title": "Строка сравнительной таблицы",
  "admin/editor.comparison-grid-row.description": "Метаданные строки для таблицы",
  "admin/editor.comparison-grid-row.field.title": "Поле",
  "admin/editor.comparison-grid-row.field.description": "Это поле, которое нам нужно добавить на страницу сравнения",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Тип поля",
  "admin/editor.comparison-grid-row.field.fieldType.description": "Тип поля строки",
  "admin/editor.comparison-grid-row.field.name.title": "Название поля",
  "admin/editor.comparison-grid-row.field.name.description": "Название поля сравнения (название поля продукта, название спецификаций продукта или артикула)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Отображаемое имя",
  "admin/editor.comparison-grid-row.field.display-value.description": "Отображаемое имя поля на сравнительной странице",
  "admin/editor.comparison-grid-row.styles.title": "Стили ячеек",
  "admin/editor.comparison-grid-row.styles.width.title": "Ширина",
  "admin/editor.comparison-grid-row.styles.width.description": "Ширина ячейки в процентах",
  "admin/editor.comparison-grid.titleText.title": "Название",
  "store/product-comparison.product-selector.product": "Товар:",
  "store/product-comparison.product-selector.product-added": "добавлено к сравнению",
  "store/product-comparison.product-selector.product-removed": "удалено из сравнения",
  "store/product-comparison.product-selector.compare": "Сравнить",
  "store/product-comparison.drawer.products": "{productsLength, plural, one {товар} few {товара} many {товаров} other {товаров}}",
  "store/product-comparison.drawer.compare": "Сравнить",
  "store/product-comparison.drawer.remove-all-message": "Все товары удалены из сравнения",
  "store/product-comparison.drawer.remove-all": "Удалить все",
  "store/product-comparison.drawer.hide": "скрыть",
  "store/product-comparison.drawer.show": "показать",
  "store/product-comparison.drawer.min-items-message": "Необходимо добавить хотя бы два товара для сравнения",
  "store/product-comparison.drawer.max-items-message-1": "Можно сравнивать до",
  "store/product-comparison.drawer.max-items-message-2": "товаров одновременно",
  "store/product-comparison.main-page.back-to-products": "Вернуться к списку товаров",
  "store/product-comparison.main-page.sort-by": "Сортировать по",
  "store/product-comparison.main-page.order-added": "По порядку добавления",
  "store/product-comparison.main-page.title": "Сравнить {productsLength, number} {productsLength, plural, one {товар} few {товара} many {товаров} other {товаров}}",
  "store/product-comparison.product-summary-row.show-differences": "Показывать только различия",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Скрыть группу спецификаций товара",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Названия групп спецификаций товара, которые должны быть скрыты на сравнительной странице (отделяются запятой «,»)",
  "store/product-comparison.product-selector.upper-limit-exceeded": "Невозможно добавить этот товар в сравнение из-за превышения размера корзины"
}

```


[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/messages/pt.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "Contagem regressiva",
  "editor.countdown.description": "Componente de contagem regressiva",
  "admin/editor.comparison-grid.title": "Grade de comparação",
  "admin/editor.comparison-grid.description": "Informações sobre a compilação de linhas da grade de comparação de produtos",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Ocultar especificações do produto",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Nomes das especificações de produto que precisam ser ocultadas na página de comparação (separe-as por vírgula `,`)",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "Ocultar especificações de SKU",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Nomes das especificações de SKU que precisam ser ocultadas na página de comparação (separe-as por vírgula `,`)",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Ocultar campos do produto",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Campos do produto que precisam ser ocultados na página de comparação (separe-os por vírgula `,`)",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "Ocultar campos do SKU",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Campos do SKU que precisam ser ocultados na página de comparação (separe-os por vírgula `,`)",
  "admin/editor.comparison-grid-row.title": "Linha da grade de comparação",
  "admin/editor.comparison-grid-row.description": "Metadados de fila para grade",
  "admin/editor.comparison-grid-row.field.title": "Campo",
  "admin/editor.comparison-grid-row.field.description": "Este é o campo que precisamos adicionar à página de detalhes de comparação",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Tipo de campo",
  "admin/editor.comparison-grid-row.field.fieldType.description": "Tipo de campo da linha",
  "admin/editor.comparison-grid-row.field.name.title": "Nome do campo",
  "admin/editor.comparison-grid-row.field.name.description": "Nome do campo de comparação (nome do campo do produto, nome da especificação do produto ou especificação do SKU)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Nome de exibição",
  "admin/editor.comparison-grid-row.field.display-value.description": "Nome de exibição do campo na página de comparação",
  "admin/editor.comparison-grid-row.styles.title": "Estilos das células",
  "admin/editor.comparison-grid-row.styles.width.title": "Largura",
  "admin/editor.comparison-grid-row.styles.width.description": "Largura da célula em porcentagem",
  "admin/editor.comparison-grid.titleText.title": "Título",
  "store/product-comparison.product-selector.product": "Produto:",
  "store/product-comparison.product-selector.product-added": "adicionado à comparação",
  "store/product-comparison.product-selector.product-removed": "removido da comparação",
  "store/product-comparison.product-selector.compare": "Comparar",
  "store/product-comparison.drawer.products": "{productsLength, plural, one {produto} other {produtos}}",
  "store/product-comparison.drawer.compare": "Comparar",
  "store/product-comparison.drawer.remove-all-message": "Todos os produtos foram removidos da comparação",
  "store/product-comparison.drawer.remove-all": "Remover todos",
  "store/product-comparison.drawer.hide": "ocultar",
  "store/product-comparison.drawer.show": "exibir",
  "store/product-comparison.drawer.min-items-message": "Você precisa adicionar pelo menos dois produtos fazer uma comparação",
  "store/product-comparison.drawer.max-items-message-1": "Você pode comparar até",
  "store/product-comparison.drawer.max-items-message-2": "produtos por vez",
  "store/product-comparison.main-page.back-to-products": "Voltar para a lista de produtos",
  "store/product-comparison.main-page.sort-by": "Ordenar por",
  "store/product-comparison.main-page.order-added": "Pedido adicionado",
  "store/product-comparison.main-page.title": "Сomparar {productsLength, number} {productsLength, plural, one {produto} other {produtos}}",
  "store/product-comparison.product-summary-row.show-differences": "Exibir somente diferenças",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Ocultar grupo de especificações do produto",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Nomes dos grupos de especificação do produto que serão ocultados na página de comparação (separe-os por vírgula `,`)",
  "store/product-comparison.product-selector.upper-limit-exceeded": "Não é possível adicionar este item à comparação. Tamanho de bucket excedido."
}
```


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

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "Countdown",
  "editor.countdown.description": "Countdown component",
  "admin/editor.comparison-grid.title": "Comparison Grid",
  "admin/editor.comparison-grid.description": "Product Comparison Grid Row Collect information",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Hide Product Specifications",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Names of product specifications which needs to be hidden in comparison page (separate them by comma `,`)",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "Hide SKU Specifications",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Names of sku specifications which needs to be hidden in comparison page (separate them by comma `,`)",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Hide product fields",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Product fields which needs to be hidden in comparison page (separate them by comma `,`)",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "Hide sku fields",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Sku fields which needs to be hidden in comparison page (separate them by comma `,`)",
  "admin/editor.comparison-grid-row.title": "Comparison Grid Row",
  "admin/editor.comparison-grid-row.description": "Row meta data for grid",
  "admin/editor.comparison-grid-row.field.title": "Field",
  "admin/editor.comparison-grid-row.field.description": "This is the field we need to add to comparison detail page",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Field type",
  "admin/editor.comparison-grid-row.field.fieldType.description": "Field type of the row",
  "admin/editor.comparison-grid-row.field.name.title": "Field Name",
  "admin/editor.comparison-grid-row.field.name.description": "Comparison field name (name of the product field, name of product specification or sku specification)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Display Name",
  "admin/editor.comparison-grid-row.field.display-value.description": "Display name of the field in comparison page",
  "admin/editor.comparison-grid-row.styles.title": "Cell styles",
  "admin/editor.comparison-grid-row.styles.width.title": "Width",
  "admin/editor.comparison-grid-row.styles.width.description": "Width of the cell as a percentage",
  "admin/editor.comparison-grid.titleText.title": "Title",
  "store/product-comparison.product-selector.product": "Product:",
  "store/product-comparison.product-selector.product-added": "added to compare",
  "store/product-comparison.product-selector.product-removed": "removed from compare",
  "store/product-comparison.product-selector.compare": "Compare",
  "store/product-comparison.drawer.products": "{productsLength, plural, one {product} other {products}}",
  "store/product-comparison.drawer.compare": "Compare",
  "store/product-comparison.drawer.remove-all-message": "All products removed from comparison",
  "store/product-comparison.drawer.remove-all": "Remove All",
  "store/product-comparison.drawer.hide": "hide",
  "store/product-comparison.drawer.show": "show",
  "store/product-comparison.drawer.min-items-message": "You need to add at least two products to compare",
  "store/product-comparison.drawer.max-items-message-1": "You can compare up to",
  "store/product-comparison.drawer.max-items-message-2": "products at a time",
  "store/product-comparison.main-page.back-to-products": "Back to products list",
  "store/product-comparison.main-page.sort-by": "Sort by",
  "store/product-comparison.main-page.order-added": "Order added",
  "store/product-comparison.main-page.title": "Сompare {productsLength, number} {productsLength, plural, one {product} other {products}}",
  "store/product-comparison.product-summary-row.show-differences": "Show only differences",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Hide Product Specification Group",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Names of product specification groups which needs to be hidden in comparison page (separate them by comma `,`)",
  "store/product-comparison.product-selector.upper-limit-exceeded": "Can't add this item to compare, bucket size exceeded"
}

```


[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/messages/it.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "Conto alla rovescia",
  "editor.countdown.description": "Componente del conto alla rovescia",
  "admin/editor.comparison-grid.title": "Griglia di confronto",
  "admin/editor.comparison-grid.description": "Riga della griglia di confronto per raccogliere informazioni",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Nascondi specifiche del prodotto",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Nomi delle specifiche del prodotto che verranno nascosti nella pagina di confronto (vanno separati con una virgola ',')",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "Nascondi specifiche dello SKU",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Nomi delle specifiche dello SKU che verranno nascosti nella pagina di confronto (vanno separati con una virgola ',')",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Nascondi campi del prodotto",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Campi del prodotto che verranno nascosti nella pagina di confronto (vanno separati con una virgola ',')",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "Nascondi campi dello SKU",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Campi dello SKU che verranno nascosti nella pagina di confronto (vanno separati con una virgola ',')",
  "admin/editor.comparison-grid-row.title": "Riga della griglia di confronto",
  "admin/editor.comparison-grid-row.description": "Riga della griglia di confronto per i metadati",
  "admin/editor.comparison-grid-row.field.title": "Campo",
  "admin/editor.comparison-grid-row.field.description": "Questo è il campo che è necessario aggiungere alla pagina dei dettagli di confronto",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Tipo di campo",
  "admin/editor.comparison-grid-row.field.fieldType.description": "Tipo di campo della riga",
  "admin/editor.comparison-grid-row.field.name.title": "Nome del campo",
  "admin/editor.comparison-grid-row.field.name.description": "Nome del campo di confronto (nome del campo del prodotto, nome della specifica del prodotto o della specifica dello SKU)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Mostra nome",
  "admin/editor.comparison-grid-row.field.display-value.description": "Mostra il nome del campo nella pagina di confronto",
  "admin/editor.comparison-grid-row.styles.title": "Stili delle celle",
  "admin/editor.comparison-grid-row.styles.width.title": "Larghezza",
  "admin/editor.comparison-grid-row.styles.width.description": "Larghezza della cella in percentuale",
  "admin/editor.comparison-grid.titleText.title": "Titolo",
  "store/product-comparison.product-selector.product": "Prodotto:",
  "store/product-comparison.product-selector.product-added": "aggiunto al confronto",
  "store/product-comparison.product-selector.product-removed": "rimosso dal confronto",
  "store/product-comparison.product-selector.compare": "Confronta",
  "store/product-comparison.drawer.products": "Prodotti",
  "store/product-comparison.drawer.compare": "Confronta",
  "store/product-comparison.drawer.remove-all-message": "Tutti i prodotti sono stati rimossi dal confronto",
  "store/product-comparison.drawer.remove-all": "Rimuovi tutti",
  "store/product-comparison.drawer.hide": "nascondi",
  "store/product-comparison.drawer.show": "mostra",
  "store/product-comparison.drawer.min-items-message": "Devi aggiungere almeno due prodotti per fare un confronto",
  "store/product-comparison.drawer.max-items-message-1": "Puoi confrontare fino a",
  "store/product-comparison.drawer.max-items-message-2": "prodotti alla volta",
  "store/product-comparison.main-page.back-to-products": "Torna all'elenco dei prodotti",
  "store/product-comparison.main-page.sort-by": "Ordina per",
  "store/product-comparison.main-page.order-added": "Ordine aggiunto",
  "store/product-comparison.main-page.title": "Confronta {productsLength} prodotti",
  "store/product-comparison.product-summary-row.show-differences": "Mostra solo le differenze",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Nascondi gruppo di specifiche del prodotto",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Nomi dei gruppi di specifiche del prodotto che verranno nascosti nella pagina di confronto (vanno separati con una virgola ',')",
  "store/product-comparison.product-selector.upper-limit-exceeded": "Impossibile aggiungere questo elemento al confronto, dimensione del bucket superata"
}
```


[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/messages/fr.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "Compte à rebours",
  "editor.countdown.description": "Composant Compte à Rebours",
  "admin/editor.comparison-grid.title": "Grille de comparaison",
  "admin/editor.comparison-grid.description": "Informations sur la collecte des lignes de la grille de comparaison de produits",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Cacher les caractéristiques du produit",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Noms des spécifications du produit qui doivent être cachés dans la page de comparaison (séparez-les par virgule `,`)",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "Masquer les caractéristiques SKU",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Noms des spécifications SKU qui doivent être cachées dans la page de comparaison (séparez-les par des virgules `,`)",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Masquer les champs de produit",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Champs de produit qui doivent être cachés dans la page de comparaison (séparez-les par une virgule `,`)",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "Masquer les champs SKU",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Champs SKU qui doivent être cachés dans la page de comparaison (séparez-les par une virgule `,`)",
  "admin/editor.comparison-grid-row.title": "Ligne de grille de comparaison",
  "admin/editor.comparison-grid-row.description": "Méta données de la ligne pour la grille",
  "admin/editor.comparison-grid-row.field.title": "Champ",
  "admin/editor.comparison-grid-row.field.description": "Champ que nous devons ajouter à la page de détail de la comparaison",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Type de champ",
  "admin/editor.comparison-grid-row.field.fieldType.description": "Type de champ de la ligne",
  "admin/editor.comparison-grid-row.field.name.title": "Nom du champ",
  "admin/editor.comparison-grid-row.field.name.description": "Nom du champ de comparaison (nom du champ produit, nom de la spécification du produit ou spécification SKU)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Afficher le nom",
  "admin/editor.comparison-grid-row.field.display-value.description": "Afficher le nom du champ dans la page de comparaison",
  "admin/editor.comparison-grid-row.styles.title": "Les styles de cellule",
  "admin/editor.comparison-grid-row.styles.width.title": "Largeur",
  "admin/editor.comparison-grid-row.styles.width.description": "Largeur de la cellule en pourcentage",
  "admin/editor.comparison-grid.titleText.title": "Titre",
  "store/product-comparison.product-selector.product": "Produit :",
  "store/product-comparison.product-selector.product-added": "ajouté à la comparaison",
  "store/product-comparison.product-selector.product-removed": "supprimé de la comparaison",
  "store/product-comparison.product-selector.compare": "Comparer",
  "store/product-comparison.drawer.products": "{productsLength, plural, one {product} other {products}}",
  "store/product-comparison.drawer.compare": "Comparer",
  "store/product-comparison.drawer.remove-all-message": "Tous les produits retirés de la comparaison",
  "store/product-comparison.drawer.remove-all": "Tout supprimer",
  "store/product-comparison.drawer.hide": "masquer",
  "store/product-comparison.drawer.show": "afficher",
  "store/product-comparison.drawer.min-items-message": "Vous devez ajouter au moins deux produits à comparer",
  "store/product-comparison.drawer.max-items-message-1": "Vous pouvez comparer jusqu’à",
  "store/product-comparison.drawer.max-items-message-2": "produits à la fois",
  "store/product-comparison.main-page.back-to-products": "Retour à la liste des produits",
  "store/product-comparison.main-page.sort-by": "Trier par",
  "store/product-comparison.main-page.order-added": "Commande ajoutée",
  "store/product-comparison.main-page.title": "Сomparer {productsLength, number} {productsLength, plural, one {product} other {products}}",
  "store/product-comparison.product-summary-row.show-differences": "Afficher uniquement les différences",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Cacher le groupe de caractéristiques de produits",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Noms des groupes de caractéristiques de produits qui doivent être cachés dans la page de comparaison (séparez-les par virgule `,`)",
  "store/product-comparison.product-selector.upper-limit-exceeded": "Impossible d’ajouter cet élément à la comparaison, la taille du segment est trop élevée"
}

```


[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/messages/context.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.product-summary-row.title": "Comparison row",
  "admin/editor.product-summary-row.description": "",
  "editor.countdown.title": "Countdown",
  "editor.countdown.description": "Countdown component",
  "admin/editor.comparison-grid.title": "Comparison Grid",
  "admin/editor.comparison-grid.description": "Product Comparison Grid Row Collect information",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Hide Product Specifications",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Names of product specifications which needs to be hidden in comparison page (separate them by comma `,`)",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "Hide SKU Specifications",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Names of sku specifications which needs to be hidden in comparison page (separate them by comma `,`)",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Hide product fields",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Product fields which needs to be hidden in comparison page (separate them by comma `,`)",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "Hide sku fields",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Sku fields which needs to be hidden in comparison page (separate them by comma `,`)",
  "admin/editor.comparison-grid-row.title": "Comparison Grid Row",
  "admin/editor.comparison-grid-row.description": "Row meta data for grid",
  "admin/editor.comparison-grid-row.field.title": "Field",
  "admin/editor.comparison-grid-row.field.description": "This is the field we need to add to comparison detail page",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Field type",
  "admin/editor.comparison-grid-row.field.fieldType.description": "Field type of the row",
  "admin/editor.comparison-grid-row.field.name.title": "Field Name",
  "admin/editor.comparison-grid-row.field.name.description": "Comparison field name (name of the product field, name of product specification or sku specification)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Display Name",
  "admin/editor.comparison-grid-row.field.display-value.description": "Display name of the field in comparison page",
  "admin/editor.comparison-grid-row.styles.title": "Cell styles",
  "admin/editor.comparison-grid-row.styles.width.title": "Width",
  "admin/editor.comparison-grid-row.styles.width.description": "Width of the cell as a percentage",
  "admin/editor.comparison-grid.titleText.title": "Title",
  "store/product-comparison.product-selector.product": "Product:",
  "store/product-comparison.product-selector.product-added": "added to compare",
  "store/product-comparison.product-selector.product-removed": "removed from compare",
  "store/product-comparison.product-selector.compare": "Compare",
  "store/product-comparison.drawer.products": "Products",
  "store/product-comparison.drawer.compare": "Compare",
  "store/product-comparison.drawer.remove-all-message": "All products removed from comparison",
  "store/product-comparison.drawer.remove-all": "Remove All",
  "store/product-comparison.drawer.hide": "hide",
  "store/product-comparison.drawer.show": "show",
  "store/product-comparison.drawer.min-items-message": "You need to add at least two products to compare",
  "store/product-comparison.drawer.max-items-message-1": "You can compare up to",
  "store/product-comparison.drawer.max-items-message-2": "products at a time",
  "store/product-comparison.main-page.back-to-products": "Back to products list",
  "store/product-comparison.main-page.sort-by": "Sort by",
  "store/product-comparison.main-page.order-added": "Order added",
  "store/product-comparison.main-page.title": "Compare {productsLength} products",
  "store/product-comparison.product-summary-row.show-differences": "Show only differences",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Hide Product Specification Group",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Names of product specification groups which needs to be hidden in comparison page (separate them by comma `,`)",
  "store/product-comparison.product-selector.upper-limit-exceeded": "Can't add this item to compare, bucket size exceeded"
}

```


[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/messages/es.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "Cuenta regresiva",
  "editor.countdown.description": "Componente de cuenta regresiva",
  "admin/editor.comparison-grid.title": "Matriz de comparación",
  "admin/editor.comparison-grid.description": "Información de recopilación de filas de la matriz de comparación de productos",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "Ocultar especificaciones del producto",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "Nombres de especificaciones de producto que deben ocultarse en la página de comparación (se deben separar con comas `,`)",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "Ocultar especificaciones de SKU",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "Nombres de SKU de producto que deben ocultarse en la página de comparación (se deben separar con comas `,`)",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "Ocultar campos del producto",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "Campos de producto que deben ocultarse en la página de comparación (se deben separar con comas `,`)",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "Ocultar campos de SKU",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "Campos de SKU que deben ocultarse en la página de comparación (se deben separar con comas `,`)",
  "admin/editor.comparison-grid-row.title": "Fila de la matriz de comparación",
  "admin/editor.comparison-grid-row.description": "Metadatos de la fila en la matriz",
  "admin/editor.comparison-grid-row.field.title": "Campo",
  "admin/editor.comparison-grid-row.field.description": "Este es el campo que debemos agregar a la página de detalles de comparación",
  "admin/editor.comparison-grid-row.field.fieldType.title": "Tipo de campo",
  "admin/editor.comparison-grid-row.field.fieldType.description": "Tipo de campo de la fila",
  "admin/editor.comparison-grid-row.field.name.title": "Nombre del campo",
  "admin/editor.comparison-grid-row.field.name.description": "Nombre del campo de comparación (nombre del campo de producto, nombre de la especificación de producto o especificación de SKU)",
  "admin/editor.comparison-grid-row.field.display-value.title": "Nombre para mostrar",
  "admin/editor.comparison-grid-row.field.display-value.description": "Nombre para mostrar del campo en la página de comparación",
  "admin/editor.comparison-grid-row.styles.title": "Estilos de celda",
  "admin/editor.comparison-grid-row.styles.width.title": "Anchura",
  "admin/editor.comparison-grid-row.styles.width.description": "Anchura de la celda en porcentaje",
  "admin/editor.comparison-grid.titleText.title": "Título",
  "store/product-comparison.product-selector.product": "Producto:",
  "store/product-comparison.product-selector.product-added": "agregado a la comparación",
  "store/product-comparison.product-selector.product-removed": "removido de la comparación",
  "store/product-comparison.product-selector.compare": "Comparar",
  "store/product-comparison.drawer.products": "{productsLength, plural, one {producto} other {productos}}",
  "store/product-comparison.drawer.compare": "Comparar",
  "store/product-comparison.drawer.remove-all-message": "Se removieron todos los productos de la comparación",
  "store/product-comparison.drawer.remove-all": "Remover todos",
  "store/product-comparison.drawer.hide": "ocultar",
  "store/product-comparison.drawer.show": "mostrar",
  "store/product-comparison.drawer.min-items-message": "Debes agregar por lo menos dos productos a la comparación",
  "store/product-comparison.drawer.max-items-message-1": "Puedes comparar hasta",
  "store/product-comparison.drawer.max-items-message-2": "productos a la vez",
  "store/product-comparison.main-page.back-to-products": "Volver a la lista de productos",
  "store/product-comparison.main-page.sort-by": "Ordenar por",
  "store/product-comparison.main-page.order-added": "Orden agregado",
  "store/product-comparison.main-page.title": "Сomparar {productsLength, number} {productsLength, plural, one {producto} other {productos}}",
  "store/product-comparison.product-summary-row.show-differences": "Solo mostrar diferencias",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "Ocultar grupo de especificaciones de producto",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "Nombres de los grupos de especificaciones de producto que deben ocultarse en la página de comparación (se deben separar con comas `,`)",
  "store/product-comparison.product-selector.upper-limit-exceeded": "No se puede agregar este ítem a la comparación. Se excedió el tamaño del contenedor."
}
```


[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/messages/ar.json from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "editor.countdown.title": "العد التنازلي",
  "editor.countdown.description": "عنصر العد التنازلي",
  "admin/editor.comparison-grid.title": "شبكة المقارنة",
  "admin/editor.comparison-grid.description": "معلومات مجموعة صف شبكة مقارنة المنتج",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.title": "إخفاء مواصفات المنتج",
  "admin/editor.comparison-grid.product-specifications-to-be-removed.description": "أسماء مواصفات المنتج التي تحتاج إلى إخفاء في صفحة المقارنة (افصلها بفاصلة '،')",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.title": "إخفاء مواصفات الـ SKU",
  "admin/editor.comparison-grid.sku-specifications-to-be-removed.description": "أسماء مواصفات الـ sku التي تحتاج إلى أن تكون مخفية في صفحة المقارنة (فصلها بفاصلة '،')",
  "admin/editor.comparison-grid.product-fields-to-be-removed.title": "إخفاء حقول المنتج",
  "admin/editor.comparison-grid.product-fields-to-be-removed.description": "حقول المنتج التي تحتاج إلى أن تكون مخفية في صفحة المقارنة (افصلها بفاصلة '،')",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.title": "إخفاء حقول الـ sku",
  "admin/editor.comparison-grid.sku-fields-to-be-removed.description": "حقول الـ Sku التي تحتاج إلى أن تكون مخفية في صفحة المقارنة (افصلها بفاصلة '،')",
  "admin/editor.comparison-grid-row.title": "صف شبكة المقارنة",
  "admin/editor.comparison-grid-row.description": "بيانات التعريف الخاصة بالشبكة",
  "admin/editor.comparison-grid-row.field.title": "الحقل",
  "admin/editor.comparison-grid-row.field.description": "هذا هو المجال الذي نحتاج إلى إضافته إلى صفحة تفاصيل المقارنة",
  "admin/editor.comparison-grid-row.field.fieldType.title": "نوع الحقل",
  "admin/editor.comparison-grid-row.field.fieldType.description": "نوع الحقل من الصف",
  "admin/editor.comparison-grid-row.field.name.title": "اسم الحقل",
  "admin/editor.comparison-grid-row.field.name.description": "اسم حقل المقارنة (اسم حقل المنتج، اسم مواصفات المنتج أو مواصفات الـ sku)",
  "admin/editor.comparison-grid-row.field.display-value.title": "عرض الاسم",
  "admin/editor.comparison-grid-row.field.display-value.description": "عرض اسم الحقل في صفحة المقارنة",
  "admin/editor.comparison-grid-row.styles.title": "أنماط الخانة",
  "admin/editor.comparison-grid-row.styles.width.title": "العرض",
  "admin/editor.comparison-grid-row.styles.width.description": "عرض الخانة كنسبة مئوية",
  "admin/editor.comparison-grid.titleText.title": "العنوان",
  "store/product-comparison.product-selector.product": "المُنتج:",
  "store/product-comparison.product-selector.product-added": "إضافة للمقارنة",
  "store/product-comparison.product-selector.product-removed": "إزالة من المقارنة",
  "store/product-comparison.product-selector.compare": "مقارنة",
  "store/product-comparison.drawer.products": "المُنتجات",
  "store/product-comparison.drawer.compare": "مقارنة",
  "store/product-comparison.drawer.remove-all-message": "تم حذف جميع المنتجات من المقارنة",
  "store/product-comparison.drawer.remove-all": "حذف الكل",
  "store/product-comparison.drawer.hide": "إخفاء",
  "store/product-comparison.drawer.show": "عرض",
  "store/product-comparison.drawer.min-items-message": "تحتاج إلى إضافة منتجين على الأقل للمقارنة",
  "store/product-comparison.drawer.max-items-message-1": "يمكنك مقارنة ما يقارب",
  "store/product-comparison.drawer.max-items-message-2": "منتجات في وقت واحد",
  "store/product-comparison.main-page.back-to-products": "عودة إلى قائمة المنتجات",
  "store/product-comparison.main-page.sort-by": "ترتيب حسب",
  "store/product-comparison.main-page.order-added": "تم إضافة الطلب",
  "store/product-comparison.main-page.title": "قارن {productsLength} منتجات",
  "store/product-comparison.product-summary-row.show-differences": "إظهار الاختلافات فقط",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.title": "إخفاء مجموعة مواصفات المنتج",
  "admin/editor.comparison-grid.product-specification-groups-to-be-removed.description": "أسماء مجموعات مواصفات المنتجات التي تحتاج إلى إخفاء في صفحة المقارنة (افصلها بفاصلة '،')",
  "store/product-comparison.product-selector.upper-limit-exceeded": "لا يمكن إضافة هذا العنصر للمقارنة، تم تجاوز حجم الدلو"
}

```


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

[Assistant]
Here's the implementation:

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

# Product Comparison

<!-- DOCS-IGNORE:start -->
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<!-- DOCS-IGNORE:end -->

The Product Comparison app compares specifications of pre-selected SKUs, allowing customers to better understand their needs when shopping.

The app exports several blocks, which you can leverage to display a Product Comparison drawer on your store's search results page and a new Product Comparison page.

![Comparison drawer](https://user-images.githubusercontent.com/2637457/90900569-43a41a00-e3e7-11ea-9aa9-9a12bbec8c5e.png)

*Product Comparison drawer on the store's search results page*

![image](https://user-images.githubusercontent.com/2637457/90900828-a1386680-e3e7-11ea-96d1-e9ba2022aa7d.png)

*Product Comparison page*

## Configuration

### Step 1 - Adding the Product Comparison app to your theme's dependencies

In your theme's `manifest.json` file, add the `Product Comparison` app as a dependency:

```diff
 "dependencies": {
+  "vtex.product-comparison": "0.x"
 }
```

Now, you can use all the blocks exported by the Product Comparison app. Check out the full list below:

| Block name   | Description                |
| :--------:   | :------------------------: |
| `product-comparison-drawer` | ![https://img.shields.io/badge/-Mandatory-red](https://img.shields.io/badge/-Mandatory-red) Main block that renders the drawer from the Product Comparison component in which the items will be compared. |
| `list-context.comparison-product-summary-slider` | Extends the `list-context` block to build the Product Comparison component using the [Slider Layout](https://vtex.io/docs/components/layout-blocks/vtex.slider-layout/.) |
| `product-summary.shelf.product-comparison` | Extends the default `product-summary.shelf` block (from the [Product Summary app](https://vtex.io/docs/components/all/vtex.product-summary/)) for the Product Comparison component's features. |
| `product-comparison-block` | Logical block that, once extended (see blocks listed below), renders the Product Comparison component's features. |
| `product-comparison-block.selector` | Renders the selector checkbox on the Product Comparison component. |
| `product-comparison-block.close-button` | Renders the close button on the Product Comparison component. |
| `product-comparison-block.product-summary-row` | Renders the first row to list and compare products on the Product Comparison component. |
| `list-context.comparison-row` | Extends the `list-context` block to build a row in the Product Comparison page using the [Slider Layout](https://vtex.io/docs/components/layout-blocks/vtex.slider-layout/.) |
| `product-comparison-block.grouped-product-specifications` | Renders the section for product specification groups. |
| `product-comparison-block.product-specifications` | Renders the section for product specifications. |
| `product-comparison-block.sku-specifications` | Renders the section for SKU specifications. |

### Step 2 - Adding extended interfaces

In the theme's `interfaces.json` file, add the following extended interfaces:

```diff
+{
+  "store.search.product-comparison": {
+    "around": ["comparison-context-wrapper"]
+  },
+  "search-result-layout.desktop.product-comparison": {
+    "allowed": ["product-comparison-drawer"]
+  },
  "store.product.product-comparison": {
    "around": ["comparison-context-wrapper"],
    "allowed": ["product-comparison-drawer"]
  }
+}
```

### Step 3 - Wrapping the search blocks with Product Comparison context

1. In the theme's `search.jsonc` file, replace the default `store.search` blocks with the `store.search.product-comparison` blocks, as shown in the example below:

```diff
{
  ...
- "store.search": {
+ "store.search.product-comparison": {
    ...
  },
- "store.search#brand": {
+ "store.search.product-comparison#brand": {
    ...
  },
- "store.search#department": {
+ "store.search.product-comparison#department": {
    ...
  }
- "store.search#category": {
+ "store.search.product-comparison#category": {
    ...
  }
- "store.search#subcategory": {
+ "store.search.product-comparison#subcategory": {
    ...
  }
  ...
```

> ℹ The `store.search.product-comparison` blocks wrap the `store.search` block with comparison context. The replacement is needed to synchronously display the selected products in the Product Comparison drawer.

2. Replace the `search-result-layout.desktop` blocks with the `search-result-layout.desktop.product-comparison` blocks, as shown below:

```diff
{
  ...
"search-result-layout": {
  "blocks": [
-   "search-result-layout.desktop#search"
+   "search-result-layout.desktop.product-comparison#search",
    "search-result-layout.mobile",
    "search-not-found-layout"
  ]
},
```

3. Replace the `product-summary.shelf`, child of the `gallery` block, with the `product-summary.shelf.product-comparison`:

```diff
"gallery": {
-  "blocks": ["product-summary.shelf"]
+  "blocks": ["product-summary.shelf.product-comparison#search"]
}
...
```

### Step 4 - Adding the Product Comparison to the product detail Page

1. In the theme's `product.json` file, replace the default `store.product` blocks with the `store.product.product-comparison` blocks, as shown in the example below:

```diff

- "store.product": {
+ "store.product.product-comparison": {
    ...
  }
```

2. Add `product-comparison-drawer` to the `store.product.product-comparison` children, as shown in the example below:

```diff
"store.product.product-comparison": {
  children:[
    ...
+    "product-comparison-drawer"
  ]
  ...
}
```

3. Add `product-comparison-block.selector#pdp` to the `store.product.product-comparison` block, as shown in the example below:

```diff
 "store.product.product-comparison": {
   ...
  children:[
    ...
+    "product-comparison-block.selector#pdp"
  ]
  ...
 }
 ```
### Step 5 - Building the Product Comparison component

In any desired template, such as `store.search`, add the `product-comparison-drawer` block, as shown below:

```json
- "search-result-layout.desktop#search": {
+ "search-result-layout.desktop.product-comparison#search": {
  "children": [
    "flex-layout.row#did-you-mean",
    "flex-layout.row#suggestion",
    "flex-layout.row#banner-one",
    "flex-layout.row#searchbread",
    "flex-layout.row#searchtitle",
    "flex-layout.row#result",
+   "product-comparison-drawer"
  ],
  "props": {
    "pagination": "show-more",
    "preventRouteChange": false,
    "mobileLayout": {
      "mode1": "small",
      "mode2": "normal"
    }
  }
}
...
```

By adding the `product-comparison-drawer` block as shown above, you will declare the following structure behind the scenes:

```json
"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-col2",
    "product-comparison-block.close-button"
  ],
  "props": {
    "blockClass": "drawer-summary"
  }
},
"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
  }
}
```

> ℹ *The code above is a default implementation of the Product Comparison component. To make any changes, declare the code above in your theme and perform the needed updates according to the available blocks.*

### Step 6 - Building the Product Comparison page

1. In the `/store/blocks` folder, create a new file called `product-comparison.json` and include the following JSON:

```json
"store.custom#product-comparison-list": {
  "blocks": ["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"]
},
"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"
  }
}
```

2. In the theme's `routes.json` file, add a new route for the Product Comparison page:

```diff
+{
+  "store.custom#product-comparison-list": {
+    "path": "/product-comparison"
+  }
+}
```

#### `product-comparison-block.grouped-product-specifications` props

| Prop name      | Type          | Description                    | Default value |
| :------------: | :-----------: | :----------------------------: | :-----------: |
| `productSpecificationsToHide` | `[string]` | List of product fields that should be hidden in the Product Comparison page. The desired product fields must be separated by a comma. | `undefined` |
| `productSpecificationGroupsToHide` | `[string]` | List of product specification groups that should be hidden on the Product Comparison page. The desired product specification groups must be separated by a comma. | `undefined` |

#### `product-comparison-block.product-specifications` props

| Prop name      | Type          | Description                    | Default value |
| :------------: | :-----------: | :----------------------------: | :--------:    |
| `productSpecificationsToHide` | `[string]` | List of product fields that should be hidden in the Product Comparison page. The desired product fields must be separated by a comma. | `undefined` |

#### `product-comparison-block.sku-specifications` props

| Prop name      | Type          | Description                    | Default value |
| :------------: | :-----------: | :----------------------------: | :--------:    |
| `skuSpecificationsToHide` | `[string]` | List of SKU specification fields that should be hidden on the Product Comparison page. The desired SKU specification fields must be separated by a comma. | `undefined` |

#### `product-comparison-block.product-summary-row` props

| Prop name      | Type          | Description                    | Default value |
| :------------: | :-----------: | :----------------------------: | :--------:    |
| `isShowDifferenceDefault` | `boolean` | Set the "show only differences" checkbox to true as the default value on the comparison page. | `false` |
=======

### Step 7 - Change the comparison bucket size

This is an optional configuration, so you can set a fixed number in the app configuration to change the maximum number of items in the comparison bucket.
The default value is 10 items; if you exceed the maximum limit, you will receive a notification.

#### Instructions

1. Go to `/admin/apps`, then find the `Product Comparison` app under `Installed` apps.
2. Click Settings.
3. Enter the maximum comparison bucket size for your application.

## Customization

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

| CSS Handles                  |
| :--------------------------: |
| `closeButton`                |
| `closeButtonContainer`       |
| `compareProductsButton`      |
| `comparisonButtons`          |
| `comparisonCol`              |
| `drawer`                     |
| `drawerContainer`            |
| `expandCollapseButton`       |
| `fieldNameCol`               |
| `fieldNameCol`               |
| `productFieldValue`          |
| `productSelectorContainer`   |
| `productSpecificationValue`  |
| `productSpecificationValues` |
| `productSummaryRowContainer` |
| `rowContainer`               |
| `showDifferencesContainer`   |
| `skuFieldValue`              |
| `skuSpecificationValue`      |
| `skuSpecificationValues`     |
| `title`                      |
| `removeAllItemsButtonWrapper`|
| `compareProductButtonWrapper`|
| `removeAllWrapper`           |
| `drawerTitleOuterContainer`  |
| `drawerTitleInnerContainer`  |
| `drawerOpened`               |
| `drawerClosed`               |

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

## Contributors ✨

Thanks goes to these wonderful people:

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

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

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

```


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

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

[Assistant]
Here's the implementation:

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

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/ComparisonProductWrapper.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ReactChildren, ReactChild } from 'react'
import React, { useEffect, useState } from 'react'
import { pathOr } from 'ramda'
// import { pathOr, propEq, find } from 'ramda'
import productsQuery from 'vtex.store-resources/QueryProduct'
import { useApolloClient } from 'react-apollo'

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

interface Props {
  children: ReactChildren | ReactChild
}
const ComparisonProductWrapper = ({ children }: Props) => {
  const [products, setProducts] = useState([] as Product[])
  const client = useApolloClient()

  const { ComparisonProductProvider } = ComparisonProductContext
  const { useProductComparisonState } = ComparisonContext

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

  useEffect(() => {
    Promise.all(
      comparisonProducts.map((productToCompare: ProductToCompare) => {
        return client.query({
          query: productsQuery,
          variables: {
            identifier: {
              field: 'id',
              value: productToCompare.productId,
            },
          },
        })
      })
    ).then((productsList: Array<{ data: { product: Product } }>) => {
      const responseProducts: Product[] = productsList.map(
        (productResponse: { data: { product: Product } }) =>
          // eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
          pathOr({} as Product, ['data', 'product'], productResponse)
      )

      // const productSummaryProducts = comparisonProducts.map(productInfo => {
      //   const selectedProduct = find(
      //     propEq('productId', productInfo.productId)
      //   )(responseProducts)

      //   return mapCatalogProductToProductSummary(
      //     selectedProduct,
      //     productInfo.skuId
      //   )
      // })

      // setProducts(productSummaryProducts)

      setProducts(responseProducts)
    })
  }, [client, comparisonProducts])

  return (
    <ComparisonProductProvider
      products={comparisonProducts.length > 0 ? products : []}
    >
      {children}
    </ComparisonProductProvider>
  )
}

export default ComparisonProductWrapper

```


[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/ProductComparisonContext.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ReactChildren, ReactChild } from 'react'
import React, { useReducer, useContext, createContext, useEffect } from 'react'
import { pathOr, reject, propEq, allPass } from 'ramda'
import { useQuery } from 'react-apollo'

import AppSettings from './queries/AppSettings.graphql'

export interface State {
  isDrawerCollapsed: boolean
  showDifferences: boolean
  products: ProductToCompare[]
  maxNumberOfItemsToCompare: number
}

interface SetShowDifferences {
  type: 'SET_SHOW_DIFFERENCES'
  args: { showDifferences: boolean }
}

interface AddAll {
  type: 'ADD_ALL'
  args: { products: ProductToCompare[] }
}

interface AddMultiple {
  type: 'ADD_MULTIPLE'
  args: { products: ProductToCompare[] }
}

interface RemoveAll {
  type: 'REMOVE_ALL'
}

interface Add {
  type: 'ADD'
  args: { product: ProductToCompare }
}

interface Remove {
  type: 'REMOVE'
  args: { product: ProductToCompare }
}

interface IsDrawerCollapsed {
  type: 'IS_DRAWER_COLLAPSED'
  args: { isDrawerCollapsed: boolean }
}

interface SetMaxLimit {
  type: 'SET_MAX_LIMIT'
  args: { maxLimit: number }
}

type ReducerActions =
  | AddAll
  | AddMultiple
  | Add
  | RemoveAll
  | Remove
  | SetShowDifferences
  | IsDrawerCollapsed
  | SetMaxLimit

export type Dispatch = (action: ReducerActions) => void

const listReducer = (state: State, action: ReducerActions): State => {
  switch (action.type) {
    case 'ADD_ALL': {
      const products = pathOr([], ['args', 'products'], action)
      const productsToCompare = [...state.products, ...products].slice(
        0,
        state.maxNumberOfItemsToCompare
      )

      return {
        ...state,
        products: productsToCompare,
      }
    }

    case 'ADD_MULTIPLE': {
      const products = pathOr([], ['args', 'products'], action)

      const productsToAdd = products.filter(
        (product: ProductToCompare) =>
          state.products.filter(
            (existing: ProductToCompare) =>
              existing.productId === product.productId &&
              existing.skuId === product.skuId
          ).length === 0
      )

      const newProductList = [...state.products, ...productsToAdd].slice(
        0,
        state.maxNumberOfItemsToCompare
      )

      localStorage.setItem(
        'PRODUCTS_TO_COMPARE',
        JSON.stringify(newProductList)
      )

      return {
        ...state,
        products: newProductList,
      }
    }

    case 'ADD': {
      const { product } = action.args
      const newProductList = [...state.products, product].slice(
        0,
        state.maxNumberOfItemsToCompare
      )

      localStorage.setItem(
        'PRODUCTS_TO_COMPARE',
        JSON.stringify(newProductList)
      )

      return {
        ...state,
        products: newProductList,
      }
    }

    case 'REMOVE_ALL': {
      localStorage.setItem('PRODUCTS_TO_COMPARE', JSON.stringify([]))

      return {
        ...state,
        products: [],
      }
    }

    case 'REMOVE': {
      const { product } = action.args
      const remaining = reject(
        allPass([
          propEq('productId', product.productId),
          propEq('skuId', product.skuId),
        ])
      )(state.products)

      localStorage.setItem('PRODUCTS_TO_COMPARE', JSON.stringify(remaining))

      return { ...state, products: remaining }
    }

    case 'SET_SHOW_DIFFERENCES': {
      return {
        ...state,
        ...{ showDifferences: action.args.showDifferences },
      }
    }

    case 'IS_DRAWER_COLLAPSED': {
      return {
        ...state,
        isDrawerCollapsed: action.args.isDrawerCollapsed,
      }
    }

    case 'SET_MAX_LIMIT': {
      return {
        ...state,
        maxNumberOfItemsToCompare: action.args.maxLimit,
      }
    }

    default: {
      throw new Error(`Unhandled action type on product-list-context`)
    }
  }
}

const MAX_ITEMS_TO_COMPARE = 10

const DEFAULT_STATE: State = {
  isDrawerCollapsed: false,
  showDifferences: false,
  products: [] as ProductToCompare[],
  maxNumberOfItemsToCompare: MAX_ITEMS_TO_COMPARE,
}

const ComparisonContext = createContext<State>(DEFAULT_STATE)
const ComparisonDispatchContext = createContext<Dispatch>((action) => {
  console.error('error in dispatch ', action)
})

const initialState: State = {
  showDifferences: false,
  isDrawerCollapsed: false,
  products: [] as ProductToCompare[],
  maxNumberOfItemsToCompare: MAX_ITEMS_TO_COMPARE,
}

interface Props {
  children: ReactChildren | ReactChild
}

const ProductComparisonProvider = ({ children }: Props) => {
  const [state, dispatch] = useReducer(listReducer, initialState)

  const { data: appSettingsData } = useQuery(AppSettings, {
    variables: {
      // eslint-disable-next-line no-undef
      version: process.env.VTEX_APP_VERSION,
    },
    ssr: false,
  })

  useEffect(() => {
    const appSettings = JSON.parse(
      pathOr(`{}`, ['publicSettingsForApp', 'message'], appSettingsData)
    )

    dispatch({
      type: 'SET_MAX_LIMIT',
      args: {
        maxLimit: pathOr(
          MAX_ITEMS_TO_COMPARE,
          ['maxNumberOfItemsToCompare'],
          appSettings
        ),
      },
    })
  }, [appSettingsData])

  useEffect(() => {
    const initialProducts = localStorage.getItem('PRODUCTS_TO_COMPARE')
    const productsToCompare = (initialProducts
      ? JSON.parse(initialProducts)
      : []) as ProductToCompare[]

    dispatch({ type: 'ADD_ALL', args: { products: productsToCompare } })
  }, [])

  return (
    <ComparisonContext.Provider value={state}>
      <ComparisonDispatchContext.Provider value={dispatch}>
        {children}
      </ComparisonDispatchContext.Provider>
    </ComparisonContext.Provider>
  )
}

const useProductComparisonState = () => useContext(ComparisonContext)

const useProductComparisonDispatch = () => useContext(ComparisonDispatchContext)

export default {
  ProductComparisonProvider,
  useProductComparisonState,
  useProductComparisonDispatch,
}

```


[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/global.css from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```css
.pageContainer {
}

.styleguide__layout {
  background-color: unset;
}

.styleguide__box {
  padding: unset;
  border: unset;
}

@-webkit-keyframes spin {
  0% { -webkit-transform: rotate(0deg); }
  100% { -webkit-transform: rotate(360deg); }
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.productLoader {
  border: 16px solid #f3f3f3;
  border-radius: 50%;
  border-top: 16px solid #3498db;
  width: 120px;
  height: 120px;
  -webkit-animation: spin 2s linear infinite; /* Safari */
  animation: spin 2s linear infinite;
}

```


[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/ComparisonProductContext.tsx from the product-comparison VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ReactChildren, ReactChild } from 'react'
import React, { useReducer, useContext, createContext, useEffect } from 'react'
import { pathOr, reject, propEq, allPass } from 'ramda'

export interface State {
  products: Product[]
}

interface AddAll {
  type: 'ADD_ALL_PRODUCTS'
  args: { products: Product[] }
}

interface RemoveAll {
  type: 'REMOVE_ALL_PRODUCTS'
}

interface Add {
  type: 'ADD_PRODUCT'
  args: { product: Product }
}

interface Remove {
  type: 'REMOVE_PRODUCT'
  args: { product: Product }
}

type ReducerActions = AddAll | Add | RemoveAll | Remove

export type Dispatch = (action: ReducerActions) => void

const listReducer = (state: State, action: ReducerActions): State => {
  switch (action.type) {
    case 'ADD_ALL_PRODUCTS': {
      const products = pathOr([], ['args', 'products'], action)

      return {
        ...state,
        products: [...products],
      }
    }

    case 'ADD_PRODUCT': {
      const { product } = action.args
      const newProductList = [...state.products, product]

      return {
        ...state,
        products: newProductList,
      }
    }

    case 'REMOVE_ALL_PRODUCTS': {
      return {
        ...state,
        products: [],
      }
    }

    case 'REMOVE_PRODUCT': {
      const { product } = action.args
      const remaining = reject(
        allPass([
          propEq('productId', product.productId),
          propEq('skuId', product.skuId),
        ])
      )(state.products)

      localStorage.setItem('PRODUCTS_TO_COMPARE', JSON.stringify(remaining))

      return { ...state, products: remaining }
    }

    default: {
      throw new Error(`Unhandled action type on product-list-context`)
    }
  }
}

const DEFAULT_STATE: State = {
  products: [] as Product[],
}

const ComparisonProductContext = createContext<State>(DEFAULT_STATE)
const ComparisonProductDispatchContext = createContext<Dispatch>((action) => {
  console.error('error in dispatch ', action)
})

const initialState: State = {
  products: [] as ProductToCompare[],
}

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

const ComparisonProductProvider = ({ products, children }: Props) => {
  const [state, dispatch] = useReducer(listReducer, initialState)

  useEffect(() => {
    if (products && products.length > 0) {
      dispatch({ type: 'ADD_ALL_PRODUCTS', args: { products } })
    }
  }, [products])

  return (
    <ComparisonProductContext.Provider value={state}>
      <ComparisonProductDispatchContext.Provider value={dispatch}>
        {children}
      </ComparisonProductDispatchContext.Provider>
    </ComparisonProductContext.Provider>
  )
}

const useComparisonProductState = () => useContext(ComparisonProductContext)

const useComparisonProductDispatch = () =>
  useContext(ComparisonProductDispatchContext)

export default {
  ComparisonProductProvider,
  useComparisonProductState,
  useComparisonProductDispatch,
}

```