Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
SSHOC
sshoc-marketplace-frontend
Commits
8511c271
Commit
8511c271
authored
Mar 03, 2021
by
Stefan Probst
Browse files
feat: add item create forms
parent
6f2b7535
Pipeline
#177674
passed with stages
in 10 minutes and 32 seconds
Changes
112
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
.dockerignore
View file @
8511c271
...
...
@@ -49,4 +49,4 @@ public/robots.txt
public/sitemap.xml
# auto-generated api clients
src/api/sshoc/index.ts
#
src/api/sshoc/index.ts
.gitignore
View file @
8511c271
...
...
@@ -51,4 +51,4 @@ public/robots.txt
public/sitemap.xml
# auto-generated api clients
src/api/sshoc/index.ts
#
src/api/sshoc/index.ts
content/pages/about.mdx
View file @
8511c271
...
...
@@ -75,7 +75,7 @@ CLARIN.
In the current governance scheme, 5 ERICs will ensure the continuity of the SSH
Open Marketplace beyond the lifetime of the SSHOC project.
<div class="grid grid-cols-5">
<div class
Name
="grid grid-cols-5">
<a href="https://www.cessda.eu">
...
...
jest.config.json
View file @
8511c271
...
...
@@ -14,7 +14,7 @@
"testPathIgnorePatterns"
:
[
"/node_modules/"
,
"/.next/"
],
"transform"
:
{
"^.+\\.(js|jsx|ts|tsx)$"
:
"<rootDir>/node_modules/babel-jest"
,
"^.+\\.(css|jpg|png|svg)$"
:
"<rootDir>/test/stub.
t
s"
"^.+\\.(css|jpg|png|svg)$"
:
"<rootDir>/test/stub.
j
s"
},
"transformIgnorePatterns"
:
[
"/node_modules/"
,
"^.+
\\
.module
\\
.css$"
]
}
package.json
View file @
8511c271
...
...
@@ -18,10 +18,10 @@
"lint"
:
"eslint . --ignore-path .gitignore"
,
"lint:fix"
:
"yarn lint --fix"
,
"postbuild"
:
"yarn create:sitemap"
,
"prebuild"
:
"rimraf .next && yarn
create:sshoc-client && yarn
create:favicons"
,
"prebuild"
:
"rimraf .next && yarn create:favicons"
,
"preexport"
:
"rimraf out"
,
"start"
:
"next start"
,
"test"
:
"jest"
,
"test"
:
"jest
--passWithNoTests
"
,
"test:coverage"
:
"yarn test --coverage"
},
"dependencies"
:
{
...
...
@@ -33,6 +33,40 @@
"
@reach/dialog
"
:
"
^0.11.2
"
,
"
@reach/disclosure
"
:
"
^0.11.2
"
,
"
@reach/visually-hidden
"
:
"
^0.11.1
"
,
"
@react-aria/breadcrumbs
"
:
"
^3.1.2
"
,
"
@react-aria/button
"
:
"
^3.3.1
"
,
"
@react-aria/checkbox
"
:
"
^3.2.1
"
,
"
@react-aria/combobox
"
:
"
^3.0.0-alpha.1
"
,
"
@react-aria/dialog
"
:
"
^3.1.2
"
,
"
@react-aria/focus
"
:
"
^3.2.3
"
,
"
@react-aria/i18n
"
:
"
^3.3.0
"
,
"
@react-aria/interactions
"
:
"
^3.3.3
"
,
"
@react-aria/link
"
:
"
^3.1.2
"
,
"
@react-aria/listbox
"
:
"
^3.2.4
"
,
"
@react-aria/menu
"
:
"
^3.1.4
"
,
"
@react-aria/numberfield
"
:
"
^3.0.0-alpha.0
"
,
"
@react-aria/overlays
"
:
"
^3.6.1
"
,
"
@react-aria/progress
"
:
"
^3.1.1
"
,
"
@react-aria/searchfield
"
:
"
^3.1.1
"
,
"
@react-aria/select
"
:
"
^3.3.0
"
,
"
@react-aria/separator
"
:
"
^3.1.1
"
,
"
@react-aria/ssr
"
:
"
^3.0.1
"
,
"
@react-aria/textfield
"
:
"
^3.2.2
"
,
"
@react-aria/tooltip
"
:
"
^3.1.1
"
,
"
@react-aria/utils
"
:
"
^3.6.0
"
,
"
@react-aria/visually-hidden
"
:
"
^3.2.1
"
,
"
@react-stately/checkbox
"
:
"
^3.0.1
"
,
"
@react-stately/collections
"
:
"
^3.3.0
"
,
"
@react-stately/combobox
"
:
"
^3.0.0-alpha.1
"
,
"
@react-stately/data
"
:
"
^3.2.0
"
,
"
@react-stately/menu
"
:
"
^3.2.1
"
,
"
@react-stately/numberfield
"
:
"
^3.0.0-alpha.0
"
,
"
@react-stately/overlays
"
:
"
^3.1.1
"
,
"
@react-stately/searchfield
"
:
"
^3.1.1
"
,
"
@react-stately/select
"
:
"
^3.1.1
"
,
"
@react-stately/toggle
"
:
"
^3.2.1
"
,
"
@react-stately/tooltip
"
:
"
^3.0.2
"
,
"
@react-stately/tree
"
:
"
^3.1.2
"
,
"
@stefanprobst/next-app-layout
"
:
"
^1.0.1
"
,
"
@stefanprobst/next-error-boundary
"
:
"
^1.0.5
"
,
"
@stefanprobst/next-page-metadata
"
:
"
^1.0.7
"
,
...
...
@@ -41,7 +75,11 @@
"
@tailwindcss/typography
"
:
"
^0.3.1
"
,
"
autoprefixer
"
:
"
^10.1.0
"
,
"
clsx
"
:
"
^1.1.1
"
,
"
final-form
"
:
"
^4.20.1
"
,
"
final-form-arrays
"
:
"
^3.0.2
"
,
"
final-form-focus
"
:
"
^1.1.2
"
,
"
focus-visible
"
:
"
^5.2.0
"
,
"
highlight-words-core
"
:
"
^1.2.2
"
,
"
jwt-decode
"
:
"
^3.1.1
"
,
"
next
"
:
"
^10.0.3
"
,
"
nodemailer
"
:
"
^6.4.16
"
,
...
...
@@ -50,9 +88,11 @@
"
postcss-focus-visible
"
:
"
^5.0.0
"
,
"
react
"
:
"
^17.0.1
"
,
"
react-dom
"
:
"
^17.0.1
"
,
"
react-final-form
"
:
"
^6.5.2
"
,
"
react-final-form-arrays
"
:
"
^3.1.3
"
,
"
react-google-recaptcha
"
:
"
^2.1.0
"
,
"
react-hook-form
"
:
"
^6.13.1
"
,
"
react-query
"
:
"
^
2.26.2
"
,
"
react-query
"
:
"
^
3.12.0
"
,
"
react-query-devtools
"
:
"
^2.6.3
"
,
"
react-toastify
"
:
"
^6.1.0
"
,
"
rehype-stringify
"
:
"
^8.0.0
"
,
...
...
@@ -80,6 +120,8 @@
"
@testing-library/jest-dom
"
:
"
^5.11.5
"
,
"
@testing-library/react
"
:
"
^11.1.1
"
,
"
@testing-library/user-event
"
:
"
^12.2.0
"
,
"
@types/final-form-focus
"
:
"
^1.1.1
"
,
"
@types/highlight-words-core
"
:
"
^1.2.0
"
,
"
@types/jest
"
:
"
^26.0.15
"
,
"
@types/jwt-decode
"
:
"
^3.1.0
"
,
"
@types/mdx-js__react
"
:
"
^1.5.3
"
,
...
...
src/api/sshoc/client.ts
View file @
8511c271
...
...
@@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-namespace */
import
type
{
Mutation
Config
,
MutationResult
Pair
}
from
'
react-query
'
import
type
{
Use
Mutation
Options
,
Use
MutationResult
}
from
'
react-query
'
import
{
useMutation
}
from
'
react-query
'
import
type
{
ImplicitGrantTokenData
,
OAuthRegistrationDto
}
from
'
@/api/sshoc
'
import
{
request
}
from
'
@/api/sshoc
'
...
...
@@ -51,13 +51,13 @@ export function signInUser([body]: [body: SignInUser.RequestBody]): Promise<
}
export
function
useSignInUser
(
config
?:
Mutation
Config
<
config
?:
Use
Mutation
Options
<
SignInUser
.
Response
.
Success
,
SignInUser
.
Response
.
Error
,
[
SignInUser
.
RequestBody
],
unknown
>
,
):
MutationResult
Pair
<
):
Use
MutationResult
<
SignInUser
.
Response
.
Success
,
SignInUser
.
Response
.
Error
,
[
SignInUser
.
RequestBody
],
...
...
@@ -105,13 +105,13 @@ export async function validateImplicitGrantTokenWithoutRegistration([body]: [
}
export
function
useValidateImplicitGrantTokenWithoutRegistration
(
config
?:
Mutation
Config
<
config
?:
Use
Mutation
Options
<
ValidateImplicitGrantTokenWithoutRegistration
.
Response
.
Success
,
ValidateImplicitGrantTokenWithoutRegistration
.
Response
.
Error
,
[
ValidateImplicitGrantTokenWithoutRegistration
.
RequestBody
],
unknown
>
,
):
MutationResult
Pair
<
):
Use
MutationResult
<
ValidateImplicitGrantTokenWithoutRegistration
.
Response
.
Success
,
ValidateImplicitGrantTokenWithoutRegistration
.
Response
.
Error
,
[
ValidateImplicitGrantTokenWithoutRegistration
.
RequestBody
],
...
...
@@ -160,13 +160,13 @@ export async function validateImplicitGrantTokenWithRegistration([body]: [
}
export
function
useValidateImplicitGrantTokenWithRegistration
(
config
?:
Mutation
Config
<
config
?:
Use
Mutation
Options
<
ValidateImplicitGrantTokenWithRegistration
.
Response
.
Success
,
ValidateImplicitGrantTokenWithRegistration
.
Response
.
Error
,
[
ValidateImplicitGrantTokenWithRegistration
.
RequestBody
],
unknown
>
,
):
MutationResult
Pair
<
):
Use
MutationResult
<
ValidateImplicitGrantTokenWithRegistration
.
Response
.
Success
,
ValidateImplicitGrantTokenWithRegistration
.
Response
.
Error
,
[
ValidateImplicitGrantTokenWithRegistration
.
RequestBody
],
...
...
src/api/sshoc/helpers.ts
0 → 100644
View file @
8511c271
import
type
{
DatasetCore
,
DatasetDto
,
PublicationCore
,
PublicationDto
,
StepCore
,
StepDto
,
ToolCore
,
ToolDto
,
TrainingMaterialCore
,
TrainingMaterialDto
,
WorkflowCore
,
WorkflowDto
,
}
from
'
@/api/sshoc
'
export
function
convertToInitialFormValues
(
item
:
DatasetDto
):
DatasetCore
export
function
convertToInitialFormValues
(
item
:
PublicationDto
,
):
PublicationCore
export
function
convertToInitialFormValues
(
item
:
StepDto
):
StepCore
export
function
convertToInitialFormValues
(
item
:
ToolDto
):
ToolCore
export
function
convertToInitialFormValues
(
item
:
TrainingMaterialDto
,
):
TrainingMaterialCore
export
function
convertToInitialFormValues
(
item
:
WorkflowDto
):
WorkflowCore
/**
* Converts item details into intitial form values,
* i.e. `${ItemCategory}Dto` into `${ItemCategory}Core`.
*/
export
function
convertToInitialFormValues
(
item
:
|
DatasetDto
|
PublicationDto
|
StepDto
|
ToolDto
|
TrainingMaterialDto
|
WorkflowDto
,
):
|
DatasetCore
|
PublicationCore
|
StepCore
|
ToolCore
|
TrainingMaterialCore
|
WorkflowCore
{
return
{
...
item
,
/**
* Only ids needed.
*/
contributors
:
item
.
contributors
?.
map
((
contributor
)
=>
{
const
id
=
contributor
.
actor
?.
id
const
code
=
contributor
.
role
?.
code
return
{
actor
:
{
id
},
role
:
{
code
}
}
}),
/**
* Only ids needed.
*/
properties
:
item
.
properties
?.
map
((
property
)
=>
{
return
{
// id: property.id, // this is in ItemDto but not in ItemCore
type
:
{
code
:
property
.
type
?.
code
},
value
:
property
.
value
,
concept
:
{
code
:
property
.
concept
?.
code
,
vocabulary
:
{
code
:
property
.
concept
?.
vocabulary
?.
code
},
uri
:
property
.
concept
?.
uri
,
},
}
}),
/**
* `persistentId` => `objectId`
*/
relatedItems
:
item
.
relatedItems
?.
map
((
relation
)
=>
{
return
{
...
relation
,
objectId
:
relation
.
persistentId
,
}
}),
/**
* FIXME: figure out what this is, and why it it incompatible
*/
externalIds
:
item
.
externalIds
?.
map
((
id
)
=>
{
return
{
serviceIdentifier
:
id
.
identifierService
?.
code
??
''
,
identifier
:
id
.
identifier
??
''
,
}
}),
/**
* Only id needed.
*/
source
:
{
id
:
item
.
source
?.
id
,
},
}
}
src/api/sshoc/index.ts
0 → 100644
View file @
8511c271
This diff is collapsed.
Click to expand it.
src/components/item/ActorsFormSection/ActorsFormSection.tsx
0 → 100644
View file @
8511c271
import
{
useState
}
from
'
react
'
import
{
useGetActors
,
useGetAllActorRoles
}
from
'
@/api/sshoc
'
import
{
useDebouncedState
}
from
'
@/lib/hooks/useDebouncedState
'
import
{
FormComboBox
}
from
'
@/modules/form/components/FormComboBox/FormComboBox
'
import
{
FormFieldAddButton
}
from
'
@/modules/form/components/FormFieldAddButton/FormFieldAddButton
'
import
{
FormFieldRemoveButton
}
from
'
@/modules/form/components/FormFieldRemoveButton/FormFieldRemoveButton
'
import
{
FormRecord
}
from
'
@/modules/form/components/FormRecord/FormRecord
'
import
{
FormRecords
}
from
'
@/modules/form/components/FormRecords/FormRecords
'
import
{
FormSection
}
from
'
@/modules/form/components/FormSection/FormSection
'
import
{
FormSelect
}
from
'
@/modules/form/components/FormSelect/FormSelect
'
import
{
FormFieldArray
}
from
'
@/modules/form/FormFieldArray
'
export
interface
ActorsFormSectionProps
{
initialValues
?:
any
}
/**
* Form section for contributors.
*/
export
function
ActorsFormSection
(
props
:
ActorsFormSectionProps
):
JSX
.
Element
{
return
(
<
FormSection
title
=
{
'
Actors
'
}
>
<
FormFieldArray
name
=
"contributors"
>
{
({
fields
})
=>
{
return
(
<
FormRecords
>
{
fields
.
map
((
name
,
index
)
=>
{
return
(
<
FormRecord
key
=
{
name
}
>
<
ActorRoleSelect
name
=
{
`
${
name
}
.role.code`
}
label
=
{
'
Actor role
'
}
/>
<
ActorComboBox
name
=
{
`
${
name
}
.actor.id`
}
label
=
{
'
Name
'
}
/>
<
FormFieldRemoveButton
onPress
=
{
()
=>
fields
.
remove
(
index
)
}
aria-label
=
{
'
Remove actor
'
}
/>
</
FormRecord
>
)
})
}
<
FormFieldAddButton
onPress
=
{
()
=>
fields
.
push
(
undefined
)
}
>
{
'
Add actor
'
}
</
FormFieldAddButton
>
</
FormRecords
>
)
}
}
</
FormFieldArray
>
</
FormSection
>
)
}
interface
ActorRoleSelectProps
{
name
:
string
label
:
string
}
/**
* Actor role.
*/
function
ActorRoleSelect
(
props
:
ActorRoleSelectProps
):
JSX
.
Element
{
const
actorRoles
=
useGetAllActorRoles
()
return
(
<
FormSelect
name
=
{
props
.
name
}
label
=
{
props
.
label
}
items
=
{
actorRoles
.
data
??
[]
}
isLoading
=
{
actorRoles
.
isLoading
}
variant
=
"form"
>
{
(
item
)
=>
(
<
FormSelect
.
Item
key
=
{
item
.
code
}
>
{
item
.
label
}
</
FormSelect
.
Item
>
)
}
</
FormSelect
>
)
}
interface
ActorComboBoxProps
{
name
:
string
label
:
string
}
/**
* Actor.
*/
function
ActorComboBox
(
props
:
ActorComboBoxProps
):
JSX
.
Element
{
const
[
searchTerm
,
setSearchTerm
]
=
useState
(
''
)
const
debouncedsearchTerm
=
useDebouncedState
(
searchTerm
,
150
).
trim
()
const
actors
=
useGetActors
(
{
q
:
debouncedsearchTerm
},
{
// enabled: debouncedsearchTerm.length > 2,
keepPreviousData
:
true
,
},
)
return
(
<
FormComboBox
name
=
{
props
.
name
}
label
=
{
props
.
label
}
items
=
{
actors
.
data
?.
actors
??
[]
}
isLoading
=
{
actors
.
isLoading
}
onInputChange
=
{
setSearchTerm
}
variant
=
"form"
style
=
{
{
flex
:
1
}
}
>
{
(
item
)
=>
<
FormComboBox
.
Item
>
{
item
.
name
}
</
FormComboBox
.
Item
>
}
</
FormComboBox
>
)
}
src/components/item/DatasetCreateForm/DatasetCreateForm.tsx
0 → 100644
View file @
8511c271
import
{
useRouter
}
from
'
next/router
'
import
{
useQueryClient
}
from
'
react-query
'
import
{
DatasetCore
,
DatasetDto
,
useCreateDataset
}
from
'
@/api/sshoc
'
import
type
{
ItemCategory
}
from
'
@/api/sshoc/types
'
import
{
ActorsFormSection
}
from
'
@/components/item/ActorsFormSection/ActorsFormSection
'
import
{
DateFormSection
}
from
'
@/components/item/DateFormSection/DateFormSection
'
import
{
MainFormSection
}
from
'
@/components/item/MainFormSection/MainFormSection
'
import
{
PropertiesFormSection
}
from
'
@/components/item/PropertiesFormSection/PropertiesFormSection
'
import
{
RelatedItemsFormSection
}
from
'
@/components/item/RelatedItemsFormSection/RelatedItemsFormSection
'
import
{
SourceFormSection
}
from
'
@/components/item/SourceFormSection/SourceFormSection
'
import
{
Button
}
from
'
@/elements/Button/Button
'
import
{
useToast
}
from
'
@/elements/Toast/useToast
'
import
{
useAuth
}
from
'
@/modules/auth/AuthContext
'
import
{
Form
}
from
'
@/modules/form/Form
'
export
type
ItemFormValues
=
DatasetCore
export
interface
ItemFormProps
<
T
>
{
category
:
ItemCategory
initialValues
?:
Partial
<
T
>
}
/**
* Item create form.
*/
export
function
ItemForm
(
props
:
ItemFormProps
<
ItemFormValues
>
):
JSX
.
Element
{
const
{
category
,
initialValues
}
=
props
const
useItemMutation
=
useCreateDataset
const
toast
=
useToast
()
const
router
=
useRouter
()
const
auth
=
useAuth
()
const
queryClient
=
useQueryClient
()
const
create
=
useItemMutation
({
onSuccess
(
data
:
DatasetDto
)
{
toast
.
success
(
`Successfully updated
${
category
}
.`
)
queryClient
.
invalidateQueries
({
queryKey
:
[
'
itemSearch
'
],
})
queryClient
.
invalidateQueries
({
queryKey
:
[
'
getDatasets
'
],
})
// queryClient.invalidateQueries({
// queryKey: ['getDataset', { id: data.persistentId }],
// })
router
.
push
({
pathname
:
`/
${
data
.
category
}
/
${
data
.
persistentId
}
`
})
},
onError
()
{
toast
.
error
(
`Failed to update
${
category
}
.`
)
},
})
function
onSubmit
(
values
:
ItemFormValues
)
{
if
(
auth
.
session
?.
accessToken
==
null
)
{
toast
.
error
(
'
Authentication required.
'
)
return
Promise
.
reject
()
}
/**
* Backend crashes with `source: {}`.
*/
if
(
values
.
source
&&
values
.
source
.
id
===
undefined
)
{
delete
values
.
source
}
return
create
.
mutateAsync
([{},
values
,
{
token
:
auth
.
session
.
accessToken
}])
}
function
onValidate
(
values
:
Partial
<
ItemFormValues
>
)
{
const
errors
:
Partial
<
Record
<
keyof
typeof
values
,
string
>>
=
{}
/** Required field `label`. */
if
(
values
.
label
===
undefined
)
{
errors
.
label
=
'
Label is required.
'
}
/** Required field `description`. */
if
(
values
.
description
===
undefined
)
{
errors
.
description
=
'
Description is required.
'
}
/** `sourceItemId` is required when `source` is set. */
if
(
values
.
source
?.
id
!=
null
&&
values
.
sourceItemId
==
null
)
{
errors
.
sourceItemId
=
'
Missing value in Source ID.
'
}
/** `source` is required when `sourceItemId` is set. */
if
(
values
.
sourceItemId
!=
null
&&
values
.
source
?.
id
==
null
)
{
errors
.
sourceItemId
=
'
Missing value in Source.
'
}
return
errors
}
function
onCancel
()
{
router
.
push
(
'
/
'
)
}
return
(
<
Form
onSubmit
=
{
onSubmit
}
validate
=
{
onValidate
}
initialValues
=
{
initialValues
}
>
{
({
handleSubmit
,
pristine
,
invalid
,
submitting
})
=>
{
return
(
<
form
onSubmit
=
{
handleSubmit
}
noValidate
className
=
"flex flex-col space-y-12"
>
<
MainFormSection
/>
<
DateFormSection
/>
<
ActorsFormSection
/>
<
PropertiesFormSection
/>
<
RelatedItemsFormSection
/>
<
SourceFormSection
/>
<
div
className
=
"flex items-center justify-end space-x-6"
>
<
Button
onPress
=
{
onCancel
}
variant
=
"link"
>
Cancel
</
Button
>
<
Button
type
=
"submit"
isDisabled
=
{
pristine
||
invalid
||
submitting
||
create
.
isLoading
}
>
Submit
</
Button
>
</
div
>
</
form
>
)
}
}
</
Form
>
)
}
src/components/item/DatasetEditForm/DatasetEditForm.tsx
0 → 100644
View file @
8511c271
import
{
useRouter
}
from
'
next/router
'
import
{
useQueryClient
}
from
'
react-query
'
import
type
{
DatasetCore
,
DatasetDto
}
from
'
@/api/sshoc
'
import
{
useUpdateDataset
}
from
'
@/api/sshoc
'
import
type
{
ItemCategory
}
from
'
@/api/sshoc/types
'
import
{
ActorsFormSection
}
from
'
@/components/item/ActorsFormSection/ActorsFormSection
'
import
{
DateFormSection
}
from
'
@/components/item/DateFormSection/DateFormSection
'
import
{
MainFormSection
}
from
'
@/components/item/MainFormSection/MainFormSection
'
import
{
PropertiesFormSection
}
from
'
@/components/item/PropertiesFormSection/PropertiesFormSection
'
import
{
RelatedItemsFormSection
}
from
'
@/components/item/RelatedItemsFormSection/RelatedItemsFormSection
'
import
{
SourceFormSection
}
from
'
@/components/item/SourceFormSection/SourceFormSection
'
import
{
Button
}
from
'
@/elements/Button/Button
'
import
{
useToast
}
from
'
@/elements/Toast/useToast
'
import
{
useAuth
}
from
'
@/modules/auth/AuthContext
'
import
{
Form
}
from
'
@/modules/form/Form
'
export
type
ItemFormValues
=
DatasetCore
export
interface
ItemFormProps
<
T
>
{
id
:
string
category
:
ItemCategory
initialValues
?:
Partial
<
T
>
}
/**
* Item edit form.
*/
export
function
ItemForm
(
props
:
ItemFormProps
<
ItemFormValues
>
):
JSX
.
Element
{
const
{
id
,
category
,
initialValues
}
=
props
const
useItemMutation
=
useUpdateDataset
const
toast
=
useToast
()
const
router
=
useRouter
()
const
auth
=
useAuth
()
const
queryClient
=
useQueryClient
()
const
create
=
useItemMutation
({
onSuccess
(
data
:
DatasetDto
)
{
toast
.
success
(
`Successfully updated
${
category
}
.`
)
queryClient
.
invalidateQueries
({
queryKey
:
[
'
itemSearch
'
],
})
queryClient
.
invalidateQueries
({
queryKey
:
[
'
getDatasets
'
],
})
queryClient
.
invalidateQueries
({
queryKey
:
[
'
getDataset
'
,
{
id
:
data
.
persistentId
}],
})
router
.
push
({
pathname
:
`/
${
data
.
category
}
/
${
data
.
persistentId
}
`
})
},
onError
()
{