more wizard refactors, functional navigation steps

This commit is contained in:
Matthias Kretschmann 2021-11-01 15:42:55 +01:00
parent 9b5cd8b8ed
commit f4eab6e09a
Signed by: m
GPG Key ID: 606EEEF3C479A91F
31 changed files with 195 additions and 173 deletions

View File

@ -15,7 +15,7 @@ import classNames from 'classnames/bind'
import Disclaimer from './Disclaimer'
import Tooltip from '@shared/atoms/Tooltip'
import Markdown from '@shared/Markdown'
import MetadataFields from 'src/components/Publish/FormPublish/Metadata'
import MetadataFields from 'src/components/Publish/Metadata'
import toPath from 'lodash/toPath'
const cx = classNames.bind(styles)

View File

@ -0,0 +1,47 @@
import React, { FormEvent, ReactElement } from 'react'
import { useOcean } from '@context/Ocean'
import Button from '@shared/atoms/Button'
import styles from './index.module.css'
import { FormikContextType, useFormikContext } from 'formik'
import { FormPublishData } from '../_types'
import { wizardSteps } from '../_constants'
export default function Actions(): ReactElement {
const { ocean, account } = useOcean()
const {
status,
values,
isValid,
setFieldValue
}: FormikContextType<FormPublishData> = useFormikContext()
function handleNext(e: FormEvent) {
e.preventDefault()
setFieldValue('step', values.step + 1)
}
function handlePrevious(e: FormEvent) {
e.preventDefault()
setFieldValue('step', values.step - 1)
}
return (
<footer className={styles.actions}>
<Button onClick={handlePrevious}>Back</Button>
{values.step < wizardSteps.length ? (
<Button style="primary" onClick={handleNext}>
Continue
</Button>
) : (
<Button
type="submit"
style="primary"
disabled={!ocean || !account || !isValid}
>
Submit
</Button>
)}
</footer>
)
}

View File

@ -1,38 +0,0 @@
import React, { 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({ step }: { step: number }): ReactElement {
const { ocean, account } = useOcean()
const { status, isValid, setFieldValue }: FormikContextType<FormPublishData> =
useFormikContext()
function handleNext() {
setFieldValue('step', step + 1)
}
function handlePrevious() {
setFieldValue('step', step - 1)
}
return (
<footer className={styles.actions}>
<Button onClick={handlePrevious}>Back</Button>
<Button style="primary" onClick={() => handleNext()}>
Continue
</Button>
{/* <Button
type="submit"
style="primary"
disabled={!ocean || !account || !isValid}
>
Submit
</Button> */}
</footer>
)
}

View File

@ -1,4 +0,0 @@
.form {
composes: box from '@shared/atoms/Box.module.css';
margin-bottom: var(--spacer);
}

View File

@ -1,48 +0,0 @@
import React, { ReactElement } from 'react'
import { useFormikContext, Form, FormikContextType } from 'formik'
import FormActions from './FormActions'
import { FormPublishData } from '../_types'
import styles from './index.module.css'
import PricingFields from './Pricing'
import Debug from '../Debug'
import MetadataFields from './Metadata'
import ServicesFields from './Services'
import content from '../../../../content/publish/form.json'
import Preview from './Preview'
function Steps({ step }: { step: number }) {
switch (step) {
case 1:
return <MetadataFields />
case 2:
return <ServicesFields />
case 3:
return <PricingFields />
case 4:
return <Preview />
default:
}
}
export default function FormPublish(): ReactElement {
const { isValid, values, resetForm }: FormikContextType<FormPublishData> =
useFormikContext()
// reset form validation on every mount
// useEffect(() => {
// setErrors({})
// setTouched({})
// // setSubmitting(false)
// }, [setErrors, setTouched])
return (
<>
<Form className={styles.form}>
<Steps step={values.step} />
<FormActions step={values.step} />
</Form>
<Debug values={values} />
</>
)
}

View File

@ -1,9 +1,25 @@
import Input from '@shared/Form/Input'
import { Field, useFormikContext } from 'formik'
import React, { ReactElement } from 'react'
import content from '../../../../../content/publish/form.json'
import { FormPublishData } from '../../_types'
import { getFieldContent } from '../../_utils'
import content from '../../../../content/publish/form.json'
import { FormPublishData } from '../_types'
import { getFieldContent } from '../_utils'
const assetTypeOptionsTitles = getFieldContent(
'type',
content.services.fields
).options
const assetTypeOptions = [
{
name: assetTypeOptionsTitles[0].toLowerCase(),
title: assetTypeOptionsTitles[0]
},
{
name: assetTypeOptionsTitles[1].toLowerCase(),
title: assetTypeOptionsTitles[1]
}
]
export default function MetadataFields(): ReactElement {
// connect with Form state, use for conditional field rendering
@ -11,6 +27,12 @@ export default function MetadataFields(): ReactElement {
return (
<>
<Field
{...getFieldContent('type', content.services.fields)}
component={Input}
name="type"
options={assetTypeOptions}
/>
<Field
{...getFieldContent('name', content.metadata.fields)}
component={Input}

View File

@ -0,0 +1,11 @@
.navigation {
background: red;
}
.navigation li {
cursor: pointer;
}
.current {
font-weight: var(--font-weight-bold);
}

View File

@ -0,0 +1,34 @@
import { FormikContextType, useFormikContext } from 'formik'
import React, { FormEvent, ReactElement } from 'react'
import { FormPublishData } from '../_types'
import { wizardSteps } from '../_constants'
import styles from './index.module.css'
export default function Navigation(): ReactElement {
const {
isValid,
values,
resetForm,
setFieldValue
}: FormikContextType<FormPublishData> = useFormikContext()
function handleStepClick(step: number) {
setFieldValue('step', step)
}
return (
<nav className={styles.navigation}>
<ol>
{wizardSteps.map((step) => (
<li
key={step.title}
onClick={() => handleStepClick(step.step)}
className={values.step === step.step ? styles.current : null}
>
{step.title}
</li>
))}
</ol>
</nav>
)
}

View File

@ -2,7 +2,7 @@ import React, { FormEvent, ReactElement, useState } from 'react'
import { File as FileMetadata } from '@oceanprotocol/lib/dist/node/ddo/interfaces/File'
import Markdown from '@shared/Markdown'
import Tags from '@shared/atoms/Tags'
import MetaItem from '../../../Asset/AssetContent/MetaItem'
import MetaItem from '../../Asset/AssetContent/MetaItem'
import FileIcon from '@shared/FileIcon'
import Button from '@shared/atoms/Button'
import { transformTags } from '@utils/ddo'
@ -11,7 +11,7 @@ import { useWeb3 } from '@context/Web3'
import styles from './index.module.css'
import Web3Feedback from '@shared/Web3Feedback'
import { useAsset } from '@context/Asset'
import { FormPublishData } from '../../_types'
import { FormPublishData } from '../_types'
import { useFormikContext } from 'formik'
function Description({ description }: { description: string }) {

View File

@ -3,7 +3,7 @@ import React, { ReactElement, useEffect, useState } from 'react'
import Alert from '@shared/atoms/Alert'
import FormHelp from '@shared/Form/Input/Help'
import Tooltip from '@shared/atoms/Tooltip'
import Wallet from '../../../Header/Wallet'
import Wallet from '../../Header/Wallet'
import Coin from './Coin'
import styles from './Dynamic.module.css'
import Fees from './Fees'
@ -12,7 +12,7 @@ import Price from './Price'
import Decimal from 'decimal.js'
import { useOcean } from '@context/Ocean'
import { useWeb3 } from '@context/Web3'
import { FormPublishData } from '../../_types'
import { FormPublishData } from '../_types'
export default function Dynamic({ content }: { content: any }): ReactElement {
const { networkId, balance } = useWeb3()

View File

@ -5,7 +5,7 @@ import Input from '@shared/Form/Input'
import Error from './Error'
import PriceUnit from '@shared/Price/PriceUnit'
import styles from './Price.module.css'
import { FormPublishData } from '../../_types'
import { FormPublishData } from '../_types'
export default function Price({
firstPrice,

View File

@ -5,11 +5,11 @@ 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 { FormPublishData } from '../_types'
import Dynamic from './Dynamic'
import Fixed from './Fixed'
import Free from './Free'
import content from '../../../../../content/price.json'
import content from '../../../../content/price.json'
import styles from './index.module.css'
export default function PricingFields(): ReactElement {

View File

@ -3,9 +3,9 @@ import { Field, useFormikContext } from 'formik'
import React, { ReactElement } from 'react'
import IconDownload from '@images/download.svg'
import IconCompute from '@images/compute.svg'
import content from '../../../../../content/publish/form.json'
import { getFieldContent } from '../../_utils'
import { FormPublishData } from '../../_types'
import content from '../../../../content/publish/form.json'
import { getFieldContent } from '../_utils'
import { FormPublishData } from '../_types'
const accessTypeOptionsTitles = getFieldContent(
'access',
@ -25,34 +25,12 @@ const accessTypeOptions = [
}
]
const assetTypeOptionsTitles = getFieldContent(
'type',
content.services.fields
).options
const assetTypeOptions = [
{
name: assetTypeOptionsTitles[0].toLowerCase(),
title: assetTypeOptionsTitles[0]
},
{
name: assetTypeOptionsTitles[1].toLowerCase(),
title: assetTypeOptionsTitles[1]
}
]
export default function ServicesFields(): ReactElement {
// connect with Form state, use for conditional field rendering
const { values } = useFormikContext<FormPublishData>()
return (
<>
<Field
{...getFieldContent('type', content.services.fields)}
component={Input}
name="type"
options={assetTypeOptions}
/>
<Field
{...getFieldContent('dataTokenOptions', content.services.fields)}
component={Input}

View File

@ -2,8 +2,8 @@ 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 content from '../../../content/publish/index.json'
import styles from './index.module.css'
import content from '../../../../content/publish/index.json'
export default function Title(): ReactElement {
const { networkId } = useWeb3()

View File

@ -1,7 +1,36 @@
import React from 'react'
import { File as FileMetadata } from '@oceanprotocol/lib'
import * as Yup from 'yup'
import { allowDynamicPricing, allowFixedPricing } from '../../../app.config.js'
import { FormPublishData } from './_types'
import { FormPublishData, StepContent } from './_types'
import content from '../../../content/publish/form.json'
import PricingFields from './Pricing'
import MetadataFields from './Metadata'
import ServicesFields from './Services'
import Preview from './Preview'
export const wizardSteps: StepContent[] = [
{
step: 1,
title: content.metadata.title,
component: <MetadataFields />
},
{
step: 2,
title: content.services.title,
component: <ServicesFields />
},
{
step: 3,
title: content.pricing.title,
component: <PricingFields />
},
{
step: 4,
title: content.preview.title,
component: <Preview />
}
]
export const initialValues: Partial<FormPublishData> = {
step: 1,

View File

@ -1,5 +1,6 @@
import { DataTokenOptions } from '@hooks/usePublish'
import { EditableMetadataLinks, File } from '@oceanprotocol/lib'
import { ReactElement } from 'react'
export interface FormPublishService {
files: string | File[]
@ -15,6 +16,7 @@ export interface FormPublishService {
export interface FormPublishData {
step: number
steps: number
type: 'dataset' | 'algorithm'
metadata: {
name: string
@ -26,3 +28,9 @@ export interface FormPublishData {
services: FormPublishService[]
pricing: PriceOptions
}
export interface StepContent {
step: number
title: string
component: ReactElement
}

View File

@ -1,41 +1,4 @@
.tabs ul[class*='tabList'] {
background-color: var(--background-content);
border: 1px solid var(--border-color);
border-top-left-radius: var(--border-radius);
border-top-right-radius: var(--border-radius);
}
.tabs div[class*='tabContent'] {
padding-left: 0;
padding-right: 0;
padding-top: 0;
}
.grid {
display: grid;
gap: calc(var(--spacer) * 1.5);
position: relative;
}
.alert,
div.alert {
.form {
composes: box from '@shared/atoms/Box.module.css';
margin-bottom: var(--spacer);
margin-left: 0;
}
@media (min-width: 55rem) {
.grid {
/* lazy golden ratio */
grid-template-columns: 1.618fr 1fr;
}
.tabs ul[class*='tabList'] {
/* fake the above 1.618fr column */
max-width: calc((100% / 1.618) - calc(var(--spacer) / 1.075));
}
.sticky {
position: sticky;
top: calc(var(--spacer) / 2);
}
}

View File

@ -1,7 +1,7 @@
import React, { ReactElement, useState, useEffect } from 'react'
import { Formik, FormikState } from 'formik'
import { Form, Formik, FormikState } from 'formik'
import { usePublish } from '@hooks/usePublish'
import { initialValues, validationSchema } from './_constants'
import { initialValues, validationSchema, wizardSteps } from './_constants'
import { validateDockerImage } from '@utils/docker'
import { Logger, Metadata } from '@oceanprotocol/lib'
import { useAccountPurgatory } from '@hooks/useAccountPurgatory'
@ -11,10 +11,20 @@ import { transformPublishFormToDdo } from './_utils'
import PageHeader from '@shared/Page/PageHeader'
import Title from './Title'
import styles from './index.module.css'
import FormPublish from './FormPublish'
import Actions from './Actions'
import Debug from './Debug'
import Navigation from './Navigation'
const formName = 'ocean-publish-form'
function Steps({ step }: { step: number }) {
const { component } = wizardSteps.filter(
(stepContent) => stepContent.step === step
)[0]
return component
}
export default function PublishPage({
content
}: {
@ -84,14 +94,24 @@ export default function PublishPage({
{isInPurgatory && purgatoryData ? null : (
<Formik
initialValues={initialValues}
initialStatus="empty"
validationSchema={validationSchema}
onSubmit={async (values, { resetForm }) => {
// kick off publishing
// await handleSubmit(values, resetForm)
}}
>
<FormPublish />
{({ values }) => {
return (
<>
<Form className={styles.form}>
<Navigation />
<Steps step={values.step} />
<Actions />
</Form>
<Debug values={values} />
</>
)
}}
</Formik>
)}
</>