sanity-plugin-workflow 1.0.0-beta.6 → 1.0.0-beta.7
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/lib/index.esm.js +138 -63
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +138 -63
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DocumentCard/CompleteButton.tsx +47 -32
- package/src/components/DocumentCard/index.tsx +20 -3
- package/src/components/DocumentList.tsx +4 -1
- package/src/components/Filters.tsx +1 -1
- package/src/components/Verify.tsx +13 -4
- package/src/components/WorkflowTool.tsx +113 -49
- package/src/hooks/useWorkflowDocuments.tsx +7 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {Button, useToast} from '@sanity/ui'
|
|
3
1
|
import {CheckmarkIcon} from '@sanity/icons'
|
|
2
|
+
import {Box, Button, Text, Tooltip, useToast} from '@sanity/ui'
|
|
3
|
+
import React from 'react'
|
|
4
4
|
import {useClient} from 'sanity'
|
|
5
5
|
|
|
6
6
|
import {API_VERSION} from '../../constants'
|
|
@@ -15,39 +15,54 @@ export default function CompleteButton(props: CompleteButtonProps) {
|
|
|
15
15
|
const client = useClient({apiVersion: API_VERSION})
|
|
16
16
|
const toast = useToast()
|
|
17
17
|
|
|
18
|
-
const handleComplete
|
|
19
|
-
(
|
|
20
|
-
|
|
21
|
-
.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
const handleComplete: React.MouseEventHandler<HTMLButtonElement> =
|
|
19
|
+
React.useCallback(
|
|
20
|
+
(event) => {
|
|
21
|
+
const id = event.currentTarget.value
|
|
22
|
+
|
|
23
|
+
if (!id) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
client
|
|
28
|
+
.delete(`workflow-metadata.${id}`)
|
|
29
|
+
.then(() => {
|
|
30
|
+
toast.push({
|
|
31
|
+
status: 'success',
|
|
32
|
+
title: 'Workflow completed',
|
|
33
|
+
})
|
|
27
34
|
})
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
description: id,
|
|
35
|
+
.catch(() => {
|
|
36
|
+
toast.push({
|
|
37
|
+
status: 'error',
|
|
38
|
+
title: 'Could not complete Workflow',
|
|
39
|
+
})
|
|
34
40
|
})
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
41
|
+
},
|
|
42
|
+
[client, toast]
|
|
43
|
+
)
|
|
39
44
|
|
|
40
45
|
return (
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
<Tooltip
|
|
47
|
+
portal
|
|
48
|
+
content={
|
|
49
|
+
<Box padding={2}>
|
|
50
|
+
<Text size={1}>Remove this document from Workflow</Text>
|
|
51
|
+
</Box>
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
<Button
|
|
55
|
+
value={documentId}
|
|
56
|
+
onClick={handleComplete}
|
|
57
|
+
text="Complete"
|
|
58
|
+
icon={CheckmarkIcon}
|
|
59
|
+
tone="positive"
|
|
60
|
+
mode="ghost"
|
|
61
|
+
fontSize={1}
|
|
62
|
+
padding={2}
|
|
63
|
+
tabIndex={-1}
|
|
64
|
+
disabled={disabled}
|
|
65
|
+
/>
|
|
66
|
+
</Tooltip>
|
|
52
67
|
)
|
|
53
68
|
}
|
|
@@ -20,6 +20,7 @@ import {ValidationStatus} from './ValidationStatus'
|
|
|
20
20
|
|
|
21
21
|
type DocumentCardProps = {
|
|
22
22
|
isDragDisabled: boolean
|
|
23
|
+
isPatching: boolean
|
|
23
24
|
userRoleCanDrop: boolean
|
|
24
25
|
isDragging: boolean
|
|
25
26
|
item: SanityDocumentWithMetadata
|
|
@@ -34,6 +35,7 @@ type DocumentCardProps = {
|
|
|
34
35
|
export function DocumentCard(props: DocumentCardProps) {
|
|
35
36
|
const {
|
|
36
37
|
isDragDisabled,
|
|
38
|
+
isPatching,
|
|
37
39
|
userRoleCanDrop,
|
|
38
40
|
isDragging,
|
|
39
41
|
item,
|
|
@@ -68,6 +70,7 @@ export function DocumentCard(props: DocumentCardProps) {
|
|
|
68
70
|
|
|
69
71
|
if (!userRoleCanDrop) return isDarkMode ? `default` : `transparent`
|
|
70
72
|
if (!documentId) return tone
|
|
73
|
+
if (isPatching) tone = isDarkMode ? `default` : `transparent`
|
|
71
74
|
if (isDragging) tone = `positive`
|
|
72
75
|
|
|
73
76
|
if (state?.requireValidation && !isValidating && validation.length > 0) {
|
|
@@ -82,6 +85,7 @@ export function DocumentCard(props: DocumentCardProps) {
|
|
|
82
85
|
}, [
|
|
83
86
|
defaultCardTone,
|
|
84
87
|
userRoleCanDrop,
|
|
88
|
+
isPatching,
|
|
85
89
|
isDarkMode,
|
|
86
90
|
documentId,
|
|
87
91
|
isDragging,
|
|
@@ -137,13 +141,18 @@ export function DocumentCard(props: DocumentCardProps) {
|
|
|
137
141
|
<Flex align="center" justify="space-between" gap={1}>
|
|
138
142
|
<Box flex={1}>
|
|
139
143
|
<Preview
|
|
140
|
-
|
|
144
|
+
// Like as in desk lists, except it has an intermittent loading state
|
|
145
|
+
// layout="default"
|
|
146
|
+
// Like in the PTE, with no loading state
|
|
147
|
+
layout="block"
|
|
141
148
|
value={item}
|
|
142
149
|
schemaType={schema.get(item._type) as SchemaType}
|
|
143
150
|
/>
|
|
144
151
|
</Box>
|
|
145
152
|
<Box style={{flexShrink: 0}}>
|
|
146
|
-
{hasError || isDragDisabled ? null :
|
|
153
|
+
{hasError || isDragDisabled || isPatching ? null : (
|
|
154
|
+
<DragHandleIcon />
|
|
155
|
+
)}
|
|
147
156
|
</Box>
|
|
148
157
|
</Flex>
|
|
149
158
|
</Card>
|
|
@@ -170,13 +179,21 @@ export function DocumentCard(props: DocumentCardProps) {
|
|
|
170
179
|
type={item._type}
|
|
171
180
|
disabled={!userRoleCanDrop}
|
|
172
181
|
/>
|
|
173
|
-
{isLastState ? (
|
|
182
|
+
{isLastState && states.length <= 3 ? (
|
|
174
183
|
<CompleteButton
|
|
175
184
|
documentId={documentId}
|
|
176
185
|
disabled={!userRoleCanDrop}
|
|
177
186
|
/>
|
|
178
187
|
) : null}
|
|
179
188
|
</Flex>
|
|
189
|
+
{isLastState && states.length > 3 ? (
|
|
190
|
+
<Stack paddingTop={2}>
|
|
191
|
+
<CompleteButton
|
|
192
|
+
documentId={documentId}
|
|
193
|
+
disabled={!userRoleCanDrop}
|
|
194
|
+
/>
|
|
195
|
+
</Stack>
|
|
196
|
+
) : null}
|
|
180
197
|
</Card>
|
|
181
198
|
</Stack>
|
|
182
199
|
</Card>
|
|
@@ -11,6 +11,7 @@ import {DocumentCard} from './DocumentCard'
|
|
|
11
11
|
type DocumentListProps = {
|
|
12
12
|
data: SanityDocumentWithMetadata[]
|
|
13
13
|
invalidDocumentIds: string[]
|
|
14
|
+
patchingIds: string[]
|
|
14
15
|
selectedSchemaTypes: string[]
|
|
15
16
|
selectedUserIds: string[]
|
|
16
17
|
state: State
|
|
@@ -28,6 +29,7 @@ export default function DocumentList(props: DocumentListProps) {
|
|
|
28
29
|
const {
|
|
29
30
|
data = [],
|
|
30
31
|
invalidDocumentIds,
|
|
32
|
+
patchingIds,
|
|
31
33
|
selectedSchemaTypes,
|
|
32
34
|
selectedUserIds,
|
|
33
35
|
state,
|
|
@@ -70,7 +72,6 @@ export default function DocumentList(props: DocumentListProps) {
|
|
|
70
72
|
scrollBehavior: 'auto',
|
|
71
73
|
}}
|
|
72
74
|
>
|
|
73
|
-
{/* {dataFiltered.map((item, itemIndex) => { */}
|
|
74
75
|
{rowVirtualizer.getVirtualItems().map((virtualItem) => {
|
|
75
76
|
const item = dataFiltered[virtualItem.index]
|
|
76
77
|
|
|
@@ -83,6 +84,7 @@ export default function DocumentList(props: DocumentListProps) {
|
|
|
83
84
|
const isInvalid = invalidDocumentIds.includes(documentId)
|
|
84
85
|
const meInAssignees = user?.id ? assignees?.includes(user.id) : false
|
|
85
86
|
const isDragDisabled =
|
|
87
|
+
patchingIds.includes(documentId) ||
|
|
86
88
|
!userRoleCanDrop ||
|
|
87
89
|
isInvalid ||
|
|
88
90
|
!(state.requireAssignment
|
|
@@ -106,6 +108,7 @@ export default function DocumentList(props: DocumentListProps) {
|
|
|
106
108
|
<DocumentCard
|
|
107
109
|
userRoleCanDrop={userRoleCanDrop}
|
|
108
110
|
isDragDisabled={isDragDisabled}
|
|
111
|
+
isPatching={patchingIds.includes(documentId)}
|
|
109
112
|
isDragging={draggableSnapshot.isDragging}
|
|
110
113
|
item={item}
|
|
111
114
|
toggleInvalidDocumentId={toggleInvalidDocumentId}
|
|
@@ -139,7 +139,7 @@ export default function Filters(props: FiltersProps) {
|
|
|
139
139
|
)}
|
|
140
140
|
</Flex>
|
|
141
141
|
|
|
142
|
-
{schemaTypes.length >
|
|
142
|
+
{schemaTypes.length > 1 ? (
|
|
143
143
|
<Flex align="center" gap={1}>
|
|
144
144
|
{schemaTypes.map((typeName) => {
|
|
145
145
|
const schemaType = schema.get(typeName)
|
|
@@ -130,17 +130,26 @@ export default function Verify(props: VerifyProps) {
|
|
|
130
130
|
status: 'info',
|
|
131
131
|
})
|
|
132
132
|
|
|
133
|
-
// Get first order
|
|
134
|
-
const firstOrder = data
|
|
135
|
-
|
|
133
|
+
// Get first and second order values
|
|
134
|
+
const [firstOrder, secondOrder] = [...data]
|
|
135
|
+
.slice(0, 2)
|
|
136
|
+
.map((d) => d._metadata?.orderRank)
|
|
137
|
+
const minLexo =
|
|
136
138
|
firstOrder && data.length !== ids.length
|
|
137
139
|
? LexoRank.parse(firstOrder)
|
|
138
140
|
: LexoRank.min()
|
|
141
|
+
const maxLexo =
|
|
142
|
+
secondOrder && data.length !== ids.length
|
|
143
|
+
? LexoRank.parse(secondOrder)
|
|
144
|
+
: LexoRank.max()
|
|
145
|
+
let newLexo = minLexo.between(maxLexo)
|
|
146
|
+
const lastLexo = maxLexo
|
|
139
147
|
|
|
140
148
|
const tx = client.transaction()
|
|
141
149
|
|
|
150
|
+
// Create a new in-between value for each document
|
|
142
151
|
for (let index = 0; index < ids.length; index += 1) {
|
|
143
|
-
newLexo = newLexo.
|
|
152
|
+
newLexo = newLexo.between(lastLexo)
|
|
144
153
|
|
|
145
154
|
tx.patch(`workflow-metadata.${ids[index]}`, {
|
|
146
155
|
set: {orderRank: newLexo.toString()},
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DragDropContext,
|
|
3
|
+
DraggableChildrenFn,
|
|
3
4
|
DragStart,
|
|
4
5
|
Droppable,
|
|
5
6
|
DropResult,
|
|
6
7
|
} from '@hello-pangea/dnd'
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Box,
|
|
10
|
+
Card,
|
|
11
|
+
Container,
|
|
12
|
+
Flex,
|
|
13
|
+
Grid,
|
|
14
|
+
Spinner,
|
|
15
|
+
useTheme,
|
|
16
|
+
useToast,
|
|
17
|
+
} from '@sanity/ui'
|
|
8
18
|
import {LexoRank} from 'lexorank'
|
|
9
19
|
import React from 'react'
|
|
10
20
|
import {Tool, useCurrentUser} from 'sanity'
|
|
@@ -30,6 +40,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
30
40
|
|
|
31
41
|
const isDarkMode = useTheme().sanity.color.dark
|
|
32
42
|
const defaultCardTone = isDarkMode ? 'default' : 'transparent'
|
|
43
|
+
const toast = useToast()
|
|
33
44
|
|
|
34
45
|
const userList = useProjectUsers({apiVersion: API_VERSION})
|
|
35
46
|
|
|
@@ -39,6 +50,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
39
50
|
: []
|
|
40
51
|
|
|
41
52
|
const {workflowData, operations} = useWorkflowDocuments(schemaTypes)
|
|
53
|
+
const [patchingIds, setPatchingIds] = React.useState<string[]>([])
|
|
42
54
|
|
|
43
55
|
// Data to display in cards
|
|
44
56
|
const {data, loading, error} = workflowData
|
|
@@ -128,48 +140,94 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
128
140
|
const destinationStateItems = [
|
|
129
141
|
...filterItemsAndSort(data, destination.droppableId, [], null),
|
|
130
142
|
]
|
|
143
|
+
const destinationStateIndex = states.findIndex(
|
|
144
|
+
(s) => s.id === destination.droppableId
|
|
145
|
+
)
|
|
146
|
+
const globalStateMinimumRank = data[0]._metadata.orderRank
|
|
147
|
+
const globalStateMaximumRank = data[data.length - 1]._metadata.orderRank
|
|
131
148
|
|
|
132
149
|
let newOrder
|
|
133
150
|
|
|
134
151
|
if (!destinationStateItems.length) {
|
|
135
152
|
// Only item in state
|
|
136
153
|
// New minimum rank
|
|
137
|
-
|
|
154
|
+
if (destinationStateIndex === 0) {
|
|
155
|
+
// Only the first state should generate an absolute minimum rank
|
|
156
|
+
newOrder = LexoRank.min().toString()
|
|
157
|
+
} else {
|
|
158
|
+
// Otherwise create the next rank between min and the globally minimum rank
|
|
159
|
+
newOrder = LexoRank.parse(globalStateMinimumRank)
|
|
160
|
+
.between(LexoRank.min())
|
|
161
|
+
.toString()
|
|
162
|
+
}
|
|
138
163
|
} else if (destination.index === 0) {
|
|
139
164
|
// Now first item in order
|
|
140
165
|
const firstItemOrderRank = [...destinationStateItems].shift()?._metadata
|
|
141
166
|
?.orderRank
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
167
|
+
|
|
168
|
+
if (firstItemOrderRank && typeof firstItemOrderRank === 'string') {
|
|
169
|
+
newOrder = LexoRank.parse(firstItemOrderRank).genPrev().toString()
|
|
170
|
+
} else if (destinationStateIndex === 0) {
|
|
171
|
+
// Only the first state should generate an absolute minimum rank
|
|
172
|
+
newOrder = LexoRank.min().toString()
|
|
173
|
+
} else {
|
|
174
|
+
// Otherwise create the next rank between min and the globally minimum rank
|
|
175
|
+
newOrder = LexoRank.parse(globalStateMinimumRank)
|
|
176
|
+
.between(LexoRank.min())
|
|
177
|
+
.toString()
|
|
178
|
+
}
|
|
146
179
|
} else if (destination.index + 1 === destinationStateItems.length) {
|
|
147
180
|
// Now last item in order
|
|
148
181
|
const lastItemOrderRank = [...destinationStateItems].pop()?._metadata
|
|
149
182
|
?.orderRank
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
183
|
+
|
|
184
|
+
if (lastItemOrderRank && typeof lastItemOrderRank === 'string') {
|
|
185
|
+
newOrder = LexoRank.parse(lastItemOrderRank).genNext().toString()
|
|
186
|
+
} else if (destinationStateIndex === states.length - 1) {
|
|
187
|
+
// Only the last state should generate an absolute maximum rank
|
|
188
|
+
newOrder = LexoRank.max().toString()
|
|
189
|
+
} else {
|
|
190
|
+
// Otherwise create the next rank between max and the globally maximum rank
|
|
191
|
+
newOrder = LexoRank.parse(globalStateMaximumRank)
|
|
192
|
+
.between(LexoRank.min())
|
|
193
|
+
.toString()
|
|
194
|
+
}
|
|
154
195
|
} else {
|
|
155
196
|
// Must be between two items
|
|
156
197
|
const itemBefore = destinationStateItems[destination.index - 1]
|
|
157
198
|
const itemBeforeRank = itemBefore?._metadata?.orderRank
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
199
|
+
let itemBeforeRankParsed
|
|
200
|
+
if (itemBeforeRank) {
|
|
201
|
+
itemBeforeRankParsed = LexoRank.parse(itemBeforeRank)
|
|
202
|
+
} else if (destinationStateIndex === 0) {
|
|
203
|
+
itemBeforeRankParsed = LexoRank.min()
|
|
204
|
+
} else {
|
|
205
|
+
itemBeforeRankParsed = LexoRank.parse(globalStateMinimumRank)
|
|
206
|
+
}
|
|
207
|
+
|
|
161
208
|
const itemAfter = destinationStateItems[destination.index]
|
|
162
209
|
const itemAfterRank = itemAfter?._metadata?.orderRank
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
210
|
+
let itemAfterRankParsed
|
|
211
|
+
if (itemAfterRank) {
|
|
212
|
+
itemAfterRankParsed = LexoRank.parse(itemAfterRank)
|
|
213
|
+
} else if (destinationStateIndex === states.length - 1) {
|
|
214
|
+
itemAfterRankParsed = LexoRank.max()
|
|
215
|
+
} else {
|
|
216
|
+
itemAfterRankParsed = LexoRank.parse(globalStateMaximumRank)
|
|
217
|
+
}
|
|
166
218
|
|
|
167
219
|
newOrder = itemBeforeRankParsed.between(itemAfterRankParsed).toString()
|
|
168
220
|
}
|
|
169
221
|
|
|
170
|
-
|
|
222
|
+
setPatchingIds([...patchingIds, draggableId])
|
|
223
|
+
toast.push({
|
|
224
|
+
status: 'info',
|
|
225
|
+
title: 'Updating document state...',
|
|
226
|
+
})
|
|
227
|
+
await move(draggableId, destination, states, newOrder)
|
|
228
|
+
setPatchingIds((ids: string[]) => ids.filter((id) => id !== draggableId))
|
|
171
229
|
},
|
|
172
|
-
[data, move, states]
|
|
230
|
+
[data, patchingIds, toast, move, states]
|
|
173
231
|
)
|
|
174
232
|
|
|
175
233
|
// Used for the user filter UI
|
|
@@ -224,6 +282,41 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
224
282
|
[]
|
|
225
283
|
)
|
|
226
284
|
|
|
285
|
+
const Clone: DraggableChildrenFn = React.useCallback(
|
|
286
|
+
(provided, snapshot, rubric) => {
|
|
287
|
+
const item = data.find(
|
|
288
|
+
(doc) => doc?._metadata?.documentId === rubric.draggableId
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<div
|
|
293
|
+
{...provided.draggableProps}
|
|
294
|
+
{...provided.dragHandleProps}
|
|
295
|
+
ref={provided.innerRef}
|
|
296
|
+
>
|
|
297
|
+
{item ? (
|
|
298
|
+
<DocumentCard
|
|
299
|
+
// Assumed false, if it's dragging it's not disabled
|
|
300
|
+
isDragDisabled={false}
|
|
301
|
+
// Assumed false, if it's dragging it's not patching
|
|
302
|
+
isPatching={false}
|
|
303
|
+
// Assumed true, if you can drag it you can drop it
|
|
304
|
+
userRoleCanDrop
|
|
305
|
+
isDragging={snapshot.isDragging}
|
|
306
|
+
item={item}
|
|
307
|
+
states={states}
|
|
308
|
+
toggleInvalidDocumentId={toggleInvalidDocumentId}
|
|
309
|
+
userList={userList}
|
|
310
|
+
/>
|
|
311
|
+
) : (
|
|
312
|
+
<Feedback title="Item not found" tone="caution" />
|
|
313
|
+
)}
|
|
314
|
+
</div>
|
|
315
|
+
)
|
|
316
|
+
},
|
|
317
|
+
[data, states, toggleInvalidDocumentId, userList]
|
|
318
|
+
)
|
|
319
|
+
|
|
227
320
|
if (!states?.length) {
|
|
228
321
|
return (
|
|
229
322
|
<Container width={1} padding={5}>
|
|
@@ -297,37 +390,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
297
390
|
isDropDisabled={isDropDisabled}
|
|
298
391
|
// props required for virtualization
|
|
299
392
|
mode="virtual"
|
|
300
|
-
|
|
301
|
-
renderClone={(provided, snapshot, rubric) => {
|
|
302
|
-
const item = data.find(
|
|
303
|
-
(doc) =>
|
|
304
|
-
doc?._metadata?.documentId === rubric.draggableId
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
return (
|
|
308
|
-
<div
|
|
309
|
-
{...provided.draggableProps}
|
|
310
|
-
{...provided.dragHandleProps}
|
|
311
|
-
ref={provided.innerRef}
|
|
312
|
-
>
|
|
313
|
-
{item ? (
|
|
314
|
-
<DocumentCard
|
|
315
|
-
isDragDisabled={false}
|
|
316
|
-
userRoleCanDrop={userRoleCanDrop}
|
|
317
|
-
isDragging={snapshot.isDragging}
|
|
318
|
-
item={item}
|
|
319
|
-
states={states}
|
|
320
|
-
toggleInvalidDocumentId={
|
|
321
|
-
toggleInvalidDocumentId
|
|
322
|
-
}
|
|
323
|
-
userList={userList}
|
|
324
|
-
/>
|
|
325
|
-
) : (
|
|
326
|
-
<Feedback title="Item not found" tone="caution" />
|
|
327
|
-
)}
|
|
328
|
-
</div>
|
|
329
|
-
)
|
|
330
|
-
}}
|
|
393
|
+
renderClone={Clone}
|
|
331
394
|
>
|
|
332
395
|
{(provided, snapshot) => (
|
|
333
396
|
<Card
|
|
@@ -349,6 +412,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
349
412
|
<DocumentList
|
|
350
413
|
data={data}
|
|
351
414
|
invalidDocumentIds={invalidDocumentIds}
|
|
415
|
+
patchingIds={patchingIds}
|
|
352
416
|
selectedSchemaTypes={selectedSchemaTypes}
|
|
353
417
|
selectedUserIds={selectedUserIds}
|
|
354
418
|
state={state}
|
|
@@ -122,30 +122,30 @@ export function useWorkflowDocuments(schemaTypes: string[]): WorkflowDocuments {
|
|
|
122
122
|
const {_id, _type} = document
|
|
123
123
|
|
|
124
124
|
// Metadata + useDocumentOperation always uses Published id
|
|
125
|
-
const {documentId} = document._metadata || {}
|
|
125
|
+
const {documentId, _rev} = document._metadata || {}
|
|
126
126
|
|
|
127
127
|
await client
|
|
128
128
|
.patch(`workflow-metadata.${documentId}`)
|
|
129
|
-
|
|
130
|
-
// TODO: Prevent dragging while patching instead while keeping optimistic updates and revert when patch fails
|
|
131
|
-
// .ifRevisionId(document._metadata._rev)
|
|
129
|
+
.ifRevisionId(_rev)
|
|
132
130
|
.set({state: newStateId, orderRank: newOrder})
|
|
133
131
|
.commit()
|
|
134
|
-
.then(() => {
|
|
135
|
-
|
|
132
|
+
.then((res) => {
|
|
133
|
+
toast.push({
|
|
136
134
|
title: `Moved to "${newState?.title ?? newStateId}"`,
|
|
137
135
|
status: 'success',
|
|
138
136
|
})
|
|
137
|
+
return res
|
|
139
138
|
})
|
|
140
139
|
.catch((err) => {
|
|
141
140
|
// Revert optimistic update
|
|
142
141
|
setLocalDocuments(currentLocalData)
|
|
143
142
|
|
|
144
|
-
|
|
143
|
+
toast.push({
|
|
145
144
|
title: `Failed to move to "${newState?.title ?? newStateId}"`,
|
|
146
145
|
description: err.message,
|
|
147
146
|
status: 'error',
|
|
148
147
|
})
|
|
148
|
+
return null
|
|
149
149
|
})
|
|
150
150
|
|
|
151
151
|
// Send back to the workflow board so a document update can happen
|