@sanity/workflow-studio-plugin 0.27.0 → 0.28.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.
@@ -10,7 +10,7 @@ import { createContext, useContext, useEffect, useSyncExternalStore, useCallback
10
10
 
11
11
  import { usePaneRouter } from "sanity/structure";
12
12
 
13
- import { WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, tryParseGdr, resourceFromParsed, latestDeployedDefinitions, isStartableDefinition, isSubjectEntry, isInputSourced, acceptsDocumentType, initialFieldIssues, gdrRef, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, EditFieldDeniedError, errorMessage, terminalState, findOpenStageEntry, actionRendering, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, ENGINE_API_VERSION, scalarValidationIssues, classifyPrincipalId, userLoginProvider, toBareId, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, parseDefinitionSnapshot, isUnprimed, isTerminalStage, DEFAULT_TRANSITION_WHEN, instancesQuery, assertReadableModel, projectStartSliceRow, StartNotSettledError, StartNotAllowedError, humanize, unboundRequirementReads, explainStartRequirement, singleSubjectRequirementRefused, evaluateStartFilter, startKindOf, hasSingleSubjectRequirement, readsRootDocument, latestDefinitionsGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, definitionLookupGroq, instanceDocId, missingRequiredInputs, documentActionDenials } from "@sanity/workflow-engine";
13
+ import { WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, tryParseGdr, resourceFromParsed, latestDeployedDefinitions, isStartableDefinition, isSubjectEntry, isInputSourced, acceptsDocumentType, initialFieldIssues, gdrRef, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, EditFieldDeniedError, errorMessage, terminalState, findOpenStageEntry, actionRendering, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, ENGINE_API_VERSION, scalarValidationIssues, classifyPrincipalId, userLoginProvider, toBareId, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, parseDefinitionSnapshot, isUnprimed, isTerminalStage, DEFAULT_TRANSITION_WHEN, instancesQuery, assertReadableModel, projectStartSliceRow, StartNotSettledError, StartNotAllowedError, isRevisionConflict, humanize, unboundRequirementReads, explainStartRequirement, singleSubjectRequirementRefused, evaluateStartFilter, startKindOf, hasSingleSubjectRequirement, readsRootDocument, latestDefinitionsGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, definitionLookupGroq, instanceDocId, missingRequiredInputs, documentActionDenials } from "@sanity/workflow-engine";
14
14
 
15
15
  import { CheckmarkIcon } from "@sanity/icons/Checkmark";
16
16
 
@@ -78,6 +78,8 @@ import { CogIcon } from "@sanity/icons/Cog";
78
78
 
79
79
  import { ArrowUpIcon } from "@sanity/icons/ArrowUp";
80
80
 
81
+ import { ErrorOutlineIcon } from "@sanity/icons/ErrorOutline";
82
+
81
83
  import { of } from "rxjs";
82
84
 
83
85
  import { switchMap, distinctUntilChanged } from "rxjs/operators";
@@ -796,13 +798,25 @@ function isItemAssignedTo(item, who) {
796
798
  return item.assignee != null && assigneeMatches(item.assignee, who);
797
799
  }
798
800
 
801
+ function matchingAssignees(state, who) {
802
+ return state.flatMap(entry => entry._type === "assignees" ? entry.value.filter(a => assigneeMatches(a, who)) : entry._type === "assignee" && entry.value !== null && assigneeMatches(entry.value, who) ? [ entry.value ] : []);
803
+ }
804
+
799
805
  function isActivityAssignedTo(args) {
800
- return args.state.some(entry => entry._type === "assignees" ? entry.value.some(a => assigneeMatches(a, args.who)) : entry._type === "assignee" ? entry.value !== null && assigneeMatches(entry.value, args.who) : !1);
806
+ return matchingAssignees(args.state, args.who).length > 0;
807
+ }
808
+
809
+ function isRoleAssignedTo(args) {
810
+ return matchingAssignees(args.state, args.who).some(member => member.type === "role");
811
+ }
812
+
813
+ function isItemRoleAssignedTo(item, who) {
814
+ return isItemAssignedTo(item, who) && item.assignee?.type === "role";
801
815
  }
802
816
 
803
817
  function assignedWork(args) {
804
- const {instance: instance, committed: committed, who: who} = args, evaluation = args.staleEvaluation === "keep" || isEvaluationCurrent(args) ? args.evaluation : void 0, byActivity = stateByActivity(instance), activities = (evaluation?.currentStage.activities ?? []).filter(t => isActivityAssignedTo({
805
- state: byActivity.get(t.activity.name) ?? [],
818
+ const {instance: instance, committed: committed, who: who} = args, evaluation = args.staleEvaluation === "keep" || isEvaluationCurrent(args) ? args.evaluation : void 0, byActivity = stateByActivity(instance), stateOf = t => byActivity.get(t.activity.name) ?? [], activities = (evaluation?.currentStage.activities ?? []).filter(t => isActivityAssignedTo({
819
+ state: stateOf(t),
806
820
  who: who
807
821
  })), items = deriveTodoItems({
808
822
  instance: instance,
@@ -812,7 +826,11 @@ function assignedWork(args) {
812
826
  return {
813
827
  activities: activities,
814
828
  openItems: items.filter(row => !isTodoDone(row.item)),
815
- doneItems: items.filter(row => isTodoDone(row.item))
829
+ doneItems: items.filter(row => isTodoDone(row.item)),
830
+ roleMatched: activities.some(t => isRoleAssignedTo({
831
+ state: stateOf(t),
832
+ who: who
833
+ })) || items.some(row => isItemRoleAssignedTo(row.item, who))
816
834
  };
817
835
  }
818
836
 
@@ -861,7 +879,8 @@ function forMeWorkOf(entries, identity) {
861
879
  return {
862
880
  work: work,
863
881
  count: work.reduce((n, {count: count}) => n + count, 0),
864
- hasRows: work.some(({shown: shown}) => hasAssignedRows(shown))
882
+ hasRows: work.some(({shown: shown}) => hasAssignedRows(shown)),
883
+ hasRoleMatched: work.some(({shown: shown}) => shown.roleMatched)
865
884
  };
866
885
  }
867
886
 
@@ -1435,9 +1454,10 @@ function projectMembersFrom(users) {
1435
1454
  }
1436
1455
 
1437
1456
  function useProjectMembers() {
1438
- const {users: users, loading: loading, error: error} = useStudioProjectUsers();
1457
+ const {users: users, roles: roles, loading: loading, error: error} = useStudioProjectUsers();
1439
1458
  return {
1440
1459
  members: useMemo(() => projectMembersFrom(users), [ users ]),
1460
+ roles: roles,
1441
1461
  loading: loading,
1442
1462
  error: error === void 0 ? void 0 : errorMessage(error)
1443
1463
  };
@@ -1791,10 +1811,12 @@ function PickedValueRow({children: children, onRequestChange: onRequestChange})
1791
1811
  }
1792
1812
 
1793
1813
  function MemberPicker({selectedIds: selectedIds, onSelect: onSelect, unassignRow: unassignRow = !1}) {
1794
- const state = useProjectMembers();
1814
+ const {error: error, loading: loading, members: members} = useProjectMembers();
1795
1815
  /* @__PURE__ */
1796
1816
  return jsx(MemberPicker$1, {
1797
- ...state,
1817
+ error: error,
1818
+ loading: loading,
1819
+ members: members,
1798
1820
  onSelect: onSelect,
1799
1821
  selectedIds: selectedIds,
1800
1822
  unassignRow: unassignRow
@@ -2578,20 +2600,20 @@ function noteRow(args) {
2578
2600
  }
2579
2601
 
2580
2602
  function AssigneeBadge({assignee: assignee}) {
2581
- const {members: members} = useProjectMembers();
2603
+ const vocabulary = useProjectMembers();
2582
2604
  /* @__PURE__ */
2583
2605
  return jsx(AssigneeBadge$1, {
2584
2606
  assignee: assignee,
2585
- members: members
2607
+ vocabulary: vocabulary
2586
2608
  });
2587
2609
  }
2588
2610
 
2589
2611
  function AssigneeBadges({assignees: assignees}) {
2590
- const {members: members} = useProjectMembers();
2612
+ const vocabulary = useProjectMembers();
2591
2613
  /* @__PURE__ */
2592
2614
  return jsx(AssigneeBadges$1, {
2593
2615
  assignees: assignees,
2594
- members: members
2616
+ vocabulary: vocabulary
2595
2617
  });
2596
2618
  }
2597
2619
 
@@ -4218,7 +4240,7 @@ function AvatarButton({avatar: avatar, onClick: onClick, onMouseDown: onMouseDow
4218
4240
  }
4219
4241
 
4220
4242
  function RowAssignees({assignees: assignees, hint: hint}) {
4221
- const ids = assignees.flatMap(item => item.type === "user" ? [ item.id ] : []), {members: members} = useProjectMembers(), roles = assignees.flatMap(item => item.type === "role" ? [ memberRoleFor(members, item.role) ] : []), displays = useUserDisplays(ids);
4243
+ const ids = assignees.flatMap(item => item.type === "user" ? [ item.id ] : []), vocabulary = useProjectMembers(), roles = assignees.flatMap(item => item.type === "role" ? [ memberRoleFor(vocabulary, item.role) ] : []), displays = useUserDisplays(ids);
4222
4244
  if (ids.length === 0 && roles.length === 0) return null;
4223
4245
  const bothKinds = roles.length > 0 && displays.length > 0;
4224
4246
  /* @__PURE__ */
@@ -6088,7 +6110,7 @@ function AvatarCapAligned({children: children}) {
6088
6110
  }
6089
6111
 
6090
6112
  function AssigneesFace({assignees: assignees}) {
6091
- const {members: members} = useProjectMembers(), userIds = assignees.flatMap(a => a.type === "user" ? [ a.id ] : []), roles = assignees.flatMap(a => a.type === "role" ? [ roleLabelFor(members, a.role) ] : []);
6113
+ const vocabulary = useProjectMembers(), userIds = assignees.flatMap(a => a.type === "user" ? [ a.id ] : []), roles = assignees.flatMap(a => a.type === "role" ? [ roleLabelFor(vocabulary, a.role) ] : []);
6092
6114
  /* @__PURE__ */
6093
6115
  return jsxs(Fragment, {
6094
6116
  children: [ userIds.length > 0 ? /* @__PURE__ */ jsx(AvatarCapAligned, {
@@ -7848,18 +7870,23 @@ function StageSection({entry: entry, onOpenActivity: onOpenActivity, surface: su
7848
7870
  });
7849
7871
  }
7850
7872
 
7851
- function CautionNote({action: action, label: label}) {
7873
+ const BANNER_PADDING = 3, ICONS = {
7874
+ caution: /* @__PURE__ */ jsx(WarningOutlineIcon, {}),
7875
+ critical: /* @__PURE__ */ jsx(ErrorOutlineIcon, {})
7876
+ };
7877
+
7878
+ function NoteBanner({action: action, label: label, tone: tone = "caution"}) {
7852
7879
  /* @__PURE__ */
7853
7880
  return jsxs(Card, {
7854
7881
  paddingLeft: 3,
7855
7882
  paddingRight: 2,
7856
- paddingY: 3,
7883
+ paddingY: BANNER_PADDING,
7857
7884
  radius: 3,
7858
7885
  style: {
7859
7886
  alignItems: "center",
7860
7887
  display: "flex"
7861
7888
  },
7862
- tone: "caution",
7889
+ tone: tone,
7863
7890
  children: [
7864
7891
  /* @__PURE__ */ jsxs(Flex, {
7865
7892
  align: "center",
@@ -7869,7 +7896,7 @@ function CautionNote({action: action, label: label}) {
7869
7896
  /* @__PURE__ */ jsx(Text, {
7870
7897
  muted: !0,
7871
7898
  size: 1,
7872
- children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
7899
+ children: ICONS[tone]
7873
7900
  }),
7874
7901
  /* @__PURE__ */ jsx(Text, {
7875
7902
  muted: !0,
@@ -7906,7 +7933,7 @@ function UnreadableDocsNote({unreadable: unreadable}) {
7906
7933
  if (unreadable.length === 0) return null;
7907
7934
  const label = unreadable.length === 1 ? "1 workflow document isn’t listed — this Studio can’t read it. Share the details with your Studio maintainers." : `${unreadable.length} workflow documents aren’t listed — this Studio can’t read them. Share the details with your Studio maintainers.`;
7908
7935
  /* @__PURE__ */
7909
- return jsx(CautionNote, {
7936
+ return jsx(NoteBanner, {
7910
7937
  action: /* @__PURE__ */ jsx(CopyDetailsButton, {
7911
7938
  unreadable: unreadable
7912
7939
  }),
@@ -7952,6 +7979,21 @@ function InvalidDocNotice({invalid: invalid}) {
7952
7979
  });
7953
7980
  }
7954
7981
 
7982
+ function RoleAssignedNotice() {
7983
+ /* @__PURE__ */
7984
+ return jsx(Box, {
7985
+ padding: 2,
7986
+ children: /* @__PURE__ */ jsx(Text, {
7987
+ muted: !0,
7988
+ size: 1,
7989
+ style: {
7990
+ fontStyle: "italic"
7991
+ },
7992
+ children: "Including tasks assigned to your role"
7993
+ })
7994
+ });
7995
+ }
7996
+
7955
7997
  function TabSwitch({ariaControls: ariaControls, idPrefix: idPrefix, options: options, selected: selected, onSelect: onSelect}) {
7956
7998
  /* @__PURE__ */
7957
7999
  return jsx(TabList, {
@@ -8216,8 +8258,15 @@ function presentedResumeEntry(args) {
8216
8258
  if (!args.startNew) return findResumeEntry(args);
8217
8259
  }
8218
8260
 
8261
+ function settledByAnotherWriter(cause, instance) {
8262
+ return isRevisionConflict(cause) && terminalState(instance) === "completed";
8263
+ }
8264
+
8219
8265
  function classifyStartError(err) {
8220
- return err instanceof StartNotSettledError ? {
8266
+ return err instanceof StartNotSettledError ? err.instance !== void 0 && settledByAnotherWriter(err.cause, err.instance) ? {
8267
+ kind: "started-settled-elsewhere",
8268
+ instance: err.instance
8269
+ } : {
8221
8270
  kind: "started-not-settled",
8222
8271
  instance: err.instance,
8223
8272
  description: describeError(err.cause)
@@ -8230,8 +8279,16 @@ function classifyStartError(err) {
8230
8279
  };
8231
8280
  }
8232
8281
 
8282
+ function startProducedRun(outcome) {
8283
+ return outcome.kind === "started-not-settled" || outcome.kind === "started-settled-elsewhere";
8284
+ }
8285
+
8233
8286
  function startOutcomeToast(outcome, label) {
8234
- return outcome.kind === "started-not-settled" ? {
8287
+ return outcome.kind === "started-settled-elsewhere" ? {
8288
+ id: TOAST_ID.start,
8289
+ status: "info",
8290
+ title: `“${label}” started and finished`
8291
+ } : outcome.kind === "started-not-settled" ? {
8235
8292
  id: TOAST_ID.start,
8236
8293
  status: "warning",
8237
8294
  title: `“${label}” started but didn’t finish`,
@@ -8962,10 +9019,10 @@ function OverviewEmptyState({mappings: mappings, docId: docId, initialValue: ini
8962
9019
  }
8963
9020
 
8964
9021
  function ForMeTab({forMe: forMe, assignedWorkUnknown: assignedWorkUnknown, onOpenActivity: onOpenActivity}) {
8965
- return forMe.hasRows ? /* @__PURE__ */ jsx(Stack, {
9022
+ return forMe.hasRows ? /* @__PURE__ */ jsxs(Stack, {
8966
9023
  gap: GROUP_GAP,
8967
9024
  paddingBottom: 4,
8968
- children: forMe.work.map(({entry: entry, who: who, shown: shown}) => /* @__PURE__ */ jsx(ForMeList, {
9025
+ children: [ forMe.hasRoleMatched ? /* @__PURE__ */ jsx(RoleAssignedNotice, {}) : null, forMe.work.map(({entry: entry, who: who, shown: shown}) => /* @__PURE__ */ jsx(ForMeList, {
8969
9026
  entry: entry,
8970
9027
  identity: who,
8971
9028
  work: shown,
@@ -8974,7 +9031,7 @@ function ForMeTab({forMe: forMe, assignedWorkUnknown: assignedWorkUnknown, onOpe
8974
9031
  activityName: activityName,
8975
9032
  stage: entry.instance.currentStage
8976
9033
  })
8977
- }, entry.instance._id))
9034
+ }, entry.instance._id)) ]
8978
9035
  }) : assignedWorkUnknown ? null : /* @__PURE__ */ jsx(ForMeEmptyState, {});
8979
9036
  }
8980
9037
 
@@ -10211,7 +10268,7 @@ function ResumeNote({onStartNew: onStartNew}) {
10211
10268
 
10212
10269
  function handleStartError(args) {
10213
10270
  const {label: label, toast: toast, seedInstance: seedInstance, onClose: onClose} = args, outcome = classifyStartError(args.err);
10214
- toast.push(startOutcomeToast(outcome, label)), outcome.kind === "started-not-settled" && (outcome.instance !== void 0 && seedInstance(outcome.instance),
10271
+ toast.push(startOutcomeToast(outcome, label)), startProducedRun(outcome) && (outcome.instance !== void 0 && seedInstance(outcome.instance),
10215
10272
  onClose());
10216
10273
  }
10217
10274
 
@@ -10564,7 +10621,7 @@ async function startRequest(engine, request) {
10564
10621
  };
10565
10622
  } catch (err) {
10566
10623
  const outcome = classifyStartError(err);
10567
- return outcome.kind === "started-not-settled" ? outcome.instance ? {
10624
+ return startProducedRun(outcome) ? outcome.instance ? {
10568
10625
  instance: outcome.instance
10569
10626
  } : {} : outcome.kind === "not-allowed" ? {} : {
10570
10627
  failure: outcome.description
@@ -11163,4 +11220,4 @@ function WorkflowRootInput(props) {
11163
11220
  });
11164
11221
  }
11165
11222
 
11166
- export { AbortWorkflowDialog, ActivityDetailDialog, ActivityLog, ActivityRow, BreadcrumbTail, CautionNote, CodeChip, CountedLabel, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, ForMeEmptyState, Hairline, HoverHint, InstanceSnapshotBody, InvalidDocNotice, LinkChip, LoadingRow, LogEventOnMount, MetaRow, SpinnerSlot, StageFace, StaleLock, TOAST_ID, TOOL_TABS, TabSwitch, UnreadableDocsNote, WORKFLOW_API_VERSION, WORKFLOW_PAGE_TABS, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTaskFiltersApplied, WorkflowTitleSeedDrifted, WorkflowToolOpened, activityRowProps, assigneesOf, backToTabLabel, committedRowFace, createSubscribers, datesOf, definitionFingerprint, definitionSnapshotOf, describeError, dueDatesOf, findActivity, findActivityNode, formatDate, formatShortDateTime, formatTimeAgo, gdrLocality, instanceBreadcrumb, instanceState, instanceTitle, isActivityAssignedTo, isEvaluationStale, isLiveEntry, isOpenActivityStatus, landingState, mappingIssueDetail, namesNoDeployedDefinition, openActivityGone, openableSchemaType, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, stageTitle, stateByActivity, tabSelectionNavigates, toolRoute, toolTabState, useAssignmentIdentity, useBadgeCapTrim, useContainerToken, useDefinition, useDelayedFlag, useLogEventOnMount, useProjectMembers, useSpaceToken, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, useWorkflowToast, workflowDefaultDocumentNode, workflowPageState, workflowState, workflowStudioPlugin, workflowsView };
11223
+ export { AbortWorkflowDialog, ActivityDetailDialog, ActivityLog, ActivityRow, BreadcrumbTail, CodeChip, CountedLabel, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, ForMeEmptyState, Hairline, HoverHint, InstanceSnapshotBody, InvalidDocNotice, LinkChip, LoadingRow, LogEventOnMount, MetaRow, NoteBanner, RoleAssignedNotice, SpinnerSlot, StageFace, StaleLock, TOAST_ID, TOOL_TABS, TabSwitch, UnreadableDocsNote, WORKFLOW_API_VERSION, WORKFLOW_PAGE_TABS, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTaskFiltersApplied, WorkflowTitleSeedDrifted, WorkflowToolOpened, activityRowProps, assigneesOf, backToTabLabel, committedRowFace, createSubscribers, datesOf, definitionFingerprint, definitionSnapshotOf, describeError, dueDatesOf, findActivity, findActivityNode, formatDate, formatShortDateTime, formatTimeAgo, gdrLocality, instanceBreadcrumb, instanceState, instanceTitle, isActivityAssignedTo, isEvaluationStale, isLiveEntry, isOpenActivityStatus, isRoleAssignedTo, landingState, mappingIssueDetail, namesNoDeployedDefinition, openActivityGone, openableSchemaType, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, stageTitle, stateByActivity, tabSelectionNavigates, toolRoute, toolTabState, useAssignmentIdentity, useBadgeCapTrim, useContainerToken, useDefinition, useDelayedFlag, useLogEventOnMount, useProjectMembers, useSpaceToken, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, useWorkflowToast, workflowDefaultDocumentNode, workflowPageState, workflowState, workflowStudioPlugin, workflowsView };