[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/form/typings.d.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { WidgetProps } from 'react-jsonschema-form'

export interface CustomWidgetProps extends WidgetProps {
  formContext: {
    messages: RenderContext['messages']
  }
  onChange: (value: unknown) => void
  rawErrors?: string[]
  schema: WidgetProps['schema'] & {
    disabled?: boolean
  }
}

```


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

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

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useIntl } from 'react-intl'
import { formatIOMessage } from 'vtex.native-types'
import { Input } from 'vtex.styleguide'

import { CustomWidgetProps } from './typings'

interface Props extends CustomWidgetProps {
  isI18n?: boolean
  label: string
  max?: number
  min?: number
  rawErrors?: string[]
  type?: string
  schema: CustomWidgetProps['schema'] & {
    readonly?: boolean
  }
}

const ReadOnlyText = (props: CustomWidgetProps) => {
  const intl = useIntl()

  if (!props.schema.title) {
    return null
  }

  return (
    <div className="pv3 c-on-base t-small mb3">
      {formatIOMessage({ id: props.schema.title || '', intl })}
    </div>
  )
}

const BaseInput: React.FunctionComponent<Props> = props => {
  const {
    disabled,
    id,
    isI18n,
    label,
    max,
    min,
    onBlur,
    onChange,
    onFocus,
    options,
    placeholder,
    rawErrors,
    readonly,
    required,
    schema,
    value,
  } = props

  const intl = useIntl()

  const schemaType = schema.type === 'number' ? 'number' : 'text'

  const type = options.inputType || props.type || schemaType

  const currentError = rawErrors && rawErrors[0]

  const handleChange = React.useCallback(
    ({ target }: React.ChangeEvent<HTMLInputElement>) => {
      if (isI18n) {
        onChange({ target, value: target.value || '' })
      } else {
        onChange(target.value)
      }
    },
    [isI18n, onChange]
  )

  if (schemaType === 'text' && schema.format === 'read-only') {
    return <ReadOnlyText {...props}></ReadOnlyText>
  }

  return (
    <Input
      disabled={disabled || schema.disabled}
      error={!!currentError}
      errorMessage={currentError}
      helpText={
        schema.description
          ? formatIOMessage({ id: schema.description, intl })
          : ''
      }
      label={formatIOMessage({ id: label, intl })}
      max={max ? `${max}` : undefined}
      min={min ? `${min}` : undefined}
      onBlur={
        onBlur &&
        ((event: React.ChangeEvent<HTMLInputElement>) =>
          onBlur(id, event.target.value))
      }
      onChange={handleChange}
      onFocus={
        onFocus &&
        ((event: React.ChangeEvent<HTMLInputElement>) =>
          onFocus(id, event.target.value))
      }
      placeholder={placeholder}
      readOnly={readonly || schema.readonly}
      required={required}
      type={type}
      value={value ? `${value}` : ''}
    />
  )
}

BaseInput.defaultProps = {
  disabled: false,
  placeholder: '',
  rawErrors: [],
  readonly: false,
  required: false,
}

export default BaseInput

```


[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/form/Dropdown.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useIntl } from 'react-intl'
import { formatIOMessage } from 'vtex.native-types'
import { Dropdown as StyleguideDropdown } from 'vtex.styleguide'

import { CustomWidgetProps } from './typings'

interface Props extends CustomWidgetProps {
  onClose?: () => void
  onOpen?: () => void
  options: { [key: string]: boolean | number | string | object | null }
}

const Dropdown: React.FunctionComponent<Props> = ({
  disabled,
  id,
  label,
  onChange,
  onClose,
  onOpen,
  options,
  placeholder,
  readonly,
  schema,
  value,
}) => {
  const intl = useIntl()
  const handleChange = React.useCallback(
    ({
      target: { value: optionValue },
    }: React.ChangeEvent<HTMLSelectElement>) =>
      onChange(!optionValue ? options.emptyValue : optionValue),
    [onChange, options.emptyValue]
  )

  const dropdownOptions = React.useMemo(
    () =>
      Array.isArray(options.enumOptions)
        ? options.enumOptions.map(option => ({
            ...option,
            label: formatIOMessage({ id: `${option.label}`, intl }),
          }))
        : [],
    [intl, options.enumOptions]
  )

  return (
    <StyleguideDropdown
      disabled={disabled || (schema && schema.disabled)}
      id={id}
      label={formatIOMessage({ id: label, intl })}
      onChange={handleChange}
      onClose={onClose}
      onOpen={onOpen}
      options={dropdownOptions}
      placeholder={
        placeholder ? formatIOMessage({ id: placeholder, intl }) : ''
      }
      readOnly={readonly}
      value={value}
    />
  )
}

Dropdown.defaultProps = {
  disabled: false,
  readonly: false,
  required: false,
  value: '',
}

export default Dropdown

```


[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/form/Toggle.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useIntl } from 'react-intl'
import { formatIOMessage } from 'vtex.native-types'
import { Toggle as StyleguideToggle } from 'vtex.styleguide'

import { CustomWidgetProps } from './typings'

const Toggle: React.FunctionComponent<CustomWidgetProps> = ({
  disabled,
  id,
  label,
  onChange,
  readonly,
  schema: { disabled: disabledBySchema },
  value,
}) => {
  const intl = useIntl()

  return (
    <StyleguideToggle
      checked={value}
      disabled={disabled || disabledBySchema || readonly}
      id={id}
      label={formatIOMessage({ id: label, intl })}
      onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
        onChange(event.target.checked)
      }
    />
  )
}

Toggle.defaultProps = {
  disabled: false,
  readonly: false,
}

export default Toggle

```


[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/form/MultiSelect.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Fragment } from 'react'
import ReactSelect, { Option } from 'react-select'

interface Props {
  disabled?: boolean
  id?: string
  label?: string
  onChange: (newValue: string[]) => void
  options: {
    enumOptions: Option[]
  }
  placeholder: string
  schema?: {
    disabled?: boolean
    title: string
  }
  value: string[]
}

const MultiSelect: React.FunctionComponent<Props> = ({
  disabled,
  id,
  label,
  onChange,
  options: { enumOptions },
  placeholder,
  schema,
  value,
}) => (
  <Fragment>
    {(label || (schema && schema.title)) && (
      <label htmlFor={id}>
        <span className="dib mb3 w-100">
          <span>{label || (schema && schema.title)}</span>
        </span>
      </label>
    )}
    <ReactSelect
      disabled={
        disabled || (schema && schema.disabled) || enumOptions.length === 0
      }
      id={id}
      multi
      onChange={optionValues => {
        const formattedValue = (optionValues as Option[]).map(
          (item: Option) => item.value as string
        )
        onChange(formattedValue)
      }}
      options={enumOptions}
      placeholder={placeholder}
      value={value}
    />
  </Fragment>
)

MultiSelect.defaultProps = {
  disabled: false,
  placeholder: '',
  value: [],
}

export default MultiSelect

```


[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/form/TextArea.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useIntl } from 'react-intl'
import { formatIOMessage } from 'vtex.native-types'
import { Textarea } from 'vtex.styleguide'

import { CustomWidgetProps } from './typings'

interface Props extends CustomWidgetProps {
  isI18n?: boolean
}

const TextArea: React.FunctionComponent<Props> = ({
  disabled,
  id,
  isI18n,
  label,
  onBlur,
  onChange,
  onFocus,
  placeholder,
  rawErrors,
  readonly,
  schema,
  value,
}) => {
  const intl = useIntl()
  const handleBlur = React.useCallback(
    (event: React.FocusEvent<HTMLTextAreaElement>) => {
      if (onBlur) {
        onBlur(id, event.target.value)
      }
    },
    [id, onBlur]
  )

  const handleChange = React.useCallback(
    ({ target }: React.ChangeEvent<HTMLTextAreaElement>) => {
      if (isI18n) {
        onChange({ target, value: target.value || '' })
      } else {
        onChange(target.value)
      }
    },
    [isI18n, onChange]
  )

  const handleFocus = React.useCallback(
    (event: React.FocusEvent<HTMLTextAreaElement>) => {
      if (onFocus) {
        onFocus(id, event.target.value)
      }
    },
    [id, onFocus]
  )

  const [currentError] = Array.isArray(rawErrors) ? rawErrors : ['']

  return (
    <Textarea
      disabled={disabled}
      error={!!currentError}
      errorMessage={currentError}
      helpText={
        schema.description
          ? formatIOMessage({ id: schema.description, intl })
          : ''
      }
      label={formatIOMessage({ id: label, intl })}
      onBlur={handleBlur}
      onChange={handleChange}
      onFocus={handleFocus}
      placeholder={
        placeholder ? formatIOMessage({ id: placeholder, intl }) : ''
      }
      readOnly={readonly}
      resize="vertical"
      value={value ? `${value}` : ''}
    />
  )
}

export default TextArea

```


[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/RichTextEditor/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import {
  AtomicBlockUtils,
  CompositeDecorator,
  Editor,
  EditorState,
  RichUtils,
  Modifier,
  SelectionState,
} from 'draft-js'
import * as React from 'react'
import { FormattedMessage } from 'react-intl'

import {
  IconBold,
  IconItalic,
  IconOrderedList,
  IconUnderline,
  IconUnorderedList,
} from 'vtex.styleguide'

import styles from './style.css'

import ImageInput from './ImageInput'
import Link from './Link'
import LinkInput from './LinkInput'
import HeadingInput from './HeadingInput'
import StyleButton from './StyleButton'

import {
  convertToEditorState,
  convertToMarkdown,
  findLinkEntities,
  mediaBlockRenderer,
  styleBlockRenderer,
} from './utils'

const INLINE_STYLES = [
  {
    label: <IconBold />,
    style: 'BOLD',
    title: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.bold.title"
        defaultMessage="Bold"
      />
    ),
  },
  {
    label: <IconItalic />,
    style: 'ITALIC',
    title: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.italic.title"
        defaultMessage="Italic"
      />
    ),
  },
  {
    label: <IconUnderline />,
    style: 'UNDERLINE',
    title: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.underline.title"
        defaultMessage="Underline"
      />
    ),
  },
]

const BLOCK_TYPES = [
  {
    label: <IconUnorderedList />,
    style: 'unordered-list-item',
    title: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.unordered-list.title"
        defaultMessage="Unordered list"
      />
    ),
  },
  {
    label: <IconOrderedList />,
    style: 'ordered-list-item',
    title: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.ordered-list.title"
        defaultMessage="Ordered list"
      />
    ),
  },
]

interface BlockStyleControlsProps {
  editorState: EditorState
  onToggle: (blockType: string | null) => void
}
const BlockStyleControls = (props: BlockStyleControlsProps) => {
  const { editorState } = props
  const selection = editorState.getSelection()
  const blockType = editorState
    .getCurrentContent()
    .getBlockForKey(selection.getStartKey())
    .getType()

  return (
    <>
      {BLOCK_TYPES.map((type, i) => (
        <StyleButton
          key={i}
          title={type.title}
          active={type.style === blockType}
          label={type.label}
          onToggle={props.onToggle}
          style={type.style}
        />
      ))}
    </>
  )
}

interface InlineStyleControlsProps {
  editorState: EditorState
  onToggle: (inlineStyle: string | null) => void
}
const InlineStyleControls = (props: InlineStyleControlsProps) => {
  const { editorState } = props
  const currentStyle = editorState.getCurrentInlineStyle()

  return (
    <>
      {INLINE_STYLES.map((type, i) => (
        <StyleButton
          key={i}
          title={type.title}
          active={currentStyle.has(type.style)}
          label={type.label}
          onToggle={props.onToggle}
          style={type.style}
        />
      ))}
    </>
  )
}

interface Props {
  onChange?: (value: string) => void
  initialState?: string
}

const RichTextEditor = ({ onChange, initialState = '' }: Props) => {
  const decorator = new CompositeDecorator([
    { strategy: findLinkEntities, component: Link },
  ])
  const [editorState, setEditorState] = React.useState(
    EditorState.createWithContent(convertToEditorState(initialState), decorator)
  )

  let className = `${styles.RichEditor_editor}`
  const contentState = editorState.getCurrentContent()
  if (!contentState.hasText()) {
    if (
      contentState
        .getBlockMap()
        .first()
        .getType() !== 'unstyled'
    ) {
      className += ` ${styles.RichEditor_hidePlaceholder}`
    }
  }

  const getContentBlockType = () => {
    const selection = editorState.getSelection()
    return editorState
      .getCurrentContent()
      .getBlockForKey(selection.getStartKey())
      .getType()
  }

  const getCurrentSelectionText = () => {
    const selectionState = editorState.getSelection()
    const anchorKey = selectionState.getAnchorKey()
    const currentContent = editorState.getCurrentContent()
    const currentContentBlock = currentContent.getBlockForKey(anchorKey)
    const start = selectionState.getStartOffset()
    const end = selectionState.getEndOffset()

    return currentContentBlock.getText().slice(start, end)
  }

  const handleAddImage = (imageUrl: string) => {
    const currentOffset = editorState
      .getCurrentContent()
      .getBlockMap()
      .keySeq()
      .findIndex(key => key === editorState.getSelection().getStartKey())

    // TODO: find out a better way to handle it
    // if editor is out of focus we force the image to be appended, not prepended
    const defEditorState =
      currentOffset === 0
        ? EditorState.moveFocusToEnd(editorState)
        : editorState

    const currentContentState = defEditorState.getCurrentContent()
    const contentStateWithEntity = currentContentState.createEntity(
      'IMAGE',
      'IMMUTABLE',
      { src: imageUrl }
    )
    const entityKey = contentStateWithEntity.getLastCreatedEntityKey()
    const newEditorState = EditorState.set(defEditorState, {
      currentContent: contentStateWithEntity,
    })
    return setEditorState(
      AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, ' ')
    )
  }

  const handleAddLink = (linkText: string, linkUrl: string) => {
    const selection = editorState.getSelection()
    let currentContentState = editorState.getCurrentContent()

    if (!getCurrentSelectionText()) {
      currentContentState = Modifier.insertText(
        currentContentState,
        selection,
        linkText
      )

      const newContentWithEntity = currentContentState.createEntity(
        'LINK',
        'MUTABLE',
        { url: linkUrl }
      )

      const entityKey = newContentWithEntity.getLastCreatedEntityKey()
      const anchorOffset = selection.getAnchorOffset()
      const newSelection = new SelectionState({
        anchorKey: selection.getAnchorKey(),
        anchorOffset,
        focusKey: selection.getAnchorKey(),
        focusOffset: anchorOffset + linkText.length,
      })

      const newContentWithLink = Modifier.applyEntity(
        newContentWithEntity,
        newSelection,
        entityKey
      )

      const withLinkText = EditorState.push(
        editorState,
        newContentWithLink,
        'insert-characters'
      )

      return setEditorState(
        EditorState.forceSelection(
          withLinkText,
          currentContentState.getSelectionAfter()
        )
      )
    }

    const contentStateWithEntity = currentContentState.createEntity(
      'LINK',
      'IMMUTABLE',
      { url: linkUrl }
    )
    const entityKey = contentStateWithEntity.getLastCreatedEntityKey()
    const newEditorState = EditorState.set(editorState, {
      currentContent: contentStateWithEntity,
    })
    return setEditorState(
      RichUtils.toggleLink(
        newEditorState,
        newEditorState.getSelection(),
        entityKey
      )
    )
  }

  const handleChange = (state: EditorState) => {
    if (onChange) {
      onChange(convertToMarkdown(state))
    }

    return setEditorState(state)
  }

  const toggleBlockType = (blockType: string | null) => {
    if (!blockType) return
    handleChange(RichUtils.toggleBlockType(editorState, blockType))
  }

  const toggleInlineStyle = (inlineStyle: string | null) => {
    if (!inlineStyle) return
    handleChange(RichUtils.toggleInlineStyle(editorState, inlineStyle))
  }

  return (
    <div className="bw1 br2 b--solid b--muted-4">
      <div className="pa4 flex flex-wrap-s">
        <HeadingInput
          onAdd={toggleBlockType}
          activeStyle={getContentBlockType()}
        />
        <InlineStyleControls
          editorState={editorState}
          onToggle={toggleInlineStyle}
        />
        <BlockStyleControls
          editorState={editorState}
          onToggle={toggleBlockType}
        />
        <LinkInput
          onAdd={handleAddLink}
          getCurrentSelection={getCurrentSelectionText}
        />
        <ImageInput onAdd={handleAddImage} />
      </div>
      <div className={className}>
        <Editor
          editorState={editorState}
          onChange={state => handleChange(state)}
          blockRendererFn={mediaBlockRenderer}
          blockStyleFn={styleBlockRenderer}
        />
      </div>
    </div>
  )
}

export default RichTextEditor

```


[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/RichTextEditor/ImageInput.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import * as React from 'react'
import { graphql, MutationFunc } from 'react-apollo'
import { useDropzone } from 'react-dropzone'
import { defineMessages, useIntl } from 'react-intl'
import {
  Button,
  IconClose,
  IconImage,
  Input,
  Spinner,
  IconCaretDown,
} from 'vtex.styleguide'

import StyleButton from './StyleButton'
import { useClickOutside } from './utils'
import SeparatorWithLine from '../admin/pages/SeparatorWithLine'
import UploadFile from '../../queries/UploadFile.graphql'

interface MutationData {
  uploadFile: { fileUrl: string }
}

interface Props {
  onAdd: (imageUrl: string) => void
  uploadFile?: MutationFunc<MutationData>
}

const MAX_SIZE = 4 * 1024 * 1024

const messages = defineMessages({
  addBtn: {
    defaultMessage: 'Add',
    id: 'admin/pages.admin.rich-text-editor.add-button',
  },
  addTitle: {
    defaultMessage: 'Insert image',
    id: 'admin/pages.admin.rich-text-editor.add-image.title',
  },
  addLabel: {
    defaultMessage: 'Image URL',
    id: 'admin/pages.admin.rich-text-editor.add-image.label',
  },
  addPlaceholder: {
    defaultMessage: 'URL',
    id: 'admin/pages.admin.rich-text-editor.add-image.placeholder',
  },
  fileSizeError: {
    defaultMessage:
      'File exceeds the size limit of 4MB. Please choose a smaller one.',
    id: 'admin/pages.editor.image-uploader.error.file-size',
  },
  genericError: {
    defaultMessage: 'Something went wrong. Please try again.',
    id: 'admin/pages.editor.image-uploader.error.generic',
  },
  uploadBtn: {
    defaultMessage: 'Upload image',
    id: 'admin/pages.admin.rich-text-editor.upload-button',
  },
  uploadLabel: {
    defaultMessage: 'Choose an image to upload',
    id: 'admin/pages.admin.rich-text-editor.upload-image-label',
  },
})

const ImageInput = ({ onAdd, uploadFile }: Props) => {
  const intl = useIntl()
  const ref = React.useRef<HTMLDivElement>(null)
  const [isLoading, setIsLoading] = React.useState(false)
  const [isOpen, setIsOpen] = React.useState(false)
  const [imageUrl, setImageUrl] = React.useState<string | undefined>()
  const [error, setError] = React.useState<string | null>()

  useClickOutside(ref, () => setIsOpen(false))

  const onDropImage = async (files: File[]) => {
    if (!uploadFile) {
      return undefined
    }

    setError(null)

    try {
      if (files && files[0]) {
        setIsLoading(true)

        const result = await uploadFile({ variables: { file: files[0] } })

        setIsLoading(false)
        setIsOpen(false)

        return result && result.data && onAdd(result.data.uploadFile.fileUrl)
      } else {
        return setError(intl.formatMessage(messages.fileSizeError))
      }
    } catch (err) {
      setError(intl.formatMessage(messages.genericError))
      setIsLoading(false)
    }
  }

  const { getInputProps, getRootProps } = useDropzone({
    accept: 'image/*',
    maxSize: MAX_SIZE,
    multiple: false,
    onDrop: onDropImage,
  })

  const handleAddImage = () => {
    setIsOpen(false)
    if (imageUrl) {
      return onAdd(imageUrl)
    }
  }

  return (
    <div className="relative" ref={ref}>
      <StyleButton
        title={intl.formatMessage(messages.addTitle)}
        active={isOpen}
        onToggle={() => setIsOpen(!isOpen)}
        style={null}
        label={
          <div className="flex flex-row justify-between items-center w-100">
            <IconImage />
            <IconCaretDown size={8} />
          </div>
        }
      />

      {isOpen && (
        <div className="absolute pa5 bg-white b--solid b--muted-4 bw1 br2 w5">
          {isLoading && (
            <div className="absolute flex justify-center items-center top-0 left-0 h-100 w-100 br2 z-1 bg-black-05">
              <Spinner />
            </div>
          )}

          <div className={`flex flex-column ${isLoading && 'o-20'}`}>
            <div className="mb4">
              <Input
                label={intl.formatMessage(messages.addLabel)}
                onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                  setImageUrl(e.target.value)
                }
                placeholder={intl.formatMessage(messages.addPlaceholder)}
              />
            </div>

            <Button onClick={handleAddImage} size="small" disabled={!imageUrl}>
              {intl.formatMessage(messages.addBtn)}
            </Button>

            <SeparatorWithLine />

            <div className="flex flex-column">
              <span className="db mb3 w-100 c-on-base t-small">
                {intl.formatMessage(messages.uploadLabel)}
              </span>
              <div {...getRootProps()} className="flex flex-column">
                <input {...getInputProps()} />
                <Button size="small">
                  {intl.formatMessage(messages.uploadBtn)}
                </Button>
              </div>
            </div>

            {error && (
              <div className="flex flex-row c-danger t-small justify-center items-center mt5">
                <IconClose />
                <span>{error}</span>
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  )
}

export default graphql<Props>(UploadFile, {
  name: 'uploadFile',
})(ImageInput)

```


[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/RichTextEditor/Link.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import * as React from 'react'

import { Link as LinkProps } from 'draft-js'

const Link = (props: LinkProps) => {
  const { contentState, entityKey, children } = props
  const { url } = contentState.getEntity(entityKey).getData()

  return (
    <a href={url} className="blue">
      {children}
    </a>
  )
}

export default Link

```


[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/RichTextEditor/LinkInput.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import * as React from 'react'
import { defineMessages, useIntl } from 'react-intl'
import { Button, IconLink, Input } from 'vtex.styleguide'

import StyleButton from './StyleButton'
import { useClickOutside } from './utils'

interface Props {
  onAdd: (text: string, link: string) => void
  getCurrentSelection: () => string | null
}

const messages = defineMessages({
  btn: {
    defaultMessage: 'Add',
    id: 'admin/pages.admin.rich-text-editor.add-button',
  },
  insertLink: {
    id: 'admin/pages.admin.rich-text-editor.link.title',
    defaultMessage: 'Insert link',
  },
  text: {
    defaultMessage: 'Text',
    id: 'admin/pages.admin.rich-text-editor.add-image.text-label',
  },
})

const LinkInput = ({ onAdd, getCurrentSelection }: Props) => {
  const intl = useIntl()
  const ref = React.useRef<HTMLDivElement>(null)

  const [isOpen, setIsOpen] = React.useState(false)
  const [link, setLink] = React.useState<string | undefined>()
  const [text, setText] = React.useState<string | undefined>()

  useClickOutside(ref, () => setIsOpen(false))

  const handleAddLink = () => {
    setIsOpen(false)
    if (text && link) {
      return onAdd(text, link)
    }
  }

  const handleToggleDialog = () => {
    if (isOpen) {
      return setIsOpen(false)
    }

    setText(getCurrentSelection() || undefined)
    setIsOpen(true)
  }

  return (
    <div className="relative" ref={ref}>
      <StyleButton
        title={intl.formatMessage(messages.insertLink)}
        active={isOpen}
        onToggle={handleToggleDialog}
        style={null}
        label={<IconLink />}
      />

      {isOpen && (
        <div className="flex flex-column absolute pa5 bg-white b--solid b--muted-4 bw1 br2 w5 z-1">
          <div className="mb4">
            <Input
              label={intl.formatMessage(messages.text)}
              value={text}
              onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                setText(e.target.value)
              }
            />
          </div>
          <div className="mb4">
            <Input
              label={'URL'}
              onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                setLink(e.target.value)
              }
            />
          </div>
          <Button
            disabled={!text || !link}
            onClick={handleAddLink}
            size="small"
          >
            {intl.formatMessage(messages.btn)}
          </Button>
        </div>
      )}
    </div>
  )
}

export default LinkInput

```


[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/RichTextEditor/StyleButton.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

import { Tooltip } from 'vtex.styleguide'

interface StyleBtnProps {
  label: string | JSX.Element
  title?: string | JSX.Element
  active: boolean
  onToggle: (style: string | null) => void
  style: string | null
  className?: string
}

const StyleButton = ({
  active,
  onToggle,
  label,
  style,
  className,
  title,
}: StyleBtnProps) => {
  const handleToggle = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    e.preventDefault()
    onToggle(style)
  }

  return (
    <Tooltip label={title} position="bottom">
      <div
        className={`f6 pointer mr3 flex flex-row justify-center items-center outline-0 ph3 h2 b--muted-4 br2 ba ${
          active ? 'c-action-primary bg-muted-5' : ''
        } ${className}`}
        onMouseDown={handleToggle}
        role="button"
        tabIndex={0}
      >
        {label}
      </div>
    </Tooltip>
  )
}

StyleButton.defaultProps = {
  title: '',
  className: '',
}

export default StyleButton

```


[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/RichTextEditor/HeadingInput.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import * as React from 'react'
import { FormattedMessage } from 'react-intl'

import { IconCaretDown, IconCheck } from 'vtex.styleguide'

import StyleButton from './StyleButton'
import { useClickOutside } from './utils'

const BLOCK_TYPES = [
  {
    label: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.heading.unstyled"
        defaultMessage="Title"
      />
    ),
    style: 'unstyled',
  },
  {
    label: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.heading.header-one"
        defaultMessage="Title"
      />
    ),
    style: 'header-one',
  },
  {
    label: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.heading.header-two"
        defaultMessage="Title"
      />
    ),
    style: 'header-two',
  },
  {
    label: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.heading.header-three"
        defaultMessage="Title"
      />
    ),
    style: 'header-three',
  },
  {
    label: (
      <FormattedMessage
        id="admin/pages.admin.rich-text-editor.heading.header-four"
        defaultMessage="Title"
      />
    ),
    style: 'header-four',
  },
]

const hasActiveStyle = (style: string) => {
  return !!BLOCK_TYPES.find(block => block.style === style)
}

interface Props {
  onAdd: (link: string) => void
  activeStyle: string
}

const HeadingInput = ({ onAdd, activeStyle }: Props) => {
  const ref = React.useRef<HTMLDivElement>(null)
  const [isOpen, setIsOpen] = React.useState(false)

  useClickOutside(ref, () => setIsOpen(false))

  const handleClick = (
    e: React.MouseEvent<HTMLDivElement, MouseEvent>,
    style: string
  ) => {
    e.preventDefault()
    setIsOpen(false)
    onAdd(style)
  }

  return (
    <div className="relative" ref={ref}>
      <StyleButton
        title={
          <FormattedMessage
            id="admin/pages.admin.rich-text-editor.heading.title"
            defaultMessage="Styles"
          />
        }
        className="justify-between w4"
        active={isOpen}
        onToggle={() => setIsOpen(!isOpen)}
        style={null}
        label={
          <div className="flex flex-row justify-between items-center w-100">
            <FormattedMessage
              id={
                hasActiveStyle(activeStyle)
                  ? `admin/pages.admin.rich-text-editor.heading.${activeStyle}`
                  : `admin/pages.admin.rich-text-editor.heading.unstyled`
              }
              defaultMessage="Regular text"
            />
            <IconCaretDown size={8} />
          </div>
        }
      />

      {isOpen && (
        <div className="flex flex-column absolute bg-white b--solid b--muted-4 bw1 br2 w5">
          {BLOCK_TYPES.map(({ label, style }, index: number) => (
            <div
              key={index}
              className={`flex justify-between items-center pointer pa5 f${index} ${
                index > 0 ? 'b bt b--muted-4' : ''
              }`}
              role="presentation"
              onMouseDown={(e: React.MouseEvent<HTMLDivElement, MouseEvent>) =>
                handleClick(e, style)
              }
            >
              {label}
              {style === activeStyle && <IconCheck />}
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

export default React.memo(HeadingInput)

```


[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/RichTextEditor/style.css from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```css
.RichEditor_root {
  background: #fff;
  border: 1px solid #ddd;
  font-size: 14px;
  padding: 15px;
}

.RichEditor_editor {
  border-top: 1px solid #ddd;
  cursor: text;
  font-size: 16px;
  min-height: 20rem;
  padding: 10px;
}

.RichEditor_editor div[data-contents='true'] {
  min-height: 20em;
}

.RichEditor_editor .public-DraftEditorPlaceholder-root,
.RichEditor_editor .public-DraftEditor-content {
  margin: 0 -15px -15px;
  padding: 15px;
}

.RichEditor_hidePlaceholder .public-DraftEditorPlaceholder-root {
  display: none;
}

.RichEditor_editor .RichEditor_blockquote {
  border-left: 5px solid #eee;
  color: #666;
  font-family: 'Hoefler Text', 'Georgia', serif;
  font-style: italic;
  margin: 16px 0;
  padding: 10px 20px;
}

.RichEditor_editor .public-DraftStyleDefault-pre {
  background-color: rgba(0, 0, 0, 0.05);
  font-family: 'Inconsolata', 'Menlo', 'Consolas', monospace;
  font-size: 16px;
  padding: 20px;
}

```


[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/RichTextEditor/utils.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import * as React from 'react'
import {
  ContentBlock,
  ContentState,
  convertFromRaw,
  convertToRaw,
  EditorState,
} from 'draft-js'
import { draftjsToMd, mdToDraftjs } from 'draftjs-md-converter'

import Media from './Media'

export function mediaBlockRenderer(block: ContentBlock) {
  if (block.getType() === 'atomic') {
    return {
      component: Media,
      editable: false,
    }
  }

  return null
}

export function styleBlockRenderer(block: ContentBlock) {
  const blockType = block.getType()
  if (blockType === 'left') return 'align-left'
  if (blockType === 'center') return 'align-center'
  if (blockType === 'right') return 'align-right'
  return ''
}

export function findLinkEntities(
  contentBlock: ContentBlock,
  callback: (start: number, end: number) => void,
  contentState: ContentState
) {
  return contentBlock.findEntityRanges(character => {
    const entityKey = character.getEntity()
    return (
      entityKey !== null &&
      contentState.getEntity(entityKey).getType() === 'LINK'
    )
  }, callback)
}

export function convertToMarkdown(editorState: EditorState) {
  const rawContentState = convertToRaw(editorState.getCurrentContent())
  return draftjsToMd(rawContentState)
}

export function convertToEditorState(markdownText: string) {
  const rawMarkdown = mdToDraftjs(markdownText)
  return convertFromRaw(rawMarkdown)
}

export function useClickOutside(
  ref: React.RefObject<HTMLDivElement>,
  callback: () => void
) {
  function handleClickOutside(event: MouseEvent) {
    if (ref && ref.current && !ref.current.contains(event.target as Node)) {
      callback()
    }
  }

  React.useEffect(() => {
    document.addEventListener('mousedown', handleClickOutside)
    return () => document.removeEventListener('mousedown', handleClickOutside)
  })
}

```


[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/RichTextEditor/Media.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import * as React from 'react'

import { Media as MediaProps } from 'draft-js'

const Media = (props: MediaProps) => {
  const { contentState, block } = props
  const blockEntity = block.getEntityAt(0)

  if (!blockEntity) {
    return null
  }

  const entity = contentState.getEntity(blockEntity)
  const { src } = entity.getData()
  const type = entity.getType()

  if (type === 'IMAGE') {
    return <img alt={src} src={src} />
  }

  return null
}

export default Media

```


[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/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import debounce from 'lodash/debounce'
import { path } from 'ramda'
import React, { useCallback, useEffect, useMemo } from 'react'
import { FormattedMessage } from 'react-intl'
import { Alert, Spinner, ToastConsumer } from 'vtex.styleguide'

import { State as HighlightOverlayState } from '../HighlightOverlay/typings'
import Sidebar from './Sidebar'
import { FormMetaProvider } from './Sidebar/FormMetaContext'
import { ModalProvider } from './Sidebar/ModalContext'
import IframeNavigationController from './IframeNavigationController'
import Styles from './Styles'
import Topbar from './Topbar'
import styles from './EditorContainer.css'

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

export const APP_CONTENT_ELEMENT_ID = 'app-content-editor'

const getContainerProps = (layout: Viewport) => {
  switch (layout) {
    case 'mobile':
      return {
        className: `${styles['mobile-preview']} center`,
        style: {
          animationDuration: '0.2s',
          transition: `width 660ms`,
        },
      }
    case 'tablet':
      return {
        className: `${styles['tablet-preview']} center`,
        style: {
          animationDuration: '0.2s',
          transition: `width 660ms`,
        },
      }
    default:
      return {
        className: 'w-100 h-100 center',
        style: {
          animationDuration: '0.2s',
          transition: `width 660ms`,
        },
      }
  }
}

interface Props {
  editor: EditorContextType
  iframeRuntime: RenderContext | null
  isSiteEditor: boolean
}

const EditorContainer: React.FC<Props> = ({
  children,
  editor,
  iframeRuntime,
  isSiteEditor,
}) => {
  const { editMode, editExtensionPoint, viewport } = editor

  const highlightExtensionPoint = useCallback(
    debounce((highlightTreePath: string | null) => {
      const iframe = document.getElementById('store-iframe') || {}
      const setHighlightTreePath = path<
        (value: Partial<HighlightOverlayState>) => void
      >(['contentWindow', '__setHighlightTreePath'], iframe)
      if (setHighlightTreePath) {
        setHighlightTreePath({
          editExtensionPoint,
          editMode,
          highlightHandler: highlightExtensionPoint,
          highlightTreePath,
        })
      }
    }, 100),
    [editMode, editExtensionPoint]
  )

  const updateHighlightTitleByTreePath = useCallback(
    (
      sidebarBlocksMap?: Record<string, { title?: string; isEditable: boolean }>
    ) => {
      const iframe = document.getElementById('store-iframe') || {}
      const setHighlightTreePath = path<
        (value: Partial<HighlightOverlayState>) => void
      >(['contentWindow', '__setHighlightTreePath'], iframe)
      if (setHighlightTreePath) {
        setHighlightTreePath({
          sidebarBlocksMap: sidebarBlocksMap || {},
        })
      }
    },
    []
  )

  useEffect(() => {
    highlightExtensionPoint(null)
  }, [editMode, highlightExtensionPoint])

  const containerProps = useMemo(() => getContainerProps(viewport), [viewport])
  const isDevelopment = iframeRuntime && iframeRuntime.production === false
  const isMasterWorkspace =
    iframeRuntime && iframeRuntime.workspace === 'master'
  const hasAlert = isMasterWorkspace || isDevelopment

  return (
    <FormMetaProvider>
      <ModalProvider>
        <IframeNavigationController iframeRuntime={iframeRuntime} />
        <div className="vtex-admin-pages-4-x h-100 min-vh-100 flex flex-row-reverse bg-base bb bw1 b--muted-5">
          <div
            className={
              editor.isSidebarVisible && isSiteEditor ? 'w-18em-ns' : 'dn'
            }
          >
            {isSiteEditor && iframeRuntime ? (
              <ToastConsumer>
                {({ showToast }) => (
                  <Sidebar
                    highlightHandler={highlightExtensionPoint}
                    iframeRuntime={iframeRuntime}
                    showToast={showToast}
                    updateHighlightTitleByTreePath={
                      updateHighlightTitleByTreePath
                    }
                  />
                )}
              </ToastConsumer>
            ) : (
              <div className="bg-white-70 flex items-center w-18em-ns h-100 justify-center z-2">
                <Spinner />
              </div>
            )}
          </div>

          <div className="w-100 dn flex-ns flex-column">
            <div style={{ minHeight: '64px' }} className="bg-muted-5">
              {isSiteEditor && iframeRuntime && (
                <Topbar iframeRuntime={iframeRuntime} />
              )}
            </div>

            {isDevelopment && (
              <div className="pa5 bg-muted-5">
                <Alert type="warning">
                  <FormattedMessage
                    id="admin/pages.editor.container.dev-mode-warning.text"
                    defaultMessage="You are in development mode. Changes to the content will not go to production."
                  />
                </Alert>
              </div>
            )}

            {isMasterWorkspace && (
              <div className="pa5 bg-muted-5">
                <Alert type="warning">
                  <FormattedMessage
                    id="admin/pages.editor.container.master-workspace-warning.text"
                    defaultMessage="You are editing the live store. Changes will have immediate effect."
                  />
                </Alert>
              </div>
            )}

            <div
              className={`pa5 bg-muted-5 flex items-start z-0 center-m left-0-m overflow-x-auto-m ${
                isSiteEditor && iframeRuntime
                  ? styles[`calc--height-relative${hasAlert ? '--dev' : ''}`]
                  : 'top-0 w-100 h-100'
              }`}
            >
              {!isSiteEditor && iframeRuntime && <Styles />}

              <div id={APP_CONTENT_ELEMENT_ID} className="relative w-100 h-100">
                <main
                  {...containerProps}
                  role="main"
                  style={{
                    transition: `width 250ms ease-in-out 0ms, height 250ms ease-in-out 0ms`,
                  }}
                >
                  {children}
                </main>
              </div>
            </div>
          </div>
        </div>
      </ModalProvider>
    </FormMetaProvider>
  )
}

export default EditorContainer

```


[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/IframeNavigationController.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect } from 'react'
import { defineMessages, useIntl } from 'react-intl'

import { useEditorContext } from '../EditorContext'
import { useFormMetaContext } from './Sidebar/FormMetaContext'

interface Props {
  iframeRuntime: RenderContext | null
}

const maybeCall = (fn: (() => void) | void) => {
  if (typeof fn === 'function') {
    return fn()
  }
}

const messages = defineMessages({
  confirm: {
    defaultMessage: 'Are you sure you want to leave?',
    id: 'admin/pages.editor.iframe.confirm-prompt',
  },
})

const IframeNavigationController: React.FunctionComponent<Props> = ({
  iframeRuntime,
}) => {
  const intl = useIntl()
  const { getWasModified, setWasModified } = useFormMetaContext()
  const { editExtensionPoint } = useEditorContext()

  const wasModified = getWasModified()

  useEffect(() => {
    let unblock: (() => void) | void
    let unlisten: (() => void) | void

    if (wasModified && iframeRuntime && iframeRuntime.history) {
      unblock = iframeRuntime.history.block(
        intl.formatMessage(messages.confirm)
      )
      unlisten = iframeRuntime.history.listen((_, action) => {
        const hasNavigated = ['PUSH', 'REPLACE', 'POP'].includes(action)
        if (hasNavigated) {
          unblock = maybeCall(unblock)
          unlisten = maybeCall(unlisten)
          setWasModified(false)
          editExtensionPoint(null)
        }
      })
    }

    return () => {
      unblock = maybeCall(unblock)
      unlisten = maybeCall(unlisten)
    }
  }, [editExtensionPoint, iframeRuntime, setWasModified, wasModified, intl])

  return null
}

export default IframeNavigationController

```


[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/EditorContainer.css from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```css
.h-3em {
  height: 48px;
}

.calc--height-relative {
  height: calc(100% - 3em);
}

.calc--height-relative--dev {
  height: calc(100% - 8.5em);
}

.mobile-preview {
  width: 360px;
  height: 640px;
  max-height: 100%;
  box-shadow: 1px 4px 8px rgba(0, 0, 0, 0.2);
  overflow: hidden;
}

.tablet-preview {
  width: 768px;
  height: 1024px;
  max-height: 100%;
  box-shadow: 1px 4px 8px rgba(0, 0, 0, 0.2);
  overflow: hidden;
}

```


[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/StoreIframe.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect, useRef } from 'react'

import { useBinding } from './Topbar/ContextSelectors/hooks/useBinding'

interface Props {
  path?: string
}

const StoreIframe: React.FunctionComponent<Props> = ({ path }) => {
  const [binding] = useBinding()
  const iframeRef = useRef<HTMLIFrameElement>(null)

  useEffect(() => {
    if (iframeRef && iframeRef.current && iframeRef.current.contentWindow) {
      /* eslint-disable prefer-rest-params */
      const oldLog = (iframeRef.current.contentWindow as any).console.log
      const oldError = (iframeRef.current.contentWindow as any).console.error
      ;(iframeRef.current
        .contentWindow as any).console.log = function newLog() {
        Array.prototype.unshift.call(arguments, `[Iframe]: `)
        oldLog.apply(this, arguments)
      }
      ;(iframeRef.current
        .contentWindow as any).console.error = function newError() {
        Array.prototype.unshift.call(arguments, `[Iframe]: `)
        oldError.apply(this, arguments)
      }
    }
  }, [])

  const getJoiner = (src: string) => (src.includes('?') ? '&' : '?')

  let src = path ? `/${path}` : '/'

  if (binding && !src.includes('__bindingAddress')) {
    src += `${getJoiner(src)}__bindingAddress=${binding.canonicalBaseAddress}`
  }

  if (!src.includes('__siteEditor')) {
    src += `${getJoiner(src)}__siteEditor=true`
  }

  return (
    <iframe
      className="w-100 h-100"
      frameBorder="0"
      id="store-iframe"
      ref={iframeRef}
      src={src}
      title="store-iframe"
    />
  )
}

export default React.memo(StoreIframe)

```


[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/TargetPathContext.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { ComponentType, useContext } from 'react'

export interface TargetPathContextProps {
  targetPath: string
  setTargetPath: (s: string) => void
}

const TargetPathContext = React.createContext<TargetPathContextProps>({
  setTargetPath: () => undefined,
  targetPath: '',
})
const useTargetPathContext = () => useContext(TargetPathContext)

const withTargetPath = <TOriginalProps extends {} = {}>(
  Component: ComponentType<TOriginalProps & TargetPathContextProps>
): ComponentType<TOriginalProps> => {
  const extendedComponent = (props: TOriginalProps) => (
    <TargetPathContext.Consumer>
      {({ setTargetPath, targetPath }) => {
        return (
          <Component
            {...props}
            setTargetPath={setTargetPath}
            targetPath={targetPath}
          />
        )
      }}
    </TargetPathContext.Consumer>
  )
  return extendedComponent
}

export { useTargetPathContext, withTargetPath }
export default TargetPathContext

```


[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/FormFieldSeparator.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const FormFieldSeparator = () => <div className="mt7" />

export default FormFieldSeparator

```


[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/utils.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const slugify = (text: string) => {
  const specialChars: { [key: string]: string } = {
    à: 'a',
    ä: 'a',
    á: 'a',
    â: 'a',
    æ: 'a',
    å: 'a',
    ë: 'e',
    è: 'e',
    é: 'e',
    ê: 'e',
    î: 'i',
    ï: 'i',
    ì: 'i',
    í: 'i',
    ò: 'o',
    ó: 'o',
    ö: 'o',
    ô: 'o',
    ø: 'o',
    ù: 'o',
    ú: 'u',
    ü: 'u',
    û: 'u',
    ñ: 'n',
    ç: 'c',
    ß: 's',
    ÿ: 'y',
    œ: 'o',
    ŕ: 'r',
    ś: 's',
    ń: 'n',
    ṕ: 'p',
    ẃ: 'w',
    ǵ: 'g',
    ǹ: 'n',
    ḿ: 'm',
    ǘ: 'u',
    ẍ: 'x',
    ź: 'z',
    ḧ: 'h',
    '·': '-',
    '/': '-',
    _: '-',
    ',': '-',
    ':': '-',
    ';': '-',
  }

  return text
    .toString()
    .toLowerCase()
    .replace(/\s+/g, '-')
    .replace(/./g, target => specialChars[target] || target) // Replace special characters using the hash map
    .replace(/&/g, '-and-') // Replace & with 'and'
    .replace(/[^\w-]+/g, '') // Remove all non-word chars
    .replace(/--+/g, '-') // Replace multiple - with single -
    .replace(/^-+/, '') // Trim - from start of text
    .replace(/-+$/, '') // Trim - from end of text
}

```


[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/AdminStructure.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

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

interface Props {
  title: string
}

const AdminStructure: React.FC<Props> = ({ children, title }) => (
  <div className="h-100 min-vh-100 overflow-y-auto bg-light-silver">
    <div className="center mw8">
      <PageHeader title={title} />

      {children}
    </div>
  </div>
)

export default AdminStructure

```


[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/MediaGalleryWidget/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { ActionMenu, Spinner, IconOptionsDots } from 'vtex.styleguide'
import MediaGallery from 'vtex.admin-cms/MediaGallery'
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'
import { JSONSchema6 } from 'json-schema'
import { useDropzone } from 'react-dropzone'

import ImagePreview from '../form/ImageUploader/ImagePreview'
import EmptyState from '../form/ImageUploader/EmptyState'

interface MediaGalleryWidgetProps {
  disabled?: boolean
  onChange?: (pathname: string | null) => void
  schema: JSONSchema6
  value: string
}

const messages = defineMessages({
  remove: {
    id: 'admin/pages.admin.redirects.form.button.remove',
  },
  replace: {
    id: 'admin/pages.admin.redirects.form.button.replace',
  },
  fileSizeError: {
    defaultMessage:
      'File exceeds the size limit of 4MB. Please choose a smaller one.',
    id: 'admin/pages.editor.image-uploader.error.file-size',
  },
  genericError: {
    defaultMessage: 'Something went wrong. Please try again.',
    id: 'admin/pages.editor.image-uploader.error.generic',
  },
})
function Widget(props: MediaGalleryWidgetProps) {
  const intl = useIntl()
  const { onChange, value } = props
  const [isImageUploading, setIsImageUploading] = React.useState(false)
  const modalState = MediaGallery.useModalState({ animated: true })
  const [previewUrl, setPreviewUrl] = React.useState('')
  const { createMedia } = MediaGallery.useCreateMedia({
    onImageSelected: handleImageSelected,
    onImagePreview: setPreviewUrl,
    onImageUploading: handleUploading,
    onError: handleError,
    modalState,
  })
  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    accept: 'image/*',
    maxSize: 4 * 10 ** 6,
    onDrop: acceptedFiles => {
      if (acceptedFiles?.[0]) {
        createMedia(acceptedFiles[0])
      } else {
        setError(intl.formatMessage(messages.fileSizeError))
      }
    },
  })
  const [error, setError] = React.useState<string | null>(null)

  const options: ActionMenuOption[] = [
    {
      label: intl.formatMessage(messages.replace),
      onClick: () => {
        modalState.setVisible(true)

        setError(null)
      },
    },
    {
      label: intl.formatMessage(messages.remove),
      onClick: () => {
        onChange?.(null)
      },
    },
  ]

  function handleUploading(isUploading: boolean) {
    setIsImageUploading(isUploading)
  }

  function handleImageSelected(imageUrl: string) {
    onChange?.(imageUrl)
  }

  const backgroundImagePreviewStyle = React.useMemo(
    () => ({
      backgroundImage: `url("${previewUrl}")`,
      height: '8rem',
    }),
    [previewUrl]
  )

  function handleError() {
    setError(intl.formatMessage(messages.genericError))
  }

  return (
    <>
      <FormattedMessage id={props.schema?.title ?? 'Image'}>
        {text => <span className="w-100 db mb3">{text}</span>}
      </FormattedMessage>

      <div className="w-100">
        <>
          {isImageUploading ? (
            <div
              className="w-100 h-100 relative bg-center contain"
              style={backgroundImagePreviewStyle}
            >
              <div className="absolute top-0 left-0 right-0 bottom-0 bg-white-70 z-2 flex items-center justify-center">
                <Spinner />
              </div>
            </div>
          ) : value && !isDragActive ? (
            <div
              {...getRootProps({
                onClick: e => {
                  e.stopPropagation()
                  modalState.setVisible(true)
                },
              })}
            >
              <input {...getInputProps()} />
              <div style={{ height: '8rem' }}>
                <ImagePreview imageUrl={value}>
                  <ActionMenu
                    variation="primary"
                    menuWidth={200}
                    options={options}
                    buttonSize="small"
                    buttonProps={{
                      size: 'small',
                      variation: 'primary',
                      icon: <IconOptionsDots color="currentColor" />,
                    }}
                  />
                </ImagePreview>
              </div>

              {error && <p className="lh-copy f7 mt3 c-danger">{error}</p>}
            </div>
          ) : (
            <>
              <div
                {...getRootProps({
                  onClick: e => {
                    e.stopPropagation()
                    modalState.setVisible(true)
                  },
                })}
              >
                <input {...getInputProps()} />
                <EmptyState
                  style={
                    isDragActive
                      ? {
                          borderColor: '#134cd8',
                          color: '#134cd8',
                          width: '14.25rem',
                          height: '112px',
                        }
                      : {}
                  }
                />
              </div>
              {error && <p className="lh-copy f7 mt3 c-danger">{error}</p>}
            </>
          )}
        </>

        {modalState.visible && (
          <MediaGallery.Modal
            state={modalState}
            onImageSelected={handleImageSelected}
            onImageUploading={handleUploading}
            onImagePreview={setPreviewUrl}
            onError={handleError}
          />
        )}
      </div>
    </>
  )
}

export default function MediaGalleryWidget(props: MediaGalleryWidgetProps) {
  return (
    <MediaGallery.MediaGalleryProvider>
      <Widget {...props} />
    </MediaGallery.MediaGalleryProvider>
  )
}

```


[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/icons/GearIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const GearIcon: React.FunctionComponent = () => (
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
    <g clipPath="url(#clip0)">
      <path
        d="M13.0667 4.81733L14.6 3.28533L12.7147 1.4L11.1827 2.93333C10.6146 2.57238 9.98923 2.31086 9.33333 2.16V0H6.66667V2.16C6.01077 2.31086 5.38537 2.57238 4.81733 2.93333L3.28533 1.4L1.4 3.28533L2.93333 4.81733C2.57238 5.38537 2.31086 6.01077 2.16 6.66667H0V9.33333H2.16C2.31086 9.98923 2.57238 10.6146 2.93333 11.1827L1.4 12.7147L3.28533 14.6L4.81733 13.0667C5.38537 13.4276 6.01077 13.6891 6.66667 13.84V16H9.33333V13.84C9.98923 13.6891 10.6146 13.4276 11.1827 13.0667L12.7147 14.6L14.6 12.7147L13.0667 11.1827C13.4276 10.6146 13.6891 9.98923 13.84 9.33333H16V6.66667H13.84C13.6891 6.01077 13.4276 5.38537 13.0667 4.81733V4.81733ZM8 10.6667C7.47258 10.6667 6.95701 10.5103 6.51848 10.2173C6.07995 9.92424 5.73815 9.50776 5.53632 9.02049C5.33449 8.53322 5.28168 7.99704 5.38457 7.47976C5.48747 6.96248 5.74144 6.48732 6.11438 6.11438C6.48732 5.74144 6.96248 5.48747 7.47976 5.38457C7.99704 5.28168 8.53322 5.33449 9.02049 5.53632C9.50776 5.73815 9.92424 6.07995 10.2173 6.51848C10.5103 6.95701 10.6667 7.47258 10.6667 8C10.6667 8.70724 10.3857 9.38552 9.88562 9.88562C9.38552 10.3857 8.70724 10.6667 8 10.6667Z"
        fill="#727273"
      />
    </g>
    <defs>
      <clipPath id="clip0">
        <rect width="16" height="16" fill="white" />
      </clipPath>
    </defs>
  </svg>
)

export default GearIcon

```


[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/icons/EarthIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const EarthIcon: React.FC = () => (
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
    <path
      d="M8 1C4.15 1 1 4.15 1 8C1 11.85 4.15 15 8 15C11.85 15 15 11.85 15 8C15 4.15 11.85 1 8 1ZM8 13.25C6.6875 13.25 5.55 12.8125 4.5875 12.025C5.1125 11.325 5.2 10.45 4.675 9.75C4.2375 9.1375 3.5375 8.525 3.5375 8C3.5375 7.3 5.2 7.0375 5.725 6.3375C6.3375 5.6375 5.725 4.325 5.1125 3.625C5.8125 3.1875 6.6 2.8375 7.5625 2.75C7.7375 3.1875 8.35 3.7125 8.6125 4.0625C9.05 4.7625 9.1375 5.2 9.75 5.2C9.925 5.2 11.5 5.2 12.375 4.9375C12.9 5.8125 13.25 6.8625 13.25 8C13.25 10.8875 10.8875 13.25 8 13.25Z"
      fill="#727273"
    />

    <path
      d="M9.92468 6.8623C8.87468 6.8623 7.91218 7.56231 7.99968 8.5248C8.26218 10.7123 7.56218 11.4998 8.52468 11.9373C9.66218 12.3748 11.9372 10.2748 11.8497 8.5248C11.8497 7.56231 11.0622 6.8623 9.92468 6.8623Z"
      fill="#727273"
    />
  </svg>
)

export default EarthIcon

```


[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/icons/ComponentDragHandleIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const ComponentDragHandleIcon: React.FunctionComponent = () => (
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
    <circle cx="10" cy="3" r="1" fill="#CACBCC" />
    <circle cx="10" cy="8" r="1" fill="#CACBCC" />
    <circle cx="10" cy="13" r="1" fill="#CACBCC" />
    <circle cx="6" cy="3" r="1" fill="#CACBCC" />
    <circle cx="6" cy="8" r="1" fill="#CACBCC" />
    <circle cx="6" cy="13" r="1" fill="#CACBCC" />
  </svg>
)

export default ComponentDragHandleIcon

```


[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/icons/ArrowIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
}

const ArrowIcon: React.FunctionComponent<Props> = ({ color }) => (
  <svg
    width="5"
    height="9"
    viewBox="0 0 5 9"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path d="M0 8.5L5 4.5L0 0.5V8.5Z" fill={color} />
  </svg>
)

ArrowIcon.defaultProps = {
  color: 'currentColor',
}

export default ArrowIcon

```


[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/icons/PageIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const PageIcon: React.FC = () => (
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
    <g clipPath="url(#clip0)">
      <path
        d="M12.8623 13.75H3.3623V2.25H9.69564L12.8623 5.38636V13.75Z"
        stroke="#727273"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth="1.4"
      />

      <path d="M9 6V2L13 6H9Z" fill="#727273" />
    </g>

    <defs>
      <clipPath id="clip0">
        <rect width="16" height="16" fill="none" />
      </clipPath>
    </defs>
  </svg>
)

export default PageIcon

```


[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/icons/PersonIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const PersonIcon: React.FC = () => (
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
    <path
      d="M7 6.5C8.38071 6.5 9.5 5.38071 9.5 4C9.5 2.61929 8.38071 1.5 7 1.5C5.61929 1.5 4.5 2.61929 4.5 4C4.5 5.38071 5.61929 6.5 7 6.5Z"
      fill="#CACBCC"
    />

    <path
      d="M7 7.5C6.34339 7.5 5.69321 7.62933 5.08658 7.8806C4.47995 8.13188 3.92876 8.50017 3.46447 8.96447C2.52678 9.90215 2 11.1739 2 12.5H12C12 11.1739 11.4732 9.90215 10.5355 8.96447C9.59785 8.02678 8.32608 7.5 7 7.5V7.5Z"
      fill="#CACBCC"
    />
  </svg>
)

export default PersonIcon

```


[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/icons/ContentScheduledIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const ContentScheduledIcon: React.FC = () => (
  <svg
    width="13"
    height="13"
    viewBox="0 0 10 10"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M5.38477 3.07693L5.38477 5.38463"
      stroke="#134CD8"
      strokeWidth="1"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    ></path>

    <path
      d="M3.8457 5.38461H5.38416"
      stroke="#134CD8"
      strokeWidth="1"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    ></path>

    <circle cx="5" cy="5" r="4.25" stroke="#134CD8" strokeWidth="1.5"></circle>
  </svg>
)

export default ContentScheduledIcon

```


[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/icons/ContentActiveIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const ContentActiveIcon: React.FC = () => (
  <svg
    width="10"
    height="10"
    viewBox="0 0 10 10"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <circle cx="5" cy="5" r="5" fill="#8BC34A" />
  </svg>
)

export default ContentActiveIcon

```


[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/icons/AddIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const AddIcon: React.FunctionComponent = () => (
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
    <path
      d="M8 5.42773V10.5706"
      stroke="#134CD8"
      strokeWidth="1.4"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path
      d="M5.42969 8H10.5725"
      stroke="#134CD8"
      strokeWidth="1.4"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path
      d="M13.1429 14H2.85714C2.384 14 2 13.616 2 13.1429V2.85714C2 2.384 2.384 2 2.85714 2H13.1429C13.616 2 14 2.384 14 2.85714V13.1429C14 13.616 13.616 14 13.1429 14Z"
      stroke="#134CD8"
      strokeWidth="1.4"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </svg>
)

export default AddIcon

```


[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/icons/DangerIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const DangerIcon = () => (
  <svg
    width="17"
    height="17"
    viewBox="0 0 17 17"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <circle cx="8.5" cy="8.5" r="8.5" fill="#FF4C4C" />
    <rect x="3" y="7" width="11" height="2" fill="white" />
  </svg>
)

export default React.memo(DangerIcon)

```


[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/icons/DragHandle.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { calcIconSize } from './utils'

import styles from './DragHandle.css'

interface Props {
  className?: string
  size?: number
}

const iconBase = {
  height: 12,
  width: 6,
}

const DragHandle: React.FunctionComponent<Props> = ({
  size,
  className,
  ...props
}) => {
  const newSize = calcIconSize(iconBase, size as number) // TS doesn't detect defaultProps

  return (
    <svg
      className={`${styles['editor-icon--fill']} ${className}`}
      width={newSize.width}
      height={newSize.height}
      viewBox="0 0 6 12"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      {...props}
    >
      <circle cx="1" cy="1" r="1" transform="translate(4)" />
      <circle cx="1" cy="1" r="1" transform="translate(4 5)" />
      <circle cx="1" cy="1" r="1" transform="translate(4 10)" />
      <circle cx="1" cy="1" r="1" />
      <circle cx="1" cy="1" r="1" transform="translate(0 5)" />
      <circle cx="1" cy="1" r="1" transform="translate(0 10)" />
    </svg>
  )
}

DragHandle.defaultProps = {
  size: 20,
}

export default React.memo(DragHandle)

```


[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/icons/ContentInactiveIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const ContentInactiveIcon: React.FC = () => (
  <svg
    width="10"
    height="10"
    viewBox="0 0 10 10"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <circle cx="5" cy="5" r="5" fill="#CACBCC" />
  </svg>
)

export default ContentInactiveIcon

```


[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/icons/PaperErrorIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const PaperErrorIcon: React.FunctionComponent = () => (
  <svg
    width="82"
    height="95"
    viewBox="0 0 82 95"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M69 64.5269V1H1V84.4433C1 89.2916 4.72084 93.589 9.46342 93.9989C9.73012 94.0219 68.7273 93.68 69 93.68"
      fill="#DBE9FD"
    />
    <path
      d="M69 64.5269V1H1V84.4433C1 89.2916 4.72084 93.589 9.46342 93.9989C9.73012 94.0219 68.7273 93.68 69 93.68"
      stroke="#3D5980"
      strokeWidth="2"
      strokeMiterlimit="10"
      strokeLinecap="square"
    />
    <path
      d="M19.3193 84.2844C19.3193 89.4911 15.1102 94 10 94L71.7479 93.7111C76.8581 93.7111 81 89.4911 81 84.2844V78H19.3193V84.2844Z"
      fill="#DBE9FD"
      stroke="#3D5980"
      strokeWidth="2"
      strokeMiterlimit="10"
      strokeLinecap="square"
    />
    <path
      d="M16 23H54"
      stroke="#3D5980"
      strokeWidth="2"
      strokeMiterlimit="10"
      strokeLinecap="square"
    />
    <path
      d="M16 40H54"
      stroke="#3D5980"
      strokeWidth="2"
      strokeMiterlimit="10"
      strokeLinecap="square"
    />
    <path
      d="M16 57H54"
      stroke="#3D5980"
      strokeWidth="2"
      strokeMiterlimit="10"
      strokeLinecap="square"
    />
    <ellipse cx="70" cy="57.5" rx="12" ry="11.5" fill="#FF4C4C" />
    <rect x="62" y="56" width="16" height="2" fill="white" />
  </svg>
)

export default React.memo(PaperErrorIcon)

```


[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/icons/DragHandle.css from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```css
.editor-icon--fill path,
.editor-icon--fill circle {
  fill: #727273;
}

```


[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/icons/CalendarIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const CalendarIcon: React.FC = () => (
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
    <path
      d="M5.25 6.4H4.08333V7.6H5.25V6.4ZM7.58333 6.4H6.41667V7.6H7.58333V6.4ZM9.91667 6.4H8.75V7.6H9.91667V6.4ZM11.0833 2.2H10.5V1H9.33333V2.2H4.66667V1H3.5V2.2H2.91667C2.26917 2.2 1.75583 2.74 1.75583 3.4L1.75 11.8C1.75 12.46 2.26917 13 2.91667 13H11.0833C11.725 13 12.25 12.46 12.25 11.8V3.4C12.25 2.74 11.725 2.2 11.0833 2.2ZM11.0833 11.8H2.91667V5.2H11.0833V11.8Z"
      fill="#CACBCC"
    />
  </svg>
)

export default CalendarIcon

```


[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/icons/utils.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
export interface Dimensions {
  width: number
  height: number
}

export interface IconProps {
  color?: string
  size?: number
  block?: boolean
}

export const calcIconSize: (d: Dimensions, s: number) => Dimensions = (
  iconBase,
  newSize
) => {
  const isHorizontal = iconBase.width >= iconBase.height

  const width = isHorizontal
    ? newSize
    : (newSize * iconBase.width) / iconBase.height

  const height = !isHorizontal
    ? newSize
    : (newSize * iconBase.height) / iconBase.width

  return { width, height }
}

```


[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/icons/UploadTable.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const UploadTableIcon: React.FunctionComponent = () => (
  <svg
    width="120"
    height="120"
    viewBox="0 0 120 120"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <circle cx="60" cy="60" r="60" fill="#F7F9FA" />
    <path
      d="M38 66H82V77C82 77.5523 81.5523 78 81 78H39C38.4477 78 38 77.5523 38 77V66Z"
      fill="#E3E4E6"
    />
    <rect x="42" y="71" width="8" height="2" rx="1" fill="#727273" />
    <rect x="56" y="71" width="8" height="2" rx="1" fill="#727273" />
    <rect x="74" y="71" width="4" height="2" rx="1" fill="#727273" />
    <path
      d="M82 54L38 54L38 43C38 42.4477 38.4477 42 39 42L81 42C81.5523 42 82 42.4477 82 43L82 54Z"
      fill="#F71963"
    />
    <rect
      x="78"
      y="49"
      width="4"
      height="2"
      rx="1"
      transform="rotate(180 78 49)"
      fill="white"
    />
    <rect
      x="64"
      y="49"
      width="8"
      height="2"
      rx="1"
      transform="rotate(180 64 49)"
      fill="white"
    />
    <rect
      x="50"
      y="49"
      width="8"
      height="2"
      rx="1"
      transform="rotate(180 50 49)"
      fill="white"
    />
    <path d="M38 54H82V66H38V54Z" fill="#CACBCC" />
    <rect x="42" y="59" width="8" height="2" rx="1" fill="#727273" />
    <rect x="56" y="59" width="8" height="2" rx="1" fill="#727273" />
    <rect x="74" y="59" width="4" height="2" rx="1" fill="#727273" />
  </svg>
)

export default React.memo(UploadTableIcon)

```


[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/icons/TemplateIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const TemplateIcon: React.FC = () => (
  // <div> is used for making sure that the icon isn't vertically centered
  <div>
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <path
        d="M14.25 15.25H4.75V3.75H11.0833L14.25 6.88636V15.25Z"
        stroke="#727273"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth="1.4"
      />

      <path
        d="M1.75 12.25V0.75H8.08333"
        stroke="#727273"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth="1.4"
      />

      <path d="M10.6377 7.25V3.25L14.6377 7.25H10.6377Z" fill="#727273" />
    </svg>
  </div>
)

export default TemplateIcon

```


[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/icons/ContentLibraryIcon.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
}

const ContentLibraryIcon: React.FC<Props> = ({ color = 'currentColor' }) => (
  <svg
    width="16"
    height="16"
    viewBox="0 0 16 16"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <mask id="path-1-inside-1" fill="white">
      <rect width="13" height="13" rx="1" />
    </mask>

    <rect
      width="13"
      height="13"
      rx="1"
      stroke={color}
      strokeWidth="2.8"
      mask="url(#path-1-inside-1)"
    />

    <rect
      x="3.25"
      y="6.25"
      width="6.5"
      height="0.5"
      rx="0.25"
      fill={color}
      stroke={color}
      strokeWidth="0.5"
    />

    <rect
      x="6.75"
      y="3.25"
      width="6.5"
      height="0.5"
      rx="0.25"
      transform="rotate(90 6.75 3.25)"
      fill={color}
      stroke={color}
      strokeWidth="0.5"
    />

    <mask id="path-4-inside-2" fill="white">
      <path d="M15 3.5C15 3.22386 15.2239 3 15.5 3V3C15.7761 3 16 3.22386 16 3.5V15C16 15.5523 15.5523 16 15 16V16V3.5Z" />
    </mask>

    <path
      d="M15 3.5C15 3.22386 15.2239 3 15.5 3V3C15.7761 3 16 3.22386 16 3.5V15C16 15.5523 15.5523 16 15 16V16V3.5Z"
      fill={color}
    />

    <path
      d="M15 3.5V15H17V3.5H15ZM16 16V3.5H14V16H16ZM15 15V15V17C16.1046 17 17 16.1046 17 15H15ZM15.5 4C15.2239 4 15 3.77614 15 3.5H17C17 2.67157 16.3284 2 15.5 2V4ZM15.5 2C14.6716 2 14 2.67157 14 3.5H16C16 3.77614 15.7761 4 15.5 4V2Z"
      fill={color}
      mask="url(#path-4-inside-2)"
    />

    <mask id="path-6-inside-3" fill="white">
      <path d="M15 16L15 15L2.5 15C2.22386 15 2 15.2239 2 15.5V15.5C2 15.7761 2.22386 16 2.5 16L15 16Z" />
    </mask>

    <path
      d="M15 16L15 15L2.5 15C2.22386 15 2 15.2239 2 15.5V15.5C2 15.7761 2.22386 16 2.5 16L15 16Z"
      fill={color}
    />

    <path
      d="M15 16L16 16L16 17L15 17L15 16ZM15 15L15 14L16 14L16 15L15 15ZM14 16L14 15L16 15L16 16L14 16ZM15 16L2.5 16L2.5 14L15 14L15 16ZM2.5 15L15 15L15 17L2.5 17L2.5 15ZM3 15.5C3 15.2239 2.77614 15 2.5 15L2.5 17C1.67157 17 1 16.3284 1 15.5L3 15.5ZM2.5 16C2.77614 16 3 15.7761 3 15.5L1 15.5C1 14.6716 1.67157 14 2.5 14L2.5 16Z"
      fill="#3F3F40"
      mask="url(#path-6-inside-3)"
    />
  </svg>
)

export default ContentLibraryIcon

```


[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/DomainMessages/index.tsx from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```tsx
// This file is .tsx because otherwise the babel plugin for messages doesn't work.
import { concat } from 'ramda'
import languagesQuery from '../../queries/Languages.graphql'
import messages from './cultureMessages'
import {
  AvailableCulturesProps,
  LabelledLocale,
  Languages,
  Variables,
} from './typings'

// TODO: Remove this when messages solve this case
const DEFAULT_LANGUAGES = ['en-US', 'pt-BR', 'es-AR']

function isValidLang(key: string): key is keyof typeof messages {
  return Object.prototype.hasOwnProperty.call(messages, key)
}

export const getAvailableCultures = async ({
  client,
  intl,
}: AvailableCulturesProps) => {
  const {
    data: { languages },
  } = await client.query<Languages, Variables>({
    query: languagesQuery,
  })

  let { supported } = languages
  supported = Array.from(new Set(concat(supported, DEFAULT_LANGUAGES)))

  return supported
    .reduce(
      (acc, locale) => {
        const [lang, country] = locale.split('-')
        if (!lang || !country) {
          return acc
        }
        const i18nId = isValidLang(lang)
          ? messages[lang] && messages[lang].id
          : `admin/pages.editor.locale.${lang}`

        return [
          ...acc,
          {
            label: `${intl.formatMessage({ id: i18nId })} (${locale})`,
            value: locale,
          },
        ]
      },
      [] as LabelledLocale[]
    )
    .sort((a: LabelledLocale, b: LabelledLocale) =>
      a.label.localeCompare(b.label)
    )
}

export { LabelledLocale }
export { editorMessagesFromRuntime } from './editorMessagesFromRuntime'

```


[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/DomainMessages/editorMessagesFromRuntime.ts from the admin-pages VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { keys, map, splitEvery } from 'ramda'

import { Data, Message, Props, Variables } from './typings'

import messagesForDomainQuery from '../../queries/MessagesForDomain.graphql'

const MAX_COMPONENTS_PER_QUERY = 15

const messagesToReactIntlFormat = (messages: Message[]) =>
  messages.reduce((acc, { key, message }) => ({ ...acc, [key]: message }), {})

const reduceP = async <T, K>(
  fn: (acc: K, item: T) => Promise<K>,
  initialValue: K,
  list: T[]
) => {
  let acc = initialValue
  for (const item of list) {
    acc = await fn(acc, item)
  }
  return acc
}

let cachedResult: Record<string, string> = {}
const fetchedKeys = new Set()

export const editorMessagesFromRuntime = async (
  { client, domain, runtime }: Props,
  { isRetry, componentsPerQuery } = {
    isRetry: false,
    componentsPerQuery: MAX_COMPONENTS_PER_QUERY,
  }
) => {
  const { components, renderMajor } = runtime
  const allComponentNames = keys(components)
  let shouldRetry = false

  const componentNamesToFetch = allComponentNames.filter(componentName => {
    const shouldFetchComponent = !fetchedKeys.has(componentName)

    return shouldFetchComponent
  })

  if (componentNamesToFetch.length === 0) {
    return cachedResult
  }

  const componentsBatch = splitEvery(componentsPerQuery, componentNamesToFetch)

  const responses = map(
    batch =>
      client
        .query<Data, Variables>({
          fetchPolicy: 'network-only',
          query: messagesForDomainQuery,
          variables: {
            components: batch,
            domain,
            renderMajor,
          },
        })
        .then(res => {
          batch.forEach(componentName => {
            fetchedKeys.add(componentName)
          })

          return res
        }),
    componentsBatch
  )
  const messages = await reduceP(
    async (acc, response) => {
      try {
        const {
          data: { messages: batchMessages },
        } = await response
        return [...acc, ...batchMessages]
      } catch (e) {
        shouldRetry = true
        return acc
      }
    },
    [] as Message[],
    responses
  )

  const messagesInReactIntlFormat = messagesToReactIntlFormat(messages)
  cachedResult = { ...cachedResult, ...messagesInReactIntlFormat }

  let retriedMessages
  if (shouldRetry && !isRetry) {
    retriedMessages = await new Promise<Record<string, string>>(res =>
      setTimeout(async () => {
        res(
          await editorMessagesFromRuntime(
            { client, domain, runtime },
            {
              isRetry: true,
              componentsPerQuery: Math.round(MAX_COMPONENTS_PER_QUERY / 2),
            }
          )
        )
      }, 700)
    )
  }

  return { ...messagesInReactIntlFormat, ...retriedMessages }
}

```