[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 flow-finance-components/react/components/AccountCreateContext.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FunctionComponent, useContext, useReducer } from 'react'

import {
  BusinessInformation,
  PersonalInformation,
  Address,
  TOSAcceptance,
} from '../typings/flow-finance'

interface AccountCreateContextProps {
  businessInformation: BusinessInformation
  businessAddress: Address
  personalInformation: PersonalInformation
  personalAddress: Address
  tosAcceptance: TOSAcceptance
  currentPage: number
  businessInfoValid: boolean
  businessInfoComplete: boolean
  personalInfoValid: boolean
  personalInfoComplete: boolean
  documentsValid: boolean
  documentsComplete: boolean
}

type ReducerActions =
  | {
      type: 'SET_BUSINESS_FIELD'
      args: { field: string; value: string | File }
    }
  | { type: 'SET_BUSINESS_ADDRESS'; args: { address: Address } }
  | {
      type: 'SET_PERSONAL_FIELD'
      args: { field: string; value: string | boolean | File }
    }
  | { type: 'SET_PERSONAL_ADDRESS'; args: { address: Address } }
  | { type: 'SET_TOS_FIELD'; args: { field: string; value: string } }
  | { type: 'SET_CURRENT_PAGE'; args: { page: number } }
  | { type: 'SET_BUSINESS_INFO_VALID'; args: { valid: boolean } }
  | { type: 'SET_PERSONAL_INFO_VALID'; args: { valid: boolean } }
  | { type: 'SET_DOCUMENTS_VALID'; args: { valid: boolean } }
  | { type: 'SET_BUSINESS_INFO_COMPLETE'; args: { complete: boolean } }
  | { type: 'SET_PERSONAL_INFO_COMPLETE'; args: { complete: boolean } }
  | { type: 'SET_DOCUMENTS_COMPLETE'; args: { complete: boolean } }

type Dispatch = (action: ReducerActions) => void

const initialAccountCreateState = {
  businessInformation: {
    businessId: '',
    name: '',
    legalName: '',
    email: '',
    phoneNumber: '',
    docType: 'CONTRATO-SOCIAL',
    physicalDocFileName: '',
  },
  businessAddress: {
    addressId: '',
    addressType: 'commercial',
    city: '',
    complement: '',
    country: 'BRA',
    geoCoordinates: [],
    receiverName: 'notApplicable',
    reference: '',
    neighborhood: '',
    number: '',
    postalCode: '',
    state: '',
    street: '',
  },
  personalInformation: {
    idNumber: '',
    maritalStatus: '',
    pep: false,
    lastName: '',
    firstName: '',
    docType: 'CNH',
    physicalDocFileName: '',
    virtualDocValue: '',
    virtualDocExp: '',
    virtualDocIssuer: '',
    email: '',
    phoneNumber: '',
    accountOpener: true,
  },
  personalAddress: {
    addressId: '',
    addressType: 'residential',
    city: '',
    complement: '',
    country: 'BRA',
    geoCoordinates: [],
    receiverName: 'notApplicable',
    reference: '',
    neighborhood: '',
    number: '',
    postalCode: '',
    state: '',
    street: '',
  },
  tosAcceptance: {
    date: '',
    ip: '',
    userAgent: '',
  },
  currentPage: 1,
  businessInfoValid: false,
  personalInfoValid: false,
  documentsValid: false,
  businessInfoComplete: false,
  personalInfoComplete: false,
  documentsComplete: false,
}

const AccountCreateStateContext = React.createContext<
  AccountCreateContextProps
>(initialAccountCreateState)
const AccountCreateDispatchContext = React.createContext<Dispatch | undefined>(
  undefined
)

function reducer(
  state: AccountCreateContextProps,
  action: ReducerActions
): AccountCreateContextProps {
  switch (action.type) {
    case 'SET_BUSINESS_FIELD':
      return {
        ...state,
        businessInformation: {
          ...state.businessInformation,
          [action.args.field]: action.args.value,
        },
      }
    case 'SET_BUSINESS_ADDRESS':
      return {
        ...state,
        businessAddress: action.args.address,
      }
    case 'SET_PERSONAL_FIELD':
      return {
        ...state,
        personalInformation: {
          ...state.personalInformation,
          [action.args.field]: action.args.value,
        },
      }
    case 'SET_PERSONAL_ADDRESS':
      return {
        ...state,
        personalAddress: action.args.address,
      }
    case 'SET_TOS_FIELD':
      return {
        ...state,
        tosAcceptance: {
          ...state.tosAcceptance,
          [action.args.field]: action.args.value,
        },
      }
    case 'SET_CURRENT_PAGE':
      return {
        ...state,
        currentPage: action.args.page,
      }
    case 'SET_BUSINESS_INFO_VALID':
      return {
        ...state,
        businessInfoValid: action.args.valid,
      }
    case 'SET_PERSONAL_INFO_VALID':
      return {
        ...state,
        personalInfoValid: action.args.valid,
      }
    case 'SET_DOCUMENTS_VALID':
      return {
        ...state,
        documentsValid: action.args.valid,
      }
    case 'SET_BUSINESS_INFO_COMPLETE':
      return {
        ...state,
        businessInfoComplete: action.args.complete,
      }
    case 'SET_PERSONAL_INFO_COMPLETE':
      return {
        ...state,
        personalInfoComplete: action.args.complete,
      }
    case 'SET_DOCUMENTS_COMPLETE':
      return {
        ...state,
        documentsComplete: action.args.complete,
      }
    default:
      return state
  }
}

const AccountCreateContextProvider: FunctionComponent<AccountCreateContextProps> = ({
  children,
}) => {
  const [state, dispatch] = useReducer(reducer, initialAccountCreateState)
  return (
    <AccountCreateStateContext.Provider value={state}>
      <AccountCreateDispatchContext.Provider value={dispatch}>
        {children}
      </AccountCreateDispatchContext.Provider>
    </AccountCreateStateContext.Provider>
  )
}

function useAccountCreateState() {
  const context = useContext(AccountCreateStateContext)
  if (context === undefined) {
    throw new Error(
      'useAccountCreateState must be used within a AccountCreateStateContextProvider'
    )
  }
  return context
}

function useAccountCreateDispatch() {
  const context = useContext(AccountCreateDispatchContext)

  if (context === undefined) {
    throw new Error(
      'useAccountCreateDispatch must be used within a AccountCreateDispatchContextProvider'
    )
  }
  return context
}

export {
  AccountCreateContextProvider,
  initialAccountCreateState,
  useAccountCreateDispatch,
  useAccountCreateState,
}

```


[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 flow-finance-components/react/components/AccountCreate.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'

import {
  AccountCreateContextProvider,
  initialAccountCreateState,
} from './AccountCreateContext'
import AccountCreatePageController from './AccountCreatePageController'
import './AccountCreatePages/styles/global.css'

interface Props {
  handleExit(): Promise<void>
  checkAccount?: boolean
}

const CSS_HANDLES = ['accountCreateContainer'] as const

const AccountCreate: StorefrontFunctionComponent<Props> = ({
  handleExit,
  checkAccount = false,
}) => {
  const handles = useCssHandles(CSS_HANDLES)
  return (
    <AccountCreateContextProvider
      businessInformation={initialAccountCreateState.businessInformation}
      businessAddress={initialAccountCreateState.businessAddress}
      personalInformation={initialAccountCreateState.personalInformation}
      personalAddress={initialAccountCreateState.personalAddress}
      tosAcceptance={initialAccountCreateState.tosAcceptance}
      currentPage={initialAccountCreateState.currentPage}
      businessInfoValid={initialAccountCreateState.businessInfoValid}
      personalInfoValid={initialAccountCreateState.personalInfoValid}
      documentsValid={initialAccountCreateState.documentsValid}
      businessInfoComplete={initialAccountCreateState.businessInfoValid}
      personalInfoComplete={initialAccountCreateState.personalInfoValid}
      documentsComplete={initialAccountCreateState.documentsValid}
    >
      <div className={handles.accountCreateContainer}>
        <AccountCreatePageController
          handleExit={handleExit}
          checkAccount={checkAccount}
        />
      </div>
    </AccountCreateContextProvider>
  )
}

export default AccountCreate

```


[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 flow-finance-components/react/components/AccountCreatePageController.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { AddressRules } from 'vtex.address-form'

import React, { Fragment } from 'react'
import { useQuery } from 'react-apollo'

import { useAccountCreateState } from './AccountCreateContext'
import AccountCreateSteps from './AccountCreatePages/AccountCreateSteps'
import IntroPage from './AccountCreatePages/IntroPage'
import PreQualifyPage from './AccountCreatePages/PreQualifyPage'
import BusinessInfoPage from './AccountCreatePages/BusinessInfoPage'
import PersonalInfoPage from './AccountCreatePages/PersonalInfoPage'
import DocumentsPage from './AccountCreatePages/DocumentsPage'
import SuccessPage from './AccountCreatePages/SuccessPage'
import AppSettings from '../graphql/AppSettings.graphql'

interface Props {
  handleExit: () => void
  checkAccount: boolean
}

interface Settings {
  storeName: string
  storePaymentName: string
  tosPdfPath: string
}

const AccountCreatePageController: StorefrontFunctionComponent<Props> = ({
  handleExit,
  checkAccount,
}) => {
  const { currentPage } = useAccountCreateState()
  const { data } = useQuery(AppSettings, { ssr: false })

  if (!data?.appSettings?.message) return null

  const settings: Settings = JSON.parse(data.appSettings.message)

  return (
    <AddressRules country="BRA" shouldUseIOFetching useGeolocation={false}>
      {currentPage === 1 && (
        <IntroPage settings={settings} checkAccount={checkAccount} />
      )}
      {currentPage === 2 && (
        <PreQualifyPage settings={settings} handleExit={handleExit} />
      )}
      {currentPage === 3 && (
        <Fragment>
          <AccountCreateSteps />
          <BusinessInfoPage />
        </Fragment>
      )}
      {currentPage === 4 && (
        <Fragment>
          <AccountCreateSteps />
          <PersonalInfoPage />
        </Fragment>
      )}
      {currentPage === 5 && (
        <Fragment>
          <AccountCreateSteps />
          <DocumentsPage settings={settings} />
        </Fragment>
      )}
      {currentPage === 6 && (
        <Fragment>
          <AccountCreateSteps />
          <SuccessPage settings={settings} handleExit={handleExit} />
        </Fragment>
      )}
    </AddressRules>
  )
}

export default AccountCreatePageController

```


[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 flow-finance-components/react/graphql/Profile.graphql from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
query Profile {
  profile @context(provider: "vtex.store-graphql") {
    firstName
    lastName
    email
    userId
    birthDate
    gender
    homePhone
    businessPhone
    corporateName
    tradeName
    corporateDocument
    stateRegistration
  }
}

```


[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 flow-finance-components/react/graphql/ProcessApplication.graphql from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation ProcessApplication(
  $application: ApplicationInput!
  $businessInfoFile: Upload!
  $personalInfoFile: Upload!
) {
  processApplication(
    application: $application
    businessInfoFile: $businessInfoFile
    personalInfoFile: $personalInfoFile
  ) @context(provider: "vtex.flow-finance-api") {
    success
    error
  }
}

```


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

[User] Show me the implementation of flow-finance-components/react/graphql/PreQualify.graphql from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation PreQualify($cnpj: String!) {
  checkPreQualify(cnpj: $cnpj) @context(provider: "vtex.flow-finance-api")
}

```


[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 flow-finance-components/react/graphql/AppSettings.graphql from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```graphql
query AppSettings {
  appSettings(app: "vtex.flow-finance-components", version: "1.x")
    @context(provider: "vtex.apps-graphql") {
    message
  }
}

```


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

[User] Show me the implementation of flow-finance-components/react/components/AccountCreatePages/BusinessInfoPage.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import {
  AddressContainer,
  AddressForm as AddressFields,
  inputs,
  helpers,
  PostalCodeGetter,
} from 'vtex.address-form'

import React, { useEffect, useState } from 'react'
import {
  defineMessages,
  injectIntl,
  WrappedComponentProps,
  FormattedMessage,
} from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'
import { Input, Button, Divider } from 'vtex.styleguide'

import {
  useAccountCreateState,
  useAccountCreateDispatch,
} from '../AccountCreateContext'
import { formatCNPJ, formatPhone } from './utils/formatters'

interface AddressProps {
  rules: any
}

const { StyleguideInput } = inputs
const { addValidation, isValidAddress, injectRules, validateAddress } = helpers

const CSS_HANDLES = [
  'businessInfoPageContainer',
  'businessInfoInstructions',
  'businessInfoSection',
  'businessAddressSection',
  'businessInfoPageButtonContainer',
] as const

const messages = defineMessages({
  cnpjLabel: {
    defaultMessage: 'CNPJ',
    id: 'store/flowFinance.accountCreate.cnpjLabel',
  },
  cnpjError: {
    defaultMessage: 'Please enter a valid CNPJ',
    id: 'store/flowFinance.accountCreate.cnpjError',
  },
  companyNameLabel: {
    defaultMessage: 'Company Name',
    id: 'store/flowFinance.accountCreate.companyNameLabel',
  },
  companyNameError: {
    defaultMessage: 'This field is required.',
    id: 'store/flowFinance.accountCreate.companyNameError',
  },
  companyLegalNameLabel: {
    defaultMessage: 'Legal Name',
    id: 'store/flowFinance.accountCreate.companyLegalNameLabel',
  },
  companyLegalNameError: {
    defaultMessage: 'This field is required.',
    id: 'store/flowFinance.accountCreate.companyLegalNameError',
  },
  companyPhoneLabel: {
    defaultMessage: 'Phone Number',
    id: 'store/flowFinance.accountCreate.companyPhoneLabel',
  },
  companyPhoneError: {
    defaultMessage: 'Please enter a valid phone number.',
    id: 'store/flowFinance.accountCreate.companyPhoneError',
  },
})

let gguid = 1

function getGGUID() {
  return (gguid++ * new Date().getTime() * -1).toString()
}

const BusinessInfoPage: StorefrontFunctionComponent<WrappedComponentProps &
  AddressProps> = ({ intl, rules }) => {
  const {
    businessInformation,
    businessAddress,
    businessInfoValid,
    currentPage,
  } = useAccountCreateState()
  const dispatch = useAccountCreateDispatch()
  const handles = useCssHandles(CSS_HANDLES)

  const addressWithValidation = addValidation({
    addressQuery: null,
    ...businessAddress,
  })
  addressWithValidation.addressId.value = getGGUID()
  addressWithValidation.receiverName.value = 'notApplicable'
  addressWithValidation.receiverName.postalCodeAutoCompleted = true
  const [address, setAddress] = useState(addressWithValidation)
  const [formattedPhone, setFormattedPhone] = useState(
    formatPhone(businessInformation.phoneNumber)
  )
  const [showErrors, setShowErrors] = useState(false)
  const [showPhoneError, setShowPhoneError] = useState(false)
  const [showNameError, setShowNameError] = useState(false)
  const [showLegalNameError, setShowLegalNameError] = useState(false)

  useEffect(() => {
    if (businessInformation.phoneNumber) setShowPhoneError(true)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  useEffect(() => {
    if (
      businessInformation.phoneNumber.length >= 10 &&
      businessInformation.phoneNumber.length <= 11 &&
      businessInformation.name !== '' &&
      businessInformation.legalName !== '' &&
      isValidAddress(address, rules).valid
    ) {
      dispatch({
        type: 'SET_BUSINESS_INFO_VALID',
        args: {
          valid: true,
        },
      })
    } else if (businessInfoValid) {
      dispatch({
        type: 'SET_BUSINESS_INFO_VALID',
        args: {
          valid: false,
        },
      })
    }
  }, [businessInfoValid, businessInformation, address, rules, dispatch])

  async function handleAddressChange(newAddress: AddressFormFields) {
    const curAddress = address
    const combinedAddress = { ...curAddress, ...newAddress }
    setAddress(combinedAddress)
  }

  async function handleAddressFieldChange(newAddress: AddressFormFields) {
    const [key] = Object.keys(newAddress)
    const curAddress = address
    const validatedField = newAddress[key].value
      ? validateAddress(newAddress, rules)
      : newAddress
    const combinedAddress = { ...curAddress, ...validatedField }
    setAddress(combinedAddress)
  }

  async function handleShowErrors() {
    const validatedAddress = validateAddress(address, rules)
    setAddress(validatedAddress)
    setShowErrors(true)
  }

  async function handleContinueClick() {
    const validAddress = {
      addressId: '',
      addressType: 'commercial',
      city: address.city.value,
      complement: address.complement.value || '',
      country: 'BRA',
      geoCoordinates: [],
      receiverName: '',
      reference: '',
      neighborhood: address.neighborhood.value,
      number: address.number.value,
      postalCode: address.postalCode.value,
      state: address.state.value,
      street: address.street.value,
    }
    dispatch({
      type: 'SET_BUSINESS_ADDRESS',
      args: {
        address: validAddress,
      },
    })
    dispatch({
      type: 'SET_BUSINESS_INFO_COMPLETE',
      args: {
        complete: true,
      },
    })
    dispatch({
      type: 'SET_CURRENT_PAGE',
      args: {
        page: currentPage + 1,
      },
    })
    window.scrollTo(0, 0)
  }

  return (
    <div className={handles.businessInfoPageContainer}>
      <div className={`${handles.businessInfoInstructions} mb7`}>
        <FormattedMessage id="store/flowFinance.accountCreate.businessInfo.instructions" />
      </div>
      <section
        className={`${handles.businessInfoSection} flex flex-row flex-wrap mb7`}
      >
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.cnpjLabel)}
            value={formatCNPJ(businessInformation.businessId)}
            readOnly
          />
        </div>
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.companyPhoneLabel)}
            value={formattedPhone}
            onChange={(e: React.FormEvent<HTMLInputElement>) => {
              const newValue = e.currentTarget.value.replace(/[^\d]/g, '')
              setFormattedPhone(() => formatPhone(newValue))
              dispatch({
                type: 'SET_BUSINESS_FIELD',
                args: {
                  field: 'phoneNumber',
                  value: newValue,
                },
              })
            }}
            onBlur={() => setShowPhoneError(true)}
            errorMessage={
              (showErrors || showPhoneError) &&
              (businessInformation.phoneNumber.length < 10 ||
                businessInformation.phoneNumber.length > 11)
                ? intl.formatMessage(messages.companyPhoneError)
                : ''
            }
          />
        </div>
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.companyNameLabel)}
            value={businessInformation.name}
            onChange={(e: React.FormEvent<HTMLInputElement>) => {
              dispatch({
                type: 'SET_BUSINESS_FIELD',
                args: {
                  field: 'name',
                  value: e.currentTarget.value,
                },
              })
            }}
            onBlur={() => setShowNameError(true)}
            errorMessage={
              (showErrors || showNameError) && businessInformation.name === ''
                ? intl.formatMessage(messages.companyNameError)
                : ''
            }
          />
        </div>
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.companyLegalNameLabel)}
            value={businessInformation.legalName}
            onChange={(e: React.FormEvent<HTMLInputElement>) => {
              dispatch({
                type: 'SET_BUSINESS_FIELD',
                args: {
                  field: 'legalName',
                  value: e.currentTarget.value,
                },
              })
            }}
            onBlur={() => setShowLegalNameError(true)}
            errorMessage={
              (showErrors || showLegalNameError) &&
              businessInformation.legalName === ''
                ? intl.formatMessage(messages.companyLegalNameError)
                : ''
            }
          />
        </div>
      </section>
      <Divider orientation="horizontal" />
      <section className={`${handles.businessAddressSection} mt7 mb7`}>
        <AddressContainer
          address={address}
          Input={StyleguideInput}
          onChangeAddress={(newAddress: AddressFormFields) =>
            handleAddressChange(newAddress)
          }
          autoCompletePostalCode
        >
          <PostalCodeGetter />
          <AddressFields
            address={address}
            Input={StyleguideInput}
            omitAutoCompletedFields={false}
            omitPostalCodeFields
            onChangeAddress={(newAddress: AddressFormFields) =>
              handleAddressFieldChange(newAddress)
            }
            notApplicableLabel={intl.formatMessage({
              id: 'store/flowFinance.accountCreate.addressNotApplicable',
            })}
          />
        </AddressContainer>
      </section>
      <Divider orientation="horizontal" />
      <div
        className={`${handles.businessInfoPageButtonContainer} mt6`}
        onClick={() => handleShowErrors()}
      >
        <Button
          variation="primary"
          disabled={!businessInfoValid}
          onClick={() => handleContinueClick()}
        >
          <FormattedMessage id="store/flowFinance.accountCreate.businessInfo.submit" />
        </Button>
      </div>
    </div>
  )
}

export default injectIntl(injectRules(BusinessInfoPage))

```


[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 flow-finance-components/react/components/AccountCreatePages/DocumentDropzone.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, Fragment } from 'react'
import { useIntl, defineMessages, FormattedMessage } from 'react-intl'
import { useDropzone } from 'react-dropzone'
import { ButtonWithIcon, IconDelete, Spinner, Divider } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import {
  useAccountCreateState,
  useAccountCreateDispatch,
} from '../AccountCreateContext'
import IconUpload from '../../images/upload.svg'
import IconWarning from '../../images/warning.svg'

interface DropProps {
  maxSize: number
  documentType: string
}

const messages = defineMessages({
  fileSizeError: {
    defaultMessage: 'File exceeds the size limit. Please choose a smaller one.',
    id: 'store/flowFinance.accountCreate.documents.fileSizeError',
  },
  genericError: {
    defaultMessage: 'Something went wrong. Please try again.',
    id: 'store/flowFinance.accountCreate.documents.genericError',
  },
  selectFileBtn: {
    defaultMessage: 'Select',
    id: 'store/flowFinance.accountCreate.documents.selectFileButton',
  },
  cnhLabel: {
    defaultMessage: 'CNH',
    id: 'store/flowFinance.accountCreate.documents.cnhLabel',
  },
  rgLabel: {
    defaultMessage: 'RG',
    id: 'store/flowFinance.accountCreate.documents.rgLabel',
  },
})

const CSS_HANDLES = [
  'dropzoneContainer',
  'dropzoneRadioOptions',
  'dropzoneRadioOption',
  'dropzoneRadioOptionInput',
  'dropzoneRadioLabelText',
  'dropzoneErrorContainer',
  'dropzoneErrorIcon',
  'dropzoneErrorText',
  'dropzoneFilenameContainer',
  'dropzoneFilenameText',
  'dropzoneUploadContainer',
  'dropzoneUploadIcon',
  'dropzoneUploadInstructions',
  'dropzoneUploadButton',
  'dropzoneUploadButtonText',
] as const

const DocumentDropzone: StorefrontFunctionComponent<DropProps> = ({
  maxSize,
  documentType,
}) => {
  const intl = useIntl()
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState<string | null>()
  const { businessInformation, personalInformation } = useAccountCreateState()
  const dispatch = useAccountCreateDispatch()
  const handles = useCssHandles(CSS_HANDLES)

  // function getBase64(file: File) {
  //   return new Promise((resolve, reject) => {
  //     const reader = new FileReader()
  //     reader.onload = () => {
  //       resolve(reader.result)
  //     }
  //     reader.onerror = reject
  //     reader.readAsDataURL(file)
  //   })
  // }

  const onDropImage = async (files: File[]) => {
    setError(null)
    try {
      if (files?.[0]) {
        setIsLoading(true)

        // const base64EncodedFile = (await getBase64(files[0])) as string

        dispatch({
          type:
            documentType === 'business'
              ? 'SET_BUSINESS_FIELD'
              : 'SET_PERSONAL_FIELD',
          args: {
            field: 'physicalDocFile',
            value: files[0],
            // value: base64EncodedFile.split(',')[1],
          },
        })
        dispatch({
          type:
            documentType === 'business'
              ? 'SET_BUSINESS_FIELD'
              : 'SET_PERSONAL_FIELD',
          args: {
            field: 'physicalDocFileName',
            value: files[0].name,
          },
        })
        setIsLoading(false)
      } else {
        return setError(intl.formatMessage(messages.fileSizeError))
      }
    } catch (err) {
      setError(intl.formatMessage(messages.genericError))
      setIsLoading(false)
    }
  }

  const { getInputProps, getRootProps } = useDropzone({
    accept:
      documentType === 'business'
        ? ['.pdf', '.jpg', '.jpeg', '.png']
        : ['.jpg', '.jpeg', '.png'],
    maxSize,
    multiple: false,
    onDrop: onDropImage,
  })

  async function handlePersonalDocTypeChange(newType: string) {
    dispatch({
      type: 'SET_PERSONAL_FIELD',
      args: {
        field: 'docType',
        value: newType,
      },
    })
  }

  async function handleRemoveFile() {
    setError(null)
    setIsLoading(true)
    dispatch({
      type:
        documentType === 'business'
          ? 'SET_BUSINESS_FIELD'
          : 'SET_PERSONAL_FIELD',
      args: {
        field: 'physicalDocValue',
        value: '',
      },
    })
    dispatch({
      type:
        documentType === 'business'
          ? 'SET_BUSINESS_FIELD'
          : 'SET_PERSONAL_FIELD',
      args: {
        field: 'physicalDocFileName',
        value: '',
      },
    })
    setIsLoading(false)
  }

  return (
    <div
      className={`${handles.dropzoneContainer} pa7 br3 b--dashed b--muted-4 bw2 tc mb7`}
    >
      {documentType === 'personal' && (
        <Fragment>
          <div className={`${handles.dropzoneRadioOptions} mb5`}>
            <label className={`${handles.dropzoneRadioOption} pa4`}>
              <input
                type="radio"
                name="document"
                checked={personalInformation.docType === 'CNH'}
                value="CNH"
                id="cnh"
                onChange={() => handlePersonalDocTypeChange('CNH')}
                className={`${handles.dropzoneRadioOptionInput}`}
              />
              {` `}
              <span className={`${handles.dropzoneRadioLabelText}`}>
                {intl.formatMessage(messages.cnhLabel)}
              </span>
            </label>
            <label className={`${handles.dropzoneRadioOption} pa4`}>
              <input
                type="radio"
                name="document"
                checked={personalInformation.docType === 'RG'}
                value="RG"
                id="rg"
                onChange={() => handlePersonalDocTypeChange('RG')}
                className={`${handles.dropzoneRadioOptionInput}`}
              />
              {` `}
              <span className={`${handles.dropzoneRadioLabelText}`}>
                {intl.formatMessage(messages.rgLabel)}
              </span>
            </label>
          </div>
          <Divider orientation="horizontal" />
        </Fragment>
      )}
      {error && (
        <Fragment>
          <div
            className={`${handles.dropzoneErrorContainer} flex flex-row c-danger t-small justify-center items-center mt7 mb7 ph7`}
          >
            <img
              className={`${handles.dropzoneErrorIcon}`}
              src={IconWarning}
              alt="Warning"
            />
            <span className={`${handles.dropzoneErrorText}`}>{error}</span>
          </div>
          <Divider orientation="horizontal" />
        </Fragment>
      )}
      <div {...getRootProps()}>
        {isLoading ? (
          <div>
            <Spinner size={32} />
          </div>
        ) : (documentType === 'business' &&
            businessInformation.physicalDocFileName) ||
          (documentType === 'personal' &&
            personalInformation.physicalDocFileName) ? (
          <div
            className={`${handles.dropzoneFilenameContainer} black-70 flex flex-row justify-between items-center`}
          >
            <span className={`${handles.dropzoneFilenameText}`}>
              {documentType === 'business' &&
                businessInformation.physicalDocFileName}
              {documentType === 'personal' &&
                personalInformation.physicalDocFileName}
            </span>
            <ButtonWithIcon
              onClick={() => handleRemoveFile()}
              variation="tertiary"
              icon={<IconDelete />}
            />
          </div>
        ) : (
          <div className={`${handles.dropzoneUploadContainer} pt7 pb7`}>
            <input {...getInputProps()} />
            <img
              className={`${handles.dropzoneUploadIcon}`}
              src={IconUpload}
              alt="Upload Icon"
            />
            <div className={`${handles.dropzoneUploadInstructions} ma3`}>
              <FormattedMessage
                id="store/flowFinance.accountCreate.documents.uploadInstructions"
                values={{
                  lineBreak: <br />,
                }}
              />
            </div>
            <button
              className={`${handles.dropzoneUploadButton} vtex-button bw1 ba fw5 v-mid relative pa0 lh-solid br2 min-h-small t-action--small bg-action-primary b--action-primary c-on-action-primary hover-bg-action-primary hover-b--action-primary hover-c-on-action-primary pointer`}
              type="button"
            >
              <div
                className={`${handles.dropzoneUploadButtonText} vtex-button__label flex items-center justify-center h-100 ph5`}
                style={{ paddingTop: '.25em', paddingBottom: '.32em' }}
              >
                {intl.formatMessage(messages.selectFileBtn)}
              </div>
            </button>
            <div className="t-mini mt3">
              <FormattedMessage
                id="store/flowFinance.accountCreate.documents.fileRestrictions"
                values={{
                  businessFileType: documentType === 'business' ? 'PDF, ' : '',
                  fileSize: documentType === 'business' ? '10' : '3',
                }}
              />
            </div>
          </div>
        )}
      </div>
    </div>
  )
}

export default DocumentDropzone

```


[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 flow-finance-components/react/components/AccountCreatePages/AccountCreateSteps.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'
import { Divider } from 'vtex.styleguide'

import {
  useAccountCreateState,
  useAccountCreateDispatch,
} from '../AccountCreateContext'
import IconCheck from '../../images/check.svg'

const CSS_HANDLES = [
  'accountCreateStepsContainer',
  'accountCreateStep',
  'accountCreateStepNumberContainer',
  'accountCreateStepNumber',
  'accountCreateStepLabel',
] as const

const AccountCreateSteps: StorefrontFunctionComponent<WrappedComponentProps> = () => {
  const handles = useCssHandles(CSS_HANDLES)
  const {
    currentPage,
    businessInfoComplete,
    personalInfoComplete,
    documentsComplete,
  } = useAccountCreateState()
  const dispatch = useAccountCreateDispatch()

  if (currentPage < 3) return null

  async function handlePageChange(page: number) {
    if (currentPage === page) return
    dispatch({
      type: 'SET_CURRENT_PAGE',
      args: {
        page,
      },
    })
    window.scrollTo(0, 0)
  }

  return (
    <div
      className={`${handles.accountCreateStepsContainer} flex flex-row justify-between pb6 mb6`}
      style={{ borderBottom: '2px solid black' }}
    >
      <div className={`${handles.accountCreateStep} tc ph3`}>
        <div className={handles.accountCreateStepNumberContainer}>
          <button
            className={`input-reset button-reset br-100 mb2 b--none bg-base--inverted ${
              currentPage !== 6 ? 'pointer' : ''
            }`}
            style={{ width: 32, height: 32 }}
            onClick={() => handlePageChange(3)}
            disabled={currentPage === 6}
          >
            <span className={`${handles.accountCreateStepNumber} white`}>
              {!businessInfoComplete ? (
                '1'
              ) : (
                <img src={IconCheck} alt="Complete" />
              )}
            </span>
          </button>
        </div>
        <div className={handles.accountCreateStepLabel}>
          <FormattedMessage id="store/flowFinance.accountCreate.steps.businessInfo" />
        </div>
      </div>
      <div className="flex-grow-1 flex flex-column justify-center">
        <Divider orientation="horizontal" />
      </div>
      <div className={`${handles.accountCreateStep} tc ph3`}>
        <div className={handles.accountCreateStepNumberContainer}>
          <button
            className={`input-reset button-reset br-100 mb2 b--none ${
              currentPage > 3 ? 'bg-base--inverted' : 'bg-muted-3'
            } ${currentPage !== 6 && businessInfoComplete ? 'pointer' : ''}`}
            style={{ width: 32, height: 32 }}
            onClick={() => handlePageChange(4)}
            disabled={!businessInfoComplete || currentPage === 6}
          >
            <span className={`${handles.accountCreateStepNumber} white`}>
              {!personalInfoComplete ? (
                '2'
              ) : (
                <img src={IconCheck} alt="Complete" />
              )}
            </span>
          </button>
        </div>
        <div
          className={`${handles.accountCreateStepLabel}  ${
            currentPage > 3 ? '' : 'c-muted-4'
          }`}
        >
          <FormattedMessage id="store/flowFinance.accountCreate.steps.personalInfo" />
        </div>
      </div>
      <div className="flex-grow-1 flex flex-column justify-center">
        <Divider orientation="horizontal" />
      </div>
      <div className={`${handles.accountCreateStep} tc ph3`}>
        <div className={handles.accountCreateStepNumberContainer}>
          <button
            className={`input-reset button-reset br-100 mb2 b--none ${
              currentPage > 4 ? 'bg-base--inverted' : 'bg-muted-3'
            } ${currentPage !== 6 && personalInfoComplete ? 'pointer' : ''}`}
            style={{ width: 32, height: 32 }}
            onClick={() => handlePageChange(5)}
            disabled={!personalInfoComplete || currentPage === 6}
          >
            <span className={`${handles.accountCreateStepNumber} white`}>
              {!documentsComplete ? (
                '3'
              ) : (
                <img src={IconCheck} alt="Complete" />
              )}
            </span>
          </button>
        </div>
        <div
          className={`${handles.accountCreateStepLabel}  ${
            currentPage > 4 ? '' : 'c-muted-4'
          }`}
        >
          <FormattedMessage id="store/flowFinance.accountCreate.steps.documents" />
        </div>
      </div>
    </div>
  )
}

export default injectIntl(AccountCreateSteps)

```


[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 flow-finance-components/react/components/AccountCreatePages/SuccessPage.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { FormattedMessage, injectIntl, WrappedComponentProps } from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'
import { Button, Divider } from 'vtex.styleguide'

interface SuccessProps {
  handleExit: () => void
  settings: Settings
}

interface Settings {
  storeName: string
  storePaymentName: string
}

const CSS_HANDLES = [
  'successPageContainer',
  'successPageTitle',
  'successPageMessage',
  'successPageButtonContainer',
] as const

const SuccessPage: StorefrontFunctionComponent<SuccessProps &
  WrappedComponentProps> = ({ handleExit, settings }) => {
  const handles = useCssHandles(CSS_HANDLES)

  return (
    <div className={handles.successPageContainer}>
      <h1 className={handles.successPageTitle}>
        <FormattedMessage id="store/flowFinance.accountCreate.successPage.title" />
      </h1>
      <div className={`${handles.successPageMessage} mb6`}>
        <FormattedMessage
          id="store/flowFinance.accountCreate.successPage.message"
          values={{
            lineBreak: <br />,
            storePaymentName: settings.storePaymentName,
          }}
        />
      </div>
      <Divider orientation="horizontal" />
      <div className={`${handles.successPageButtonContainer} mt6`}>
        <Button variation="primary" onClick={() => handleExit()}>
          <FormattedMessage id="store/flowFinance.accountCreate.continueShopping" />
        </Button>
      </div>
    </div>
  )
}

export default injectIntl(SuccessPage)

```


[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 flow-finance-components/react/components/AccountCreatePages/IntroPage.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect, useState } from 'react'
import { FormattedMessage } from 'react-intl'
import { useQuery } from 'react-apollo'
import { useCssHandles } from 'vtex.css-handles'
import { Button, Divider } from 'vtex.styleguide'

import {
  useAccountCreateState,
  useAccountCreateDispatch,
} from '../AccountCreateContext'
import IconMouse from '../../images/mouse.svg'
import IconCart from '../../images/cart.svg'
import FlowFinanceLogo from '../../images/flow-finance-logo.png'
import Profile from '../../graphql/Profile.graphql'

interface Props {
  settings: Settings
  checkAccount: boolean
}

interface Settings {
  storeName: string
  storePaymentName: string
}

const CSS_HANDLES = [
  'introPageContainer',
  'introPageInstructions',
  'introPageTitle',
  'introPageIconsContainer',
  'introPageLeftIconContainer',
  'introPageLeftIcon',
  'introPageLeftIconTitle',
  'introPageLeftIconText',
  'introPageRightIconContainer',
  'introPageRightIcon',
  'introPageRightIconTitle',
  'introPageRightIconText',
  `introPageButtonContainer`,
  'introPageAccountErrorContainer',
  `poweredByContainer`,
] as const

const IntroPage: StorefrontFunctionComponent<Props> = ({
  settings,
  checkAccount,
}) => {
  const { currentPage } = useAccountCreateState()
  const dispatch = useAccountCreateDispatch()
  const [accountStatus, setAccountStatus] = useState('none')
  const handles = useCssHandles(CSS_HANDLES)
  const { data } = useQuery(Profile, { skip: !checkAccount })

  async function postData(url = '', dataToPost = {}) {
    const response = await fetch(url, {
      method: 'POST',
      mode: 'cors',
      cache: 'no-cache',
      credentials: 'same-origin',
      headers: {
        'Content-Type': 'application/json',
      },
      redirect: 'follow',
      referrerPolicy: 'no-referrer',
      body: JSON.stringify(dataToPost),
    })
    if (!response.ok) {
      console.error(response)
      return response
    }
    return response.json()
  }

  useEffect(() => {
    if (data?.profile) {
      postData(`/_v/api/connectors/flow-finance/get-loan-options`, {
        email: data.profile.email,
        total: 1000,
      }).then(response => {
        setAccountStatus(response.accountStatus)
      })
    }
  }, [data])

  return (
    <div className={handles.introPageContainer}>
      <h1 className={`${handles.introPageTitle} tc mb6`}>
        <FormattedMessage id="store/flowFinance.accountCreate.introPage.title" />
      </h1>
      <div className={`${handles.introPageInstructions} tc mb6`}>
        <FormattedMessage
          id="store/flowFinance.accountCreate.introPage.instructions"
          values={{
            storePaymentName: settings.storePaymentName,
          }}
        />
      </div>
      <div
        className={`${handles.introPageIconsContainer} flex flex-row justify-around mb6`}
      >
        <div className={`${handles.introPageLeftIconContainer} w-50 pa3 tc`}>
          <img
            src={IconMouse}
            alt="Mouse icon"
            className={handles.introPageLeftIcon}
          />
          <div className={`${handles.introPageLeftIconTitle} b mt2`}>
            <FormattedMessage id="store/flowFinance.accountCreate.introPage.leftIconTitle" />
          </div>
          <div className={handles.introPageLeftIconText}>
            <FormattedMessage id="store/flowFinance.accountCreate.introPage.leftIconText" />
          </div>
        </div>
        <div className={`${handles.introPageRightIconContainer} w-50 pa3 tc`}>
          <img
            src={IconCart}
            alt="Cart icon"
            className={handles.introPageRightIcon}
          />
          <div className={`${handles.introPageRightIconTitle} b mt2`}>
            <FormattedMessage id="store/flowFinance.accountCreate.introPage.rightIconTitle" />
          </div>
          <div className={handles.introPageRightIconText}>
            <FormattedMessage
              id="store/flowFinance.accountCreate.introPage.rightIconText"
              values={{
                storePaymentName: settings.storePaymentName,
              }}
            />
          </div>
        </div>
      </div>
      <Divider orientation="horizontal" />
      <div
        className={`${handles.introPageButtonContainer} mt6 flex flex-row justify-between`}
      >
        <div>
          <Button
            variation="primary"
            onClick={() => {
              dispatch({
                type: 'SET_CURRENT_PAGE',
                args: {
                  page: currentPage + 1,
                },
              })
              window.scrollTo(0, 0)
            }}
            disabled={accountStatus !== 'none' && accountStatus !== 'pending'}
          >
            <FormattedMessage id="store/flowFinance.accountCreate.introPage.continue" />
          </Button>
          <div
            className={`${handles.introPageAccountErrorContainer} mt2 red mw6`}
          >
            {accountStatus === 'under-review' && (
              <FormattedMessage id="store/flowFinance.accountCreate.introPage.accountErrors.underReview" />
            )}
            {accountStatus === 'approved' && (
              <FormattedMessage id="store/flowFinance.accountCreate.introPage.accountErrors.approved" />
            )}
            {accountStatus === 'rejected' && (
              <FormattedMessage id="store/flowFinance.accountCreate.introPage.accountErrors.rejected" />
            )}
          </div>
        </div>
        <div className={`${handles.poweredByContainer} flex flex-row`}>
          <div className="flex flex-column justify-center">
            <FormattedMessage id="store/flowFinance.accountCreate.introPage.poweredBy" />
          </div>
          <img
            src={FlowFinanceLogo}
            alt="Flow Finance"
            className="self-center"
          />
        </div>
      </div>
    </div>
  )
}

export default IntroPage

```


[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 flow-finance-components/react/components/AccountCreatePages/PersonalInfoPage.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import {
  AddressContainer,
  AddressForm as AddressFields,
  inputs,
  helpers,
  PostalCodeGetter,
} from 'vtex.address-form'

import React, { useEffect, useState } from 'react'
import {
  defineMessages,
  injectIntl,
  WrappedComponentProps,
  FormattedMessage,
} from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'
import { Input, Dropdown, Button, Toggle, Divider } from 'vtex.styleguide'

import {
  useAccountCreateState,
  useAccountCreateDispatch,
} from '../AccountCreateContext'
import {
  formatPhone,
  formatCPF,
  // formatRG,
  // formatRGExp,
} from './utils/formatters'

interface AddressProps {
  rules: any
}

const { StyleguideInput } = inputs
const { addValidation, isValidAddress, validateAddress, injectRules } = helpers

const CSS_HANDLES = [
  'personalInfoPageContainer',
  'personalInfoInstructions',
  'personalInfoSection',
  'personalContactSection',
  'personalAddressSection',
  'personalInfoPageButtonContainer',
] as const

const messages = defineMessages({
  firstNameLabel: {
    defaultMessage: 'First Name',
    id: 'store/flowFinance.accountCreate.firstNameLabel',
  },
  firstNameError: {
    defaultMessage: 'This field is required.',
    id: 'store/flowFinance.accountCreate.firstNameError',
  },
  lastNameLabel: {
    defaultMessage: 'Last Name',
    id: 'store/flowFinance.accountCreate.lastNameLabel',
  },
  lastNameError: {
    defaultMessage: 'This field is required.',
    id: 'store/flowFinance.accountCreate.lastNameError',
  },
  cpfLabel: {
    defaultMessage: 'CPF',
    id: 'store/flowFinance.accountCreate.cpfLabel',
  },
  cpfError: {
    defaultMessage: 'This field is required.',
    id: 'store/flowFinance.accountCreate.cpfError',
  },
  rgLabel: {
    defaultMessage: 'RG',
    id: 'store/flowFinance.accountCreate.rgLabel',
  },
  rgError: {
    defaultMessage: 'This field is required.',
    id: 'store/flowFinance.accountCreate.rgError',
  },
  rgExpLabel: {
    defaultMessage: 'RG Expiration Date (DD/MM/YYYY)',
    id: 'store/flowFinance.accountCreate.rgExpLabel',
  },
  rgExpError: {
    defaultMessage: 'This field is required.',
    id: 'store/flowFinance.accountCreate.rgExpError',
  },
  phoneNumberLabel: {
    defaultMessage: 'Phone Number',
    id: 'store/flowFinance.accountCreate.personalPhoneLabel',
  },
  phoneNumberError: {
    defaultMessage: 'Please enter a valid phone number.',
    id: 'store/flowFinance.accountCreate.personalPhoneError',
  },
  pepLabel: {
    defaultMessage: 'PEP',
    id: 'store/flowFinance.accountCreate.pepLabel',
  },
  pepText: {
    defaultMessage: '',
    id: 'store/flowFinance.accountCreate.pepText',
  },
  pepLinkText: {
    defaultMessage: '',
    id: 'store/flowFinance.accountCreate.pepLinkText',
  },
  emailLabel: {
    defaultMessage: 'Email',
    id: 'store/flowFinance.accountCreate.emailLabel',
  },
  emailError: {
    defaultMessage: 'Please enter a valid email address.',
    id: 'store/flowFinance.accountCreate.emailError',
  },
  maritalStatusLabel: {
    defaultMessage: 'Marital Status',
    id: 'store/flowFinance.accountCreate.maritalStatusLabel',
  },
  maritalStatusError: {
    defaultMessage: 'Please choose a marital status.',
    id: 'store/flowFinance.accountCreate.maritalStatusError',
  },
  singleLabel: {
    defaultMessage: 'Single',
    id: 'store/flowFinance.accountCreate.maritalStatusOptions.single',
  },
  marriedLabel: {
    defaultMessage: 'Married',
    id: 'store/flowFinance.accountCreate.maritalStatusOptions.married',
  },
  separatedLabel: {
    defaultMessage: 'Legally Separated',
    id: 'store/flowFinance.accountCreate.maritalStatusOptions.separated',
  },
  divorcedLabel: {
    defaultMessage: 'Divorced',
    id: 'store/flowFinance.accountCreate.maritalStatusOptions.divorced',
  },
  widowedLabel: {
    defaultMessage: 'Widowed',
    id: 'store/flowFinance.accountCreate.maritalStatusOptions.widowed',
  },
  otherStatusLabel: {
    defaultMessage: 'Other',
    id: 'store/flowFinance.accountCreate.maritalStatusOptions.other',
  },
})

let gguid = 1

function getGGUID() {
  return (gguid++ * new Date().getTime() * -1).toString()
}

const PersonalInfoPage: StorefrontFunctionComponent<WrappedComponentProps &
  AddressProps> = ({ intl, rules }) => {
  const {
    personalInformation,
    personalAddress,
    personalInfoValid,
    currentPage,
  } = useAccountCreateState()
  const dispatch = useAccountCreateDispatch()
  const handles = useCssHandles(CSS_HANDLES)
  const [formattedPhone, setFormattedPhone] = useState(
    formatPhone(personalInformation.phoneNumber)
  )
  const [formattedCPF, setFormattedCPF] = useState(
    formatCPF(personalInformation.idNumber)
  )
  const [showErrors, setShowErrors] = useState(false)
  const [showPhoneError, setShowPhoneError] = useState(false)
  const [showFirstNameError, setShowFirstNameError] = useState(false)
  const [showLastlNameError, setShowLastNameError] = useState(false)
  const [showEmailError, setShowEmailError] = useState(false)
  const [showCPFError, setShowCPFError] = useState(false)
  const [showMaritalStatusError, setShowMaritalStatusError] = useState(false)

  const addressWithValidation = addValidation({
    ...personalAddress,
    addressQuery: null,
    addressId: getGGUID(),
  })
  addressWithValidation.addressId.value = getGGUID()
  addressWithValidation.receiverName.value = 'notApplicable'
  addressWithValidation.receiverName.postalCodeAutoCompleted = true
  const [address, setAddress] = useState(addressWithValidation)

  useEffect(() => {
    if (personalInformation.phoneNumber) setShowPhoneError(true)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  useEffect(() => {
    if (
      personalInformation.phoneNumber.length >= 10 &&
      personalInformation.phoneNumber.length <= 11 &&
      personalInformation.email.indexOf('@') >= 0 &&
      personalInformation.email.indexOf('.') >= 0 &&
      personalInformation.firstName !== '' &&
      personalInformation.lastName !== '' &&
      personalInformation.idNumber.length === 11 &&
      personalInformation.maritalStatus !== '' &&
      isValidAddress(address, rules).valid
    ) {
      dispatch({
        type: 'SET_PERSONAL_INFO_VALID',
        args: {
          valid: true,
        },
      })
    } else if (personalInfoValid) {
      dispatch({
        type: 'SET_PERSONAL_INFO_VALID',
        args: {
          valid: false,
        },
      })
    }
  }, [personalInfoValid, personalInformation, address, rules, dispatch])

  async function storeAddress() {
    const validAddress = {
      addressId: '',
      addressType: 'residential',
      city: address.city.value,
      complement: address.complement.value || '',
      country: 'BRA',
      geoCoordinates: [],
      receiverName: '',
      reference: '',
      neighborhood: address.neighborhood.value,
      number: address.number.value,
      postalCode: address.postalCode.value,
      state: address.state.value,
      street: address.street.value,
    }
    dispatch({
      type: 'SET_PERSONAL_ADDRESS',
      args: {
        address: validAddress,
      },
    })
  }

  async function handleAddressChange(newAddress: AddressFormFields) {
    const curAddress = address
    const combinedAddress = { ...curAddress, ...newAddress }
    setAddress(combinedAddress)
    storeAddress()
  }

  async function handleAddressFieldChange(newAddress: AddressFormFields) {
    const [key] = Object.keys(newAddress)
    const curAddress = address
    const validatedField = newAddress[key].value
      ? validateAddress(newAddress, rules)
      : newAddress
    const combinedAddress = { ...curAddress, ...validatedField }
    setAddress(combinedAddress)
    storeAddress()
  }

  async function handleShowErrors() {
    const validatedAddress = validateAddress(address, rules)
    setAddress(validatedAddress)
    setShowErrors(true)
  }

  async function handleContinueClick() {
    dispatch({
      type: 'SET_PERSONAL_INFO_COMPLETE',
      args: {
        complete: true,
      },
    })
    dispatch({
      type: 'SET_CURRENT_PAGE',
      args: {
        page: currentPage + 1,
      },
    })
    window.scrollTo(0, 0)
  }

  return (
    <div className={handles.personalInfoPageContainer}>
      <div className={`${handles.personalInfoInstructions} mb7`}>
        <FormattedMessage id="store/flowFinance.accountCreate.personalInfo.instructions" />
      </div>
      <section
        className={`${handles.personalInfoSection} flex flex-row flex-wrap mb7`}
      >
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.firstNameLabel)}
            value={personalInformation.firstName}
            onChange={(e: React.FormEvent<HTMLInputElement>) => {
              dispatch({
                type: 'SET_PERSONAL_FIELD',
                args: {
                  field: 'firstName',
                  value: e.currentTarget.value,
                },
              })
            }}
            onBlur={() => setShowFirstNameError(true)}
            errorMessage={
              (showErrors || showFirstNameError) &&
              personalInformation.firstName === ''
                ? intl.formatMessage(messages.firstNameError)
                : ''
            }
          />
        </div>
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.lastNameLabel)}
            value={personalInformation.lastName}
            onChange={(e: React.FormEvent<HTMLInputElement>) => {
              dispatch({
                type: 'SET_PERSONAL_FIELD',
                args: {
                  field: 'lastName',
                  value: e.currentTarget.value,
                },
              })
            }}
            onBlur={() => setShowLastNameError(true)}
            errorMessage={
              (showErrors || showLastlNameError) &&
              personalInformation.lastName === ''
                ? intl.formatMessage(messages.lastNameError)
                : ''
            }
          />
        </div>
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.cpfLabel)}
            value={formattedCPF}
            onChange={(e: React.FormEvent<HTMLInputElement>) => {
              const newValue = e.currentTarget.value.replace(/[^\d]/g, '')
              setFormattedCPF(formatCPF(newValue))
              dispatch({
                type: 'SET_PERSONAL_FIELD',
                args: {
                  field: 'idNumber',
                  value: newValue,
                },
              })
            }}
            onBlur={() => setShowCPFError(true)}
            errorMessage={
              (showErrors || showCPFError) &&
              personalInformation.idNumber.length !== 11
                ? intl.formatMessage(messages.cpfError)
                : ''
            }
          />
        </div>
        <div className="w-50-ns w-100-s pa2 pb4">
          <span className="vtex-input__label db mb3 w-100 c-on-base t-small ">

          </span>
          <Toggle
            label={intl.formatMessage(messages.pepLabel)}
            checked={personalInformation.pep}
            onChange={(e: React.FormEvent<HTMLInputElement>) =>
              dispatch({
                type: 'SET_PERSONAL_FIELD',
                args: {
                  field: 'pep',
                  value: e.currentTarget.checked,
                },
              })
            }
            helpText={
              <span>
                {intl.formatMessage(messages.pepText)}{' '}
                <a
                  href="http://www.cvm.gov.br/legislacao/instrucoes/inst301.html"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  {intl.formatMessage(messages.pepLinkText)}
                </a>
              </span>
            }
          />
        </div>
      </section>
      <Divider orientation="horizontal" />
      <section
        className={`${handles.personalContactSection} flex flex-row flex-wrap mt7 mb7`}
      >
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.phoneNumberLabel)}
            value={formattedPhone}
            onChange={(e: React.FormEvent<HTMLInputElement>) => {
              const newValue = e.currentTarget.value.replace(/[^\d]/g, '')
              setFormattedPhone(() => formatPhone(newValue))
              dispatch({
                type: 'SET_PERSONAL_FIELD',
                args: {
                  field: 'phoneNumber',
                  value: newValue,
                },
              })
            }}
            onBlur={() => setShowPhoneError(true)}
            errorMessage={
              (showErrors || showPhoneError) &&
              (personalInformation.phoneNumber.length < 10 ||
                personalInformation.phoneNumber.length > 11)
                ? intl.formatMessage(messages.phoneNumberError)
                : ''
            }
          />
        </div>
        <div className="w-50-ns w-100-s pa2 pb4">
          <Input
            label={intl.formatMessage(messages.emailLabel)}
            value={personalInformation.email}
            onChange={(e: React.FormEvent<HTMLInputElement>) => {
              dispatch({
                type: 'SET_BUSINESS_FIELD',
                args: {
                  field: 'email',
                  value: e.currentTarget.value,
                },
              })
              dispatch({
                type: 'SET_PERSONAL_FIELD',
                args: {
                  field: 'email',
                  value: e.currentTarget.value,
                },
              })
            }}
            onBlur={() => setShowEmailError(true)}
            errorMessage={
              (showErrors || showEmailError) &&
              (personalInformation.email === '' ||
                personalInformation.email.indexOf('@') < 0 ||
                personalInformation.email.indexOf('.') < 0)
                ? intl.formatMessage(messages.emailError)
                : ''
            }
          />
        </div>
        <div className="w-50-ns w-100-s pa2 pb4">
          <Dropdown
            label={intl.formatMessage(messages.maritalStatusLabel)}
            value={personalInformation.maritalStatus}
            options={[
              {
                value: 'single',
                label: intl.formatMessage(messages.singleLabel),
              },
              {
                value: 'married',
                label: intl.formatMessage(messages.marriedLabel),
              },
              {
                value: 'legally-separated',
                label: intl.formatMessage(messages.separatedLabel),
              },
              {
                value: 'divorced',
                label: intl.formatMessage(messages.divorcedLabel),
              },
              {
                value: 'widowed',
                label: intl.formatMessage(messages.widowedLabel),
              },
              {
                value: 'other',
                label: intl.formatMessage(messages.otherStatusLabel),
              },
            ]}
            onChange={(e: React.FormEvent<HTMLSelectElement>) => {
              setShowMaritalStatusError(true)
              dispatch({
                type: 'SET_PERSONAL_FIELD',
                args: {
                  field: 'maritalStatus',
                  value: e.currentTarget.value,
                },
              })
            }}
            errorMessage={
              (showErrors || showMaritalStatusError) &&
              personalInformation.maritalStatus === ''
                ? intl.formatMessage(messages.maritalStatusError)
                : ''
            }
          />
        </div>
      </section>
      <Divider orientation="horizontal" />
      <section className={`${handles.personalAddressSection} mt7 mb7`}>
        <AddressContainer
          address={address}
          Input={StyleguideInput}
          onChangeAddress={(newAddress: AddressFormFields) =>
            handleAddressChange(newAddress)
          }
          autoCompletePostalCode
        >
          <PostalCodeGetter />
          <AddressFields
            address={address}
            Input={StyleguideInput}
            omitAutoCompletedFields={false}
            omitPostalCodeFields
            onChangeAddress={(newAddress: AddressFormFields) =>
              handleAddressFieldChange(newAddress)
            }
            notApplicableLabel={intl.formatMessage({
              id: 'store/flowFinance.accountCreate.addressNotApplicable',
            })}
          />
        </AddressContainer>
      </section>
      <Divider orientation="horizontal" />
      <div
        className={`${handles.personalInfoPageButtonContainer} mt6`}
        onClick={() => handleShowErrors()}
      >
        <Button
          variation="primary"
          disabled={!personalInfoValid}
          onClick={() => handleContinueClick()}
        >
          <FormattedMessage id="store/flowFinance.accountCreate.personalInfo.submit" />
        </Button>
      </div>
    </div>
  )
}

export default injectIntl(injectRules(PersonalInfoPage))

```


[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 flow-finance-components/react/components/AccountCreatePages/DocumentsPage.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useState, useEffect } from 'react'
import {
  injectIntl,
  defineMessages,
  WrappedComponentProps,
  FormattedMessage,
} from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'
import {
  Button,
  Divider,
  Checkbox,
  ButtonWithIcon,
  IconDelete,
} from 'vtex.styleguide'
import { useMutation } from 'react-apollo'

import {
  useAccountCreateState,
  useAccountCreateDispatch,
} from '../AccountCreateContext'
import { formatPhone } from './utils/formatters'
import DocumentDropzone from './DocumentDropzone'
import IconId from '../../images/id.svg'
import IconIdGray from '../../images/id-gray.svg'
import IconContrato from '../../images/contrato.svg'
import IconContratoGray from '../../images/contrato-gray.svg'
import ProcessApplication from '../../graphql/ProcessApplication.graphql'

interface DocumentsPageProps {
  settings: Settings
}

interface Settings {
  tosPdfPath: string
}

const CSS_HANDLES = [
  'documentsPageContainer',
  'documentsPageInstructions',
  'documentTypesContainer',
  'documentTypeContainer',
  'documentTypeIcon',
  'documentTypeLabel',
  'documentFilenameContainer',
  'documentFilenameText',
  'documentRemoveButton',
  'documentUploadButton',
  'documentUploadButtonText',
  'tosAcceptanceContainer',
  'tosIframeContainer',
  'tosAcceptanceCheckbox',
  'tosAcceptanceCheckboxLabel',
  'documentsPageButtonContainer',
  'documentsPageErrorContainer',
] as const

const messages = defineMessages({
  submitError: {
    defaultMessage:
      'There was an error submitting your application. Please try again.',
    id: 'store/flowFinance.accountCreate.submitApplication.error',
  },
})

const DocumentsPage: StorefrontFunctionComponent<DocumentsPageProps &
  WrappedComponentProps> = ({ settings: { tosPdfPath }, intl }) => {
  const handles = useCssHandles(CSS_HANDLES)
  const [pageMode, setPageMode] = useState('select')
  const [pdfLoaded, setPdfLoaded] = useState(false)
  const [tosAccepted, setTosAccepted] = useState(false)
  const [isLoading, setIsLoading] = useState(false)
  const [submitError, setSubmitError] = useState('')
  const {
    businessInformation,
    businessAddress,
    personalInformation,
    personalAddress,
    documentsValid,
    currentPage,
  } = useAccountCreateState()
  const dispatch = useAccountCreateDispatch()

  const [submitApplication] = useMutation(ProcessApplication)

  useEffect(() => {
    if (
      businessInformation.physicalDocFile &&
      personalInformation.physicalDocFile
    ) {
      dispatch({
        type: 'SET_DOCUMENTS_VALID',
        args: {
          valid: true,
        },
      })
    } else if (documentsValid) {
      dispatch({
        type: 'SET_DOCUMENTS_VALID',
        args: {
          valid: false,
        },
      })
    }
  }, [
    businessInformation.physicalDocFile,
    dispatch,
    documentsValid,
    personalInformation.physicalDocFile,
  ])

  async function handlePageModeChange(newPageMode: string) {
    setPageMode(newPageMode)
    window.scrollTo(0, 0)
  }

  async function handleRemoveFile(documentType: string) {
    dispatch({
      type:
        documentType === 'business'
          ? 'SET_BUSINESS_FIELD'
          : 'SET_PERSONAL_FIELD',
      args: {
        field: 'physicalDocValue',
        value: '',
      },
    })
    dispatch({
      type:
        documentType === 'business'
          ? 'SET_BUSINESS_FIELD'
          : 'SET_PERSONAL_FIELD',
      args: {
        field: 'physicalDocFileName',
        value: '',
      },
    })
  }

  async function handleContinueClick() {
    setIsLoading(true)
    submitApplication({
      variables: {
        application: {
          businessInfo: {
            businessId: businessInformation.businessId,
            name: businessInformation.name,
            legalName: businessInformation.legalName,
            address: {
              streetName: businessAddress.street,
              streetNumber: businessAddress.number,
              postalCode: businessAddress.postalCode,
              district: businessAddress.neighborhood,
              city: businessAddress.city,
              stateCode: businessAddress.state,
              country: businessAddress.country,
              extraAddressInfo: businessAddress.complement,
            },
            contactInfo: {
              email: businessInformation.email,
              phoneNumber: formatPhone(businessInformation.phoneNumber),
            },
            documents: {
              physicalDocument: [
                {
                  type: businessInformation.docType,
                },
              ],
            },
          },
          personalInfo: {
            idNumber: personalInformation.idNumber,
            maritalStatus: personalInformation.maritalStatus,
            pep: personalInformation.pep,
            lastName: personalInformation.lastName,
            firstName: personalInformation.firstName,
            address: {
              streetName: personalAddress.street,
              streetNumber: personalAddress.number,
              postalCode: personalAddress.postalCode,
              district: personalAddress.neighborhood,
              city: personalAddress.city,
              stateCode: personalAddress.state,
              country: personalAddress.country,
              extraAddressInfo: personalAddress.complement,
            },
            documents: {
              physicalDocument: [
                {
                  type: personalInformation.docType,
                },
              ],
            },
            contactInfo: {
              email: personalInformation.email,
              phoneNumber: formatPhone(personalInformation.phoneNumber),
            },
            accountOpener: personalInformation.accountOpener,
          },
          tosAcceptance: {
            userAgent: navigator.userAgent,
          },
        },
        businessInfoFile: businessInformation.physicalDocFile,
        personalInfoFile: personalInformation.physicalDocFile,
      },
    }).then(
      response => {
        if (
          !response.data ||
          !response.data.processApplication ||
          (!response.data.processApplication.success &&
            !response.data.processApplication.error)
        ) {
          setSubmitError(intl.formatMessage(messages.submitError))
          setIsLoading(false)
          return
        }
        if (
          !response.data.processApplication.success &&
          response.data.processApplication.error
        ) {
          setSubmitError(intl.formatMessage(messages.submitError))
          console.error(response.data.processApplication.error)
          setIsLoading(false)
          return
        }
        setSubmitError('')
        dispatch({
          type: 'SET_DOCUMENTS_COMPLETE',
          args: {
            complete: true,
          },
        })
        dispatch({
          type: 'SET_CURRENT_PAGE',
          args: {
            page: currentPage + 1,
          },
        })
        window.scrollTo(0, 0)
      },
      () => {
        setSubmitError(intl.formatMessage(messages.submitError))
        setIsLoading(false)
      }
    )
  }

  switch (pageMode) {
    case 'personal': {
      return (
        <div className={handles.documentsPageContainer}>
          <DocumentDropzone documentType="personal" maxSize={3 * 1024 * 1024} />
          <Divider orientation="horizontal" />
          <div className="mt6">
            <Button
              variation="primary"
              onClick={() => handlePageModeChange('select')}
              disabled={!personalInformation.physicalDocFile}
            >
              <FormattedMessage id="store/flowFinance.accountCreate.documents.continueLabel" />
            </Button>
          </div>
        </div>
      )
    }
    case 'business': {
      return (
        <div className={handles.documentsPageContainer}>
          <DocumentDropzone
            documentType="business"
            maxSize={10 * 1024 * 1024}
          />
          <Divider orientation="horizontal" />
          <div className="mt6">
            <Button
              variation="primary"
              onClick={() => handlePageModeChange('select')}
              disabled={!businessInformation.physicalDocFile}
            >
              <FormattedMessage id="store/flowFinance.accountCreate.documents.continueLabel" />
            </Button>
          </div>
        </div>
      )
    }
    default: {
      return (
        <div className={handles.documentsPageContainer}>
          <div className={`${handles.documentsPageInstructions} mb7`}>
            <FormattedMessage id="store/flowFinance.accountCreate.documents.instructions" />
          </div>
          <div
            className={`${handles.documentTypesContainer} flex flex-row justify-between mb7`}
          >
            <div className={`${handles.documentTypeContainer} w-50 pa2 tc`}>
              <img
                src={!personalInformation.physicalDocFile ? IconIdGray : IconId}
                alt="ID Icon"
                className={`${handles.documentTypeIcon}`}
              />
              <div
                className={`${handles.documentTypeLabel} ma3 ${
                  !personalInformation.physicalDocFile ? 'c-muted-4' : ''
                }`}
              >
                <FormattedMessage id="store/flowFinance.accountCreate.documents.personalIdLabel" />
              </div>
              {personalInformation.physicalDocFileName !== '' ? (
                <div
                  className={`${handles.documentFilenameContainer} ma3 flex flex-row justify-center items-center`}
                >
                  <span className={`${handles.documentFilenameText}`}>
                    {personalInformation.physicalDocFileName}
                  </span>
                  <ButtonWithIcon
                    onClick={() => handleRemoveFile('personal')}
                    variation="tertiary"
                    icon={<IconDelete />}
                    className={`${handles.documentRemoveButton}`}
                  />
                </div>
              ) : (
                <button
                  onClick={() => handlePageModeChange('personal')}
                  className={`${handles.documentUploadButton} vtex-button bw1 ba fw5 v-mid relative pa0 lh-solid br2 min-h-regular t-action bg-action-secondary b--action-secondary c-on-action-secondary hover-bg-action-secondary hover-b--action-secondary hover-c-on-action-secondary pointer`}
                  type="button"
                >
                  <div
                    className={`${handles.documentUploadButtonText} vtex-button__label flex items-center justify-center h-100 ph6`}
                    style={{ paddingTop: '0.25em', paddingBottom: '0.32em' }}
                  >
                    <FormattedMessage id="store/flowFinance.accountCreate.documents.uploadLabel" />
                  </div>
                </button>
              )}
            </div>
            <div className={`${handles.documentTypeContainer} w-50 pa2 tc`}>
              <img
                src={
                  !businessInformation.physicalDocFile
                    ? IconContratoGray
                    : IconContrato
                }
                alt="ID Icon"
                className={`${handles.documentTypeIcon}`}
              />
              <div
                className={`${handles.documentTypeLabel} ma3 ${
                  !businessInformation.physicalDocFile ? 'c-muted-4' : ''
                }`}
              >
                <FormattedMessage id="store/flowFinance.accountCreate.documents.businessIdLabel" />
              </div>
              {businessInformation.physicalDocFileName !== '' ? (
                <div
                  className={`${handles.documentFilenameContainer} ma3 flex flex-row justify-center items-center`}
                >
                  <span className={`${handles.documentFilenameText}`}>
                    {businessInformation.physicalDocFileName}
                  </span>
                  <ButtonWithIcon
                    onClick={() => handleRemoveFile('business')}
                    variation="tertiary"
                    icon={<IconDelete />}
                    className={`${handles.documentRemoveButton}`}
                  />
                </div>
              ) : (
                <Button
                  variation="secondary"
                  onClick={() => handlePageModeChange('business')}
                  className={`${handles.documentUploadButton}`}
                >
                  <span className={`${handles.documentUploadButtonText}`}>
                    <FormattedMessage id="store/flowFinance.accountCreate.documents.uploadLabel" />
                  </span>
                </Button>
              )}
            </div>
          </div>
          <Divider orientation="horizontal" />
          {documentsValid && (
            <div className={`${handles.tosAcceptanceContainer} mt7 mb7`}>
              <h2>
                <FormattedMessage id="store/flowFinance.accountCreate.tosTitle" />
              </h2>
              <div className={`${handles.tosIframeContainer} mb7`}>
                <iframe
                  title="Flow Finance Loan Agreement"
                  width="95%"
                  height={400}
                  src={tosPdfPath}
                  data-testid="embedded-pdf"
                  className="db center"
                  frameBorder="0"
                  onLoad={() => setPdfLoaded(true)}
                />
              </div>
              <Checkbox
                label={
                  <span className={`${handles.tosAcceptanceCheckboxLabel}`}>
                    <FormattedMessage id="store/flowFinance.accountCreate.tosCheckboxLabel" />
                  </span>
                }
                className={`${handles.tosAcceptanceCheckbox} mt4`}
                checked={tosAccepted}
                id="tos-accept"
                name="tos-accept"
                value="tos-accept"
                onChange={() => setTosAccepted(!tosAccepted)}
              />
            </div>
          )}
          <div className={`${handles.documentsPageButtonContainer} mt6`}>
            <Button
              variation="primary"
              disabled={!documentsValid || !tosAccepted || !pdfLoaded}
              isLoading={isLoading}
              onClick={() => handleContinueClick()}
            >
              <FormattedMessage id="store/flowFinance.accountCreate.submitApplication" />
            </Button>
          </div>
          {submitError && (
            <div className={`${handles.documentsPageErrorContainer} mt2 red`}>
              {submitError}
            </div>
          )}
        </div>
      )
    }
  }
}

export default injectIntl(DocumentsPage)

```


[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 flow-finance-components/react/components/AccountCreatePages/PreQualifyPage.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect, useState } from 'react'
import {
  defineMessages,
  injectIntl,
  WrappedComponentProps,
  FormattedMessage,
} from 'react-intl'
import { useQuery, useMutation } from 'react-apollo'
import { useCssHandles } from 'vtex.css-handles'
import { Input, Spinner, Button, Divider } from 'vtex.styleguide'

import {
  useAccountCreateState,
  useAccountCreateDispatch,
} from '../AccountCreateContext'
import { formatCNPJ } from './utils/formatters'
import Profile from '../../graphql/Profile.graphql'
import PreQualify from '../../graphql/PreQualify.graphql'

interface PreQualifyProps {
  handleExit: () => void
  settings: Settings
}

interface Settings {
  storeName: string
  storePaymentName: string
}

const CSS_HANDLES = [
  'preQualifyPageContainer',
  'preQualifyInstructions',
  'preQualifySection',
  'preQualifyPageButtonContainer',
] as const

const messages = defineMessages({
  cnpjLabel: {
    defaultMessage: 'CNPJ',
    id: 'store/flowFinance.accountCreate.cnpjLabel',
  },
  cnpjError: {
    defaultMessage: 'Please enter a valid CNPJ',
    id: 'store/flowFinance.accountCreate.cnpjError',
  },
})

const PreQualifyPage: StorefrontFunctionComponent<PreQualifyProps &
  WrappedComponentProps> = ({ intl, handleExit, settings }) => {
  const { data, loading } = useQuery(Profile)
  const [preQualified, setPreQualified] = useState(true)
  const { businessInformation, currentPage } = useAccountCreateState()
  const dispatch = useAccountCreateDispatch()
  const handles = useCssHandles(CSS_HANDLES)

  const [formattedCNPJ, setFormattedCNPJ] = useState('')
  const [isLoading, setIsLoading] = useState(false)
  const [showError, setShowError] = useState(false)

  const [checkPreQualify] = useMutation(PreQualify)

  useEffect(() => {
    if (!data?.profile) return
    if (data.profile.corporateDocument) {
      const cleanDocument = data.profile.corporateDocument.replace(/[^\d]/g, '')
      dispatch({
        type: 'SET_BUSINESS_FIELD',
        args: {
          field: 'businessId',
          value: cleanDocument,
        },
      })
      setFormattedCNPJ(formatCNPJ(cleanDocument))
      setShowError(true)
    }
    dispatch({
      type: 'SET_BUSINESS_FIELD',
      args: {
        field: 'email',
        value: data.profile.email,
      },
    })
    dispatch({
      type: 'SET_PERSONAL_FIELD',
      args: {
        field: 'email',
        value: data.profile.email,
      },
    })
    if (data.profile.corporateName) {
      dispatch({
        type: 'SET_BUSINESS_FIELD',
        args: {
          field: 'name',
          value: data.profile.corporateName,
        },
      })
    }
    if (data.profile.tradeName) {
      dispatch({
        type: 'SET_BUSINESS_FIELD',
        args: {
          field: 'legalName',
          value: data.profile.tradeName,
        },
      })
    }
    if (data.profile.businessPhone) {
      dispatch({
        type: 'SET_BUSINESS_FIELD',
        args: {
          field: 'phoneNumber',
          value: data.profile.businessPhone.replace('+55', ''),
        },
      })
    }
    if (data.profile.firstName) {
      dispatch({
        type: 'SET_PERSONAL_FIELD',
        args: {
          field: 'firstName',
          value: data.profile.firstName,
        },
      })
    }
    if (data.profile.lastName) {
      dispatch({
        type: 'SET_PERSONAL_FIELD',
        args: {
          field: 'lastName',
          value: data.profile.lastName,
        },
      })
    }
    if (data.profile.homePhone) {
      dispatch({
        type: 'SET_PERSONAL_FIELD',
        args: {
          field: 'phoneNumber',
          value: data.profile.homePhone.replace('+55', ''),
        },
      })
    }
  }, [data, dispatch])

  async function submitPreQualify() {
    setIsLoading(true)
    checkPreQualify({
      variables: { cnpj: businessInformation.businessId },
    }).then(response => {
      setIsLoading(false)
      if (!response.data || !response.data.checkPreQualify) {
        setPreQualified(false)
        return
      }
      dispatch({
        type: 'SET_CURRENT_PAGE',
        args: {
          page: currentPage + 1,
        },
      })
      window.scrollTo(0, 0)
    })
  }

  if (loading) return <Spinner />

  if (!preQualified)
    return (
      <div className={handles.preQualifyPageContainer}>
        <div className={`${handles.preQualifyInstructions} mb6`}>
          <FormattedMessage
            id="store/flowFinance.accountCreate.preQualify.notQualifiedMessage"
            values={{
              storePaymentName: settings.storePaymentName,
              lineBreak: <br />,
            }}
          />
        </div>
        <Divider orientation="horizontal" />
        <div className={`${handles.preQualifyPageButtonContainer} mt6`}>
          <Button variation="primary" onClick={() => handleExit()}>
            <FormattedMessage id="store/flowFinance.accountCreate.continueShopping" />
          </Button>
        </div>
      </div>
    )

  return (
    <div className={handles.preQualifyPageContainer}>
      <div className={`${handles.preQualifyInstructions} mb6`}>
        <FormattedMessage
          id="store/flowFinance.accountCreate.preQualify.instructions"
          values={{
            storeName: settings.storeName,
          }}
        />
      </div>
      <section className={`${handles.preQualifySection} mb6`}>
        <Input
          label={intl.formatMessage(messages.cnpjLabel)}
          value={formattedCNPJ}
          onChange={(e: React.FormEvent<HTMLInputElement>) => {
            const newValue = e.currentTarget.value
              .replace(/[^\d]/g, '')
              .slice(0, 14)
            setFormattedCNPJ(() => formatCNPJ(newValue))
            dispatch({
              type: 'SET_BUSINESS_FIELD',
              args: {
                field: 'businessId',
                value: newValue,
              },
            })
          }}
          onBlur={() => setShowError(true)}
          errorMessage={
            showError && businessInformation.businessId.length !== 14
              ? intl.formatMessage(messages.cnpjError)
              : ''
          }
        />
      </section>
      <Divider orientation="horizontal" />
      <div className={`${handles.preQualifyPageButtonContainer} mt6`}>
        <Button
          variation="primary"
          disabled={businessInformation.businessId.length !== 14}
          isLoading={isLoading}
          onClick={() => submitPreQualify()}
        >
          <FormattedMessage id="store/flowFinance.accountCreate.preQualify.submit" />
        </Button>
      </div>
    </div>
  )
}

export default injectIntl(PreQualifyPage)

```


[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 flow-finance-components/react/components/AccountCreatePages/utils/formatters.tsx from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
export function formatCNPJ(value: string | undefined) {
  if (!value) return ''
  const currentValue = value.replace(/[^\d]/g, '')
  const cvLength = currentValue.length

  if (cvLength < 3) return currentValue
  if (cvLength < 6)
    return `${currentValue.slice(0, 2)}.${currentValue.slice(2)}`
  if (cvLength < 9)
    return `${currentValue.slice(0, 2)}.${currentValue.slice(
      2,
      5
    )}.${currentValue.slice(5)}`
  if (cvLength < 13)
    return `${currentValue.slice(0, 2)}.${currentValue.slice(
      2,
      5
    )}.${currentValue.slice(5, 8)}/${currentValue.slice(8)}`
  return `${currentValue.slice(0, 2)}.${currentValue.slice(
    2,
    5
  )}.${currentValue.slice(5, 8)}/${currentValue.slice(
    8,
    12
  )}-${currentValue.slice(12)}`
}

export function formatCPF(value: string | undefined) {
  if (!value) return ''
  const currentValue = value.replace(/[^\d]/g, '')
  const cvLength = currentValue.length

  if (cvLength < 4) return currentValue
  if (cvLength < 7)
    return `${currentValue.slice(0, 3)}.${currentValue.slice(3)}`
  if (cvLength < 10)
    return `${currentValue.slice(0, 3)}.${currentValue.slice(
      3,
      6
    )}.${currentValue.slice(6)}`
  if (cvLength > 9)
    return `${currentValue.slice(0, 3)}.${currentValue.slice(
      3,
      6
    )}.${currentValue.slice(6, 9)}-${currentValue.slice(9)}`
  return ''
}

export function formatRG(value: string | undefined) {
  if (!value) return ''
  const currentValue = value.replace(/[^\d]/g, '')
  const cvLength = currentValue.length

  if (cvLength < 3) return currentValue
  if (cvLength < 4)
    return `${currentValue.slice(0, 2)}.${currentValue.slice(2)}`
  if (cvLength < 9)
    return `${currentValue.slice(0, 2)}.${currentValue.slice(
      2,
      5
    )}.${currentValue.slice(5)}`
  if (cvLength > 8)
    return `${currentValue.slice(0, 2)}.${currentValue.slice(
      2,
      5
    )}.${currentValue.slice(5, 8)}-${currentValue.slice(8)}`
  return ''
}

export function formatRGExp(value: string | undefined) {
  if (!value) return ''
  const currentValue = value.replace(/[^\d]/g, '')
  const cvLength = currentValue.length

  if (cvLength < 3) return currentValue
  if (cvLength < 4)
    return `${currentValue.slice(0, 2)}/${currentValue.slice(2)}`
  if (cvLength < 6)
    return `${currentValue.slice(0, 2)}/${currentValue.slice(
      2,
      4
    )}/${currentValue.slice(4)}`
  return ''
}

export function formatPhone(value: string | undefined) {
  if (!value) return ''
  const currentValue = value.replace(/[^\d]/g, '')
  const cvLength = currentValue.length

  if (cvLength < 3) return currentValue
  if (cvLength < 4)
    return `(${currentValue.slice(0, 2)}) ${currentValue.slice(2)}`
  if (parseInt(currentValue.slice(2, 3), 10) < 6) {
    if (cvLength < 7) {
      return `(${currentValue.slice(0, 2)}) ${currentValue.slice(2)}`
    }
    if (cvLength <= 10) {
      return `(${currentValue.slice(0, 2)}) ${currentValue.slice(
        2,
        6
      )}-${currentValue.slice(6)}`
    }
  }
  if (cvLength < 8) {
    return `(${currentValue.slice(0, 2)}) ${currentValue.slice(
      2,
      3
    )} ${currentValue.slice(3)}`
  }
  return `(${currentValue.slice(0, 2)}) ${currentValue.slice(
    2,
    3
  )} ${currentValue.slice(3, 7)}-${currentValue.slice(7)}`
}

```


[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 flow-finance-components/react/components/AccountCreatePages/styles/global.css from the flow-finance-components VTEX app.

[Assistant]
Here's the implementation:

```css
.vtex-flow-finance-components-1-x-AccountCreatePageContainer
  .styleguide__pageBlock {
  max-width: 800px;
  margin: 0 auto;
}

.vtex-flow-finance-components-1-x-businessAddressSection > div,
.vtex-flow-finance-components-1-x-personalAddressSection > div {
  display: -webkit-box;
  display: flex;
  flex-flow: row wrap;
  -webkit-box-direction: normal;
  -webkit-box-orient: horizontal;
}

.vtex-flow-finance-components-1-x-accountCreateStepNumber svg {
  color: white;
}

.vtex-flow-finance-components-1-x-businessAddressSection
  .vtex-address-form__receiverName,
.vtex-flow-finance-components-1-x-personalAddressSection
  .vtex-address-form__receiverName {
  display: none;
}

.vtex-flow-finance-components-1-x-businessAddressSection
  .vtex-address-form__postalCode
  > div,
.vtex-flow-finance-components-1-x-personalAddressSection
  .vtex-address-form__postalCode
  > div {
  display: none;
}

@media screen and (min-width: 20em) {
  .vtex-flow-finance-components-1-x-businessAddressSection > div > *,
  .vtex-flow-finance-components-1-x-personalAddressSection > div > *,
  .vtex-address-form__postalCode {
    width: 100%;
    padding: 0.25rem;
  }
}

@media screen and (min-width: 40em) {
  .vtex-flow-finance-components-1-x-businessAddressSection > div > *,
  .vtex-flow-finance-components-1-x-personalAddressSection > div > *,
  .vtex-address-form__postalCode {
    width: 50%;
    padding: 0.25rem;
  }
}

```


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

[User] Show me the implementation of events-example-subscriber/docs/README.md from the events-example-subscriber VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Events Example

App in VTEX IO designed to send events using a time interval and IO's Events system.

### How it works?

```


[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 events-example-subscriber/node/service.json from the events-example-subscriber VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "memory": 128,
  "ttl": 10,
  "timeout": 2,
  "minReplicas": 2,
  "maxReplicas": 10,
  "workers": 4,
  "events": {
    "listener": {
      "sender": "vtex.events-example-publisher",
      "topics": ["random-number-generated"]
    }
  }
}

```


[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 events-example-subscriber/node/package.json from the events-example-subscriber VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "dependencies": {
    "co-body": "^6.0.0",
    "jwt-decode": "^3.1.2"
  },
  "devDependencies": {
    "@types/co-body": "^0.0.3",
    "@types/node": "12.x",
    "@vtex/api": "6.45.12",
    "eslint": "^6.3.0",
    "eslint-config-vtex": "^11.0.0",
    "prettier": "^1.18.2",
    "typescript": "3.9.7"
  },
  "scripts": {
    "lint": "tsc --noEmit --pretty && eslint -c .eslintrc.json --fix './**/*.ts'"
  },
  "version": "0.7.2"
}

```


[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 events-example-subscriber/node/constants.ts from the events-example-subscriber VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const ENABLED_GLOBALLY = true

export const EVENT_EXAMPLE = 'batata'

export const TIME_INTERVAL = 10

```


[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 events-example-subscriber/node/index.ts from the events-example-subscriber VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  Service,
  IOClients,
  ParamsContext,
  ServiceContext,
  RecorderState,
} from '@vtex/api'
import { listener } from './events/listenEvent'

declare global {
  type Context = ServiceContext<IOClients, State>

  interface State extends RecorderState {
    code: number
  }
}

export default new Service<IOClients, State, ParamsContext>({
  events: {
    listener,
  },
})

```


[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 events-example-subscriber/node/utils/randomNumber.ts from the events-example-subscriber VTEX app.

[Assistant]
Here's the implementation:

```typescript
export function getRandomNumber() {
  return Math.round(Math.random() * 100)
}

```


[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 events-example-subscriber/node/utils/cachedContext.ts from the events-example-subscriber VTEX app.

[Assistant]
Here's the implementation:

```typescript
let context: any = null

export const getCacheContext = () => {
  return context
}

export function setCacheContext(ctx: any) {
  context = ctx
}

```


[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 events-example-subscriber/node/events/listenEvent.ts from the events-example-subscriber VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { EventContext, IOClients } from '@vtex/api'

export async function listener(ctx: EventContext<IOClients>) {
  console.log('Evento recebido com o paylod ', ctx.body)
  return true
}

```