sanity-plugin-workflow 1.0.6 → 2.0.0

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.
Files changed (52) hide show
  1. package/README.md +2 -17
  2. package/dist/index.d.ts +17 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +1647 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +30 -69
  7. package/lib/index.d.ts +0 -20
  8. package/lib/index.esm.js +0 -2135
  9. package/lib/index.esm.js.map +0 -1
  10. package/lib/index.js +0 -2147
  11. package/lib/index.js.map +0 -1
  12. package/sanity.json +0 -8
  13. package/src/actions/AssignWorkflow.tsx +0 -47
  14. package/src/actions/BeginWorkflow.tsx +0 -63
  15. package/src/actions/CompleteWorkflow.tsx +0 -64
  16. package/src/actions/UpdateWorkflow.tsx +0 -126
  17. package/src/badges/AssigneesBadge.tsx +0 -53
  18. package/src/badges/StateBadge.tsx +0 -28
  19. package/src/components/DocumentCard/AvatarGroup.tsx +0 -43
  20. package/src/components/DocumentCard/CompleteButton.tsx +0 -56
  21. package/src/components/DocumentCard/EditButton.tsx +0 -28
  22. package/src/components/DocumentCard/Field.tsx +0 -38
  23. package/src/components/DocumentCard/Validate.tsx +0 -21
  24. package/src/components/DocumentCard/ValidationStatus.tsx +0 -37
  25. package/src/components/DocumentCard/core/DraftStatus.tsx +0 -32
  26. package/src/components/DocumentCard/core/PublishedStatus.tsx +0 -39
  27. package/src/components/DocumentCard/core/TimeAgo.tsx +0 -11
  28. package/src/components/DocumentCard/index.tsx +0 -200
  29. package/src/components/DocumentList.tsx +0 -169
  30. package/src/components/Filters.tsx +0 -174
  31. package/src/components/FloatingCard.tsx +0 -36
  32. package/src/components/StateTitle/Status.tsx +0 -27
  33. package/src/components/StateTitle/index.tsx +0 -78
  34. package/src/components/UserAssignment.tsx +0 -121
  35. package/src/components/UserAssignmentInput.tsx +0 -27
  36. package/src/components/UserDisplay.tsx +0 -57
  37. package/src/components/Verify.tsx +0 -297
  38. package/src/components/WorkflowContext.tsx +0 -71
  39. package/src/components/WorkflowSignal.tsx +0 -30
  40. package/src/components/WorkflowTool.tsx +0 -437
  41. package/src/constants/index.ts +0 -31
  42. package/src/helpers/arraysContainMatchingString.ts +0 -6
  43. package/src/helpers/filterItemsAndSort.ts +0 -41
  44. package/src/helpers/generateMultipleOrderRanks.ts +0 -80
  45. package/src/helpers/initialRank.ts +0 -13
  46. package/src/hooks/useWorkflowDocuments.tsx +0 -167
  47. package/src/hooks/useWorkflowMetadata.tsx +0 -49
  48. package/src/index.ts +0 -97
  49. package/src/schema/workflow/workflow.metadata.ts +0 -68
  50. package/src/tools/index.ts +0 -15
  51. package/src/types/index.ts +0 -71
  52. package/v2-incompatible.js +0 -11
@@ -1,167 +0,0 @@
1
- import {DraggableLocation} from '@hello-pangea/dnd'
2
- import {useToast} from '@sanity/ui'
3
- import groq from 'groq'
4
- import React from 'react'
5
- import {useClient} from 'sanity'
6
- import {useListeningQuery} from 'sanity-plugin-utils'
7
-
8
- import {API_VERSION} from '../constants'
9
- import {SanityDocumentWithMetadata, State} from '../types'
10
-
11
- const QUERY = groq`*[_type == "workflow.metadata"]|order(orderRank){
12
- "_metadata": {
13
- _rev,
14
- assignees,
15
- documentId,
16
- state,
17
- orderRank,
18
- "draftDocumentId": "drafts." + documentId,
19
- }
20
- }{
21
- ...,
22
- ...(
23
- *[_id == ^._metadata.documentId || _id == ^._metadata.draftDocumentId]|order(_updatedAt)[0]{
24
- _id,
25
- _type,
26
- _rev,
27
- _updatedAt
28
- }
29
- )
30
- }`
31
-
32
- type WorkflowDocuments = {
33
- workflowData: {
34
- data: SanityDocumentWithMetadata[]
35
- loading: boolean
36
- error: boolean | unknown | ProgressEvent
37
- }
38
- operations: {
39
- move: (
40
- draggedId: string,
41
- destination: DraggableLocation,
42
- states: State[],
43
- newOrder: string
44
- ) => void
45
- }
46
- }
47
-
48
- export function useWorkflowDocuments(schemaTypes: string[]): WorkflowDocuments {
49
- const toast = useToast()
50
- const client = useClient({apiVersion: API_VERSION})
51
-
52
- // Get and listen to changes on documents + workflow metadata documents
53
- const {data, loading, error} = useListeningQuery<
54
- SanityDocumentWithMetadata[]
55
- >(QUERY, {
56
- params: {schemaTypes},
57
- initialValue: [],
58
- })
59
-
60
- const [localDocuments, setLocalDocuments] = React.useState<
61
- SanityDocumentWithMetadata[]
62
- >([])
63
-
64
- React.useEffect(() => {
65
- if (data) {
66
- setLocalDocuments(data)
67
- }
68
- }, [data])
69
-
70
- const move = React.useCallback(
71
- async (
72
- draggedId: string,
73
- destination: DraggableLocation,
74
- states: State[],
75
- newOrder: string
76
- ) => {
77
- // Optimistic update
78
- const currentLocalData = localDocuments
79
- const newLocalDocuments = localDocuments.map((item) => {
80
- if (item?._metadata?.documentId === draggedId) {
81
- return {
82
- ...item,
83
- _metadata: {
84
- ...item._metadata,
85
- state: destination.droppableId,
86
- orderRank: newOrder,
87
- // This value won't be written to the document
88
- // It's done so that un/publish operations don't happen twice
89
- // Because a moved document's card will update once optimistically
90
- // and then again when the document is updated
91
- optimistic: true,
92
- },
93
- }
94
- }
95
-
96
- return item
97
- })
98
-
99
- setLocalDocuments(newLocalDocuments)
100
-
101
- // Now client-side update
102
- const newStateId = destination.droppableId
103
- const newState = states.find((s) => s.id === newStateId)
104
- const document = localDocuments.find(
105
- (d) => d?._metadata?.documentId === draggedId
106
- )
107
-
108
- if (!newState?.id) {
109
- toast.push({
110
- title: `Could not find target state ${newStateId}`,
111
- status: 'error',
112
- })
113
- return null
114
- }
115
-
116
- if (!document) {
117
- toast.push({
118
- title: `Could not find dragged document in data`,
119
- status: 'error',
120
- })
121
- return null
122
- }
123
-
124
- // We need to know if it's a draft or not
125
- const {_id, _type} = document
126
-
127
- // Metadata + useDocumentOperation always uses Published id
128
- const {documentId, _rev} = document._metadata || {}
129
-
130
- await client
131
- .patch(`workflow-metadata.${documentId}`)
132
- .ifRevisionId(_rev)
133
- .set({state: newStateId, orderRank: newOrder})
134
- .commit()
135
- .then((res) => {
136
- toast.push({
137
- title:
138
- newState.id === document._metadata.state
139
- ? `Reordered in "${newState?.title ?? newStateId}"`
140
- : `Moved to "${newState?.title ?? newStateId}"`,
141
- status: 'success',
142
- })
143
- return res
144
- })
145
- .catch((err) => {
146
- // Revert optimistic update
147
- setLocalDocuments(currentLocalData)
148
-
149
- toast.push({
150
- title: `Failed to move to "${newState?.title ?? newStateId}"`,
151
- description: err.message,
152
- status: 'error',
153
- })
154
- return null
155
- })
156
-
157
- // Send back to the workflow board so a document update can happen
158
- return {_id, _type, documentId, state: newState as State}
159
- },
160
- [client, toast, localDocuments]
161
- )
162
-
163
- return {
164
- workflowData: {data: localDocuments, loading, error},
165
- operations: {move},
166
- }
167
- }
@@ -1,49 +0,0 @@
1
- import {useMemo} from 'react'
2
- import {useListeningQuery} from 'sanity-plugin-utils'
3
-
4
- import {API_VERSION} from '../constants'
5
- import {KeyedMetadata, Metadata} from '../types'
6
-
7
- /**
8
- * Takes the published ID of documents and return the metadata for those documents.
9
- *
10
- * @param ids Source document published IDs
11
- */
12
- export function useWorkflowMetadata(ids: string[]): {
13
- data: KeyedMetadata
14
- loading: boolean
15
- error: boolean | unknown | ProgressEvent
16
- } {
17
- const {
18
- data: rawData,
19
- loading,
20
- error,
21
- } = useListeningQuery<Metadata[]>(
22
- `*[_type == "workflow.metadata" && documentId in $ids]{
23
- _id,
24
- _type,
25
- _rev,
26
- assignees,
27
- documentId,
28
- state,
29
- orderRank
30
- }`,
31
- {
32
- params: {ids},
33
- options: {apiVersion: API_VERSION},
34
- }
35
- )
36
-
37
- const keyedMetadata = useMemo(() => {
38
- if (!rawData || rawData.length === 0) return {}
39
-
40
- return rawData.reduce<KeyedMetadata>((acc, cur) => {
41
- return {
42
- ...acc,
43
- [cur.documentId]: cur,
44
- }
45
- }, {})
46
- }, [rawData])
47
-
48
- return {data: keyedMetadata, loading, error}
49
- }
package/src/index.ts DELETED
@@ -1,97 +0,0 @@
1
- import {definePlugin, DocumentActionProps, isObjectInputProps} from 'sanity'
2
-
3
- import {AssignWorkflow} from './actions/AssignWorkflow'
4
- import {BeginWorkflow} from './actions/BeginWorkflow'
5
- import {CompleteWorkflow} from './actions/CompleteWorkflow'
6
- import {UpdateWorkflow} from './actions/UpdateWorkflow'
7
- import {AssigneesBadge} from './badges/AssigneesBadge'
8
- import {StateBadge} from './badges/StateBadge'
9
- import {WorkflowProvider} from './components/WorkflowContext'
10
- import WorkflowSignal from './components/WorkflowSignal'
11
- import {DEFAULT_CONFIG} from './constants'
12
- import metadata from './schema/workflow/workflow.metadata'
13
- import {workflowTool} from './tools'
14
- import {WorkflowConfig} from './types'
15
-
16
- export const workflow = definePlugin<WorkflowConfig>(
17
- (config = DEFAULT_CONFIG) => {
18
- const {schemaTypes, states} = {...DEFAULT_CONFIG, ...config}
19
-
20
- if (!states?.length) {
21
- throw new Error(`Workflow plugin: Missing "states" in config`)
22
- }
23
-
24
- if (!schemaTypes?.length) {
25
- throw new Error(`Workflow plugin: Missing "schemaTypes" in config`)
26
- }
27
-
28
- return {
29
- name: 'sanity-plugin-workflow',
30
- schema: {
31
- types: [metadata(states)],
32
- },
33
- // TODO: Remove 'workflow.metadata' from list of new document types
34
- // ...
35
- studio: {
36
- components: {
37
- layout: (props) =>
38
- WorkflowProvider({...props, workflow: {schemaTypes, states}}),
39
- },
40
- },
41
- form: {
42
- components: {
43
- input: (props) => {
44
- if (
45
- props.id === `root` &&
46
- isObjectInputProps(props) &&
47
- schemaTypes.includes(props.schemaType.name)
48
- ) {
49
- return WorkflowSignal(props)
50
- }
51
-
52
- return props.renderDefault(props)
53
- },
54
- },
55
- },
56
- document: {
57
- actions: (prev, context) => {
58
- if (!schemaTypes.includes(context.schemaType)) {
59
- return prev
60
- }
61
-
62
- return [
63
- (props) => BeginWorkflow(props),
64
- (props) => AssignWorkflow(props),
65
- ...states.map(
66
- (state) => (props: DocumentActionProps) =>
67
- UpdateWorkflow(props, state)
68
- ),
69
- (props) => CompleteWorkflow(props),
70
- ...prev,
71
- ]
72
- },
73
- badges: (prev, context) => {
74
- if (!schemaTypes.includes(context.schemaType)) {
75
- return prev
76
- }
77
-
78
- const {documentId, currentUser} = context
79
-
80
- if (!documentId) {
81
- return prev
82
- }
83
-
84
- return [
85
- () => StateBadge(documentId),
86
- () => AssigneesBadge(documentId, currentUser),
87
- ...prev,
88
- ]
89
- },
90
- },
91
- tools: [
92
- // TODO: These configs could be read from Context
93
- workflowTool({schemaTypes, states}),
94
- ],
95
- }
96
- }
97
- )
@@ -1,68 +0,0 @@
1
- import {defineField, defineType} from 'sanity'
2
-
3
- import Field from '../../components/DocumentCard/Field'
4
- import UserAssignmentInput from '../../components/UserAssignmentInput'
5
- import {API_VERSION} from '../../constants'
6
- import initialRank from '../../helpers/initialRank'
7
- import {State} from '../../types'
8
-
9
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
10
- export default (states: State[]) =>
11
- defineType({
12
- type: 'document',
13
- name: 'workflow.metadata',
14
- title: 'Workflow metadata',
15
- liveEdit: true,
16
- fields: [
17
- defineField({
18
- name: 'state',
19
- description: `The current "State" of the document. Field is read only as changing it would not fire the state's "operation" setting. These are fired in the Document Actions and in the custom Tool.`,
20
- readOnly: true,
21
- type: 'string',
22
- options: {
23
- list: states.length
24
- ? states.map((state) => ({
25
- value: state.id,
26
- title: state.title,
27
- }))
28
- : [],
29
- layout: 'radio',
30
- },
31
- }),
32
- defineField({
33
- name: 'documentId',
34
- title: 'Document ID',
35
- description:
36
- 'Used to help identify the target document that this metadata is tracking state for.',
37
- type: 'string',
38
- readOnly: true,
39
- components: {
40
- input: Field,
41
- },
42
- }),
43
- defineField({
44
- name: 'orderRank',
45
- description: 'Used to maintain order position of cards in the Tool.',
46
- type: 'string',
47
- readOnly: true,
48
- initialValue: async (p, {getClient}) => {
49
- const lastDocOrderRank = await getClient({
50
- apiVersion: API_VERSION,
51
- }).fetch(`*[_type == $type]|order(@[$order] desc)[0][$order]`, {
52
- order: `orderRank`,
53
- type: `workflow.metadata`,
54
- })
55
-
56
- return initialRank(lastDocOrderRank)
57
- },
58
- }),
59
- defineField({
60
- type: 'array',
61
- name: 'assignees',
62
- of: [{type: 'string'}],
63
- components: {
64
- input: UserAssignmentInput,
65
- },
66
- }),
67
- ],
68
- })
@@ -1,15 +0,0 @@
1
- import {SplitVerticalIcon} from '@sanity/icons'
2
- import {Tool} from 'sanity'
3
-
4
- import WorkflowTool from '../components/WorkflowTool'
5
- import {WorkflowConfig} from '../types'
6
-
7
- export type WorkflowToolConfig = (options: WorkflowConfig) => Tool
8
-
9
- export const workflowTool: WorkflowToolConfig = (options: WorkflowConfig) => ({
10
- name: 'workflow',
11
- title: 'Workflow',
12
- component: WorkflowTool,
13
- icon: SplitVerticalIcon,
14
- options,
15
- })
@@ -1,71 +0,0 @@
1
- import {SanityDocumentLike} from 'sanity'
2
-
3
- export type State = {
4
- id: string
5
- transitions: string[]
6
- title: string
7
- roles?: string[]
8
- requireAssignment?: boolean
9
- requireValidation?: boolean
10
- // From document badges
11
- color?: 'primary' | 'success' | 'warning' | 'danger'
12
- }
13
-
14
- export type StateCheck<Id, States> = {
15
- id: Id
16
- // Transitions is an array of State ids
17
- transitions?: States extends {id: infer Id2}[] ? Id2[] : never
18
- } & State
19
-
20
- export type WorkflowConfig = {
21
- schemaTypes: string[]
22
- states?: State[]
23
- }
24
-
25
- export function defineStates<
26
- Id extends string,
27
- States extends StateCheck<Id, States>[]
28
- >(states: States): States {
29
- return states
30
- }
31
-
32
- export type User = {
33
- createdAt: string
34
- displayName: string
35
- email: string
36
- familyName: string
37
- givenName: string
38
- id: string
39
- imageUrl: string
40
- isCurrentUser: boolean
41
- middleName: string
42
- projectId: string
43
- provider: string
44
- sanityUserId: string
45
- updatedAt: string
46
- }
47
-
48
- export type DragData = {
49
- documentId?: string
50
- x?: number
51
- y?: number
52
- state?: string
53
- }
54
-
55
- export type Metadata = SanityDocumentLike & {
56
- _rev: string
57
- assignees: string[]
58
- documentId: string
59
- state: string
60
- orderRank: string
61
- }
62
-
63
- export type KeyedMetadata = {[key: string]: Metadata}
64
-
65
- export type SanityDocumentWithMetadata = {
66
- _metadata: Metadata
67
- _id: string
68
- _type: string
69
- _rev: string
70
- _updatedAt: string
71
- }
@@ -1,11 +0,0 @@
1
- const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
2
- const {name, version, sanityExchangeUrl} = require('./package.json')
3
-
4
- export default showIncompatiblePluginDialog({
5
- name: name,
6
- versions: {
7
- v3: version,
8
- v2: undefined,
9
- },
10
- sanityExchangeUrl,
11
- })