sanity-plugin-workflow 1.0.0-beta.3 → 1.0.0-beta.5
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 +6 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.esm.js +1536 -1393
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1535 -1392
- package/lib/index.js.map +1 -1
- package/package.json +4 -3
- package/src/actions/UpdateWorkflow.tsx +22 -24
- package/src/components/DocumentCard/Validate.tsx +21 -0
- package/src/components/DocumentCard/core/PublishedStatus.tsx +10 -3
- package/src/components/DocumentCard/index.tsx +94 -67
- package/src/components/DocumentList.tsx +122 -0
- package/src/components/Filters.tsx +3 -3
- package/src/components/{Validators.tsx → Verify.tsx} +42 -5
- package/src/components/WorkflowTool.tsx +107 -107
- package/src/constants/index.ts +2 -2
- package/src/helpers/filterItemsAndSort.ts +2 -0
- package/src/hooks/useWorkflowDocuments.tsx +6 -6
- package/src/index.ts +5 -5
- package/src/schema/workflow/workflow.metadata.ts +1 -1
- package/src/tools/index.ts +1 -1
- package/src/types/index.ts +1 -0
- package/src/actions/RequestReviewAction.js +0 -55
- package/src/actions/index.js +0 -21
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {Flex, Card, Grid, Spinner, Container, useTheme} from '@sanity/ui'
|
|
3
|
-
import {Feedback, useProjectUsers} from 'sanity-plugin-utils'
|
|
4
|
-
import {Tool, useCurrentUser} from 'sanity'
|
|
5
1
|
import {
|
|
6
2
|
DragDropContext,
|
|
3
|
+
DragStart,
|
|
7
4
|
Droppable,
|
|
8
|
-
Draggable,
|
|
9
5
|
DropResult,
|
|
10
|
-
|
|
11
|
-
} from '
|
|
6
|
+
} from '@hello-pangea/dnd'
|
|
7
|
+
import {Box, Card, Container, Flex, Grid, Spinner, useTheme} from '@sanity/ui'
|
|
8
|
+
import {LexoRank} from 'lexorank'
|
|
9
|
+
import React from 'react'
|
|
10
|
+
import {Tool, useCurrentUser} from 'sanity'
|
|
11
|
+
import {Feedback, useProjectUsers} from 'sanity-plugin-utils'
|
|
12
12
|
|
|
13
|
+
import {API_VERSION} from '../constants'
|
|
14
|
+
import {arraysContainMatchingString} from '../helpers/arraysContainMatchingString'
|
|
15
|
+
import {filterItemsAndSort} from '../helpers/filterItemsAndSort'
|
|
16
|
+
import {useWorkflowDocuments} from '../hooks/useWorkflowDocuments'
|
|
13
17
|
import {State, WorkflowConfig} from '../types'
|
|
14
18
|
import {DocumentCard} from './DocumentCard'
|
|
15
|
-
import
|
|
16
|
-
import {API_VERSION} from '../constants'
|
|
17
|
-
|
|
18
|
-
import Validators from './Validators'
|
|
19
|
+
import DocumentList from './DocumentList'
|
|
19
20
|
import Filters from './Filters'
|
|
20
|
-
import {filterItemsAndSort} from '../helpers/filterItemsAndSort'
|
|
21
|
-
import {arraysContainMatchingString} from '../helpers/arraysContainMatchingString'
|
|
22
21
|
import StateTitle from './StateTitle'
|
|
23
|
-
import
|
|
22
|
+
import Verify from './Verify'
|
|
24
23
|
|
|
25
24
|
type WorkflowToolProps = {
|
|
26
25
|
tool: Tool<WorkflowConfig>
|
|
@@ -154,14 +153,14 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
154
153
|
: LexoRank.min().toString()
|
|
155
154
|
} else {
|
|
156
155
|
// Must be between two items
|
|
157
|
-
const itemBefore = destinationStateItems[destination.index]
|
|
156
|
+
const itemBefore = destinationStateItems[destination.index - 1]
|
|
158
157
|
const itemBeforeRank = itemBefore?._metadata?.orderRank
|
|
159
|
-
const itemBeforeRankParsed =
|
|
158
|
+
const itemBeforeRankParsed = itemBeforeRank
|
|
160
159
|
? LexoRank.parse(itemBeforeRank)
|
|
161
160
|
: LexoRank.min()
|
|
162
|
-
const itemAfter = destinationStateItems[destination.index
|
|
161
|
+
const itemAfter = destinationStateItems[destination.index]
|
|
163
162
|
const itemAfterRank = itemAfter?._metadata?.orderRank
|
|
164
|
-
const itemAfterRankParsed =
|
|
163
|
+
const itemAfterRankParsed = itemAfterRank
|
|
165
164
|
? LexoRank.parse(itemAfterRank)
|
|
166
165
|
: LexoRank.max()
|
|
167
166
|
|
|
@@ -173,6 +172,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
173
172
|
[data, move, states]
|
|
174
173
|
)
|
|
175
174
|
|
|
175
|
+
// Used for the user filter UI
|
|
176
176
|
const uniqueAssignedUsers = React.useMemo(() => {
|
|
177
177
|
const uniqueUserIds = data.reduce((acc, item) => {
|
|
178
178
|
const {assignees = []} = item._metadata ?? {}
|
|
@@ -185,6 +185,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
185
185
|
return userList.filter((u) => uniqueUserIds.includes(u.id))
|
|
186
186
|
}, [data, userList])
|
|
187
187
|
|
|
188
|
+
// Selected user IDs filter the visible workflow documents
|
|
188
189
|
const [selectedUserIds, setSelectedUserIds] = React.useState<string[]>(
|
|
189
190
|
uniqueAssignedUsers.map((u) => u.id)
|
|
190
191
|
)
|
|
@@ -199,6 +200,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
199
200
|
setSelectedUserIds([])
|
|
200
201
|
}, [])
|
|
201
202
|
|
|
203
|
+
// Selected schema types filter the visible workflow documents
|
|
202
204
|
const [selectedSchemaTypes, setSelectedSchemaTypes] =
|
|
203
205
|
React.useState<string[]>(schemaTypes)
|
|
204
206
|
const toggleSelectedSchemaType = React.useCallback((schemaType: string) => {
|
|
@@ -209,6 +211,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
209
211
|
)
|
|
210
212
|
}, [])
|
|
211
213
|
|
|
214
|
+
// Document IDs that have validation errors
|
|
212
215
|
const [invalidDocumentIds, setInvalidDocumentIds] = React.useState<string[]>(
|
|
213
216
|
[]
|
|
214
217
|
)
|
|
@@ -245,8 +248,9 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
245
248
|
}
|
|
246
249
|
|
|
247
250
|
return (
|
|
248
|
-
<
|
|
249
|
-
<
|
|
251
|
+
<Flex direction="column" height="fill" overflow="hidden">
|
|
252
|
+
<Verify data={data} userList={userList} states={states} />
|
|
253
|
+
|
|
250
254
|
<Filters
|
|
251
255
|
uniqueAssignedUsers={uniqueAssignedUsers}
|
|
252
256
|
selectedUserIds={selectedUserIds}
|
|
@@ -270,100 +274,96 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
270
274
|
key={state.id}
|
|
271
275
|
borderLeft={stateIndex > 0}
|
|
272
276
|
tone={defaultCardTone}
|
|
273
|
-
height="fill"
|
|
274
|
-
overflow="auto"
|
|
275
277
|
>
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
278
|
+
<Flex direction="column" height="fill">
|
|
279
|
+
<StateTitle
|
|
280
|
+
state={state}
|
|
281
|
+
requireAssignment={state.requireAssignment ?? false}
|
|
282
|
+
userRoleCanDrop={userRoleCanDrop}
|
|
283
|
+
// operation={state.operation}
|
|
284
|
+
isDropDisabled={isDropDisabled}
|
|
285
|
+
draggingFrom={draggingFrom}
|
|
286
|
+
/>
|
|
287
|
+
<Box flex={1}>
|
|
288
|
+
<Droppable
|
|
289
|
+
droppableId={state.id}
|
|
290
|
+
isDropDisabled={isDropDisabled}
|
|
291
|
+
// props required for virtualization
|
|
292
|
+
mode="virtual"
|
|
293
|
+
// TODO: Render this as a memo/callback
|
|
294
|
+
renderClone={(provided, snapshot, rubric) => {
|
|
295
|
+
const item = data.find(
|
|
296
|
+
(doc) =>
|
|
297
|
+
doc?._metadata?.documentId === rubric.draggableId
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<div
|
|
302
|
+
{...provided.draggableProps}
|
|
303
|
+
{...provided.dragHandleProps}
|
|
304
|
+
ref={provided.innerRef}
|
|
305
|
+
>
|
|
306
|
+
{item ? (
|
|
307
|
+
<DocumentCard
|
|
308
|
+
isDragDisabled={false}
|
|
309
|
+
userRoleCanDrop={userRoleCanDrop}
|
|
310
|
+
isDragging={snapshot.isDragging}
|
|
311
|
+
item={item}
|
|
312
|
+
states={states}
|
|
313
|
+
toggleInvalidDocumentId={
|
|
314
|
+
toggleInvalidDocumentId
|
|
315
|
+
}
|
|
316
|
+
userList={userList}
|
|
317
|
+
/>
|
|
318
|
+
) : (
|
|
319
|
+
<Feedback title="Item not found" tone="caution" />
|
|
320
|
+
)}
|
|
321
|
+
</div>
|
|
322
|
+
)
|
|
323
|
+
}}
|
|
296
324
|
>
|
|
297
|
-
{
|
|
298
|
-
<
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
filterItemsAndSort(
|
|
305
|
-
data,
|
|
306
|
-
state.id,
|
|
307
|
-
selectedUserIds,
|
|
308
|
-
selectedSchemaTypes
|
|
309
|
-
).map((item, itemIndex) => {
|
|
310
|
-
const isInvalid = invalidDocumentIds.includes(
|
|
311
|
-
String(item?._metadata?.documentId)
|
|
312
|
-
)
|
|
313
|
-
const meInAssignees = user?.id
|
|
314
|
-
? item?._metadata?.assignees?.includes(user.id)
|
|
315
|
-
: false
|
|
316
|
-
const isDragDisabled =
|
|
317
|
-
!userRoleCanDrop ||
|
|
318
|
-
isInvalid ||
|
|
319
|
-
!(state.requireAssignment
|
|
320
|
-
? state.requireAssignment && meInAssignees
|
|
321
|
-
: true)
|
|
322
|
-
const {documentId} = item._metadata ?? {}
|
|
323
|
-
|
|
324
|
-
if (!documentId) {
|
|
325
|
-
return null
|
|
325
|
+
{(provided, snapshot) => (
|
|
326
|
+
<Card
|
|
327
|
+
ref={provided.innerRef}
|
|
328
|
+
tone={
|
|
329
|
+
snapshot.isDraggingOver
|
|
330
|
+
? `primary`
|
|
331
|
+
: defaultCardTone
|
|
326
332
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
)
|
|
357
|
-
})}
|
|
358
|
-
{provided.placeholder}
|
|
359
|
-
</Card>
|
|
360
|
-
)}
|
|
361
|
-
</Droppable>
|
|
333
|
+
height="fill"
|
|
334
|
+
paddingTop={1}
|
|
335
|
+
>
|
|
336
|
+
{loading ? (
|
|
337
|
+
<Flex padding={5} align="center" justify="center">
|
|
338
|
+
<Spinner muted />
|
|
339
|
+
</Flex>
|
|
340
|
+
) : null}
|
|
341
|
+
|
|
342
|
+
<DocumentList
|
|
343
|
+
data={data}
|
|
344
|
+
invalidDocumentIds={invalidDocumentIds}
|
|
345
|
+
selectedSchemaTypes={selectedSchemaTypes}
|
|
346
|
+
selectedUserIds={selectedUserIds}
|
|
347
|
+
state={state}
|
|
348
|
+
states={states}
|
|
349
|
+
toggleInvalidDocumentId={toggleInvalidDocumentId}
|
|
350
|
+
user={user}
|
|
351
|
+
userList={userList}
|
|
352
|
+
userRoleCanDrop={userRoleCanDrop}
|
|
353
|
+
/>
|
|
354
|
+
|
|
355
|
+
{/* Not required for virtualized lists */}
|
|
356
|
+
{/* {provided.placeholder} */}
|
|
357
|
+
</Card>
|
|
358
|
+
)}
|
|
359
|
+
</Droppable>
|
|
360
|
+
</Box>
|
|
361
|
+
</Flex>
|
|
362
362
|
</Card>
|
|
363
363
|
)
|
|
364
364
|
})}
|
|
365
365
|
</Grid>
|
|
366
366
|
</DragDropContext>
|
|
367
|
-
</
|
|
367
|
+
</Flex>
|
|
368
368
|
)
|
|
369
369
|
}
|
package/src/constants/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {defineStates, WorkflowConfig} from '../types'
|
|
2
2
|
|
|
3
3
|
export const API_VERSION = `2023-01-01`
|
|
4
4
|
|
|
@@ -24,8 +24,8 @@ export const DEFAULT_CONFIG: WorkflowConfig = {
|
|
|
24
24
|
title: 'Approved',
|
|
25
25
|
color: 'success',
|
|
26
26
|
roles: ['administrator'],
|
|
27
|
-
requireAssignment: true,
|
|
28
27
|
transitions: ['changesRequested'],
|
|
28
|
+
requireAssignment: true,
|
|
29
29
|
},
|
|
30
30
|
]),
|
|
31
31
|
}
|
|
@@ -8,6 +8,8 @@ export function filterItemsAndSort(
|
|
|
8
8
|
): SanityDocumentWithMetadata[] {
|
|
9
9
|
return (
|
|
10
10
|
items
|
|
11
|
+
// Only items that have existing documents
|
|
12
|
+
.filter((item) => item?._id)
|
|
11
13
|
// Only items of this state
|
|
12
14
|
.filter((item) => item?._metadata?.state === stateId)
|
|
13
15
|
// Only items with selected users, if the document has any assigned users
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {useListeningQuery} from 'sanity-plugin-utils'
|
|
1
|
+
import {DraggableLocation} from '@hello-pangea/dnd'
|
|
3
2
|
import {useToast} from '@sanity/ui'
|
|
4
|
-
import {useClient} from 'sanity'
|
|
5
|
-
import {DraggableLocation} from 'react-beautiful-dnd'
|
|
6
3
|
import groq from 'groq'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import {useClient} from 'sanity'
|
|
6
|
+
import {useListeningQuery} from 'sanity-plugin-utils'
|
|
7
7
|
|
|
8
|
-
import {SanityDocumentWithMetadata, State} from '../types'
|
|
9
8
|
import {API_VERSION} from '../constants'
|
|
9
|
+
import {SanityDocumentWithMetadata, State} from '../types'
|
|
10
10
|
|
|
11
11
|
const QUERY = groq`*[_type == "workflow.metadata"]|order(orderRank){
|
|
12
12
|
"_metadata": {
|
|
@@ -24,7 +24,7 @@ const QUERY = groq`*[_type == "workflow.metadata"]|order(orderRank){
|
|
|
24
24
|
_updatedAt
|
|
25
25
|
}
|
|
26
26
|
)
|
|
27
|
-
}
|
|
27
|
+
}`
|
|
28
28
|
|
|
29
29
|
type WorkflowDocuments = {
|
|
30
30
|
workflowData: {
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {definePlugin, DocumentActionProps} from 'sanity'
|
|
2
2
|
|
|
3
|
-
import {DEFAULT_CONFIG} from './constants'
|
|
4
|
-
import {WorkflowConfig} from './types'
|
|
5
|
-
import {workflowTool} from './tools'
|
|
6
|
-
import metadata from './schema/workflow/workflow.metadata'
|
|
7
3
|
import {AssignWorkflow} from './actions/AssignWorkflow'
|
|
8
4
|
import {BeginWorkflow} from './actions/BeginWorkflow'
|
|
9
5
|
import {CompleteWorkflow} from './actions/CompleteWorkflow'
|
|
6
|
+
import {UpdateWorkflow} from './actions/UpdateWorkflow'
|
|
10
7
|
import {AssigneesBadge} from './badges/AssigneesBadge'
|
|
11
8
|
import {StateBadge} from './badges/StateBadge'
|
|
12
|
-
import {
|
|
9
|
+
import {DEFAULT_CONFIG} from './constants'
|
|
10
|
+
import metadata from './schema/workflow/workflow.metadata'
|
|
11
|
+
import {workflowTool} from './tools'
|
|
12
|
+
import {WorkflowConfig} from './types'
|
|
13
13
|
|
|
14
14
|
export const workflow = definePlugin<WorkflowConfig>(
|
|
15
15
|
(config = DEFAULT_CONFIG) => {
|
package/src/tools/index.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import PropTypes from 'prop-types'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import {EyeOpenIcon} from '@sanity/icons'
|
|
4
|
-
|
|
5
|
-
import {inferMetadataState, useWorkflowMetadata} from '../../lib/workflow'
|
|
6
|
-
import RequestReviewWizard from '../../components/RequestReviewWizard'
|
|
7
|
-
|
|
8
|
-
export function RequestReviewAction(props) {
|
|
9
|
-
const [showWizardDialog, setShowWizardDialog] = React.useState(false)
|
|
10
|
-
const metadata = useWorkflowMetadata(props.id, inferMetadataState(props))
|
|
11
|
-
const {state} = metadata.data
|
|
12
|
-
|
|
13
|
-
if (!props.draft || state === 'inReview' || state === 'approved') {
|
|
14
|
-
return null
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const onHandle = () => {
|
|
18
|
-
if (!showWizardDialog) {
|
|
19
|
-
setShowWizardDialog(true)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const onSend = (assignees) => {
|
|
24
|
-
setShowWizardDialog(false)
|
|
25
|
-
|
|
26
|
-
if (assignees.length === 0) {
|
|
27
|
-
metadata.clearAssignees()
|
|
28
|
-
} else {
|
|
29
|
-
metadata.setAssignees(assignees)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
metadata.setState('inReview')
|
|
33
|
-
props.onComplete()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const onClose = () => setShowWizardDialog(false)
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
dialog: showWizardDialog && {
|
|
40
|
-
type: 'popover',
|
|
41
|
-
content: <RequestReviewWizard metadata={metadata.data} onClose={onClose} onSend={onSend} />,
|
|
42
|
-
onClose: props.onComplete,
|
|
43
|
-
},
|
|
44
|
-
disabled: showWizardDialog,
|
|
45
|
-
icon: EyeOpenIcon,
|
|
46
|
-
label: 'Request review',
|
|
47
|
-
onHandle,
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
RequestReviewAction.propTypes = {
|
|
52
|
-
draft: PropTypes.object,
|
|
53
|
-
id: PropTypes.string,
|
|
54
|
-
onComplete: PropTypes.func,
|
|
55
|
-
}
|
package/src/actions/index.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import {ApproveAction} from './ApproveAction'
|
|
2
|
-
import {DeleteAction} from './DeleteAction'
|
|
3
|
-
import {DiscardChangesAction} from './DiscardChangesAction'
|
|
4
|
-
import {PublishAction} from './PublishAction'
|
|
5
|
-
import {RequestChangesAction} from './RequestChangesAction'
|
|
6
|
-
import {RequestReviewAction} from './RequestReviewAction'
|
|
7
|
-
import {SyncAction} from './SyncAction'
|
|
8
|
-
import {UnpublishAction} from './Unpublish'
|
|
9
|
-
|
|
10
|
-
export function resolveWorkflowActions(/* docInfo */) {
|
|
11
|
-
return [
|
|
12
|
-
SyncAction,
|
|
13
|
-
RequestReviewAction,
|
|
14
|
-
ApproveAction,
|
|
15
|
-
RequestChangesAction,
|
|
16
|
-
PublishAction,
|
|
17
|
-
UnpublishAction,
|
|
18
|
-
DiscardChangesAction,
|
|
19
|
-
DeleteAction,
|
|
20
|
-
]
|
|
21
|
-
}
|