sanity-plugin-workflow 1.0.0-beta.4 → 1.0.0-beta.5
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 +4 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.esm.js +132 -98
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +130 -96
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/actions/UpdateWorkflow.tsx +22 -24
- package/src/components/DocumentCard/Validate.tsx +21 -0
- package/src/components/DocumentCard/index.tsx +91 -64
- package/src/components/{Validators.tsx → Verify.tsx} +6 -2
- package/src/components/WorkflowTool.tsx +6 -4
- package/src/constants/index.ts +1 -1
- package/src/types/index.ts +1 -0
- package/src/actions/RequestReviewAction.js +0 -55
- package/src/actions/index.js +0 -21
package/README.md
CHANGED
|
@@ -71,10 +71,13 @@ Documents can be promoted and demoted in the Workflow with the provided Document
|
|
|
71
71
|
// Optional settings:
|
|
72
72
|
// Used for the color of the Document Badge
|
|
73
73
|
color: 'success',
|
|
74
|
-
// Will
|
|
74
|
+
// Will limit document actions and drag-and-drop for only users with these Role
|
|
75
75
|
roles: ['publisher', 'administrator'],
|
|
76
76
|
// Requires the user to be "assigned" in order to update to this State
|
|
77
77
|
requireAssignment: true,
|
|
78
|
+
// Requires the document to be valid before being promoted out of this State
|
|
79
|
+
// Warning: With many documents in the Kanban view this can negatively impact performance
|
|
80
|
+
requireValidation: true,
|
|
78
81
|
// Defines which States a document can be moved to from this one
|
|
79
82
|
transitions: ['changesRequested', 'approved']
|
|
80
83
|
}
|
package/lib/index.d.ts
CHANGED
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,
|
|
3
|
+
import { useClient, useCurrentUser, useValidationStatus, useSchema, Preview, useFormValue, defineType, defineField, UserAvatar, useTimeAgo, TextWithTone, definePlugin } 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,
|
|
6
|
+
import React, { useState, useCallback, useEffect, useMemo, 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';
|
|
@@ -36,8 +36,8 @@ const DEFAULT_CONFIG = {
|
|
|
36
36
|
title: "Approved",
|
|
37
37
|
color: "success",
|
|
38
38
|
roles: ["administrator"],
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
transitions: ["changesRequested"],
|
|
40
|
+
requireAssignment: true
|
|
41
41
|
}])
|
|
42
42
|
};
|
|
43
43
|
function UserAssignment(props) {
|
|
@@ -291,11 +291,6 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
291
291
|
id,
|
|
292
292
|
type
|
|
293
293
|
} = props;
|
|
294
|
-
const {
|
|
295
|
-
validation,
|
|
296
|
-
isValidating
|
|
297
|
-
} = useValidationStatus(id, type);
|
|
298
|
-
const hasValidationErrors = !isValidating && (validation == null ? void 0 : validation.length) > 0 && validation.find(v => v.level === "error");
|
|
299
294
|
const user = useCurrentUser();
|
|
300
295
|
const client = useClient({
|
|
301
296
|
apiVersion: API_VERSION
|
|
@@ -313,6 +308,11 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
313
308
|
const {
|
|
314
309
|
assignees = []
|
|
315
310
|
} = (_a = data == null ? void 0 : data.metadata) != null ? _a : {};
|
|
311
|
+
const {
|
|
312
|
+
validation,
|
|
313
|
+
isValidating
|
|
314
|
+
} = useValidationStatus(id, type);
|
|
315
|
+
const hasValidationErrors = (currentState == null ? void 0 : currentState.requireValidation) && !isValidating && (validation == null ? void 0 : validation.length) > 0 && validation.find(v => v.level === "error");
|
|
316
316
|
if (error) {
|
|
317
317
|
console.error(error);
|
|
318
318
|
}
|
|
@@ -342,40 +342,39 @@ function UpdateWorkflow(props, allStates, actionState) {
|
|
|
342
342
|
const direction = actionStateIndex > currentStateIndex ? "promote" : "demote";
|
|
343
343
|
const DirectionIcon = direction === "promote" ? ArrowRightIcon : ArrowLeftIcon;
|
|
344
344
|
const directionLabel = direction === "promote" ? "Promote" : "Demote";
|
|
345
|
-
let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
346
345
|
const userRoleCanUpdateState = ((_b = user == null ? void 0 : user.roles) == null ? void 0 : _b.length) && ((_c = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _c.length) ?
|
|
347
346
|
// If the Action state is limited to specific roles
|
|
348
347
|
// check that the current user has one of those roles
|
|
349
348
|
arraysContainMatchingString(user.roles.map(r => r.name), actionState.roles) :
|
|
350
349
|
// No roles specified on the next state, so anyone can update
|
|
351
350
|
((_d = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _d.length) !== 0;
|
|
352
|
-
if (!userRoleCanUpdateState) {
|
|
353
|
-
title = "Your User role cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
354
|
-
}
|
|
355
351
|
const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && currentState.transitions.length ?
|
|
356
352
|
// If the Current State limits transitions to specific States
|
|
357
353
|
// Check that the Action State is in Current State's transitions array
|
|
358
354
|
currentState.transitions.includes(actionState.id) :
|
|
359
355
|
// Otherwise this isn't a problem
|
|
360
356
|
true;
|
|
361
|
-
if (!actionStateIsAValidTransition) {
|
|
362
|
-
title = "You cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\" from \"").concat(currentState == null ? void 0 : currentState.title, "\"");
|
|
363
|
-
}
|
|
364
357
|
const userAssignmentCanUpdateState = actionState.requireAssignment ?
|
|
365
358
|
// If the Action State requires assigned users
|
|
366
359
|
// Check the current user ID is in the assignees array
|
|
367
360
|
currentUser && assignees.length && assignees.includes(currentUser.id) :
|
|
368
361
|
// Otherwise this isn't a problem
|
|
369
362
|
true;
|
|
370
|
-
|
|
363
|
+
let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
364
|
+
if (!userRoleCanUpdateState) {
|
|
365
|
+
title = "Your User role cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
366
|
+
} else if (!actionStateIsAValidTransition) {
|
|
367
|
+
title = "You cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\" from \"").concat(currentState == null ? void 0 : currentState.title, "\"");
|
|
368
|
+
} else if (!userAssignmentCanUpdateState) {
|
|
371
369
|
title = "You must be assigned to the document to ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
372
|
-
}
|
|
373
|
-
|
|
370
|
+
} else if ((currentState == null ? void 0 : currentState.requireValidation) && isValidating) {
|
|
371
|
+
title = "Document is validating, cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
372
|
+
} else if (hasValidationErrors) {
|
|
374
373
|
title = "Document has validation errors, cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
|
|
375
374
|
}
|
|
376
375
|
return {
|
|
377
376
|
icon: DirectionIcon,
|
|
378
|
-
disabled: loading || error || isValidating || hasValidationErrors || !currentState || !userRoleCanUpdateState || !actionStateIsAValidTransition || !userAssignmentCanUpdateState,
|
|
377
|
+
disabled: loading || error || (currentState == null ? void 0 : currentState.requireValidation) && isValidating || hasValidationErrors || !currentState || !userRoleCanUpdateState || !actionStateIsAValidTransition || !userAssignmentCanUpdateState,
|
|
379
378
|
title,
|
|
380
379
|
label: actionState.title,
|
|
381
380
|
onHandle: () => onHandle(id, actionState)
|
|
@@ -908,6 +907,24 @@ function PublishedStatus(props) {
|
|
|
908
907
|
})
|
|
909
908
|
});
|
|
910
909
|
}
|
|
910
|
+
function Validate(props) {
|
|
911
|
+
const {
|
|
912
|
+
documentId,
|
|
913
|
+
type,
|
|
914
|
+
onChange
|
|
915
|
+
} = props;
|
|
916
|
+
const {
|
|
917
|
+
isValidating,
|
|
918
|
+
validation = []
|
|
919
|
+
} = useValidationStatus(documentId, type);
|
|
920
|
+
useEffect(() => {
|
|
921
|
+
onChange({
|
|
922
|
+
isValidating,
|
|
923
|
+
validation
|
|
924
|
+
});
|
|
925
|
+
}, [onChange, isValidating, validation]);
|
|
926
|
+
return null;
|
|
927
|
+
}
|
|
911
928
|
function ValidationStatus(props) {
|
|
912
929
|
const {
|
|
913
930
|
validation = []
|
|
@@ -933,7 +950,7 @@ function ValidationStatus(props) {
|
|
|
933
950
|
});
|
|
934
951
|
}
|
|
935
952
|
function DocumentCard(props) {
|
|
936
|
-
var _a;
|
|
953
|
+
var _a, _b;
|
|
937
954
|
const {
|
|
938
955
|
isDragDisabled,
|
|
939
956
|
userRoleCanDrop,
|
|
@@ -948,18 +965,29 @@ function DocumentCard(props) {
|
|
|
948
965
|
documentId
|
|
949
966
|
} = (_a = item._metadata) != null ? _a : {};
|
|
950
967
|
const schema = useSchema();
|
|
968
|
+
const state = states.find(s => {
|
|
969
|
+
var _a2;
|
|
970
|
+
return s.id === ((_a2 = item._metadata) == null ? void 0 : _a2.state);
|
|
971
|
+
});
|
|
951
972
|
const isDarkMode = useTheme().sanity.color.dark;
|
|
952
973
|
const defaultCardTone = isDarkMode ? "transparent" : "default";
|
|
974
|
+
const [optimisticValidation, setOptimisticValidation] = useState({
|
|
975
|
+
isValidating: (_b = state == null ? void 0 : state.requireValidation) != null ? _b : false,
|
|
976
|
+
validation: []
|
|
977
|
+
});
|
|
953
978
|
const {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
} =
|
|
979
|
+
isValidating,
|
|
980
|
+
validation
|
|
981
|
+
} = optimisticValidation;
|
|
982
|
+
const handleValidation = useCallback(updates => {
|
|
983
|
+
setOptimisticValidation(updates);
|
|
984
|
+
}, []);
|
|
957
985
|
const cardTone = useMemo(() => {
|
|
958
986
|
let tone = defaultCardTone;
|
|
959
987
|
if (!userRoleCanDrop) return isDarkMode ? "default" : "transparent";
|
|
960
988
|
if (!documentId) return tone;
|
|
961
989
|
if (isDragging) tone = "positive";
|
|
962
|
-
if (!isValidating && validation.length > 0) {
|
|
990
|
+
if ((state == null ? void 0 : state.requireValidation) && !isValidating && validation.length > 0) {
|
|
963
991
|
if (validation.some(v => v.level === "error")) {
|
|
964
992
|
tone = "critical";
|
|
965
993
|
} else {
|
|
@@ -967,7 +995,7 @@ function DocumentCard(props) {
|
|
|
967
995
|
}
|
|
968
996
|
}
|
|
969
997
|
return tone;
|
|
970
|
-
}, [
|
|
998
|
+
}, [defaultCardTone, userRoleCanDrop, isDarkMode, documentId, isDragging, isValidating, validation, state == null ? void 0 : state.requireValidation]);
|
|
971
999
|
useEffect(() => {
|
|
972
1000
|
if (!isValidating && validation.length > 0) {
|
|
973
1001
|
if (validation.some(v => v.level === "error")) {
|
|
@@ -984,75 +1012,81 @@ function DocumentCard(props) {
|
|
|
984
1012
|
var _a2;
|
|
985
1013
|
return states[states.length - 1].id === ((_a2 = item._metadata) == null ? void 0 : _a2.state);
|
|
986
1014
|
}, [states, item._metadata.state]);
|
|
987
|
-
return /* @__PURE__ */
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1015
|
+
return /* @__PURE__ */jsxs(Fragment, {
|
|
1016
|
+
children: [(state == null ? void 0 : state.requireValidation) ? /* @__PURE__ */jsx(Validate, {
|
|
1017
|
+
documentId,
|
|
1018
|
+
type: item._type,
|
|
1019
|
+
onChange: handleValidation
|
|
1020
|
+
}) : null, /* @__PURE__ */jsx(Box, {
|
|
1021
|
+
paddingBottom: 3,
|
|
1022
|
+
paddingX: 3,
|
|
1023
|
+
children: /* @__PURE__ */jsx(Card, {
|
|
1024
|
+
radius: 2,
|
|
1025
|
+
shadow: isDragging ? 3 : 1,
|
|
1026
|
+
tone: cardTone,
|
|
1027
|
+
children: /* @__PURE__ */jsxs(Stack, {
|
|
1028
|
+
children: [/* @__PURE__ */jsx(Card, {
|
|
1029
|
+
borderBottom: true,
|
|
1030
|
+
radius: 2,
|
|
1031
|
+
padding: 3,
|
|
1032
|
+
paddingLeft: 2,
|
|
1033
|
+
tone: cardTone,
|
|
1034
|
+
style: {
|
|
1035
|
+
pointerEvents: "none"
|
|
1036
|
+
},
|
|
1037
|
+
children: /* @__PURE__ */jsxs(Flex, {
|
|
1038
|
+
align: "center",
|
|
1039
|
+
justify: "space-between",
|
|
1040
|
+
gap: 1,
|
|
1041
|
+
children: [/* @__PURE__ */jsx(Box, {
|
|
1042
|
+
flex: 1,
|
|
1043
|
+
children: /* @__PURE__ */jsx(Preview, {
|
|
1044
|
+
layout: "default",
|
|
1045
|
+
value: item,
|
|
1046
|
+
schemaType: schema.get(item._type)
|
|
1047
|
+
})
|
|
1048
|
+
}), /* @__PURE__ */jsx(Box, {
|
|
1049
|
+
style: {
|
|
1050
|
+
flexShrink: 0
|
|
1051
|
+
},
|
|
1052
|
+
children: hasError || isDragDisabled ? null : /* @__PURE__ */jsx(DragHandleIcon, {})
|
|
1053
|
+
})]
|
|
1054
|
+
})
|
|
1055
|
+
}), /* @__PURE__ */jsx(Card, {
|
|
1056
|
+
padding: 2,
|
|
1057
|
+
radius: 2,
|
|
1058
|
+
tone: "inherit",
|
|
1059
|
+
children: /* @__PURE__ */jsxs(Flex, {
|
|
1060
|
+
align: "center",
|
|
1061
|
+
justify: "space-between",
|
|
1062
|
+
gap: 3,
|
|
1063
|
+
children: [/* @__PURE__ */jsx(Box, {
|
|
1064
|
+
flex: 1,
|
|
1065
|
+
children: documentId && /* @__PURE__ */jsx(UserDisplay, {
|
|
1066
|
+
userList,
|
|
1067
|
+
assignees,
|
|
1068
|
+
documentId,
|
|
1069
|
+
disabled: !userRoleCanDrop
|
|
1070
|
+
})
|
|
1071
|
+
}), validation.length > 0 ? /* @__PURE__ */jsx(ValidationStatus, {
|
|
1072
|
+
validation
|
|
1073
|
+
}) : null, /* @__PURE__ */jsx(DraftStatus, {
|
|
1074
|
+
document: item
|
|
1075
|
+
}), /* @__PURE__ */jsx(PublishedStatus, {
|
|
1076
|
+
document: item
|
|
1077
|
+
}), /* @__PURE__ */jsx(EditButton, {
|
|
1078
|
+
id: item._id,
|
|
1079
|
+
type: item._type,
|
|
1080
|
+
disabled: !userRoleCanDrop
|
|
1081
|
+
}), isLastState ? /* @__PURE__ */jsx(CompleteButton, {
|
|
1035
1082
|
documentId,
|
|
1036
1083
|
disabled: !userRoleCanDrop
|
|
1037
|
-
})
|
|
1038
|
-
})
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
document: item
|
|
1042
|
-
}), /* @__PURE__ */jsx(PublishedStatus, {
|
|
1043
|
-
document: item
|
|
1044
|
-
}), /* @__PURE__ */jsx(EditButton, {
|
|
1045
|
-
id: item._id,
|
|
1046
|
-
type: item._type,
|
|
1047
|
-
disabled: !userRoleCanDrop
|
|
1048
|
-
}), isLastState ? /* @__PURE__ */jsx(CompleteButton, {
|
|
1049
|
-
documentId,
|
|
1050
|
-
disabled: !userRoleCanDrop
|
|
1051
|
-
}) : null]
|
|
1052
|
-
})
|
|
1053
|
-
})]
|
|
1084
|
+
}) : null]
|
|
1085
|
+
})
|
|
1086
|
+
})]
|
|
1087
|
+
})
|
|
1054
1088
|
})
|
|
1055
|
-
})
|
|
1089
|
+
})]
|
|
1056
1090
|
});
|
|
1057
1091
|
}
|
|
1058
1092
|
function DocumentList(props) {
|
|
@@ -1351,12 +1385,12 @@ function FloatingCard(_ref3) {
|
|
|
1351
1385
|
}, "floater") : null
|
|
1352
1386
|
});
|
|
1353
1387
|
}
|
|
1354
|
-
function
|
|
1355
|
-
|
|
1388
|
+
function Verify(props) {
|
|
1389
|
+
const {
|
|
1356
1390
|
data,
|
|
1357
1391
|
userList,
|
|
1358
1392
|
states
|
|
1359
|
-
} =
|
|
1393
|
+
} = props;
|
|
1360
1394
|
const client = useClient({
|
|
1361
1395
|
apiVersion: API_VERSION
|
|
1362
1396
|
});
|
|
@@ -1587,10 +1621,10 @@ function WorkflowTool(props) {
|
|
|
1587
1621
|
} else {
|
|
1588
1622
|
const itemBefore = destinationStateItems[destination.index - 1];
|
|
1589
1623
|
const itemBeforeRank = (_e = itemBefore == null ? void 0 : itemBefore._metadata) == null ? void 0 : _e.orderRank;
|
|
1590
|
-
const itemBeforeRankParsed =
|
|
1624
|
+
const itemBeforeRankParsed = itemBeforeRank ? LexoRank.parse(itemBeforeRank) : LexoRank.min();
|
|
1591
1625
|
const itemAfter = destinationStateItems[destination.index];
|
|
1592
1626
|
const itemAfterRank = (_f = itemAfter == null ? void 0 : itemAfter._metadata) == null ? void 0 : _f.orderRank;
|
|
1593
|
-
const itemAfterRankParsed =
|
|
1627
|
+
const itemAfterRankParsed = itemAfterRank ? LexoRank.parse(itemAfterRank) : LexoRank.max();
|
|
1594
1628
|
newOrder = itemBeforeRankParsed.between(itemAfterRankParsed).toString();
|
|
1595
1629
|
}
|
|
1596
1630
|
move(draggableId, destination, states, newOrder);
|
|
@@ -1646,7 +1680,7 @@ function WorkflowTool(props) {
|
|
|
1646
1680
|
direction: "column",
|
|
1647
1681
|
height: "fill",
|
|
1648
1682
|
overflow: "hidden",
|
|
1649
|
-
children: [/* @__PURE__ */jsx(
|
|
1683
|
+
children: [/* @__PURE__ */jsx(Verify, {
|
|
1650
1684
|
data,
|
|
1651
1685
|
userList,
|
|
1652
1686
|
states
|