Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
SSHOC
sshoc-marketplace-frontend
Commits
951cb162
Commit
951cb162
authored
Mar 05, 2021
by
Stefan Probst
Browse files
feat: add multipage form for workflows
parent
c247a3db
Pipeline
#178335
passed with stages
in 11 minutes and 46 seconds
Changes
4
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/api/sshoc/helpers.ts
View file @
951cb162
...
...
@@ -106,5 +106,12 @@ export function convertToInitialFormValues(
initialValues
.
dateLastUpdated
=
item
.
dateLastUpdated
}
if
(
item
.
category
===
'
workflow
'
)
{
// @ts-expect-error items are not discriminated unions
initialValues
.
composedOf
=
item
.
composedOf
?.
map
((
step
)
=>
convertToInitialFormValues
(
step
),
)
}
return
initialValues
}
src/components/item/WorkflowCreateForm/WorkflowCreateForm.tsx
View file @
951cb162
import
type
{
Config
as
FormConfig
}
from
'
final-form
'
import
{
useRouter
}
from
'
next/router
'
import
type
{
ReactNode
}
from
'
react
'
import
{
Fragment
,
useState
}
from
'
react
'
import
{
useQueryClient
}
from
'
react-query
'
import
{
useCreateWorkflow
,
...
...
@@ -12,6 +15,7 @@ import { MainFormSection } from '@/components/item/MainFormSection/MainFormSecti
import
{
PropertiesFormSection
}
from
'
@/components/item/PropertiesFormSection/PropertiesFormSection
'
import
{
RelatedItemsFormSection
}
from
'
@/components/item/RelatedItemsFormSection/RelatedItemsFormSection
'
import
{
SourceFormSection
}
from
'
@/components/item/SourceFormSection/SourceFormSection
'
import
{
WorkflowStepsFormSection
}
from
'
@/components/item/WorkflowStepsFormSection/WorkflowStepsFormSection
'
import
{
Button
}
from
'
@/elements/Button/Button
'
import
{
useToast
}
from
'
@/elements/Toast/useToast
'
import
{
validateCommonFormFields
}
from
'
@/lib/sshoc/validateCommonFormFields
'
...
...
@@ -108,7 +112,7 @@ export function ItemForm(props: ItemFormProps<ItemFormValues>): JSX.Element {
])
}
function
onValidate
(
values
:
Partial
<
ItemFormValues
>
)
{
function
onValidate
Workflow
(
values
:
Partial
<
ItemFormValues
>
)
{
const
errors
:
Partial
<
Record
<
keyof
typeof
values
,
string
>>
=
{}
validateCommonFormFields
(
values
,
errors
)
...
...
@@ -120,11 +124,60 @@ export function ItemForm(props: ItemFormProps<ItemFormValues>): JSX.Element {
router
.
push
(
'
/
'
)
}
/**
* Multi-page form.
* */
const
[
state
,
setState
]
=
useState
({
page
:
0
,
values
:
initialValues
})
const
pages
=
[
<
FormPage
key
=
"workflow-page"
onValidate
=
{
onValidateWorkflow
}
>
<
MainFormSection
/>
<
ActorsFormSection
/>
<
PropertiesFormSection
/>
<
RelatedItemsFormSection
/>
<
SourceFormSection
/>
</
FormPage
>,
<
FormPage
key
=
"steps-page"
>
<
WorkflowStepsFormSection
/>
</
FormPage
>,
]
const
activePage
=
pages
[
state
.
page
]
const
isLastPage
=
state
.
page
===
pages
.
length
-
1
function
nextPage
(
values
:
Partial
<
ItemFormValues
>
)
{
setState
((
state
)
=>
({
values
,
page
:
Math
.
min
(
state
.
page
+
1
,
pages
.
length
-
1
),
}))
}
function
previousPage
()
{
setState
((
state
)
=>
({
values
:
state
.
values
,
page
:
Math
.
max
(
state
.
page
-
1
,
0
),
}))
}
function
handleSubmit
(
values
:
Partial
<
ItemFormValues
>
)
{
if
(
isLastPage
)
{
return
onSubmit
(
values
)
}
else
{
nextPage
(
values
)
}
}
function
validate
(
values
:
Partial
<
ItemFormValues
>
)
{
return
typeof
activePage
.
props
.
onValidate
===
'
function
'
?
activePage
.
props
.
onValidate
(
values
)
:
undefined
}
return
(
<
Form
onSubmit
=
{
on
Submit
}
validate
=
{
onV
alidate
}
initialValues
=
{
initialV
alues
}
onSubmit
=
{
handle
Submit
}
validate
=
{
v
alidate
}
initialValues
=
{
state
.
v
alues
}
>
{
({
handleSubmit
,
form
,
pristine
,
invalid
,
submitting
})
=>
{
return
(
...
...
@@ -133,38 +186,45 @@ export function ItemForm(props: ItemFormProps<ItemFormValues>): JSX.Element {
noValidate
className
=
"flex flex-col space-y-12"
>
<
MainFormSection
/>
<
ActorsFormSection
/>
<
PropertiesFormSection
/>
<
RelatedItemsFormSection
/>
<
SourceFormSection
/>
{
pages
[
state
.
page
]
}
<
div
className
=
"flex items-center justify-end space-x-6"
>
<
Button
onPress
=
{
onCancel
}
variant
=
"link"
>
Cancel
</
Button
>
<
Button
type
=
"submit"
onPress
=
{
()
=>
{
form
.
change
(
'
draft
'
,
true
)
}
}
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
variant
=
"link"
>
Save as draft
</
Button
>
<
Button
type
=
"submit"
onPress
=
{
()
=>
{
form
.
change
(
'
draft
'
,
undefined
)
}
}
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
>
{
isAllowedToPublish
?
'
Publish
'
:
'
Submit
'
}
</
Button
>
{
state
.
page
>
0
?
(
<
Button
onPress
=
{
previousPage
}
>
Previous
</
Button
>
)
:
null
}
{
isLastPage
?
(
<
Fragment
>
<
Button
type
=
"submit"
onPress
=
{
()
=>
{
form
.
change
(
'
draft
'
,
true
)
}
}
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
variant
=
"link"
>
Save as draft
</
Button
>
<
Button
type
=
"submit"
onPress
=
{
()
=>
{
form
.
change
(
'
draft
'
,
undefined
)
}
}
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
>
{
isAllowedToPublish
?
'
Publish
'
:
'
Submit
'
}
</
Button
>
</
Fragment
>
)
:
(
<
Button
type
=
"submit"
isDisabled
=
{
invalid
}
>
Next
</
Button
>
)
}
</
div
>
</
form
>
)
...
...
@@ -172,3 +232,12 @@ export function ItemForm(props: ItemFormProps<ItemFormValues>): JSX.Element {
</
Form
>
)
}
interface
FormPageProps
{
onValidate
?:
FormConfig
<
ItemFormValues
>
[
'
validate
'
]
children
:
ReactNode
}
function
FormPage
(
props
:
FormPageProps
)
{
return
<
Fragment
>
{
props
.
children
}
</
Fragment
>
}
src/components/item/WorkflowEditForm/WorkflowEditForm.tsx
View file @
951cb162
import
type
{
Config
as
FormConfig
}
from
'
final-form
'
import
{
useRouter
}
from
'
next/router
'
import
type
{
ReactNode
}
from
'
react
'
import
{
Fragment
,
useState
}
from
'
react
'
import
{
useQueryClient
}
from
'
react-query
'
import
{
useGetLoggedInUser
,
...
...
@@ -12,6 +15,7 @@ import { MainFormSection } from '@/components/item/MainFormSection/MainFormSecti
import
{
PropertiesFormSection
}
from
'
@/components/item/PropertiesFormSection/PropertiesFormSection
'
import
{
RelatedItemsFormSection
}
from
'
@/components/item/RelatedItemsFormSection/RelatedItemsFormSection
'
import
{
SourceFormSection
}
from
'
@/components/item/SourceFormSection/SourceFormSection
'
import
{
WorkflowStepsFormSection
}
from
'
@/components/item/WorkflowStepsFormSection/WorkflowStepsFormSection
'
import
{
Button
}
from
'
@/elements/Button/Button
'
import
{
useToast
}
from
'
@/elements/Toast/useToast
'
import
{
validateCommonFormFields
}
from
'
@/lib/sshoc/validateCommonFormFields
'
...
...
@@ -111,7 +115,7 @@ export function ItemForm(props: ItemFormProps<ItemFormValues>): JSX.Element {
])
}
function
onValidate
(
values
:
Partial
<
ItemFormValues
>
)
{
function
onValidate
Workflow
(
values
:
Partial
<
ItemFormValues
>
)
{
const
errors
:
Partial
<
Record
<
keyof
typeof
values
,
string
>>
=
{}
validateCommonFormFields
(
values
,
errors
)
...
...
@@ -123,11 +127,60 @@ export function ItemForm(props: ItemFormProps<ItemFormValues>): JSX.Element {
router
.
push
(
'
/
'
)
}
/**
* Multi-page form.
* */
const
[
state
,
setState
]
=
useState
({
page
:
0
,
values
:
initialValues
})
const
pages
=
[
<
FormPage
key
=
"workflow-page"
onValidate
=
{
onValidateWorkflow
}
>
<
MainFormSection
/>
<
ActorsFormSection
initialValues
=
{
props
.
item
}
/>
<
PropertiesFormSection
initialValues
=
{
props
.
item
}
/>
<
RelatedItemsFormSection
initialValues
=
{
props
.
item
}
/>
<
SourceFormSection
/>
</
FormPage
>,
<
FormPage
key
=
"steps-page"
>
<
WorkflowStepsFormSection
/>
</
FormPage
>,
]
const
activePage
=
pages
[
state
.
page
]
const
isLastPage
=
state
.
page
===
pages
.
length
-
1
function
nextPage
(
values
:
Partial
<
ItemFormValues
>
)
{
setState
((
state
)
=>
({
values
,
page
:
Math
.
min
(
state
.
page
+
1
,
pages
.
length
-
1
),
}))
}
function
previousPage
()
{
setState
((
state
)
=>
({
values
:
state
.
values
,
page
:
Math
.
max
(
state
.
page
-
1
,
0
),
}))
}
function
handleSubmit
(
values
:
Partial
<
ItemFormValues
>
)
{
if
(
isLastPage
)
{
return
onSubmit
(
values
)
}
else
{
nextPage
(
values
)
}
}
function
validate
(
values
:
Partial
<
ItemFormValues
>
)
{
return
typeof
activePage
.
props
.
onValidate
===
'
function
'
?
activePage
.
props
.
onValidate
(
values
)
:
undefined
}
return
(
<
Form
onSubmit
=
{
on
Submit
}
validate
=
{
onV
alidate
}
initialValues
=
{
initialV
alues
}
onSubmit
=
{
handle
Submit
}
validate
=
{
v
alidate
}
initialValues
=
{
state
.
v
alues
}
>
{
({
handleSubmit
,
form
,
pristine
,
invalid
,
submitting
})
=>
{
return
(
...
...
@@ -136,38 +189,45 @@ export function ItemForm(props: ItemFormProps<ItemFormValues>): JSX.Element {
noValidate
className
=
"flex flex-col space-y-12"
>
<
MainFormSection
/>
<
ActorsFormSection
initialValues
=
{
props
.
item
}
/>
<
PropertiesFormSection
initialValues
=
{
props
.
item
}
/>
<
RelatedItemsFormSection
initialValues
=
{
props
.
item
}
/>
<
SourceFormSection
/>
{
pages
[
state
.
page
]
}
<
div
className
=
"flex items-center justify-end space-x-6"
>
<
Button
onPress
=
{
onCancel
}
variant
=
"link"
>
Cancel
</
Button
>
<
Button
type
=
"submit"
onPress
=
{
()
=>
{
form
.
change
(
'
draft
'
,
true
)
}
}
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
variant
=
"link"
>
Save as draft
</
Button
>
<
Button
type
=
"submit"
onPress
=
{
()
=>
{
form
.
change
(
'
draft
'
,
undefined
)
}
}
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
>
{
isAllowedToPublish
?
'
Publish
'
:
'
Submit
'
}
</
Button
>
{
state
.
page
>
0
?
(
<
Button
onPress
=
{
previousPage
}
>
Previous
</
Button
>
)
:
null
}
{
isLastPage
?
(
<
Fragment
>
<
Button
type
=
"submit"
onPress
=
{
()
=>
{
form
.
change
(
'
draft
'
,
true
)
}
}
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
variant
=
"link"
>
Save as draft
</
Button
>
<
Button
type
=
"submit"
onPress
=
{
()
=>
{
form
.
change
(
'
draft
'
,
undefined
)
}
}
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
>
{
isAllowedToPublish
?
'
Publish
'
:
'
Submit
'
}
</
Button
>
</
Fragment
>
)
:
(
<
Button
type
=
"submit"
isDisabled
=
{
invalid
}
>
Next
</
Button
>
)
}
</
div
>
</
form
>
)
...
...
@@ -175,3 +235,12 @@ export function ItemForm(props: ItemFormProps<ItemFormValues>): JSX.Element {
</
Form
>
)
}
interface
FormPageProps
{
onValidate
?:
FormConfig
<
ItemFormValues
>
[
'
validate
'
]
children
:
ReactNode
}
function
FormPage
(
props
:
FormPageProps
)
{
return
<
Fragment
>
{
props
.
children
}
</
Fragment
>
}
src/components/item/WorkflowStepsFormSection/WorkflowStepsFormSection.tsx
0 → 100644
View file @
951cb162
import
React
from
'
react
'
import
{
WorkflowCore
}
from
'
@/api/sshoc
'
import
{
FormField
}
from
'
@/modules/form/FormField
'
import
{
FormFieldArray
}
from
'
@/modules/form/FormFieldArray
'
export
interface
ItemFormValues
extends
WorkflowCore
{
draft
?:
boolean
}
/**
* Form section for workflow steps.
*/
export
function
WorkflowStepsFormSection
():
JSX
.
Element
{
return
(
<
section
>
<
FormField
name
=
"label"
subscription
=
{
{
value
:
true
}
}
>
{
({
input
})
=>
{
return
<
h2
>
{
input
.
value
}
</
h2
>
}
}
</
FormField
>
<
strong
>
Editing and sorting workflow steps is not yet implemented.
</
strong
>
<
FormFieldArray
name
=
"composedOf"
>
{
({
fields
})
=>
{
return
(
<
div
>
{
fields
.
map
((
name
)
=>
{
return
(
<
FormField
key
=
{
name
}
name
=
{
`
${
name
}
.label`
}
subscription
=
{
{
value
:
true
}
}
>
{
({
input
})
=>
{
return
<
h3
>
{
input
.
value
}
</
h3
>
}
}
</
FormField
>
)
})
}
</
div
>
)
}
}
</
FormFieldArray
>
</
section
>
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment