[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 b2b-organizations/react/services/getBulkImportDetails.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import bulkImportClient from '.'
import type { ImportDetails, ImportReportData } from '../types/BulkImport'

export type BulkImportDetails = Omit<ImportDetails, 'percentage'> & {
  importReportList: ImportReportData[]
  percentage: number
}

const getBulkImportDetails = async (
  importId?: string
): Promise<BulkImportDetails> => {
  const importListResponse = await bulkImportClient.get<ImportDetails>(
    `/buyer-orgs/${importId}`
  )

  const { data } = importListResponse

  const { importResult } = data

  const importList = importResult?.imports ?? []

  const [totalSuccess, totalError] = importList.reduce(
    ([successAcc, errorAcc], { importedRows, rowsWithError }) => {
      return [successAcc + importedRows, errorAcc + rowsWithError]
    },
    [0, 0] as [number, number]
  )

  const percentage = (totalSuccess * 100) / (totalSuccess + totalError)

  const fullPercentage = Math.round((percentage + Number.EPSILON) * 100) / 100

  return {
    ...data,
    importReportList: importList.map(
      ({ importedRows, rowsWithError, name }) => ({
        title: name,
        success: {
          percentage: fullPercentage,
          imports: importedRows,
        },
        error: {
          percentage: 100 - fullPercentage,
          imports: rowsWithError,
        },
      })
    ),
    percentage: fullPercentage,
  }
}

export default getBulkImportDetails

```


[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 b2b-organizations/react/services/validateBulkImport.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import bulkImportClient from '.'

const validateBulkImport = async (importId?: string): Promise<unknown> => {
  return bulkImportClient.post(`/buyer-orgs/validate/${importId}`)
}

export default validateBulkImport

```


[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 b2b-organizations/react/services/index.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
export { default } from './bulkImportClient'
export { default as getBulkImportList } from './getBulkImportList'
export { default as getBulkImportDetails } from './getBulkImportDetails'
export { default as uploadBulkImportFile } from './uploadBulkImportFile'
export { default as startBulkImport } from './startBulkImport'
export { default as validateBulkImport } from './validateBulkImport'
export { default as checkUserAdminPermission } from './orgPermissionClient'

```


[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 b2b-organizations/react/services/startBulkImport.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ImportDetails } from '../types/BulkImport'
import bulkImportClient from '.'

const startBulkImport = async (importId: string) => {
  const startImportResponse = await bulkImportClient.post(
    `/buyer-orgs/${importId}`
  )

  return startImportResponse.data as ImportDetails
}

export default startBulkImport

```


[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 b2b-organizations/react/typings/vtex.my-account-commons/Router.d.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.my-account-commons/Router' {
  export const Link
  export const withRouter
  export const Route
  export const Switch
  export const Redirect
  export const HashRouter
}

```


[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 b2b-organizations/react/typings/vtex.my-account-commons/index.d.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.my-account-commons' {
  export const BaseLoading
  export const SkeletonPiece
  export const SkeletonBox
  export const ContentWrapper
  export const GenericError
}

```


[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 b2b-organizations/react/admin/OrganizationSettings/index.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable vtex/prefer-early-return */
import type { FunctionComponent } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { useQuery, useMutation } from 'react-apollo'
import { PageBlock, Table, IconCheck, Button, Checkbox } from 'vtex.styleguide'
import { Spinner, useToast } from '@vtex/admin-ui'

import { organizationSettingsMessages as messages } from '../utils/messages'
import { organizationBulkAction } from '../utils/organizationBulkAction'
import GET_SALES_CHANNELS from '../../graphql/getSalesChannels.graphql'
import SELECTED_SALES_CHANNELS from '../../graphql/getSelectedChannels.graphql'
import UPDATE_SALES_CHANNELS from '../../graphql/updateSalesChannels.graphql'
import UPDATE_B2B_SETTINGS from '../../graphql/updateB2BSettings.graphql'
import GET_B2B_SETTINGS from '../../graphql/getB2BSettings.graphql'
import { TopbarCustom } from './TopbarCustom'
import { useOrgPermission } from '../../hooks/useOrgPermission'
import { ORGANIZATION_EDIT } from '../../utils/constants'

interface SalesChannel {
  channelId: string
  name: string
  tableName: string
}

interface CellRendererProps<RowType> {
  rowData: RowType
  updateCellMeasurements: () => void
}

const OrganizationSettings: FunctionComponent = () => {
  const defaultSchema = {
    properties: {
      tableName: {
        title: 'Sales Channels',
      },
    },
  }

  const showToast = useToast()
  const { formatMessage } = useIntl()
  const [salesChannels, setSalesChannels] = useState([] as SalesChannel[])
  const [selectedChannels, setSelectedChannel] = useState([] as SalesChannel[])
  const [loading, setLoading] = useState(false)
  const [settings, setSettings] = useState({
    autoApprove: false,
    businessReadOnly: false,
    stateReadOnly: false,
    uiSettings: {
      clearCart: false,
      showModal: false,
      fullImpersonation: false,
    },
  })

  const [warningTopbarColor, setWarningTopbarColor] = useState({
    color: { hex: '#141E7A' },
    history: [],
  })

  const [warningTopbarMessage, setWarningTopbarMessage] = useState('')

  const { data } = useQuery(GET_SALES_CHANNELS, {
    ssr: false,
  })

  const { data: selectedData, refetch } = useQuery(SELECTED_SALES_CHANNELS, {
    ssr: false,
  })

  const {
    data: dataSettings,
    loading: loadingSettings,
    refetch: refetchSettings,
  } = useQuery(GET_B2B_SETTINGS, {
    ssr: false,
  })

  const [updateSalesChannels] = useMutation(UPDATE_SALES_CHANNELS)
  const [updateB2BSettings] = useMutation(UPDATE_B2B_SETTINGS)

  const { data: canEditBuyerOrg } = useOrgPermission({
    resourceCode: ORGANIZATION_EDIT,
  })

  useEffect(() => {
    if (!data) return

    const options: SalesChannel[] = []

    data.salesChannels.forEach((item: { id: string; name: string }) => {
      options.push({
        channelId: item.id,
        name: item.name,
        tableName: `${item.name} (${item.id})`,
      })
    })
    setSalesChannels(options)
  }, [data])

  useEffect(() => {
    if (!selectedData) return

    const selectedOptions: any[] = []

    selectedData.getSalesChannels.forEach(
      (item: { id: string; name: string }) => {
        selectedOptions.push({
          channelId: item.id,
          name: item.name,
          tableName: `${item.name} (${item.id})`,
        })
      }
    )
    setSelectedChannel(selectedOptions)
  }, [selectedData])

  useEffect(() => {
    if (!dataSettings) {
      return
    }

    const { getB2BSettings } = dataSettings

    setSettings({
      autoApprove: getB2BSettings?.autoApprove,
      businessReadOnly: getB2BSettings?.businessReadOnly,
      stateReadOnly: getB2BSettings?.stateReadOnly,
      uiSettings: {
        clearCart: getB2BSettings?.uiSettings?.clearCart,
        showModal: getB2BSettings?.uiSettings?.showModal,
        fullImpersonation: getB2BSettings?.uiSettings?.fullImpersonation,
      },
    })

    setWarningTopbarColor({
      color: {
        hex: getB2BSettings?.uiSettings?.topBar?.hexColor ?? '#656896',
      },
      history: [],
    })
    setWarningTopbarMessage(getB2BSettings?.uiSettings?.topBar?.name ?? '')
  }, [dataSettings])

  const getSchema = () => {
    const cellRenderer = ({
      rowData: { channelId, name, tableName },
    }: CellRendererProps<SalesChannel>) => {
      const assigned = selectedChannels.some(
        channel => channel.channelId === channelId
      )

      return (
        <span className={assigned ? 'c-disabled' : ''}>
          {tableName ?? name}
          {assigned && <IconCheck />}
        </span>
      )
    }

    return {
      properties: {
        name: {
          title: 'Sales Channels',
          ...{ cellRenderer },
        },
      },
    }
  }

  const handleUpdateRequest = () => {
    const channels = selectedChannels.map((item: any) => {
      return {
        id: item.channelId,
        name: item.name,
      }
    })

    setLoading(true)

    const promises = []

    promises.push(
      updateSalesChannels({
        variables: {
          channels,
        },
      }).catch(error => {
        console.error(error)
        showToast({
          variant: 'critical',
          message: formatMessage(messages.toastUpdateFailure),
        })
      })
    )
    promises.push(
      updateB2BSettings({
        variables: {
          input: {
            ...settings,
            uiSettings: {
              ...settings.uiSettings,
              topBar: {
                name: warningTopbarMessage,
                hexColor: warningTopbarColor.color.hex,
              },
            },
          },
        },
      })
    )

    Promise.all(promises)
      .then(() => {
        showToast({
          variant: 'positive',
          message: formatMessage(messages.toastUpdateSuccess),
        })
        refetch()
        refetchSettings()
        setLoading(false)
      })
      .catch(error => {
        console.error(error)
        showToast({
          variant: 'critical',
          message: formatMessage(messages.toastUpdateFailure),
        })
      })
  }

  const handleAddBinding = (rowParams: any) => {
    const { selectedRows = [] } = rowParams
    const newBindings = [] as SalesChannel[]

    selectedRows.forEach((row: any) => {
      if (
        !selectedChannels.some(channel => channel.channelId === row.channelId)
      ) {
        newBindings.push({
          channelId: row.channelId,
          name: row.name,
          tableName: `${row.name} (${row.channelId})`,
        })
      }
    })

    setSelectedChannel([...selectedChannels, ...newBindings])
  }

  const handleRemoveBinding = (rowParams: any) => {
    const { selectedRows = [] } = rowParams
    const bindingToRemove = [] as string[]

    selectedRows.forEach((row: any) => {
      bindingToRemove.push(row.channelId)
    })

    const newBindingList = selectedChannels.filter(
      channel => !bindingToRemove.includes(channel.channelId)
    )

    setSelectedChannel(newBindingList)
  }

  const handleChangeWarningTopbarColor = useCallback(
    (color: { hex: string }) => {
      setWarningTopbarColor(prevState => ({
        ...prevState,
        color,
      }))
    },
    []
  )

  const handleChangeWarningTopbarMessage = useCallback((text: string) => {
    setWarningTopbarMessage(text)
  }, [])

  return (
    <PageBlock
      title={formatMessage(messages.tablePageTitle)}
      titleAside={
        <span className="mt4 flex justify-end mb4">
          <Button
            isLoading={loading}
            variation="primary"
            onClick={() => handleUpdateRequest()}
            disabled={!canEditBuyerOrg}
          >
            <FormattedMessage id="admin/b2b-organizations.costCenter-details.button.save" />
          </Button>
        </span>
      }
    >
      <div>
        <h3 className="t-heading-3">
          {formatMessage(messages.generalSettings)}
        </h3>
        <div className="pa6">
          <div className="mb4">
            <Checkbox
              checked={settings.autoApprove}
              id="autoApprove"
              name="autoApprove"
              onChange={() => {
                setSettings({ ...settings, autoApprove: !settings.autoApprove })
              }}
              label={formatMessage(messages.autoApprove)}
            />
          </div>
          {loadingSettings ? (
            <Spinner />
          ) : (
            <>
              {!settings.autoApprove && (
                <TopbarCustom
                  colorState={warningTopbarColor}
                  message={warningTopbarMessage}
                  onChangeColor={handleChangeWarningTopbarColor}
                  onChangeMessage={handleChangeWarningTopbarMessage}
                />
              )}
            </>
          )}

          <div className="mb4">
            <Checkbox
              checked={settings.businessReadOnly}
              id="businessReadOnly"
              name="businessReadOnly"
              onChange={() => {
                setSettings({
                  ...settings,
                  businessReadOnly: !settings.businessReadOnly,
                })
              }}
              label={formatMessage(messages.businessReadOnly)}
            />
          </div>
          <div className="mb4">
            <Checkbox
              checked={settings.stateReadOnly}
              id="stateReadOnly"
              name="stateReadOnly"
              onChange={() => {
                setSettings({
                  ...settings,
                  stateReadOnly: !settings.stateReadOnly,
                })
              }}
              label={formatMessage(messages.stateReadOnly)}
            />
          </div>
          <div className="mb4">
            <Checkbox
              checked={settings.uiSettings.showModal}
              id="showModal"
              name="showModal"
              onChange={() => {
                setSettings({
                  ...settings,
                  uiSettings: {
                    ...settings.uiSettings,
                    showModal: !settings.uiSettings.showModal,
                  },
                })
              }}
              label={formatMessage(messages.showModal)}
            />
          </div>
          <div className="mb4">
            <Checkbox
              checked={settings.uiSettings.clearCart}
              id="clearCart"
              name="clearCart"
              onChange={() => {
                setSettings({
                  ...settings,
                  uiSettings: {
                    ...settings.uiSettings,
                    clearCart: !settings.uiSettings.clearCart,
                  },
                })
              }}
              label={formatMessage(messages.clearCart)}
            />
          </div>
          <div className="mb6">
            <Checkbox
              checked={settings.uiSettings.fullImpersonation}
              id="fullImpersonation"
              name="fullImpersonation"
              onChange={() => {
                setSettings({
                  ...settings,
                  uiSettings: {
                    ...settings.uiSettings,
                    fullImpersonation: !settings.uiSettings.fullImpersonation,
                  },
                })
              }}
              label={formatMessage(messages.fullImpersonation)}
            />
          </div>
        </div>
      </div>
      <div className="w-100 mt4 mb4">
        <h3 className="t-heading-3">{formatMessage(messages.bindingTitle)}</h3>
      </div>
      <div className="flex">
        <div className="w-50">
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-settings-select.binding.selected" />
          </h4>
          {selectedChannels ? (
            <Table
              fullWidth
              schema={defaultSchema}
              items={selectedChannels}
              bulkActions={organizationBulkAction(
                handleRemoveBinding,
                messages.removeFromBinding,
                formatMessage
              )}
            />
          ) : null}
        </div>
        <div className="w-50">
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-settings-select.binding.available" />
          </h4>
          <Table
            fullWidth
            schema={getSchema()}
            items={salesChannels}
            bulkActions={organizationBulkAction(
              handleAddBinding,
              messages.addToBinding,
              formatMessage
            )}
          />
        </div>
      </div>
    </PageBlock>
  )
}

export default OrganizationSettings

```


[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 b2b-organizations/react/admin/OrganizationSettings/TopbarCustom.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ChangeEvent } from 'react'
import React from 'react'
import { useIntl, FormattedMessage } from 'react-intl'
import { Box, ColorPicker, Textarea } from 'vtex.styleguide'

import { organizationSettingsMessages } from '../utils/messages'

type Color = { hex: string }

interface TopbarCustomProps {
  colorState: {
    color: Color
    history: string[]
  }
  message: string
  onChangeColor: (color: Color) => void
  onChangeMessage: (text: string) => void
}

export function TopbarCustom({
  colorState,
  message,
  onChangeColor,
  onChangeMessage,
}: TopbarCustomProps) {
  const { formatMessage } = useIntl()

  return (
    <div className="mv4 topbar-custom">
      <Box style={{ padding: 0 }}>
        <h3 className="t-heading-3">
          <FormattedMessage id="admin/b2b-organizations.warning-topbar-inative-org.form.title" />
        </h3>
        <p className="t-small mw9 mb5">
          <FormattedMessage id="admin/b2b-organizations.warning-topbar-inative-org.form.help-text" />
        </p>
        <ColorPicker
          label={formatMessage(
            organizationSettingsMessages.formColorPickerLabel
          )}
          color={colorState.color}
          colorHistory={colorState.history}
          onChange={onChangeColor}
        />
        <Textarea
          onChange={(event: ChangeEvent<HTMLInputElement>) =>
            onChangeMessage(event.target.value)
          }
          value={message}
          label={formatMessage(organizationSettingsMessages.formTextareaLabel)}
          name="message"
          size="small"
          helpText={formatMessage(
            organizationSettingsMessages.formTextareaHelpText
          )}
        />
      </Box>
    </div>
  )
}

```


[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 b2b-organizations/react/admin/utils/organizationBulkAction.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { organizationMessages as messages } from './messages'

export const organizationBulkAction = (
  handleCallback: { (rowParams: any): void; (rowParams: any): void },
  labelMessage: { id: string },
  formatMessage: { (...args: any): unknown }
) => {
  return {
    texts: {
      rowsSelected: (qty: number) =>
        formatMessage(messages.selectedRows, {
          qty,
        }),
    },
    main: {
      label: formatMessage(labelMessage),
      handleCallback,
    },
  }
}

```


[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 b2b-organizations/react/admin/utils/messages.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defineMessages } from 'react-intl'

const adminPrefix = 'admin/b2b-organizations.'

export const organizationMessages = defineMessages({
  statusActive: {
    id: `${adminPrefix}organization-details.status.active`,
  },
  statusOnHold: {
    id: `${adminPrefix}organization-details.status.on-hold`,
  },
  statusInactive: {
    id: `${adminPrefix}organization-details.status.inactive`,
  },
  toastUpdateSuccess: {
    id: `${adminPrefix}organization-details.toast.update-success`,
  },
  toastUpdateFailure: {
    id: `${adminPrefix}organization-details.toast.update-failure`,
  },
  toastAddCostCenterSuccess: {
    id: `${adminPrefix}organization-details.toast.add-costCenter-success`,
  },
  toastAddCostCenterFailure: {
    id: `${adminPrefix}organization-details.toast.add-costCenter-failure`,
  },
  detailsColumnName: {
    id: `${adminPrefix}organization-details.table.column-name.title`,
  },
  columnAddresses: {
    id: `${adminPrefix}organization-details.table.column-addresses.title`,
  },
  detailsPageTitle: {
    id: `${adminPrefix}organization-details.title`,
  },
  back: {
    id: `${adminPrefix}back`,
  },
  status: {
    id: `${adminPrefix}organization-details.status`,
  },
  costCenters: {
    id: `${adminPrefix}organization-details.costCenters`,
  },
  settings: {
    id: `${adminPrefix}organization-details.settings`,
  },
  createQuote: {
    id: `${adminPrefix}organization-details.settings.createQuote`,
  },
  showRows: {
    id: `${adminPrefix}showRows`,
  },
  of: {
    id: `${adminPrefix}of`,
  },
  checkItOut: {
    id: `${adminPrefix}check-it-out`,
  },
  invalidSchema: {
    id: `${adminPrefix}invalid-schema`,
  },
  autocompleteSearching: {
    id: `${adminPrefix}autocomplete-searching`,
  },
  new: {
    id: `${adminPrefix}organization-details.button.new`,
  },
  collections: {
    id: `${adminPrefix}organization-details.collections`,
  },
  paymentTerms: {
    id: `${adminPrefix}organization-details.paymentTerms`,
  },
  priceTables: {
    id: `${adminPrefix}organization-details.price-tables`,
  },
  sellers: {
    id: `${adminPrefix}organization-details.sellers`,
  },
  salesChannelNone: {
    id: `${adminPrefix}organization-details.none`,
  },
  salesChannel: {
    id: `${adminPrefix}organization-details.sales-channel`,
  },
  users: {
    id: `${adminPrefix}organization-details.users`,
  },
  selectedRows: {
    id: `${adminPrefix}selected-rows`,
  },
  removeFromOrg: {
    id: `${adminPrefix}organization-details.remove-from-org`,
  },
  addToOrg: {
    id: `${adminPrefix}organization-details.add-to-org`,
  },
  add: {
    id: `${adminPrefix}organization-details.button.add`,
  },
  cancel: {
    id: `${adminPrefix}organization-details.button.cancel`,
  },
  costCenterName: {
    id: `${adminPrefix}costCenter-details.costCenter-name`,
  },
  stateRegistration: {
    id: `${adminPrefix}costCenter-details.stateRegistration`,
  },
  stateRegistrationHelp: {
    id: `${adminPrefix}costCenter-details.stateRegistration.helpText`,
  },
  businessDocument: {
    id: `${adminPrefix}costCenter-details.businessDocument`,
  },
  businessDocumentHelp: {
    id: `${adminPrefix}costCenter-details.businessDocument.helpText`,
  },
  phoneNumber: {
    id: `${adminPrefix}costCenter-details.phoneNumber`,
  },
  phoneNumberHelp: {
    id: `${adminPrefix}costCenter-details.phoneNumber.helpText`,
  },
  editUser: {
    id: `${adminPrefix}organization-details.edit-user`,
  },
  save: {
    id: `${adminPrefix}organization-details.button.save`,
  },
  removeUser: {
    id: `${adminPrefix}organization-details.button.remove-user`,
  },
  default: {
    id: `${adminPrefix}organization-details.default`,
  },
  organizationNameRequired: {
    id: `${adminPrefix}organization-details.organization-name-required`,
  },
  tradeName: {
    id: `${adminPrefix}organization-details.tradeName`,
  },
  tradeNameHelp: {
    id: `${adminPrefix}organization-details.tradeName.helpText`,
  },
  email: {
    id: `${adminPrefix}user-details.email`,
  },
  costCenter: {
    id: `${adminPrefix}user-details.placeholder-costCenter`,
  },
  role: {
    id: `${adminPrefix}user-details.placeholder-role`,
  },
  userCostCenter: {
    id: `${adminPrefix}user-details.costCenter`,
  },
  userOrganization: {
    id: `${adminPrefix}user-details.organization`,
  },
  userRole: {
    id: `${adminPrefix}user-details.role`,
  },
  addUser: {
    id: `${adminPrefix}organization-details.add-user`,
  },
  addUserHelp: {
    id: `${adminPrefix}organization-details.add-user.helpText`,
  },
  name: {
    id: `${adminPrefix}user-details.name`,
  },
  columnEmail: {
    id: `${adminPrefix}organization-users.column.email`,
  },
  columnRole: {
    id: `${adminPrefix}organization-users.column.role`,
  },
  columnCostCenter: {
    id: `${adminPrefix}organization-users.column.costCenter`,
  },
  usersEmptyState: {
    id: `${adminPrefix}organization-users.emptyState`,
  },
  toastAddUserSuccess: {
    id: `${adminPrefix}organization-users.toast.add-success`,
  },
  toastUserDuplicatedOrganization: {
    id: `${adminPrefix}organization-users.toast.duplicated-organization`,
  },
  toastUserDuplicated: {
    id: `${adminPrefix}organization-users.toast.duplicated`,
  },
  toastAddUserFailure: {
    id: `${adminPrefix}organization-users.toast.add-failure`,
  },
  toastUpdateUserSuccess: {
    id: `${adminPrefix}organization-users.toast.update-success`,
  },
  toastUpdateUserFailure: {
    id: `${adminPrefix}organization-users.toast.update-failure`,
  },
  toastRemoveUserSuccess: {
    id: `${adminPrefix}organization-users.toast.remove-success`,
  },
  toastRemoveUserFailure: {
    id: `${adminPrefix}organization-users.toast.remove-failure`,
  },
  removeUserConfirm: {
    id: `${adminPrefix}organization-details.button.remove-user-confirm`,
  },
  removeUserHelp: {
    id: `${adminPrefix}organization-details.remove-user.helpText`,
  },
  addSingle: {
    id: `${adminPrefix}organizations-admin.add-single`,
  },
  addBulk: {
    id: `${adminPrefix}organizations-admin.add-bulk`,
  },
  toastAddOrgSuccess: {
    id: `${adminPrefix}organizations-admin.toast.add-organization-success`,
  },
  toastAddOrgFailure: {
    id: `${adminPrefix}organizations-admin.toast.add-organization-failure`,
  },
  tableColumnName: {
    id: `${adminPrefix}organizations-admin.table.column-name.title`,
  },
  columnStatus: {
    id: `${adminPrefix}organizations-admin.table.column-status.title`,
  },
  view: {
    id: `${adminPrefix}organizations-admin.table.view.label`,
  },
  tablePageTitle: {
    id: `${adminPrefix}organizations-admin.title`,
  },
  organizationsEmptyState: {
    id: `${adminPrefix}organizations-admin.table.empty-state`,
  },
  searchPlaceholder: {
    id: `${adminPrefix}organizations-admin.table.search.placeholder`,
  },
  clearFilters: {
    id: `${adminPrefix}organizations-admin.table.clearFilters.label`,
  },
  filterStatus: {
    id: `${adminPrefix}organizations-admin.table.statusFilter.label`,
  },
  filtersAll: {
    id: `${adminPrefix}organizations-admin.table.filters.all`,
  },
  filtersNone: {
    id: `${adminPrefix}organizations-admin.table.filters.none`,
  },
  filtersIncludes: {
    id: `${adminPrefix}organizations-admin.table.filters.includes`,
  },
  organizationName: {
    id: `${adminPrefix}organizations-admin.add-organization.organization-name`,
  },
  defaultCostCenterName: {
    id: `${adminPrefix}organizations-admin.add-organization.default-costCenter-name`,
  },
  organizationsTitle: {
    id: `${adminPrefix}organizations.navigation.label`,
  },
  notAllowViewMessage: {
    id: `${adminPrefix}organizations.not-allow-view-message`,
  },
  permissionSettings: {
    id: `${adminPrefix}organization-details.permission-settings`,
  },
})

export const costCenterMessages = defineMessages({
  toastUpdateSuccess: {
    id: `${adminPrefix}costCenter-details.toast.update-success`,
  },
  toastUpdateFailure: {
    id: `${adminPrefix}costCenter-details.toast.update-failure`,
  },
  toastDeleteFailure: {
    id: `${adminPrefix}costCenter-details.toast.delete-failure`,
  },
  addressEdit: {
    id: `${adminPrefix}costCenter-details.address.edit`,
  },
  addressDelete: {
    id: `${adminPrefix}costCenter-details.address.delete`,
  },
  pageTitle: {
    id: `${adminPrefix}costCenter-details.title`,
  },
  back: {
    id: `${adminPrefix}back`,
  },
  costCenterName: {
    id: `${adminPrefix}costCenter-details.costCenter-name`,
  },
  phoneNumber: {
    id: `${adminPrefix}costCenter-details.phoneNumber`,
  },
  phoneNumberHelp: {
    id: `${adminPrefix}costCenter-details.phoneNumber.helpText`,
  },
  stateRegistration: {
    id: `${adminPrefix}costCenter-details.stateRegistration`,
  },
  stateRegistrationHelp: {
    id: `${adminPrefix}costCenter-details.stateRegistration.helpText`,
  },
  businessDocument: {
    id: `${adminPrefix}costCenter-details.businessDocument`,
  },
  businessDocumentHelp: {
    id: `${adminPrefix}costCenter-details.businessDocument.helpText`,
  },
  addresses: {
    id: `${adminPrefix}costCenter-details.addresses`,
  },
  add: {
    id: `${adminPrefix}costCenter-details.button.add`,
  },
  cancel: {
    id: `${adminPrefix}costCenter-details.button.cancel`,
  },
  update: {
    id: `${adminPrefix}costCenter-details.button.update`,
  },
  deleteConfirm: {
    id: `${adminPrefix}costCenter-details.button.delete-confirm`,
  },
  defaultAddress: {
    id: `${adminPrefix}costCenter-details.default-address`,
  },
  duplicateAddress: {
    id: `${adminPrefix}costCenter-details.duplicate-address`,
  },
  marketingTags: {
    id: `${adminPrefix}costCenter-details.marketing-tags`,
  },
  nothingToShow: {
    id: `${adminPrefix}costCenter-details.addresses.nothing-to-show`,
  },
  searchAddress: {
    id: `${adminPrefix}costCenter-details.addresses.search-address`,
  },
  showRows: {
    id: `${adminPrefix}costCenter-details.addresses.show-rows`,
  },
  of: {
    id: `${adminPrefix}costCenter-details.addresses.pagination.of`,
  },
  address: {
    id: `${adminPrefix}costCenter-details.table.column-address.title`,
  },
  receiverName: {
    id: `${adminPrefix}costCenter-details.table.column-address.receiverName`,
  },
})

export const organizationRequestMessages = defineMessages({
  toastCreatedSuccess: {
    id: `${adminPrefix}organization-request-details.toast.created-success`,
  },
  toastUpdateSuccess: {
    id: `${adminPrefix}organization-request-details.toast.update-success`,
  },
  toastUpdateFailure: {
    id: `${adminPrefix}organization-request-details.toast.update-failure`,
  },
  toastUpdateFailureNoEmail: {
    id: `${adminPrefix}organization-request-details.toast.update-failure-missing-email`,
  },
  detailsPageTitle: {
    id: `${adminPrefix}organization-request-details.title`,
  },
  back: {
    id: `${adminPrefix}back`,
  },
  addNote: {
    id: `${adminPrefix}organization-request-details.add-note.label`,
  },
  columnName: {
    id: `${adminPrefix}organization-requests-admin.table.column-name.title`,
  },
  columnAdmin: {
    id: `${adminPrefix}organization-requests-admin.table.column-admin.title`,
  },
  columnStatus: {
    id: `${adminPrefix}organization-requests-admin.table.column-status.title`,
  },
  columnCreated: {
    id: `${adminPrefix}organization-requests-admin.table.column-created.title`,
  },
  view: {
    id: `${adminPrefix}organization-requests-admin.table.view.label`,
  },
  tablePageTitle: {
    id: `${adminPrefix}organization-requests-admin.title`,
  },
  emptyState: {
    id: `${adminPrefix}organization-requests-admin.table.empty-state`,
  },
  showRows: {
    id: `${adminPrefix}showRows`,
  },
  of: {
    id: `${adminPrefix}of`,
  },
  searchPlaceholder: {
    id: `${adminPrefix}organization-requests-admin.table.search.placeholder`,
  },
  clearFilters: {
    id: `${adminPrefix}organization-requests-admin.table.clearFilters.label`,
  },
  statusFilter: {
    id: `${adminPrefix}organization-requests-admin.table.statusFilter.label`,
  },
  filtersAll: {
    id: `${adminPrefix}organization-requests-admin.table.filters.all`,
  },
  filtersNone: {
    id: `${adminPrefix}organization-requests-admin.table.filters.none`,
  },
  filtersIncludes: {
    id: `${adminPrefix}organization-requests-admin.table.filters.includes`,
  },
})

export const organizationSettingsMessages = defineMessages({
  toastUpdateSuccess: {
    id: `${adminPrefix}organization-settings-admin.toast.update-success`,
  },
  toastUpdateFailure: {
    id: `${adminPrefix}organization-settings-admin.toast.update-failure`,
  },
  tablePageTitle: {
    id: `${adminPrefix}organization-settings-admin.title`,
  },
  customFieldsTitle: {
    id: `${adminPrefix}organization-settings-admin.customFieldsTitle`,
  },
  emptyState: {
    id: `${adminPrefix}organization-requests-admin.table.empty-state`,
  },
  autoApprove: {
    id: `${adminPrefix}organization-settings-admin.autoApprove`,
  },
  businessReadOnly: {
    id: `${adminPrefix}organization-settings-admin.businessReadOnly`,
  },
  stateReadOnly: {
    id: `${adminPrefix}organization-settings-admin.stateReadOnly`,
  },
  saveSettings: {
    id: `${adminPrefix}organization-settings-admin.saveSettings`,
  },
  selectedPaymentsTableTitle: {
    id: `${adminPrefix}organization-settings-admin.selectedPaymentsTableTitle`,
  },
  availablePaymentsTableTitle: {
    id: `${adminPrefix}organization-settings-admin.availablePaymentsTableTitle`,
  },
  selectedPriceTablesTitle: {
    id: `${adminPrefix}organization-settings-admin.selectedPriceTablesTitle`,
  },
  availablePriceTablesTitle: {
    id: `${adminPrefix}organization-settings-admin.availablePriceTablesTitle`,
  },
  bindingTitle: {
    id: `${adminPrefix}organization-settings-select.binding.title`,
  },
  bindingAvailable: {
    id: `${adminPrefix}organization-settings-select.binding.available`,
  },
  bindingSelected: {
    id: `${adminPrefix}organization-settings-select.binding.selected`,
  },
  selectedRows: {
    id: `${adminPrefix}selected-rows`,
  },
  removeFromBinding: {
    id: `${adminPrefix}organization-settings-select.remove-from-binding`,
  },
  addToBinding: {
    id: `${adminPrefix}organization-settings-select.add-to-binding`,
  },
  showModal: {
    id: `${adminPrefix}organization-settings-showModal`,
  },
  clearCart: {
    id: `${adminPrefix}organization-settings-clearCart`,
  },
  fullImpersonation: {
    id: `${adminPrefix}organization-settings-fullImpersonation`,
  },
  formColorPickerLabel: {
    id: `${adminPrefix}warning-topbar-inative-org.form.color-picker-label`,
  },
  formTextareaLabel: {
    id: `${adminPrefix}warning-topbar-inative-org.form.textarea-label`,
  },
  formTextareaHelpText: {
    id: `${adminPrefix}warning-topbar-inative-org.form.textarea-help-text`,
  },
  generalSettings: {
    id: `${adminPrefix}organization-settings-admin.generalSettings`,
  },
})

export const organizationCustomFieldsMessages = defineMessages({
  customFieldsTitle: {
    id: `${adminPrefix}custom-fields.title`,
  },
  customFieldsTitleSingular: {
    id: `${adminPrefix}custom-fields.name-singular`,
  },
  customFieldsExplanation: {
    id: `${adminPrefix}custom-fields.explanation`,
  },
  customFieldsTableFieldName: {
    id: `${adminPrefix}custom-fields.table.field-name`,
  },
  customFieldsTableFieldType: {
    id: `${adminPrefix}custom-fields.table.field-type`,
  },
  customFieldsTableDropdownPreview: {
    id: `${adminPrefix}custom-fields.table.dropdown-preview`,
  },
  customFieldsTableUseOnRegistration: {
    id: `${adminPrefix}custom-fields.table.use-on-registration`,
  },
  customFieldsAddField: {
    id: `${adminPrefix}custom-fields.add-field`,
  },
  customFieldsRemoveField: {
    id: `${adminPrefix}custom-fields.remove-field`,
  },
  customFieldsAddDropdownLine: {
    id: `${adminPrefix}custom-fields.table.add-dropdown-line`,
  },
  customFieldsRemoveDropdownLine: {
    id: `${adminPrefix}custom-fields.table.remove-dropdown-line`,
  },
  customFieldsDropdownLineValue: {
    id: `${adminPrefix}custom-fields.table.dropdown-line-value`,
  },
  customFieldsDropdownLineLabel: {
    id: `${adminPrefix}custom-fields.table.dropdown-line-label`,
  },
  customFieldsTextLabel: {
    id: `${adminPrefix}custom-fields.table.text-label`,
  },
  customFieldsDropdownLabel: {
    id: `${adminPrefix}custom-fields.table.dropdown-label`,
  },
})

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsPayTerms.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { FormattedMessage, useIntl } from 'react-intl'
import { PageBlock, Table } from 'vtex.styleguide'
import React, { Fragment, useEffect, useState } from 'react'
import { useQuery } from 'react-apollo'

import { organizationMessages as messages } from '../utils/messages'
import { organizationBulkAction } from '../utils/organizationBulkAction'
import GET_PAYMENT_TERMS from '../../graphql/getPaymentTerms.graphql'

export interface PaymentTerm {
  paymentTermId: number
  name: string
  id?: number
}

const OrganizationDetailsPayTerms = ({
  getSchema,
  paymentTermsState,
  setPaymentTermsState,
}: {
  getSchema: (argument?: any) => any
  paymentTermsState: PaymentTerm[]
  setPaymentTermsState: (value: any) => void
}) => {
  /**
   * Hooks
   */
  const { formatMessage } = useIntl()
  /**
   * States
   */

  const [paymentTermsOptions, setPaymentTermsOptions] = useState(
    [] as PaymentTerm[]
  )

  /**
   * Queries
   */

  const { data: paymentTermsData, loading } = useQuery<{
    getPaymentTerms: PaymentTerm[]
  }>(GET_PAYMENT_TERMS, { ssr: false })

  /**
   * Effects
   */

  useEffect(() => {
    if (!paymentTermsData?.getPaymentTerms?.length) {
      return
    }

    const paymentTerms =
      paymentTermsData.getPaymentTerms.map((paymentTerm: any) => {
        return { name: paymentTerm.name, paymentTermId: paymentTerm.id }
      }) ?? []

    setPaymentTermsOptions(paymentTerms)
  }, [paymentTermsData])

  /**
   * Functions
   */

  const handleRemovePaymentTerms = (rowParams: {
    selectedRows: PaymentTerm[]
  }) => {
    const { selectedRows = [] } = rowParams
    const paymentTermsToRemove = [] as number[]

    selectedRows.forEach(row => {
      paymentTermsToRemove.push(row.paymentTermId)
    })

    const newPaymentTerms = paymentTermsState.filter(
      paymentTerm => !paymentTermsToRemove.includes(paymentTerm.paymentTermId)
    )

    setPaymentTermsState(newPaymentTerms)
  }

  const handleAddPaymentTerms = (rowParams: {
    selectedRows: PaymentTerm[]
  }) => {
    const { selectedRows = [] } = rowParams
    const newPaymentTerms = [] as PaymentTerm[]

    selectedRows.forEach((row: any) => {
      if (
        !paymentTermsState.some(
          paymentTerm => paymentTerm.paymentTermId === row.paymentTermId
        )
      ) {
        newPaymentTerms.push({
          name: row.name,
          paymentTermId: row.paymentTermId,
        })
      }
    })

    setPaymentTermsState([...paymentTermsState, ...newPaymentTerms])
  }

  return (
    <Fragment>
      <PageBlock variation="half" title={formatMessage(messages.paymentTerms)}>
        <div>
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-details.assigned-to-org" />
          </h4>
          <Table
            fullWidth
            schema={getSchema()}
            items={paymentTermsState}
            bulkActions={organizationBulkAction(
              handleRemovePaymentTerms,
              messages.removeFromOrg,
              formatMessage
            )}
          />
        </div>
        <div>
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-details.available" />
          </h4>
          <Table
            fullWidth
            schema={getSchema('availablePayments')}
            items={paymentTermsOptions}
            loading={loading}
            bulkActions={organizationBulkAction(
              handleAddPaymentTerms,
              messages.addToOrg,
              formatMessage
            )}
          />
        </div>
      </PageBlock>
    </Fragment>
  )
}

export default OrganizationDetailsPayTerms

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsSettings.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { PageBlock, Checkbox } from 'vtex.styleguide'
import React, { Fragment } from 'react'
import { useIntl } from 'react-intl'

import { organizationMessages as messages } from '../utils/messages'

export interface PermissionsOptions {
  value: boolean
  label: string
}

type Permission = 'createQuote'

interface OrganizationDetailsSettingsProps {
  permissionsOptions: PermissionsOptions[]
  setPermissionsOptions: React.Dispatch<
    React.SetStateAction<PermissionsOptions[]>
  >
}

const OrganizationDetailsSettings = ({
  permissionsOptions,
  setPermissionsOptions,
}: OrganizationDetailsSettingsProps) => {
  const { formatMessage } = useIntl()

  const simulateTranslate: Record<Permission, string> = {
    createQuote: formatMessage(messages.createQuote),
  }

  const handleCheckboxChange = (label: string) => {
    setPermissionsOptions(prevOptions =>
      prevOptions.map(option =>
        option.label === label ? { ...option, value: !option.value } : option
      )
    )
  }

  return (
    <Fragment>
      <PageBlock title={formatMessage(messages.permissionSettings)}>
        {permissionsOptions.map(permissionOption => (
          <div>
            <Checkbox
              checked={permissionOption.value}
              id="permissionOrganization"
              name="permissionOrganization"
              onChange={() => handleCheckboxChange(permissionOption.label)}
              label={simulateTranslate[permissionOption.label as Permission]}
            />
          </div>
        ))}
      </PageBlock>
    </Fragment>
  )
}

export default OrganizationDetailsSettings

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsSalesChannel.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Fragment, useEffect, useState } from 'react'
import { useIntl } from 'react-intl'
import { PageBlock, RadioGroup } from 'vtex.styleguide'
import { useQuery } from 'react-apollo'

import { organizationMessages as messages } from '../utils/messages'
import GET_SALES_CHANNELS from '../../graphql/getSalesChannels.graphql'

interface SalesChannel {
  value: string
  label?: string
}

const OrganizationDetailsSalesChannel = ({
  salesChannelState,
  setSalesChannelState,
}: {
  salesChannelState: string
  setSalesChannelState: (value: any) => void
}) => {
  /**
   * Hooks
   */
  const { formatMessage } = useIntl()

  /**
   * States
   */
  const [salesChannelOptions, setSalesChannelOptions] = useState(
    [] as SalesChannel[]
  )

  /**
   * Queries
   */
  const { data: salesChannelsData } = useQuery(GET_SALES_CHANNELS, {
    ssr: false,
  })

  /**
   * Effects
   */
  useEffect(() => {
    if (!salesChannelsData?.salesChannels?.length) {
      return
    }

    const options = [
      {
        value: '',
        label: formatMessage(messages.salesChannelNone),
      },
    ] as SalesChannel[]

    salesChannelsData.salesChannels.forEach(
      (channel: { id: string; name: string }) => {
        options.push({
          value: channel.id,
          label: `${channel.name} (${channel.id})`,
        })
      }
    )

    setSalesChannelOptions(options)
  }, [salesChannelsData])

  return (
    <Fragment>
      <PageBlock title={formatMessage(messages.salesChannel)}>
        <div>
          <RadioGroup
            name="salesChannels"
            options={salesChannelOptions}
            value={salesChannelState}
            onChange={(e: any) => setSalesChannelState(e.currentTarget.value)}
          />
        </div>
      </PageBlock>
    </Fragment>
  )
}

export default OrganizationDetailsSalesChannel

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsDefault.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { Dropdown, Input, PageBlock } from 'vtex.styleguide'
import { FormattedMessage, useIntl } from 'react-intl'
import React, { Fragment } from 'react'
import type { FunctionComponent } from 'react'

import { organizationMessages as messages } from '../utils/messages'
import CustomFieldInput from '../OrganizationDetailsCustomField'

interface Props {
  organizationNameState: string
  setOrganizationNameState: (value: string) => void
  organizationTradeNameState: string
  setOrganizationTradeNameState: (value: string) => void
  statusState: string
  setStatusState: (value: string) => void
  data: any
  customFieldsState: CustomField[]
  setCustomFieldsState: (value: CustomField[]) => void
}

const OrganizationDetailsDefault: FunctionComponent<Props> = ({
  organizationNameState,
  setOrganizationNameState,
  organizationTradeNameState,
  setOrganizationTradeNameState,
  statusState,
  setStatusState,
  data,
  customFieldsState,
  setCustomFieldsState,
}) => {
  /**
   * Hooks
   */
  const { formatMessage, formatDate } = useIntl()

  /**
   * States
   */
  const statusOptions = [
    {
      value: 'active',
      label: formatMessage(messages.statusActive),
    },
    {
      value: 'on-hold',
      label: formatMessage(messages.statusOnHold),
    },
    {
      value: 'inactive',
      label: formatMessage(messages.statusInactive),
    },
  ]

  const handleCustomFieldsUpdate = (
    index: number,
    customField: CustomField
  ) => {
    const newCustomFields = [...customFieldsState]

    newCustomFields[index] = customField
    setCustomFieldsState(newCustomFields)
  }

  return (
    <Fragment>
      <PageBlock>
        <Input
          autocomplete="off"
          size="large"
          label={
            <h4 className="t-heading-5 mb0 pt3">
              <FormattedMessage id="admin/b2b-organizations.organization-details.organization-name" />
            </h4>
          }
          value={organizationNameState}
          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
            setOrganizationNameState(e.target.value)
          }}
          required
        />
        <div className="pv3">
          <Input
            autocomplete="off"
            size="large"
            label={
              <h4 className="t-heading-5 mb0 pt3">
                <FormattedMessage id="admin/b2b-organizations.organization-details.tradeName" />
              </h4>
            }
            helpText={
              <FormattedMessage id="admin/b2b-organizations.organization-details.tradeName.helpText" />
            }
            value={organizationTradeNameState}
            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
              setOrganizationTradeNameState(e.target.value)
            }}
          />
        </div>
        <div className="pv3">
          <Dropdown
            label={
              <h4 className="t-heading-5 mb0 pt3">
                <FormattedMessage id="admin/b2b-organizations.organization-details.status" />
              </h4>
            }
            placeholder={formatMessage(messages.status)}
            options={statusOptions}
            value={statusState}
            onChange={(_: void, v: string) => setStatusState(v)}
          />
        </div>
        <h4 className="t-heading-5 mb0 pt3">
          <FormattedMessage id="admin/b2b-organizations.organization-details.created" />
        </h4>
        <div className="mv3">
          {formatDate(data.getOrganizationById.created, {
            day: 'numeric',
            month: 'numeric',
            year: 'numeric',
          })}
        </div>
      </PageBlock>
      {customFieldsState?.length ? (
        <PageBlock>
          <h4 className="t-heading-5 mb0 pt3">
            <FormattedMessage id="admin/b2b-organizations.custom-fields.title" />
          </h4>
          {customFieldsState?.map((customField: CustomField, index: number) => (
            <CustomFieldInput
              key={`${customField.name}`}
              index={index}
              handleUpdate={handleCustomFieldsUpdate}
              customField={customField}
            />
          ))}
        </PageBlock>
      ) : null}
    </Fragment>
  )
}

export default OrganizationDetailsDefault

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsCollections.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ChangeEvent } from 'react'
import React, { Fragment, useEffect, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { PageBlock, Table } from 'vtex.styleguide'
import { useQuery } from 'react-apollo'

import { organizationMessages as messages } from '../utils/messages'
import { organizationBulkAction } from '../utils/organizationBulkAction'
import GET_COLLECTIONS from '../../graphql/getCollections.graphql'

export interface Collection {
  collectionId: string
  name: string
}

const OrganizationDetailsCollections = ({
  getSchema,
  collectionsState,
  setCollectionsState,
}: {
  getSchema: (argument?: any) => any
  collectionsState: Collection[]
  setCollectionsState: (value: any) => void
}) => {
  /**
   * Hooks
   */
  const { formatMessage } = useIntl()
  /**
   * States
   */

  const [collectionOptions, setCollectionOptions] = useState([] as Collection[])
  const [collectionPaginationState, setCollectionPaginationState] = useState({
    page: 1,
    pageSize: 25,
  })

  /**
   * Queries
   */
  const {
    data: collectionsData,
    refetch: refetchCollections,
    loading,
  } = useQuery(GET_COLLECTIONS, {
    variables: collectionPaginationState,
    notifyOnNetworkStatusChange: true,
    ssr: false,
  })

  /**
   * Effects
   */

  useEffect(() => {
    if (!collectionsData?.collections?.items?.length) {
      return
    }

    const collections =
      collectionsData.collections.items.map((collection: any) => {
        return { name: collection.name, collectionId: collection.id }
      }) ?? []

    setCollectionOptions(collections)
  }, [collectionsData])

  /**
   * Functions
   */
  const handleRemoveCollections = (rowParams: any) => {
    const { selectedRows = [] } = rowParams
    const collectionsToRemove = [] as string[]

    selectedRows.forEach((row: any) => {
      collectionsToRemove.push(row.collectionId)
    })

    const newCollectionList = collectionsState.filter(
      collection => !collectionsToRemove.includes(collection.collectionId)
    )

    setCollectionsState(newCollectionList)
  }

  const handleCollectionsPrevClick = () => {
    if (collectionPaginationState.page === 1) return

    const newPage = collectionPaginationState.page - 1

    setCollectionPaginationState({
      ...collectionPaginationState,
      page: newPage,
    })

    refetchCollections({
      ...collectionPaginationState,
      page: newPage,
    })
  }

  const handleCollectionsNextClick = () => {
    const newPage = collectionPaginationState.page + 1

    setCollectionPaginationState({
      ...collectionPaginationState,
      page: newPage,
    })

    refetchCollections({
      ...collectionPaginationState,
      page: newPage,
    })
  }

  const handleRowsChange = (e: ChangeEvent<HTMLInputElement>) => {
    const {
      target: { value },
    } = e

    setCollectionPaginationState({
      page: 1,
      pageSize: +value,
    })

    refetchCollections({
      page: 1,
      pageSize: +value,
    })
  }

  const handleAddCollections = (rowParams: any) => {
    const { selectedRows = [] } = rowParams
    const newCollections = [] as Collection[]

    selectedRows.forEach((row: any) => {
      if (
        !collectionsState.some(
          collection => collection.collectionId === row.collectionId
        )
      ) {
        newCollections.push({ name: row.name, collectionId: row.collectionId })
      }
    })

    setCollectionsState([...collectionsState, ...newCollections])
  }

  return (
    <Fragment>
      <PageBlock variation="half" title={formatMessage(messages.collections)}>
        <div>
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-details.assigned-to-org" />
          </h4>
          <Table
            fullWidth
            schema={getSchema()}
            items={collectionsState}
            bulkActions={organizationBulkAction(
              handleRemoveCollections,
              messages.removeFromOrg,
              formatMessage
            )}
          />
        </div>
        <div>
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-details.available" />
          </h4>
          <Table
            fullWidth
            schema={getSchema('availableCollections')}
            items={collectionOptions}
            loading={loading}
            pagination={{
              onNextClick: handleCollectionsNextClick,
              onPrevClick: handleCollectionsPrevClick,
              onRowsChange: handleRowsChange,
              selectedOption: collectionPaginationState.pageSize,
              currentItemFrom:
                (collectionPaginationState.page - 1) *
                  collectionPaginationState.pageSize +
                1,
              currentItemTo:
                collectionsData?.collections?.paging?.total <
                collectionPaginationState.page *
                  collectionPaginationState.pageSize
                  ? collectionsData?.collections?.paging?.total
                  : collectionPaginationState.page *
                    collectionPaginationState.pageSize,
              textShowRows: formatMessage(messages.showRows),
              textOf: formatMessage(messages.of),
              totalItems: collectionsData?.collections?.paging?.total ?? 0,
              rowsOptions: [25, 50],
            }}
            bulkActions={organizationBulkAction(
              handleAddCollections,
              messages.addToOrg,
              formatMessage
            )}
          />
        </div>
      </PageBlock>
    </Fragment>
  )
}

export default OrganizationDetailsCollections

```


[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 b2b-organizations/react/admin/OrganizationDetails/useHashRouter.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import React, { useEffect, useRef, useState } from 'react'

const useHashRouter = ({
  sessionKey,
  defaultPath,
  routes,
}: {
  sessionKey: string
  defaultPath: string
  routes: string[]
}) => {
  const [tab, setTab] = React.useState('')
  const [location, setLocation] = useState(null as any)
  const routerRef = useRef(null as any)

  const setupTab = (_tab: string) => {
    sessionStorage.setItem(sessionKey, _tab)
    setTab(_tab)
  }

  const handleTabChange = (_tab: string) => {
    if (!routerRef?.current) {
      return
    }

    routerRef.current?.history?.push(`/${_tab}`)
    setupTab(_tab)
  }

  const onHashChange = () => {
    const hash = window.location.hash.replace('#/', '')

    if (routes.includes(hash)) {
      setupTab(hash)
    }
  }

  useEffect(() => {
    if (!location) return
    setupTab(location.pathname.replace('/', ''))
  }, [location?.pathname])

  useEffect(() => {
    let lastHash = window.location.hash
    const handleHashChange = () => {
      if (lastHash !== window.location.hash) {
        lastHash = window.location.hash
        onHashChange()
      }
    }

    const interval = setInterval(handleHashChange, 400)

    return () => {
      clearInterval(interval)
    }
  }, [])

  useEffect(() => {
    if (!routerRef?.current) return
    const { pathname } = routerRef.current?.history?.location

    if (pathname === '/' || !routes.includes(pathname.replace('/', ''))) {
      const sessionTab = sessionStorage.getItem(sessionKey)

      routerRef.current?.history?.push(
        sessionTab ? `/${sessionTab}` : `/${defaultPath}`
      )
    }

    setLocation(routerRef.current?.history?.location)
  }, [routerRef.current])

  return {
    tab,
    handleTabChange,
    routerRef,
  }
}

export default useHashRouter

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsPriceTables.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Fragment, useEffect, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { PageBlock, Table } from 'vtex.styleguide'
import { useQuery } from 'react-apollo'

import { organizationMessages as messages } from '../utils/messages'
import { organizationBulkAction } from '../utils/organizationBulkAction'
import GET_PRICE_TABLES from '../../graphql/getPriceTables.graphql'

export interface PriceTable {
  tableId: string
  name?: string
}

const OrganizationDetailsPriceTables = ({
  getSchema,
  priceTablesState,
  setPriceTablesState,
}: {
  getSchema: (argument?: any) => any
  priceTablesState: string[]
  setPriceTablesState: (value: any) => void
}) => {
  /**
   * Hooks
   */
  const { formatMessage } = useIntl()

  /**
   * States
   */
  const [priceTableOptions, setPriceTableOptions] = useState([] as PriceTable[])

  /**
   * Queries
   */
  const { data: priceTablesData, loading } = useQuery(GET_PRICE_TABLES, {
    ssr: false,
  })

  /**
   * Effects
   */
  useEffect(() => {
    if (!priceTablesData?.priceTables?.length) {
      return
    }

    const options = [] as PriceTable[]

    priceTablesData.priceTables.forEach((priceTable: string) => {
      if (!options.find(option => option.tableId === priceTable)) {
        options.push({ tableId: priceTable })
      }
    })

    setPriceTableOptions(options)
  }, [priceTablesData])

  /**
   * Functions
   */
  const handleRemovePriceTables = (rowParams: any) => {
    const { selectedRows = [] } = rowParams
    const priceTablesToRemove = [] as string[]

    selectedRows.forEach((row: PriceTable) => {
      priceTablesToRemove.push(row.tableId)
    })

    const newPriceTablesList = priceTablesState.filter(
      priceTable => !priceTablesToRemove.includes(priceTable)
    )

    setPriceTablesState(newPriceTablesList)
  }

  const handleAddPriceTables = (rowParams: any) => {
    const { selectedRows = [] } = rowParams
    const newPriceTables = [] as string[]

    selectedRows.forEach((row: PriceTable) => {
      if (!priceTablesState.includes(row.tableId)) {
        newPriceTables.push(row.tableId)
      }
    })

    setPriceTablesState((prevState: any) => [...prevState, ...newPriceTables])
  }

  return (
    <Fragment>
      <PageBlock variation="half" title={formatMessage(messages.priceTables)}>
        <div>
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-details.assigned-to-org" />
          </h4>
          <Table
            fullWidth
            schema={getSchema()}
            items={priceTablesState.map(priceTable => {
              return {
                tableId: priceTable,
                name:
                  priceTableOptions.find(
                    option => option.tableId === priceTable
                  )?.name ?? priceTable,
              }
            })}
            bulkActions={organizationBulkAction(
              handleRemovePriceTables,
              messages.removeFromOrg,
              formatMessage
            )}
          />
        </div>
        <div>
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-details.available" />
          </h4>
          <Table
            fullWidth
            schema={getSchema('availablePriceTables')}
            items={priceTableOptions}
            loading={loading}
            bulkActions={organizationBulkAction(
              handleAddPriceTables,
              messages.addToOrg,
              formatMessage
            )}
          />
        </div>
      </PageBlock>
    </Fragment>
  )
}

export default OrganizationDetailsPriceTables

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsSellers.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Fragment, useEffect, useState } from 'react'
import { useQuery } from 'react-apollo'
import { useToast } from '@vtex/admin-ui'
import { PageBlock, Table } from 'vtex.styleguide'
import { FormattedMessage, useIntl } from 'react-intl'

import type { AvailabilityTypes } from '../OrganizationDetails'
import GET_ACCOUNT from '../../graphql/getAccount.graphql'
import { organizationMessages as messages } from '../utils/messages'
import { organizationBulkAction } from '../utils/organizationBulkAction'
import GET_SELLERS_PAGINATED from '../../graphql/getSellersPaginated.graphql'

export interface Seller {
  sellerId: string
  name?: string
}

export interface SellerItem {
  id: string
  name: string
}

interface RowParams {
  selectedRows: Seller[]
}

interface GetAccountResponse {
  getAccount: {
    id: string
    name: string
  }
}

interface GetSellersPaginatedQueryResponse {
  getSellersPaginated: {
    items: SellerItem[]
    pagination: {
      page: number
      pageSize: number
      total: number
    }
  }
}

const OrganizationDetailsSellers = ({
  getSchema,
  sellersState,
  setSellersState,
}: {
  getSchema: (type?: AvailabilityTypes) => unknown
  sellersState: Seller[]
  setSellersState: React.Dispatch<React.SetStateAction<Seller[]>>
}) => {
  /**
   * Hooks
   */
  const toast = useToast()
  const { formatMessage } = useIntl()

  /**
   * States
   */
  const [variables, setVariables] = useState({ page: 1, pageSize: 25 })
  const [sellerOptions, setSellerOptions] = useState<Seller[]>([])

  /**
   * Queries
   */
  const { data: accountData, loading: loadingAccountData } = useQuery<
    GetAccountResponse
  >(GET_ACCOUNT, {
    onError: error => {
      toast({ variant: 'critical', message: error.message })
    },
  })

  const {
    data: sellersData,
    loading: loadingSellersData,
    refetch: refetchSellersData,
  } = useQuery<GetSellersPaginatedQueryResponse>(GET_SELLERS_PAGINATED, {
    variables,
    skip: !accountData,
    onCompleted: data => {
      if (!data?.getSellersPaginated?.items) {
        return
      }

      const options = data.getSellersPaginated.items.map(({ name, id }) => {
        // This is a workaround to show the account name in the seller list only for the first seller.
        // This is because the first seller is created with the account name,
        // and in turn the trade name is indexed to the product catalogs.
        if (id === '1' && accountData) {
          return {
            name: accountData.getAccount.name,
            sellerId: id,
          }
        }

        return {
          name,
          sellerId: id,
        }
      })

      setSellerOptions(options)
    },
    onError: error => {
      toast({ variant: 'critical', message: error.message })
    },
  })

  /**
   * Constants
   */
  const totalItems = sellersData?.getSellersPaginated?.pagination?.total ?? 0

  /**
   * Functions
   */
  const handleRemoveSellers = (rowParams: RowParams) => {
    const { selectedRows = [] } = rowParams
    const sellersToRemove: Seller[] = []

    selectedRows.forEach((row: Seller) => {
      sellersToRemove.push(row)
    })

    const newSellersList = sellersState.filter(
      seller =>
        !sellersToRemove.some(
          removeSeller => removeSeller.sellerId === seller.sellerId
        )
    )

    setSellersState(newSellersList)
  }

  const handleAddSellers = (rowParams: RowParams) => {
    const { selectedRows = [] } = rowParams
    const newSellers: Seller[] = []

    selectedRows.forEach((row: Seller) => {
      if (!sellersState.some(seller => seller.sellerId === row.sellerId)) {
        newSellers.push(row)
      }
    })

    setSellersState(prevState => [...prevState, ...newSellers])
  }

  const handleNext = () => {
    if (variables.page * variables.pageSize >= totalItems) return

    setVariables(prev => ({ ...prev, page: prev.page + 1 }))

    refetchSellersData({
      page: variables.page + 1,
      pageSize: variables.pageSize,
    })
  }

  const handlePrev = () => {
    if (variables.page === 1) return

    setVariables(prev => ({ ...prev, page: prev.page - 1 }))

    refetchSellersData({
      page: variables.page - 1,
      pageSize: variables.pageSize,
    })
  }

  const handleRowsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const {
      target: { value },
    } = e

    setVariables({ page: 1, pageSize: +value })

    refetchSellersData({
      page: 1,
      pageSize: +value,
    })
  }

  useEffect(() => {
    if (accountData) {
      refetchSellersData()
    }
  }, [accountData, refetchSellersData])

  return (
    <Fragment>
      <PageBlock variation="half" title={formatMessage(messages.sellers)}>
        <div>
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-details.assigned-to-org" />
          </h4>
          <Table
            fullWidth
            schema={getSchema()}
            items={sellersState}
            bulkActions={organizationBulkAction(
              handleRemoveSellers,
              messages.removeFromOrg,
              formatMessage
            )}
          />
        </div>

        <div>
          <h4 className="t-heading-4 mt0 mb0">
            <FormattedMessage id="admin/b2b-organizations.organization-details.available" />
          </h4>
          <Table
            fullWidth
            loading={loadingAccountData || loadingSellersData}
            schema={getSchema('availableSellers')}
            bulkActions={organizationBulkAction(
              handleAddSellers,
              messages.addToOrg,
              formatMessage
            )}
            items={sellerOptions}
            pagination={{
              onNextClick: handleNext,
              onPrevClick: handlePrev,
              onRowsChange: handleRowsChange,
              selectedOption: variables.pageSize,
              currentItemFrom: (variables.page - 1) * variables.pageSize + 1,
              currentItemTo: Math.min(
                variables.page * variables.pageSize,
                totalItems
              ),
              textShowRows: formatMessage(messages.showRows),
              textOf: formatMessage(messages.of),
              rowsOptions: [25, 50, 100],
              totalItems,
            }}
          />
        </div>
      </PageBlock>
    </Fragment>
  )
}

export default OrganizationDetailsSellers

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsCostCenters.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ChangeEvent } from 'react'
import React, { Fragment, useState, useEffect } from 'react'
import {
  Button,
  Input,
  Modal,
  PageBlock,
  Table,
  Spinner,
} from 'vtex.styleguide'
import { FormattedMessage, useIntl } from 'react-intl'
import { useMutation, useQuery } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'
import { addValidation } from 'vtex.address-form/helpers'
import {
  AddressContainer,
  AddressForm,
  AddressRules,
  CountrySelector,
  PostalCodeGetter,
} from 'vtex.address-form'
import { StyleguideInput } from 'vtex.address-form/inputs'
import type { ToastProps } from '@vtex/admin-ui'

import GET_COST_CENTERS from '../../graphql/getCostCentersByOrganizationId.graphql'
import type { CellRendererProps } from '../OrganizationDetails'
import { organizationMessages as messages } from '../utils/messages'
import { getEmptyAddress, isValidAddress } from '../../utils/addresses'
import CREATE_COST_CENTER from '../../graphql/createCostCenter.graphql'
import GET_LOGISTICS from '../../graphql/getLogistics.graphql'
import { validatePhoneNumber } from '../../modules/formValidators'
import GET_B2B_CUSTOM_FIELDS from '../../graphql/getB2BCustomFields.graphql'
import CustomFieldInput from '../OrganizationDetailsCustomField'
import { useOrgPermission } from '../../hooks/useOrgPermission'
import { ORGANIZATION_EDIT } from '../../utils/constants'

interface CostCenterSimple {
  id: string
  name: string
  addresses: Address[]
}

const OrganizationDetailsCostCenters = ({
  setLoadingState,
  showToast,
  loadingState,
}: {
  setLoadingState: (state: boolean) => void
  showToast: (toast: ToastProps) => void
  loadingState: boolean
}) => {
  /**
   * Hooks
   */
  const {
    culture: { country },
    route: { params },
    navigate,
  } = useRuntime()

  const { formatMessage } = useIntl()

  /**
   * States
   */
  const [costCenterPaginationState, setCostCenterPaginationState] = useState({
    page: 1,
    pageSize: 25,
  })

  const [newCostCenterModalState, setNewCostCenterModalState] = useState(false)
  const [newCostCenterName, setNewCostCenterName] = useState('')
  const [newCostCenterPhoneNumber, setNewCostCenterPhoneNumber] = useState('')
  const [
    newCostCenterBusinessDocument,
    setNewCostCenterBusinessDocument,
  ] = useState('')

  const [
    newCostCenterStateRegistration,
    setNewCostCenterStateRegistration,
  ] = useState('')

  const [newCostCenterAddressState, setNewCostCenterAddressState] = useState(
    addValidation(getEmptyAddress(country))
  )

  /**
   * Mutations
   */
  const [createCostCenter] = useMutation(CREATE_COST_CENTER)
  const {
    data: costCentersData,
    refetch: refetchCostCenters,
    loading,
  } = useQuery(GET_COST_CENTERS, {
    variables: { ...costCenterPaginationState, id: params?.id },
    fetchPolicy: 'network-only',
    notifyOnNetworkStatusChange: true,
    skip: !params?.id,
    ssr: false,
  })

  //! CUSTOM FIELDS
  const {
    data: defaultCustomFieldsData,
    loading: defaultCustomFieldsDataLoading,
  } = useQuery(GET_B2B_CUSTOM_FIELDS, {
    ssr: false,
  })

  const { data: canEditBuyerOrg } = useOrgPermission({
    resourceCode: ORGANIZATION_EDIT,
  })

  const [
    costCenterCustomFieldsState,
    setCostCenterCustomFieldsState,
  ] = useState<CustomField[]>([])

  useEffect(() => {
    if (defaultCustomFieldsDataLoading) return

    const costCenterFieldsToDisplay = defaultCustomFieldsData?.getB2BSettings.costCenterCustomFields.filter(
      (item: CustomField) => item.useOnRegistration
    )

    setCostCenterCustomFieldsState(costCenterFieldsToDisplay)
  }, [defaultCustomFieldsData])

  const handleCostCenterCustomFieldsUpdate = (
    index: number,
    customField: CustomField
  ) => {
    const newCustomFields = [...costCenterCustomFieldsState]

    newCustomFields[index] = customField
    setCostCenterCustomFieldsState(newCustomFields)
  }
  //! CUSTOM FIELDS

  /**
   * Functions
   */
  const handleCostCentersRowsChange = (e: ChangeEvent<HTMLInputElement>) => {
    const {
      target: { value },
    } = e

    setCostCenterPaginationState({
      page: 1,
      pageSize: +value,
    })

    refetchCostCenters({
      id: params?.id,
      page: 1,
      pageSize: +value,
    })
  }

  const handleAddNewCostCenter = () => {
    setLoadingState(true)
    const newAddress = {
      addressId: newCostCenterAddressState.addressId.value,
      addressType: newCostCenterAddressState.addressType.value,
      city: newCostCenterAddressState.city.value,
      complement: newCostCenterAddressState.complement.value,
      country: newCostCenterAddressState.country.value,
      receiverName: newCostCenterAddressState.receiverName.value,
      geoCoordinates: newCostCenterAddressState.geoCoordinates.value,
      neighborhood: newCostCenterAddressState.neighborhood.value,
      number: newCostCenterAddressState.number.value,
      postalCode: newCostCenterAddressState.postalCode.value,
      reference: newCostCenterAddressState.reference.value,
      state: newCostCenterAddressState.state.value,
      street: newCostCenterAddressState.street.value,
      addressQuery: newCostCenterAddressState.addressQuery.value,
    }

    const variables = {
      organizationId: params.id,
      input: {
        name: newCostCenterName,
        addresses: [newAddress],
        businessDocument: newCostCenterBusinessDocument,
        customFields: costCenterCustomFieldsState,
        stateRegistration: newCostCenterStateRegistration,
      },
    }

    createCostCenter({ variables })
      .then(() => {
        setNewCostCenterModalState(false)
        setLoadingState(false)
        showToast({
          variant: 'positive',
          message: formatMessage(messages.toastAddCostCenterSuccess),
        })
        refetchCostCenters({ ...costCenterPaginationState, id: params?.id })
      })
      .catch(error => {
        setNewCostCenterModalState(false)
        setLoadingState(false)
        console.error(error)
        showToast({
          variant: 'critical',
          message: formatMessage(messages.toastAddCostCenterFailure),
        })
      })
  }

  const handleCostCentersPrevClick = () => {
    if (costCenterPaginationState.page === 1) return

    const newPage = costCenterPaginationState.page - 1

    setCostCenterPaginationState({
      ...costCenterPaginationState,
      page: newPage,
    })

    refetchCostCenters({
      ...costCenterPaginationState,
      id: params?.id,
      page: newPage,
    })
  }

  const handleCostCentersNextClick = () => {
    const newPage = costCenterPaginationState.page + 1

    setCostCenterPaginationState({
      ...costCenterPaginationState,
      page: newPage,
    })

    refetchCostCenters({
      ...costCenterPaginationState,
      id: params?.id,
      page: newPage,
    })
  }

  const getCostCenterSchema = () => ({
    properties: {
      name: {
        title: formatMessage(messages.detailsColumnName),
      },
      addresses: {
        title: formatMessage(messages.columnAddresses),
        cellRenderer: ({
          rowData: { addresses },
        }: CellRendererProps<CostCenterSimple>) => (
          <span>{addresses.length}</span>
        ),
      },
    },
  })

  const handleNewCostCenterAddressChange = (
    changedAddress: AddressFormFields
  ) => {
    const curAddress = newCostCenterAddressState

    const newAddress = { ...curAddress, ...changedAddress }

    setNewCostCenterAddressState(newAddress)
  }

  const handleCloseModal = () => {
    setNewCostCenterModalState(false)
    setNewCostCenterName('')
    setNewCostCenterAddressState(addValidation(getEmptyAddress(country)))
  }

  const { data: logisticsData } = useQuery(GET_LOGISTICS, { ssr: false })

  const translateCountries = () => {
    const { shipsTo = [] } = logisticsData?.logistics ?? {}

    return shipsTo.map((code: string) => ({
      label: formatMessage({ id: `country.${code}` }),
      value: code,
    }))
  }

  /**
   * View
   */
  return (
    <Fragment>
      <PageBlock title={formatMessage(messages.costCenters)}>
        <Table
          fullWidth
          schema={getCostCenterSchema()}
          loading={loading}
          items={costCentersData?.getCostCentersByOrganizationId?.data}
          onRowClick={({
            rowData: { id },
          }: CellRendererProps<CostCenterSimple>) => {
            if (!id) return

            navigate({
              page: 'admin.app.b2b-organizations.costCenter-details',
              params: { id },
            })
          }}
          pagination={{
            onNextClick: handleCostCentersNextClick,
            onPrevClick: handleCostCentersPrevClick,
            onRowsChange: handleCostCentersRowsChange,
            selectedOption: costCenterPaginationState.pageSize,
            currentItemFrom:
              (costCenterPaginationState.page - 1) *
                costCenterPaginationState.pageSize +
              1,
            currentItemTo:
              costCentersData?.getCostCentersByOrganizationId?.pagination
                ?.total <
              costCenterPaginationState.page *
                costCenterPaginationState.pageSize
                ? costCentersData?.getCostCentersByOrganizationId?.pagination
                    ?.total
                : costCenterPaginationState.page *
                  costCenterPaginationState.pageSize,
            textShowRows: formatMessage(messages.showRows),
            textOf: formatMessage(messages.of),
            totalItems:
              costCentersData?.getCostCentersByOrganizationId?.pagination
                ?.total ?? 0,
            rowsOptions: [25, 50, 100],
          }}
          toolbar={{
            newLine: {
              label: formatMessage(messages.new),
              handleCallback: () => setNewCostCenterModalState(true),
              disabled: !canEditBuyerOrg,
            },
          }}
        />
      </PageBlock>
      <Modal
        centered
        bottomBar={
          <div className="nowrap">
            <span className="mr4">
              <Button
                variation="tertiary"
                onClick={() => handleCloseModal()}
                disabled={loadingState}
              >
                {formatMessage(messages.cancel)}
              </Button>
            </span>
            <span>
              <Button
                variation="primary"
                onClick={() => handleAddNewCostCenter()}
                isLoading={loadingState}
                disabled={
                  !newCostCenterName ||
                  !isValidAddress(newCostCenterAddressState) ||
                  (newCostCenterPhoneNumber &&
                    !validatePhoneNumber(newCostCenterPhoneNumber))
                }
              >
                {formatMessage(messages.add)}
              </Button>
            </span>
          </div>
        }
        isOpen={newCostCenterModalState}
        onClose={() => handleCloseModal()}
        closeOnOverlayClick={false}
      >
        <p className="f3 f1-ns fw3 gray">
          <FormattedMessage id="admin/b2b-organizations.organization-details.add-costCenter" />
        </p>
        <div className="w-100 mv6">
          <Input
            autocomplete="off"
            size="large"
            label={formatMessage(messages.costCenterName)}
            value={newCostCenterName}
            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
              setNewCostCenterName(e.target.value)
            }}
            required
          />
        </div>
        <div className="w-100 mv6">
          <Input
            autocomplete="off"
            size="large"
            label={formatMessage(messages.phoneNumber)}
            helpText={formatMessage(messages.phoneNumberHelp)}
            value={newCostCenterPhoneNumber}
            error={
              newCostCenterPhoneNumber &&
              !validatePhoneNumber(newCostCenterPhoneNumber)
            }
            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
              setNewCostCenterPhoneNumber(e.target.value)
            }}
          />
        </div>
        <div className="w-100 mv6">
          <Input
            autocomplete="off"
            size="large"
            label={formatMessage(messages.businessDocument)}
            helpText={formatMessage(messages.businessDocumentHelp)}
            value={newCostCenterBusinessDocument}
            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
              setNewCostCenterBusinessDocument(e.target.value)
            }}
          />
        </div>
        {/* //! Custom fields */}
        {defaultCustomFieldsDataLoading ? (
          <div className="mb5 flex flex-column">
            <Spinner />
          </div>
        ) : (
          <>
            {costCenterCustomFieldsState?.map(
              (customField: CustomField, index: number) => {
                return (
                  <CustomFieldInput
                    key={`${customField.name}`}
                    index={index}
                    handleUpdate={handleCostCenterCustomFieldsUpdate}
                    customField={customField}
                  />
                )
              }
            )}
          </>
        )}
        {/* //! Custom fields */}
        <div className="w-100 mv6">
          <Input
            autocomplete="off"
            size="large"
            label={formatMessage(messages.stateRegistration)}
            helpText={formatMessage(messages.stateRegistrationHelp)}
            value={newCostCenterStateRegistration}
            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
              setNewCostCenterStateRegistration(e.target.value)
            }}
          />
        </div>
        <div className="w-100 mv6">
          <FormattedMessage id="admin/b2b-organizations.organization-details.add-costCenter.helpText" />
        </div>
        <AddressRules
          country={newCostCenterAddressState?.country?.value}
          shouldUseIOFetching
          useGeolocation={false}
        >
          <AddressContainer
            address={newCostCenterAddressState}
            Input={StyleguideInput}
            onChangeAddress={handleNewCostCenterAddressChange}
            autoCompletePostalCode
          >
            <CountrySelector shipsTo={translateCountries()} />

            <PostalCodeGetter />

            <AddressForm
              Input={StyleguideInput}
              omitAutoCompletedFields={false}
              omitPostalCodeFields
            />
          </AddressContainer>
        </AddressRules>
      </Modal>
    </Fragment>
  )
}

export default OrganizationDetailsCostCenters

```


[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 b2b-organizations/react/admin/OrganizationDetails/OrganizationDetailsUsers.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { PageBlock } from 'vtex.styleguide'
import React, { Fragment } from 'react'
import { useIntl } from 'react-intl'

import OrganizationUsersTable from '../../components/OrganizationUsersTable'
import { organizationMessages as messages } from '../utils/messages'

const OrganizationDetailsUsers = ({
  params,
  loadingState,
}: {
  params: any
  loadingState: any
}) => {
  /**
   * Hooks
   */
  const { formatMessage } = useIntl()

  return (
    <Fragment>
      <PageBlock title={formatMessage(messages.users)}>
        <OrganizationUsersTable
          organizationId={params?.id}
          permissions={[]}
          refetchCostCenters={loadingState}
          isAdmin={true}
        />
      </PageBlock>
    </Fragment>
  )
}

export default OrganizationDetailsUsers

```


[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 b2b-organizations/react/admin/OrganizationsList/OrganizationsListSearch/index.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
export { default } from './OrganizationsListSearch'

```


[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 b2b-organizations/react/admin/OrganizationsList/OrganizationsListSearch/OrganizationsListSearch.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { Search, csx, useSearchState } from '@vtex/admin-ui'
import type { TagProps } from '@vtex/admin-ui'

import type { FetchListOptions } from '../../../organizations/hooks'

export const TagVariantByStatus: Record<string, TagProps['variant']> = {
  active: 'green',
  inactive: 'red',
  'on-hold': 'orange',
}

interface Props {
  onSearch: (options: Partial<FetchListOptions>) => void
}

const OrganizationsListSearch: React.FC<Props> = ({ onSearch }) => {
  const search = useSearchState()

  const handleSearchKeyDown: React.KeyboardEventHandler<HTMLFormElement> = event => {
    if (event.key !== 'Enter') return

    onSearch({
      search: search.value,
      page: 1,
    })
  }

  const { onClear, ...inputProps } = search.getInputProps()

  const handleSearchClear = () => {
    onClear()

    onSearch({
      search: '',
      page: 1,
    })
  }

  return (
    <Search
      rel=""
      {...inputProps}
      onClear={handleSearchClear}
      onKeyDown={handleSearchKeyDown}
      className={csx({ marginRight: '$space-5' })}
    />
  )
}

export default OrganizationsListSearch

```


[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 b2b-organizations/react/admin/OrganizationsList/OrganizationsListStatusFilter/index.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
export { default } from './OrganizationsListStatusFilter'

```


[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 b2b-organizations/react/admin/OrganizationsList/OrganizationsListStatusFilter/OrganizationsListStatusFilter.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect } from 'react'
import {
  FilterDisclosure,
  FilterFooter,
  FilterListbox,
  FilterOptionCheckbox,
  FilterPopover,
  useFilterMultipleState,
} from '@vtex/admin-ui'
import { useIntl } from 'react-intl'

import { organizationMessages as messages } from '../../utils/messages'

const AllStatus = ['active', 'on-hold', 'inactive']

interface Props {
  onChange: (value: string[]) => void
}

const OrganizationsListStatusFilter: React.FC<Props> = ({ onChange }) => {
  const { formatMessage } = useIntl()
  const state = useFilterMultipleState()
  const { appliedItems } = state

  useEffect(() => {
    let statuses = appliedItems.map(({ id }) => id)

    if (statuses.length === 0) {
      statuses = AllStatus
    }

    onChange(statuses)
  }, [appliedItems])

  return (
    <>
      <FilterDisclosure state={state}>
        {formatMessage(messages.filterStatus)}
      </FilterDisclosure>

      <FilterPopover state={state}>
        <FilterListbox>
          {AllStatus.map(item => (
            <FilterOptionCheckbox key={item} label={item} id={item} />
          ))}
        </FilterListbox>
        <FilterFooter />
      </FilterPopover>
    </>
  )
}

export default OrganizationsListStatusFilter

```


[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 b2b-organizations/react/utils/metrics/changeTeam.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Session } from '../../modules/session'
import type { Metric } from './metrics'
import { sendMetric } from './metrics'

type ChangeTeamFieldsMetric = {
  date: string
  user_role: string
  user_email?: string
  org_id: string
  cost_center_id: string
}

type ChangeTeamMetric = Metric & { fields: ChangeTeamFieldsMetric }

export type StopImpersonateMetricParams = {
  sessionResponse: Session
  currentCostCenter: string
  costCenterInput: string
  currentOrganization: string
  organizationInput: string
  email: string
}

export type ChangeTeamParams = {
  sessionResponse: Session
  currentRoleName: string
  currentOrganization: string
  currentCostCenter: string
}

const buildMetric = (metricParams: ChangeTeamParams): ChangeTeamMetric => {
  return {
    name: 'b2b-suite-buyerorg-data' as const,
    account:
      metricParams.sessionResponse?.namespaces?.account?.accountName?.value,
    kind: 'change-team-ui-event',
    description: 'User change team/organization - UI',
    fields: {
      date: new Date().toISOString(),
      user_role: metricParams.currentRoleName,
      user_email:
        metricParams.sessionResponse?.namespaces?.profile?.email?.value,
      org_id: metricParams.currentOrganization,
      cost_center_id: metricParams.currentCostCenter,
    },
  }
}

export const sendChangeTeamMetric = async (metricParams: ChangeTeamParams) => {
  try {
    const metric: ChangeTeamMetric = buildMetric(metricParams)

    await sendMetric(metric)
  } catch (error) {
    console.warn('Unable to log metrics', 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 b2b-organizations/react/utils/metrics/impersonate.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { B2BUserSimple } from '../../components/OrganizationUsersTable'
import type { Session } from '../../modules/session'
import { getSession } from '../../modules/session'
import type { Metric } from './metrics'
import { sendMetric } from './metrics'

type ImpersonatePerson = {
  email: string
  buyer_org_id: string
  cost_center_id: string
  cost_center_name: string
}

type ImpersonateTarget = ImpersonatePerson & { buyer_org_name: string }

type ImpersonateFieldsMetric = {
  user: ImpersonatePerson
  target: ImpersonateTarget
  date: string
}

type ImpersonateMetric = Metric & { fields: ImpersonateFieldsMetric }

export type ImpersonateMetricParams = {
  costCenterData: {
    getCostCenterByIdStorefront: {
      id: string
      name: string
      organization: string
    }
  }
  target: B2BUserSimple
}

export type StopImpersonateMetricParams = {
  sessionResponse: Session
  currentCostCenter: string
  costCenterInput: string
  currentOrganization: string
  organizationInput: string
  email: string
}

const buildImpersonateMetric = async (
  metricParams: ImpersonateMetricParams
) => {
  const { target, costCenterData } = metricParams

  const session = await getSession()
  const sessionResponse = session?.response
  let namespace

  if (sessionResponse && 'namespaces' in sessionResponse) {
    namespace = sessionResponse?.namespaces
  }

  return {
    name: 'b2b-suite-buyerorg-data' as const,
    kind: 'impersonate-ui-event',
    description: 'Impersonate User Action - UI',
    account: namespace?.account?.accountName?.value,
    fields: {
      user: {
        email: namespace?.profile?.email?.value,
        buyer_org_id: costCenterData?.getCostCenterByIdStorefront.organization,
        cost_center_id: costCenterData?.getCostCenterByIdStorefront.id,
        cost_center_name: costCenterData?.getCostCenterByIdStorefront.name,
      },
      target: {
        email: target.email,
        buyer_org_id: target.orgId,
        buyer_org_name: target.organizationName,
        cost_center_id: target.costId,
        cost_center_name: target.costCenterName,
      },
      date: new Date().toISOString(),
    },
  } as ImpersonateMetric
}

const buildStopImpersonateMetric = (
  metricParams: StopImpersonateMetricParams
) => {
  return {
    name: 'b2b-suite-buyerorg-data' as const,
    kind: 'stop-impersonate-ui-event',
    description: 'Stop Impersonate User Action - UI',
    account:
      metricParams.sessionResponse?.namespaces?.account?.accountName?.value,
    fields: {
      user: {
        email:
          metricParams.sessionResponse?.namespaces?.authentication
            ?.storeUserEmail?.value,
      },
      target: {
        email: metricParams.email,
        buyer_org_id: metricParams.currentOrganization,
        buyer_org_name: metricParams.organizationInput,
        cost_center_id: metricParams.currentCostCenter,
        cost_center_name: metricParams.costCenterInput,
      },
      date: new Date().toISOString(),
    },
  } as ImpersonateMetric
}

export const sendImpersonateMetric = async (
  metricParams: ImpersonateMetricParams
) => {
  try {
    const metric = await buildImpersonateMetric(metricParams)

    await sendMetric(metric)
  } catch (error) {
    console.warn('Unable to log metrics', error)
  }
}

export const sendStopImpersonateMetric = async (
  metricParams: StopImpersonateMetricParams
) => {
  try {
    const metric = buildStopImpersonateMetric(metricParams)

    await sendMetric(metric)
  } catch (error) {
    console.warn('Unable to log metrics', 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 b2b-organizations/react/utils/metrics/metrics.ts from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import axios from 'axios'

const ANALYTICS_URL = 'https://rc.vtex.com/api/analytics/schemaless-events'

type ImpersonateMetric = {
  kind: 'impersonate-ui-event'
  description: 'Impersonate User Action - UI'
}

type StopImpersonateMetric = {
  kind: 'stop-impersonate-ui-event'
  description: 'Stop Impersonate User Action - UI'
}

export type ChangeTeamMetric = {
  kind: 'change-team-ui-event'
  description: 'User change team/organization - UI'
}

export type Metric = {
  name: 'b2b-suite-buyerorg-data'
  account: string
} & (ImpersonateMetric | StopImpersonateMetric | ChangeTeamMetric)

export const sendMetric = async (metric: Metric) => {
  try {
    await axios.post(ANALYTICS_URL, metric)
  } catch (error) {
    console.warn('Unable to log metrics', 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 b2b-organizations/react/components/CustomFieldInputList/CustomFieldInputList.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { Spinner } from 'vtex.styleguide'

import CustomFieldInput from '../../admin/OrganizationDetailsCustomField'

interface Props {
  customFields: CustomField[] | null
  onChange: (customFields: CustomField[]) => void
}

const CustomFieldInputList: React.FC<Props> = ({ customFields, onChange }) => {
  const isLoading = !customFields

  const handleUpdate = (index: number, customField: CustomField) => {
    if (!customFields) return

    const newCustomFields = [...customFields]

    newCustomFields[index] = customField
    onChange(newCustomFields)
  }

  return (
    <>
      {isLoading ? (
        <div className="mb5">
          <Spinner />
        </div>
      ) : (
        customFields?.map((customField: CustomField, index: number) => {
          return (
            <CustomFieldInput
              key={`${customField.name}`}
              index={index}
              handleUpdate={handleUpdate}
              customField={customField}
            />
          )
        })
      )}
    </>
  )
}

export default CustomFieldInputList

```


[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 b2b-organizations/react/components/CreateOrganizationButton/index.tsx from the b2b-organizations VTEX app.

[Assistant]
Here's the implementation:

```tsx
export { default } from './CreateOrganizationButton'

```