sanity-plugin-workflow 1.0.0-beta.9 → 1.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 +15 -19
- package/lib/index.esm.js +157 -78
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +155 -76
- 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/components/WorkflowTool.tsx +2 -4
- 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/lib/index.js
CHANGED
|
@@ -135,58 +135,99 @@ function UserAssignment(props) {
|
|
|
135
135
|
onRemove: removeAssignee
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
|
-
function useWorkflowMetadata(
|
|
138
|
+
function useWorkflowMetadata(ids) {
|
|
139
139
|
const {
|
|
140
|
-
data:
|
|
140
|
+
data: rawData,
|
|
141
141
|
loading,
|
|
142
142
|
error
|
|
143
|
-
} = sanityPluginUtils.useListeningQuery("*[_type == \"workflow.metadata\" && documentId
|
|
143
|
+
} = sanityPluginUtils.useListeningQuery("*[_type == \"workflow.metadata\" && documentId in $ids]{\n _id,\n _type,\n _rev,\n assignees,\n documentId,\n state,\n orderRank\n }", {
|
|
144
144
|
params: {
|
|
145
|
-
|
|
145
|
+
ids
|
|
146
|
+
},
|
|
147
|
+
options: {
|
|
148
|
+
apiVersion: API_VERSION
|
|
146
149
|
}
|
|
147
150
|
});
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
151
|
+
const keyedMetadata = React.useMemo(() => {
|
|
152
|
+
if (!rawData || rawData.length === 0) return {};
|
|
153
|
+
return rawData.reduce((acc, cur) => {
|
|
154
|
+
return {
|
|
155
|
+
...acc,
|
|
156
|
+
[cur.documentId]: cur
|
|
157
|
+
};
|
|
158
|
+
}, {});
|
|
159
|
+
}, [rawData]);
|
|
158
160
|
return {
|
|
159
|
-
data:
|
|
161
|
+
data: keyedMetadata,
|
|
160
162
|
loading,
|
|
161
163
|
error
|
|
162
164
|
};
|
|
163
165
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
const WorkflowContext = React.createContext({
|
|
167
|
+
data: {},
|
|
168
|
+
loading: false,
|
|
169
|
+
error: false,
|
|
170
|
+
ids: [],
|
|
171
|
+
addId: () => null,
|
|
172
|
+
removeId: () => null,
|
|
173
|
+
...DEFAULT_CONFIG
|
|
174
|
+
});
|
|
175
|
+
function useWorkflowContext(id) {
|
|
176
|
+
const current = React.useContext(WorkflowContext);
|
|
177
|
+
return {
|
|
178
|
+
...current,
|
|
179
|
+
metadata: id ? current.data[id] : null
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function WorkflowProvider(props) {
|
|
183
|
+
const [ids, setIds] = React.useState([]);
|
|
184
|
+
const addId = React.useCallback(id => setIds(current => current.includes(id) ? current : [...current, id]), []);
|
|
185
|
+
const removeId = React.useCallback(id => setIds(current => current.filter(i => i !== id)), []);
|
|
186
|
+
const {
|
|
187
|
+
data,
|
|
188
|
+
loading,
|
|
189
|
+
error
|
|
190
|
+
} = useWorkflowMetadata(ids);
|
|
191
|
+
return /* @__PURE__ */jsxRuntime.jsx(WorkflowContext.Provider, {
|
|
192
|
+
value: {
|
|
193
|
+
data,
|
|
194
|
+
loading,
|
|
195
|
+
error,
|
|
196
|
+
ids,
|
|
197
|
+
addId,
|
|
198
|
+
removeId,
|
|
199
|
+
states: props.workflow.states,
|
|
200
|
+
schemaTypes: props.workflow.schemaTypes
|
|
201
|
+
},
|
|
202
|
+
children: props.renderDefault(props)
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function AssignWorkflow(props) {
|
|
206
|
+
var _a;
|
|
166
207
|
const {
|
|
167
208
|
id
|
|
168
209
|
} = props;
|
|
210
|
+
const {
|
|
211
|
+
metadata,
|
|
212
|
+
loading,
|
|
213
|
+
error
|
|
214
|
+
} = useWorkflowContext(id);
|
|
169
215
|
const [isDialogOpen, setDialogOpen] = React.useState(false);
|
|
170
216
|
const userList = sanityPluginUtils.useProjectUsers({
|
|
171
217
|
apiVersion: API_VERSION
|
|
172
218
|
});
|
|
173
|
-
const {
|
|
174
|
-
data,
|
|
175
|
-
loading,
|
|
176
|
-
error
|
|
177
|
-
} = useWorkflowMetadata(id, states);
|
|
178
219
|
if (error) {
|
|
179
220
|
console.error(error);
|
|
180
221
|
}
|
|
181
|
-
if (!
|
|
222
|
+
if (!metadata) {
|
|
182
223
|
return null;
|
|
183
224
|
}
|
|
184
225
|
return {
|
|
185
226
|
icon: icons.UsersIcon,
|
|
186
227
|
type: "dialog",
|
|
187
|
-
disabled: !
|
|
228
|
+
disabled: !metadata || loading || error,
|
|
188
229
|
label: "Assign",
|
|
189
|
-
title:
|
|
230
|
+
title: metadata ? null : "Document is not in Workflow",
|
|
190
231
|
dialog: isDialogOpen && {
|
|
191
232
|
type: "popover",
|
|
192
233
|
onClose: () => {
|
|
@@ -194,7 +235,7 @@ function AssignWorkflow(props, states) {
|
|
|
194
235
|
},
|
|
195
236
|
content: /* @__PURE__ */jsxRuntime.jsx(UserAssignment, {
|
|
196
237
|
userList,
|
|
197
|
-
assignees: (
|
|
238
|
+
assignees: ((_a = metadata == null ? void 0 : metadata.assignees) == null ? void 0 : _a.length) > 0 ? metadata.assignees : [],
|
|
198
239
|
documentId: id
|
|
199
240
|
})
|
|
200
241
|
},
|
|
@@ -203,16 +244,17 @@ function AssignWorkflow(props, states) {
|
|
|
203
244
|
}
|
|
204
245
|
};
|
|
205
246
|
}
|
|
206
|
-
function BeginWorkflow(props
|
|
247
|
+
function BeginWorkflow(props) {
|
|
207
248
|
const {
|
|
208
249
|
id,
|
|
209
250
|
draft
|
|
210
251
|
} = props;
|
|
211
252
|
const {
|
|
212
|
-
|
|
253
|
+
metadata,
|
|
213
254
|
loading,
|
|
214
|
-
error
|
|
215
|
-
|
|
255
|
+
error,
|
|
256
|
+
states
|
|
257
|
+
} = useWorkflowContext(id);
|
|
216
258
|
const client = sanity.useClient({
|
|
217
259
|
apiVersion: API_VERSION
|
|
218
260
|
});
|
|
@@ -233,10 +275,6 @@ function BeginWorkflow(props, states) {
|
|
|
233
275
|
documentId: id,
|
|
234
276
|
state: states[0].id,
|
|
235
277
|
orderRank: lowestOrderFirstState ? lexorank.LexoRank.parse(lowestOrderFirstState).genNext().toString() : lexorank.LexoRank.min().toString()
|
|
236
|
-
},
|
|
237
|
-
// Faster!
|
|
238
|
-
{
|
|
239
|
-
visibility: "async"
|
|
240
278
|
}).then(() => {
|
|
241
279
|
toast.push({
|
|
242
280
|
status: "success",
|
|
@@ -247,29 +285,29 @@ function BeginWorkflow(props, states) {
|
|
|
247
285
|
setComplete(true);
|
|
248
286
|
});
|
|
249
287
|
}, [id, states, client, toast]);
|
|
250
|
-
if (!draft || complete ||
|
|
288
|
+
if (!draft || complete || metadata) {
|
|
251
289
|
return null;
|
|
252
290
|
}
|
|
253
291
|
return {
|
|
254
292
|
icon: icons.SplitVerticalIcon,
|
|
255
293
|
type: "dialog",
|
|
256
|
-
disabled:
|
|
294
|
+
disabled: metadata || loading || error || beginning || complete,
|
|
257
295
|
label: beginning ? "Beginning..." : "Begin Workflow",
|
|
258
296
|
onHandle: () => {
|
|
259
297
|
handle();
|
|
260
298
|
}
|
|
261
299
|
};
|
|
262
300
|
}
|
|
263
|
-
function CompleteWorkflow(props
|
|
264
|
-
var _a;
|
|
301
|
+
function CompleteWorkflow(props) {
|
|
265
302
|
const {
|
|
266
303
|
id
|
|
267
304
|
} = props;
|
|
268
305
|
const {
|
|
269
|
-
|
|
306
|
+
metadata,
|
|
270
307
|
loading,
|
|
271
|
-
error
|
|
272
|
-
|
|
308
|
+
error,
|
|
309
|
+
states
|
|
310
|
+
} = useWorkflowContext(id);
|
|
273
311
|
const client = sanity.useClient({
|
|
274
312
|
apiVersion: API_VERSION
|
|
275
313
|
});
|
|
@@ -279,10 +317,11 @@ function CompleteWorkflow(props, states) {
|
|
|
279
317
|
const handle = React.useCallback(() => {
|
|
280
318
|
client.delete("workflow-metadata.".concat(id));
|
|
281
319
|
}, [id, client]);
|
|
282
|
-
|
|
283
|
-
if (!data.metadata) {
|
|
320
|
+
if (!metadata) {
|
|
284
321
|
return null;
|
|
285
322
|
}
|
|
323
|
+
const state = states.find(s => s.id === metadata.state);
|
|
324
|
+
const isLastState = (state == null ? void 0 : state.id) === states[states.length - 1].id;
|
|
286
325
|
return {
|
|
287
326
|
icon: icons.CheckmarkIcon,
|
|
288
327
|
type: "dialog",
|
|
@@ -298,7 +337,7 @@ function CompleteWorkflow(props, states) {
|
|
|
298
337
|
function arraysContainMatchingString(one, two) {
|
|
299
338
|
return one.some(item => two.includes(item));
|
|
300
339
|
}
|
|
301
|
-
function UpdateWorkflow(props,
|
|
340
|
+
function UpdateWorkflow(props, actionState) {
|
|
302
341
|
var _a, _b, _c, _d;
|
|
303
342
|
const {
|
|
304
343
|
id,
|
|
@@ -311,16 +350,15 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
311
350
|
const toast = ui.useToast();
|
|
312
351
|
const currentUser = sanity.useCurrentUser();
|
|
313
352
|
const {
|
|
314
|
-
|
|
353
|
+
metadata,
|
|
315
354
|
loading,
|
|
316
|
-
error
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
} = data;
|
|
355
|
+
error,
|
|
356
|
+
states
|
|
357
|
+
} = useWorkflowContext(id);
|
|
358
|
+
const currentState = states.find(s => s.id === (metadata == null ? void 0 : metadata.state));
|
|
321
359
|
const {
|
|
322
360
|
assignees = []
|
|
323
|
-
} =
|
|
361
|
+
} = metadata != null ? metadata : {};
|
|
324
362
|
const {
|
|
325
363
|
validation,
|
|
326
364
|
isValidating
|
|
@@ -347,21 +385,21 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
347
385
|
});
|
|
348
386
|
});
|
|
349
387
|
};
|
|
350
|
-
if (!
|
|
388
|
+
if (!metadata || currentState && currentState.id === actionState.id) {
|
|
351
389
|
return null;
|
|
352
390
|
}
|
|
353
|
-
const currentStateIndex =
|
|
354
|
-
const actionStateIndex =
|
|
391
|
+
const currentStateIndex = states.findIndex(s => s.id === (currentState == null ? void 0 : currentState.id));
|
|
392
|
+
const actionStateIndex = states.findIndex(s => s.id === actionState.id);
|
|
355
393
|
const direction = actionStateIndex > currentStateIndex ? "promote" : "demote";
|
|
356
394
|
const DirectionIcon = direction === "promote" ? icons.ArrowRightIcon : icons.ArrowLeftIcon;
|
|
357
395
|
const directionLabel = direction === "promote" ? "Promote" : "Demote";
|
|
358
|
-
const userRoleCanUpdateState = ((
|
|
396
|
+
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) ?
|
|
359
397
|
// If the Action state is limited to specific roles
|
|
360
398
|
// check that the current user has one of those roles
|
|
361
399
|
arraysContainMatchingString(user.roles.map(r => r.name), actionState.roles) :
|
|
362
400
|
// No roles specified on the next state, so anyone can update
|
|
363
|
-
((
|
|
364
|
-
const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && currentState.transitions.length ?
|
|
401
|
+
((_c = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _c.length) !== 0;
|
|
402
|
+
const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && ((_d = currentState == null ? void 0 : currentState.transitions) == null ? void 0 : _d.length) ?
|
|
365
403
|
// If the Current State limits transitions to specific States
|
|
366
404
|
// Check that the Action State is in Current State's transitions array
|
|
367
405
|
currentState.transitions.includes(actionState.id) :
|
|
@@ -370,7 +408,7 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
370
408
|
const userAssignmentCanUpdateState = actionState.requireAssignment ?
|
|
371
409
|
// If the Action State requires assigned users
|
|
372
410
|
// Check the current user ID is in the assignees array
|
|
373
|
-
currentUser && assignees.length && assignees.includes(currentUser.id) :
|
|
411
|
+
currentUser && (assignees == null ? void 0 : assignees.length) && assignees.includes(currentUser.id) :
|
|
374
412
|
// Otherwise this isn't a problem
|
|
375
413
|
true;
|
|
376
414
|
let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
@@ -393,16 +431,13 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
393
431
|
onHandle: () => onHandle(id, actionState)
|
|
394
432
|
};
|
|
395
433
|
}
|
|
396
|
-
function AssigneesBadge(
|
|
434
|
+
function AssigneesBadge(documentId, currentUser) {
|
|
397
435
|
var _a;
|
|
398
436
|
const {
|
|
399
|
-
|
|
437
|
+
metadata,
|
|
400
438
|
loading,
|
|
401
439
|
error
|
|
402
|
-
} =
|
|
403
|
-
const {
|
|
404
|
-
metadata
|
|
405
|
-
} = data;
|
|
440
|
+
} = useWorkflowContext(documentId);
|
|
406
441
|
const userList = sanityPluginUtils.useProjectUsers({
|
|
407
442
|
apiVersion: API_VERSION
|
|
408
443
|
});
|
|
@@ -438,15 +473,14 @@ function AssigneesBadge(states, documentId, currentUser) {
|
|
|
438
473
|
color: "primary"
|
|
439
474
|
};
|
|
440
475
|
}
|
|
441
|
-
function StateBadge(
|
|
476
|
+
function StateBadge(documentId) {
|
|
442
477
|
const {
|
|
443
|
-
|
|
478
|
+
metadata,
|
|
444
479
|
loading,
|
|
445
|
-
error
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
} = data;
|
|
480
|
+
error,
|
|
481
|
+
states
|
|
482
|
+
} = useWorkflowContext(documentId);
|
|
483
|
+
const state = states.find(s => s.id === (metadata == null ? void 0 : metadata.state));
|
|
450
484
|
if (loading || error) {
|
|
451
485
|
if (error) {
|
|
452
486
|
console.error(error);
|
|
@@ -462,6 +496,25 @@ function StateBadge(states, documentId) {
|
|
|
462
496
|
color: state == null ? void 0 : state.color
|
|
463
497
|
};
|
|
464
498
|
}
|
|
499
|
+
function WorkflowSignal(props) {
|
|
500
|
+
var _a;
|
|
501
|
+
const documentId = ((_a = props == null ? void 0 : props.value) == null ? void 0 : _a._id) ? props.value._id.replace("drafts.", "") : null;
|
|
502
|
+
const {
|
|
503
|
+
addId,
|
|
504
|
+
removeId
|
|
505
|
+
} = useWorkflowContext();
|
|
506
|
+
React.useEffect(() => {
|
|
507
|
+
if (documentId) {
|
|
508
|
+
addId(documentId);
|
|
509
|
+
}
|
|
510
|
+
return () => {
|
|
511
|
+
if (documentId) {
|
|
512
|
+
removeId(documentId);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
}, [documentId, addId, removeId]);
|
|
516
|
+
return props.renderDefault(props);
|
|
517
|
+
}
|
|
465
518
|
function EditButton(props) {
|
|
466
519
|
const {
|
|
467
520
|
id,
|
|
@@ -1782,7 +1835,7 @@ function WorkflowTool(props) {
|
|
|
1782
1835
|
if (destinationStateIndex === 0) {
|
|
1783
1836
|
newOrder = lexorank.LexoRank.min().toString();
|
|
1784
1837
|
} else {
|
|
1785
|
-
newOrder = lexorank.LexoRank.
|
|
1838
|
+
newOrder = lexorank.LexoRank.min().genNext().toString();
|
|
1786
1839
|
}
|
|
1787
1840
|
} else if (destination.index === 0) {
|
|
1788
1841
|
const firstItemOrderRank = (_b2 = (_a2 = [...destinationStateItems].shift()) == null ? void 0 : _a2._metadata) == null ? void 0 : _b2.orderRank;
|
|
@@ -2001,7 +2054,10 @@ const workflow = sanity.definePlugin(function () {
|
|
|
2001
2054
|
...config
|
|
2002
2055
|
};
|
|
2003
2056
|
if (!(states == null ? void 0 : states.length)) {
|
|
2004
|
-
throw new Error("Workflow: Missing states in config");
|
|
2057
|
+
throw new Error("Workflow plugin: Missing \"states\" in config");
|
|
2058
|
+
}
|
|
2059
|
+
if (!(schemaTypes == null ? void 0 : schemaTypes.length)) {
|
|
2060
|
+
throw new Error("Workflow plugin: Missing \"schemaTypes\" in config");
|
|
2005
2061
|
}
|
|
2006
2062
|
return {
|
|
2007
2063
|
name: "sanity-plugin-workflow",
|
|
@@ -2010,12 +2066,33 @@ const workflow = sanity.definePlugin(function () {
|
|
|
2010
2066
|
},
|
|
2011
2067
|
// TODO: Remove 'workflow.metadata' from list of new document types
|
|
2012
2068
|
// ...
|
|
2069
|
+
studio: {
|
|
2070
|
+
components: {
|
|
2071
|
+
layout: props => WorkflowProvider({
|
|
2072
|
+
...props,
|
|
2073
|
+
workflow: {
|
|
2074
|
+
schemaTypes,
|
|
2075
|
+
states
|
|
2076
|
+
}
|
|
2077
|
+
})
|
|
2078
|
+
}
|
|
2079
|
+
},
|
|
2080
|
+
form: {
|
|
2081
|
+
components: {
|
|
2082
|
+
input: props => {
|
|
2083
|
+
if (props.id === "root" && sanity.isObjectInputProps(props) && schemaTypes.includes(props.schemaType.name)) {
|
|
2084
|
+
return WorkflowSignal(props);
|
|
2085
|
+
}
|
|
2086
|
+
return props.renderDefault(props);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
},
|
|
2013
2090
|
document: {
|
|
2014
2091
|
actions: (prev, context) => {
|
|
2015
2092
|
if (!schemaTypes.includes(context.schemaType)) {
|
|
2016
2093
|
return prev;
|
|
2017
2094
|
}
|
|
2018
|
-
return [props => BeginWorkflow(props
|
|
2095
|
+
return [props => BeginWorkflow(props), props => AssignWorkflow(props), ...states.map(state => props => UpdateWorkflow(props, state)), props => CompleteWorkflow(props), ...prev];
|
|
2019
2096
|
},
|
|
2020
2097
|
badges: (prev, context) => {
|
|
2021
2098
|
if (!schemaTypes.includes(context.schemaType)) {
|
|
@@ -2028,10 +2105,12 @@ const workflow = sanity.definePlugin(function () {
|
|
|
2028
2105
|
if (!documentId) {
|
|
2029
2106
|
return prev;
|
|
2030
2107
|
}
|
|
2031
|
-
return [() => StateBadge(
|
|
2108
|
+
return [() => StateBadge(documentId), () => AssigneesBadge(documentId, currentUser), ...prev];
|
|
2032
2109
|
}
|
|
2033
2110
|
},
|
|
2034
|
-
tools: [
|
|
2111
|
+
tools: [
|
|
2112
|
+
// TODO: These configs could be read from Context
|
|
2113
|
+
workflowTool({
|
|
2035
2114
|
schemaTypes,
|
|
2036
2115
|
states
|
|
2037
2116
|
})]
|