sanity-plugin-workflow 1.0.0-beta.1 → 1.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +73 -12
- package/lib/{src/index.d.ts → index.d.ts} +3 -3
- package/lib/index.esm.js +1800 -1
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1813 -1
- package/lib/index.js.map +1 -1
- package/package.json +51 -40
- package/src/actions/AssignWorkflow.tsx +48 -0
- package/src/actions/BeginWorkflow.tsx +68 -0
- package/src/actions/CompleteWorkflow.tsx +41 -0
- package/src/actions/RequestReviewAction.js +1 -7
- package/src/actions/UpdateWorkflow.tsx +142 -0
- package/src/badges/AssigneesBadge.tsx +52 -0
- package/src/badges/{index.tsx → StateBadge.tsx} +4 -8
- package/src/components/DocumentCard/AvatarGroup.tsx +12 -8
- package/src/components/DocumentCard/CompleteButton.tsx +53 -0
- package/src/components/DocumentCard/EditButton.tsx +3 -2
- package/src/components/DocumentCard/Field.tsx +38 -0
- package/src/components/DocumentCard/ValidationStatus.tsx +37 -0
- package/src/components/DocumentCard/core/DraftStatus.tsx +32 -0
- package/src/components/DocumentCard/core/PublishedStatus.tsx +39 -0
- package/src/components/DocumentCard/core/TimeAgo.tsx +11 -0
- package/src/components/DocumentCard/index.tsx +156 -50
- package/src/components/DocumentList.tsx +122 -0
- package/src/components/Filters.tsx +168 -0
- package/src/components/FloatingCard.tsx +29 -0
- package/src/components/StateTitle/Status.tsx +27 -0
- package/src/components/StateTitle/index.tsx +73 -0
- package/src/components/UserAssignment.tsx +57 -75
- package/src/components/UserAssignmentInput.tsx +27 -0
- package/src/components/UserDisplay.tsx +57 -0
- package/src/components/Validators.tsx +229 -0
- package/src/components/WorkflowTool.tsx +302 -163
- package/src/constants/index.ts +31 -0
- package/src/helpers/arraysContainMatchingString.ts +6 -0
- package/src/helpers/filterItemsAndSort.ts +41 -0
- package/src/helpers/initialRank.ts +13 -0
- package/src/hooks/useWorkflowDocuments.tsx +62 -70
- package/src/hooks/useWorkflowMetadata.tsx +0 -1
- package/src/index.ts +38 -58
- package/src/schema/workflow/workflow.metadata.ts +68 -0
- package/src/tools/index.ts +15 -0
- package/src/types/index.ts +27 -6
- package/src/actions/DemoteAction.tsx +0 -62
- package/src/actions/PromoteAction.tsx +0 -62
- package/src/components/Mutate.tsx +0 -54
- package/src/components/StateTimeline.tsx +0 -98
- package/src/components/UserSelectInput.tsx +0 -43
- package/src/schema/workflow/metadata.ts +0 -38
|
@@ -1,88 +1,76 @@
|
|
|
1
|
+
import {DraggableLocation} from '@hello-pangea/dnd'
|
|
2
|
+
import {useToast} from '@sanity/ui'
|
|
3
|
+
import groq from 'groq'
|
|
1
4
|
import React from 'react'
|
|
5
|
+
import {useClient} from 'sanity'
|
|
2
6
|
import {useListeningQuery} from 'sanity-plugin-utils'
|
|
3
|
-
import {useToast} from '@sanity/ui'
|
|
4
|
-
import {SanityDocumentLike, useClient} from 'sanity'
|
|
5
|
-
import {DraggableLocation} from 'react-beautiful-dnd'
|
|
6
|
-
import {SanityDocumentWithMetadata, Metadata, State} from '../types'
|
|
7
|
-
|
|
8
|
-
type DocumentsAndMetadata = {
|
|
9
|
-
documents: SanityDocumentLike[]
|
|
10
|
-
metadata: Metadata[]
|
|
11
|
-
}
|
|
12
7
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
8
|
+
import {API_VERSION} from '../constants'
|
|
9
|
+
import {SanityDocumentWithMetadata, State} from '../types'
|
|
10
|
+
|
|
11
|
+
const QUERY = groq`*[_type == "workflow.metadata"]|order(orderRank){
|
|
12
|
+
"_metadata": {
|
|
13
|
+
_rev,
|
|
14
|
+
assignees,
|
|
15
|
+
documentId,
|
|
16
|
+
state,
|
|
17
|
+
orderRank
|
|
18
|
+
},
|
|
19
|
+
...(
|
|
20
|
+
*[_id in [^.documentId, "drafts." + ^.documentId]]|order(_updatedAt)[0]{
|
|
21
|
+
_id,
|
|
22
|
+
_type,
|
|
23
|
+
_rev,
|
|
24
|
+
_updatedAt
|
|
25
|
+
}
|
|
26
|
+
)
|
|
24
27
|
}`
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
type WorkflowDocuments = {
|
|
30
|
+
workflowData: {
|
|
31
|
+
data: SanityDocumentWithMetadata[]
|
|
32
|
+
loading: boolean
|
|
33
|
+
error: boolean
|
|
34
|
+
}
|
|
35
|
+
operations: {
|
|
36
|
+
move: (
|
|
37
|
+
draggedId: string,
|
|
38
|
+
destination: DraggableLocation,
|
|
39
|
+
states: State[],
|
|
40
|
+
newOrder: string
|
|
41
|
+
) => void
|
|
42
|
+
}
|
|
29
43
|
}
|
|
30
44
|
|
|
31
|
-
export function useWorkflowDocuments(schemaTypes: string[]) {
|
|
45
|
+
export function useWorkflowDocuments(schemaTypes: string[]): WorkflowDocuments {
|
|
32
46
|
const toast = useToast()
|
|
33
|
-
const client = useClient()
|
|
47
|
+
const client = useClient({apiVersion: API_VERSION})
|
|
48
|
+
|
|
49
|
+
// Get and listen to changes on documents + workflow metadata documents
|
|
50
|
+
const {data, loading, error} = useListeningQuery<
|
|
51
|
+
SanityDocumentWithMetadata[]
|
|
52
|
+
>(QUERY, {
|
|
53
|
+
params: {schemaTypes},
|
|
54
|
+
initialValue: [],
|
|
55
|
+
})
|
|
56
|
+
|
|
34
57
|
const [localDocuments, setLocalDocuments] = React.useState<
|
|
35
58
|
SanityDocumentWithMetadata[]
|
|
36
59
|
>([])
|
|
37
60
|
|
|
38
|
-
// Get and listen to changes on documents + workflow metadata documents
|
|
39
|
-
const {data, loading, error} = useListeningQuery<DocumentsAndMetadata>(
|
|
40
|
-
COMBINED_QUERY,
|
|
41
|
-
{
|
|
42
|
-
params: {schemaTypes},
|
|
43
|
-
initialValue: INITIAL_DATA,
|
|
44
|
-
}
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
// Store local state for optimistic updates
|
|
48
61
|
React.useEffect(() => {
|
|
49
62
|
if (data) {
|
|
50
|
-
|
|
51
|
-
const documentsWithMetadata = data.documents.reduce(
|
|
52
|
-
(acc: SanityDocumentWithMetadata[], cur) => {
|
|
53
|
-
// Filter out documents without metadata
|
|
54
|
-
const curMeta = data.metadata.find(
|
|
55
|
-
(d) => d.documentId === cur._id.replace(`drafts.`, ``)
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
// Add _metadata as null so it can be shown as a document that needs to be imported into workflow
|
|
59
|
-
if (!curMeta) {
|
|
60
|
-
return [...acc, {_metadata: null, ...cur}]
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const curWithMetadata = {_metadata: curMeta, ...cur}
|
|
64
|
-
|
|
65
|
-
// Remove `published` from array if `draft` exists
|
|
66
|
-
if (!cur._id.startsWith(`drafts.`)) {
|
|
67
|
-
// eslint-disable-next-line max-nested-callbacks
|
|
68
|
-
const alsoHasDraft: boolean = Boolean(
|
|
69
|
-
data.documents.find((doc) => doc._id === `drafts.${cur._id}`)
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
return alsoHasDraft ? acc : [...acc, curWithMetadata]
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return [...acc, curWithMetadata]
|
|
76
|
-
},
|
|
77
|
-
[]
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
setLocalDocuments(documentsWithMetadata)
|
|
63
|
+
setLocalDocuments(data)
|
|
81
64
|
}
|
|
82
65
|
}, [data])
|
|
83
66
|
|
|
84
67
|
const move = React.useCallback(
|
|
85
|
-
(
|
|
68
|
+
(
|
|
69
|
+
draggedId: string,
|
|
70
|
+
destination: DraggableLocation,
|
|
71
|
+
states: State[],
|
|
72
|
+
newOrder: string
|
|
73
|
+
) => {
|
|
86
74
|
// Optimistic update
|
|
87
75
|
const currentLocalData = localDocuments
|
|
88
76
|
const newLocalDocuments = localDocuments.map((item) => {
|
|
@@ -92,6 +80,12 @@ export function useWorkflowDocuments(schemaTypes: string[]) {
|
|
|
92
80
|
_metadata: {
|
|
93
81
|
...item._metadata,
|
|
94
82
|
state: destination.droppableId,
|
|
83
|
+
orderRank: newOrder,
|
|
84
|
+
// This value won't be written to the document
|
|
85
|
+
// It's done so that un/publish operations don't happen twice
|
|
86
|
+
// Because a moved document's card will update once optimistically
|
|
87
|
+
// and then again when the document is updated
|
|
88
|
+
optimistic: true,
|
|
95
89
|
},
|
|
96
90
|
}
|
|
97
91
|
}
|
|
@@ -133,12 +127,11 @@ export function useWorkflowDocuments(schemaTypes: string[]) {
|
|
|
133
127
|
client
|
|
134
128
|
.patch(`workflow-metadata.${documentId}`)
|
|
135
129
|
.ifRevisionId(_rev as string)
|
|
136
|
-
.set({state: newStateId})
|
|
130
|
+
.set({state: newStateId, orderRank: newOrder})
|
|
137
131
|
.commit()
|
|
138
132
|
.then(() => {
|
|
139
133
|
return toast.push({
|
|
140
134
|
title: `Moved to "${newState?.title ?? newStateId}"`,
|
|
141
|
-
description: documentId,
|
|
142
135
|
status: 'success',
|
|
143
136
|
})
|
|
144
137
|
})
|
|
@@ -148,7 +141,6 @@ export function useWorkflowDocuments(schemaTypes: string[]) {
|
|
|
148
141
|
|
|
149
142
|
return toast.push({
|
|
150
143
|
title: `Failed to move to "${newState?.title ?? newStateId}"`,
|
|
151
|
-
description: documentId,
|
|
152
144
|
status: 'error',
|
|
153
145
|
})
|
|
154
146
|
})
|
package/src/index.ts
CHANGED
|
@@ -1,40 +1,15 @@
|
|
|
1
|
-
import {definePlugin} from 'sanity'
|
|
2
|
-
import {CheckmarkIcon, SplitVerticalIcon} from '@sanity/icons'
|
|
1
|
+
import {definePlugin, DocumentActionProps} from 'sanity'
|
|
3
2
|
|
|
4
|
-
import
|
|
3
|
+
import {AssignWorkflow} from './actions/AssignWorkflow'
|
|
4
|
+
import {BeginWorkflow} from './actions/BeginWorkflow'
|
|
5
|
+
import {CompleteWorkflow} from './actions/CompleteWorkflow'
|
|
6
|
+
import {UpdateWorkflow} from './actions/UpdateWorkflow'
|
|
7
|
+
import {AssigneesBadge} from './badges/AssigneesBadge'
|
|
8
|
+
import {StateBadge} from './badges/StateBadge'
|
|
9
|
+
import {DEFAULT_CONFIG} from './constants'
|
|
10
|
+
import metadata from './schema/workflow/workflow.metadata'
|
|
11
|
+
import {workflowTool} from './tools'
|
|
5
12
|
import {WorkflowConfig} from './types'
|
|
6
|
-
import metadata from './schema/workflow/metadata'
|
|
7
|
-
import {StateBadge} from './badges'
|
|
8
|
-
import {PromoteAction} from './actions/PromoteAction'
|
|
9
|
-
import {DemoteAction} from './actions/DemoteAction'
|
|
10
|
-
//import StateTimeline from './components/StateTimeline'
|
|
11
|
-
|
|
12
|
-
const DEFAULT_CONFIG: WorkflowConfig = {
|
|
13
|
-
schemaTypes: [],
|
|
14
|
-
states: [
|
|
15
|
-
{id: 'draft', title: 'Draft', operation: 'unpublish'},
|
|
16
|
-
{id: 'inReview', title: 'In review', operation: null, color: 'primary'},
|
|
17
|
-
{
|
|
18
|
-
id: 'approved',
|
|
19
|
-
title: 'Approved',
|
|
20
|
-
operation: null,
|
|
21
|
-
color: 'success',
|
|
22
|
-
icon: CheckmarkIcon,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
id: 'changesRequested',
|
|
26
|
-
title: 'Changes requested',
|
|
27
|
-
operation: null,
|
|
28
|
-
color: 'warning',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
id: 'published',
|
|
32
|
-
title: 'Published',
|
|
33
|
-
operation: 'publish',
|
|
34
|
-
color: 'success',
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
}
|
|
38
13
|
|
|
39
14
|
export const workflow = definePlugin<WorkflowConfig>(
|
|
40
15
|
(config = DEFAULT_CONFIG) => {
|
|
@@ -49,26 +24,29 @@ export const workflow = definePlugin<WorkflowConfig>(
|
|
|
49
24
|
schema: {
|
|
50
25
|
types: [metadata(states)],
|
|
51
26
|
},
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// item: (props) => {
|
|
55
|
-
// console.log(props)
|
|
56
|
-
// // if (props.id === `root` && schemaTypes.includes(props.schemaType.name)) {
|
|
57
|
-
// // return StateTimeline(props)
|
|
58
|
-
// // }
|
|
59
|
-
// return props.renderDefault(props)
|
|
60
|
-
// },
|
|
61
|
-
// },
|
|
62
|
-
// },
|
|
27
|
+
// TODO: Remove 'workflow.metadata' from list of new document types
|
|
28
|
+
// ...
|
|
63
29
|
document: {
|
|
64
30
|
actions: (prev, context) => {
|
|
65
31
|
if (!schemaTypes.includes(context.schemaType)) {
|
|
66
32
|
return prev
|
|
67
33
|
}
|
|
68
34
|
|
|
35
|
+
// TODO: Augment 'publish' and 'unpublish' to be disabled if a document IS in Workflow
|
|
36
|
+
// This would be best done with a listening query here, but we don't have access to documentStore
|
|
37
|
+
|
|
38
|
+
// TODO: Performance improvements:
|
|
39
|
+
// Each of these actions registers their own listener!
|
|
40
|
+
// One should probably be responsible for listening and storing the response in context
|
|
41
|
+
|
|
69
42
|
return [
|
|
70
|
-
(props) =>
|
|
71
|
-
(props) =>
|
|
43
|
+
(props) => BeginWorkflow(props, states),
|
|
44
|
+
(props) => AssignWorkflow(props, states),
|
|
45
|
+
...states.map(
|
|
46
|
+
(state) => (props: DocumentActionProps) =>
|
|
47
|
+
UpdateWorkflow(props, states, state)
|
|
48
|
+
),
|
|
49
|
+
(props) => CompleteWorkflow(props, states),
|
|
72
50
|
...prev,
|
|
73
51
|
]
|
|
74
52
|
},
|
|
@@ -77,18 +55,20 @@ export const workflow = definePlugin<WorkflowConfig>(
|
|
|
77
55
|
return prev
|
|
78
56
|
}
|
|
79
57
|
|
|
80
|
-
|
|
58
|
+
const {documentId, currentUser} = context
|
|
59
|
+
|
|
60
|
+
if (!documentId) {
|
|
61
|
+
return prev
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return [
|
|
65
|
+
() => StateBadge(states, documentId),
|
|
66
|
+
() => AssigneesBadge(states, documentId, currentUser),
|
|
67
|
+
...prev,
|
|
68
|
+
]
|
|
81
69
|
},
|
|
82
70
|
},
|
|
83
|
-
tools: [
|
|
84
|
-
{
|
|
85
|
-
name: 'workflow',
|
|
86
|
-
title: 'Workflow',
|
|
87
|
-
component: WorkflowTool,
|
|
88
|
-
icon: SplitVerticalIcon,
|
|
89
|
-
options: {schemaTypes, states},
|
|
90
|
-
},
|
|
91
|
-
],
|
|
71
|
+
tools: [workflowTool({schemaTypes, states})],
|
|
92
72
|
}
|
|
93
73
|
}
|
|
94
74
|
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {defineField, defineType} from 'sanity'
|
|
2
|
+
|
|
3
|
+
import Field from '../../components/DocumentCard/Field'
|
|
4
|
+
import UserAssignmentInput from '../../components/UserAssignmentInput'
|
|
5
|
+
import {API_VERSION} from '../../constants'
|
|
6
|
+
import initialRank from '../../helpers/initialRank'
|
|
7
|
+
import {State} from '../../types'
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
10
|
+
export default (states: State[]) =>
|
|
11
|
+
defineType({
|
|
12
|
+
type: 'document',
|
|
13
|
+
name: 'workflow.metadata',
|
|
14
|
+
title: 'Workflow metadata',
|
|
15
|
+
liveEdit: true,
|
|
16
|
+
fields: [
|
|
17
|
+
defineField({
|
|
18
|
+
name: 'state',
|
|
19
|
+
description: `The current "State" of the document. Field is read only as changing it would not fire the state's "operation" setting. These are fired in the Document Actions and in the custom Tool.`,
|
|
20
|
+
readOnly: true,
|
|
21
|
+
type: 'string',
|
|
22
|
+
options: {
|
|
23
|
+
list: states.length
|
|
24
|
+
? states.map((state) => ({
|
|
25
|
+
value: state.id,
|
|
26
|
+
title: state.title,
|
|
27
|
+
}))
|
|
28
|
+
: [],
|
|
29
|
+
layout: 'radio',
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
defineField({
|
|
33
|
+
name: 'documentId',
|
|
34
|
+
title: 'Document ID',
|
|
35
|
+
description:
|
|
36
|
+
'Used to help identify the target document that this metadata is tracking state for.',
|
|
37
|
+
type: 'string',
|
|
38
|
+
readOnly: true,
|
|
39
|
+
components: {
|
|
40
|
+
input: Field,
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
defineField({
|
|
44
|
+
name: 'orderRank',
|
|
45
|
+
description: 'Used to maintain order position of cards in the Tool.',
|
|
46
|
+
type: 'string',
|
|
47
|
+
readOnly: true,
|
|
48
|
+
initialValue: async (p, {getClient}) => {
|
|
49
|
+
const lastDocOrderRank = await getClient({
|
|
50
|
+
apiVersion: API_VERSION,
|
|
51
|
+
}).fetch(`*[_type == $type]|order(@[$order] desc)[0][$order]`, {
|
|
52
|
+
order: `orderRank`,
|
|
53
|
+
type: `workflow.metadata`,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return initialRank(lastDocOrderRank)
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
defineField({
|
|
60
|
+
type: 'array',
|
|
61
|
+
name: 'assignees',
|
|
62
|
+
of: [{type: 'string'}],
|
|
63
|
+
components: {
|
|
64
|
+
input: UserAssignmentInput,
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {SplitVerticalIcon} from '@sanity/icons'
|
|
2
|
+
import {Tool} from 'sanity'
|
|
3
|
+
|
|
4
|
+
import WorkflowTool from '../components/WorkflowTool'
|
|
5
|
+
import {WorkflowConfig} from '../types'
|
|
6
|
+
|
|
7
|
+
export type WorkflowToolConfig = (options: WorkflowConfig) => Tool
|
|
8
|
+
|
|
9
|
+
export const workflowTool: WorkflowToolConfig = (options: WorkflowConfig) => ({
|
|
10
|
+
name: 'workflow',
|
|
11
|
+
title: 'Workflow',
|
|
12
|
+
component: WorkflowTool,
|
|
13
|
+
icon: SplitVerticalIcon,
|
|
14
|
+
options,
|
|
15
|
+
})
|
package/src/types/index.ts
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import {SanityDocumentLike} from 'sanity'
|
|
3
2
|
|
|
3
|
+
// export type Operation = 'publish' | 'unpublish'
|
|
4
|
+
|
|
4
5
|
export type State = {
|
|
5
6
|
id: string
|
|
7
|
+
transitions: string[]
|
|
6
8
|
title: string
|
|
7
|
-
operation?:
|
|
8
|
-
|
|
9
|
+
// operation?: Operation
|
|
10
|
+
roles?: string[]
|
|
11
|
+
requireAssignment?: boolean
|
|
12
|
+
// From document badges
|
|
9
13
|
color?: 'primary' | 'success' | 'warning' | 'danger'
|
|
10
|
-
icon?: React.ReactNode | React.ComponentType
|
|
11
14
|
}
|
|
12
15
|
|
|
16
|
+
export type StateCheck<Id, States> = {
|
|
17
|
+
id: Id
|
|
18
|
+
// Transitions is an array of State ids
|
|
19
|
+
transitions?: States extends {id: infer Id2}[] ? Id2[] : never
|
|
20
|
+
} & State
|
|
21
|
+
|
|
13
22
|
export type WorkflowConfig = {
|
|
14
23
|
schemaTypes: string[]
|
|
15
24
|
states?: State[]
|
|
16
25
|
}
|
|
17
26
|
|
|
27
|
+
export function defineStates<
|
|
28
|
+
Id extends string,
|
|
29
|
+
States extends StateCheck<Id, States>[]
|
|
30
|
+
>(states: States): States {
|
|
31
|
+
return states
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
export type User = {
|
|
19
35
|
createdAt: string
|
|
20
36
|
displayName: string
|
|
@@ -43,8 +59,13 @@ export type Metadata = SanityDocumentLike & {
|
|
|
43
59
|
assignees: string[]
|
|
44
60
|
documentId: string
|
|
45
61
|
state: string
|
|
62
|
+
orderRank: string
|
|
46
63
|
}
|
|
47
64
|
|
|
48
|
-
export type SanityDocumentWithMetadata =
|
|
49
|
-
_metadata: Metadata
|
|
65
|
+
export type SanityDocumentWithMetadata = {
|
|
66
|
+
_metadata: Metadata
|
|
67
|
+
_id: string
|
|
68
|
+
_type: string
|
|
69
|
+
_rev: string
|
|
70
|
+
_updatedAt: string
|
|
50
71
|
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import {ArrowLeftIcon} from '@sanity/icons'
|
|
2
|
-
import {useToast} from '@sanity/ui'
|
|
3
|
-
import {DocumentActionProps, useClient} from 'sanity'
|
|
4
|
-
import {useWorkflowMetadata} from '../hooks/useWorkflowMetadata'
|
|
5
|
-
|
|
6
|
-
import {State} from '../types'
|
|
7
|
-
|
|
8
|
-
export function DemoteAction(props: DocumentActionProps, states: State[]) {
|
|
9
|
-
const {id} = props
|
|
10
|
-
const {data, loading, error} = useWorkflowMetadata(id, states)
|
|
11
|
-
const {state} = data
|
|
12
|
-
const client = useClient()
|
|
13
|
-
const toast = useToast()
|
|
14
|
-
|
|
15
|
-
if (loading || error) {
|
|
16
|
-
if (error) {
|
|
17
|
-
console.error(error)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return null
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!state) {
|
|
24
|
-
return null
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const onHandle = (documentId: string, newState: State) => {
|
|
28
|
-
client
|
|
29
|
-
.patch(`workflow-metadata.${documentId}`)
|
|
30
|
-
.set({state: newState.id})
|
|
31
|
-
.commit()
|
|
32
|
-
.then(() => {
|
|
33
|
-
props.onComplete()
|
|
34
|
-
toast.push({
|
|
35
|
-
status: 'success',
|
|
36
|
-
title: `Document demoted to ${newState.title}`,
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
.catch((err) => {
|
|
40
|
-
props.onComplete()
|
|
41
|
-
console.error(err)
|
|
42
|
-
toast.push({
|
|
43
|
-
status: 'error',
|
|
44
|
-
title: `Document demotion failed`,
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const currentStateIndex = states.findIndex((s) => s.id === state.id)
|
|
50
|
-
const prevState = states[currentStateIndex - 1]
|
|
51
|
-
|
|
52
|
-
if (!prevState) {
|
|
53
|
-
return null
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
icon: ArrowLeftIcon,
|
|
58
|
-
label: `Demote`,
|
|
59
|
-
title: `Demote State to "${prevState.title}"`,
|
|
60
|
-
onHandle: () => onHandle(id, prevState),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import {ArrowRightIcon} from '@sanity/icons'
|
|
2
|
-
import {useToast} from '@sanity/ui'
|
|
3
|
-
import {DocumentActionProps, useClient} from 'sanity'
|
|
4
|
-
import {useWorkflowMetadata} from '../hooks/useWorkflowMetadata'
|
|
5
|
-
|
|
6
|
-
import {State} from '../types'
|
|
7
|
-
|
|
8
|
-
export function PromoteAction(props: DocumentActionProps, states: State[]) {
|
|
9
|
-
const {id} = props
|
|
10
|
-
const {data, loading, error} = useWorkflowMetadata(id, states)
|
|
11
|
-
const {state} = data
|
|
12
|
-
const client = useClient()
|
|
13
|
-
const toast = useToast()
|
|
14
|
-
|
|
15
|
-
if (loading || error) {
|
|
16
|
-
if (error) {
|
|
17
|
-
console.error(error)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return null
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!state) {
|
|
24
|
-
return null
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const onHandle = (documentId: string, newState: State) => {
|
|
28
|
-
client
|
|
29
|
-
.patch(`workflow-metadata.${documentId}`)
|
|
30
|
-
.set({state: newState.id})
|
|
31
|
-
.commit()
|
|
32
|
-
.then(() => {
|
|
33
|
-
props.onComplete()
|
|
34
|
-
toast.push({
|
|
35
|
-
status: 'success',
|
|
36
|
-
title: `Document promoted to ${newState.title}`,
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
.catch((err) => {
|
|
40
|
-
props.onComplete()
|
|
41
|
-
console.error(err)
|
|
42
|
-
toast.push({
|
|
43
|
-
status: 'error',
|
|
44
|
-
title: `Document promotion failed`,
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const currentStateIndex = states.findIndex((s) => s.id === state.id)
|
|
50
|
-
const nextState = states[currentStateIndex + 1]
|
|
51
|
-
|
|
52
|
-
if (!nextState) {
|
|
53
|
-
return null
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
icon: ArrowRightIcon,
|
|
58
|
-
label: `Promote`,
|
|
59
|
-
title: `Promote State to "${nextState.title}"`,
|
|
60
|
-
onHandle: () => onHandle(id, nextState),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {Card, useToast} from '@sanity/ui'
|
|
3
|
-
import {useDocumentOperation} from 'sanity'
|
|
4
|
-
|
|
5
|
-
import {State} from '../types'
|
|
6
|
-
|
|
7
|
-
type MutateProps = {
|
|
8
|
-
_id: string
|
|
9
|
-
_type: string
|
|
10
|
-
state: State
|
|
11
|
-
documentId: string
|
|
12
|
-
onComplete: (id: string) => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function Mutate(props: MutateProps) {
|
|
16
|
-
const {_id, _type, documentId, state, onComplete} = props
|
|
17
|
-
const ops = useDocumentOperation(documentId, _type)
|
|
18
|
-
const isDraft = _id.startsWith('drafts.')
|
|
19
|
-
|
|
20
|
-
const toast = useToast()
|
|
21
|
-
|
|
22
|
-
if (isDraft && state.operation === 'publish') {
|
|
23
|
-
if (!ops.publish.disabled) {
|
|
24
|
-
ops.publish.execute()
|
|
25
|
-
onComplete(_id)
|
|
26
|
-
toast.push({
|
|
27
|
-
title: 'Published Document',
|
|
28
|
-
description: documentId,
|
|
29
|
-
status: 'success',
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
} else if (!isDraft && state.operation === 'unpublish') {
|
|
33
|
-
if (!ops.unpublish.disabled) {
|
|
34
|
-
ops.unpublish.execute()
|
|
35
|
-
onComplete(_id)
|
|
36
|
-
toast.push({
|
|
37
|
-
title: 'Unpublished Document',
|
|
38
|
-
description: documentId,
|
|
39
|
-
status: 'success',
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
// Clean up if it's not going to un/publish
|
|
44
|
-
onComplete(_id)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// return null
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<Card padding={3} shadow={2} tone="primary">
|
|
51
|
-
Mutating: {_id} to {state.title}
|
|
52
|
-
</Card>
|
|
53
|
-
)
|
|
54
|
-
}
|