@sanity/workflow-studio-plugin 0.29.0 → 0.31.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.
@@ -18,7 +18,7 @@ import { CloseIcon } from "@sanity/icons/Close";
18
18
 
19
19
  import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
20
20
 
21
- import { sameAssignee, projectMemberRow, ClearableDatePicker, AssigneePicker as AssigneePicker$1, MemberPicker as MemberPicker$1, MemberAvatar, AssigneeBadges as AssigneeBadges$1, AssigneeBadge as AssigneeBadge$1, ProgressBar, memberRoleFor, AssigneeStack, roleLabel, clampProgress, MemberAvatarGroup, progressCaption, DatePicker, roleLabelFor, roleListLabel } from "@sanity/workflow-components";
21
+ import { sameAssignee, projectMemberRow, providerTitle, ClearableDatePicker, AssigneePicker as AssigneePicker$1, MemberPicker as MemberPicker$1, MemberAvatar, AssigneeBadges as AssigneeBadges$1, AssigneeBadge as AssigneeBadge$1, ProgressBar, memberRoleFor, AssigneeStack, roleLabel, clampProgress, MemberAvatarGroup, progressCaption, DatePicker, roleLabelFor, roleListLabel } from "@sanity/workflow-components";
22
22
 
23
23
  import { getPublishedId, getDraftId } from "@sanity/client/csm";
24
24
 
@@ -26,7 +26,7 @@ import { defineEvent } from "@sanity/telemetry";
26
26
 
27
27
  import { useStudioProjectUsers, useStudioObserver, useWorkflowEngine, useWorkflowInstances, useWorkflowSession } from "@sanity/workflow-studio";
28
28
 
29
- import { useCurrentUser, Preview, SanityDefaultPreview, useValuePreview, useClient, useSchema, useWorkspace, TextWithTone, usePerspective, useIsReleaseActive, useDocumentPreviewStore, useActiveReleases, useTemplates, useInitialValueResolverContext, resolveInitialValue, definePlugin, isObjectInputProps } from "sanity";
29
+ import { useCurrentUser, Preview, useValuePreview, SanityDefaultPreview, useClient, useSchema, useWorkspace, TextWithTone, usePerspective, useIsReleaseActive, useDocumentPreviewStore, useActiveReleases, useTemplates, useInitialValueResolverContext, resolveInitialValue, definePlugin, isObjectInputProps } from "sanity";
30
30
 
31
31
  import { CalendarIcon } from "@sanity/icons/Calendar";
32
32
 
@@ -367,7 +367,7 @@ function landingDefaultOpen(landing, instanceId) {
367
367
  if (!(landing === void 0 || !landing.instanceIds.includes(instanceId))) return instanceId === landing.focusedId;
368
368
  }
369
369
 
370
- const WORKFLOWS_TOOL_NAME = "workflows", WORKFLOW_INTENT = "workflow";
370
+ const WORKFLOWS_TOOL_NAME = "workflows", WORKFLOW_INTENT = "workflow", RUN_SEARCH_PARAM = "run";
371
371
 
372
372
  function pathSegmentName(args) {
373
373
  return args.names.find(name => name === args.segment);
@@ -379,9 +379,10 @@ function handlesWorkflowIntent(intent, params) {
379
379
 
380
380
  function workflowIntentState(intent, params) {
381
381
  const instanceId = params.instanceId;
382
- return intent === WORKFLOW_INTENT && typeof instanceId == "string" ? {
383
- instanceId: instanceId
384
- } : null;
382
+ return intent !== WORKFLOW_INTENT || typeof instanceId != "string" ? null : {
383
+ workflowsTab: "overview",
384
+ _searchParams: [ [ RUN_SEARCH_PARAM, instanceId ] ]
385
+ };
385
386
  }
386
387
 
387
388
  const pad2 = n => String(n).padStart(2, "0"), DUE_DATE_FIELD_KINDS = [ "dueDate", "dueDatetime" ], DATE_FIELD_KINDS = [ "date", "datetime", ...DUE_DATE_FIELD_KINDS ], DATE_FIELD_KIND_SET = new Set(DATE_FIELD_KINDS), DUE_DATE_FIELD_KIND_SET = new Set(DUE_DATE_FIELD_KINDS);
@@ -478,14 +479,22 @@ function formatTimeAgo(value) {
478
479
  }
479
480
 
480
481
  function formatShortAgo(value, now) {
481
- const d = new Date(value);
482
- if (Number.isNaN(d.getTime())) return "—";
483
- const seconds = Math.floor((now.getTime() - d.getTime()) / 1e3);
484
- if (seconds < 60) return "just now";
482
+ const span = formatShortSpan(value, now);
483
+ return span === "—" ? "—" : span === "now" ? "just now" : `${span} ago`;
484
+ }
485
+
486
+ function formatShortSpan(value, now) {
487
+ return shortSpanOf(new Date(value), now);
488
+ }
489
+
490
+ function shortSpanOf(from, now) {
491
+ if (from === void 0 || Number.isNaN(from.getTime())) return "—";
492
+ const seconds = Math.floor((now.getTime() - from.getTime()) / 1e3);
493
+ if (seconds < 60) return "now";
485
494
  const minutes = Math.floor(seconds / 60);
486
- if (minutes < 60) return `${minutes}m ago`;
495
+ if (minutes < 60) return `${minutes}m`;
487
496
  const hours = Math.floor(minutes / 60);
488
- return hours < 24 ? `${hours}h ago` : `${Math.floor(hours / 24)}d ago`;
497
+ return hours < 24 ? `${hours}h` : `${Math.floor(hours / 24)}d`;
489
498
  }
490
499
 
491
500
  function stampFace(verb, at) {
@@ -709,7 +718,7 @@ function todoFieldEditability(args) {
709
718
  }
710
719
 
711
720
  function itemRow(args) {
712
- const {item: item, items: items, field: field, source: source, ticks: ticks, editability: editability} = args, tick = ticks.get(tickKey(field.name, item._key));
721
+ const {item: item, items: items, field: field, source: source, ticks: ticks, editability: editability} = args, tick = ticks.get(tickKey(field.name, item._key)), eligibleAssigneeRoles = field.of.find(column => column.name === "assignee" && column.type === "assignee")?.roles;
713
722
  return {
714
723
  item: item,
715
724
  items: items,
@@ -724,6 +733,9 @@ function itemRow(args) {
724
733
  ...editability.editDisabledReason ? {
725
734
  editDisabledReason: editability.editDisabledReason
726
735
  } : {},
736
+ ...eligibleAssigneeRoles === void 0 ? {} : {
737
+ eligibleAssigneeRoles: eligibleAssigneeRoles
738
+ },
727
739
  ...tick ? {
728
740
  tick: tick
729
741
  } : {}
@@ -792,6 +804,10 @@ function assigneesOf(entries) {
792
804
  return entries.flatMap(entry => entry._type === "assignees" ? entry.value : []);
793
805
  }
794
806
 
807
+ function userIdsOf(assignees) {
808
+ return assignees.flatMap(assignee => assignee.type === "user" ? [ assignee.id ] : []);
809
+ }
810
+
795
811
  function assigneeEdit(current, next) {
796
812
  const changed = changedAssignee(current, next);
797
813
  if (changed !== void 0) return next.some(item => sameAssignee(item, changed)) ? {
@@ -971,8 +987,15 @@ const ACTIVITY_STATUS_LABEL = {
971
987
  skipped: "Skipped"
972
988
  };
973
989
 
990
+ function isHandsOff(args) {
991
+ return args.classification === "autonomous" && args.completesWithoutCaller === "yes";
992
+ }
993
+
974
994
  function showsAutomatedPill(activityEval) {
975
- return !isTerminalActivityStatus(activityEval.status) && activityEval.classification === "autonomous" && activityEval.autonomy.completesWithoutCaller === "yes";
995
+ return !isTerminalActivityStatus(activityEval.status) && isHandsOff({
996
+ classification: activityEval.classification,
997
+ completesWithoutCaller: activityEval.autonomy.completesWithoutCaller
998
+ });
976
999
  }
977
1000
 
978
1001
  function showsBlockedTag(activityEval) {
@@ -1123,24 +1146,10 @@ function dateValuesOf(state, matches) {
1123
1146
  return state.filter(entry => matches(entry._type)).map(entry => entry.value).filter(value => typeof value == "string" && value !== "");
1124
1147
  }
1125
1148
 
1126
- function datesOf(state) {
1127
- return dateValuesOf(state, isDateFieldKind);
1128
- }
1129
-
1130
1149
  function dueDatesOf(state) {
1131
1150
  return dateValuesOf(state, isDueDateFieldKind);
1132
1151
  }
1133
1152
 
1134
- function committedRowFace(row) {
1135
- return {
1136
- activityName: row.activityName,
1137
- title: row.title,
1138
- status: row.status,
1139
- automated: !1,
1140
- blocked: !1
1141
- };
1142
- }
1143
-
1144
1153
  function activityRowProps(args) {
1145
1154
  const {activityEval: activityEval, activityState: activityState, editableFields: editableFields} = args, state = activityState.get(activityEval.activity.name) ?? [], controlArgs = {
1146
1155
  activity: activityEval.activity,
@@ -1239,15 +1248,15 @@ function useWorkflowInstanceEntry(instanceId, options = {}) {
1239
1248
  }
1240
1249
 
1241
1250
  const WorkflowToolOpened = defineEvent({
1242
- name: "Editorial Workflows Studio Plugin Tool Opened",
1243
- version: 2,
1251
+ name: "Workflows Studio Plugin Tool Opened",
1252
+ version: 3,
1244
1253
  description: "The Workflows tool was opened or its tab was switched"
1245
1254
  }), WorkflowDocumentViewOpened = defineEvent({
1246
- name: "Editorial Workflows Studio Plugin Document View Opened",
1255
+ name: "Workflows Studio Plugin Document View Opened",
1247
1256
  version: 1,
1248
1257
  description: "The Workflows document view was opened or its tab was switched"
1249
1258
  }), WorkflowInstanceDetailViewed = defineEvent({
1250
- name: "Editorial Workflows Studio Plugin Instance Detail Viewed",
1259
+ name: "Workflows Studio Plugin Instance Detail Viewed",
1251
1260
  version: 1,
1252
1261
  description: "The Workflows tool's per-instance detail view was opened"
1253
1262
  });
@@ -1260,67 +1269,63 @@ function definitionFingerprint(definition) {
1260
1269
  }
1261
1270
 
1262
1271
  const WorkflowDefinitionDetailViewed = defineEvent({
1263
- name: "Editorial Workflows Studio Plugin Definition Detail Viewed",
1272
+ name: "Workflows Studio Plugin Definition Detail Viewed",
1264
1273
  version: 1,
1265
1274
  description: "A workflow's definition page was opened in the Workflows tool"
1266
1275
  }), WorkflowFormStripClicked = defineEvent({
1267
- name: "Editorial Workflows Studio Plugin Form Strip Clicked",
1276
+ name: "Workflows Studio Plugin Form Strip Clicked",
1268
1277
  version: 1,
1269
1278
  description: "The form strip's instance line was clicked, opening the document view"
1270
1279
  }), WorkflowDocumentLinkClicked = defineEvent({
1271
- name: "Editorial Workflows Studio Plugin Document Link Clicked",
1272
- version: 2,
1280
+ name: "Workflows Studio Plugin Document Link Clicked",
1281
+ version: 3,
1273
1282
  description: "A document link was clicked, opening the referenced document's editor"
1274
1283
  }), WorkflowStartDialogOpened = defineEvent({
1275
- name: "Editorial Workflows Studio Plugin Start Dialog Opened",
1284
+ name: "Workflows Studio Plugin Start Dialog Opened",
1276
1285
  version: 1,
1277
1286
  description: "The start-workflow dialog was opened"
1278
1287
  }), WorkflowStartDialogSubmitted = defineEvent({
1279
- name: "Editorial Workflows Studio Plugin Start Dialog Submitted",
1288
+ name: "Workflows Studio Plugin Start Dialog Submitted",
1280
1289
  version: 1,
1281
1290
  description: "The start-workflow dialog's confirm was pressed"
1282
1291
  }), WorkflowAutoStartRan = defineEvent({
1283
- name: "Editorial Workflows Studio Plugin Auto Start Ran",
1292
+ name: "Workflows Studio Plugin Auto Start Ran",
1284
1293
  version: 1,
1285
1294
  description: "A fresh document's auto-start ran — one start request per configured workflow"
1286
1295
  }), WorkflowActionControlUsed = defineEvent({
1287
- name: "Editorial Workflows Studio Plugin Action Control Used",
1288
- version: 2,
1296
+ name: "Workflows Studio Plugin Action Control Used",
1297
+ version: 3,
1289
1298
  description: "An action-firing control was used, attributed to its UI surface"
1290
1299
  }), WorkflowFieldControlUsed = defineEvent({
1291
- name: "Editorial Workflows Studio Plugin Field Control Used",
1292
- version: 1,
1300
+ name: "Workflows Studio Plugin Field Control Used",
1301
+ version: 2,
1293
1302
  description: "A field-editing control committed an edit, attributed to its UI surface"
1294
1303
  }), WorkflowActivityDialogOpened = defineEvent({
1295
- name: "Editorial Workflows Studio Plugin Activity Dialog Opened",
1296
- version: 1,
1304
+ name: "Workflows Studio Plugin Activity Dialog Opened",
1305
+ version: 2,
1297
1306
  description: "The activity detail dialog was opened"
1298
- }), WorkflowTaskFiltersApplied = defineEvent({
1299
- name: "Editorial Workflows Studio Plugin Task Filters Applied",
1300
- version: 1,
1301
- description: "The tool's task-filter menu closed with a changed selection"
1302
1307
  }), WorkflowBoardWorkflowSelected = defineEvent({
1303
- name: "Editorial Workflows Studio Plugin Board Workflow Selected",
1304
- version: 2,
1305
- description: "The workflow documents page settled on the workflow it's showing"
1308
+ name: "Workflows Studio Plugin Board Workflow Selected",
1309
+ version: 3,
1310
+ description: "The Overview's board display settled on the workflow it's showing"
1306
1311
  }), WorkflowTitleSeedDrifted = defineEvent({
1307
- name: "Editorial Workflows Studio Plugin Title Seed Drifted",
1312
+ name: "Workflows Studio Plugin Title Seed Drifted",
1308
1313
  version: 1,
1309
1314
  description: "A cold-start seeded preview title diverged from the live preview pipeline"
1310
1315
  }), WorkflowTodoToggled = defineEvent({
1311
- name: "Editorial Workflows Studio Plugin Todo Toggled",
1316
+ name: "Workflows Studio Plugin Todo Toggled",
1312
1317
  version: 1,
1313
1318
  description: "A todo checkbox was toggled, attributed to its UI surface and write seam"
1314
1319
  }), WorkflowTodoEdited = defineEvent({
1315
- name: "Editorial Workflows Studio Plugin Todo Edited",
1320
+ name: "Workflows Studio Plugin Todo Edited",
1316
1321
  version: 1,
1317
1322
  description: "A non-toggle todo-list write, attributed to its UI surface and gesture"
1318
1323
  }), WorkflowAbortDialogOpened = defineEvent({
1319
- name: "Editorial Workflows Studio Plugin Abort Dialog Opened",
1324
+ name: "Workflows Studio Plugin Abort Dialog Opened",
1320
1325
  version: 1,
1321
1326
  description: "The abort-workflow confirm was opened"
1322
1327
  }), WorkflowAbortDialogSubmitted = defineEvent({
1323
- name: "Editorial Workflows Studio Plugin Abort Dialog Submitted",
1328
+ name: "Workflows Studio Plugin Abort Dialog Submitted",
1324
1329
  version: 1,
1325
1330
  description: "The abort-workflow confirm was submitted, carrying the attempt's outcome"
1326
1331
  });
@@ -1356,6 +1361,12 @@ function scalarValidationIssue(args) {
1356
1361
  return issues === void 0 ? void 0 : `${args.label}: ${issues.join("; ")}`;
1357
1362
  }
1358
1363
 
1364
+ function countBy(rows, keysOf) {
1365
+ const counts = /* @__PURE__ */ new Map;
1366
+ for (const row of rows) for (const key of keysOf(row)) counts.set(key, (counts.get(key) ?? 0) + 1);
1367
+ return counts;
1368
+ }
1369
+
1359
1370
  function personActor(user) {
1360
1371
  const roles = user.roles.map(role => typeof role == "string" ? role : role.name);
1361
1372
  return {
@@ -1387,10 +1398,20 @@ function userDisplayFor(args) {
1387
1398
  return {
1388
1399
  id: args.id,
1389
1400
  name: member?.displayName ?? self?.name ?? args.id,
1390
- imageUrl: member?.imageUrl ?? self?.profileImage ?? void 0
1401
+ imageUrl: member?.imageUrl ?? self?.profileImage ?? void 0,
1402
+ loginProvider: member?.loginProvider
1391
1403
  };
1392
1404
  }
1393
1405
 
1406
+ function disambiguatedNames(displays) {
1407
+ const counts = countBy(displays, display => [ display.name ]);
1408
+ return displays.map(display => (counts.get(display.name) ?? 0) < 2 || display.loginProvider === void 0 ? display.name : `${display.name} (${providerTitle(display.loginProvider) ?? display.loginProvider})`);
1409
+ }
1410
+
1411
+ function uniqueIds(ids) {
1412
+ return [ ...new Set(ids) ];
1413
+ }
1414
+
1394
1415
  function useUserDisplays(ids) {
1395
1416
  const {members: members} = useProjectMembers(), {users: users} = useStudioProjectUsers(), meRaw = useCurrentUser(), me = useMemo(() => meRaw === null ? null : {
1396
1417
  ...meRaw,
@@ -1473,6 +1494,10 @@ function identityOf(state) {
1473
1494
  return state.kind === "resolved" ? state.identity : void 0;
1474
1495
  }
1475
1496
 
1497
+ function useCurrentUserId() {
1498
+ return identityOf(useAssignmentIdentity())?.userId;
1499
+ }
1500
+
1476
1501
  const projectMembersByUsers = /* @__PURE__ */ new WeakMap;
1477
1502
 
1478
1503
  function projectMembersFrom(users) {
@@ -1863,23 +1888,27 @@ function MemberPicker({selectedIds: selectedIds, onSelect: onSelect, unassignRow
1863
1888
  });
1864
1889
  }
1865
1890
 
1866
- function AssigneePicker({value: value, onChange: onChange}) {
1891
+ function AssigneePicker({value: value, onChange: onChange, eligibleRoles: eligibleRoles, roleAliases: roleAliases}) {
1867
1892
  const state = useProjectMembers();
1868
1893
  /* @__PURE__ */
1869
1894
  return jsx(AssigneePicker$1, {
1870
1895
  ...state,
1896
+ eligibleRoles: eligibleRoles,
1871
1897
  onChange: onChange,
1898
+ roleAliases: roleAliases,
1872
1899
  value: value
1873
1900
  });
1874
1901
  }
1875
1902
 
1876
- function SingleAssigneePicker({current: current, onPick: onPick}) {
1903
+ function SingleAssigneePicker({current: current, onPick: onPick, eligibleRoles: eligibleRoles, roleAliases: roleAliases}) {
1877
1904
  /* @__PURE__ */
1878
1905
  return jsx(AssigneePicker, {
1906
+ eligibleRoles: eligibleRoles,
1879
1907
  onChange: next => {
1880
1908
  const picked = singleAssigneePick(current, next);
1881
1909
  picked !== void 0 && onPick(picked);
1882
1910
  },
1911
+ roleAliases: roleAliases,
1883
1912
  value: current ? [ current ] : []
1884
1913
  });
1885
1914
  }
@@ -2192,7 +2221,6 @@ const TOAST_ID = {
2192
2221
  effectResolve: "workflow-effect-resolve",
2193
2222
  effectsDrain: "workflow-effects-drain",
2194
2223
  effectsIncomplete: "workflow-effects-incomplete",
2195
- orphanSettle: "workflow-orphan-settle",
2196
2224
  start: "workflow-start"
2197
2225
  };
2198
2226
 
@@ -3052,8 +3080,7 @@ function PendingDocFace({bareId: bareId, layout: layout}) {
3052
3080
  children: id
3053
3081
  });
3054
3082
  }
3055
- return compact ?
3056
- /* @__PURE__ */ jsxs(Flex, {
3083
+ return compact ? /* @__PURE__ */ jsxs(Flex, {
3057
3084
  align: "center",
3058
3085
  gap: 2,
3059
3086
  children: [
@@ -3727,9 +3754,11 @@ function AssigneeField({field: field, instanceId: instanceId, onSave: onSave, on
3727
3754
  children: /* @__PURE__ */ jsx(DismissablePopover, {
3728
3755
  content: /* @__PURE__ */ jsx(SingleAssigneePicker, {
3729
3756
  current: cur,
3757
+ eligibleRoles: field.roles,
3730
3758
  onPick: picked => {
3731
3759
  setOpen(!1), pick(picked);
3732
- }
3760
+ },
3761
+ roleAliases: field.roleAliases
3733
3762
  }),
3734
3763
  onDismiss: () => setOpen(!1),
3735
3764
  open: open,
@@ -4408,6 +4437,12 @@ function useSpaceToken(index) {
4408
4437
  return value;
4409
4438
  }
4410
4439
 
4440
+ function useRadiusToken(index) {
4441
+ const value = useTheme_v2().radius[index];
4442
+ if (value === void 0) throw new Error(`theme is missing radius token ${index}`);
4443
+ return value;
4444
+ }
4445
+
4411
4446
  function useContainerToken(index) {
4412
4447
  const value = useTheme_v2().container[index];
4413
4448
  if (value === void 0) throw new Error(`theme is missing container token ${index}`);
@@ -4448,10 +4483,9 @@ function useBadgeCapTrim() {
4448
4483
  };
4449
4484
  }
4450
4485
 
4451
- function SpinnerSlot({busy: busy}) {
4486
+ function SpinnerSlot({busy: busy, reserve: reserve = !0}) {
4452
4487
  const size = useTextIconSize(), trim = useCapTrimFor(size), spinning = useDelayedFlag(busy, LOADING_CUE_DELAY_MS);
4453
- /* @__PURE__ */
4454
- return jsx(Flex, {
4488
+ return !spinning && !reserve ? null : /* @__PURE__ */ jsx(Flex, {
4455
4489
  align: "center",
4456
4490
  flex: "none",
4457
4491
  justify: "center",
@@ -4540,13 +4574,12 @@ function AvatarButton({avatar: avatar, onClick: onClick, onMouseDown: onMouseDow
4540
4574
  }
4541
4575
 
4542
4576
  function RowAssignees({assignees: assignees, hint: hint}) {
4543
- 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);
4577
+ const ids = userIdsOf(assignees), vocabulary = useProjectMembers(), roles = assignees.flatMap(item => item.type === "role" ? [ memberRoleFor(vocabulary, item.role) ] : []), displays = useUserDisplays(ids);
4544
4578
  if (ids.length === 0 && roles.length === 0) return null;
4545
4579
  const bothKinds = roles.length > 0 && displays.length > 0;
4546
4580
  /* @__PURE__ */
4547
4581
  return jsx(HoverHint, {
4548
- content:
4549
- /* @__PURE__ */ jsxs(Stack, {
4582
+ content: /* @__PURE__ */ jsxs(Stack, {
4550
4583
  gap: 3,
4551
4584
  children: [ roles.length > 0 ? /* @__PURE__ */ jsx(HintSection, {
4552
4585
  names: roles.map(roleLabel),
@@ -4554,7 +4587,7 @@ function RowAssignees({assignees: assignees, hint: hint}) {
4554
4587
  label: "Roles"
4555
4588
  } : {}
4556
4589
  }) : null, displays.length > 0 ? /* @__PURE__ */ jsx(HintSection, {
4557
- names: displays.map(display => display.name),
4590
+ names: disambiguatedNames(displays),
4558
4591
  ...bothKinds ? {
4559
4592
  label: "Members"
4560
4593
  } : {}
@@ -4633,8 +4666,7 @@ function AssignActivityControl({instanceId: instanceId, state: state, surface: s
4633
4666
  }
4634
4667
  }, handleClick = e => {
4635
4668
  e.stopPropagation(), setOpen(v => !v);
4636
- }, button = assignees.length > 0 ?
4637
- /* @__PURE__ */ jsx(AvatarButton, {
4669
+ }, button = assignees.length > 0 ? /* @__PURE__ */ jsx(AvatarButton, {
4638
4670
  "aria-label": "Assignees",
4639
4671
  as: "span",
4640
4672
  avatar: /* @__PURE__ */ jsx(RowAssignees, {
@@ -4664,9 +4696,11 @@ function AssignActivityControl({instanceId: instanceId, state: state, surface: s
4664
4696
  label: "Loading…",
4665
4697
  padding: 3
4666
4698
  }) : /* @__PURE__ */ jsx(AssigneePicker, {
4699
+ eligibleRoles: state.field.roles,
4667
4700
  onChange: next => {
4668
4701
  change(next);
4669
4702
  },
4703
+ roleAliases: state.field.roleAliases,
4670
4704
  value: current
4671
4705
  })
4672
4706
  }),
@@ -4768,8 +4802,7 @@ function DueDate({dueDate: dueDate, hintDisabled: hintDisabled, overdue: overdue
4768
4802
  const face = hintFace({
4769
4803
  overdue: overdue,
4770
4804
  reason: reason
4771
- }), text = overdue ?
4772
- /* @__PURE__ */ jsx(TextWithTone, {
4805
+ }), text = overdue ? /* @__PURE__ */ jsx(TextWithTone, {
4773
4806
  size: 1,
4774
4807
  tone: "caution",
4775
4808
  children: formatDate(dueDate)
@@ -4880,7 +4913,7 @@ function DueDateControl({item: item, editable: editable, onPatch: onPatch}) {
4880
4913
  }) : display;
4881
4914
  }
4882
4915
 
4883
- function AssigneeControl({item: item, editable: editable, onPatch: onPatch}) {
4916
+ function AssigneeControl({item: item, editable: editable, eligibleRoles: eligibleRoles, onPatch: onPatch, roleAliases: roleAliases}) {
4884
4917
  const [open, setOpen] = useState(!1), current = item.assignee ?? null, chip = current ? /* @__PURE__ */ jsx(RowAssignees, {
4885
4918
  assignees: [ current ]
4886
4919
  }) : null;
@@ -4888,11 +4921,13 @@ function AssigneeControl({item: item, editable: editable, onPatch: onPatch}) {
4888
4921
  content:
4889
4922
  /* @__PURE__ */ jsx(SingleAssigneePicker, {
4890
4923
  current: current,
4924
+ eligibleRoles: eligibleRoles,
4891
4925
  onPick: picked => {
4892
4926
  setOpen(!1), onPatch({
4893
4927
  assignee: picked
4894
4928
  });
4895
- }
4929
+ },
4930
+ roleAliases: roleAliases
4896
4931
  }),
4897
4932
  onDismiss: () => setOpen(!1),
4898
4933
  open: open,
@@ -4994,8 +5029,7 @@ function BreadcrumbTail({segments: segments, style: style}) {
4994
5029
  }
4995
5030
 
4996
5031
  function WorkRow({lead: lead, children: children, end: end, onOpen: onOpen}) {
4997
- const frame =
4998
- /* @__PURE__ */ jsxs(Flex, {
5032
+ const frame = /* @__PURE__ */ jsxs(Flex, {
4999
5033
  align: "center",
5000
5034
  children: [
5001
5035
  /* @__PURE__ */ jsx(Flex, {
@@ -5029,8 +5063,7 @@ function WorkRow({lead: lead, children: children, end: end, onOpen: onOpen}) {
5029
5063
  padding: 1,
5030
5064
  radius: 3,
5031
5065
  children: frame
5032
- }) :
5033
- /* @__PURE__ */ jsx(Card, {
5066
+ }) : /* @__PURE__ */ jsx(Card, {
5034
5067
  as: "button",
5035
5068
  onClick: onOpen,
5036
5069
  padding: 1,
@@ -5042,7 +5075,7 @@ function WorkRow({lead: lead, children: children, end: end, onOpen: onOpen}) {
5042
5075
  });
5043
5076
  }
5044
5077
 
5045
- function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle, onPatch: onPatch, onRemove: onRemove}) {
5078
+ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle, onPatch: onPatch, onRemove: onRemove, roleAliases: roleAliases}) {
5046
5079
  const {canClick: canClick, hint: hint} = rowInteractivity(row), editable = row.editTarget !== void 0, checkbox = /* @__PURE__ */ jsx(TodoCheckbox, {
5047
5080
  canClick: canClick,
5048
5081
  checked: isTodoDone(row.item),
@@ -5059,8 +5092,10 @@ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle,
5059
5092
  }),
5060
5093
  /* @__PURE__ */ jsx(AssigneeControl, {
5061
5094
  editable: editable,
5095
+ eligibleRoles: row.eligibleAssigneeRoles,
5062
5096
  item: row.item,
5063
- onPatch: onPatch
5097
+ onPatch: onPatch,
5098
+ roleAliases: roleAliases
5064
5099
  }), onRemove === void 0 ? null : /* @__PURE__ */ jsx(RemoveItemButton, {
5065
5100
  onClick: onRemove
5066
5101
  }) ]
@@ -5237,6 +5272,7 @@ function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter, sh
5237
5272
  onToggle: () => {
5238
5273
  toggleRow(row);
5239
5274
  },
5275
+ roleAliases: definition?.roleAliases,
5240
5276
  row: row
5241
5277
  }, rowKey(row)))
5242
5278
  }), addControl === null ? null : /* @__PURE__ */ jsx(Flex, {
@@ -5795,6 +5831,13 @@ function GroupHeading({busy: busy, count: count, title: title, end: end}) {
5795
5831
 
5796
5832
  const EMPTY_METRIC_OPACITY = .5;
5797
5833
 
5834
+ function labelledImgProps(label) {
5835
+ return label === void 0 ? {} : {
5836
+ "aria-label": label,
5837
+ role: "img"
5838
+ };
5839
+ }
5840
+
5798
5841
  function MetricText({children: children, tone: tone}) {
5799
5842
  return tone === void 0 ? /* @__PURE__ */ jsx(Text, {
5800
5843
  muted: !0,
@@ -5807,34 +5850,42 @@ function MetricText({children: children, tone: tone}) {
5807
5850
  });
5808
5851
  }
5809
5852
 
5810
- function MetricPair({Icon: Icon, empty: empty, face: face, tone: tone, value: value}) {
5853
+ function MetricPair({face: face, ...lockup}) {
5811
5854
  /* @__PURE__ */
5812
5855
  return jsx(HoverHint, {
5813
5856
  ...face,
5814
- children: /* @__PURE__ */ jsxs(Flex, {
5815
- align: "center",
5816
- gap: 2,
5817
- style: empty ? {
5818
- opacity: EMPTY_METRIC_OPACITY
5819
- } : void 0,
5820
- children: [
5821
- /* @__PURE__ */ jsx(MetricText, {
5822
- tone: tone,
5823
- children: /* @__PURE__ */ jsx(Icon, {
5824
- style: {
5825
- color: "inherit"
5826
- }
5827
- })
5828
- }),
5829
- /* @__PURE__ */ jsx(MetricText, {
5830
- tone: tone,
5831
- children: value
5832
- }) ]
5857
+ children: /* @__PURE__ */ jsx(MetricLockup, {
5858
+ ...lockup
5833
5859
  })
5834
5860
  });
5835
5861
  }
5836
5862
 
5837
- function AlertGlyph({Icon: Icon, hint: hint, tone: tone}) {
5863
+ function MetricLockup({Icon: Icon, empty: empty, label: label, tone: tone, value: value}) {
5864
+ /* @__PURE__ */
5865
+ return jsxs(Flex, {
5866
+ align: "center",
5867
+ gap: 2,
5868
+ style: empty ? {
5869
+ opacity: EMPTY_METRIC_OPACITY
5870
+ } : void 0,
5871
+ ...labelledImgProps(label),
5872
+ children: [
5873
+ /* @__PURE__ */ jsx(MetricText, {
5874
+ tone: tone,
5875
+ children: /* @__PURE__ */ jsx(Icon, {
5876
+ style: {
5877
+ color: "inherit"
5878
+ }
5879
+ })
5880
+ }),
5881
+ /* @__PURE__ */ jsx(MetricText, {
5882
+ tone: tone,
5883
+ children: value
5884
+ }) ]
5885
+ });
5886
+ }
5887
+
5888
+ function AlertGlyph({Icon: Icon, hint: hint, label: label, tone: tone}) {
5838
5889
  /* @__PURE__ */
5839
5890
  return jsx(HoverHint, {
5840
5891
  text: hint,
@@ -5842,6 +5893,7 @@ function AlertGlyph({Icon: Icon, hint: hint, tone: tone}) {
5842
5893
  size: 1,
5843
5894
  tone: tone,
5844
5895
  children: /* @__PURE__ */ jsx(Icon, {
5896
+ ...labelledImgProps(label),
5845
5897
  style: {
5846
5898
  color: "inherit"
5847
5899
  }
@@ -5850,6 +5902,37 @@ function AlertGlyph({Icon: Icon, hint: hint, tone: tone}) {
5850
5902
  });
5851
5903
  }
5852
5904
 
5905
+ function AlertChip({Icon: Icon, hint: hint, label: label, tone: tone}) {
5906
+ const glyph = useTextIconSize(), disc = glyph + useSpaceToken(1) * 2;
5907
+ /* @__PURE__ */
5908
+ return jsx(HoverHint, {
5909
+ text: hint,
5910
+ children: /* @__PURE__ */ jsx(Card, {
5911
+ radius: "full",
5912
+ style: {
5913
+ alignItems: "center",
5914
+ display: "flex",
5915
+ height: disc,
5916
+ justifyContent: "center",
5917
+ width: disc
5918
+ },
5919
+ tone: tone,
5920
+ children: /* @__PURE__ */ jsx(TextWithTone, {
5921
+ size: 1,
5922
+ tone: tone,
5923
+ children: /* @__PURE__ */ jsx(Icon, {
5924
+ ...labelledImgProps(label),
5925
+ style: {
5926
+ color: "inherit",
5927
+ height: glyph,
5928
+ width: glyph
5929
+ }
5930
+ })
5931
+ })
5932
+ })
5933
+ });
5934
+ }
5935
+
5853
5936
  function ActivityDateControl({instanceId: instanceId, state: state, surface: surface, dueDates: dueDates}) {
5854
5937
  const editField = useEditField(surface), {save: save} = useSaveField({
5855
5938
  instanceId: instanceId
@@ -6015,8 +6098,7 @@ function ActivityRow({face: face, breadcrumb: breadcrumb, assignees: assignees,
6015
6098
  const settled = isTerminalActivityStatus(face.status), dimmed = settledDim(settled);
6016
6099
  /* @__PURE__ */
6017
6100
  return jsxs(WorkRow, {
6018
- end:
6019
- /* @__PURE__ */ jsxs(Fragment, {
6101
+ end: /* @__PURE__ */ jsxs(Fragment, {
6020
6102
  children: [
6021
6103
  /* @__PURE__ */ jsx(ActivityDateControl, {
6022
6104
  dueDates: dueDates,
@@ -6064,8 +6146,7 @@ function ActivityRow({face: face, breadcrumb: breadcrumb, assignees: assignees,
6064
6146
 
6065
6147
  function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity, surface: surface}) {
6066
6148
  const activities = (entry.evaluation?.currentStage.activities ?? []).filter(t => !t.scopedOut), activityState = stateByActivity(entry.instance), fault = useInstanceFault(entry);
6067
- return activities.length === 0 ?
6068
- /* @__PURE__ */ jsx(Box, {
6149
+ return activities.length === 0 ? /* @__PURE__ */ jsx(Box, {
6069
6150
  paddingX: 1,
6070
6151
  paddingY: 3,
6071
6152
  children: /* @__PURE__ */ jsx(Text, {
@@ -6093,8 +6174,7 @@ function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity, surface:
6093
6174
 
6094
6175
  function instanceNotice(entry) {
6095
6176
  const {instance: instance, committed: committed, evaluation: evaluation} = entry, state = terminalState(instance);
6096
- if (state !== "in-flight")
6097
- /* @__PURE__ */
6177
+ if (state !== "in-flight") /* @__PURE__ */
6098
6178
  return jsx(Box, {
6099
6179
  paddingX: 1,
6100
6180
  paddingY: 2,
@@ -6282,7 +6362,7 @@ function UserAvatar({id: id}) {
6282
6362
  function UserAvatarGroup({ids: ids, hint: hint}) {
6283
6363
  const displays = useUserDisplays(ids);
6284
6364
  return displays.length === 0 ? null : /* @__PURE__ */ jsx(HoverHint, {
6285
- text: displays.map(d => d.name).join(", "),
6365
+ text: disambiguatedNames(displays).join(", "),
6286
6366
  ...hint === void 0 ? {} : {
6287
6367
  description: hint
6288
6368
  },
@@ -6341,8 +6421,7 @@ function FieldPills({entry: entry, scope: scope, definition: definition, surface
6341
6421
  pill: pill,
6342
6422
  scope: scope,
6343
6423
  surface: surface
6344
- }, pill.name)), overflow > 0 ?
6345
- /* @__PURE__ */ jsx(Button, {
6424
+ }, pill.name)), overflow > 0 ? /* @__PURE__ */ jsx(Button, {
6346
6425
  mode: "bleed",
6347
6426
  onClick: () => setExpanded(v => !v),
6348
6427
  padding: 2,
@@ -6467,7 +6546,7 @@ function AvatarCapAligned({children: children}) {
6467
6546
  }
6468
6547
 
6469
6548
  function AssigneesFace({assignees: assignees}) {
6470
- const vocabulary = useProjectMembers(), userIds = assignees.flatMap(a => a.type === "user" ? [ a.id ] : []), roles = assignees.flatMap(a => a.type === "role" ? [ roleLabelFor(vocabulary, a.role) ] : []);
6549
+ const vocabulary = useProjectMembers(), userIds = userIdsOf(assignees), roles = assignees.flatMap(a => a.type === "role" ? [ roleLabelFor(vocabulary, a.role) ] : []);
6471
6550
  /* @__PURE__ */
6472
6551
  return jsxs(Fragment, {
6473
6552
  children: [ userIds.length > 0 ? /* @__PURE__ */ jsx(AvatarCapAligned, {
@@ -6781,9 +6860,11 @@ function AssigneePill({entry: entry, pill: pill, editability: editability, surfa
6781
6860
  return jsx(PillPopover, {
6782
6861
  content: /* @__PURE__ */ jsx(SingleAssigneePicker, {
6783
6862
  current: current,
6863
+ eligibleRoles: editability.roles,
6784
6864
  onPick: picked => {
6785
6865
  setOpen(!1), pick(picked);
6786
- }
6866
+ },
6867
+ roleAliases: editability.roleAliases
6787
6868
  }),
6788
6869
  onDismiss: () => setOpen(!1),
6789
6870
  onToggle: () => setOpen(v => !v),
@@ -6797,13 +6878,15 @@ function AssigneesPill({entry: entry, pill: pill, editability: editability, surf
6797
6878
  entry: entry,
6798
6879
  editability: editability,
6799
6880
  surface: surface
6800
- }), current = pill.face.kind === "assignees" ? pill.face.assignees : [];
6881
+ }), current = pill.face.kind === "assignees" ? pill.face.assignees : [], change = next => {
6882
+ saving || save(() => set(next));
6883
+ };
6801
6884
  /* @__PURE__ */
6802
6885
  return jsx(PillPopover, {
6803
6886
  content: /* @__PURE__ */ jsx(AssigneePicker, {
6804
- onChange: next => {
6805
- saving || save(() => set(next));
6806
- },
6887
+ eligibleRoles: editability.roles,
6888
+ onChange: change,
6889
+ roleAliases: editability.roleAliases,
6807
6890
  value: current
6808
6891
  }),
6809
6892
  onDismiss: () => setOpen(!1),
@@ -7389,6 +7472,24 @@ function historyLine(h, titles) {
7389
7472
  return historyLiner[h._type](h, titles);
7390
7473
  }
7391
7474
 
7475
+ function AgoStamp({at: at, now: now}) {
7476
+ /* @__PURE__ */
7477
+ return jsx(Flex, {
7478
+ align: "center",
7479
+ children: /* @__PURE__ */ jsx(HoverHint, {
7480
+ text: formatShortDateTime(at),
7481
+ children: /* @__PURE__ */ jsx(Text, {
7482
+ muted: !0,
7483
+ size: 1,
7484
+ style: {
7485
+ whiteSpace: "nowrap"
7486
+ },
7487
+ children: formatShortAgo(at, now)
7488
+ })
7489
+ })
7490
+ });
7491
+ }
7492
+
7392
7493
  function CodeChip({value: value}) {
7393
7494
  /* @__PURE__ */
7394
7495
  return jsx("code", {
@@ -7614,16 +7715,9 @@ function FeedStamp({at: at, now: now}) {
7614
7715
  size: 1,
7615
7716
  children: "·"
7616
7717
  }),
7617
- /* @__PURE__ */ jsx(HoverHint, {
7618
- text: formatDateTime(at),
7619
- children: /* @__PURE__ */ jsx(Text, {
7620
- muted: !0,
7621
- size: 1,
7622
- style: {
7623
- whiteSpace: "nowrap"
7624
- },
7625
- children: formatShortAgo(at, now)
7626
- })
7718
+ /* @__PURE__ */ jsx(AgoStamp, {
7719
+ at: at,
7720
+ now: now
7627
7721
  }) ]
7628
7722
  });
7629
7723
  }
@@ -7732,8 +7826,7 @@ function InstanceCardMenu({entry: entry, title: title}) {
7732
7826
  return jsxs(Fragment, {
7733
7827
  children: [
7734
7828
  /* @__PURE__ */ jsx(MenuButton, {
7735
- button:
7736
- /* @__PURE__ */ jsx(Button, {
7829
+ button: /* @__PURE__ */ jsx(Button, {
7737
7830
  "aria-label": `More options for ${title}`,
7738
7831
  fontSize: 1,
7739
7832
  icon: EllipsisHorizontalIcon,
@@ -8054,8 +8147,7 @@ function StageOverviewCard({overview: overview}) {
8054
8147
  size: 1,
8055
8148
  weight: "medium",
8056
8149
  children: overview.title
8057
- }), overview.description === void 0 ? null :
8058
- /* @__PURE__ */ jsx(Text, {
8150
+ }), overview.description === void 0 ? null : /* @__PURE__ */ jsx(Text, {
8059
8151
  muted: !0,
8060
8152
  size: 1,
8061
8153
  style: {
@@ -8099,8 +8191,7 @@ function AdvanceCard({advance: advance}) {
8099
8191
  /* @__PURE__ */ jsx(InlineEmphasis, {
8100
8192
  children: advance.toTitle
8101
8193
  }), ADVANCE_TAILS[advance.kind] ]
8102
- }), advance.kind !== "conditions" ? null : advance.lines.map((line, index) =>
8103
- /* @__PURE__ */ jsx(Text, {
8194
+ }), advance.kind !== "conditions" ? null : advance.lines.map((line, index) => /* @__PURE__ */ jsx(Text, {
8104
8195
  muted: !0,
8105
8196
  size: 1,
8106
8197
  style: {
@@ -9154,8 +9245,7 @@ const VIEW_TAB_CODEC = workflowsTabCodec([ "overview", "for-me" ]), WorkflowsVie
9154
9245
  children: /* @__PURE__ */ jsx(InvalidDocNotice, {
9155
9246
  invalid: discoveryInvalid
9156
9247
  })
9157
- }) : resolved ?
9158
- /* @__PURE__ */ jsx(Flex, {
9248
+ }) : resolved ? /* @__PURE__ */ jsx(Flex, {
9159
9249
  direction: "column",
9160
9250
  height: "fill",
9161
9251
  padding: 4,
@@ -9457,7 +9547,10 @@ function resolveEntry(args) {
9457
9547
  }
9458
9548
  valid.push(name), definition && definitions.push({
9459
9549
  name: name,
9460
- fields: definition.fields
9550
+ fields: definition.fields,
9551
+ ...definition.roleAliases === void 0 ? {} : {
9552
+ roleAliases: definition.roleAliases
9553
+ }
9461
9554
  });
9462
9555
  }
9463
9556
  return {
@@ -9974,6 +10067,7 @@ function WorkflowProvider(props) {
9974
10067
  engine: engine,
9975
10068
  binding: binding,
9976
10069
  mappings: mappings,
10070
+ previewHydration: config.previewHydration,
9977
10071
  mappingIssues: issues,
9978
10072
  mappingStarts: starts,
9979
10073
  mappingFields: fields,
@@ -9981,7 +10075,7 @@ function WorkflowProvider(props) {
9981
10075
  effectHandlers: engine.effectHandlers,
9982
10076
  autoStartByType: autoStartByType,
9983
10077
  autoStartDefinitions: autoStartDefinitions
9984
- }), [ register, unregister, isDocResolved, entriesStore, actualTypes, requestInstance, releaseInstance, requestEvaluation, releaseEvaluation, seedInstance, openStartDialog, closeStartDialog, startRequest2, loading, discoveryInvalid, fireActionFor, editFieldFor, previewFieldFor, discardFieldPreviewFor, drainEffectsFor, completeEffectFor, engine, binding, mappings, issues, starts, fields, latestVersions, autoStartByType, autoStartDefinitions ]);
10078
+ }), [ register, unregister, isDocResolved, entriesStore, actualTypes, requestInstance, releaseInstance, requestEvaluation, releaseEvaluation, seedInstance, openStartDialog, closeStartDialog, startRequest2, loading, discoveryInvalid, fireActionFor, editFieldFor, previewFieldFor, discardFieldPreviewFor, drainEffectsFor, completeEffectFor, engine, binding, mappings, config.previewHydration, issues, starts, fields, latestVersions, autoStartByType, autoStartDefinitions ]);
9985
10079
  /* @__PURE__ */
9986
10080
  return jsxs(WorkflowContext.Provider, {
9987
10081
  value: value,
@@ -10083,7 +10177,7 @@ function logOnce(messages, logged) {
10083
10177
  for (const message of messages) logged.has(message) || (logged.add(message), console.warn(`[workflow-studio-plugin] ${message}`));
10084
10178
  }
10085
10179
 
10086
- function AssigneesInput({value: value, onChange: onChange}) {
10180
+ function AssigneesInput({value: value, onChange: onChange, eligibleRoles: eligibleRoles, roleAliases: roleAliases}) {
10087
10181
  const [open, setOpen] = useState(!1), remove = index => onChange(value.filter((_, i) => i !== index));
10088
10182
  /* @__PURE__ */
10089
10183
  return jsxs(Stack, {
@@ -10117,7 +10211,9 @@ function AssigneesInput({value: value, onChange: onChange}) {
10117
10211
  wrap: "wrap",
10118
10212
  children: /* @__PURE__ */ jsx(DismissablePopover, {
10119
10213
  content: /* @__PURE__ */ jsx(AssigneePicker, {
10214
+ eligibleRoles: eligibleRoles,
10120
10215
  onChange: next => onChange([ ...next ]),
10216
+ roleAliases: roleAliases,
10121
10217
  value: value
10122
10218
  }),
10123
10219
  onDismiss: () => setOpen(!1),
@@ -10186,11 +10282,29 @@ function ChecklistInput({value: value, onChange: onChange}) {
10186
10282
  });
10187
10283
  }
10188
10284
 
10189
- function ReleasePicker({value: value, onChange: onChange}) {
10190
- const {binding: binding} = useWorkflowContext(), {data: data, loading: loading, error: error} = useActiveReleases(), releases = useMemo(() => data.filter(release => release.state === "active").map(release => ({
10285
+ function titleOf(release) {
10286
+ return release.metadata.title ?? release.name;
10287
+ }
10288
+
10289
+ function useReleaseTitles() {
10290
+ const {data: data} = useActiveReleases();
10291
+ return useMemo(() => new Map(data.map(release => [ release.name, titleOf(release) ])), [ data ]);
10292
+ }
10293
+
10294
+ function useActiveReleaseOptions() {
10295
+ const {data: data, loading: loading, error: error} = useActiveReleases(), releases = useMemo(() => data.filter(release => release.state === "active").map(release => ({
10191
10296
  name: release.name,
10192
- title: release.metadata.title ?? release.name
10193
- })).toSorted((a, b) => a.title.localeCompare(b.title)), [ data ]), labelOf = useMemo(() => {
10297
+ title: titleOf(release)
10298
+ })).toSorted((a, b) => a.title.localeCompare(b.title)), [ data ]);
10299
+ return {
10300
+ error: error,
10301
+ loading: loading,
10302
+ releases: releases
10303
+ };
10304
+ }
10305
+
10306
+ function ReleasePicker({value: value, onChange: onChange}) {
10307
+ const {binding: binding} = useWorkflowContext(), {error: error, loading: loading, releases: releases} = useActiveReleaseOptions(), labelOf = useMemo(() => {
10194
10308
  const m = new Map(releases.map(r => [ r.name, r.title ]));
10195
10309
  return name => m.get(name) ?? name;
10196
10310
  }, [ releases ]);
@@ -10458,8 +10572,10 @@ const docRefInput = ({entry: entry, docType: docType, releaseId: releaseId, valu
10458
10572
  onChange: onChange,
10459
10573
  value: value ?? []
10460
10574
  }),
10461
- assignees: ({value: value, onChange: onChange}) => /* @__PURE__ */ jsx(AssigneesInput, {
10575
+ assignees: ({entry: entry, roleAliases: roleAliases, value: value, onChange: onChange}) => /* @__PURE__ */ jsx(AssigneesInput, {
10576
+ eligibleRoles: entry.roles,
10462
10577
  onChange: onChange,
10578
+ roleAliases: roleAliases,
10463
10579
  value: value ?? []
10464
10580
  }),
10465
10581
  array: ({entry: entry, value: value, onChange: onChange}) => {
@@ -10501,7 +10617,7 @@ function FieldInput(props) {
10501
10617
  });
10502
10618
  }
10503
10619
 
10504
- function InitFieldRow({entry: entry, docType: docType, releaseId: releaseId, value: value, onChange: onChange}) {
10620
+ function InitFieldRow({entry: entry, docType: docType, releaseId: releaseId, roleAliases: roleAliases, value: value, onChange: onChange}) {
10505
10621
  const label = entry.title ?? entry.name;
10506
10622
  /* @__PURE__ */
10507
10623
  return jsxs(Stack, {
@@ -10525,6 +10641,7 @@ function InitFieldRow({entry: entry, docType: docType, releaseId: releaseId, val
10525
10641
  entry: entry,
10526
10642
  onChange: onChange,
10527
10643
  releaseId: releaseId,
10644
+ roleAliases: roleAliases,
10528
10645
  value: value
10529
10646
  }) ]
10530
10647
  });
@@ -10574,7 +10691,7 @@ function StartFooter({canStart: canStart, starting: starting, startLabel: startL
10574
10691
  });
10575
10692
  }
10576
10693
 
10577
- function StartInputList({initFields: initFields, mapping: mapping, releaseId: releaseId, values: values, onChange: onChange}) {
10694
+ function StartInputList({initFields: initFields, mapping: mapping, releaseId: releaseId, roleAliases: roleAliases, values: values, onChange: onChange}) {
10578
10695
  return initFields.length === 0 ? /* @__PURE__ */ jsx(Text, {
10579
10696
  muted: !0,
10580
10697
  size: 1,
@@ -10585,6 +10702,7 @@ function StartInputList({initFields: initFields, mapping: mapping, releaseId: re
10585
10702
  entry: entry,
10586
10703
  onChange: v => onChange(entry.name, v),
10587
10704
  releaseId: releaseId,
10705
+ roleAliases: roleAliases,
10588
10706
  value: values[entry.name] ?? null
10589
10707
  }, entry.name))
10590
10708
  });
@@ -10715,6 +10833,7 @@ function StartWorkflowForm({request: request, onClose: onClose}) {
10715
10833
  mapping: mapping,
10716
10834
  onChange: setValue,
10717
10835
  releaseId: releaseId,
10836
+ roleAliases: definition.roleAliases,
10718
10837
  values: values
10719
10838
  }),
10720
10839
  /* @__PURE__ */ jsx(StartFooter, {
@@ -10769,11 +10888,12 @@ function AutoStartInputDialog(props) {
10769
10888
  }),
10770
10889
  /* @__PURE__ */ jsx(Stack, {
10771
10890
  gap: 4,
10772
- children: inputs.map(({entry: entry}) => /* @__PURE__ */ jsx(InitFieldRow, {
10891
+ children: inputs.map(({entry: entry, roleAliases: roleAliases}) => /* @__PURE__ */ jsx(InitFieldRow, {
10773
10892
  docType: docType,
10774
10893
  entry: entry,
10775
10894
  onChange: value => setValue(entry.name, value),
10776
10895
  releaseId: void 0,
10896
+ roleAliases: roleAliases,
10777
10897
  value: values[entry.name] ?? null
10778
10898
  }, entry.name))
10779
10899
  }),
@@ -10802,23 +10922,65 @@ function subjectEntry(definition) {
10802
10922
  return documentSubjectEntry(definition);
10803
10923
  }
10804
10924
 
10805
- function autoStartInputs(args) {
10925
+ function autoStartInputPlan(args) {
10806
10926
  const byName = /* @__PURE__ */ new Map;
10807
10927
  for (const name of args.workflows) {
10808
10928
  const definition = args.definitions.get(name);
10809
10929
  if (definition !== void 0) for (const entry of extraRequiredInputs(definition)) {
10810
- const existing = byName.get(entry.name);
10811
- existing === void 0 ? byName.set(entry.name, {
10930
+ const conflict = mergeAutoStartInput(byName, {
10812
10931
  entry: entry,
10813
- workflows: [ name ]
10814
- }) : existing.workflows.push(name);
10932
+ workflow: name,
10933
+ roleAliases: definition.roleAliases
10934
+ });
10935
+ if (conflict !== void 0) return {
10936
+ inputs: [],
10937
+ conflict: conflict
10938
+ };
10815
10939
  }
10816
10940
  }
10817
- return [ ...byName.values() ];
10941
+ return {
10942
+ inputs: [ ...byName.values() ]
10943
+ };
10944
+ }
10945
+
10946
+ function mergeAutoStartInput(byName, args) {
10947
+ const existing = byName.get(args.entry.name);
10948
+ if (existing === void 0) {
10949
+ byName.set(args.entry.name, {
10950
+ entry: args.entry,
10951
+ workflows: [ args.workflow ],
10952
+ ...args.roleAliases === void 0 ? {} : {
10953
+ roleAliases: args.roleAliases
10954
+ }
10955
+ });
10956
+ return;
10957
+ }
10958
+ if (!sameRoleSet(existing.entry.roles, args.entry.roles)) return `Auto-start input "${args.entry.name}" is shared by workflows "${existing.workflows.join('", "')}" and "${args.workflow}" with different role constraints`;
10959
+ existing.workflows.push(args.workflow), sameRoleAliases(existing.roleAliases, args.roleAliases) || delete existing.roleAliases;
10960
+ }
10961
+
10962
+ function sameRoleSet(left, right) {
10963
+ return roleSetKey(left) === roleSetKey(right);
10964
+ }
10965
+
10966
+ function roleSetKey(roles) {
10967
+ return roles === void 0 ? void 0 : JSON.stringify(sortedUnique(roles));
10968
+ }
10969
+
10970
+ function sameRoleAliases(left, right) {
10971
+ return roleAliasesKey(left) === roleAliasesKey(right);
10972
+ }
10973
+
10974
+ function roleAliasesKey(aliases) {
10975
+ if (aliases !== void 0) return JSON.stringify(Object.entries(aliases).map(([role, fulfillers]) => [ role, sortedUnique(fulfillers) ]).toSorted(([left], [right]) => left.localeCompare(right)));
10976
+ }
10977
+
10978
+ function sortedUnique(values) {
10979
+ return [ ...new Set(values) ].toSorted();
10818
10980
  }
10819
10981
 
10820
10982
  function autoStartAction(args) {
10821
- return args.fired ? args.settled ? "reveal" : "starting" : args.pendingCount === 0 ? "reveal" : args.resolved ? args.needsInput ? "collect" : "silent-start" : "wait";
10983
+ return args.fired ? args.settled ? "reveal" : "starting" : args.blocked || args.pendingCount === 0 ? "reveal" : args.resolved ? args.needsInput ? "collect" : "silent-start" : "wait";
10822
10984
  }
10823
10985
 
10824
10986
  function pendingWorkflows(workflows, live) {
@@ -10875,10 +11037,14 @@ function AutoStartDriver(props) {
10875
11037
  engine: engine
10876
11038
  }), {selectedReleaseId: selectedReleaseId} = usePerspective(), {entries: entries} = useWorkflowsForDocument(rawDocId), docId = getPublishedId(rawDocId), valueRef = useRef(value);
10877
11039
  valueRef.current = value;
10878
- const pending = useMemo(() => pendingWorkflows(workflows, new Set(entries.map(entry => entry.instance.definition))), [ workflows, entries ]), inputs = useMemo(() => autoStartInputs({
11040
+ const pending = useMemo(() => pendingWorkflows(workflows, new Set(entries.map(entry => entry.instance.definition))), [ workflows, entries ]), inputPlan = useMemo(() => autoStartInputPlan({
10879
11041
  workflows: pending,
10880
11042
  definitions: autoStartDefinitions
10881
- }), [ pending, autoStartDefinitions ]), definitionsResolved = workflows.every(name => autoStartDefinitions.has(name)), pendingRef = useRef(pending);
11043
+ }), [ pending, autoStartDefinitions ]), inputs = inputPlan.inputs;
11044
+ useEffect(() => {
11045
+ inputPlan.conflict !== void 0 && console.warn(`[Workflows] Auto-start skipped. ${inputPlan.conflict}`);
11046
+ }, [ inputPlan.conflict ]);
11047
+ const definitionsResolved = workflows.every(name => autoStartDefinitions.has(name)), pendingRef = useRef(pending);
10882
11048
  pendingRef.current = pending;
10883
11049
  const instanceIds = useRef(/* @__PURE__ */ new Map), instanceIdFor = useCallback(workflow => {
10884
11050
  const held = instanceIds.current.get(workflow);
@@ -10930,6 +11096,7 @@ function AutoStartDriver(props) {
10930
11096
  resolved: definitionsResolved && isDocResolved(docId),
10931
11097
  pendingCount: pending.length,
10932
11098
  needsInput: inputs.length > 0,
11099
+ blocked: inputPlan.conflict !== void 0,
10933
11100
  fired: fired,
10934
11101
  settled: settled
10935
11102
  });
@@ -11375,19 +11542,13 @@ function StartWorkflowDialogHost() {
11375
11542
  }
11376
11543
 
11377
11544
  const TOOL_TAB_LABELS = {
11378
- overview: "Workflows",
11379
- "all-documents": "All documents",
11380
- "for-me": "For me"
11545
+ overview: "Overview",
11546
+ "for-me": "For me",
11547
+ definitions: "Definitions"
11381
11548
  }, TOOL_TAB_VALUES = Object.keys(TOOL_TAB_LABELS), TOOL_TABS = TOOL_TAB_VALUES.map(value => ({
11382
11549
  value: value,
11383
11550
  label: TOOL_TAB_LABELS[value]
11384
- })), LANDING_TAB = "overview", TASKS_TAB = "all-documents", WORKFLOW_PAGE_LABELS = {
11385
- workflow: "Documents",
11386
- definition: "Definition"
11387
- }, WORKFLOW_PAGE_VALUES = Object.keys(WORKFLOW_PAGE_LABELS), DEFAULT_WORKFLOW_PAGE = "workflow", WORKFLOW_PAGES = WORKFLOW_PAGE_VALUES.filter(page => page !== DEFAULT_WORKFLOW_PAGE), WORKFLOW_PAGE_TABS = WORKFLOW_PAGE_VALUES.map(value => ({
11388
- value: value,
11389
- label: WORKFLOW_PAGE_LABELS[value]
11390
- })), toolRouter = route.create("/", [ route.create("/instance/:instanceId"), route.create(`/${LANDING_TAB}/:workflowName`, [ route.create("/:workflowPage") ]), route.create("/:workflowsTab") ]);
11551
+ })), LANDING_TAB = "overview", DEFINITIONS_TAB = "definitions", toolRouter = route.create("/", [ route.create(`/${DEFINITIONS_TAB}/:workflowName`), route.create("/:workflowsTab") ]);
11391
11552
 
11392
11553
  function toolTabState(tab) {
11393
11554
  return {
@@ -11405,39 +11566,17 @@ function workflowState(workflowName) {
11405
11566
  };
11406
11567
  }
11407
11568
 
11408
- function instanceState(instanceId) {
11409
- return {
11410
- instanceId: instanceId
11411
- };
11412
- }
11413
-
11414
11569
  function tabSelectionNavigates(current, tab) {
11415
11570
  return !(current.kind === "home" && tab === current.tab);
11416
11571
  }
11417
11572
 
11418
- function toolTabLabel(tab) {
11419
- return TOOL_TAB_LABELS[tab];
11420
- }
11421
-
11422
- function backToTabLabel(tab) {
11423
- return `Back to ${toolTabLabel(tab)}`;
11424
- }
11425
-
11426
- function workflowPageState(page, workflowName) {
11427
- return page === DEFAULT_WORKFLOW_PAGE ? workflowState(workflowName) : {
11428
- ...workflowState(workflowName),
11429
- workflowPage: page
11430
- };
11431
- }
11432
-
11433
11573
  function toolRoute(state) {
11434
- const {instanceId: instanceId, workflowName: workflowName} = state;
11435
- if (typeof instanceId == "string") return {
11436
- kind: "instance",
11437
- instanceId: instanceId,
11438
- tab: TASKS_TAB
11574
+ const {workflowName: workflowName} = state;
11575
+ if (typeof workflowName == "string") return {
11576
+ kind: "definition",
11577
+ workflowName: workflowName,
11578
+ tab: DEFINITIONS_TAB
11439
11579
  };
11440
- if (typeof workflowName == "string") return workflowRoute(workflowName, state.workflowPage);
11441
11580
  const tab = pathSegmentName({
11442
11581
  segment: state.workflowsTab,
11443
11582
  names: TOOL_TAB_VALUES
@@ -11451,24 +11590,11 @@ function toolRoute(state) {
11451
11590
  };
11452
11591
  }
11453
11592
 
11454
- function workflowRoute(workflowName, pageSegment) {
11455
- if (pageSegment === void 0) return {
11456
- kind: "workflow",
11457
- workflowName: workflowName,
11458
- tab: LANDING_TAB
11459
- };
11460
- const page = pathSegmentName({
11461
- segment: pageSegment,
11462
- names: WORKFLOW_PAGES
11463
- });
11464
- return page === void 0 ? {
11465
- kind: "unnamed",
11466
- tab: LANDING_TAB
11467
- } : {
11468
- kind: page,
11469
- workflowName: workflowName,
11470
- tab: LANDING_TAB
11593
+ function definitionsRouteOf(route2) {
11594
+ if (route2.kind === "definition") return {
11595
+ workflowName: route2.workflowName
11471
11596
  };
11597
+ if (route2.kind === "home" && route2.tab === "definitions") return {};
11472
11598
  }
11473
11599
 
11474
11600
  const WorkflowsToolRoot = lazy(() => import("./workflows-tool-root.js"));
@@ -11580,4 +11706,4 @@ function WorkflowRootInput(props) {
11580
11706
  });
11581
11707
  }
11582
11708
 
11583
- export { AbortWorkflowDialog, ActivityDetailDialog, ActivityLog, ActivityRow, AlertGlyph, BreadcrumbTail, CodeChip, CountedHeading, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, ForMeEmptyState, Hairline, HoverHint, InlineEmphasis, InstanceFaultNote, InstanceSnapshotBody, InvalidDocNotice, LinkChip, LoadingRow, LogEventOnMount, MetaRow, MetricPair, 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, formatDate, formatShortAgo, formatShortDateTime, formatTimeAgo, gdrLocality, identityOf, instanceBreadcrumb, instanceState, instanceTitle, isActivityAssignedTo, isDueDatePast, isEvaluationStale, isLiveEntry, isOpenActivityStatus, isRoleAssignedTo, landingState, mappingIssueDetail, namesNoDeployedDefinition, openActivityGone, openableSchemaType, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, stageTitle, stateByActivity, tabSelectionNavigates, toolRoute, toolTabState, useAssignmentIdentity, useContainerToken, useDefinition, useLogEventOnMount, useMinuteClock, useProjectMembers, useSpaceToken, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, useWorkflowToast, workflowDefaultDocumentNode, workflowPageState, workflowState, workflowStudioPlugin, workflowsView };
11709
+ export { AbortWorkflowDialog, ActivityDetailDialog, ActivityLog, AgoStamp, AlertChip, AlertGlyph, CodeChip, CountedHeading, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, HoverHint, InstanceFaultNote, InstanceSnapshotBody, InvalidDocNotice, LoadingRow, LogEventOnMount, MetaRow, MetricLockup, MetricPair, NoteBanner, RUN_SEARCH_PARAM, RoleAssignedNotice, SpinnerSlot, TOOL_TABS, TabSwitch, UnreadableDocsNote, WORKFLOW_API_VERSION, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTitleSeedDrifted, WorkflowToolOpened, activityLabel, assigneesOf, countBy, definitionFingerprint, definitionsRouteOf, describeError, disambiguatedNames, dueDatesOf, findActivity, formatShortDateTime, formatTimeAgo, gdrLocality, identityOf, instanceBreadcrumb, instanceTitle, isDueDatePast, isEvaluationStale, isHandsOff, isLiveEntry, isOpenActivityStatus, landingState, mappingIssueDetail, namesNoDeployedDefinition, openActivityGone, openableSchemaType, parseStoredDateValue, readDeployedDefinitions, shortSpanOf, stageTitle, tabSelectionNavigates, toolRoute, toolTabState, uniqueIds, useAssignmentIdentity, useAvatarSize, useCapTrimFor, useContainerToken, useCurrentUserId, useDefinition, useLogEventOnMount, useMinuteClock, useProjectMembers, useRadiusToken, useReleaseTitles, useSpaceToken, useTextIconSize, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, userIdsOf, workflowDefaultDocumentNode, workflowState, workflowStudioPlugin, workflowsView };