[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 admin-catalog-translations/node/resolvers/product/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  PRODUCT_BUCKET,
  ALL_TRANSLATIONS_FILES,
  entryTranslations,
} from '../../utils'
import {
  mutations as uploadMutations,
  queries as uploadQueries,
} from './upload'

export const Product = {
  locale: (
    _root: ResolvedPromise<ProductTranslationResponse>,
    _args: unknown,
    ctx: Context
  ) => {
    return ctx.state.locale
  },
  id: (root: ResolvedPromise<ProductTranslationResponse>) =>
    root.data.product.id,
  name: (root: ResolvedPromise<ProductTranslationResponse>) =>
    root.data.product.name,
  description: (root: ResolvedPromise<ProductTranslationResponse>) =>
    root.data.product.description,
  shortDescription: (root: ResolvedPromise<ProductTranslationResponse>) =>
    root.data.product.shortDescription,
  title: (root: ResolvedPromise<ProductTranslationResponse>) =>
    root.data.product.title,
  linkId: (root: ResolvedPromise<ProductTranslationResponse>) =>
    root.data.product.linkId,
}

const productTranslations = async (
  _root: unknown,
  args: { locale: string; categoryId: string },
  ctx: Context
) => {
  const {
    clients: { catalog, catalogGQL },
    vtex: { requestId },
  } = ctx

  const { locale, categoryId } = args

  const productIdCollection = await catalog.getAllProducts(categoryId)

  const params: EntryTranslations<string> = {
    entries: productIdCollection,
    requestId,
    categoryId,
    locale,
    bucket: PRODUCT_BUCKET,
    path: ALL_TRANSLATIONS_FILES,
    translateEntry: catalogGQL.getProductTranslation,
  }
  return entryTranslations<string, ProductTranslationResponse>(params, ctx)
}

const productTranslationRequests = (
  _root: unknown,
  _args: unknown,
  ctx: Context
) => ctx.clients.vbase.getJSON(PRODUCT_BUCKET, ALL_TRANSLATIONS_FILES, true)

const downloadProductTranslation = async (
  _root: unknown,
  args: { requestId: string },
  ctx: Context
) => {
  const {
    clients: { vbase },
  } = ctx

  const { translations, locale } = await vbase.getJSON<
    TranslationRequest<ProductTranslationResponse>
  >(PRODUCT_BUCKET, args.requestId, true)

  ctx.state.locale = locale

  return translations
}

export const mutations = {
  ...uploadMutations,
}

export const queries = {
  productTranslations,
  productTranslationRequests,
  downloadProductTranslation,
  ...uploadQueries,
}

```


[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 admin-catalog-translations/node/resolvers/product/upload.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ReadStream } from 'fs'

import {
  PRODUCT_BUCKET,
  PRODUCT_TRANSLATION_UPLOAD,
  uploadTranslations,
} from '../../utils'

const uploadProductTranslations = async (
  _root: unknown,
  { products, locale }: { products: UploadFile<ReadStream>; locale: string },
  ctx: Context
) => {
  const params: UploadTranslations<ProductTranslationInput> = {
    entries: products,
    locale,
    bucket: PRODUCT_BUCKET,
    path: PRODUCT_TRANSLATION_UPLOAD,
    translateEntry: ctx?.clients?.catalogGQL?.translateProduct,
  }

  return uploadTranslations(params, ctx)
}

const productTranslationsUploadRequests = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
) =>
  ctx.clients.vbase.getJSON<string[]>(
    PRODUCT_BUCKET,
    PRODUCT_TRANSLATION_UPLOAD,
    true
  )

export const mutations = { uploadProductTranslations }

export const queries = {
  productTranslationsUploadRequests,
}

```


[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 admin-catalog-translations/node/resolvers/field/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { FIELD_BUCKET } from '../../utils'
import {
  queries as uploadQueries,
  mutations as uploadMutations,
} from './upload'

export const Field = {
  fieldId: (root: ResolvedPromise<FieldTranslationResponse>) =>
    root?.data?.field?.fieldId,
  name: (root: ResolvedPromise<FieldTranslationResponse>) =>
    root?.data?.field?.name,
}

const downloadFieldTranslations = async (
  _root: unknown,
  args: { requestId: string },
  ctx: Context
) => {
  const { translations } = await ctx.clients.vbase.getJSON<
    TranslationRequest<FieldTranslationResponse>
  >(FIELD_BUCKET, args.requestId, true)

  return translations
}

export const mutations = {
  ...uploadMutations,
}

export const queries = {
  downloadFieldTranslations,
  ...uploadQueries,
}

```


[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 admin-catalog-translations/node/resolvers/field/upload.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ReadStream } from 'fs'

import {
  FIELD_BUCKET,
  FIELD_TRANSLATION_EXPORT_UPLOAD,
  parseStreamToJSON,
  uploadTranslations,
  FIELD_TRANSLATION_IMPORT_UPLOAD,
  entryTranslations,
} from '../../utils'

const fieldTranslations = async (
  _root: unknown,
  { fields, locale }: { fields: UploadFile<ReadStream>; locale: string },
  ctx: Context
) => {
  const { createReadStream } = await fields
  const fieldStream = createReadStream()
  const fieldsParsed = await parseStreamToJSON<FieldTranslationInput>(
    fieldStream
  )

  let entryIdCollection: string[] = []
  for (let i = 0; i < fieldsParsed?.length; i++) {
    if (fieldsParsed) {
      entryIdCollection = [...entryIdCollection, fieldsParsed[i].fieldId]
    }
  }

  const params: EntryTranslations<string> = {
    entries: entryIdCollection,
    requestId: ctx?.vtex?.requestId,
    locale,
    bucket: FIELD_BUCKET,
    path: FIELD_TRANSLATION_EXPORT_UPLOAD,
    translateEntry: ctx?.clients?.catalogGQL.getFieldTranslation,
  }
  return entryTranslations<string, FieldTranslationResponse>(params, ctx)
}

const fieldTranslationsRequests = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
) =>
  ctx.clients.vbase.getJSON<string[]>(
    FIELD_BUCKET,
    FIELD_TRANSLATION_EXPORT_UPLOAD,
    true
  )

const uploadFieldTranslationsImport = async (
  _root: unknown,
  { fields, locale }: { fields: UploadFile<ReadStream>; locale: string },
  ctx: Context
) => {
  const params: UploadTranslations<FieldTranslationInput> = {
    entries: fields,
    locale,
    bucket: FIELD_BUCKET,
    path: FIELD_TRANSLATION_IMPORT_UPLOAD,
    translateEntry: ctx?.clients?.catalogGQL?.translateField,
  }
  return uploadTranslations(params, ctx)
}

const fieldTranslationsUploadRequests = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
) =>
  ctx.clients.vbase.getJSON<string[]>(
    FIELD_BUCKET,
    FIELD_TRANSLATION_IMPORT_UPLOAD,
    true
  )

export const mutations = {
  uploadFieldTranslationsImport,
}

export const queries = {
  fieldTranslations,
  fieldTranslationsRequests,
  fieldTranslationsUploadRequests,
}

```


[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 admin-catalog-translations/node/resolvers/sku/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Logger, VBase } from '@vtex/api'

import { CatalogGQL } from '../../clients/catalogGQL'
import {
  ALL_SKU_TRANSLATIONS_FILES,
  SKU_BUCKET,
  calculateExportProcessTime,
  pacer,
} from '../../utils'
import {
  mutations as uploadMutations,
  queries as uploadQueries,
} from './upload'

const CALLS_PER_MINUTE = 350

export const SKU = {
  locale: (
    _root: ResolvedPromise<SKUTranslationsResponse>,
    _args: unknown,
    ctx: Context
  ) => ctx.state.locale,
  id: (root: ResolvedPromise<SKUTranslationsResponse>) => root.data.sku.id,
  name: (root: ResolvedPromise<SKUTranslationsResponse>) => root.data.sku.name,
}

const saveSkuTranslation = async (
  {
    skuIds,
    locale,
    requestId,
  }: { skuIds: number[]; locale: string; requestId: string },
  {
    catalogGQLClient,
    vbase,
    logger,
  }: { catalogGQLClient: CatalogGQL; vbase: VBase; logger: Logger }
): Promise<void> => {
  const translationRequest = await vbase.getJSON<SKUTranslationRequest>(
    SKU_BUCKET,
    requestId,
    true
  )

  const skuTranslationPromises = []
  try {
    for (const skuId of skuIds) {
      const translationPromise = catalogGQLClient.getSKUTranslation(
        String(skuId),
        locale
      )
      skuTranslationPromises.push(translationPromise)
      // eslint-disable-next-line no-await-in-loop
      await pacer(CALLS_PER_MINUTE)
    }

    const translations = await Promise.all(skuTranslationPromises)

    const updateTranslation = {
      ...translationRequest,
      translations,
      completedAt: new Date(),
    }

    await vbase.saveJSON<SKUTranslationRequest>(
      SKU_BUCKET,
      requestId,
      updateTranslation
    )
  } catch (e) {
    logger.error({
      message: 'Error getting Sku translations',
      error: e,
    })

    const addError = {
      ...translationRequest,
      error: true,
    }
    await vbase.saveJSON<SKUTranslationRequest>(SKU_BUCKET, requestId, addError)
  }
}

// TODO: refactor this fn, see node\resolvers\product\index.ts => productTranslations
const skuTranslations = async (
  _root: unknown,
  args: { locale: string; categoryId: string },
  ctx: Context
) => {
  const {
    clients: { catalog, catalogGQL, vbase, licenseManager },
    vtex: { adminUserAuthToken, requestId, logger },
  } = ctx

  const {
    profile: { email },
  } = await licenseManager.getTopbarData(adminUserAuthToken as string)

  const { locale, categoryId } = args

  const skuIdColletion = await catalog.getAllSKUs(categoryId)

  const allSkuTranslationsRequest = await vbase.getJSON<string[]>(
    SKU_BUCKET,
    ALL_SKU_TRANSLATIONS_FILES,
    true
  )

  const updateRequests = allSkuTranslationsRequest
    ? [requestId, ...allSkuTranslationsRequest]
    : [requestId]

  await vbase.saveJSON<string[]>(
    SKU_BUCKET,
    ALL_SKU_TRANSLATIONS_FILES,
    updateRequests
  )

  const requestInfo: SKUTranslationRequest = {
    requestId,
    requestedBy: email,
    categoryId,
    locale,
    createdAt: new Date(),
    estimatedTime: calculateExportProcessTime(
      skuIdColletion.length,
      CALLS_PER_MINUTE
    ),
  }

  await vbase.saveJSON<SKUTranslationRequest>(
    SKU_BUCKET,
    requestId,
    requestInfo
  )

  saveSkuTranslation(
    {
      skuIds: skuIdColletion,
      locale,
      requestId,
    },
    {
      catalogGQLClient: catalogGQL,
      vbase,
      logger,
    }
  )

  return requestInfo
}

const skuTranslationRequests = (_root: unknown, _args: unknown, ctx: Context) =>
  ctx.clients.vbase.getJSON(SKU_BUCKET, ALL_SKU_TRANSLATIONS_FILES, true)

const downloadSKUTranslation = async (
  _root: unknown,
  args: { requestId: string },
  ctx: Context
) => {
  const {
    clients: { vbase },
  } = ctx

  const { translations, locale } = await vbase.getJSON<SKUTranslationRequest>(
    SKU_BUCKET,
    args.requestId,
    true
  )

  ctx.state.locale = locale

  return translations
}

export const mutations = {
  ...uploadMutations,
}

export const queries = {
  skuTranslations,
  skuTranslationRequests,
  downloadSKUTranslation,
  ...uploadQueries,
}

```


[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 admin-catalog-translations/node/resolvers/sku/upload.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ReadStream } from 'fs'

import {
  SKU_BUCKET,
  SKU_TRANSLATION_UPLOAD,
  uploadTranslations,
} from '../../utils'

const uploadSkuTranslations = async (
  _root: unknown,
  { skus, locale }: { skus: UploadFile<ReadStream>; locale: string },
  ctx: Context
) => {
  const params: UploadTranslations<SkuTranslationInput> = {
    entries: skus,
    locale,
    bucket: SKU_BUCKET,
    path: SKU_TRANSLATION_UPLOAD,
    translateEntry: ctx?.clients?.catalogGQL?.translateSku,
  }

  return uploadTranslations(params, ctx)
}

const skuTranslationsUploadRequests = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
) =>
  ctx.clients.vbase.getJSON<string[]>(
    SKU_BUCKET,
    SKU_TRANSLATION_UPLOAD,
    true
  )

export const mutations = { uploadSkuTranslations }

export const queries = {
  skuTranslationsUploadRequests,
}

```


[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 admin-catalog-translations/react/providers/AlertProvider.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, {
  createContext,
  FC,
  useContext,
  useEffect,
  useState,
  useCallback,
} from 'react'
import { Alert } from 'vtex.styleguide'

interface AlertContextInterface {
  openAlert: (status: 'success' | 'error', entry: string) => void
}

const AlertContext = createContext<AlertContextInterface>(
  {} as AlertContextInterface
)

const AlertProvider: FC = ({ children }) => {
  const [entryType, setEntryTipe] = useState('')
  const [open, setOpen] = useState<'success' | 'error' | ''>('')

  const handleClose = useCallback(() => {
    if (open) {
      setOpen('')
    }
  }, [open])

  const openAlert = (status: 'success' | 'error', entry: string) => {
    setEntryTipe(entry)
    setOpen(status)
  }

  useEffect(() => {
    const timeout = setTimeout(() => {
      handleClose()
    }, 5000)

    return () => {
      clearTimeout(timeout)
    }
  }, [handleClose, open])

  return (
    <AlertContext.Provider value={{ openAlert }}>
      {open ? (
        <div className="w-100 fixed z-max overflow-hidden">
          <div
            className="mt7"
            style={{ maxWidth: '520px', margin: '2rem auto' }}
          >
            {open === 'success' && (
              <Alert type="success" onClose={handleClose}>
                {`The ${entryType} was translated successfully!`}
              </Alert>
            )}
            {open === 'error' && (
              <Alert type="error" onClose={handleClose}>
                {`There was an error translating ${entryType}. Try again.`}
              </Alert>
            )}
          </div>
        </div>
      ) : null}
      {children}
    </AlertContext.Provider>
  )
}

const useAlert = () => useContext(AlertContext)

export { AlertProvider, useAlert }

```


[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 admin-catalog-translations/react/providers/index.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

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

import { BindingProvider } from '../components/LocaleSelector'
import { AlertProvider } from './AlertProvider'

const ProviderWrapper: FC = ({ children }) => (
  <AlertProvider>
    <BindingProvider>{children}</BindingProvider>
  </AlertProvider>
)

export default ProviderWrapper

```


[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 admin-catalog-translations/react/typings/bindings.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface Binding {
  id: string
  defaultLocale: string
  supportedLocales: string[]
  targetProduct?: string
}

interface BindingsData {
  tenantInfo: {
    defaultLocale: string
    bindings: Binding[]
  }
}

```


[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 admin-catalog-translations/react/typings/graphlql.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

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

  const value: DocumentNode
  export default value
}

declare module 'graphql' {
  export { DocumentNode } from 'graphql'
}

```


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

[User] Show me the implementation of admin-catalog-translations/react/typings/import.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface ImportEntriesSettings {
  bucket: UploadBucket
  entryHeaders: EntryHeaders[]
  entryQueryFile: DocumentNode
  entryQueryName: string
  entryName: string
  fileName: string
  paramEntryName: string
  sheetHeaders?: string[]
  sheetName: string
  uploadMutationFile: DocumentNode
  uploadMutationName: string
}

interface ImportEntries extends ComponentProps {
  settings: ImportEntriesSettings
}

```


[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 admin-catalog-translations/react/typings/utils.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface Messages {
  errors: Message[]
  warnings: Message[]
}

interface Message {
  line: number
  missingFields: []
}

type EntryHeadersBrand = Extract<keyof Brand, string>
type EntryHeadersCategory = Extract<keyof Category, string>
type EntryHeadersProduct = Extract<keyof Product, string>
type EntryHeadersSpecifications = Extract<keyof Field, string>

type EntryHeaders =
  | EntryHeadersBrand
  | EntryHeadersCategory
  | EntryHeadersProduct
  | EntryHeadersSpecifications
  | 'locale'

type UploadBucket =
  | 'brand_translation'
  | 'product_translation'
  | 'collection_transl'
  | 'field_translation'
  | 'category_transl'
  | 'sku_translation'

type DownloadBucket =
  | 'product_translation'
  | 'field_translation'
  | 'sku_translation'

type typeItem = 'product' | 'sku' | 'field'

```


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

[Assistant]
Here's the implementation:

```typescript
type HandleOpen = (open: boolean) => void

interface ComponentProps {
  isExportOpen?: boolean
  handleOpenExport?: HandleOpen
  isImportOpen?: boolean
  handleOpenImport?: HandleOpen
}

```


[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 admin-catalog-translations/react/typings/catalog.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface CategoriesData {
  category: Category
}

interface Category extends CategoryInputTranslation {
  id: string
}

interface CategoryInputTranslation {
  description: string
  linkId: string
  name: string
  title: string
}
interface SKUData {
  sku: SKU
}
interface SKU extends SKUInputTranslation {
  id: string
}
interface SKUInputTranslation {
  name: string
}
interface BrandData {
  brand: Brand
}
interface Brand extends BrandInputTranslation {
  id: string
}
interface BrandInputTranslation {
  name: string
  text: string
  siteTitle: string
  active: boolean
}

interface ProductData {
  product: Product
}

interface Product extends ProductInputTranslation {
  id: string
}

interface ProductInputTranslation {
  name: string
  description: string
  shortDescription: string
  metaTagDescription: string
  title: string
  linkId: string
}

interface FieldsData {
  field: Field
}
interface Field extends FieldInputTranslation {
  fieldId: string
}
interface FieldInputTranslation {
  name: string
}
interface SpecificationValuesTranslation {
  fieldId: string
  fieldValuesNames: SpecificationValuesUnit[]
}

interface SpecificationValuesUnit {
  id: string
  name: string
}
interface CategoriesNameAndId {
  getCategoriesName: Array<{ id: string; name: string }>
}

interface TranslationRequestByCategoryId {
  requestId: string
  requestedBy: string
  categoryId: string
  error: boolean
  createdAt: string
  locale: string
  completedAt: string
  estimatedTime: number
}

interface TranslationRequest {
  requestId: string
  requestedBy: string
  categoryId?: string
  error: boolean
  createdAt: string
  locale: string
  completedAt: string
  estimatedTime: number
}

interface ProductTranslationRequest {
  productTranslations: TranslationRequestByCategoryId
}

interface ProdTranslationRequests {
  productTranslationRequests: string[]
}

interface TransInfoReq {
  [key: string]: TranslationRequest
}

interface ProductTranslationDownload {
  downloadProductTranslation: Product[]
}

interface Collection extends CollectionInputTranslation {
  id: string
}

interface CollectionInputTranslation {
  name: string
}

interface CollectionsData {
  collection: Collections
}
interface Collections extends CollectionsName {
  id: string
}
interface CollectionsName {
  name: string
}
interface SkuTranslationRequest {
  skuTranslations: TranslationRequestByCategoryId
}

interface SkuTranslationRequests {
  skuTranslationRequests: string[]
}

interface SkuTranslationDownload {
  downloadSKUTranslation: SKU[]
}

interface ProductTranslationInput {
  id: string
  name?: string
  description?: string
  shortDescription?: string
  title?: string
  linkId?: string
}

interface UploadRequest {
  requestId: string
  translatedBy: string
  createdAt: string
  estimatedTime: number
  locale: string
  error?: boolean
  progress?: number
}

interface TranslationDownload<T> {
  [key: string]: T[]
}

```


[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 admin-catalog-translations/react/utils/fileParsers.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import XLSX from 'xlsx'

/**
 * Parse json to XLS and prompt a download window for user
 *
 * @param {object} data JSON to be parsed to xls
 * @param {object} options
 * @param {options.fileName} string
 * @param {options.sheetName} string
 */

export function parseJSONToXLS(
  data: unknown[],
  { fileName, sheetName }: { fileName: string; sheetName: string }
) {
  const workSheet = XLSX.utils.json_to_sheet(data)
  const workBook = XLSX.utils.book_new()
  XLSX.utils.book_append_sheet(workBook, workSheet, sheetName)
  const exportFileName = `${fileName}.xlsx`
  XLSX.writeFile(workBook, exportFileName)
}

export function parseXLSToJSON(
  file: Blob,
  { sheetName }: { sheetName: string }
): Promise<Array<{}>> {
  const promise = new Promise<Array<{}>>((resolve, reject) => {
    const fileReader = new FileReader()
    fileReader.onload = (e: ProgressEvent<FileReader>) => {
      const data = e.target?.result
      const workbook = XLSX.read(data, { type: 'binary' })
      const [fileSheetName] = workbook.SheetNames

      if (fileSheetName !== sheetName) {
        reject(`Sheet name - ${fileSheetName} - doesn't match ${sheetName}`)
        return
      }

      const json = XLSX.utils.sheet_to_json<{}>(workbook.Sheets[sheetName])
      resolve(json)
    }
    fileReader.onerror = () => {
      reject('Error parsing file')
    }
    fileReader.readAsBinaryString(file)
  })
  return promise
}

```


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

[Assistant]
Here's the implementation:

```typescript
import { Translation } from 'vtex.messages'

import { parseJSONToXLS } from './fileParsers'

export * from './fileParsers'
export * from './sanitizeImportJSON'

export const UPLOAD_LIST_SIZE = 10
export const DOWNLOAD_LIST_SIZE = 6

/**
 * Keep the xVtexTenant in the top of the dropdown button
 */
const sortBindings = (bindings: Binding[], xVtexTenant: string): Binding[] => {
  const xVtexTenantBinding = []
  const otherBindings = []

  for (const binding of bindings) {
    if (binding.defaultLocale === xVtexTenant) {
      xVtexTenantBinding.push(binding)
    } else {
      otherBindings.push(binding)
    }
  }

  return [...xVtexTenantBinding, ...otherBindings]
}

/**
 * Returns all the unique binding locales
 *
 * @param {array} Array received from api graphql
 * @return {array} Array with only unique locales and without the admin binding.
 */

export const filterLocales = (
  bindings: Binding[],
  xVtexTenant: string
): Binding[] => {
  const storeFrontBindings = bindings.filter(
    (binding) => binding.targetProduct === 'vtex-storefront'
  )
  const uniqueBindings: { [id: string]: boolean } = {}
  const filteredBindings: Binding[] = []
  const supportedLocales: string[] = []

  for (const binding of storeFrontBindings) {
    if (!uniqueBindings[binding.id]) {
      filteredBindings.push(binding)
      supportedLocales.push(...binding.supportedLocales)
      uniqueBindings[binding.defaultLocale] = true
    }
  }

  // to show also the supported locales in the app, we transform them into a binding type, with an empty supported locales array
  for (const supportedLocale of supportedLocales) {
    if (!uniqueBindings[supportedLocale]) {
      filteredBindings.push({
        id: supportedLocale,
        defaultLocale: supportedLocale,
        supportedLocales: [],
      })
      uniqueBindings[supportedLocale] = true
    }
  }

  // in case the xVtexTenant is not in the bindings, we add it
  if (!uniqueBindings[xVtexTenant]) {
    filteredBindings.push({
      id: xVtexTenant,
      defaultLocale: xVtexTenant,
      supportedLocales: [],
    })
    uniqueBindings[xVtexTenant] = true
  }

  return sortBindings(filteredBindings, xVtexTenant)
}

/**
 * Checks if an object from a form has any changes from its original one
 *
 * @param {object} formValues Object that hold values from the form
 * @param {object} originalValues Object with the original values from the parent component
 * @return {boolean} true if values have changed. false if not.
 */

export function hasChanges<S>(formValues: S, originalValues: S): boolean {
  const keys = Object.keys(formValues)
  for (const key of keys) {
    if (formValues[key as keyof S] !== originalValues[key as keyof S]) {
      return true
    }
  }
  return false
}

interface DropDownProps {
  label: string
  value: string
}

export const convertToDropDownOptions = (
  bindings: Binding[]
): DropDownProps[] => {
  const formattedOptions: DropDownProps[] = []

  for (const binding of bindings) {
    formattedOptions.push({
      label: binding.defaultLocale,
      value: binding.defaultLocale,
    })
  }

  return formattedOptions
}

interface FilterSearchCategoriesArgs {
  categoryList: Array<{ id: string; name: string }>
  term: string
}

export const filterSearchCategories = ({
  categoryList,
  term,
}: FilterSearchCategoriesArgs): Array<{ label: string; value: string }> => {
  return (
    categoryList
      .map(({ id, name }) => ({ label: `${id} - ${name}`, value: id }))
      .filter(({ label }) =>
        label.toLowerCase().includes(term.toLowerCase())
      ) ?? []
  )
}

const ESTIMATED_MARGIN = 5

export const remainingTime = (date: string, estimatedTime: number): number => {
  const remaining =
    estimatedTime * ESTIMATED_MARGIN -
    (new Date().valueOf() - new Date(date).valueOf())
  return remaining > 0 ? remaining : 0
}

export const shouldHaveCompleted = (
  date: string,
  estimatedTime: number
): boolean => {
  return (
    estimatedTime * ESTIMATED_MARGIN <
    new Date().valueOf() - new Date(date).valueOf()
  )
}

interface FormatCollectionFromMessagesArgs {
  translations: Translation[]
  context: string
  srcLang: string
  originalMessage: string
}

export const formatCollectionFromMessages = ({
  translations,
  context,
  srcLang,
  originalMessage,
}: FormatCollectionFromMessagesArgs): Record<string, Collections> => {
  if (!translations.length) {
    return {
      [srcLang]: { name: originalMessage, id: context },
    }
  }

  return translations.reduce((col, translation) => {
    col[translation.lang] = { name: translation.translation, id: context }
    return col
  }, {} as Record<string, Collections>)
}

export const getValueByKey = (
  obj: { [k: string]: string },
  keyName: string
) => {
  let value = ''
  Object.keys(obj).forEach((key) => {
    if (key === keyName) {
      value = obj[key]
    }
  })

  return value
}

export const createModel = <T>(
  headers: Array<keyof T>,
  sheetName: string,
  type: string
) => {
  const headersObject = headers.reduce<Record<typeof headers[number], string>>(
    (obj, header) => {
      obj[header] = ''
      return obj
    },
    {} as Record<typeof headers[number], string>
  )

  parseJSONToXLS([headersObject], {
    fileName: `${type}_translate_model`,
    sheetName,
  })
}

```


[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 admin-catalog-translations/react/utils/sanitizeImportJSON.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
export function sanitizeImportJSON({
  data,
  entryHeaders,
  requiredHeaders,
}: {
  data: Array<{}>
  entryHeaders: EntryHeaders[]
  requiredHeaders: string[]
}): [Array<Record<EntryHeaders, string>>, Messages] {
  const warningList = []
  const errorList = []
  const entryList: Array<Record<EntryHeaders, string>> = []

  for (let i = 0; i < data.length; i++) {
    const entry = data[i]
    const { warnings, errors } = validateEntry(entry, {
      entryHeaders,
      requiredHeaders,
    })

    if (errors.length) {
      errorList.push({
        line: i + 2,
        missingFields: errors,
      } as Message)
    } else {
      const entrySanitize = createEntry(entry, entryHeaders)
      entryList.push(entrySanitize)

      if (warnings.length) {
        warningList.push({
          line: i + 2,
          missingFields: warnings,
        } as Message)
      }
    }
  }

  return [entryList, { warnings: warningList, errors: errorList }]
}

function validateEntry(
  entry: {},
  {
    entryHeaders,
    requiredHeaders,
  }: {
    entryHeaders: EntryHeaders[]
    requiredHeaders: string[]
  }
) {
  const warnings: string[] = []
  const errors: string[] = []

  const optionalFields = entryHeaders.filter(
    (header) => !requiredHeaders.includes(header as string)
  )

  const entryFields = Object.keys(entry)
  requiredHeaders.forEach((field) => {
    if (entryFields.indexOf(field) === -1) {
      errors.push(field)
    }
  })

  optionalFields.forEach((field) => {
    if (entryFields.indexOf(field) === -1) {
      warnings.push(field)
    }
  })

  return { warnings, errors }
}

function createEntry(
  entry: Record<string, string>,
  entryHeaders: EntryHeaders[]
) {
  const entrySanitized = {} as Record<EntryHeaders, string>
  entryHeaders.forEach((header) => {
    if (entry[header]) {
      entrySanitized[header] = entry[header]
    }
  })

  return entrySanitized
}

```


[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 admin-catalog-translations/react/components/ImportButton.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { ButtonWithIcon, IconUpload } from 'vtex.styleguide'

import { useLocaleSelector } from './LocaleSelector'

interface Props {
  openImport: HandleOpen
}

const ImportButton = ({ openImport }: Props) => {
  const { isXVtexTenant } = useLocaleSelector()

  return isXVtexTenant ? null : (
    <div className="ml7">
      <ButtonWithIcon
        name="import-catalog"
        type="button"
        icon={<IconUpload />}
        variation="primary"
        onClick={() => openImport(true)}
      >
        Import
      </ButtonWithIcon>
    </div>
  )
}

export default ImportButton

```


[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 admin-catalog-translations/react/components/CatalogTranslationWrapper.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, ReactElement, useState } from 'react'
import { Layout, PageHeader } from 'vtex.styleguide'

import ProviderWrapper from '../providers'
import ExportButton from './ExportButton'
import ImportButton from './ImportButton'
import LocaleSelector from './LocaleSelector'

interface CatalogTranslationWrapperProps {
  titleComponent: ReactElement
  hasExport?: boolean
  hasImport?: boolean
}

const CatalogTranslationWrapper: FC<CatalogTranslationWrapperProps> = ({
  titleComponent,
  hasExport,
  children,
  hasImport,
}) => {
  const [isExportOpen, setIsExportOpen] = useState(false)
  const [isImportOpen, setIsImportOpen] = useState(false)

  const handleOpenExport = (open: boolean) => {
    setIsExportOpen(open)
  }

  const handleOpenImport = (open: boolean) => {
    setIsImportOpen(open)
  }

  return (
    <ProviderWrapper>
      <Layout pageHeader={<PageHeader title={titleComponent} />}>
        <div className="flex items-end">
          <LocaleSelector />
          {hasExport ? <ExportButton openExport={handleOpenExport} /> : null}
          {hasImport ? <ImportButton openImport={handleOpenImport} /> : null}
        </div>

        {React.Children.map(children, (child) =>
          React.isValidElement(child)
            ? React.cloneElement(child, {
                isExportOpen,
                handleOpenExport,
                isImportOpen,
                handleOpenImport,
              })
            : null
        )}
      </Layout>
    </ProviderWrapper>
  )
}

export default CatalogTranslationWrapper

```


[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 admin-catalog-translations/react/components/ImportEntriesModal.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState } from 'react'
import { useMutation, useQuery } from 'react-apollo'
import { ModalDialog, ButtonPlain, Dropzone, Tabs, Tab } from 'vtex.styleguide'
import { FormattedMessage } from 'react-intl'

import {
  sanitizeImportJSON,
  parseXLSToJSON,
  parseJSONToXLS,
  getValueByKey,
} from '../utils'
import WarningAndErrorsImportModal from './WarningAndErrorsImportModal'
import { useLocaleSelector } from './LocaleSelector'
import ImportStatusList from './ImportStatusList'

const UPLOAD_LIST_SIZE = 10

const ImportEntriesModal = ({
  isImportOpen = false,
  handleOpenImport = () => {},
  settings,
}: ImportEntries) => {
  const {
    bucket,
    entryHeaders,
    entryQueryFile,
    entryQueryName,
    entryName,
    fileName,
    paramEntryName,
    sheetHeaders: requiredHeaders = ['id'],
    sheetName,
    uploadMutationFile,
    uploadMutationName,
  } = settings

  const [errorParsingFile, setErrorParsingFile] = useState('')
  const [loading, setLoading] = useState(false)
  const [errorModal, setErrorModal] = useState(false)
  const [warningModal, setWarningModal] = useState(false)
  const [validationErrors, setValidationErrors] = useState<Message[]>([])
  const [validationWarnings, setValidationWarnings] = useState<Message[]>([])
  const [originalFile, setOriginalFile] = useState<Array<{}>>([])
  const [formattedTranslations, setFormattedTranslations] = useState<
    Blob | undefined
  >(undefined)

  const [tabSelected, setTabSelected] = useState<1 | 2>(1)
  const { selectedLocale } = useLocaleSelector()

  const handleFile = async (files: FileList) => {
    if (loading) {
      return
    }

    setLoading(true)

    try {
      const fileParsed = await parseXLSToJSON(files[0], {
        sheetName,
      })

      setOriginalFile(fileParsed)

      const [translations, { errors, warnings }] = sanitizeImportJSON({
        data: fileParsed,
        entryHeaders,
        requiredHeaders,
      })

      if (errors.length) {
        setValidationErrors(errors)
      }

      if (warnings.length) {
        setValidationWarnings(warnings)
      }

      const blob = new Blob([JSON.stringify(translations, null, 2)], {
        type: 'application/json',
      })

      setFormattedTranslations(blob)
    } catch (e) {
      // The errors being handled on parseXLSToJSON are thrown as strings.
      if (typeof e === 'string') {
        setErrorParsingFile(e)
      } else if (e instanceof Error) {
        setErrorParsingFile(e.message)
      } else {
        setErrorParsingFile('An unknown error occurred')
      }
    } finally {
      setLoading(false)
    }
  }

  const cleanErrors = () => {
    setValidationErrors([])
    setErrorParsingFile('')
    setValidationWarnings([])
    setOriginalFile([])
  }

  const handleReset = () => {
    if (loading) {
      return
    }
    setOriginalFile([])
    setFormattedTranslations(undefined)
    cleanErrors()
  }

  const createModel = () => {
    const headersObject = entryHeaders.reduce<
      Record<typeof entryHeaders[number], string>
    >((obj, header) => {
      obj[header] = ''
      return obj
    }, {} as Record<typeof entryHeaders[number], string>)

    parseJSONToXLS([headersObject], {
      fileName,
      sheetName,
    })
  }

  const [startEntriesUpload, { error: uploadError }] = useMutation<
    {
      [key: string]: string
    },
    {
      [key: string]: string | Blob
    }
  >(uploadMutationFile)

  const { data, updateQuery } = useQuery<{
    [key: string]: string[]
  }>(entryQueryFile)

  const handleUploadRequest = async () => {
    if (!formattedTranslations) {
      return
    }

    const { data: newRequest } = await startEntriesUpload({
      variables: {
        locale: selectedLocale,
        [paramEntryName]: formattedTranslations,
      },
    })

    const request = newRequest
      ? getValueByKey(newRequest, uploadMutationName)
      : null

    // eslint-disable-next-line vtex/prefer-early-return
    if (request) {
      updateQuery((prevResult) => {

        return {
          [entryQueryName]: [request, ...(prevResult[entryQueryName] ?? [])],
        }
      })
      setTabSelected(2)
    }
  }

  return (
    <ModalDialog
      loading={loading}
      cancelation={{
        label: (
          <FormattedMessage id="admin/catalog-translation.import.modal.cancelation" />
        ),
        onClick: () => {
          handleOpenImport(false)
          cleanErrors()
        },
      }}
      confirmation={{
        label: (
          <FormattedMessage id="admin/catalog-translation.import.modal.confirmation" />
        ),
        onClick: () => {
          if (errorParsingFile) {
            return
          }
          handleUploadRequest()
        },
      }}
      isOpen={isImportOpen}
      onClose={() => {
        handleOpenImport(false)
        cleanErrors()
      }}
    >
      <h3>
        <FormattedMessage
          id="admin/catalog-translation.import.modal.entry-name-header"
          values={{
            selectedLocale,
            entryName,
          }}
        />
      </h3>
      <div>
        <Tabs>
          <Tab
            label={
              <FormattedMessage id="admin/catalog-translation.import.modal.import-tab" />
            }
            active={tabSelected === 1}
            onClick={() => {
              setTabSelected(1)
              handleReset()
            }}
          >
            <div>
              <div className="mv4">
                <ButtonPlain onClick={createModel}>
                  <FormattedMessage id="admin/catalog-translation.import.modal.download-button" />
                </ButtonPlain>
              </div>
              <div>
                <Dropzone
                  accept=".xlsx"
                  onDropAccepted={handleFile}
                  onFileReset={handleReset}
                >
                  <div className="pt7">
                    <span className="f4">
                      <FormattedMessage id="admin/catalog-translation.import.modal.dropzone" />
                    </span>
                    <span className="f4 c-link" style={{ cursor: 'pointer' }}>
                      <FormattedMessage id="admin/catalog-translation.import.modal.dropzone-choose-file" />
                    </span>
                  </div>
                </Dropzone>
              </div>
              {errorParsingFile ? (
                <p className="c-danger i f7">{errorParsingFile}</p>
              ) : null}
            </div>
            <ul>
              {originalFile.length ? (
                <li>
                  {originalFile?.length}{' '}
                  <FormattedMessage id="admin/catalog-translation.import.modal.total-entries" />
                </li>
              ) : null}
              {validationWarnings.length ? (
                <li>
                  <ButtonPlain onClick={() => setWarningModal(true)}>
                    {validationWarnings.length}{' '}
                    <FormattedMessage id="admin/catalog-translation.import.modal.total-warnings" />
                  </ButtonPlain>
                </li>
              ) : null}
              {validationErrors.length ? (
                <li>
                  <ButtonPlain
                    variation="danger"
                    onClick={() => setErrorModal(true)}
                  >
                    {validationErrors.length}{' '}
                    <FormattedMessage id="admin/catalog-translation.import.modal.total-errors" />
                  </ButtonPlain>
                </li>
              ) : null}
            </ul>
            {uploadError ? (
              <p className="absolute c-danger i-s bottom-0-m right-0-m mr8">
                <FormattedMessage id="admin/catalog-translation.import.modal.error-uploading" />
              </p>
            ) : null}
          </Tab>
          <Tab
            label={
              <FormattedMessage id="admin/catalog-translation.import.modal.request-tab" />
            }
            active={tabSelected === 2}
            onClick={() => {
              setTabSelected(2)
              handleReset()
            }}
          >
            <table className="w-100 mt7 tc">
              <thead>
                <tr>
                  <th>
                    <FormattedMessage id="admin/catalog-translation.import.modal.table-header.locale" />
                  </th>
                  <th>
                    <FormattedMessage id="admin/catalog-translation.import.modal.table-header.translated-by" />
                  </th>
                  <th>
                    <FormattedMessage id="admin/catalog-translation.import.modal.table-header.created-at" />
                  </th>
                  <th>
                    <FormattedMessage id="admin/catalog-translation.import.modal.table-header.progress" />
                  </th>
                  <th>
                    <FormattedMessage id="admin/catalog-translation.import.modal.table-header.total-translated" />
                  </th>
                </tr>
              </thead>
              <tbody>
                {data?.[entryQueryName]
                  ?.slice(0, UPLOAD_LIST_SIZE)
                  .map((requestId) => (
                    <ImportStatusList
                      requestId={requestId}
                      key={requestId}
                      bucket={bucket}
                    />
                  ))}
              </tbody>
            </table>
          </Tab>
        </Tabs>
      </div>
      <WarningAndErrorsImportModal
        isOpen={warningModal}
        modalName="Warning Modal"
        handleClose={setWarningModal}
        data={validationWarnings}
      />
      <WarningAndErrorsImportModal
        isOpen={errorModal}
        modalName="Error Modal"
        handleClose={setErrorModal}
        data={validationErrors}
      />
    </ModalDialog>
  )
}

export default ImportEntriesModal

```


[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 admin-catalog-translations/react/components/ActionButtons.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { Button } from 'vtex.styleguide'
import { FormattedMessage } from 'react-intl'

interface ActionButtonsProps {
  toggleEdit: () => void
  canEdit: boolean
  changed: boolean
  loading: boolean
}

const ActionButtons: FC<ActionButtonsProps> = ({
  toggleEdit,
  canEdit,
  changed,
  loading,
}) => (
  <div>
    <span className="mr5">
      <Button type="button" variation="secondary" onClick={toggleEdit}>
        {canEdit ? (
          <FormattedMessage id="admin/catalog-translation.action-buttons.cancel" />
        ) : (
          <FormattedMessage id="admin/catalog-translation.action-buttons.edit" />
        )}
      </Button>
    </span>
    <span>
      <Button
        type="submit"
        variation="primary"
        disabled={!changed}
        isLoading={loading}
      >
        <FormattedMessage id="admin/catalog-translation.action-buttons.save" />
      </Button>
    </span>
  </div>
)

export default ActionButtons

```


[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 admin-catalog-translations/react/components/ExportButton.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { ButtonWithIcon, IconDownload } from 'vtex.styleguide'

import { useLocaleSelector } from './LocaleSelector'

interface Props {
  openExport: HandleOpen
}

const ExportButton = ({ openExport }: Props) => {
  const { isXVtexTenant } = useLocaleSelector()

  return isXVtexTenant ? null : (
    <div className="ml7">
      <ButtonWithIcon
        name="export-catalog"
        type="button"
        icon={<IconDownload />}
        variation="primary"
        onClick={() => openExport(true)}
      >
        Export
      </ButtonWithIcon>
    </div>
  )
}

export default ExportButton

```


[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 admin-catalog-translations/react/components/LocaleSelector.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, {
  createContext,
  FC,
  useContext,
  useEffect,
  useState,
} from 'react'
import { useQuery } from 'react-apollo'
import { Spinner, Dropdown } from 'vtex.styleguide'

import accountLocalesQuery from '../graphql/accountLocales.gql'
import { convertToDropDownOptions, filterLocales } from '../utils'

interface DropDownProps {
  label: string
  value: string
}
interface BindingContextInterface {
  xVtexTenant: string
  isXVtexTenant: boolean
  selectedLocale: string
  dropDownOptions: DropDownProps[]
  loading: boolean
  handleLocaleSelection: (localeSelected: string) => void
}

const BindingContext = createContext<BindingContextInterface>(
  {} as BindingContextInterface
)

const BindingProvider: FC = ({ children }) => {
  const [dropDownOptions, setDropDownOptions] = useState<DropDownProps[]>([])
  const [selectedLocale, setSelectedLocale] = useState<string>('')
  const [xVtexTenant, setXVtexTenant] = useState('')

  const { data: bindingData, loading } = useQuery<BindingsData>(
    accountLocalesQuery
  )

  useEffect(() => {
    // eslint-disable-next-line vtex/prefer-early-return
    if (bindingData) {
      const { defaultLocale, bindings: fetchedLocales } = bindingData.tenantInfo
      const filteredLocales = filterLocales(fetchedLocales, defaultLocale)
      setDropDownOptions(convertToDropDownOptions(filteredLocales))
      setSelectedLocale(defaultLocale)
      setXVtexTenant(defaultLocale)
    }
  }, [bindingData])

  const handleLocaleSelection = (localeSelected: string) => {
    setSelectedLocale(localeSelected)
  }

  const isXVtexTenant = selectedLocale === xVtexTenant
  return (
    <BindingContext.Provider
      value={{
        xVtexTenant,
        selectedLocale,
        isXVtexTenant,
        dropDownOptions,
        loading,
        handleLocaleSelection,
      }}
    >
      {children}
    </BindingContext.Provider>
  )
}

const LocaleSelector: FC = () => {
  const {
    dropDownOptions = [],
    selectedLocale,
    handleLocaleSelection,
    loading,
  } = useContext(BindingContext)

  return loading ? (
    <Spinner />
  ) : (
    <div className="w-100 w4-ns">
      <Dropdown
        label="Available Language"
        placeholder="Select a language"
        value={selectedLocale}
        options={dropDownOptions}
        onChange={(_: unknown, value: string) => handleLocaleSelection(value)}
      />
    </div>
  )
}

const useLocaleSelector = () => useContext(BindingContext)

export { BindingProvider, useLocaleSelector }

export default LocaleSelector

```


[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 admin-catalog-translations/react/components/WarningAndErrorsImportModal.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, {
  Dispatch,
  SetStateAction,
  useEffect,
  useMemo,
  useState,
} from 'react'
import { FormattedMessage, IntlShape, useIntl } from 'react-intl'
import { Modal, Pagination, Table } from 'vtex.styleguide'

const tableSchemaGenerator = (intl: IntlShape) => ({
  properties: {
    line: {
      title: intl.formatMessage({
        id: 'admin/catalog-translation.import.table-header.line',
      }),
    },
    missingFields: {
      title: intl.formatMessage({
        id: 'admin/catalog-translation.import.table-header.missing-fields',
      }),
      // eslint-disable-next-line react/display-name
      cellRenderer: ({ cellData }: { cellData: string[] }) => (
        <p>{cellData.join(', ')}</p>
      ),
    },
  },
})

interface ComponentProps {
  modalName: string
  isOpen: boolean
  handleClose: Dispatch<SetStateAction<boolean>>
  data: Message[]
}

const ITEM_FROM = 0
const STEPS = 10

const WarningAndErrorsImportModal = ({
  modalName,
  isOpen,
  handleClose,
  data,
}: ComponentProps) => {
  const [slicedData, setSlicedData] = useState<Message[]>([])
  const [from, setFrom] = useState(ITEM_FROM)
  const [to, setTo] = useState(ITEM_FROM + STEPS)
  const intl = useIntl()

  useEffect(() => {
    setSlicedData(data.slice(ITEM_FROM, ITEM_FROM + STEPS))
  }, [data])

  const handleNext = () => {
    const nextFrom = from + STEPS
    const nextNext = nextFrom + STEPS

    setFrom(nextFrom)
    setTo(nextNext)
    const selectedData = data.slice(nextFrom, nextNext)
    setSlicedData(selectedData)
  }

  const handlePrev = () => {
    const nextFrom = from - STEPS
    const nextNext = nextFrom + STEPS

    setFrom(nextFrom)
    setTo(nextNext)
    const selectedData = data.slice(nextFrom, nextNext)
    setSlicedData(selectedData)
  }

  const tableSchema = useMemo(() => tableSchemaGenerator(intl), [intl])

  return (
    <Modal isOpen={isOpen} onClose={() => handleClose(false)}>
      <Pagination
        currentItemFrom={from + 1}
        currentItemTo={to}
        textOf={
          <FormattedMessage id="admin/catalog-translation.import.warning-and-error" />
        }
        totalItems={data.length}
        onNextClick={handleNext}
        onPrevClick={handlePrev}
      >
        <h4>{modalName}</h4>
        <Table
          fullWidth
          density="high"
          items={slicedData}
          schema={tableSchema}
        />
      </Pagination>
    </Modal>
  )
}

export default WarningAndErrorsImportModal

```


[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 admin-catalog-translations/react/components/ExportListItem.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect, useRef, useState } from 'react'
import { useQuery } from 'react-apollo'
import { FormattedDate, FormattedTime, FormattedMessage } from 'react-intl'
import { Progress, ButtonPlain } from 'vtex.styleguide'
import { ApolloError } from 'apollo-client'

import TRANSLATION_REQUEST_INFO from '../graphql/translationRequestInfo.gql'
import { shouldHaveCompleted, remainingTime, parseJSONToXLS } from '../utils'

const getBucket = (type: typeItem): DownloadBucket => {
  switch (type) {
    case 'product': {
      return 'product_translation'
    }
    case 'field': {
      return 'field_translation'
    }
    case 'sku': {
      return 'sku_translation'
    }
    default: {
      throw new Error('Invalid type')
    }
  }
}

const getFileParams = (
  type: typeItem,
  categoryId?: string,
  locale?: string
) => {
  const name = type === 'field' ? 'specification' : type

  const fileName = categoryId
    ? `category-${categoryId}-${name}-data-${locale}`
    : `${name}-data-${locale}`

  const sheetName = `${name}_data`
  return { fileName, sheetName }
}
interface Options {
  variables: {
    requestId: string
  }
}

interface Props {
  requestId: string
  download: (options: Options) => void
  downloadJson: any
  downloadError?: ApolloError
  type: typeItem
}

const ExportListItem = ({
  requestId,
  download,
  downloadJson,
  downloadError,
  type,
}: Props) => {
  const [longTimeAgo, setLongTimeAgo] = useState(false)
  const [downloading, setDownloading] = useState(false)
  const [errorDownloading, setErrorDownloading] = useState(false)
  const {
    data,
    error: errorFetching,
    startPolling,
    stopPolling,
    refetch,
  } = useQuery<TransInfoReq, { requestId: string; bucket: DownloadBucket }>(
    TRANSLATION_REQUEST_INFO,
    {
      variables: {
        requestId,
        bucket: getBucket(type),
      },
    }
  )

  const {
    categoryId,
    locale,
    requestedBy,
    createdAt,
    completedAt,
    error,
    estimatedTime,
  } = data?.translationRequestInfo ?? {}

  const tooLongRef = useRef<any>()

  useEffect(() => {
    if (!completedAt) {
      refetch({ requestId, bucket: getBucket(type) })
    }
  }, [completedAt, refetch, requestId, type])

  useEffect(() => {
    if (!completedAt && !error && createdAt && estimatedTime) {
      if (shouldHaveCompleted(createdAt, estimatedTime)) {
        stopPolling()
        setLongTimeAgo(true)
        return
      }
      startPolling(estimatedTime)
      clearTimeout(tooLongRef.current)
      tooLongRef.current = setTimeout(() => {
        setLongTimeAgo(true)
        stopPolling()
      }, remainingTime(createdAt, estimatedTime))
    }
    if (completedAt || error) {
      stopPolling()
      clearTimeout(tooLongRef.current)
    }

    return () => stopPolling()
  }, [completedAt, createdAt, error, startPolling, stopPolling, estimatedTime])

  useEffect(() => {
    // eslint-disable-next-line vtex/prefer-early-return
    if (downloadJson && downloading) {
      const { fileName, sheetName } = getFileParams(type, categoryId, locale)
      parseJSONToXLS(downloadJson, {
        fileName,
        sheetName,
      })
      setDownloading(false)
    }
  }, [categoryId, downloadJson, downloading, locale, type])

  useEffect(() => {
    // eslint-disable-next-line vtex/prefer-early-return
    if (downloadError) {
      setDownloading(false)
      setErrorDownloading(true)
    }
  }, [downloadError])

  return !data || errorFetching ? null : (
    <tr>
      {!!categoryId && (
        <td>
          <p>{categoryId}</p>
        </td>
      )}
      <td>
        <p>{locale}</p>
      </td>
      <td>
        <p>{requestedBy}</p>
      </td>
      <td>
        {createdAt ? (
          <span>
            <p>
              <FormattedTime value={createdAt} />
              {' - '}
              <FormattedDate value={createdAt} />
            </p>
          </span>
        ) : null}
      </td>
      <td>
        {error || longTimeAgo || errorDownloading ? (
          <p className="c-danger i f7">
            <FormattedMessage id="admin/catalog-translation.export.modal.download-list.error" />
          </p>
        ) : completedAt ? (
          <ButtonPlain
            name="download-file"
            type="button"
            onClick={() => {
              download({
                variables: { requestId },
              })
              setDownloading(true)
            }}
          >
            <FormattedMessage id="admin/catalog-translation.export.modal.download-list.download-btn" />
          </ButtonPlain>
        ) : (
          <Progress type="steps" steps={['inProgress']} />
        )}
      </td>
    </tr>
  )
}

export default ExportListItem

```


[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 admin-catalog-translations/react/components/ExportByCatIdModal.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { FormattedMessage, useIntl, defineMessages } from 'react-intl'
import { useQuery } from 'react-apollo'
import { ApolloError } from 'apollo-client'
import {
  ModalDialog,
  Alert,
  AutocompleteInput,
  Spinner,
  Tabs,
  Tab,
} from 'vtex.styleguide'

import GET_CATEGORIES_NAME from '../graphql/getCategoriesName.gql'
import { DOWNLOAD_LIST_SIZE, filterSearchCategories } from '../utils'
import { useLocaleSelector } from './LocaleSelector'
import ExportListItem from './ExportListItem'

const AUTOCOMPLETE_LIST_SIZE = 6

interface AutocompleteValue {
  label: string
  value: string
}

interface Options {
  variables: {
    requestId: string
  }
}

type typeItem = 'product' | 'sku'

interface Props {
  isExportOpen: boolean
  setIsExportOpen: (open: boolean) => void
  translationRequests: string[]
  startDownload: (categoryId: string) => void
  errorTranslation?: ApolloError
  loadingTranslations: boolean
  hasNewRequest: boolean
  download: (options: Options) => void
  downloadJson: any
  downloadError?: ApolloError
  type: typeItem
}

export const ExportByCategoryIdModal = ({
  isExportOpen,
  setIsExportOpen,
  translationRequests = [],
  startDownload,
  errorTranslation,
  loadingTranslations,
  hasNewRequest,
  type,
  ...props
}: Props) => {
  const [searchTerm, setSearchTerm] = useState('')
  const [hasError, setHasError] = useState(false)
  const [showMissingCatId, setShowMissingCatId] = useState(false)
  const { selectedLocale } = useLocaleSelector()
  const [tabSelected, setTabSelected] = useState<1 | 2>(1)
  const intl = useIntl()

  const headerEntityMessage = defineMessages({
    product: {
      id: 'admin/catalog-translation.export.modal.header-product',
    },
    sku: {
      id: 'admin/catalog-translation.export.modal.header-sku',
    },
  })

  const confirmationButtonLabel = defineMessages({
    product: {
      id: 'admin/catalog-translation.export.modal.confirmation-products',
    },
    sku: {
      id: 'admin/catalog-translation.export.modal.confirmation-skus',
    },
  })

  const [selectedCategory, setSelectedCategory] = useState<AutocompleteValue>(
    {} as AutocompleteValue
  )

  const {
    data: categoryInfo,
    loading: loadingCategoryInfo,
    error: categoryError,
  } = useQuery<CategoriesNameAndId>(GET_CATEGORIES_NAME)

  const listOfOptions = filterSearchCategories({
    categoryList: categoryInfo?.getCategoriesName ?? [],
    term: searchTerm,
  })

  useEffect(() => {
    // eslint-disable-next-line vtex/prefer-early-return
    if (hasNewRequest) {
      setSearchTerm('')
      setTabSelected(2)
    }
  }, [hasNewRequest])

  const handleClose = useCallback(() => {
    setSelectedCategory({} as AutocompleteValue)
    setIsExportOpen(false)
    setSearchTerm('')
    setHasError(false)
  }, [setIsExportOpen])

  useEffect(() => {
    if (errorTranslation || categoryError) {
      setHasError(true)
    }
  }, [errorTranslation, categoryError])

  const alertRef = useRef<any>()

  useEffect(() => {
    clearTimeout(alertRef.current)
    if (showMissingCatId) {
      alertRef.current = setTimeout(() => {
        setShowMissingCatId(false)
      }, 5000)
    }
  }, [showMissingCatId])

  return (
    <ModalDialog
      isOpen={isExportOpen}
      loading={loadingTranslations}
      cancelation={{
        label: (
          <FormattedMessage id="admin/catalog-translation.export.modal.cancelation" />
        ),
        onClick: handleClose,
      }}
      confirmation={{
        label: intl.formatMessage(confirmationButtonLabel[type]),
        onClick: () => {
          if (!selectedCategory.value) {
            setShowMissingCatId(true)
            return
          }
          startDownload(selectedCategory.value)
        },
      }}
      onClose={handleClose}
    >
      {showMissingCatId ? (
        <div className="relative">
          <div className="w-100 absolute z-max overflow-hidden top-0 left-0">
            <Alert type="warning" onClose={() => setShowMissingCatId(false)}>
              <FormattedMessage id="admin/catalog-translation.export.modal.missing-category-id" />
            </Alert>
          </div>
        </div>
      ) : null}
      <div style={{ minHeight: '420px' }}>
        <h3>
          <FormattedMessage
            id="admin/catalog-translation.export.modal.header"
            values={{
              selectedLocale,
              entry: intl.formatMessage(headerEntityMessage[type]),
            }}
          />
        </h3>
        {loadingCategoryInfo ? (
          <Spinner />
        ) : (
          <Tabs>
            <Tab
              label={
                <FormattedMessage id="admin/catalog-translation.export.modal.export-tab" />
              }
              active={tabSelected === 1}
              onClick={() => setTabSelected(1)}
            >
              <div>
                <h4>
                  <FormattedMessage id="admin/catalog-translation.export.modal.search-header" />
                </h4>
                <AutocompleteInput
                  input={{
                    placeholder: intl.formatMessage({
                      id:
                        'admin/catalog-translation.export.modal.category-search-placeholder',
                    }),
                    onChange: (term: string) => setSearchTerm(term),
                    onClear: () => {
                      setSearchTerm('')
                      setSelectedCategory({} as AutocompleteValue)
                    },
                    value: searchTerm,
                  }}
                  options={{
                    onSelect: (selectedItem: AutocompleteValue) =>
                      setSelectedCategory(selectedItem),
                    value: !searchTerm.length
                      ? []
                      : listOfOptions.slice(0, AUTOCOMPLETE_LIST_SIZE),
                    loading:
                      searchTerm.length > 0 &&
                      listOfOptions.length > AUTOCOMPLETE_LIST_SIZE,
                  }}
                />
                {hasError ? (
                  <p className="absolute c-danger i-s bottom-0-m right-0-m mr8">
                    <FormattedMessage id="admin/catalog-translation.export.modal.error-exporting" />
                  </p>
                ) : null}
              </div>
            </Tab>
            <Tab
              label={
                <FormattedMessage id="admin/catalog-translation.export.modal.see-files-tab" />
              }
              active={tabSelected === 2}
              onClick={() => setTabSelected(2)}
            >
              <p className="i f7 tr">
                <FormattedMessage id="admin/catalog-translation.export.modal.long-process.warning" />
              </p>
              <table className="w-100 mt7 tc">
                <thead>
                  <tr>
                    <th>
                      <FormattedMessage id="admin/catalog-translation.export.modal.table-header.catId" />
                    </th>
                    <th>
                      <FormattedMessage id="admin/catalog-translation.export.modal.table-header.locale" />
                    </th>
                    <th>
                      <FormattedMessage id="admin/catalog-translation.export.modal.table-header.requested.by" />
                    </th>
                    <th>
                      <FormattedMessage id="admin/catalog-translation.export.modal.table-header.requested.at" />
                    </th>
                    <th>
                      <FormattedMessage id="admin/catalog-translation.export.modal.table-header.download" />
                    </th>
                  </tr>
                </thead>
                <tbody>
                  {translationRequests
                    .slice(0, DOWNLOAD_LIST_SIZE)
                    .map((requestId) => (
                      <ExportListItem
                        key={requestId}
                        requestId={requestId}
                        type={type}
                        {...props}
                      />
                    ))}
                </tbody>
              </table>
            </Tab>
          </Tabs>
        )}
      </div>
    </ModalDialog>
  )
}

export default ExportByCategoryIdModal

```


[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 admin-catalog-translations/react/components/ImportStatusList.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { useQuery } from 'react-apollo'
import React, { useEffect, useRef, useState } from 'react'
import { FormattedDate, FormattedMessage, FormattedTime } from 'react-intl'
import { Progress } from 'vtex.styleguide'

import TRANSLATION_UPLOAD_REQUEST_INFO from '../graphql/translationUploadRequestInfo.gql'
import { shouldHaveCompleted } from '../utils'

const ImportStatusList = ({
  requestId,
  bucket,
}: {
  requestId: string
  bucket: UploadBucket
}) => {
  const [shouldHaveFinished, setShouldHaveFinished] = useState(false)
  const {
    data,
    error: errorFetchingInfo,
    startPolling,
    stopPolling,
  } = useQuery<
    { translationUploadRequestInfo: UploadRequest },
    { requestId: string; bucket: UploadBucket }
  >(TRANSLATION_UPLOAD_REQUEST_INFO, {
    variables: {
      requestId,
      bucket,
    },
  })

  const { translatedBy, createdAt, estimatedTime, locale, error, progress } =
    data?.translationUploadRequestInfo ?? {}

  const tooLongRef = useRef<any>()

  useEffect(() => {
    if (progress !== 100 && !error && createdAt && estimatedTime) {
      if (shouldHaveCompleted(createdAt, estimatedTime * 1.5)) {
        stopPolling()
        setShouldHaveFinished(true)
        return
      }
      startPolling(estimatedTime / 8)
    }
    if (progress === 100 || error) {
      stopPolling()
      clearTimeout(tooLongRef.current)
    }

    return () => stopPolling()
  }, [progress, error, createdAt, estimatedTime, stopPolling, startPolling])

  return !data || errorFetchingInfo ? null : (
    <tr>
      <td>
        <p>{locale}</p>
      </td>
      <td>
        <p>{translatedBy}</p>
      </td>
      <td>
        {createdAt ? (
          <span>
            <p>
              <FormattedTime value={createdAt} />
              {' - '}
              <FormattedDate value={createdAt} />
            </p>
          </span>
        ) : null}
      </td>
      <td>
        {error || shouldHaveFinished ? (
          <p className="c-danger i f7">
            <FormattedMessage id="admin/catalog-translation.import.modal.status-list.error" />
          </p>
        ) : (
          <Progress
            type="steps"
            steps={[progress === 100 ? 'completed' : 'inProgress']}
          />
        )}
      </td>
      <td>
        <p>{progress ?? '0'}%</p>
      </td>
    </tr>
  )
}

export default ImportStatusList

```


[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 admin-catalog-translations/react/components/ErrorHandler.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { EmptyState } from 'vtex.styleguide'
import { FormattedMessage } from 'react-intl'

type ErrorHandlerProps = {
  errorMessage: string
  entryId: string
  entry: string
}

const ErrorHandler: FC<ErrorHandlerProps> = ({
  errorMessage,
  entryId,
  entry,
}) => (
  <div>
    {errorMessage.indexOf('code 404') !== -1 ? (
      <EmptyState
        title={
          <FormattedMessage
            id="admin/catalog-translation.errors.not-found-header"
            values={{ entry }}
          />
        }
      >
        <p>
          <FormattedMessage
            id="admin/catalog-translation.errors.not-found-message"
            values={{
              entry,
              entryId,
            }}
          />
        </p>
      </EmptyState>
    ) : (
      <EmptyState
        title={
          <FormattedMessage
            id="admin/catalog-translation.errors.error-header"
            values={{ entry }}
          />
        }
      >
        <p>
          <FormattedMessage
            id="admin/catalog-translation.errors.error-message"
            values={{ entry }}
          />
        </p>
      </EmptyState>
    )}
  </div>
)

export default ErrorHandler

```


[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 admin-catalog-translations/react/graphql/accountLocales.gql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
query accountLocales {
  tenantInfo @context(provider: "vtex.tenant-graphql") {
    defaultLocale
    bindings {
      id
      defaultLocale
      supportedLocales
      targetProduct
    }
  }
}

```


[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 admin-catalog-translations/react/graphql/translationUploadRequestInfo.gql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
query translationUploadRequestInfo(
  $requestId: String!
  $bucket: UploadBucket!
) {
  translationUploadRequestInfo(requestId: $requestId, bucket: $bucket)
    @context(provider: "vtex.admin-catalog-translation") {
    requestId
    translatedBy
    createdAt
    estimatedTime
    error
    progress
    locale
  }
}

```


[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 admin-catalog-translations/react/graphql/translationRequestInfo.gql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
query translationRequestInfo($requestId: String!, $bucket: DownloadBucket!) {
  translationRequestInfo(requestId: $requestId, bucket: $bucket) {
    requestId
    requestedBy
    categoryId
    error
    createdAt
    locale
    completedAt
    estimatedTime
  }
}

```


[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 admin-catalog-translations/react/graphql/getCategoriesName.gql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
query getCategoriesName {
  getCategoriesName @context(provider: "vtex.admin-catalog-translation") {
    id
    name
  }
}

```


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

[User] Show me the implementation of admin-catalog-translations/react/hooks/useFormTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { FormEvent, useEffect, useState } from 'react'

import { hasChanges } from '../utils'

function useFormTranslation<S>(entryInfo: S) {
  const [formState, setFormState] = useState(entryInfo)
  const [canEdit, setCanEdit] = useState<boolean>(false)

  useEffect(() => {
    setCanEdit(false)
    setFormState(entryInfo)
  }, [entryInfo])

  const handleInputChange = (e: FormEvent<HTMLInputElement>) => {
    const { name: fieldName, value } = e.currentTarget

    setFormState((state) => ({
      ...state,
      ...{ [fieldName]: value },
    }))
  }

  const changed = hasChanges<S>(formState, entryInfo)

  const handleToggleEdit = () => {
    if (canEdit) {
      setCanEdit(false)
      setFormState(entryInfo)
    } else {
      setCanEdit(true)
    }
  }

  return { formState, canEdit, handleInputChange, changed, handleToggleEdit }
}

export default useFormTranslation

```


[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 admin-catalog-translations/react/hooks/useCatalogQuery.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { DocumentNode } from 'graphql'
import { FormEvent, useEffect, useState } from 'react'
import { useLazyQuery } from 'react-apollo'

import { useLocaleSelector } from '../components/LocaleSelector'

function useCatalogQuery<QueryReturn, Variables>(query: DocumentNode) {
  const { selectedLocale, xVtexTenant } = useLocaleSelector()
  const [memoEntries, setMemoEntries] = useState<{
    [Identifier: string]: QueryReturn
  }>({})
  const [entryId, setEntryId] = useState('')
  const [errorMessage, setErrorMessage] = useState('')

  const [
    fetchEntry,
    { refetch, loading: LoadingEntry, networkStatus },
  ] = useLazyQuery<QueryReturn, Variables>(query, {
    context: {
      headers: {
        'x-vtex-tenant': `${xVtexTenant}`,
        'x-vtex-locale': `${selectedLocale}`,
      },
    },
    fetchPolicy: 'no-cache',
    notifyOnNetworkStatusChange: true,
    onError: (e) => {
      setErrorMessage(e.message)
    },
  })

  useEffect(() => {
    async function refetchAndUpdate() {
      try {
        const { data } = await refetch()
        setMemoEntries({
          ...memoEntries,
          ...{ [selectedLocale]: data },
        })
      } catch (err) {
        setErrorMessage(err.message)
      }
    }

    if (!memoEntries[selectedLocale] && refetch && entryId) {
      refetchAndUpdate()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [memoEntries, refetch, selectedLocale])

  const handleEntryIdInput = (e: FormEvent<HTMLInputElement>) => {
    if (errorMessage) {
      setErrorMessage('')
    }
    const inputValue = e.currentTarget.value
    const onlyNumberRegex = /^\d{0,}$/
    if (onlyNumberRegex.test(inputValue)) {
      setEntryId(inputValue)
    }
  }

  const handleCleanSearch = () => {
    setEntryId('')
    setMemoEntries({})
  }

  const isLoadingOrRefetching = LoadingEntry || networkStatus === 4

  return {
    entryInfo: memoEntries[selectedLocale],
    entryId,
    setMemoEntries,
    isLoadingOrRefetching,
    handleCleanSearch,
    handleEntryIdInput,
    fetchEntry,
    errorMessage,
  }
}

export default useCatalogQuery

```


[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 admin-catalog-translations/react/components/BrandTranslation/BrandForm.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, SyntheticEvent } from 'react'
import { Input } from 'vtex.styleguide'
import { useMutation } from 'react-apollo'

import translateBrandMutation from './graphql/translateBrand.gql'
import { useAlert } from '../../providers/AlertProvider'
import { useLocaleSelector } from '../LocaleSelector'
import useFormTranslation from '../../hooks/useFormTranslation'
import ActionButtons from '../ActionButtons'

interface BrandFormProps {
  BrandInfo: BrandInputTranslation
  BrandId: string
  updateMemoBrands: (
    value: React.SetStateAction<{
      [Identifier: string]: BrandData
    }>
  ) => void
}

const BrandForm: FC<BrandFormProps> = ({
  BrandInfo,
  BrandId,
  updateMemoBrands,
}) => {
  const {
    formState,
    canEdit,
    handleInputChange,
    changed,
    handleToggleEdit,
  } = useFormTranslation<BrandInputTranslation>(BrandInfo)

  const { isXVtexTenant, selectedLocale } = useLocaleSelector()

  const [translateBrand, { loading }] = useMutation<
    { translateBrand: boolean },
    { brand: Brand; locale: string }
  >(translateBrandMutation)

  const { openAlert } = useAlert()

  const handleSubmit = async (e: SyntheticEvent) => {
    e.preventDefault()
    if (loading) {
      return
    }
    const brandArgs = {
      ...formState,
      ...{ id: BrandId },
    }
    try {
      const { data, errors } = await translateBrand({
        variables: {
          brand: brandArgs,
          locale: selectedLocale,
        },
      })
      const { translateBrand: translateBrandResult } = data ?? {}
      if (translateBrandResult) {
        // update cache value (local state)
        updateMemoBrands((state) => ({
          ...state,
          ...{ [selectedLocale]: { brand: brandArgs } },
        }))
        // send user feedback
        openAlert('success', 'brand')
      }
      if (errors?.length) {
        throw new TypeError('Error translating brand')
      }
    } catch (err) {
      openAlert('error', 'brand')
    }
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <div className="mb5">
          <Input
            label="Name"
            value={formState.name}
            name="name"
            disabled={isXVtexTenant || !canEdit}
            onChange={handleInputChange}
            required
          />
        </div>
        <div className="mb5">
          <Input
            label="Site Title"
            value={formState.siteTitle}
            name="siteTitle"
            disabled={isXVtexTenant || !canEdit}
            onChange={handleInputChange}
          />
        </div>
        <div className="mb5">
          <Input
            label="Brand Text"
            value={formState.text}
            name="text"
            disabled={isXVtexTenant || !canEdit}
            onChange={handleInputChange}
            required
          />
        </div>

        {isXVtexTenant ? null : (
          <ActionButtons
            toggleEdit={handleToggleEdit}
            canEdit={canEdit}
            changed={changed}
            loading={loading}
          />
        )}
      </form>
    </div>
  )
}
export default BrandForm

```


[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 admin-catalog-translations/react/components/BrandTranslation/BrandImportModal.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

import uploadMutationFile from './graphql/uploadBrandTranslation.gql'
import entryQueryFile from './graphql/brandUploadRequests.gql'
import ImportEntriesModal from '../ImportEntriesModal'

const bucket = 'brand_translation'
const entryHeaders: EntryHeadersBrand[] = ['id', 'name', 'text', 'siteTitle']
const entryQueryName = 'brandTranslationsUploadRequests'
const entryName = 'Brand'
const fileName = 'brand_translate_model'
const paramEntryName = 'brands'
const sheetName = 'brands_data'
const uploadMutationName = 'uploadBrandTranslations'

const settings: ImportEntriesSettings = {
  bucket,
  entryHeaders,
  entryQueryFile,
  entryQueryName,
  entryName,
  fileName,
  paramEntryName,
  sheetName,
  uploadMutationFile,
  uploadMutationName,
}

const BrandImportModal = ({
  isImportOpen = false,
  handleOpenImport = () => {},
}: ComponentProps) => {
  return (
    <>
      <ImportEntriesModal
        isImportOpen={isImportOpen}
        handleOpenImport={handleOpenImport}
        settings={settings}
      />
    </>
  )
}

export default BrandImportModal

```


[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 admin-catalog-translations/react/components/BrandTranslation/BrandTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { SyntheticEvent } from 'react'
import { PageBlock, Spinner, InputSearch } from 'vtex.styleguide'

import getBrand from './graphql/getBrand.gql'
import { useLocaleSelector } from '../LocaleSelector'
import ErrorHandler from '../ErrorHandler'
import BrandForm from './BrandForm'
import useCatalogQuery from '../../hooks/useCatalogQuery'
import BrandExportModal from './BrandExportModal'
import BrandImportModal from './BrandImportModal'

const BrandTranslation = ({
  isExportOpen = false,
  handleOpenExport = () => {},
  isImportOpen = false,
  handleOpenImport = () => {},
}: ComponentProps) => {
  const {
    entryInfo,
    isLoadingOrRefetching,
    entryId,
    handleCleanSearch,
    handleEntryIdInput,
    fetchEntry,
    setMemoEntries,
    errorMessage,
  } = useCatalogQuery<BrandData, { id: number }>(getBrand)

  const { selectedLocale } = useLocaleSelector()

  const handleSubmitBrandId = (e: SyntheticEvent) => {
    e.preventDefault()
    if (!entryId) {
      return
    }
    setMemoEntries({})
    fetchEntry({ variables: { id: Number(entryId) } })
  }

  const { id, ...brandInfo } = entryInfo?.brand || ({} as Brand)

  return (
    <>
      <main>
        <div style={{ maxWidth: '340px' }} className="mv7">
          <InputSearch
            value={entryId}
            placeholder="Search Brand..."
            label="Brand ID"
            size="regular"
            onChange={handleEntryIdInput}
            onSubmit={handleSubmitBrandId}
            onClear={handleCleanSearch}
          />
        </div>
        {id || isLoadingOrRefetching || errorMessage ? (
          <PageBlock variation="full" title={`Brand Info - ${selectedLocale}`}>
            {errorMessage ? (
              <ErrorHandler
                errorMessage={errorMessage}
                entryId={entryId}
                entry="Brand"
              />
            ) : isLoadingOrRefetching ? (
              <Spinner />
            ) : (
              <BrandForm
                BrandInfo={brandInfo}
                BrandId={id}
                updateMemoBrands={setMemoEntries}
              />
            )}
          </PageBlock>
        ) : null}
      </main>
      <BrandExportModal
        isExportOpen={isExportOpen}
        handleOpenExport={handleOpenExport}
      />
      <BrandImportModal
        isImportOpen={isImportOpen}
        handleOpenImport={handleOpenImport}
      />
    </>
  )
}

export default BrandTranslation

```


[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 admin-catalog-translations/react/components/BrandTranslation/BrandExportModal.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, useEffect } from 'react'
import { useLazyQuery } from 'react-apollo'
import { defineMessages, useIntl } from 'react-intl'
import { ModalDialog, Checkbox } from 'vtex.styleguide'

import { useLocaleSelector } from '../LocaleSelector'
import getAllBrands from './graphql/getAllBrands.gql'
import { parseJSONToXLS } from '../../utils'

const modalMessage = defineMessages({
  export: {
    id: 'admin/catalog-translation.export.modal.title-brand',
  },
  confirmation: {
    id: 'admin/catalog-translation.export.modal.confirmation-brands',
  },
  active: {
    id: 'admin/catalog-translation.export.modal.export-active-brands',
  },
  cancel: {
    id: 'admin/catalog-translation.action-buttons.cancel',
  },
  error: {
    id: 'admin/catalog-translation.export.modal.error-exporting',
  },
})
interface BrandTranslations {
  brandTranslations: Brand[]
}

const BrandExportModal = ({
  isExportOpen = false,
  handleOpenExport = () => {},
}: ComponentProps) => {
  const intl = useIntl()

  const [onlyActive, setOnlyActive] = useState(true)
  const [downloading, setDownloading] = useState(false)
  const [hasError, setHasError] = useState(false)

  const { selectedLocale } = useLocaleSelector()

  const [fetchTranslations, { data, error }] = useLazyQuery<
    BrandTranslations,
    { locale: string; active?: boolean }
  >(getAllBrands, {
    context: {
      headers: {
        'x-vtex-locale': `${selectedLocale}`,
      },
    },
    fetchPolicy: 'no-cache',
  })

  const downloadTranslations = () => {
    setHasError(false)
    setDownloading(true)
    fetchTranslations({
      variables: { active: onlyActive, locale: selectedLocale },
    })
  }

  useEffect(() => {
    // eslint-disable-next-line vtex/prefer-early-return
    if (data && downloading) {
      parseJSONToXLS(data.brandTranslations, {
        fileName: `brands-data-${selectedLocale}`,
        sheetName: 'brands_data',
      })

      setDownloading(false)
      handleOpenExport(false)
    }
  }, [data, selectedLocale, downloading, handleOpenExport])

  useEffect(() => {
    // eslint-disable-next-line vtex/prefer-early-return
    if (error) {
      setDownloading(false)
      setHasError(true)
    }
  }, [error])

  return (
    <ModalDialog
      loading={downloading}
      cancelation={{
        label: intl.formatMessage(modalMessage.cancel),
        onClick: () => {
          handleOpenExport(false)
          setHasError(false)
        },
      }}
      confirmation={{
        label: intl.formatMessage(modalMessage.confirmation),
        onClick: downloadTranslations,
      }}
      isOpen={isExportOpen}
      onClose={() => {
        handleOpenExport(false)
        setHasError(false)
      }}
    >
      <div>
        <h3>
          {intl.formatMessage(modalMessage.export)} {selectedLocale}
        </h3>
        <Checkbox
          label={intl.formatMessage(modalMessage.active)}
          name="active-selection"
          value={onlyActive}
          checked={onlyActive}
          onChange={() => setOnlyActive(!onlyActive)}
        />
      </div>
      {hasError ? (
        <p className="absolute c-danger i-s bottom-0-m right-0-m mr8">
          {intl.formatMessage(modalMessage.error)}
        </p>
      ) : null}
    </ModalDialog>
  )
}

export default BrandExportModal

```


[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 admin-catalog-translations/react/components/CategoryTranslation/CategoryTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { SyntheticEvent, useEffect, useState } from 'react'
import {
  PageBlock,
  Spinner,
  InputSearch,
  ModalDialog,
  Checkbox,
} from 'vtex.styleguide'
import { useLazyQuery } from 'react-apollo'

import getCategory from './graphql/getCategory.gql'
import { useLocaleSelector } from '../LocaleSelector'
import ErrorHandler from '../ErrorHandler'
import CategoryForm from './CategoryForm'
import useCatalogQuery from '../../hooks/useCatalogQuery'
import getAllCategories from './graphql/getAllCategories.gql'
import { parseJSONToXLS } from '../../utils'
import CategoryImportModal from './CategoryImportModal'

interface CategoryTranslations {
  categoryTranslations: Category[]
}

const CategoryTranslation = ({
  isExportOpen = false,
  handleOpenExport = () => {},
  isImportOpen = false,
  handleOpenImport = () => {},
}: ComponentProps) => {
  const [onlyActive, setOnlyActive] = useState(true)
  const [downloading, setDownloading] = useState(false)
  const [hasError, setHasError] = useState(false)
  const {
    entryInfo,
    isLoadingOrRefetching,
    entryId,
    handleCleanSearch,
    handleEntryIdInput,
    fetchEntry,
    setMemoEntries,
    errorMessage,
  } = useCatalogQuery<CategoriesData, { id: number }>(getCategory)

  const { selectedLocale } = useLocaleSelector()

  const [fetchCategories, { data, error }] = useLazyQuery<
    CategoryTranslations,
    { locale: string; active?: boolean }
  >(getAllCategories, {
    context: {
      headers: {
        'x-vtex-locale': `${selectedLocale}`,
      },
    },
    fetchPolicy: 'no-cache',
  })

  const handleSubmitCategoryId = (e: SyntheticEvent) => {
    e.preventDefault()
    if (!entryId) {
      return
    }
    setMemoEntries({})
    fetchEntry({ variables: { id: Number(entryId) } })
  }

  const { id, ...categoryInfo } = entryInfo?.category || ({} as Category)

  const downloadCategories = () => {
    setHasError(false)
    setDownloading(true)
    fetchCategories({
      variables: { active: onlyActive, locale: selectedLocale },
    })
  }

  useEffect(() => {
    // eslint-disable-next-line vtex/prefer-early-return
    if (data && downloading) {
      parseJSONToXLS(data.categoryTranslations, {
        fileName: `category-data-${selectedLocale}`,
        sheetName: 'category_data',
      })

      setDownloading(false)
      handleOpenExport(false)
    }
  }, [data, selectedLocale, downloading, handleOpenExport])

  useEffect(() => {
    // eslint-disable-next-line vtex/prefer-early-return
    if (error) {
      setDownloading(false)
      setHasError(true)
    }
  }, [error])

  return (
    <>
      <main>
        <div className="flex">
          <div style={{ maxWidth: '340px' }} className="mv7">
            <InputSearch
              value={entryId}
              placeholder="Search category..."
              label="Category Id"
              size="regular"
              onChange={handleEntryIdInput}
              onSubmit={handleSubmitCategoryId}
              onClear={handleCleanSearch}
            />
          </div>
        </div>
        {id || isLoadingOrRefetching || errorMessage ? (
          <PageBlock
            variation="full"
            title={`Category Info - ${selectedLocale}`}
          >
            {errorMessage ? (
              <ErrorHandler
                errorMessage={errorMessage}
                entryId={entryId}
                entry="Category"
              />
            ) : isLoadingOrRefetching ? (
              <Spinner />
            ) : (
              <CategoryForm
                categoryInfo={categoryInfo}
                categoryId={id}
                updateMemoCategories={setMemoEntries}
              />
            )}
          </PageBlock>
        ) : null}
      </main>
      <ModalDialog
        loading={downloading}
        cancelation={{
          label: 'Cancel',
          onClick: () => {
            handleOpenExport(false)
            setHasError(false)
          },
        }}
        confirmation={{
          label: 'Export Categories',
          onClick: downloadCategories,
        }}
        isOpen={isExportOpen}
        onClose={() => {
          handleOpenExport(false)
          setHasError(false)
        }}
      >
        <div>
          <h3>Export Category Data for {selectedLocale}</h3>
          <Checkbox
            label="Export only Active Categories"
            name="active-selection"
            value={onlyActive}
            checked={onlyActive}
            onChange={() => setOnlyActive(!onlyActive)}
          />
        </div>
        {hasError ? (
          <p className="absolute c-danger i-s bottom-0-m right-0-m mr8">
            There was an error exporting categories. Please try again.
          </p>
        ) : null}
      </ModalDialog>
      <CategoryImportModal
        isImportOpen={isImportOpen}
        handleOpenImport={handleOpenImport}
      />
    </>
  )
}

export default CategoryTranslation

```


[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 admin-catalog-translations/react/components/CategoryTranslation/CategoryForm.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, SyntheticEvent } from 'react'
import { Input, Textarea } from 'vtex.styleguide'
import { useMutation } from 'react-apollo'

import translateCategoryMutation from './graphql/translateCategory.gql'
import { useAlert } from '../../providers/AlertProvider'
import { useLocaleSelector } from '../LocaleSelector'
import useFormTranslation from '../../hooks/useFormTranslation'
import ActionButtons from '../ActionButtons'

interface CategoryFormProps {
  categoryInfo: CategoryInputTranslation
  categoryId: string
  updateMemoCategories: (
    value: React.SetStateAction<{
      [Identifier: string]: CategoriesData
    }>
  ) => void
}

const CategoryForm: FC<CategoryFormProps> = ({
  categoryInfo,
  categoryId,
  updateMemoCategories,
}) => {
  const {
    formState,
    canEdit,
    handleInputChange,
    changed,
    handleToggleEdit,
  } = useFormTranslation(categoryInfo)

  const [translateCategory, { loading }] = useMutation<
    { translateCategory: boolean },
    { args: Category; locale: string }
  >(translateCategoryMutation)
  const { openAlert } = useAlert()

  const { isXVtexTenant, selectedLocale } = useLocaleSelector()

  const handleSubmit = async (e: SyntheticEvent) => {
    e.preventDefault()
    if (loading) {
      return
    }
    const args = {
      ...formState,
      ...{ id: categoryId },
    }
    try {
      const { data, errors } = await translateCategory({
        variables: {
          args,
          locale: selectedLocale,
        },
      })
      const { translateCategory: translateCategoryResult } = data ?? {}
      if (translateCategoryResult) {
        // update cache value (local state)
        updateMemoCategories((state) => ({
          ...state,
          ...{ [selectedLocale]: { category: args } },
        }))
        // send user feedback
        openAlert('success', 'category')
      }
      if (errors?.length) {
        throw new TypeError('Error translating category')
      }
    } catch (err) {
      openAlert('error', 'category')
    }
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <div className="mb5">
          <Input
            label="Name"
            value={formState.name}
            name="name"
            disabled={isXVtexTenant || !canEdit}
            onChange={handleInputChange}
            required
          />
        </div>
        <div className="mb5">
          <Input
            label="Title"
            value={formState.title}
            name="title"
            disabled={isXVtexTenant || !canEdit}
            onChange={handleInputChange}
            required
          />
        </div>
        <div className="mb5">
          <Textarea
            resize="none"
            label="Description"
            value={formState.description}
            name="description"
            disabled={isXVtexTenant || !canEdit}
            onChange={handleInputChange}
            required
          />
        </div>
        <div className="mb5">
          <Input
            label="Link Id"
            value={formState.linkId}
            name="linkId"
            disabled={isXVtexTenant || !canEdit}
            onChange={handleInputChange}
            required
            pattern="^[^\s]+$"
            helpText={<p>Link ID cannot have whitespaces</p>}
          />
        </div>
        {isXVtexTenant ? null : (
          <ActionButtons
            toggleEdit={handleToggleEdit}
            canEdit={canEdit}
            changed={changed}
            loading={loading}
          />
        )}
      </form>
    </div>
  )
}

export default CategoryForm

```