sanity-plugin-workflow 1.0.0-beta.4 → 1.0.0-beta.6

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/lib/index.js CHANGED
@@ -49,8 +49,8 @@ const DEFAULT_CONFIG = {
49
49
  title: "Approved",
50
50
  color: "success",
51
51
  roles: ["administrator"],
52
- requireAssignment: true,
53
- transitions: ["changesRequested"]
52
+ transitions: ["changesRequested"],
53
+ requireAssignment: true
54
54
  }])
55
55
  };
56
56
  function UserAssignment(props) {
@@ -304,11 +304,6 @@ function UpdateWorkflow(props, allStates, actionState) {
304
304
  id,
305
305
  type
306
306
  } = props;
307
- const {
308
- validation,
309
- isValidating
310
- } = sanity.useValidationStatus(id, type);
311
- const hasValidationErrors = !isValidating && (validation == null ? void 0 : validation.length) > 0 && validation.find(v => v.level === "error");
312
307
  const user = sanity.useCurrentUser();
313
308
  const client = sanity.useClient({
314
309
  apiVersion: API_VERSION
@@ -326,6 +321,11 @@ function UpdateWorkflow(props, allStates, actionState) {
326
321
  const {
327
322
  assignees = []
328
323
  } = (_a = data == null ? void 0 : data.metadata) != null ? _a : {};
324
+ const {
325
+ validation,
326
+ isValidating
327
+ } = sanity.useValidationStatus(id, type);
328
+ const hasValidationErrors = (currentState == null ? void 0 : currentState.requireValidation) && !isValidating && (validation == null ? void 0 : validation.length) > 0 && validation.find(v => v.level === "error");
329
329
  if (error) {
330
330
  console.error(error);
331
331
  }
@@ -355,40 +355,39 @@ function UpdateWorkflow(props, allStates, actionState) {
355
355
  const direction = actionStateIndex > currentStateIndex ? "promote" : "demote";
356
356
  const DirectionIcon = direction === "promote" ? icons.ArrowRightIcon : icons.ArrowLeftIcon;
357
357
  const directionLabel = direction === "promote" ? "Promote" : "Demote";
358
- let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
359
358
  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) ?
360
359
  // If the Action state is limited to specific roles
361
360
  // check that the current user has one of those roles
362
361
  arraysContainMatchingString(user.roles.map(r => r.name), actionState.roles) :
363
362
  // No roles specified on the next state, so anyone can update
364
363
  ((_d = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _d.length) !== 0;
365
- if (!userRoleCanUpdateState) {
366
- title = "Your User role cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
367
- }
368
364
  const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && currentState.transitions.length ?
369
365
  // If the Current State limits transitions to specific States
370
366
  // Check that the Action State is in Current State's transitions array
371
367
  currentState.transitions.includes(actionState.id) :
372
368
  // Otherwise this isn't a problem
373
369
  true;
374
- if (!actionStateIsAValidTransition) {
375
- title = "You cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\" from \"").concat(currentState == null ? void 0 : currentState.title, "\"");
376
- }
377
370
  const userAssignmentCanUpdateState = actionState.requireAssignment ?
378
371
  // If the Action State requires assigned users
379
372
  // Check the current user ID is in the assignees array
380
373
  currentUser && assignees.length && assignees.includes(currentUser.id) :
381
374
  // Otherwise this isn't a problem
382
375
  true;
383
- if (!userAssignmentCanUpdateState) {
376
+ let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
377
+ if (!userRoleCanUpdateState) {
378
+ title = "Your User role cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
379
+ } else if (!actionStateIsAValidTransition) {
380
+ title = "You cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\" from \"").concat(currentState == null ? void 0 : currentState.title, "\"");
381
+ } else if (!userAssignmentCanUpdateState) {
384
382
  title = "You must be assigned to the document to ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
385
- }
386
- if (hasValidationErrors) {
383
+ } else if ((currentState == null ? void 0 : currentState.requireValidation) && isValidating) {
384
+ title = "Document is validating, cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
385
+ } else if (hasValidationErrors) {
387
386
  title = "Document has validation errors, cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
388
387
  }
389
388
  return {
390
389
  icon: DirectionIcon,
391
- disabled: loading || error || isValidating || hasValidationErrors || !currentState || !userRoleCanUpdateState || !actionStateIsAValidTransition || !userAssignmentCanUpdateState,
390
+ disabled: loading || error || (currentState == null ? void 0 : currentState.requireValidation) && isValidating || hasValidationErrors || !currentState || !userRoleCanUpdateState || !actionStateIsAValidTransition || !userAssignmentCanUpdateState,
392
391
  title,
393
392
  label: actionState.title,
394
393
  onHandle: () => onHandle(id, actionState)
@@ -647,7 +646,7 @@ function useWorkflowDocuments(schemaTypes) {
647
646
  setLocalDocuments(data);
648
647
  }
649
648
  }, [data]);
650
- const move = React__default.default.useCallback((draggedId, destination, states, newOrder) => {
649
+ const move = React__default.default.useCallback(async (draggedId, destination, states, newOrder) => {
651
650
  const currentLocalData = localDocuments;
652
651
  const newLocalDocuments = localDocuments.map(item => {
653
652
  var _a;
@@ -694,10 +693,9 @@ function useWorkflowDocuments(schemaTypes) {
694
693
  _type
695
694
  } = document;
696
695
  const {
697
- _rev,
698
696
  documentId
699
697
  } = document._metadata || {};
700
- client.patch("workflow-metadata.".concat(documentId)).ifRevisionId(_rev).set({
698
+ await client.patch("workflow-metadata.".concat(documentId)).set({
701
699
  state: newStateId,
702
700
  orderRank: newOrder
703
701
  }).commit().then(() => {
@@ -706,11 +704,12 @@ function useWorkflowDocuments(schemaTypes) {
706
704
  title: "Moved to \"".concat((_a = newState == null ? void 0 : newState.title) != null ? _a : newStateId, "\""),
707
705
  status: "success"
708
706
  });
709
- }).catch(() => {
707
+ }).catch(err => {
710
708
  var _a;
711
709
  setLocalDocuments(currentLocalData);
712
710
  return toast.push({
713
711
  title: "Failed to move to \"".concat((_a = newState == null ? void 0 : newState.title) != null ? _a : newStateId, "\""),
712
+ description: err.message,
714
713
  status: "error"
715
714
  });
716
715
  });
@@ -921,6 +920,24 @@ function PublishedStatus(props) {
921
920
  })
922
921
  });
923
922
  }
923
+ function Validate(props) {
924
+ const {
925
+ documentId,
926
+ type,
927
+ onChange
928
+ } = props;
929
+ const {
930
+ isValidating,
931
+ validation = []
932
+ } = sanity.useValidationStatus(documentId, type);
933
+ React.useEffect(() => {
934
+ onChange({
935
+ isValidating,
936
+ validation
937
+ });
938
+ }, [onChange, isValidating, validation]);
939
+ return null;
940
+ }
924
941
  function ValidationStatus(props) {
925
942
  const {
926
943
  validation = []
@@ -946,7 +963,7 @@ function ValidationStatus(props) {
946
963
  });
947
964
  }
948
965
  function DocumentCard(props) {
949
- var _a;
966
+ var _a, _b;
950
967
  const {
951
968
  isDragDisabled,
952
969
  userRoleCanDrop,
@@ -961,18 +978,29 @@ function DocumentCard(props) {
961
978
  documentId
962
979
  } = (_a = item._metadata) != null ? _a : {};
963
980
  const schema = sanity.useSchema();
981
+ const state = states.find(s => {
982
+ var _a2;
983
+ return s.id === ((_a2 = item._metadata) == null ? void 0 : _a2.state);
984
+ });
964
985
  const isDarkMode = ui.useTheme().sanity.color.dark;
965
986
  const defaultCardTone = isDarkMode ? "transparent" : "default";
987
+ const [optimisticValidation, setOptimisticValidation] = React.useState({
988
+ isValidating: (_b = state == null ? void 0 : state.requireValidation) != null ? _b : false,
989
+ validation: []
990
+ });
966
991
  const {
967
- validation = [],
968
- isValidating
969
- } = sanity.useValidationStatus(documentId != null ? documentId : "", item._type);
992
+ isValidating,
993
+ validation
994
+ } = optimisticValidation;
995
+ const handleValidation = React.useCallback(updates => {
996
+ setOptimisticValidation(updates);
997
+ }, []);
970
998
  const cardTone = React.useMemo(() => {
971
999
  let tone = defaultCardTone;
972
1000
  if (!userRoleCanDrop) return isDarkMode ? "default" : "transparent";
973
1001
  if (!documentId) return tone;
974
1002
  if (isDragging) tone = "positive";
975
- if (!isValidating && validation.length > 0) {
1003
+ if ((state == null ? void 0 : state.requireValidation) && !isValidating && validation.length > 0) {
976
1004
  if (validation.some(v => v.level === "error")) {
977
1005
  tone = "critical";
978
1006
  } else {
@@ -980,7 +1008,7 @@ function DocumentCard(props) {
980
1008
  }
981
1009
  }
982
1010
  return tone;
983
- }, [isDarkMode, userRoleCanDrop, defaultCardTone, documentId, isDragging, validation, isValidating]);
1011
+ }, [defaultCardTone, userRoleCanDrop, isDarkMode, documentId, isDragging, isValidating, validation, state == null ? void 0 : state.requireValidation]);
984
1012
  React.useEffect(() => {
985
1013
  if (!isValidating && validation.length > 0) {
986
1014
  if (validation.some(v => v.level === "error")) {
@@ -997,75 +1025,81 @@ function DocumentCard(props) {
997
1025
  var _a2;
998
1026
  return states[states.length - 1].id === ((_a2 = item._metadata) == null ? void 0 : _a2.state);
999
1027
  }, [states, item._metadata.state]);
1000
- return /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1001
- paddingBottom: 3,
1002
- paddingX: 3,
1003
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1004
- radius: 2,
1005
- shadow: isDragging ? 3 : 1,
1006
- tone: cardTone,
1007
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Stack, {
1008
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1009
- borderBottom: true,
1010
- radius: 2,
1011
- padding: 3,
1012
- paddingLeft: 2,
1013
- tone: cardTone,
1014
- style: {
1015
- pointerEvents: "none"
1016
- },
1017
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1018
- align: "center",
1019
- justify: "space-between",
1020
- gap: 1,
1021
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1022
- flex: 1,
1023
- children: /* @__PURE__ */jsxRuntime.jsx(sanity.Preview, {
1024
- layout: "default",
1025
- value: item,
1026
- schemaType: schema.get(item._type)
1027
- })
1028
- }), /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1029
- style: {
1030
- flexShrink: 0
1031
- },
1032
- children: hasError || isDragDisabled ? null : /* @__PURE__ */jsxRuntime.jsx(icons.DragHandleIcon, {})
1033
- })]
1034
- })
1035
- }), /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1036
- padding: 2,
1037
- radius: 2,
1038
- tone: "inherit",
1039
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1040
- align: "center",
1041
- justify: "space-between",
1042
- gap: 3,
1043
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1044
- flex: 1,
1045
- children: documentId && /* @__PURE__ */jsxRuntime.jsx(UserDisplay, {
1046
- userList,
1047
- assignees,
1028
+ return /* @__PURE__ */jsxRuntime.jsxs(jsxRuntime.Fragment, {
1029
+ children: [(state == null ? void 0 : state.requireValidation) ? /* @__PURE__ */jsxRuntime.jsx(Validate, {
1030
+ documentId,
1031
+ type: item._type,
1032
+ onChange: handleValidation
1033
+ }) : null, /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1034
+ paddingBottom: 3,
1035
+ paddingX: 3,
1036
+ children: /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1037
+ radius: 2,
1038
+ shadow: isDragging ? 3 : 1,
1039
+ tone: cardTone,
1040
+ children: /* @__PURE__ */jsxRuntime.jsxs(ui.Stack, {
1041
+ children: [/* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1042
+ borderBottom: true,
1043
+ radius: 2,
1044
+ padding: 3,
1045
+ paddingLeft: 2,
1046
+ tone: cardTone,
1047
+ style: {
1048
+ pointerEvents: "none"
1049
+ },
1050
+ children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1051
+ align: "center",
1052
+ justify: "space-between",
1053
+ gap: 1,
1054
+ children: [/* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1055
+ flex: 1,
1056
+ children: /* @__PURE__ */jsxRuntime.jsx(sanity.Preview, {
1057
+ layout: "default",
1058
+ value: item,
1059
+ schemaType: schema.get(item._type)
1060
+ })
1061
+ }), /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1062
+ style: {
1063
+ flexShrink: 0
1064
+ },
1065
+ children: hasError || isDragDisabled ? null : /* @__PURE__ */jsxRuntime.jsx(icons.DragHandleIcon, {})
1066
+ })]
1067
+ })
1068
+ }), /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1069
+ padding: 2,
1070
+ radius: 2,
1071
+ tone: "inherit",
1072
+ children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1073
+ align: "center",
1074
+ justify: "space-between",
1075
+ gap: 3,
1076
+ children: [/* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1077
+ flex: 1,
1078
+ children: documentId && /* @__PURE__ */jsxRuntime.jsx(UserDisplay, {
1079
+ userList,
1080
+ assignees,
1081
+ documentId,
1082
+ disabled: !userRoleCanDrop
1083
+ })
1084
+ }), validation.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ValidationStatus, {
1085
+ validation
1086
+ }) : null, /* @__PURE__ */jsxRuntime.jsx(DraftStatus, {
1087
+ document: item
1088
+ }), /* @__PURE__ */jsxRuntime.jsx(PublishedStatus, {
1089
+ document: item
1090
+ }), /* @__PURE__ */jsxRuntime.jsx(EditButton, {
1091
+ id: item._id,
1092
+ type: item._type,
1093
+ disabled: !userRoleCanDrop
1094
+ }), isLastState ? /* @__PURE__ */jsxRuntime.jsx(CompleteButton, {
1048
1095
  documentId,
1049
1096
  disabled: !userRoleCanDrop
1050
- })
1051
- }), validation.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ValidationStatus, {
1052
- validation
1053
- }) : null, /* @__PURE__ */jsxRuntime.jsx(DraftStatus, {
1054
- document: item
1055
- }), /* @__PURE__ */jsxRuntime.jsx(PublishedStatus, {
1056
- document: item
1057
- }), /* @__PURE__ */jsxRuntime.jsx(EditButton, {
1058
- id: item._id,
1059
- type: item._type,
1060
- disabled: !userRoleCanDrop
1061
- }), isLastState ? /* @__PURE__ */jsxRuntime.jsx(CompleteButton, {
1062
- documentId,
1063
- disabled: !userRoleCanDrop
1064
- }) : null]
1065
- })
1066
- })]
1097
+ }) : null]
1098
+ })
1099
+ })]
1100
+ })
1067
1101
  })
1068
- })
1102
+ })]
1069
1103
  });
1070
1104
  }
1071
1105
  function DocumentList(props) {
@@ -1093,7 +1127,7 @@ function DocumentList(props) {
1093
1127
  return (_c = (_b = (_a = dataFiltered[index]) == null ? void 0 : _a._metadata) == null ? void 0 : _b.documentId) != null ? _c : index;
1094
1128
  },
1095
1129
  estimateSize: () => 113,
1096
- overscan: 5
1130
+ overscan: 10
1097
1131
  });
1098
1132
  if (!data.length) {
1099
1133
  return null;
@@ -1305,7 +1339,8 @@ function StateTitle(props) {
1305
1339
  requireAssignment,
1306
1340
  userRoleCanDrop,
1307
1341
  isDropDisabled,
1308
- draggingFrom
1342
+ draggingFrom,
1343
+ documentCount
1309
1344
  } = props;
1310
1345
  let tone = "default";
1311
1346
  const isSource = draggingFrom === state.id;
@@ -1330,7 +1365,15 @@ function StateTitle(props) {
1330
1365
  }), requireAssignment ? /* @__PURE__ */jsxRuntime.jsx(Status, {
1331
1366
  text: "You must be assigned to the document to move documents to this State",
1332
1367
  icon: icons.UserIcon
1333
- }) : null]
1368
+ }) : null, /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1369
+ flex: 1,
1370
+ children: documentCount > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Text, {
1371
+ weight: "semibold",
1372
+ align: "right",
1373
+ size: 1,
1374
+ children: documentCount
1375
+ }) : null
1376
+ })]
1334
1377
  })
1335
1378
  });
1336
1379
  }
@@ -1364,12 +1407,12 @@ function FloatingCard(_ref3) {
1364
1407
  }, "floater") : null
1365
1408
  });
1366
1409
  }
1367
- function Validators(_ref4) {
1368
- let {
1410
+ function Verify(props) {
1411
+ const {
1369
1412
  data,
1370
1413
  userList,
1371
1414
  states
1372
- } = _ref4;
1415
+ } = props;
1373
1416
  const client = sanity.useClient({
1374
1417
  apiVersion: API_VERSION
1375
1418
  });
@@ -1400,6 +1443,17 @@ function Validators(_ref4) {
1400
1443
  } = (_a = cur._metadata) != null ? _a : {};
1401
1444
  return !orderRank && documentId ? [...acc, documentId] : acc;
1402
1445
  }, []) : [];
1446
+ const documentsWithDuplicatedOrderIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
1447
+ var _a;
1448
+ const {
1449
+ documentId,
1450
+ orderRank
1451
+ } = (_a = cur._metadata) != null ? _a : {};
1452
+ return orderRank && data.filter(d => {
1453
+ var _a2;
1454
+ return ((_a2 = d._metadata) == null ? void 0 : _a2.orderRank) === orderRank;
1455
+ }).length > 1 && documentId ? [...acc, documentId] : acc;
1456
+ }, []) : [];
1403
1457
  const correctDocuments = React__default.default.useCallback(async ids => {
1404
1458
  toast.push({
1405
1459
  title: "Correcting...",
@@ -1500,6 +1554,10 @@ function Validators(_ref4) {
1500
1554
  tone: "caution",
1501
1555
  onClick: () => addOrderToDocuments(documentsWithoutOrderIds),
1502
1556
  text: documentsWithoutOrderIds.length === 1 ? "Set Order for 1 Document" : "Set Order for ".concat(documentsWithoutOrderIds.length, " Documents")
1557
+ }) : null, documentsWithDuplicatedOrderIds.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1558
+ tone: "caution",
1559
+ onClick: () => addOrderToDocuments(documentsWithDuplicatedOrderIds),
1560
+ text: documentsWithDuplicatedOrderIds.length === 1 ? "Set Unique Order for 1 Document" : "Set Unique Order for ".concat(documentsWithDuplicatedOrderIds.length, " Documents")
1503
1561
  }) : null, orphanedMetadataDocumentIds.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1504
1562
  text: "Cleanup orphaned metadata",
1505
1563
  onClick: handleOrphans,
@@ -1571,7 +1629,7 @@ function WorkflowTool(props) {
1571
1629
  setUndroppableStates(undroppableExceptSelf);
1572
1630
  }
1573
1631
  }, [data, states, user]);
1574
- const handleDragEnd = React__default.default.useCallback(result => {
1632
+ const handleDragEnd = React__default.default.useCallback(async result => {
1575
1633
  var _a2, _b2, _c2, _d, _e, _f;
1576
1634
  setUndroppableStates([]);
1577
1635
  setDraggingFrom("");
@@ -1600,10 +1658,10 @@ function WorkflowTool(props) {
1600
1658
  } else {
1601
1659
  const itemBefore = destinationStateItems[destination.index - 1];
1602
1660
  const itemBeforeRank = (_e = itemBefore == null ? void 0 : itemBefore._metadata) == null ? void 0 : _e.orderRank;
1603
- const itemBeforeRankParsed = itemBefore._metadata.orderRank ? lexorank.LexoRank.parse(itemBeforeRank) : lexorank.LexoRank.min();
1661
+ const itemBeforeRankParsed = itemBeforeRank ? lexorank.LexoRank.parse(itemBeforeRank) : lexorank.LexoRank.min();
1604
1662
  const itemAfter = destinationStateItems[destination.index];
1605
1663
  const itemAfterRank = (_f = itemAfter == null ? void 0 : itemAfter._metadata) == null ? void 0 : _f.orderRank;
1606
- const itemAfterRankParsed = itemAfter._metadata.orderRank ? lexorank.LexoRank.parse(itemAfterRank) : lexorank.LexoRank.max();
1664
+ const itemAfterRankParsed = itemAfterRank ? lexorank.LexoRank.parse(itemAfterRank) : lexorank.LexoRank.max();
1607
1665
  newOrder = itemBeforeRankParsed.between(itemAfterRankParsed).toString();
1608
1666
  }
1609
1667
  move(draggableId, destination, states, newOrder);
@@ -1659,7 +1717,7 @@ function WorkflowTool(props) {
1659
1717
  direction: "column",
1660
1718
  height: "fill",
1661
1719
  overflow: "hidden",
1662
- children: [/* @__PURE__ */jsxRuntime.jsx(Validators, {
1720
+ children: [/* @__PURE__ */jsxRuntime.jsx(Verify, {
1663
1721
  data,
1664
1722
  userList,
1665
1723
  states
@@ -1692,7 +1750,8 @@ function WorkflowTool(props) {
1692
1750
  requireAssignment: (_b2 = state.requireAssignment) != null ? _b2 : false,
1693
1751
  userRoleCanDrop,
1694
1752
  isDropDisabled,
1695
- draggingFrom
1753
+ draggingFrom,
1754
+ documentCount: filterItemsAndSort(data, state.id, selectedUserIds, selectedSchemaTypes).length
1696
1755
  }), /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1697
1756
  flex: 1,
1698
1757
  children: /* @__PURE__ */jsxRuntime.jsx(dnd.Droppable, {