From 99453623d250f4d732db8d6eb4f5f48c1c8740b3 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 21 Oct 2021 19:58:55 +0100 Subject: [PATCH] new publish form data setup --- .../{pages/publish => }/form-algorithm.json | 0 content/pages/publish/form-dataset.json | 89 ------- content/publish/form.json | 98 ++++++++ content/{pages => }/publish/index.json | 2 +- gatsby/createTypes.js | 8 +- src/@types/Form.d.ts | 5 +- .../Form/FormFields/AdvancedSettings.tsx | 2 +- .../@shared/Form/FormFields/Terms.tsx | 2 +- src/components/@shared/Form/Input/index.tsx | 6 +- .../@shared/Page/PageHeader.module.css | 1 + src/components/@shared/Page/PageHeader.tsx | 2 +- src/components/Asset/AssetContent/index.tsx | 4 - src/components/Publish/Debug.tsx | 34 +-- src/components/Publish/FormPublish.module.css | 7 - src/components/Publish/FormPublish.tsx | 125 ---------- .../{ => FormPublish}/FormActions.module.css | 0 .../Publish/{ => FormPublish}/FormActions.tsx | 18 +- .../Pricing}/Coin.module.css | 0 .../Pricing}/Coin.tsx | 3 +- .../Pricing}/Dynamic.module.css | 4 - .../Pricing}/Dynamic.tsx | 28 +-- .../Pricing}/Error.tsx | 0 .../Pricing}/Fees.module.css | 0 .../Pricing}/Fees.tsx | 0 .../Publish/FormPublish/Pricing/Fixed.tsx | 14 ++ .../Publish/FormPublish/Pricing/Free.tsx | 12 + .../Pricing}/Price.module.css | 0 .../Pricing}/Price.tsx | 29 +-- .../Publish/FormPublish/Pricing/index.tsx | 152 ++++++++++++ .../Publish/FormPublish/index.module.css | 4 + src/components/Publish/FormPublish/index.tsx | 224 +++++++++++++++++ src/components/Publish/FormTitle.module.css | 21 -- .../Publish/Pricing/Feedback.module.css | 8 - src/components/Publish/Pricing/Feedback.tsx | 34 --- .../Pricing/FormPricing/Fixed.module.css | 3 - .../Publish/Pricing/FormPricing/Fixed.tsx | 23 -- .../Pricing/FormPricing/Free.module.css | 3 - .../Publish/Pricing/FormPricing/Free.tsx | 21 -- .../Pricing/FormPricing/index.module.css | 52 ---- .../Publish/Pricing/FormPricing/index.tsx | 109 --------- src/components/Publish/Pricing/_constants.ts | 39 --- src/components/Publish/Pricing/_types.d.ts | 0 .../Publish/Pricing/index.module.css | 11 - src/components/Publish/Title.module.css | 16 ++ src/components/Publish/Title.tsx | 43 ++++ src/components/Publish/_constants.ts | 230 +++++++++++------- src/components/Publish/_types.ts | 6 +- src/components/Publish/index.tsx | 37 +-- src/pages/publish.tsx | 2 +- 49 files changed, 788 insertions(+), 743 deletions(-) rename content/{pages/publish => }/form-algorithm.json (100%) delete mode 100644 content/pages/publish/form-dataset.json create mode 100644 content/publish/form.json rename content/{pages => }/publish/index.json (50%) delete mode 100644 src/components/Publish/FormPublish.module.css delete mode 100644 src/components/Publish/FormPublish.tsx rename src/components/Publish/{ => FormPublish}/FormActions.module.css (100%) rename src/components/Publish/{ => FormPublish}/FormActions.tsx (81%) rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Coin.module.css (100%) rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Coin.tsx (91%) rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Dynamic.module.css (95%) rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Dynamic.tsx (85%) rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Error.tsx (100%) rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Fees.module.css (100%) rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Fees.tsx (100%) create mode 100644 src/components/Publish/FormPublish/Pricing/Fixed.tsx create mode 100644 src/components/Publish/FormPublish/Pricing/Free.tsx rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Price.module.css (100%) rename src/components/Publish/{Pricing/FormPricing => FormPublish/Pricing}/Price.tsx (65%) create mode 100644 src/components/Publish/FormPublish/Pricing/index.tsx create mode 100644 src/components/Publish/FormPublish/index.module.css create mode 100644 src/components/Publish/FormPublish/index.tsx delete mode 100644 src/components/Publish/FormTitle.module.css delete mode 100644 src/components/Publish/Pricing/Feedback.module.css delete mode 100644 src/components/Publish/Pricing/Feedback.tsx delete mode 100644 src/components/Publish/Pricing/FormPricing/Fixed.module.css delete mode 100644 src/components/Publish/Pricing/FormPricing/Fixed.tsx delete mode 100644 src/components/Publish/Pricing/FormPricing/Free.module.css delete mode 100644 src/components/Publish/Pricing/FormPricing/Free.tsx delete mode 100644 src/components/Publish/Pricing/FormPricing/index.module.css delete mode 100644 src/components/Publish/Pricing/FormPricing/index.tsx delete mode 100644 src/components/Publish/Pricing/_constants.ts delete mode 100644 src/components/Publish/Pricing/_types.d.ts delete mode 100644 src/components/Publish/Pricing/index.module.css create mode 100644 src/components/Publish/Title.module.css create mode 100644 src/components/Publish/Title.tsx diff --git a/content/pages/publish/form-algorithm.json b/content/form-algorithm.json similarity index 100% rename from content/pages/publish/form-algorithm.json rename to content/form-algorithm.json diff --git a/content/pages/publish/form-dataset.json b/content/pages/publish/form-dataset.json deleted file mode 100644 index 527c0598a..000000000 --- a/content/pages/publish/form-dataset.json +++ /dev/null @@ -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!" -} diff --git a/content/publish/form.json b/content/publish/form.json new file mode 100644 index 000000000..267cb7882 --- /dev/null +++ b/content/publish/form.json @@ -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" }] + } +} diff --git a/content/pages/publish/index.json b/content/publish/index.json similarity index 50% rename from content/pages/publish/index.json rename to content/publish/index.json index 666ff4815..d35432fca 100644 --- a/content/pages/publish/index.json +++ b/content/publish/index.json @@ -1,6 +1,6 @@ { "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.", - "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." } diff --git a/gatsby/createTypes.js b/gatsby/createTypes.js index 9c5fd2137..24495b398 100644 --- a/gatsby/createTypes.js +++ b/gatsby/createTypes.js @@ -16,9 +16,15 @@ function createTypes(actions) { desc: String! cookieName: String! } - type PublishJsonData implements Node { + type PublishJson implements Node { + metadata: Extensions + services: Extensions + pricing: Extensions + } + type Extensions { disclaimer: String disclaimerValues: [String!] + advanced: Boolean } ` createTypes(typeDefs) diff --git a/src/@types/Form.d.ts b/src/@types/Form.d.ts index 8ab26c77c..30c10f8fc 100644 --- a/src/@types/Form.d.ts +++ b/src/@types/Form.d.ts @@ -21,10 +21,9 @@ declare global { advanced?: boolean } - interface FormContent { + interface FormStepContent { title: string description?: string - success: string - data: FormFieldProps[] + fields: FormFieldProps[] } } diff --git a/src/components/@shared/Form/FormFields/AdvancedSettings.tsx b/src/components/@shared/Form/FormFields/AdvancedSettings.tsx index 5b1ee1fc2..94e10f0e1 100644 --- a/src/components/@shared/Form/FormFields/AdvancedSettings.tsx +++ b/src/components/@shared/Form/FormFields/AdvancedSettings.tsx @@ -5,7 +5,7 @@ import { Field } from 'formik' import styles from './AdvancedSettings.module.css' export default function AdvancedSettings(prop: { - content: FormContent + content: FormStepContent handleFieldChange: ( e: ChangeEvent, field: FormFieldProps diff --git a/src/components/@shared/Form/FormFields/Terms.tsx b/src/components/@shared/Form/FormFields/Terms.tsx index 1af88f316..51cc262cf 100644 --- a/src/components/@shared/Form/FormFields/Terms.tsx +++ b/src/components/@shared/Form/FormFields/Terms.tsx @@ -16,7 +16,7 @@ export default function Terms(props: InputProps): ReactElement { const data = useStaticQuery(query) const termsProps: InputProps = { ...props, - defaultChecked: props.value.toString() === 'true' + defaultChecked: props?.value?.toString() === 'true' } return ( diff --git a/src/components/@shared/Form/Input/index.tsx b/src/components/@shared/Form/Input/index.tsx index c38af38b9..573e80b17 100644 --- a/src/components/@shared/Form/Input/index.tsx +++ b/src/components/@shared/Form/Input/index.tsx @@ -13,6 +13,8 @@ import styles from './index.module.css' import { ErrorMessage, FieldInputProps } from 'formik' import classNames from 'classnames/bind' import Disclaimer from './Disclaimer' +import Tooltip from '@shared/atoms/Tooltip' +import Markdown from '@shared/atoms/Markdown' const cx = classNames.bind(styles) @@ -97,7 +99,7 @@ export default function Input(props: Partial): ReactElement { data-is-submitting={props.form?.isSubmitting ? true : null} > @@ -107,7 +109,7 @@ export default function Input(props: Partial): ReactElement { )} - {help && {help}} + {/* {help && {help}} */} {disclaimer && ( {disclaimer} diff --git a/src/components/@shared/Page/PageHeader.module.css b/src/components/@shared/Page/PageHeader.module.css index 97216e2dd..54fdb1e53 100644 --- a/src/components/@shared/Page/PageHeader.module.css +++ b/src/components/@shared/Page/PageHeader.module.css @@ -7,6 +7,7 @@ font-size: var(--font-size-h3); margin-top: 0; margin-bottom: 0; + display: inline-flex; } @media (min-width: 40rem) { diff --git a/src/components/@shared/Page/PageHeader.tsx b/src/components/@shared/Page/PageHeader.tsx index 892ee846d..b9fe44d00 100644 --- a/src/components/@shared/Page/PageHeader.tsx +++ b/src/components/@shared/Page/PageHeader.tsx @@ -10,7 +10,7 @@ export default function PageHeader({ description, center }: { - title: string + title: ReactElement description?: string center?: boolean }): ReactElement { diff --git a/src/components/Asset/AssetContent/index.tsx b/src/components/Asset/AssetContent/index.tsx index 3ac361f7c..88f467b46 100644 --- a/src/components/Asset/AssetContent/index.tsx +++ b/src/components/Asset/AssetContent/index.tsx @@ -4,7 +4,6 @@ import MetaFull from './MetaFull' import MetaSecondary from './MetaSecondary' import AssetActions from '../AssetActions' import { useUserPreferences } from '@context/UserPreferences' -import Pricing from '../../Publish/Pricing' import Bookmark from './Bookmark' import { useAsset } from '@context/Asset' import Alert from '@shared/atoms/Alert' @@ -31,7 +30,6 @@ export default function AssetContent(): ReactElement { const { debug } = useUserPreferences() const { accountId } = useWeb3() const { owner, isInPurgatory, purgatoryData, isAssetNetwork } = useAsset() - const [showPricing, setShowPricing] = useState(false) const [showEdit, setShowEdit] = useState() const [isComputeType, setIsComputeType] = useState(false) const [showEditCompute, setShowEditCompute] = useState() @@ -43,7 +41,6 @@ export default function AssetContent(): ReactElement { const isOwner = accountId.toLowerCase() === owner.toLowerCase() setIsOwner(isOwner) - setShowPricing(isOwner && price.type === '') setIsComputeType(Boolean(ddo.findServiceByType('compute'))) }, [accountId, price, owner, ddo]) @@ -70,7 +67,6 @@ export default function AssetContent(): ReactElement {
- {showPricing && }
diff --git a/src/components/Publish/Debug.tsx b/src/components/Publish/Debug.tsx index a5bc8ef95..0b45c1c82 100644 --- a/src/components/Publish/Debug.tsx +++ b/src/components/Publish/Debug.tsx @@ -10,23 +10,23 @@ export default function Debug({ values: Partial }): ReactElement { const ddo = { - '@context': 'https://w3id.org/did/v1', - dataTokenInfo: { - ...values.dataTokenOptions - }, - service: [ - { - index: 0, - type: 'metadata', - attributes: { ...transformPublishFormToMetadata(values) } - }, - { - index: 1, - type: values.access, - serviceEndpoint: values.providerUri, - attributes: {} - } - ] + '@context': 'https://w3id.org/did/v1' + // dataTokenInfo: { + // ...values.dataTokenOptions + // }, + // service: [ + // { + // index: 0, + // type: 'metadata', + // attributes: { ...transformPublishFormToMetadata(values) } + // }, + // { + // index: 1, + // type: values.access, + // serviceEndpoint: values.providerUri, + // attributes: {} + // } + // ] } return ( diff --git a/src/components/Publish/FormPublish.module.css b/src/components/Publish/FormPublish.module.css deleted file mode 100644 index 59f56e9e8..000000000 --- a/src/components/Publish/FormPublish.module.css +++ /dev/null @@ -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; -} diff --git a/src/components/Publish/FormPublish.tsx b/src/components/Publish/FormPublish.tsx deleted file mode 100644 index a7eddee86..000000000 --- a/src/components/Publish/FormPublish.tsx +++ /dev/null @@ -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 = useFormikContext() - - const [computeTypeSelected, setComputeTypeSelected] = useState(false) - - // reset form validation on every mount - useEffect(() => { - setErrors({}) - setTouched({}) - - // setSubmitting(false) - }, [setErrors, setTouched]) - - const accessTypeOptions = [ - { - name: 'Download', - title: 'Download', - icon: - }, - { - name: 'Compute', - title: 'Compute', - icon: - } - ] - - 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, - // 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) => { - e.preventDefault() - resetForm({ - values: initialValues as FormPublishData, - status: 'empty' - }) - setStatus('empty') - } - - return ( -
status === 'empty' && setStatus(null)} - > - - - {content.data.map( - (field: FormFieldProps) => - field.advanced !== true && ( - ) => - // handleFieldChange(e, field) - // } - /> - ) - )} - - - - ) -} diff --git a/src/components/Publish/FormActions.module.css b/src/components/Publish/FormPublish/FormActions.module.css similarity index 100% rename from src/components/Publish/FormActions.module.css rename to src/components/Publish/FormPublish/FormActions.module.css diff --git a/src/components/Publish/FormActions.tsx b/src/components/Publish/FormPublish/FormActions.tsx similarity index 81% rename from src/components/Publish/FormActions.tsx rename to src/components/Publish/FormPublish/FormActions.tsx index c39c80840..b24dc901e 100644 --- a/src/components/Publish/FormActions.tsx +++ b/src/components/Publish/FormPublish/FormActions.tsx @@ -2,6 +2,8 @@ import React, { FormEvent, ReactElement } from 'react' import { useOcean } from '@context/Ocean' import Button from '@shared/atoms/Button' import styles from './FormActions.module.css' +import { FormikContextType, useFormikContext } from 'formik' +import { FormPublishData } from '../_types' export default function FormActions({ isValid, @@ -11,22 +13,22 @@ export default function FormActions({ resetFormAndClearStorage: (e: FormEvent) => void }): ReactElement { const { ocean, account } = useOcean() + const { status }: FormikContextType = useFormikContext() return (
- - {status !== 'empty' && ( )} + +
) } diff --git a/src/components/Publish/Pricing/FormPricing/Coin.module.css b/src/components/Publish/FormPublish/Pricing/Coin.module.css similarity index 100% rename from src/components/Publish/Pricing/FormPricing/Coin.module.css rename to src/components/Publish/FormPublish/Pricing/Coin.module.css diff --git a/src/components/Publish/Pricing/FormPricing/Coin.tsx b/src/components/Publish/FormPublish/Pricing/Coin.tsx similarity index 91% rename from src/components/Publish/Pricing/FormPricing/Coin.tsx rename to src/components/Publish/FormPublish/Pricing/Coin.tsx index d3431948c..6a23fa135 100644 --- a/src/components/Publish/Pricing/FormPricing/Coin.tsx +++ b/src/components/Publish/FormPublish/Pricing/Coin.tsx @@ -1,5 +1,4 @@ import React, { ReactElement } from 'react' -import stylesIndex from './index.module.css' import styles from './Coin.module.css' import InputElement from '@shared/Form/Input/InputElement' import Logo from '@images/logo.svg' @@ -46,7 +45,7 @@ export default function Coin({ {...field} /> {datatokenOptions?.symbol === 'OCEAN' && ( - + )}
diff --git a/src/components/Publish/Pricing/FormPricing/Dynamic.module.css b/src/components/Publish/FormPublish/Pricing/Dynamic.module.css similarity index 95% rename from src/components/Publish/Pricing/FormPricing/Dynamic.module.css rename to src/components/Publish/FormPublish/Pricing/Dynamic.module.css index df54005ed..2b914baa7 100644 --- a/src/components/Publish/Pricing/FormPricing/Dynamic.module.css +++ b/src/components/Publish/FormPublish/Pricing/Dynamic.module.css @@ -1,7 +1,3 @@ -.dynamic { - composes: content from './index.module.css'; -} - .wallet { display: flex; align-items: center; diff --git a/src/components/Publish/Pricing/FormPricing/Dynamic.tsx b/src/components/Publish/FormPublish/Pricing/Dynamic.tsx similarity index 85% rename from src/components/Publish/Pricing/FormPricing/Dynamic.tsx rename to src/components/Publish/FormPublish/Pricing/Dynamic.tsx index af284eac2..f2fd3a650 100644 --- a/src/components/Publish/Pricing/FormPricing/Dynamic.tsx +++ b/src/components/Publish/FormPublish/Pricing/Dynamic.tsx @@ -7,27 +7,21 @@ import Wallet from '../../../Header/Wallet' import Coin from './Coin' import styles from './Dynamic.module.css' import Fees from './Fees' -import stylesIndex from './index.module.css' import { FormikContextType, useFormikContext } from 'formik' -import { DDO } from '@oceanprotocol/lib' import Price from './Price' import Decimal from 'decimal.js' import { useOcean } from '@context/Ocean' import { useWeb3 } from '@context/Web3' +import { FormPublishData } from '../../_types' -export default function Dynamic({ - ddo, - content -}: { - ddo: DDO - content: any -}): ReactElement { +export default function Dynamic({ content }: { content: any }): ReactElement { const { networkId, balance } = useWeb3() const { account } = useOcean() const [firstPrice, setFirstPrice] = useState() // Connect with form - const { values }: FormikContextType = useFormikContext() + const { values }: FormikContextType = useFormikContext() + const { dataTokenOptions } = values.services[0] const { price, @@ -36,7 +30,7 @@ export default function Dynamic({ swapFee, dtAmount, oceanAmount - } = values + } = values.pricing const [error, setError] = useState() @@ -69,8 +63,8 @@ export default function Dynamic({ }, [price, networkId, account, balance]) return ( -
- {content.info} + <> + {content.info}
)} -
+ ) } diff --git a/src/components/Publish/Pricing/FormPricing/Error.tsx b/src/components/Publish/FormPublish/Pricing/Error.tsx similarity index 100% rename from src/components/Publish/Pricing/FormPricing/Error.tsx rename to src/components/Publish/FormPublish/Pricing/Error.tsx diff --git a/src/components/Publish/Pricing/FormPricing/Fees.module.css b/src/components/Publish/FormPublish/Pricing/Fees.module.css similarity index 100% rename from src/components/Publish/Pricing/FormPricing/Fees.module.css rename to src/components/Publish/FormPublish/Pricing/Fees.module.css diff --git a/src/components/Publish/Pricing/FormPricing/Fees.tsx b/src/components/Publish/FormPublish/Pricing/Fees.tsx similarity index 100% rename from src/components/Publish/Pricing/FormPricing/Fees.tsx rename to src/components/Publish/FormPublish/Pricing/Fees.tsx diff --git a/src/components/Publish/FormPublish/Pricing/Fixed.tsx b/src/components/Publish/FormPublish/Pricing/Fixed.tsx new file mode 100644 index 000000000..20698ea29 --- /dev/null +++ b/src/components/Publish/FormPublish/Pricing/Fixed.tsx @@ -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 ( + <> + {content.info} + + + + ) +} diff --git a/src/components/Publish/FormPublish/Pricing/Free.tsx b/src/components/Publish/FormPublish/Pricing/Free.tsx new file mode 100644 index 000000000..75ebbd116 --- /dev/null +++ b/src/components/Publish/FormPublish/Pricing/Free.tsx @@ -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 ( + <> + {content.info} + + + ) +} diff --git a/src/components/Publish/Pricing/FormPricing/Price.module.css b/src/components/Publish/FormPublish/Pricing/Price.module.css similarity index 100% rename from src/components/Publish/Pricing/FormPricing/Price.module.css rename to src/components/Publish/FormPublish/Pricing/Price.module.css diff --git a/src/components/Publish/Pricing/FormPricing/Price.tsx b/src/components/Publish/FormPublish/Pricing/Price.tsx similarity index 65% rename from src/components/Publish/Pricing/FormPricing/Price.tsx rename to src/components/Publish/FormPublish/Pricing/Price.tsx index 2318a0550..261ae739c 100644 --- a/src/components/Publish/Pricing/FormPricing/Price.tsx +++ b/src/components/Publish/FormPublish/Pricing/Price.tsx @@ -1,40 +1,23 @@ import Conversion from '@shared/Price/Conversion' -import { useField } from 'formik' -import React, { ReactElement, useState, useEffect } from 'react' +import { useField, useFormikContext } from 'formik' +import React, { ReactElement } from 'react' import Input from '@shared/Form/Input' import Error from './Error' -import { DDO } from '@oceanprotocol/lib' import PriceUnit from '@shared/Price/PriceUnit' -import usePricing from '@hooks/usePricing' import styles from './Price.module.css' +import { FormPublishData } from '../../_types' export default function Price({ - ddo, firstPrice, free }: { - ddo: DDO firstPrice?: string free?: boolean }): ReactElement { const [field, meta] = useField('price') - const { getDTName, getDTSymbol } = usePricing() - const [dtSymbol, setDtSymbol] = useState() - const [dtName, setDtName] = useState() - useEffect(() => { - if (!ddo) return - 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) - }, []) + const { values } = useFormikContext() + const { dataTokenOptions } = values.services[0] return (
@@ -62,7 +45,7 @@ export default function Price({

- = 1 {dtSymbol}{' '} + = 1 {dataTokenOptions.symbol}{' '}

diff --git a/src/components/Publish/FormPublish/Pricing/index.tsx b/src/components/Publish/FormPublish/Pricing/index.tsx new file mode 100644 index 000000000..debca6ccc --- /dev/null +++ b/src/components/Publish/FormPublish/Pricing/index.tsx @@ -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() + 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: + } + : undefined, + appConfig.allowDynamicPricing === 'true' + ? { + title: content.dynamic.title, + content: + } + : undefined, + appConfig.allowFreePricing === 'true' + ? { + title: content.free.title, + content: + } + : undefined + ].filter((tab) => tab !== undefined) + + return ( + + ) + + // 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) + // } + // } +} diff --git a/src/components/Publish/FormPublish/index.module.css b/src/components/Publish/FormPublish/index.module.css new file mode 100644 index 000000000..55c0b5be0 --- /dev/null +++ b/src/components/Publish/FormPublish/index.module.css @@ -0,0 +1,4 @@ +.form { + composes: box from '@shared/atoms/Box.module.css'; + margin-bottom: var(--spacer); +} diff --git a/src/components/Publish/FormPublish/index.tsx b/src/components/Publish/FormPublish/index.tsx new file mode 100644 index 000000000..4f3546fc6 --- /dev/null +++ b/src/components/Publish/FormPublish/index.tsx @@ -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: + }, + { + name: 'Compute', + title: 'Compute', + icon: + } +] + +export default function FormPublish(): ReactElement { + const { content } = useStaticQuery(query) + + const { + setStatus, + isValid, + values, + setErrors, + setTouched, + resetForm, + validateField, + setFieldValue + }: FormikContextType = useFormikContext() + + const [computeTypeSelected, setComputeTypeSelected] = useState(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, + 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) => { + e.preventDefault() + resetForm({ + values: initialValues as FormPublishData, + status: 'empty' + }) + setStatus('empty') + } + + function getStepContentFields(contentStep: FormStepContent) { + return contentStep.fields.map( + (field: FormFieldProps) => + field.advanced !== true && ( + ) => + handleFieldChange(e, field) + } + /> + ) + ) + } + + const tabs = [ + { + title: content.metadata.title, + content: ( + <> + {getStepContentFields(content.metadata)} + + + + ) + }, + { + title: content.services.title, + content: ( + <> + {getStepContentFields(content.services)} + + + + ) + }, + { + title: content.pricing.title, + content: ( + <> + + + + ) + } + ] + + return ( + <> +
+ + + + + ) +} diff --git a/src/components/Publish/FormTitle.module.css b/src/components/Publish/FormTitle.module.css deleted file mode 100644 index 9ff189d87..000000000 --- a/src/components/Publish/FormTitle.module.css +++ /dev/null @@ -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; -} diff --git a/src/components/Publish/Pricing/Feedback.module.css b/src/components/Publish/Pricing/Feedback.module.css deleted file mode 100644 index a2cc161bc..000000000 --- a/src/components/Publish/Pricing/Feedback.module.css +++ /dev/null @@ -1,8 +0,0 @@ -.feedback { - width: 100%; - min-height: 20vh; - display: flex; - flex-wrap: wrap; - justify-content: center; - align-items: center; -} diff --git a/src/components/Publish/Pricing/Feedback.tsx b/src/components/Publish/Pricing/Feedback.tsx deleted file mode 100644 index 611b1ed70..000000000 --- a/src/components/Publish/Pricing/Feedback.tsx +++ /dev/null @@ -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 = () => ( - - ) - - return ( -
- {success ? ( - } /> - ) : ( - - )} -
- ) -} diff --git a/src/components/Publish/Pricing/FormPricing/Fixed.module.css b/src/components/Publish/Pricing/FormPricing/Fixed.module.css deleted file mode 100644 index c96e35d5a..000000000 --- a/src/components/Publish/Pricing/FormPricing/Fixed.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.fixed { - composes: content from './index.module.css'; -} diff --git a/src/components/Publish/Pricing/FormPricing/Fixed.tsx b/src/components/Publish/Pricing/FormPricing/Fixed.tsx deleted file mode 100644 index 2bc3c4945..000000000 --- a/src/components/Publish/Pricing/FormPricing/Fixed.tsx +++ /dev/null @@ -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 ( -
- {content.info} - - -
- ) -} diff --git a/src/components/Publish/Pricing/FormPricing/Free.module.css b/src/components/Publish/Pricing/FormPricing/Free.module.css deleted file mode 100644 index 5dcdab375..000000000 --- a/src/components/Publish/Pricing/FormPricing/Free.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.free { - composes: content from './index.module.css'; -} diff --git a/src/components/Publish/Pricing/FormPricing/Free.tsx b/src/components/Publish/Pricing/FormPricing/Free.tsx deleted file mode 100644 index 9673aad09..000000000 --- a/src/components/Publish/Pricing/FormPricing/Free.tsx +++ /dev/null @@ -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 ( -
- {content.info} - -
- ) -} diff --git a/src/components/Publish/Pricing/FormPricing/index.module.css b/src/components/Publish/Pricing/FormPricing/index.module.css deleted file mode 100644 index c8002d1f9..000000000 --- a/src/components/Publish/Pricing/FormPricing/index.module.css +++ /dev/null @@ -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); -} diff --git a/src/components/Publish/Pricing/FormPricing/index.tsx b/src/components/Publish/Pricing/FormPricing/index.tsx deleted file mode 100644 index 56b331a99..000000000 --- a/src/components/Publish/Pricing/FormPricing/index.tsx +++ /dev/null @@ -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: - } - : undefined, - appConfig.allowDynamicPricing === 'true' - ? { - title: content.dynamic.title, - content: - } - : undefined, - appConfig.allowFreePricing === 'true' - ? { - title: content.free.title, - content: - } - : undefined - ].filter((tab) => tab !== undefined) - - return ( - <> - - -
- - - - {content.empty.action.help} - -
- - {debug === true && ( -
-          {JSON.stringify(values, null, 2)}
-        
- )} - - ) -} diff --git a/src/components/Publish/Pricing/_constants.ts b/src/components/Publish/Pricing/_constants.ts deleted file mode 100644 index 0fe6179f5..000000000 --- a/src/components/Publish/Pricing/_constants.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { allowDynamicPricing, allowFixedPricing } from '../../../../app.config' -import * as Yup from 'yup' - -export const validationSchema: Yup.SchemaOf = 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 % -} diff --git a/src/components/Publish/Pricing/_types.d.ts b/src/components/Publish/Pricing/_types.d.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/components/Publish/Pricing/index.module.css b/src/components/Publish/Pricing/index.module.css deleted file mode 100644 index 5e2b95406..000000000 --- a/src/components/Publish/Pricing/index.module.css +++ /dev/null @@ -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; -} diff --git a/src/components/Publish/Title.module.css b/src/components/Publish/Title.module.css new file mode 100644 index 000000000..2fc3d8b7e --- /dev/null +++ b/src/components/Publish/Title.module.css @@ -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; +} diff --git a/src/components/Publish/Title.tsx b/src/components/Publish/Title.tsx new file mode 100644 index 000000000..c9ecc0179 --- /dev/null +++ b/src/components/Publish/Title.tsx @@ -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 + + + )} + + ) +} diff --git a/src/components/Publish/_constants.ts b/src/components/Publish/_constants.ts index 2826e38cd..d9b95e7b2 100644 --- a/src/components/Publish/_constants.ts +++ b/src/components/Publish/_constants.ts @@ -1,101 +1,149 @@ import { File as FileMetadata } from '@oceanprotocol/lib' import * as Yup from 'yup' +import { allowDynamicPricing, allowFixedPricing } from '../../../app.config' import { FormPublishData } from './_types' +export const initialValues: Partial = { + metadata: { + 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() + .min(4, (param) => `Title must be at least ${param.min} characters`) + .required('Required'), + description: Yup.string().min(10).required('Required'), + author: Yup.string().required('Required'), + tags: Yup.string().nullable(), + termsAndConditions: Yup.boolean().required('Required') +} + +const validationService = { + files: Yup.array() + .required('Enter a valid URL and click "ADD FILE"') + .nullable(), + links: Yup.array().nullable(), + dataTokenOptions: Yup.object() + .shape({ + name: Yup.string(), + symbol: Yup.string() + }) + .required('Required'), + timeout: Yup.string().required('Required'), + access: Yup.string() + .matches(/Compute|Download/g, { excludeEmptyString: true }) + .required('Required'), + 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 = Yup.object() .shape({ - // ---- required fields ---- - name: Yup.string() - .min(4, (param) => `Title must be at least ${param.min} characters`) - .required('Required'), - author: Yup.string().required('Required'), - dataTokenOptions: Yup.object() - .shape({ - name: Yup.string(), - symbol: Yup.string() - }) - .required('Required'), - files: Yup.array() - .required('Enter a valid URL and click "ADD FILE"') - .nullable(), - description: Yup.string().min(10).required('Required'), - timeout: Yup.string().required('Required'), - access: Yup.string() - .matches(/Compute|Download/g, { excludeEmptyString: true }) - .required('Required'), - termsAndConditions: Yup.boolean().required('Required'), - // ---- optional fields ---- - tags: Yup.string().nullable(), - links: Yup.array().nullable(), - providerUri: Yup.string().url().nullable() + metadata: Yup.object().shape(validationMetadata), + services: Yup.array().of(Yup.object().shape(validationService)), + pricing: Yup.object().shape(validationPricing) }) .defined() -export const initialValues: Partial = { - name: '', - author: '', - dataTokenOptions: { - name: '', - symbol: '' - }, - files: '', - description: '', - timeout: 'Forever', - access: '', - termsAndConditions: false, - tags: '', - providerUri: '' -} +// export const validationSchemaAlgo: Yup.SchemaOf = +// Yup.object() +// .shape({ +// // ---- required fields ---- +// name: Yup.string() +// .min(4, (param) => `Title must be at least ${param.min} characters`) +// .required('Required'), +// description: Yup.string().min(10).required('Required'), +// files: Yup.array().required('Required').nullable(), +// timeout: Yup.string().required('Required'), +// dataTokenOptions: Yup.object() +// .shape({ +// name: Yup.string(), +// 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().nullable() +// }) +// .defined() -export const validationSchemaAlgo: Yup.SchemaOf = - Yup.object() - .shape({ - // ---- required fields ---- - name: Yup.string() - .min(4, (param) => `Title must be at least ${param.min} characters`) - .required('Required'), - description: Yup.string().min(10).required('Required'), - files: Yup.array().required('Required').nullable(), - timeout: Yup.string().required('Required'), - dataTokenOptions: Yup.object() - .shape({ - name: Yup.string(), - 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().nullable() - }) - .defined() - -export const initialValuesAlgo: Partial = { - 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: '' -} +// export const initialValuesAlgo: Partial = { +// 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: '' +// } diff --git a/src/components/Publish/_types.ts b/src/components/Publish/_types.ts index 77d06d41d..4a742d262 100644 --- a/src/components/Publish/_types.ts +++ b/src/components/Publish/_types.ts @@ -2,9 +2,12 @@ import { DataTokenOptions } from '@hooks/usePublish' import { EditableMetadataLinks } from '@oceanprotocol/lib' export interface FormPublishService { + files: string | File[] + links?: string | EditableMetadataLinks[] timeout: string dataTokenOptions: DataTokenOptions access: 'Download' | 'Compute' | string + providerUri?: string } export interface FormPublishData { @@ -12,12 +15,9 @@ export interface FormPublishData { metadata: { name: string description: string - files: string | File[] author: string termsAndConditions: boolean tags?: string - links?: string | EditableMetadataLinks[] - providerUri?: string } services: FormPublishService[] pricing: PriceOptions diff --git a/src/components/Publish/index.tsx b/src/components/Publish/index.tsx index 2a63885d0..f1768f689 100644 --- a/src/components/Publish/index.tsx +++ b/src/components/Publish/index.tsx @@ -2,7 +2,6 @@ import React, { ReactElement, useState, useEffect } from 'react' import Permission from '@shared/Permission' import { Formik, FormikState } from 'formik' import { usePublish } from '@hooks/usePublish' -import styles from './index.module.css' import { initialValues, validationSchema } from './_constants' // import { // transformPublishFormToMetadata, @@ -13,13 +12,17 @@ import { Logger, Metadata } from '@oceanprotocol/lib' import { useAccountPurgatory } from '@hooks/useAccountPurgatory' import { useWeb3 } from '@context/Web3' 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' export default function PublishPage({ content }: { - content: { warning: string } + content: { title: string; description: string; warning: string } }): ReactElement { const { accountId } = useWeb3() const { isInPurgatory, purgatoryData } = useAccountPurgatory(accountId) @@ -78,21 +81,19 @@ export default function PublishPage({ // } // } - return isInPurgatory && purgatoryData ? null : ( - - { - // kick off publishing - // await handleSubmit(values, resetForm) - }} - > - {({ values }) => { - return <>Hello - }} - - + {isInPurgatory && purgatoryData ? null : ( + { + // kick off publishing + // await handleSubmit(values, resetForm) + }} + > + + + )} + ) } diff --git a/src/pages/publish.tsx b/src/pages/publish.tsx index 0d28554cc..8d1eb85b8 100644 --- a/src/pages/publish.tsx +++ b/src/pages/publish.tsx @@ -15,4 +15,4 @@ export default function PagePublish(): ReactElement { ) -} +} \ No newline at end of file