sanity-plugin-workflow 1.0.0-beta.3 → 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/README.md +2 -0
- package/lib/index.esm.js +1498 -1389
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1495 -1386
- package/lib/index.js.map +1 -1
- package/package.json +4 -3
- package/src/components/DocumentCard/core/PublishedStatus.tsx +10 -3
- package/src/components/DocumentCard/index.tsx +5 -5
- package/src/components/DocumentList.tsx +122 -0
- package/src/components/Filters.tsx +3 -3
- package/src/components/Validators.tsx +36 -3
- package/src/components/WorkflowTool.tsx +102 -104
- package/src/constants/index.ts +1 -1
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-workflow",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
4
|
"description": "A demonstration of a custom content publishing workflow using Sanity.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -49,13 +49,14 @@
|
|
|
49
49
|
"watch": "pkg-utils watch --strict"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
+
"@hello-pangea/dnd": "^16.2.0",
|
|
52
53
|
"@sanity/icons": "^2.2.2",
|
|
53
54
|
"@sanity/incompatible-plugin": "^1.0.4",
|
|
55
|
+
"@tanstack/react-virtual": "^3.0.0-beta.54",
|
|
54
56
|
"@types/styled-components": "^5.1.26",
|
|
55
57
|
"framer-motion": "^10.6.1",
|
|
56
58
|
"groq": "^3.3.1",
|
|
57
59
|
"lexorank": "^1.0.5",
|
|
58
|
-
"react-beautiful-dnd": "^13.1.1",
|
|
59
60
|
"react-fast-compare": "^3.2.1",
|
|
60
61
|
"sanity-plugin-utils": "^1.3.0"
|
|
61
62
|
},
|
|
@@ -66,7 +67,6 @@
|
|
|
66
67
|
"@sanity/plugin-kit": "^3.1.4",
|
|
67
68
|
"@sanity/semantic-release-preset": "^4.0.1",
|
|
68
69
|
"@types/react": "^18.0.27",
|
|
69
|
-
"@types/react-beautiful-dnd": "^13.1.2",
|
|
70
70
|
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
|
71
71
|
"@typescript-eslint/parser": "^5.51.0",
|
|
72
72
|
"eslint": "^8.33.0",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"eslint-plugin-prettier": "^4.2.1",
|
|
76
76
|
"eslint-plugin-react": "^7.32.2",
|
|
77
77
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
78
|
+
"eslint-plugin-simple-import-sort": "^10.0.0",
|
|
78
79
|
"husky": "^8.0.3",
|
|
79
80
|
"lint-staged": "^13.2.0",
|
|
80
81
|
"npm-run-all": "^4.1.5",
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import {PublishIcon} from '@sanity/icons'
|
|
2
2
|
import {PreviewValue, SanityDocument} from '@sanity/types'
|
|
3
3
|
import {Box, Text, Tooltip} from '@sanity/ui'
|
|
4
|
+
import {TextWithTone} from 'sanity'
|
|
4
5
|
|
|
5
6
|
import {TimeAgo} from './TimeAgo'
|
|
6
|
-
import {TextWithTone} from 'sanity'
|
|
7
7
|
|
|
8
|
-
export function PublishedStatus(props: {
|
|
8
|
+
export function PublishedStatus(props: {
|
|
9
|
+
document?: PreviewValue | Partial<SanityDocument> | null
|
|
10
|
+
}) {
|
|
9
11
|
const {document} = props
|
|
10
12
|
const updatedAt = document && '_updatedAt' in document && document._updatedAt
|
|
11
13
|
|
|
@@ -24,7 +26,12 @@ export function PublishedStatus(props: {document?: PreviewValue | Partial<Sanity
|
|
|
24
26
|
</Box>
|
|
25
27
|
}
|
|
26
28
|
>
|
|
27
|
-
<TextWithTone
|
|
29
|
+
<TextWithTone
|
|
30
|
+
tone="positive"
|
|
31
|
+
dimmed={!document}
|
|
32
|
+
muted={!document}
|
|
33
|
+
size={1}
|
|
34
|
+
>
|
|
28
35
|
<PublishIcon />
|
|
29
36
|
</TextWithTone>
|
|
30
37
|
</Tooltip>
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/* eslint-disable react/prop-types */
|
|
2
|
-
import {useEffect, useMemo} from 'react'
|
|
3
|
-
import {Box, Card, CardTone, Flex, Stack, useTheme} from '@sanity/ui'
|
|
4
2
|
import {DragHandleIcon} from '@sanity/icons'
|
|
5
|
-
import {
|
|
3
|
+
import {Box, Card, CardTone, Flex, Stack, useTheme} from '@sanity/ui'
|
|
4
|
+
import {useEffect, useMemo} from 'react'
|
|
5
|
+
import {SchemaType, useSchema, useValidationStatus} from 'sanity'
|
|
6
6
|
import {Preview} from 'sanity'
|
|
7
7
|
|
|
8
|
-
import EditButton from './EditButton'
|
|
9
8
|
import {SanityDocumentWithMetadata, State, User} from '../../types'
|
|
10
9
|
import UserDisplay from '../UserDisplay'
|
|
10
|
+
import CompleteButton from './CompleteButton'
|
|
11
11
|
import {DraftStatus} from './core/DraftStatus'
|
|
12
12
|
import {PublishedStatus} from './core/PublishedStatus'
|
|
13
|
+
import EditButton from './EditButton'
|
|
13
14
|
import {ValidationStatus} from './ValidationStatus'
|
|
14
|
-
import CompleteButton from './CompleteButton'
|
|
15
15
|
|
|
16
16
|
type DocumentCardProps = {
|
|
17
17
|
isDragDisabled: boolean
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {Draggable} from '@hello-pangea/dnd'
|
|
2
|
+
import {useVirtualizer} from '@tanstack/react-virtual'
|
|
3
|
+
import {useMemo, useRef} from 'react'
|
|
4
|
+
import {CurrentUser} from 'sanity'
|
|
5
|
+
import {UserExtended} from 'sanity-plugin-utils'
|
|
6
|
+
|
|
7
|
+
import {filterItemsAndSort} from '../helpers/filterItemsAndSort'
|
|
8
|
+
import {SanityDocumentWithMetadata, State} from '../types'
|
|
9
|
+
import {DocumentCard} from './DocumentCard'
|
|
10
|
+
|
|
11
|
+
type DocumentListProps = {
|
|
12
|
+
data: SanityDocumentWithMetadata[]
|
|
13
|
+
invalidDocumentIds: string[]
|
|
14
|
+
selectedSchemaTypes: string[]
|
|
15
|
+
selectedUserIds: string[]
|
|
16
|
+
state: State
|
|
17
|
+
states: State[]
|
|
18
|
+
toggleInvalidDocumentId: (
|
|
19
|
+
documentId: string,
|
|
20
|
+
action: 'ADD' | 'REMOVE'
|
|
21
|
+
) => void
|
|
22
|
+
user: CurrentUser | null
|
|
23
|
+
userList: UserExtended[]
|
|
24
|
+
userRoleCanDrop: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default function DocumentList(props: DocumentListProps) {
|
|
28
|
+
const {
|
|
29
|
+
data = [],
|
|
30
|
+
invalidDocumentIds,
|
|
31
|
+
selectedSchemaTypes,
|
|
32
|
+
selectedUserIds,
|
|
33
|
+
state,
|
|
34
|
+
states,
|
|
35
|
+
toggleInvalidDocumentId,
|
|
36
|
+
user,
|
|
37
|
+
userList,
|
|
38
|
+
userRoleCanDrop,
|
|
39
|
+
} = props
|
|
40
|
+
|
|
41
|
+
const dataFiltered = useMemo(() => {
|
|
42
|
+
return data.length
|
|
43
|
+
? filterItemsAndSort(data, state.id, selectedUserIds, selectedSchemaTypes)
|
|
44
|
+
: []
|
|
45
|
+
}, [data, selectedSchemaTypes, selectedUserIds, state.id])
|
|
46
|
+
|
|
47
|
+
const parentRef = useRef(null)
|
|
48
|
+
|
|
49
|
+
const rowVirtualizer = useVirtualizer({
|
|
50
|
+
count: data.length,
|
|
51
|
+
getScrollElement: () => parentRef.current,
|
|
52
|
+
getItemKey: (index) => dataFiltered[index]?._metadata?.documentId ?? index,
|
|
53
|
+
estimateSize: () => 113,
|
|
54
|
+
overscan: 5,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
if (!data.length) {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div
|
|
63
|
+
ref={parentRef}
|
|
64
|
+
style={{
|
|
65
|
+
height: `100%`,
|
|
66
|
+
overflow: 'auto',
|
|
67
|
+
paddingTop: 1,
|
|
68
|
+
// Smooths scrollbar behaviour
|
|
69
|
+
overflowAnchor: 'none',
|
|
70
|
+
scrollBehavior: 'auto',
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{/* {dataFiltered.map((item, itemIndex) => { */}
|
|
74
|
+
{rowVirtualizer.getVirtualItems().map((virtualItem) => {
|
|
75
|
+
const item = dataFiltered[virtualItem.index]
|
|
76
|
+
|
|
77
|
+
const {documentId, assignees} = item?._metadata ?? {}
|
|
78
|
+
|
|
79
|
+
if (!documentId) {
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const isInvalid = invalidDocumentIds.includes(documentId)
|
|
84
|
+
const meInAssignees = user?.id ? assignees?.includes(user.id) : false
|
|
85
|
+
const isDragDisabled =
|
|
86
|
+
!userRoleCanDrop ||
|
|
87
|
+
isInvalid ||
|
|
88
|
+
!(state.requireAssignment
|
|
89
|
+
? state.requireAssignment && meInAssignees
|
|
90
|
+
: true)
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<Draggable
|
|
94
|
+
// The metadata's documentId is always the published one to avoid rerendering
|
|
95
|
+
key={documentId}
|
|
96
|
+
draggableId={documentId}
|
|
97
|
+
index={virtualItem.index}
|
|
98
|
+
isDragDisabled={isDragDisabled}
|
|
99
|
+
>
|
|
100
|
+
{(draggableProvided, draggableSnapshot) => (
|
|
101
|
+
<div
|
|
102
|
+
ref={draggableProvided.innerRef}
|
|
103
|
+
{...draggableProvided.draggableProps}
|
|
104
|
+
{...draggableProvided.dragHandleProps}
|
|
105
|
+
>
|
|
106
|
+
<DocumentCard
|
|
107
|
+
userRoleCanDrop={userRoleCanDrop}
|
|
108
|
+
isDragDisabled={isDragDisabled}
|
|
109
|
+
isDragging={draggableSnapshot.isDragging}
|
|
110
|
+
item={item}
|
|
111
|
+
toggleInvalidDocumentId={toggleInvalidDocumentId}
|
|
112
|
+
userList={userList}
|
|
113
|
+
states={states}
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
)}
|
|
117
|
+
</Draggable>
|
|
118
|
+
)
|
|
119
|
+
})}
|
|
120
|
+
</div>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {ResetIcon, UserIcon} from '@sanity/icons'
|
|
2
|
+
import {Button, Card, Flex, Menu, MenuButton} from '@sanity/ui'
|
|
3
|
+
import {useCallback} from 'react'
|
|
2
4
|
import {useCurrentUser, UserAvatar, useSchema} from 'sanity'
|
|
3
|
-
import {UserIcon, ResetIcon} from '@sanity/icons'
|
|
4
5
|
import {UserExtended, UserSelectMenu} from 'sanity-plugin-utils'
|
|
5
|
-
import {useCallback} from 'react'
|
|
6
6
|
|
|
7
7
|
type FiltersProps = {
|
|
8
8
|
uniqueAssignedUsers: UserExtended[]
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import {Button, useToast} from '@sanity/ui'
|
|
2
|
+
import {LexoRank} from 'lexorank'
|
|
1
3
|
import React from 'react'
|
|
2
|
-
import {useToast, Button} from '@sanity/ui'
|
|
3
4
|
import {useClient} from 'sanity'
|
|
4
5
|
import {UserExtended} from 'sanity-plugin-utils'
|
|
5
|
-
import {LexoRank} from 'lexorank'
|
|
6
6
|
|
|
7
|
-
import FloatingCard from './FloatingCard'
|
|
8
7
|
import {API_VERSION} from '../constants'
|
|
9
8
|
import {SanityDocumentWithMetadata, State} from '../types'
|
|
9
|
+
import FloatingCard from './FloatingCard'
|
|
10
10
|
|
|
11
11
|
type ValidatorsProps = {
|
|
12
12
|
data: SanityDocumentWithMetadata[]
|
|
@@ -143,6 +143,32 @@ export default function Validators({data, userList, states}: ValidatorsProps) {
|
|
|
143
143
|
[data, client, toast]
|
|
144
144
|
)
|
|
145
145
|
|
|
146
|
+
// A document could be deleted and the workflow metadata left behind
|
|
147
|
+
const orphanedMetadataDocumentIds = React.useMemo(() => {
|
|
148
|
+
return data.length
|
|
149
|
+
? data.filter((doc) => !doc?._id).map((doc) => doc._metadata.documentId)
|
|
150
|
+
: []
|
|
151
|
+
}, [data])
|
|
152
|
+
|
|
153
|
+
const handleOrphans = React.useCallback(() => {
|
|
154
|
+
toast.push({
|
|
155
|
+
title: 'Removing orphaned metadata...',
|
|
156
|
+
status: 'info',
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
const tx = client.transaction()
|
|
160
|
+
orphanedMetadataDocumentIds.forEach((id) => {
|
|
161
|
+
tx.delete(`workflow-metadata.${id}`)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
tx.commit()
|
|
165
|
+
|
|
166
|
+
toast.push({
|
|
167
|
+
title: `Removed ${orphanedMetadataDocumentIds.length} orphaned metadata documents`,
|
|
168
|
+
status: 'success',
|
|
169
|
+
})
|
|
170
|
+
}, [client, orphanedMetadataDocumentIds, toast])
|
|
171
|
+
|
|
146
172
|
return (
|
|
147
173
|
<FloatingCard>
|
|
148
174
|
{documentsWithoutValidMetadataIds.length > 0 ? (
|
|
@@ -178,6 +204,13 @@ export default function Validators({data, userList, states}: ValidatorsProps) {
|
|
|
178
204
|
}
|
|
179
205
|
/>
|
|
180
206
|
) : null}
|
|
207
|
+
{orphanedMetadataDocumentIds.length > 0 ? (
|
|
208
|
+
<Button
|
|
209
|
+
text="Cleanup orphaned metadata"
|
|
210
|
+
onClick={handleOrphans}
|
|
211
|
+
tone="caution"
|
|
212
|
+
/>
|
|
213
|
+
) : null}
|
|
181
214
|
{/* <Button
|
|
182
215
|
tone="caution"
|
|
183
216
|
onClick={() =>
|
|
@@ -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 Validators from './Validators'
|
|
24
23
|
|
|
25
24
|
type WorkflowToolProps = {
|
|
26
25
|
tool: Tool<WorkflowConfig>
|
|
@@ -154,12 +153,12 @@ 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
158
|
const itemBeforeRankParsed = itemBefore._metadata.orderRank
|
|
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
163
|
const itemAfterRankParsed = itemAfter._metadata.orderRank
|
|
165
164
|
? LexoRank.parse(itemAfterRank)
|
|
@@ -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,7 +248,7 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
245
248
|
}
|
|
246
249
|
|
|
247
250
|
return (
|
|
248
|
-
<
|
|
251
|
+
<Flex direction="column" height="fill" overflow="hidden">
|
|
249
252
|
<Validators data={data} userList={userList} states={states} />
|
|
250
253
|
<Filters
|
|
251
254
|
uniqueAssignedUsers={uniqueAssignedUsers}
|
|
@@ -270,100 +273,95 @@ export default function WorkflowTool(props: WorkflowToolProps) {
|
|
|
270
273
|
key={state.id}
|
|
271
274
|
borderLeft={stateIndex > 0}
|
|
272
275
|
tone={defaultCardTone}
|
|
273
|
-
height="fill"
|
|
274
|
-
overflow="auto"
|
|
275
276
|
>
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
277
|
+
<Flex direction="column" height="fill">
|
|
278
|
+
<StateTitle
|
|
279
|
+
state={state}
|
|
280
|
+
requireAssignment={state.requireAssignment ?? false}
|
|
281
|
+
userRoleCanDrop={userRoleCanDrop}
|
|
282
|
+
// operation={state.operation}
|
|
283
|
+
isDropDisabled={isDropDisabled}
|
|
284
|
+
draggingFrom={draggingFrom}
|
|
285
|
+
/>
|
|
286
|
+
<Box flex={1}>
|
|
287
|
+
<Droppable
|
|
288
|
+
droppableId={state.id}
|
|
289
|
+
isDropDisabled={isDropDisabled}
|
|
290
|
+
// props required for virtualization
|
|
291
|
+
mode="virtual"
|
|
292
|
+
renderClone={(provided, snapshot, rubric) => {
|
|
293
|
+
const item = data.find(
|
|
294
|
+
(doc) =>
|
|
295
|
+
doc?._metadata?.documentId === rubric.draggableId
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<div
|
|
300
|
+
{...provided.draggableProps}
|
|
301
|
+
{...provided.dragHandleProps}
|
|
302
|
+
ref={provided.innerRef}
|
|
303
|
+
>
|
|
304
|
+
{item ? (
|
|
305
|
+
<DocumentCard
|
|
306
|
+
isDragDisabled={false}
|
|
307
|
+
userRoleCanDrop={userRoleCanDrop}
|
|
308
|
+
isDragging={snapshot.isDragging}
|
|
309
|
+
item={item}
|
|
310
|
+
states={states}
|
|
311
|
+
toggleInvalidDocumentId={
|
|
312
|
+
toggleInvalidDocumentId
|
|
313
|
+
}
|
|
314
|
+
userList={userList}
|
|
315
|
+
/>
|
|
316
|
+
) : (
|
|
317
|
+
<Feedback title="Item not found" tone="caution" />
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
)
|
|
321
|
+
}}
|
|
296
322
|
>
|
|
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
|
|
323
|
+
{(provided, snapshot) => (
|
|
324
|
+
<Card
|
|
325
|
+
ref={provided.innerRef}
|
|
326
|
+
tone={
|
|
327
|
+
snapshot.isDraggingOver
|
|
328
|
+
? `primary`
|
|
329
|
+
: defaultCardTone
|
|
326
330
|
}
|
|
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>
|
|
331
|
+
height="fill"
|
|
332
|
+
paddingTop={1}
|
|
333
|
+
>
|
|
334
|
+
{loading ? (
|
|
335
|
+
<Flex padding={5} align="center" justify="center">
|
|
336
|
+
<Spinner muted />
|
|
337
|
+
</Flex>
|
|
338
|
+
) : null}
|
|
339
|
+
|
|
340
|
+
<DocumentList
|
|
341
|
+
data={data}
|
|
342
|
+
invalidDocumentIds={invalidDocumentIds}
|
|
343
|
+
selectedSchemaTypes={selectedSchemaTypes}
|
|
344
|
+
selectedUserIds={selectedUserIds}
|
|
345
|
+
state={state}
|
|
346
|
+
states={states}
|
|
347
|
+
toggleInvalidDocumentId={toggleInvalidDocumentId}
|
|
348
|
+
user={user}
|
|
349
|
+
userList={userList}
|
|
350
|
+
userRoleCanDrop={userRoleCanDrop}
|
|
351
|
+
/>
|
|
352
|
+
|
|
353
|
+
{/* Not required for virtualized lists */}
|
|
354
|
+
{/* {provided.placeholder} */}
|
|
355
|
+
</Card>
|
|
356
|
+
)}
|
|
357
|
+
</Droppable>
|
|
358
|
+
</Box>
|
|
359
|
+
</Flex>
|
|
362
360
|
</Card>
|
|
363
361
|
)
|
|
364
362
|
})}
|
|
365
363
|
</Grid>
|
|
366
364
|
</DragDropContext>
|
|
367
|
-
</
|
|
365
|
+
</Flex>
|
|
368
366
|
)
|
|
369
367
|
}
|
package/src/constants/index.ts
CHANGED
|
@@ -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) => {
|