mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Merge pull request #37 from oceanprotocol/feature/price
Custom price input
This commit is contained in:
commit
12d3b2f0a0
@ -35,11 +35,9 @@
|
|||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "cost",
|
"name": "price",
|
||||||
"label": "Price",
|
"label": "Price",
|
||||||
"help": "Set your price for accessing this data set in Ocean Tokens.",
|
|
||||||
"type": "price",
|
"type": "price",
|
||||||
"min": 1,
|
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
8
src/@types/MetaData.d.ts
vendored
8
src/@types/MetaData.d.ts
vendored
@ -5,12 +5,9 @@ import {
|
|||||||
ServiceMetadata
|
ServiceMetadata
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
|
|
||||||
export declare type AccessType = 'Download' | 'Compute'
|
|
||||||
|
|
||||||
export interface AdditionalInformationMarket extends AdditionalInformation {
|
export interface AdditionalInformationMarket extends AdditionalInformation {
|
||||||
links?: File[]
|
links?: File[]
|
||||||
termsAndConditions: boolean
|
termsAndConditions: boolean
|
||||||
access: AccessType | string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MetadataMarket extends Metadata {
|
export interface MetadataMarket extends Metadata {
|
||||||
@ -24,7 +21,10 @@ export interface MetadataPublishForm {
|
|||||||
files: string | File[]
|
files: string | File[]
|
||||||
author: string
|
author: string
|
||||||
license: string
|
license: string
|
||||||
cost: string
|
price: {
|
||||||
|
cost: number
|
||||||
|
tokensToMint: number
|
||||||
|
}
|
||||||
access: 'Download' | 'Compute' | string
|
access: 'Download' | 'Compute' | string
|
||||||
termsAndConditions: boolean
|
termsAndConditions: boolean
|
||||||
// ---- optional fields ----
|
// ---- optional fields ----
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Help.module.css'
|
import styles from './Help.module.css'
|
||||||
import Markdown from '../Markdown'
|
import Markdown from '../Markdown'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
|
||||||
const FormHelp = ({ children }: { children: string }): ReactElement => (
|
const cx = classNames.bind(styles)
|
||||||
<Markdown className={styles.help} text={children} />
|
|
||||||
)
|
const FormHelp = ({
|
||||||
|
children,
|
||||||
|
className
|
||||||
|
}: {
|
||||||
|
children: string
|
||||||
|
className?: string
|
||||||
|
}): ReactElement => {
|
||||||
|
const styleClasses = cx({
|
||||||
|
help: true,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
|
return <Markdown className={styleClasses} text={children} />
|
||||||
|
}
|
||||||
|
|
||||||
export default FormHelp
|
export default FormHelp
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
transition: 0.2s ease-out;
|
transition: 0.2s ease-out;
|
||||||
min-height: 43px;
|
min-height: 43px;
|
||||||
|
min-width: 0;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@ -33,7 +34,8 @@
|
|||||||
|
|
||||||
.input[readonly],
|
.input[readonly],
|
||||||
.input[disabled] {
|
.input[disabled] {
|
||||||
background-color: var(--brand-grey-lighter);
|
background-color: var(--brand-grey-dimmed);
|
||||||
|
color: var(--brand-grey-light);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
@ -84,6 +86,52 @@
|
|||||||
padding-left: 0.5rem;
|
padding-left: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prefixGroup,
|
||||||
|
.postfixGroup {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefixGroup input {
|
||||||
|
border-left: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.postfixGroup input {
|
||||||
|
border-right: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefix,
|
||||||
|
.postfix {
|
||||||
|
border: 1px solid var(--brand-grey-lighter);
|
||||||
|
min-height: 43px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: calc(var(--spacer) / 4);
|
||||||
|
padding-right: calc(var(--spacer) / 4);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
transition: border 0.2s ease-out;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefix {
|
||||||
|
border-top-left-radius: var(--border-radius);
|
||||||
|
border-bottom-left-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.postfix {
|
||||||
|
border-top-right-radius: var(--border-radius);
|
||||||
|
border-bottom-right-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input:focus + .postfix {
|
||||||
|
border-color: var(--brand-grey);
|
||||||
|
}
|
||||||
|
|
||||||
/* Size modifiers */
|
/* Size modifiers */
|
||||||
|
|
||||||
.small {
|
.small {
|
||||||
|
@ -4,9 +4,23 @@ import styles from './InputElement.module.css'
|
|||||||
import { InputProps } from '.'
|
import { InputProps } from '.'
|
||||||
import FilesInput from '../../molecules/FormFields/FilesInput'
|
import FilesInput from '../../molecules/FormFields/FilesInput'
|
||||||
import Terms from '../../molecules/FormFields/Terms'
|
import Terms from '../../molecules/FormFields/Terms'
|
||||||
|
import Price from '../../molecules/FormFields/Price'
|
||||||
|
|
||||||
|
const DefaultInput = (
|
||||||
|
{ name, type }: { name: string; type?: string },
|
||||||
|
props: InputProps
|
||||||
|
) => (
|
||||||
|
<input
|
||||||
|
id={name}
|
||||||
|
className={styles.input}
|
||||||
|
name={name}
|
||||||
|
{...props}
|
||||||
|
type={type || 'text'}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
export default function InputElement(props: InputProps): ReactElement {
|
export default function InputElement(props: InputProps): ReactElement {
|
||||||
const { type, options, rows, name, value } = props
|
const { type, options, rows, name, prefix, postfix } = props
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'select':
|
case 'select':
|
||||||
@ -56,18 +70,19 @@ export default function InputElement(props: InputProps): ReactElement {
|
|||||||
)
|
)
|
||||||
case 'files':
|
case 'files':
|
||||||
return <FilesInput name={name} {...props} />
|
return <FilesInput name={name} {...props} />
|
||||||
|
case 'price':
|
||||||
|
return <Price name={name} {...props} />
|
||||||
case 'terms':
|
case 'terms':
|
||||||
return <Terms name={name} {...props} />
|
return <Terms name={name} {...props} />
|
||||||
default:
|
default:
|
||||||
return (
|
return prefix || postfix ? (
|
||||||
<input
|
<div className={`${prefix ? styles.prefixGroup : styles.postfixGroup}`}>
|
||||||
id={name}
|
{prefix && <div className={styles.prefix}>{prefix}</div>}
|
||||||
className={styles.input}
|
<DefaultInput name={name} type={type || 'text'} />
|
||||||
name={name}
|
{postfix && <div className={styles.postfix}>{postfix}</div>}
|
||||||
{...props}
|
</div>
|
||||||
value={value || ''}
|
) : (
|
||||||
type={type || 'text'}
|
<DefaultInput name={name} type={type || 'text'} />
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { FormEvent, ChangeEvent, ReactElement } from 'react'
|
import React, { FormEvent, ChangeEvent, ReactElement, ReactNode } from 'react'
|
||||||
import InputElement from './InputElement'
|
import InputElement from './InputElement'
|
||||||
import Help from './Help'
|
import Help from './Help'
|
||||||
import Label from './Label'
|
import Label from './Label'
|
||||||
@ -31,8 +31,12 @@ export interface InputProps {
|
|||||||
pattern?: string
|
pattern?: string
|
||||||
min?: string
|
min?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
readOnly?: boolean
|
||||||
field?: any
|
field?: any
|
||||||
form?: any
|
form?: any
|
||||||
|
prefix?: string
|
||||||
|
postfix?: string
|
||||||
|
step?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Input(props: Partial<InputProps>): ReactElement {
|
export default function Input(props: Partial<InputProps>): ReactElement {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.conversion {
|
.conversion {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-small);
|
||||||
margin-left: calc(var(--spacer) / 6);
|
margin-left: calc(var(--spacer) / 6);
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,29 @@ import React, { useEffect, useState, ReactElement } from 'react'
|
|||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { fetchData, isBrowser } from '../../../utils'
|
import { fetchData, isBrowser } from '../../../utils'
|
||||||
import styles from './Conversion.module.css'
|
import styles from './Conversion.module.css'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
const currencies = 'EUR' // comma-separated list
|
const currencies = 'EUR' // comma-separated list
|
||||||
const url = `https://api.coingecko.com/api/v3/simple/price?ids=ocean-protocol&vs_currencies=${currencies}&include_24hr_change=true`
|
const url = `https://api.coingecko.com/api/v3/simple/price?ids=ocean-protocol&vs_currencies=${currencies}&include_24hr_change=true`
|
||||||
|
|
||||||
export default function Conversion({
|
export default function Conversion({
|
||||||
price,
|
price,
|
||||||
update = true
|
update = true,
|
||||||
|
className
|
||||||
}: {
|
}: {
|
||||||
price: string // expects price in OCEAN, not wei
|
price: string // expects price in OCEAN, not wei
|
||||||
update?: boolean
|
update?: boolean
|
||||||
|
className?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [priceEur, setPriceEur] = useState('0.00')
|
const [priceEur, setPriceEur] = useState('0.00')
|
||||||
|
|
||||||
|
const styleClasses = cx({
|
||||||
|
conversion: true,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
const onSuccess = async (data: { 'ocean-protocol': { eur: number } }) => {
|
const onSuccess = async (data: { 'ocean-protocol': { eur: number } }) => {
|
||||||
if (!data) return
|
if (!data) return
|
||||||
if (!price || price === '' || price === '0') {
|
if (!price || price === '' || price === '0') {
|
||||||
@ -45,5 +55,12 @@ export default function Conversion({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span className={styles.conversion}>≈ EUR {priceEur}</span>
|
return (
|
||||||
|
<span
|
||||||
|
className={styleClasses}
|
||||||
|
title="Approximation based on current spot price on Coingecko"
|
||||||
|
>
|
||||||
|
≈ EUR {priceEur}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import DataTable from 'react-data-table-component'
|
import DataTable from 'react-data-table-component'
|
||||||
|
|
||||||
export declare type AssetTablePagination = {
|
export declare type AssetTablePagination = {
|
||||||
|
26
src/components/atoms/Tabs.module.css
Normal file
26
src/components/atoms/Tabs.module.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
.tabList {
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||||
|
padding-top: calc(var(--spacer) / 2);
|
||||||
|
padding-bottom: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
display: inline-block;
|
||||||
|
padding: calc(var(--spacer) / 12) var(--spacer);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
text-transform: uppercase;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--brand-grey-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab[aria-selected='true'] {
|
||||||
|
background: var(--brand-black);
|
||||||
|
color: var(--brand-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabContent {
|
||||||
|
padding: var(--spacer);
|
||||||
|
}
|
33
src/components/atoms/Tabs.tsx
Normal file
33
src/components/atoms/Tabs.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React, { ReactElement, ReactNode } from 'react'
|
||||||
|
import { Tab, Tabs as ReactTabs, TabList, TabPanel } from 'react-tabs'
|
||||||
|
import styles from './Tabs.module.css'
|
||||||
|
|
||||||
|
interface TabsItem {
|
||||||
|
title: string
|
||||||
|
content: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Tabs({
|
||||||
|
items,
|
||||||
|
className
|
||||||
|
}: {
|
||||||
|
items: TabsItem[]
|
||||||
|
className?: string
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<ReactTabs className={`${className && className}`}>
|
||||||
|
<TabList className={styles.tabList}>
|
||||||
|
{items.map((item) => (
|
||||||
|
<Tab className={styles.tab} key={item.title}>
|
||||||
|
{item.title}
|
||||||
|
</Tab>
|
||||||
|
))}
|
||||||
|
</TabList>
|
||||||
|
<div className={styles.tabContent}>
|
||||||
|
{items.map((item) => (
|
||||||
|
<TabPanel key={item.title}>{item.content}</TabPanel>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ReactTabs>
|
||||||
|
)
|
||||||
|
}
|
@ -36,7 +36,7 @@ export default function FilesInput(props: InputProps): ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{typeof field.value === 'object' ? (
|
{field && typeof field.value === 'object' ? (
|
||||||
<FileInfo file={field.value[0]} removeItem={removeItem} />
|
<FileInfo file={field.value[0]} removeItem={removeItem} />
|
||||||
) : (
|
) : (
|
||||||
<FileInput
|
<FileInput
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
.advanced {
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance {
|
||||||
|
text-align: center;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
border: 1px solid var(--brand-grey-lighter);
|
||||||
|
border-right: 0;
|
||||||
|
margin-right: -3px;
|
||||||
|
padding: calc(var(--spacer) / 4.5) calc(var(--spacer) / 2);
|
||||||
|
border-top-left-radius: var(--border-radius);
|
||||||
|
border-bottom-left-radius: var(--border-radius);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance strong {
|
||||||
|
color: var(--brand-grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: var(--color-secondary);
|
||||||
|
text-align: center;
|
||||||
|
padding-bottom: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tokens {
|
||||||
|
display: grid;
|
||||||
|
margin-bottom: -2rem;
|
||||||
|
margin-left: -3rem;
|
||||||
|
margin-right: -3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 40rem) {
|
||||||
|
.tokens {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alertArea {
|
||||||
|
margin-left: -3rem;
|
||||||
|
margin-right: -3rem;
|
||||||
|
padding: var(--spacer) calc(var(--spacer) / 2);
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
border-top: 1px solid var(--brand-grey-lighter);
|
||||||
|
}
|
89
src/components/molecules/FormFields/Price/Advanced.tsx
Normal file
89
src/components/molecules/FormFields/Price/Advanced.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import React, { ReactElement, useState, ChangeEvent, useEffect } from 'react'
|
||||||
|
import stylesIndex from './index.module.css'
|
||||||
|
import styles from './Advanced.module.css'
|
||||||
|
import FormHelp from '../../../atoms/Input/Help'
|
||||||
|
import Wallet from '../../Wallet'
|
||||||
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
|
import Alert from '../../../atoms/Alert'
|
||||||
|
import Coin from './Coin'
|
||||||
|
import { isCorrectNetwork } from '../../../../utils/wallet'
|
||||||
|
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||||
|
|
||||||
|
export default function Advanced({
|
||||||
|
ocean,
|
||||||
|
tokensToMint,
|
||||||
|
weightOnDataToken,
|
||||||
|
onChange
|
||||||
|
}: {
|
||||||
|
ocean: string
|
||||||
|
tokensToMint: number
|
||||||
|
weightOnDataToken: string
|
||||||
|
onChange: (event: ChangeEvent<HTMLInputElement>) => void
|
||||||
|
}): ReactElement {
|
||||||
|
const { appConfig } = useSiteMetadata()
|
||||||
|
const { account, balance, chainId } = useOcean()
|
||||||
|
|
||||||
|
const [error, setError] = useState<string>()
|
||||||
|
const correctNetwork = isCorrectNetwork(chainId)
|
||||||
|
const desiredNetworkName = appConfig.network.replace(/^\w/, (c: string) =>
|
||||||
|
c.toUpperCase()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check: account, network & insuffciant balance
|
||||||
|
useEffect(() => {
|
||||||
|
if (!account) {
|
||||||
|
setError(`No account connected. Please connect your Web3 wallet.`)
|
||||||
|
} else if (!correctNetwork) {
|
||||||
|
setError(`Wrong Network. Please connect to ${desiredNetworkName}.`)
|
||||||
|
} else if (balance.ocean < ocean) {
|
||||||
|
setError(`Insufficiant balance. You need at least ${ocean} OCEAN`)
|
||||||
|
} else {
|
||||||
|
setError(undefined)
|
||||||
|
}
|
||||||
|
}, [ocean])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={stylesIndex.content}>
|
||||||
|
<div className={styles.advanced}>
|
||||||
|
<FormHelp className={stylesIndex.help}>
|
||||||
|
{`Let's create a decentralized, automated market for your data set. A Data Token contract for this data set worth the entered amount of OCEAN will be created. Additionally, you will provide liquidity into a Data Token/OCEAN
|
||||||
|
liquidity pool with Balancer.`}
|
||||||
|
</FormHelp>
|
||||||
|
|
||||||
|
<aside className={styles.wallet}>
|
||||||
|
{balance && balance.ocean && (
|
||||||
|
<div className={styles.balance}>
|
||||||
|
OCEAN <strong>{balance.ocean}</strong>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Wallet />
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<h4 className={styles.title}>Data Token Liquidity Pool</h4>
|
||||||
|
|
||||||
|
<div className={styles.tokens}>
|
||||||
|
<Coin
|
||||||
|
name="ocean"
|
||||||
|
symbol="OCEAN"
|
||||||
|
value={ocean}
|
||||||
|
weight={`${100 - Number(weightOnDataToken)}%`}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
<Coin
|
||||||
|
name="tokensToMint"
|
||||||
|
symbol="OCEAN-CAV"
|
||||||
|
value={tokensToMint.toString()}
|
||||||
|
weight={`${weightOnDataToken}%`}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className={styles.alertArea}>
|
||||||
|
<Alert text={error} state="error" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
59
src/components/molecules/FormFields/Price/Coin.module.css
Normal file
59
src/components/molecules/FormFields/Price/Coin.module.css
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
.coin {
|
||||||
|
padding: var(--spacer) calc(var(--spacer) / 2);
|
||||||
|
border-top: 1px solid var(--brand-grey-lighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.coin:last-child {
|
||||||
|
border-left: 1px solid var(--brand-grey-lighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
composes: box from '../../../atoms/Box.module.css';
|
||||||
|
padding: calc(var(--spacer) / 1.5);
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid var(--brand-grey-lighter);
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--brand-white);
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.coin:last-child .icon path {
|
||||||
|
fill: var(--brand-grey-dimmed);
|
||||||
|
stroke: var(--brand-black);
|
||||||
|
stroke-width: 5px;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 12rem;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.max {
|
||||||
|
position: absolute;
|
||||||
|
right: var(--spacer);
|
||||||
|
top: calc(var(--spacer) / 2.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight strong {
|
||||||
|
color: var(--brand-grey);
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
}
|
46
src/components/molecules/FormFields/Price/Coin.tsx
Normal file
46
src/components/molecules/FormFields/Price/Coin.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React, { ReactElement, ChangeEvent } from 'react'
|
||||||
|
import stylesIndex from './index.module.css'
|
||||||
|
import styles from './Coin.module.css'
|
||||||
|
import InputElement from '../../../atoms/Input/InputElement'
|
||||||
|
import { ReactComponent as Logo } from '../../../../images/logo.svg'
|
||||||
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
|
|
||||||
|
export default function Coin({
|
||||||
|
symbol,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
weight,
|
||||||
|
onChange,
|
||||||
|
readOnly
|
||||||
|
}: {
|
||||||
|
symbol: string
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
|
weight: string
|
||||||
|
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
||||||
|
readOnly?: boolean
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={styles.coin}>
|
||||||
|
<figure className={styles.icon}>
|
||||||
|
<Logo />
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<div className={styles.data}>
|
||||||
|
<InputElement
|
||||||
|
value={value}
|
||||||
|
name={name}
|
||||||
|
type="number"
|
||||||
|
onChange={onChange}
|
||||||
|
readOnly={readOnly}
|
||||||
|
prefix={symbol}
|
||||||
|
/>
|
||||||
|
<Conversion price={value} className={stylesIndex.conversion} />
|
||||||
|
|
||||||
|
<div className={styles.weight}>
|
||||||
|
Weight <strong>{weight}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
.form {
|
||||||
|
max-width: 12rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple label {
|
||||||
|
display: none;
|
||||||
|
}
|
40
src/components/molecules/FormFields/Price/Simple.tsx
Normal file
40
src/components/molecules/FormFields/Price/Simple.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import React, { ReactElement, ChangeEvent } from 'react'
|
||||||
|
import stylesIndex from './index.module.css'
|
||||||
|
import styles from './Simple.module.css'
|
||||||
|
import FormHelp from '../../../atoms/Input/Help'
|
||||||
|
import Label from '../../../atoms/Input/Label'
|
||||||
|
import InputElement from '../../../atoms/Input/InputElement'
|
||||||
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
|
|
||||||
|
export default function Simple({
|
||||||
|
ocean,
|
||||||
|
onChange
|
||||||
|
}: {
|
||||||
|
ocean: string
|
||||||
|
onChange: (event: ChangeEvent<HTMLInputElement>) => void
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={stylesIndex.content}>
|
||||||
|
<div className={styles.simple}>
|
||||||
|
<FormHelp className={stylesIndex.help}>
|
||||||
|
Set your price for accessing this data set. A Data Token contract for
|
||||||
|
this data set, worth the entered amount of OCEAN will be created.
|
||||||
|
</FormHelp>
|
||||||
|
|
||||||
|
<form className={styles.form}>
|
||||||
|
<Label htmlFor="ocean">Ocean Tokens</Label>
|
||||||
|
|
||||||
|
<InputElement
|
||||||
|
value={ocean}
|
||||||
|
name="ocean"
|
||||||
|
type="number"
|
||||||
|
prefix="OCEAN"
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Conversion price={ocean} className={stylesIndex.conversion} />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
33
src/components/molecules/FormFields/Price/index.module.css
Normal file
33
src/components/molecules/FormFields/Price/index.module.css
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
.price {
|
||||||
|
border: 1px solid var(--brand-grey-lighter);
|
||||||
|
background: var(--brand-grey-dimmed);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 0 calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content label {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content input {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversion {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: calc(var(--spacer) / 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.help {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: calc(var(--spacer) / 1.5);
|
||||||
|
}
|
54
src/components/molecules/FormFields/Price/index.tsx
Normal file
54
src/components/molecules/FormFields/Price/index.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import React, { ReactElement, useState, ChangeEvent, useEffect } from 'react'
|
||||||
|
import { InputProps } from '../../../atoms/Input'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import Tabs from '../../../atoms/Tabs'
|
||||||
|
import Simple from './Simple'
|
||||||
|
import Advanced from './Advanced'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
|
||||||
|
export default function Price(props: InputProps): ReactElement {
|
||||||
|
const [field, meta, helpers] = useField(props)
|
||||||
|
|
||||||
|
const cost = 1
|
||||||
|
const weightOnDataToken = '90' // in %
|
||||||
|
const [ocean, setOcean] = useState('1')
|
||||||
|
const [tokensToMint, setTokensToMint] = useState<number>()
|
||||||
|
|
||||||
|
function handleOceanChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
|
setOcean(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always update everything when ocean changes
|
||||||
|
useEffect(() => {
|
||||||
|
const tokensToMint = Number(ocean) * (Number(weightOnDataToken) / 10)
|
||||||
|
setTokensToMint(tokensToMint)
|
||||||
|
helpers.setValue({ cost, tokensToMint })
|
||||||
|
}, [ocean])
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: 'Simple: Fixed',
|
||||||
|
content: <Simple ocean={ocean} onChange={handleOceanChange} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Advanced: Dynamic',
|
||||||
|
content: (
|
||||||
|
<Advanced
|
||||||
|
ocean={ocean}
|
||||||
|
tokensToMint={tokensToMint}
|
||||||
|
weightOnDataToken={weightOnDataToken}
|
||||||
|
onChange={handleOceanChange}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.price}>
|
||||||
|
<Tabs items={tabs} />
|
||||||
|
<pre>
|
||||||
|
<code>{JSON.stringify(field.value)}</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -66,7 +66,7 @@
|
|||||||
padding: calc(var(--spacer) / 2);
|
padding: calc(var(--spacer) / 2);
|
||||||
margin-left: var(--spacer);
|
margin-left: var(--spacer);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--brand-grey);
|
color: var(--brand-grey-light);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-base);
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -82,13 +82,13 @@
|
|||||||
.link:hover,
|
.link:hover,
|
||||||
.link:focus,
|
.link:focus,
|
||||||
.link:active {
|
.link:active {
|
||||||
color: var(--brand-pink);
|
color: var(--brand-grey);
|
||||||
}
|
}
|
||||||
|
|
||||||
.link[aria-current],
|
.link[aria-current],
|
||||||
.link[aria-current]:hover,
|
.link[aria-current]:hover,
|
||||||
.link[aria-current]:focus {
|
.link[aria-current]:focus {
|
||||||
color: var(--brand-pink);
|
color: var(--brand-black);
|
||||||
}
|
}
|
||||||
|
|
||||||
.link:last-child {
|
.link:last-child {
|
||||||
|
@ -8,12 +8,16 @@
|
|||||||
padding: calc(var(--spacer) / 4);
|
padding: calc(var(--spacer) / 4);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
background: none;
|
background: none;
|
||||||
color: var(--brand-grey);
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
transition: border 0.2s ease-out;
|
transition: border 0.2s ease-out;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button,
|
||||||
|
.address {
|
||||||
|
color: var(--brand-grey);
|
||||||
|
}
|
||||||
|
|
||||||
.button:hover,
|
.button:hover,
|
||||||
.button:focus {
|
.button:focus {
|
||||||
transform: none;
|
transform: none;
|
||||||
@ -21,6 +25,10 @@
|
|||||||
border-color: var(--brand-grey-light);
|
border-color: var(--brand-grey-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.initial {
|
||||||
|
color: var(--brand-pink);
|
||||||
|
}
|
||||||
|
|
||||||
.blockies {
|
.blockies {
|
||||||
width: var(--font-size-large);
|
width: var(--font-size-large);
|
||||||
height: var(--font-size-large);
|
height: var(--font-size-large);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
import React, { FormEvent } from 'react'
|
||||||
import styles from './Account.module.css'
|
import styles from './Account.module.css'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import { toDataUrl } from 'ethereum-blockies'
|
import { toDataUrl } from 'ethereum-blockies'
|
||||||
@ -26,8 +26,20 @@ const Account = React.forwardRef((props, ref: any) => {
|
|||||||
const { accountId, status, connect, chainId } = useOcean()
|
const { accountId, status, connect, chainId } = useOcean()
|
||||||
const hasSuccess = status === 1 && isCorrectNetwork(chainId)
|
const hasSuccess = status === 1 && isCorrectNetwork(chainId)
|
||||||
|
|
||||||
|
async function handleActivation(e: FormEvent<HTMLButtonElement>) {
|
||||||
|
// prevent accidentially submitting a form the button might be in
|
||||||
|
e.preventDefault()
|
||||||
|
await connect()
|
||||||
|
}
|
||||||
|
|
||||||
return accountId ? (
|
return accountId ? (
|
||||||
<button className={styles.button} aria-label="Account" ref={ref}>
|
<button
|
||||||
|
className={styles.button}
|
||||||
|
aria-label="Account"
|
||||||
|
ref={ref}
|
||||||
|
// prevent accidentially submitting a form the button might be in
|
||||||
|
onClick={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
<Blockies account={accountId} />
|
<Blockies account={accountId} />
|
||||||
<span className={styles.address} title={accountId}>
|
<span className={styles.address} title={accountId}>
|
||||||
{accountTruncate(accountId)}
|
{accountTruncate(accountId)}
|
||||||
@ -39,13 +51,13 @@ const Account = React.forwardRef((props, ref: any) => {
|
|||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
className={styles.button}
|
className={`${styles.button} ${styles.initial}`}
|
||||||
onClick={async () => await connect()}
|
onClick={(e) => handleActivation(e)}
|
||||||
// Need the `ref` here although we do not want
|
// Need the `ref` here although we do not want
|
||||||
// the Tippy to show in this state.
|
// the Tippy to show in this state.
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
Activate Wallet
|
Connect Wallet
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -21,7 +21,7 @@ export default function Web3Feedback({
|
|||||||
const isOceanConnectionError = status === -1
|
const isOceanConnectionError = status === -1
|
||||||
const correctNetwork = isCorrectNetwork(chainId)
|
const correctNetwork = isCorrectNetwork(chainId)
|
||||||
const showFeedback = !account || isOceanConnectionError || !correctNetwork
|
const showFeedback = !account || isOceanConnectionError || !correctNetwork
|
||||||
const networkName = appConfig.network.replace(/^\w/, (c: string) =>
|
const desiredNetworkName = appConfig.network.replace(/^\w/, (c: string) =>
|
||||||
c.toUpperCase()
|
c.toUpperCase()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ export default function Web3Feedback({
|
|||||||
: isOceanConnectionError
|
: isOceanConnectionError
|
||||||
? 'Please try again.'
|
? 'Please try again.'
|
||||||
: !correctNetwork
|
: !correctNetwork
|
||||||
? `Please connect to ${networkName}.`
|
? `Please connect to ${desiredNetworkName}.`
|
||||||
: isBalanceInsufficient === true
|
: isBalanceInsufficient === true
|
||||||
? 'You do not have enough OCEAN in your wallet to purchase this asset.'
|
? 'You do not have enough OCEAN in your wallet to purchase this asset.'
|
||||||
: 'Something went wrong.'
|
: 'Something went wrong.'
|
||||||
|
@ -4,29 +4,3 @@
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabList {
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: 1px solid var(--brand-grey-lighter);
|
|
||||||
padding-top: calc(var(--spacer) / 2);
|
|
||||||
padding-bottom: calc(var(--spacer) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
display: inline-block;
|
|
||||||
padding: calc(var(--spacer) / 12) var(--spacer);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
font-weight: var(--font-weight-bold);
|
|
||||||
text-transform: uppercase;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--brand-grey-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab[aria-selected='true'] {
|
|
||||||
background: var(--brand-black);
|
|
||||||
color: var(--brand-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabContent {
|
|
||||||
padding: var(--spacer);
|
|
||||||
}
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
|
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import Compute from './Compute'
|
import Compute from './Compute'
|
||||||
import Consume from './Consume'
|
import Consume from './Consume'
|
||||||
import { MetadataMarket } from '../../../@types/Metadata'
|
import { MetadataMarket } from '../../../@types/Metadata'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
import Tabs from '../../atoms/Tabs'
|
||||||
|
|
||||||
export default function AssetActions({
|
export default function AssetActions({
|
||||||
metadata,
|
metadata,
|
||||||
@ -15,23 +15,22 @@ export default function AssetActions({
|
|||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { access } = metadata.additionalInformation
|
const { access } = metadata.additionalInformation
|
||||||
const isCompute = access && access === 'Compute'
|
const isCompute = access && access === 'Compute'
|
||||||
|
const UseContent = isCompute ? (
|
||||||
return (
|
<Compute ddo={ddo} />
|
||||||
<Tabs className={styles.actions}>
|
) : (
|
||||||
<TabList className={styles.tabList}>
|
<Consume ddo={ddo} file={metadata.main.files[0]} />
|
||||||
<Tab className={styles.tab}>Use</Tab>
|
|
||||||
<Tab className={styles.tab}>Trade</Tab>
|
|
||||||
</TabList>
|
|
||||||
<div className={styles.tabContent}>
|
|
||||||
<TabPanel>
|
|
||||||
{isCompute ? (
|
|
||||||
<Compute ddo={ddo} />
|
|
||||||
) : (
|
|
||||||
<Consume ddo={ddo} file={metadata.main.files[0]} />
|
|
||||||
)}
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel>Trade Me</TabPanel>
|
|
||||||
</div>
|
|
||||||
</Tabs>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: 'Use',
|
||||||
|
content: UseContent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Trade',
|
||||||
|
content: 'Trade Me'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return <Tabs items={tabs} className={styles.actions} />
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,10 @@ export default function AssetContent({
|
|||||||
/> */}
|
/> */}
|
||||||
{/* <DeleteAction ddo={ddo} /> */}
|
{/* <DeleteAction ddo={ddo} /> */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<code>{JSON.stringify(ddo, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.sticky}>
|
<div className={styles.sticky}>
|
||||||
|
@ -38,6 +38,7 @@ export default function Preview({
|
|||||||
key.includes('tags') ||
|
key.includes('tags') ||
|
||||||
key.includes('files') ||
|
key.includes('files') ||
|
||||||
key.includes('termsAndConditions') ||
|
key.includes('termsAndConditions') ||
|
||||||
|
key.includes('price') ||
|
||||||
value === undefined ||
|
value === undefined ||
|
||||||
value === ''
|
value === ''
|
||||||
)
|
)
|
||||||
|
@ -19,7 +19,6 @@ export default function PublishForm({
|
|||||||
status,
|
status,
|
||||||
setStatus,
|
setStatus,
|
||||||
isValid,
|
isValid,
|
||||||
touched,
|
|
||||||
setErrors,
|
setErrors,
|
||||||
setTouched,
|
setTouched,
|
||||||
resetForm,
|
resetForm,
|
||||||
@ -42,7 +41,6 @@ export default function PublishForm({
|
|||||||
{content.data.map((field: FormFieldProps) => (
|
{content.data.map((field: FormFieldProps) => (
|
||||||
<Field key={field.name} {...field} component={Input} />
|
<Field key={field.name} {...field} component={Input} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Loader message={publishStepText} />
|
<Loader message={publishStepText} />
|
||||||
) : (
|
) : (
|
||||||
|
@ -7,7 +7,7 @@ import styles from './index.module.css'
|
|||||||
import PublishForm from './PublishForm'
|
import PublishForm from './PublishForm'
|
||||||
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
||||||
import { FormContent } from '../../../@types/Form'
|
import { FormContent } from '../../../@types/Form'
|
||||||
import { initialValues, validationSchema } from './validation'
|
import { initialValues, validationSchema } from '../../../models/FormPublish'
|
||||||
import { MetadataPublishForm } from '../../../@types/Metadata'
|
import { MetadataPublishForm } from '../../../@types/Metadata'
|
||||||
import { transformPublishFormToMetadata } from './utils'
|
import { transformPublishFormToMetadata } from './utils'
|
||||||
import Preview from './Preview'
|
import Preview from './Preview'
|
||||||
@ -28,20 +28,20 @@ export default function PublishPage({
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
const metadata = transformPublishFormToMetadata(values)
|
const metadata = transformPublishFormToMetadata(values)
|
||||||
const tokensToMint = '4' // how to know this?
|
const { cost, tokensToMint } = values.price
|
||||||
const serviceType = values.access === 'Download' ? 'access' : 'compute'
|
const serviceType = values.access === 'Download' ? 'access' : 'compute'
|
||||||
|
|
||||||
console.log(`
|
console.log(`
|
||||||
Transformed metadata values:
|
Transformed metadata values:
|
||||||
----------------------
|
----------------------
|
||||||
${JSON.stringify(metadata, null, 2)}
|
${JSON.stringify(metadata, null, 2)}
|
||||||
Cost: 1
|
Cost: ${cost}
|
||||||
Tokens to mint: ${tokensToMint}
|
Tokens to mint: ${tokensToMint}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const ddo = await publish(metadata as any, tokensToMint, [
|
const ddo = await publish(metadata as any, tokensToMint.toString(), [
|
||||||
{ serviceType, cost: '1' }
|
{ serviceType, cost: cost.toString() }
|
||||||
])
|
])
|
||||||
|
|
||||||
if (publishError) {
|
if (publishError) {
|
||||||
@ -82,6 +82,26 @@ export default function PublishPage({
|
|||||||
<Web3Feedback />
|
<Web3Feedback />
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h5>Collected Form Values</h5>
|
||||||
|
<pre>
|
||||||
|
<code>{JSON.stringify(values, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h5>Transformed Values</h5>
|
||||||
|
<pre>
|
||||||
|
<code>
|
||||||
|
{JSON.stringify(
|
||||||
|
transformPublishFormToMetadata(values),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
@ -16,8 +16,7 @@ export function transformPublishFormToMetadata(
|
|||||||
tags,
|
tags,
|
||||||
links,
|
links,
|
||||||
termsAndConditions,
|
termsAndConditions,
|
||||||
files,
|
files
|
||||||
access
|
|
||||||
} = data
|
} = data
|
||||||
|
|
||||||
const metadata: MetadataMarket = {
|
const metadata: MetadataMarket = {
|
||||||
@ -38,8 +37,7 @@ export function transformPublishFormToMetadata(
|
|||||||
// links: {
|
// links: {
|
||||||
// url: links
|
// url: links
|
||||||
// },
|
// },
|
||||||
termsAndConditions,
|
termsAndConditions
|
||||||
access: access || 'Download'
|
|
||||||
},
|
},
|
||||||
curation: AssetModel.curation
|
curation: AssetModel.curation
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
code {
|
code {
|
||||||
font-family: var(--font-family-monospace);
|
font-family: var(--font-family-monospace);
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
color: var(--brand-grey-light);
|
color: var(--brand-grey);
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,13 +14,22 @@ code {
|
|||||||
pre {
|
pre {
|
||||||
display: block;
|
display: block;
|
||||||
margin: calc(var(--spacer) / 2) 0;
|
margin: calc(var(--spacer) / 2) 0;
|
||||||
padding: 0;
|
padding: calc(var(--spacer) / 2);
|
||||||
border: 1px solid var(--brand-grey-dark);
|
background-color: var(--brand-grey-dimmed) !important;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
|
overflow: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
max-height: 800px;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code {
|
pre code {
|
||||||
padding: calc(var(--spacer) / 2);
|
padding: 0;
|
||||||
|
white-space: pre;
|
||||||
display: block;
|
display: block;
|
||||||
white-space: pre-wrap;
|
overflow-wrap: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
word-break: normal;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,28 @@ fieldset {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
display: block;
|
||||||
|
overflow: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: calc(var(--spacer) / 2);
|
||||||
|
border-bottom: 1px solid var(--brand-grey-lighter);
|
||||||
|
text-align: left;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
}
|
||||||
|
|
||||||
@import '_code.css';
|
@import '_code.css';
|
||||||
@import '_toast.css';
|
@import '_toast.css';
|
||||||
@import '_web3modal.css';
|
@import '_web3modal.css';
|
||||||
|
26
src/images/logo.svg
Normal file
26
src/images/logo.svg
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<svg width="394" height="399" viewBox="0 0 394 399" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M196.742 57.2631C212.525 57.2631 225.33 44.4482 225.33 28.6321C225.33 12.82 212.525 0 196.742 0C180.944 0 168.139 12.82 168.139 28.6321C168.139 44.4482 180.944 57.2631 196.742 57.2631Z" fill="#141414"/>
|
||||||
|
<path d="M29.167 213.019C40.8609 213.019 50.3274 203.527 50.3274 191.826C50.3274 180.125 40.8609 170.644 29.167 170.644C17.4833 170.644 8.00663 180.125 8.00663 191.826C8.00663 203.527 17.4833 213.019 29.167 213.019Z" fill="#141414"/>
|
||||||
|
<path d="M365.451 213.019C377.129 213.019 386.606 203.527 386.606 191.826C386.606 180.125 377.129 170.644 365.451 170.644C353.762 170.644 344.285 180.125 344.285 191.826C344.285 203.527 353.762 213.019 365.451 213.019Z" fill="#141414"/>
|
||||||
|
<path d="M218.467 275.433C218.467 287.132 208.991 296.623 197.309 296.623C185.622 296.623 176.146 287.132 176.146 275.433C176.146 263.733 185.622 254.248 197.309 254.248C208.991 254.248 218.467 263.733 218.467 275.433Z" fill="#141414"/>
|
||||||
|
<path d="M112.662 255.393C124.355 255.393 133.825 245.902 133.825 234.206C133.825 222.5 124.355 213.019 112.662 213.019C100.975 213.019 91.5044 222.5 91.5044 234.206C91.5044 245.902 100.975 255.393 112.662 255.393Z" fill="#141414"/>
|
||||||
|
<path d="M301.965 234.206C301.965 245.902 292.489 255.393 280.807 255.393C269.114 255.393 259.644 245.902 259.644 234.206C259.644 222.5 269.114 213.019 280.807 213.019C292.489 213.019 301.965 222.5 301.965 234.206Z" fill="#141414"/>
|
||||||
|
<path d="M29.1748 270.282C37.0689 270.282 43.4646 263.864 43.4646 255.971C43.4646 248.058 37.0689 241.65 29.1748 241.65C21.2703 241.65 14.8695 248.058 14.8695 255.971C14.8695 263.864 21.2703 270.282 29.1748 270.282Z" fill="#141414"/>
|
||||||
|
<path d="M379.743 255.971C379.743 263.864 373.338 270.282 365.446 270.282C357.548 270.282 351.148 263.864 351.148 255.971C351.148 248.058 357.548 241.65 365.446 241.65C373.338 241.65 379.743 248.058 379.743 255.971Z" fill="#141414"/>
|
||||||
|
<path d="M197.312 355.031C205.196 355.031 211.604 348.613 211.604 340.705C211.604 332.802 205.196 326.4 197.312 326.4C189.422 326.4 183.009 332.802 183.009 340.705C183.009 348.613 189.422 355.031 197.312 355.031Z" fill="#141414"/>
|
||||||
|
<path d="M126.962 298.346C126.962 306.251 120.563 312.656 112.67 312.656C104.772 312.656 98.3672 306.251 98.3672 298.346C98.3672 290.435 104.772 284.025 112.67 284.025C120.563 284.025 126.962 290.435 126.962 298.346Z" fill="#141414"/>
|
||||||
|
<path d="M280.812 312.656C288.693 312.656 295.102 306.251 295.102 298.346C295.102 290.435 288.693 284.025 280.812 284.025C272.91 284.025 266.507 290.435 266.507 298.346C266.507 306.251 272.91 312.656 280.812 312.656Z" fill="#141414"/>
|
||||||
|
<path d="M36.6018 306.35C36.6018 310.469 33.2752 313.802 29.1697 313.802C25.0588 313.802 21.7323 310.469 21.7323 306.35C21.7323 302.241 25.0588 298.913 29.1697 298.913C33.2752 298.913 36.6018 302.241 36.6018 306.35Z" fill="#141414"/>
|
||||||
|
<path d="M365.448 313.802C369.546 313.802 372.88 310.469 372.88 306.35C372.88 302.241 369.546 298.913 365.448 298.913C361.335 298.913 358.011 302.241 358.011 306.35C358.011 310.469 361.335 313.802 365.448 313.802Z" fill="#141414"/>
|
||||||
|
<path d="M204.741 391.102C204.741 395.214 201.407 398.551 197.309 398.551C193.201 398.551 189.872 395.214 189.872 391.102C189.872 386.989 193.201 383.663 197.309 383.663C201.407 383.663 204.741 386.989 204.741 391.102Z" fill="#141414"/>
|
||||||
|
<path d="M112.662 356.176C116.768 356.176 120.1 352.839 120.1 348.732C120.1 344.615 116.768 341.288 112.662 341.288C108.562 341.288 105.23 344.615 105.23 348.732C105.23 352.839 108.562 356.176 112.662 356.176Z" fill="#141414"/>
|
||||||
|
<path d="M288.239 348.732C288.239 352.839 284.905 356.176 280.807 356.176C276.688 356.176 273.369 352.839 273.369 348.732C273.369 344.615 276.688 341.288 280.807 341.288C284.905 341.288 288.239 344.615 288.239 348.732Z" fill="#141414"/>
|
||||||
|
<path d="M225.33 113.381C225.33 129.195 212.525 142.012 196.742 142.012C180.944 142.012 168.139 129.195 168.139 113.381C168.139 97.572 180.944 84.7494 196.742 84.7494C212.525 84.7494 225.33 97.572 225.33 113.381Z" fill="#141414"/>
|
||||||
|
<path d="M196.742 225.617C212.525 225.617 225.33 212.797 225.33 196.99C225.33 181.173 212.525 168.353 196.742 168.353C180.944 168.353 168.139 181.173 168.139 196.99C168.139 212.797 180.944 225.617 196.742 225.617Z" fill="#141414"/>
|
||||||
|
<path d="M393.469 113.381C393.469 129.195 380.667 142.012 364.876 142.012C349.081 142.012 336.279 129.195 336.279 113.381C336.279 97.572 349.081 84.7494 364.876 84.7494C380.667 84.7494 393.469 97.572 393.469 113.381Z" fill="#141414"/>
|
||||||
|
<path d="M28.5977 142.012C44.3907 142.012 57.1903 129.195 57.1903 113.381C57.1903 97.572 44.3907 84.7494 28.5977 84.7494C12.8046 84.7494 0 97.572 0 113.381C0 129.195 12.8046 142.012 28.5977 142.012Z" fill="#141414"/>
|
||||||
|
<path d="M141.832 71.0062C141.832 86.818 129.031 99.6378 113.237 99.6378C97.4422 99.6378 84.6416 86.818 84.6416 71.0062C84.6416 55.1842 97.4422 42.3747 113.237 42.3747C129.031 42.3747 141.832 55.1842 141.832 71.0062Z" fill="#141414"/>
|
||||||
|
<path d="M113.237 183.242C129.031 183.242 141.832 170.421 141.832 154.608C141.832 138.8 129.031 125.979 113.237 125.979C97.4422 125.979 84.6416 138.8 84.6416 154.608C84.6416 170.421 97.4422 183.242 113.237 183.242Z" fill="#141414"/>
|
||||||
|
<path d="M309.971 71.0062C309.971 86.818 297.167 99.6378 281.373 99.6378C265.58 99.6378 252.781 86.818 252.781 71.0062C252.781 55.1842 265.58 42.3747 281.373 42.3747C297.167 42.3747 309.971 55.1842 309.971 71.0062Z" fill="#141414"/>
|
||||||
|
<path d="M281.373 183.242C297.167 183.242 309.971 170.421 309.971 154.608C309.971 138.8 297.167 125.979 281.373 125.979C265.58 125.979 252.781 138.8 252.781 154.608C252.781 170.421 265.58 183.242 281.373 183.242Z" fill="#141414"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
@ -5,21 +5,20 @@ const AssetModel: MetadataMarket = {
|
|||||||
// https://github.com/oceanprotocol/OEPs/tree/master/8
|
// https://github.com/oceanprotocol/OEPs/tree/master/8
|
||||||
main: {
|
main: {
|
||||||
type: 'dataset',
|
type: 'dataset',
|
||||||
name: '',
|
name: undefined,
|
||||||
dateCreated: '',
|
dateCreated: undefined,
|
||||||
author: '',
|
author: undefined,
|
||||||
license: '',
|
license: undefined,
|
||||||
files: []
|
files: []
|
||||||
},
|
},
|
||||||
additionalInformation: {
|
additionalInformation: {
|
||||||
description: '',
|
description: undefined,
|
||||||
copyrightHolder: '',
|
copyrightHolder: undefined,
|
||||||
tags: [],
|
tags: undefined,
|
||||||
links: [],
|
links: undefined,
|
||||||
|
|
||||||
// custom items
|
// custom items
|
||||||
termsAndConditions: false,
|
termsAndConditions: false
|
||||||
access: 'Download'
|
|
||||||
},
|
},
|
||||||
curation: {
|
curation: {
|
||||||
rating: 0,
|
rating: 0,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { MetadataPublishForm } from '../../../@types/Metadata'
|
import { MetadataPublishForm } from '../@types/Metadata'
|
||||||
import { File as FileMetadata } from '@oceanprotocol/lib'
|
import { File as FileMetadata } from '@oceanprotocol/lib'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
@ -6,7 +6,10 @@ 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'),
|
||||||
cost: Yup.string().required('Required'),
|
price: Yup.object().shape({
|
||||||
|
cost: Yup.number().required('Required'),
|
||||||
|
tokensToMint: Yup.number().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'),
|
||||||
@ -22,15 +25,18 @@ export const validationSchema = Yup.object().shape<MetadataPublishForm>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const initialValues: MetadataPublishForm = {
|
export const initialValues: MetadataPublishForm = {
|
||||||
name: '',
|
name: undefined,
|
||||||
author: '',
|
author: undefined,
|
||||||
cost: '',
|
price: {
|
||||||
files: '',
|
cost: 1,
|
||||||
description: '',
|
tokensToMint: 1
|
||||||
license: '',
|
},
|
||||||
access: '',
|
files: undefined,
|
||||||
|
description: undefined,
|
||||||
|
license: undefined,
|
||||||
|
access: undefined,
|
||||||
termsAndConditions: false,
|
termsAndConditions: false,
|
||||||
copyrightHolder: '',
|
copyrightHolder: undefined,
|
||||||
tags: '',
|
tags: undefined,
|
||||||
links: ''
|
links: undefined
|
||||||
}
|
}
|
@ -32,7 +32,6 @@ export const contentQuery = graphql`
|
|||||||
type
|
type
|
||||||
required
|
required
|
||||||
options
|
options
|
||||||
min
|
|
||||||
}
|
}
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user