mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
new publish form data setup
This commit is contained in:
parent
70470a9459
commit
99453623d2
@ -1,89 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "Publish a Data Set",
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"label": "Title",
|
|
||||||
"placeholder": "e.g. Shapes of Desert Plants",
|
|
||||||
"help": "Enter a concise title.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "description",
|
|
||||||
"label": "Description",
|
|
||||||
"help": "Add a thorough description with as much detail as possible. You can use [Markdown](https://daringfireball.net/projects/markdown/basics). You can change the description at any time. If you provide personal data, please note that it will remain in the transaction history. For more information on how personal data is handled within the metadata, please refer to our [privacy policy](/privacy/en).",
|
|
||||||
"type": "textarea",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "files",
|
|
||||||
"label": "File",
|
|
||||||
"placeholder": "e.g. https://file.com/file.json",
|
|
||||||
"help": "Please enter the URL to your data set file and click \"ADD FILE\" to validate the data. This URL will be stored encrypted after publishing. For a compute data set, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size.",
|
|
||||||
"type": "files",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "links",
|
|
||||||
"label": "Sample file",
|
|
||||||
"placeholder": "e.g. https://file.com/samplefile.json",
|
|
||||||
"help": "Please enter the URL to a sample of your data set file and click \"ADD FILE\" to validate the data. This file should reveal the data structure of your data set, e.g. by including the header and one line of a CSV file. This file URL will be publicly available after publishing.",
|
|
||||||
"type": "files"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "access",
|
|
||||||
"label": "Access Type",
|
|
||||||
"help": "Choose how you want your files to be accessible for the specified price.",
|
|
||||||
"type": "boxSelection",
|
|
||||||
"options": ["Download", "Compute"],
|
|
||||||
"required": true,
|
|
||||||
"disclaimer": "Please do not provide downloadable personal data without the consent of the data subjects.",
|
|
||||||
"disclaimerValues": ["Download"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "providerUri",
|
|
||||||
"label": "Custom Provider URL",
|
|
||||||
"type": "providerUri",
|
|
||||||
"help": "Enter the URL for your custom provider or leave blank to use the default provider. [Learn more](https://github.com/oceanprotocol/provider/).",
|
|
||||||
"placeholder": "https://provider.polygon.oceanprotocol.com/",
|
|
||||||
"advanced": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "timeout",
|
|
||||||
"label": "Timeout",
|
|
||||||
"help": "Define how long buyers should be able to download the data set again after the initial purchase.",
|
|
||||||
"type": "select",
|
|
||||||
"options": ["Forever", "1 day", "1 week", "1 month", "1 year"],
|
|
||||||
"sortOptions": false,
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "dataTokenOptions",
|
|
||||||
"label": "Datatoken Name & Symbol",
|
|
||||||
"type": "datatoken",
|
|
||||||
"help": "The datatoken for this data set will be created with this name & symbol.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "author",
|
|
||||||
"label": "Author",
|
|
||||||
"placeholder": "e.g. Jelly McJellyfish",
|
|
||||||
"help": "Give proper attribution for your data set. You are welcome to use a pseudonym, and you can change your author name at any time. Please note that it will remain in the transaction history. For more information on how personal data is handled within the metadata, please refer to our [privacy policy](/privacy/en).",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "tags",
|
|
||||||
"label": "Tags",
|
|
||||||
"placeholder": "e.g. logistics, ai",
|
|
||||||
"help": "Separate tags with comma."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "termsAndConditions",
|
|
||||||
"label": "Terms & Conditions",
|
|
||||||
"type": "terms",
|
|
||||||
"options": ["I agree to these Terms and Conditions"],
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"success": "Asset Created!"
|
|
||||||
}
|
|
98
content/publish/form.json
Normal file
98
content/publish/form.json
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"title": "Enter details",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"label": "Title",
|
||||||
|
"placeholder": "e.g. Shapes of Desert Plants",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"label": "Description",
|
||||||
|
"help": "Add a thorough description with as much detail as possible. You can use [Markdown](https://daringfireball.net/projects/markdown/basics). You can change the description at any time. If you provide personal data, please note that it will remain in the transaction history. For more information on how personal data is handled within the metadata, please refer to our [privacy policy](/privacy/en).",
|
||||||
|
"type": "textarea",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "files",
|
||||||
|
"label": "File",
|
||||||
|
"placeholder": "e.g. https://file.com/file.json",
|
||||||
|
"help": "Please enter the URL to your data set file and click \"ADD FILE\" to validate the data. This URL will be stored encrypted after publishing. For a compute data set, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size.",
|
||||||
|
"type": "files",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "links",
|
||||||
|
"label": "Sample file",
|
||||||
|
"placeholder": "e.g. https://file.com/samplefile.json",
|
||||||
|
"help": "Please enter the URL to a sample of your data set file and click \"ADD FILE\" to validate the data. This file should reveal the data structure of your data set, e.g. by including the header and one line of a CSV file. This file URL will be publicly available after publishing.",
|
||||||
|
"type": "files"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "author",
|
||||||
|
"label": "Author",
|
||||||
|
"placeholder": "e.g. Jelly McJellyfish",
|
||||||
|
"help": "Give proper attribution for your data set. You are welcome to use a pseudonym, and you can change your author name at any time. Please note that it will remain in the transaction history. For more information on how personal data is handled within the metadata, please refer to our [privacy policy](/privacy/en).",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tags",
|
||||||
|
"label": "Tags",
|
||||||
|
"placeholder": "e.g. logistics, ai",
|
||||||
|
"help": "Separate tags with comma."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "termsAndConditions",
|
||||||
|
"label": "Terms & Conditions",
|
||||||
|
"type": "terms",
|
||||||
|
"options": ["I agree to these Terms and Conditions"],
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"title": "Create services",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "access",
|
||||||
|
"label": "Access Type",
|
||||||
|
"help": "Choose how you want your files to be accessible for the specified price.",
|
||||||
|
"type": "boxSelection",
|
||||||
|
"options": ["Download", "Compute"],
|
||||||
|
"required": true,
|
||||||
|
"disclaimer": "Please do not provide downloadable personal data without the consent of the data subjects.",
|
||||||
|
"disclaimerValues": ["Download"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "providerUri",
|
||||||
|
"label": "Custom Provider URL",
|
||||||
|
"type": "providerUri",
|
||||||
|
"help": "Enter the URL for your custom provider or leave blank to use the default provider. [Learn more](https://github.com/oceanprotocol/provider/).",
|
||||||
|
"placeholder": "https://provider.polygon.oceanprotocol.com/",
|
||||||
|
"advanced": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "timeout",
|
||||||
|
"label": "Timeout",
|
||||||
|
"help": "Define how long buyers should be able to download the data set again after the initial purchase.",
|
||||||
|
"type": "select",
|
||||||
|
"options": ["Forever", "1 day", "1 week", "1 month", "1 year"],
|
||||||
|
"sortOptions": false,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dataTokenOptions",
|
||||||
|
"label": "Datatoken Name & Symbol",
|
||||||
|
"type": "datatoken",
|
||||||
|
"help": "The datatoken for this data set will be created with this name & symbol.",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pricing": {
|
||||||
|
"title": "Create pricing schema",
|
||||||
|
"fields": [{ "name": "dummy content" }]
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"title": "Publish",
|
"title": "Publish",
|
||||||
"description": "Highlight the important features of your data set or algorithm to make it more discoverable and catch the interest of data consumers.",
|
"description": "Highlight the important features of your data set or algorithm to make it more discoverable and catch the interest of data consumers.",
|
||||||
"warning": "Given the beta status, publishing on Ropsten or Rinkeby first is strongly recommended. Please familiarize yourself with [the market](https://oceanprotocol.com/technology/marketplaces), [the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13), and the [Terms of Use](/terms).",
|
"warning": "Publishing into a test network first is strongly recommended. Please familiarize yourself with [the market](https://oceanprotocol.com/technology/marketplaces), [the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13), and the [Terms of Use](/terms).",
|
||||||
"tooltipNetwork": "Assets are published into the network your wallet is connected to. Switch your wallet's network to publish into another one."
|
"tooltipNetwork": "Assets are published into the network your wallet is connected to. Switch your wallet's network to publish into another one."
|
||||||
}
|
}
|
@ -16,9 +16,15 @@ function createTypes(actions) {
|
|||||||
desc: String!
|
desc: String!
|
||||||
cookieName: String!
|
cookieName: String!
|
||||||
}
|
}
|
||||||
type PublishJsonData implements Node {
|
type PublishJson implements Node {
|
||||||
|
metadata: Extensions
|
||||||
|
services: Extensions
|
||||||
|
pricing: Extensions
|
||||||
|
}
|
||||||
|
type Extensions {
|
||||||
disclaimer: String
|
disclaimer: String
|
||||||
disclaimerValues: [String!]
|
disclaimerValues: [String!]
|
||||||
|
advanced: Boolean
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
createTypes(typeDefs)
|
createTypes(typeDefs)
|
||||||
|
5
src/@types/Form.d.ts
vendored
5
src/@types/Form.d.ts
vendored
@ -21,10 +21,9 @@ declare global {
|
|||||||
advanced?: boolean
|
advanced?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormContent {
|
interface FormStepContent {
|
||||||
title: string
|
title: string
|
||||||
description?: string
|
description?: string
|
||||||
success: string
|
fields: FormFieldProps[]
|
||||||
data: FormFieldProps[]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { Field } from 'formik'
|
|||||||
import styles from './AdvancedSettings.module.css'
|
import styles from './AdvancedSettings.module.css'
|
||||||
|
|
||||||
export default function AdvancedSettings(prop: {
|
export default function AdvancedSettings(prop: {
|
||||||
content: FormContent
|
content: FormStepContent
|
||||||
handleFieldChange: (
|
handleFieldChange: (
|
||||||
e: ChangeEvent<HTMLInputElement>,
|
e: ChangeEvent<HTMLInputElement>,
|
||||||
field: FormFieldProps
|
field: FormFieldProps
|
||||||
|
@ -16,7 +16,7 @@ export default function Terms(props: InputProps): ReactElement {
|
|||||||
const data = useStaticQuery(query)
|
const data = useStaticQuery(query)
|
||||||
const termsProps: InputProps = {
|
const termsProps: InputProps = {
|
||||||
...props,
|
...props,
|
||||||
defaultChecked: props.value.toString() === 'true'
|
defaultChecked: props?.value?.toString() === 'true'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -13,6 +13,8 @@ import styles from './index.module.css'
|
|||||||
import { ErrorMessage, FieldInputProps } from 'formik'
|
import { ErrorMessage, FieldInputProps } from 'formik'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import Disclaimer from './Disclaimer'
|
import Disclaimer from './Disclaimer'
|
||||||
|
import Tooltip from '@shared/atoms/Tooltip'
|
||||||
|
import Markdown from '@shared/atoms/Markdown'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
@ -97,7 +99,7 @@ export default function Input(props: Partial<InputProps>): ReactElement {
|
|||||||
data-is-submitting={props.form?.isSubmitting ? true : null}
|
data-is-submitting={props.form?.isSubmitting ? true : null}
|
||||||
>
|
>
|
||||||
<Label htmlFor={props.name} required={props.required}>
|
<Label htmlFor={props.name} required={props.required}>
|
||||||
{label}
|
{label} {help && <Tooltip content={<Markdown text={help} />} />}
|
||||||
</Label>
|
</Label>
|
||||||
<InputElement size={size} {...field} {...props} />
|
<InputElement size={size} {...field} {...props} />
|
||||||
|
|
||||||
@ -107,7 +109,7 @@ export default function Input(props: Partial<InputProps>): ReactElement {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{help && <Help>{help}</Help>}
|
{/* {help && <Help>{help}</Help>} */}
|
||||||
|
|
||||||
{disclaimer && (
|
{disclaimer && (
|
||||||
<Disclaimer visible={disclaimerVisible}>{disclaimer}</Disclaimer>
|
<Disclaimer visible={disclaimerVisible}>{disclaimer}</Disclaimer>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
font-size: var(--font-size-h3);
|
font-size: var(--font-size-h3);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 40rem) {
|
@media (min-width: 40rem) {
|
||||||
|
@ -10,7 +10,7 @@ export default function PageHeader({
|
|||||||
description,
|
description,
|
||||||
center
|
center
|
||||||
}: {
|
}: {
|
||||||
title: string
|
title: ReactElement
|
||||||
description?: string
|
description?: string
|
||||||
center?: boolean
|
center?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
@ -4,7 +4,6 @@ import MetaFull from './MetaFull'
|
|||||||
import MetaSecondary from './MetaSecondary'
|
import MetaSecondary from './MetaSecondary'
|
||||||
import AssetActions from '../AssetActions'
|
import AssetActions from '../AssetActions'
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
import Pricing from '../../Publish/Pricing'
|
|
||||||
import Bookmark from './Bookmark'
|
import Bookmark from './Bookmark'
|
||||||
import { useAsset } from '@context/Asset'
|
import { useAsset } from '@context/Asset'
|
||||||
import Alert from '@shared/atoms/Alert'
|
import Alert from '@shared/atoms/Alert'
|
||||||
@ -31,7 +30,6 @@ export default function AssetContent(): ReactElement {
|
|||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { owner, isInPurgatory, purgatoryData, isAssetNetwork } = useAsset()
|
const { owner, isInPurgatory, purgatoryData, isAssetNetwork } = useAsset()
|
||||||
const [showPricing, setShowPricing] = useState(false)
|
|
||||||
const [showEdit, setShowEdit] = useState<boolean>()
|
const [showEdit, setShowEdit] = useState<boolean>()
|
||||||
const [isComputeType, setIsComputeType] = useState<boolean>(false)
|
const [isComputeType, setIsComputeType] = useState<boolean>(false)
|
||||||
const [showEditCompute, setShowEditCompute] = useState<boolean>()
|
const [showEditCompute, setShowEditCompute] = useState<boolean>()
|
||||||
@ -43,7 +41,6 @@ export default function AssetContent(): ReactElement {
|
|||||||
|
|
||||||
const isOwner = accountId.toLowerCase() === owner.toLowerCase()
|
const isOwner = accountId.toLowerCase() === owner.toLowerCase()
|
||||||
setIsOwner(isOwner)
|
setIsOwner(isOwner)
|
||||||
setShowPricing(isOwner && price.type === '')
|
|
||||||
setIsComputeType(Boolean(ddo.findServiceByType('compute')))
|
setIsComputeType(Boolean(ddo.findServiceByType('compute')))
|
||||||
}, [accountId, price, owner, ddo])
|
}, [accountId, price, owner, ddo])
|
||||||
|
|
||||||
@ -70,7 +67,6 @@ export default function AssetContent(): ReactElement {
|
|||||||
|
|
||||||
<article className={styles.grid}>
|
<article className={styles.grid}>
|
||||||
<div>
|
<div>
|
||||||
{showPricing && <Pricing ddo={ddo} />}
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<MetaMain />
|
<MetaMain />
|
||||||
<Bookmark did={ddo.id} />
|
<Bookmark did={ddo.id} />
|
||||||
|
@ -10,23 +10,23 @@ export default function Debug({
|
|||||||
values: Partial<FormPublishData>
|
values: Partial<FormPublishData>
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const ddo = {
|
const ddo = {
|
||||||
'@context': 'https://w3id.org/did/v1',
|
'@context': 'https://w3id.org/did/v1'
|
||||||
dataTokenInfo: {
|
// dataTokenInfo: {
|
||||||
...values.dataTokenOptions
|
// ...values.dataTokenOptions
|
||||||
},
|
// },
|
||||||
service: [
|
// service: [
|
||||||
{
|
// {
|
||||||
index: 0,
|
// index: 0,
|
||||||
type: 'metadata',
|
// type: 'metadata',
|
||||||
attributes: { ...transformPublishFormToMetadata(values) }
|
// attributes: { ...transformPublishFormToMetadata(values) }
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
index: 1,
|
// index: 1,
|
||||||
type: values.access,
|
// type: values.access,
|
||||||
serviceEndpoint: values.providerUri,
|
// serviceEndpoint: values.providerUri,
|
||||||
attributes: {}
|
// attributes: {}
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
.form {
|
|
||||||
composes: box from '../../atoms/Box.module.css';
|
|
||||||
margin-bottom: var(--spacer);
|
|
||||||
border-top: none;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
import React, {
|
|
||||||
ReactElement,
|
|
||||||
useEffect,
|
|
||||||
FormEvent,
|
|
||||||
ChangeEvent,
|
|
||||||
useState
|
|
||||||
} from 'react'
|
|
||||||
import { useFormikContext, Field, Form, FormikContextType } from 'formik'
|
|
||||||
import Input from '@shared/Form/Input'
|
|
||||||
import Download from '@images/download.svg'
|
|
||||||
import Compute from '@images/compute.svg'
|
|
||||||
import FormTitle from './FormTitle'
|
|
||||||
import FormActions from './FormActions'
|
|
||||||
import AdvancedSettings from '@shared/Form/FormFields/AdvancedSettings'
|
|
||||||
import { FormPublishData } from './_types'
|
|
||||||
import styles from './FormPublish.module.css'
|
|
||||||
import { initialValues } from './_constants'
|
|
||||||
import content from '../../../content/pages/publish/form-dataset.json'
|
|
||||||
|
|
||||||
export default function FormPublish(): ReactElement {
|
|
||||||
const {
|
|
||||||
status,
|
|
||||||
setStatus,
|
|
||||||
isValid,
|
|
||||||
values,
|
|
||||||
setErrors,
|
|
||||||
setTouched,
|
|
||||||
resetForm,
|
|
||||||
validateField,
|
|
||||||
setFieldValue
|
|
||||||
}: FormikContextType<FormPublishData> = useFormikContext()
|
|
||||||
|
|
||||||
const [computeTypeSelected, setComputeTypeSelected] = useState<boolean>(false)
|
|
||||||
|
|
||||||
// reset form validation on every mount
|
|
||||||
useEffect(() => {
|
|
||||||
setErrors({})
|
|
||||||
setTouched({})
|
|
||||||
|
|
||||||
// setSubmitting(false)
|
|
||||||
}, [setErrors, setTouched])
|
|
||||||
|
|
||||||
const accessTypeOptions = [
|
|
||||||
{
|
|
||||||
name: 'Download',
|
|
||||||
title: 'Download',
|
|
||||||
icon: <Download />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Compute',
|
|
||||||
title: 'Compute',
|
|
||||||
icon: <Compute />
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const computeTypeOptions = ['1 day', '1 week', '1 month', '1 year']
|
|
||||||
|
|
||||||
// Manually handle change events instead of using `handleChange` from Formik.
|
|
||||||
// Workaround for default `validateOnChange` not kicking in
|
|
||||||
// function handleFieldChange(
|
|
||||||
// e: ChangeEvent<HTMLInputElement>,
|
|
||||||
// field: FormFieldProps
|
|
||||||
// ) {
|
|
||||||
// const value =
|
|
||||||
// field.type === 'terms' ? !JSON.parse(e.target.value) : e.target.value
|
|
||||||
|
|
||||||
// if (field.name === 'access' && value === 'Compute') {
|
|
||||||
// setComputeTypeSelected(true)
|
|
||||||
// if (values.timeout === 'Forever')
|
|
||||||
// setFieldValue('timeout', computeTypeOptions[0])
|
|
||||||
// } else {
|
|
||||||
// if (field.name === 'access' && value === 'Download') {
|
|
||||||
// setComputeTypeSelected(false)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// validateField(field.name)
|
|
||||||
// setFieldValue(field.name, value)
|
|
||||||
// }
|
|
||||||
|
|
||||||
const resetFormAndClearStorage = (e: FormEvent<Element>) => {
|
|
||||||
e.preventDefault()
|
|
||||||
resetForm({
|
|
||||||
values: initialValues as FormPublishData,
|
|
||||||
status: 'empty'
|
|
||||||
})
|
|
||||||
setStatus('empty')
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
className={styles.form}
|
|
||||||
// do we need this?
|
|
||||||
onChange={() => status === 'empty' && setStatus(null)}
|
|
||||||
>
|
|
||||||
<FormTitle title={content.title} />
|
|
||||||
|
|
||||||
{content.data.map(
|
|
||||||
(field: FormFieldProps) =>
|
|
||||||
field.advanced !== true && (
|
|
||||||
<Field
|
|
||||||
key={field.name}
|
|
||||||
{...field}
|
|
||||||
options={
|
|
||||||
field.type === 'boxSelection'
|
|
||||||
? accessTypeOptions
|
|
||||||
: field.name === 'timeout' && computeTypeSelected === true
|
|
||||||
? computeTypeOptions
|
|
||||||
: field.options
|
|
||||||
}
|
|
||||||
component={Input}
|
|
||||||
// onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
|
||||||
// handleFieldChange(e, field)
|
|
||||||
// }
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FormActions
|
|
||||||
isValid={isValid}
|
|
||||||
resetFormAndClearStorage={resetFormAndClearStorage}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
)
|
|
||||||
}
|
|
@ -2,6 +2,8 @@ import React, { FormEvent, ReactElement } from 'react'
|
|||||||
import { useOcean } from '@context/Ocean'
|
import { useOcean } from '@context/Ocean'
|
||||||
import Button from '@shared/atoms/Button'
|
import Button from '@shared/atoms/Button'
|
||||||
import styles from './FormActions.module.css'
|
import styles from './FormActions.module.css'
|
||||||
|
import { FormikContextType, useFormikContext } from 'formik'
|
||||||
|
import { FormPublishData } from '../_types'
|
||||||
|
|
||||||
export default function FormActions({
|
export default function FormActions({
|
||||||
isValid,
|
isValid,
|
||||||
@ -11,22 +13,22 @@ export default function FormActions({
|
|||||||
resetFormAndClearStorage: (e: FormEvent<Element>) => void
|
resetFormAndClearStorage: (e: FormEvent<Element>) => void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean, account } = useOcean()
|
const { ocean, account } = useOcean()
|
||||||
|
const { status }: FormikContextType<FormPublishData> = useFormikContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className={styles.actions}>
|
<footer className={styles.actions}>
|
||||||
<Button
|
|
||||||
style="primary"
|
|
||||||
type="submit"
|
|
||||||
disabled={!ocean || !account || !isValid || status === 'empty'}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{status !== 'empty' && (
|
{status !== 'empty' && (
|
||||||
<Button style="text" size="small" onClick={resetFormAndClearStorage}>
|
<Button style="text" size="small" onClick={resetFormAndClearStorage}>
|
||||||
Reset Form
|
Reset Form
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="primary"
|
||||||
|
disabled={!ocean || !account || !isValid || status === 'empty'}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import stylesIndex from './index.module.css'
|
|
||||||
import styles from './Coin.module.css'
|
import styles from './Coin.module.css'
|
||||||
import InputElement from '@shared/Form/Input/InputElement'
|
import InputElement from '@shared/Form/Input/InputElement'
|
||||||
import Logo from '@images/logo.svg'
|
import Logo from '@images/logo.svg'
|
||||||
@ -46,7 +45,7 @@ export default function Coin({
|
|||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
{datatokenOptions?.symbol === 'OCEAN' && (
|
{datatokenOptions?.symbol === 'OCEAN' && (
|
||||||
<Conversion price={field.value} className={stylesIndex.conversion} />
|
<Conversion price={field.value} />
|
||||||
)}
|
)}
|
||||||
<Error meta={meta} />
|
<Error meta={meta} />
|
||||||
</div>
|
</div>
|
@ -1,7 +1,3 @@
|
|||||||
.dynamic {
|
|
||||||
composes: content from './index.module.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.wallet {
|
.wallet {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
@ -7,27 +7,21 @@ import Wallet from '../../../Header/Wallet'
|
|||||||
import Coin from './Coin'
|
import Coin from './Coin'
|
||||||
import styles from './Dynamic.module.css'
|
import styles from './Dynamic.module.css'
|
||||||
import Fees from './Fees'
|
import Fees from './Fees'
|
||||||
import stylesIndex from './index.module.css'
|
|
||||||
import { FormikContextType, useFormikContext } from 'formik'
|
import { FormikContextType, useFormikContext } from 'formik'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
|
||||||
import Price from './Price'
|
import Price from './Price'
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
import { useOcean } from '@context/Ocean'
|
import { useOcean } from '@context/Ocean'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import { FormPublishData } from '../../_types'
|
||||||
|
|
||||||
export default function Dynamic({
|
export default function Dynamic({ content }: { content: any }): ReactElement {
|
||||||
ddo,
|
|
||||||
content
|
|
||||||
}: {
|
|
||||||
ddo: DDO
|
|
||||||
content: any
|
|
||||||
}): ReactElement {
|
|
||||||
const { networkId, balance } = useWeb3()
|
const { networkId, balance } = useWeb3()
|
||||||
const { account } = useOcean()
|
const { account } = useOcean()
|
||||||
const [firstPrice, setFirstPrice] = useState<string>()
|
const [firstPrice, setFirstPrice] = useState<string>()
|
||||||
|
|
||||||
// Connect with form
|
// Connect with form
|
||||||
const { values }: FormikContextType<PriceOptions> = useFormikContext()
|
const { values }: FormikContextType<FormPublishData> = useFormikContext()
|
||||||
|
const { dataTokenOptions } = values.services[0]
|
||||||
|
|
||||||
const {
|
const {
|
||||||
price,
|
price,
|
||||||
@ -36,7 +30,7 @@ export default function Dynamic({
|
|||||||
swapFee,
|
swapFee,
|
||||||
dtAmount,
|
dtAmount,
|
||||||
oceanAmount
|
oceanAmount
|
||||||
} = values
|
} = values.pricing
|
||||||
|
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
|
|
||||||
@ -69,8 +63,8 @@ export default function Dynamic({
|
|||||||
}, [price, networkId, account, balance])
|
}, [price, networkId, account, balance])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.dynamic}>
|
<>
|
||||||
<FormHelp className={stylesIndex.help}>{content.info}</FormHelp>
|
<FormHelp>{content.info}</FormHelp>
|
||||||
|
|
||||||
<aside className={styles.wallet}>
|
<aside className={styles.wallet}>
|
||||||
{balance?.ocean && (
|
{balance?.ocean && (
|
||||||
@ -88,7 +82,7 @@ export default function Dynamic({
|
|||||||
Price <Tooltip content={content.tooltips.poolInfo} />
|
Price <Tooltip content={content.tooltips.poolInfo} />
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<Price ddo={ddo} firstPrice={firstPrice} />
|
<Price firstPrice={firstPrice} />
|
||||||
|
|
||||||
<h4 className={styles.title}>
|
<h4 className={styles.title}>
|
||||||
Datatoken Liquidity Pool <Tooltip content={content.tooltips.poolInfo} />
|
Datatoken Liquidity Pool <Tooltip content={content.tooltips.poolInfo} />
|
||||||
@ -103,8 +97,8 @@ export default function Dynamic({
|
|||||||
<Coin
|
<Coin
|
||||||
name="dtAmount"
|
name="dtAmount"
|
||||||
datatokenOptions={{
|
datatokenOptions={{
|
||||||
symbol: ddo.dataTokenInfo.symbol,
|
symbol: dataTokenOptions.symbol,
|
||||||
name: ddo.dataTokenInfo.name
|
name: dataTokenOptions.name
|
||||||
}}
|
}}
|
||||||
weight={`${Number(weightOnDataToken) * 10}%`}
|
weight={`${Number(weightOnDataToken) * 10}%`}
|
||||||
readOnly
|
readOnly
|
||||||
@ -123,6 +117,6 @@ export default function Dynamic({
|
|||||||
<Alert text={error} state="error" />
|
<Alert text={error} state="error" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
14
src/components/Publish/FormPublish/Pricing/Fixed.tsx
Normal file
14
src/components/Publish/FormPublish/Pricing/Fixed.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import FormHelp from '@shared/Form/Input/Help'
|
||||||
|
import Price from './Price'
|
||||||
|
import Fees from './Fees'
|
||||||
|
|
||||||
|
export default function Fixed({ content }: { content: any }): ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FormHelp>{content.info}</FormHelp>
|
||||||
|
<Price />
|
||||||
|
<Fees tooltips={content.tooltips} pricingType="fixed" />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
12
src/components/Publish/FormPublish/Pricing/Free.tsx
Normal file
12
src/components/Publish/FormPublish/Pricing/Free.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import FormHelp from '@shared/Form/Input/Help'
|
||||||
|
import Price from './Price'
|
||||||
|
|
||||||
|
export default function Free({ content }: { content: any }): ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FormHelp>{content.info}</FormHelp>
|
||||||
|
<Price free />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -1,40 +1,23 @@
|
|||||||
import Conversion from '@shared/Price/Conversion'
|
import Conversion from '@shared/Price/Conversion'
|
||||||
import { useField } from 'formik'
|
import { useField, useFormikContext } from 'formik'
|
||||||
import React, { ReactElement, useState, useEffect } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Input from '@shared/Form/Input'
|
import Input from '@shared/Form/Input'
|
||||||
import Error from './Error'
|
import Error from './Error'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
|
||||||
import PriceUnit from '@shared/Price/PriceUnit'
|
import PriceUnit from '@shared/Price/PriceUnit'
|
||||||
import usePricing from '@hooks/usePricing'
|
|
||||||
import styles from './Price.module.css'
|
import styles from './Price.module.css'
|
||||||
|
import { FormPublishData } from '../../_types'
|
||||||
|
|
||||||
export default function Price({
|
export default function Price({
|
||||||
ddo,
|
|
||||||
firstPrice,
|
firstPrice,
|
||||||
free
|
free
|
||||||
}: {
|
}: {
|
||||||
ddo: DDO
|
|
||||||
firstPrice?: string
|
firstPrice?: string
|
||||||
free?: boolean
|
free?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [field, meta] = useField('price')
|
const [field, meta] = useField('price')
|
||||||
const { getDTName, getDTSymbol } = usePricing()
|
|
||||||
const [dtSymbol, setDtSymbol] = useState<string>()
|
|
||||||
const [dtName, setDtName] = useState<string>()
|
|
||||||
|
|
||||||
useEffect(() => {
|
const { values } = useFormikContext<FormPublishData>()
|
||||||
if (!ddo) return
|
const { dataTokenOptions } = values.services[0]
|
||||||
async function setDatatokenSymbol(ddo: DDO) {
|
|
||||||
const dtSymbol = await getDTSymbol(ddo)
|
|
||||||
setDtSymbol(dtSymbol)
|
|
||||||
}
|
|
||||||
async function setDatatokenName(ddo: DDO) {
|
|
||||||
const dtName = await getDTName(ddo)
|
|
||||||
setDtName(dtName)
|
|
||||||
}
|
|
||||||
setDatatokenSymbol(ddo)
|
|
||||||
setDatatokenName(ddo)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.price}>
|
<div className={styles.price}>
|
||||||
@ -62,7 +45,7 @@ export default function Price({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.datatoken}>
|
<div className={styles.datatoken}>
|
||||||
<h4>
|
<h4>
|
||||||
= <strong>1</strong> {dtSymbol}{' '}
|
= <strong>1</strong> {dataTokenOptions.symbol}{' '}
|
||||||
<Conversion price={field.value} className={styles.conversion} />
|
<Conversion price={field.value} className={styles.conversion} />
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
152
src/components/Publish/FormPublish/Pricing/index.tsx
Normal file
152
src/components/Publish/FormPublish/Pricing/index.tsx
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
|
import { useFormikContext } from 'formik'
|
||||||
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
|
import { useSiteMetadata } from '@hooks/useSiteMetadata'
|
||||||
|
import Tabs from '@shared/atoms/Tabs'
|
||||||
|
import { isValidNumber } from '@utils/numbers'
|
||||||
|
import Decimal from 'decimal.js'
|
||||||
|
import { FormPublishData } from '../../_types'
|
||||||
|
import Dynamic from './Dynamic'
|
||||||
|
import Fixed from './Fixed'
|
||||||
|
import Free from './Free'
|
||||||
|
|
||||||
|
const query = graphql`
|
||||||
|
query PricingQuery {
|
||||||
|
content: allFile(filter: { relativePath: { eq: "price.json" } }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
childContentJson {
|
||||||
|
create {
|
||||||
|
empty {
|
||||||
|
title
|
||||||
|
info
|
||||||
|
action {
|
||||||
|
name
|
||||||
|
help
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixed {
|
||||||
|
title
|
||||||
|
info
|
||||||
|
tooltips {
|
||||||
|
communityFee
|
||||||
|
marketplaceFee
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynamic {
|
||||||
|
title
|
||||||
|
info
|
||||||
|
tooltips {
|
||||||
|
poolInfo
|
||||||
|
swapFee
|
||||||
|
communityFee
|
||||||
|
marketplaceFee
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free {
|
||||||
|
title
|
||||||
|
info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function Pricing(): ReactElement {
|
||||||
|
// Get content
|
||||||
|
const data = useStaticQuery(query)
|
||||||
|
const content = data.content.edges[0].node.childContentJson.create
|
||||||
|
|
||||||
|
const { appConfig } = useSiteMetadata()
|
||||||
|
|
||||||
|
// Connect with main publish form
|
||||||
|
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
||||||
|
const { pricing } = values
|
||||||
|
const { price, oceanAmount, weightOnOcean, weightOnDataToken, type } = pricing
|
||||||
|
|
||||||
|
// Switch type value upon tab change
|
||||||
|
function handleTabChange(tabName: string) {
|
||||||
|
const type = tabName.toLowerCase()
|
||||||
|
setFieldValue('pricing.type', type)
|
||||||
|
type === 'fixed' && setFieldValue('pricing.dtAmount', 1000)
|
||||||
|
type === 'free' && price < 1 && setFieldValue('pricing.price', 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always update everything when price value changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (type === 'fixed') return
|
||||||
|
const dtAmount =
|
||||||
|
isValidNumber(oceanAmount) &&
|
||||||
|
isValidNumber(weightOnOcean) &&
|
||||||
|
isValidNumber(price) &&
|
||||||
|
isValidNumber(weightOnDataToken)
|
||||||
|
? new Decimal(oceanAmount)
|
||||||
|
.dividedBy(new Decimal(weightOnOcean))
|
||||||
|
.dividedBy(new Decimal(price))
|
||||||
|
.mul(new Decimal(weightOnDataToken))
|
||||||
|
: 0
|
||||||
|
|
||||||
|
setFieldValue('pricing.dtAmount', dtAmount)
|
||||||
|
}, [price, oceanAmount, weightOnOcean, weightOnDataToken, type])
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
appConfig.allowFixedPricing === 'true'
|
||||||
|
? {
|
||||||
|
title: content.fixed.title,
|
||||||
|
content: <Fixed content={content.fixed} />
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
appConfig.allowDynamicPricing === 'true'
|
||||||
|
? {
|
||||||
|
title: content.dynamic.title,
|
||||||
|
content: <Dynamic content={content.dynamic} />
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
appConfig.allowFreePricing === 'true'
|
||||||
|
? {
|
||||||
|
title: content.free.title,
|
||||||
|
content: <Free content={content.free} />
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
].filter((tab) => tab !== undefined)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
items={tabs}
|
||||||
|
handleTabChange={handleTabChange}
|
||||||
|
defaultIndex={type === 'fixed' ? 0 : 1}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
// async function handleCreatePricing(values: PriceOptions) {
|
||||||
|
// try {
|
||||||
|
// const priceOptions = {
|
||||||
|
// ...values,
|
||||||
|
// // swapFee is tricky: to get 0.1% you need to send 0.001 as value
|
||||||
|
// swapFee: `${values.swapFee / 100}`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const tx = await createPricing(priceOptions, ddo)
|
||||||
|
|
||||||
|
// // Pricing failed
|
||||||
|
// if (!tx || pricingError) {
|
||||||
|
// toast.error(pricingError || 'Price creation failed.')
|
||||||
|
// Logger.error(pricingError || 'Price creation failed.')
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Pricing succeeded
|
||||||
|
// setSuccess(
|
||||||
|
// `🎉 Successfully created a ${values.type} price. 🎉 Reload the page to get all updates.`
|
||||||
|
// )
|
||||||
|
// Logger.log(`Transaction: ${tx}`)
|
||||||
|
// } catch (error) {
|
||||||
|
// toast.error(error.message)
|
||||||
|
// Logger.error(error.message)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
4
src/components/Publish/FormPublish/index.module.css
Normal file
4
src/components/Publish/FormPublish/index.module.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.form {
|
||||||
|
composes: box from '@shared/atoms/Box.module.css';
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
}
|
224
src/components/Publish/FormPublish/index.tsx
Normal file
224
src/components/Publish/FormPublish/index.tsx
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
import React, {
|
||||||
|
ReactElement,
|
||||||
|
useEffect,
|
||||||
|
FormEvent,
|
||||||
|
ChangeEvent,
|
||||||
|
useState
|
||||||
|
} from 'react'
|
||||||
|
import { useStaticQuery, graphql } from 'gatsby'
|
||||||
|
import { useFormikContext, Field, Form, FormikContextType } from 'formik'
|
||||||
|
import Input from '@shared/Form/Input'
|
||||||
|
import { ReactComponent as Download } from '@images/download.svg'
|
||||||
|
import { ReactComponent as Compute } from '@images/compute.svg'
|
||||||
|
import FormActions from './FormActions'
|
||||||
|
import AdvancedSettings from '@shared/Form/FormFields/AdvancedSettings'
|
||||||
|
import { FormPublishData } from '../_types'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import { initialValues } from '../_constants'
|
||||||
|
import Tabs from '@shared/atoms/Tabs'
|
||||||
|
import Pricing from './Pricing'
|
||||||
|
import Debug from '../Debug'
|
||||||
|
|
||||||
|
const query = graphql`
|
||||||
|
query {
|
||||||
|
content: publishJson {
|
||||||
|
metadata {
|
||||||
|
title
|
||||||
|
fields {
|
||||||
|
name
|
||||||
|
placeholder
|
||||||
|
label
|
||||||
|
help
|
||||||
|
type
|
||||||
|
required
|
||||||
|
options
|
||||||
|
disclaimer
|
||||||
|
disclaimerValues
|
||||||
|
advanced
|
||||||
|
}
|
||||||
|
}
|
||||||
|
services {
|
||||||
|
title
|
||||||
|
fields {
|
||||||
|
name
|
||||||
|
placeholder
|
||||||
|
label
|
||||||
|
help
|
||||||
|
type
|
||||||
|
required
|
||||||
|
options
|
||||||
|
disclaimer
|
||||||
|
disclaimerValues
|
||||||
|
advanced
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pricing {
|
||||||
|
title
|
||||||
|
fields {
|
||||||
|
name
|
||||||
|
placeholder
|
||||||
|
label
|
||||||
|
help
|
||||||
|
type
|
||||||
|
required
|
||||||
|
options
|
||||||
|
disclaimer
|
||||||
|
disclaimerValues
|
||||||
|
advanced
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const accessTypeOptions = [
|
||||||
|
{
|
||||||
|
name: 'Download',
|
||||||
|
title: 'Download',
|
||||||
|
icon: <Download />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Compute',
|
||||||
|
title: 'Compute',
|
||||||
|
icon: <Compute />
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function FormPublish(): ReactElement {
|
||||||
|
const { content } = useStaticQuery(query)
|
||||||
|
|
||||||
|
const {
|
||||||
|
setStatus,
|
||||||
|
isValid,
|
||||||
|
values,
|
||||||
|
setErrors,
|
||||||
|
setTouched,
|
||||||
|
resetForm,
|
||||||
|
validateField,
|
||||||
|
setFieldValue
|
||||||
|
}: FormikContextType<FormPublishData> = useFormikContext()
|
||||||
|
|
||||||
|
const [computeTypeSelected, setComputeTypeSelected] = useState<boolean>(false)
|
||||||
|
|
||||||
|
// reset form validation on every mount
|
||||||
|
useEffect(() => {
|
||||||
|
setErrors({})
|
||||||
|
setTouched({})
|
||||||
|
|
||||||
|
// setSubmitting(false)
|
||||||
|
}, [setErrors, setTouched])
|
||||||
|
|
||||||
|
const computeTypeOptions = ['1 day', '1 week', '1 month', '1 year']
|
||||||
|
|
||||||
|
// Manually handle change events instead of using `handleChange` from Formik.
|
||||||
|
// Workaround for default `validateOnChange` not kicking in
|
||||||
|
function handleFieldChange(
|
||||||
|
e: ChangeEvent<HTMLInputElement>,
|
||||||
|
field: FormFieldProps
|
||||||
|
) {
|
||||||
|
const value =
|
||||||
|
field.type === 'terms' ? !JSON.parse(e.target.value) : e.target.value
|
||||||
|
|
||||||
|
if (field.name === 'access' && value === 'Compute') {
|
||||||
|
setComputeTypeSelected(true)
|
||||||
|
if (values.timeout === 'Forever')
|
||||||
|
setFieldValue('timeout', computeTypeOptions[0])
|
||||||
|
} else {
|
||||||
|
if (field.name === 'access' && value === 'Download') {
|
||||||
|
setComputeTypeSelected(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateField(field.name)
|
||||||
|
setFieldValue(field.name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetFormAndClearStorage = (e: FormEvent<Element>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
resetForm({
|
||||||
|
values: initialValues as FormPublishData,
|
||||||
|
status: 'empty'
|
||||||
|
})
|
||||||
|
setStatus('empty')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStepContentFields(contentStep: FormStepContent) {
|
||||||
|
return contentStep.fields.map(
|
||||||
|
(field: FormFieldProps) =>
|
||||||
|
field.advanced !== true && (
|
||||||
|
<Field
|
||||||
|
key={field.name}
|
||||||
|
{...field}
|
||||||
|
options={
|
||||||
|
field.type === 'boxSelection'
|
||||||
|
? accessTypeOptions
|
||||||
|
: field.name === 'timeout' && computeTypeSelected === true
|
||||||
|
? computeTypeOptions
|
||||||
|
: field.options
|
||||||
|
}
|
||||||
|
component={Input}
|
||||||
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
handleFieldChange(e, field)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: content.metadata.title,
|
||||||
|
content: (
|
||||||
|
<>
|
||||||
|
{getStepContentFields(content.metadata)}
|
||||||
|
<AdvancedSettings
|
||||||
|
content={content.metadata}
|
||||||
|
handleFieldChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
<FormActions
|
||||||
|
isValid={isValid}
|
||||||
|
resetFormAndClearStorage={resetFormAndClearStorage}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: content.services.title,
|
||||||
|
content: (
|
||||||
|
<>
|
||||||
|
{getStepContentFields(content.services)}
|
||||||
|
<AdvancedSettings
|
||||||
|
content={content.services}
|
||||||
|
handleFieldChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
<FormActions
|
||||||
|
isValid={isValid}
|
||||||
|
resetFormAndClearStorage={resetFormAndClearStorage}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: content.pricing.title,
|
||||||
|
content: (
|
||||||
|
<>
|
||||||
|
<Pricing />
|
||||||
|
<FormActions
|
||||||
|
isValid={isValid}
|
||||||
|
resetFormAndClearStorage={resetFormAndClearStorage}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form className={styles.form}>
|
||||||
|
<Tabs items={tabs} />
|
||||||
|
</Form>
|
||||||
|
<Debug values={values} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
.title {
|
|
||||||
font-size: var(--font-size-h4);
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.network {
|
|
||||||
color: var(--font-color-heading);
|
|
||||||
margin-left: calc(var(--spacer) / 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.network svg {
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
margin-top: -0.25em;
|
|
||||||
fill: var(--color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip {
|
|
||||||
width: 0.75em;
|
|
||||||
height: 0.75em;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
.feedback {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 20vh;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
import Loader from '@shared/atoms/Loader'
|
|
||||||
import SuccessConfetti from '@shared/SuccessConfetti'
|
|
||||||
import React, { ReactElement } from 'react'
|
|
||||||
import styles from './Feedback.module.css'
|
|
||||||
import Button from '@shared/atoms/Button'
|
|
||||||
|
|
||||||
export default function Feedback({
|
|
||||||
success,
|
|
||||||
pricingStepText
|
|
||||||
}: {
|
|
||||||
success: string
|
|
||||||
pricingStepText: string
|
|
||||||
}): ReactElement {
|
|
||||||
const SuccessAction = () => (
|
|
||||||
<Button
|
|
||||||
style="primary"
|
|
||||||
size="small"
|
|
||||||
className={styles.action}
|
|
||||||
onClick={() => window?.location.reload()}
|
|
||||||
>
|
|
||||||
Reload Page
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.feedback}>
|
|
||||||
{success ? (
|
|
||||||
<SuccessConfetti success={success} action={<SuccessAction />} />
|
|
||||||
) : (
|
|
||||||
<Loader message={pricingStepText} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
.fixed {
|
|
||||||
composes: content from './index.module.css';
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
import React, { ReactElement } from 'react'
|
|
||||||
import stylesIndex from './index.module.css'
|
|
||||||
import styles from './Fixed.module.css'
|
|
||||||
import FormHelp from '@shared/Form/Input/Help'
|
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
|
||||||
import Price from './Price'
|
|
||||||
import Fees from './Fees'
|
|
||||||
|
|
||||||
export default function Fixed({
|
|
||||||
ddo,
|
|
||||||
content
|
|
||||||
}: {
|
|
||||||
ddo: DDO
|
|
||||||
content: any
|
|
||||||
}): ReactElement {
|
|
||||||
return (
|
|
||||||
<div className={styles.fixed}>
|
|
||||||
<FormHelp className={stylesIndex.help}>{content.info}</FormHelp>
|
|
||||||
<Price ddo={ddo} />
|
|
||||||
<Fees tooltips={content.tooltips} pricingType="fixed" />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
.free {
|
|
||||||
composes: content from './index.module.css';
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
import React, { ReactElement } from 'react'
|
|
||||||
import stylesIndex from './index.module.css'
|
|
||||||
import styles from './Free.module.css'
|
|
||||||
import FormHelp from '@shared/Form/Input/Help'
|
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
|
||||||
import Price from './Price'
|
|
||||||
|
|
||||||
export default function Free({
|
|
||||||
ddo,
|
|
||||||
content
|
|
||||||
}: {
|
|
||||||
ddo: DDO
|
|
||||||
content: any
|
|
||||||
}): ReactElement {
|
|
||||||
return (
|
|
||||||
<div className={styles.free}>
|
|
||||||
<FormHelp className={stylesIndex.help}>{content.info}</FormHelp>
|
|
||||||
<Price ddo={ddo} free />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
.content {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content label {
|
|
||||||
color: var(--color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content input {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content p {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content [class*='error'] {
|
|
||||||
text-align: left;
|
|
||||||
top: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions button {
|
|
||||||
margin-left: calc(var(--spacer) / 2);
|
|
||||||
margin-right: calc(var(--spacer) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actionsHelp {
|
|
||||||
margin-top: calc(var(--spacer) / 2);
|
|
||||||
padding-left: var(--spacer);
|
|
||||||
padding-right: var(--spacer);
|
|
||||||
}
|
|
||||||
|
|
||||||
.free {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: calc(var(--spacer) / 1.5);
|
|
||||||
}
|
|
@ -1,109 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect } from 'react'
|
|
||||||
import styles from './index.module.css'
|
|
||||||
import Tabs from '@shared/atoms/Tabs'
|
|
||||||
import Fixed from './Fixed'
|
|
||||||
import Dynamic from './Dynamic'
|
|
||||||
import Free from './Free'
|
|
||||||
import { useFormikContext } from 'formik'
|
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
|
||||||
import Button from '@shared/atoms/Button'
|
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
|
||||||
import FormHelp from '@shared/Form/Input/Help'
|
|
||||||
import { useSiteMetadata } from '@hooks/useSiteMetadata'
|
|
||||||
|
|
||||||
import { isValidNumber } from '@utils/numbers'
|
|
||||||
import Decimal from 'decimal.js'
|
|
||||||
|
|
||||||
Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
|
|
||||||
|
|
||||||
export default function FormPricing({
|
|
||||||
ddo,
|
|
||||||
setShowPricing,
|
|
||||||
content
|
|
||||||
}: {
|
|
||||||
ddo: DDO
|
|
||||||
setShowPricing: (value: boolean) => void
|
|
||||||
content: any
|
|
||||||
}): ReactElement {
|
|
||||||
const { debug } = useUserPreferences()
|
|
||||||
const { appConfig } = useSiteMetadata()
|
|
||||||
|
|
||||||
// Connect with form
|
|
||||||
const { values, setFieldValue, submitForm } = useFormikContext()
|
|
||||||
const { price, oceanAmount, weightOnOcean, weightOnDataToken, type } =
|
|
||||||
values as PriceOptions
|
|
||||||
|
|
||||||
// Switch type value upon tab change
|
|
||||||
function handleTabChange(tabName: string) {
|
|
||||||
const type = tabName.toLowerCase()
|
|
||||||
setFieldValue('type', type)
|
|
||||||
type === 'fixed' && setFieldValue('dtAmount', 1000)
|
|
||||||
type === 'free' && price < 1 && setFieldValue('price', 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always update everything when price value changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (type === 'fixed') return
|
|
||||||
const dtAmount =
|
|
||||||
isValidNumber(oceanAmount) &&
|
|
||||||
isValidNumber(weightOnOcean) &&
|
|
||||||
isValidNumber(price) &&
|
|
||||||
isValidNumber(weightOnDataToken)
|
|
||||||
? new Decimal(oceanAmount)
|
|
||||||
.dividedBy(new Decimal(weightOnOcean))
|
|
||||||
.dividedBy(new Decimal(price))
|
|
||||||
.mul(new Decimal(weightOnDataToken))
|
|
||||||
: 0
|
|
||||||
|
|
||||||
setFieldValue('dtAmount', dtAmount)
|
|
||||||
}, [price, oceanAmount, weightOnOcean, weightOnDataToken, type])
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
appConfig.allowFixedPricing === 'true'
|
|
||||||
? {
|
|
||||||
title: content.fixed.title,
|
|
||||||
content: <Fixed content={content.fixed} ddo={ddo} />
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
appConfig.allowDynamicPricing === 'true'
|
|
||||||
? {
|
|
||||||
title: content.dynamic.title,
|
|
||||||
content: <Dynamic content={content.dynamic} ddo={ddo} />
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
appConfig.allowFreePricing === 'true'
|
|
||||||
? {
|
|
||||||
title: content.free.title,
|
|
||||||
content: <Free content={content.free} ddo={ddo} />
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
].filter((tab) => tab !== undefined)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Tabs
|
|
||||||
items={tabs}
|
|
||||||
handleTabChange={handleTabChange}
|
|
||||||
defaultIndex={type === 'fixed' ? 0 : 1}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.actions}>
|
|
||||||
<Button style="primary" onClick={() => submitForm()}>
|
|
||||||
{content.empty.action.name}
|
|
||||||
</Button>
|
|
||||||
<Button style="text" size="small" onClick={() => setShowPricing(false)}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<FormHelp className={styles.actionsHelp}>
|
|
||||||
{content.empty.action.help}
|
|
||||||
</FormHelp>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{debug === true && (
|
|
||||||
<pre>
|
|
||||||
<code>{JSON.stringify(values, null, 2)}</code>
|
|
||||||
</pre>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
import { allowDynamicPricing, allowFixedPricing } from '../../../../app.config'
|
|
||||||
import * as Yup from 'yup'
|
|
||||||
|
|
||||||
export const validationSchema: Yup.SchemaOf<PriceOptions> = Yup.object().shape({
|
|
||||||
price: Yup.number()
|
|
||||||
.min(1, (param) => `Must be more or equal to ${param.min}`)
|
|
||||||
.required('Required'),
|
|
||||||
dtAmount: Yup.number()
|
|
||||||
.min(9, (param) => `Must be more or equal to ${param.min}`)
|
|
||||||
.required('Required'),
|
|
||||||
oceanAmount: Yup.number()
|
|
||||||
.min(21, (param) => `Must be more or equal to ${param.min}`)
|
|
||||||
.required('Required'),
|
|
||||||
type: Yup.string()
|
|
||||||
.matches(/fixed|dynamic|free/g, { excludeEmptyString: true })
|
|
||||||
.required('Required'),
|
|
||||||
weightOnDataToken: Yup.string().required('Required'),
|
|
||||||
weightOnOcean: Yup.string().required('Required'),
|
|
||||||
swapFee: Yup.number()
|
|
||||||
.min(0.1, (param) => `Must be more or equal to ${param.min}`)
|
|
||||||
.max(10, 'Maximum is 10%')
|
|
||||||
.required('Required')
|
|
||||||
.nullable()
|
|
||||||
})
|
|
||||||
|
|
||||||
export const initialValues: PriceOptions = {
|
|
||||||
price: 1,
|
|
||||||
type:
|
|
||||||
allowDynamicPricing === 'true'
|
|
||||||
? 'dynamic'
|
|
||||||
: allowFixedPricing === 'true'
|
|
||||||
? 'fixed'
|
|
||||||
: 'free',
|
|
||||||
dtAmount: allowDynamicPricing === 'true' ? 9 : 1000,
|
|
||||||
oceanAmount: 21,
|
|
||||||
weightOnOcean: '7', // 70% on OCEAN
|
|
||||||
weightOnDataToken: '3', // 30% on datatoken
|
|
||||||
swapFee: 0.1 // in %
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
.pricing {
|
|
||||||
composes: box from '@shared/atoms/Box.module.css';
|
|
||||||
padding: 0;
|
|
||||||
padding-bottom: var(--spacer);
|
|
||||||
margin-top: var(--spacer);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pricing [class*='alert'] {
|
|
||||||
margin: var(--spacer);
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
16
src/components/Publish/Title.module.css
Normal file
16
src/components/Publish/Title.module.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.network {
|
||||||
|
color: var(--font-color-heading);
|
||||||
|
margin-left: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.network svg {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
margin-top: -0.25em;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
width: 0.5em;
|
||||||
|
height: 0.5em;
|
||||||
|
}
|
43
src/components/Publish/Title.tsx
Normal file
43
src/components/Publish/Title.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import NetworkName from '@shared/NetworkName'
|
||||||
|
import Tooltip from '@shared/atoms/Tooltip'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import styles from './Title.module.css'
|
||||||
|
|
||||||
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
|
|
||||||
|
const query = graphql`
|
||||||
|
query {
|
||||||
|
content: allFile(filter: { relativePath: { eq: "publish/index.json" } }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
childPublishJson {
|
||||||
|
title
|
||||||
|
tooltipNetwork
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function Title(): ReactElement {
|
||||||
|
const data = useStaticQuery(query)
|
||||||
|
const content = data.content.edges[0].node.childPublishJson
|
||||||
|
const { networkId } = useWeb3()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{content.title}{' '}
|
||||||
|
{networkId && (
|
||||||
|
<>
|
||||||
|
into <NetworkName networkId={networkId} className={styles.network} />
|
||||||
|
<Tooltip
|
||||||
|
content={content.tooltipNetwork}
|
||||||
|
className={styles.tooltip}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -1,101 +1,149 @@
|
|||||||
import { File as FileMetadata } from '@oceanprotocol/lib'
|
import { File as FileMetadata } from '@oceanprotocol/lib'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
import { allowDynamicPricing, allowFixedPricing } from '../../../app.config'
|
||||||
import { FormPublishData } from './_types'
|
import { FormPublishData } from './_types'
|
||||||
|
|
||||||
export const validationSchema: Yup.SchemaOf<FormPublishData> = Yup.object()
|
export const initialValues: Partial<FormPublishData> = {
|
||||||
.shape({
|
metadata: {
|
||||||
// ---- required fields ----
|
name: '',
|
||||||
|
author: '',
|
||||||
|
description: '',
|
||||||
|
termsAndConditions: false,
|
||||||
|
tags: ''
|
||||||
|
},
|
||||||
|
services: [
|
||||||
|
{
|
||||||
|
files: '',
|
||||||
|
links: '',
|
||||||
|
dataTokenOptions: { name: '', symbol: '' },
|
||||||
|
timeout: 'Forever',
|
||||||
|
access: '',
|
||||||
|
providerUri: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pricing: {
|
||||||
|
price: 1,
|
||||||
|
type:
|
||||||
|
allowDynamicPricing === 'true'
|
||||||
|
? 'dynamic'
|
||||||
|
: allowFixedPricing === 'true'
|
||||||
|
? 'fixed'
|
||||||
|
: 'free',
|
||||||
|
dtAmount: allowDynamicPricing === 'true' ? 9 : 1000,
|
||||||
|
oceanAmount: 21,
|
||||||
|
weightOnOcean: '7', // 70% on OCEAN
|
||||||
|
weightOnDataToken: '3', // 30% on datatoken
|
||||||
|
swapFee: 0.1 // in %
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validationMetadata = {
|
||||||
name: Yup.string()
|
name: Yup.string()
|
||||||
.min(4, (param) => `Title must be at least ${param.min} characters`)
|
.min(4, (param) => `Title must be at least ${param.min} characters`)
|
||||||
.required('Required'),
|
.required('Required'),
|
||||||
|
description: Yup.string().min(10).required('Required'),
|
||||||
author: Yup.string().required('Required'),
|
author: Yup.string().required('Required'),
|
||||||
|
tags: Yup.string().nullable(),
|
||||||
|
termsAndConditions: Yup.boolean().required('Required')
|
||||||
|
}
|
||||||
|
|
||||||
|
const validationService = {
|
||||||
|
files: Yup.array<FileMetadata>()
|
||||||
|
.required('Enter a valid URL and click "ADD FILE"')
|
||||||
|
.nullable(),
|
||||||
|
links: Yup.array<FileMetadata[]>().nullable(),
|
||||||
dataTokenOptions: Yup.object()
|
dataTokenOptions: Yup.object()
|
||||||
.shape({
|
.shape({
|
||||||
name: Yup.string(),
|
name: Yup.string(),
|
||||||
symbol: Yup.string()
|
symbol: Yup.string()
|
||||||
})
|
})
|
||||||
.required('Required'),
|
.required('Required'),
|
||||||
files: Yup.array<FileMetadata>()
|
|
||||||
.required('Enter a valid URL and click "ADD FILE"')
|
|
||||||
.nullable(),
|
|
||||||
description: Yup.string().min(10).required('Required'),
|
|
||||||
timeout: Yup.string().required('Required'),
|
timeout: Yup.string().required('Required'),
|
||||||
access: Yup.string()
|
access: Yup.string()
|
||||||
.matches(/Compute|Download/g, { excludeEmptyString: true })
|
.matches(/Compute|Download/g, { excludeEmptyString: true })
|
||||||
.required('Required'),
|
.required('Required'),
|
||||||
termsAndConditions: Yup.boolean().required('Required'),
|
|
||||||
// ---- optional fields ----
|
|
||||||
tags: Yup.string().nullable(),
|
|
||||||
links: Yup.array<FileMetadata[]>().nullable(),
|
|
||||||
providerUri: Yup.string().url().nullable()
|
providerUri: Yup.string().url().nullable()
|
||||||
|
}
|
||||||
|
|
||||||
|
const validationPricing = {
|
||||||
|
price: Yup.number()
|
||||||
|
.min(1, (param) => `Must be more or equal to ${param.min}`)
|
||||||
|
.required('Required'),
|
||||||
|
dtAmount: Yup.number()
|
||||||
|
.min(9, (param) => `Must be more or equal to ${param.min}`)
|
||||||
|
.required('Required'),
|
||||||
|
oceanAmount: Yup.number()
|
||||||
|
.min(21, (param) => `Must be more or equal to ${param.min}`)
|
||||||
|
.required('Required'),
|
||||||
|
type: Yup.string()
|
||||||
|
.matches(/fixed|dynamic|free/g, { excludeEmptyString: true })
|
||||||
|
.required('Required'),
|
||||||
|
weightOnDataToken: Yup.string().required('Required'),
|
||||||
|
weightOnOcean: Yup.string().required('Required'),
|
||||||
|
swapFee: Yup.number()
|
||||||
|
.min(0.1, (param) => `Must be more or equal to ${param.min}`)
|
||||||
|
.max(10, 'Maximum is 10%')
|
||||||
|
.required('Required')
|
||||||
|
.nullable()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validationSchema: Yup.SchemaOf<FormPublishData> = Yup.object()
|
||||||
|
.shape({
|
||||||
|
metadata: Yup.object().shape(validationMetadata),
|
||||||
|
services: Yup.array().of(Yup.object().shape(validationService)),
|
||||||
|
pricing: Yup.object().shape(validationPricing)
|
||||||
})
|
})
|
||||||
.defined()
|
.defined()
|
||||||
|
|
||||||
export const initialValues: Partial<FormPublishData> = {
|
// export const validationSchemaAlgo: Yup.SchemaOf<MetadataPublishFormAlgorithm> =
|
||||||
name: '',
|
// Yup.object()
|
||||||
author: '',
|
// .shape({
|
||||||
dataTokenOptions: {
|
// // ---- required fields ----
|
||||||
name: '',
|
// name: Yup.string()
|
||||||
symbol: ''
|
// .min(4, (param) => `Title must be at least ${param.min} characters`)
|
||||||
},
|
// .required('Required'),
|
||||||
files: '',
|
// description: Yup.string().min(10).required('Required'),
|
||||||
description: '',
|
// files: Yup.array<FileMetadata>().required('Required').nullable(),
|
||||||
timeout: 'Forever',
|
// timeout: Yup.string().required('Required'),
|
||||||
access: '',
|
// dataTokenOptions: Yup.object()
|
||||||
termsAndConditions: false,
|
// .shape({
|
||||||
tags: '',
|
// name: Yup.string(),
|
||||||
providerUri: ''
|
// symbol: Yup.string()
|
||||||
}
|
// })
|
||||||
|
// .required('Required'),
|
||||||
|
// dockerImage: Yup.string()
|
||||||
|
// .matches(/node:latest|python:latest|custom image/g, {
|
||||||
|
// excludeEmptyString: true
|
||||||
|
// })
|
||||||
|
// .required('Required'),
|
||||||
|
// image: Yup.string().required('Required'),
|
||||||
|
// containerTag: Yup.string().required('Required'),
|
||||||
|
// entrypoint: Yup.string().required('Required'),
|
||||||
|
// author: Yup.string().required('Required'),
|
||||||
|
// termsAndConditions: Yup.boolean().required('Required'),
|
||||||
|
// // ---- optional fields ----
|
||||||
|
// algorithmPrivacy: Yup.boolean().nullable(),
|
||||||
|
// tags: Yup.string().nullable(),
|
||||||
|
// links: Yup.array<FileMetadata[]>().nullable()
|
||||||
|
// })
|
||||||
|
// .defined()
|
||||||
|
|
||||||
export const validationSchemaAlgo: Yup.SchemaOf<MetadataPublishFormAlgorithm> =
|
// export const initialValuesAlgo: Partial<MetadataPublishFormAlgorithm> = {
|
||||||
Yup.object()
|
// name: '',
|
||||||
.shape({
|
// author: '',
|
||||||
// ---- required fields ----
|
// dataTokenOptions: {
|
||||||
name: Yup.string()
|
// name: '',
|
||||||
.min(4, (param) => `Title must be at least ${param.min} characters`)
|
// symbol: ''
|
||||||
.required('Required'),
|
// },
|
||||||
description: Yup.string().min(10).required('Required'),
|
// dockerImage: 'node:latest',
|
||||||
files: Yup.array<FileMetadata>().required('Required').nullable(),
|
// image: 'node',
|
||||||
timeout: Yup.string().required('Required'),
|
// containerTag: 'latest',
|
||||||
dataTokenOptions: Yup.object()
|
// entrypoint: 'node $ALGO',
|
||||||
.shape({
|
// files: '',
|
||||||
name: Yup.string(),
|
// description: '',
|
||||||
symbol: Yup.string()
|
// algorithmPrivacy: false,
|
||||||
})
|
// termsAndConditions: false,
|
||||||
.required('Required'),
|
// tags: '',
|
||||||
dockerImage: Yup.string()
|
// timeout: 'Forever',
|
||||||
.matches(/node:latest|python:latest|custom image/g, {
|
// providerUri: ''
|
||||||
excludeEmptyString: true
|
// }
|
||||||
})
|
|
||||||
.required('Required'),
|
|
||||||
image: Yup.string().required('Required'),
|
|
||||||
containerTag: Yup.string().required('Required'),
|
|
||||||
entrypoint: Yup.string().required('Required'),
|
|
||||||
author: Yup.string().required('Required'),
|
|
||||||
termsAndConditions: Yup.boolean().required('Required'),
|
|
||||||
// ---- optional fields ----
|
|
||||||
algorithmPrivacy: Yup.boolean().nullable(),
|
|
||||||
tags: Yup.string().nullable(),
|
|
||||||
links: Yup.array<FileMetadata[]>().nullable()
|
|
||||||
})
|
|
||||||
.defined()
|
|
||||||
|
|
||||||
export const initialValuesAlgo: Partial<MetadataPublishFormAlgorithm> = {
|
|
||||||
name: '',
|
|
||||||
author: '',
|
|
||||||
dataTokenOptions: {
|
|
||||||
name: '',
|
|
||||||
symbol: ''
|
|
||||||
},
|
|
||||||
dockerImage: 'node:latest',
|
|
||||||
image: 'node',
|
|
||||||
containerTag: 'latest',
|
|
||||||
entrypoint: 'node $ALGO',
|
|
||||||
files: '',
|
|
||||||
description: '',
|
|
||||||
algorithmPrivacy: false,
|
|
||||||
termsAndConditions: false,
|
|
||||||
tags: '',
|
|
||||||
timeout: 'Forever',
|
|
||||||
providerUri: ''
|
|
||||||
}
|
|
||||||
|
@ -2,9 +2,12 @@ import { DataTokenOptions } from '@hooks/usePublish'
|
|||||||
import { EditableMetadataLinks } from '@oceanprotocol/lib'
|
import { EditableMetadataLinks } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
export interface FormPublishService {
|
export interface FormPublishService {
|
||||||
|
files: string | File[]
|
||||||
|
links?: string | EditableMetadataLinks[]
|
||||||
timeout: string
|
timeout: string
|
||||||
dataTokenOptions: DataTokenOptions
|
dataTokenOptions: DataTokenOptions
|
||||||
access: 'Download' | 'Compute' | string
|
access: 'Download' | 'Compute' | string
|
||||||
|
providerUri?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormPublishData {
|
export interface FormPublishData {
|
||||||
@ -12,12 +15,9 @@ export interface FormPublishData {
|
|||||||
metadata: {
|
metadata: {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
files: string | File[]
|
|
||||||
author: string
|
author: string
|
||||||
termsAndConditions: boolean
|
termsAndConditions: boolean
|
||||||
tags?: string
|
tags?: string
|
||||||
links?: string | EditableMetadataLinks[]
|
|
||||||
providerUri?: string
|
|
||||||
}
|
}
|
||||||
services: FormPublishService[]
|
services: FormPublishService[]
|
||||||
pricing: PriceOptions
|
pricing: PriceOptions
|
||||||
|
@ -2,7 +2,6 @@ import React, { ReactElement, useState, useEffect } from 'react'
|
|||||||
import Permission from '@shared/Permission'
|
import Permission from '@shared/Permission'
|
||||||
import { Formik, FormikState } from 'formik'
|
import { Formik, FormikState } from 'formik'
|
||||||
import { usePublish } from '@hooks/usePublish'
|
import { usePublish } from '@hooks/usePublish'
|
||||||
import styles from './index.module.css'
|
|
||||||
import { initialValues, validationSchema } from './_constants'
|
import { initialValues, validationSchema } from './_constants'
|
||||||
// import {
|
// import {
|
||||||
// transformPublishFormToMetadata,
|
// transformPublishFormToMetadata,
|
||||||
@ -13,13 +12,17 @@ import { Logger, Metadata } from '@oceanprotocol/lib'
|
|||||||
import { useAccountPurgatory } from '@hooks/useAccountPurgatory'
|
import { useAccountPurgatory } from '@hooks/useAccountPurgatory'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
import { FormPublishData } from './_types'
|
import { FormPublishData } from './_types'
|
||||||
|
import PageHeader from '@shared/Page/PageHeader'
|
||||||
|
import Title from './Title'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import FormPublish from './FormPublish'
|
||||||
|
|
||||||
const formName = 'ocean-publish-form'
|
const formName = 'ocean-publish-form'
|
||||||
|
|
||||||
export default function PublishPage({
|
export default function PublishPage({
|
||||||
content
|
content
|
||||||
}: {
|
}: {
|
||||||
content: { warning: string }
|
content: { title: string; description: string; warning: string }
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId)
|
const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId)
|
||||||
@ -78,8 +81,7 @@ export default function PublishPage({
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return isInPurgatory && purgatoryData ? null : (
|
{isInPurgatory && purgatoryData ? null : (
|
||||||
<Permission eventType="publish">
|
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
initialStatus="empty"
|
initialStatus="empty"
|
||||||
@ -89,10 +91,9 @@ export default function PublishPage({
|
|||||||
// await handleSubmit(values, resetForm)
|
// await handleSubmit(values, resetForm)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ values }) => {
|
<FormPublish />
|
||||||
return <>Hello</>
|
|
||||||
}}
|
|
||||||
</Formik>
|
</Formik>
|
||||||
</Permission>
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user