[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 glovo-integration/node/utils/orders.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable max-params */

import type { ClientProfileData } from 'vtex.glovo-integration'

import { RESIDENTIAL, HOME } from '../constants'
import { isSkuAvailable } from './utils'

/* eslint-disable @typescript-eslint/naming-convention */
export const createVtexOrderData = (
  glovoOrder: GlovoOrder,
  orderSimulation: any,
  clientProfileData: ClientProfileData,
  marketplace: boolean,
  account: string
): CreateOrderPayload => {
  const {
    order_id,
    customer: { name, phone_number },
  } = glovoOrder

  const { items, pickupPoints, postalCode, logisticsInfo, totals } =
    orderSimulation

  const {
    email,
    firstName,
    lastName,
    documentType,
    document,
    phone,
    corporateName,
  } = clientProfileData

  /** Check if Glovo sends customer name */
  const customerName = name ?? `${firstName} ${lastName}`

  /** Re-Index items array */
  let counter = 0
  const updatedItems = items.reduce((reIndexedItems: any, item: any) => {
    if (isSkuAvailable(item)) {
      const updatedItem = {
        ...item,
        itemIndex: 0,
      }

      counter++

      return [...reIndexedItems, updatedItem]
    }

    return reIndexedItems
  }, [])

  /** Re-Index logisticsInfo array */
  counter = 0
  const updatedLogisticsInfo = logisticsInfo.reduce(
    (reIndexedItems: any[], item: any, index: number) => {
      if (items[index].availability === 'available') {
        const updatedItem = {
          itemIndex: counter,
          selectedSla: item.slas[0].id,
          selectedDeliveryChannel: item.slas[0].deliveryChannel,
          name: item.slas[0].name,
          lockTTL: '1d',
          deliveryWindow: item.slas[0].availableDeliveryWindows[0],
          shippingEstimate: item.slas[0].shippingEstimate,
          price: item.slas[0].price,
        }

        counter++

        return [...reIndexedItems, updatedItem]
      }

      return reIndexedItems
    },
    []
  )

  const totalValue = totals.reduce(
    (total: number, item: SimulationTotalsItem) => (total += item.value),
    0
  )

  const vtexOrderData: CreateOrderPayload = {
    marketplaceOrderId: order_id,
    marketplaceServicesEndpoint: `https://${account}.myvtex.com`,
    marketplacePaymentValue: totalValue,
    marketplaceOrderGroup: order_id,
    isCreatedAsync: true,
    items: updatedItems,
    savePersonalData: false,
    clientProfileData: {
      email: getEmail(customerName, phone_number, email),
      firstName: getFirstName(customerName) ?? firstName,
      lastName: getLastName(customerName) ?? lastName,
      documentType,
      document,
      phone: phone_number ?? phone,
      corporateName,
      tradeName: null,
      corporateDocument: null,
      stateInscription: null,
      corporatePhone: null,
      isCorporate: false,
      userProfileId: null,
    },
    shippingData: {
      address: {
        addressType: RESIDENTIAL,
        receiverName: getReceiverName(customerName, phone_number),
        addressId: HOME,
        postalCode,
        city: pickupPoints[0].address.city,
        state: pickupPoints[0].address.state,
        country: pickupPoints[0].address.country,
        street: pickupPoints[0].address.street,
        number: null,
        neighborhood: null,
        complement: null,
        reference: null,
        geoCoordinates: [],
      },
      logisticsInfo: updatedLogisticsInfo,
    },
  }

  if (marketplace) {
    vtexOrderData.paymentData = {
      payments: [
        {
          installments: 1,
          paymentSystem: glovoOrder.payment_method,
          referenceValue: totalValue,
          value: totalValue,
        },
      ],
    }
  }

  return vtexOrderData
}

export const createAuthorizationPayload = (
  orderIdentifier: string,
  marketplace: boolean,
  glovoOrder: GlovoOrder
): AuthorizeOrderPayload | AuthorizeMarketplaceOrderPayload => {
  const payload = marketplace
    ? {
        marketplaceOrderGroup: orderIdentifier,
        authorizationReceipt: {
          date: glovoOrder.order_time,
          receipt: glovoOrder.order_code,
        },
      }
    : {
        marketplaceOrderId: orderIdentifier,
      }

  return payload
}

function getEmail(name: string, phone_number: string, email: string) {
  return (
    `${name.replace(/\W/g, '').toLowerCase()}_${phone_number.replace(
      /\W/g,
      ''
    )}@glovointegration.com` ?? email
  )
}

function getFirstName(name: string) {
  return name.split(' ')[0]
}

function getLastName(name: string) {
  return name.split(' ')[1]
}

function getReceiverName(name: string, phone_number: string) {
  return `${name} - ${phone_number}`
}

/**
 * Function to get the items that were changed in an order
 *
 * @param changesData Order modification event changesAttachements
 * @returns changedItems Object with the items that were changed { itemId: { quantity: number } }
 */
export function getChangedItems(changesData: ChangesData[]) {
  const changedItems: Record<string, { quantity: number }> = {}

  for (const change of changesData) {
    for (const item of change.itemsAdded) {
      if (!changedItems[item.id]) {
        changedItems[item.id] = { quantity: item.quantity }
      } else {
        changedItems[item.id].quantity += item.quantity
      }
    }

    for (const item of change.itemsRemoved) {
      if (!changedItems[item.id]) {
        changedItems[item.id] = { quantity: -item.quantity }
      } else {
        changedItems[item.id].quantity -= item.quantity
      }
    }
  }

  return changedItems
}

```


[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 glovo-integration/node/utils/records.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ServiceError } from './errors'

export const generateStoreMenuRecord = async (
  ctx: Context,
  affiliateId: string
) => {
  const {
    clients: { recordsManager },
  } = ctx

  try {
    const glovoMenu = await recordsManager.getGlovoMenu()

    if (!glovoMenu) {
      throw new Error('Unable to get Glovo Menu')
    }

    const storeMenuRecord: StoreMenuRecord = {
      items: {},
      lastUpdated: '',
    }

    const itemIds = Object.keys(glovoMenu)

    for await (const item of itemIds) {
      const recordItem = await recordsManager.getProductRecord(
        affiliateId,
        item
      )

      if (recordItem) {
        storeMenuRecord.items[item] = recordItem
      }
    }

    storeMenuRecord.lastUpdated = Date()

    recordsManager.saveStoreMenuRecord(affiliateId, storeMenuRecord)
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason: `There was a problem creating the store menu record for ${affiliateId}`,
      metric: 'menu',
    })
  }
}

```


[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 glovo-integration/node/utils/index.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export * from './errors'
export * from './glovo'
export * from './orders'
export * from './simulation'
export * from './utils'
export * from './records'
export * from './requests'

```


[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 glovo-integration/node/utils/glovo.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { OrderFormItem, SimulationOrderForm } from '@vtex/clients'
import type {
  GlovoIntegrationSettings,
  MaxItemsForSimulation,
} from 'vtex.glovo-integration'

import {
  ACCEPTED,
  INVOICED,
  READY_FOR_PICKUP,
  GLOVO,
  APP_SETTINGS,
  READY_FOR_HANDLING,
  WAITING_SELLER_HANDLING,
  MAX_ITEMS_FOR_SIMULATION,
} from '../constants'
import { ServiceError } from './errors'
import { requestWithRetries } from './requests'
import {
  createGlovoBulkUpdatePayload,
  createSimulationItems,
  createSimulationPayload,
  iterationLimits,
  simulateItem,
} from './simulation'

export const setGlovoStatus = (state: string) => {
  if (state === WAITING_SELLER_HANDLING) return ACCEPTED
  if (state === READY_FOR_HANDLING) return ACCEPTED
  if (state === INVOICED) return READY_FOR_PICKUP

  return ''
}

export const updateGlovoProduct = async (
  ctx: Context,
  catalogUpdate: CatalogChange
) => {
  const {
    clients: { vbase, recordsManager },
    vtex: { logger },
  } = ctx

  const appSettings: GlovoIntegrationSettings = await vbase.getJSON(
    GLOVO,
    APP_SETTINGS,
    true
  )

  if (!appSettings.glovoToken) {
    logger.error({
      message: 'Missing Glovo token. Please check app settings',
    })

    return
  }

  const { stores } = appSettings
  const { IdSku, IsActive } = catalogUpdate

  if (!stores) {
    logger.error({
      message: 'Missing or invalid store information',
      catalogUpdate,
    })

    return
  }

  const glovoMenu = await recordsManager.getGlovoMenu()

  if (!glovoMenu[IdSku]) {
    return
  }

  for await (const store of stores) {
    const { id, storeName, glovoStoreId } = store

    const simulation = await simulateItem(IdSku, store, ctx)

    if (!simulation) {
      logger.warn({
        message: `Simulation failed for product with sku ${IdSku} for store ${storeName}`,
        catalogUpdate,
      })

      continue
    }

    const { price, available } = simulation

    let newProduct = false
    let productRecord = await recordsManager.getProductRecord(
      glovoStoreId,
      IdSku
    )

    if (!productRecord) {
      if (!IsActive) {
        continue
      }

      newProduct = true

      const newProductRecord: ProductRecord = {
        id: IdSku,
        available,
        price,
      }

      productRecord = newProductRecord
    }

    let glovoPayload: GlovoProductUpdate = {
      skuId: IdSku,
      glovoStoreId,
    }

    if (IsActive && price) {
      glovoPayload = {
        ...glovoPayload,
        price,
        available,
      }
    } else {
      glovoPayload.available = false
    }

    if (
      !newProduct &&
      productRecord.price === glovoPayload.price &&
      productRecord.available === glovoPayload.available
    ) {
      continue
    }

    try {
      const updatedProductRecord: ProductRecord = {
        id: IdSku,
        price: glovoPayload.price,
        available: glovoPayload.available,
      }

      recordsManager.saveProductRecord(
        glovoStoreId,
        IdSku,
        updatedProductRecord
      )

      let storeMenuUpdates = await recordsManager.getStoreMenuUpdates(
        glovoStoreId
      )

      if (!storeMenuUpdates) {
        // If the Store Menu Updates Record does not exist already, it is created.
        storeMenuUpdates = {
          current: {
            responseId: null,
            createdAt: Date.now(),
            storeId: id,
            storeName,
            glovoStoreId,
            items: [],
          },
        }
      }

      const currentUpdateItemsIds = storeMenuUpdates.current.items.map(
        (item) => item.id
      )

      if (!currentUpdateItemsIds.includes(IdSku)) {
        storeMenuUpdates.current.items.push(updatedProductRecord)
      } else {
        storeMenuUpdates.current.items.map((item) => {
          if (item.id === IdSku) {
            item.id = IdSku
            item.price = updatedProductRecord.price
            item.available = updatedProductRecord.available
          }

          return item
        })
      }

      recordsManager.saveStoreMenuUpdates(glovoStoreId, storeMenuUpdates)

      logger.info({
        message: `Product with sku ${IdSku} from store ${glovoStoreId} has been updated`,
        updatedProductRecord,
      })
    } catch (error) {
      logger.error({
        message: `Product with sku ${IdSku} from store ${glovoStoreId} could not be updated`,
        data: error.response,
      })

      return error
    }
  }
}

export const updateGlovoCompleteMenu = async (ctx: Context) => {
  const {
    clients: { vbase, checkout, glovo, recordsManager },
    vtex: { logger },
  } = ctx

  try {
    const appSettings: GlovoIntegrationSettings = await vbase.getJSON(
      GLOVO,
      APP_SETTINGS,
      true
    )

    if (!appSettings?.glovoToken) {
      logger.error({
        message: 'Menu update for stores failed',
        reason: 'Missing or invalid Glovo token. Please check app settings',
      })

      return
    }

    const { glovoToken, stores, minimumStock } = appSettings

    if (!stores?.length) {
      logger.error({
        message: 'Menu update for stores failed',
        reason: 'Missing or invalid stores information',
      })

      return
    }

    const glovoMenu = await recordsManager.getGlovoMenu()

    const { max }: MaxItemsForSimulation = await vbase.getJSON(
      GLOVO,
      MAX_ITEMS_FOR_SIMULATION,
      true
    )

    const maxItemsForSimulation = max ?? 300

    const iterations = Math.ceil(
      Object.keys(glovoMenu).length / maxItemsForSimulation
    )

    for await (const store of stores) {
      logger.info({
        message: `Updating menu for store ${store.storeName}`,
      })

      const {
        affiliateId,
        sellerId,
        salesChannel,
        glovoStoreId,
        storeName,
        postalCode,
        country,
      } = store

      let payloadItems: OrderFormItem[] = []

      for (let i = 0; i < iterations; i++) {
        const [from, to] = iterationLimits(i, maxItemsForSimulation)

        const itemsForSimulation = Object.keys(glovoMenu).slice(from, to)

        const simulationItems = createSimulationItems(
          itemsForSimulation,
          sellerId,
          minimumStock
        )

        const [simulationPayload, querystring] = createSimulationPayload({
          items: simulationItems,
          affiliateId,
          salesChannel,
          postalCode,
          country,
        })

        let simulation: SimulationOrderForm

        try {
          // eslint-disable-next-line no-await-in-loop
          simulation = await requestWithRetries(
            checkout.simulation(simulationPayload, querystring)
          )
        } catch (error) {
          logger.warn({
            message: `Catalog update for store ${storeName} - ${glovoStoreId} failed`,
            reason: `Simulation for items failed`,
          })

          continue
        }

        if (!simulation.items.length) {
          logger.warn({
            message: `Catalog update for store ${storeName} - ${glovoStoreId} failed`,
            reason: `Simulation returned no items`,
          })

          continue
        }

        payloadItems = [...payloadItems, ...simulation.items]
      }

      const glovoPayload = createGlovoBulkUpdatePayload(
        payloadItems,
        minimumStock
      )

      try {
        const glovoResponse = await requestWithRetries<GlovoBulkUpdateResponse>(
          glovo.bulkUpdateProducts(glovoPayload, glovoStoreId, glovoToken)
        )

        logger.info({
          message: `Catalog for store ${storeName} - ${glovoStoreId} has been updated. (${payloadItems.length} items)`,
          glovoResponse,
        })

        recordsManager.saveStoreCompleteMenuUpdate(glovoStoreId, {
          items: [...glovoPayload.products],
          transactionId: glovoResponse.transaction_id,
          lastUpdated: Date(),
          successful: true,
          minimumStock,
        })
      } catch (error) {
        logger.error({
          message: `Catalog for store ${storeName} - ${glovoStoreId} could not be updated`,
          reason: `Bulk update request failed`,
        })

        recordsManager.saveStoreCompleteMenuUpdate(glovoStoreId, {
          items: [...glovoPayload.products],
          transactionId: null,
          lastUpdated: Date(),
          successful: false,
          minimumStock,
        })

        continue
      }
    }
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason: error.reason ?? 'Catalog update for stores failed',
      metric: 'menu',
      data: error.response?.data,
    })
  }
}

export const updateGlovoPartialMenu = async (ctx: Context) => {
  const {
    clients: { vbase, glovo, recordsManager },
    vtex: { logger },
  } = ctx

  const appSettings: GlovoIntegrationSettings = await vbase.getJSON(
    GLOVO,
    APP_SETTINGS,
    true
  )

  if (!appSettings.glovoToken) {
    logger.error({
      message: 'Missing or invalid Glovo token. Please check app settings',
    })

    return
  }

  const { stores, glovoToken } = appSettings

  if (!stores?.length) {
    logger.error({
      message: 'Missing or invalid stores information',
    })

    return
  }

  // Send a partial bulk product update for each store
  for await (const store of stores) {
    const { id, storeName, glovoStoreId } = store

    try {
      const menuUpdates = await recordsManager.getStoreMenuUpdates(glovoStoreId)

      const { current: currentUpdate } = menuUpdates

      const glovoPayload: GlovoProductBulkUpdate = {
        products: currentUpdate.items,
      }

      const newUpdate: MenuUpdatesItem = {
        responseId: null,
        createdAt: new Date().getTime(),
        storeId: id,
        storeName,
        glovoStoreId,
        items: [],
      }

      const glovoResponse = await glovo.bulkUpdateProducts(
        glovoPayload,
        glovoStoreId,
        glovoToken
      )

      currentUpdate.responseId = glovoResponse.transaction_id
      menuUpdates.previous = currentUpdate
      menuUpdates.current = newUpdate

      recordsManager.saveStoreMenuUpdates(glovoStoreId, menuUpdates)

      logger.info({
        message: `Menu for store ${storeName} with id ${id} was updated`,
        data: menuUpdates,
      })
    } catch (error) {
      logger.error({
        message: `Partial Catalog for store ${storeName} with ${id} could not be updated`,
        data: error.response,
      })
    }
  }
}

```


[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 glovo-integration/node/utils/simulation.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  OrderFormItem,
  PayloadItem,
  SimulationPayload,
} from '@vtex/clients'
import type { Store } from 'vtex.glovo-integration'

import { AVAILABLE } from '../constants'
import { isSkuAvailable } from './utils'

export const createSimulationItem = ({
  id,
  quantity,
  sellerId,
}: SimulationItem): PayloadItem => {
  return {
    id,
    quantity,
    seller: sellerId,
  }
}

export const createSimulationItems = (
  items: string[],
  sellerId: string,
  quantity = 1
): PayloadItem[] => {
  const simulationItems: PayloadItem[] = []

  for (const item of items) {
    const payloadItem: PayloadItem = {
      id: item,
      quantity,
      seller: sellerId,
    }

    simulationItems.push(payloadItem)
  }

  return simulationItems
}

export function iterationLimits(step: number, maxItemsForSimulation: number) {
  return [
    maxItemsForSimulation * step,
    maxItemsForSimulation * step + maxItemsForSimulation - 1,
  ]
}

export const createSimulationPayload = ({
  items,
  affiliateId,
  salesChannel,
  postalCode,
  country,
}: CreateSimulationArgs): [SimulationPayload, string] => {
  const simulationPayload = {
    items,
    postalCode,
    country,
  }

  const queryString = `?affiliateId=${affiliateId}&sc=${salesChannel}`

  return [simulationPayload, queryString]
}

export const createGlovoBulkUpdatePayload = (
  items: OrderFormItem[],
  minimumStock = 1
): GlovoProductBulkUpdate => {
  const payload: GlovoProductBulkUpdate = {
    products: [],
  }

  for (const item of items) {
    const { id, price, listPrice, unitMultiplier, availability, quantity } =
      item

    const available = availability === AVAILABLE && quantity >= minimumStock

    const payloadProduct: GlovoProductPatch = {
      id,
      available,
    }

    if (available) {
      payloadProduct.price = (Math.max(price, listPrice) * unitMultiplier) / 100
    }

    payload.products.push(payloadProduct)
  }

  return payload
}

export const simulateItem = async (
  IdSku: string,
  store: Store,
  ctx: Context
): Promise<SimulatedItem | null> => {
  const {
    clients: { checkout },
    vtex: { logger },
  } = ctx

  const { affiliateId, sellerId, salesChannel, postalCode, country } = store
  const simulationItem = createSimulationItem({
    id: IdSku,
    quantity: 1,
    sellerId,
  })

  const simulationPayload = createSimulationPayload({
    items: [simulationItem],
    affiliateId,
    salesChannel,
    postalCode,
    country,
  })

  try {
    const simulation = await checkout.simulation(...simulationPayload)

    const {
      items: [item],
    } = simulation

    let itemInfo = {
      price: 0,
      available: false,
    }

    if (isSkuAvailable(item)) {
      const { price, listPrice, unitMultiplier } = item

      itemInfo = {
        price: (Math.max(price, listPrice) * unitMultiplier) / 100,
        available: true,
      }
    }

    return itemInfo
  } catch (error) {
    logger.error({
      message:
        error.message ?? `Simulation for product with skuId ${IdSku} failed`,
      data: error.response,
    })

    return null
  }
}

```


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

[User] Show me the implementation of glovo-integration/node/resolvers/getAppSettings.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { GlovoIntegrationSettings } from 'vtex.glovo-integration'

import { APP_SETTINGS, GLOVO } from '../constants'

export const getGlovoIntegrationSettings = async (
  _: unknown,
  __: unknown,
  ctx: Context
): Promise<GlovoIntegrationSettings> => {
  const {
    clients: { vbase },
  } = ctx

  const appSettings: GlovoIntegrationSettings = await vbase.getJSON(
    GLOVO,
    APP_SETTINGS,
    true
  )

  return appSettings
}

```


[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 glovo-integration/node/resolvers/maxItemsForSimulation.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { MaxItemsForSimulation } from 'vtex.glovo-integration'

import { GLOVO, MAX_ITEMS_FOR_SIMULATION } from '../constants'

export async function getMaxItemsForSimulation(
  _root: unknown,
  _args: unknown,
  ctx: Context
): Promise<MaxItemsForSimulation | null> {
  const {
    clients: { vbase },
  } = ctx

  try {
    const maxItemsForSimulation: MaxItemsForSimulation = await vbase.getJSON(
      GLOVO,
      MAX_ITEMS_FOR_SIMULATION,
      true
    )

    return maxItemsForSimulation
  } catch (error) {
    return null
  }
}

export async function updateMaxItemsForSimulation(
  _root: unknown,
  { max }: { max: number },
  ctx: Context
): Promise<MaxItemsForSimulation | null> {
  const {
    clients: { vbase },
  } = ctx

  try {
    await vbase.saveJSON(GLOVO, MAX_ITEMS_FOR_SIMULATION, { max })

    return { max }
  } catch (error) {
    return null
  }
}

```


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

[User] Show me the implementation of glovo-integration/node/resolvers/saveAppSettings.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { SaveGlovoIntegrationSettings } from 'vtex.glovo-integration'

import { validateInputs } from '../../common/utils'
import { APP_SETTINGS, GLOVO } from '../constants'

export const saveGlovoIntegrationSettings = async (
  _: unknown,
  { settings }: { settings: SaveGlovoIntegrationSettings },
  ctx: Context
): Promise<boolean> => {
  const {
    clients: { vbase },
    vtex: { logger },
  } = ctx

  if (!validateInputs(settings)) {
    return false
  }

  const validatedSettings: SaveGlovoIntegrationSettings = {
    ...settings,
    minimumStock: settings.minimumStock > 20 ? 20 : settings.minimumStock,
  }

  try {
    await vbase.saveJSON(GLOVO, APP_SETTINGS, validatedSettings)

    return true
  } catch (error) {
    logger.error({
      message: `Unable to save apps settings`,
      error: error.response,
    })

    return false
  }
}

```


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

[User] Show me the implementation of glovo-integration/node/resolvers/index.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { getGlovoIntegrationSettings } from './getAppSettings'
import { saveGlovoIntegrationSettings } from './saveAppSettings'
import {
  getMaxItemsForSimulation,
  updateMaxItemsForSimulation,
} from './maxItemsForSimulation'

const queries = {
  getGlovoIntegrationSettings,
  getMaxItemsForSimulation,
}

const mutations = {
  saveGlovoIntegrationSettings,
  updateMaxItemsForSimulation,
}

export const resolvers = {
  Query: {
    ...queries,
  },
  Mutation: {
    ...mutations,
  },
}

```


[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 glovo-integration/node/middlewares/events/updateGlovoOrderStatus.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  getStoreInfoFromStoreId,
  ServiceError,
  setGlovoStatus,
} from '../../utils'

export async function updateGlovoOrderStatus(ctx: StatusChangeContext) {
  const {
    body,
    clients: { glovo },
    state: { glovoToken, stores },
    vtex: { logger },
  } = ctx

  const { orderId, currentState } = body
  const [orderIdAffiliate, glovoOrderId] = orderId.split('-')

  const storeInfo = getStoreInfoFromStoreId(orderIdAffiliate, stores)

  const { glovoStoreId } = storeInfo
  const status = setGlovoStatus(currentState)

  if (!status) {
    logger.warn({
      message: `The status is required for order modification for order ${orderId}`,
      data: body,
    })

    return
  }

  const glovoPayload: GlovoUpdateOrderStatus = {
    glovoStoreId,
    glovoOrderId,
    status,
  }

  try {
    await glovo.updateOrderStatus(glovoPayload, glovoToken)

    logger.info({
      message: `Glovo order ${glovoPayload.glovoOrderId} status updated`,
      glovoPayload,
    })
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason:
        error.reason ??
        `Glovo order ${glovoPayload.glovoOrderId} status update failed`,
      metric: 'orders',
      data: {
        body,
      },
      error: error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/events/modifyGlovoOrder.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/naming-convention */
import { getChangedItems, isValidAffiliateId, ServiceError } from '../../utils'

export async function modifyGlovoOrder(
  ctx: StatusChangeContext,
  next: () => Promise<void>
) {
  const {
    body,
    clients: { glovo, orders, recordsManager },
    state: { glovoToken, stores },
    vtex: { logger },
  } = ctx

  const { orderId } = body
  const [orderIdAffiliate] = orderId.split('-')

  /**
   * Check if the order comes from Glovo and remove the affiliateId (i.e. 'TST') from the VTEX orderId to get the glovoOrderId.
   */
  if (!isValidAffiliateId(orderIdAffiliate, stores)) {
    return
  }

  try {
    let order: VTEXOrder

    try {
      order = await orders.getOrder(orderId)
    } catch (error) {
      throw new Error(`Error fetching order ${orderId}`)
    }

    if (!order.changesAttachment) {
      return
    }

    let orderRecord: OrderRecord

    try {
      orderRecord = await recordsManager.getOrderRecord(orderId)
    } catch (error) {
      throw new Error(`The record for the order ${orderId} was not found`)
    }

    const {
      glovoOrder: { order_id, store_id, products },
    } = orderRecord

    const {
      changesAttachment: { changesData },
    } = order

    const replacements: GlovoModifyReplacement[] = []
    const removedProducts: string[] = []
    const addedProducts: GlovoModifiedProduct[] = []
    const changedItems = getChangedItems(changesData)

    if (Object.keys(changedItems).length <= 0) {
      return
    }

    // Replacements and removed products
    for (const product of products) {
      const { id } = product

      if (!changedItems[id]) {
        continue
      }

      const updatedQuantity = product.quantity + changedItems[id].quantity

      if (updatedQuantity <= 0) {
        removedProducts.push(product.purchased_product_id)
      } else {
        replacements.push({
          purchased_product_id: product.purchased_product_id,
          product: {
            id,
            quantity: updatedQuantity,
            attributes: product.attributes,
          },
        })
      }
    }

    // Added products
    const itemsInOrder = products.map((product) => product.id)

    for (const [id, item] of Object.entries(changedItems)) {
      if (!itemsInOrder.includes(id)) {
        addedProducts.push({
          id,
          quantity: item.quantity,
          attributes: [],
        })
      }
    }

    const payload: GlovoModifyOrderPayload = {
      glovoStoreId: store_id,
      glovoOrderId: order_id,
      replacements,
      removed_purchases: removedProducts,
      added_products: addedProducts,
    }

    logger.info({
      message: `Glovo order ${order_id} payload for modification`,
      data: payload,
    })

    let modifiedGlovoOrder

    try {
      modifiedGlovoOrder = await glovo.modifyOrder(payload, glovoToken)
    } catch (error) {
      throw new Error(`Unable to modify Glovo order for order ${orderId}`)
    }

    const data: OrderRecord = {
      ...orderRecord,
      invoiced: order,
      hasChanged: true,
      invoicedAt: new Date().getTime(),
    }

    await recordsManager.saveOrderRecord(orderId, data)

    logger.info({
      message: `Glovo order ${order_id} was modified successfully`,
      data: modifiedGlovoOrder,
    })

    await next()
  } catch (error) {
    throw new ServiceError({
      message: `Order modification for order ${orderId} failed`,
      reason: error.message,
      metric: 'orders',
      data: { body },
    })
  }
}

```


[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 glovo-integration/node/middlewares/events/validateEventSettings.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { UserInputError } from '@vtex/api'
import type { GlovoIntegrationSettings } from 'vtex.glovo-integration'

import { APP_SETTINGS, GLOVO } from '../../constants'

export async function validateEventSettings(
  ctx: StatusChangeContext,
  next: () => Promise<void>
) {
  const {
    clients: { vbase },
  } = ctx

  const appSettings: GlovoIntegrationSettings = await vbase.getJSON(
    GLOVO,
    APP_SETTINGS,
    true
  )

  if (!appSettings.glovoToken) {
    throw new UserInputError(
      'Missing or invalid Glovo token. Please check app settings'
    )
  }

  ctx.state.stores = appSettings.stores
  ctx.state.glovoToken = appSettings.glovoToken

  await next()
}

```


[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 glovo-integration/node/middlewares/events/eventsErrorHandler.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export async function eventsErrorHandler(
  ctx: StatusChangeContext,
  next: () => Promise<void>
) {
  const {
    vtex: { logger },
  } = ctx

  try {
    await next()
  } catch (error) {
    logger.error({
      orderId: ctx.body.orderId ?? 'No orderId information',
      message: error.message,
      reason: error.reason,
      metric: error.metric,
      data: error.data,
      error: error.error,
    })

    ctx.body = error.reason
  }
}

```


[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 glovo-integration/node/middlewares/events/index.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { eventsErrorHandler } from './eventsErrorHandler'
import { modifyGlovoOrder } from './modifyGlovoOrder'
import { updateGlovoOrderStatus } from './updateGlovoOrderStatus'
import { validateEventSettings } from './validateEventSettings'

export const events = {
  updateGlovoOrder: [
    eventsErrorHandler,
    validateEventSettings,
    modifyGlovoOrder,
    updateGlovoOrderStatus,
  ],
}

```


[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 glovo-integration/node/middlewares/routes/glovoMenuCompleteUpdate.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { updateGlovoCompleteMenu } from '../../utils'

export async function glovoMenuCompleteUpdate(
  ctx: Context,
  next: () => Promise<void>
) {
  const {
    vtex: { logger },
  } = ctx

  // Send response to VTEX Notificator to avoid retries
  await next()

  try {
    updateGlovoCompleteMenu(ctx)
  } catch (error) {
    logger.error({
      message: `There was a problem updating the Glovo menu`,
      data: error.response?.data,
    })

    return error
  }
}

```


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

[User] Show me the implementation of glovo-integration/node/middlewares/routes/saveOrderRecord.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ServiceError } from '../../utils'

export async function saveOrderRecord(ctx: Context) {
  const {
    clients: { recordsManager },
    state: { glovoOrder, vtexOrder },
  } = ctx

  const { orderId } = vtexOrder
  const currentDate = new Date()
  const data: OrderRecord = {
    orderId,
    glovoOrder,
    invoiced: null,
    hasChanged: false,
    createdAt: currentDate.getTime(),
  }

  try {
    await recordsManager.saveOrderRecord(orderId, data)

    ctx.status = 201
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason: `Unable to save order record for order ${vtexOrder}`,
      metric: 'orders',
      data: { orderId, orderRecord: data },
      error: error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/routes/errorHandler.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export async function errorHandler(ctx: Context, next: () => Promise<void>) {
  const {
    vtex: { logger },
  } = ctx

  try {
    await next()
  } catch (error) {
    logger.error({
      glovoOrder: ctx.state?.glovoOrder ?? 'No Glovo order information',
      message: error.message,
      reason: error.reason,
      metric: error.metric,
      data: error.data,
      error: error.error,
    })

    ctx.body = error.reason
    ctx.app.emit('error', error, ctx)
  }
}

```


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

[User] Show me the implementation of glovo-integration/node/middlewares/routes/createOrder.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ServiceError, createVtexOrderData } from '../../utils'

export async function createOrder(ctx: Context, next: () => Promise<void>) {
  const {
    state: {
      glovoOrder,
      orderSimulation,
      storeInfo,
      marketplace,
      clientProfileData,
    },
    clients: { orders },
    vtex: { account },
  } = ctx

  const { salesChannel, affiliateId, sellerId } = storeInfo

  try {
    const vtexOrderData = createVtexOrderData(
      glovoOrder,
      orderSimulation,
      clientProfileData,
      marketplace,
      account
    )

    let createdOrder

    switch (marketplace && sellerId !== '1') {
      case true:
        createdOrder = await orders.createMarketplaceOrder(
          vtexOrderData,
          salesChannel,
          affiliateId
        )

        ctx.state.vtexOrder = createdOrder.orders[0]
        break

      default:
        createdOrder = await orders.createOrder(
          vtexOrderData,
          salesChannel,
          affiliateId
        )

        ctx.state.vtexOrder = createdOrder[0]
        break
    }

    await next()
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason:
        error.reason ??
        `Order creation for order Glovo Order ${glovoOrder.order_id} failed`,
      metric: 'orders',
      data: error.data ?? { glovoOrder },
      error: error.error ?? error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/routes/storeMenuUpdate.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ServiceError } from '../../utils'

export async function storeMenuUpdates(ctx: Context) {
  const {
    clients: { recordsManager },
    params: { affiliateId },
  } = ctx

  try {
    const storeMenuUpdatesRecord =
      await recordsManager.getStoreCompleteMenuUpdate(affiliateId)

    ctx.body = storeMenuUpdatesRecord ?? 'Store menu updates record not found'
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason:
        error.reason ??
        `There was a problem getting the Glovo menu updates for the specified store`,
      metric: 'menu',
      error: error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/routes/saveGlovoMenu.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { json } from 'co-body'
import type { GlovoIntegrationSettings, Store } from 'vtex.glovo-integration'

import { APP_SETTINGS, GLOVO } from '../../constants'
import { ServiceError } from '../../utils'

export async function saveGlovoMenu(ctx: Context) {
  const {
    clients: { vbase, recordsManager },
  } = ctx

  try {
    // Create/update Glovo Menu record
    const glovoMenuItems: number[] | string[] = await json(ctx.req)
    const glovoMenu: GlovoMenu = {}

    for (const skuId of glovoMenuItems) {
      glovoMenu[skuId] = true
    }

    await recordsManager.saveGlovoMenu(glovoMenu)

    // Create initial Store Menu Updates record
    const appSettings: GlovoIntegrationSettings = await vbase.getJSON(
      GLOVO,
      APP_SETTINGS,
      true
    )

    const { stores }: { stores: Store[] } = appSettings

    const newStores: StoreMenuUpdates[] = []

    for await (const store of stores) {
      const menuUpdatesRecord = await recordsManager.getStoreMenuUpdates(
        store.glovoStoreId
      )

      if (!menuUpdatesRecord) {
        const { id, storeName, glovoStoreId } = store
        const storeMenuUpdates: StoreMenuUpdates = {
          current: {
            responseId: null,
            createdAt: new Date().getTime(),
            storeId: id,
            storeName,
            glovoStoreId,
            items: [],
          },
        }

        newStores.push(storeMenuUpdates)
        await recordsManager.saveStoreMenuUpdates(
          glovoStoreId,
          storeMenuUpdates
        )
      }
    }

    ctx.status = 200
    ctx.body = { glovoMenu, newStores }
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason: `There was a problem saving the Glovo menu`,
      metric: 'menu',
      data: ctx.req,
      error: error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/routes/getGlovoMenuByStore.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ServiceError, generateStoreMenuRecord } from '../../utils'

export async function getGlovoMenuByStore(ctx: Context) {
  const {
    clients: { recordsManager },
    params: { affiliateId },
  } = ctx

  try {
    const storeMenuRecord = await recordsManager.getStoreMenuRecord(affiliateId)

    ctx.body = storeMenuRecord ?? 'Store menu record will be generated shortly'

    generateStoreMenuRecord(ctx, affiliateId)
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason:
        error.reason ??
        `There was a problem getting the Glovo menu for the specified store`,
      metric: 'menu',
      error: error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/routes/authorizeOrder.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { createAuthorizationPayload, ServiceError } from '../../utils'

export async function authorizeOrder(ctx: Context, next: () => Promise<void>) {
  const {
    state: { vtexOrder, storeInfo, marketplace, glovoOrder },
    clients: { orders },
    vtex: { logger },
  } = ctx

  const { salesChannel, affiliateId, sellerId } = storeInfo
  let orderIdentifier: string

  switch (marketplace) {
    case true:
      orderIdentifier = vtexOrder.orderId
      break

    default:
      // eslint-disable-next-line no-case-declarations
      const orderInfo = vtexOrder as VTEXOrder

      orderIdentifier = orderInfo.marketplaceOrderId
      break
  }

  const payload = createAuthorizationPayload(
    orderIdentifier,
    marketplace,
    glovoOrder
  )

  let order

  try {
    switch (marketplace && sellerId !== '1') {
      case true:
        order = await orders.authorizeMarketplaceOrder(
          payload as AuthorizeMarketplaceOrderPayload,
          orderIdentifier
        )
        break

      default:
        order = await orders.authorizeOrder(
          payload as AuthorizeOrderPayload,
          vtexOrder.orderId,
          salesChannel,
          affiliateId
        )
        break
    }

    logger.info({
      message: `Order ${orderIdentifier} has been placed.`,
      order,
    })

    ctx.state.vtexOrder = vtexOrder

    await next()
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason:
        error.reason ?? `Authorization for order ${orderIdentifier} failed`,
      metric: 'orders',
      data: { glovoOrder, vtexOrder },
      error: error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/routes/glovoProductUpdate.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { json } from 'co-body'

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

export async function glovoProductUpdate(
  ctx: Context,
  next: () => Promise<void>
) {
  const {
    vtex: { logger },
  } = ctx

  const catalogUpdate: CatalogChange = await json(ctx.req)

  // Send response to VTEX Notificator to avoid retries.
  await next()

  try {
    updateGlovoProduct(ctx, catalogUpdate)
  } catch (error) {
    logger.error({
      message: `There was a problem updating ${catalogUpdate.IdSku} for affiliate ${catalogUpdate.IdAffiliate}`,
      data: error.response?.data,
    })

    return error
  }
}

```


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

[User] Show me the implementation of glovo-integration/node/middlewares/routes/glovoMenuPartialUpdate.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { updateGlovoPartialMenu } from '../../utils'

export async function glovoMenuPartialUpdate(
  ctx: Context,
  next: () => Promise<void>
) {
  const {
    vtex: { logger },
  } = ctx

  // Send response to VTEX Notificator to avoid retries
  await next()

  try {
    updateGlovoPartialMenu(ctx)
  } catch (error) {
    logger.error({
      message: `There was a problem updating the Glovo menu`,
      data: error.response?.data,
    })

    return error
  }
}

```


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

[User] Show me the implementation of glovo-integration/node/middlewares/routes/getGlovoMenu.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ServiceError } from '../../utils'

export async function getGlovoMenu(ctx: Context) {
  const {
    clients: { recordsManager },
  } = ctx

  try {
    const glovoMenu = await recordsManager.getGlovoMenu()

    ctx.body = glovoMenu
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason: `There was a problem getting the Glovo menu`,
      metric: 'menu',
      error: error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/routes/index.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

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

import { authorizeOrder } from './authorizeOrder'
import { createOrder } from './createOrder'
import { errorHandler } from './errorHandler'
import { getGlovoMenu } from './getGlovoMenu'
import { getGlovoMenuByStore } from './getGlovoMenuByStore'
import { glovoMenuCompleteUpdate } from './glovoMenuCompleteUpdate'
import { glovoMenuPartialUpdate } from './glovoMenuPartialUpdate'
import { glovoProductUpdate } from './glovoProductUpdate'
import { orderChange } from './orderChange'
import { saveGlovoMenu } from './saveGlovoMenu'
import { saveOrderRecord } from './saveOrderRecord'
import { sendResponse } from './sendReponse'
import { simulateOrder } from './simulateOrder'
import { storeMenuUpdates } from './storeMenuUpdate'
import { validateGlovoToken } from './validateGlovoToken'
import { validateSettings } from './validateSettings'

export const routes = {
  createOrder: method({
    POST: [
      errorHandler,
      validateSettings,
      validateGlovoToken,
      simulateOrder,
      createOrder,
      authorizeOrder,
      saveOrderRecord,
    ],
  }),
  updateProduct: method({
    POST: [glovoProductUpdate, sendResponse],
  }),
  updateCompleteMenu: method({
    POST: [glovoMenuCompleteUpdate, sendResponse],
  }),
  updatePartialMenu: method({
    POST: [glovoMenuPartialUpdate, sendResponse],
  }),
  glovoMenu: method({
    GET: [errorHandler, getGlovoMenu],
    POST: [errorHandler, saveGlovoMenu],
  }),
  getGlovoMenuByStore: method({
    GET: [errorHandler, validateSettings, getGlovoMenuByStore],
  }),
  storeMenuUpdates: method({
    GET: [errorHandler, validateSettings, storeMenuUpdates],
  }),
  orderChange: method({
    POST: [errorHandler, orderChange],
  }),
}

```


[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 glovo-integration/node/middlewares/routes/validateGlovoToken.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

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

export async function validateGlovoToken(
  ctx: Context,
  next: () => Promise<void>
) {
  const {
    req: { headers },
    state: { glovoToken },
  } = ctx

  const reqAuth = headers.authorization

  if (!reqAuth || reqAuth !== glovoToken) {
    throw new AuthenticationError('Missing or invalid token')
  }

  await next()
}

```


[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 glovo-integration/node/middlewares/routes/validateSettings.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { UserInputError } from '@vtex/api'
import type { GlovoIntegrationSettings } from 'vtex.glovo-integration'

import { APP_SETTINGS, GLOVO } from '../../constants'

export async function validateSettings(
  ctx: Context,
  next: () => Promise<void>
) {
  const {
    clients: { vbase },
  } = ctx

  const appSettings: GlovoIntegrationSettings = await vbase.getJSON(
    GLOVO,
    APP_SETTINGS,
    true
  )

  if (!appSettings.glovoToken) {
    throw new UserInputError('Missing Glovo token. Please check app settings')
  }

  ctx.state.glovoToken = appSettings.glovoToken
  ctx.state.marketplace = appSettings.marketplace
  ctx.state.stores = appSettings.stores
  ctx.state.clientProfileData = appSettings.clientProfileData

  await next()
}

```


[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 glovo-integration/node/middlewares/routes/simulateOrder.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { json } from 'co-body'
import type { Store } from 'vtex.glovo-integration'

import {
  convertGlovoProductToItems,
  createSimulationPayload,
  getStoreInfoFormGlovoStoreId,
  ServiceError,
} from '../../utils'

export async function simulateOrder(ctx: Context, next: () => Promise<void>) {
  const {
    state: { stores },
    vtex: { logger },
    clients: { checkout },
  } = ctx

  const glovoOrder: GlovoOrder = await json(ctx.req)

  try {
    ctx.state.glovoOrder = glovoOrder

    logger.info({
      message: `Received order ${glovoOrder.order_id} from store ${glovoOrder.store_id} from Glovo`,
      glovoOrder,
    })

    const storeInfo = getStoreInfoFormGlovoStoreId(
      glovoOrder.store_id,
      stores
    ) as Store

    if (!storeInfo) {
      throw new Error(
        `Order not handled. Missing or invalid store with Glovo Store Id ${glovoOrder.store_id}`
      )
    }

    const { sellerId, salesChannel, affiliateId, postalCode, country } =
      storeInfo

    const simulationItems = convertGlovoProductToItems(
      sellerId,
      glovoOrder.products
    )

    const simulationPayload = createSimulationPayload({
      items: simulationItems,
      affiliateId,
      salesChannel,
      postalCode,
      country,
    })

    logger.info({
      message: `Simulation payload for order ${glovoOrder.order_id}`,
      simulationPayload,
    })

    const simulation = await checkout.simulation(...simulationPayload)

    if (!simulation.items.length) {
      throw new Error(
        `No items were returned from simulation for Glovo Order ${glovoOrder.order_id}`
      )
    }

    logger.info({
      message: `Simulation for order ${glovoOrder.order_id}`,
      simulation,
    })

    ctx.state.orderSimulation = simulation
    ctx.state.storeInfo = storeInfo

    await next()
  } catch (error) {
    throw new ServiceError({
      message: error.message ?? 'Order simulation error',
      reason:
        error.reason ??
        `Simulation failed for Glovo Order ${glovoOrder.order_id}`,
      metric: 'orders',
      data: error.data ?? glovoOrder,
      error: error.error ?? error.response?.data,
    })
  }
}

```


[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 glovo-integration/node/middlewares/routes/sendReponse.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export async function sendResponse(ctx: Context) {
  return (ctx.status = 204)
}

```


[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 glovo-integration/node/middlewares/routes/orderChange.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { json } from 'co-body'

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

export async function orderChange(ctx: Context) {
  const {
    vtex: {
      route: { params },
    },
  } = ctx

  try {
    const { orderId } = params
    const body: OrderChangeBody = await json(ctx.req)

    ctx.body = {
      orderId,
      receipt: body.requestId,
    }
  } catch (error) {
    throw new ServiceError({
      message: error.message,
      reason:
        error.reason ?? `There was a problem changing order ${params?.orderId}`,
      metric: 'orders',
      data: ctx.body,
      error: error.response?.data,
    })
  }
}

```


[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 glovo-integration/react/constants/index.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const GLOVO_TOKEN = 'glovoToken'
export const PRODUCTION = 'production'
export const MARKETPLACE = 'marketplace'
export const MINIMUM_STOCK = 'minimumStock'

export const STORE_NAME = 'storeName'
export const SELLER_ID = 'sellerId'
export const AFFILIATE_ID = 'affiliateId'
export const SALES_CHANNEL = 'salesChannel'
export const POSTAL_CODE = 'postalCode'
export const COUNTRY = 'country'
export const GLOVO_STORE_ID = 'glovoStoreId'

export const EMAIL = 'email'
export const FIRST_NAME = 'firstName'
export const LAST_NAME = 'lastName'
export const CORPORATE_NAME = 'corporateName'
export const DOCUMENT_TYPE = 'documentType'
export const DOCUMENT = 'document'
export const PHONE_NUMBER = 'phone'

export const TYPE_NEW = 'new'
export const TYPE_EDIT = 'edit'
export const DANGER = 'danger'
export const SUCCESS = 'success'
export const ERROR = 'error'

```


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

[User] Show me the implementation of glovo-integration/react/typings/settingsSections.d.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Dispatch, SetStateAction } from 'react'

export interface SettingsSection {
  settings: AppSettings
  setSettings: Dispatch<SetStateAction<AppSettings>>
  saveSettings: (settings: AppSettings) => Promise<boolean>
}

```


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

[User] Show me the implementation of glovo-integration/react/typings/files.d.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
declare module '*.gql'
declare module '*.css'
declare module '*.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 glovo-integration/react/typings/global.d.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'uuid'
declare module 'react-apollo'
declare module 'vtex.styleguide'
declare module 'vtex.native-types'

interface AppSettings {
  glovoToken: string
  production: boolean
  marketplace: boolean
  stores: StoreInfo[]
  clientProfileData: ClientProfileData
  minimumStock: number
}
interface StoreInfo {
  id: string
  storeName: string
  sellerId: string
  sellerName: string
  affiliateId: string
  salesChannel: string
  postalCode: string
  country: string
  glovoStoreId: string
}

interface ClientProfileData {
  email: string
  firstName: string
  lastName: string
  documentType: string
  document: string
  phone: string
  corporateName: string
}

interface Seller {
  id: string
  name: string
  isActive: boolean
}

interface TableItem {
  cellData: unknown
  rowData: StoreInfo
  updateCellMeasurements: () => void
}

type AlertType = 'success' | 'warning' | 'error'

interface SelectOption {
  label: string
  value: string
}

interface UpdateStore {
  storeId: string
  store: StoreInfo | null
  isOpen: boolean
  loading: boolean
}

type ModifyStore = Pick<UpdateStore, 'isOpen' | 'loading'>
type RemoveStore = Omit<UpdateStore, 'store'>
type AddOrEditStore = Omit<UpdateStore, 'storeId'>

```


[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 glovo-integration/react/typings/vtex.styleguide.d.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type PropTypes from 'prop-types'
import type React from 'react'
import 'vtex.styleguide'

declare const propTypes: any
declare const Progress: React.FC<PropTypes.InferProps<typeof propTypes>>

declare module 'vtex.styleguide' {
  export const Button: React.FC<PropTypes.InferProps<any>>
  export const Card: React.FC<PropTypes.InferProps<any>>
  export const Checkbox: React.FC<PropTypes.InferProps<any>>
  export const Input: React.FC<PropTypes.InferProps<any>>
  export const Tag: React.FC<PropTypes.InferProps<any>>
  export const Alert: React.FC<PropTypes.InferProps<any>>
  export const Layout: React.FC<PropTypes.InferProps<any>>
  export const PageHeader: React.FC<PropTypes.InferProps<any>>
  export const Dropdown: React.FC<PropTypes.InferProps<any>>
  export const ButtonWithIcon: React.FC<PropTypes.InferProps<any>>
  export const InputSearch: React.FC<PropTypes.InferProps<any>>
  export const Table: React.FC<PropTypes.InferProps<any>>
  export const Toggle: React.FC<PropTypes.InferProps<any>>
  export const PageBlock: React.FC<PropTypes.InferProps<any>>
  export const Tabs: React.FC<PropTypes.InferProps<any>>
  export const Tab: React.FC<PropTypes.InferProps<any>>
  export const Collapsible: React.FC<PropTypes.InferProps<any>>
  export const Textarea: React.FC<PropTypes.InferProps<any>>
  export const Spinner: React.FC<PropTypes.InferProps<any>>
  export const ModalDialog: React.FC<PropTypes.InferProps<any>>
  export const Modal: React.FC<PropTypes.InferProps<any>>
  /* eslint-disable @typescript-eslint/naming-convention */
  export const EXPERIMENTAL_Select: React.FC<PropTypes.InferProps<any>>
}

```


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

[User] Show me the implementation of glovo-integration/react/utils/countries.ts from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
// This is a list of the countries where Glovo operates currently

export type CountriesRecord = Record<string, string>

export const countriesMap: CountriesRecord = {
  ESP: 'Spain',
  PRT: 'Portugal',
  ITA: 'Italy',
  POL: 'Poland',
  HRV: 'Croatia',
  SRB: 'Serbia',
  KAZ: 'Kazakhstan',
  ROU: 'Romania',
  MAR: 'Morocco',
  GEO: 'Georgia',
  KEN: 'Kenya',
  CIV: 'Ivory Coast',
  UKR: 'Ukraine',
  MDA: 'Moldova',
  UGA: 'Uganda',
  KGZ: 'Kyrgyzstan',
  BIH: 'Bosnia & Herzegovina',
  GHA: 'Ghana',
  MNE: 'Montenegro',
  BGR: 'Bulgaria',
}

export const countriesOptions: CountriesRecord[] = Object.keys(
  countriesMap
).map((country) => ({
  value: country,
  label: countriesMap[country],
}))

```


[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 glovo-integration/react/graphql/getAppSettings.gql from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```graphql
query getGlovoIntegrationSettings {
  settings: getGlovoIntegrationSettings {
    glovoToken
    production
    marketplace
    minimumStock
    stores {
      id
      storeName
      sellerId
      sellerName
      affiliateId
      salesChannel
      postalCode
      country
      glovoStoreId
    }
    clientProfileData {
      email
      firstName
      lastName
      documentType
      document
      phone
      corporateName
    }
  }
}

```


[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 glovo-integration/react/graphql/saveAppSettings.gql from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation SaveGlovoIntegrationSettings($settings: SaveGlovoIntegrationSettings) {
  settingsUpdated: saveGlovoIntegrationSettings(settings: $settings)
}

```


[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 glovo-integration/react/graphql/getSellers.gql from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```graphql
query getSellers {
  sellers @context(provider: "vtex.sellers-graphql") {
    items {
      id
      name
      isActive
    }
  }
}

```


[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 glovo-integration/react/icons/IconGlovo.tsx from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

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

import logo from './GlovoLogo.png'

interface Props {
  size?: number
}

const IconGlovo: FC<Props> = ({ size = 80 }) => {
  return <img src={logo} style={{ width: `${size}px` }} alt="Glovo logo" />
}

export { IconGlovo }

```


[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 glovo-integration/react/components/admin/Stores.tsx from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, Fragment } from 'react'
import { FormattedMessage } from 'react-intl'
import { ButtonWithIcon, ModalDialog, IconPlusLines } from 'vtex.styleguide'

import { StoreModal } from './StoreModal'
import type { AlertProps } from './AlertBanner'
import { AlertBanner } from './AlertBanner'
import type { SettingsSection } from '../../typings/settingsSections'
import { StoreTable } from './StoresTable'

const Stores = ({ settings, saveSettings }: SettingsSection) => {
  const [addOrEditStore, setAddOrEditStore] = useState<AddOrEditStore>({
    isOpen: false,
    loading: false,
    store: null,
  })

  const [removeStore, setRemoveStore] = useState<RemoveStore>({
    isOpen: false,
    loading: false,
    storeId: '',
  })

  const [alert, setAlert] = useState<AlertProps>({
    show: false,
    type: 'success',
  })

  const handleOpenStoreModal = () => {
    setAddOrEditStore({
      ...addOrEditStore,
      isOpen: true,
      store: null,
    })
  }

  const handleCloseRemoveStoreDialog = () => {
    setRemoveStore({
      isOpen: false,
      loading: false,
      storeId: '',
    })
  }

  const updateSettings = async (stores: StoreInfo[]) => {
    const successfulUpdate = await saveSettings({
      ...settings,
      stores,
    })

    if (!successfulUpdate) {
      setAlert({
        show: true,
        type: 'error',
      })

      return
    }

    setAlert({
      show: true,
      type: 'success',
    })
  }

  const handleAddStore = async (store: StoreInfo) => {
    const updatedStores = [...settings.stores, store]

    updateSettings(updatedStores)

    setAddOrEditStore({
      ...addOrEditStore,
      isOpen: false,
    })
  }

  const handleEditStore = async (editedStore: StoreInfo) => {
    const updatedStores = settings.stores.map((store: StoreInfo) => {
      if (store.id === editedStore.id) {
        return editedStore
      }

      return store
    })

    updateSettings(updatedStores)

    setAddOrEditStore({
      isOpen: false,
      loading: false,
      store: null,
    })
  }

  const handleRemoveStore = async () => {
    setRemoveStore({
      ...removeStore,
      loading: true,
    })

    const updatedStores = settings.stores.filter(
      (store: StoreInfo) => store.id !== removeStore.storeId
    )

    if (updatedStores.length === settings.stores.length) {
      setAlert({
        show: true,
        type: 'error',
      })
    }

    updateSettings(updatedStores)

    setRemoveStore({
      isOpen: false,
      loading: false,
      storeId: '',
    })
  }

  return (
    <Fragment>
      <StoreModal
        isOpen={addOrEditStore.isOpen}
        setIsOpen={setAddOrEditStore}
        saving={addOrEditStore.loading}
        store={addOrEditStore.store}
        addStore={handleAddStore}
        editStore={handleEditStore}
      />

      <ModalDialog
        centered
        loading={removeStore.loading}
        isOpen={removeStore.isOpen}
        onClose={handleCloseRemoveStoreDialog}
        confirmation={{
          onClick: handleRemoveStore,
          label: (
            <FormattedMessage id="admin/glovo-integration.delete-store-dialog.confirmation" />
          ),
          isDangerous: true,
        }}
        cancelation={{
          onClick: handleCloseRemoveStoreDialog,
          label: (
            <FormattedMessage id="admin/glovo-integration.delete-store-dialog.cancel" />
          ),
        }}
      >
        <div>
          <p className="f3 f3-ns fw3 gray">
            <FormattedMessage id="admin/glovo-integration.delete-store-dialog.title" />
          </p>
          <p>
            <FormattedMessage id="admin/glovo-integration.delete-store-dialog.subtitle" />
          </p>
        </div>
      </ModalDialog>

      <AlertBanner show={alert.show} type={alert.type} onClose={setAlert} />

      <div>
        <div className="flex justify-between items-center">
          <div>
            <h2>
              <FormattedMessage id="admin/glovo-integration.table.title" />
            </h2>
            <p className="i mt0 mb7 gray">
              <FormattedMessage id="admin/glovo-integration.table.subtitle" />
            </p>
          </div>
          <ButtonWithIcon
            icon={<IconPlusLines />}
            onClick={handleOpenStoreModal}
          >
            <FormattedMessage id="admin/glovo-integration.add-more" />
          </ButtonWithIcon>
        </div>

        <StoreTable
          stores={settings.stores}
          addOrEditStore={addOrEditStore}
          setAddOrEditStore={setAddOrEditStore}
          removeStore={removeStore}
          setRemoveStore={setRemoveStore}
        />
      </div>
    </Fragment>
  )
}

export { Stores }

```


[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 glovo-integration/react/components/admin/StoresTable.tsx from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ChangeEvent, Dispatch, SetStateAction } from 'react'
import React, { useEffect, useState } from 'react'
import { Checkbox, Table } from 'vtex.styleguide'
import { FormattedMessage, useIntl } from 'react-intl'

import type { AddOrEditStore, RemoveStore } from './Stores'
import { countriesMap } from '../../utils'

interface Props {
  stores: StoreInfo[]
  addOrEditStore: AddOrEditStore
  setAddOrEditStore: Dispatch<SetStateAction<AddOrEditStore>>
  removeStore: RemoveStore
  setRemoveStore: Dispatch<SetStateAction<RemoveStore>>
}

type BooleanRecords = Record<string, boolean>

type FilterSelectorEvent = {
  error: any
  onChange: any
  value: any
}

type Statement = {
  subject: string
  verb: string
  object: any
}

const StoreTable = ({
  stores,
  addOrEditStore,
  setAddOrEditStore,
  removeStore,
  setRemoveStore,
}: Props) => {
  // Pagination state
  const [storesToDisplay, setStoresToDisplay] = useState<StoreInfo[]>([])
  const [tableLength, setTableLength] = useState(10)
  const [currentItemFrom, setCurrentItemFrom] = useState(1)
  const [currentItemTo, setCurrentItemTo] = useState(10)
  const [totalItems, setTotalItems] = useState(0)

  // Filters state
  const [filteredStores, setFilteredStores] = useState<StoreInfo[]>([])
  const [filterStatements, setFilterStatements] = useState<Statement[]>([])
  const [affiliatesFilters, setAffiliatesFilters] = useState<BooleanRecords>({})
  const [countriesFilters, setCountriesFilters] = useState<BooleanRecords>({})

  const schema = {
    properties: {
      storeName: {
        title: <FormattedMessage id="admin/glovo-integration.store-name" />,
      },
      sellerName: {
        title: <FormattedMessage id="admin/glovo-integration.seller-name" />,
        width: 300,
      },
      affiliateId: {
        title: <FormattedMessage id="admin/glovo-integration.affiliate-id" />,
      },
      salesChannel: {
        title: <FormattedMessage id="admin/glovo-integration.sales-channel" />,
      },
      glovoStoreId: {
        title: <FormattedMessage id="admin/glovo-integration.glovo-store-id" />,
      },
      postalCode: {
        title: <FormattedMessage id="admin/glovo-integration.postal-code" />,
      },
      country: {
        title: <FormattedMessage id="admin/glovo-integration.country" />,
        cellRenderer: ({ cellData }: TableItem) =>
          countriesMap[cellData as string],
      },
    },
  }

  const lineActions = [
    {
      label: () => (
        <FormattedMessage id="admin/glovo-integration.table.edit-store" />
      ),
      onClick: ({ rowData: store }: TableItem) => {
        setAddOrEditStore({
          ...addOrEditStore,
          isOpen: true,
          store,
        })
      },
    },
    {
      label: () => (
        <FormattedMessage id="admin/glovo-integration.table.remove-store" />
      ),
      isDangerous: true,
      onClick: ({ rowData: { id } }: TableItem) => {
        setRemoveStore({
          ...removeStore,
          isOpen: true,
          storeId: id,
        })
      },
    },
  ]

  const intl = useIntl()

  // Pagination
  useEffect(() => {
    setFilteredStores(stores)
    setStoresToDisplay(stores.slice(currentItemFrom - 1, currentItemTo))
    setTotalItems(stores.length)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [stores])

  const setTablePage = (
    from: number,
    to: number,
    storesInPage: StoreInfo[]
  ) => {
    setCurrentItemFrom(from)
    setCurrentItemTo(to)
    setStoresToDisplay(storesInPage.slice(from - 1, to))
  }

  const handleNextClick = () => {
    const from = currentItemFrom + tableLength
    const to = currentItemTo + tableLength

    setTablePage(from, to, filteredStores)
  }

  const handlePrevClick = () => {
    let from = currentItemFrom - tableLength
    let to = currentItemTo - tableLength

    if (from < 0) {
      from = 1
      to = tableLength
    }

    setTablePage(from, to, filteredStores)
  }

  const handleRowsChange = (_: ChangeEvent, value: string) => {
    const to = currentItemFrom - 1 + parseInt(value, 10)

    setTableLength(parseInt(value, 10))
    setCurrentItemTo(to)

    setTablePage(currentItemFrom, to, filteredStores)
  }

  // Filters
  useEffect(() => {
    const affiliatesForFilters: BooleanRecords = {}
    const countriesForFilters: BooleanRecords = {}

    for (const store of stores) {
      const { affiliateId, country } = store

      if (!(affiliateId in affiliatesForFilters)) {
        affiliatesForFilters[affiliateId] = true
      }

      if (!(country in countriesForFilters)) {
        countriesForFilters[country] = true
      }
    }

    setAffiliatesFilters(affiliatesForFilters)
    setCountriesFilters(countriesForFilters)
  }, [stores])

  const handleFiltersChange = async (statements: Statement[] = []) => {
    let storesToFilter: StoreInfo[] = stores.slice()

    statements.forEach((statement) => {
      if (statement) {
        switch (statement.subject) {
          case 'affiliate':
            if (!statement.object) return

            storesToFilter = storesToFilter.filter(
              (store) => statement.object[store.affiliateId]
            )
            break

          case 'country':
            if (!statement.object) return

            storesToFilter = storesToFilter.filter(
              (store) => statement.object[store.country]
            )
            break

          default:
            break
        }
      }
    })

    setFilteredStores(storesToFilter)
    setFilterStatements(statements)
    setTotalItems(storesToFilter.length)
    setTablePage(1, tableLength, storesToFilter)
  }

  const renderFilterLabel = (statement: Statement) => {
    if (!statement) {
      return intl.formatMessage({ id: 'admin/glovo-integration.table.all' })
    }

    const { object } = statement

    const keys = Object.keys(object) ?? []
    const isAllTrue = !keys.some((key) => !object[key])
    const isAllFalse = !keys.some((key) => object[key])
    const trueKeys = keys.filter((key) => object[key])

    let trueKeysLabel = ''

    trueKeys.forEach((key, i) => {
      trueKeysLabel += `${key}${i === trueKeys.length - 1 ? '' : ', '}`
    })

    return `${
      isAllTrue
        ? intl.formatMessage({ id: 'admin/glovo-integration.table.all' })
        : isAllFalse
        ? intl.formatMessage({ id: 'admin/glovo-integration.table.none' })
        : `${trueKeysLabel}`
    }`
  }

  const toggleCheckbox = (values: any, defaultValues: any, key: string) => {
    const updatedValues = {
      ...(values ?? defaultValues),
      [key]: values ? !values[key] : false,
    }

    return updatedValues
  }

  const affiliatesSelector = ({ onChange, value }: FilterSelectorEvent) => {
    return (
      <div>
        {Object.keys(affiliatesFilters).map((affiliate, i) => (
          <div className="mb2 ml2" key={`${affiliate}-${i}`}>
            <Checkbox
              checked={value ? value[affiliate] : affiliatesFilters[affiliate]}
              label={affiliate}
              onChange={() => {
                const updated = toggleCheckbox(
                  value,
                  affiliatesFilters,
                  affiliate
                )

                onChange(updated)
              }}
              value={affiliate}
            />
          </div>
        ))}
      </div>
    )
  }

  const countriesSelector = ({ onChange, value }: FilterSelectorEvent) => {
    return (
      <div>
        {Object.keys(countriesFilters).map((country, i) => (
          <div className="mb2 ml2" key={`${countriesMap[country]}-${i}`}>
            <Checkbox
              checked={value ? value[country] : countriesMap[country]}
              label={countriesMap[country]}
              onChange={() => {
                const updated = toggleCheckbox(value, countriesFilters, country)

                onChange(updated)
              }}
              value={country}
            />
          </div>
        ))}
      </div>
    )
  }

  return (
    <Table
      items={storesToDisplay}
      schema={schema}
      lineActions={lineActions}
      fullWidth
      filters={{
        statements: filterStatements,
        onChangeStatements: handleFiltersChange,
        moreOptionsLabel: (
          <FormattedMessage id="admin/glovo-integration.table.more-options" />
        ),
        alwaysVisibleFilters: ['affiliate', 'country'],
        clearAllFiltersButtonLabel: (
          <FormattedMessage id="admin/glovo-integration.table.clear-all" />
        ),
        submitFilterLabel: (
          <FormattedMessage id="admin/glovo-integration.table.apply" />
        ),
        options: {
          affiliate: {
            label: intl.formatMessage({
              id: 'admin/glovo-integration.affiliate-id',
            }),
            renderFilterLabel,
            verbs: [
              {
                value: '=',
                object: affiliatesSelector,
              },
            ],
          },
          country: {
            label: intl.formatMessage({
              id: 'admin/glovo-integration.country',
            }),
            renderFilterLabel,
            verbs: [
              {
                value: '=',
                object: countriesSelector,
              },
            ],
          },
        },
      }}
      pagination={{
        currentItemFrom,
        currentItemTo,
        totalItems,
        onNextClick: handleNextClick,
        onPrevClick: handlePrevClick,
        onRowsChange: handleRowsChange,
        textOf: <FormattedMessage id="admin/glovo-integration.table.of" />,
        textShowRows: (
          <FormattedMessage id="admin/glovo-integration.table.show-rows" />
        ),
        rowsOptions: [10, 25],
      }}
      emptyStateLabel=""
      emptyStateChildren={
        <div className="">
          <h4 className="t-heading-4 pt8">
            <FormattedMessage id="admin/glovo-integration.table.empty" />
          </h4>
        </div>
      }
    />
  )
}

export { StoreTable }

```


[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 glovo-integration/react/components/admin/Configuration.tsx from the glovo-integration VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable padding-line-between-statements */
import type { ChangeEvent } from 'react'
import React, { Fragment, useState } from 'react'
import { FormattedMessage } from 'react-intl'
import { Button, Input, InputPassword, Toggle } from 'vtex.styleguide'

import {
  CORPORATE_NAME,
  DOCUMENT,
  DOCUMENT_TYPE,
  EMAIL,
  FIRST_NAME,
  GLOVO_TOKEN,
  LAST_NAME,
  MARKETPLACE,
  MINIMUM_STOCK,
  PHONE_NUMBER,
  PRODUCTION,
} from '../../constants'
import { validateInputs } from '../../../common/utils'
import type { AlertProps } from './AlertBanner'
import { AlertBanner } from './AlertBanner'
import type { SettingsSection } from '../../typings/settingsSections'

const Configuration = ({
  settings,
  setSettings,
  saveSettings,
}: SettingsSection) => {
  const [error, setError] = useState(false)
  const [savingSettings, setSavingSettings] = useState(false)
  const [alert, setAlert] = useState<AlertProps>({
    show: false,
    type: 'success',
  })

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    switch (e.target.id) {
      case GLOVO_TOKEN: {
        setSettings({
          ...settings,
          glovoToken: e.target.value,
        })

        return
      }

      case MINIMUM_STOCK: {
        setSettings({
          ...settings,
          minimumStock: Number(e.target.value),
        })

        return
      }
      case PRODUCTION:
      case MARKETPLACE: {
        setSettings({
          ...settings,
          [e.target.id]: !settings[e.target.id],
        })

        return
      }

      default: {
        setSettings({
          ...settings,
          clientProfileData: {
            ...settings.clientProfileData,
            [e.target.id]: e.target.value,
          },
        })
      }
    }
  }

  const handleSaveSettings = async () => {
    setSavingSettings(true)

    const validation = validateInputs({
      glovoToken: settings.glovoToken,
      clientProfileData: settings.clientProfileData,
    })

    if (!validation) {
      setError(true)
      setSavingSettings(false)

      return
    }

    setError(false)

    try {
      const succesfulUpdate = await saveSettings(settings)

      if (!succesfulUpdate) {
        throw new Error('Unable to save settings.')
      }

      setAlert({
        show: true,
        type: 'success',
      })
    } catch (err) {
      console.error(err)

      setAlert({
        show: true,
        type: 'error',
      })
    }

    setSavingSettings(false)
  }

  return (
    <Fragment>
      <AlertBanner show={alert.show} type={alert.type} onClose={setAlert} />
      {/* Integration settings */}
      <div>
        <h2>
          <FormattedMessage id="admin/glovo-integration.integration-settings.title" />
        </h2>
        <p className="i mt0 mb7 gray">
          <FormattedMessage id="admin/glovo-integration.integration-settings.subtitle" />
        </p>

        <div className="mt4">
          <p>
            <FormattedMessage id="admin/glovo-integration.integration-settings.glovo-token" />
          </p>
          <InputPassword
            id={GLOVO_TOKEN}
            value={settings.glovoToken}
            label={
              <FormattedMessage id="admin/glovo-integration.integration-settings.glovo-token.description" />
            }
            onChange={handleChange}
            errorMessage={
              error &&
              !settings.glovoToken && (
                <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
              )
            }
          />
        </div>
        <div className="mt4">
          <p>
            <FormattedMessage id="admin/glovo-integration.integration-settings.minimumStock.label" />
          </p>
          <Input
            id={MINIMUM_STOCK}
            value={settings.minimumStock}
            label={
              <FormattedMessage id="admin/glovo-integration.integration-settings.minimumStock.description" />
            }
            type="number"
            onChange={handleChange}
            errorMessage={
              error &&
              !settings.minimumStock && (
                <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
              )
            }
          />
        </div>
        <div className="mt7">
          <p className="i mt0 gray">
            <FormattedMessage id="admin/glovo-integration.integration-settings.marketplace.description" />
          </p>
          <Toggle
            checked={settings.marketplace}
            id={MARKETPLACE}
            label={
              <FormattedMessage id="admin/glovo-integration.integration-settings.marketplace.label" />
            }
            name="marketplace"
            value={settings.marketplace}
            onChange={handleChange}
          />
        </div>
        <div className="mt7">
          <p className="i mt0 gray">
            <FormattedMessage id="admin/glovo-integration.integration-settings.production.description" />
          </p>
          <Toggle
            checked={settings.production}
            id={PRODUCTION}
            label={
              <FormattedMessage id="admin/glovo-integration.integration-settings.production.label" />
            }
            name="production"
            value={settings.production}
            onChange={handleChange}
            semantic
          />
        </div>
      </div>

      {/* Client information */}
      <div className="mt8">
        <h2>
          <FormattedMessage id="admin/glovo-integration.client-info.title" />
        </h2>
        <p className="i mt0 mb7 gray">
          <FormattedMessage id="admin/glovo-integration.client-info.subtitle" />
        </p>
        <div className="mt4">
          <Input
            id={EMAIL}
            value={settings.clientProfileData.email}
            label={
              <FormattedMessage id="admin/glovo-integration.client-info.email" />
            }
            onChange={handleChange}
            errorMessage={
              error &&
              !settings.clientProfileData.email && (
                <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
              )
            }
          />
          <div className="flex">
            <div className="w-50 pr5 pt5">
              <Input
                id={FIRST_NAME}
                value={settings.clientProfileData.firstName}
                label={
                  <FormattedMessage id="admin/glovo-integration.client-info.first-name" />
                }
                onChange={handleChange}
                errorMessage={
                  error &&
                  !settings.clientProfileData.firstName && (
                    <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
                  )
                }
              />
            </div>
            <div className="w-50 pt5">
              <Input
                id={LAST_NAME}
                value={settings.clientProfileData.lastName}
                label={
                  <FormattedMessage id="admin/glovo-integration.client-info.last-name" />
                }
                onChange={handleChange}
                errorMessage={
                  error &&
                  !settings.clientProfileData.lastName && (
                    <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
                  )
                }
              />
            </div>
          </div>
          <div className="flex">
            <div className="w-50 pr5 pt5">
              <Input
                id={DOCUMENT_TYPE}
                value={settings.clientProfileData.documentType}
                label={
                  <FormattedMessage id="admin/glovo-integration.client-info.document-type" />
                }
                onChange={handleChange}
                errorMessage={
                  error &&
                  !settings.clientProfileData.documentType && (
                    <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
                  )
                }
              />
            </div>
            <div className="w-50 pt5">
              <Input
                id={DOCUMENT}
                value={settings.clientProfileData.document}
                label={
                  <FormattedMessage id="admin/glovo-integration.client-info.document" />
                }
                onChange={handleChange}
                errorMessage={
                  error &&
                  !settings.clientProfileData.document && (
                    <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
                  )
                }
              />
            </div>
          </div>
          <div className="pt5">
            <Input
              id={PHONE_NUMBER}
              value={settings.clientProfileData.phone}
              label={
                <FormattedMessage id="admin/glovo-integration.client-info.phone-number" />
              }
              onChange={handleChange}
              errorMessage={
                error &&
                !settings.clientProfileData.phone && (
                  <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
                )
              }
            />
          </div>
          <div className="pt5">
            <Input
              id={CORPORATE_NAME}
              value={settings.clientProfileData.corporateName}
              label={
                <FormattedMessage id="admin/glovo-integration.client-info.corporate-name" />
              }
              onChange={handleChange}
              errorMessage={
                error &&
                !settings.clientProfileData.corporateName && (
                  <FormattedMessage id="admin/glovo-integration.inputs.error-message" />
                )
              }
            />
          </div>
        </div>
        <div className="flex justify-end mt8">
          <Button isLoading={savingSettings} onClick={handleSaveSettings} block>
            {<FormattedMessage id="admin/glovo-integration.save" />}
          </Button>
        </div>
      </div>
    </Fragment>
  )
}

export { Configuration }

```