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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { WithApolloClient, withApollo } from 'react-apollo'
import { useIntl } from 'react-intl'
import { ToastConsumerFunctions } from 'vtex.styleguide'

import { useEditorContext } from '../../../EditorContext'
import BindingSelector from './BindingSelector'
import TenantInfoQuery from './graphql/TenantInfo.graphql'
import LocaleSelector from './LocaleSelector'
import { QueryStateReducer, TenantInfoData } from './typings'
import { useBinding } from './hooks/useBinding'
import BindingCloning from './BindingCloning'

interface Props {
  iframeRuntime: RenderContext
  showToast: ToastConsumerFunctions['showToast']
}

const ContextSelectors: React.FC<WithApolloClient<Props>> = ({
  client,
  iframeRuntime,
  showToast,
}) => {
  const [queryState, setQueryState] = React.useReducer<QueryStateReducer>(
    (prevState, state) => ({
      ...prevState,
      ...state,
    }),
    { data: undefined, hasError: false, isLoading: true }
  )

  const { binding: iframeBinding, query } = iframeRuntime
  const explicitBinding = Boolean(query?.__bindingAddress)
  const [binding, setBinding] = useBinding()
  const intl = useIntl()

  React.useEffect(() => {
    if (iframeBinding || explicitBinding) {
      const fetchBindings = async () => {
        let data, hasError

        try {
          const result = await client.query<TenantInfoData>({
            fetchPolicy: 'network-only',
            query: TenantInfoQuery,
          })

          data = result.data
          hasError = !!result.errors

          setBinding(
            iframeBinding &&
              data.tenantInfo.bindings &&
              data.tenantInfo.bindings.find(
                item => item.id === iframeBinding.id
              )
          )
        } catch {
          hasError = true

          showToast({
            horizontalPosition: 'right',
            message: intl.formatMessage({
              defaultMessage:
                'The list of available bindings could not be loaded.',
              id: 'admin/pages.editor.topbar.context.binding.error',
            }),
          })
        } finally {
          setQueryState({ data, hasError, isLoading: false })
        }
      }

      fetchBindings()
    }
  }, [client, explicitBinding, setBinding, iframeBinding, intl, showToast])

  const bindings = React.useMemo(
    () =>
      queryState.data &&
      queryState.data.tenantInfo.bindings &&
      queryState.data.tenantInfo.bindings.filter(
        item => item.targetProduct === 'vtex-storefront'
      ),
    [queryState.data]
  )

  const editor = useEditorContext()

  const handleBindingChange = React.useCallback<
    React.ChangeEventHandler<HTMLSelectElement>
  >(
    ({ target: { value } }) => {
      if (bindings) {
        const newBinding = bindings.find(binding => binding.id === value)

        if (newBinding && editor.iframeWindow) {
          setBinding(newBinding)

          editor.iframeWindow.location.search = `__bindingAddress=${newBinding.canonicalBaseAddress}&__siteEditor=true`
        }

        editor.setMode('disabled')
      }
    },
    [bindings, setBinding, editor.iframeWindow]
  )

  const bindingOptions = React.useMemo(
    () =>
      bindings
        ? bindings.map(binding => ({
            label: binding.canonicalBaseAddress,
            value: binding.id,
          }))
        : [],
    [bindings]
  )

  const localeOptions = React.useMemo(
    () =>
      binding
        ? binding.supportedLocales.map(locale => ({
            label:
              intl.formatMessage({
                id: `admin/pages.editor.locale.${locale.split('-')[0]}`,
              }) + ` (${locale})`,
            value: locale,
          }))
        : editor.availableCultures,
    [binding, editor.availableCultures, intl]
  )

  const shouldShowBindingSelector = !!(
    (iframeBinding || explicitBinding) &&
    bindings &&
    bindings.length > 1 &&
    !queryState.hasError
  )

  return (
    <>
      {shouldShowBindingSelector && (
        <BindingSelector
          isDisabled={queryState.isLoading || !!editor.editTreePath}
          onChange={handleBindingChange}
          options={bindingOptions}
          value={binding ? binding.id : ''}
        />
      )}

      <LocaleSelector
        className={
          shouldShowBindingSelector
            ? 'locale-selector-with-bindings'
            : undefined
        }
        iframeRuntime={iframeRuntime}
        isDisabled={!!editor.editTreePath}
        options={localeOptions}
      />
      {shouldShowBindingSelector && bindings && binding && (
        <div className="mh3">
          <BindingCloning
            bindings={bindings}
            binding={binding}
            iframeRuntime={iframeRuntime}
          />
        </div>
      )}
    </>
  )
}

export default withApollo(ContextSelectors)

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/BindingSelector.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

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

import { useEditorContext } from '../../../EditorContext'
import { DropdownOptions, DropdownValue } from './typings'

interface Props {
  isDisabled: boolean
  onChange: React.ChangeEventHandler<HTMLSelectElement>
  options: DropdownOptions
  value: DropdownValue
}

const BindingSelector: React.FC<Props> = ({
  isDisabled,
  onChange,
  options,
  value,
}) => {
  const Selector = React.useCallback(
    () => (
      <div
        className={
          'binding-selector' + (isDisabled ? ' binding-selector-disabled' : '')
        }
      >
        <Dropdown
          disabled={isDisabled}
          onChange={onChange}
          options={options}
          size="small"
          value={value}
        />
      </div>
    ),
    [isDisabled, onChange, options, value]
  )

  const editor = useEditorContext()
  const intl = useIntl()

  return <>
    {editor.editTreePath ? (
      <Tooltip
        label={intl.formatMessage({
          defaultMessage: 'The binding cannot be changed while editing a block.',
          id: 'admin/pages.editor.topbar.context.binding.tooltip',
        })}
        position="bottom"
      >
        <div className="flex">
          <Selector />
        </div>
      </Tooltip>
    ) : (
      <Selector />
    )}
  </>
}

export default BindingSelector

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/LocaleSelector.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

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

import { DropdownOptions } from './typings'
import { useEditorContext } from '../../../EditorContext'

interface Props {
  className?: string
  iframeRuntime: RenderContext
  isDisabled: boolean
  options: DropdownOptions
}

const LocaleSelector: React.FC<Props> = ({
  className,
  iframeRuntime,
  isDisabled,
  options,
}) => {
  const { culture, emitter } = iframeRuntime

  const editor = useEditorContext()

  const [locale, setLocale] = React.useState(culture.locale)

  const handleChange = React.useCallback(
    (e: React.ChangeEvent<HTMLSelectElement>) => {
      editor.setMode('disabled')

      setLocale(e?.target?.value)

      emitter.emit(
        'localesChanged',
        e?.target?.value,
        null,
        (locale: string) => {
          let bindingQueryString

          if (editor.iframeWindow) {
            const searchParams = new URLSearchParams(
              editor?.iframeWindow?.location.search
            )

            const bindingAddress = searchParams.get('__bindingAddress')

            if (bindingAddress) {
              bindingQueryString = `__bindingAddress=${decodeURIComponent(
                bindingAddress
              )}`
            }

            if (e?.target?.value) {
              editor.iframeWindow.location.search = `${bindingQueryString}&__locale=${e.target.value}&__siteEditor=true`
            } else {
              editor.iframeWindow.location.search = `${bindingQueryString}&__locale=${locale}&__siteEditor=true`
            }
          }
        }
      )
    },
    [emitter]
  )

  React.useEffect(() => {
    if (locale !== culture.locale) {
      setLocale(culture.locale)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [culture.locale])

  React.useEffect(() => {
    if (editor.mode !== 'content') {
      editor.setMode('content')
    }
  }, [culture.locale, culture.country])

  const Selector = React.useCallback(
    () => (
      <div className={className}>
        <Dropdown
          disabled={isDisabled}
          onChange={handleChange}
          options={options}
          size="small"
          value={locale}
        />
      </div>
    ),
    [className, handleChange, isDisabled, locale, options]
  )

  const intl = useIntl()

  return editor.editTreePath ? (
    <Tooltip
      label={intl.formatMessage({
        defaultMessage: 'The locale cannot be changed while editing a block.',
        id: 'admin/pages.editor.topbar.context.locale.tooltip',
      })}
      position="bottom"
    >
      <div className="flex">
        <Selector />
      </div>
    </Tooltip>
  ) : (
    <Selector />
  )
}

export default LocaleSelector

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/typings.d.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { QueryResult } from 'react-apollo'

export type DropdownOptions = {
  label: string
  value: string
}[]

export type DropdownValue = string

type BaseAddress = string

type Locale = string

interface Binding {
  canonicalBaseAddress: string
  id: string
  supportedLocales: Locale[]
  targetProduct: string
}

interface TenantInfo {
  bindings: Binding[]
}

export interface TenantInfoData {
  tenantInfo: TenantInfo
}

interface QueryState extends Pick<QueryResult<TenantInfoData>, 'data'> {
  hasError: boolean
  isLoading: QueryResult<TenantInfoData>['loading']
}

export type QueryStateReducer = (
  prevState: QueryState,
  state: Partial<QueryState>
) => QueryState

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/icons/IconPicker.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
}

const IconEdit: React.FC<Props> = ({ color = 'currentColor' }) => (
  <svg
    width="16"
    height="16"
    viewBox="0 0 16 16"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M15 5.2L15 2.4C15 2.0287 14.8525 1.6726 14.5899 1.41005C14.3274 1.1475 13.9713 1 13.6 1L2.4 1C2.0287 1 1.6726 1.1475 1.41005 1.41005C1.1475 1.6726 0.999999 2.0287 0.999999 2.4L1 13.6C1 13.9713 1.1475 14.3274 1.41005 14.5899C1.6726 14.8525 2.0287 15 2.4 15L5.2 15"
      stroke={color}
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path
      fillRule="evenodd"
      clipRule="evenodd"
      d="M6 6L8.6329 15.8734L11.5549 12.9513L14.6037 16.0001L16.0001 14.6038L12.9513 11.555L15.8734 8.6329L6 6Z"
      fill={color}
    />
  </svg>
)

export default IconEdit

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/icons/CopyContent.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
}

const CopyContent = ({ color = 'currentColor' }: Props) => (
  <svg
    name="copy"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 18 18"
  >
    <path
      d="M6.4 0H9.2C10.3046 0 11.2 0.895431 11.2 2V9.2C11.2 10.3046 10.3046 11.2 9.2 11.2H2C0.89543 11.2 0 10.3046 0 9.2V6.4"
      transform="translate(5.80005 12.2) rotate(-90)"
      stroke={color}
      strokeWidth="2"
      strokeMiterlimit="10"
      strokeLinecap="square"
    />
    <path
      d="M9.2 0H2C0.895431 0 0 0.89543 0 2V9.2C0 10.3046 0.89543 11.2 2 11.2H9.2C10.3046 11.2 11.2 10.3046 11.2 9.2V2C11.2 0.895431 10.3046 0 9.2 0Z"
      transform="translate(1 17) rotate(-90)"
      stroke={color}
      strokeWidth="2"
      strokeMiterlimit="10"
      strokeLinecap="square"
    />
  </svg>
)

export default CopyContent

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/icons/IconView.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
}

const IconView: React.FC<Props> = ({ color = 'currentColor' }) => (
  <svg
    width="17"
    height="14"
    viewBox="0 0 17 14"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M1 7C1 7 4 1.5 8.5 1.5C13 1.5 16 7 16 7C16 7 13 12.5 8.5 12.5C4 12.5 1 7 1 7Z"
      stroke={color}
      strokeWidth="1.5"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path
      d="M8.5 9.5C9.88071 9.5 11 8.38071 11 7C11 5.61929 9.88071 4.5 8.5 4.5C7.11929 4.5 6 5.61929 6 7C6 8.38071 7.11929 9.5 8.5 9.5Z"
      stroke={color}
      strokeWidth="1.5"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </svg>
)

export default IconView

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/DeviceSwitcher/icons/IconDesktop.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
}

const IconDesktop: React.FC<Props> = ({ color = 'currentColor' }) => (
  <svg
    width="16"
    height="17"
    viewBox="0 0 16 17"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M5 16H11"
      stroke={color}
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path
      d="M14 1H2C1.44772 1 1 1.44771 1 2V11C1 11.5523 1.44772 12 2 12H14C14.5523 12 15 11.5523 15 11V2C15 1.44772 14.5523 1 14 1Z"
      stroke={color}
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </svg>
)

export default IconDesktop

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/DeviceSwitcher/icons/IconMobile.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
}

const IconPhone: React.FC<Props> = ({ color = 'currentColor' }) => (
  <svg
    width="10"
    height="16"
    viewBox="0 0 10 16"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M9 13L9 3C9 1.89543 8.10457 1 7 1L3 1C1.89543 1 1 1.89543 1 3L1 13C1 14.1046 1.89543 15 3 15L7 15C8.10457 15 9 14.1046 9 13Z"
      stroke={color}
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <circle cx="5" cy="12" r="0.5" fill="#CACBCC" stroke={color} />
  </svg>
)

export default IconPhone

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/DeviceSwitcher/icons/IconTablet.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
}

const IconTablet: React.FC<Props> = ({ color = 'currentColor' }) => (
  <svg
    width="14"
    height="18"
    viewBox="0 0 14 18"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M13 15L13 3C13 1.89543 12.1046 1 11 1L3 1C1.89543 1 1 1.89543 1 3L1 15C1 16.1046 1.89543 17 3 17L11 17C12.1046 17 13 16.1046 13 15Z"
      stroke={color}
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <circle cx="7" cy="13" r="0.5" fill="#CACBCC" stroke={color} />
  </svg>
)

export default IconTablet

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/graphql/TenantInfo.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
query TenantInfoQuery {
  tenantInfo {
    bindings {
      canonicalBaseAddress
      id
      supportedLocales
      targetProduct
    }
  }
}

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/graphql/CopyBindings.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation CopyBindingContent($from: String!, $to: String!, $template: String, $context: PageContextInput) {
  copyBindingContent(from: $from, to: $to, template: $template, context: $context) {
    added {
      contentId
      origin
    }
    removed {
      contentId
      origin
    }
  }
}

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/graphql/SaveRoute.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation SaveRoute($route: NewRouteInput) {
  saveRoute(route: $route) {
    auth
    blockId
    binding
    context
    declarer
    domain
    interfaceId
    path
    routeId
    uuid
    metaTags {
      description
      keywords
      robots
    }
    title
  }
}

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/graphql/appSettings.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

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

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/graphql/GetRoute.graphql from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```graphql
query GetRoute($routeId: String!) {
  route(routeId: $routeId, domain: "store") {
    auth
    blockId
    binding
    context
    declarer
    domain
    interfaceId
    conflicts {
      binding
      blockId
      interfaceId
      routeId
    }
    pages {
      pageId
      condition {
        id
        pageContext {
          id
          type
        }
        allMatches
        statements {
          subject
          verb
          objectJSON
        }
      }
      template
    }
    path
    routeId
    title
    uuid
    metaTags {
      description
      keywords
      robots
    }
  }
}

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/hooks/useBinding.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import React, { useMemo } from 'react'

import { Binding } from '../typings'

const LAST_BINDING_KEY = 'vtex.site-editor.lastBinding'

export const useBinding = (): [
  Binding | undefined,
  React.Dispatch<Binding | undefined>
] => {
  const [binding, setBinding] = React.useState<Binding | undefined>(() => {
    const lastBinding = window.localStorage.getItem(LAST_BINDING_KEY)
    return lastBinding ? (JSON.parse(lastBinding) as Binding) : undefined
  })

  return [
    binding,
    useMemo(() => {
      return (value: Binding | undefined) => {
        if (value) {
          window.localStorage.setItem(LAST_BINDING_KEY, JSON.stringify(value))
        } else {
          window.localStorage.removeItem(LAST_BINDING_KEY)
        }
        setBinding(value)
      }
    }, [setBinding]),
  ]
}

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/BindingCloning/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, FunctionComponent, useState } from 'react'
import { useKeydownFromClick } from 'keydown-from-click'
import { Tooltip } from 'vtex.styleguide'
import { FormattedMessage } from 'react-intl'
import { compose, graphql } from 'react-apollo'

import { useEditorContext } from '../../../../EditorContext'
import { useHover } from '../../hooks'
import CopyContent from '../../icons/CopyContent'
import { Binding } from '../typings'
import BindingCloningModal from './BindingCloningModal'
import { CloneContentProvider } from './CloneContentContext'
import APP_SETTINGS from '../graphql/appSettings.graphql'

interface Props {
  bindings: Binding[]
  binding: Binding
  iframeRuntime: RenderContext
}

const withSettings = compose(
  graphql(APP_SETTINGS, {
    options: () => {
      const [appName, version] = (process.env.VTEX_APP_ID as string).split('@')
      return {
        variables: {
          appName,
          version,
        },
      }
    },
  })
)

const FeatureFlagComponent: FC<{ data: { appSettings?: AppSettings } }> = ({
  children,
  data,
}) => {
  const { message } = data.appSettings ?? {}
  const settingsParsed = message && (JSON.parse(message) as SettingsParsed)
  const enableFeature =
    settingsParsed && Boolean(settingsParsed.copyContentBinding)

  return <>{enableFeature ? children : null}</>
}

const FeatureFlag = withSettings(FeatureFlagComponent)

const BindingCloning: FunctionComponent<Props> = ({
  bindings,
  binding,
  iframeRuntime,
}) => {
  const [isModalOpen, setModalOpen] = useState(false)
  const editor = useEditorContext()
  const { handleMouseEnter, handleMouseLeave, hover } = useHover()

  const handleClick = () => {
    setModalOpen(true)
  }

  const handleKeyPress = useKeydownFromClick(handleClick)

  const { route, binding: iframeBinding } = iframeRuntime ?? {}
  const { id: routeId, pageContext } = route ?? {}

  return (
    <FeatureFlag>
      <Tooltip
        label={
          <FormattedMessage id="admin/pages.editor.topbar.button.copy-page.tooltip" />
        }
        position="bottom"
      >
        <button
          style={editor.mode === 'disabled' ? { cursor: 'wait' } : {}}
          className={`w2 h2 pa3 bg-white br2 b--transparent outline-0 pointer flex justify-center items-center ${
            editor.editMode || (hover && editor.mode !== 'disabled')
              ? 'c-action-primary'
              : 'c-on-disabled'
          }`}
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}
          onClick={handleClick}
          onKeyPress={handleKeyPress}
        >
          <CopyContent />
        </button>
      </Tooltip>
      <CloneContentProvider
        routeId={routeId}
        bindings={bindings}
        currentBinding={binding}
        pageContext={pageContext}
        iframeBinding={iframeBinding}
      >
        <BindingCloningModal
          isOpen={isModalOpen}
          onClose={() => {
            setModalOpen(false)
          }}
        />
      </CloneContentProvider>
    </FeatureFlag>
  )
}

export default BindingCloning

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/BindingCloning/BindingSelector.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FunctionComponent, useReducer } from 'react'
import { FormattedMessage } from 'react-intl'
import { Checkbox, IconWarning } from 'vtex.styleguide'

export interface BindingSelectorItem {
  label: string
  id: string
  supportedLocales?: string[]
  checked: boolean
  overwrites?: boolean
  isCurrent?: boolean
  disabled?: boolean
}

export type BindingSelectorState = BindingSelectorItem[]

export type BindingSelectorAction =
  | {
      type: 'set'
      payload: {
        id: string
        value: boolean
      }
    }
  | {
      type: 'toggle'
      payload: { id: string }
    }
  | {
      type: 'check-all' | 'uncheck-all'
    }
  | {
      type: 'set-initial'
      payload: BindingSelectorItem[]
    }

const reducer = (
  state: BindingSelectorState,
  action: BindingSelectorAction
) => {
  switch (action.type) {
    case 'toggle': {
      const { id } = action.payload

      return state.map(item => {
        if (item.id === id && !item.disabled && !item.isCurrent) {
          return {
            ...item,
            checked: !item.checked,
          }
        }

        return item
      })
    }
    case 'set': {
      const { id, value } = action.payload

      return state.map(item => {
        if (item.id === id && !item.disabled && !item.isCurrent) {
          return {
            ...item,
            checked: value,
          }
        }
        return item
      })
    }
    case 'check-all':
      return state.map(item => ({
        ...item,
        checked: !item.disabled && !item.isCurrent,
      }))
    case 'uncheck-all':
      return state.map(item => ({
        ...item,
        checked: false,
      }))
    case 'set-initial': {
      return action.payload
    }
    default:
      return state
  }
}

export const setInitialBindingState = (bindings: BindingSelectorItem[]) => ({
  type: 'set-initial' as const,
  payload: bindings,
})

type BindingSelectorReducer = typeof reducer

export const useBindingSelectorReducer = () => {
  return useReducer<BindingSelectorReducer>(reducer, [])
}

interface Props {
  reducer: [BindingSelectorState, React.Dispatch<BindingSelectorAction>]
  pathId: string
}

const BindingSelector: FunctionComponent<Props> = ({ reducer, pathId }) => {
  const [state, dispatch] = reducer

  const areAllSelected = state.every(
    item => item.checked || item.disabled || item.isCurrent
  )
  const areSomeSelected = state.some(item => item.checked)

  return (
    <table className="w-100">
      <thead className="w-100 ph4 truncate overflow-x-hidden c-muted-2 f6">
        <tr className="w-100 truncate overflow-x-hidden">
          <th className="v-mid pv0 tl bb b--muted-4 normal bg-base bt ph3 z1 pv3-s">
            <Checkbox
              id="checkAll"
              onChange={() => {
                dispatch({
                  type: areAllSelected ? 'uncheck-all' : 'check-all',
                })
              }}
              checked={areAllSelected}
              partial={areSomeSelected}
            />
          </th>
          <th className="v-mid pv0 tl bb b--muted-4 normal bg-base bt ph3 z1 pv3-s">
            Binding
          </th>
          <th className="v-mid pv0 tl bb b--muted-4 normal bg-base bt ph3 z1 pv3-s">
            <FormattedMessage id="admin/pages.editor.copy-page.modal.table-header.supported-locales" />
          </th>
          <th className="v-mid pv0 tl bb b--muted-4 normal bg-base bt ph3 z1 pv3-s">
            <span className="flex items-center">
              <IconWarning />{' '}
              <p className="ml3-s">
                <FormattedMessage id="admin/pages.editor.copy-page.modal.table-header.warning" />
              </p>
            </span>
          </th>
        </tr>
      </thead>
      <tbody>
        {state.map(item => (
          <tr
            className={`w-100 truncate overflow-x-hidden ${
              item.checked ? 'bg-washed-blue' : ''
            }`}
            key={item.id}
          >
            <td className="v-mid pv0 tl bb b--muted-4 ph3 z1 pv4-s">
              <Checkbox
                id={item.id}
                onChange={() =>
                  dispatch({
                    type: 'toggle',
                    payload: {
                      id: item.id,
                    },
                  })
                }
                checked={item.checked}
                disabled={item.disabled || item.isCurrent}
              />
            </td>
            <td className="v-mid pv0 tl bb b--muted-4 ph3 z1 pv4-s">
              <label htmlFor={item.id}>
                {item.label}
                <span className="c-muted-2">{pathId}</span>
              </label>
            </td>
            <td className="v-mid pv0 tl bb b--muted-4 ph3 z1 pv4-s">
              {item.supportedLocales && item.supportedLocales.length > 1 ? (
                <p>{item.supportedLocales.join(', ')}</p>
              ) : null}
            </td>
            <td className="v-mid pv0 tl bb b--muted-4 ph3 z1 pv4-s">
              {item.overwrites && !item.isCurrent && (
                <div className="f6 c-muted-1 dib ml4">
                  <FormattedMessage id="admin/pages.editor.copy-page.modal.warning.page-exists" />
                </div>
              )}
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

export default BindingSelector

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/BindingCloning/OverwriteDialog.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

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

import { BindingSelectorState } from './BindingSelector'

interface Props {
  isOpen?: boolean
  state: BindingSelectorState
  pathId: string
  onClose: () => void
  onConfirm?: (value: void | PromiseLike<void>) => void
  onCancel?: (reason?: unknown) => void
}

export const useOverwriteDialogState = () => {
  const [overwriteHandlers, setOverwriteHandlers] = useState<null | {
    onConfirm: (value: void | PromiseLike<void>) => void
    onCancel: (reason?: unknown) => void
  }>(null)

  return {
    isOverwriteDialogOpen: Boolean(overwriteHandlers),
    showOverwriteDialog: setOverwriteHandlers,
    hideOverwriteDialog: () => {
      setOverwriteHandlers(null)
    },
    onOverwriteConfirm: overwriteHandlers?.onConfirm ?? (() => undefined),
    onOverwriteCancel: overwriteHandlers?.onCancel ?? (() => undefined),
  }
}

const OverwriteDialog: FunctionComponent<Props> = ({
  isOpen,
  onClose,
  state,
  pathId,
  onConfirm,
  onCancel,
}) => {
  const overwrittenContent =
    state?.filter(item => item.overwrites && item.checked) ?? []

  return (
    <Modal
      isOpen={isOpen}
      onClose={onClose}
      bottomBar={
        <div className="flex justify-end">
          <span className="mr4">
            <Button
              variation="secondary"
              onClick={() => {
                onCancel?.()
                onClose()
              }}
            >
              <FormattedMessage id="admin/pages.editor.components.button.go-back" />
            </Button>
          </span>
          <Button
            variation="danger"
            onClick={() => {
              onConfirm?.()
              onClose()
            }}
          >
            <FormattedMessage id="admin/pages.editor.components.button.overwrite" />
          </Button>
        </div>
      }
    >
      <div className="mb6">
        <div className="mb6">
          <h2 className="mb4">
            <FormattedMessage id="admin/pages.editor.copy-page.overwritedialog.header" />
          </h2>
          {overwrittenContent.length > 1 ? (
            <FormattedMessage id="admin/pages.editor.copy-page.overwritedialog.subheader.singular" />
          ) : (
            <FormattedMessage id="admin/pages.editor.copy-page.overwritedialog.subheader.plural" />
          )}
        </div>
        <div className="mb6 ml6">
          <ul>
            {overwrittenContent.map(item => (
              <li className="mb4" key={item.id}>
                <div>
                  {item.label}
                  <span className="c-muted-2">{pathId}</span>
                </div>
                {item.supportedLocales && item.supportedLocales.length > 0 && (
                  <ul className="list pa0 ma0 mt2 ml6 f6 c-muted-1">
                    {item.supportedLocales.map((locale, i, arr) => (
                      <li className="dib" key={locale}>
                        {locale}
                        {i < arr.length - 1 && <span>, </span>}
                      </li>
                    ))}
                  </ul>
                )}
              </li>
            ))}
          </ul>
        </div>
        <FormattedMessage id="admin/pages.editor.copy-page.overwritedialog.confirmation" />
      </div>
    </Modal>
  )
}

export default OverwriteDialog

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/BindingCloning/BindingCloningModal.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FunctionComponent, useEffect, useRef } from 'react'
import {
  Button,
  Modal,
  ShowToastFunction,
  Spinner,
  ToastConsumer,
  EmptyState,
} from 'vtex.styleguide'
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'
import { pick } from 'ramda'
import { useRuntime } from 'vtex.render-runtime'

import BindingSelector, { BindingSelectorItem } from './BindingSelector'
import {
  CopyBindingVariables,
  SaveRouteVariables,
  useCloneContent,
} from './CloneContentContext'
import OverwriteDialog, { useOverwriteDialogState } from './OverwriteDialog'
import { createEventObject } from '../../../../../utils/auditEvents'
import SendEventToAuditMutation, {
  SendEventToAuditMutationFn,
} from '../../../mutations/SendEventToAudit'

interface Props {
  isOpen?: boolean
  onClose: () => void
}

const saveRouteSanityCheck = (variables: SaveRouteVariables) => {
  const route = variables?.route

  if (
    !route ||
    // uuid shouldn't be defined, it will be generated on route creation,
    // otherwise it might replace an existing route
    route.uuid ||
    // dataSource needs to be 'vtex.rewriter', otherwise it will be
    // treated as a "colossus" route, which is not what we want here
    route.dataSource !== 'vtex.rewriter' ||
    !route.bindingId
  ) {
    return false
  }

  return true
}

const copyBindingsSanityCheck = (variables: CopyBindingVariables) => {
  if (
    !variables ||
    // If any of these values is empty, it might cause the
    // copyBindingContent mutation to be too broad, so we
    // are double checking them here to be a bit more safe
    !variables.from ||
    !variables.to ||
    !variables.template ||
    !variables.context ||
    !variables.context.type ||
    !variables.context.id
  ) {
    return false
  }

  return true
}

type SubmitStatus = 'IDLE' | 'SUBMITTING'

const toastMessages = defineMessages({
  saving: {
    id: 'admin/pages.editor.copy-page.modal.toast.saving',
  },
  success: {
    id: 'admin/pages.editor.copy-page.modal.toast.success',
  },
  error: {
    id: 'admin/pages.editor.copy-page.modal.toast.error',
  },
})

const BindingCloningModal: FunctionComponent<Props> = ({ isOpen, onClose }) => {
  const [submitStatus, setSubmitStatus] = React.useState<SubmitStatus>('IDLE')
  const intl = useIntl()

  const {
    showOverwriteDialog,
    isOverwriteDialogOpen,
    hideOverwriteDialog,
    onOverwriteCancel,
    onOverwriteConfirm,
  } = useOverwriteDialogState()

  const { data, actions } = useCloneContent()

  const {
    loading,
    error,
    routeInfo,
    bindingSelector,
    currentBinding,
    pageContext,
  } = data

  const {
    refetchRouteInfo,
    dispatchBindingSelector,
    saveRoute,
    copyBindings,
  } = actions
  const { account, workspace } = useRuntime()

  const checkOverwrites = () => {
    return new Promise<void>((resolve, reject) => {
      const checkedItems = bindingSelector.filter(item => item.checked)
      const willOverwrite = checkedItems.some(item => item.overwrites)

      if (!willOverwrite) {
        resolve()
        return
      }

      showOverwriteDialog({
        onConfirm: resolve,
        onCancel: reject,
      })
    })
  }

  // Refetches and cleans up selection when the modal is
  // closed and reopened
  const wasOpen = useRef(true)
  useEffect(() => {
    if (!wasOpen.current && isOpen) {
      refetchRouteInfo()
      dispatchBindingSelector({ type: 'uncheck-all' })
      wasOpen.current = false
    }

    if (wasOpen.current && isOpen) {
      wasOpen.current = false
    }
  }, [wasOpen, isOpen, refetchRouteInfo, dispatchBindingSelector])

  const saveItem = async (item: BindingSelectorItem) => {
    if (!routeInfo) return

    const copyBindingsVariables = {
      from: currentBinding.id,
      to: item.id,
      template: routeInfo.blockId,
      context: pageContext,
    }

    if (!copyBindingsSanityCheck(copyBindingsVariables)) {
      throw new Error()
    }

    try {
      if (!item.overwrites) {
        const saveRouteVariables = {
          route: {
            ...pick(
              [
                'auth',
                'blockId',
                'context',
                'declarer',
                'domain',
                'interfaceId',
                'path',
                'routeId',
                'pages',
                'title',
                'metaTags',
              ],
              routeInfo
            ),
            dataSource: 'vtex.rewriter',
            bindingId: item.id,
          },
        }

        if (!saveRouteSanityCheck(saveRouteVariables)) {
          throw new Error()
        }

        const { error: errorSavingRoute } = await saveRoute({
          variables: saveRouteVariables,
        })
        if (errorSavingRoute) {
          throw new Error()
        }
      }
      const { error: errorCopyingBindings } = await copyBindings({
        variables: copyBindingsVariables,
      })
      if (errorCopyingBindings) {
        throw new Error()
      }
    } catch (error) {
      console.error(error)
      throw error
    }
  }

  const applyChanges = async () => {
    const checkedItems = bindingSelector.filter(item => item.checked)
    for (const item of checkedItems) {
      try {
        await saveItem(item)
      } catch (error) {
        console.error(error)
        throw error
      }
    }
  }

  const handleClose = () => {
    setSubmitStatus('IDLE')
    onClose()
  }

  const handleConfirmCopyContent = async ({
    showToast,
    sendEventToAudit,
  }: {
    showToast: ShowToastFunction
    sendEventToAudit: SendEventToAuditMutationFn
  }) => {
    if (submitStatus === 'SUBMITTING') {
      return
    }

    setSubmitStatus('SUBMITTING')

    try {
      // It doesn't throw an error. User just refused to overwrite
      await checkOverwrites()

      showToast({
        message: intl.formatMessage(toastMessages.saving),
        duration: Infinity,
      })
      try {
        await applyChanges()

        const event = createEventObject(
          'Copy binding content',
          'binding',
          account,
          workspace
        )
        await sendEventToAudit({
          variables: { input: event },
        })

        setTimeout(() => {
          showToast(intl.formatMessage(toastMessages.success))
        }, 500)
      } catch {
        // There is a bug with the Toast component, where
        // if you call two toasts on rapid succession,
        // the second one will be ignored. This timeout
        // gets around this issue, which happens if the error
        // is immediate
        setTimeout(() => {
          showToast(intl.formatMessage(toastMessages.error))
        }, 500)
      }
    } finally {
      handleClose()
    }
  }

  return (
    <>
      <ToastConsumer>
        {({ showToast }) => (
          <Modal
            isOpen={isOpen}
            onClose={handleClose}
            bottomBar={
              <div className="flex justify-end">
                <span className="mr4">
                  <Button onClick={handleClose} variation="secondary">
                    <FormattedMessage id="admin/pages.editor.components.button.cancel" />
                  </Button>
                </span>
                <SendEventToAuditMutation>
                  {sendEventToAudit => (
                    <Button
                      disabled={submitStatus === 'SUBMITTING'}
                      onClick={() =>
                        handleConfirmCopyContent({
                          showToast,
                          sendEventToAudit,
                        })
                      }
                    >
                      <FormattedMessage id="admin/pages.editor.components.button.save" />
                    </Button>
                  )}
                </SendEventToAuditMutation>
              </div>
            }
          >
            <div className="mb6">
              <h3>
                <FormattedMessage id="admin/pages.editor.copy-page.modal.header" />
              </h3>
              {loading ? (
                <div className="tc min-h-large-l">
                  <Spinner color="currentColor" />
                </div>
              ) : error || !routeInfo ? (
                <EmptyState
                  title={
                    <FormattedMessage id="admin/pages.editor.copy-page.modal.error.title" />
                  }
                >
                  <p>
                    <FormattedMessage id="admin/pages.editor.copy-page.modal.error.body" />
                  </p>
                </EmptyState>
              ) : (
                <>
                  <div className="f5 mb5">
                    <FormattedMessage id="admin/pages.editor.copy-page.modal.subheader" />
                  </div>
                  <BindingSelector
                    reducer={[bindingSelector, dispatchBindingSelector]}
                    pathId={routeInfo.path}
                  />
                </>
              )}
            </div>
          </Modal>
        )}
      </ToastConsumer>
      <OverwriteDialog
        state={bindingSelector}
        pathId={routeInfo?.path ?? ''}
        isOpen={isOverwriteDialogOpen}
        onClose={hideOverwriteDialog}
        onConfirm={onOverwriteConfirm}
        onCancel={onOverwriteCancel}
      />
    </>
  )
}

export default BindingCloningModal

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/BindingCloning/CloneContentContext.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, {
  FC,
  createContext,
  useContext,
  useEffect,
  Dispatch,
} from 'react'
import { compose, graphql, QueryResult } from 'react-apollo'
import {
  ApolloError,
  ApolloQueryResult,
  OperationVariables,
} from 'apollo-client'

import GetRouteQuery from '../graphql/GetRoute.graphql'
import {
  BindingSelectorState,
  setInitialBindingState,
  useBindingSelectorReducer,
  BindingSelectorAction,
  BindingSelectorItem,
} from './BindingSelector'
import SaveRouteMutation from '../graphql/SaveRoute.graphql'
import CopyBindingsMutation from '../graphql/CopyBindings.graphql'
import { Binding } from '../typings'
import { createInitialCloningState } from './utils/initialReducerState'

const withBindingsQueries = compose(
  graphql<{ routeId: string }, {}>(GetRouteQuery, {
    name: 'routeInfoQuery',
    options: ({ routeId }) => ({
      notifyOnNetworkStatusChange: true,
      variables: { routeId },
    }),
  }),
  graphql(SaveRouteMutation, { name: 'saveRoute' }),
  graphql(CopyBindingsMutation, { name: 'copyBindings' })
)

interface CloneContentContextValue {
  data: {
    loading: boolean
    error?: ApolloError
    routeInfo?: Route
    bindingSelector: BindingSelectorState
    currentBinding: Binding
    pageContext: PageContext
  }
  actions: {
    refetchRouteInfo: (
      variables?: OperationVariables
    ) => Promise<ApolloQueryResult<Route>>
    dispatchBindingSelector: Dispatch<BindingSelectorAction>
    saveRoute: (
      args: MutationArgs<SaveRouteVariables>
    ) => Promise<QueryResult<unknown>>
    copyBindings: (
      args: MutationArgs<CopyBindingVariables>
    ) => Promise<QueryResult<unknown>>
  }
}

const CloneContentContext = createContext<CloneContentContextValue>(
  {} as CloneContentContextValue
)

export interface SaveRouteVariables {
  route: Route
}

type MutationArgs<T> = {
  variables: T
}

export interface CopyBindingVariables {
  from: Binding['id']
  to: BindingSelectorItem['id']
  template: Route['blockId']
  context: PageContext
}

interface WithBindingsQueriesProps {
  routeInfoQuery: QueryResult & { route?: Route }
  saveRoute: (
    args: MutationArgs<SaveRouteVariables>
  ) => Promise<QueryResult<unknown>>
  copyBindings: (
    args: MutationArgs<CopyBindingVariables>
  ) => Promise<QueryResult<unknown>>
}

interface CloneContentProps {
  routeId: string
  bindings: Binding[]
  currentBinding: Binding
  pageContext: PageContext
  iframeBinding: RenderContext['binding']
}

const CloneContentProvider: FC<CloneContentProps &
  WithBindingsQueriesProps> = ({
  children,
  routeInfoQuery,
  bindings,
  currentBinding,
  pageContext,
  saveRoute,
  copyBindings,
  iframeBinding,
}) => {
  const [bindingSelector, dispatchBindingSelector] = useBindingSelectorReducer()
  const {
    route: routeInfo,
    loading: routeInfoLoading,
    error,
    refetch: refetchRouteInfo,
  } = routeInfoQuery

  useEffect(() => {
    if (routeInfo) {
      const initialState = bindings.map(binding =>
        createInitialCloningState(binding, currentBinding, routeInfo)
      )
      dispatchBindingSelector(setInitialBindingState(initialState))
    }
  }, [bindings, currentBinding, dispatchBindingSelector, routeInfo])

  /**
   * This check currentBinding.id !== iframeBinding?.id is needed to avoid the blinking
   * in the modal when user changes the binding.
   * The blinking is happening because there is a race condition on ContextSelectors/index.ts
   * When handleBindingChange is called, it sets the target binding as binding in the state.
   * However, when the useEffect in that file runs next time, it sets bindings to the old one,
   * coming from iframeBinding, which seems to be beeing update afterwards. Then, when iframeBinding
   * updates, the setBinding is called called, now with the target binding causing the blinking.
   */
  const loading =
    routeInfoLoading ||
    (currentBinding && currentBinding.id !== iframeBinding?.id)

  return (
    <CloneContentContext.Provider
      value={{
        data: {
          loading,
          error,
          routeInfo,
          bindingSelector,
          currentBinding,
          pageContext,
        },
        actions: {
          refetchRouteInfo,
          dispatchBindingSelector,
          saveRoute,
          copyBindings,
        },
      }}
    >
      {children}
    </CloneContentContext.Provider>
  )
}

const useCloneContent = () => useContext(CloneContentContext)

const ProviderWithQueries: FC<CloneContentProps> = withBindingsQueries(
  CloneContentProvider
)

export { ProviderWithQueries as CloneContentProvider, useCloneContent }

```


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

[User] Show me the implementation of admin-pages/react/components/EditorContainer/Topbar/ContextSelectors/BindingCloning/utils/initialReducerState.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Binding } from '../../typings'

const formatBindings = (binding: Binding, currentBinding: Binding) => ({
  label: binding.canonicalBaseAddress,
  id: binding.id,
  supportedLocales: binding.supportedLocales,
  checked: false,
  overwrites: false,
  isCurrent: currentBinding.id === binding.id,
})

// Mark items as conflicting or not--that is, if saving on a
// binding will overwrite a page present there or not
export const createInitialCloningState = (
  binding: Binding,
  currentBinding: Binding,
  routeInfo: Route
) => {
  const formattedBinding = formatBindings(binding, currentBinding)

  if (formattedBinding.isCurrent) {
    return formattedBinding
  }

  // If the page has undefined binding, it is present on all bindings
  if (!routeInfo.binding) {
    return {
      ...formattedBinding,
      overwrites: true,
    }
  }

  if (!routeInfo.conflicts) {
    return formattedBinding
  }

  if (
    routeInfo.conflicts.find(
      conflict => formattedBinding.id === conflict.binding
    )
  ) {
    return {
      ...formattedBinding,
      overwrites: true,
    }
  }

  return formattedBinding
}

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/utils.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import {
  AvailableApp,
  InstalledApp,
} from '../store/StoreForm/components/withStoreSettings'

export const parseStoreAppId = (
  store: InstalledApp & AvailableApp & { settings: string }
) => {
  const appMajor = store.version.split('.')[0]
  return `${store.slug}@${appMajor}.x`
}

```


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

[User] Show me the implementation of admin-pages/react/components/admin/redirects/typings.d.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AlertProps } from 'vtex.styleguide'

export interface RedirectsQuery {
  redirect: {
    list: Redirect[]
    numberOfEntries: number
  }
}

export interface AlertState {
  type: AlertProps['type']
  message: string
  meta?: {
    failedRedirects: Redirect[]
    mutation: (data: Redirect[]) => Promise<void>
    isSave: boolean
  }
  action?: {
    label: string
  }
}

```


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

[User] Show me the implementation of admin-pages/react/components/admin/redirects/consts.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const BASE_URL = '/admin/app/cms/redirects'
export const CSV_SEPARATOR = ';'

export const getCSVHeader = (hasMultipleBindings: boolean) =>
  hasMultipleBindings ? 'from;to;type;binding;endDate' : 'from;to;type;endDate'

const CSV_SINGLE_BINDING_TEMPLATE = `/temporary;/;TEMPORARY;
/temporary-with-date;/;TEMPORARY;2025-01-01T00:00:00.000Z;
/permanent;/;PERMANENT;
`
const CSV_MULTIPLE_BINDING_TEMPLATE = `/temporary;/;TEMPORARY;
/temporary-with-date;/;TEMPORARY;1234_binding_id;2025-01-01T00:00:00.000Z;
/permanent;/;PERMANENT;123_binding_id;
`

export const getCSVTemplate = (hasMultipleBindings: boolean) =>
  `${getCSVHeader(hasMultipleBindings)}
    ${
      hasMultipleBindings
        ? CSV_MULTIPLE_BINDING_TEMPLATE
        : CSV_SINGLE_BINDING_TEMPLATE
    }`

export const NEW_REDIRECT_ID = 'new'

export const PAGINATION_START = 0
export const PAGINATION_STEP = 10
export const REDIRECTS_LIMIT = 500

export const WRAPPER_PATH = 'redirects'

```


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

[User] Show me the implementation of admin-pages/react/components/admin/redirects/bulkUploadRedirects.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { MutableRefObject } from 'react'
import { splitEvery } from 'ramda'

const MAX_REDIRECTS_PER_REQUEST = 200
const NUMBER_OF_RETRIES = 3

interface BulkUploadRedirectsArgs {
  data: Redirect[]
  mutation: (data: Redirect[]) => Promise<void>
  isSave: boolean
  shouldUploadRef: MutableRefObject<boolean>
  updateProgress?: (processed: number) => void
}

export default async function bulkUploadRedirects({
  data,
  mutation,
  shouldUploadRef,
  updateProgress = () => {
    return undefined
  },
}: BulkUploadRedirectsArgs): Promise<{ failedRedirects: Redirect[] }> {
  let failedRedirects: Redirect[] = []
  let processedCount = 0

  const redirectBatches = splitEvery(MAX_REDIRECTS_PER_REQUEST, data)

  for (const payload of redirectBatches) {
    if (!shouldUploadRef.current) {
      break
    }

    for (let i = 1; i <= NUMBER_OF_RETRIES; i++) {
      try {
        await mutation(payload)
        processedCount += payload.length
        updateProgress(processedCount)
        break
      } catch (e) {
        await new Promise(res => {
          setTimeout(() => res(), i * 750)
        })
        if (i === NUMBER_OF_RETRIES) {
          failedRedirects = failedRedirects.concat(payload)
        }
      }
    }

    await new Promise(res => {
      setTimeout(() => res(), 750)
    })
  }

  return { failedRedirects }
}

```


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

[User] Show me the implementation of admin-pages/react/components/admin/pages/utils.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import startCase from 'lodash/startCase'

type NewRouteTypeArg = Pick<Route, 'uuid' | 'declarer'>
export const isNewRoute = (route: NewRouteTypeArg) =>
  !route.uuid && !route.declarer

export const isUserRoute = (route: NewRouteTypeArg) => !route.declarer

type GetRouteTitleArg = Pick<Route, 'title' | 'blockId' | 'interfaceId'> &
  NewRouteTypeArg

export const getRouteTitle = (route: GetRouteTitleArg) => {
  const { blockId, declarer, interfaceId, title } = route

  const nameFromInterfaceOrBlock = startCase(
    blockId.split('#')[1] ||
      interfaceId.split('.')[interfaceId.split('.').length - 1]
  )

  if (isNewRoute(route)) {
    return title || ''
  }

  return declarer
    ? nameFromInterfaceOrBlock || title
    : title || nameFromInterfaceOrBlock
}

export const isStoreRoute = (domain: Route['domain']) => domain === 'store'

```


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

[User] Show me the implementation of admin-pages/react/components/admin/pages/SeparatorWithLine.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const SeparatorWithLine = () => <div className="mv5 bb b--light-silver" />

export default SeparatorWithLine

```


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

[User] Show me the implementation of admin-pages/react/components/admin/pages/consts.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const NEW_ROUTE_ID = 'new'

export const ROUTES_FORM = 'admin.app.cms.pages.page-details'

export const ROUTES_LIST = 'admin.app.cms.pages.page-list'

export const INSTITUTIONAL_ROUTES_LIST = 'admin.app.cms.content'

export const INSTITUTIONAL_ROUTES_FORM = 'admin.app.cms.content-details'

export const WRAPPER_PATH = 'pages'

export const DATA_SOURCE = 'vtex.rewriter'

```


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

[User] Show me the implementation of admin-pages/react/components/admin/store/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'
import { Tab, Tabs } from 'vtex.styleguide'

import PWAForm from './PWAForm'
import StoreForm from './StoreForm'

import '../../../styles.global.css'

const messages = defineMessages({
  advanced: {
    defaultMessage: 'Advanced',
    id: 'admin/pages.editor.store.settings.tabs.advanced',
  },
  general: {
    defaultMessage: 'General',
    id: 'admin/pages.editor.store.settings.tabs.general',
  },
  pwa: {
    defaultMessage: 'PWA',
    id: 'admin/pages.editor.store.settings.tabs.pwa',
  },
})

const Store: React.FC<WrappedComponentProps> = ({ intl }) => {
  const [activeTab, setActiveTab] = React.useState<
    'advanced' | 'general' | 'pwa'
  >('general')

  const handleAdvancedOpen = React.useCallback(() => {
    setActiveTab('advanced')
  }, [])

  const handlePWAOpen = React.useCallback(() => {
    setActiveTab('pwa')
  }, [])

  const handleGeneralOpen = React.useCallback(() => {
    setActiveTab('general')
  }, [])

  return (
    <div className="mb7 ph7">
      <Tabs>
        <Tab
          active={activeTab === 'general'}
          onClick={handleGeneralOpen}
          label={intl.formatMessage(messages.general)}
        >
          <div className="pv6">
            <StoreForm tab="general" />
          </div>
        </Tab>

        <Tab
          active={activeTab === 'advanced'}
          onClick={handleAdvancedOpen}
          label={intl.formatMessage(messages.advanced)}
        >
          <div className="pv6">
            <StoreForm tab="advanced" />
          </div>
        </Tab>

        <Tab
          active={activeTab === 'pwa'}
          onClick={handlePWAOpen}
          label={intl.formatMessage(messages.pwa)}
        >
          <div className="pv6">
            <PWAForm />
          </div>
        </Tab>
      </Tabs>
    </div>
  )
}

export default injectIntl(Store)

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/Form/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { RouteFormData } from 'pages'
import { isEmpty } from 'ramda'
import * as React from 'react'
import {
  defineMessages,
  injectIntl,
  WrappedComponentProps as ComponentWithIntlProps,
} from 'react-intl'
import { ToastConsumerFunctions } from 'vtex.styleguide'

import { formatStatements } from '../../../../utils/conditions'
import { FormErrors } from '../../pages/Form/typings'
import { generateNewRouteId } from '../../pages/Form/utils'
import { FormProps } from '../../store/StoreForm/components/withStoreSettings'
import Form from './Form'
import { getValidateFormState } from './utils'
import { OperationsResults } from './withContentContext'

interface ComponentProps {
  culture: RenderContext['culture']
  initialData: RouteFormData
  initialContent: {
    id: string
    text: string
  }
  onDelete: OperationsResults['deleteRoute']
  onExit: () => void
  onSave: OperationsResults['saveRoute']
  onSaveContent: OperationsResults['saveContent']
  showToast: ToastConsumerFunctions['showToast']
  hideToast: ToastConsumerFunctions['hideToast']
}

export interface RouteContentFromData {
  pageContent?: string
  contentId: string
}

type Props = ComponentProps & ComponentWithIntlProps & FormProps

export interface State {
  data: RouteFormData & RouteContentFromData
  isLoading: boolean
  formErrors: FormErrors
  isDeletable: boolean
  isInfoEditable: boolean
}

const messages = defineMessages({
  deleteError: {
    defaultMessage: 'Error: route could not be deleted.',
    id: 'admin/pages.admin.pages.form.delete.error',
  },
  deleteSuccess: {
    defaultMessage: 'Success',
    id: 'admin/pages.admin.pages.form.delete.success',
  },
  saveError: {
    defaultMessage: 'Error: route could not be saved.',
    id: 'admin/pages.admin.pages.form.save.error',
  },
  saveSuccess: {
    defaultMessage: 'Success',
    id: 'admin/pages.admin.pages.form.save.success',
  },
})

class FormContainer extends React.PureComponent<Props, State> {
  public constructor(props: Props) {
    super(props)

    const isNew = !props.initialData.uuid

    this.state = {
      data: {
        ...props.initialData,
        contentId: props.initialContent.id,
        pageContent: props.initialContent.text,
      },
      formErrors: {},
      isDeletable: !isNew,
      isInfoEditable: isNew,
      isLoading: false,
    }
  }

  public render() {
    const { onExit } = this.props
    const {
      data,
      isDeletable,
      isInfoEditable,
      isLoading,
      formErrors,
    } = this.state

    return (
      <Form
        data={data}
        errors={formErrors}
        isDeletable={isDeletable}
        isInfoEditable={isInfoEditable}
        isLoading={isLoading}
        onDelete={this.handleDelete}
        onExit={onExit}
        onFieldValueChange={this.handleChangeFieldValue}
        onSubmit={this.handleSave}
      />
    )
  }

  private handleChangeFieldValue = (
    field: string,
    value: string | number | null
  ) => {
    return this.setState(prevState => ({
      ...prevState,
      data: {
        ...prevState.data,
        [field]: value,
      },
      formErrors: {},
    }))
  }

  private handleSave = (event: React.FormEvent) => {
    const {
      intl,
      onExit,
      onSave,
      showToast,
      onSaveContent,
      culture,
    } = this.props
    event.preventDefault()

    const nextState = getValidateFormState(this.state)
    if (isEmpty(nextState.formErrors)) {
      const {
        auth,
        blockId,
        contentId,
        context,
        declarer,
        domain,
        interfaceId,
        metaTagDescription,
        metaTagKeywords,
        metaTagRobots,
        pageContent,
        pages,
        path,
        routeId: routeIdCandidate,
        title,
        uuid,
      } = this.state.data
      const routeId = routeIdCandidate || generateNewRouteId(interfaceId, path)

      this.setState({ isLoading: true }, async () => {
        try {
          await onSave({
            variables: {
              route: {
                auth,
                blockId,
                context,
                declarer,
                domain,
                interfaceId,
                metaTags: {
                  description: metaTagDescription,
                  keywords: (metaTagKeywords || []).map(({ value }) => value),
                  robots: metaTagRobots
                },
                pages: pages.map(page => {
                  return {
                    condition: {
                      allMatches: page.condition.allMatches,
                      id: page.condition.id || undefined,
                      statements: formatStatements(page.condition.statements),
                    },
                    pageId: page.pageId || undefined,
                    template: page.template,
                  }
                }),
                path,
                routeId,
                title,
                uuid,
              },
            },
          })

          await onSaveContent({
            variables: {
              blockId,
              configuration: {
                condition: {
                  allMatches: true,
                  id: 'vtex.rich-text@0.x:rich-text',
                  pageContext: {
                    id: '*',
                    type: '*',
                  },
                  statements: [],
                },
                contentId,
                contentJSON: JSON.stringify({ text: pageContent }),
                label: null,
                origin: 'vtex.rich-text@0.x:rich-text',
              },
              lang: culture.locale,
              template: blockId,
              treePath: `${routeId}/flex-layout.row#content-body/rich-text`,
            },
          })

          showToast({
            horizontalPosition: 'right',
            message: intl.formatMessage(messages.saveSuccess),
          })

          onExit()
        } catch (err) {
          this.setState({ isLoading: false }, () => {
            console.error(err)

            showToast({
              horizontalPosition: 'right',
              message: intl.formatMessage(messages.saveError),
            })
          })
        }
      })
    } else {
      this.setState(getValidateFormState)
    }
  }

  private handleDelete = () => {
    const { showToast, intl, onDelete, onExit } = this.props
    const { data } = this.state

    this.setState({ isLoading: true }, async () => {
      try {
        if (!data.uuid) {
          throw new Error('No uuid')
        }

        await onDelete({
          variables: {
            uuid: data.uuid,
          },
        })

        showToast({
          horizontalPosition: 'right',
          message: intl.formatMessage(messages.deleteSuccess),
        })

        onExit()
      } catch (err) {
        this.setState({ isLoading: false }, () => {
          console.error(err)

          showToast({
            horizontalPosition: 'right',
            message: intl.formatMessage(messages.deleteError),
          })
        })
      }
    })
  }
}

export default injectIntl(FormContainer)

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/Form/Loader.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

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

const Loader = () => (
  <div className="vh-100 flex items-center justify-center">
    <Spinner />
  </div>
)

export default Loader

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/Form/UnallowedWarning.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

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

const UnallowedWarning = () => (
  <EmptyState
    title={
      <FormattedMessage
        id="admin/pages.admin.content.not-supported.title"
        defaultMessage="This feature is not available :/"
      />
    }
  >
    <FormattedMessage id="admin/pages.admin.content.not-supported.description">
      {message => <p>{message}</p>}
    </FormattedMessage>
    <div className="pt5">
      <Button
        variation="secondary"
        size="small"
        href="https://github.com/vtex-apps/admin-pages/blob/master/docs/CONTENT_PAGE.md"
        target="_blank"
      >
        <FormattedMessage
          id="admin/pages.admin.content.not-supported.action"
          defaultMessage="See instructions"
        />
      </Button>
    </div>
  </EmptyState>
)

export default UnallowedWarning

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/Form/withContentContext.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { pathOr } from 'ramda'
import * as React from 'react'
import { Mutation, MutationFn, Query, QueryResult } from 'react-apollo'

import Loader from './Loader'
import UnallowedWarning from './UnallowedWarning'
import {
  DeleteMutationResult,
  DeleteRouteVariables,
  SaveMutationResult,
  SaveRouteVariables,
} from '../../pages/Form/typings'
import {
  updateStoreAfterDelete,
  updateStoreAfterSave,
} from '../../pages/Form/utils'
import AvailableTemplates from '../../../../queries/AvailableTemplates.graphql'
import ContentIOMessageQuery from '../../../../queries/ContentIOMessage.graphql'
import DeleteRoute from '../../../../queries/DeleteRoute.graphql'
import RouteQuery from '../../../../queries/Route.graphql'
import SaveRoute from '../../../../queries/SaveRoute.graphql'
import SaveContentMutation from '../../../EditorContainer/mutations/SaveContent'
import ListContentQuery from '../../../EditorContainer/queries/ListContent'

interface TemplateData {
  availableTemplates: {
    id: string
  }[]
}

interface TemplateVariables {
  interfaceId: string
}

interface RouteData {
  route: Route
}

interface RouteVariables {
  domain: string
  routeId: string
}

interface MessagesData {
  translate: string[]
}

interface MessagesVariables {
  args: {
    indexedByFrom: {
      from: string
      messages: {
        content: string
        context?: string
        behavior?: 'USER_ONLY' | 'USER_AND_APP' | 'FULL'
      }[]
    }[]
    to: string
  }
}

export interface OperationsResults {
  deleteRoute: MutationFn<unknown, DeleteRouteVariables>
  saveRoute: MutationFn<unknown, SaveRouteVariables>
  saveContent: MutationFn<unknown, unknown>
  templatesResults: QueryResult<unknown, TemplateVariables>
}

export interface ContentContextProps {
  route: Route
  blockId: string
  interfaceId: string
  content: {
    id: string
    text: string
  }
  deleteRoute: MutationFn<unknown, DeleteRouteVariables>
  saveRoute: MutationFn<unknown, SaveRouteVariables>
  saveContent: MutationFn<unknown, unknown>
}

interface Params {
  culture: {
    locale: string
  }
  routeId: string
  storeAppId: string
}

function withContentContext<T>(
  WrappedComponent: React.ComponentType<T>,
  getParams: (props: T) => Params
) {
  const ComponentWithContent = (props: T) => {
    const { storeAppId, routeId, culture } = getParams(props)
    const interfaceId = `${storeAppId}:store.content`

    return (
      <Query<RouteData, RouteVariables>
        query={RouteQuery}
        variables={{ domain: 'store', routeId }}
      >
        {({ data: dataRoute, loading: loadingRoute }) => {
          if (loadingRoute) {
            return <Loader />
          }

          return (
            <Query<TemplateData, TemplateVariables>
              query={AvailableTemplates}
              variables={{ interfaceId }}
            >
              {({ data: dataTemplates, loading: loadingTemplates }) => {
                if (loadingTemplates) {
                  return <Loader />
                }

                const eligibleTemplates =
                  dataTemplates &&
                  dataTemplates.availableTemplates.filter(
                    (template: Template) => template.id !== interfaceId
                  )

                if (!eligibleTemplates || !eligibleTemplates.length) {
                  return <UnallowedWarning />
                }

                const blockId = eligibleTemplates[0].id

                return (
                  <ListContentQuery
                    variables={{
                      blockId,
                      pageContext: {
                        id: '*',
                        type: '*',
                      },
                      template: blockId,
                      treePath: `${routeId}/flex-layout.row#content-body/rich-text`,
                    }}
                  >
                    {({ data: dataContent, loading: loadingContent }) => {
                      if (loadingContent) {
                        return <Loader />
                      }

                      const contentJSON: string | null = pathOr(
                        null,
                        [
                          'listContentWithSchema',
                          'content',
                          '0',
                          'contentJSON',
                        ],
                        dataContent || {}
                      )
                      const contentText: string = contentJSON
                        ? JSON.parse(contentJSON).text
                        : ''
                      const contentId: string = pathOr(
                        '',
                        ['listContentWithSchema', 'content', '0', 'contentId'],
                        dataContent || {}
                      )

                      return (
                        <Query<MessagesData, MessagesVariables>
                          query={ContentIOMessageQuery}
                          variables={{
                            args: {
                              indexedByFrom: [
                                {
                                  from: 'en-DV',
                                  messages: [
                                    {
                                      context: contentId,
                                      content: contentText || '',
                                    },
                                  ],
                                },
                              ],
                              to: culture.locale,
                            },
                          }}
                        >
                          {({ data: dataMessage, loading: loadingMessage }) => {
                            if (loadingMessage) {
                              return <Loader />
                            }

                            const content = {
                              id: contentId,
                              text: pathOr(
                                contentText,
                                ['translate', 0],
                                dataMessage || {}
                              ),
                            }

                            return (
                              <Mutation<
                                DeleteMutationResult['data'],
                                DeleteRouteVariables
                              >
                                mutation={DeleteRoute}
                                update={updateStoreAfterDelete()}
                              >
                                {deleteRoute => (
                                  <Mutation<
                                    SaveMutationResult['data'],
                                    SaveRouteVariables
                                  >
                                    mutation={SaveRoute}
                                    update={updateStoreAfterSave}
                                  >
                                    {saveRoute => (
                                      <SaveContentMutation>
                                        {saveContent => (
                                          <WrappedComponent
                                            {...props}
                                            route={dataRoute && dataRoute.route}
                                            saveRoute={saveRoute}
                                            deleteRoute={deleteRoute}
                                            saveContent={saveContent}
                                            content={content}
                                            blockId={blockId}
                                            interfaceId={interfaceId}
                                          />
                                        )}
                                      </SaveContentMutation>
                                    )}
                                  </Mutation>
                                )}
                              </Mutation>
                            )
                          }}
                        </Query>
                      )
                    }}
                  </ListContentQuery>
                )
              }}
            </Query>
          )
        }}
      </Query>
    )
  }

  return ComponentWithContent
}

export default withContentContext

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/Form/utils.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { RouteFormData } from 'pages'
import { State } from './index'

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

const requiredMessage = 'admin/pages.admin.pages.form.templates.field.required'

const validateFalsyPath = (path: keyof RouteFormData) => (
  data: RouteFormData
) => !data[path] && { [path]: requiredMessage }

const validatePathUrl = (path: string) => {
  if (!path) return { path: requiredMessage }
  if (!path.startsWith('/'))
    return {
      path: 'admin/pages.admin.pages.form.templates.path.validation-error',
    }
  if (slugify(path) !== path.slice(1))
    return {
      path: 'admin/pages.admin.pages.form.templates.path.invalid-format',
    }

  return {}
}

export const getValidateFormState = (prevState: State) => {
  return {
    ...prevState,
    formErrors: {
      ...prevState.formErrors,
      ...(validatePathUrl(prevState.data.path) as Record<string, string>),
      ...(prevState.isInfoEditable
        ? validateFalsyPath('title')(prevState.data)
        : {}),
    },
  }
}

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/Form/Form.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { RouteFormData } from 'pages'
import * as React from 'react'
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'
import { Button, Input, Textarea, IconEdit } from 'vtex.styleguide'

import { RouteContentFromData } from './index'
import FormFieldSeparator from '../../FormFieldSeparator'
import { FormErrors } from '../../pages/Form/typings'
import SeparatorWithLine from '../../pages/SeparatorWithLine'
import { slugify } from '../../utils'
import RichTextEditor from '../../../RichTextEditor/index'
import SeoPreview from '../../../SeoPreview'

interface Props {
  data: RouteFormData & RouteContentFromData
  errors: FormErrors
  isLoading: boolean
  onDelete: () => void
  onExit: () => void
  onFieldValueChange: (field: string, value: string | number | null) => void
  onSubmit: (event: React.FormEvent) => void
  isDeletable: boolean
  isInfoEditable: boolean
}

const messages = defineMessages({
  contentSection: {
    defaultMessage: 'Content',
    id: 'admin/pages.admin.content.content.section-title',
  },
  fieldPath: {
    defaultMessage: 'URL',
    id: 'admin/pages.admin.content.general.url.label',
  },
  fieldTitle: {
    defaultMessage: 'Title',
    id: 'admin/pages.admin.content.general.title.label',
  },
  generalSection: {
    defaultMessage: 'General',
    id: 'admin/pages.admin.content.general.section-title',
  },
  pathHint: {
    defaultMessage: '/my-custom-page',
    id: 'admin/pages.admin.content.general.url.placeholder',
  },
  seoDescription: {
    defaultMessage: 'Description',
    id: 'admin/pages.admin.content.general.description.label',
  },
  titleHint: {
    defaultMessage: 'e.g.: About us',
    id: 'admin/pages.admin.content.general.title.placeholder',
  },
})

const Form = ({
  data,
  errors,
  isDeletable,
  isLoading,
  onDelete,
  onExit,
  onFieldValueChange,
  onSubmit,
}: Props) => {
  const intl = useIntl()
  const pathRef = React.useRef<HTMLInputElement>(null)
  const [hasDefinedPath, setHasDefinedPath] = React.useState(false)

  const handlePathChange = (value: string | null) => {
    if (!value) return
    return onFieldValueChange('path', `/${slugify(value)}`)
  }

  const handleEventValue = (
    event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
  ) => {
    const { name, value } = event.target

    if (name === 'title' && !hasDefinedPath) {
      handlePathChange(value)
    }

    return onFieldValueChange(name, value)
  }

  return (
    <form onSubmit={() => null}>
      <p className="mv7 f4 b">Geral</p>
      <div className="flex-ns justify-between">
        <div className="w-100 w-50-ns pr4-ns">
          <Input
            name="title"
            disabled={false}
            label={intl.formatMessage(messages.fieldTitle)}
            onChange={handleEventValue}
            placeholder={intl.formatMessage(messages.titleHint)}
            required
            value={data.title}
            errorMessage={
              errors.title && intl.formatMessage({ id: errors.title })
            }
          />
          <FormFieldSeparator />
          <Input
            ref={pathRef}
            name="path"
            disabled={!hasDefinedPath}
            label={intl.formatMessage(messages.fieldPath)}
            onChange={handleEventValue}
            onBlur={() => {
              const value = data.path

              if (!value) {
                if (hasDefinedPath) setHasDefinedPath(false)
                return handlePathChange(data.title)
              }

              if (!hasDefinedPath) setHasDefinedPath(true)
              return handlePathChange(data.path)
            }}
            placeholder={intl.formatMessage(messages.pathHint)}
            required
            value={data.path || ''}
            errorMessage={
              errors.path && intl.formatMessage({ id: errors.path })
            }
            suffix={
              <div
                role="button"
                tabIndex={0}
                className="pointer"
                onMouseDown={() => {
                  setHasDefinedPath(true)
                  pathRef.current && pathRef.current.focus()
                }}
              >
                <IconEdit />
              </div>
            }
          />
          <FormFieldSeparator />
          <Textarea
            name="metaTagDescription"
            disabled={false}
            label={intl.formatMessage(messages.seoDescription)}
            onChange={handleEventValue}
            resize="vertical"
            value={data.metaTagDescription}
            size="small"
          />
        </div>

        <div className="w-100 w-50-ns pl4-ns">
          <SeoPreview
            title={data.title}
            url={data.path}
            description={data.metaTagDescription}
          />
        </div>
      </div>

      <FormFieldSeparator />
      <SeparatorWithLine />

      <p className="mv7 f4 b">{intl.formatMessage(messages.contentSection)}</p>
      <RichTextEditor
        initialState={data.pageContent}
        onChange={(value: string) => onFieldValueChange('pageContent', value)}
      />

      <FormFieldSeparator />

      <div className={`${isDeletable && 'flex justify-between'}`}>
        {isDeletable && (
          <Button
            disabled={isLoading}
            onClick={onDelete}
            size="small"
            variation="danger"
          >
            <FormattedMessage
              id="admin/pages.admin.pages.form.button.delete"
              defaultMessage="Delete"
            />
          </Button>
        )}

        <div className="flex justify-end">
          <div className="mr6">
            <Button
              disabled={isLoading}
              onClick={onExit}
              size="small"
              variation="tertiary"
            >
              <FormattedMessage
                id="admin/pages.admin.pages.form.button.cancel"
                defaultMessage="Cancel"
              />
            </Button>
          </div>
          <Button
            disabled={isLoading}
            isLoading={isLoading}
            onClick={onSubmit}
            size="small"
            variation="primary"
          >
            <FormattedMessage
              id="admin/pages.admin.pages.form.button.save"
              defaultMessage="Save"
            />
          </Button>
        </div>
      </div>
    </form>
  )
}

export default Form

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/List/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Fragment } from 'react'
import { FormattedMessage } from 'react-intl'
import { withRuntimeContext } from 'vtex.render-runtime'
import { Box, Button } from 'vtex.styleguide'

import { INSTITUTIONAL_ROUTES_FORM, NEW_ROUTE_ID } from '../../pages/consts'
import SeparatorWithLine from '../../pages/SeparatorWithLine'

import Entry from './Entry'

interface Props {
  hasCreateButton?: boolean
  routes: Route[]
  runtime: RenderContext
  titleId: string
}

const Section = ({ hasCreateButton, routes, runtime, titleId }: Props) => (
  <Box>
    <div className="relative flex items-center">
      <FormattedMessage id={titleId}>
        {title => <div className="b">{title}</div>}
      </FormattedMessage>
      {hasCreateButton && (
        <div className="absolute right-0">
          <Button
            onClick={() => {
              runtime.navigate({
                page: INSTITUTIONAL_ROUTES_FORM,
                params: { id: NEW_ROUTE_ID },
              })
            }}
            size="small"
            variation="primary"
          >
            <FormattedMessage
              id="admin/pages.admin.pages.list.button.create"
              defaultMessage="Create New"
            />
          </Button>
        </div>
      )}
    </div>
    {routes.map(route => (
      <Fragment key={route.path}>
        <SeparatorWithLine />
        <Entry route={route} />
      </Fragment>
    ))}
  </Box>
)

export default withRuntimeContext(Section)

```


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

[User] Show me the implementation of admin-pages/react/components/admin/institutional/List/Entry.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { useKeydownFromClick } from 'keydown-from-click'
import React from 'react'
import { withRuntimeContext } from 'vtex.render-runtime'
import { IconEdit } from 'vtex.styleguide'

import { INSTITUTIONAL_ROUTES_FORM } from '../../pages/consts'
import { getRouteTitle } from '../../pages/utils'

interface Props {
  route: Route
  runtime: RenderContext
}

const Entry = ({ route, runtime }: Props) => {
  const handleClick = React.useCallback(() => {
    runtime.navigate({
      page: INSTITUTIONAL_ROUTES_FORM,
      params: { id: encodeURIComponent(route.routeId) },
    })
  }, [route.routeId, runtime])

  const handleKeyDown = useKeydownFromClick(handleClick)

  return (
    <div className="flex justify-between items-center">
      <div className="f6">{getRouteTitle(route)}</div>
      <div
        className="flex pointer outline-0"
        onClick={handleClick}
        onKeyDown={handleKeyDown}
        role="button"
        tabIndex={0}
      >
        <IconEdit color="silver" />
      </div>
    </div>
  )
}

export default withRuntimeContext(Entry)

```