sanity-plugin-workflow 1.0.6 → 2.0.1
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/README.md +2 -17
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1647 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -71
- package/lib/index.d.ts +0 -20
- package/lib/index.esm.js +0 -2135
- package/lib/index.esm.js.map +0 -1
- package/lib/index.js +0 -2147
- package/lib/index.js.map +0 -1
- package/sanity.json +0 -8
- package/src/actions/AssignWorkflow.tsx +0 -47
- package/src/actions/BeginWorkflow.tsx +0 -63
- package/src/actions/CompleteWorkflow.tsx +0 -64
- package/src/actions/UpdateWorkflow.tsx +0 -126
- package/src/badges/AssigneesBadge.tsx +0 -53
- package/src/badges/StateBadge.tsx +0 -28
- package/src/components/DocumentCard/AvatarGroup.tsx +0 -43
- package/src/components/DocumentCard/CompleteButton.tsx +0 -56
- package/src/components/DocumentCard/EditButton.tsx +0 -28
- package/src/components/DocumentCard/Field.tsx +0 -38
- package/src/components/DocumentCard/Validate.tsx +0 -21
- package/src/components/DocumentCard/ValidationStatus.tsx +0 -37
- package/src/components/DocumentCard/core/DraftStatus.tsx +0 -32
- package/src/components/DocumentCard/core/PublishedStatus.tsx +0 -39
- package/src/components/DocumentCard/core/TimeAgo.tsx +0 -11
- package/src/components/DocumentCard/index.tsx +0 -200
- package/src/components/DocumentList.tsx +0 -169
- package/src/components/Filters.tsx +0 -174
- package/src/components/FloatingCard.tsx +0 -36
- package/src/components/StateTitle/Status.tsx +0 -27
- package/src/components/StateTitle/index.tsx +0 -78
- package/src/components/UserAssignment.tsx +0 -121
- package/src/components/UserAssignmentInput.tsx +0 -27
- package/src/components/UserDisplay.tsx +0 -57
- package/src/components/Verify.tsx +0 -297
- package/src/components/WorkflowContext.tsx +0 -71
- package/src/components/WorkflowSignal.tsx +0 -30
- package/src/components/WorkflowTool.tsx +0 -437
- package/src/constants/index.ts +0 -31
- package/src/helpers/arraysContainMatchingString.ts +0 -6
- package/src/helpers/filterItemsAndSort.ts +0 -41
- package/src/helpers/generateMultipleOrderRanks.ts +0 -80
- package/src/helpers/initialRank.ts +0 -13
- package/src/hooks/useWorkflowDocuments.tsx +0 -167
- package/src/hooks/useWorkflowMetadata.tsx +0 -49
- package/src/index.ts +0 -97
- package/src/schema/workflow/workflow.metadata.ts +0 -68
- package/src/tools/index.ts +0 -15
- package/src/types/index.ts +0 -71
- package/v2-incompatible.js +0 -11
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DragDropContext,
|
|
3
|
-
DraggableChildrenFn,
|
|
4
|
-
DragStart,
|
|
5
|
-
Droppable,
|
|
6
|
-
DropResult,
|
|
7
|
-
} from '@hello-pangea/dnd'
|
|
8
|
-
import {
|
|
9
|
-
Box,
|
|
10
|
-
Card,
|
|
11
|
-
Container,
|
|
12
|
-
Flex,
|
|
13
|
-
Grid,
|
|
14
|
-
Spinner,
|
|
15
|
-
useTheme,
|
|
16
|
-
useToast,
|
|
17
|
-
} from '@sanity/ui'
|
|
18
|
-
import {LexoRank} from 'lexorank'
|
|
19
|
-
import React from 'react'
|
|
20
|
-
import {Tool, useCurrentUser} from 'sanity'
|
|
21
|
-
import {Feedback, useProjectUsers} from 'sanity-plugin-utils'
|
|
22
|
-
|
|
23
|
-
import {API_VERSION} from '../constants'
|
|
24
|
-
import {arraysContainMatchingString} from '../helpers/arraysContainMatchingString'
|
|
25
|
-
import {filterItemsAndSort} from '../helpers/filterItemsAndSort'
|
|
26
|
-
import {useWorkflowDocuments} from '../hooks/useWorkflowDocuments'
|
|
27
|
-
import {State, WorkflowConfig} from '../types'
|
|
28
|
-
import {DocumentCard} from './DocumentCard'
|
|
29
|
-
import DocumentList from './DocumentList'
|
|
30
|
-
import Filters from './Filters'
|
|
31
|
-
import StateTitle from './StateTitle'
|
|
32
|
-
import Verify from './Verify'
|
|
33
|
-
|
|
34
|
-
type WorkflowToolProps = {
|
|
35
|
-
tool: Tool<WorkflowConfig>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default function WorkflowTool(props: WorkflowToolProps) {
|
|
39
|
-
const {schemaTypes = [], states = []} = props?.tool?.options ?? {}
|
|
40
|
-
|
|
41
|
-
const isDarkMode = useTheme().sanity.color.dark
|
|
42
|
-
const defaultCardTone = isDarkMode ? 'default' : 'transparent'
|
|
43
|
-
const toast = useToast()
|
|
44
|
-
|
|
45
|
-
const userList = useProjectUsers({apiVersion: API_VERSION})
|
|
46
|
-
|
|
47
|
-
const user = useCurrentUser()
|
|
48
|
-
const userRoleNames = user?.roles?.length
|
|
49
|
-
? user?.roles.map((r) => r.name)
|
|
50
|
-
: []
|
|
51
|
-
|
|
52
|
-
const {workflowData, operations} = useWorkflowDocuments(schemaTypes)
|
|
53
|
-
const [patchingIds, setPatchingIds] = React.useState<string[]>([])
|
|
54
|
-
|
|
55
|
-
// Data to display in cards
|
|
56
|
-
const {data, loading, error} = workflowData
|
|
57
|
-
|
|
58
|
-
// Operations to perform on cards
|
|
59
|
-
const {move} = operations
|
|
60
|
-
|
|
61
|
-
const [undroppableStates, setUndroppableStates] = React.useState<string[]>([])
|
|
62
|
-
const [draggingFrom, setDraggingFrom] = React.useState(``)
|
|
63
|
-
|
|
64
|
-
// When drag starts, check for any States we should not allow dropping on
|
|
65
|
-
// Because of either:
|
|
66
|
-
// 1. The "destination" State requires user assignment and the user is not assigned to the dragged document
|
|
67
|
-
// 2. The "source" State State has a list of transitions and the "destination" State is not in that list
|
|
68
|
-
const handleDragStart = React.useCallback(
|
|
69
|
-
(start: DragStart) => {
|
|
70
|
-
const {draggableId, source} = start
|
|
71
|
-
const {droppableId: currentStateId} = source
|
|
72
|
-
setDraggingFrom(currentStateId)
|
|
73
|
-
|
|
74
|
-
const document = data.find(
|
|
75
|
-
(item) => item._metadata?.documentId === draggableId
|
|
76
|
-
)
|
|
77
|
-
const state = states.find((s) => s.id === currentStateId)
|
|
78
|
-
|
|
79
|
-
// This shouldn't happen but TypeScript
|
|
80
|
-
if (!document || !state) return
|
|
81
|
-
|
|
82
|
-
const undroppableStateIds = []
|
|
83
|
-
const statesThatRequireAssignmentIds = states
|
|
84
|
-
.filter((s) => s.requireAssignment)
|
|
85
|
-
.map((s) => s.id)
|
|
86
|
-
|
|
87
|
-
if (statesThatRequireAssignmentIds.length) {
|
|
88
|
-
const documentAssignees = document._metadata?.assignees ?? []
|
|
89
|
-
const userIsAssignedToDocument = user?.id
|
|
90
|
-
? documentAssignees.includes(user.id)
|
|
91
|
-
: false
|
|
92
|
-
|
|
93
|
-
if (!userIsAssignedToDocument) {
|
|
94
|
-
undroppableStateIds.push(...statesThatRequireAssignmentIds)
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const statesThatCannotBeTransitionedToIds =
|
|
99
|
-
state.transitions && state.transitions.length
|
|
100
|
-
? states
|
|
101
|
-
.filter((s) => !state.transitions?.includes(s.id))
|
|
102
|
-
.map((s) => s.id)
|
|
103
|
-
: []
|
|
104
|
-
|
|
105
|
-
if (statesThatCannotBeTransitionedToIds.length) {
|
|
106
|
-
undroppableStateIds.push(...statesThatCannotBeTransitionedToIds)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Remove currentStateId from undroppableStates
|
|
110
|
-
const undroppableExceptSelf = undroppableStateIds.filter(
|
|
111
|
-
(id) => id !== currentStateId
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
if (undroppableExceptSelf.length) {
|
|
115
|
-
setUndroppableStates(undroppableExceptSelf)
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
[data, states, user]
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
const handleDragEnd = React.useCallback(
|
|
122
|
-
async (result: DropResult) => {
|
|
123
|
-
// Reset undroppable states
|
|
124
|
-
setUndroppableStates([])
|
|
125
|
-
setDraggingFrom(``)
|
|
126
|
-
|
|
127
|
-
const {draggableId, source, destination} = result
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
// No destination?
|
|
131
|
-
!destination ||
|
|
132
|
-
// No change in position?
|
|
133
|
-
(destination.droppableId === source.droppableId &&
|
|
134
|
-
destination.index === source.index)
|
|
135
|
-
) {
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Find all items in current state
|
|
140
|
-
const destinationStateItems = [
|
|
141
|
-
...filterItemsAndSort(data, destination.droppableId, [], null),
|
|
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
|
|
148
|
-
|
|
149
|
-
let newOrder
|
|
150
|
-
|
|
151
|
-
if (!destinationStateItems.length) {
|
|
152
|
-
// Only item in state
|
|
153
|
-
// New minimum rank
|
|
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 one rank above the minimum
|
|
159
|
-
newOrder = LexoRank.min().genNext().toString()
|
|
160
|
-
}
|
|
161
|
-
} else if (destination.index === 0) {
|
|
162
|
-
// Now first item in order
|
|
163
|
-
const firstItemOrderRank = [...destinationStateItems].shift()?._metadata
|
|
164
|
-
?.orderRank
|
|
165
|
-
|
|
166
|
-
if (firstItemOrderRank && typeof firstItemOrderRank === 'string') {
|
|
167
|
-
newOrder = LexoRank.parse(firstItemOrderRank).genPrev().toString()
|
|
168
|
-
} else if (destinationStateIndex === 0) {
|
|
169
|
-
// Only the first state should generate an absolute minimum rank
|
|
170
|
-
newOrder = LexoRank.min().toString()
|
|
171
|
-
} else {
|
|
172
|
-
// Otherwise create the next rank between min and the globally minimum rank
|
|
173
|
-
newOrder = LexoRank.parse(globalStateMinimumRank)
|
|
174
|
-
.between(LexoRank.min())
|
|
175
|
-
.toString()
|
|
176
|
-
}
|
|
177
|
-
} else if (destination.index + 1 === destinationStateItems.length) {
|
|
178
|
-
// Now last item in order
|
|
179
|
-
const lastItemOrderRank = [...destinationStateItems].pop()?._metadata
|
|
180
|
-
?.orderRank
|
|
181
|
-
|
|
182
|
-
if (lastItemOrderRank && typeof lastItemOrderRank === 'string') {
|
|
183
|
-
newOrder = LexoRank.parse(lastItemOrderRank).genNext().toString()
|
|
184
|
-
} else if (destinationStateIndex === states.length - 1) {
|
|
185
|
-
// Only the last state should generate an absolute maximum rank
|
|
186
|
-
newOrder = LexoRank.max().toString()
|
|
187
|
-
} else {
|
|
188
|
-
// Otherwise create the next rank between max and the globally maximum rank
|
|
189
|
-
newOrder = LexoRank.parse(globalStateMaximumRank)
|
|
190
|
-
.between(LexoRank.min())
|
|
191
|
-
.toString()
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
// Must be between two items
|
|
195
|
-
const itemBefore = destinationStateItems[destination.index - 1]
|
|
196
|
-
const itemBeforeRank = itemBefore?._metadata?.orderRank
|
|
197
|
-
let itemBeforeRankParsed
|
|
198
|
-
if (itemBeforeRank) {
|
|
199
|
-
itemBeforeRankParsed = LexoRank.parse(itemBeforeRank)
|
|
200
|
-
} else if (destinationStateIndex === 0) {
|
|
201
|
-
itemBeforeRankParsed = LexoRank.min()
|
|
202
|
-
} else {
|
|
203
|
-
itemBeforeRankParsed = LexoRank.parse(globalStateMinimumRank)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const itemAfter = destinationStateItems[destination.index]
|
|
207
|
-
const itemAfterRank = itemAfter?._metadata?.orderRank
|
|
208
|
-
let itemAfterRankParsed
|
|
209
|
-
if (itemAfterRank) {
|
|
210
|
-
itemAfterRankParsed = LexoRank.parse(itemAfterRank)
|
|
211
|
-
} else if (destinationStateIndex === states.length - 1) {
|
|
212
|
-
itemAfterRankParsed = LexoRank.max()
|
|
213
|
-
} else {
|
|
214
|
-
itemAfterRankParsed = LexoRank.parse(globalStateMaximumRank)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
newOrder = itemBeforeRankParsed.between(itemAfterRankParsed).toString()
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
setPatchingIds([...patchingIds, draggableId])
|
|
221
|
-
toast.push({
|
|
222
|
-
status: 'info',
|
|
223
|
-
title: 'Updating document state...',
|
|
224
|
-
})
|
|
225
|
-
await move(draggableId, destination, states, newOrder)
|
|
226
|
-
setPatchingIds((ids: string[]) => ids.filter((id) => id !== draggableId))
|
|
227
|
-
},
|
|
228
|
-
[data, patchingIds, toast, move, states]
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
// Used for the user filter UI
|
|
232
|
-
const uniqueAssignedUsers = React.useMemo(() => {
|
|
233
|
-
const uniqueUserIds = data.reduce((acc, item) => {
|
|
234
|
-
const {assignees = []} = item._metadata ?? {}
|
|
235
|
-
const newAssignees = assignees?.length
|
|
236
|
-
? assignees.filter((a) => !acc.includes(a))
|
|
237
|
-
: []
|
|
238
|
-
return newAssignees.length ? [...acc, ...newAssignees] : acc
|
|
239
|
-
}, [] as string[])
|
|
240
|
-
|
|
241
|
-
return userList.filter((u) => uniqueUserIds.includes(u.id))
|
|
242
|
-
}, [data, userList])
|
|
243
|
-
|
|
244
|
-
// Selected user IDs filter the visible workflow documents
|
|
245
|
-
const [selectedUserIds, setSelectedUserIds] = React.useState<string[]>(
|
|
246
|
-
uniqueAssignedUsers.map((u) => u.id)
|
|
247
|
-
)
|
|
248
|
-
const toggleSelectedUser = React.useCallback((userId: string) => {
|
|
249
|
-
setSelectedUserIds((prev) =>
|
|
250
|
-
prev.includes(userId)
|
|
251
|
-
? prev.filter((u) => u !== userId)
|
|
252
|
-
: [...prev, userId]
|
|
253
|
-
)
|
|
254
|
-
}, [])
|
|
255
|
-
const resetSelectedUsers = React.useCallback(() => {
|
|
256
|
-
setSelectedUserIds([])
|
|
257
|
-
}, [])
|
|
258
|
-
|
|
259
|
-
// Selected schema types filter the visible workflow documents
|
|
260
|
-
const [selectedSchemaTypes, setSelectedSchemaTypes] =
|
|
261
|
-
React.useState<string[]>(schemaTypes)
|
|
262
|
-
const toggleSelectedSchemaType = React.useCallback((schemaType: string) => {
|
|
263
|
-
setSelectedSchemaTypes((prev) =>
|
|
264
|
-
prev.includes(schemaType)
|
|
265
|
-
? prev.filter((u) => u !== schemaType)
|
|
266
|
-
: [...prev, schemaType]
|
|
267
|
-
)
|
|
268
|
-
}, [])
|
|
269
|
-
|
|
270
|
-
// Document IDs that have validation errors
|
|
271
|
-
const [invalidDocumentIds, setInvalidDocumentIds] = React.useState<string[]>(
|
|
272
|
-
[]
|
|
273
|
-
)
|
|
274
|
-
const toggleInvalidDocumentId = React.useCallback(
|
|
275
|
-
(docId: string, action: 'ADD' | 'REMOVE') => {
|
|
276
|
-
setInvalidDocumentIds((prev) =>
|
|
277
|
-
action === 'ADD' ? [...prev, docId] : prev.filter((id) => id !== docId)
|
|
278
|
-
)
|
|
279
|
-
},
|
|
280
|
-
[]
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
const Clone: DraggableChildrenFn = React.useCallback(
|
|
284
|
-
(provided, snapshot, rubric) => {
|
|
285
|
-
const item = data.find(
|
|
286
|
-
(doc) => doc?._metadata?.documentId === rubric.draggableId
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
return (
|
|
290
|
-
<div
|
|
291
|
-
{...provided.draggableProps}
|
|
292
|
-
{...provided.dragHandleProps}
|
|
293
|
-
ref={provided.innerRef}
|
|
294
|
-
>
|
|
295
|
-
{item ? (
|
|
296
|
-
<DocumentCard
|
|
297
|
-
// Assumed false, if it's dragging it's not disabled
|
|
298
|
-
isDragDisabled={false}
|
|
299
|
-
// Assumed false, if it's dragging it's not patching
|
|
300
|
-
isPatching={false}
|
|
301
|
-
// Assumed true, if you can drag it you can drop it
|
|
302
|
-
userRoleCanDrop
|
|
303
|
-
isDragging={snapshot.isDragging}
|
|
304
|
-
item={item}
|
|
305
|
-
states={states}
|
|
306
|
-
toggleInvalidDocumentId={toggleInvalidDocumentId}
|
|
307
|
-
userList={userList}
|
|
308
|
-
/>
|
|
309
|
-
) : (
|
|
310
|
-
<Feedback title="Item not found" tone="caution" />
|
|
311
|
-
)}
|
|
312
|
-
</div>
|
|
313
|
-
)
|
|
314
|
-
},
|
|
315
|
-
[data, states, toggleInvalidDocumentId, userList]
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
if (!states?.length) {
|
|
319
|
-
return (
|
|
320
|
-
<Container width={1} padding={5}>
|
|
321
|
-
<Feedback
|
|
322
|
-
tone="caution"
|
|
323
|
-
title="Plugin options error"
|
|
324
|
-
description="No States defined in plugin config"
|
|
325
|
-
/>
|
|
326
|
-
</Container>
|
|
327
|
-
)
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (error && !data.length) {
|
|
331
|
-
return (
|
|
332
|
-
<Container width={1} padding={5}>
|
|
333
|
-
<Feedback
|
|
334
|
-
tone="critical"
|
|
335
|
-
title="Error querying for Workflow documents"
|
|
336
|
-
/>
|
|
337
|
-
</Container>
|
|
338
|
-
)
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return (
|
|
342
|
-
<Flex direction="column" height="fill" overflow="hidden">
|
|
343
|
-
<Verify data={data} userList={userList} states={states} />
|
|
344
|
-
|
|
345
|
-
<Filters
|
|
346
|
-
uniqueAssignedUsers={uniqueAssignedUsers}
|
|
347
|
-
selectedUserIds={selectedUserIds}
|
|
348
|
-
toggleSelectedUser={toggleSelectedUser}
|
|
349
|
-
resetSelectedUsers={resetSelectedUsers}
|
|
350
|
-
schemaTypes={schemaTypes}
|
|
351
|
-
selectedSchemaTypes={selectedSchemaTypes}
|
|
352
|
-
toggleSelectedSchemaType={toggleSelectedSchemaType}
|
|
353
|
-
/>
|
|
354
|
-
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
|
355
|
-
<Grid columns={states.length} height="fill">
|
|
356
|
-
{states.map((state: State, stateIndex: number) => {
|
|
357
|
-
const userRoleCanDrop = state?.roles?.length
|
|
358
|
-
? arraysContainMatchingString(state.roles, userRoleNames)
|
|
359
|
-
: true
|
|
360
|
-
const isDropDisabled =
|
|
361
|
-
!userRoleCanDrop || undroppableStates.includes(state.id)
|
|
362
|
-
|
|
363
|
-
return (
|
|
364
|
-
<Card
|
|
365
|
-
key={state.id}
|
|
366
|
-
borderLeft={stateIndex > 0}
|
|
367
|
-
tone={defaultCardTone}
|
|
368
|
-
>
|
|
369
|
-
<Flex direction="column" height="fill">
|
|
370
|
-
<StateTitle
|
|
371
|
-
state={state}
|
|
372
|
-
requireAssignment={state.requireAssignment ?? false}
|
|
373
|
-
userRoleCanDrop={userRoleCanDrop}
|
|
374
|
-
isDropDisabled={isDropDisabled}
|
|
375
|
-
draggingFrom={draggingFrom}
|
|
376
|
-
documentCount={
|
|
377
|
-
filterItemsAndSort(
|
|
378
|
-
data,
|
|
379
|
-
state.id,
|
|
380
|
-
selectedUserIds,
|
|
381
|
-
selectedSchemaTypes
|
|
382
|
-
).length
|
|
383
|
-
}
|
|
384
|
-
/>
|
|
385
|
-
<Box flex={1}>
|
|
386
|
-
<Droppable
|
|
387
|
-
droppableId={state.id}
|
|
388
|
-
isDropDisabled={isDropDisabled}
|
|
389
|
-
// props required for virtualization
|
|
390
|
-
mode="virtual"
|
|
391
|
-
renderClone={Clone}
|
|
392
|
-
>
|
|
393
|
-
{(provided, snapshot) => (
|
|
394
|
-
<Card
|
|
395
|
-
ref={provided.innerRef}
|
|
396
|
-
tone={
|
|
397
|
-
snapshot.isDraggingOver
|
|
398
|
-
? `primary`
|
|
399
|
-
: defaultCardTone
|
|
400
|
-
}
|
|
401
|
-
height="fill"
|
|
402
|
-
>
|
|
403
|
-
{loading ? (
|
|
404
|
-
<Flex padding={5} align="center" justify="center">
|
|
405
|
-
<Spinner muted />
|
|
406
|
-
</Flex>
|
|
407
|
-
) : null}
|
|
408
|
-
|
|
409
|
-
<DocumentList
|
|
410
|
-
data={data}
|
|
411
|
-
invalidDocumentIds={invalidDocumentIds}
|
|
412
|
-
patchingIds={patchingIds}
|
|
413
|
-
selectedSchemaTypes={selectedSchemaTypes}
|
|
414
|
-
selectedUserIds={selectedUserIds}
|
|
415
|
-
state={state}
|
|
416
|
-
states={states}
|
|
417
|
-
toggleInvalidDocumentId={toggleInvalidDocumentId}
|
|
418
|
-
user={user}
|
|
419
|
-
userList={userList}
|
|
420
|
-
userRoleCanDrop={userRoleCanDrop}
|
|
421
|
-
/>
|
|
422
|
-
|
|
423
|
-
{/* Not required for virtualized lists */}
|
|
424
|
-
{/* {provided.placeholder} */}
|
|
425
|
-
</Card>
|
|
426
|
-
)}
|
|
427
|
-
</Droppable>
|
|
428
|
-
</Box>
|
|
429
|
-
</Flex>
|
|
430
|
-
</Card>
|
|
431
|
-
)
|
|
432
|
-
})}
|
|
433
|
-
</Grid>
|
|
434
|
-
</DragDropContext>
|
|
435
|
-
</Flex>
|
|
436
|
-
)
|
|
437
|
-
}
|
package/src/constants/index.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import {defineStates, WorkflowConfig} from '../types'
|
|
2
|
-
|
|
3
|
-
export const API_VERSION = `2023-01-01`
|
|
4
|
-
|
|
5
|
-
export const DEFAULT_CONFIG: Required<WorkflowConfig> = {
|
|
6
|
-
schemaTypes: [],
|
|
7
|
-
states: defineStates([
|
|
8
|
-
{
|
|
9
|
-
id: 'inReview',
|
|
10
|
-
title: 'In review',
|
|
11
|
-
color: 'primary',
|
|
12
|
-
roles: ['editor', 'administrator'],
|
|
13
|
-
transitions: ['changesRequested', 'approved'],
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
id: 'changesRequested',
|
|
17
|
-
title: 'Changes requested',
|
|
18
|
-
color: 'warning',
|
|
19
|
-
roles: ['editor', 'administrator'],
|
|
20
|
-
transitions: ['approved'],
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
id: 'approved',
|
|
24
|
-
title: 'Approved',
|
|
25
|
-
color: 'success',
|
|
26
|
-
roles: ['administrator'],
|
|
27
|
-
transitions: ['changesRequested'],
|
|
28
|
-
requireAssignment: true,
|
|
29
|
-
},
|
|
30
|
-
]),
|
|
31
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import {SanityDocumentWithMetadata} from '../types'
|
|
2
|
-
|
|
3
|
-
export function filterItemsAndSort(
|
|
4
|
-
items: SanityDocumentWithMetadata[],
|
|
5
|
-
stateId: string,
|
|
6
|
-
selectedUsers: string[] = [],
|
|
7
|
-
selectedSchemaTypes: null | string[] = []
|
|
8
|
-
): SanityDocumentWithMetadata[] {
|
|
9
|
-
return (
|
|
10
|
-
items
|
|
11
|
-
// Only items that have existing documents
|
|
12
|
-
.filter((item) => item?._id)
|
|
13
|
-
// Only items of this state
|
|
14
|
-
.filter((item) => item?._metadata?.state === stateId)
|
|
15
|
-
// Only items with selected users, if the document has any assigned users
|
|
16
|
-
.filter((item) =>
|
|
17
|
-
selectedUsers.length && item._metadata?.assignees?.length
|
|
18
|
-
? item._metadata?.assignees.some((assignee) =>
|
|
19
|
-
selectedUsers.includes(assignee)
|
|
20
|
-
)
|
|
21
|
-
: !selectedUsers.length
|
|
22
|
-
)
|
|
23
|
-
// Only items of selected schema types, if any are selected
|
|
24
|
-
.filter((item) => {
|
|
25
|
-
if (!selectedSchemaTypes) {
|
|
26
|
-
return true
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return selectedSchemaTypes.length
|
|
30
|
-
? selectedSchemaTypes.includes(item._type)
|
|
31
|
-
: false
|
|
32
|
-
})
|
|
33
|
-
// Sort by metadata orderRank, a string field
|
|
34
|
-
.sort((a, b) => {
|
|
35
|
-
const aOrderRank = a._metadata?.orderRank || '0'
|
|
36
|
-
const bOrderRank = b._metadata?.orderRank || '0'
|
|
37
|
-
|
|
38
|
-
return aOrderRank.localeCompare(bOrderRank)
|
|
39
|
-
})
|
|
40
|
-
)
|
|
41
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import {LexoRank} from 'lexorank'
|
|
2
|
-
|
|
3
|
-
function generateMiddleValue(ranks: (LexoRank | undefined)[]) {
|
|
4
|
-
// Has no undefined values
|
|
5
|
-
if (!ranks.some((rank) => !rank)) {
|
|
6
|
-
return ranks
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// Find the first undefined value
|
|
10
|
-
const firstUndefined = ranks.findIndex((rank) => !rank)
|
|
11
|
-
|
|
12
|
-
// Find the first defined value after the undefined value
|
|
13
|
-
const firstDefinedAfter = ranks.findIndex(
|
|
14
|
-
(rank, index) => rank && index > firstUndefined
|
|
15
|
-
)
|
|
16
|
-
// Find the first defined value before the undefined value
|
|
17
|
-
const firstDefinedBefore = ranks.findLastIndex(
|
|
18
|
-
(rank, index) => rank && index < firstUndefined
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
if (firstDefinedAfter === -1 || firstDefinedBefore === -1) {
|
|
22
|
-
throw new Error(
|
|
23
|
-
`Unable to generate middle value between indexes ${firstDefinedBefore} and ${firstDefinedAfter}`
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const beforeRank = ranks[firstDefinedBefore]
|
|
28
|
-
const afterRank = ranks[firstDefinedAfter]
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
!beforeRank ||
|
|
32
|
-
typeof beforeRank === 'undefined' ||
|
|
33
|
-
!afterRank ||
|
|
34
|
-
typeof afterRank === 'undefined'
|
|
35
|
-
) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
`Unable to generate middle value between indexes ${firstDefinedBefore} and ${firstDefinedAfter}`
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Generate a new value between the two
|
|
42
|
-
const between = beforeRank.between(afterRank)
|
|
43
|
-
|
|
44
|
-
// Calculate the middle index between the defined values
|
|
45
|
-
const middle = Math.floor((firstDefinedAfter + firstDefinedBefore) / 2)
|
|
46
|
-
|
|
47
|
-
if (ranks[middle]) {
|
|
48
|
-
throw new Error(`Should not have overwritten value at index ${middle}`)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Insert the new value into the array
|
|
52
|
-
ranks[middle] = between
|
|
53
|
-
|
|
54
|
-
// Return as a new array
|
|
55
|
-
return ranks
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Generates an array of LexoRanks between two values
|
|
59
|
-
export function generateMultipleOrderRanks(
|
|
60
|
-
count: number,
|
|
61
|
-
start?: LexoRank,
|
|
62
|
-
end?: LexoRank
|
|
63
|
-
): LexoRank[] {
|
|
64
|
-
// Begin array with correct size
|
|
65
|
-
let ranks = [...Array(count)]
|
|
66
|
-
|
|
67
|
-
// Use or create default values
|
|
68
|
-
const rankStart = start ?? LexoRank.min().genNext().genNext()
|
|
69
|
-
const rankEnd = end ?? LexoRank.max().genPrev().genPrev()
|
|
70
|
-
|
|
71
|
-
ranks[0] = rankStart
|
|
72
|
-
ranks[count - 1] = rankEnd
|
|
73
|
-
|
|
74
|
-
// Keep processing the array until every value between undefined values is defined
|
|
75
|
-
for (let i = 0; i < count; i++) {
|
|
76
|
-
ranks = generateMiddleValue(ranks)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return ranks.sort((a, b) => a.toString().localeCompare(b.toString()))
|
|
80
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import {LexoRank} from 'lexorank'
|
|
2
|
-
|
|
3
|
-
// Use in initial value field by passing in the rank value of the last document
|
|
4
|
-
// If not value passed, generate a sensibly low rank
|
|
5
|
-
export default function initialRank(lastRankValue = ``): string {
|
|
6
|
-
const lastRank =
|
|
7
|
-
lastRankValue && typeof lastRankValue === 'string'
|
|
8
|
-
? LexoRank.parse(lastRankValue)
|
|
9
|
-
: LexoRank.min()
|
|
10
|
-
const nextRank = lastRank.genNext().genNext()
|
|
11
|
-
|
|
12
|
-
return (nextRank as any).value
|
|
13
|
-
}
|