sanity-plugin-workflow 1.0.0-beta.9 → 1.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.
- package/README.md +15 -19
- package/lib/index.esm.js +156 -77
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +154 -75
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/AssignWorkflow.tsx +9 -10
- package/src/actions/BeginWorkflow.tsx +16 -21
- package/src/actions/CompleteWorkflow.tsx +8 -8
- package/src/actions/UpdateWorkflow.tsx +11 -25
- package/src/badges/AssigneesBadge.tsx +9 -8
- package/src/badges/StateBadge.tsx +6 -5
- package/src/components/WorkflowContext.tsx +71 -0
- package/src/components/WorkflowSignal.tsx +30 -0
- package/src/constants/index.ts +1 -1
- package/src/hooks/useWorkflowDocuments.tsx +1 -1
- package/src/hooks/useWorkflowMetadata.tsx +32 -26
- package/src/index.ts +39 -16
- package/src/types/index.ts +2 -0
package/README.md
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
> This is a **Sanity Studio v3** plugin.
|
|
2
2
|
|
|
3
|
-
## Installation
|
|
4
|
-
|
|
5
|
-
```sh
|
|
6
|
-
npm install sanity-plugin-workflow
|
|
7
|
-
```
|
|
8
|
-
|
|
9
3
|
# Sanity Workflow Demo Plugin Example
|
|
10
4
|
|
|
11
5
|
With Sanity Studio you can [customize your content tools to support arbitrary workflows like assignment and content pipelines](https://www.sanity.io/docs/custom-workflows).
|
|
12
6
|
|
|
13
|
-
This plugin is distributed as
|
|
7
|
+
This plugin is distributed as an **example implementation** of customization APIs in the Sanity Studio V3 and is not considered to be a feature-complete implementation of what workflow management requires in production. It is meant as a starting point intended to be forked and customized to the needs of your organization and content creators, or simply as an illustration of what is possible in Sanity Studio V3.
|
|
8
|
+
|
|
9
|
+
An intentional design choice of this plugin is that it **does not influence or modify whether a document is in draft or published**. It only tracks the values of a separate "metadata" document. In this implementation, an "Approved" document could be a draft but will still need publishing. "Approving" the document deletes the "metadata" and so removes it from the "Workflow" process. You choose if Publishing the document happens in the Studio like normal, using the [Scheduled Publishing plugin](https://www.sanity.io/plugins/scheduled-publishing) or the [Scheduling API](https://www.sanity.io/docs/scheduling-api#fa3bb95f83ed).
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
This plugin is considered finished in its current form. Your feedback for workflow features you would like to see in Sanity Studio would be appreciated and can be [shared in our Slack community](https://slack.sanity.io/).
|
|
16
12
|
|
|
17
13
|

|
|
18
14
|
|
|
@@ -28,13 +24,13 @@ This work demonstrates how a single plugin can define:
|
|
|
28
24
|
## Installation
|
|
29
25
|
|
|
30
26
|
```
|
|
31
|
-
npm install --save sanity-plugin-workflow
|
|
27
|
+
npm install --save sanity-plugin-workflow
|
|
32
28
|
```
|
|
33
29
|
|
|
34
30
|
or
|
|
35
31
|
|
|
36
32
|
```
|
|
37
|
-
yarn add sanity-plugin-workflow
|
|
33
|
+
yarn add sanity-plugin-workflow
|
|
38
34
|
```
|
|
39
35
|
|
|
40
36
|
## Usage
|
|
@@ -61,9 +57,9 @@ Add it as a plugin in sanity.config.ts (or .js):
|
|
|
61
57
|
|
|
62
58
|
## Configuring "States"
|
|
63
59
|
|
|
64
|
-
The plugin comes with a default set of "States". These are tracked by the plugin creating a separate "metadata" document for each document that has begun the Workflow.
|
|
60
|
+
The plugin comes with a default set of "States". These are tracked by the plugin creating a separate "metadata" document for each document that has begun the Workflow.
|
|
65
61
|
|
|
66
|
-
Documents can be promoted and demoted in the Workflow with the provided Document Actions as well as a drag-and-drop custom Tool. The settings below are not enforced by the API, custom access control rules could be used to do so.
|
|
62
|
+
Documents can be promoted and demoted in the Workflow with the provided Document Actions as well as a drag-and-drop custom Tool. The settings below are not enforced by the API, custom access control rules could be used to do so.
|
|
67
63
|
|
|
68
64
|
```ts
|
|
69
65
|
{
|
|
@@ -78,7 +74,7 @@ Documents can be promoted and demoted in the Workflow with the provided Document
|
|
|
78
74
|
// Requires the user to be "assigned" in order to update to this State
|
|
79
75
|
requireAssignment: true,
|
|
80
76
|
// Requires the document to be valid before being promoted out of this State
|
|
81
|
-
// Warning: With many documents in the Kanban view this can negatively impact performance
|
|
77
|
+
// Warning: With many documents in the Kanban view this can negatively impact performance
|
|
82
78
|
requireValidation: true,
|
|
83
79
|
// Defines which States a document can be moved to from this one
|
|
84
80
|
transitions: ['changesRequested', 'approved']
|
|
@@ -103,12 +99,12 @@ Once the Workflow is complete, the metadata can be removed by using the "Complet
|
|
|
103
99
|
|
|
104
100
|
This plugin is largely based on the original Workflow Demo built into a Sanity Studio v2 project. The major differences are:
|
|
105
101
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
- This plugin is not concerned with nor will modify whether a document is in draft or published.
|
|
103
|
+
- This plugin can be more easily installed and configured.
|
|
104
|
+
- Documents must "opt-in" to and be removed from the Workflow. In the previous version, all documents were in the workflow which would fill up the interface and negatively affect performance.
|
|
105
|
+
- Document validation status can be used as a way to prevent movement through the workflow.
|
|
106
|
+
- User Roles and Assignments can affect the Workflow. Set rules to enforce which States documents can move between and if being assigned to a document is required to move it to a new State. These are only enforced in the Studio and not the API.
|
|
107
|
+
- This plugin can filter Schema types and assigned Users.
|
|
112
108
|
|
|
113
109
|
## License
|
|
114
110
|
|
package/lib/index.esm.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var _templateObject, _templateObject2, _templateObject3;
|
|
2
2
|
function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
|
|
3
|
-
import { useClient, useCurrentUser, useValidationStatus, useSchema, Preview, useFormValue, defineType, defineField, UserAvatar, useTimeAgo, TextWithTone, definePlugin } from 'sanity';
|
|
3
|
+
import { useClient, useCurrentUser, useValidationStatus, useSchema, Preview, useFormValue, defineType, defineField, UserAvatar, useTimeAgo, TextWithTone, definePlugin, isObjectInputProps } from 'sanity';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import { UsersIcon, SplitVerticalIcon, CheckmarkIcon, ArrowRightIcon, ArrowLeftIcon, EditIcon, AddIcon, PublishIcon, ErrorOutlineIcon, WarningOutlineIcon, DragHandleIcon, UserIcon, ResetIcon, InfoOutlineIcon } from '@sanity/icons';
|
|
6
|
-
import React, { useState, useCallback, useEffect,
|
|
6
|
+
import React, { useMemo, createContext, useContext, useState, useCallback, useEffect, useRef } from 'react';
|
|
7
7
|
import { UserSelectMenu, useListeningQuery, useProjectUsers, Feedback } from 'sanity-plugin-utils';
|
|
8
8
|
import { useToast, Button, Spinner, Card, Flex, Box, Text, useClickOutside, Popover, Grid, Tooltip, useTheme, Stack, MenuButton, Menu, Badge, Container } from '@sanity/ui';
|
|
9
9
|
import { LexoRank } from 'lexorank';
|
|
@@ -122,58 +122,99 @@ function UserAssignment(props) {
|
|
|
122
122
|
onRemove: removeAssignee
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
|
-
function useWorkflowMetadata(
|
|
125
|
+
function useWorkflowMetadata(ids) {
|
|
126
126
|
const {
|
|
127
|
-
data:
|
|
127
|
+
data: rawData,
|
|
128
128
|
loading,
|
|
129
129
|
error
|
|
130
|
-
} = useListeningQuery("*[_type == \"workflow.metadata\" && documentId
|
|
130
|
+
} = useListeningQuery("*[_type == \"workflow.metadata\" && documentId in $ids]{\n _id,\n _type,\n _rev,\n assignees,\n documentId,\n state,\n orderRank\n }", {
|
|
131
131
|
params: {
|
|
132
|
-
|
|
132
|
+
ids
|
|
133
|
+
},
|
|
134
|
+
options: {
|
|
135
|
+
apiVersion: API_VERSION
|
|
133
136
|
}
|
|
134
137
|
});
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
138
|
+
const keyedMetadata = useMemo(() => {
|
|
139
|
+
if (!rawData || rawData.length === 0) return {};
|
|
140
|
+
return rawData.reduce((acc, cur) => {
|
|
141
|
+
return {
|
|
142
|
+
...acc,
|
|
143
|
+
[cur.documentId]: cur
|
|
144
|
+
};
|
|
145
|
+
}, {});
|
|
146
|
+
}, [rawData]);
|
|
145
147
|
return {
|
|
146
|
-
data:
|
|
148
|
+
data: keyedMetadata,
|
|
147
149
|
loading,
|
|
148
150
|
error
|
|
149
151
|
};
|
|
150
152
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
const WorkflowContext = createContext({
|
|
154
|
+
data: {},
|
|
155
|
+
loading: false,
|
|
156
|
+
error: false,
|
|
157
|
+
ids: [],
|
|
158
|
+
addId: () => null,
|
|
159
|
+
removeId: () => null,
|
|
160
|
+
...DEFAULT_CONFIG
|
|
161
|
+
});
|
|
162
|
+
function useWorkflowContext(id) {
|
|
163
|
+
const current = useContext(WorkflowContext);
|
|
164
|
+
return {
|
|
165
|
+
...current,
|
|
166
|
+
metadata: id ? current.data[id] : null
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function WorkflowProvider(props) {
|
|
170
|
+
const [ids, setIds] = useState([]);
|
|
171
|
+
const addId = useCallback(id => setIds(current => current.includes(id) ? current : [...current, id]), []);
|
|
172
|
+
const removeId = useCallback(id => setIds(current => current.filter(i => i !== id)), []);
|
|
173
|
+
const {
|
|
174
|
+
data,
|
|
175
|
+
loading,
|
|
176
|
+
error
|
|
177
|
+
} = useWorkflowMetadata(ids);
|
|
178
|
+
return /* @__PURE__ */jsx(WorkflowContext.Provider, {
|
|
179
|
+
value: {
|
|
180
|
+
data,
|
|
181
|
+
loading,
|
|
182
|
+
error,
|
|
183
|
+
ids,
|
|
184
|
+
addId,
|
|
185
|
+
removeId,
|
|
186
|
+
states: props.workflow.states,
|
|
187
|
+
schemaTypes: props.workflow.schemaTypes
|
|
188
|
+
},
|
|
189
|
+
children: props.renderDefault(props)
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function AssignWorkflow(props) {
|
|
193
|
+
var _a;
|
|
153
194
|
const {
|
|
154
195
|
id
|
|
155
196
|
} = props;
|
|
197
|
+
const {
|
|
198
|
+
metadata,
|
|
199
|
+
loading,
|
|
200
|
+
error
|
|
201
|
+
} = useWorkflowContext(id);
|
|
156
202
|
const [isDialogOpen, setDialogOpen] = useState(false);
|
|
157
203
|
const userList = useProjectUsers({
|
|
158
204
|
apiVersion: API_VERSION
|
|
159
205
|
});
|
|
160
|
-
const {
|
|
161
|
-
data,
|
|
162
|
-
loading,
|
|
163
|
-
error
|
|
164
|
-
} = useWorkflowMetadata(id, states);
|
|
165
206
|
if (error) {
|
|
166
207
|
console.error(error);
|
|
167
208
|
}
|
|
168
|
-
if (!
|
|
209
|
+
if (!metadata) {
|
|
169
210
|
return null;
|
|
170
211
|
}
|
|
171
212
|
return {
|
|
172
213
|
icon: UsersIcon,
|
|
173
214
|
type: "dialog",
|
|
174
|
-
disabled: !
|
|
215
|
+
disabled: !metadata || loading || error,
|
|
175
216
|
label: "Assign",
|
|
176
|
-
title:
|
|
217
|
+
title: metadata ? null : "Document is not in Workflow",
|
|
177
218
|
dialog: isDialogOpen && {
|
|
178
219
|
type: "popover",
|
|
179
220
|
onClose: () => {
|
|
@@ -181,7 +222,7 @@ function AssignWorkflow(props, states) {
|
|
|
181
222
|
},
|
|
182
223
|
content: /* @__PURE__ */jsx(UserAssignment, {
|
|
183
224
|
userList,
|
|
184
|
-
assignees: (
|
|
225
|
+
assignees: ((_a = metadata == null ? void 0 : metadata.assignees) == null ? void 0 : _a.length) > 0 ? metadata.assignees : [],
|
|
185
226
|
documentId: id
|
|
186
227
|
})
|
|
187
228
|
},
|
|
@@ -190,16 +231,17 @@ function AssignWorkflow(props, states) {
|
|
|
190
231
|
}
|
|
191
232
|
};
|
|
192
233
|
}
|
|
193
|
-
function BeginWorkflow(props
|
|
234
|
+
function BeginWorkflow(props) {
|
|
194
235
|
const {
|
|
195
236
|
id,
|
|
196
237
|
draft
|
|
197
238
|
} = props;
|
|
198
239
|
const {
|
|
199
|
-
|
|
240
|
+
metadata,
|
|
200
241
|
loading,
|
|
201
|
-
error
|
|
202
|
-
|
|
242
|
+
error,
|
|
243
|
+
states
|
|
244
|
+
} = useWorkflowContext(id);
|
|
203
245
|
const client = useClient({
|
|
204
246
|
apiVersion: API_VERSION
|
|
205
247
|
});
|
|
@@ -220,10 +262,6 @@ function BeginWorkflow(props, states) {
|
|
|
220
262
|
documentId: id,
|
|
221
263
|
state: states[0].id,
|
|
222
264
|
orderRank: lowestOrderFirstState ? LexoRank.parse(lowestOrderFirstState).genNext().toString() : LexoRank.min().toString()
|
|
223
|
-
},
|
|
224
|
-
// Faster!
|
|
225
|
-
{
|
|
226
|
-
visibility: "async"
|
|
227
265
|
}).then(() => {
|
|
228
266
|
toast.push({
|
|
229
267
|
status: "success",
|
|
@@ -234,29 +272,29 @@ function BeginWorkflow(props, states) {
|
|
|
234
272
|
setComplete(true);
|
|
235
273
|
});
|
|
236
274
|
}, [id, states, client, toast]);
|
|
237
|
-
if (!draft || complete ||
|
|
275
|
+
if (!draft || complete || metadata) {
|
|
238
276
|
return null;
|
|
239
277
|
}
|
|
240
278
|
return {
|
|
241
279
|
icon: SplitVerticalIcon,
|
|
242
280
|
type: "dialog",
|
|
243
|
-
disabled:
|
|
281
|
+
disabled: metadata || loading || error || beginning || complete,
|
|
244
282
|
label: beginning ? "Beginning..." : "Begin Workflow",
|
|
245
283
|
onHandle: () => {
|
|
246
284
|
handle();
|
|
247
285
|
}
|
|
248
286
|
};
|
|
249
287
|
}
|
|
250
|
-
function CompleteWorkflow(props
|
|
251
|
-
var _a;
|
|
288
|
+
function CompleteWorkflow(props) {
|
|
252
289
|
const {
|
|
253
290
|
id
|
|
254
291
|
} = props;
|
|
255
292
|
const {
|
|
256
|
-
|
|
293
|
+
metadata,
|
|
257
294
|
loading,
|
|
258
|
-
error
|
|
259
|
-
|
|
295
|
+
error,
|
|
296
|
+
states
|
|
297
|
+
} = useWorkflowContext(id);
|
|
260
298
|
const client = useClient({
|
|
261
299
|
apiVersion: API_VERSION
|
|
262
300
|
});
|
|
@@ -266,10 +304,11 @@ function CompleteWorkflow(props, states) {
|
|
|
266
304
|
const handle = useCallback(() => {
|
|
267
305
|
client.delete("workflow-metadata.".concat(id));
|
|
268
306
|
}, [id, client]);
|
|
269
|
-
|
|
270
|
-
if (!data.metadata) {
|
|
307
|
+
if (!metadata) {
|
|
271
308
|
return null;
|
|
272
309
|
}
|
|
310
|
+
const state = states.find(s => s.id === metadata.state);
|
|
311
|
+
const isLastState = (state == null ? void 0 : state.id) === states[states.length - 1].id;
|
|
273
312
|
return {
|
|
274
313
|
icon: CheckmarkIcon,
|
|
275
314
|
type: "dialog",
|
|
@@ -285,7 +324,7 @@ function CompleteWorkflow(props, states) {
|
|
|
285
324
|
function arraysContainMatchingString(one, two) {
|
|
286
325
|
return one.some(item => two.includes(item));
|
|
287
326
|
}
|
|
288
|
-
function UpdateWorkflow(props,
|
|
327
|
+
function UpdateWorkflow(props, actionState) {
|
|
289
328
|
var _a, _b, _c, _d;
|
|
290
329
|
const {
|
|
291
330
|
id,
|
|
@@ -298,16 +337,15 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
298
337
|
const toast = useToast();
|
|
299
338
|
const currentUser = useCurrentUser();
|
|
300
339
|
const {
|
|
301
|
-
|
|
340
|
+
metadata,
|
|
302
341
|
loading,
|
|
303
|
-
error
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
} = data;
|
|
342
|
+
error,
|
|
343
|
+
states
|
|
344
|
+
} = useWorkflowContext(id);
|
|
345
|
+
const currentState = states.find(s => s.id === (metadata == null ? void 0 : metadata.state));
|
|
308
346
|
const {
|
|
309
347
|
assignees = []
|
|
310
|
-
} =
|
|
348
|
+
} = metadata != null ? metadata : {};
|
|
311
349
|
const {
|
|
312
350
|
validation,
|
|
313
351
|
isValidating
|
|
@@ -334,21 +372,21 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
334
372
|
});
|
|
335
373
|
});
|
|
336
374
|
};
|
|
337
|
-
if (!
|
|
375
|
+
if (!metadata || currentState && currentState.id === actionState.id) {
|
|
338
376
|
return null;
|
|
339
377
|
}
|
|
340
|
-
const currentStateIndex =
|
|
341
|
-
const actionStateIndex =
|
|
378
|
+
const currentStateIndex = states.findIndex(s => s.id === (currentState == null ? void 0 : currentState.id));
|
|
379
|
+
const actionStateIndex = states.findIndex(s => s.id === actionState.id);
|
|
342
380
|
const direction = actionStateIndex > currentStateIndex ? "promote" : "demote";
|
|
343
381
|
const DirectionIcon = direction === "promote" ? ArrowRightIcon : ArrowLeftIcon;
|
|
344
382
|
const directionLabel = direction === "promote" ? "Promote" : "Demote";
|
|
345
|
-
const userRoleCanUpdateState = ((
|
|
383
|
+
const userRoleCanUpdateState = ((_a = user == null ? void 0 : user.roles) == null ? void 0 : _a.length) && ((_b = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _b.length) ?
|
|
346
384
|
// If the Action state is limited to specific roles
|
|
347
385
|
// check that the current user has one of those roles
|
|
348
386
|
arraysContainMatchingString(user.roles.map(r => r.name), actionState.roles) :
|
|
349
387
|
// No roles specified on the next state, so anyone can update
|
|
350
|
-
((
|
|
351
|
-
const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && currentState.transitions.length ?
|
|
388
|
+
((_c = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _c.length) !== 0;
|
|
389
|
+
const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && ((_d = currentState == null ? void 0 : currentState.transitions) == null ? void 0 : _d.length) ?
|
|
352
390
|
// If the Current State limits transitions to specific States
|
|
353
391
|
// Check that the Action State is in Current State's transitions array
|
|
354
392
|
currentState.transitions.includes(actionState.id) :
|
|
@@ -357,7 +395,7 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
357
395
|
const userAssignmentCanUpdateState = actionState.requireAssignment ?
|
|
358
396
|
// If the Action State requires assigned users
|
|
359
397
|
// Check the current user ID is in the assignees array
|
|
360
|
-
currentUser && assignees.length && assignees.includes(currentUser.id) :
|
|
398
|
+
currentUser && (assignees == null ? void 0 : assignees.length) && assignees.includes(currentUser.id) :
|
|
361
399
|
// Otherwise this isn't a problem
|
|
362
400
|
true;
|
|
363
401
|
let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
@@ -380,16 +418,13 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
380
418
|
onHandle: () => onHandle(id, actionState)
|
|
381
419
|
};
|
|
382
420
|
}
|
|
383
|
-
function AssigneesBadge(
|
|
421
|
+
function AssigneesBadge(documentId, currentUser) {
|
|
384
422
|
var _a;
|
|
385
423
|
const {
|
|
386
|
-
|
|
424
|
+
metadata,
|
|
387
425
|
loading,
|
|
388
426
|
error
|
|
389
|
-
} =
|
|
390
|
-
const {
|
|
391
|
-
metadata
|
|
392
|
-
} = data;
|
|
427
|
+
} = useWorkflowContext(documentId);
|
|
393
428
|
const userList = useProjectUsers({
|
|
394
429
|
apiVersion: API_VERSION
|
|
395
430
|
});
|
|
@@ -425,15 +460,14 @@ function AssigneesBadge(states, documentId, currentUser) {
|
|
|
425
460
|
color: "primary"
|
|
426
461
|
};
|
|
427
462
|
}
|
|
428
|
-
function StateBadge(
|
|
463
|
+
function StateBadge(documentId) {
|
|
429
464
|
const {
|
|
430
|
-
|
|
465
|
+
metadata,
|
|
431
466
|
loading,
|
|
432
|
-
error
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
} = data;
|
|
467
|
+
error,
|
|
468
|
+
states
|
|
469
|
+
} = useWorkflowContext(documentId);
|
|
470
|
+
const state = states.find(s => s.id === (metadata == null ? void 0 : metadata.state));
|
|
437
471
|
if (loading || error) {
|
|
438
472
|
if (error) {
|
|
439
473
|
console.error(error);
|
|
@@ -449,6 +483,25 @@ function StateBadge(states, documentId) {
|
|
|
449
483
|
color: state == null ? void 0 : state.color
|
|
450
484
|
};
|
|
451
485
|
}
|
|
486
|
+
function WorkflowSignal(props) {
|
|
487
|
+
var _a;
|
|
488
|
+
const documentId = ((_a = props == null ? void 0 : props.value) == null ? void 0 : _a._id) ? props.value._id.replace("drafts.", "") : null;
|
|
489
|
+
const {
|
|
490
|
+
addId,
|
|
491
|
+
removeId
|
|
492
|
+
} = useWorkflowContext();
|
|
493
|
+
useEffect(() => {
|
|
494
|
+
if (documentId) {
|
|
495
|
+
addId(documentId);
|
|
496
|
+
}
|
|
497
|
+
return () => {
|
|
498
|
+
if (documentId) {
|
|
499
|
+
removeId(documentId);
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}, [documentId, addId, removeId]);
|
|
503
|
+
return props.renderDefault(props);
|
|
504
|
+
}
|
|
452
505
|
function EditButton(props) {
|
|
453
506
|
const {
|
|
454
507
|
id,
|
|
@@ -1988,7 +2041,10 @@ const workflow = definePlugin(function () {
|
|
|
1988
2041
|
...config
|
|
1989
2042
|
};
|
|
1990
2043
|
if (!(states == null ? void 0 : states.length)) {
|
|
1991
|
-
throw new Error("Workflow: Missing states in config");
|
|
2044
|
+
throw new Error("Workflow plugin: Missing \"states\" in config");
|
|
2045
|
+
}
|
|
2046
|
+
if (!(schemaTypes == null ? void 0 : schemaTypes.length)) {
|
|
2047
|
+
throw new Error("Workflow plugin: Missing \"schemaTypes\" in config");
|
|
1992
2048
|
}
|
|
1993
2049
|
return {
|
|
1994
2050
|
name: "sanity-plugin-workflow",
|
|
@@ -1997,12 +2053,33 @@ const workflow = definePlugin(function () {
|
|
|
1997
2053
|
},
|
|
1998
2054
|
// TODO: Remove 'workflow.metadata' from list of new document types
|
|
1999
2055
|
// ...
|
|
2056
|
+
studio: {
|
|
2057
|
+
components: {
|
|
2058
|
+
layout: props => WorkflowProvider({
|
|
2059
|
+
...props,
|
|
2060
|
+
workflow: {
|
|
2061
|
+
schemaTypes,
|
|
2062
|
+
states
|
|
2063
|
+
}
|
|
2064
|
+
})
|
|
2065
|
+
}
|
|
2066
|
+
},
|
|
2067
|
+
form: {
|
|
2068
|
+
components: {
|
|
2069
|
+
input: props => {
|
|
2070
|
+
if (props.id === "root" && isObjectInputProps(props) && schemaTypes.includes(props.schemaType.name)) {
|
|
2071
|
+
return WorkflowSignal(props);
|
|
2072
|
+
}
|
|
2073
|
+
return props.renderDefault(props);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
},
|
|
2000
2077
|
document: {
|
|
2001
2078
|
actions: (prev, context) => {
|
|
2002
2079
|
if (!schemaTypes.includes(context.schemaType)) {
|
|
2003
2080
|
return prev;
|
|
2004
2081
|
}
|
|
2005
|
-
return [props => BeginWorkflow(props
|
|
2082
|
+
return [props => BeginWorkflow(props), props => AssignWorkflow(props), ...states.map(state => props => UpdateWorkflow(props, state)), props => CompleteWorkflow(props), ...prev];
|
|
2006
2083
|
},
|
|
2007
2084
|
badges: (prev, context) => {
|
|
2008
2085
|
if (!schemaTypes.includes(context.schemaType)) {
|
|
@@ -2015,10 +2092,12 @@ const workflow = definePlugin(function () {
|
|
|
2015
2092
|
if (!documentId) {
|
|
2016
2093
|
return prev;
|
|
2017
2094
|
}
|
|
2018
|
-
return [() => StateBadge(
|
|
2095
|
+
return [() => StateBadge(documentId), () => AssigneesBadge(documentId, currentUser), ...prev];
|
|
2019
2096
|
}
|
|
2020
2097
|
},
|
|
2021
|
-
tools: [
|
|
2098
|
+
tools: [
|
|
2099
|
+
// TODO: These configs could be read from Context
|
|
2100
|
+
workflowTool({
|
|
2022
2101
|
schemaTypes,
|
|
2023
2102
|
states
|
|
2024
2103
|
})]
|