[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 wishlist/react/components/Form/UpdateList.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { map } from 'ramda'
import React, { Component } from 'react'
import { compose, withApollo, WithApolloClient } from 'react-apollo'
import { InjectedIntlProps, injectIntl, defineMessages } from 'react-intl'
import { withToast } from 'vtex.styleguide'
import { updateList } from '../../GraphqlClient'
import Header from '../Header'
import FormView from './FormView'
import ListForm from './ListForm'

import styles from '../../wishList.css'

interface UpdateListProps extends InjectedIntlProps, WithApolloClient<{}> {
  list: List
  onFinishUpdate: (list: List) => void
  onClose: () => void
  showToast?: (input: ToastInput) => void
}

interface UpdateListState {
  isLoading?: boolean
}

const messages = defineMessages({
  optionConfiguration: {
    defaultMessage: '',
    id: 'store/wishlist-option-configuration',
  },
  save: {
    defaultMessage: '',
    id: 'store/wishlist-save',
  },
  listUpdate: {
    defaultMessage: '',
    id: 'store/wishlist-list-updated',
  },
})

class UpdateList extends Component<UpdateListProps, UpdateListState> {
  public state: UpdateListState = {}
  private isComponentMounted: boolean = false

  public componentDidMount() {
    this.isComponentMounted = true
  }

  public componentWillUnmount() {
    this.isComponentMounted = false
  }

  public render() {
    const { onClose, intl, list } = this.props
    const { isLoading } = this.state
    return (
      <FormView onClose={onClose}>
        <div className={`${styles.updateList}`}>
          <Header
            title={intl.formatMessage(messages.optionConfiguration)}
            onClose={onClose}
            showIconBack
          />
          <ListForm
            list={list}
            buttonLabel={intl.formatMessage(messages.save)}
            onSubmit={this.handleSubmit}
            isLoading={isLoading}
          />
        </div>
      </FormView>
    )
  }

  private itemsToItemsInput = (items: ListItem[] | undefined): ListItem[] =>
    items
      ? map(
          ({ id, productId, skuId, quantity }) => ({
            id,
            productId,
            skuId,
            quantity,
          }),
          items
        )
      : []

  private handleSubmit = ({ name, isPublic }: List): void => {
    const {
      client,
      list: { id, items },
      showToast,
      intl,
    } = this.props
    this.setState({ isLoading: true })
    updateList(client, id || '', {
      isPublic,
      items: this.itemsToItemsInput(items),
      name,
    })
      .then((response: ResponseList) => {
        if (this.isComponentMounted) {
          this.setState({ isLoading: false })
        }
        if (showToast) {
          showToast({
            message: intl.formatMessage(messages.listUpdate),
          })
        }
        this.props.onFinishUpdate({ ...response.data.updateList, items })
      })
      .catch((err: {}) => {
        console.error(err)
      })
  }
}

export default compose(
  withToast,
  withApollo,
  injectIntl
)(UpdateList)

```


[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 wishlist/react/components/Form/FormView.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { isMobile } from 'react-device-detect'
import Dialog from '../Dialog/index'
import Screen from '../Screen'

import styles from '../../wishList.css'

interface FormViewProps {
  onClose: () => void
  children: JSX.Element
}

const FormView = ({ children, onClose }: FormViewProps): JSX.Element =>
  isMobile ? (
    <Screen>{children}</Screen>
  ) : (
    <Dialog onClose={onClose}>
      <div className={styles.formViewDialog}>{children}</div>
    </Dialog>
  )

export default FormView

```


[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 wishlist/react/components/Form/ListForm.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, FormEvent, Fragment, ReactNode } from 'react'
import {
  FormattedMessage,
  InjectedIntlProps,
  injectIntl,
  defineMessages,
} from 'react-intl'
import { Button, Input, Toggle } from 'vtex.styleguide'

import styles from '../../wishList.css'

const LIST_NAME_MINIMUM_LENGTH = 1
const messages = defineMessages({
  listNamePlaceholder: {
    defaultMessage: '',
    id: 'store/wishlist-list-name-placeholder',
  },
  listNameLabel: {
    defaultMessage: '',
    id: 'store/wishlist-list-name-label',
  },
  isPublic: {
    defaultMessage: '',
    id: 'store/wishlist-is-public',
  },
  isPublicHint: {
    defaultMessage: '',
    id: 'store/wishlist-is-public-hint',
  },
})

interface ListFormProps extends InjectedIntlProps {
  buttonLabel: string
  onSubmit: (listData: List) => void
  list?: List
  isLoading?: boolean
}

interface ListFormState {
  listData: List
  isLoading?: boolean
  isValid?: boolean
  isChanged?: boolean
}

class ListForm extends Component<ListFormProps, ListFormState> {
  public state: ListFormState = {
    listData: {},
  }

  public componentDidMount(): void {
    const { list } = this.props
    if (list) {
      this.setState({ listData: list })
    }
  }

  public render(): ReactNode {
    const { intl, onSubmit, isLoading, buttonLabel } = this.props
    const {
      isValid,
      isChanged,
      listData: { name, isPublic },
      listData,
    } = this.state
    return (
      <Fragment>
        <div className={`${styles.form} w-100 gray f5 pv5 ph5`}>
          <div className={`${styles.nameInputContainer} tl`}>
            <Input
              autoFocus
              value={name || ''}
              placeholder={intl.formatMessage(messages.listNamePlaceholder)}
              label={intl.formatMessage(messages.listNameLabel)}
              onChange={this.handleChangeName}
            />
          </div>
          <div
            className={`${
              styles.isPublicContainer
            } flex flex-row justify-between tl mt5`}
          >
            <div className="flex flex-column">
              <span className={`${styles.isPublicLabel} c-on-base mt1 t-small`}>
                <FormattedMessage {...messages.isPublic} />
              </span>
              <span className={`${styles.isPublicHint} light-gray mt3`}>
                <FormattedMessage {...messages.isPublicHint} />
              </span>
            </div>
            <Toggle
              size="regular"
              checked={!isPublic}
              onChange={this.handleChangePublic}
            />
          </div>
        </div>
        <div
          className={`${
            styles.createListButtonContainer
          } flex flex-row justify-center pb5`}
        >
          <Button
            variation="primary"
            size="small"
            disabled={!isValid || !isChanged}
            isLoading={isLoading}
            onClick={() => onSubmit(listData)}
          >
            {buttonLabel}
          </Button>
        </div>
      </Fragment>
    )
  }

  private handleChangeName = (event: FormEvent<HTMLInputElement>): void => {
    const { listData } = this.state
    const { list } = this.props
    const target = event.target as HTMLInputElement
    const name = target.value
    this.setState({
      isChanged: !list || list.name !== name,
      isValid: this.isNameValid(name),
      listData: { isPublic: listData.isPublic, name },
    })
  }

  private handleChangePublic = (): void => {
    const { isPublic, name } = this.state.listData
    const { list } = this.props
    this.setState({
      isChanged: !list || list.isPublic !== !isPublic,
      isValid: this.isNameValid(name),
      listData: { ...this.state.listData, isPublic: !isPublic },
    })
  }

  private isNameValid = (name?: string): boolean => {
    return name !== undefined && name.length >= LIST_NAME_MINIMUM_LENGTH
  }
}

export default injectIntl(ListForm)

```


[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 wishlist/react/components/Form/CreateList.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

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

import { isMobile } from 'react-device-detect'
import { compose, withApollo, WithApolloClient, graphql } from 'react-apollo'
import { InjectedIntlProps, injectIntl, defineMessages } from 'react-intl'
import { withRuntimeContext, withSession } from 'vtex.render-runtime'
import { session } from 'vtex.store-resources/Queries'
import { getProfile } from '../../utils/profile'

import { createList } from '../../GraphqlClient'
import Header from '../Header'
import FormView from './FormView'
import ListForm from './ListForm'

import styles from '../../wishList.css'

interface CreateListProps extends InjectedIntlProps, WithApolloClient<{}> {
  onFinishAdding: (list: List) => void
  onClose: () => void
  runtime: Runtime
  session: Session
}

interface CreateListState {
  isLoading?: boolean
}

const messages = defineMessages({
  new: {
    defaultMessage: '',
    id: 'store/wishlist-new',
  },
  addButton: {
    defaultMessage: '',
    id: 'store/wishlist-add-button',
  },
})

class CreateList extends Component<CreateListProps, CreateListState> {
  public state: CreateListState = {}
  private isComponentMounted: boolean = false

  public componentDidMount() {
    this.isComponentMounted = true
  }

  public componentWillUnmount() {
    this.isComponentMounted = false
  }

  public render() {
    const { onClose, intl } = this.props
    const { isLoading } = this.state
    return (
      <FormView onClose={onClose}>
        <div className={`${styles.createList} bg-base h-100`}>
          <Header
            title={intl.formatMessage(messages.new)}
            onClose={onClose}
            showIconBack
          />
          <ListForm
            buttonLabel={intl.formatMessage(messages.addButton)}
            onSubmit={this.handleSubmit}
            isLoading={isLoading}
          />
        </div>
      </FormView>
    )
  }

  private handleSubmit = (listData: List): void => {
    const { client, session } = this.props
    const profile = getProfile(session)
    this.setState({ isLoading: true })
    createList(client, {
      ...listData,
      items: [],
      isEditable: true,
      owner: profile ? profile.email : '',
    })
      .then((response: ResponseList) => {
        const list = response.data.createList
        if (list) {
          !isMobile && this.redirectToList(list.id)
          this.props.onFinishAdding(list)
        }
        if (this.isComponentMounted) {
          this.setState({ isLoading: false })
        }
      })
      .catch(error => {
        console.error(error)
      })
  }

  private redirectToList = (id: string | undefined): void => {
    const {
      runtime: { setQuery },
    } = this.props
    setQuery({ listId: id }, { merge: false, replace: true })
  }
}

const options = {
  name: 'session',
  options: () => ({ ssr: false }),
}

export default withSession()(
  compose(
    injectIntl,
    withApollo,
    withRuntimeContext,
    graphql(session, options)
  )(CreateList)
)

```


[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 wishlist/react/components/AddToList/index.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { isMobile } from 'react-device-detect'
import BottomBar from '../BottomBar'
import Popover from '../Popover'
import ListMenuContent from './Content'

interface AddToListProps {
  product: ListItem
  onClose: () => void
  onAddToListsSuccess: () => void
  onAddToListsFail: () => void
}

const AddToList = (props: AddToListProps): JSX.Element => {
  const { onClose } = props
  const content = <ListMenuContent {...props} />
  return isMobile ? (
    <BottomBar onOutsideClick={onClose}>{content}</BottomBar>
  ) : (
    <Popover onOutsideClick={onClose} left>
      {content}
    </Popover>
  )
}

export default AddToList

```


[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 wishlist/react/components/AddToList/Content.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, ReactNode } from 'react'

import classNames from 'classnames'
import { append, filter, indexOf, map, path, remove, update } from 'ramda'
import { compose, withApollo, WithApolloClient } from 'react-apollo'
import { isMobile } from 'react-device-detect'
import { InjectedIntlProps, injectIntl, defineMessages } from 'react-intl'
import { getListsFromLocaleStorage, updateList } from '../../GraphqlClient'

import CreateList from '../Form/CreateList'
import Header from '../Header'
import ListDetails from '../ListDetails'
import ListItem from '../ListItem'
import renderLoading from '../Loading'
import Footer from './Footer'

import styles from '../../wishList.css'

const DEFAULT_LIST_INDEX = 0
const QUANTITY_WITH_ONLY_DEFAULT_LIST = 1
const messages = defineMessages({
  addToList: {
    defaultMessage: '',
    id: 'store/wishlist-add-to-list',
  },
})

interface AddToListContentProps
  extends InjectedIntlProps,
    WithApolloClient<{}> {
  product: ListItem
  onClose: () => void
  onAddToListsSuccess: () => void
  onAddToListsFail: () => void
}

interface AddToListContentState {
  isLoading: boolean
  isAdding?: boolean
  showCreateList?: boolean
  lists: List[]
  changedLists: number[]
  showListDetails?: boolean
  selectedListId: string
}

class AddToListContent extends Component<
  AddToListContentProps,
  AddToListContentState
> {
  public state: AddToListContentState = {
    changedLists: [],
    isLoading: true,
    lists: [],
    selectedListId: '',
  }

  public componentDidMount(): void {
    const { client } = this.props
    getListsFromLocaleStorage(client)
      .then((response: ResponseList[]) => {
        const lists = map(item => item.data.list || {}, response)
        this.setState({ isLoading: false, lists })
      })
      .catch(() => this.setState({ isLoading: false }))
  }

  public render(): ReactNode {
    const { onClose, intl } = this.props
    const {
      showCreateList,
      showListDetails,
      selectedListId,
      changedLists,
      isAdding,
      lists,
    } = this.state

    const className = classNames(styles.contentContainer, 'overflow-y-auto', {
      [styles.contentContainerMobile]: isMobile,
      [styles.contentContainerDesktop]: !isMobile,
    })
    return (
      <div className={`${styles.addToListContent} z-4 bg-base`}>
        <Header
          title={intl.formatMessage(messages.addToList)}
          onClose={onClose}
          action={() => this.setState({ showCreateList: true })}
        />
        <div className={className}>{this.renderMainContent()}</div>
        {lists && lists.length > QUANTITY_WITH_ONLY_DEFAULT_LIST && (
          <Footer
            changedLists={changedLists}
            isLoading={isAdding}
            onClick={this.handleAddProductTochangedLists}
          />
        )}
        {showCreateList && (
          <CreateList
            onFinishAdding={this.handleListCreated}
            onClose={() => this.setState({ showCreateList: false })}
          />
        )}
        {showListDetails && (
          <ListDetails
            listId={selectedListId}
            onClose={this.handleOnCloseListDetails}
            onDeleted={this.handleOnDeleted}
          />
        )}
      </div>
    )
  }

  private handleListCreated = (list: List): void => {
    const { lists } = this.state
    this.setState({ showCreateList: false, lists: append(list, lists) })
  }

  private handleAddProductTochangedLists = (): void => {
    const {
      client,
      onClose,
      onAddToListsSuccess,
      onAddToListsFail,
    } = this.props
    const { lists, changedLists } = this.state
    this.setState({ isAdding: true })
    Promise.all(
      map(index => {
        const { id } = lists[index]
        return updateList(client, id || '', { ...lists[index] })
      }, changedLists)
    )
      .then(() => {
        onClose()
        onAddToListsSuccess()
      })
      .catch(error => {
        console.error(error)
        onClose()
        onAddToListsFail()
      })
  }

  private containsProduct = (list: List): boolean => {
    const { product } = this.props
    return (
      filter(
        (item: ListItem) =>
          item.productId === product.productId && item.skuId === product.skuId,
        list.items || []
      ).length > 0
    )
  }

  private handleUpdateChangedLists = (
    listIndex: number,
    isSelected?: boolean
  ): void => {
    const { changedLists } = this.state
    if (listIndex !== DEFAULT_LIST_INDEX) {
      const index = indexOf(listIndex, changedLists)
      const listsUpdated = !isSelected
        ? this.addProductToList(listIndex)
        : this.removeProductFromList(listIndex)
      let changedListsUpdated: number[] = []
      if (index !== -1) {
        changedListsUpdated = remove(index, 1, changedLists)
      } else {
        changedListsUpdated = append(listIndex, changedLists)
      }
      this.setState({ changedLists: changedListsUpdated, lists: listsUpdated })
    }
  }

  private handleShowListDetails = (index: number): void => {
    const { lists } = this.state
    const listId: string = path(['id'], lists[index]) || ''
    this.setState({
      selectedListId: listId,
      showListDetails: true,
    })
  }

  private handleOnDeleted = (listId: string): void => {
    const { lists } = this.state
    const listsWithDeletedList = filter(
      list => path(['id'], list) !== listId,
      lists
    )
    this.setState({ lists: listsWithDeletedList })
  }

  private addProductToList = (index: number): List[] => {
    const { product } = this.props
    const { lists } = this.state
    const list = lists[index]
    const listUpdated = { ...list, items: append(product, list.items || []) }
    return update(index, listUpdated, lists)
  }

  private removeProductFromList = (index: number): List[] => {
    const {
      product: { productId, skuId },
    } = this.props
    const { lists } = this.state
    const list = lists[index]
    const items = filter(
      (item: ListItem) => item.productId !== productId || item.skuId !== skuId,
      list.items || []
    )
    return update(index, { ...list, items }, lists)
  }

  private handleOnCloseListDetails = (listUpdated?: List): void => {
    const { lists } = this.state
    const listsUpdated = listUpdated
      ? map(list => {
          if (list.id === listUpdated.id) {
            return listUpdated
          }
          return list
        }, lists)
      : lists
    this.setState({ showListDetails: false, lists: listsUpdated })
  }

  private renderSwitchLists = (): ReactNode => {
    const { lists } = this.state
    return (
      <div className={`${styles.addToListListsToSwitch} flex flex-column`}>
        {lists.map((list: List, index: number) => (
          <ListItem
            key={index}
            id={index}
            list={list}
            isDefault={index === DEFAULT_LIST_INDEX}
            isSelected={
              index === DEFAULT_LIST_INDEX || this.containsProduct(list)
            }
            onClick={this.handleShowListDetails}
            onSelected={this.handleUpdateChangedLists}
          />
        ))}
      </div>
    )
  }

  private renderMainContent = (): ReactNode => {
    const { isLoading } = this.state
    return isLoading ? renderLoading() : this.renderSwitchLists()
  }
}

export default compose(
  withApollo,
  injectIntl
)(AddToListContent)

```


[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 wishlist/react/components/AddToList/Footer.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

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

import styles from '../../wishList.css'

interface FooterProps {
  isLoading?: boolean
  changedLists: number[]
  onClick: () => void
}

const messages = defineMessages({
  apply: {
    defaultMessage: '',
    id: 'store/wishlist-apply',
  },
})

const Footer = ({
  isLoading,
  changedLists,
  onClick,
}: FooterProps): JSX.Element => (
  <div className={styles.confirmSelectedListsBtn}>
    <Button
      vatiation="primary"
      disabled={!changedLists.length}
      block
      onClick={onClick}
      isLoading={isLoading}
    >
      <FormattedMessage {...messages.apply} />
    </Button>
  </div>
)

export default Footer

```


[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 wishlist/react/components/Lists/index.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, Fragment, ReactNode } from 'react'

import { append, filter, update, isEmpty } from 'ramda'
import { compose, withApollo, WithApolloClient, graphql } from 'react-apollo'
import { createPortal } from 'react-dom'
import {
  FormattedMessage,
  InjectedIntlProps,
  injectIntl,
  defineMessages,
} from 'react-intl'
import { session } from 'vtex.store-resources/Queries'

import { withRuntimeContext, withSession } from 'vtex.render-runtime'
import { deleteList } from '../../GraphqlClient'

import CreateList from '../Form/CreateList'
import UpdateList from '../Form/UpdateList'
import Header from '../Header'
import ListDetails from '../ListDetails/index'
import ListItem from '../ListItem'
import renderLoading from '../Loading'
import Screen from '../Screen'

import styles from '../../wishList.css'

const DEFAULT_LIST_INDEX = 0
const OPEN_LISTS_CLASS = styles.open
const messages = defineMessages({
  myLists: {
    defaultMessage: '',
    id: 'store/wishlist-my-lists',
  },
  noListCreated: {
    defaultMessage: '',
    id: 'store/wishlist-no-list-created',
  },
})

interface ListsState {
  listSelected: number
  showCreateList?: boolean
  showUpdateList?: boolean
  showListDetails?: boolean
  lists?: List[]
}

interface ListsProps extends InjectedIntlProps, WithApolloClient<{}> {
  onClose: () => void
  session: Session
  lists: List[]
  loading?: boolean
  enableMultipleLists: boolean
}

class Lists extends Component<ListsProps, ListsState> {
  public state: ListsState = {
    listSelected: 0,
  }

  private isComponentMounted: boolean = false

  public componentWillUnmount() {
    this.isComponentMounted = false
    document.body.classList.remove(OPEN_LISTS_CLASS)
  }

  public componentDidMount(): void {
    this.isComponentMounted = true
    this.setState({ lists: this.props.lists })
  }

  public render = (): ReactNode => {
    const {
      showCreateList,
      showUpdateList,
      showListDetails,
      listSelected,
      lists,
    } = this.state
    const { onClose, intl, enableMultipleLists } = this.props

    return createPortal(
      <Screen>
        {enableMultipleLists && (
          <Fragment>
            <Header
              title={intl.formatMessage(messages.myLists)}
              onClose={onClose}
              action={() => this.setState({ showCreateList: true })}
            />
            {this.renderContent()}
            {showCreateList && (
              <div className="fixed vw-100 top-0 bg-base">
                <CreateList
                  onClose={() => this.setState({ showCreateList: false })}
                  onFinishAdding={this.handleListCreated}
                />
              </div>
            )}
            {showUpdateList && (
              <Screen>
                <UpdateList
                  onClose={() => this.setState({ showUpdateList: false })}
                  list={lists && lists[listSelected]}
                  onFinishUpdate={this.handleListUpdated}
                />
              </Screen>
            )}
          </Fragment>
        )}
        {(showListDetails || !enableMultipleLists) && (
          <div className="fixed vw-100 top-0 left-0 bg-base">
            <ListDetails
              onClose={
                enableMultipleLists &&
                (() => this.setState({ showListDetails: false }))
              }
              listId={lists && lists[listSelected].id}
              onDeleted={this.handleDeleteList}
            />
          </div>
        )}
      </Screen>,
      document.body
    )
  }

  private renderLists = (): ReactNode => {
    const { lists } = this.state

    return (
      <Fragment>
        {lists && !isEmpty(lists) ? (
          <div className="bb b--muted-4 h-100 overflow-auto">
            {lists.map((list, key) => (
              <ListItem
                key={key}
                list={list}
                id={key}
                isDefault={key === DEFAULT_LIST_INDEX}
                onClick={() =>
                  this.setState({ showListDetails: true, listSelected: key })
                }
                showMenuOptions
                onDeleted={this.handleDeleteList}
                onUpdated={this.handleUpdateList}
              />
            ))}
          </div>
        ) : (
          <div className="tc pv4 c-muted-2">
            <FormattedMessage {...messages.noListCreated} />
          </div>
        )}
      </Fragment>
    )
  }

  private handleDeleteList = (listId: string): Promise<void> => {
    const { client } = this.props
    const { lists } = this.state
    return deleteList(client, listId)
      .then(() => {
        if (this.isComponentMounted) {
          this.setState({
            lists: filter(list => list.id !== listId, lists || []),
            showListDetails: false,
          })
        }
      })
      .catch(error => console.error(error))
  }

  private handleUpdateList = (index: number): void => {
    this.setState({ listSelected: index, showUpdateList: true })
  }

  private handleListCreated = (list: List): void => {
    const { lists } = this.state
    this.setState({ showCreateList: false, lists: append(list, lists || []) })
  }

  private handleListUpdated = (list: List): void => {
    const { lists, listSelected } = this.state
    this.setState({
      lists: update(listSelected, list, lists || []),
      showUpdateList: false,
    })
  }

  private renderContent = (): ReactNode => {
    const { loading } = this.props
    return loading ? renderLoading() : this.renderLists()
  }
}

const options = {
  name: 'session',
  options: () => ({ ssr: false }),
}

export default withSession()(
  compose(
    injectIntl,
    withRuntimeContext,
    withApollo,
    graphql(session, options)
  )(Lists)
)

```


[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 wishlist/react/components/Dialog/index.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { createPortal } from 'react-dom'
import OutsideClickHandler from 'react-outside-click-handler'

interface DialogProps {
  onClose: () => void
  children: JSX.Element
}

const Dialog = ({ onClose, children }: DialogProps): JSX.Element =>
  createPortal(
    <div className="vh-100 vw-100 fixed top-0 left-0 z-max">
      <div className="h-100 w-100 bg-base--inverted o-40" />
      <div className="vh-100 vw-100 fixed top-0 left-0 flex items-center justify-center z-4">
        <OutsideClickHandler onOutsideClick={() => onClose()}>
          <div className="bg-base flex flex-column shadow-3 mh7">
            {children}
          </div>
        </OutsideClickHandler>
      </div>
    </div>,
    document.body
  )

export default Dialog

```


[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 wishlist/react/components/Dialog/DialogMessage.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, ReactNode } from 'react'
import {
  FormattedMessage,
  InjectedIntlProps,
  injectIntl,
  defineMessages,
} from 'react-intl'
import { Button } from 'vtex.styleguide'
import Dialog from './index'

interface DialogMessageProps extends InjectedIntlProps {
  message: string
  onClose: () => void
  onSuccess: () => void
}

interface DialogMessageState {
  isLoading?: boolean
}

const messages = defineMessages({
  cancelDialog: {
    defaultMessage: '',
    id: 'store/wishlist-dialog-cancel',
  },
  confirmDialog: {
    defaultMessage: '',
    id: 'store/wishlist-dialog-confirm',
  },
})

class DialogMessage extends Component<DialogMessageProps, DialogMessageState> {
  public state: DialogMessageState = {
    isLoading: false,
  }

  public render(): ReactNode {
    const { message, onClose, onSuccess } = this.props
    const { isLoading } = this.state
    return (
      <Dialog onClose={() => !isLoading && onClose()}>
        <div className="mv6 mh7">
          <span className="tc c-muted-1 t-small">{message}</span>
          <div className="flex flex-row mt4 justify-center">
            <Button
              variation="secondary"
              size="small"
              onClick={onClose}
              disabled={isLoading}
            >
              <FormattedMessage {...messages.cancelDialog} />
            </Button>
            <div className="ml3">
              <Button
                variation="primary"
                size="small"
                onClick={() => {
                  this.setState({ isLoading: true })
                  onSuccess()
                }}
                isLoading={isLoading}
              >
                <FormattedMessage {...messages.confirmDialog} />
              </Button>
            </div>
          </div>
        </div>
      </Dialog>
    )
  }
}

export default injectIntl(DialogMessage)

```


[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 wishlist/react/components/ListDetails/index.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, Fragment, ReactNode } from 'react'
import { ActionMenu, IconOptionsDots } from 'vtex.styleguide'
import {
  compose,
  graphql,
  WithApolloClient,
  withApollo,
  ChildDataProps,
} from 'react-apollo'
import { append, filter, map } from 'ramda'
import { InjectedIntlProps, injectIntl, defineMessages } from 'react-intl'
import { withRuntimeContext } from 'vtex.render-runtime'

import { deleteList, updateList } from '../../GraphqlClient'
import DialogMessage from '../Dialog/DialogMessage'
import UpdateList from '../Form/UpdateList'
import Header from '../Header'
import renderLoading from '../Loading'
import Content from './Content'
import Footer from './Footer'

import LIST_DETAILS_QUERY from '../../graphql/queries/getListDetails.gql'
import Loading from '../Loading'
import withSettings from '../../withSettings'

interface ListDetailState {
  isAddingToCart?: boolean
  selectedItems: ListItemWithProduct[]
  showDeleteConfirmation?: boolean
  showUpdateList?: boolean
}

interface ListDetailProps
  extends InjectedIntlProps,
    WithApolloClient<{}>,
    ChildDataProps<{}, { list: List }, {}>,
    SettingsProps {
  listId: string
  onClose: (lists?: List[]) => void
  onDeleted?: (id: string) => void
  runtime: Runtime
}

const messages = defineMessages({
  optionConfiguration: {
    defaultMessage: '',
    id: 'store/wishlist-option-configuration',
  },
  optionDelete: {
    defaultMessage: '',
    id: 'store/wishlist-option-delete',
  },
  messageDeleteConfirmation: {
    defaultMessage: '',
    id: 'store/wishlist-delete-confirmation-message',
  },
  defaultListName: {
    id: 'store/wishlist-default-list-name',
    defaultMessage: '',
  },
})

class ListDetail extends Component<ListDetailProps, ListDetailState> {
  public state: ListDetailState = {
    selectedItems: [],
  }

  public render(): ReactNode {
    const { showDeleteConfirmation, showUpdateList } = this.state
    const {
      intl,
      listId,
      data: { loading, list },
    } = this.props

    return list ? (
      <div className="fixed top-0 left-0 vw-100 vh-100 flex flex-column z-4 bg-base">
        {loading ? renderLoading() : this.renderContent()}
        {showDeleteConfirmation && (
          <DialogMessage
            message={intl.formatMessage(messages.messageDeleteConfirmation, {
              listName: list.name,
            })}
            onClose={() => this.setState({ showDeleteConfirmation: false })}
            onSuccess={this.handleDeleteList}
          />
        )}
        {showUpdateList && (
          <div className="fixed top-0 left-0 w-100 bg-base">
            <UpdateList
              onClose={() => this.setState({ showUpdateList: false })}
              list={{ ...list, id: listId }}
              onFinishUpdate={this.handleFinishUpdate}
            />
          </div>
        )}
      </div>
    ) : null
  }

  private renderContent = (): ReactNode => {
    const { selectedItems } = this.state
    const {
      data: { loading, list },
      onClose,
      intl: { formatMessage },
      settings: { appSettings },
    } = this.props
    const options = [
      {
        onClick: () => this.setState({ showUpdateList: true }),
        label: this.props.intl.formatMessage(messages.optionConfiguration),
      },
      {
        onClick: () => this.setState({ showDeleteConfirmation: true }),
        label: this.props.intl.formatMessage(messages.optionDelete),
      },
    ]

    if (loading || !list) {
      return <Loading />
    }
    const { isEditable, name } = list

    const title = !isEditable
      ? (appSettings && appSettings.defaultListName) ||
        formatMessage(messages.defaultListName)
      : name

    return (
      <Fragment>
        <Header
          title={title}
          onClose={this.handleOnClose}
          showIconBack={!!onClose}
        >
          {!loading && list.isEditable && (
            <ActionMenu
              options={options}
              hideCaretIcon
              buttonProps={{
                variation: 'tertiary',
                size: 'small',
                icon: <IconOptionsDots size={20} />,
              }}
            />
          )}
        </Header>
        <Content
          items={list.items}
          onItemSelect={this.handleItemSelectedChange}
          onItemRemove={this.handleItemRemove}
        />
        {selectedItems.length > 0 && <Footer items={selectedItems} />}
      </Fragment>
    )
  }

  private handleItemSelectedChange = (
    itemId: string,
    product: {},
    isSelected: boolean
  ) => {
    const { selectedItems } = this.state
    if (isSelected) {
      this.setState({
        selectedItems: append({ itemId, product }, selectedItems),
      })
    } else {
      this.setState({
        selectedItems: filter(({ itemId: id }) => id !== itemId, selectedItems),
      })
    }
  }

  private itemWithoutProduct = ({
    id,
    productId,
    skuId,
    quantity,
  }: ListItem): ListItem => ({ id, productId, skuId, quantity })

  private handleItemRemove = async (itemId: string): Promise<void> => {
    const {
      client,
      listId,
      data: { list, refetch },
    } = this.props
    const { selectedItems } = this.state
    if (list) {
      const listUpdated = {
        ...list,
        items: filter(({ id }: ListItem) => id !== itemId, list.items || []),
      }
      const itemsUpdated = map(
        item => this.itemWithoutProduct(item),
        listUpdated.items
      )

      await updateList(client, listId, { ...list, items: itemsUpdated })
      refetch({ id: listId })
      this.setState({
        selectedItems: filter(({ itemId: id }) => id !== itemId, selectedItems),
      })
    }
  }

  private handleFinishUpdate = (): void => {
    this.setState({ showUpdateList: false })
  }

  private handleOnClose = (): void => {
    const {
      onClose,
      data: { list },
      runtime: { goBack },
    } = this.props
    if (!onClose) {
      goBack()
    } else if (list) {
      const currentList: List = {
        ...list,
        items: map(item => this.itemWithoutProduct(item), list.items || []),
      }
      onClose([currentList])
    }
  }

  private handleDeleteList = (): void => {
    const { client, listId, onDeleted, onClose } = this.props
    deleteList(client, listId)
      .then(() => {
        if (onDeleted) {
          onDeleted(listId)
        }
        onClose()
      })
      .catch(error => console.error(error))
  }
}

const withQuery = graphql(LIST_DETAILS_QUERY, {
  options: (props: ListDetailProps) => ({
    fetchPolicy: 'network-only',
    variables: {
      id: props.listId,
    },
  }),
})

export default compose(
  withApollo,
  withQuery,
  withSettings,
  injectIntl,
  withRuntimeContext
)(ListDetail)

```


[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 wishlist/react/components/ListDetails/Content.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, Fragment, ReactNode } from 'react'

import { map } from 'ramda'
import { compose } from 'react-apollo'
import {
  FormattedMessage,
  InjectedIntlProps,
  injectIntl,
  defineMessages,
} from 'react-intl'
import { withRuntimeContext } from 'vtex.render-runtime'
import { Button } from 'vtex.styleguide'
import ItemDetails from './ItemDetails'

import styles from '../../wishList.css'

interface ContentProps extends InjectedIntlProps {
  items: ListItem[]
  lists?: List[]
  hideItemsQuantityLabel?: boolean
  onItemSelect: (itemId: string, product: ListItem, isSelected: boolean) => void
  onItemRemove: (id: string) => Promise<void>
  runtime: Runtime
}

const messages = defineMessages({
  itemsQuantity: {
    defaultMessage: '',
    id: 'store/wishlist-quantity-of-items',
  },
  emptyList: {
    defaultMessage: '',
    id: 'store/wishlist-list-empty',
  },
  addItems: {
    defaultMessage: '',
    id: 'store/wishlist-add-items',
  },
})

class Content extends Component<ContentProps, {}> {
  public render(): ReactNode {
    const { items, hideItemsQuantityLabel } = this.props
    return (
      <div
        className={`${
          styles.listDetailsContent
        } h-100 overflow-y-auto flex flex-column`}
      >
        {items.length > 0 ? (
          <div>
            {!hideItemsQuantityLabel && (
              <div className="h3 flex items-center justify-center c-muted-1">
                <FormattedMessage
                  {...messages.itemsQuantity}
                  values={{ itemsQuantity: items.length }}
                />
              </div>
            )}
            {this.renderItems()}
          </div>
        ) : (
          this.renderListEmpty()
        )}
      </div>
    )
  }

  private handleRedirectToGallery = (): void => {
    const {
      runtime: { navigate },
    } = this.props
    navigate({
      to: '/',
    })
  }

  private renderListEmpty = (): ReactNode => {
    return (
      <div
        className={`${
          styles.listEmptyContainer
        } flex flex-column w-100 h-100 items-center mv8 c-muted-2`}
      >
        <div className={styles.listEmptyLabel}>
          <FormattedMessage {...messages.emptyList} />
        </div>
        <div className={`${styles.goToAddProductsButtonContainer} mt8`}>
          <Button variation="primary" onClick={this.handleRedirectToGallery}>
            <FormattedMessage {...messages.addItems} />
          </Button>
        </div>
      </div>
    )
  }

  private renderItems = (): ReactNode => {
    const { items, lists } = this.props
    return (
      <Fragment>
        {map(
          item => (
            <ItemDetails
              {...this.props}
              item={item}
              lists={lists}
              key={item.id}
            />
          ),
          items
        )}
      </Fragment>
    )
  }
}

export default compose(
  withRuntimeContext,
  injectIntl
)(Content)

```


[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 wishlist/react/components/ListDetails/ItemDetails.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, ReactNode } from 'react'

import classNames from 'classnames'
import { append, head, map, path } from 'ramda'
import { compose, withApollo, WithApolloClient } from 'react-apollo'
import { isMobile } from 'react-device-detect'
import { InjectedIntlProps, injectIntl, defineMessages } from 'react-intl'
import { ExtensionPoint, withRuntimeContext } from 'vtex.render-runtime'
import {
  ButtonWithIcon,
  Checkbox,
  IconDelete,
  withToast,
  ActionMenu,
} from 'vtex.styleguide'
import renderLoading from '../Loading'

import { getListDetailed, updateList } from '../../GraphqlClient'

import styles from '../../wishList.css'

interface ItemDetailsProps extends WithApolloClient<{}>, InjectedIntlProps {
  lists?: List[]
  item: ListItem
  onItemSelect: (itemId: string, product: Product, isSelected: boolean) => void
  onItemRemove: (id: string) => Promise<{}>
  showToast: (toastInput: ToastInput) => void
  runtime: Runtime
}

interface ItemDetailsState {
  isSelected?: boolean
  isLoading?: boolean
  isCopying?: boolean
}

const messages = defineMessages({
  copyTo: {
    defaultMessage: '',
    id: 'store/wishlist-copy-to',
  },
  see: {
    defaultMessage: '',
    id: 'store/wishlist-see',
  },
  copied: {
    defaultMessage: '',
    id: 'store/wishlist-copied',
  },
})

class ItemDetails extends Component<ItemDetailsProps, ItemDetailsState> {
  public state: ItemDetailsState = {}
  private isComponentMounted: boolean = false

  public componentDidMount() {
    this.isComponentMounted = true
  }

  public componentWillUnmount() {
    this.isComponentMounted = false
  }

  public render(): ReactNode {
    const {
      item: { product },
      lists,
      intl,
    } = this.props
    const { isSelected, isLoading, isCopying } = this.state
    const deleteIcon = <IconDelete />
    const className = classNames(styles.summaryContainer, 'h4 b--muted-4', {
      [styles.summaryContainerLarge]: !isMobile,
    })

    return (
      <div className="relative">
        <div className="absolute ml4 mt4">
          <label className="db pa3">
            <Checkbox
              checked={isSelected}
              onChange={this.handleItemSelectedChange}
            />
          </label>
        </div>
        <div className="absolute top-0 right-0 flex flex-column items-end">
          {isLoading ? (
            <div className="mr4">{renderLoading()}</div>
          ) : (
            <div className="ph3">
              <ButtonWithIcon
                icon={deleteIcon}
                variation="tertiary"
                onClick={this.handleItemRemove}
              />
            </div>
          )}
          <div className="mt2 mr3">
            {lists &&
              (isCopying ? (
                <div className="mr4">{renderLoading()}</div>
              ) : (
                <ActionMenu
                  label={intl.formatMessage(messages.copyTo)}
                  options={this.getActionMenuOptions(lists)}
                  buttonProps={{
                    variation: 'tertiary',
                    size: 'small',
                    disabled: !(lists && lists.length > 0),
                  }}
                />
              ))}
          </div>
        </div>
        <div className={className}>
          <ExtensionPoint
            id="product-summary"
            showBorders
            product={this.normalizeProduct(product)}
            displayMode="inlinePrice"
            showListPrice={false}
            showBadge={false}
            showInstallments={false}
            showLabels={false}
            showQuantitySelector={false}
            priceAlignLeft
          />
        </div>
      </div>
    )
  }

  private getActionMenuOptions = (lists: List[]): ActionMenuItem[] =>
    lists.map(({ id, name }) => ({
      label: name,
      onClick: () => this.copyProductToList(String(id)),
    }))

  private normalizeProduct = (product: Product | undefined): {} | null => {
    if (!product) {
      return null
    }
    const normalizedProduct = { ...product }
    const sku = head(normalizedProduct.items || [])
    if (sku) {
      const [seller = { commertialOffer: { Price: 0, ListPrice: 0 } }] =
        path(['sellers'], sku) || []
      const [referenceId = { Value: '' }] = path(['referenceId'], sku) || []
      const [image = { imageUrl: '' }] = path(['images'], sku) || []
      const unmixedImage = {
        ...image,
        imageUrl: image.imageUrl.replace(/^https?:/, ''),
      }
      normalizedProduct.sku = {
        ...sku,
        seller,
        referenceId,
        image: unmixedImage,
      }
    }
    return normalizedProduct
  }

  private handleItemSelectedChange = (): void => {
    const {
      item: { id, product },
      onItemSelect,
    } = this.props
    const { isSelected } = this.state
    this.setState({ isSelected: !isSelected })
    onItemSelect(id || '', product || { items: [], sku: '' }, !isSelected)
  }

  private handleItemRemove = (): void => {
    const { onItemRemove, item } = this.props
    this.setState({ isLoading: true })
    onItemRemove(item.id || '')
      .then(
        () => this.isComponentMounted && this.setState({ isLoading: false })
      )
      .catch(
        () => this.isComponentMounted && this.setState({ isLoading: false })
      )
  }

  private copyProductToList = async (listId: string): Promise<void> => {
    const { client, item } = this.props
    this.setState({ isCopying: true })
    const {
      data: { list },
    } = await getListDetailed(client, listId)
    if (list) {
      list.items = append(
        this.itemWithoutProduct(item),
        map(
          (listItem: ListItem) => this.itemWithoutProduct(listItem),
          list.items
        )
      )
      await updateList(client, listId, list)
      this.showMessage(list.name || '', listId)
      this.setState({ isCopying: false })
    }
  }

  private itemWithoutProduct = ({
    id,
    productId,
    skuId,
    quantity,
  }: ListItem): ListItem => ({ id, productId, skuId, quantity })

  private showMessage = (listName: string, listId: string) => {
    const {
      showToast,
      intl,
      runtime: { setQuery },
    } = this.props
    showToast({
      action: {
        label: intl.formatMessage(messages.see),
        onClick: () => setQuery({ listId }, { merge: false, replace: true }),
      },
      message: intl.formatMessage(messages.copied, { listName }),
    })
  }
}

export default compose(
  withToast,
  withApollo,
  injectIntl,
  withRuntimeContext
)(ItemDetails)

```


[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 wishlist/react/components/ListDetails/Footer.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, ReactNode } from 'react'

import { map, path, head } from 'ramda'
import { compose } from 'react-apollo'
import { isMobile } from 'react-device-detect'
import {
  FormattedMessage,
  InjectedIntlProps,
  injectIntl,
  defineMessages,
} from 'react-intl'
import { withToast } from 'vtex.styleguide'

import BuyButton from 'vtex.store-components/BuyButton'
import ProductPrice from 'vtex.store-components/ProductPrice'

import styles from '../../wishList.css'

interface FooterProps extends InjectedIntlProps {
  items: ListItemWithProduct[]
}

const messages = defineMessages({
  selectedItemsQuantity: {
    defaultMessage: '',
    id: 'store/wishlist-quantity-selected-items',
  },
  total: {
    defaultMessage: '',
    id: 'store/wishlist-total',
  },
  buyItems: {
    defaultMessage: '',
    id: 'store/wishlist-buy-items',
  },
})

class Footer extends Component<FooterProps, {}> {
  public render(): ReactNode {
    const { items } = this.props
    const totalPrice = this.calculateTotal()
    const itemsToAddToCart = map(this.productShape, items)

    return (
      <div
        className={`${
          styles.ListDetailsFooter
        } flex flex-column pa4 bt b--muted-4 w-100 items-end`}
      >
        <div className="tr">
          <span className={`${styles.quantityOfSelectedItemsLabel} ml2`}>
            <FormattedMessage
              id={messages.selectedItemsQuantity.id}
              values={{ selectedItemsQuantity: <b>{items.length}</b> }}
            />
          </span>
        </div>
        <div
          className={`${
            styles.pricesContainer
          } pv4 flex flex-row justify-end b`}
        >
          <span className={`${styles.totalPriceLabel} mr2`}>
            <FormattedMessage
              id={messages.total.id}
              values={{ selectedItemsQuantity: <b>{items.length}</b> }}
            />
          </span>
          <ProductPrice
            sellingPrice={totalPrice}
            listPrice={totalPrice}
            showLabels={false}
            showListPrice={false}
          />
        </div>
        <div className={styles.buySelectedItemsBtnContainer}>
          <BuyButton
            available={items.length > 0}
            isAvailable
            skuItems={itemsToAddToCart}
            isOneClickBuy={false}
            large={isMobile}
          >
            <FormattedMessage {...messages.buyItems} />
          </BuyButton>
        </div>
      </div>
    )
  }

  private findAvailableProduct = (item: Item): boolean =>
    item.sellers.find(
      (seller: Seller) =>
        seller !== undefined &&
        seller.commertialOffer !== undefined &&
        seller.commertialOffer.AvailableQuantity !== undefined &&
        seller.commertialOffer.AvailableQuantity > 0
    ) !== undefined

  private normalizeProduct = (product: Product): Product | null => {
    if (!product) {
      return null
    }
    const normalizedProduct = { ...product }
    const items = product.items || []
    const sku = items.find(this.findAvailableProduct)
    if (sku) {
      const [seller = { commertialOffer: { Price: 0, ListPrice: 0 } }] =
        path(['sellers'], sku) || []
      const [referenceId = { Value: '' }] = path(['referenceId'], sku) || []
      const [image = { imageUrl: '' }] = path(['images'], sku) || []
      const resizedImage = image.imageUrl
      const normalizedImage = { ...image, imageUrl: resizedImage }
      normalizedProduct.sku = {
        ...sku,
        image: normalizedImage,
        referenceId,
        seller,
      }
    }
    return normalizedProduct
  }

  private productShape = (item: ListItemWithProduct): Product | undefined => {
    const product = this.normalizeProduct(item.product)
    return product
      ? path(['sku', 'itemId'], product) && {
          brand: product.brand,
          detailUrl: `/${product.linkText}/p`,
          imageUrl: path(['sku', 'image', 'imageUrl'], product),
          listPrice: path(
            ['sku', 'seller', 'commertialOffer', 'ListPrice'],
            product
          ),
          name: product.productName,
          price: path(['sku', 'seller', 'commertialOffer', 'Price'], product),
          quantity: 1,
          seller: path(['sku', 'seller', 'sellerId'], product),
          skuId: path(['sku', 'itemId'], product),
          variant: path(['sku', 'name'], product),
        }
      : undefined
  }

  private calculateTotal = (): number => {
    const { items } = this.props
    if (items) {
      return map(({ product: { items } }) => {
        if (items && head(items)) {
          return items[0].sellers[0].commertialOffer.Price
        }
        return 0
      }, items).reduce((a, b) => a + b, 0)
    }
    return 0
  }
}

export default compose(
  withToast,
  injectIntl
)(Footer)

```


[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 wishlist/react/components/ListsPage/index.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, Fragment } from 'react'
import { withRuntimeContext, withSession } from 'vtex.render-runtime'
import { Spinner } from 'vtex.styleguide'

import { concat, filter, findIndex, update } from 'ramda'
import { compose, withApollo, WithApolloClient, graphql } from 'react-apollo'
import { InjectedIntlProps, injectIntl, defineMessages } from 'react-intl'
import { session } from 'vtex.store-resources/Queries'
import { getProfile } from '../../utils/profile'
import { createList, getListsByOwner } from '../../GraphqlClient'
import withSettings from '../../withSettings'

import Content from './Content'
import ListSelector from './ListSelector'
import Lists from '../Lists'

import styles from '../../wishList.css'
import { isMobile } from 'react-device-detect'
import Loading from '../Loading'

const ON_LISTS_PAGE_CLASS = 'vtex-lists-page'
const messages = defineMessages({
  listNameDefault: {
    defaultMessage: '',
    id: 'store/wishlist-default-list-name',
  },
})
interface ListsPageState {
  lists: any
  selectedListId?: string
  isLoading?: boolean
}

interface ListsPageProps
  extends InjectedIntlProps,
    WithApolloClient<{}>,
    SettingsProps {
  runtime: Runtime
  session: Session
}

class ListsPage extends Component<ListsPageProps, ListsPageState> {
  public state: ListsPageState = {
    isLoading: true,
    lists: [],
  }

  public componentWillUnmount(): void {
    document.body.classList.remove(ON_LISTS_PAGE_CLASS)
  }

  public componentDidUpdate(prevProps: ListsPageProps): void {
    const {
      runtime: { query },
      client,
      session,
    } = this.props
    // Verify if the selected list changed by checking the location query
    if (
      client &&
      prevProps.runtime.query &&
      prevProps.runtime.query.listId !== this.state.selectedListId
    ) {
      this.setState({ selectedListId: query.listId })
    }
    // Verify if the session is loaded, so it can fetch the user's lists
    else if (
      session !== prevProps.session &&
      prevProps.session &&
      !prevProps.session.loading
    ) {
      this.fetchLists(prevProps)
    }
  }

  public componentDidMount(): void {
    document.body.classList.add(ON_LISTS_PAGE_CLASS)
    this.fetchLists(this.props)
  }

  public render() {
    const {
      session,
      runtime: { goBack },
      settings: { appSettings },
    } = this.props
    const enableMultipleLists = appSettings && appSettings.enableMultipleLists

    const profile = getProfile(session)

    if (!session || session.loading) {
      return <Loading />
    }

    if (session && !session.loading && !profile) {
      this.toLogin()
    }

    const { selectedListId: id, lists, isLoading } = this.state
    const selectedListId = id || (lists.length > 0 && lists[0].id)

    return isMobile ? (
      lists.length ? (
        <Lists
          enableMultipleLists={enableMultipleLists}
          loading={!session || session.loading || isLoading}
          lists={lists}
          onClose={goBack}
        />
      ) : null
    ) : (
      <div className={`${styles.listPage} flex flex-row mt6 ph10 pv8 h-100`}>
        {isLoading ? (
          <div className="flex justify-center w-100">
            <Spinner />
          </div>
        ) : (
          <Fragment>
            {enableMultipleLists && (
              <div className="h-100 mr6">
                <ListSelector
                  {...this.state}
                  selectedListId={selectedListId}
                  onListCreated={this.handleListCreated}
                />
              </div>
            )}
            <div className="w-100 h-100">
              <Content
                listId={selectedListId}
                lists={enableMultipleLists && lists}
                onListUpdated={this.handleListUpdated}
                onListDeleted={this.handleListDeleted}
                enableCopy={enableMultipleLists}
              />
            </div>
          </Fragment>
        )}
      </div>
    )
  }

  private handleListCreated = (list: List): void => {
    const { lists } = this.state
    this.setState({ lists: concat(lists, [list]) })
  }

  private handleListUpdated = (listUpdated: List): void => {
    const { lists } = this.state
    const index = findIndex((list: List) => list.id === listUpdated.id, lists)
    this.setState({
      lists: update(index, listUpdated, lists),
    })
  }

  private handleListDeleted = (): void => {
    const { lists, selectedListId } = this.state
    const {
      runtime: { navigate },
    } = this.props
    const listsUpdate = filter((list: List) => list !== selectedListId, lists)
    this.setState({ lists: listsUpdate })
    navigate({
      page: 'store.lists',
      query: `listId=${lists[0].id}`,
    })
  }

  private fetchLists = async (props: ListsPageProps): Promise<void> => {
    const {
      client,
      runtime: { query },
      intl,
      session,
    } = props
    const profile = getProfile(session)

    if (session && !session.loading && profile && profile.email) {
      const {
        data: { listsByOwner: lists },
      } = await getListsByOwner(client, profile.email)

      if (lists) {
        if (lists.length == 0) {
          const {
            data: { createList: defaultList },
          } = await createList(client, {
            isEditable: false,
            items: [],
            owner: profile.email,
            name: intl.formatMessage(messages.listNameDefault),
          })
          this.setState({
            lists: [defaultList],
            selectedListId: defaultList ? defaultList.id : '',
          })
        } else {
          const selectedListId = query ? query.listId : lists[0].id
          this.setState({ lists, selectedListId })
        }
      }
      this.setState({ isLoading: false })
    }
  }

  private toLogin = (): void => {
    this.props.runtime.navigate({
      page: 'store.login',
      fallbackToWindowLocation: true,
    })
  }
}

const options = {
  name: 'session',
  options: () => ({ ssr: false }),
}

export default withSession()(
  compose(
    injectIntl,
    withRuntimeContext,
    withApollo,
    graphql(session, options),
    withSettings
  )(ListsPage)
)

```


[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 wishlist/react/components/ListsPage/Content.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, Fragment, ReactNode } from 'react'

import classNames from 'classnames'
import { append, filter, map, tail } from 'ramda'
import { Spinner } from 'vtex.styleguide'

import { withApollo, WithApolloClient, compose } from 'react-apollo'
import { getListDetailed, updateList } from '../../GraphqlClient'
import ListItems from '../ListDetails/Content'
import Footer from '../ListDetails/Footer'
import Header from './Header'

import styles from '../../wishList.css'

interface ContentProps extends WithApolloClient<{}> {
  listId: string
  lists?: List[]
  onListCreated: (list: List) => void
  onListUpdated: (list: List) => void
  onListDeleted: () => void
}

interface ContentState {
  list?: List
  selectedItems: ListItemWithProduct[]
  isLoading?: boolean
}

class Content extends Component<ContentProps, ContentState> {
  public state: ContentState = {
    isLoading: true,
    selectedItems: [],
  }
  private isComponentMounted = false

  public componentWillUnmount(): void {
    this.isComponentMounted = false
  }

  public componentDidMount(): void {
    this.isComponentMounted = true
    this.fetchListDetails()
  }

  public componentDidUpdate(prevProps: ContentProps): void {
    if (this.props.listId !== prevProps.listId) {
      this.fetchListDetails()
    }
  }

  public render(): ReactNode {
    const { list, selectedItems, isLoading } = this.state
    const { listId, lists } = this.props
    const className = classNames(
      'ba b--muted-4 mt6 relative overflow-auto w-100 h-100',
      {
        pb10: selectedItems && selectedItems.length > 0,
      }
    )
    const listsAsOptions = lists
      ? filter((e: List) => e.id !== listId, tail(lists))
      : null

    return (
      <div className="h-100 flex flex-column">
        {isLoading ? (
          <div className="flex justify-center w-100">
            <Spinner />
          </div>
        ) : (
          <Fragment>
            <Header
              list={{ ...list, id: listId }}
              onListCreated={this.props.onListCreated}
              onListUpdated={this.props.onListUpdated}
              onListDeleted={this.props.onListDeleted}
            />
            <div className={className}>
              <div
                className={`${
                  styles.listPageItemsContainer
                } overflow-auto w-100`}
              >
                <div className="w-100">
                  <ListItems
                    lists={listsAsOptions}
                    hideItemsQuantityLabel
                    items={list ? list.items : []}
                    onItemSelect={this.handleItemSelect}
                    onItemRemove={this.handleItemRemove}
                  />
                </div>
              </div>
              {selectedItems.length > 0 && (
                <div className="absolute bottom-0 left-0 w-100">
                  <div className="bg-base">
                    <Footer items={selectedItems} />
                  </div>
                </div>
              )}
            </div>
          </Fragment>
        )}
      </div>
    )
  }

  private handleItemSelect = (
    itemId: string,
    product: Product,
    isSelected: boolean
  ): void => {
    const { selectedItems } = this.state
    if (isSelected) {
      this.setState({
        selectedItems: append({ itemId, product }, selectedItems),
      })
    } else {
      this.setState({
        selectedItems: filter(({ itemId: id }) => id !== itemId, selectedItems),
      })
    }
  }

  private itemWithoutProduct = ({
    id,
    productId,
    skuId,
    quantity,
  }: ListItem): ListItem => ({ id, productId, skuId, quantity })

  private handleItemRemove = (itemId: string): Promise<void> => {
    const { client, listId } = this.props
    const { list, selectedItems } = this.state
    const listUpdated = {
      ...list,
      items: filter(
        ({ id }: ListItem) => id !== itemId,
        list && list.items ? list.items : []
      ),
    }
    const itemsUpdated = map(
      item => this.itemWithoutProduct(item),
      listUpdated.items
    )
    return updateList(client, listId, { ...list, items: itemsUpdated }).then(
      () => {
        if (this.isComponentMounted) {
          this.setState({
            list: listUpdated,
            selectedItems: filter(
              ({ itemId: id }) => id !== itemId,
              selectedItems
            ),
          })
        }
      }
    )
  }

  private fetchListDetails(): void {
    const { client, listId } = this.props
    this.setState({ isLoading: true, list: {}, selectedItems: [] })
    getListDetailed(client, listId).then(response => {
      if (this.isComponentMounted) {
        this.setState({ list: response.data.list, isLoading: false })
      }
    })
  }
}

export default compose(withApollo)(Content)

```


[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 wishlist/react/components/ListsPage/ListSelector.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, ReactNode } from 'react'
import { FormattedMessage, defineMessages } from 'react-intl'
import { withRuntimeContext } from 'vtex.render-runtime'
import { ButtonWithIcon, IconPlusLines } from 'vtex.styleguide'
import CreateList from '../Form/CreateList'

import ListItem from '../ListItem'

interface ListSelectorProps {
  lists: List[]
  selectedListId: string
  onListCreated: (list: List) => void
  runtime: Runtime
}

interface ListSelectorState {
  showCreateList: boolean
  selectedListId: string
}

const ICONS_SIZE = 20
const messages = defineMessages({
  myLists: {
    defaultMessage: '',
    id: 'store/wishlist-my-lists',
  },
})

class ListSelector extends Component<ListSelectorProps, ListSelectorState> {
  public state = {
    showCreateList: false,
    selectedListId: '',
  }

  public componentDidMount(): void {
    this.setState({ selectedListId: this.props.selectedListId })
  }

  public render(): ReactNode {
    const { showCreateList } = this.state
    const plusIcon = <IconPlusLines size={ICONS_SIZE} />

    return (
      <div className="flex flex-column w5 h-100">
        <div className="bl b--rebel-pink bw2 pa4 b flex items-center flex-row">
          <div className="w-100">
            <FormattedMessage {...messages.myLists} />
          </div>
          <ButtonWithIcon
            variation="tertiary"
            icon={plusIcon}
            onClick={this.handleCreateList}
          />
        </div>
        <div className="h-100 overflow-auto">{this.renderLists()}</div>
        {showCreateList && (
          <CreateList
            onClose={() => this.setState({ showCreateList: false })}
            onFinishAdding={this.handleListCreated}
          />
        )}
      </div>
    )
  }

  private renderLists = (): ReactNode => {
    const { lists } = this.props
    const { selectedListId } = this.state
    return lists
      ? lists.map((list: List, index: number) => (
          <ListItem
            key={list.id}
            id={index}
            list={list}
            isDefault={index === 0}
            hideAction
            hideBorders
            isSelected={list.id === selectedListId}
            onClick={this.handleOnListSelect}
          />
        ))
      : null
  }

  private handleCreateList = () => {
    this.setState({ showCreateList: true })
  }

  private handleListCreated = (list: List): void => {
    const { onListCreated } = this.props
    this.setState({ showCreateList: false })
    onListCreated(list)
  }

  private handleOnListSelect = (id: number): void => {
    const {
      runtime: { setQuery },
      lists,
    } = this.props
    this.setState({ selectedListId: lists[id].id || '' })
    setQuery({ listId: lists[id].id }, { merge: false, replace: true })
  }
}

export default withRuntimeContext(ListSelector)

```


[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 wishlist/react/components/ListsPage/Header.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, ReactNode } from 'react'
import {
  FormattedMessage,
  InjectedIntlProps,
  injectIntl,
  defineMessages,
} from 'react-intl'
import { ActionMenu, IconOptionsDots } from 'vtex.styleguide'

import { compose, withApollo, WithApolloClient } from 'react-apollo'

import DialogMessage from '../Dialog/DialogMessage'
import UpdateList from '../Form/UpdateList'

import { deleteList } from '../../GraphqlClient'
import withSettings from '../../withSettings'

interface HeaderState {
  showcreateList?: boolean
  showUpdateList?: boolean
  showDeleteConfirmation?: boolean
}

interface HeaderProps
  extends InjectedIntlProps,
    WithApolloClient<{}>,
    SettingsProps {
  isDefault?: boolean
  list: List
  onListUpdated: (list: List) => void
  onListDeleted: () => void
}

const ICONS_SIZE = 20
const messages = defineMessages({
  optionConfiguration: {
    defaultMessage: '',
    id: 'store/wishlist-option-configuration',
  },
  optionDelete: {
    defaultMessage: '',
    id: 'store/wishlist-option-delete',
  },
  messageDeleteConfirmation: {
    defaultMessage: '',
    id: 'store/wishlist-delete-confirmation-message',
  },
  productsQuantity: {
    defaultMessage: '',
    id: 'store/wishlist-quantity-products',
  },
  defaultListName: {
    id: 'store/wishlist-default-list-name',
    defaultMessage: '',
  },
})

class Header extends Component<HeaderProps, HeaderState> {
  public state: HeaderState = {}
  private options = [
    {
      onClick: () => this.setState({ showUpdateList: true }),
      label: this.props.intl.formatMessage(messages.optionConfiguration),
    },
    {
      onClick: () => this.setState({ showDeleteConfirmation: true }),
      label: this.props.intl.formatMessage(messages.optionDelete),
    },
  ]

  public render(): ReactNode {
    const { showUpdateList, showDeleteConfirmation } = this.state
    const {
      list,
      list: { name, isEditable, items },
      settings: { appSettings },
      intl: { formatMessage },
    } = this.props
    const title = !isEditable
      ? (appSettings && appSettings.defaultListName) ||
        formatMessage(messages.defaultListName)
      : name

    return (
      <div className="w-100 flex items-center">
        <div className="w-100 t-heading-2">{title}</div>
        <div className="flex flex-row items-center w-100 justify-end">
          <div className="ttu mh2">
            <FormattedMessage
              {...messages.productsQuantity}
              values={{ productsQuantity: items && items.length }}
            />
          </div>
          {isEditable && (
            <div className="ml3">
              <ActionMenu
                hideCaretIcon
                options={this.options}
                buttonProps={{
                  variation: 'tertiary',
                  icon: <IconOptionsDots size={ICONS_SIZE} />,
                }}
              />
            </div>
          )}
        </div>
        {showUpdateList && (
          <UpdateList
            list={list}
            onClose={() => this.setState({ showUpdateList: false })}
            onFinishUpdate={this.handleListUpdated}
          />
        )}
        {showDeleteConfirmation && (
          <DialogMessage
            message={formatMessage(messages.messageDeleteConfirmation, {
              listName: name,
            })}
            onClose={() => this.setState({ showDeleteConfirmation: false })}
            onSuccess={this.handleDeleteList}
          />
        )}
      </div>
    )
  }

  private handleListUpdated = (list: List) => {
    this.setState({ showUpdateList: false })
    this.props.onListUpdated(list)
  }

  private handleDeleteList = (): void => {
    const { client, list } = this.props
    if (list && list.id) {
      deleteList(client, list.id).then(() => {
        this.setState({ showDeleteConfirmation: false })
        this.props.onListDeleted()
      })
    }
  }
}

export default compose(
  withApollo,
  injectIntl,
  withSettings
)(Header)

```


[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 wishlist/react/graphql/mutations/deleteList.gql from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation deleteList($id: ID!) {
  deleteList(id: $id) @context(provider: "vtex.store-graphql") {
    id
  }
}

```


[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 wishlist/react/graphql/mutations/updateList.gql from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation updateList($id: ID!, $list: ListInput) {
  updateList(id: $id, list: $list) @context(provider: "vtex.store-graphql") {
    id
    name
    isPublic
    isEditable
    owner
    items {
      id
      productId
      skuId
      quantity
    }
  }
}

```


[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 wishlist/react/graphql/mutations/createList.gql from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation createList(
  $name: String!
  $isPublic: Boolean
  $isEditable: Boolean
  $items: [ListItemInput]
  $owner: String
) {
  createList(
    list: {
      name: $name
      isPublic: $isPublic
      items: $items
      isEditable: $isEditable
      owner: $owner
    }
  ) @context(provider: "vtex.store-graphql") {
    id
    name
    owner
    isPublic
    isEditable
    items {
      id
      quantity
      productId
      quantity
    }
  }
}

```


[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 wishlist/react/graphql/queries/getListDetails.gql from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```graphql
query getListDetails($id: ID) {
  list(id: $id) @context(provider: "vtex.store-graphql") {
    name
    isEditable
    isPublic
    items {
      id
      productId
      skuId
      quantity
      product {
        productId
        productName
        description
        categories
        link
        linkText
        brand
        items {
          name
          itemId
          referenceId {
            Value
          }
          images {
            imageUrl
            imageTag
          }
          sellers {
            sellerId
            commertialOffer {
              Installments {
                Value
                InterestRate
                TotalValuePlusInterestRate
                NumberOfInstallments
                Name
              }
              AvailableQuantity
              Price
              ListPrice
            }
          }
        }
        productClusters {
          id
          name
        }
      }
    }
  }
}

```


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

[User] Show me the implementation of wishlist/react/graphql/queries/getList.gql from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```graphql
query getList($id: ID) {
  list(id: $id) @context(provider: "vtex.store-graphql") {
    id
    name
    isPublic
    isEditable
    owner
    items {
      id
      productId
      skuId
      quantity
    }
  }
}

```


[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 wishlist/react/graphql/queries/getListsByOwner.gql from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```graphql
query getListByOwner($owner: ID!) {
  listsByOwner(owner: $owner) @context(provider: "vtex.store-graphql") {
    id
    name
    isPublic
    isEditable
    owner
    items {
      id
      productId
      skuId
      quantity
    }
  }
}
```


[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 wishlist/react/graphql/queries/appSettings.gql from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```graphql
query appSettings {
  appSettings {
    enableMultipleLists
    defaultListName
  }
}
```


[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 bitcot-qa-theme/sitemap/sitemap.json from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "urlset": {
    "url": [
      {
        "loc": "https://storetheme.vtex.com/about-us"
      }
    ]
  }
}
```


[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 bitcot-qa-theme/docs/README.md from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Store theme
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Our boilerplate theme to create stores in the VTEX IO platform.

## Preview
![store-theme-default](https://user-images.githubusercontent.com/1354492/63937047-e8d81c80-ca37-11e9-86fc-61e88847bbfb.png)

## Tutorial
To understand how things work check our tutorial [Build a store using VTEX IO](https://vtex.io/docs/getting-started/build-stores-with-store-framework/1/)

## Dependencies
All store components that you see on this document are open source too. Production ready, you can found those apps in this GitHub organization.

Store framework is the baseline to create any store using _VTEX IO Web Framework_.
- [Store](https://github.com/vtex-apps/store/blob/master/README.md)

Store GraphQL is a middleware to access all VTEX APIs.
- [Store GraphQL](https://github.com/vtex-apps/store-graphql/blob/master/docs/README.md)

### Store Component Apps
- [Header](https://github.com/vtex-apps/store-header/blob/master/docs/README.md)
- [Footer](https://github.com/vtex-apps/store-footer/blob/master/docs/README.md)
- [Slider Layout](https://github.com/vtex-apps/slider-layout/blob/master/docs/README.md)
- [Shelf](https://github.com/vtex-apps/shelf/blob/master/docs/README.md)
- [Telemarketing](https://github.com/vtex-apps/telemarketing/blob/master/docs/README.md)
- [Menu](https://github.com/vtex-apps/menu/blob/master/docs/README.md)
- [Login](https://github.com/vtex-apps/login/blob/master/docs/README.md)
- [Minicart](https://github.com/vtex-apps/minicart/blob/master/docs/README.md)
- [Category Menu](https://github.com/vtex-apps/category-menu/blob/master/docs/README.md)
- [Product Summary](https://github.com/vtex-apps/product-summary/blob/master/docs/README.md)
- [Breadcrumb](https://github.com/vtex-apps/breadcrumb/blob/master/docs/README.md)
- [Search Result](https://github.com/vtex-apps/search-result/blob/master/docs/README.md)
- [Product Details](https://github.com/vtex-apps/product-details/blob/master/docs/README.md)
- [Store Components](https://github.com/vtex-apps/store-components/blob/master/docs/README.md)
- [Order Placed](https://github.com/vtex-apps/order-placed/blob/master/docs/README.md)

### Store Pixel Apps

 - [Facebook Pixel](https://github.com/vtex-apps/facebook-pixel/blob/master/docs/README.md)
 - [Google Tag Manager](https://github.com/vtex-apps/google-tag-manager/blob/master/docs/README.md)

## Contributing

Check it out [how to contribute](https://github.com/vtex-apps/awesome-io#contributing) with this project.

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
  <tr>
    <td align="center"><a href="http://www.hugoccosta.com"><img src="https://avatars2.githubusercontent.com/u/20212776?v=4" width="100px;" alt=""/><br /><sub><b>Hugo Costa</b></sub></a><br /><a href="https://github.com/vtex-apps/store-theme/commits?author=hugocostadev" title="Documentation">📖</a></td>
  </tr>
</table>

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

```


[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 bitcot-qa-theme/docs/contact-schema.json from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "title": "Contact",
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "title": "Title"
    },
    "name": {
      "type": "string",
      "title": "Name"
    },
    "email": {
      "type": "string",
      "format": "email",
      "title": "Email address"
    },
    "message": {
      "type": "string",
      "title": "Message"
    }
  },
  "required": ["email", "message"],
  "v-security": {
    "publicJsonSchema": true
  }
}

```


[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 bitcot-qa-theme/store/blocks.jsonc from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```json
/* Blocks can be defined both on the store/blocks.json file, and in any number of json
 * files inside the store/blocks directory, with any arbitrary directory structure.
 *
 * Additionally, all blocks files can support comments, such as this one, if given the
 * .jsonc file extension.
 *
 * The store/blocks.json file is intended for backwards compatibility, for miscellaneous
 * blocks, or if the app is simple enough that it doesn't warrant splitting the file.
 */
{
  "store.orderplaced": {
    "blocks": ["order-placed"]
  },

  "store.account": {
    "blocks": ["my-account"],
    "parent": {
      "challenge": "challenge.profile"
    }
  },

  "store.login": {
    "blocks": ["login-content#default"]
  },

  "login-content#default": {
    "props": {
      "isInitialScreenOptionOnly": false,
      "defaultOption": 1,
      "showPasswordVerificationIntoTooltip": true
    }
  },

  "store.product-review-form": {
    "blocks": ["product-review-form"]
  },

  "store.not-found#product": {
    "blocks": ["rich-text#not-found"]
  },

  "store.not-found#search": {
    "blocks": ["rich-text#not-found"]
  },

  "rich-text#not-found": {
    "props": {
      "textAlignment": "CENTER",
      "textPosition": "CENTER",
      "text": "**PAGE NOT FOUND**",
      "font": "t-heading-1"
    }
  }
}

```


[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 bitcot-qa-theme/store/routes.json from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.custom#about-us": {
    "path": "/about-us"
  },
  "store.custom#faq": {
    "path": "/faq"
  },
  "store.custom#contact-us": {
    "path": "/contact-us"
  }
}

```


[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 bitcot-qa-theme/styles/css/vtex.store-video.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.videoContainer {
  width: 100%;
  height: 300px;
  margin-top: 2%;
}

@media only screen and (min-width: 640px) {
  .videoContainer {
    height: 700px;
  }
}

```


[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 bitcot-qa-theme/styles/css/vtex.product-price.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.listPrice {
  color: #727273;
  margin-bottom: .25rem;
  font-size: 1rem;
}

.sellingPrice {
  color: #3f3f40;
  font-size: 1.25rem;
}

.sellingPriceValue {
  font-size: 2.25rem;
  font-weight: 700;
}

.installments {
  color: #727273;
  margin-bottom: 1rem;
}

.savings {
  font-weight: 500;
  color: #79B03A;
}

.sellingPriceValue--summary {
  font-size: 1.25rem;
  font-weight: 600;
  color: #2E2E2E;
}

.savings--summary {
  background: #8BC34A;
  border-radius: 1000px;
  align-items: center;
  display: flex;

  padding-left: 0.5rem;
  padding-right: 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
  vertical-align: baseline;
  color: #FFFFFF;
}

.savings-discount--summary {
  font-size: 0.875rem;
  font-weight: 600;
  vertical-align: baseline;
  color: #FFFFFF;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
}

.listPrice--summary {
  margin-bottom: 0.25rem;
  font-size: .875rem;
}

.installments--summary {
  margin-bottom: 2rem;
  font-size: 0.875rem;
}

.savings--summaryPercentage {
  background: #0f3e99;
  border-radius: 1000px;
  align-items: center;
  display: flex;
}

.savingsPercentage--summaryPercentage {
  font-size: 0.875rem;
  font-weight: 600;
  vertical-align: baseline;
  color: #FFFFFF;
  padding: 0.25rem 0.5rem 0.25rem 0.5rem;
}

```


[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 bitcot-qa-theme/styles/css/vtex.rich-text.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.container--deals {
  color: #ffffff;
  text-align: center;
  justify-content: center;
}

.container--shelfTitle {
  color: #727273;
  justify-content: center;
  text-align: center;
}

.headingLevel2--shelfTitle {
  font-weight: 200;
}

.container--deals .paragraph {
  margin: 0;
}

.container--link {
  justify-content: center;
}

.container--question {
  justify-content: center;
}

.container--question .paragraph {
  text-align: center;
  max-width: 540px;
  margin: 0 16px;
  padding-top: 120px;
  color: #03034E;
}

.container--question .strong {
  font-size: 32px;
  line-height: 2rem;
  font-weight: 700;
}

.container--link .paragraph {
  text-align: center;
  max-width: 540px;
  padding-top: 36px;
  padding-bottom: 84px;
  color: #03034E;
}

.container--link .strong {
  font-size: 16px;
  font-weight: 700;
  text-transform: uppercase;
}

.container--link .link {
  text-decoration: none;
  font-size: 36px;
  font-weight: 300;
  color: #03034E;
}

.container--title {
  color: #03034E;
  font-size: 28px;
  font-weight: 700;
}

@media only screen and (min-width: 640px) {
  .container--question .strong {
    font-size: 40px;
    line-height: 3rem;
  }

  .container--about {
    max-width: 85%;
  }

  .container--title {
    color: #03034E;
    font-size: 40px;
    font-weight: 700;
    max-width: 60%;
  }

  .container--title .paragraph {
    line-height: 1.3em;
  }
}

```


[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 bitcot-qa-theme/styles/css/vtex.product-summary.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.skuSelectorContainer--quickview .skuSelectorItemImage .frameAround, .skuSelectorContainer--quickview .skuSelectorItemImage .skuSelectorInternalBox {
  border-radius: 50%;
}

.container :global(.vtex-modal-layout-0-x-triggerContainer) {
  opacity: 0;
  transition: opacity 200ms ease-in-out;
}

.container:hover :global(.vtex-modal-layout-0-x-triggerContainer) {
  opacity: 1;
}

@media screen and (max-width: 40em) {
  .container :global(.vtex-modal-layout-0-x-triggerContainer) {
    display: none;
  }
}

.nameContainer {
  justify-content: start;
  padding-top: 1rem;
  padding-bottom: 1rem;
}

.brandName {
  font-weight: 600;
  font-size: 18px;
  color: #2E2E2E;
}

.container {
  text-align: start;
}

.imageContainer {
  text-align: center;
}

.image {
  border-radius: 0.25rem;
}

```


[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 bitcot-qa-theme/styles/css/vtex.modal-layout.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
@media screen and (min-width: 40em) {
  .paper--quickview {
    height: 260px;
    width: 580px;
  }

  .actionsContainer--quickview {
    padding: 0 2rem 2rem 2rem;
  }
}

@media screen and (min-width: 50em) {
  .paper--quickview {
    height: 450px;
    width: 900px;
  }
}

@media screen and (min-width: 75em) {
  .paper--quickview {
    width: calc(100% - 4rem);
    height: auto;
  }
}

@media screen and (min-width: 82em) {
  .actionsContainer--quickview {
    padding: 0 2.5rem 2.5rem 2.5rem;
  }
}

.paper--quickview {
  min-width: 320px;
  max-width: 96rem;
}

.headerContainer--quickview {
  border: none;
  position: absolute;
  right: 0;
}

.actionsContainer--quickview {
  height: 100%;
  width: 100%;
  transition: box-shadow 150ms ease-in-out, border-top 150ms ease-in-out;
  border-top-style: solid;
  border-top-width: 1px;
  border-top-color: transparent;
  box-shadow: -4px 0px 27px -12px transparent;
}

.actionsContentWrapper {
  border-top-width: 1px;
  border-top-style: solid;
  border-top-color: #aaa;
  padding-top: 1.5rem;
}

.contentContainer--quickviewContent {
  padding: 2.5rem 0 2.5rem 2rem;
  overflow-x: hidden;
}

.triggerContainer--quickview {
  padding: .75rem;
}

.triggerContainer {
  outline: none;
}

.actionsContainer--quickview:not(.actionsContainerEndOfContent--quickview) {
  border-top-color: rgb(238, 238, 238);
  box-shadow: -4px 0px 27px -12px rgba(0,0,0,0.64);
}

.actionsContainer--quickview:not(.actionsContainerEndOfContent--quickview) .actionsContentWrapper {
  border-top-color: transparent;
}

.closeButton {
  padding-right: 1rem;
}

.paper--product-gifts {
  padding: 1.5rem;
}

```


[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 bitcot-qa-theme/styles/css/vtex.store-header.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
/* add transitions */
:global(.vtex-sticky-layout-0-x-container)
  :global(.vtex-store-components-3-x-logoLink),
:global(.vtex-sticky-layout-0-x-container)
  :global(.vtex-minicart-2-x-openIconContainer),
:global(.vtex-sticky-layout-0-x-container)
  :global(.vtex-store-components-3-x-searchBarContainer) {
  transition: all 0.3s ease;
  display: block;
}

/* desktop/mobile main header padding and background transitions */
:global(.vtex-flex-layout-0-x-flexRowContent--main-header),
:global(.vtex-flex-layout-0-x-flexRowContent--main-header-mobile) {
  transition: background 0.3s ease;
  will-change: padding, background;
}

/* main header desktop has a bigger padding when not stuck */
:global(.vtex-flex-layout-0-x-flexRowContent--main-header) {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

/* add a box-shadow to desktop/mobile header when stuck */
:global(.vtex-sticky-layout-0-x-wrapper--stuck)
  :global(.vtex-flex-layout-0-x-flexRowContent--main-header),
:global(.vtex-sticky-layout-0-x-wrapper--stuck)
  :global(.vtex-flex-layout-0-x-flexRowContent--main-header-mobile) {
  box-shadow: 0 4px 5px rgba(0, 0, 0, 0.1);
}

/*
 * remove the extra padding when stuck from the desktop header
 * we add a transition only when "stuck" for the "unstick" step seem faster
 */
:global(.vtex-sticky-layout-0-x-wrapper--stuck)
  :global(.vtex-flex-layout-0-x-flexRowContent--main-header) {
  transition: padding 0.3s ease, background 0.3s ease;
  padding-top: 0;
  padding-bottom: 0;
}

/* if supported add some translucency to the desktop/mobile header background */
@supports (backdrop-filter: blur(5px)) {
  :global(.vtex-sticky-layout-0-x-wrapper--stuck)
    :global(.vtex-flex-layout-0-x-flexRowContent--main-header),
  :global(.vtex-sticky-layout-0-x-wrapper--stuck)
    :global(.vtex-flex-layout-0-x-flexRowContent--main-header-mobile) {
    backdrop-filter: blur(8px);
  }

  /* header background when stuck and not hovered */
  :global(.vtex-sticky-layout-0-x-wrapper--stuck):not(:hover)
    :global(.vtex-flex-layout-0-x-flexRowContent--main-header),
  :global(.vtex-sticky-layout-0-x-wrapper--stuck):not(:hover)
    :global(.vtex-flex-layout-0-x-flexRowContent--main-header-mobile) {
    background: rgba(240, 240, 240, 0.8);
  }
}

/* stick wrapper with the same color as the desktop/mobile header */
:global(.vtex-sticky-layout-0-x-wrapper--sticky-header) {
  background-color: #f0f0f0;
}

/* resize the logo for neat effect */
:global(.vtex-sticky-layout-0-x-wrapper--stuck)
  :global(.vtex-store-components-3-x-logoLink) {
  transform: scale(0.85);
}

@media (prefers-reduced-motion: reduce) {
  :global(.vtex-sticky-layout-0-x-container)
    :global(.vtex-store-components-3-x-logoLink),
  :global(.vtex-sticky-layout-0-x-container)
    :global(.vtex-minicart-2-x-openIconContainer),
  :global(.vtex-sticky-layout-0-x-container)
    :global(.vtex-store-components-3-x-searchBarContainer),
  :global(.vtex-sticky-layout-0-x-wrapper--stuck)
    :global(.vtex-flex-layout-0-x-flexRowContent--main-header) {
    transition: none;
  }
}

```


[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 bitcot-qa-theme/styles/css/vtex.store-components.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.notificationBarContainer {
  background-color: #e6f1e6;
  color: #03034e;
  font-weight: 700;
  font-size: 12px;
  text-decoration: underline;
}

.infoCardContainer--info-card-home {
  max-width: 1520px;
  margin: 0 auto;
  background-color: #e6f1e6;
  padding: 0px;
}

.newsletter {
  background-color: #03054e;
  max-width: 1528px;
  margin: 0 auto;
}

.newsletter .container {
  padding: 60px 10px;
}

.label {
  color: #fff;
  font-size: 26px;
}

.inputGroup {
  padding-bottom: 4px;
  display: flex;
  color: #fff;
}

.inputGroup :global(.vtex-styleguide-9-x-hideDecorators) {
  background-color: transparent;
  color: #fff;
  border: none;
}

.inputGroup :global(.vtex-styleguide-9-x-hideDecorators)::placeholder {
  color: #fff;
  font-size: 16px;
}

.inputGroup :global(.vtex-button) {
  background-color: transparent;
  color: #fff;
  border: none;
  margin-right: -24px;
}

.buttonContainer {
  padding-top: 0px;
}

.imageElement--storePrint {
  max-height: 300px;
  margin-bottom: 20px;
}

.skuSelectorContainer {
  margin: 1rem 0;
}

@media only screen and (min-width: 640px) {
  .newsletter .container {
    padding: 140px;
  }

  .label {
    font-size: 40px;
  }

  .infoCardContainer--info-card-home .infoCardTextContainer {
    width: 30%;
  }

  .infoCardContainer--info-card-home .infoCardImageContainer {
    width: 70%;
  }

  .imageElement--storePrint {
    margin-bottom: 0px;
    max-height: 600px;
  }
}

.productImagesContainer--quickview .carouselContainer {
  justify-content: flex-start;
}

.productImagesContainer--quickview .productImageTag {
  object-fit: cover !important;
}

.productImagesContainer--quickview .carouselGaleryThumbs {
  display: none;
}

.productImagesContainer--quickview .carouselGaleryCursor {
  margin: 0;
  padding: 0;
  width: 100%;
}

.productNameContainer {
  color: #3f3f40;
  font-weight: bold;
  font-size: 30px;
}

```


[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 bitcot-qa-theme/styles/css/vtex.store-footer.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.row--menu-row {
  padding-right: 24px;
}

.row--menu-row .rowContainer {
  align-items: flex-start;
}

.row--payment-methods {
  padding-top: 16px;
}

```


[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 bitcot-qa-theme/styles/css/vtex.product-identifier.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.product-identifier--productReference {
  margin-bottom: 1rem;
}

```


[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 bitcot-qa-theme/styles/css/vtex.reviews-and-ratings.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.container {
  max-width: 96rem;
  padding-left: .5rem;
  padding-right: .5rem;
}

.inlineContainer {
  margin-left: 0rem;
}

.star--filled {
  color: #FFB100;
}

.star--empty {
  color: #FFB100;
}

```


[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 bitcot-qa-theme/styles/css/vtex.product-highlights.css from the bitcot-qa-theme VTEX app.

[Assistant]
Here's the implementation:

```css
.productHighlightWrapper--collection {
  position: absolute;
  top: 10px;
  left: 5px;
  width: 200px;
}

.productHighlightText {
  background: antiquewhite;
  border-radius: 20px;
  padding: 5px 10px;
}

```