[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 product-customizer/react/components/Product/ProductCustomizerContainer.jsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```jsx
import React, { Component } from 'react'
import { all, both, propEq, values, find, path, prop, findLast } from 'ramda'

import ProductCustomizerWrapper from './ProductCustomizerWrapper'

class ProductCustomizerContainer extends Component {
  parseProduct(product) {
    const compositionPrices = this.getPriceMap(product.itemMetadata.priceTable)

    const items = product.itemMetadata.items.reduce(
      (acc, item) => ({
        ...acc,
        ...this.parseItemMetadata(item, compositionPrices, product.items),
      }),
      {}
    )

    return {
      productName: product.productName,
      imageUrl: product.items[0].images[0].imageUrl,
      imageText: product.items[0].images[0].imageText,
      items,
    }
  }

  /**
   * Produces an object in which each key is a price table (like small) and the fields are objects that map
   * product ids to its prices
   */
  getPriceMap(priceTable) {
    return priceTable.reduce((prev, curr) => {
      // eslint-disable-next-line no-shadow
      const { type, values } = curr
      const priceMap = values.reduce(
        (currMap, itemPrice) => ({
          ...currMap,
          [itemPrice.id]: itemPrice.price,
        }),
        {}
      )

      return { ...prev, [type]: priceMap }
    }, {})
  }

  parseAssemblyOption(assemblyOption, prices) {
    const { composition, id } = assemblyOption
    const [, optionName] = id.split('_')
    const items = composition.items.reduce(
      (prev, compCurr) => ({
        ...prev,
        ...this.parseCompositionItem(compCurr, prices),
      }),
      {}
    )

    return {
      [optionName]: {
        name: optionName,
        assemblyId: id,
        items,
        properties: {
          maxTotalItems: composition.maxQuantity,
          minTotalItems: composition.minQuantity,
        },
        isSingleChoice: both(
          propEq('minQuantity', 1),
          propEq('maxQuantity', 1)
        )(composition),
        isToggleChoice: all(propEq('maxQuantity', 1))(
          values(composition.items)
        ),
      },
    }
  }

  parseCompositionItem(compItem, prices) {
    const { product } = this.props.productQuery
    const metadatas = product.itemMetadata.items
    const { id, priceTable } = compItem
    const compMeta = metadatas.find((metadata) => metadata.id === id)
    const price = prices[priceTable][id]
    const fullComp = {
      ...compMeta,
      ...compItem,
      price,
    }

    return { [compMeta.name]: fullComp }
  }

  parseItemMetadata(itemMetadata, prices, productItems) {
    if (itemMetadata.assemblyOptions.length === 0) {
      return {}
    }

    const { name } = itemMetadata
    const attachments = itemMetadata.assemblyOptions.reduce(
      (prev, option) => ({
        ...prev,
        ...this.parseAssemblyOption(option, prices),
      }),
      {}
    )

    const productItem = find(propEq('itemId', itemMetadata.id))(productItems)
    const crustImage = prop('imageUrl')(
      findLast(propEq('imageLabel', 'Crust'))(productItem.images)
    )

    const commertialOffer = prop('commertialOffer')(
      find(propEq('sellerId', itemMetadata.seller))(productItem.sellers)
    )

    return {
      [name]: {
        attachments,
        commertialOffer,
        imageUrl: crustImage,
        price: commertialOffer.Price,
        seller: itemMetadata.seller,
        skuId: itemMetadata.id,
        listPrice: commertialOffer.ListPrice,
        name: productItem.nameComplete,
        detailUrl: `${this.props.productQuery.product.linkText}/p`,
        skuImageUrl: path(['images', '0', 'imageUrl'], productItem),
      },
    }
  }

  render() {
    const { product } = this.props.productQuery

    if (!product) {
      return null
    }

    return (
      <ProductCustomizerWrapper
        productQuery={this.props.productQuery}
        product={this.parseProduct(product)}
      />
    )
  }
}

export default ProductCustomizerContainer

```


[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 product-customizer/react/components/Product/SkuSelector.jsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```jsx
import React, { Fragment } from 'react'
import { FormattedMessage } from 'react-intl'
import { ExtensionPoint } from 'vtex.render-runtime'
import { find, pathOr, prop, propEq, sort } from 'ramda'
import ProductPrice from 'vtex.store-components/ProductPrice'

import SingleChoice from '../Variation/Items/SingleChoice'

// Order by availability or by Price
const compareSku = (item, otherItem) => {
  const [
    {
      commertialOffer: { AvailableQuantity: quantity, Price: price },
    },
  ] = item.sellers

  const [
    {
      commertialOffer: { AvailableQuantity: otherQuantity, Price: otherPrice },
    },
  ] = otherItem.sellers

  return quantity === 0 ? 1 : (otherQuantity === 0 && -1) || price - otherPrice
}

const moreThanVariations = (items, count) =>
  pathOr(0, ['0', 'variations', 'length'], items) > count

const getSkuItems = (items) => sort(compareSku, items)

const getSelectedItem = (items, selectedSku) =>
  selectedSku ? find(propEq('name', selectedSku), items) : null

const saveSkuSelector = (onSkuChange, skuId, items) => {
  const name = prop('name', find(propEq('itemId', skuId), items))

  onSkuChange(name, false)
}

const SelectorOptions = ({
  productQuery,
  selectedSku,
  onSkuChange,
  parsedItems,
}) => {
  const items = pathOr([], ['product', 'items'], productQuery)

  // If item has variations field, show SKUSelector
  if (moreThanVariations(items, 0)) {
    return (
      <div className="ph5">
        <ExtensionPoint
          id="sku-selector"
          skuItems={getSkuItems(items)}
          skuSelected={getSelectedItem(items, selectedSku)}
          productSlug={productQuery.product.linkText}
          onSKUSelected={(skuId) => saveSkuSelector(onSkuChange, skuId, items)}
          alwaysShowSecondary={false}
        />
      </div>
    )
  }

  // Otherwise, show simple list
  return (
    <Fragment>
      <div className="ph5 pb4 c-muted-2 t-small">
        <FormattedMessage id="store/product-customizer.pick-size" />
      </div>
      {Object.entries(parsedItems).map(([name, item]) => (
        <SingleChoice
          price={item.price * 100}
          imageUrl={item.imageUrl}
          name={name}
          selected={name === selectedSku}
          onChange={onSkuChange}
          key={name}
          showPlus={false}
        />
      ))}
    </Fragment>
  )
}

function SkuSelector({ items, selectedSku, onSkuChange, productQuery }) {
  return (
    <Fragment>
      <div className="ph5 pt4 pb2 pb4-ns">
        {selectedSku && (
          <ProductPrice
            showListPrice
            showLabels={false}
            sellingPrice={items[selectedSku].commertialOffer.Price}
            listPrice={items[selectedSku].commertialOffer.ListPrice}
            sellingPriceClass="t-heading-2"
            sellingPriceContainerClass="pv2"
            listPriceClass="c-muted-2 strike"
          />
        )}
      </div>
      <SelectorOptions
        productQuery={productQuery}
        selectedSku={selectedSku}
        onSkuChange={onSkuChange}
        parsedItems={items}
      />
    </Fragment>
  )
}

export default SkuSelector

```


[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 product-customizer/react/components/ProductAssemblyOptions/InputValue/OptionBox.tsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import classNames from 'classnames'
import { useCssHandles, applyModifiers } from 'vtex.css-handles'

import slugify from '../../../modules/slugify'
import styles from '../styles.css'

const CSS_HANDLES = ['inputValueOptionBox'] as const

const OptionBox: FC<Props> = ({ option, selected, onClick, onKeyDown }) => {
  const handles = useCssHandles(CSS_HANDLES)

  return (
    <div
      role="button"
      tabIndex={0}
      className={classNames(
        applyModifiers(handles.inputValueOptionBox, slugify(option)),
        'relative di pointer flex items-center outline-0 mr4'
      )}
      onClick={onClick}
      onKeyDown={onKeyDown}
    >
      <div
        className={classNames(
          styles.frameAround,
          'absolute b--action-primary br3 bw1',
          {
            ba: selected,
          }
        )}
      />
      <div
        className={classNames(
          'w-100 h-100 ba br2 bw1 b--muted-4 z-1 c-muted-5 flex items-center overflow-hidden',
          {
            'hover-b--muted-2': !selected,
          }
        )}
      >
        <div className="c-on-base center pv3 ph5 z-1 t-body">{option}</div>
      </div>
    </div>
  )
}

interface Props {
  option: string
  selected: boolean
  onClick: React.MouseEventHandler
  onKeyDown: React.KeyboardEventHandler
}

export default OptionBox

```


[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 product-customizer/react/components/ProductAssemblyOptions/InputValue/useInputValue.tsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```tsx
import {
  useProductAssemblyGroupState,
  useProductAssemblyGroupDispatch,
} from '../../ProductAssemblyContext/Group'

interface OnChangeParams {
  value: string | boolean
}

export default function useInputValue(
  inputId: string
): [string | boolean, (param: OnChangeParams) => void] {
  const {
    path,
    valuesOfInputValues,
  } = useProductAssemblyGroupState() as AssemblyOptionGroupState

  const dispatch = useProductAssemblyGroupDispatch()

  const onChange = ({ value }: OnChangeParams) => {
    dispatch({
      type: 'SET_INPUT_VALUE',
      args: {
        inputValueLabel: inputId,
        inputValue: value,
        groupPath: path,
      },
    })
  }

  const value = valuesOfInputValues[inputId]

  return [value, onChange]
}

export function useInputValueId(inputValueInfo: InputValue) {
  const { path } = useProductAssemblyGroupState() as AssemblyOptionGroupState

  return inputValueInfo.label + path.join('-')
}

```


[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 product-customizer/react/components/ProductAssemblyOptions/InputValue/BooleanInputValue.tsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { Checkbox } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import useInputValue, { useInputValueId } from './useInputValue'

const CSS_HANDLES = ['booleanInputValue'] as const

const BooleanInputValue: FC<Props> = ({ inputValueInfo }) => {
  const [state, onChange] = useInputValue(inputValueInfo.label)
  const id = useInputValueId(inputValueInfo)

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.checked

    onChange({ value })
  }

  const handles = useCssHandles(CSS_HANDLES)

  return (
    <div className={`${handles.booleanInputValue} mb4`}>
      <Checkbox
        id={id}
        value={inputValueInfo.label}
        checked={Boolean(state)}
        name={inputValueInfo.label}
        label={inputValueInfo.label}
        onChange={handleChange}
      />
    </div>
  )
}

interface Props {
  inputValueInfo: BooleanInputValue
}

export default BooleanInputValue

```


[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 product-customizer/react/components/ProductAssemblyOptions/InputValue/OptionsInputValue.tsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, useMemo } from 'react'
import { Dropdown } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'
import { useIntl } from 'react-intl'

import { useProductAssemblyGroupState } from '../../ProductAssemblyContext/Group'
import useInputValue from './useInputValue'
import OptionBox from './OptionBox'
import {
  formatSubscriptionLabel,
  formatSubscriptionOptions,
  isSubscriptionRelated,
  parseFrequency,
  SUBSCRIPTION_KEY_PURCHASEDAY,
  SUBSCRIPTION_KEY_FREQUENCY,
} from '../../../modules/subscriptions'

const DROPDOWN_OPTIONS_HANDLES = ['optionsInputValueDropdown'] as const
const BOX_OPTIONS_HANDLES = [
  'optionsInputValue',
  'optionsInputValueLabelContainer',
  'optionsInputValueLabel',
  'optionsInputValueOptionBoxContainer',
] as const

export type OptionDisplay = 'select' | 'box' | 'smart'

type Options = {
  label: string
  value: string
}

type InnerProps = {
  label: string
  options: Options[]
  inputId: string
}

type Props = {
  optionsDisplay?: OptionDisplay
  inputValueInfo: OptionsInputValue
}

const DropdownOptions: FC<InnerProps> = ({ label, options, inputId }) => {
  const [state, onChange] = useInputValue(inputId)
  const handles = useCssHandles(DROPDOWN_OPTIONS_HANDLES)

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

    onChange({ value })
  }

  return (
    <div className={`${handles.optionsInputValueDropdown} mb4`}>
      <Dropdown
        value={state}
        onChange={handleChange}
        label={label}
        options={options}
      />
    </div>
  )
}

const BoxOptions: FC<InnerProps> = ({ label, options, inputId }) => {
  const [state, onChange] = useInputValue(inputId)
  const handles = useCssHandles(BOX_OPTIONS_HANDLES)

  const handleKeyDown = (event: React.KeyboardEvent) => {
    const selected = state as string
    const selectedIndex: number = options.findIndex((i) => i.value === selected)

    switch (event.key) {
      case 'ArrowRight': {
        const count = options.length
        const nextSelectedIndex = (selectedIndex + 1) % count
        const newOption = options[nextSelectedIndex]

        onChange({ value: newOption.value })
        break
      }

      case 'ArrowLeft': {
        const count = options.length
        const previousSelectedIndex = (selectedIndex - 1 + count) % count
        const newOption = options[previousSelectedIndex]

        onChange({ value: newOption.value })
        break
      }

      case 'Home': {
        const [newOption] = options

        onChange({ value: newOption.value })
        break
      }

      case 'End': {
        const count = options.length
        const newOption = options[count - 1]

        onChange({ value: newOption.value })
        break
      }

      default: {
        break
      }
    }
  }

  return (
    <div className={`${handles.optionsInputValue} mb4`}>
      <div className={`${handles.optionsInputValueLabelContainer} mb3`}>
        <span
          className={`${handles.optionsInputValueLabel} c-muted-1 t-small overflow-hidden`}
        >
          {label}
        </span>
      </div>
      <div
        className={`${handles.optionsInputValueOptionBoxContainer} inline-flex flex-wrap flex items-center`}
      >
        {options.map((option) => {
          return (
            <OptionBox
              key={option.value}
              onKeyDown={handleKeyDown}
              option={option.label}
              selected={state === option.value}
              onClick={() => onChange({ value: option.value })}
            />
          )
        })}
      </div>
    </div>
  )
}

const SubscriptionOption: FC<Props> = ({ optionsDisplay, inputValueInfo }) => {
  const intl = useIntl()
  const state = useProductAssemblyGroupState()
  const inputId = inputValueInfo.label
  const inputDomain = inputValueInfo.domain

  // We get the current `frequency` value here instead of inside the useMemo
  // so it doesn't depend on the state, as it is a new object at every update.
  // For non-subscription inputValues, this is `undefined` which is fine.
  // Prevents re-rendering when changing `purchaseDay`.
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  const frequency = parseFrequency(
    state?.valuesOfInputValues[SUBSCRIPTION_KEY_FREQUENCY] as string
  )!

  const { label, options } = useMemo(() => {
    return {
      label: formatSubscriptionLabel(inputId, intl),
      options: formatSubscriptionOptions({
        inputId,
        inputDomain,
        frequency: {
          type: frequency?.type,
          interval: frequency?.interval,
        },
        intl,
      }),
    }
  }, [inputId, intl, inputDomain, frequency?.type, frequency?.interval])

  let OptionComponent = optionsDisplay === 'box' ? BoxOptions : DropdownOptions

  // enforce dropdown format for numeric days
  if (
    inputValueInfo.label === SUBSCRIPTION_KEY_PURCHASEDAY &&
    (frequency.type === 'month' || frequency.type === 'year')
  ) {
    OptionComponent = DropdownOptions
  }

  return (
    <OptionComponent
      inputId={inputValueInfo.label}
      label={label}
      options={options}
    />
  )
}

const OptionsInputValue: FC<Props> = (props) => {
  const { optionsDisplay = 'select', inputValueInfo } = props

  if (isSubscriptionRelated(inputValueInfo.label)) {
    return <SubscriptionOption {...props} />
  }

  const OptionComponent =
    optionsDisplay === 'box' ? BoxOptions : DropdownOptions

  return (
    <OptionComponent
      inputId={inputValueInfo.label}
      label={inputValueInfo.label}
      options={inputValueInfo.domain.map((value) => ({ label: value, value }))}
    />
  )
}

export default OptionsInputValue

```


[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 product-customizer/react/components/ProductAssemblyOptions/InputValue/TextInputValue.tsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { Input } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import useInputValue from './useInputValue'

const CSS_HANDLES = ['textInputValue'] as const

const TextInputValue: FC<Props> = ({ inputValueInfo }) => {
  const [state, onChange] = useInputValue(inputValueInfo.label)

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

    onChange({ value })
  }

  const handles = useCssHandles(CSS_HANDLES)

  return (
    <div className={`${handles.textInputValue} mb4`}>
      <Input
        value={state}
        onChange={handleChange}
        label={inputValueInfo.label}
        maxLength={inputValueInfo.maxLength}
      />
    </div>
  )
}

interface Props {
  inputValueInfo: TextInputValue
}

export default TextInputValue

```


[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 product-customizer/react/components/Variation/Items/ToggledChoice.jsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```jsx
import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { Checkbox } from 'vtex.styleguide'

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

const ToggledChoice = ({ imageUrl, name, selected, onChange, disabled }) => (
  // eslint-disable-next-line jsx-a11y/label-has-associated-control
  <label
    className={`vtex-product-customizer__toggled-choice ${
      selected ? 'selected bg-washed-blue' : 'hover-bg-near-white'
    } db pa4 pointer bb b--light-gray`}
  >
    <div className="relative flex items-center">
      <div className="flex-none pv2">
        {/* eslint-disable-next-line jsx-a11y/alt-text */}
        <img src={imageUrl} width="48" className="br3" />
      </div>
      <div className="flex-auto ml5">
        <div className="toggled-choice__name t-heading-5">{name}</div>
      </div>
      <div className={`${disabled ? 'o-70' : ''}`}>
        <Checkbox
          id={`${name}-choice`}
          disabled={disabled}
          name={name}
          checked={selected}
          onChange={() => onChange(name)}
        />
      </div>
    </div>
  </label>
)

ToggledChoice.propTypes = {
  /* Max limit of selections */
  selected: PropTypes.bool,
  /* Trigger function to handle changes on inputs  */
  onChange: PropTypes.func,
  disabled: PropTypes.bool,
  name: PropTypes.string.isRequired,
  imageUrl: PropTypes.string,
}

export default memo(ToggledChoice)

```


[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 product-customizer/react/components/Variation/Items/SingleChoice.jsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```jsx
import PropTypes from 'prop-types'
import React, { memo } from 'react'
import ProductPrice from 'vtex.store-components/ProductPrice'
import Radio from 'vtex.styleguide/Radio'

import ItemDescription from './ItemDescription'

const SingleChoice = ({
  imageUrl,
  name,
  price,
  selected,
  onChange,
  showPlus,
}) => {
  const calculatedPrice = (price / 100).toFixed(2)
  const parsedPrice = parseFloat(calculatedPrice)

  const description = parsedPrice && (
    <div className="single-choice__price flex t-small fw5 c-action-primary mt1">
      {showPlus && <div>+ </div>}
      <ProductPrice
        {...{
          showLabels: false,
          showListPrice: false,
          sellingPrice: parsedPrice,
        }}
      />
    </div>
  )

  return (
    <div
      role="button"
      tabIndex={0}
      onKeyDown={(e) => e.key === ' ' && onChange(name, true)}
      onClick={() => onChange(name, true)}
      className={`${
        selected && 'selected bg-muted-5'
      } hover-bg-muted-5 ph4 pointer bb b--muted-5 bw1`}
    >
      <div className="relative flex items-center justify-between pv5">
        <ItemDescription {...{ description, imageUrl, name }} />
        <div className="mt3">
          <Radio
            checked={selected}
            // Required but useless props
            {...{ id: '', label: '', name: '', value: '', onChange: () => {} }}
          />
        </div>
      </div>
    </div>
  )
}

SingleChoice.propTypes = {
  /* Define if current component is selected  */
  selected: PropTypes.bool,
  /* Trigger function to handle changes on inputs  */
  onChange: PropTypes.func.isRequired,
  imageUrl: PropTypes.string,
  price: PropTypes.number,
  name: PropTypes.string,
  showPlus: PropTypes.bool,
}

export default memo(SingleChoice)

```


[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 product-customizer/react/components/Variation/Items/ItemDescription.jsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```jsx
import React, { memo } from 'react'

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

/* eslint-disable jsx-a11y/alt-text */
function ItemDescription({ description, name, imageUrl }) {
  return (
    <div className="flex">
      <div>
        {imageUrl && (
          <img
            src={imageUrl}
            className="br3 mr4"
            style={{ height: '54px', width: '54px' }}
          />
        )}
      </div>
      <div className="multiple-choice__title flex flex-column justify-center">
        <div className="multiple-choice__name t-heading-5 c-on-base">
          {name}
        </div>
        <div className="single-choice__description">{description || null}</div>
      </div>
    </div>
  )
}

export default memo(ItemDescription)

```


[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 product-customizer/react/components/Variation/Items/MultipleChoice.jsx from the product-customizer VTEX app.

[Assistant]
Here's the implementation:

```jsx
import PropTypes from 'prop-types'
import React, { memo } from 'react'
import NumericStepper from 'vtex.styleguide/NumericStepper'
import ProductPrice from 'vtex.store-components/ProductPrice'

import ItemDescription from './ItemDescription'

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

const MultipleChoice = ({
  imageUrl,
  price,
  name,
  chosenAmount,
  canIncrease,
  canDecrease,
  onChange,
}) => {
  const calculatedPrice = (price / 100).toFixed(2)
  const parsedPrice = parseFloat(calculatedPrice)
  const isSelected = !!chosenAmount

  const sellingPriceClass = 'c-action-primary t-small fw5'

  const description = !!parsedPrice && (
    <div className="multiple-choice__price flex items-center c-action-primary t-small fw5 mt1">
      <div>+ </div>
      <ProductPrice
        {...{
          showLabels: false,
          showListPrice: false,
          sellingPrice: parsedPrice,
          sellingPriceClass,
        }}
      />
    </div>
  )

  return (
    <div
      className={`vtex-product-customizer__multiple-choice ${
        isSelected && 'selected bg-muted-5'
      } hover-bg-muted-5 w-100 ph4 pv5 bb b--muted-5`}
    >
      <div className="relative flex justify-between items-center">
        <ItemDescription {...{ imageUrl, name, description }} />
        <div className="flex-auto flex-none-ns flex justify-end">
          <div className="multiple-choice__actions flex-none ml4 c-action-primary t-body fw5 z-1">
            <NumericStepper
              lean
              value={chosenAmount}
              minValue={chosenAmount - canDecrease}
              maxValue={chosenAmount + canIncrease}
              onChange={({ value }) => onChange(value, name)}
            />
          </div>
        </div>
      </div>
    </div>
  )
}

MultipleChoice.propTypes = {
  imageUrl: PropTypes.string,
  price: PropTypes.number,
  name: PropTypes.string,
  chosenAmount: PropTypes.number,
  canIncrease: PropTypes.bool,
  canDecrease: PropTypes.bool,
  onChange: PropTypes.func.isRequired,
}

export default memo(MultipleChoice)

```


[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 apub-app-center/messages/pt.json from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "apub-app-center.navigation.label-group": "APUB App Center",
  "apub-app-center.navigation.search.kws": "app, admin, apub, apps",
  "apub-app-center.hello-world": "APUB App Center",
  "apub-app-center.details": "Nome da App",
  "apub-app-center.filter-header": "Filtros",
  "apub-app-center.filter": "Aplicar",
  "apub-app-center.filter-category": "Categoria",
  "apub-app-center.filter-store": "Loja",
  "apub-app-center.filter-status": "Status",
  "apub-app-center.apps-header": "Todas as aplicações",
  "apub-app-center.apps-view": "Ver mais"
}

```


[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 apub-app-center/messages/en.json from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "apub-app-center.navigation.label-group": "APUB App Center",
  "apub-app-center.navigation.search.kws": "app, admin, apub, apps",
  "apub-app-center.hello-world": "APUB App Center",
  "apub-app-center.details": "Name of the App",
  "apub-app-center.filter-header": "Filters",
  "apub-app-center.filter": "Apply",
  "apub-app-center.filter-category": "Category",
  "apub-app-center.filter-store": "Store",
  "apub-app-center.filter-status": "Status",
  "apub-app-center.apps-header": "All the apps",
  "apub-app-center.apps-view": "See more"
}

```


[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 apub-app-center/messages/es.json from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "apub-app-center.navigation.label-group": "APUB App Center",
  "apub-app-center.navigation.search.kws": "app, admin, apub, apps",
  "apub-app-center.hello-world": "APUB App Center",
  "apub-app-center.details": "Nombre de la App",
  "apub-app-center.filter-header": "Filtros",
  "apub-app-center.filter": "Aplicar",
  "apub-app-center.filter-category": "Categoria",
  "apub-app-center.filter-store": "Tienda",
  "apub-app-center.filter-status": "Status",
  "apub-app-center.apps-header": "Totas las apps",
  "apub-app-center.apps-view": "Ver más"
}

```


[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 apub-app-center/admin/navigation.json from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "section": "other",
  "titleId": "apub-app-center.navigation.label-group",
  "path": "/admin/apub-app-center",
  "searchKeyWordsHelpers": "apub-app-center.navigation.search.kws"
}

```


[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 apub-app-center/admin/routes.json from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin.app.apub-app-center": {
    "component": "Admin",
    "path": "/admin/app/apub-app-center"
  },
  "admin.app.apub-app-center-detail": {
    "component": "AppDetail",
    "path": "/admin/app/apub-app-center/: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 apub-app-center/docs/README.md from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```markdown
📢 Use this project, [contribute](https://github.com/vtex-apps/apub-app-center) to it or open issues to help evolve it using [Store Discussion](https://github.com/vtex-apps/store-discussion).

# # APUB App Center

<!-- DOCS-IGNORE:start -->
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<!-- DOCS-IGNORE:end -->

Admin component that groups all the apps made in the region for quick and easy access to documentation, requirements, tutorials and demos.

### How to develop admins

1. Admins always declare routes in `/admin/app/<route>`

2. Declare the `admin` builder in your manifest

3. When installed, the user navigates to `/admin/<route>`, but your app runs in an iframe that points to `/admin/app/<route>`.

4. You can develop directly in the `/admin/app` route for convenience, but don't forget to test it inside the iframe. :)

### Quickstart

1. Clone this repo

2. `yarn --cwd react/` for code completion

3. `vtex link`

4. Navigate to `workspace--account.myvtex.com/admin/app/example`

```


[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 apub-app-center/react/package.json from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.admin-example",
  "version": "0.0.12",
  "devDependencies": {
    "@types/node": "^11.12.1",
    "@types/react": "^16.8.10",
    "@types/react-intl": "^2.3.17",
    "@vtex/tsconfig": "^0.4.4",
    "apollo-client": "^2.4.7",
    "graphql": "^14.0.2",
    "prop-types": "^15.7.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-intl": "^2.7.2",
    "typescript": "3.9.7",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.3/public/@types/vtex.render-runtime",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide",
    "vtexarg.apub-app-center": "https://dev--vtexarg.myvtex.com/_v/private/typings/linked/v1/vtexarg.apub-app-center@0.0.12+build1647284822/public/@types/vtexarg.apub-app-center"
  },
  "dependencies": {
    "@types/faker": "^4.1.5",
    "faker": "^4.1.0"
  }
}

```


[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 apub-app-center/react/AppDetail.tsx from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, useEffect, useState } from 'react'
// import { FormattedMessage } from 'react-intl'
import { Divider, Layout, PageBlock, PageHeader } from 'vtex.styleguide'
import './styles/styles.global.css'
import AppTabs from './components/Detail/AppTabs'
import Resume from './components/Detail/Resume'
import { useRuntime } from 'vtex.render-runtime'
import { AppData } from './typings/appdata'
import MockedData from './data/mockedData'

interface Props {
  params: any
}

const AppDetail: FC<Props> = ({ params }) => {
  const { navigate } = useRuntime();
  const [appInfo, setAppInfo] = useState<AppData>();
  useEffect(() => {
    if (!appInfo){
      let apps = MockedData.GetData();
      let app = apps.filter(ap => ap.id == params.id)[0];
      setAppInfo(app);
    }
  }, [])

  return (
    <Layout
      pageHeader={
        <PageHeader
          title={appInfo?.name}
          linkLabel="APUB App Center"
          onLinkClick={() => {
            navigate({to: `/admin/app/apub-app-center`})
          }}
        />
      }
    >
      <PageBlock variation="full">
        <div className="flex">
          <Resume
            photo={appInfo?.photo || ''}
            version={appInfo?.version || 0}
            status={appInfo?.status || ''}
            store={appInfo?.store || []}
            category={appInfo?.category || []}
            documentation={appInfo?.documentation || ''}
          />
          <div
            style={{ flexGrow: 1 }}
            className="flex items-stretch w-10 justify-center">
            <Divider orientation="vertical" />
          </div>
          <AppTabs
            name={appInfo?.name || ''}
            download={appInfo?.download || ''}
            overview={appInfo?.overview || ''}
            feature={appInfo?.feature || []}
            screenshot= {appInfo?.screenshot || ''}
            workspace= {appInfo?.workspace || ''}
            video= {appInfo?.video || ''}
            requirements={appInfo?.requirements || []}
            tutorial={appInfo?.tutorial || []}
            assets={appInfo?.assets || []}
            github= {appInfo?.github || ''}
          />
        </div>
      </PageBlock>
    </Layout>
  )
}

export default AppDetail

```


[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 apub-app-center/react/Admin.tsx from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { FormattedMessage } from 'react-intl'
import { Layout, PageBlock, PageHeader } from 'vtex.styleguide'

import Cards from './components/Cards'

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

const Admin: FC = () => {
  return (
    <Layout
      pageHeader={
        <PageHeader
          title={<FormattedMessage id="apub-app-center.hello-world" />}
        />
      }
    >
      <PageBlock variation="full">
        <Cards />
      </PageBlock>
    </Layout>
  )
}

export default Admin

```


[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 apub-app-center/react/typings/appdata.d.ts from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface AppData {
  id: number,
  name: string,
  photo: string,
  status: string,
  store: string[],
  category: string[],
  description: string
  documentation: string,
  download: string,
  version: number,
  overview: string,
  feature: string[],
  screenshot: string,
  workspace: string,
  video: string,
  requirements: string[],
  tutorial: string[],
  assets: string[],
  github: string
}

```


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

[User] Show me the implementation of apub-app-center/react/utils/intl.ts from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

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

const prefix = 'admin/admin.app.custom-stock-export.'
const prefixFilters = 'admin/admin.app.custom-stock-export.filters.'

export const appMessages = defineMessages({
  pageTitle: { id: `${prefix}pageTitle` },
  exportButton: { id: `${prefix}exportButton` },
})

export const filtersMessages = defineMessages({
  clear: { id: `${prefixFilters}clear` },
  all: { id: `${prefixFilters}all` },
  none: { id: `${prefixFilters}none` },
  any: { id: `${prefixFilters}any` },
  is: { id: `${prefixFilters}is` },
  isNot: { id: `${prefixFilters}isNot` },
  contains: { id: `${prefixFilters}contains` },
  apply: { id: `${prefixFilters}apply` },
  loading: { id: `${prefixFilters}category.loading` },
  placeholder: { id: `${prefixFilters}category.placeholder` },
  error: { id: `${prefixFilters}category.error` },
  ok: { id: `${prefixFilters}ok` },
  and: { id: `${prefixFilters}and` },
  noOptionsFound: { id: `${prefixFilters}noOptionsFound` },
  tryAgain: { id: `${prefixFilters}tryAgain` },
  greaterOrEqualDropdown: { id: `${prefixFilters}dropdown.greater-or-equal` },
  greaterOrEqualLabel: { id: `${prefixFilters}label.greater-or-equal-value` },
  lessOrEqualDropdown: { id: `${prefixFilters}dropdown.less-or-equal` },
  lessOrEqualLabel: { id: `${prefixFilters}label.less-or-equal-value` },
  betweenDropdown: { id: `${prefixFilters}dropdown.between` },
  betweenLabel: { id: `${prefixFilters}label.between-values` },
  betweenInvalid: { id: `${prefixFilters}error.between` },
  maximumPlaceholder: { id: `${prefixFilters}placeholder.maximum` },
  minimumPlaceholder: { id: `${prefixFilters}placeholder.minimum` },
  search: { id: `${prefixFilters}search` },
  warehousesQueryError: { id: `${prefixFilters}warehouses-query-error` },
  warehousesNamesQueryError: {
    id: `${prefixFilters}warehouses-names-query-error`,
  },
})

export const commonMessages = defineMessages({
  loading: { id: `${prefix}commons.loading` },
})

export const columnsMessages = defineMessages({
  lastUpdate: { id: `${prefix}columns.last-update` },
  reserved: { id: `${prefix}columns.reserved` },
  available: { id: `${prefix}columns.available` },
  warehouse: { id: `${prefix}columns.warehouse` },
})

```


[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 apub-app-center/react/styles/styles.global.css from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```css
html,
body,
#render-container,
#render-provider,
.render-container,
.render-provider {
  height: 100%;
}

.card-hover:hover {
  box-shadow: rgba(61, 62, 64, 0.2) 0px 3px 9px 0px;
}

.image-size{
  width: 3.5rem;
  height: 3.5rem;
}

.image-size-detail {
  width: 10rem;
  height: 10rem;
}

.card-div-name {
  width: calc(100% - 75px);
}

.vtex-tag {
  padding: .1rem .4rem;
}

```


[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 apub-app-center/react/data/mockedData.ts from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AppData } from '../typings/appdata'

const CreateData1 = () => {
  let app1 = {} as AppData;
  app1.id             = 1;
  app1.name           = "Massive price update";
  app1.photo          = "https://extensions.vtexassets.com/arquivos/ids/157174/image-b925968a706f46f29cc8ed06633a6f95.jpg?v=637680032654000000";
  app1.status         = "prod";
  app1.store          = ['legacy', 'io'];
  app1.category       = ['service'];
  app1.description    = "This application allows you to update SKU prices in batches.";
  app1.documentation  = "https://vtex.io/docs/app/vtexarg.massive-price-update@1.x";
  app1.download       = "https://apps.vtex.com/vtexarg-massive-price-update/p";
  app1.version        = 1;
  app1.overview       = "This application allows you to update SKU prices in batches.";
  app1.feature        = ["Reduction of integration times", "Reduction of overhead for applications", "Simplicity"];
  app1.screenshot     = "https://extensions.vtexassets.com/arquivos/ids/157175/image-25412d34ae77460fa7a7b141b7a00dd6.jpg?v=637680032659230000";
  app1.workspace      = "";
  app1.video          = "";
  app1.requirements   = ["Whitelabel", "Search v1", "Installar la app"];
  app1.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app1.assets         = [];
  app1.github         = "https://github.com/vtex-apps/massive-price-update";

  return app1;
}

const CreateData2 = () => {
  let app2 = {} as AppData;
  app2.id             = 2;
  app2.name           = "Product highlight by vtex-segment";
  app2.photo          = "https://dev--vtexarg.myvtex.com/_v/public/assets/v1/published/vtexarg.product-highlight-by-vtex-segment@0.3.2/public/metadata/images/icon.png";
  app2.status         = "beta";
  app2.store          = ['io'];
  app2.category       = ['pdp', 'gallery'];
  app2.description    = "Filters the product listing results by Whitelabel Seller selected.";
  app2.documentation  = "https://vtex.io/docs/app/vtexarg.product-highlight-by-vtex-segment@0.3.2/";
  app2.download       = "";
  app2.version        = 0;
  app2.overview       = "Allows to select a Whitelabel Seller available in their region, and it filters the product listing results.";
  app2.feature        = ["Es genial", "Es gratis", "Es rápida"];
  app2.screenshot     = "https://user-images.githubusercontent.com/55905671/143295939-7d1b5625-5c57-46ad-a197-ef959db268de.gif";
  app2.workspace      = "Link del workspace";
  app2.video          = "Preview del video";
  app2.requirements   = ["Whitelabel", "Search v2", "Installar la app"];
  app2.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app2.assets         = ["Imagenes", "Videos", "Styles"];
  app2.github         = "https://github.com/vtex-apps/product-highlight-by-vtex-segment";

  return app2;
}

const CreateData3 = () => {
  let app3 = {} as AppData;
  app3.id             = 3;
  app3.name           = "Massive score update";
  app3.photo          = "https://dev--vtexarg.myvtex.com/_v/public/assets/v1/published/vtexarg.massive-score-update@3.1.1/public/metadata/images/icon.png";
  app3.status         = "beta";
  app3.store          = ['legacy', 'io'];
  app3.category       = ['service'];
  app3.description    = "Endpoints of massive update of product scores and catalogs.";
  app3.documentation  = "https://vtex.io/docs/app/vtexarg.massive-score-update@3.1.1/";
  app3.download       = "";
  app3.version        = 3;
  app3.overview       = "This application that exposes two endpoints of massive update of product scores and catalogs.";
  app3.feature        = ["Es maravillosa", "Es gratis", "Es rápida"];
  app3.screenshot     = "https://user-images.githubusercontent.com/33711188/132861772-a09ddf58-c326-4d43-bd57-0f4c5157e681.png";
  app3.workspace      = "";
  app3.video          = "";
  app3.requirements   = ["Whitelabel", "Search v3", "Installar la app"];
  app3.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app3.assets         = [];
  app3.github         = "https://github.com/vtex-apps/massive-score-update";

  return app3;
}

const CreateData4 = () => {
  let app4 = {} as AppData;
  app4.id             = 4;
  app4.name           = "Massive stock update";
  app4.photo          = "https://extensions.vtexassets.com/arquivos/ids/157172-800-auto?v=637678529445200000&width=800&height=auto&aspect=true";
  app4.status         = "prod";
  app4.store          = ['legacy', 'io'];
  app4.category       = ['service'];
  app4.description    = "Endpoints of massive update stock in batches to decrease integration times.";
  app4.documentation  = "https://vtex.io/docs/app/vtexarg.massive-stock-update@2.x/";
  app4.download       = "https://apps.vtex.com/vtexarg-massive-stock-update/p";
  app4.version        = 2;
  app4.overview       = "Update stock in batches to decrease integration times and requests overhead.";
  app4.feature        = ["Massive inventory stock update service through SKU and warehouseId"];
  app4.screenshot     = "https://extensions.vtexassets.com/arquivos/ids/157173/image-0b87b2a10d504f7685ecb6f561bd93cf.jpg?v=637678529456730000";
  app4.workspace      = "";
  app4.video          = "";
  app4.requirements   = ["Whitelabel", "Search v4", "Installar la app"];
  app4.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app4.assets         = [];
  app4.github         = "https://github.com/vtex-apps/massive-stock-update";

  return app4;
}

const CreateData5 = () => {
  let app5 = {} as AppData;
  app5.id             = 5;
  app5.name           = "Tintometric";
  app5.photo          = "https://extensions.vtexassets.com/arquivos/ids/157290/image-809fc5c05bdd4e6ab0c6a07ff3106191.jpg?v=637741542193970000";
  app5.status         = "prod";
  app5.store          = ['io'];
  app5.category       = ['admin', 'pdp'];
  app5.description    = "Displays a color chart on your PDP and updates its price by composition.";
  app5.documentation  = "https://vtex.io/docs/app/vtexarg.tintometric@3.x/";
  app5.download       = "https://apps.vtex.com/vtexarg-tintometric/p";
  app5.version        = 3;
  app5.overview       = "Displays a color chart on your product page and promptly update its price based on the color composition.";
  app5.feature        = ["Manage color combination for Color Chart for paint stores", "Bulk update SKU prices based on the product color"];
  app5.screenshot     = "https://extensions.vtexassets.com/arquivos/ids/157294/image-fe602f14a5ee47ca9ad7a763dba6faa0.jpg?v=637741542202600000";
  app5.workspace      = "Link del workspace";
  app5.video          = "Preview del video";
  app5.requirements   = ["Whitelabel", "Search v5", "Installar la app"];
  app5.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app5.assets         = ["Imagenes", "Videos", "Styles"];
  app5.github         = "https://github.com/vtex-apps/tintometric";

  return app5;
}

const CreateData6 = () => {
  let app6 = {} as AppData;
  app6.id             = 6;
  app6.name           = "Pharma orders admin";
  app6.photo          = "";
  app6.status         = "beta";
  app6.store          = ['io'];
  app6.category       = ['admin'];
  app6.description    = "The Pharma Orders Admin app will allow you to manage your Pharma Orders.";
  app6.documentation  = "https://vtex.io/docs/app/vtexarg.pharma-orders-admin@0.1.4/";
  app6.download       = "";
  app6.version        = 0;
  app6.overview       = "The Pharma Orders Admin app will allow you to manage your Pharma Orders.";
  app6.feature        = ["See the orders with products of the category Medicamentos", "See the prescription of the orders", "See products of the orders",  "Aprove, Cancel and confirm the Orders"];
  app6.screenshot     = "https://user-images.githubusercontent.com/55905671/139487826-4fa4dcea-101e-486c-a106-acc8c3abd36a.gif";
  app6.workspace      = "Link del workspace";
  app6.video          = "Preview del video";
  app6.requirements   = ["Whitelabel", "Search v6", "Installar la app"];
  app6.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app6.assets         = ["Imagenes", "Videos", "Styles"];
  app6.github         = "https://github.com/vtex-apps/pharma-orders-admin";

  return app6;
}

const CreateData7 = () => {
  let app7 = {} as AppData;
  app7.id             = 7;
  app7.name           = "Product availability gallery";
  app7.photo          = "";
  app7.status         = "beta";
  app7.store          = ['io'];
  app7.category       = ['pdp', 'gallery', 'search'];
  app7.description    = "The Product Availability component shows the available amount of products. ";
  app7.documentation  = "https://vtex.io/docs/app/vtexarg.product-availability-gallery@1.1.2/";
  app7.download       = "";
  app7.version        = 1;
  app7.overview       = "The Product Availability component shows the available amount of products. ";
  app7.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app7.screenshot     = "https://extensions.vtexassets.com/arquivos/ids/157316/image-5516ab6fdd624a748e299584e917e082.jpg?v=637771685440800000";
  app7.workspace      = "Link del workspace";
  app7.video          = "Preview del video";
  app7.requirements   = ["Whitelabel", "Search v7", "Installar la app"];
  app7.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app7.assets         = ["Imagenes", "Videos", "Styles"];
  app7.github         = "https://github.com/vtex-apps/product-availability-gallery";

  return app7;
}

const CreateData8 = () => {
  let app8 = {} as AppData;
  app8.id             = 8;
  app8.name           = "QR & Barcode Reader";
  app8.photo          = "https://extensions.vtexassets.com/arquivos/ids/157356-800-auto?v=637789878477770000&width=800&height=auto&aspect=true";
  app8.status         = "prod";
  app8.store          = ['io'];
  app8.category       = ['admin', 'pdp'];
  app8.description    = "Permite escanear y leer códigos de barras o QR de productos para dirigirte a su PDP o agregarlos al carrito.";
  app8.documentation  = "https://vtex.io/docs/app/vtexarg.qr-barcode-reader@1.x/";
  app8.download       = "https://apps.vtex.com/vtexarg-qr-barcode-reader/p";
  app8.version        = 1;
  app8.overview       = "Permite escanear y leer códigos QR o código de barras de productos, identificando el EAN del mismo y luego (dependiendo como se configuró la app) tomando una de estas dos acciones:";
  app8.feature        = ["Te dirige a la PDP", "Agrega el producto al carrito"];
  app8.screenshot     = "https://user-images.githubusercontent.com/55905671/141179209-ebbeeb19-7556-4a59-9913-d04b1f6c8f5d.gif";
  app8.workspace      = "https://reader--vtexarg.myvtex.com/";
  app8.video          = "Preview del video";
  app8.requirements   = ["Whitelabel", "Search v8", "Installar la app"];
  app8.tutorial       = ["Instalar la app en la theme dependency", "Agregar los bloques Qr & Barcode Reader a la template del theme", "Probar, leer documentación"];
  app8.assets         = ["Imagenes", "Videos", "Styles"];
  app8.github         = "https://github.com/vtex-apps/qr-barcode-reader";

  return app8;
}

const CreateData9 = () => {
  let app9 = {} as AppData;
  app9.id             = 9;
  app9.name           = "Workspace manager";
  app9.photo          = "https://extensions.vtexassets.com/arquivos/ids/157197/image-4974b0ca908a447bb3bbb55156f2496b.jpg?v=637693051293400000";
  app9.status         = "prod";
  app9.store          = ['io'];
  app9.category       = ['admin'];
  app9.description    = "Through this App you can create, delete and promote workspaces from the admin.";
  app9.documentation  = "https://vtex.io/docs/app/vtexarg.workspace-manager@2.x/";
  app9.download       = "https://apps.vtex.com/vtexarg-workspace-manager/p";
  app9.version        = 2;
  app9.overview       = "Workspace Manger app enables the possibility to manage both production and development workspaces without the need of using Toolbelt CLI.";
  app9.feature        = ["Create Workspaces", "Delete Workspaces"];
  app9.screenshot     = "https://user-images.githubusercontent.com/36748003/130658171-88416ced-a160-4677-bdd8-31a0968ad3bd.png";
  app9.workspace      = "Link del workspace";
  app9.video          = "Preview del video";
  app9.requirements   = ["Whitelabel", "Search v9", "Installar la app"];
  app9.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app9.assets         = ["Imagenes", "Videos", "Styles"];
  app9.github         = "https://github.com/vtex-apps/workspace-manager";

  return app9;
}

const CreateData10 = () => {
  let app10 = {} as AppData;
  app10.id             = 10;
  app10.name           = "Admin AB Tester";
  app10.photo          = "https://dev--vtexarg.myvtex.com/_v/public/assets/v1/published/vtexarg.abtester@1.0.4/public/metadata/images/icon.png";
  app10.status         = "beta";
  app10.store          = ['io'];
  app10.category       = ['admin'];
  app10.description    = "Allows you to list, start and finish A/B tests via the VTEX Admin.";
  app10.documentation  = "https://vtex.io/docs/app/vtexarg.abtester@1.0.7/";
  app10.download       = "";
  app10.version        = 1;
  app10.overview       = "Allows you to list, start and finish A/B tests via the VTEX Admin.";
  app10.feature        = ["Create A/B tests", "Compare A/B test results", "Finish an A/B test"];
  app10.screenshot     = "https://user-images.githubusercontent.com/36748003/138300964-63bc3545-efd7-435c-acd6-3e3e3a35b285.png";
  app10.workspace      = "Link del workspace";
  app10.video          = "Preview del video";
  app10.requirements   = ["Whitelabel", "Search v10", "Installar la app"];
  app10.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app10.assets         = ["Imagenes", "Videos", "Styles"];
  app10.github         = "https://github.com/vtex-apps/admin-ab-tester";

  return app10;
}

const CreateData11 = () => {
  let app11 = {} as AppData;
  app11.id             = 11;
  app11.name           = "Admin Ley de Gondolas Arg";
  app11.photo          = "";
  app11.status         = "beta";
  app11.store          = ['legacy', 'io'];
  app11.category       = ['admin', 'pdp'];
  app11.description    = "Integration to comply with the Ley de Gondolas from Argentina.";
  app11.documentation  = "https://vtex.io/docs/app/vtexarg.admin-ley-gondolas-arg@0.2.0/";
  app11.download       = "";
  app11.version        = 0;
  app11.overview       = "Helps to carry out the necessary integration to comply with the Ley de Gondolas from Argentina.";
  app11.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app11.screenshot     = "https://user-images.githubusercontent.com/55905671/137961451-a9f0f85e-a3d9-43ab-af9e-84e5be9f6346.gif";
  app11.workspace      = "Link del workspace";
  app11.video          = "Preview del video";
  app11.requirements   = ["Whitelabel", "Search v11", "Installar la app"];
  app11.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app11.assets         = ["Imagenes", "Videos", "Styles"];
  app11.github         = "https://github.com/vtex-apps/admin-ley-gondolas-arg";

  return app11;
}

const CreateData12 = () => {
  let app12 = {} as AppData;
  app12.id             = 12;
  app12.name           = "Vtex-logger react";
  app12.photo          = "";
  app12.status         = "prod";
  app12.store          = ['io'];
  app12.category       = ['service'];
  app12.description    = "Makes log entries in the VTEX IO Logging Service on React Apps.";
  app12.documentation  = "https://vtex.io/docs/app/vtex.vtex-logger-react@1.0.1/";
  app12.download       = "";
  app12.version        = 1;
  app12.overview       = "The VTEX Logger React app will help to make log entries in the VTEX IO Logging Service on React Apps.";
  app12.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app12.screenshot     = "https://extensions.vtexassets.com/arquivos/ids/157316/image-5516ab6fdd624a748e299584e917e082.jpg?v=637771685440800000";
  app12.workspace      = "";
  app12.video          = "";
  app12.requirements   = ["Whitelabel", "Search v12", "Installar la app"];
  app12.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app12.assets         = [];
  app12.github         = "https://github.com/vtex-apps/vtex-logger-react";

  return app12;
}

const CreateData13 = () => {
  let app13 = {} as AppData;
  app13.id             = 13;
  app13.name           = "Speech to Text";
  app13.photo          = "https://extensions.vtexassets.com/arquivos/ids/157152/image-a7036d42ff77459ebe575750e2d40871.jpg?v=637644004825600000";
  app13.status         = "prod";
  app13.store          = ['io'];
  app13.category       = ['search'];
  app13.description    = "Speech search using google chrome NLP system.";
  app13.documentation  = "https://developers.vtex.com/vtex-developer-docs/docs/vtexarg-speech-to-text";
  app13.download       = "https://apps.vtex.com/vtexarg-speech-to-text/p";
  app13.version        = 1;
  app13.overview       = "Boost your commerce offering quick speech search using google chrome NLP system.";
  app13.feature        = ["Search in Google Chrome using microphone"];
  app13.screenshot     = "https://extensions.vtexassets.com/arquivos/ids/157150/image-06311a72e32149ac8ece2702f72e3712.jpg?v=637644004824800000";
  app13.workspace      = "https://speechtotext--vtexarg.myvtex.com/";
  app13.video          = "Preview del video";
  app13.requirements   = ["Whitelabel", "Search v13", "Installar la app"];
  app13.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app13.assets         = [];
  app13.github         = "https://github.com/vtex-apps/speech-to-text";

  return app13;
}

const CreateData14 = () => {
  let app14 = {} as AppData;
  app14.id             = 14;
  app14.name           = "Quantity on cart";
  app14.photo          = "https://extensions.vtexassets.com/arquivos/ids/157188/image-ec75452634b8492f9bd015432f654e42.jpg?v=637692310251370000";
  app14.status         = "prod";
  app14.store          = ['io'];
  app14.category       = ['checkout'];
  app14.description    = "See the quantity of products added to the cart.";
  app14.documentation  = "https://vtex.io/docs/app/vtexarg.quantity-on-cart@2.2.7/";
  app14.download       = "https://apps.vtex.com/vtexarg-quantity-on-cart/p";
  app14.version        = 2;
  app14.overview       = "With Quantity On Cart, your users can now see the quantity of products they have added to their cart directly, creating a more seamless and dynamic experience for buyers.";
  app14.feature        = ["Same-page display of products added to the cart", "Integration with minicart for live count"];
  app14.screenshot     = "https://extensions.vtexassets.com/arquivos/ids/157192/image-4e978d9142484c98a028c256e79d719e.jpg?v=637692310276130000";
  app14.workspace      = "Link del workspace";
  app14.video          = "Preview del video";
  app14.requirements   = ["Whitelabel", "Search v14", "Installar la app"];
  app14.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app14.assets         = ["Imagenes", "Videos", "Styles"];
  app14.github         = "https://github.com/vtex-apps/quantity-on-cart";

  return app14;
}

const CreateData15 = () => {
  let app15 = {} as AppData;
  app15.id             = 15;
  app15.name           = "Vtex-logger GraphQL";
  app15.photo          = "";
  app15.status         = "prod";
  app15.store          = ['io'];
  app15.category       = ['service'];
  app15.description    = "GraphQL API to use the VTEX Logger to be consume through an React Front App.";
  app15.documentation  = "https://vtex.io/docs/app/vtex.vtex-logger-graphql@0.2.0/";
  app15.download       = "";
  app15.version        = 0;
  app15.overview       = "GraphQL API build in our VTEX IO Platform to use the VTEX Logger with the objetive of be consume through an React Front App.";
  app15.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app15.screenshot     = "";
  app15.workspace      = "";
  app15.video          = "";
  app15.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app15.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app15.assets         = [];
  app15.github         = "https://github.com/vtex-apps/vtex-logger-graphql";

  return app15;
}

const CreateData16 = () => {
  let app16 = {} as AppData;
  app16.id             = 16;
  app16.name           = "Apub App Center";
  app16.photo          = "";
  app16.status         = "beta";
  app16.store          = ['legacy', 'io'];
  app16.category       = ['admin'];
  app16.description    = "Permite escanear y leer códigos de barras o QR de productos para dirigirte a su PDP o agregarlos al carrito.";
  app16.documentation  = "https://vtex.io/docs/app/vtex.vtex-logger-graphql@0.2.0/";
  app16.download       = "";
  app16.version        = 0;
  app16.overview       = "Permite escanear y leer códigos de barras o QR de productos para dirigirte a su PDP o agregarlos al carrito.";
  app16.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app16.screenshot     = "https://extensions.vtexassets.com/arquivos/ids/157316/image-5516ab6fdd624a748e299584e917e082.jpg?v=637771685440800000";
  app16.workspace      = "Link del workspace";
  app16.video          = "Preview del video";
  app16.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app16.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app16.assets         = ["Imagenes", "Videos", "Styles"];
  app16.github         = "https://github.com/vtex-apps/apub-app-center";

  return app16;
}

const CreateData17 = () => {
  let app17 = {} as AppData;
  app17.id             = 17;
  app17.name           = "Proxy Mailing Flow";
  app17.photo          = "";
  app17.status         = "beta";
  app17.store          = ['legacy', 'io'];
  app17.category       = ['service'];
  app17.description    = "Cancels and generates orders without repeating notifications to the user.";
  app17.documentation  = "https://vtex.io/docs/app/vtexarg.proxy-mailing-flow@3.0.8/";
  app17.download       = "";
  app17.version        = 3;
  app17.overview       = "This service allows you to handling the mailing to be able to cancel and generate orders without repeating notifications to the user.";
  app17.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app17.screenshot     = "";
  app17.workspace      = "";
  app17.video          = "";
  app17.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app17.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app17.assets         = [];
  app17.github         = "https://github.com/vtex-apps/proxy-mailing-flow";

  return app17;
}

const CreateData18 = () => {
  let app18 = {} as AppData;
  app18.id             = 18;
  app18.name           = "Pedidos Ya";
  app18.photo          = "";
  app18.status         = "beta";
  app18.store          = ['legacy', 'io'];
  app18.category       = ['admin'];
  app18.description    = "Allows you to process orders generated on PedidosYa into VTEX OMS.";
  app18.documentation  = "https://vtex.io/docs/app/vtexarg.pedidosya@2.0.29/";
  app18.download       = "";
  app18.version        = 2;
  app18.overview       = "The PedidosYa application allows you to process orders generated on PedidosYa into VTEX OMS.";
  app18.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app18.screenshot     = "https://camo.githubusercontent.com/128a139d981ed607c3797dad67dc85919984a343fe27060c569327b0e022f3a5/68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f764a5641744b545556634d71546d557a43642f67697068792e676966";
  app18.workspace      = "Link del workspace";
  app18.video          = "Preview del video";
  app18.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app18.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app18.assets         = ["Imagenes", "Videos", "Styles"];
  app18.github         = "https://github.com/vtex-apps/pedidosya";

  return app18;
}

const CreateData19 = () => {
  let app19 = {} as AppData;
  app19.id             = 19;
  app19.name           = "Promotions Cloner";
  app19.photo          = "";
  app19.status         = "beta";
  app19.store          = ['legacy', 'io'];
  app19.category       = ['admin'];
  app19.description    = "Clones promotions from one environment to another master + white labels and MKP.";
  app19.documentation  = "https://vtex.io/docs/app/vtexarg.promotion-cloner@4.0.5/";
  app19.download       = "";
  app19.version        = 4;
  app19.overview       = "Allows to clone promotions from one environment to another, in architecture master + white labels and marketplaces.";
  app19.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app19.screenshot     = "https://user-images.githubusercontent.com/55905671/139843737-795688e9-c44a-45c5-a32d-3714d2eab2e2.gif";
  app19.workspace      = "Link del workspace";
  app19.video          = "Preview del video";
  app19.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app19.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app19.assets         = ["Imagenes", "Videos", "Styles"];
  app19.github         = "https://github.com/vtex-apps/promotion-cloner";

  return app19;
}

const CreateData20 = () => {
  let app20 = {} as AppData;
  app20.id             = 20;
  app20.name           = "Catalog Analyzer";
  app20.photo          = "";
  app20.status         = "beta";
  app20.store          = ['legacy', 'io'];
  app20.category       = ['admin'];
  app20.description    = "Allows to obtain information about the products of any white label seller.";
  app20.documentation  = "https://vtex.io/docs/app/vtexarg.catalog-analyzer@2.0.1/";
  app20.download       = "";
  app20.version        = 2;
  app20.overview       = "Allows you to obtain information about the products of any white label seller. The information will be the name, price, stock and images of the products.";
  app20.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app20.screenshot     = "https://user-images.githubusercontent.com/33711188/134082316-a2ca8998-86fc-4388-9f6e-27a1e62621e8.png";
  app20.workspace      = "Link del workspace";
  app20.video          = "Preview del video";
  app20.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app20.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app20.assets         = ["Imagenes", "Videos", "Styles"];
  app20.github         = "https://github.com/vtex-apps/catalog-analyzer";

  return app20;
}

const CreateData21 = () => {
  let app21 = {} as AppData;
  app21.id             = 21;
  app21.name           = "File Manager REST";
  app21.photo          = "";
  app21.status         = "beta";
  app21.store          = ['legacy', 'io'];
  app21.category       = ['service'];
  app21.description    = "This app provides a public POST endpoint using file-manager.";
  app21.documentation  = "https://vtex.io/docs/app/vtexarg.file-manager-rest@1.0.2/";
  app21.download       = "";
  app21.version        = 1;
  app21.overview       = "This app provides a public POST endpoint using file-manager.";
  app21.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app21.screenshot     = "";
  app21.workspace      = "";
  app21.video          = "";
  app21.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app21.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app21.assets         = [];
  app21.github         = "https://github.com/vtex-apps/file-manager-rest";

  return app21;
}

const CreateData22 = () => {
  let app22 = {} as AppData;
  app22.id             = 22;
  app22.name           = "Custom Stock Export";
  app22.photo          = "";
  app22.status         = "beta";
  app22.store          = ['legacy', 'io'];
  app22.category       = ['service'];
  app22.description    = "This app provides a public POST endpoint using file-manager.";
  app22.documentation  = "https://vtex.io/docs/app/vtexarg.file-manager-rest@1.0.2/";
  app22.download       = "";
  app22.version        = 1;
  app22.overview       = "This app provides a public POST endpoint using file-manager.";
  app22.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app22.screenshot     = "";
  app22.workspace      = "";
  app22.video          = "";
  app22.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app22.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app22.assets         = [""];
  app22.github         = "https://github.com/vtex-apps/custom-stock-export";

  return app22;
}

const CreateData23 = () => {
  let app23 = {} as AppData;
  app23.id             = 23;
  app23.name           = "MasterData v2 Manager";
  app23.photo          = "";
  app23.status         = "beta";
  app23.store          = ['legacy', 'io'];
  app23.category       = ['admin'];
  app23.description    = "Component with interface to use Master Data V2.";
  app23.documentation  = "https://vtex.io/docs/app/vtexarg.file-manager-rest@1.0.2/";
  app23.download       = "";
  app23.version        = 1;
  app23.overview       = "Component with interface to use Master Data V2.";
  app23.feature        = ["Es excelente", "Es gratis", "Es rápida"];
  app23.screenshot     = "https://user-images.githubusercontent.com/33711188/134082316-a2ca8998-86fc-4388-9f6e-27a1e62621e8.png";
  app23.workspace      = "Link del workspace";
  app23.video          = "Preview del video";
  app23.requirements   = ["Whitelabel", "Search v15", "Installar la app"];
  app23.tutorial       = ["Instalar", "Descargar", "Probar, leer documentación"];
  app23.assets         = ["Imagenes", "Videos", "Styles"];
  app23.github         = "https://github.com/vtex-apps/Master-Data-Manager";

  return app23;
}

const MockedData = {
  GetData: () => {
    let mockedDataList: AppData[] = [];

    let app1 = CreateData1();
    let app2 = CreateData2();
    let app3 = CreateData3();
    let app4 = CreateData4();
    let app5 = CreateData5();
    let app6 = CreateData6();
    let app7 = CreateData7();
    let app8 = CreateData8();
    let app9 = CreateData9();
    let app10 = CreateData10();
    let app11 = CreateData11();
    let app12 = CreateData12();
    let app13 = CreateData13();
    let app14 = CreateData14();
    let app15 = CreateData15();
    let app16 = CreateData16();
    let app17 = CreateData17();
    let app18 = CreateData18();
    let app19 = CreateData19();
    let app20 = CreateData20();
    let app21 = CreateData21();
    let app22 = CreateData22();
    let app23 = CreateData23();

    mockedDataList.push(app1);
    mockedDataList.push(app2);
    mockedDataList.push(app3);
    mockedDataList.push(app4);
    mockedDataList.push(app5);
    mockedDataList.push(app6);
    mockedDataList.push(app7);
    mockedDataList.push(app8);
    mockedDataList.push(app9);
    mockedDataList.push(app10);
    mockedDataList.push(app11);
    mockedDataList.push(app12);
    mockedDataList.push(app13);
    mockedDataList.push(app14);
    mockedDataList.push(app15);
    mockedDataList.push(app16);
    mockedDataList.push(app17);
    mockedDataList.push(app18);
    mockedDataList.push(app19);
    mockedDataList.push(app20);
    mockedDataList.push(app21);
    mockedDataList.push(app22);
    mockedDataList.push(app23);

    return mockedDataList;
  }

}

export default MockedData

```


[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 apub-app-center/react/components/Cards/index.tsx from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, useEffect } from 'react'
import { FilterBar, Checkbox } from 'vtex.styleguide'

import { AppData } from '../../typings/appdata'
import AppCard from './AppCard'
import Header from './Header'
import MockedData from '../../data/mockedData'
import LoadingSpinner from './LoadingSpinner'

interface CardFilter {
  category: string[]
  store: string[]
  status: string[]
}

let auxCardList: any[] = []
const cardFilter: CardFilter = { category: [], store: [], status: [] }

const SetCardData = (setCardsList: any, setLoadingState: any) => {
  auxCardList = MockedData.GetData()
  setCardsList(auxCardList)
  setLoadingState(false)
}

const FilterCardData = (statementList: any) => {
  let cardsId: any[] = []
  FillCardFilter(statementList)
  let listToFilter = auxCardList

  if (cardFilter.category.length > 0) {
    listToFilter.forEach((c: AppData) => {
      cardFilter.category.forEach((cat) => {
        const hasCategory = c.category.includes(cat)
        if (hasCategory) {
          if (!cardsId.includes(c.id)) {
            cardsId.push(c.id)
          }
        }
      })
    })
  }

  if (cardsId.length > 0) {
    listToFilter = listToFilter.filter((c) => cardsId.includes(c.id))

    cardsId = []
  }

  if (cardFilter.store.length > 0) {
    listToFilter.forEach((c: AppData) => {
      cardFilter.store.forEach((st) => {
        const hasStore = c.store.includes(st)
        if (hasStore) {
          if (!cardsId.includes(c.id)) {
            cardsId.push(c.id)
          }
        }
      })
    })
  }

  if (cardsId.length > 0) {
    listToFilter = listToFilter.filter((c) => cardsId.includes(c.id))

    cardsId = []
  }

  if (cardFilter.status.length > 0) {
    listToFilter.forEach((c: AppData) => {
      cardFilter.status.forEach((sta) => {
        const hasStatus = c.status.includes(sta)
        if (hasStatus) {
          if (!cardsId.includes(c.id)) {
            cardsId.push(c.id)
          }
        }
      })
    })
  }

  if (cardsId.length > 0) {
    listToFilter = listToFilter.filter((c) => cardsId.includes(c.id))

    cardsId = []
  }

  return listToFilter
}

const FillCardFilter = (statementList: any[]) => {
  statementList.forEach((s) => {
    const filteredStatements = GetSelectedStatements(s.object)
    const statements = ConvertStatementsToStringArray(s.object)

    const isCategory = IsCategoryFilter(statements)
    const isStore = IsStoreFilter(statements)
    const isStatus = IsStatusFilter(statements)

    if (isCategory) {
      cardFilter.category = filteredStatements
    }

    if (isStore) {
      cardFilter.store = filteredStatements
    }

    if (isStatus) {
      cardFilter.status = filteredStatements
    }
  })
}

const IsCategoryFilter = (statementList: string[]) => {
  return statementList.includes('pdp')
}

const IsStoreFilter = (statementList: string[]) => {
  return statementList.includes('legacy')
}

const IsStatusFilter = (statementList: string[]) => {
  return statementList.includes('prod')
}

const ConvertStatementsToStringArray = (statements: any) => {
  const keys = Object.keys(statements)

  return keys
}

const GetSelectedStatements = (statements: any) => {
  const keys = Object.keys(statements)
  const values = Object.values(statements)
  const filteredStatements: string[] = []

  for (let i = 0; i < keys.length; i++) {
    if (values[i] == true) {
      filteredStatements.push(keys[i])
    }
  }

  return filteredStatements
}

const CategorySelectorObject = ({ value, onChange }: any) => {
  const initialValue = {
    pdp: true,
    admin: true,
    search: true,
    service: true,
    gallery: true,
    checkout: true,
    ...(value || {}),
  }
  const toggleValueByKey = (key: any) => {
    const newValues = {
      ...(value || initialValue),
      [key]: value ? !value[key] : false,
    }
    return newValues
  }
  return (
    <div>
      {Object.keys(initialValue).map((opt, index) => {
        return (
          <div className="mb3" key={`class-statment-object-${opt}-${index}`}>
            <Checkbox
              checked={value ? value[opt] : initialValue[opt]}
              id={`class-${opt}`}
              label={opt}
              name="class-checkbox-group"
              onChange={() => {
                const newValue = toggleValueByKey(`${opt}`)
                onChange(newValue)
              }}
              value={opt}
            />
          </div>
        )
      })}
    </div>
  )
}

const StoreSelectorObject = ({ value, onChange }: any) => {
  const initialValue = {
    legacy: true,
    io: true,
    ...(value || {}),
  }
  const toggleValueByKey = (key: any) => {
    const newValues = {
      ...(value || initialValue),
      [key]: value ? !value[key] : false,
    }
    return newValues
  }
  return (
    <div>
      {Object.keys(initialValue).map((opt, index) => {
        return (
          <div className="mb3" key={`class-statment-object-${opt}-${index}`}>
            <Checkbox
              checked={value ? value[opt] : initialValue[opt]}
              id={`class-${opt}`}
              label={opt}
              name="class-checkbox-group"
              onChange={() => {
                const newValue = toggleValueByKey(`${opt}`)
                onChange(newValue)
              }}
              value={opt}
            />
          </div>
        )
      })}
    </div>
  )
}

const StatusSelectorObject = ({ value, onChange }: any) => {
  const initialValue = {
    prod: true,
    beta: true,
    ...(value || {}),
  }
  const toggleValueByKey = (key: any) => {
    const newValues = {
      ...(value || initialValue),
      [key]: value ? !value[key] : false,
    }
    return newValues
  }
  return (
    <div>
      {Object.keys(initialValue).map((opt, index) => {
        return (
          <div className="mb3" key={`class-statment-object-${opt}-${index}`}>
            <Checkbox
              checked={value ? value[opt] : initialValue[opt]}
              id={`class-${opt}`}
              label={opt}
              name="class-checkbox-group"
              onChange={() => {
                const newValue = toggleValueByKey(`${opt}`)
                onChange(newValue)
              }}
              value={opt}
            />
          </div>
        )
      })}
    </div>
  )
}

const RenderCardList = (filterCardsList: any[]) => {
  return (
    filterCardsList && (
      <div className="flex flex-wrap">
        {}
        {filterCardsList.map((card) => {
          return (
            <AppCard
              key={card.id}
              id={card.id}
              name={card.name}
              photo={card.photo}
              status={card.status}
              store={card.store}
              category={card.category}
              description={card.description}
              documentation={card.documentation}
              download={card.download}
              version={card.version}
              overview={card.overview}
              feature={card.feature}
              screenshot={card.screenshot}
              workspace={card.workspace}
              video={card.video}
              requirements={card.requirements}
              tutorial={card.tutorial}
              assets={card.assets}
              github={card.github}
            />
          )
        })}
      </div>
    )
  )
}

const LoadingCards = () => {
  return <LoadingSpinner />
}

export default function Cards() {
  const [cardsList, setCardsList] = useState<AppData[]>([])
  useEffect(() => {
    if (cardsList.length === 0) {
      SetCardData(setCardsList, setLoadingState)
    }
  }, [])

  const [statementsList, setStatementsList] = useState([])
  useEffect(() => {
    if (statementsList) {
      const list = FilterCardData(statementsList)
      setFilterCardsList(list)
    }

    if (cardsList.length > 0 && !statementsList) {
      setFilterCardsList(cardsList)
    } else {

      if(statementsList && statementsList.length == 0){
        setFilterCardsList(cardsList)
      }
    }
  }, [statementsList, cardsList])

  const [isLoading, setLoadingState] = useState<boolean>(true)

  const [filterCardsList, setFilterCardsList] = useState<AppData[]>()

  const RenderCards = isLoading
    ? LoadingCards()
    : RenderCardList(filterCardsList ?? [])

  return (
    <>
      <Header text="Filtros" />
      <div className="ma3 pa3 mb2 ba br2 b--muted-4">
        <FilterBar
          alwaysVisibleFilters={['categoria', 'tienda', 'status']}
          statements={statementsList}
          onChangeStatements={(statement: any) => setStatementsList(statement)}
          clearAllFiltersButtonLabel="Limpiar filtros"
          options={{
            categoria: {
              label: 'Categoria',
              renderFilterLabel: (st: any) => {
                if (!st || !st.object) {
                  // you should treat empty object cases only for alwaysVisibleFilters
                  return 'Todas'
                }
                const keys = st.object ? Object.keys(st.object) : []
                const isAllTrue = !keys.some((key) => !st.object[key])
                const isAllFalse = !keys.some((key) => st.object[key])
                const trueKeys = keys.filter((key) => st.object[key])
                let trueKeysLabel = ''
                trueKeys.forEach((key, index) => {
                  trueKeysLabel += `${key}${
                    index === trueKeys.length - 1 ? '' : ', '
                  }`
                })
                return `${
                  isAllTrue
                    ? 'Todas'
                    : isAllFalse
                    ? 'Ninguna'
                    : `${trueKeysLabel}`
                }`
              },
              verbs: [
                {
                  value: 'includes',
                  object: (props: any) => <CategorySelectorObject {...props} />,
                },
              ],
            },

            tienda: {
              label: 'Tienda',
              renderFilterLabel: (st: any) => {
                if (!st || !st.object) {
                  // you should treat empty object cases only for alwaysVisibleFilters
                  return 'Todas'
                }
                const keys = st.object ? Object.keys(st.object) : []
                const isAllTrue = !keys.some((key) => !st.object[key])
                const isAllFalse = !keys.some((key) => st.object[key])
                const trueKeys = keys.filter((key) => st.object[key])
                let trueKeysLabel = ''
                trueKeys.forEach((key, index) => {
                  trueKeysLabel += `${key}${
                    index === trueKeys.length - 1 ? '' : ', '
                  }`
                })
                return `${
                  isAllTrue
                    ? 'Todas'
                    : isAllFalse
                    ? 'Ninguna'
                    : `${trueKeysLabel}`
                }`
              },
              verbs: [
                {
                  value: 'includes',
                  object: (props: any) => <StoreSelectorObject {...props} />,
                },
              ],
            },

            status: {
              label: 'Status',
              renderFilterLabel: (st: any) => {
                if (!st || !st.object) {
                  // you should treat empty object cases only for alwaysVisibleFilters
                  return 'Todas'
                }
                const keys = st.object ? Object.keys(st.object) : []
                const isAllTrue = !keys.some((key) => !st.object[key])
                const isAllFalse = !keys.some((key) => st.object[key])
                const trueKeys = keys.filter((key) => st.object[key])
                let trueKeysLabel = ''
                trueKeys.forEach((key, index) => {
                  trueKeysLabel += `${key}${
                    index === trueKeys.length - 1 ? '' : ', '
                  }`
                })
                return `${
                  isAllTrue
                    ? 'Todas'
                    : isAllFalse
                    ? 'Ninguna'
                    : `${trueKeysLabel}`
                }`
              },
              verbs: [
                {
                  value: 'includes',
                  object: (props: any) => <StatusSelectorObject {...props} />,
                },
              ],
            },
          }}
        />
      </div>
      <Header text="Todas las Apps" />
      {RenderCards}
    </>
  )
}

```


[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 apub-app-center/react/components/Cards/AppCard.tsx from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, {useState} from 'react'
import { Button, Tag } from 'vtex.styleguide'
import CardDiv from './CardDiv';
import { AppData } from '../../typings/appdata'

const GetStatusTagByColor = (appStatus: string) => {
  switch(appStatus) {
    case 'prod':
      return (
        <Tag size="small" type="success">
          {appStatus}
        </Tag>
    );
    case 'beta':
      return (
        <Tag size="small" type="warning">
          {appStatus}
        </Tag>
    );
    default:
      return (
        <Tag size="small" variation="low">
          {appStatus}
        </Tag>
    );
  }
}

const RenderStatusTag = (appStatus: string) => {
  return(
    <div className="mb2">
      <span className="mr2">
        {GetStatusTagByColor(appStatus)}
      </span>
    </div>
  )
}

const RenderStoreTags = (appStatus: string, stores: string[]) => {
  return(
    <div className="mb2">
      {stores.map((store, i) => {
        if (appStatus == "idea"){
          return(
            <span key={"span-" + store + i} className="mr2 mb2">
              <Tag size="small" variation="low">
                {store}
              </Tag>
            </span>
          )
        } else {
          return(
            <span key={"span-" + store + i} className="mr2 mb2">
              <Tag size="small">
                {store}
              </Tag>
            </span>
          )
        }
      })}
    </div>
  );
}

const RenderCategoryTags = (appStatus: string, categories: string []) => {
  return(
    <div className="mb2">
      {categories.map((category, i) =>{
        if (appStatus == "idea"){
          return(
            <span key={"span-" + category + i} className="mr2 mb2">
              <Tag size="small" variation="low">
                {category}
              </Tag>
            </span>
          )
        } else {
          return(
            <span key={"span-" + category + i} className="mr2 mb2">
              <Tag size="small" bgColor="#134CD8">
                {category}
              </Tag>
            </span>
          )
        }
      })}
    </div>
  )
}

const RenderPhoto = (appPhoto: string) => {
  const svg =
    <svg width="14" height="14" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M5 24L9 17L13 20L19 13L25 24H5Z" stroke="#979899" stroke-width="2" stroke-miterlimit="10" stroke-linecap="square">
      </path>
      <path d="M11.5 12C12.8807 12 14 10.8807 14 9.5C14 8.11929 12.8807 7 11.5 7C10.1193 7 9 8.11929 9 9.5C9 10.8807 10.1193 12 11.5 12Z" stroke="#979899" stroke-width="2" stroke-miterlimit="10" stroke-linecap="square"></path><path d="M29 1H1V29H29V1Z" stroke="#979899" stroke-width="2" stroke-miterlimit="10" stroke-linecap="square">
      </path>
    </svg>;

  return(
    <>
    {
      appPhoto == null || appPhoto == '' ?
      (
        <div className="image-size mw-100 mh-100 bg-muted-5 flex justify-center items-center mr4">
          {svg}
        </div>
      )
      :
      (
        <div className="image-size mr4">
          <img
            className='mw-100 mh-100'
            src={appPhoto}
            alt="AppIcon"
          />
        </div>
      )
    }
    </>
  )
}

const AppCard = (props: AppData) => {
  const [loadingState, setLoadingState]= useState<boolean>(false);
  return (
    <div className='w-100 w-third-ns pa3' key={"card-" + props.id}>
      <CardDiv>
        <div className='flex flex-wrap items-center mv5'>
          {RenderStatusTag(props.status)}
          {RenderStoreTags(props.status, props.store)}
          {RenderCategoryTags(props.status, props.category)}
        </div>
        <div className='flex flex-column'>
          <a className="link" href={`/admin/app/apub-app-center/${props.id}`}>
            <div className='flex'>
                {RenderPhoto(props.photo)}
                <div className="card-div-name mr4">
                  <h3 className='f5 fw5 near-black mv3'>
                    {props.name}
                  </h3>
                </div>
            </div>
          </a>
          <div>
            <p className='f7 c-muted-2'>
              {props.description}
            </p>
          </div>
          <div className='flex justify-end items-center'>
            <Button
            variation="secondary"
            size="small"
            href={`/admin/app/apub-app-center/${props.id}`}
            onClick={() => setLoadingState(!loadingState)}
            isLoading={loadingState}
            >
              Ver más
            </Button>
          </div>
        </div>
      </CardDiv>
    </div>
  )
}

export default AppCard

```


[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 apub-app-center/react/components/Cards/LoadingSpinner.tsx from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

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

export default function LoadingSpinner() {
  return (
    <div className="loading-container">
      <Spinner />
    </div>
  )
}

```


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

[User] Show me the implementation of apub-app-center/react/components/Cards/Header.tsx from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Header {
  text: string
}

const Header = (props: Header) => {
  return (
    <div className="w-100 flex flex-wrap bg-white pa3 mb2">
      <div className="flex flex-column w-100">
        <div className="flex justify-between w-100 items-center">
          <span className="f4 fw5">{props.text}</span>
        </div>
      </div>
    </div>
  )
}

export default 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 apub-app-center/react/components/Cards/CardDiv.tsx from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

const CardDiv = (props: any) => {
  return (
    <div className='w-100 flex flex-wrap bg-white ba br2 b--muted-4 pa6 card-hover pa6 h-100 flex flex-column justify-between'>
      {props.children}
    </div>
  )
}

export default CardDiv

```


[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 apub-app-center/react/components/Detail/Resume.tsx from the apub-app-center VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from "react";
import { Link } from 'vtex.styleguide'

const RenderPhoto = (appPhoto: string) => {
  const svg =
    <svg width="14" height="14" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M5 24L9 17L13 20L19 13L25 24H5Z" stroke="#979899" stroke-width="2" stroke-miterlimit="10" stroke-linecap="square">
      </path>
      <path d="M11.5 12C12.8807 12 14 10.8807 14 9.5C14 8.11929 12.8807 7 11.5 7C10.1193 7 9 8.11929 9 9.5C9 10.8807 10.1193 12 11.5 12Z" stroke="#979899" stroke-width="2" stroke-miterlimit="10" stroke-linecap="square"></path><path d="M29 1H1V29H29V1Z" stroke="#979899" stroke-width="2" stroke-miterlimit="10" stroke-linecap="square">
      </path>
    </svg>;

  return(
    <>
      {
        appPhoto == null || appPhoto == '' ?
        (
          <div className="mw-100 mh-100 image-size-detail bg-muted-5 flex justify-center items-center">
            {svg}
          </div>
        )
        :
        (
          <img src={appPhoto} alt="App detail photo" />
        )
      }
    </>
  )
}

const Resume = (props: { photo: string, version: number, status: string, store: string[], category: string[], documentation: string}) => {
  return(
    <div className="w-20">
      <div className="mb7">
        {RenderPhoto(props.photo)}
      </div>
      <h6 className="f6 near-black mv3">Última versión</h6>
      <p className="f6 gray mt0 mb7">
      {props.version}.x
      </p>
      <h6 className="f6 near-black mv3">Status</h6>
      <p className="f6 gray mt0 mb7">
        {props.status}
      </p>
      <h6 className="f6 near-black mv3">Tiendas</h6>
      <p className="f6 gray mt0 mb7">
        {props.store.join(', ')}
      </p>
      <h6 className="f6 near-black mv3">Categorias</h6>
      <p className="f6 gray mt0 mb7">
        {props.category.join(', ')}
      </p>
      <h6 className="f6 near-black mv3">Soporte</h6>
      <p>
        <Link
          href={props.documentation}
          target="_blank"
          mediumWeigth>
          Documentación
        </Link>
      </p>
    </div>
  )
}

export default Resume

```