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,121 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {useToast} from '@sanity/ui'
|
|
3
|
-
import {UserSelectMenu} from 'sanity-plugin-utils'
|
|
4
|
-
import {useClient} from 'sanity'
|
|
5
|
-
|
|
6
|
-
import {User} from '../types'
|
|
7
|
-
import {API_VERSION} from '../constants'
|
|
8
|
-
|
|
9
|
-
type UserAssignmentProps = {
|
|
10
|
-
userList: User[]
|
|
11
|
-
assignees: string[]
|
|
12
|
-
documentId: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function UserAssignment(props: UserAssignmentProps) {
|
|
16
|
-
const {assignees, userList, documentId} = props
|
|
17
|
-
const client = useClient({apiVersion: API_VERSION})
|
|
18
|
-
const toast = useToast()
|
|
19
|
-
|
|
20
|
-
const addAssignee = React.useCallback(
|
|
21
|
-
(userId: string) => {
|
|
22
|
-
const user = userList.find((u) => u.id === userId)
|
|
23
|
-
|
|
24
|
-
if (!userId || !user) {
|
|
25
|
-
return toast.push({
|
|
26
|
-
status: 'error',
|
|
27
|
-
title: 'Could not find User',
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return client
|
|
32
|
-
.patch(`workflow-metadata.${documentId}`)
|
|
33
|
-
.setIfMissing({assignees: []})
|
|
34
|
-
.insert(`after`, `assignees[-1]`, [userId])
|
|
35
|
-
.commit()
|
|
36
|
-
.then(() => {
|
|
37
|
-
return toast.push({
|
|
38
|
-
title: `Added ${user.displayName} to assignees`,
|
|
39
|
-
status: 'success',
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
.catch((err) => {
|
|
43
|
-
console.error(err)
|
|
44
|
-
|
|
45
|
-
return toast.push({
|
|
46
|
-
title: `Failed to add assignee`,
|
|
47
|
-
description: userId,
|
|
48
|
-
status: 'error',
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
},
|
|
52
|
-
[documentId, client, toast, userList]
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
const removeAssignee = React.useCallback(
|
|
56
|
-
(userId: string) => {
|
|
57
|
-
const user = userList.find((u) => u.id === userId)
|
|
58
|
-
|
|
59
|
-
if (!userId || !user) {
|
|
60
|
-
return toast.push({
|
|
61
|
-
status: 'error',
|
|
62
|
-
title: 'Could not find User',
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return client
|
|
67
|
-
.patch(`workflow-metadata.${documentId}`)
|
|
68
|
-
.unset([`assignees[@ == "${userId}"]`])
|
|
69
|
-
.commit()
|
|
70
|
-
.then(() => {
|
|
71
|
-
return toast.push({
|
|
72
|
-
title: `Removed ${user.displayName} from assignees`,
|
|
73
|
-
status: 'success',
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
.catch((err) => {
|
|
77
|
-
console.error(err)
|
|
78
|
-
|
|
79
|
-
return toast.push({
|
|
80
|
-
title: `Failed to remove assignee`,
|
|
81
|
-
description: documentId,
|
|
82
|
-
status: 'error',
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
},
|
|
86
|
-
[client, toast, documentId, userList]
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
const clearAssignees = React.useCallback(() => {
|
|
90
|
-
return client
|
|
91
|
-
.patch(`workflow-metadata.${documentId}`)
|
|
92
|
-
.unset([`assignees`])
|
|
93
|
-
.commit()
|
|
94
|
-
.then(() => {
|
|
95
|
-
return toast.push({
|
|
96
|
-
title: `Cleared assignees`,
|
|
97
|
-
status: 'success',
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
.catch((err) => {
|
|
101
|
-
console.error(err)
|
|
102
|
-
|
|
103
|
-
return toast.push({
|
|
104
|
-
title: `Failed to clear assignees`,
|
|
105
|
-
description: documentId,
|
|
106
|
-
status: 'error',
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
}, [client, toast, documentId])
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<UserSelectMenu
|
|
113
|
-
style={{maxHeight: 300}}
|
|
114
|
-
value={assignees || []}
|
|
115
|
-
userList={userList}
|
|
116
|
-
onAdd={addAssignee}
|
|
117
|
-
onClear={clearAssignees}
|
|
118
|
-
onRemove={removeAssignee}
|
|
119
|
-
/>
|
|
120
|
-
)
|
|
121
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {Card} from '@sanity/ui'
|
|
2
|
-
import {FunctionComponent} from 'react'
|
|
3
|
-
import {ArraySchemaType, ArrayOfPrimitivesInputProps, useFormValue} from 'sanity'
|
|
4
|
-
import {useProjectUsers} from 'sanity-plugin-utils'
|
|
5
|
-
|
|
6
|
-
import {API_VERSION} from '../constants'
|
|
7
|
-
import UserAssignment from './UserAssignment'
|
|
8
|
-
|
|
9
|
-
const UserAssignmentInput: FunctionComponent<
|
|
10
|
-
ArrayOfPrimitivesInputProps<string | number | boolean, ArraySchemaType>
|
|
11
|
-
> = (props) => {
|
|
12
|
-
const documentId = useFormValue([`documentId`])
|
|
13
|
-
const userList = useProjectUsers({apiVersion: API_VERSION})
|
|
14
|
-
|
|
15
|
-
const stringValue =
|
|
16
|
-
Array.isArray(props?.value) && props?.value?.length
|
|
17
|
-
? props.value.map((item) => String(item))
|
|
18
|
-
: []
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<Card border padding={1}>
|
|
22
|
-
<UserAssignment userList={userList} assignees={stringValue} documentId={String(documentId)} />
|
|
23
|
-
</Card>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default UserAssignmentInput
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {Button, Grid, Popover, useClickOutside} from '@sanity/ui'
|
|
3
|
-
import {AddIcon} from '@sanity/icons'
|
|
4
|
-
|
|
5
|
-
import AvatarGroup from './DocumentCard/AvatarGroup'
|
|
6
|
-
import {User} from '../types'
|
|
7
|
-
import UserAssignment from './UserAssignment'
|
|
8
|
-
|
|
9
|
-
type UserDisplayProps = {
|
|
10
|
-
userList: User[]
|
|
11
|
-
assignees: string[]
|
|
12
|
-
documentId: string
|
|
13
|
-
disabled?: boolean
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default function UserDisplay(props: UserDisplayProps) {
|
|
17
|
-
const {assignees, userList, documentId, disabled = false} = props
|
|
18
|
-
|
|
19
|
-
const [button] = React.useState(null)
|
|
20
|
-
const [popover, setPopover] = React.useState(null)
|
|
21
|
-
const [isOpen, setIsOpen] = React.useState(false)
|
|
22
|
-
|
|
23
|
-
const close = React.useCallback(() => setIsOpen(false), [])
|
|
24
|
-
const open = React.useCallback(() => setIsOpen(true), [])
|
|
25
|
-
|
|
26
|
-
useClickOutside(close, [button, popover])
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<Popover
|
|
30
|
-
// @ts-ignore
|
|
31
|
-
ref={setPopover}
|
|
32
|
-
content={<UserAssignment userList={userList} assignees={assignees} documentId={documentId} />}
|
|
33
|
-
portal
|
|
34
|
-
open={isOpen}
|
|
35
|
-
>
|
|
36
|
-
{!assignees || assignees.length === 0 ? (
|
|
37
|
-
<Button
|
|
38
|
-
onClick={open}
|
|
39
|
-
fontSize={1}
|
|
40
|
-
padding={2}
|
|
41
|
-
tabIndex={-1}
|
|
42
|
-
icon={AddIcon}
|
|
43
|
-
text="Assign"
|
|
44
|
-
tone="positive"
|
|
45
|
-
mode="ghost"
|
|
46
|
-
disabled={disabled}
|
|
47
|
-
/>
|
|
48
|
-
) : (
|
|
49
|
-
<Grid>
|
|
50
|
-
<Button onClick={open} padding={0} mode="bleed" disabled={disabled}>
|
|
51
|
-
<AvatarGroup users={userList.filter((u) => assignees.includes(u.id))} />
|
|
52
|
-
</Button>
|
|
53
|
-
</Grid>
|
|
54
|
-
)}
|
|
55
|
-
</Popover>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
import {Button, useToast} from '@sanity/ui'
|
|
2
|
-
import {LexoRank} from 'lexorank'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import {useClient} from 'sanity'
|
|
5
|
-
import {UserExtended} from 'sanity-plugin-utils'
|
|
6
|
-
|
|
7
|
-
import {API_VERSION} from '../constants'
|
|
8
|
-
import {generateMultipleOrderRanks} from '../helpers/generateMultipleOrderRanks'
|
|
9
|
-
import {SanityDocumentWithMetadata, State} from '../types'
|
|
10
|
-
import FloatingCard from './FloatingCard'
|
|
11
|
-
|
|
12
|
-
type VerifyProps = {
|
|
13
|
-
data: SanityDocumentWithMetadata[]
|
|
14
|
-
userList: UserExtended[]
|
|
15
|
-
states: State[]
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// This component checks the validity of the data in the Kanban
|
|
19
|
-
// It will only render something it there is invalid date
|
|
20
|
-
// And will render buttons to fix the data
|
|
21
|
-
export default function Verify(props: VerifyProps) {
|
|
22
|
-
const {data, userList, states} = props
|
|
23
|
-
const client = useClient({apiVersion: API_VERSION})
|
|
24
|
-
const toast = useToast()
|
|
25
|
-
|
|
26
|
-
// A lot of error-checking
|
|
27
|
-
const documentsWithoutValidMetadataIds = data?.length
|
|
28
|
-
? data.reduce((acc, cur) => {
|
|
29
|
-
const {documentId, state} = cur._metadata ?? {}
|
|
30
|
-
const stateExists = states.find((s) => s.id === state)
|
|
31
|
-
|
|
32
|
-
return !stateExists && documentId ? [...acc, documentId] : acc
|
|
33
|
-
}, [] as string[])
|
|
34
|
-
: []
|
|
35
|
-
|
|
36
|
-
const documentsWithInvalidUserIds =
|
|
37
|
-
data?.length && userList?.length
|
|
38
|
-
? data.reduce((acc, cur) => {
|
|
39
|
-
const {documentId, assignees} = cur._metadata ?? {}
|
|
40
|
-
const allAssigneesExist = assignees?.length
|
|
41
|
-
? assignees?.every((a) => userList.find((u) => u.id === a))
|
|
42
|
-
: true
|
|
43
|
-
|
|
44
|
-
return !allAssigneesExist && documentId ? [...acc, documentId] : acc
|
|
45
|
-
}, [] as string[])
|
|
46
|
-
: []
|
|
47
|
-
|
|
48
|
-
const documentsWithoutOrderIds = data?.length
|
|
49
|
-
? data.reduce((acc, cur) => {
|
|
50
|
-
const {documentId, orderRank} = cur._metadata ?? {}
|
|
51
|
-
|
|
52
|
-
return !orderRank && documentId ? [...acc, documentId] : acc
|
|
53
|
-
}, [] as string[])
|
|
54
|
-
: []
|
|
55
|
-
|
|
56
|
-
const documentsWithDuplicatedOrderIds = data?.length
|
|
57
|
-
? data.reduce((acc, cur) => {
|
|
58
|
-
const {documentId, orderRank} = cur._metadata ?? {}
|
|
59
|
-
|
|
60
|
-
return orderRank &&
|
|
61
|
-
data.filter((d) => d._metadata?.orderRank === orderRank).length > 1 &&
|
|
62
|
-
documentId
|
|
63
|
-
? [...acc, documentId]
|
|
64
|
-
: acc
|
|
65
|
-
}, [] as string[])
|
|
66
|
-
: []
|
|
67
|
-
|
|
68
|
-
// Updates metadata documents to a valid, existing state
|
|
69
|
-
const correctDocuments = React.useCallback(
|
|
70
|
-
async (ids: string[]) => {
|
|
71
|
-
toast.push({
|
|
72
|
-
title: 'Correcting...',
|
|
73
|
-
status: 'info',
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
const tx = ids.reduce((item, documentId) => {
|
|
77
|
-
return item.patch(`workflow-metadata.${documentId}`, {
|
|
78
|
-
set: {state: states[0].id},
|
|
79
|
-
})
|
|
80
|
-
}, client.transaction())
|
|
81
|
-
|
|
82
|
-
await tx.commit()
|
|
83
|
-
|
|
84
|
-
toast.push({
|
|
85
|
-
title: `Corrected ${
|
|
86
|
-
ids.length === 1 ? `1 Document` : `${ids.length} Documents`
|
|
87
|
-
}`,
|
|
88
|
-
status: 'success',
|
|
89
|
-
})
|
|
90
|
-
},
|
|
91
|
-
[client, states, toast]
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
// Remove users that are no longer in the project from documents
|
|
95
|
-
const removeUsersFromDocuments = React.useCallback(
|
|
96
|
-
async (ids: string[]) => {
|
|
97
|
-
toast.push({
|
|
98
|
-
title: 'Removing users...',
|
|
99
|
-
status: 'info',
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
const tx = ids.reduce((item, documentId) => {
|
|
103
|
-
const {assignees} =
|
|
104
|
-
data.find((d) => d._id === documentId)?._metadata ?? {}
|
|
105
|
-
const validAssignees = assignees?.length
|
|
106
|
-
? // eslint-disable-next-line max-nested-callbacks
|
|
107
|
-
assignees.filter((a) => userList.find((u) => u.id === a)?.id)
|
|
108
|
-
: []
|
|
109
|
-
|
|
110
|
-
return item.patch(`workflow-metadata.${documentId}`, {
|
|
111
|
-
set: {assignees: validAssignees},
|
|
112
|
-
})
|
|
113
|
-
}, client.transaction())
|
|
114
|
-
|
|
115
|
-
await tx.commit()
|
|
116
|
-
|
|
117
|
-
toast.push({
|
|
118
|
-
title: `Corrected ${
|
|
119
|
-
ids.length === 1 ? `1 Document` : `${ids.length} Documents`
|
|
120
|
-
}`,
|
|
121
|
-
status: 'success',
|
|
122
|
-
})
|
|
123
|
-
},
|
|
124
|
-
[client, data, toast, userList]
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
// Add order value to metadata documents
|
|
128
|
-
const addOrderToDocuments = React.useCallback(
|
|
129
|
-
async (ids: string[]) => {
|
|
130
|
-
toast.push({
|
|
131
|
-
title: 'Adding ordering...',
|
|
132
|
-
status: 'info',
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
// Get first and second order values, if they exist
|
|
136
|
-
const [firstOrder, secondOrder] = [...data]
|
|
137
|
-
.slice(0, 2)
|
|
138
|
-
.map((d) => d._metadata?.orderRank)
|
|
139
|
-
const minLexo = firstOrder ? LexoRank.parse(firstOrder) : undefined
|
|
140
|
-
const maxLexo = secondOrder ? LexoRank.parse(secondOrder) : undefined
|
|
141
|
-
const ranks = generateMultipleOrderRanks(ids.length, minLexo, maxLexo)
|
|
142
|
-
|
|
143
|
-
const tx = client.transaction()
|
|
144
|
-
|
|
145
|
-
// Create a new in-between value for each document
|
|
146
|
-
for (let index = 0; index < ids.length; index += 1) {
|
|
147
|
-
tx.patch(`workflow-metadata.${ids[index]}`, {
|
|
148
|
-
set: {orderRank: ranks[index].toString()},
|
|
149
|
-
})
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
await tx.commit()
|
|
153
|
-
|
|
154
|
-
toast.push({
|
|
155
|
-
title: `Added order to ${
|
|
156
|
-
ids.length === 1 ? `1 Document` : `${ids.length} Documents`
|
|
157
|
-
}`,
|
|
158
|
-
status: 'success',
|
|
159
|
-
})
|
|
160
|
-
},
|
|
161
|
-
[data, client, toast]
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
// Reset order value on all metadata documents
|
|
165
|
-
const resetOrderOfAllDocuments = React.useCallback(
|
|
166
|
-
async (ids: string[]) => {
|
|
167
|
-
toast.push({
|
|
168
|
-
title: 'Adding ordering...',
|
|
169
|
-
status: 'info',
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
const ranks = generateMultipleOrderRanks(ids.length)
|
|
173
|
-
|
|
174
|
-
const tx = client.transaction()
|
|
175
|
-
|
|
176
|
-
// Create a new in-between value for each document
|
|
177
|
-
for (let index = 0; index < ids.length; index += 1) {
|
|
178
|
-
tx.patch(`workflow-metadata.${ids[index]}`, {
|
|
179
|
-
set: {orderRank: ranks[index].toString()},
|
|
180
|
-
})
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
await tx.commit()
|
|
184
|
-
|
|
185
|
-
toast.push({
|
|
186
|
-
title: `Added order to ${
|
|
187
|
-
ids.length === 1 ? `1 Document` : `${ids.length} Documents`
|
|
188
|
-
}`,
|
|
189
|
-
status: 'success',
|
|
190
|
-
})
|
|
191
|
-
},
|
|
192
|
-
[data, client, toast]
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
// A document could be deleted and the workflow metadata left behind
|
|
196
|
-
const orphanedMetadataDocumentIds = React.useMemo(() => {
|
|
197
|
-
return data.length
|
|
198
|
-
? data.filter((doc) => !doc?._id).map((doc) => doc._metadata.documentId)
|
|
199
|
-
: []
|
|
200
|
-
}, [data])
|
|
201
|
-
|
|
202
|
-
const handleOrphans = React.useCallback(() => {
|
|
203
|
-
toast.push({
|
|
204
|
-
title: 'Removing orphaned metadata...',
|
|
205
|
-
status: 'info',
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
const tx = client.transaction()
|
|
209
|
-
orphanedMetadataDocumentIds.forEach((id) => {
|
|
210
|
-
tx.delete(`workflow-metadata.${id}`)
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
tx.commit()
|
|
214
|
-
|
|
215
|
-
toast.push({
|
|
216
|
-
title: `Removed ${orphanedMetadataDocumentIds.length} orphaned metadata documents`,
|
|
217
|
-
status: 'success',
|
|
218
|
-
})
|
|
219
|
-
}, [client, orphanedMetadataDocumentIds, toast])
|
|
220
|
-
|
|
221
|
-
return (
|
|
222
|
-
<FloatingCard>
|
|
223
|
-
{documentsWithoutValidMetadataIds.length > 0 ? (
|
|
224
|
-
<Button
|
|
225
|
-
tone="caution"
|
|
226
|
-
mode="ghost"
|
|
227
|
-
onClick={() => correctDocuments(documentsWithoutValidMetadataIds)}
|
|
228
|
-
text={
|
|
229
|
-
documentsWithoutValidMetadataIds.length === 1
|
|
230
|
-
? `Correct 1 Document State`
|
|
231
|
-
: `Correct ${documentsWithoutValidMetadataIds.length} Document States`
|
|
232
|
-
}
|
|
233
|
-
/>
|
|
234
|
-
) : null}
|
|
235
|
-
{documentsWithInvalidUserIds.length > 0 ? (
|
|
236
|
-
<Button
|
|
237
|
-
tone="caution"
|
|
238
|
-
mode="ghost"
|
|
239
|
-
onClick={() => removeUsersFromDocuments(documentsWithInvalidUserIds)}
|
|
240
|
-
text={
|
|
241
|
-
documentsWithInvalidUserIds.length === 1
|
|
242
|
-
? `Remove Invalid Users from 1 Document`
|
|
243
|
-
: `Remove Invalid Users from ${documentsWithInvalidUserIds.length} Documents`
|
|
244
|
-
}
|
|
245
|
-
/>
|
|
246
|
-
) : null}
|
|
247
|
-
{documentsWithoutOrderIds.length > 0 ? (
|
|
248
|
-
<Button
|
|
249
|
-
tone="caution"
|
|
250
|
-
mode="ghost"
|
|
251
|
-
onClick={() => addOrderToDocuments(documentsWithoutOrderIds)}
|
|
252
|
-
text={
|
|
253
|
-
documentsWithoutOrderIds.length === 1
|
|
254
|
-
? `Set Order for 1 Document`
|
|
255
|
-
: `Set Order for ${documentsWithoutOrderIds.length} Documents`
|
|
256
|
-
}
|
|
257
|
-
/>
|
|
258
|
-
) : null}
|
|
259
|
-
{documentsWithDuplicatedOrderIds.length > 0 ? (
|
|
260
|
-
<>
|
|
261
|
-
<Button
|
|
262
|
-
tone="caution"
|
|
263
|
-
mode="ghost"
|
|
264
|
-
onClick={() => addOrderToDocuments(documentsWithDuplicatedOrderIds)}
|
|
265
|
-
text={
|
|
266
|
-
documentsWithDuplicatedOrderIds.length === 1
|
|
267
|
-
? `Set Unique Order for 1 Document`
|
|
268
|
-
: `Set Unique Order for ${documentsWithDuplicatedOrderIds.length} Documents`
|
|
269
|
-
}
|
|
270
|
-
/>
|
|
271
|
-
<Button
|
|
272
|
-
tone="caution"
|
|
273
|
-
mode="ghost"
|
|
274
|
-
onClick={() =>
|
|
275
|
-
resetOrderOfAllDocuments(
|
|
276
|
-
data.map((doc) => String(doc._metadata?.documentId))
|
|
277
|
-
)
|
|
278
|
-
}
|
|
279
|
-
text={
|
|
280
|
-
data.length === 1
|
|
281
|
-
? `Reset Order for 1 Document`
|
|
282
|
-
: `Reset Order for all ${data.length} Documents`
|
|
283
|
-
}
|
|
284
|
-
/>
|
|
285
|
-
</>
|
|
286
|
-
) : null}
|
|
287
|
-
{orphanedMetadataDocumentIds.length > 0 ? (
|
|
288
|
-
<Button
|
|
289
|
-
text="Cleanup orphaned metadata"
|
|
290
|
-
onClick={handleOrphans}
|
|
291
|
-
tone="caution"
|
|
292
|
-
mode="ghost"
|
|
293
|
-
/>
|
|
294
|
-
) : null}
|
|
295
|
-
</FloatingCard>
|
|
296
|
-
)
|
|
297
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import {useCallback, useContext, useState} from 'react'
|
|
2
|
-
import {createContext} from 'react'
|
|
3
|
-
import {LayoutProps} from 'sanity'
|
|
4
|
-
|
|
5
|
-
import {DEFAULT_CONFIG} from '../constants'
|
|
6
|
-
import {useWorkflowMetadata} from '../hooks/useWorkflowMetadata'
|
|
7
|
-
import {KeyedMetadata, WorkflowConfig} from '../types'
|
|
8
|
-
|
|
9
|
-
export type WorkflowContextValue = Required<WorkflowConfig> & {
|
|
10
|
-
data: KeyedMetadata
|
|
11
|
-
loading: boolean
|
|
12
|
-
error: boolean | unknown | ProgressEvent
|
|
13
|
-
ids: string[]
|
|
14
|
-
addId: (id: string) => void
|
|
15
|
-
removeId: (id: string) => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const WorkflowContext = createContext<WorkflowContextValue>({
|
|
19
|
-
data: {},
|
|
20
|
-
loading: false,
|
|
21
|
-
error: false,
|
|
22
|
-
ids: [],
|
|
23
|
-
addId: () => null,
|
|
24
|
-
removeId: () => null,
|
|
25
|
-
...DEFAULT_CONFIG,
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
export function useWorkflowContext(id?: string) {
|
|
29
|
-
const current = useContext(WorkflowContext)
|
|
30
|
-
|
|
31
|
-
return {...current, metadata: id ? current.data[id] : null}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
type WorkflowProviderProps = LayoutProps & {workflow: Required<WorkflowConfig>}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* This Provider wraps the Studio and provides the workflow context to document actions and badges.
|
|
38
|
-
* This is so individual actions and badges do not need to all register their own listeners.
|
|
39
|
-
* Instead, each document "signals" its ID up to the provider, which then registers a single listener
|
|
40
|
-
* This is performed inside of a component loaded at the root level of the Document Form
|
|
41
|
-
*/
|
|
42
|
-
export function WorkflowProvider(props: WorkflowProviderProps) {
|
|
43
|
-
const [ids, setIds] = useState<string[]>([])
|
|
44
|
-
const addId = useCallback(
|
|
45
|
-
(id: string) =>
|
|
46
|
-
setIds((current) => (current.includes(id) ? current : [...current, id])),
|
|
47
|
-
[]
|
|
48
|
-
)
|
|
49
|
-
const removeId = useCallback(
|
|
50
|
-
(id: string) => setIds((current) => current.filter((i) => i !== id)),
|
|
51
|
-
[]
|
|
52
|
-
)
|
|
53
|
-
const {data, loading, error} = useWorkflowMetadata(ids)
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<WorkflowContext.Provider
|
|
57
|
-
value={{
|
|
58
|
-
data,
|
|
59
|
-
loading,
|
|
60
|
-
error,
|
|
61
|
-
ids,
|
|
62
|
-
addId,
|
|
63
|
-
removeId,
|
|
64
|
-
states: props.workflow.states,
|
|
65
|
-
schemaTypes: props.workflow.schemaTypes,
|
|
66
|
-
}}
|
|
67
|
-
>
|
|
68
|
-
{props.renderDefault(props)}
|
|
69
|
-
</WorkflowContext.Provider>
|
|
70
|
-
)
|
|
71
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {useEffect} from 'react'
|
|
2
|
-
import {ObjectInputProps} from 'sanity'
|
|
3
|
-
|
|
4
|
-
import {useWorkflowContext} from './WorkflowContext'
|
|
5
|
-
|
|
6
|
-
// This component is loaded at the root level of the Document Form
|
|
7
|
-
// It is used to signal the document ID to the WorkflowProvider
|
|
8
|
-
export default function WorkflowSignal(props: ObjectInputProps) {
|
|
9
|
-
const documentId = props?.value?._id
|
|
10
|
-
? props.value._id.replace(`drafts.`, ``)
|
|
11
|
-
: null
|
|
12
|
-
|
|
13
|
-
const {addId, removeId} = useWorkflowContext()
|
|
14
|
-
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
// On mount, add to the query of listening documents
|
|
17
|
-
if (documentId) {
|
|
18
|
-
addId(documentId)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// On unmount, remove from the query of listening documents
|
|
22
|
-
return () => {
|
|
23
|
-
if (documentId) {
|
|
24
|
-
removeId(documentId)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}, [documentId, addId, removeId])
|
|
28
|
-
|
|
29
|
-
return props.renderDefault(props)
|
|
30
|
-
}
|