mirror of
https://github.com/oceanprotocol/market.git
synced 2024-06-30 22:01:44 +02:00
refactor to make live validation work
This commit is contained in:
parent
041dfcee08
commit
2a13a760bd
|
@ -10,7 +10,7 @@ const cx = classNames.bind(styles)
|
||||||
|
|
||||||
export interface InputProps {
|
export interface InputProps {
|
||||||
name: string
|
name: string
|
||||||
label?: string
|
label?: string | ReactNode
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
required?: boolean
|
required?: boolean
|
||||||
help?: string
|
help?: string
|
||||||
|
@ -54,8 +54,7 @@ export default function Input(props: Partial<InputProps>): ReactElement {
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const hasError =
|
const hasError =
|
||||||
props.form?.touched[field.name] &&
|
props.form?.touched[field.name] && props.form?.errors[field.name]
|
||||||
typeof props.form.errors[field.name] === 'string'
|
|
||||||
|
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
field: true,
|
field: true,
|
||||||
|
@ -72,7 +71,7 @@ export default function Input(props: Partial<InputProps>): ReactElement {
|
||||||
</Label>
|
</Label>
|
||||||
<InputElement small={small} {...field} {...props} />
|
<InputElement small={small} {...field} {...props} />
|
||||||
|
|
||||||
{field && (
|
{field && field.name !== 'price' && (
|
||||||
<div className={styles.error}>
|
<div className={styles.error}>
|
||||||
<ErrorMessage name={field.name} />
|
<ErrorMessage name={field.name} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,24 +6,23 @@ import { ReactComponent as Logo } from '../../../../images/logo.svg'
|
||||||
import Conversion from '../../../atoms/Price/Conversion'
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
import { DataTokenOptions } from '@oceanprotocol/react'
|
import { DataTokenOptions } from '@oceanprotocol/react'
|
||||||
import RefreshName from './RefreshName'
|
import RefreshName from './RefreshName'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
|
||||||
export default function Coin({
|
export default function Coin({
|
||||||
datatokenOptions,
|
datatokenOptions,
|
||||||
name,
|
name,
|
||||||
value,
|
|
||||||
weight,
|
weight,
|
||||||
onOceanChange,
|
|
||||||
generateName,
|
generateName,
|
||||||
readOnly
|
readOnly
|
||||||
}: {
|
}: {
|
||||||
datatokenOptions: DataTokenOptions
|
datatokenOptions: DataTokenOptions
|
||||||
name: string
|
name: string
|
||||||
value: string
|
|
||||||
weight: string
|
weight: string
|
||||||
onOceanChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
|
||||||
generateName?: () => void
|
generateName?: () => void
|
||||||
readOnly?: boolean
|
readOnly?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const [field, meta, helpers] = useField(name)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.coin}>
|
<div className={styles.coin}>
|
||||||
<figure className={styles.icon}>
|
<figure className={styles.icon}>
|
||||||
|
@ -43,15 +42,15 @@ export default function Coin({
|
||||||
|
|
||||||
<div className={styles.data}>
|
<div className={styles.data}>
|
||||||
<InputElement
|
<InputElement
|
||||||
value={value}
|
value={field.value}
|
||||||
name={name}
|
name={name}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={onOceanChange}
|
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
prefix={datatokenOptions?.symbol || 'DT'}
|
prefix={datatokenOptions?.symbol || 'DT'}
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
{datatokenOptions?.symbol === 'OCEAN' && (
|
{datatokenOptions?.symbol === 'OCEAN' && (
|
||||||
<Conversion price={value} className={stylesIndex.conversion} />
|
<Conversion price={field.value} className={stylesIndex.conversion} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,22 +15,18 @@ export default function Dynamic({
|
||||||
ocean,
|
ocean,
|
||||||
priceOptions,
|
priceOptions,
|
||||||
datatokenOptions,
|
datatokenOptions,
|
||||||
onOceanChange,
|
|
||||||
onLiquidityProviderFeeChange,
|
|
||||||
generateName,
|
generateName,
|
||||||
content
|
content
|
||||||
}: {
|
}: {
|
||||||
ocean: string
|
ocean: string
|
||||||
priceOptions: PriceOptions
|
priceOptions: PriceOptions
|
||||||
datatokenOptions: DataTokenOptions
|
datatokenOptions: DataTokenOptions
|
||||||
onOceanChange: (event: ChangeEvent<HTMLInputElement>) => void
|
|
||||||
onLiquidityProviderFeeChange: (event: ChangeEvent<HTMLInputElement>) => void
|
|
||||||
generateName: () => void
|
generateName: () => void
|
||||||
content: any
|
content: any
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { appConfig } = useSiteMetadata()
|
const { appConfig } = useSiteMetadata()
|
||||||
const { account, balance, chainId, refreshBalance } = useOcean()
|
const { account, balance, chainId, refreshBalance } = useOcean()
|
||||||
const { weightOnDataToken, tokensToMint, liquidityProviderFee } = priceOptions
|
const { weightOnDataToken } = priceOptions
|
||||||
|
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
const correctNetwork = isCorrectNetwork(chainId)
|
const correctNetwork = isCorrectNetwork(chainId)
|
||||||
|
@ -77,33 +73,25 @@ export default function Dynamic({
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<h4 className={styles.title}>
|
<h4 className={styles.title}>
|
||||||
Data Token Liquidity Pool{' '}
|
Datatoken Liquidity Pool <Tooltip content={content.tooltips.poolInfo} />
|
||||||
<Tooltip content={content.tooltips.poolInfo} />
|
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div className={styles.tokens}>
|
<div className={styles.tokens}>
|
||||||
<Coin
|
<Coin
|
||||||
name="ocean"
|
name="price.price"
|
||||||
datatokenOptions={{ symbol: 'OCEAN', name: 'Ocean Token' }}
|
datatokenOptions={{ symbol: 'OCEAN', name: 'Ocean Token' }}
|
||||||
value={ocean}
|
|
||||||
weight={`${100 - Number(Number(weightOnDataToken) * 10)}%`}
|
weight={`${100 - Number(Number(weightOnDataToken) * 10)}%`}
|
||||||
onOceanChange={onOceanChange}
|
|
||||||
/>
|
/>
|
||||||
<Coin
|
<Coin
|
||||||
name="tokensToMint"
|
name="price.tokensToMint"
|
||||||
datatokenOptions={datatokenOptions}
|
datatokenOptions={datatokenOptions}
|
||||||
value={tokensToMint.toString()}
|
|
||||||
weight={`${Number(weightOnDataToken) * 10}%`}
|
weight={`${Number(weightOnDataToken) * 10}%`}
|
||||||
generateName={generateName}
|
generateName={generateName}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Fees
|
<Fees tooltips={content.tooltips} />
|
||||||
liquidityProviderFee={liquidityProviderFee}
|
|
||||||
onLiquidityProviderFeeChange={onLiquidityProviderFeeChange}
|
|
||||||
tooltips={content.tooltips}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<footer className={styles.summary}>
|
<footer className={styles.summary}>
|
||||||
You will get: <br />
|
You will get: <br />
|
||||||
|
|
|
@ -1,57 +1,68 @@
|
||||||
import React, { ChangeEvent } from 'react'
|
import React, { ChangeEvent, ReactElement } from 'react'
|
||||||
import InputElement from '../../../atoms/Input/InputElement'
|
|
||||||
import Label from '../../../atoms/Input/Label'
|
|
||||||
import Tooltip from '../../../atoms/Tooltip'
|
import Tooltip from '../../../atoms/Tooltip'
|
||||||
import styles from './Fees.module.css'
|
import styles from './Fees.module.css'
|
||||||
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
import Input from '../../../atoms/Input'
|
||||||
|
|
||||||
export default function Fees({
|
export default function Fees({
|
||||||
liquidityProviderFee,
|
|
||||||
onLiquidityProviderFeeChange,
|
|
||||||
tooltips
|
tooltips
|
||||||
}: {
|
}: {
|
||||||
liquidityProviderFee: string
|
|
||||||
onLiquidityProviderFeeChange: (event: ChangeEvent<HTMLInputElement>) => void
|
|
||||||
tooltips: { [key: string]: string }
|
tooltips: { [key: string]: string }
|
||||||
}) {
|
}): ReactElement {
|
||||||
const { appConfig } = useSiteMetadata()
|
const { appConfig } = useSiteMetadata()
|
||||||
|
const [field, meta, helpers] = useField('price.liquidityProviderFee')
|
||||||
|
|
||||||
|
// TODO: trigger Yup inline validation
|
||||||
|
function handleLiquidityProviderFeeChange(
|
||||||
|
event: ChangeEvent<HTMLInputElement>
|
||||||
|
) {
|
||||||
|
helpers.setValue(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.fees}>
|
<>
|
||||||
<div>
|
<div className={styles.fees}>
|
||||||
<Label htmlFor="liquidityProviderFee">
|
<Input
|
||||||
Liquidity Provider Fee{' '}
|
label={
|
||||||
<Tooltip content={tooltips.liquidityProviderFee} />
|
<>
|
||||||
</Label>
|
Liquidity Provider Fee
|
||||||
<InputElement
|
<Tooltip content={tooltips.liquidityProviderFee} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
type="number"
|
type="number"
|
||||||
value={liquidityProviderFee}
|
value={field.value}
|
||||||
name="liquidityProviderFee"
|
name="price.liquidityProviderFee"
|
||||||
postfix="%"
|
postfix="%"
|
||||||
onChange={onLiquidityProviderFeeChange}
|
onChange={handleLiquidityProviderFeeChange}
|
||||||
min="0.1"
|
min="0.1"
|
||||||
max="0.9"
|
max="0.9"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
small
|
small
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div>
|
<Input
|
||||||
<Label htmlFor="communityFee">
|
label={
|
||||||
Community Fee <Tooltip content={tooltips.communityFee} />
|
<>
|
||||||
</Label>
|
Community Fee
|
||||||
<InputElement
|
<Tooltip content={tooltips.communityFee} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
value="0.1"
|
value="0.1"
|
||||||
name="communityFee"
|
name="communityFee"
|
||||||
postfix="%"
|
postfix="%"
|
||||||
readOnly
|
readOnly
|
||||||
small
|
small
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div>
|
<Input
|
||||||
<Label htmlFor="marketplaceFee">
|
label={
|
||||||
Marketplace Fee <Tooltip content={tooltips.marketplaceFee} />
|
<>
|
||||||
</Label>
|
Marketplace Fee
|
||||||
<InputElement
|
<Tooltip content={tooltips.marketplaceFee} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
value={appConfig.marketFeeAmount}
|
value={appConfig.marketFeeAmount}
|
||||||
name="marketplaceFee"
|
name="marketplaceFee"
|
||||||
postfix="%"
|
postfix="%"
|
||||||
|
@ -59,6 +70,7 @@ export default function Fees({
|
||||||
small
|
small
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{meta.error && meta.touched && <div>{meta.error}</div>}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,41 @@
|
||||||
import React, { ReactElement, ChangeEvent } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
import styles from './Fixed.module.css'
|
import styles from './Fixed.module.css'
|
||||||
import FormHelp from '../../../atoms/Input/Help'
|
import FormHelp from '../../../atoms/Input/Help'
|
||||||
import Label from '../../../atoms/Input/Label'
|
|
||||||
import InputElement from '../../../atoms/Input/InputElement'
|
|
||||||
import Conversion from '../../../atoms/Price/Conversion'
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
import { DataTokenOptions } from '@oceanprotocol/react'
|
import { DataTokenOptions } from '@oceanprotocol/react'
|
||||||
import RefreshName from './RefreshName'
|
import RefreshName from './RefreshName'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
import Input from '../../../atoms/Input'
|
||||||
|
|
||||||
export default function Fixed({
|
export default function Fixed({
|
||||||
ocean,
|
|
||||||
datatokenOptions,
|
datatokenOptions,
|
||||||
onChange,
|
|
||||||
generateName,
|
generateName,
|
||||||
content
|
content
|
||||||
}: {
|
}: {
|
||||||
ocean: string
|
|
||||||
datatokenOptions: DataTokenOptions
|
datatokenOptions: DataTokenOptions
|
||||||
onChange: (event: ChangeEvent<HTMLInputElement>) => void
|
|
||||||
generateName: () => void
|
generateName: () => void
|
||||||
content: any
|
content: any
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const [field, meta, helpers] = useField('price.price')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.fixed}>
|
<div className={styles.fixed}>
|
||||||
<FormHelp className={stylesIndex.help}>{content.info}</FormHelp>
|
<FormHelp className={stylesIndex.help}>{content.info}</FormHelp>
|
||||||
|
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<div className={styles.form}>
|
<div className={styles.form}>
|
||||||
<Label htmlFor="ocean">Ocean Token</Label>
|
<Input
|
||||||
<InputElement
|
label="Ocean Token"
|
||||||
value={ocean}
|
value={field.value}
|
||||||
name="ocean"
|
name="price.price"
|
||||||
type="number"
|
type="number"
|
||||||
prefix="OCEAN"
|
prefix="OCEAN"
|
||||||
onChange={onChange}
|
{...field}
|
||||||
/>
|
/>
|
||||||
<Conversion price={ocean} className={stylesIndex.conversion} />
|
<Conversion price={field.value} className={stylesIndex.conversion} />
|
||||||
</div>
|
</div>
|
||||||
|
{meta.error && meta.touched && <div>{meta.error}</div>}
|
||||||
{datatokenOptions && (
|
{datatokenOptions && (
|
||||||
<div className={styles.datatoken}>
|
<div className={styles.datatoken}>
|
||||||
<h4>
|
<h4>
|
||||||
|
|
|
@ -44,26 +44,11 @@ export default function Price(props: InputProps): ReactElement {
|
||||||
const content = data.content.edges[0].node.childPagesJson.price
|
const content = data.content.edges[0].node.childPagesJson.price
|
||||||
const { ocean } = useOcean()
|
const { ocean } = useOcean()
|
||||||
|
|
||||||
const [field, meta, helpers] = useField(props)
|
const [field, meta, helpers] = useField(props.name)
|
||||||
const priceOptions: PriceOptions = field.value
|
const priceOptions: PriceOptions = field.value
|
||||||
|
|
||||||
const [amountOcean, setAmountOcean] = useState('1')
|
|
||||||
const [tokensToMint, setTokensToMint] = useState<number>()
|
const [tokensToMint, setTokensToMint] = useState<number>()
|
||||||
const [datatokenOptions, setDatatokenOptions] = useState<DataTokenOptions>()
|
const [datatokenOptions, setDatatokenOptions] = useState<DataTokenOptions>()
|
||||||
const [liquidityProviderFee, setLiquidityProviderFee] = useState<string>(
|
|
||||||
priceOptions.liquidityProviderFee
|
|
||||||
)
|
|
||||||
|
|
||||||
function handleOceanChange(event: ChangeEvent<HTMLInputElement>) {
|
|
||||||
setAmountOcean(event.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: trigger Yup inline validation
|
|
||||||
function handleLiquidityProviderFeeChange(
|
|
||||||
event: ChangeEvent<HTMLInputElement>
|
|
||||||
) {
|
|
||||||
setLiquidityProviderFee(event.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTabChange(tabName: string) {
|
function handleTabChange(tabName: string) {
|
||||||
const type = tabName.toLowerCase()
|
const type = tabName.toLowerCase()
|
||||||
|
@ -79,14 +64,10 @@ export default function Price(props: InputProps): ReactElement {
|
||||||
// Always update everything when amountOcean changes
|
// Always update everything when amountOcean changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tokensToMint =
|
const tokensToMint =
|
||||||
Number(amountOcean) * Number(priceOptions.weightOnDataToken)
|
Number(field.value.price) * Number(priceOptions.weightOnDataToken)
|
||||||
setTokensToMint(tokensToMint)
|
setTokensToMint(tokensToMint)
|
||||||
helpers.setValue({ ...field.value, price: amountOcean, tokensToMint })
|
helpers.setValue({ ...field.value, tokensToMint })
|
||||||
}, [amountOcean])
|
}, [field.value.price])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
helpers.setValue({ ...field.value, liquidityProviderFee })
|
|
||||||
}, [liquidityProviderFee])
|
|
||||||
|
|
||||||
// Generate new DT name & symbol
|
// Generate new DT name & symbol
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -98,9 +79,7 @@ export default function Price(props: InputProps): ReactElement {
|
||||||
title: content.fixed.title,
|
title: content.fixed.title,
|
||||||
content: (
|
content: (
|
||||||
<Fixed
|
<Fixed
|
||||||
ocean={amountOcean}
|
|
||||||
datatokenOptions={datatokenOptions}
|
datatokenOptions={datatokenOptions}
|
||||||
onChange={handleOceanChange}
|
|
||||||
generateName={generateName}
|
generateName={generateName}
|
||||||
content={content.fixed}
|
content={content.fixed}
|
||||||
/>
|
/>
|
||||||
|
@ -110,11 +89,9 @@ export default function Price(props: InputProps): ReactElement {
|
||||||
title: content.dynamic.title,
|
title: content.dynamic.title,
|
||||||
content: (
|
content: (
|
||||||
<Dynamic
|
<Dynamic
|
||||||
ocean={amountOcean}
|
ocean={field.value.price}
|
||||||
priceOptions={{ ...priceOptions, tokensToMint, liquidityProviderFee }}
|
priceOptions={{ ...priceOptions, tokensToMint }}
|
||||||
datatokenOptions={datatokenOptions}
|
datatokenOptions={datatokenOptions}
|
||||||
onOceanChange={handleOceanChange}
|
|
||||||
onLiquidityProviderFeeChange={handleLiquidityProviderFeeChange}
|
|
||||||
generateName={generateName}
|
generateName={generateName}
|
||||||
content={content.dynamic}
|
content={content.dynamic}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -6,19 +6,17 @@ export const validationSchema = Yup.object().shape<MetadataPublishForm>({
|
||||||
// ---- required fields ----
|
// ---- required fields ----
|
||||||
name: Yup.string().required('Required'),
|
name: Yup.string().required('Required'),
|
||||||
author: Yup.string().required('Required'),
|
author: Yup.string().required('Required'),
|
||||||
price: Yup.object().shape({
|
price: Yup.object()
|
||||||
price: Yup.number().required('Required'),
|
.shape({
|
||||||
tokensToMint: Yup.number().required('Required'),
|
price: Yup.number().min(1, 'Must be greater than 0').required('Required'),
|
||||||
type: Yup.string()
|
tokensToMint: Yup.number().required('Required'),
|
||||||
.matches(/fixed|dynamic/g)
|
type: Yup.string()
|
||||||
.required('Required'),
|
.matches(/fixed|dynamic/g)
|
||||||
weightOnDataToken: Yup.string().required('Required'),
|
.required('Required'),
|
||||||
liquidityProviderFee: Yup.string()
|
weightOnDataToken: Yup.string().required('Required'),
|
||||||
.length(3)
|
liquidityProviderFee: Yup.string().min(0.1).max(0.9).required('Required')
|
||||||
.min(0.1)
|
})
|
||||||
.max(0.9)
|
.required('Required'),
|
||||||
.required('Required')
|
|
||||||
}),
|
|
||||||
files: Yup.array<FileMetadata>().required('Required').nullable(),
|
files: Yup.array<FileMetadata>().required('Required').nullable(),
|
||||||
description: Yup.string().required('Required'),
|
description: Yup.string().required('Required'),
|
||||||
license: Yup.string().required('Required'),
|
license: Yup.string().required('Required'),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user